Page 1 of 2

Reading serial port with event handler

Posted: Fri Sep 06, 2013 1:34 pm
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

Re: Reading serial port with event handler

Posted: Fri Sep 06, 2013 2:25 pm
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

Re: Reading serial port with event handler

Posted: Sat Sep 07, 2013 8:47 am
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.

Re: Reading serial port with event handler

Posted: Wed Oct 02, 2013 3:12 pm
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?

Re: Reading serial port with event handler

Posted: Wed Oct 02, 2013 3:33 pm
by Giovanni
It is a documentation error... fixing. I-class functions are always postfixed with a "I" anyway.

Giovanni

Re: Reading serial port with event handler

Posted: Wed May 28, 2014 3:28 am
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.

Re: Reading serial port with event handler

Posted: Wed May 28, 2014 8:53 am
by Giovanni
Lost how? event not triggered? event not served? data not received at all (not in the buffer)?

Giovanni

Re: Reading serial port with event handler

Posted: Thu May 29, 2014 12:57 am
by DeusExMachina
It was strange bug of debugger in Eclipse Galileo, it was solved by upgrading to Kepler (code remains the same). Sorry

Re: Reading serial port with event handler

Posted: Tue Jul 29, 2014 10:01 pm
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;
}

Re: Reading serial port with event handler

Posted: Tue Jul 29, 2014 10:17 pm
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?