There are several (three) pulse-output encoders that should be measured with the least amount of overhead possible. Their outputs are independent, with 0-3ms widths depending on rotation angle; most of the time spent in the 0-1ms region. Refresh rate is roughly 250Hz, but varies for each encoder. Each encoder is connected to an interrupt pin of a 1281e.
Currently the primary PID loop in main is waiting for these measurements using Pulsin(), but such blocking code is very inefficient, and results in limited 70-90hz main loop performance. This is far from the theoretical 250Hz that could be achieved if the encoders were updated separately, albeit not without phase lag, which is acceptable.
However it's not clear how this updating should be done. Ideally there are separate tasks for each encoder, waiting for interrupts from specific edges, storing timer2 values on consecutive transitions. This would not slow down the operation of main() which might arise from constant task switching.
Pulsin() for each encoder in various tasks doesn't work very well.
Anyone have thoughts?
EDIT: the TMR2 prescaler seems by default set to 8, so it will need to be changed so that TCNT2 can be used for these long pulses. However changing CS2_i in TCCR2B will effect the rest of the system, so this may not be the best solution.
Measure several pulses with interrupts
Re: Measure several pulses with interrupts
You have other timers on the ZX-1281 that could be used. For example, Timer5 could be set to free-run at a convenient frequency. Then, each of the encoder outputs could be connected to a different hardware interrupt input with a separate task awaiting the leading edge. When the leading edge occurs, the value of Register.TCNT5 would be saved and then the task would await the trailing edge. Upon the occurrence of the trailing edge, Register.TCNT5 would be read again. The difference between the two readings, taking a possible rollover into account, would represent the pulse width.pjc30943 wrote:There are several (three) pulse-output encoders that should be measured with the least amount of overhead possible.
- Don Kinzer
Re: Measure several pulses with interrupts
With 3 channels at 250Hz, this means the ZVM has to handle 1500 (3 * 250 * 2) interrupts per second. I think that the issue is whether the ZVM is fast enough to handle this and still have a main loop that can operate at the required speed.dkinzer wrote:You have other timers on the ZX-1281 that could be used. For example, Timer5 could be set to free-run at a convenient frequency. Then, each of the encoder outputs could be connected to a different hardware interrupt input with a separate task awaiting the leading edge. When the leading edge occurs, the value of Register.TCNT5 would be saved and then the task would await the trailing edge. Upon the occurrence of the trailing edge, Register.TCNT5 would be read again. The difference between the two readings, taking a possible rollover into account, would represent the pulse width.
Rather than time each pulse to understand the speed to rotation, perhaps it might be faster to simply count the pulses (half the interrupts and half the arithmetic). Then every so often (e.g. each main loop iteration) the speed can be averaged as the number of counts since the last time. A public function in the interrupt task module can be used to calculate the average speed and then the count reset. A semaphore may be needed between the counter task and the average speed public function to protect the counter variable.
Mike Perks
Re: Measure several pulses with interrupts
That's true about the VM, and a valid point. However these are dutycycle outputs, vs. rate outputs. So counting pulses would always yield the same results.With 3 channels at 250Hz, this means the ZVM has to handle 1500 (3 * 250 * 2) interrupts per second. I think that the issue is whether the ZVM is fast enough to handle this and still have a main loop that can operate at the required speed.
Rather than time each pulse to understand the speed to rotation, perhaps it might be faster to simply count the pulses (half the interrupts and half the arithmetic). Then every so often (e.g. each main loop iteration) the speed can be averaged as the number of counts since the last time. A public function in the interrupt task module can be used to calculate the average speed and then the count reset. A semaphore may be needed between the counter task and the average speed public function to protect the counter variable.
Re: Measure several pulses with interrupts
Another approach is to use an R-C filter to convert the duty cycle to a representative voltage and use the ADC to read the voltage.pjc30943 wrote:[T]hese are dutycycle outputs, vs. rate outputs.
- Don Kinzer
The answer: the VM cannot keep up.
But the loop frequency improved by using TMR5 and interrupts, though not by as much as hoped for. The main loop is now somewhere between 90-150Hz, with the rate varying, depending on the varying sequence of the encoder pulses.
Each encoder task is not able to keep up; with just one encoder task running, each new encoder pulse is correctly measured. With two tasks, 30% of the time pulses are missed. With three, about half the pulses are skipped.
It seems it should be able to speed up the main loop, since the 1281 still has some resources not yet exploited.
If it was possible to add inline assembly or assembly ISRs...
An RC filter is possible, but an external ADC would have to be added to gain the 12 bits accuracy currently obtained by the pulsed outputs. With temperature and time effects, etc., an RC filter with such accuracy would not be as fun.
Another solution would be to just add another ZX, and talk via SPI; there's a high speed SPI port in use on this 1281 to output voltages to DACs. The second ZX could gather data continually, and transmit when desired. However this solution is awkward and not elegant, and is not preferable due to the complexity it adds to the system. At that pont it'd be faster to just move over to another processor, heaven fordid
But the loop frequency improved by using TMR5 and interrupts, though not by as much as hoped for. The main loop is now somewhere between 90-150Hz, with the rate varying, depending on the varying sequence of the encoder pulses.
Each encoder task is not able to keep up; with just one encoder task running, each new encoder pulse is correctly measured. With two tasks, 30% of the time pulses are missed. With three, about half the pulses are skipped.
It seems it should be able to speed up the main loop, since the 1281 still has some resources not yet exploited.
If it was possible to add inline assembly or assembly ISRs...
An RC filter is possible, but an external ADC would have to be added to gain the 12 bits accuracy currently obtained by the pulsed outputs. With temperature and time effects, etc., an RC filter with such accuracy would not be as fun.
Another solution would be to just add another ZX, and talk via SPI; there's a high speed SPI port in use on this 1281 to output voltages to DACs. The second ZX could gather data continually, and transmit when desired. However this solution is awkward and not elegant, and is not preferable due to the complexity it adds to the system. At that pont it'd be faster to just move over to another processor, heaven fordid
maybe a little 8 pin AVR dedicated to the task, coded in C or ASM, talking to the ZBasic chip via SPI or UART or whatever. Complex application in ZBasic, barebones high speed I/O on the itty guy.
I have a thing that runs about 1200 interrupts per second and it struggles, with these plus the clock. My application ignores the external device most of the time. I should but don't, shut off interrupts when not needed. But I'm not trying for high precision, just above/below a threshold, with some hysteresis.
I have a thing that runs about 1200 interrupts per second and it struggles, with these plus the clock. My application ignores the external device most of the time. I should but don't, shut off interrupts when not needed. But I'm not trying for high precision, just above/below a threshold, with some hysteresis.