2013-12-17 20:48:07 +01:00
|
|
|
#include "servos.h"
|
|
|
|
#include "hal.h"
|
2013-12-27 20:16:04 +01:00
|
|
|
#include "roverif.h"
|
2013-12-17 20:48:07 +01:00
|
|
|
#include <avr/io.h>
|
|
|
|
#include <avr/interrupt.h>
|
|
|
|
|
2013-12-27 21:35:37 +01:00
|
|
|
/* The timer runs pretty fast to get good enough resolution. The
|
|
|
|
prescaler has limited steps, meaning that a 2.0 ms pulse gets very
|
|
|
|
close to overflowing an eight bit counter and would need the first
|
|
|
|
compare to fire very shortly after the start of the cycle. Instead
|
|
|
|
split the pulse into two overflows and run two at once to get a
|
|
|
|
fast enough update rate.
|
|
|
|
|
|
|
|
So:
|
|
|
|
* Run two channels at once.
|
|
|
|
* Use OCR0A for channel 0.
|
|
|
|
* Use OCR0B for channel 1.
|
|
|
|
* On overflow, schedule the rising edge.
|
|
|
|
* On mid overflow, schedule the falling edge.
|
|
|
|
*/
|
|
|
|
|
2013-12-17 20:48:07 +01:00
|
|
|
const uint8_t Servos::pins_[] = {
|
2014-02-21 22:46:56 +01:00
|
|
|
_BV(0), _BV(1), _BV(2), _BV(3),
|
2013-12-17 20:48:07 +01:00
|
|
|
};
|
|
|
|
|
2013-12-27 20:16:04 +01:00
|
|
|
Servos::Servos()
|
2013-12-27 21:35:37 +01:00
|
|
|
: at_(0), state_(State::Start) {
|
|
|
|
|
|
|
|
static_assert(NumChannels % 2 == 0, "Need an even number of channels.");
|
|
|
|
|
2014-02-02 15:02:53 +01:00
|
|
|
for (volatile int8_t& position : position_) {
|
2013-12-27 20:16:04 +01:00
|
|
|
position = Mid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-27 21:35:37 +01:00
|
|
|
void Servos::init() {
|
2013-12-17 20:48:07 +01:00
|
|
|
for (uint8_t pin : pins_) {
|
2013-12-21 21:48:45 +01:00
|
|
|
DDRC |= pin;
|
2013-12-17 20:48:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
TCCR0A = 0
|
|
|
|
| (0 << COM0A0)
|
|
|
|
| (0 << COM0B0)
|
|
|
|
| (0 << WGM00)
|
|
|
|
;
|
|
|
|
|
2013-12-21 21:48:45 +01:00
|
|
|
static_assert(HAL::Prescaler == 64, "Prescaler disagrees.");
|
2013-12-17 20:48:07 +01:00
|
|
|
TCCR0B = 0
|
|
|
|
| (0 << WGM02)
|
2013-12-21 21:48:45 +01:00
|
|
|
| (3 << CS00)
|
2013-12-17 20:48:07 +01:00
|
|
|
;
|
|
|
|
|
|
|
|
TIMSK0 = _BV(OCIE0A) | _BV(OCIE0B) | _BV(TOIE0);
|
|
|
|
}
|
|
|
|
|
2014-02-02 15:02:53 +01:00
|
|
|
void Servos::set(uint8_t channel, int8_t position) {
|
2013-12-17 20:48:07 +01:00
|
|
|
position_[channel] = position;
|
|
|
|
}
|
|
|
|
|
2013-12-27 21:35:37 +01:00
|
|
|
inline void Servos::overflow() {
|
|
|
|
uint8_t at = at_;
|
2013-12-26 21:46:37 +01:00
|
|
|
|
2013-12-27 21:35:37 +01:00
|
|
|
if (state_ == State::Start) {
|
|
|
|
at += 2;
|
2013-12-26 21:46:37 +01:00
|
|
|
|
2013-12-27 21:35:37 +01:00
|
|
|
if (at >= NumChannels) {
|
|
|
|
at = 0;
|
|
|
|
/* Be conservitative and reset the port each cycle. */
|
|
|
|
PORTC = 0;
|
|
|
|
}
|
|
|
|
|
2014-02-02 15:02:53 +01:00
|
|
|
// TODO(michaelh): check that this is cheap.
|
|
|
|
OCR0A = -(uint8_t)((position_[at+0] + Bias)/2);
|
|
|
|
OCR0B = -(uint8_t)((position_[at+1] + Bias)/2);
|
2013-12-27 21:35:37 +01:00
|
|
|
|
|
|
|
at_ = at;
|
|
|
|
state_ = State::Centre;
|
|
|
|
} else {
|
2014-02-02 15:02:53 +01:00
|
|
|
OCR0A = (uint8_t)((position_[at+0] + Bias + 1)/2);
|
|
|
|
OCR0B = (uint8_t)((position_[at+1] + Bias + 1)/2);
|
2013-12-27 21:35:37 +01:00
|
|
|
|
|
|
|
state_ = State::Start;
|
|
|
|
}
|
2013-12-17 20:48:07 +01:00
|
|
|
}
|
|
|
|
|
2013-12-27 21:35:37 +01:00
|
|
|
inline void Servos::compare_a() {
|
|
|
|
PINC = pins_[at_+0];
|
2013-12-17 20:48:07 +01:00
|
|
|
}
|
|
|
|
|
2013-12-27 21:35:37 +01:00
|
|
|
inline void Servos::compare_b() {
|
|
|
|
PINC = pins_[at_+1];
|
2013-12-17 20:48:07 +01:00
|
|
|
}
|
|
|
|
|
2013-12-27 21:35:37 +01:00
|
|
|
ISR(TIMER0_OVF_vect) {
|
2013-12-27 20:16:04 +01:00
|
|
|
RoverIf::servos.overflow();
|
2013-12-17 20:48:07 +01:00
|
|
|
HAL::ticks++;
|
|
|
|
}
|
|
|
|
|
2013-12-27 21:35:37 +01:00
|
|
|
ISR(TIMER0_COMPA_vect) {
|
2013-12-27 20:16:04 +01:00
|
|
|
RoverIf::servos.compare_a();
|
2013-12-17 20:48:07 +01:00
|
|
|
}
|
|
|
|
|
2013-12-27 21:35:37 +01:00
|
|
|
ISR(TIMER0_COMPB_vect) {
|
2013-12-27 20:16:04 +01:00
|
|
|
RoverIf::servos.compare_b();
|
2013-12-17 20:48:07 +01:00
|
|
|
}
|