c unblocking wait() implementatoin

Thread Starter

bug13

Joined Feb 13, 2012
2,002
Hi guys

Is there a way to implement a unblocking wait() without a RTOS. I usually have a timer tick variable for time keeping, and simple task like flashing a led or something.

But I was wondering if there is a way to implement a unblocking wait() without using a RTOS?

Thanks guys!
 

WBahn

Joined Mar 31, 2012
30,076
What is this unblocking wait() supposed to unblock?

Do you mean a nonblocking wait()?

If so, what other things are supposed to be allowed to happen?

Is your code multithreaded? Or is that the effect you are trying to emulate?
 

nsaspook

Joined Aug 27, 2009
13,312
A xc16 example of software timers that be used for 'waits'.

C:
#include <xc.h>
#include <stdint.h>
#include <stdbool.h>

//Software timers - use these to refer to timers rather than
//integers.  Add more as needed.
//NOTE: 32767 ms max delay

enum APP_TIMERS {
    TMR_INTERNAL = 0, //Used in timers.c - do not remove or use elsewhere
    TMR_LEDS,
    TMR_RN_COMMS,
    TMR_ADC,
    TMR_POT,
    TMR_SW1_DEBOUNCE,
    TMR_SW2_DEBOUNCE,
    TMR_SW3_DEBOUNCE,
    TMR_SW4_DEBOUNCE,
    TMR_BT_TX,
    TMR_BAT_CHECK,
    TMR_SPI,
    TMR_HR,
    TMR_BATT,
    TMR_AIO_DIG,
    TMR_AIO_ANA,
    TMR_AIO_AGG,
    //
    //(Add timers here as needed)
    //
    TMR_COUNT //number of timers - always keep at end of enum!
};

static volatile uint16_t tickCount[TMR_COUNT] = {0};

void Timers_Init(void)
{
    //Timer is used for interrupt based software timers counting 1ms intervals to a resolution of 500us
   /* setup required hardware timer here for Interrupt every 500us */

}

//**********************************************************************************************************************
// Start one of the software timers

inline void StartTimer(uint8_t timer, uint16_t count)
{
    tickCount[timer] = count << 1; //Interrupt is every 500us but StartTimer() takes multiple of 1ms so multiply by 2
}

//**********************************************************************************************************************
// Check if one of the software software timers has timed out

inline bool TimerDone(uint8_t timer)
{
    if (tickCount[timer] == 0) { //Check if counted down to zero
        return true; //then return true
    }
    return false; //else return false
}

/* for xc16 but easily changed to xc8 or other types of interrupt vector formats */

// Interrupt is every 500us
void _ISR_NO_AUTO_PSV _T1Interrupt(void)
{
    uint8_t i;

    IFS0bits.T1IF = 0; //Clear the interrupt flag
    //Decrement each software timer
    for (i = 0; i < TMR_COUNT; i++) {
        if (tickCount[i] != 0) {
            tickCount[i]--;
        }
    }
}

// example use fragment in a loop.
// packet transmission queue, TimerDone puts packet in the transmission stream

#define BT_TX_MS            10         //minimum time between consecutive BTLE message transmissions
#define BATT_TX_MS        100
#define AIO_TX_MS           40

Timers_Init(void);

while (1)  {
        if (TimerDone(TMR_BATT)) {
            //Form message
            sprintf(appData.transmit_packet, "suw,"PUBLIC_BATT_CHAR_BL",%02d\r", (appData.potValue >> 6)&0b00111111);
            //Try to transmit the message; reset timer if successful
            if (BT_SendCommand(appData.transmit_packet, true)) { // wait for transmission slot
                StartTimer(TMR_BATT, BATT_TX_MS);
            }
        }

        if (TimerDone(TMR_AIO_DIG)) {
            //Form message
            sprintf(appData.transmit_packet, "shw,"PUBLIC_AIO_CHAR_DIG_H",0101010101010101\r"); // digital data
            //Try to transmit the message; reset timer if successful
            if (BT_SendCommand(appData.transmit_packet, true)) {
                StartTimer(TMR_AIO_DIG, AIO_TX_MS);
            }
        }

    // do other stuff
}
 

joeyd999

Joined Jun 6, 2011
5,287
A xc16 example of software timers that be used for 'waits'.

C:
#include <xc.h>
#include <stdint.h>
#include <stdbool.h>

//Software timers - use these to refer to timers rather than
//integers.  Add more as needed.
//NOTE: 32767 ms max delay

