1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
/**
* Marlin 3D Printer Firmware
*
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
* SAMD51 HAL developed by Giuliano Zaro (AKA GMagician)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/**
* This comes from Arduino library which at the moment is buggy and uncompilable
*/
#ifdef __SAMD51__
#include "../../inc/MarlinConfig.h"
#if HAS_SERVOS
#include "../shared/servo.h"
#include "../shared/servo_private.h"
#include "SAMD51.h"
#define __TC_GCLK_ID(t) TC##t##_GCLK_ID
#define _TC_GCLK_ID(t) __TC_GCLK_ID(t)
#define TC_GCLK_ID _TC_GCLK_ID(SERVO_TC)
#define _TC_PRESCALER(d) TC_CTRLA_PRESCALER_DIV##d##_Val
#define TC_PRESCALER(d) _TC_PRESCALER(d)
#define __SERVO_IRQn(t) TC##t##_IRQn
#define _SERVO_IRQn(t) __SERVO_IRQn(t)
#define SERVO_IRQn _SERVO_IRQn(SERVO_TC)
#define HAL_SERVO_TIMER_ISR() TC_HANDLER(SERVO_TC)
#define TIMER_TCCHANNEL(t) ((t) & 1)
#define TC_COUNTER_START_VAL 0xFFFF
static volatile int8_t currentServoIndex[_Nbr_16timers]; // index for the servo being pulsed for each timer (or -1 if refresh interval)
FORCE_INLINE static uint16_t getTimerCount() {
Tc * const tc = TimerConfig[SERVO_TC].pTc;
tc->COUNT16.CTRLBSET.reg = TC_CTRLBCLR_CMD_READSYNC;
SYNC(tc->COUNT16.SYNCBUSY.bit.CTRLB || tc->COUNT16.SYNCBUSY.bit.COUNT);
return tc->COUNT16.COUNT.reg;
}
// ----------------------------
// Interrupt handler for the TC
// ----------------------------
HAL_SERVO_TIMER_ISR() {
Tc * const tc = TimerConfig[SERVO_TC].pTc;
const timer16_Sequence_t timer =
#ifndef _useTimer1
_timer2
#elif !defined(_useTimer2)
_timer1
#else
(tc->COUNT16.INTFLAG.reg & tc->COUNT16.INTENSET.reg & TC_INTFLAG_MC0) ? _timer1 : _timer2
#endif
;
const uint8_t tcChannel = TIMER_TCCHANNEL(timer);
if (currentServoIndex[timer] < 0) {
#if defined(_useTimer1) && defined(_useTimer2)
if (currentServoIndex[timer ^ 1] >= 0) {
// Wait for both channels
// Clear the interrupt
tc->COUNT16.INTFLAG.reg = (tcChannel == 0) ? TC_INTFLAG_MC0 : TC_INTFLAG_MC1;
return;
}
#endif
tc->COUNT16.COUNT.reg = TC_COUNTER_START_VAL;
SYNC(tc->COUNT16.SYNCBUSY.bit.COUNT);
}
else if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && SERVO(timer, currentServoIndex[timer]).Pin.isActive)
digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, LOW); // pulse this channel low if activated
// Select the next servo controlled by this timer
currentServoIndex[timer]++;
if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && currentServoIndex[timer] < SERVOS_PER_TIMER) {
if (SERVO(timer, currentServoIndex[timer]).Pin.isActive) // check if activated
digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, HIGH); // it's an active channel so pulse it high
tc->COUNT16.CC[tcChannel].reg = getTimerCount() - (uint16_t)SERVO(timer, currentServoIndex[timer]).ticks;
}
else {
// finished all channels so wait for the refresh period to expire before starting over
currentServoIndex[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
const uint16_t tcCounterValue = getTimerCount();
if ((TC_COUNTER_START_VAL - tcCounterValue) + 4UL < usToTicks(REFRESH_INTERVAL)) // allow a few ticks to ensure the next OCR1A not missed
tc->COUNT16.CC[tcChannel].reg = TC_COUNTER_START_VAL - (uint16_t)usToTicks(REFRESH_INTERVAL);
else
tc->COUNT16.CC[tcChannel].reg = (uint16_t)(tcCounterValue - 4UL); // at least REFRESH_INTERVAL has elapsed
}
if (tcChannel == 0) {
SYNC(tc->COUNT16.SYNCBUSY.bit.CC0);
// Clear the interrupt
tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC0;
}
else {
SYNC(tc->COUNT16.SYNCBUSY.bit.CC1);
// Clear the interrupt
tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC1;
}
}
void initISR(timer16_Sequence_t timer) {
Tc * const tc = TimerConfig[SERVO_TC].pTc;
const uint8_t tcChannel = TIMER_TCCHANNEL(timer);
static bool initialized = false; // Servo TC has been initialized
if (!initialized) {
NVIC_DisableIRQ(SERVO_IRQn);
// Disable the timer
tc->COUNT16.CTRLA.bit.ENABLE = false;
SYNC(tc->COUNT16.SYNCBUSY.bit.ENABLE);
// Select GCLK0 as timer/counter input clock source
GCLK->PCHCTRL[TC_GCLK_ID].bit.CHEN = false;
SYNC(GCLK->PCHCTRL[TC_GCLK_ID].bit.CHEN);
GCLK->PCHCTRL[TC_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0 | GCLK_PCHCTRL_CHEN; // 120MHz startup code programmed
SYNC(!GCLK->PCHCTRL[TC_GCLK_ID].bit.CHEN);
// Reset the timer
tc->COUNT16.CTRLA.bit.SWRST = true;
SYNC(tc->COUNT16.SYNCBUSY.bit.SWRST);
SYNC(tc->COUNT16.CTRLA.bit.SWRST);
// Set timer counter mode to 16 bits
tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16;
// Set timer counter mode as normal PWM
tc->COUNT16.WAVE.bit.WAVEGEN = TCC_WAVE_WAVEGEN_NPWM_Val;
// Set the prescaler factor
tc->COUNT16.CTRLA.bit.PRESCALER = TC_PRESCALER(SERVO_TIMER_PRESCALER);
// Count down
tc->COUNT16.CTRLBSET.reg = TC_CTRLBCLR_DIR;
SYNC(tc->COUNT16.SYNCBUSY.bit.CTRLB);
// Reset all servo indexes
memset((void *)currentServoIndex, 0xFF, sizeof(currentServoIndex));
// Configure interrupt request
NVIC_ClearPendingIRQ(SERVO_IRQn);
NVIC_SetPriority(SERVO_IRQn, 5);
NVIC_EnableIRQ(SERVO_IRQn);
initialized = true;
}
if (!tc->COUNT16.CTRLA.bit.ENABLE) {
// Reset the timer counter
tc->COUNT16.COUNT.reg = TC_COUNTER_START_VAL;
SYNC(tc->COUNT16.SYNCBUSY.bit.COUNT);
// Enable the timer and start it
tc->COUNT16.CTRLA.bit.ENABLE = true;
SYNC(tc->COUNT16.SYNCBUSY.bit.ENABLE);
}
// First interrupt request after 1 ms
tc->COUNT16.CC[tcChannel].reg = getTimerCount() - (uint16_t)usToTicks(1000UL);
if (tcChannel == 0 ) {
SYNC(tc->COUNT16.SYNCBUSY.bit.CC0);
// Clear pending match interrupt
tc->COUNT16.INTFLAG.reg = TC_INTENSET_MC0;
// Enable the match channel interrupt request
tc->COUNT16.INTENSET.reg = TC_INTENSET_MC0;
}
else {
SYNC(tc->COUNT16.SYNCBUSY.bit.CC1);
// Clear pending match interrupt
tc->COUNT16.INTFLAG.reg = TC_INTENSET_MC1;
// Enable the match channel interrupt request
tc->COUNT16.INTENSET.reg = TC_INTENSET_MC1;
}
}
void finISR(timer16_Sequence_t timer) {
Tc * const tc = TimerConfig[SERVO_TC].pTc;
const uint8_t tcChannel = TIMER_TCCHANNEL(timer);
// Disable the match channel interrupt request
tc->COUNT16.INTENCLR.reg = (tcChannel == 0) ? TC_INTENCLR_MC0 : TC_INTENCLR_MC1;
if (true
#if defined(_useTimer1) && defined(_useTimer2)
&& (tc->COUNT16.INTENCLR.reg & (TC_INTENCLR_MC0|TC_INTENCLR_MC1)) == 0
#endif
) {
// Disable the timer if not used
tc->COUNT16.CTRLA.bit.ENABLE = false;
SYNC(tc->COUNT16.SYNCBUSY.bit.ENABLE);
}
}
#endif // HAS_SERVOS
#endif // __SAMD51__
|