sdWrite only two bytes

ChibiOS public support forum for all topics not covered by a specific support forum.

Moderators: RoccoMarco, lbednarz, utzig, tfAteba, barthess

matis
Posts: 53
Joined: Fri Jul 01, 2011 1:46 pm

sdWrite only two bytes

Postby matis » Wed Feb 29, 2012 11:22 am

Hello,

currently I'm working on a UART communication between two platforms. One is equipped with an STM32F103VET6. I have to use UART3 for the communication. What it does (should do) is receive an byte array from the master, change a byte and reply the byte array to the master. For debugging I'm only echoing the bytes received.
I can successfully read the whole message until the carriage return character. But when I try to transmit the buffer, the sdWrite only puts the two first bytes on the phy. This happens every time. I've already removed the receiver and fill the tx_buf with dummy data.
When I remove the resetting of the in- and output queue it works like it should, but I have to be sure that the buffers are completely empty.
My code looks (simplified) like this:

Code: Select all


static SerialConfig uart3_cfg 
= {
        .sc_speed = 115200,
        /* 8 bits UART */
        .sc_cr1 = USART_CR1_WAKE,
        .sc_cr2 = 0,
        .sc_cr3 = 0,
};

static void /*@null@*/* listen(void /*@unused@*/*a)
{
    int bus_addr = max_get_bus_addr();
    uint8_t rx_buf[32] = { 0 };
    uint8_t tx_buf[32] = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19 };
    init(bus_addr);
    while (1)
    {
#if 0
    /* Original code, not working. Outputs only two first bytes. All ten bytes are read correctly. */
        chIQResetI(&SD3.iqueue);
        chOQResetI(&SD3.oqueue);
        memset(rx_buf, 0, sizeof(rx_buf));
        memset(tx_buf, 0, sizeof(tx_buf));
        size_t length = sdGetLine(&SD3, rx_buf, sizeof(rx_buf));
        sdWrite(&SD3, rx_buf, length);
#elif 0
    /* Working code. Outputs all 10 bytes */
        chThdSleepMilliseconds(1000);
        sdWrite(&SD3, tx_buf, 10);
#else
    /* Dummy read code, not working. Outputs only two first bytes */
        chIQResetI(&SD3.iqueue);
        chOQResetI(&SD3.oqueue);
        memset(rx_buf, 0, sizeof(rx_buf));
        memset(tx_buf, 0, sizeof(tx_buf));
        /* Simulate data read from UART*/
        uint8_t tx_switch[10] = { 0x01, 0x08, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, '\r' };
        size_t length = sizeof(tx_switch);
        chThdSleepMilliseconds(1000);
        memcpy(rx_buf, tx_switch, length);
        sdWrite(&SD3, rx_buf, length);
#endif
    }
    exit();
    return NULL;
}

static size_t sdGetLine(SerialDriver *sdp, uint8_t *buf, size_t max)
{
    size_t n;
    uint8_t c;
    n = 0;
    do
    
{
        c = sdGet(sdp);
        *buf = c;
        buf++;
        n++;
        max--;
    } while ((c != '\r') && (max > 0));
    *buf = 0;
    return n;
}

static void init(int bus_addr)
{
    uart3_cfg.sc_cr2 |= (bus_addr & 0x0F);
    sdStart(&SD3, &uart3_cfg);
}

static void exit(void)
{
    sdStop(&SD3);
}
 


Hope that someone can point me into the right direction to fix this problem. I'm working this 2.3.4 branch: ftp://opensource.axon.tv/mirror/ChibiOS_2.3.4.zip

When you want more info, please let me know :)
Thanks in advance!

Matis

matis
Posts: 53
Joined: Fri Jul 01, 2011 1:46 pm

Re: sdWrite only two bytes

Postby matis » Wed Feb 29, 2012 11:43 am

The problem seems to be in the chOQResetI(&SD3.oqueue);

When I remove this line, all the ten bytes are outputted like it should. Not sure why :(

User avatar
Giovanni
Site Admin
Posts: 14458
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: sdWrite only two bytes

Postby Giovanni » Wed Feb 29, 2012 11:53 am

It is easy to understand.

When you exit from sdWrite() you copied the message in the transmission buffer but it is still being transmitted asynchronously, when you do the reset then the still un-transmitted data is removed from the queue.

You need to make sure that your message has been transmitted, resetting the queue does not do that, that simply stops the transmission.

You can do one of the following:
1) Use the UART driver which is synchronous, a callback is sent when data is completely transmitted.
2) Use the serial driver and wait for a driver event, when the transmission is finished a IO_TRANSMISSION_END flag is added to the mask. This requires the use of an event listener and event APIs.
3) Simply add a small delay after sdWrite(), for example 5mS. Quick and dirty.

Giovanni

matis
Posts: 53
Joined: Fri Jul 01, 2011 1:46 pm

Re: sdWrite only two bytes

Postby matis » Wed Feb 29, 2012 3:28 pm

Giovanni wrote:It is easy to understand.

When you exit from sdWrite() you copied the message in the transmission buffer but it is still being transmitted asynchronously, when you do the reset then the still un-transmitted data is removed from the queue.

You need to make sure that your message has been transmitted, resetting the queue does not do that, that simply stops the transmission.

You can do one of the following:
1) Use the UART driver which is synchronous, a callback is sent when data is completely transmitted.
2) Use the serial driver and wait for a driver event, when the transmission is finished a IO_TRANSMISSION_END flag is added to the mask. This requires the use of an event listener and event APIs.
3) Simply add a small delay after sdWrite(), for example 5mS. Quick and dirty.

Giovanni

Thanks for your reply.
I can't use the UART driver, while I'm out of DMA channels. Option 3 is dirty and unnecessarily locks the UART. Option 2 seems to be the most decent way to do it.
I've used the following

Code: Select all


        sdWrite
(&SD3, rx_buf, length);
        while ((chIOGetAndClearFlags(&SD3) & IO_TRANSMISSION_END) == 0)
        {
            chThdSleepMilliseconds(1);
        }
 

This doesn't work.

Code: Select all


        sdWrite
(&SD3, rx_buf, length);
        int flags = chIOGetAndClearFlags(&SD3);
        while ((chIOGetAndClearFlags(&SD3) & IO_TRANSMISSION_END) == 0)
        {
            chThdSleepMilliseconds(1);
        }
  

Above does work!

In the first example, the breakpoint on chThdSleepMilliseconds(1) never hits, on the second example, it does.

User avatar
Giovanni
Site Admin
Posts: 14458
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: sdWrite only two bytes

Postby Giovanni » Sun Mar 04, 2012 3:13 pm

You don't have to do polling, the serial driver has an event source that is signaled when some flag is added to the mask, you should wait for the event until the IO_TRANSMISSION_END flag appears.

Giovanni


Return to “General Support”

Who is online

Users browsing this forum: No registered users and 10 guests