UART latency

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

Moderator: RoccoMarco

Post Reply
goeck
Posts: 92
Joined: Mon Feb 11, 2013 12:00 pm

UART latency

Post by goeck »

Hey everyone,

using a BLE112 bluetooth low energy device at my STM32F0 Discovery UART@115000baud I have some trouble somtimes to capture the events if they come quickly each one after another.
I appended a saleae screen where you see a command sent from STM32 at first and the ack from the BLE device. Just 4ms after this the BLE event throws an event. Sometimes my UART handler gets that and sometimes not...
Here is my UART listening thread

Code: Select all

static msg_t Thread4(void *arg)
{
   (void)arg;
   chRegSetThreadName("BLEData");

   static unsigned char data[256];
   static struct ble_header hdr;
   static uint32_t payload_length = 0, cnt_received = 0;

   EventListener elBLEData;
   flagsmask_t flags;
   chEvtRegisterMask((EventSource *)chnGetEventSource(&SD1), &elBLEData, EVENT_MASK(1));

   while (TRUE)
   {
      chEvtWaitAllTimeout(EVENT_MASK(1), TIME_IMMEDIATE);
      chSysLock();
      flags = chEvtGetAndClearFlags(&elBLEData);
      chSysUnlock();

      if(flags & SD_PARITY_ERROR){                                          // Parity error happened.
         gwinPrintf(GW1, "[UART] parity error\n");
         gdispFlush();
      }
      if(flags & SD_FRAMING_ERROR){                                       // Framing error happened.
         gwinPrintf(GW1, "[UART] framing error\n");
         gdispFlush();
      }
      if(flags & SD_OVERRUN_ERROR){                                       // Overflow happened.
         gwinPrintf(GW1, "[UART] Overflow\n");
         gdispFlush();
      }
      if(flags & SD_NOISE_ERROR){                                          // Noise on the line.
         gwinPrintf(GW1, "[UART] Noisy line\n");
         gdispFlush();
      }
      if(flags & SD_BREAK_DETECTED){                                       // Break detected.
         gwinPrintf(GW1, "[UART] Break!\n");
         gdispFlush();
      }
      if(flags & CHN_INPUT_AVAILABLE)
      {
         ledFlash();                                                   //Indicate: something has been received

         msg_t charbuf;
         do{                                                         //Read all there is into buffer
            charbuf = chnGetTimeout(&SD1, TIME_IMMEDIATE);                                                   
            if(charbuf != Q_TIMEOUT){
               if(cnt_received < sizeof(hdr)){
                  if(cnt_received == 0)
                     hdr.type_hilen = (uint8_t)charbuf;
                  if(cnt_received == 1)
                     hdr.lolen = (uint8_t)charbuf;
                  if(cnt_received == 2)
                     hdr.cls= (uint8_t)charbuf;
                  if(cnt_received == 3)
                     hdr.command= (uint8_t)charbuf;
               }
               else
                  data[cnt_received - sizeof(hdr)] = (uint8_t)charbuf;

               cnt_received++;
            }
         }
         while(charbuf != Q_TIMEOUT);

         if(cnt_received >= sizeof(hdr)){                                    //Do we have at least the header bytes?
            payload_length = ((uint32_t)(hdr.type_hilen & 0x07))<<8 | (uint32_t)hdr.lolen;
            if(cnt_received >= sizeof(hdr)+payload_length){                     //Have we gathered all payload bytes?
               cnt_received = 0;                                       //Reset receiver counter
               const struct ble_msg *msg = ble_get_msg_hdr(hdr);                  //Process message header
               if(!msg){
                  gwinPrintf(GW1, "[BLE] unknown message\n");
                  gdispFlush();
               }
               else
                  msg->handler(data);                                                            //Here message handler gets called
            }
         }
      }
   }
}

Also one can see what else is running

Code: Select all

int main(void) {
   halInit();
   chSysInit();

   tpBlinkBlue    = chThdCreateStatic(waThread_2, sizeof(waThread_2), NORMALPRIO, Thread2, NULL);   //Blinker thread from STM32 demo code
   tpButton      = chThdCreateStatic(waThread_3, sizeof(waThread_3), NORMALPRIO, Thread3, NULL);   //This sleeping reacts on button press events. IRQ wakes up this thread
   tpBLEData      = chThdCreateStatic(waThread_4, sizeof(waThread_4), NORMALPRIO, Thread4, NULL);   //This is the UART listener

   initDisplay();
   initUART();
   initBLE();
   extStart(&EXTD1, &extcfg);                    // Activates the EXT driver 1.

   while(TRUE){
   }
}

What else you can see is, that I have a GPIO to serve a indicator function. My UART handler also pulls the line low, when it gets called.
First off I had a 10ms timeout in the charbuf = chnGetTimeout(&SD1, TIME_IMMEDIATE); routine which led to never see events thrown on the line within 10ms after an earlier UART RX activity. I was able to solve that but now I don't have a reliable and quick UART reading.

Did I overlook something crucial?
Thanks for the help, I appreciate it!

Greetings
Stefan
Attachments
Saleae screenshot of UART events (BLE TX up, STM32 RX low)
Saleae screenshot of UART events (BLE TX up, STM32 RX low)
screenshot.png (156.86 KiB) Viewed 9574 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: UART latency