enum APP_TIMERS {
    TMR_INTERNAL = 0, //Used in timers.c - do not remove or use elsewhere
    TMR_LEDS,
    TMR_RN_COMMS,
    TMR_ADC,
    TMR_POT,
    TMR_SW1_DEBOUNCE,
    TMR_SW2_DEBOUNCE,
    TMR_SW3_DEBOUNCE,
    TMR_SW4_DEBOUNCE,
    TMR_BT_TX,
    TMR_BAT_CHECK,
    TMR_SPI,
    TMR_HR,
    TMR_BATT,
    TMR_AIO_DIG,
    TMR_AIO_ANA,
    TMR_AIO_AGG,
    //
    //(Add timers here as needed)
    //
    TMR_COUNT //number of timers - always keep at end of enum!
};

static volatile uint16_t tickCount[TMR_COUNT] = {0};

void Timers_Init(void)
{
    //Timer is used for interrupt based software timers counting 1ms intervals to a resolution of 500us
   /* setup required hardware timer here for Interrupt every 500us */

}

//**********************************************************************************************************************
// Start one of the software timers

inline void StartTimer(uint8_t timer, uint16_t count)
{
    tickCount[timer] = count << 1; //Interrupt is every 500us but StartTimer() takes multiple of 1ms so multiply by 2
}

//**********************************************************************************************************************
// Check if one of the software software timers has timed out

inline bool TimerDone(uint8_t timer)
{
    if (tickCount[timer] == 0) { //Check if counted down to zero
        return true; //then return true
    }
    return false; //else return false
}

/* for xc16 but easily changed to xc8 or other types of interrupt vector formats */

// Interrupt is every 500us
void _ISR_NO_AUTO_PSV _T1Interrupt(void)
{
    uint8_t i;

    IFS0bits.T1IF = 0; //Clear the interrupt flag
    //Decrement each software timer
    for (i = 0; i < TMR_COUNT; i++) {
        if (tickCount[i] != 0) {
            tickCount[i]--;
        }
    }
}

// example use fragment in a loop.
// packet transmission queue, TimerDone puts packet in the transmission stream

#define BT_TX_MS            10         //minimum time between consecutive BTLE message transmissions
#define BATT_TX_MS        100
#define AIO_TX_MS           40

Timers_Init(void);

while (1)  {
        if (TimerDone(TMR_BATT)) {
            //Form message
            sprintf(appData.transmit_packet, "suw,"PUBLIC_BATT_CHAR_BL",%02d\r", (appData.potValue >> 6)&0b00111111);
            //Try to transmit the message; reset timer if successful
            if (BT_SendCommand(appData.transmit_packet, true)) { // wait for transmission slot
                StartTimer(TMR_BATT, BATT_TX_MS);
            }
        }

        if (TimerDone(TMR_AIO_DIG)) {
            //Form message
            sprintf(appData.transmit_packet, "shw,"PUBLIC_AIO_CHAR_DIG_H",0101010101010101\r"); // digital data
            //Try to transmit the message; reset timer if successful
            if (BT_SendCommand(appData.transmit_packet, true)) {
                StartTimer(TMR_AIO_DIG, AIO_TX_MS);
            }
        }

    // do other stuff
}
Wow. That's a lot of RAM and Tinsts.

Make it worth it: at least implement some automatic callback code for when a timer expires.
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
Doh, I mean to say non-blocking, facepalm myself!

If so, what other things are supposed to be allowed to happen?
Run other codes?

Is your code multithreaded? Or is that the effect you are trying to emulate?
I think technically no, but I have a few interrupts running at the same time.
Yes, I think that's the effect that I am trying to emulate, just like one in a RTOS.

The non blocking wait() doesn't need to be absolute accurate. I can do non blocking repeating task like @nsaspook suggested.

Let me try to explain what I want to do:
Code:
wait(){
  if (timer is not expired){
     // somehow give control to other code?
    }else if (timer is expired){
    // continue code after me??
    }
}
Hmmm, after writing out, I don't think I can do this without a RTOS. I guess the key here is to give control to other code. If I do that, I must do some content switching, and control the program counter. If I do that, I may as well implement a RTOS?

Am i right?
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
You and I work in two different worlds.

It makes me cringe to see an architecture that requires 34 RAM bytes to do what I usually do in two.
How do you do it in two? Keen to learn. The way I do it is quite similar with nsaspook
 

joeyd999

Joined Jun 6, 2011
5,287
Doh, I mean to say non-blocking, facepalm myself!


Run other codes?


I think technically no, but I have a few interrupts running at the same time.
Yes, I think that's the effect that I am trying to emulate, just like one in a RTOS.

The non blocking wait() doesn't need to be absolute accurate. I can do non blocking repeating task like @nsaspook suggested.

