Reading serial port with event handler

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

Moderator: RoccoMarco

EsEblog
Posts: 2
Joined: Thu Sep 05, 2013 7:15 am

Reading serial port with event handler

Post by EsEblog »

Hello!

I am trying to read NMEA data from a GPS using serial driver 2 on the STM32F4 Discovery.
My event listener thread looks like this:

Code: Select all

tatic msg_t Thread6(void *arg)
{
   (void)arg;
   chRegSetThreadName("GPSdata");

   static char charbuf; // character buffer

   // Activates the Serial driver 2, PA2(TX) and PA3(RX) are routed to USART2, 9600 8N1.
   palSetPadMode(GPIOA, 2, PAL_MODE_ALTERNATE(7));
   palSetPadMode(GPIOA, 3, PAL_MODE_ALTERNATE(7));
   sdStart(&SD2, NULL);
   
   EventListener elGPSdata;
   flagsmask_t flags;
   chEvtRegisterMask((EventSource *)chnGetEventSource(&SD2), &elGPSdata, EVENT_MASK(1));
   
   while (TRUE)
   {
      
      chEvtWaitOneTimeout(EVENT_MASK(1), MS2ST(10));
      chSysLock();
      flags = chEvtGetAndClearFlags(&elGPSdata);
      chSysUnlock();
            
      if (flags & CHN_INPUT_AVAILABLE)
      {
         while ( charbuf != Q_TIMEOUT )
         {
            charbuf = chnGetTimeout(&SD2, TIME_IMMEDIATE);
            chprintf((BaseSequentialStream*)&SDU1, "%c\n", charbuf);
         }
      }
   }
   
   return 0;
}


I have increased the serial buffer size to 560 bytes (longest sentence is 75 characters, 7 sentences) but when printing the flags variable I still get 128 in the flags, which I believe indicates an overrun error.

In another Thread Giovanni suggested reading the data like this:
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.


Where will CHN_INPUT_AVAILABLE be stored in? Still in the flags variable?
Additionally, which events does the event listener react to? Only when there is input data or also on errors?

Thank you!
Elia
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Reading serial port with event handler

Post by Giovanni »

Hi,

I would change that while(){} in a do{}while() because charbuf would keep the previous Q_TIMEOUT and prevent the read in the next cycle. Also not that the returned value is a msg_t not a char even if in this specific case it would not be a problem because you handle only ASCII data.

Code: Select all

   while (TRUE)
   {
     
      chEvtWaitOneTimeout(EVENT_MASK(1), MS2ST(10));
      chSysLock();
      flags = chEvtGetAndClearFlags(&elGPSdata);
      chSysUnlock();
           
      if (flags & CHN_INPUT_AVAILABLE)
      {
         msg_t charbuf;
         do
         {
            charbuf = chnGetTimeout(&SD2, TIME_IMMEDIATE);
            chprintf((BaseSequentialStream*)&SDU1, "%c\n", charbuf);
         }
         while (charbuf != Q_TIMEOUT);
      }
   }


The event is broadcasted for all the defined flags, so both data events and error events.

Giovanni
EsEblog
Posts: 2
Joined: Thu Sep 05, 2013 7:15 am

Re: Reading serial port with event handler

Post by EsEblog »

Thank you very much Giovanni!

This has solved the problem, the code now looks like this:

Code: Select all

