Page 1 of 2

Serial Driver Event Handling Example

Posted: Fri Feb 24, 2017 8:57 am
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);
  }
}

Re: Serial Driver Event Handling Example

Posted: Fri Feb 24, 2017 8:59 am
by Giovanni
Thanks, events have always be confusing despite my efforts to explain them :)

Giovanni

Re: Serial Driver Event Handling Example

Posted: Thu Jan 11, 2018 11:28 am
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!

Re: Serial Driver Event Handling Example

Posted: Thu Jan 11, 2018 11:55 am
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

Re: Serial Driver Event Handling Example

Posted: Thu Jan 11, 2018 12:28 pm
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..

Re: Serial Driver Event Handling Example

Posted: Thu Jan 11, 2018 12:43 pm
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

Re: Serial Driver Event Handling Example

Posted: Thu Jan 11, 2018 2:17 pm
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)

Re: Serial Driver Event Handling Example

Posted: Thu Jan 11, 2018 4:21 pm
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()?

Re: Serial Driver Event Handling Example

Posted: Thu Jan 11, 2018 4:49 pm
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

Re: Serial Driver Event Handling Example

Posted: Thu Jan 11, 2018 5:45 pm
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?