Events for serial driver transmission completion

Discussions and support about ChibiOS/RT, the free embedded RTOS.
User avatar
FXCoder
Posts: 403
Joined: Sun Jun 12, 2016 4:10 am
Has thanked: 188 times
Been thanked: 134 times

Re: Events for serial driver transmission completion

Post by FXCoder »

Hi Raul.
I was just putting your code into a test project to dump event sequences out and got a compile error.
Maybe you just retyped the code into your post rather than copy and paste?
If you did copy/paste then do you have two buffers defined?
One named buff and the other buf?

Code: Select all

sdWrite (&SD2, buff, sizeof (buf));


Bob
User avatar
FXCoder
Posts: 403
Joined: Sun Jun 12, 2016 4:10 am
Has thanked: 188 times
Been thanked: 134 times

Re: Events for serial driver transmission completion

Post by FXCoder »

Hi Raul,
Using an STM32F413 NUCLEO board I couldn't re-create your issue.
In my case I've allowed all flags on the USART side to wake up the user thread.


And here is the test thread code...

Code: Select all

static THD_WORKING_AREA(waThread2, 128);
static THD_FUNCTION(Thread2, arg) {

  (void)arg;
  event_listener_t listener;
  uint8_t buff[] = {0x00, 0x01, 0x02, 0x03};
  chEvtRegisterMaskWithFlags (chnGetEventSource (&SD2),
                                           &listener, EVENT_MASK (0),
                                           ALL_EVENTS);
  char *flag_text;

  while (true) {
    sdWrite (&SD2, buff, sizeof (buff));
    chEvtWaitOne (EVENT_MASK (0));
    eventflags_t source_flags = chEvtGetAndClearFlags (&listener);
    eventmask_t source_mask = EVENT_MASK(0);
    chprintf((BaseSequentialStream *)&SD3, "USART flag test\r\n");
    while (source_flags) {
      switch (source_flags & source_mask) {
        case 0:
          flag_text = NULL;
          break;
        case CHN_CONNECTED:
          flag_text = "Connected";
          break;
        case CHN_DISCONNECTED:
          flag_text = "Disconnected";
          break;
        case CHN_INPUT_AVAILABLE:
          flag_text = "Input available";
          break;
        case CHN_OUTPUT_EMPTY:
          flag_text = "Output empty";
          break;
        case CHN_TRANSMISSION_END:
          flag_text = "Transmission end";
          break;
        default:
          flag_text = "Unknown";
          break;
      } /* End switch. */
      if (flag_text)
        chprintf((BaseSequentialStream *)&SD3, "Flag: %x (%s)\r\n", \
                 source_mask, flag_text);
      source_flags &= ~source_mask;
      source_mask <<= 1U;
    } /* End while. */
    chThdSleepSeconds(2);
  }
}



This is what I get repeating each 2 seconds on SD3...

Code: Select all

USART flag test
Flag: 8 (Output empty)
Flag: 10 (Transmission end)


I'm using default SD baud rate, etc.

Will be interested to know which uC you are using and if the test code produces any more clues.

Bob
Raul
Posts: 43
Joined: Thu Aug 13, 2015 5:15 pm
Has thanked: 3 times
Been thanked: 1 time

Re: Events for serial driver transmission completion

Post by Raul »

Hi all,

Thanks for looking into this so attentively and apologies for the typographic errors in the original snippet (Any way to amend the original post?), it was just a simplified non-tested fragment I should have mentioned that.

Below there is a minimalist main.c tested against a STM32F407 on a discovery board. It is working perfectly fine, as the capture depicts. So my problem must be somewhere else. I need to go back to my actual set up and see what is actually happening. I'll let you know once I find out.

Code: Select all

#include "ch.h"
#include "hal.h"


static THD_WORKING_AREA(waThread1, 128);
static THD_FUNCTION(Thread1, arg) {

   (void)arg;
   event_listener_t txComplete;
   uint8_t const data[] = {0x00, 0x01, 0x02, 0x03};

   chEvtRegisterMaskWithFlags (chnGetEventSource (&SD2),
                &txComplete, EVENT_MASK (0),
                CHN_TRANSMISSION_END);

  while (true) {

   sdWrite (&SD2, data, sizeof (data));

   chEvtWaitOne (EVENT_MASK (0));

   palSetPad (GPIOD, GPIOD_LED3);

   eventflags_t f = chEvtGetAndClearFlags (&txComplete);

   if (f & CHN_TRANSMISSION_END)
   {
      chThdSleepMilliseconds(250);
      palClearPad(GPIOD, GPIOD_LED3);
      chThdSleepMilliseconds(250);
   }
  }
}

/*
 * Application entry point.
 */
int main(void) {

  halInit();
  chSysInit();

  /*
   * Activates the serial driver 2 using the driver default configuration.
   * PA2(TX) and PA3(RX) are routed to USART2.
   */
  sdStart(&SD2, NULL);
  palSetPadMode(GPIOA, 2, PAL_MODE_ALTERNATE(7));
  palSetPadMode(GPIOA, 3, PAL_MODE_ALTERNATE(7));

  /*
   * Creates the example thread.
   */
  chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);

  while (true) {
    chThdSleep(TIME_INFINITE);
  }
}