static msg_t Thread6(void *arg)
{
   (void)arg;
   chRegSetThreadName("GPSdata");
   
   // Activates the Serial driver 2, PA2(TX) and PA3(RX) are routed to USART2, 9600 8N1.
   palSetPadMode(GPIOA, 2, PAL_MODE_ALTERNATE(7));
   palSetPadMode(GPIOA, 3, PAL_MODE_ALTERNATE(7));
   sdStart(&SD2, NULL);
   
   EventListener elGPSdata;
   flagsmask_t flags;
   chEvtRegisterMask((EventSource *)chnGetEventSource(&SD2), &elGPSdata, EVENT_MASK(1));
   
   while (TRUE)
   {
      chEvtWaitOneTimeout(EVENT_MASK(1), MS2ST(10));
      chSysLock();
      flags = chEvtGetAndClearFlags(&elGPSdata);
      chSysUnlock();
      
      if (flags & CHN_INPUT_AVAILABLE)
      {
         msg_t charbuf;
         do
         {
            charbuf = chnGetTimeout(&SD2, TIME_IMMEDIATE);
            if ( charbuf != Q_TIMEOUT )
            {
               chprintf((BaseSequentialStream*)&SDU1, "%c", (char)charbuf);
            }
         }
         while (charbuf != Q_TIMEOUT);
      }
   }
   
   return 0;
}


The if-statement is added so that the Q_TIMEOUT flag is not printed to the debug output.
trepidacious
Posts: 58
Joined: Mon Jan 21, 2013 3:36 pm
Been thanked: 1 time

Re: Reading serial port with event handler

Post by trepidacious »

I get a port_halt on the chSysLock() call in your code, but works fine without it. The docs for chEvtGetAndClearFlags() in 2.6.1 say it is an I-Class API, but it seems like chEvtGetAndClearFlagsI() is the I-Class version?
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Reading serial port with event handler

Post by Giovanni »

It is a documentation error... fixing. I-class functions are always postfixed with a "I" anyway.

Giovanni
User avatar
DeusExMachina
Posts: 223
Joined: Tue Apr 03, 2012 5:08 am
Has thanked: 3 times
Been thanked: 3 times

Re: Reading serial port with event handler

Post by DeusExMachina »

I use this code but except debug check lock I have a problem with receiving 10th byte in a frame. 9 bytes are received normally, but after 9 byte all data is lost.
Increasing buffer size in halconf.h did not solve the problem.
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Reading serial port with event handler

Post by Giovanni »

Lost how? event not triggered? event not served? data not received at all (not in the buffer)?

Giovanni
User avatar
DeusExMachina
Posts: 223
Joined: Tue Apr 03, 2012 5:08 am
Has thanked: 3 times
Been thanked: 3 times

Re: Reading serial port with event handler

Post by DeusExMachina »

It was strange bug of debugger in Eclipse Galileo, it was solved by upgrading to Kepler (code remains the same). Sorry
hkerner
Posts: 24
Joined: Tue Jul 22, 2014 12:05 am

Re: Reading serial port with event handler

Post by hkerner »

I am also trying to implement an event listener here in much the same way. I do it pretty much exactly how you do here but it crashes somewhere between chSysLock() and chSysUnlock()... I don't really see why since I'm doing it the same way as you. Any help appreciated.

Code: Select all

static WORKING_AREA(wa_comms_thread, 3000);
static msg_t comms_thread(void *arg)
{
  (void)arg;
  chRegSetThreadName("comms thread");

  EventListener elUSART3Data;
  flagsmask_t flags;
  chEvtRegisterMask((EventSource *)chnGetEventSource(&SD3), &elUSART3Data, EVENT_MASK(1)); // why 1?

  while (TRUE) {

    chEvtWaitOneTimeout(EVENT_MASK(1), MS2ST(10));
    chSysLock();
    flags = chEvtGetAndClearFlags(&elUSART3Data);
    chSysUnlock();
    palTogglePad(GPIOA, 1);
    if (flags & CHN_INPUT_AVAILABLE) {
      // do something
    }
  }
  return 0;
}
hkerner
Posts: 24
Joined: Tue Jul 22, 2014 12:05 am

Re: Reading serial port with event handler

Post by hkerner »

Actually I just read your comment here viewtopic.php?f=4&t=1964 where you pointed out not needing the lock/unlock there because it's not an I-class function so it doesn't crash there, but the if (flags & CHN_INPUT_AVAILABLE) still never evaluates to true. Am I creating these flags or registering them wrong?
Post Reply