Servo Reverse with ATTiny

This code (for ATTiny microcontrollers) shows how to reverse a PPM servo signal. Feel free to use it in your own servo reverse controller.

// Reverses servo signal
//   Tiny13 pinout:
//   PB5 – /RESET  / PCINT5
//   PB3 – ADC3 – used to configure center point / PCINT3
//   PB4 – Servo Output  / PCINT4
//   PB2 – SCK  / PCINT2
//   PB1 – MISO / PCINT1
//   PB0 – Servo Input / MOSI / PCINT0
// AT Tiny13 Specs:  http://www.atmel.com/Images/doc2535.pdf
// flashing with avrdude:
//    avrdude -F -c stk500v2 -p t13 -P com10 -U flash:w:servo.hex

#include <avr/io.h>
// #include <avr/delay.h>
#include <avr/interrupt.h>

// CPU speed (Hz)
#define F_CPU 4800000L

volatile static uint8_t in_time  = 0;
volatile static uint8_t out_time = 0;

// read ADC channel
// http://www.rn-wissen.de/index.php/ADC_(Avr)
uint16_t adc(void)
{

ADCSRA |= (1<<ADSC); // start conversion
while (ADCSRA & (1<<ADSC)); // wait to finish conversion
uint16_t val = ADCW;
return val;

}

volatile static uint8_t in_start = 0;
volatile static uint8_t timer = 0;

// input ppm pin signal change interrupt
ISR(PCINT0_vect)
{

timer = TCNT0;
if (PINB & (1<<PB0)) {

// low->high
in_start = timer;

} else {

// high->low
in_time = timer – in_start;

}

}

volatile static uint8_t sethigh = 1;

// timer comparator interrupt
ISR(TIM0_COMPA_vect)
{

if (sethigh == 1){

PORTB |= (1<<PB4); // set output port high
OCR0A += out_time;

} else {

PORTB &= ~(1<<PB4); // set output port low
OCR0A=TCNT0-1;  // wait max. possible time for next pulse

}

sethigh = !sethigh;

}

int main (void)
{

// configure ADC
ADMUX &= ~(1<<REFS0); // voltage reference (VCC)
ADMUX |= (1<<MUX1) | (1<<MUX0); // choose ADC3 mux
ADCSRA = ( 1 << ADPS2 );  // ADC speed (clk/16)
ADCSRA |= (1<<ADEN);  // enable ADC

// configure timer0 (8bit)
// used to control servo pulse length
TCCR0A = 0;                        // normal operation
TCCR0B = (1 << CS01) |  (1 << CS00);     // timer speed clk/64  (75 kHz)
TIMSK0 |= (1 << OCIE0A);  // enable timer comparator interrupt
//TIMSK0 |= (1 << TOIE0);   // enable timer overflow interrupt

TCNT0 = 0;
OCR0A = 255;

// configure I/O pins
DDRB |= (1<<PB4);  // PB4 as output
DDRB &= ~(1<<PB0);  // PB0 as input

// configure PCINT0 change interrupt
PCMSK |= (1 << PCINT0);
GIMSK |= (1 << PCIE);

sei();      // enable interrupts*/

// no-reverse ppm test
/*while (1){
PORTB = (PORTB & (~(1<<PB4))) | ((PINB & (1<<PB0)) << PB4);
} */

// idea used:

http://homepages.which.net/~paul.hills/Circuits/ServoReverser/ServoReverser.html

while (1){

int16_t adcofs = ((int16_t) adc() – 512) >> 4;

// 225 tt entspricht 3ms
// weight time values:
//    new value = x* last value + y* new value,
//    x+y=1

out_time = (((uint16_t)out_time) *2 + ((uint16_t)225-in_time +adcofs) * 1) / 3;

//delay_ms(50);

}

}

Download code:  servoreverse.zip

Leave a Reply

Your email address will not be published. Required fields are marked *

IMPORTANT! To be able to proceed, please enter the magic word 'ndgeaa' so we know hat you are a human)

Enter the magic word:
Please leave these two fields as-is:

A blog on projects with robotics, computer vision, 3D printing, microcontrollers, car diagnostics, localization & mapping, digital filters, LiDAR and more