Hi Kef,
I wish I could simply post the code. It is assembly language, so it is pretty fast, even though it may sound complex.
kef wrote:
. . . I just wondered how are you dealing with too small ticks%65536. Too small is for example less than interrupt latency.
I have not needed to. I use this timer queue to primarily schedule tasks. If I need to do things on a 'tight' schedule, I would do that in a dedicated routine with a dedicated timer. I don't need to generate waveforms, which does sound demanding.
As I understand it, you are not triggering too small timeouts, but you are queuing them and sorting them. Right?
Yes, I'm not dealing with short timeouts at all. This technique is mainly for long timeouts. By "short", I mean around 200 microseconds, which is how often I schedule scanning & debouncing a keypad. I am using a queue, and new timer requests get inserted in time-order. For the queue, I use a unidirectional linked-list (so that I don't need to move things around).
Then1) it is not clear what are you doing with long timeouts but with too small lower 16bits of timeout? I may be wrong, but without extra work timer will be not accurate and could delay for additional up to 2^16 ticks.
Long and short timers are handled the same way, with the caveat that a short timer must be long enough to not risk missing the output-compare interrupt. If you look at the timer and my extension to it as a 24-bit timer, than it works as follows:
Let's say the timer counts 1 tick per microsecond. And
I need to schedule two time-outs: 2 seconds from now and 100 microseconds from now.
I read the 24-bit timer (timer+extension).
I add 2 million to it. I find that the queue is empty, so I stick the low 16-bits into the output-compare register, and the high 8 bits in the queue entry. The 2-second timer is now running.
I read the 24-bit timer (timer+extension).
I add 100 to it. Note that the high 8 bits probably has not changed, So the first output-compare will be the correct one. I find that this queue entry goes ahead of the prior one, so I stick the low 16-bits into the output-compare register, and the high 8 bits in the queue entry. The 100 microsecond timer is now running.
100 microseconds later:
The output compare interrupt occurs. In the ISR, I compare the high 8 bits and find they match. I schedule the task (its semaphore was stored in the queue entry), take the low 16-bits from the next queue entry and load it in the output-compare register. We are now waiting for the 2-second timer.
On subsequent output-compare interrupts, I compare the timer extension with the high 8-bits in the queue entry, and they don't match, so I just exit.
2 seconds (minus 100 microseconds) later:
After many output-compare interrupts where I did nothing because the high eight bits did not match the timer-extension, the extension finally matches, and I schedule its task. The timer queue is now empty again.
You are checking if timeout is done in two ISR's, output compare and timer overflow, right?
No, I just check for timeouts in the output-compare ISR. All the overflow ISR does is increment the timer extension counter.
2) OK, you are not using "bad" timeouts with too small lower 16bits, but are you sure difference between two adjacent queued delays doesn't have too small lower 16bits? The same issue may happen.
Yes, you are right! I had not seen that! That is a possibility, but it hasn't bitten me yet (that I know of).
Looking at the code, the output-compare ISR takes just under 3 microseconds on a 20mHz bus clock. Add that to my maximum latency of 5 microseconds, and it means 8 microseconds is the closest two timers can safely get (I almost always use 2 microseconds per tick, left over from 68701). So I can't support less than a 4-count difference.
I guess I should add code to my queuing routine to check for that. It wouldn't be too hard since I'm already looking at the time in order to find the correct place to insert into the list.
If I was not clear, for long delays I'm using only output compare interrupt, timer is free running. To count overflows, output compare is triggered with old compare register setting, etc
Yes. I believe I understand. In summary, we are both using the output compare interrupt. But you are counting down the interrupts to your final destination, and I am comparing with an extension to the timer for my final destination.
I suspect your way is more efficient. But mine took a lot longer to write.