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