Page 1 of 3

Virtual Timers

Posted: Sat May 05, 2012 9:47 am
by levrik
Hi there,
I'm having problems implementing a RX-timeout with Virtual timers:

1. Is there a way to implement the vt-callback not in a IRQ-context?
2. Is there a way to reset the timer before it reaches its end to implement a timeout?

Momentarily i've implemented it this way:

Code: Select all

if( chVTIsArmedI( &rfmp->vt ) )
{
      chSysLock();
      chVTResetI( &rfmp->vt );
      chVTSetI( &rfmp->vt, MS2ST(msec), vtmr_cb, rfmp );
      chSysUnlock();
}

This code does not work for me properly, because sometimes it seems, that the vt does not start. Looking into the vt-structure with a debugger shows that it contains a valid vt->vt-time, a valid vt->vt_func and a valid vt->vt_par, but the callback does not get called. Any hints?

Thanks in advance.
Thanks.

Re: Virtual Timers

Posted: Sat May 05, 2012 9:59 am
by Giovanni
Hi,

1) Not directly, you can wakeup a thread from there, then do processing from the thread. Alternatively you can use an "Event Timer" see under ./os/various.
2) Yes, it is done in several places, see the function chSchGoSleepTimeoutS() for an example.

You code is failing because before resetting a timer you have to check if it is armed. See the example:

Code: Select all

  chSysLock();
  if (chVTIsArmedI(vtp))
    chVTResetI(vtp);
  chSysUnlock();


Giovanni

Re: Virtual Timers

Posted: Mon May 07, 2012 3:11 pm
by levrik
Hi Giovanny,
thank you for your answer. It works now with code similar to the example you've presented.

But i do not understand why the first version did not work, because there the timer was also checked if armed before it was reset.

Kind regards.

Re: Virtual Timers

Posted: Mon May 07, 2012 3:26 pm
by Giovanni
Hi,

Because chVTIsArmedI() was called outside the lock, the whole operation is not atomic. The timer could expire after chVTIsArmedI() and before chVTResetI().

That "sometime", was a clear hint about a race condition somewhere.

Giovanni

Re: Virtual Timers

Posted: Fri Mar 06, 2015 5:03 am
by Chudik
Have a problem with sending data through SPI in Virtual Driver header. Symptoms are as same as I had when I just started to work with SPI and used wrong driver. Can't figure out how to solve it.
Shortly, what I need. I'm using OLED display in my design and since it has limited time of life, I want to switch off the display. So, after showing text on the display, I want to switch on the display, run virtual timer, dim the display after 1 sec and switch off after the next 1 sec.
Here is part of my code

Code: Select all

uint8_t dt_data[2];

void  hDisplayTimer(void *arg);

static uint8_t  DisplayState = DISPLAYOFF;
static struct VirtualTimer DisplayTimer;

void UI_Send(ui_msg *msg, uint8_t *data, int cnt, ...)
{
   va_list ap;
   uint8_t i, checkcmd = 0, tag;
   int indata;
   uint32_t address;

   va_start(ap,cnt);
   indata = va_arg(ap, int);
   msg->tag = (uint8_t)indata;


    tag = msg->tag;
   switch (tag)
   {
           ....
   }


   chMsgSend(UI_Console,(msg_t)msg);
   if(tag==DISPLAY_TXT)
        {
         DisplayState = DISPLAYON;
         dt_data[0] = DISPLAY_CMD;
         dt_data[1] = DisplayState;
         spiAcquireBus(&SPID2);
         spiStart(&SPID2, &display_cfg);
         UISendData(&SPID2, 2, dt_data);      //  <<<--- here it works without any problem
         spiReleaseBus(&SPID2);

         chVTSet(&DisplayTimer, S2ST(1), hDisplayTimer, NULL);
      }
}


void  hDisplayTimer(void *arg)
{

    switch(DisplayState)
    {
        case DISPLAYON:  DisplayState = DISPLAYDIM;
                         break;
        case DISPLAYDIM: DisplayState = DISPLAYOFF;
                         break;
        case DISPLAYOFF:
        default:         DisplayState = DISPLAYOFF;
                         break;
    }

         dt_data[0] = DISPLAY_CMD;
         dt_data[1] = DisplayState;
         spiAcquireBus(&SPID2);
         spiStart(&SPID2, &display_cfg);   

         UISendData(&SPID2, 2, dt_data); //  <<<--- here it does not work, data sent to SPI, but can't get back from this function
         spiReleaseBus(&SPID2);

         chSysLockFromIsr();
         chVTSetI(&DisplayTimer, S2ST(1), hDisplayTimer, NULL);
         chSysUnlockFromIsr();
}


Probably it happens because I send data to SPI from the timer interrupt handler (is it true?). So I tried to use chSysLockFromIsr() instead of chSysLock() (used in SPISend() anyway). It did not help.
What would be the solution for that?

And another problem related to that. Why after I ran the timer it calls the handler immediately, without actual waiting for one second? I watch window I see the values. Not sure that 0x205 (517d) is correct value for 1 second, though.
CH_FREQUENCY defined as 1000

