For my ROV project I need to control an RC servo to tilt the camera up and down. I've gotten that working nicely using a 16-bit timer to generate an appropriate PWM waveform.
But when I change the the position by a lot – for example, going from one endpoint to the middle position – the servo moves too fast. (Which, incidentally, draws a huge current which makes my 5 V rail drop enough to reset the MAX7456 – but that is fixable.) So I need to slow the movement down by gradually changing the position instead of jumping straight from one position to another.
The full servo movement corresponds to a change in timer match value by 1000 (the PWM timer is set up so the match values correspond to PWM pulse on-time in microseconds so minimum is 1000 and maximum is 2000). I would like the full range of movement to take one second so I thought that changing the position in increments of 10 at a frequency of 100 sounds about right.
Unfortunately, I'm fresh out of timers: CT32B0 and -1 are used for motor control PWM, CT16B0 is used for periodic analog-to-digital conversions (for the pressure sensor used as depth gauge) and CT16B1 is used for servo PWM. Yeah, sure, I guess I could use a timer for more than one purpose but since the LPC1347 actually has one more timer, I might as well use that: the Repetitive Interrupt Timer.
The Repetitive Interrupt Timer (RIT for short) generates interrupts every X clock cycles and if I configure the timer to fire an interrupt every 720,000 clock cycles I get a 100 Hz interrupt (on a 72 HMz PCLK) in which I can move the servo if necessary. Nice.
This is the code I came up with: Repetitive Interrupt Timer on LPC1347
// MIN and MAX macros #ifndef MIN #define MAX(a,b) (((a)>(b))?(a):(b)) #define MIN(a,b) (((a)<(b))?(a):(b)) #endif // Servo position variables volatile uint16_t servo_position; volatile uint16_t servo_target; // Stop RI timer – otherwise we can't reset the counter LPC_RITIMER->CTRL = 0; // Init RI timer (we don't need to enable timer – it is always running as per the updated User Manual UM10524) LPC_RITIMER->COMPVAL = 720000; LPC_RITIMER->COMPVAL_H = 0; // Reset counter (otherwise it might already be above the compare value and then it would take a loong time to overflow to zero) LPC_RITIMER->COUNTER = 0; LPC_RITIMER->COUNTER_H = 0; // Set RITENCLR (clear on match), RITENBR (halt timer on debug) and RITEN (start timer) LPC_RITIMER->CTRL = (1<< 1) | (1<< 2) | (1<< 3); // Enable RIT interrupt NVIC_EnableIRQ( RIT_IRQn ); // Interrupt routine // (Yes, it's a little weird that the interrupt routine is called OSTIMER but the NVIC interrupt is called RIT_IRQn) void OSTIMER_IRQHandler() { // Clear interrupt LPC_RITIMER->CTRL |= (1<< 0); // Write RITINT to clear // If we're at target, do nothing if( servo_position == servo_target ) return; if( servo_target > servo_position ) servo_position += MIN( servo_target - servo_position, 10 ); else // Not equal and not greater than, so it's less than – we don't need to check servo_position -= MIN( servo_position - servo_target, 10 ); }
// Reset counter (otherwise it might already be above the compare value and then it would take a loong time to overflow to zero)
LPC_RITIMER->COUNTER = 0;
LPC_RITIMER->COMPVAL_H = 0; <-----should this be COUNTER_H???