capture.png
capture.png (8.51 KiB) Viewed 7150 times
Raul
Posts: 43
Joined: Thu Aug 13, 2015 5:15 pm
Has thanked: 3 times
Been thanked: 1 time

Re: Events for serial driver transmission completion

Post by Raul »

Hi,

So, going back to my problematic case, chEvtWaitOne returns immediately because when it gets called the pending flag for the events seems to be already set. I set a breakpoint at chevents.c line 390 and it's never hit. Although when it returns ctp->p_epending has the event reset, as it should.

Code: Select all

 eventmask_t chEvtWaitOne(eventmask_t events) {
384│   thread_t *ctp = currp;
385│   eventmask_t m;
386│
387│   chSysLock();
388│   m = ctp->p_epending & events;
389│   if (m == (eventmask_t)0) {
390│     ctp->p_u.ewmask = events;
391│     chSchGoSleepS(CH_STATE_WTOREVT);
392│     m = ctp->p_epending & events;
393│   }
394|   m ^= m & (m - (eventmask_t)1);
395│   ctp->p_epending &= ~m;
396│   chSysUnlock();
397│
398│   return m;
399│ }


After that finding I decided to perform sdWrites only if there are no pending event flags, and if there were any I would clear them explicitly and return. See below.
Every single time trySend is called it evaluates ctp->p_epending with a set event mask, at this point I get very confused, note that sd write operations are not happening in this case. A silly question, that could explain it all, if data is received by the same serial drive. Would it also trigger an event 0 even though only the CHN_TRANSMISSION_END event flag is registered?

Code: Select all

bool trySend (uint8_t *data, uint8_t n)
{
             thread_t *ctp = currp;
             chSysLock ();
             eventmask_t s = ctp->p_epending;
             chSysUnlock ();
             
              if (s == EVENT_MASK (0))
              {
                      chSysLock ();
                      ctp->p_epending = 0;
                      chSysUnlock ();
                     return false;
              }
   
             sdWrite (&SD2, data, n);
             chEvtWaitOne (EVENT_MASK (0));
   
             eventflags_t f = chEvtGetAndClearFlags (&txComplete);
 
             return f & CHN_TRANSMISSION_END;
}


Update, If I clear the flags as well as the pending events it only meets the first if condition once. The system works as expected from that point onwards. So, I would answer my previous question with a no, as subsequent events and activity don't trigger event 0.
Now here is THE question, why do you guys think it is doing it initially? At that point some data would have been received but nothing transmitted.

Code: Select all

bool trySend (uint8_t *data, uint8_t n)
{
             thread_t *ctp = currp;
             chSysLock ();
             eventmask_t s = ctp->p_epending;
             chSysUnlock ();
             
              // It gets here only once, the first time it gets called, and after the system becomes "synced up".     
              if (s == EVENT_MASK (0))
              {
                      chSysLock ();
                      txComplete.el_flags = 0;
                      ctp->p_epending = 0;
                      chSysUnlock ();
                     return false;
              }
   
             sdWrite (&SD2, data, n);
             chEvtWaitOne (EVENT_MASK (0));
   
             eventflags_t f = chEvtGetAndClearFlags (&txComplete);
 
             return f & CHN_TRANSMISSION_END;
}


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

Re: Events for serial driver transmission completion

Post by Giovanni »

Hi,

What version of ChibiOS are you using? that "p_epending" suggests it is very old.

Giovanni
Raul
Posts: 43
Joined: Thu Aug 13, 2015 5:15 pm
Has thanked: 3 times
Been thanked: 1 time

Re: Events for serial driver transmission completion

Post by Raul »

Hi Giovanni,

It is release 16.1.0, about 9/8 months old, would you suggest running it against Adagio?

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

Re: Events for serial driver transmission completion

Post by Giovanni »

17.6.0 or 16.1.8, lots of bugs get fixed with time.

Giovanni
Raul
Posts: 43
Joined: Thu Aug 13, 2015 5:15 pm
Has thanked: 3 times
Been thanked: 1 time

Re: Events for serial driver transmission completion

Post by Raul »

Hi Giovanni,

Same results with 17.6.0. However, I have narrowed it down further. In my project I am performing the event registration, chEvtRegisterMaskWithFlags, right after starting the serial driver, sdStart. If I induce a delay between those calls of at least 100 system ticks then all works as it should right from the beginning. What do you make of this?

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

Re: Events for serial driver transmission completion

Post by Giovanni »

It could be a glitch on the serial port triggering an RX error event, do you switch pins to alternate before or after the call to sdStart()? Anyway I am beginning to think to something electric in nature.

Giovanni
Raul
Posts: 43
Joined: Thu Aug 13, 2015 5:15 pm
Has thanked: 3 times
Been thanked: 1 time

Re: Events for serial driver transmission completion

Post by Raul »

The pins are configured before calling sdStart (), all of them done as as per their board definition file.
The event flags returned by the unexpected event contained a value of 20, TRANSMISSION_END and INPUT_AVAILABLE. I can understand the latter but no the former at that point, unless caused by some electrical side effect. It's probably down to some initial condition that must be triggering the tx interrupt somehow.

Thanks
Post Reply