-
Xoaquin Castrelo authoredXoaquin Castrelo authored
servos.cpp 3.19 KiB
#include "servos.hpp"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#define MIN_PULSE 999
#define MAX_PULSE 4999
static void reorderServos();
static void initTimer1();
struct Servo
{
volatile uint8_t * const port; // the PORT register connected to the servo
volatile uint8_t * const ddr; // the DDR register connected to the servo
const uint8_t pinMask; // the pin mask for the servo
uint16_t tempValue; // stores a temporary value before it's written to the servo
uint16_t value; // current pulse width value
int16_t angle; // the angle in arc-minutes
};
static Servo servos[NUM_SERVOS] = {
{ &PORTC, &DDRC, (1 << PB0), 0, 0 },
{ &PORTC, &DDRC, (1 << PB1), 0, 0 },
{ &PORTC, &DDRC, (1 << PB2), 0, 0 }
};
static uint16_t orderedValues[NUM_SERVOS]; // current servo values in ascending order
static uint8_t orderedValueIndex = 0; // index in orderedValues array
static bool updateFlag = false; // used to flag when the servo values have been updated
void initServos()
{
for (Servo &servo : servos)
{
*servo.ddr |= servo.pinMask;
}
initTimer1();
}
void setServoAngle(uint8_t servo, int16_t arcMin)
{
if (0 <= servo && servo < NUM_SERVOS)
{
if (arcMin < -180 * 60)
arcMin = -180 * 60;
else if (arcMin > 180 * 60)
arcMin = 180 * 60;
servos[servo].tempValue = MIN_PULSE + (int32_t) (arcMin + 180 * 60) * (MAX_PULSE - MIN_PULSE) / (360 * 60);
servos[servo].angle = arcMin;
}
}
int16_t getServoAngle(uint8_t servo)
{
if (0 <= servo && servo < NUM_SERVOS)
return servos[servo].angle;
else
return 0;
}
void updateServos()
{
updateFlag = true;
}
static void reorderServos()
{
uint16_t minValue, maxValue = 0;
for (Servo &servo : servos)
servo.value = servo.tempValue;
for (uint16_t &value : orderedValues)
{
minValue = (uint16_t) -1;
for (Servo servo : servos)
{
if (servo.value < minValue && servo.value > maxValue)
minValue = servo.value;
}
maxValue = minValue;
value = maxValue;
}
}
static void initTimer1()
{
cli();
TCCR1A = 0;
TCCR1B = (1 << WGM12) // CTC mode, TOP = OCR1A
| (1 << CS11); // 8th prescaler
TCCR1C = 0;
OCR1A = 39999; // set TOP for 50 Hz
TIMSK1 = (1 << OCIE1A) | (1 << OCIE1B); // enable interrupts
sei();
}
ISR(TIMER1_COMPA_vect)
{
if (updateFlag)
{
updateFlag = false;
reorderServos();
}
for (Servo &servo : servos)
*servo.port |= (servo.pinMask); // set servo pin HIGH
orderedValueIndex = 0;
OCR1B = orderedValues[orderedValueIndex];
}
ISR(TIMER1_COMPB_vect)
{
uint16_t currValue = orderedValues[orderedValueIndex];
for (Servo &servo : servos)
{
if (servo.value == currValue)
*servo.port &= ~(servo.pinMask); // set servo pin LOW
}
orderedValueIndex++;
if (orderedValueIndex < NUM_SERVOS)
OCR1B = orderedValues[orderedValueIndex];
}