Post by Giovanni »

Hi, what version are you using? I fixed a serial-related bug in the repository. Try compiling using -O1 instead of -O2 and see if it makes any difference.

Giovanni
goeck
Posts: 92
Joined: Mon Feb 11, 2013 12:00 pm

Re: UART latency

Post by goeck »

Hey Giovanni,

I am on GIT master branch, not latest actually, but like some days old. Will sync with repository tomorrow and do the optimisation tricks.
Thanks, Will keep you posted.

Stefan
goeck
Posts: 92
Joined: Mon Feb 11, 2013 12:00 pm

Re: UART latency

Post by goeck »

Whooops... I am on SVN trunck, that's what I was gonna say, actually at latest revision #6512.
I am compiling without optimisation and if I use antoher than -O0 I get nothing printed on the display (hooked up over SPI) anymore.

OK, now I am using -O1 and no display to get rid of any interference with processing regarding display output. No goodness.. :-(

Any suggestions?
Stefan
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: UART latency

Post by Giovanni »

Hi,

I see several problems in that code:

1) You never wait, you always do TIME_IMMEDIATE.
2) You are decoding a packet of some kind, but there is no synchronization mechanism, if your state machine is not synchronized with a packet start it remains out of synch.
3) chEvtGetAndClearFlags() must not go between lock/unlock it is not an i-class function.

What I can sugges:
1) Design your state machine considering timeouts, if the state machine timeouts then you return in the initial state and wait for the first byte again.
2) Waiting for events should not be done using TIME_IMMEDIATE but using the mentioned timeout.
3) Enable the state checker and the other debugging options.

Giovanni
goeck
Posts: 92
Joined: Mon Feb 11, 2013 12:00 pm

Re: UART latency

Post by goeck »

Hey Giovanni,
thanks for the reply. Again we're talking about the implementation of the UART listener. I see several threads in this forum dealing with that .
Regarding your hints...
Please have a look at extract from that old post:
The correct way of handling a serial event is:
1) Wait for an event.
2) Call chEvtGetAndClearFlags().
3) If some error flag is set then handle it (see serial.h).
4) If CHN_INPUT_AVAILABLE is specified then read all chars from the queue using TIME_IMMEDIATE until the queue is empty.
5) Repeat from 1.

I thought I just implemented this. I am a little confused right now. So I put back in a timeout of 1ms.
So two questions rise:
Is this approach I quoted the way to go?
Where in my implementation is one of those 5 points not well implemented?

It seems strange to me, just to clearify that: I can read and decode messages from BLE112 very fine, except when they come in very quickly (with just some [<10] ms time in between). But I know from the rising and falling edges on the indicator pin the decoding, parsing and handling of the message itself doesn't take very long (have a look at the screenshot, rising is done at very first and lowering edge is done as very last in handling sequence).
Doesn't this mean that listening works fine, but after handling is done the listener doesn't return to listening state quick enough, or something similar?

Thanks for the help, Giovanni!

EDIT: --------------
So I found, I am not waiting for the event, I always return immediately, which is stupi. I got the point and changed that. First off, I thought you ment the waiting in the serial driver reading routine. Nothing changed that much though.
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: UART latency

Post by Giovanni »

Yes, it is the way to go, but you have to consider the state machine you want to implement while receiving data.
Note that you have to WAIT for event, you can't use TIME_IMMEDIATE there or the thread will loop without interruptions and consume all the CPU.

Giovanni
goeck
Posts: 92
Joined: Mon Feb 11, 2013 12:00 pm

Re: UART latency

Post by goeck »

Hey,

I added a

Code: Select all

palTogglePad(INDICATE_PORT, INDICATE_PIN);
(called ENABLE in Saleae screenshot) command everytime a character gets read from the queue in my UART listener to find out more about the timings. What I wanted to see was an edge on the scope directly right after one byte has been seen on the UART Rx line. What I find most of the time is that several Milliseconds after the last byte has been seen on the UART Rx line reading from the queue seems to take place. I added a screenshot.
Why is this behaviour?
Attachments
Saleae screenshot
Saleae screenshot
screenshot.png (88.55 KiB) Viewed 9484 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: UART latency

Post by Giovanni »

I noticed you have an empty loop in your main(), that loop uses up CPU cycles because main() is a thread with priority NORMALPRIO. Your thread is also at NORMALPRIO so it has to wait for the main() thread to finish its time slot (they are working in round-robin).

Put a chThdSleepMilliseconds(500) in your loop, this should be sufficient but normally is a good idea to have all threads at different priority levels.

Giovanni
goeck
Posts: 92
Joined: Mon Feb 11, 2013 12:00 pm

Re: UART latency

Post by goeck »

Giovanni,

seems like you've saved my day with this! :D
Actually such a stupid mistake, I must have removed this line by accident... :oops:
I now have a behaviour like expected. See screenshot again.
Well, I once had the priotity for the UART listener thread set to HIGHPRIO but this also didn't end up in a proper result.
Anyways, I will see how the longterm behaviour will be.

Thanks for all the support you're supplying.

Cheers
Stefan
Attachments
Screenshot
Screenshot
screenshot.png (94.47 KiB) Viewed 9482 times
Post Reply