I think the answer to this is "No", but maybe someone who knows the programming environment well can tell me how to do it. What I want to do is use a 1msec timer interrupt in my program. That's easy to do, but it's occurred to me that the processor already uses a 1msec interrupt derived from Timer0 (OK, I know it's not very accurate) so I was wondering if it's possible to add new tasks to it. So when the millisecond counter gets incremented, it would do something I've programmed as well. Can it be done?
Does whatever you are doing need to be triggered by an interrupt or would using millis() or micros() be good enough ?
See this:
...It suggests using an ISR(TIMER0_COMPA_vect){...}
to piggyback an interrupt off of the same timer0 configuration.
I would recommend do not interfere with system Timer interrupt and use a micros() instead. The 1ms interval is long enough and can be emulated by software easily.
Also take a note, that your code should be very short and run in a few uS to able a system work properly.
Thanks for the replies. To get my 1msec interrupt (and I do want an interrupt, without polling millis() ) I'm already doing it the way the Adafruit link suggests, with a comparison to the Timer0 count. But there's lost time associated with an interrupt, and my program has to accept that delay twice per millisecond. I'd be happier if the two could be combined, and it wouldn't be interfering with the system timer, just making the interrupt there do a little more work. But I expect that the existing millisecond timer is a fixed block of code which can't be changed without loading up a whole new kernel (if stuff like that is the kernel).
I also wonder why the millisecond timer works off an overflow, meaning it counts up to 255 and wraps around, and then the built-in code approximately fixes it by adding an extra count 1 cycle in every 42. If it used a count to 250, it could be more accurate. By using the comparison myself and not allowing for that mismatch, I'm actually getting milliseconds that are 1024 microseconds long, but I'm more interested in consistency than accuracy. Maybe the people who wrote the Arduino code were Douglas Adams fans?
I would start with how every AVR interrupt has 85 cycles of overhead and how here on the forum we show/tell/teach how to run tasking on a single thread without reverting to interrupts at all. The technique goes back to the 70's but in the early 80's with cheap computers it was hobbyists doing it as well as EE's.
Wanna learn? Wanna write seriously fast IPO code?
Every so often in years we get someone with their version of RTOS making speed claims. Fact is that non-blocking code has been faster every time so far, it has less overhead!
If timer0 TOP was 250, it would make the timer0 PWMs strange--analogWrite(5,251) and analogWrite(6,251) wouldn't work like the rest.
If you're interested, the code is here:
Can you explain why?
Yes, my program is actually a library routine, and I want it to be as clean as possible from the user's viewpoint (even if the user is only me). So I can tolerate an interrupt, but I don't want to have to insert something in the user's loop() function. I can live with the comparison interrupt using Timer0, but the ideal would be to make my millisecond interrupt run without using any extra processor resources at all, and that's the way it would be if I could add my millisecond operation to the one that's already in the Arduino's code.
Thank you DaveX, I was interested! That point about the PWMs certainly would never have occurred to me. Although I wonder if it would be better to offer Arduino users a fixed PWM frequency of 1KHz on those two outputs, with duty cycles in the range 0-249 and the warning "Above 249 produces a 100% duty cycle". But they wanted to use the full available range.
That's your call. My only comment is that It will only require one statement in loop() to let your library do what you want it to do.
Clean as possible would be not interfering with millis(), micros(), or the PWM pins.
The common way to do this is ask the user to call a myLibraryThingy.run()
function as often as necessary.
As I said, one statement
Not the same track but a tool that lets a sketch be compared before and after edits. It was run on an Uno, as only itself it clocks over 370K loops per second.
// NoBlockLoopCounterLT Mar 16, 2024 by GoForSmoke @ Arduino.cc Forum
// Some speed changes thanks to J-M-L Jackson of the Arduino forum!
// 3/16/24 -- another change from 777 of the Arduino forum uses timer0_millis.
// Free for use. Compiled on Arduino IDE 1.8.19
// This sketch counts times that loop has run each second and prints it.
// It uses the void LoopCounter() function that does not block other code.
// The LT is for Lighter Timing. Instead pf 32-bit subtraction we take
// advantage of what makes the low 8 bits of 32-bit millis() +/-1.
// The low 8 skips 6 values counting to 256, it takes 250ms to flip bit 8.
// Using 16 bits of millis() allows solid timing of 1/4 sec to 32 sec.
// I keep 1 byte to hold the ON/OFF of the last read of bit 10 = 1 sec.
// On an 8-bit CPU it should take fewer cycles.
// Please note that optimizations from J-M-L Jackson and 777 have been added.
// run this sketch just to see what such a lightweight sketch can run at
// ~370033 loops per second!
extern volatile unsigned long timer0_millis; // might be faster than millis()
void setup()
{
Serial.begin( 115200 );
Serial.println( F( "\n\n\n Loop Counter, free by GoForSmoke\n" ));
Serial.println( F( "This sketch counts times that loop has run each second and prints it." ));
}
void LoopCounter() // tells the average response speed of void loop()
{ // inside a function, static variables keep their value from run to run
static unsigned long count; // only this function sees this
static bool lastBit10Set; // only this function sees this
word millis16 = timer0_millis;
count++; // adds 1 to count after any use in an expression, here it just adds 1.
bool currentBit10Set = millis16 & 0x0400; // leverage integral to bool implicit promotion
if (currentBit10Set != lastBit10Set) // 1 second
{
// Serial.print( millis16 ); // 16-bit binary into decimal text, many micros
// Serial.write('\t');
Serial.println( count ); // 32-bit binary into decimal text, load of cycles!
count = 0; // don't forget to reset the counter
lastBit10Set = currentBit10Set; // changes 0<==>1
}
}
void loop() // runs over and over, see how often with LoopCounter()
{
LoopCounter(); // the function runs as a task, the optimizer will inline the code.
}
Are you using an AVR board, or something based on some other CPU?
Why complicate matters unnecessarily? Just use an .update() or .run() function that's called from loop and do whatever timing you need with millis().
One of the (many) drawbacks of this approach is that you'll make your library/code dependent on the hardware configuration. I.e. it won't be portable across platforms, which is one of the main benefits of using Arduino in the first place.
It'll still perform some task in the ISR, so it'll consume 'extra processor resources'. The main drawback of your ISR-based approach is that this resource consumption will be time-critical/high-priority, while the loop()-based approach will allow other (actually time-critical) interrupts to take precedence.
So not only would you build in hardware-dependence, you'd also end up modifying Arduino platform/board code, meaning that a system re-install or port to a different board would have to include also re-installing your modified board definition, and sharing your library with others would involve the same complication.
I think you're making this unnecessarily difficult and you're introducing a variety of new problems in exchange for very little tangible benefit.
So far just Nanos, so the processor is an Atmega328.
In the meantime there are a lot of different Nanos with very different processors ...
OP stated "so the processor is an Atmega328"
Yeah, but I wanted to point out that 'just Nanos' no longer does in any case mean 'so processor is 386P' - may be he would like to use another 'Nano' in the furure?