Serial Driver Event Handling Example

This forum is dedicated to feedback, discussions about ongoing or future developments, ideas and suggestions regarding the ChibiOS projects are welcome. This forum is NOT for support.
genosensor
Posts: 65
Joined: Thu Oct 03, 2013 1:06 am
Has thanked: 1 time
Been thanked: 1 time

Serial Driver Event Handling Example

Post by genosensor »

It was surprisingly difficult (for me at least :-) to work out how to properly service serial driver events.
The main source of confusion was that event mask bits are entirely separate from event flags from the LLD.
I'm posting this to get feed back on it and to provide an example that may save others hours of head scratching.
This seems to work, but it was more effort than I'd expected.
Is there an easier way?

(note that this includes logic to suppress framing errors when they are immediately followed by line BREAK)

Code: Select all

/*
 * This thread processes serial cmd port input
 */
static ThreadMain(cmdMain, arg)
{
  (void) arg;
  enum {
    eid = EVENT_MASK(0),
    ignore = CHN_OUTPUT_EMPTY | CHN_TRANSMISSION_END,
    corruptInput =
        SD_OVERRUN_ERROR | SD_FRAMING_ERROR | SD_PARITY_ERROR | SD_NOISE_ERROR
  };
  bool pendingFramingErr = false;
  EventListener cmdListener;
  flagsmask_t pending;
  chEvtRegisterMaskWithFlags(&cmdSerial.event, &cmdListener, eid, ~ignore);
  for(;;) {
    chEvtWaitAny(eid);
    pending = chEvtGetAndClearFlags(&cmdListener) & ~ignore;
    do {
      flagsmask_t reason = leastSetBit(pending);
      pending &= ~reason;
      switch (reason) {
        case CHN_INPUT_AVAILABLE:
          if (pending & corruptInput) {
            if (pendingFramingErr) {
              pendingFramingErr=false;
              debugPuts("Serial FramingError");
            }
            goto flush;
          }
          debugPuts("Serial Input Available");
          echoInput(&cmdSerial);
          break;

        case SD_OVERRUN_ERROR:
          debugPuts("Serial Overrun!");
          break;

        case SD_BREAK_DETECTED:
          pendingFramingErr=false;
          debugPuts("Serial BREAK!");
flush:
          chSysLock();
          chIQResetI(&cmdSerial.iqueue);
          chSysUnlock();
          break;

        case SD_FRAMING_ERROR:
          //mark "pending" because framing err often preceeds line BREAK
          pendingFramingErr = true;
          goto flush;
        case SD_PARITY_ERROR:
          debugPuts("Serial Parity Error!");
          goto flush;
        case SD_NOISE_ERROR:
          debugPuts("Serial Line Noise!");
          goto flush;

        case CHN_OUTPUT_EMPTY:
          debugPuts("Serial Output Empty");
          break;
        case CHN_TRANSMISSION_END:
          debugPuts("Serial Transmission End");
          break;

        case 0:  //sometimes we get woken for no good reason
          break;
        default:  //unhandled event
          debugPrint("Unknown Serial Event (flags=0x%x)", reason);
          msleep(50);
      }
    } while (pending);
  }
}
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Serial Driver Event Handling Example

Post by Giovanni »

Thanks, events have always be confusing despite my efforts to explain them :)

Giovanni
king2
Posts: 98
Joined: Mon May 28, 2012 1:57 pm
Been thanked: 2 times

Re: Serial Driver Event Handling Example

Post by king2 »

I have tried simplified version of this, I need just to turn off DE line after sending was finished.
I do at start of thread:

Code: Select all

chEvtRegisterMaskWithFlags(&SD4.event, &cmdListener, EVENT_MASK(0), CHN_TRANSMISSION_END);

then sending:

Code: Select all

// set DE and send packet
palSetPort(GPIOF, CONSOLE_DE);
sdWrite(& SD4, (uint8_t const *) shproto_console_tx.data, shproto_send_packet(&shproto_console_tx));

// wait it to actually send data
chEvtWaitAny(EVENT_MASK(0));
eventflags_t pending = chEvtGetAndClearFlags(&cmdListener);
if (pending & CHN_TRANSMISSION_END) palClearPort(GPIOF, CONSOLE_DE);


It works for a some time, but after a time it leaves driver line high.
I tried to make breakpoint, 'pending' variable is or 0 or 8 at breakpoint ('nothing' or CHN_OUTPUT_EMPTY).
'Live watch' tells me that it rises flag with mask '16' (CHN_TRANSMISSION_END), but not when breakpoint is fired.

Is this error or I misunderstood something?
What should I do to get it working?
Maybe there is SIMPLIER method to just turn off DE line at the end of transmission (than make entire thread to wait)?

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

Re: Serial Driver Event Handling Example

Post by Giovanni »

Hi,

One possible cause is that you do other writes before the event is fired, the event is fired when the transmitter becomes idle.

Giovanni
king2
Posts: 98
Joined: Mon May 28, 2012 1:57 pm
Been thanked: 2 times

Re: Serial Driver Event Handling Example

Post by king2 »

Only one point of write to SD4 exists, and it is shown in my sources.
As I understand, it should pass chEvtWaitAny() line ONLY if CHN_TRANSMISSION_END was raised, so, next write can be done only after previous was already fully completed.

If I do chEvtRegisterMaskWithFlags(&SD4.event, &cmdListener, EVENT_MASK(0), CHN_TRANSMISSION_END), it will send events only from SD4 and only with CHN_TRANSMISSION_END set, right?

But I'm getting CHN_OUTPUT_EMPTY for unknown reason..
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Serial Driver Event Handling Example

Post by Giovanni »

After writing, you get first CHN_OUTPUT_EMPTY when the software queue is empty then CHN_TRANSMISSION_END when the UART is done sending the last frame.

Giovanni
steved
Posts: 834
Joined: Fri Nov 09, 2012 2:22 pm
Has thanked: 12 times
Been thanked: 138 times

Re: Serial Driver Event Handling Example

Post by steved »

You could try this modified serial driver - it can generate callbacks rather than events. Sounds more useful in your specific situation (and that's how I use it)
king2
Posts: 98
Joined: Mon May 28, 2012 1:57 pm
Been thanked: 2 times

Re: Serial Driver Event Handling Example

Post by king2 »

Giovanni wrote:After writing, you get first CHN_OUTPUT_EMPTY when the software queue is empty then CHN_TRANSMISSION_END when the UART is done sending the last frame.

Wow, I will get CHN_OUTPUT_EMPTY even if I using CHN_TRANSMISSION_END as 4th argument of chEvtRegisterMaskWithFlags()?
User avatar
Giovanni
Site Admin
Posts: 14892
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Serial Driver Event Handling Example

Post by Giovanni »

You should be able to mask any flag you don't need.

Note, that masked flags are still added to the listener but do not wake it up.

Giovanni
king2
Posts: 98
Joined: Mon May 28, 2012 1:57 pm
Been thanked: 2 times

Re: Serial Driver Event Handling Example

Post by king2 »

Sorry, but I still cannot understand.

We have a line at start of the thread:

Code: Select all

chEvtRegisterMaskWithFlags(&SD4.event, &cmdListener, EVENT_MASK(0), CHN_TRANSMISSION_END);

Will it raise event on CHN_TRANSMISSION_END only, or it will raise events on all flags except CHN_TRANSMISSION_END?
Post Reply