I have a servo that has to hold a set position. At the same time I need to run a stepper motor. At certain speeds the motor pulses fall in time with the servo pwm output (or close enough that the motor ISR is still running when the servo timer is due). This causes the servo output to be delayed, and results in bad oscillation as the servo is seeing a changing duty cycle.
I'm thinking of using a second Arduino to just run the servo and so avoid the issue. Before I do that I was wondering if there are any approaches that could get around this issue whilst staying with a single board?
Thanks for the input, it's not a supply issue. The servo and motor are on two independent supplies.
I have checked with a logic analyser, the higher priority timer (running on Timer1) pushes the lower priority pulse off by the amount of time that the ISR runs for. The motor ISR does some calculation and takes ~20us to run, so if the servo pulse is due within that time it gets delayed and hence the servo moves due to the fluctuating duty cycle.
Really it boils down to is there a way to fire interrupt B whilst interrupt A is consuming the processor's time.
If you think the problem is with the code, would it not be a good idea to post that code?
Please use code tags.
is there a way to fire interrupt B whilst interrupt A is consuming the processor's time
Yes, for advanced users, but "interrupt A" should consume a vanishingly small amount of the processor's time. If "interrupt A" IS consuming a significant amount of the processor's time, that is the problem.
Would that be using a Timer interrupt to toggle a pin rather than trigger an ISR? If so is that guaranteed to run on time even if my other ISR is running when it is due?
A much better idea is to take that calculation out of the motor ISR. Don't forget that variables shared between the main program and an interrupt routine must be declared volatile.
If a shared variable is multibyte, you must prevent an interrupt from modifying that variable, while it is being accessed by the main program.
I took the easy route and used the Servo library, which does use interrupts.
I've had a look at generating the PWM and it's all over my head. I can get a 50Hz frequency which the servo needs but I cannot work out how to vary the duty cycle.
I would love to have the time to learn how to do it properly, but right now I just need a solution - so I'm buying a second board and that can just sit there running the Servo library without any possible interference from my code.
If anyone does feel like providing code to run on a Mega using Timer5, without an ISR and which can set a servo to 90deg, 77deg and 103deg I would of course be very happy to use it
Here's an example for the Arduino Uno that outputs 50Hz PWM signals on D9 and D10 using timer1:
// Set-up hardware PWM on the Arduino UNO at 50Hz on digital pins D9 and D10
void setup() {
// Initialise timer 1 for phase and frequency correct PWM
pinMode(9, OUTPUT); // Set digital pin 9 (D9) to an output
pinMode(10, OUTPUT); // Set digital pin 10 (D10) to an output
TCCR1A = _BV(COM1A1) | _BV(COM1B1); // Enable the PWM outputs OC1A, and OC1B on digital pins 9, 10
TCCR1B = _BV(WGM13) | _BV(CS11); // Set phase and frequency correct PWM and prescaler of 8 on timer 1
ICR1 = 19999; // Set the PWM frequency to 50Hz
OCR1A = 1500; // Centre the servo on D9
OCR1B = 1500; // Centre the servo on D10
}
void loop() {
OCR1A = 1000; // Move the servo to min position on D9
OCR1B = 1000; // Move the servo to min position on D10
delay(1000); // Wait for 1 second
OCR1A = 2000; // Move the servo to max position on D9
OCR1B = 2000; // Move the servo to max position on D10
delay(1000); // Wait for 1 second
}
It's working in wokwi simulator, I figured out the pin for the Mega (9 is 11 on there). Seems that there's more complexity than I knew about as only certain pins work from certain timers, I also hadn't come across the ICR register. I was reading the docs just now and my head was spinning, this has really helped.
@gibbobaz If you'd prefer to use the Mega's timer5, output channels A, B & C are on digital pins 46, 45 and 44:
TCCR5A = _BV(COM5A1) | _BV(COM5B1) | _BV(COM5C1); // Enable the PWM outputs OC5A, OC5B and OC5C on digital pins 46, 45 and 44
TCCR5B = _BV(WGM53) | _BV(CS51); // Set phase and frequency correct PWM and prescaler of 8 on timer 5
I'm happy to report that removing the Servo library and using the code kindly provided by @MartinL has fixed the issues I had. The motor acceleration now runs smoothly and the servo holds it's position without oscillating.