forked from fabianoriccardi/dimmable-light
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhw_timer_samd.cpp
More file actions
137 lines (111 loc) · 5.59 KB
/
hw_timer_samd.cpp
File metadata and controls
137 lines (111 loc) · 5.59 KB
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
/******************************************************************************
* This file is part of Dimmable Light for Arduino, a library to control *
* dimmers. *
* *
* Copyright (C) 2018-2023 Fabiano Riccardi *
* *
* Dimmable Light for Arduino is free software; you can redistribute *
* it and/or modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this library; if not, see <http://www.gnu.org/licenses/>. *
******************************************************************************/
/***********************************************************************************
* Here there is specific SAMD code. SAMD21 is usually programmed at register level
* so this file is needed to provide a minimalistic "HAL" to simplify timer usage.
***********************************************************************************/
#ifdef ARDUINO_ARCH_SAMD
#include "hw_timer_samd.h"
#include <Arduino.h>
// Supported timer: 3,4,5,... (NOTE: the one named as TC and not TCC).
// TC and TCC share the enumeration, where TCCs start from 0 and TCs
// follow up
#define TIMER_ID 3
#if TIMER_ID <= 2
#error "TIMER_ID must be between [3;7]"
#endif
// Some helpful macros to support different timers
#define _TCx(X) TC##X
#define TCx(X) _TCx(X)
#define _TCx_Handler(X) TC##X##_Handler
#define TCx_Handler(X) _TCx_Handler(X)
#define _TCx_IRQn(X) TC##X##_IRQn
#define TCx_IRQn(X) _TCx_IRQn(X)
#define _TCx_(X) TC##X##_
#define TCx_(X) _TCx_(X)
#if TIMER_ID == 3
#define GCLK_CLKCTRL_ID_x GCLK_CLKCTRL_ID_TCC2_TC3
#elif TIMER_ID == 4 || TIMER_ID == 5
#define GCLK_CLKCTRL_ID_x GCLK_CLKCTRL_ID_TC4_TC5
#elif TIMER_ID == 6 || TIMER_ID == 7
#define GCLK_CLKCTRL_ID_x GCLK_CLKCTRL_ID_TC6_TC7
#endif
static void (*timer_callback)() = nullptr;
void TCx_Handler(TIMER_ID)() {
TCx(TIMER_ID)->COUNT16.CTRLA.bit.ENABLE = 0;
// Wait until TC3 is enabled
while (TCx(TIMER_ID)->COUNT16.STATUS.bit.SYNCBUSY == 1)
;
TCx(TIMER_ID)->COUNT16.INTFLAG.bit.MC0 = 1;
timer_callback();
}
uint16_t microsecond2Tick(uint16_t micro) {
// Source clock / prescaler (NOTE that multiple prescaler can be chained)
static const uint32_t OSC8M_FREQ = 8000000;
static const uint32_t baseFreq = OSC8M_FREQ / 2;
static const uint16_t baseFreqForMicro = baseFreq / 1000000;
if (micro > 10000) { return 0; }
return baseFreqForMicro * micro;
}
void timerBegin() {
// enable 8Mhz clock, prescaler to 0
SYSCTRL->OSC8M.bit.PRESC = 0;
SYSCTRL->OSC8M.reg |= SYSCTRL_OSC8M_ENABLE;
// Configure Generic Clock Controller
// Configure asynchronous clock source
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_x; // select TCx peripheral channel
GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_GEN_GCLK7; // select source GCLK_GEN[0]
GCLK->CLKCTRL.bit.CLKEN = 1; // enable TCx generic clock
GCLK->GENCTRL.bit.SRC = GCLK_GENCTRL_SRC_OSC8M_Val; // 0x06 OSC8M oscillator output, High
// accuracy 8Mhz clock
GCLK->GENCTRL.bit.ID = 0x07; // select GCLK_GEN[7]
GCLK->GENCTRL.bit.GENEN = 1; // enable generator
GCLK->GENDIV.bit.ID = 0x07; // select GCLK_GEN[7]
GCLK->GENDIV.bit.DIV = 0; // write no prescaler
// Power Manager, usually peripheral are disabled on power reset!
PM->APBCSEL.bit.APBCDIV = 0; // no prescaler
PM->APBCMASK.bit.TCx_(TIMER_ID) = 1; // enable TCx interface
TCx(TIMER_ID)->COUNT16.CTRLA.bit.MODE = 0; // Configure Count Mode (16-bit)
TCx(TIMER_ID)->COUNT16.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV2_Val; // Configure Prescaler
// for divide by 2
TCx(TIMER_ID)->COUNT16.CTRLBCLR.bit.DIR = 1;
TCx(TIMER_ID)->COUNT16.CTRLC.bit.CPTEN0 = 0;
TCx(TIMER_ID)->COUNT16.INTENSET.bit.MC0 = 1; // Enable TCx compare mode interrupt generation //
// Enable match interrupts on compare channel 0
TCx(TIMER_ID)->COUNT16.CC[0].reg = 0; // Initialize the compare register
NVIC_EnableIRQ(TCx_IRQn(TIMER_ID)); // Enable TCx NVIC Interrupt Line
}
void timerSetCallback(void (*callback)()) {
timer_callback = callback;
}
void timerStart(uint16_t t) {
if (t <= 1) { return; }
TCx(TIMER_ID)->COUNT16.COUNT.reg = 0;
while (TCx(TIMER_ID)->COUNT16.STATUS.bit.SYNCBUSY == 1)
;
TCx(TIMER_ID)->COUNT16.CC[0].reg = t;
while (TCx(TIMER_ID)->COUNT16.STATUS.bit.SYNCBUSY == 1)
;
TCx(TIMER_ID)->COUNT16.CTRLA.bit.ENABLE = 1;
// Wait until Timer is enabled
while (TCx(TIMER_ID)->COUNT16.STATUS.bit.SYNCBUSY == 1)
;
}
#endif // END ARDUINO_ARCH_SAMD