Loading...
Searching...
No Matches
tim.c
1/***********************************************************************************
2 * @file timer.c *
3 * @author Matt Ricci *
4 * @brief *
5 ***********************************************************************************/
6
7#include "tim.h"
8
9#include "system_stm32f4xx.h"
10#include "config.h"
11
12#include "math.h"
13#include "stddef.h"
14#include "string.h"
15
16static void _TIM_init(TIM_TypeDef *, TIM_Config *);
17
18/* =============================================================================== */
30TIM_t TIM_init(TIM_TypeDef *interface, TIM_Config *config) {
31 // Early return error struct if peripheral is NULL
32 if (interface == NULL)
33 return (TIM_t){.interface = NULL};
34
35 // Initialise TIM struct with interface
36 TIM_t tim = {.interface = interface};
37
39 tim.pollUpdate = TIM_pollUpdate;
40 tim.setTimingPeriod = TIM_setTimingPeriod;
41 tim.pollCompare = TIM_pollCompare;
42 tim.setTimingPWM = TIM_setTimingPWM;
44
45 // Update config and enable peripheral
46 TIM_updateConfig(&tim, config);
47
48 return tim;
49}
50
51// ALLOW FORMATTING
52#ifndef __DOXYGEN__
53
54/* =============================================================================== */
66static void _TIM_init(TIM_TypeDef *interface, TIM_Config *config) {
67
68 // --- Configure CR1 ---
69 //
70 // clang-format off
71 interface->CR1 &= ~TIM_CR1_CONFIG_MASK;
72 interface->CR1 |= (
73 (config->ARPE << TIM_CR1_ARPE_Pos)
74 | (config->DIR << TIM_CR1_DIR_Pos)
75 | (config->OPM << TIM_CR1_OPM_Pos)
76 | (config->URS << TIM_CR1_URS_Pos)
77 | (config->UDIS << TIM_CR1_UDIS_Pos)
78 );
79 // clang-format on
80
81 // --- Configure CR2 ---
82 interface->CR2 &= ~TIM_CR2_CONFIG_MASK;
83 interface->CR2 |= (config->CCDS << TIM_CR2_CCDS_Pos);
84
85 // --- Configure DIER ---
86 //
87 // clang-format off
88 interface->DIER &= ~TIM_DIER_CONFIG_MASK;
89 interface->DIER |= (
90 ((config->CCDE[0] & 0x01) << TIM_DIER_CC1DE_Pos)
91 | ((config->CCDE[1] & 0x01) << TIM_DIER_CC2DE_Pos)
92 | ((config->CCDE[2] & 0x01) << TIM_DIER_CC3DE_Pos)
93 | ((config->CCDE[3] & 0x01) << TIM_DIER_CC4DE_Pos)
94 | (config->UDE << TIM_DIER_UDE_Pos)
95 | ((config->CCIE[0] & 0x01) << TIM_DIER_CC1IE_Pos)
96 | ((config->CCIE[1] & 0x01) << TIM_DIER_CC2IE_Pos)
97 | ((config->CCIE[2] & 0x01) << TIM_DIER_CC3IE_Pos)
98 | ((config->CCIE[3] & 0x01) << TIM_DIER_CC4IE_Pos)
99 | (config->UIE << TIM_DIER_UIE_Pos)
100 );
101 // clang-format on
102
103 // --- Configure CCMR 1 & 2 ---
104 for (TIM_Channel i = 0; i < TIM_CHANNEL4; i++) {
105 volatile uint32_t *reg = (i > TIM_CHANNEL2) ? &interface->CCMR2 : &interface->CCMR1;
106 // clang-format off
107 uint8_t channelConfig = (
108 config->OC[i].S << 0
109 | config->OC[i].FE << 2
110 | config->OC[i].PE << 3
111 | config->OC[i].M << 4
112 | config->OC[i].CE << 7
113 );
114 // clang-format on
115 *reg |= channelConfig << (i % 2);
116 }
117}
118
119/* =============================================================================== */
128uint32_t _TIM_getBaseCLock(TIM_TypeDef *interface) {
129 // Calculate relevant APBx clock for peripheral
130 uint32_t sysclk = HSE_USED ? SYSCLK_HSE : SYSCLK_HSI;
131 uint8_t hpre = AHBPresc[((RCC->CFGR & RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos)];
132
133 uint8_t ppre;
134 // Set prescale value according to peripheral bus
135 if ((unsigned long)interface >= APB2PERIPH_BASE) {
136 ppre = APBPresc[((RCC->CFGR & RCC_CFGR_PPRE2) >> RCC_CFGR_PPRE2_Pos)];
137 } else {
138 ppre = APBPresc[((RCC->CFGR & RCC_CFGR_PPRE1) >> RCC_CFGR_PPRE1_Pos)];
139 }
140
141 // Calculate peripheral bus clock from prescalers
142 uint32_t pclk = sysclk / hpre / ppre;
143
144 // TIMCLK = pclk when APB prescale = 1,
145 // otherwise TIMCLK = 2 * pclk
146 return (ppre > 1) ? 2 * pclk : pclk;
147}
148
149#endif
150
151/* =============================================================================== */
161 tim->interface->EGR = TIM_EGR_UG;
162 tim->interface->SR &= ~TIM_SR_UIF;
163 tim->interface->CR1 |= TIM_CR1_CEN;
164}
165
166/* =============================================================================== */
176 if (tim->interface->SR & TIM_SR_UIF) {
177 tim->interface->SR &= ~TIM_SR_UIF;
178 return true;
179 }
180 return false;
181}
182
183/* =============================================================================== */
193bool TIM_setTimingPeriod(TIM_t *tim, float period) {
194
195 // Determine active peripheral clock frequency and period
196 uint32_t pclk = _TIM_getBaseCLock(tim->interface);
197 float clkPeriod = 1.0f / pclk;
198
199 // Eearly exit false if attempting to set
200 // a period less than the base clock period
201 if (period < clkPeriod)
202 return false;
203
204 uint32_t maxCount;
205 // Determine maximum value that can be stored by the counter
206 if (tim->interface == TIM2 || tim->interface == TIM5) {
207 maxCount = 0xFFFFFFFF; // 32-bit counters
208 } else {
209 maxCount = 0xFFFF; // 16-bit counters
210 }
211
212 // Maximum time period that can be achieved by
213 // the counter at base frequency.
214 float maxPeriod = maxCount * clkPeriod;
215
216 // Timer prescale is proportional to the max achievable period
217 // by a factor of the total desired period
218 tim->interface->PSC = (uint16_t)(period / maxPeriod);
219
220 // New clock period = fck_psc / PSC[15:0] + 1
221 float timClk = (float)pclk / (tim->interface->PSC + 1);
222
223 // ARR value is the product of calculated frequency and desired period
224 float arr = period * timClk;
225
226 // Set ARR to nearest integer value
227 tim->interface->ARR = (uint32_t)(arr + 0.5f);
228
229 return true;
230}
231
232/* =============================================================================== */
241bool TIM_pollCompare(TIM_t *tim, TIM_Channel channel) {
242 if (tim->interface->SR & (TIM_SR_CC1IF << channel)) {
243 tim->interface->SR &= ~(TIM_SR_CC1IF << channel);
244 return true;
245 }
246 return false;
247}
248
249/* =============================================================================== */
259bool TIM_setTimingPWM(TIM_t *tim, TIM_Channel channel, float duty) {
260 // Early exit false if duty cycle is
261 // not in valid range
262 if (duty > 100 || duty < 0)
263 return false;
264
265 volatile uint32_t *channelCCR = &(tim->interface->CCR1) + (4 * channel);
266 *channelCCR = (((float)tim->interface->ARR / 100) * duty);
267 return true;
268}
269
270/* =============================================================================== */
282void TIM_updateConfig(TIM_t *tim, TIM_Config *config) {
283 // Initialise config with default values if passed NULL.
284 if (config == NULL) {
285 config = &TIM_CONFIG_DEFAULT;
286 }
287
288 // Update peripheral with new config
289 tim->config = *config;
290
291 // Initialise TIM registers and enable peripheral
292 _TIM_init(tim->interface, config);
293}
void(* updateConfig)(struct TIM *tim, TIM_Config *config)
TIM configuration update method.
Definition tim.h:178
TIM_Config config
Configuration parameters for the TIM peripheral.
Definition tim.h:172
void(* startCounter)(struct TIM *tim)
TIM configuration update method.
Definition tim.h:173
TIM_TypeDef * interface
Pointer to TIM interface struct.
Definition tim.h:171
bool TIM_setTimingPeriod(TIM_t *tim, float period)
Definition tim.c:193
void TIM_updateConfig(TIM_t *tim, TIM_Config *config)
Update TIM peripheral configuration.
Definition tim.c:282
bool TIM_pollUpdate(TIM_t *tim)
Definition tim.c:175
TIM_t TIM_init(TIM_TypeDef *interface, TIM_Config *config)
Initialiser for an SPI device interface.
Definition tim.c:30
bool TIM_setTimingPWM(TIM_t *tim, TIM_Channel channel, float duty)
Definition tim.c:259
bool TIM_pollCompare(TIM_t *tim, TIM_Channel channel)
Definition tim.c:241
void TIM_startCounter(TIM_t *tim)
Definition tim.c:160
TIM configuration struct.
Definition tim.h:152
Struct definition for TIM interface. Provides the interface for API consumers to interact with the TI...
Definition tim.h:170