chprintf buffer partially sent over rs232

ChibiOS public support forum for topics related to the STMicroelectronics STM32 family of micro-controllers.

Moderator: RoccoMarco

Post Reply
pyro
Posts: 3
Joined: Mon Feb 17, 2014 8:03 pm

chprintf buffer partially sent over rs232

Post by pyro »

I have a custom board that I am trying to use Serial Driver with UART1 on a STM32F205. I am having trouble sending data with the Serial Driver with chprintf function. I am sending two ascii strings. It seems some data always get stuck in the output queue and never finishes sending. If I manually do a sdPut() function, the next character in the outgoing buffer is sent. In the following example, the PC terminal will receive the following:

"STM32F205 initializing
This is a t"

It should have printed "This is a test". Changing a few lines will make it send more or less data. For example, removing the line feeds, makes it only print "STM32F205 initializin"

Code: Select all

int main(void)
{
    const SerialConfig sc1_config =
        {
            19200,      // baud rate
            0,          // CR1 register
            0,          // CR2 register
            0           // CR3 register
        };

    halInit();
    chSysInit();

    sdStart(&SD1, &sc1_config);
    palSetPadMode(GPIOA, 9,  PAL_MODE_ALTERNATE(7));
    palSetPadMode(GPIOA, 10, PAL_MODE_ALTERNATE(7));

    chprintf((BaseSequentialStream *)&SD1, "\nSTM32F205 initializing\n\r");
    chprintf((BaseSequentialStream *)&SD1, "This is a test\n\r");

    chThdSleepMilliseconds(500);


Note, I added a huge sleep of 500ms to make sure there is plenty of time for the entire output queue to be sent. After the sleep I start a blinking LED thread and then an infinite while(1){} loop. I tried looking at the "flags" from chEvtGetAndClearFlags() based on the FAQ, but it returns 0 (no flags set).

I tried a quick loop using chSequentialStreamPut which works with no problems:

Code: Select all

    int result;
    int i;
    const char tx_string1[] = "\nSTM32F205 initializing\n\r";
    const char tx_string2[] = "This is a test\n\r";
    i = 0;
    do  {
        result = chSequentialStreamPut((BaseSequentialStream *)&SD1, tx_string1[i]);
    } while (tx_string1[i++] != 0);

    i = 0;
    do {
        result = chSequentialStreamPut((BaseSequentialStream *)&SD1, tx_string2[i]);
    } while (tx_string2[i++] != 0);


Any suggestions? I don't have much knowledge about how each byte is sent out from the output queue to the UART.

Thanks in advance.
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: chprintf buffer partially sent over rs232

Post by Giovanni »

Hi,

I tested on an F4 Discovery and it works. I just had to use SD2 instead of SD1 but this should make no difference.

I used version 2.6.3, what version are you using? is your F205 clocked to 120MHz max?

Note, make your config structure "static const" or it is an automatic variable in RAM.

Giovanni
alexblack
Posts: 277
Joined: Mon Sep 24, 2012 3:52 pm
Been thanked: 33 times

Re: chprintf buffer partially sent over rs232

Post by alexblack »

Hi.
Sometimes I see the same things on STM32F1 with serial driver and chprintf. I have havy IRQ task in my program - but I did not find the reason of this and think that it's only my problem.
pyro
Posts: 3
Joined: Mon Feb 17, 2014 8:03 pm

Re: chprintf buffer partially sent over rs232

Post by pyro »

I did find one other post a while ago about a similiar problem, and it seemed the user changed the "method" he was using to send data. But the post was vague and inconclusive - and then they disappeared.

I am using 2.6.3. I have a 25Mhz external crystal and running at 100 Mhz

Code: Select all

#define STM32_PLLM_VALUE                    25
#define STM32_PLLN_VALUE                    200
#define STM32_PLLP_VALUE                    2
#define STM32_PLLQ_VALUE                    4


I'm reluctant to try it on my STM32VLDISCOVERY. I suspect it will work and won't tell me much. It's also a different device (I ordered the wrong discovery board, meant to buy the F4).

When I step through into the code including chSequentialStreamPut(), I don't see anything get sent at all. When I step over the chSequentialStreamPut() call in chvprintf(), I see something written every other time. So actually, the fact that the first string gets sent out properly, is only because the second string is adding additional characters into the buffer so the send can get called enough times. Hope that makes sense. I'm new to Eclipse and having a hell of a time learning to debug using it.

Can you explain how and where the output queue buffer is transmitted to the USART? Maybe then I can debug this better.
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: chprintf buffer partially sent over rs232

Post by Giovanni »

Hi,

The VL Discovery would use the very same driver so a test would be meaningful.

The queue is emptied in the low level driver: ./os/hal/platforms/STM32/USARTv1/serial_lld.c

Giovanni
pyro
Posts: 3
Joined: Mon Feb 17, 2014 8:03 pm

Re: chprintf buffer partially sent over rs232

Post by pyro »

Errrrr. Exactly as I expected - the problem disappeared. I sort of expected that since minor changes changed the behaviour slightly (printed more or less characters). I started to strip down the code to absolute bare minimum getting it ready to run on the Discovery board, when it started to work. Then I started adding everything back including the code I posted above which worked. I then even added the entire application which makes use of a lot of ADCs, and everything is working. I don't think I changed anything.

So, I'll wait for it to come back (I'm sure it will), and then start debugging it again. How frustrating! But thank you for your assistance Giovanni.
alexblack
Posts: 277
Joined: Mon Sep 24, 2012 3:52 pm
Been thanked: 33 times