Let me try to explain what I want to do:
Code:
wait(){
  if (timer is not expired){
     // somehow give control to other code?
    }else if (timer is expired){
    // continue code after me??
    }
}
Hmmm, after writing out, I don't think I can do this without a RTOS. I guess the key here is to give control to other code. If I do that, I must do some content switching, and control the program counter. If I do that, I may as well implement a RTOS?

Am i right?
What you want is cooperative multitasking, and you do not require an RTOS to achieve this.

Create a master timer. It doesn't matter how you do this.

In the main loop, poll each routine. Do nothing and return to main loop if not time to act. Otherwise, perform action and return to main loop.
 

nsaspook

Joined Aug 27, 2009
13,312
Doh, I mean to say non-blocking, facepalm myself!


Run other codes?


I think technically no, but I have a few interrupts running at the same time.
Yes, I think that's the effect that I am trying to emulate, just like one in a RTOS.

The non blocking wait() doesn't need to be absolute accurate. I can do non blocking repeating task like @nsaspook suggested.

Let me try to explain what I want to do:
Code:
wait(){
  if (timer is not expired){
     // somehow give control to other code?
    }else if (timer is expired){
    // continue code after me??
    }
}
Hmmm, after writing out, I don't think I can do this without a RTOS. I guess the key here is to give control to other code. If I do that, I must do some content switching, and control the program counter. If I do that, I may as well implement a RTOS?

Am i right?
If you need asynchronous code execution then yes, you need a RTOS or interrupt driven task switcher (for a PIC18 this could be the low priority vector) timer with a possible callback to execute higher level code at the timer event. For most simple embedded tasks other than GUI type functions that need to 'wait' for inputs while processing I/O near real-time events a loop driven event synchronous state machine tasker will work for events that are not time critical.
 

joeyd999

Joined Jun 6, 2011
5,287
FYI, cooperative multitasking is just a fancy way of saying that a routine uses as many CPU cycles as it requires, and only when it requires them. The alternative is preemptive multitasking, which means a master program (i.e. OS) decides when and how many CPU cycles a routine gets to execute.
 

joeyd999

Joined Jun 6, 2011
5,287
f you need asynchronous code execution then yes, you need a RTOS or interrupt driven task switcher.
I disagree. But that's my purpose in life.

Somehow, I've written dozens of professional high performance real time embedded applications, and never once have I had to resort to using an RTOS.
 

nsaspook

Joined Aug 27, 2009
13,312
I disagree. But that's my purpose in life.

Somehow, I've written dozens of professional high performance real time embedded applications, and never once have I had to resort to using an RTOS.
There's nothing to disagree on IRT to needing a RTOS for high performance real time embedded applications. They just get in the way for most tasks on a non-mmu no vm processor. I'm starting a pic32mzef port of the RIOTOS to the PIC32MZ EF Curiosity Development Board so I do understand when one is needed.
 
Last edited:

joeyd999

Joined Jun 6, 2011
5,287
There's nothing to disagree on IRT to needing a RTOS for high performance real time embedded applications. They just get in the way for most tasks on a non-mmu no vm processor. I'm starting a pic32mzef port of the RIOTOS to the PIC32MZ EF Curiosity Development Board so I do understand when one is needed.
I bet if I set my mind to it, I could write a proof that any application written in the context of an RTOS could be written without one -- likely with less overall code and fewer instruction cycles and just, if not more, effective. Call it "JoeyD Complete" in the same sense as Turing.

This is not to say that writing under an RTOS would not be easier -- especially in the context of the examples you mentioned.

I just don't like absolutes: "You need an RTOS if... "
 

nsaspook

Joined Aug 27, 2009
13,312
I bet if I set my mind to it, I could write a proof that any application written in the context of an RTOS could be written without one -- likely with less overall code and fewer instruction cycles and just, if not more, effective. Call it "JoeyD Complete" in the same sense as Turing.

This is not to say that writing under an RTOS would not be easier -- especially in the context of the examples you mentioned.

I just don't like absolutes: "You need an RTOS if... "
I said: RTOS or interrupt driven task switcher for asynchronous code execution (a timer callback for example) for main task events.

https://www.bisque.com/products/orc...COM/Synchronous_vs_Asynchronous_Execution.htm
 

joeyd999

Joined Jun 6, 2011
5,287
A great example is my RN4020 code. Each possible action is "atomized" into the smallest amount of code that requires complete execution prior to releasing the CPU. Those are encoded into distinct states of the controlling state machine.

While a Bluetooth transaction may require many milliseconds to complete, any one state will only consume microseconds prior to returning to the main loop.

Any hardware/code that cannot wait for the CPU has its own -- short as possible -- interrupt routine. And this is usually used only to synchronize the asynchronous data with the main loop.

But I spend a lot of time up front deciding what is time critical, and what is not.
 
Top