I'm using Timer 1 to drive a pulse train on six pins to drive some motors. Under acceleration and deceleration I have some synchronization issues that require I keep the top value on the counter set right on each pass. This needs to happen as early as possible each time because I don't want to end up needing to set the top value lower than the current count and missing an interrupt.
There's nothing that's particularly timing sensitive in my loop, but running the calculations in the ISR makes the ISR in accelerating and decelerating states well over 100us. But that get's my new TOP number set as early as possible.
Any suggestions? Or are there situations where long ISRs are OK.
If there's only one ISR in the system, then you only have to worry about it locking out the main program, but its still a situation best avoided really.
If you show your code it might suggest an alternate approach to what you are trying to achieve.
If there's only one ISR in the system, then you only have to worry about it locking out the main program, but its still a situation best avoided really.
It also blocks serial data reception/transmission, if any, other external interrupts, if any, timers, if any are used, and a variety of other things. None of which might matter, but we'd need to see the code to be sure.
You can handle the ugly computation parts off-interrupt by setting a flag inside the ISR, then handling the lower priority parts in the loop(). When you're done, disable interrupts, clear the flag, then re-enable interrupts.
And yes, long ISRs are evil. Especially on a platform without multiple level interrupts, nested interrupts, etc.
You can re-enable interrupts in your ISR to allow other background operations to happen. That should not cause a problem unless the current ISR takes longer than the interrupt interval that triggers it. Does the interrupt ever happen again in less than 100 microseconds?
jfhaugh:
You can handle the ugly computation parts off-interrupt by setting a flag inside the ISR, then handling the lower priority parts in the loop(). When you're done, disable interrupts, clear the flag, then re-enable interrupts.
And yes, long ISRs are evil. Especially on a platform without multiple level interrupts, nested interrupts, etc.
The thing I'm scared of is the interrupt firing twice before the loop has got back around to handle calculating the new value to put in OCR1A for the top. Or if the interrupt fires and then the loop is too late getting OCR1A reset and ends up setting it to a value lower than the current count.
Sorry guys, I don't have any code to show you. Well, I guess I could post the bit that sends the pulse train. Right now it's just various functions in various programs that haven't been synthesized into any usable code.
I just forsee this problem and was looking for a way around.
I have a byte that stores the bit pattern for the pins in PORTD.
First it applies the value to the port to set the pins.
Then it gets the new value for next time. This just means recording the MSB, shifting the byte by 1, then putting the old MSB into the LSB. That part is more than fast enough to happen in the ISR.
Under acceleration or deceleration, I am going to have to change OCR1A that I am using to set the top value for the timer. So I am looking at the accel rate in RPM / min and the length of time since the last interrupt (just the current OCR1A value)and figure how to set the OCR1A right. While the motor is running slower, there is longer between two interrupts and to get the same accel rate I have to make bigger changes to the top number. Bigger steps. When the motor is running fast, then I am only making small changes.
It's doing this that requires I either do a couple of division steps, or use floating point maths. Either of those will push the length of the ISR to being too long, but it is critical that it gets done right away while the counter is still low.
Like I said, this isn't any written code, I am just trying to get my head around how to move it out of the ISR or how to do the math faster. I have an accel rate in RPM/sec and I have the length of the last interrupt interval in ms. I can use floating math and keep up with RPM/ms and then I could just multiply by the length of the ISR. I can convert the RPM/sec number into just about anything. I've tried thinking of it in terms of the number of us between decreasing the OCR1A by 1, but that will require I divide the number of us since the last interrupt by that number. I've thought of lots of different ways to get there mathematically, but any way I do it I need either division steps or floating point maths.
You can do it with multiplication -- multiply by the reciprocal times some big number. Let's say you're dividing by 12.34. Pick a big number, like 65536 and get 5,311.
To divide 9,876 by 12.34 you multiply 9876 by 5311 and that yields 52,451,436. Now divide that by 65536 (shift by 16, which is fast ...) and you get 800. Checking the floating point math, 9876.0 / 12.34 = 800.324.
The only restrictions are that the product of the multiplier and multiplicand must always fit in whatever size variable you're using and the divisor must be a constant one way or the other.
jfhaugh:
You can handle the ugly computation parts off-interrupt by setting a flag inside the ISR, then handling the lower priority parts in the loop(). When you're done, disable interrupts, clear the flag, then re-enable interrupts.
And yes, long ISRs are evil. Especially on a platform without multiple level interrupts, nested interrupts, etc.
The thing I'm scared of is the interrupt firing twice before the loop has got back around to handle calculating the new value to put in OCR1A for the top. Or if the interrupt fires and then the loop is too late getting OCR1A reset and ends up setting it to a value lower than the current count.
What Bad Thing happens in those cases?
You can't always be perfect with real-time programming. You have to know what kinds of things are going to happen if you miss a deadline. If it's a "bad thing", you have to figure out how to give yourself more time, or how to stretch out the interval between actions.
One approach is to calculate (in loop) the value to be used in the next ISR. In other words, always be one out. So each ISR you update the timer for the value calculated the previous time around. That way the calculations are outside the ISR.