Infrared4Arduino 1.2.3
Loading...
Searching...
No Matches
Sam.cpp
Go to the documentation of this file.
1// Support routines for SAM processor boards
2
3// Largely based on https://forum.arduino.cc/index.php?topic=346731.0
4
5// Partially based upon (heavily modified):
6/*
7 DimmerZero.cpp - Library for dimmer application with SAMD21G18A (e.g. Arduino Zero/M0).
8 Created by E.Burkowski, February 18, 2017.
9 Released into the public domain.
10*/
11
12/*
13Recommended pwm pins: 2,3,4,5,6,7,11,13
14For other pins see: https://github.com/Adminius/DimmerZero
15Supported PWM frequencies 250Hz, 500Hz, 1000Hz. default is 1000Hz
16*/
17
18#if (defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD)) & ! defined(ARDUINO_SAM_DUE)
19#ifndef __SAMD21G18A__
20
21#error "This library only supports SAMD21G18A based boards (e.g. Zero/M0...)"
22
23#endif
24
25#include "Board.h" // includes Sam.h
26
27// following based on setup from GitHub jdneo/timerInterrupt.ino
28void Sam::setTimerFrequency(frequency_t frequencyHz) {
29 int compareValue = (F_CPU / (TIMER_PRESCALER_DIV * frequencyHz)) - 1;
30 TcCount16* TC = (TcCount16*) TC3;
31 // Make sure the count is in a proportional position to where it was
32 // to prevent any jitter or disconnect when changing the compare value.
33 TC->COUNT.reg = map(TC->COUNT.reg, 0, TC->CC[0].reg, 0, compareValue);
34 TC->CC[0].reg = compareValue;
35 while (TC->STATUS.bit.SYNCBUSY == 1);
36}
37
38void Sam::timerEnableIntr() {
39 REG_GCLK_CLKCTRL = static_cast<uint16_t>(GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TCC2_TC3);
40 while (GCLK->STATUS.bit.SYNCBUSY == 1); // wait for sync
41
42 TcCount16* TC = (TcCount16*) TC3;
43
44 TC->CTRLA.reg &= ~TC_CTRLA_ENABLE;
45 while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
46
47 // Use the 16-bit timer
48 TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
49 while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
50
51 // Use match mode so that the timer counter resets when the count matches the compare register
52 TC->CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
53 while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
54
55 // Set prescaler to 1024
56 //TC->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1024;
57 TC->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV64;
58 while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
59
60 setTimerFrequency(1000000 / microsPerTick);
61
62 // Enable the compare interrupt
63 TC->INTENSET.reg = 0;
64 TC->INTENSET.bit.MC0 = 1;
65
66 NVIC_EnableIRQ(TC3_IRQn);
67
68 TC->CTRLA.reg |= TC_CTRLA_ENABLE;
69 while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
70}
71
72void Sam::timerDisableIntr() {
73 TcCount16* TC = (TcCount16*) TC3;
74 NVIC_DisableIRQ(TC3_IRQn);
75 TC->CTRLA.reg &= ~TC_CTRLA_ENABLE;
76 while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync
77}
78
79
80ISR(x); //interruptServiceRoutine(); // Defined in IRRemote as ISR(TIMER_INTR_NAME)
81
82// Is used!!
83void TC3_Handler() {
84 TcCount16* TC = (TcCount16*) TC3;
85 // If this interrupt is due to the compare register matching the timer count
86 // we toggle the LED.
87 if (TC->INTFLAG.bit.MC0 == 1) {
88 TC->INTFLAG.bit.MC0 = 1;
89 interruptServiceRoutine();
90 }
91}
92
93void Sam::timerConfigHz(pin_t pin, frequency_t frequency, dutycycle_t dutyCycle) {
94 pwmPin = pin;
95 maxValue = F_CPU / 2 / frequency;
96 onLength = maxValue * dutyCycle / 100;
97
98 timerTCC0 = false;
99 timerTCC1 = false;
100 timerTCC2 = false;
101
102 REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Divide the 48MHz clock source by divisor div: e.g. 48MHz/4=12MHz
103 GCLK_GENDIV_ID(4); // Select Generic Clock (GCLK) 4
104 while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
105
106 REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
107 GCLK_GENCTRL_GENEN | // Enable GCLK4
108 GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
109 GCLK_GENCTRL_ID(4); // Select GCLK4
110 while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
111
112 PORT->Group[g_APinDescription[pwmPin].ulPort].PINCFG[g_APinDescription[pwmPin].ulPin].bit.PMUXEN = 1;
113 switch (pwmPin) {
114 case 0:
115 PORT->Group[g_APinDescription[1].ulPort].PMUX[g_APinDescription[1].ulPin >> 1].reg |= PORT_PMUX_PMUXO_E;
116 timerTCC1 = true;
117 break;
118 case 1:
119 PORT->Group[g_APinDescription[1].ulPort].PMUX[g_APinDescription[1].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E;
120 timerTCC1 = true;
121 break;
122 case 2:
123 PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg |= PORT_PMUX_PMUXE_F;
124 timerTCC0 = true;
125 break;
126 case 3:
127 PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg |= PORT_PMUX_PMUXO_F;
128 timerTCC1 = true;
129 break;
130 case 4:
131 PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg |= PORT_PMUX_PMUXE_F;
132 timerTCC1 = true;
133 break;
134 case 5:
135 PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg |= PORT_PMUX_PMUXO_F;
136 timerTCC0 = true;
137 break;
138 case 6:
139 PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg |= PORT_PMUX_PMUXE_F;
140 timerTCC0 = true;
141 break;
142 case 7:
143 PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg |= PORT_PMUX_PMUXO_F;
144 timerTCC0 = true;
145 break;
146 case 8:
147 PORT->Group[g_APinDescription[8].ulPort].PMUX[g_APinDescription[8].ulPin >> 1].reg |= PORT_PMUX_PMUXO_E;
148 timerTCC1 = true;
149 break;
150 case 9:
151 PORT->Group[g_APinDescription[8].ulPort].PMUX[g_APinDescription[8].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E;
152 timerTCC1 = true;
153 break;
154 case 10:
155 PORT->Group[g_APinDescription[10].ulPort].PMUX[g_APinDescription[10].ulPin >> 1].reg |= PORT_PMUX_PMUXE_F;
156 timerTCC0 = true;
157 break;
158 case 11:
159 PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E;
160 timerTCC2 = true;
161 break;
162 case 12:
163 PORT->Group[g_APinDescription[10].ulPort].PMUX[g_APinDescription[10].ulPin >> 1].reg |= PORT_PMUX_PMUXO_F;
164 timerTCC0 = true;
165 break;
166 case 13:
167 PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg |= PORT_PMUX_PMUXO_E;
168 timerTCC2 = true;
169 break;
170 case A3:
171 PORT->Group[g_APinDescription[A3].ulPort].PMUX[g_APinDescription[A3].ulPin >> 1].reg |= PORT_PMUX_PMUXO_E;
172 timerTCC0 = true;
173 break;
174 case A4:
175 PORT->Group[g_APinDescription[A3].ulPort].PMUX[g_APinDescription[A3].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E;
176 timerTCC0 = true;
177 break;
178 default:
179 // error("Not supported pin!");
180 break;
181 }
182
183 TCCx = timerTCC0 ? TCC0
184 : timerTCC1 ? TCC1
185 : TCC2;
186
187 if (timerTCC0) {
188 // Feed GCLK4 to TCC0
189 REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC0 and TCC1
190 GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
191 GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed GCLK4 to TCC0 and TCC1
192 while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
193
194 if (invert) {
195 REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_DSBOTH;
196 while (TCC0->SYNCBUSY.bit.WAVE);
197 } else {
198 REG_TCC0_WAVE |= TCC_WAVE_POL(0xF) | TCC_WAVE_WAVEGEN_DSBOTH; // Setup dual slope PWM on TCC0
199 while (TCC0->SYNCBUSY.bit.WAVE);
200 }
201
202 REG_TCC0_PER = maxValue; // Set the frequency of the PWM on TCC0
203 while (TCC0->SYNCBUSY.bit.PER); // Wait for synchronization
204
205 REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
206 TCC_CTRLA_ENABLE; // Enable the TCC0 output
207 while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
208
209 }
210 if (timerTCC1) {
211 // Feed GCLK4 to TCC1
212 REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC0 and TCC1
213 GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
214 GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed GCLK4 to TCC0 and TCC1
215 while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
216
217 if (invert) {
218 REG_TCC1_WAVE |= TCC_WAVE_WAVEGEN_DSBOTH;
219 while (TCC1->SYNCBUSY.bit.WAVE);
220 } else {
221 REG_TCC1_WAVE |= TCC_WAVE_POL(0xF) | TCC_WAVE_WAVEGEN_DSBOTH; // Setup dual slope PWM on TCC1
222 while (TCC1->SYNCBUSY.bit.WAVE);
223 }
224
225 REG_TCC1_PER = maxValue; // Set the frequency of the PWM on TCC0
226 while (TCC1->SYNCBUSY.bit.PER); // Wait for synchronization
227
228 REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
229 TCC_CTRLA_ENABLE; // Enable the TCC1 output
230 while (TCC1->SYNCBUSY.bit.ENABLE); // Wait for synchronization
231
232 }
233 if (timerTCC2) {
234 // Feed GCLK4 to TCC2
235 REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC0 and TCC1
236 GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
237 GCLK_CLKCTRL_ID_TCC2_TC3; // Feed GCLK4 to TCC0 and TCC1
238 while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
239
240 if (invert) {
241 REG_TCC2_WAVE |= TCC_WAVE_WAVEGEN_DSBOTH;
242 while (TCC0->SYNCBUSY.bit.WAVE);
243 } else {
244 REG_TCC2_WAVE |= TCC_WAVE_POL(0xF) | TCC_WAVE_WAVEGEN_DSBOTH; // Setup dual slope PWM on TCC2
245 while (TCC2->SYNCBUSY.bit.WAVE);
246 }
247
248 REG_TCC2_PER = maxValue; // Set the frequency of the PWM on TCC0
249 while (TCC2->SYNCBUSY.bit.PER); // Wait for synchronization
250
251 REG_TCC2_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
252 TCC_CTRLA_ENABLE; // Enable the TCC2 output
253 while (TCC2->SYNCBUSY.bit.ENABLE); // Wait for synchronization
254
255 }
256}
257
258void Sam::setValue(uint16_t value) {
259 switch (pwmPin) {
260 case 0:
261 REG_TCC1_CC1 = value; // Set the PWM signal
262 while (TCC1->SYNCBUSY.bit.CC1); // Wait for synchronization
263 break;
264 case 1:
265 REG_TCC1_CC0 = value; // Set the PWM signal
266 while (TCC1->SYNCBUSY.bit.CC0); // Wait for synchronization
267 break;
268 case 2:
269 REG_TCC0_CC0 = value; // Set the PWM signal
270 while (TCC0->SYNCBUSY.bit.CC0); // Wait for synchronization
271 break;
272 case 3:
273 REG_TCC1_CC1 = value; // Set the PWM signal
274 while (TCC1->SYNCBUSY.bit.CC1); // Wait for synchronization
275 break;
276 case 4:
277 REG_TCC1_CC0 = value; // Set the PWM signal
278 while (TCC1->SYNCBUSY.bit.CC0); // Wait for synchronization
279 break;
280 case 5:
281 REG_TCC0_CC1 = value; // Set the PWM signal
282 while (TCC0->SYNCBUSY.bit.CC1); // Wait for synchronization
283 break;
284 case 6:
285 REG_TCC0_CC2 = value; // Set the PWM signal
286 while (TCC0->SYNCBUSY.bit.CC2); // Wait for synchronization
287 break;
288 case 7:
289 REG_TCC0_CC3 = value; // Set the PWM signal
290 while (TCC0->SYNCBUSY.bit.CC3); // Wait for synchronization
291 break;
292 case 8:
293 REG_TCC1_CC0 = value; // Set the PWM signal
294 while (TCC1->SYNCBUSY.bit.CC0); // Wait for synchronization
295 break;
296 case 9:
297 REG_TCC1_CC1 = value; // Set the PWM signal
298 while (TCC1->SYNCBUSY.bit.CC1); // Wait for synchronization
299 break;
300 case 10:
301 REG_TCC0_CC2 = value; // Set the PWM signal
302 while (TCC0->SYNCBUSY.bit.CC2); // Wait for synchronization
303 break;
304 case 11:
305 REG_TCC2_CC0 = value; // Set the PWM signal
306 while (TCC2->SYNCBUSY.bit.CC0); // Wait for synchronization
307 break;
308 case 12:
309 REG_TCC0_CC3 = value; // Set the PWM signal
310 while (TCC0->SYNCBUSY.bit.CC3); // Wait for synchronization
311 break;
312 case 13:
313 REG_TCC2_CC1 = value; // Set the PWM signal
314 while (TCC2->SYNCBUSY.bit.CC1); // Wait for synchronization
315 break;
316 case A3:
317 REG_TCC0_CC0 = value; // Set the PWM signal
318 while (TCC0->SYNCBUSY.bit.CC0); // Wait for synchronization
319 break;
320 case A4:
321 REG_TCC0_CC1 = value; // Set the PWM signal
322 while (TCC0->SYNCBUSY.bit.CC1); // Wait for synchronization
323 break;
324 default:
325 break;
326 }
327}
328
329// https://forum.arduino.cc/index.php?topic=346731.120 post #121
330//To stop the (TCC0) timer:
331//
332//Code: [Select]
333//
334//REG_TCC0_CTRLBSET = TCC_CTRLBSET_CMD_STOP;
335//while(TCC0->SYNCBUSY.bit.CTRLB);
336//
337//
338//To restart the timer again:
339//
340//Code: [Select]
341//
342//REG_TCC0_CTRLBSET = TCC_CTRLBSET_CMD_RETRIGGER;
343//while(TCC0->SYNCBUSY.bit.CTRLB);
344
345void Sam::timerConfigNormal() {
346 TCCx->CTRLA.bit.ENABLE = 0; // Disable TCC1 timer
347 while (TCCx->SYNCBUSY.bit.ENABLE); // Wait for synchronization
348}
349
350#endif // defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_SAMD)
#define ISR(f)
Definition: Esp32.h:48
int8_t dutycycle_t
Type for duty cycle in percent.
Definition: InfraredTypes.h:36
uint32_t frequency_t
Type for modulation frequency in Hz.
Definition: InfraredTypes.h:31
uint8_t pin_t
Type for GPIO pin, compatible with Arduino libs.
Definition: InfraredTypes.h:41
static const unsigned long microsPerTick
Definition: Board.h:57