Reset a timer at the end of the ISR for that timer.

Hello,

Currently I am working on making a robot drive platform (2 stepper motors) with the Arduino using the AccelStepper library and some step/dir drivers. In the beginning things were going well with it just riding the main program loop and I was able to reach speeds close to 4000KHz, which was realy overkill given the torque lost at high RPM. Now however the code is getting a little less speedy and I have several conditions that need to be addressed beyond just running the motors. The biggie is I2C commands that need to be caught and evaluated and some need to return the status back over I2C. The I2C write/read is not the issue. The time spent in code doing stuff with the data is. At higher pulse rates your can 'hear' the commands being processed as little glitches in the motor. At over 2300KHz, in full-step mode, they can cause a no-load stall if they happen just so.

Anyway to get around that I thought it would be better to setup a timer and interrupt every say 250 to 200 micros and run the steppers using that. The I2C command processing being lower priority anyway then just gets the leftovers and that's ok. In implementation however the code fails to reach speeds much over 2KHz no matter how short the interval and eventually of course it just sputters and dies since the the stepper.run(); function is expensive when it calculates the next step with acceleration.

The only reason I can think of for this is that in the loop, even though the overall repeat rate is not quite as fast the majority of the loop is consumed by the run() function and then its right back to it again in short order but the time taken by run() is always accounted for. In an interrupt the next ISR call is ticking away even as the code is still trying to step so that you can't set it too low or the ISR starts running over itself. What would be best I think is if I could instead stop the timer at the start of the ISR, do run() for both motors, and then reset the timer and restart it. Then in another 150 micros we fire off the ISR again. As I recall from the logic analyzer the two run() functions take about ~100 usecs to generate the two pulses at speed but there may be a little more overhead there than I can see just by the offset from the start of one to the end of the other.

So long story short. Can the timer be made to work this way? I'm currently useing the TimerOne library and it's really setup to simply count to X, fire ISR, repeat. Looking at the datasheet on the timers I was still kind of scratching my head a bit. If I could get up to 2500 KHz out of these thats great. Any faster is largely useless for propulsion as the torque drop off will too much for even my belt-reduction plans to prop-up.

Photomankc:
In the beginning things were going well with it just riding the main program loop and I was able to reach speeds close to 4000KHz, which was realy overkill given the torque lost at high RPM.

What was turning at a speed of 4 MHz? The processor itself only runs at 16 MHz, and some instructions take 2 or 3 clock cycles.

At higher pulse rates your can 'hear' the commands being processed as little glitches in the motor.

Perhaps if you post your code? This might happen if you try to do stuff in the ISR, but it sounds like the I2C stuff isn't critical.

Looks like the ISR is supposed to interrupt at 4000 KHz by running a 250 microsec wait. I think that's 4000 Hz, not 4000 KHz. Maybe one of your programs is giving you inflated feedback?

16 Mhz CPU / 4 KHz is 4000 CPU cycles between interrupts for both ISR and program run time. Call it an interrupt cycle budget. How do you spend it?

Are you using floating point anywhere? Doing squares and roots? Trig? Any math-large steps that can be pre-calculated and tabled that are not? What is taking up a lot of cycles? Even an analog read "takes about 100 microseconds" in Arduino reference but according to ATMEL it's 13 - 260?s. 260 microsecs with an interrupt set to trigger in 250?

"At over 2300KHz, in full-step mode, they can cause a no-load stall if they happen just so."
You mean 2300 Hz? That's 435 microsecond interrupt cycle. What did you use to get 2300 KHz?
If you started noticing trouble with a 435 micro wait between interrupts then maybe 500 should run stable?
That's 2 KHz.

Oh good grief!!! My appologies. Of course 4000KHz is retarded. 4000Hz that shoud be. I’ll post up the code here shortly. A little time spent with the scope and the ISR version showed it’s not really working at all. Some speeds come out right some are way off and towards the higher end, past 1500Hz the speed doesn’t change at all till you add hundreds more Hz to setMaxSpeed and it takes a period of 100 microseconds on the Timer to get that good. Using just the loop is getting pretty good speeds up to 1800 and after that the accuracy of the speed drops off badly. 2000 is useable though.

The current code is running in the loop but you can see the TimerOne functions commented out in the setup.

@GoForSmoke - The 2300Hz comes from setting the setMaxSpeed function to 2300 which is the pulses per second it should generate assuming the run function is called frequently enough to do it. Reducing the pulse rate reduces the chances of a command colliding with a pulse but does not eliminate the problem so long as the I have to step away from the loop to process the command. Status is one that I can’t ignore as it’s designed to let the I2C master know certain important state values. Using the ISR the commands never get in the way but the period has to very short for to get even a good 1000Hz rate from run(). It’s got to have to do with the fact that time is relative in the loop and fixed in the ISR.

RobotDrive0_1_6.pde (23.3 KB)