1+ #if defined(ARDUINO_ARCH_MEGAAVR)
2+
3+ #include < Arduino.h>
4+ #include < Servo.h>
5+
6+ #define usToTicks (_us ) ((clockCyclesPerMicrosecond() / 16 * _us) / 4 ) // converts microseconds to tick
7+ #define ticksToUs (_ticks ) (((unsigned ) _ticks * 16 ) / (clockCyclesPerMicrosecond() / 4 )) // converts from ticks back to microseconds
8+
9+ #define TRIM_DURATION 5 // compensation ticks to trim adjust for digitalWrite delays
10+
11+ static servo_t servos[MAX_SERVOS]; // static array of servo structures
12+
13+ uint8_t ServoCount = 0 ; // the total number of attached servos
14+
15+ static volatile int8_t currentServoIndex[_Nbr_16timers]; // index for the servo being pulsed for each timer (or -1 if refresh interval)
16+
17+ // convenience macros
18+ #define SERVO_INDEX_TO_TIMER (_servo_nbr ) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo
19+ #define SERVO_INDEX_TO_CHANNEL (_servo_nbr ) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer
20+ #define SERVO_INDEX (_timer,_channel ) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel
21+ #define SERVO (_timer,_channel ) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel
22+
23+ #define SERVO_MIN () (MIN_PULSE_WIDTH - this ->min * 4 ) // minimum value in uS for this servo
24+ #define SERVO_MAX () (MAX_PULSE_WIDTH - this ->max * 4 ) // maximum value in uS for this servo
25+
26+ void ServoHandler (int timer)
27+ {
28+ if (currentServoIndex[timer] < 0 ) {
29+ // Write compare register
30+ _timer->CCMP = 0 ;
31+ } else {
32+ if (SERVO_INDEX (timer, currentServoIndex[timer]) < ServoCount && SERVO (timer, currentServoIndex[timer]).Pin .isActive == true ) {
33+ digitalWrite (SERVO (timer, currentServoIndex[timer]).Pin .nbr , LOW); // pulse this channel low if activated
34+ }
35+ }
36+
37+ // Select the next servo controlled by this timer
38+ currentServoIndex[timer]++;
39+
40+ if (SERVO_INDEX (timer, currentServoIndex[timer]) < ServoCount && currentServoIndex[timer] < SERVOS_PER_TIMER) {
41+ if (SERVO (timer, currentServoIndex[timer]).Pin .isActive == true ) { // check if activated
42+ digitalWrite (SERVO (timer, currentServoIndex[timer]).Pin .nbr , HIGH); // it's an active channel so pulse it high
43+ }
44+
45+ // Get the counter value
46+ uint16_t tcCounterValue = 0 ; // _timer->CCMP;
47+ _timer->CCMP = (uint16_t ) (tcCounterValue + SERVO (timer, currentServoIndex[timer]).ticks );
48+ }
49+ else {
50+ // finished all channels so wait for the refresh period to expire before starting over
51+
52+ // Get the counter value
53+ uint16_t tcCounterValue = _timer->CCMP ;
54+
55+ if (tcCounterValue + 4UL < usToTicks (REFRESH_INTERVAL)) { // allow a few ticks to ensure the next OCR1A not missed
56+ _timer->CCMP = (uint16_t ) usToTicks (REFRESH_INTERVAL);
57+ }
58+ else {
59+ _timer->CCMP = (uint16_t ) (tcCounterValue + 4UL ); // at least REFRESH_INTERVAL has elapsed
60+ }
61+
62+ currentServoIndex[timer] = -1 ; // this will get incremented at the end of the refresh period to start again at the first channel
63+ }
64+
65+ /* Clear flag */
66+ _timer->INTFLAGS = TCB_CAPT_bm;
67+ }
68+
69+ #if defined USE_TIMERB0
70+ ISR (TCB0_INT_vect)
71+ #elif defined USE_TIMERB1
72+ ISR (TCB1_INT_vect)
73+ #elif defined USE_TIMERB2
74+ ISR (TCB2_INT_vect)
75+ #endif
76+ {
77+ ServoHandler (0 );
78+ }
79+
80+ static void initISR (timer16_Sequence_t timer)
81+ {
82+ // TCA0.SINGLE.CTRLA = (TCA_SINGLE_CLKSEL_DIV16_gc) | (TCA_SINGLE_ENABLE_bm);
83+
84+ _timer->CTRLA = TCB_CLKSEL_CLKTCA_gc;
85+ // Timer to Periodic interrupt mode
86+ // This write will also disable any active PWM outputs
87+ _timer->CTRLB = TCB_CNTMODE_INT_gc;
88+ // Enable interrupt
89+ _timer->INTCTRL = TCB_CAPTEI_bm;
90+ // Enable timer
91+ _timer->CTRLA |= TCB_ENABLE_bm;
92+ }
93+
94+ static void finISR (timer16_Sequence_t timer)
95+ {
96+ // Disable interrupt
97+ _timer->INTCTRL = 0 ;
98+ }
99+
100+ static boolean isTimerActive (timer16_Sequence_t timer)
101+ {
102+ // returns true if any servo is active on this timer
103+ for (uint8_t channel=0 ; channel < SERVOS_PER_TIMER; channel++) {
104+ if (SERVO (timer,channel).Pin .isActive == true )
105+ return true ;
106+ }
107+ return false ;
108+ }
109+
110+ /* ***************** end of static functions ******************************/
111+
112+ Servo::Servo ()
113+ {
114+ if (ServoCount < MAX_SERVOS) {
115+ this ->servoIndex = ServoCount++; // assign a servo index to this instance
116+ servos[this ->servoIndex ].ticks = usToTicks (DEFAULT_PULSE_WIDTH); // store default values
117+ } else {
118+ this ->servoIndex = INVALID_SERVO; // too many servos
119+ }
120+ }
121+
122+ uint8_t Servo::attach (int pin)
123+ {
124+ return this ->attach (pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
125+ }
126+
127+ uint8_t Servo::attach (int pin, int min, int max)
128+ {
129+ timer16_Sequence_t timer;
130+
131+ if (this ->servoIndex < MAX_SERVOS) {
132+ pinMode (pin, OUTPUT); // set servo pin to output
133+ servos[this ->servoIndex ].Pin .nbr = pin;
134+ // todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
135+ this ->min = (MIN_PULSE_WIDTH - min)/4 ; // resolution of min/max is 4 uS
136+ this ->max = (MAX_PULSE_WIDTH - max)/4 ;
137+ // initialize the timer if it has not already been initialized
138+ timer = SERVO_INDEX_TO_TIMER (servoIndex);
139+ if (isTimerActive (timer) == false ) {
140+ initISR (timer);
141+ }
142+ servos[this ->servoIndex ].Pin .isActive = true ; // this must be set after the check for isTimerActive
143+ }
144+ return this ->servoIndex ;
145+ }
146+
147+ void Servo::detach ()
148+ {
149+ timer16_Sequence_t timer;
150+
151+ servos[this ->servoIndex ].Pin .isActive = false ;
152+ timer = SERVO_INDEX_TO_TIMER (servoIndex);
153+ if (isTimerActive (timer) == false ) {
154+ finISR (timer);
155+ }
156+ }
157+
158+ void Servo::write (int value)
159+ {
160+ // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
161+ if (value < MIN_PULSE_WIDTH)
162+ {
163+ if (value < 0 )
164+ value = 0 ;
165+ else if (value > 180 )
166+ value = 180 ;
167+
168+ value = map (value, 0 , 180 , SERVO_MIN (), SERVO_MAX ());
169+ }
170+ writeMicroseconds (value);
171+ }
172+
173+ void Servo::writeMicroseconds (int value)
174+ {
175+ // calculate and store the values for the given channel
176+ byte channel = this ->servoIndex ;
177+ if ( (channel < MAX_SERVOS) ) // ensure channel is valid
178+ {
179+ if (value < SERVO_MIN ()) // ensure pulse width is valid
180+ value = SERVO_MIN ();
181+ else if (value > SERVO_MAX ())
182+ value = SERVO_MAX ();
183+
184+ value = value - TRIM_DURATION;
185+ value = usToTicks (value); // convert to ticks after compensating for interrupt overhead
186+ servos[channel].ticks = value;
187+ }
188+ }
189+
190+ int Servo::read () // return the value as degrees
191+ {
192+ return map (readMicroseconds ()+1 , SERVO_MIN (), SERVO_MAX (), 0 , 180 );
193+ }
194+
195+ int Servo::readMicroseconds ()
196+ {
197+ unsigned int pulsewidth;
198+ if (this ->servoIndex != INVALID_SERVO)
199+ pulsewidth = ticksToUs (servos[this ->servoIndex ].ticks ) + TRIM_DURATION;
200+ else
201+ pulsewidth = 0 ;
202+
203+ return pulsewidth;
204+ }
205+
206+ bool Servo::attached ()
207+ {
208+ return servos[this ->servoIndex ].Pin .isActive ;
209+ }
210+
211+ #endif
0 commit comments