I am trying to design and build a simple servo reverser for RC applications. I am using Atmel microcontrollers and write the code in Arduino IDE. This simplifies program uploading to the device for me.
As you already know, RC systems work with PWM pulses. They are usually 500 - 2000 microsecond pulses repeated every 20 milliseconds. 1500 microseconds represents the neutral position of the servo. If we measure the length of the input pulse, we can subtract it from 3000 and this will give us the reverse signal pulse. That means the mirror position of the servo arm with respect to the neutral position.
To have a usable device we need at least 1 microsecond resolution. The more the better.
Reading a pulse width can of course be done in ArduinoIDE using the pulseIn() function. But this function has a resolution of 4 microseconds which is too coarse for this kind of application. Output pulse generation can be done with delayMicroseconds() function but this too has a 4 microseconds resolution.
Reading and generating the pulses must be done precisely to avoid servo jitter which makes the circuit unusable in a real RC model.
Using an ATMega168/328 for such a job seems to be an overkill. In fact an ATTiny45/85 would be the best solution but with only 8 bit timer counters this chip makes the precise pulse measurement and generation very difficult. So I decided to try it with an ATTiny2313 which has a 16 bit timer counter.
To prevent issues with Arduino IDE background functions (like millis(), micros(), etc.) I decided to stop the unused Timer0 and inhibit all interrupts.
I decided to run the MCU at 8 MHz internal clock with Timer1 prescaler of “1”. This gives me a signal resolution of 125 nanoseconds.
(That means a pulse of 1500 microseconds duration is being represented as 8 * 1500 = 12000 internally).
First I tried the signal generation part. I assigned a fixed value to the variable “inPWM” and produced output pulses. A servo connected to the circuit stayed at the appropriate position and was rock solid.
Then I moved on to the signal input. There was no problems. The servo followed the commands from the radio transmitter precisely.
Then I added the buttons and the code for offset adjustment. This function is required for the centering of the reversed slave servo which is a very handy function in real life. This worked also flawlessly.
But...
As I said before, to get the reverse pulse width we must subtract the actual input PWM value from 3000. As we are using 8 MHz this 3000 will be 24000. That means;
revPWM = 24000 – inPWM; (All PWM variables are unsigned integers btw.)
Then we will add the offset to get the final value; revPWM = revPWM + offset
Now my problem: If I use the inPWM value to generate an output pulse the servo works fine without jitter. But if I use the calculated revPWM value, there is a significant jitter.
I could not find a logical explanation for this behaviour and would like to ask whether I am missing something.
What I tried so far:
*Using a 16 MHz crystal and running the system with external clock.
*Eliminating the offset system, EEPROM reads and writes, buttons.
*Reading the incoming pulse using pin change interrupts.
Thank you...
As you already know, RC systems work with PWM pulses. They are usually 500 - 2000 microsecond pulses repeated every 20 milliseconds. 1500 microseconds represents the neutral position of the servo. If we measure the length of the input pulse, we can subtract it from 3000 and this will give us the reverse signal pulse. That means the mirror position of the servo arm with respect to the neutral position.
To have a usable device we need at least 1 microsecond resolution. The more the better.
Reading a pulse width can of course be done in ArduinoIDE using the pulseIn() function. But this function has a resolution of 4 microseconds which is too coarse for this kind of application. Output pulse generation can be done with delayMicroseconds() function but this too has a 4 microseconds resolution.
Reading and generating the pulses must be done precisely to avoid servo jitter which makes the circuit unusable in a real RC model.
Using an ATMega168/328 for such a job seems to be an overkill. In fact an ATTiny45/85 would be the best solution but with only 8 bit timer counters this chip makes the precise pulse measurement and generation very difficult. So I decided to try it with an ATTiny2313 which has a 16 bit timer counter.
To prevent issues with Arduino IDE background functions (like millis(), micros(), etc.) I decided to stop the unused Timer0 and inhibit all interrupts.
I decided to run the MCU at 8 MHz internal clock with Timer1 prescaler of “1”. This gives me a signal resolution of 125 nanoseconds.
(That means a pulse of 1500 microseconds duration is being represented as 8 * 1500 = 12000 internally).
First I tried the signal generation part. I assigned a fixed value to the variable “inPWM” and produced output pulses. A servo connected to the circuit stayed at the appropriate position and was rock solid.
Then I moved on to the signal input. There was no problems. The servo followed the commands from the radio transmitter precisely.
Then I added the buttons and the code for offset adjustment. This function is required for the centering of the reversed slave servo which is a very handy function in real life. This worked also flawlessly.
But...
As I said before, to get the reverse pulse width we must subtract the actual input PWM value from 3000. As we are using 8 MHz this 3000 will be 24000. That means;
revPWM = 24000 – inPWM; (All PWM variables are unsigned integers btw.)
Then we will add the offset to get the final value; revPWM = revPWM + offset
Now my problem: If I use the inPWM value to generate an output pulse the servo works fine without jitter. But if I use the calculated revPWM value, there is a significant jitter.
I could not find a logical explanation for this behaviour and would like to ask whether I am missing something.
What I tried so far:
*Using a 16 MHz crystal and running the system with external clock.
*Eliminating the offset system, EEPROM reads and writes, buttons.
*Reading the incoming pulse using pin change interrupts.
Thank you...
C-like:
/*
Servo Reverser with subtrim
ATTiny2313 @ 8 MHz @ 3.3V
Siignal input PB5
Reverse output PB3
Buttons PD0 (+), PD1 (-)
*/
#include <EEPROM.h>
#define pwmIn PB5
#define revOut PB3
#define button1 PD0
#define button2 PD1
#define revOutMaskHIGH B00001000
#define revOutMaskLOW B11110111
#define inMask B00100000
#define buttonMask B00000011
int offset; // -1000 ... 1000
unsigned int inPWM;
unsigned int revPWM;
byte buttonIn;
unsigned int tempOffset;
boolean EEPROM_flag;
void setup() {
DDRB = B00001000; // PB3 OUTPUT
DDRD = B00000000; // PD... INPUT
PORTD |= B00000011; // PD0 PD1 INPUT_PULLUP
TCCR0B = B00000000; // Timer0 stop
TCCR1A = B00000000;
TCCR1B = B00000001; // Timer1 prescaler 1, runs at 8 MHz
TCCR1C = B00000000;
TCCR1B = B00000000; // Timer1 stop
if ((EEPROM.read(0) == 255) && (EEPROM.read(1) == 255)) { // Provision for a blank EEPROM
EEPROM.write(0, 3);
EEPROM.write(1, 232); // value = 1000, effective offset = 0
}
offset = 256 * EEPROM.read(0) + EEPROM.read(1) - 1000;
if (offset > 1000) {
offset = 1000;
}
if (offset < -1000) offset = -1000;
tempOffset = offset + 1000;
EEPROM.update(0, highByte(tempOffset));
EEPROM.update(1, lowByte(tempOffset));
cli();
}
void loop() {
//Read incoming pulse
GTCCR |= B00000001; // Reset prescalers
TCNT1 = 0;
TCCR1B = B00000000; // Timer1 stop
while (!(PINB & inMask))
; // Wait until HIGH pulse start
TCCR1B = B00000001; // HIGH pulse started, Timer1 prescaler 1, runs at 8 MHz
while (PINB & inMask)
; // Wait until pulse end
TCCR1B = B00000000; // Timer1 stop
inPWM = TCNT1;
//Check values
revPWM = 24000 - inPWM;
revPWM = revPWM + 8 * offset;
if (revPWM > 20000) revPWM = 20000;
if (revPWM < 4000) revPWM = 4000;
// Output reverse pulse
TCCR1B = B00000000; // Timer1 stop
GTCCR |= B00000001; // Reset prescalers
TCNT1 = 0;
PORTB |= revOutMaskHIGH;
TCCR1B = B00000001; // Timer1 prescaler 1, runs at 8 MHz
while (TCNT1 < revPWM)
;
PORTB &= revOutMaskLOW;
// Check buttons
buttonIn = PIND & buttonMask;
if (buttonIn == 1) {
offset++;
EEPROM_flag = 1;
}
if (buttonIn == 2) {
offset--;
EEPROM_flag = 1;
}
if (offset > 1000) offset = 1000;
if (offset < -1000) offset = -1000;
tempOffset = offset + 1000;
if (EEPROM_flag) {
EEPROM.update(0, highByte(tempOffset));
EEPROM.update(1, lowByte(tempOffset));
EEPROM_flag = 0;
}
}

