c unblocking wait() implementatoin

WBahn

Joined Mar 31, 2012
32,823
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
Even in high level code this isn't a necessity. I wrote a simulation engine for processing blocks and since I have almost no experience in writing threads or callback function or using interrupts on a PC, I simply wrote a scheduler that did a round robin call for each block. The block processed one chunk of input and produced one chunk of output, if it could (it might not have the input needed or it might have no place to put the output because the bridging FIFOs might be full). If it couldn't, it returned without doing anything. If it could, it processed just the one chunk, no matter how much data was available, and returned.

This is nothing more than cooperative multitasking. The key to cooperative multitasking is that each task has to play nice. It can either be designed so that it does just a small amount of work and returns, or it can abide by a time limit set either globally or told to it by the scheduler and it agrees to return control when the time expires. The big thing it can't do is block waiting for some resource that will now never become available because the only process running -- in the case of MCU code running without an OS -- has blocked.
 

nsaspook

Joined Aug 27, 2009
16,321
Even in high level code this isn't a necessity. I wrote a simulation engine for processing blocks and since I have almost no experience in writing threads or callback function or using interrupts on a PC, I simply wrote a scheduler that did a round robin call for each block. The block processed one chunk of input and produced one chunk of output, if it could (it might not have the input needed or it might have no place to put the output because the bridging FIFOs might be full). If it couldn't, it returned without doing anything. If it could, it processed just the one chunk, no matter how much data was available, and returned.

This is nothing more than cooperative multitasking. The key to cooperative multitasking is that each task has to play nice. It can either be designed so that it does just a small amount of work and returns, or it can abide by a time limit set either globally or told to it by the scheduler and it agrees to return control when the time expires. The big thing it can't do is block waiting for some resource that will now never become available because the only process running -- in the case of MCU code running without an OS -- has blocked.
No, it's not a necessity for synchronous programming and I don't think anyone said it was. Using function pointers, cooperative multitasking or other methods to change the program flow sequence is still synchronous. Asynchronous programming is OS and language agnostic but there must be some method of execution context switching separate from mainline program control..

https://docs.microsoft.com/en-us/pr...lstudio/visual-studio-2013/hh191443(v=vs.120)
 
Last edited:

Thread Starter

bug13

Joined Feb 13, 2012
2,002
Let's get back to my case.

In this firmware, there is a 1ms timer interrupt, in the interrupt, I need to do something fancy to an IO, and then I need to wait exactly __delay_us(DELAY_TIME), then do something fancy to an IO again.

Here is what it looks like:
Code:
// a pre-define time, usually 3-5 us
#define DELAY_TIME (5??)

void interrupt TIMER_ISR_1MS (void){
  // clear flags etc...
  // do something fancy to an IO
  __delay_us(DELAY_TIME);
  // do something fancy to an IO again
  // other stuff
}
I know I need to spend minimum time in an interrupt, set a flag, then do thing in the main loop. But in this case, I need to guarantee this __delay_us() to be exactly this long.

What is the best way to handle this?

Thanks guys!
 

WBahn

Joined Mar 31, 2012
32,823
I don't know that there is any way to guaranteed that it is EXACTLY that long (even if we allow for "exactly" meaning just an exact number of clock cycles). IF this is the ONLY interrupt that can be generated by the system, then it should be doable. But at soon as you add a second interrupt, then you have the possibility that the other interrupt occurs just before this one and so this one can't be handled immediately.

Have you considered using a separate circuit that is dedicated to handling this I/O and that your main system simply configures and enables/disables?

I'm not positive, but I think some chips actually have features like this, don't they?
 

joeyd999

Joined Jun 6, 2011
6,279
Let's get back to my case.

In this firmware, there is a 1ms timer interrupt, in the interrupt, I need to do something fancy to an IO, and then I need to wait exactly __delay_us(DELAY_TIME), then do something fancy to an IO again.

Here is what it looks like:
Code:
// a pre-define time, usually 3-5 us
#define DELAY_TIME (5??)

void interrupt TIMER_ISR_1MS (void){
  // clear flags etc...
  // do something fancy to an IO
  __delay_us(DELAY_TIME);
  // do something fancy to an IO again
  // other stuff
}
I know I need to spend minimum time in an interrupt, set a flag, then do thing in the main loop. But in this case, I need to guarantee this __delay_us() to be exactly this long.

What is the best way to handle this?

Thanks guys!
Correct me if I am wrong, but I don't think you've mentioned the processor you are working with or the clock speed. 3 to 5 uS, in many cases, is not too far different than just the latency time + context save + context restore + return time of a single interrupt. Depending on the processor and clock speed, you could spend this much time just processing interrupt overhead. For example, a PIC running 20Mhz has a Tinst of 200nS. 5 uS is only 25 instruction cycles.

If it doesn't hurt, I'd just idle for those 3 to 5 uS by counting instruction cycles.

If it does, @WBahn is right: you probably need a hardware solution. Or a really, really fast MCU.

As an alternative, find a way to write the other portions of the code so they are tolerant of a 5+ uS pause in execution.
 
Last edited:

Thread Starter

bug13

Joined Feb 13, 2012
2,002
As usual I left out a lot of details, let me correct myself with more accurate info.

This EXACTLY is based on the crystal, assuming the crystal is within specs.

The 1ms interrupt is not so important, as long as ~1ms, also the interrupt latency time not critical as well. The important is the sequence, and the sequence is not interrupted:
  • do something to the IO
  • wait() xyz cycles
  • do somehting to the IO again
I have multiple interrupts, but only one level interrupt priority, so I guess I am OK. As the new interrupt can't interrupt my sequence. Also judging from the @joeyd999 and @WBahn comments, have a __delay_us(a few us) is acceptable in an interrupt?

