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.
genosensor
Posts: 65
Joined: Thu Oct 03, 2013 1:06 am
Location: Santa Cruz, California
Has thanked: 1 time
Been thanked: 1 time

Serial Driver Event Handling Example

Postby genosensor » Fri Feb 24, 2017 8:57 am

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);
  }
}
- brent

User avatar
Giovanni
Site Admin
Posts: 11312
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 412 times
Been thanked: 342 times
Contact:

Re: Serial Driver Event Handling Example

Postby Giovanni » Fri Feb 24, 2017 8:59 am

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

Postby king2 » Thu Jan 11, 2018 11:28 am

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: 11312
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 412 times
Been thanked: 342 times
Contact:

Re: Serial Driver Event Handling Example

Postby Giovanni » Thu Jan 11, 2018 11:55 am

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

Postby king2 » Thu Jan 11, 2018 12:28 pm

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: 11312
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 412 times
Been thanked: 342 times
Contact:

Re: Serial Driver Event Handling Example

Postby Giovanni » Thu Jan 11, 2018 12:43 pm

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: 550
Joined: Fri Nov 09, 2012 2:22 pm
Has thanked: 4 times
Been thanked: 58 times

Re: Serial Driver Event Handling Example

Postby steved » Thu Jan 11, 2018 2:17 pm

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

Postby king2 » Thu Jan 11, 2018 4:21 pm

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: 11312
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 412 times
Been thanked: 342 times
Contact:

Re: Serial Driver Event Handling Example

Postby Giovanni » Thu Jan 11, 2018 4:49 pm

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

Postby king2 » Thu Jan 11, 2018 5:45 pm

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?


Return to “Development and Feedback”

Who is online

Users browsing this forum: No registered users and 1 guest