Re: chprintf buffer partially sent over rs232

Post by alexblack »

Hi.
It seems I found the problem in serial_lld file. It is race condition in serve_interrupt routine.
I can't make simple test application to reproduce error but in my application I can show it.
In this test I periodically writing some text with chprintf to SD3 at high baud rate: 2Mbit (I allways using such speed to print debug info to PC termial, I used putty). After some portion of text I make pause 3 sec. Theoretically all data in SerialDriver output buffer must be sended to PC, but in some cases it is not. See the picture 'test_serial1.jpg'. In this picture You can see that SD3 out queue not empty but TXEIE bit of CR1 of USART3 is clear that means no Interrupt will be generated and no characters will be sended. I check the serve_interrupt routine and I think it has error. After my modifications the driver works without "frozen" buffers. Also You change USARTx.CR1 register without locks and use it "cached" copy in 'cr1' later but function 'Notify' also change this register in Thread context.

Code: Select all

..\STM23\USARTv1\serial_lld.c:
...
149 /**
150  * @brief   Common IRQ handler.
151  *
152  * @param[in] sdp       communication channel associated to the USART
153  */
154 static void serve_interrupt(SerialDriver *sdp) {
...
192  /* Physical transmission end.*/
193  if (sr & USART_SR_TC) {
194     chSysLockFromIsr();
195     chnAddFlagsI(sdp, CHN_TRANSMISSION_END);
196     //chSysUnlockFromIsr(); // removed
197     u->CR1 = cr1 & ~(/*USART_CR1_TXEIE |*/ USART_CR1_TCIE); // modification
198     u->SR = ~USART_SR_TC;
199     chSysUnlockFromIsr(); // added
200   }
Attachments
Output result
Output result
test_serial2.jpg (260.24 KiB) Viewed 4596 times
debug screen
debug screen
test_serial1.jpg (421.67 KiB) Viewed 4596 times
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: chprintf buffer partially sent over rs232

Post by Giovanni »

Hi,

Thanks, I see the race condition.

What happens is: the TC interrupt is triggered but the queue is filled again, the TC handler disables both TXE and TC so the queue remains filled and transmission is not restarted until the next write to the queue (which could not happen, this is your stalled condition I think).

I don't know if your fix is sufficient, probably, if the queue is again full, the TC event should not be generated at all because data is still being transmitted.

Could you try this?

Code: Select all

  /* Physical transmission end.*/
  if (sr & USART_SR_TC) {
    osalSysLockFromISR();
    if (oqIsEmptyI(&sdp->oqueue))
      chnAddFlagsI(sdp, CHN_TRANSMISSION_END);
    u->CR1 = cr1 & ~USART_CR1_TCIE;
    u->SR = ~USART_SR_TC;
    osalSysUnlockFromISR();
  }


Note, the above code is for 3.0, there are small changes for 2.6.x.

This is serious enough for a 2.6.5 release ahead of schedule.

Giovanni
alexblack
Posts: 277
Joined: Mon Sep 24, 2012 3:52 pm
Been thanked: 33 times

Re: chprintf buffer partially sent over rs232

Post by alexblack »

Yes, it works with this code.

Code: Select all

  /* Physical transmission end.*/
  if (sr & USART_SR_TC) {
    chSysLockFromIsr();
    if (chOQIsEmptyI(&sdp->oqueue))
      chnAddFlagsI(sdp, CHN_TRANSMISSION_END);
    u->CR1 = cr1 & ~USART_CR1_TCIE;
    u->SR = ~USART_SR_TC;
    chSysUnlockFromIsr();
  }

Looks like the problem is solved.
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: chprintf buffer partially sent over rs232

Post by Giovanni »

Great, thanks for finding.

Giovanni
Post Reply