Re: Virtual Timers

Posted: Fri Mar 06, 2015 5:35 am
by skute
Chudik wrote:Have a problem with sending data through SPI in Virtual Driver header. Symptoms are as same as I had when I just started to work with SPI and used wrong driver. Can't figure out how to solve it.
Shortly, what I need. I'm using OLED display in my design and since it has limited time of life, I want to switch off the display. So, after showing text on the display, I want to switch on the display, run virtual timer, dim the display after 1 sec and switch off after the next 1 sec.
Here is part of my code

Code: Select all

uint8_t dt_data[2];

void  hDisplayTimer(void *arg);

static uint8_t  DisplayState = DISPLAYOFF;
static struct VirtualTimer DisplayTimer;

void UI_Send(ui_msg *msg, uint8_t *data, int cnt, ...)
{
   va_list ap;
   uint8_t i, checkcmd = 0, tag;
   int indata;
   uint32_t address;

   va_start(ap,cnt);
   indata = va_arg(ap, int);
   msg->tag = (uint8_t)indata;


    tag = msg->tag;
   switch (tag)
   {
           ....
   }


   chMsgSend(UI_Console,(msg_t)msg);
   if(tag==DISPLAY_TXT)
        {
         DisplayState = DISPLAYON;
         dt_data[0] = DISPLAY_CMD;
         dt_data[1] = DisplayState;
         spiAcquireBus(&SPID2);
         spiStart(&SPID2, &display_cfg);
         UISendData(&SPID2, 2, dt_data);      //  <<<--- here it works without any problem
         spiReleaseBus(&SPID2);

         chVTSet(&DisplayTimer, S2ST(1), hDisplayTimer, NULL);
      }
}


void  hDisplayTimer(void *arg)
{

    switch(DisplayState)
    {
        case DISPLAYON:  DisplayState = DISPLAYDIM;
                         break;
        case DISPLAYDIM: DisplayState = DISPLAYOFF;
                         break;
        case DISPLAYOFF:
        default:         DisplayState = DISPLAYOFF;
                         break;
    }

         dt_data[0] = DISPLAY_CMD;
         dt_data[1] = DisplayState;
         spiAcquireBus(&SPID2);
         spiStart(&SPID2, &display_cfg);   

         UISendData(&SPID2, 2, dt_data); //  <<<--- here it does not work, data sent to SPI, but can't get back from this function
         spiReleaseBus(&SPID2);

         chSysLockFromIsr();
         chVTSetI(&DisplayTimer, S2ST(1), hDisplayTimer, NULL);
         chSysUnlockFromIsr();
}


Probably it happens because I send data to SPI from the timer interrupt handler (is it true?). So I tried to use chSysLockFromIsr() instead of chSysLock() (used in SPISend() anyway). It did not help.
What would be the solution for that?

And another problem related to that. Why after I ran the timer it calls the handler immediately, without actual waiting for one second? I watch window I see the values. Not sure that 0x205 (517d) is correct value for 1 second, though.
CH_FREQUENCY defined as 1000


Chudik,

It's likely that the SPI driver uses interrupts and you are blocking them from happening because you are attempting a SPI transfer from an interrupt handler. However, even more incorrect is that you are calling spiAcquireBus from the timer interrupt handler because this will attempt to acquire a mutex and "block" the ISR if the mutex isn't available and cause an exception. As Giovanni always asks, please enable the state checker as it would catch these errors.

Re: Virtual Timers

Posted: Fri Mar 06, 2015 10:21 am
by Chudik
Of course, my mistake was with setting the time - 1 min, not 1 second.
So, the last question in my previous post is not a problem,
However, all the other stuff is valid. When I try to send data to SPI from the handler, the system gets into _unhandled_exception() and stuck in there.

Re: Virtual Timers

Posted: Fri Mar 06, 2015 10:31 am
by Chudik
skute wrote:It's likely that the SPI driver uses interrupts and you are blocking them from happening because you are attempting a SPI transfer from an interrupt handler.

Yes, it is correct statement. However, in my original version I was trying to send message to my UI_ConsoleServer with the same result. After that happened I decided to try such direct attempt.

I will try tomorrow to change it back to sending message to be sure that I get the same problem

skute wrote:As Giovanni always asks, please enable the state checker as it would catch these errors.

How to enable it?

Re: Virtual Timers

Posted: Fri Mar 06, 2015 11:32 am
by Giovanni
Hi,

See the debug section in chconf.h, I recommend enabling assertions, checks and state checker during development. It stops your application in case of errors then inspect the global variable dbg_panic_msg, it points to an error message in memory.

Giovanni

Re: Virtual Timers

Posted: Fri Mar 06, 2015 8:45 pm
by Chudik
OK, got panic message
SV#4, misplaced @p chSysLock().

Traced the program. Got this message in chMsgSend() (chmsg.c)
Line 80. Calling chSysLock()

Do I need to show where chMsgSend() called from?