Thanks guys!
 

WBahn

Joined Mar 31, 2012
32,823
As usual I left out a lot of details, let me correct myself with more accurate info.

This EXACTLY is based on the crystal, assuming the crystal is within specs.

The 1ms interrupt is not so important, as long as ~1ms, also the interrupt latency time not critical as well. The important is the sequence, and the sequence is not interrupted:
  • do something to the IO
  • wait() xyz cycles
  • do somehting to the IO again
I have multiple interrupts, but only one level interrupt priority, so I guess I am OK. As the new interrupt can't interrupt my sequence. Also judging from the @joeyd999 and @WBahn comments, have a __delay_us(a few us) is acceptable in an interrupt?

Thanks guys!
If the new interrupt can't interrupt your sequence, isn't it also the case that the sequence can't interrupt the new interrupt?

What if some other interrupt is being serviced when the timer throws the interrupt that you need serviced right away so as to maintain your timing between sequence points? Isn't it going to have wait until the prior interrupt is finished being serviced?
 

joeyd999

Joined Jun 6, 2011
6,279
a __delay_us(a few us) is acceptable in an interrupt?
I don't use C for embedded work, I still don't know what MCU you're using, and I have no idea how __delay_us() is implemented in the library you have chosen.

But, aside from the above mentioned hardware solution, there really is no alternative -- whether you use __delay_us() or your own delay code.
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
If the new interrupt can't interrupt your sequence, isn't it also the case that the sequence can't interrupt the new interrupt?

What if some other interrupt is being serviced when the timer throws the interrupt that you need serviced right away so as to maintain your timing between sequence points? Isn't it going to have wait until the prior interrupt is finished being serviced?
Yes, and the other interrupt just set some flags, so I am OK with that. The 1ms timer is not so critical (it can be every 2ms), it's the sequence after a timer interrupt can't be interrupted. Hope I am not confusing you too much.
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
I don't use C for embedded work, I still don't know what MCU you're using, and I have no idea how __delay_us() is implemented in the library you have chosen.

But, aside from the above mentioned hardware solution, there really is no alternative -- whether you use __delay_us() or your own delay code.
Sorry forgot to mention, I am using PIC18F, XC8 Free compiler.
 

joeyd999

Joined Jun 6, 2011
6,279
While you still haven't provided much detail, I'd like to remind you that the PIC18F series allows you to toggle (or set high or low) pins asynchronously to your running program using a CCP module and a timer. See options 0010, 1000, and 1001.

Selection_007.png

I use this mode frequently when I need some hardware timing that software is not fast enough to perform, and it is not required to actually service an interrupt.
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
While you still haven't provided much detail, I'd like to remind you that the PIC18F series allows you to toggle (or set high or low) pins asynchronously to your running program using a CCP module and a timer. See options 0010, 1000, and 1001.

View attachment 151775

I use this mode frequently when I need some hardware timing that software is not fast enough to perform, and it is not required to actually service an interrupt.
Basically my secret (wink wink :)) fancy stuff is, drive the pins high and low according to a global variable (just like a hardware PWM but do it in the software), but in between those steps, I will need to switch the pins to input and read the state.

So the PWM is about 100Hz, as I am using 1ms, I can have 10 steps (10% each step). But every time the pin switch from low to high, I will need to change the pins to input (no pull up or low, digital read), wait xyz us, read the pins, and put the reading into a counters. The main loop will do something according to the counters.

There are 16 pins in total.
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
I assume you know the pin will not go from "low to high", but from low to high-Z.
Assuming you are referring to my code, and not CCP module, yes, that's what I need to do:
  • drive pins low (output mode up to this point)
  • then switch to input mode (high z, no pull up or pull low resistor)
  • wait xyz us
  • read the pins (digital read)
 

WBahn

Joined Mar 31, 2012
32,823
Yes, and the other interrupt just set some flags, so I am OK with that. The 1ms timer is not so critical (it can be every 2ms), it's the sequence after a timer interrupt can't be interrupted. Hope I am not confusing you too much.
Actually, you are. Before you said, a couple times, that you needed to guarantee that the delay was exactly (within oscillator tolerance) the specified amount of time. But now you are saying this isn't critical and that a factor of two is just fine. Which is it? Or what am I missing?

What is this "sequence" that can't be interrupted?
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
Before you said, a couple times, that you needed to guarantee that the delay was exactly (within oscillator tolerance) the specified amount of time.
The delay I am referring to is the delay within the interrupt, please refer to post #23, that needs to be exactly (within oscillator tolerance)

But now you are saying this isn't critical and that a factor of two is just fine.
This isn't critical is referring to the timer interrupt. The interrupt that start the timing sensitive code.

Sorry for the more confusion.

Here is what I mean:
Code:
// timer interrupt, 1ms or 2ms, not so important
// also interrupt delay, context switching are not important
// critical codes are executed within interrupt, there is only one priority level, so the codes within the interrupt can't be interrupted.
void interrupt timer_ISR(void){
  // the following codes are the sequence I am referring to
  // timing is important

  /* critical sequence start below, once started, no interrupt allowed,  delay must be excat */

  // to-do: do something fancy to IO again
  // delay xyz us
  // to-do: do something facny to IOs again

  /* critical sequence finished here */
}
The "sequences" are already within interrupt, and I only have one priority level of interrupt. That's why I said it can't be interrupted.
 

WBahn

Joined Mar 31, 2012
32,823
Okay, I see what you are talking about now. Thanks.

So, do you have a solution now or is there still some aspect that is not in place?
 
Top