Skip to content

Commit 938a3eb

Browse files
authored
Merge pull request #18 from facchinm/avr4809
Add support for megaAVR architecture (UNO WiFi Rev2)
2 parents d2a4a47 + 3dbe390 commit 938a3eb

File tree

4 files changed

+266
-1
lines changed

4 files changed

+266
-1
lines changed

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ sentence=Allows Arduino/Genuino boards to control a variety of servo motors.
66
paragraph=This library can control a great number of servos.<br />It makes careful use of timers: the library can control 12 servos using only 1 timer.<br />On the Arduino Due you can control up to 60 servos.<br />
77
category=Device Control
88
url=http://www.arduino.cc/en/Reference/Servo
9-
architectures=avr,sam,samd,nrf52,stm32f4
9+
architectures=avr,megaAVR,sam,samd,nrf52,stm32f4

src/Servo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@
6969
#include "stm32f4/ServoTimers.h"
7070
#elif defined(ARDUINO_ARCH_NRF52)
7171
#include "nrf52/ServoTimers.h"
72+
#elif defined(ARDUINO_ARCH_MEGAAVR)
73+
#include "megaavr/ServoTimers.h"
7274
#else
7375
#error "This library only supports boards with an AVR, SAM, SAMD, NRF52 or STM32F4 processor."
7476
#endif

src/megaavr/Servo.cpp

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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

src/megaavr/ServoTimers.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
Copyright (c) 2018 Arduino LLC. All right reserved.
3+
4+
This library is free software; you can redistribute it and/or
5+
modify it under the terms of the GNU Lesser General Public
6+
License as published by the Free Software Foundation; either
7+
version 2.1 of the License, or (at your option) any later version.
8+
9+
This library is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
Lesser General Public License for more details.
13+
14+
You should have received a copy of the GNU Lesser General Public
15+
License along with this library; if not, write to the Free Software
16+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*/
18+
19+
/*
20+
* Defines for 16 bit timers used with Servo library
21+
*
22+
*/
23+
24+
#ifndef __SERVO_TIMERS_H__
25+
#define __SERVO_TIMERS_H__
26+
27+
#define USE_TIMERB1 // interferes with PWM on pin 3
28+
//#define USE_TIMERB2 // interferes with PWM on pin 11
29+
//#define USE_TIMERB0 // interferes with PWM on pin 6
30+
31+
#if !defined(USE_TIMERB1) && !defined(USE_TIMERB2) && !defined(USE_TIMERB0)
32+
# error "No timers allowed for Servo"
33+
/* Please uncomment a timer above and rebuild */
34+
#endif
35+
36+
static volatile TCB_t* _timer =
37+
#if defined(USE_TIMERB0)
38+
&TCB0;
39+
#endif
40+
#if defined(USE_TIMERB1)
41+
&TCB1;
42+
#endif
43+
#if defined(USE_TIMERB2)
44+
&TCB2;
45+
#endif
46+
47+
typedef enum {
48+
timer0,
49+
_Nbr_16timers } timer16_Sequence_t;
50+
51+
52+
#endif /* __SERVO_TIMERS_H__ */

0 commit comments

Comments
 (0)