drv_pwm.c
Go to the documentation of this file.
1 /*
2  drv_pwm.c : PWM support for STM32F103CB
3 
4  Adapted from https://github.com/multiwii/baseflight/blob/master/src/drv_pwm.c
5 
6  This file is part of BreezySTM32.
7 
8  BreezySTM32 is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  BreezySTM32 is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with BreezySTM32. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 
23 /*
24  Configuration maps:
25 
26  1) multirotor PPM input
27  PWM1 used for PPM
28  PWM5..8 used for motors
29  PWM9..10 used for motors
30  PWM11..14 used for motors
31 
32  2) multirotor PWM input
33  PWM1..8 used for input
34  PWM9..10 used for motors
35  PWM11..14 used for motors
36  */
37 
38 #include <stdint.h>
39 #include <stdbool.h>
40 #include <stdlib.h>
41 
42 #include "stm32f10x_conf.h"
43 
44 #include "drv_gpio.h"
45 #include "drv_timer.h"
46 #include "drv_pwm.h"
47 #include "drv_system.h"
48 
49 typedef struct {
50  volatile uint16_t *ccr;
51  volatile uint16_t *cr1;
52  volatile uint16_t *cnt;
53  uint16_t period;
54 
55  // for input only
56  uint8_t channel;
57  uint8_t state;
58  uint16_t rise;
59  uint16_t fall;
60  uint16_t capture;
62 
63 static uint8_t sonar_trigger_port;
64 
65 typedef void (*pwmWriteFuncPtr)(uint8_t index, uint16_t value); // function pointer used to write motors
66 
68 static uint16_t captures[MAX_INPUTS];
69 static uint16_t sonar_reads[MAX_INPUTS];
71 static uint8_t pwmFilter = 0;
72 static uint32_t pwmLastUpdateTime_ms = 0;
73 static bool new_data = false;
74 static bool sonar_present = false;
75 
76 #define PWM_TIMER_MHZ 1
77 #define PWM_TIMER_8_MHZ 8
78 
79 static void pwmOCConfig(TIM_TypeDef *tim, uint8_t channel, uint16_t value)
80 {
81  uint16_t tim_oc_preload;
82  TIM_OCInitTypeDef TIM_OCInitStructure;
83 
84  TIM_OCStructInit(&TIM_OCInitStructure);
85  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
86  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
87  TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
88  TIM_OCInitStructure.TIM_Pulse = value;
89  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
90  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
91 
92  tim_oc_preload = TIM_OCPreload_Enable;
93 
94  switch (channel) {
95  case TIM_Channel_1:
96  TIM_OC1Init(tim, &TIM_OCInitStructure);
97  TIM_OC1PreloadConfig(tim, tim_oc_preload);
98  break;
99  case TIM_Channel_2:
100  TIM_OC2Init(tim, &TIM_OCInitStructure);
101  TIM_OC2PreloadConfig(tim, tim_oc_preload);
102  break;
103  case TIM_Channel_3:
104  TIM_OC3Init(tim, &TIM_OCInitStructure);
105  TIM_OC3PreloadConfig(tim, tim_oc_preload);
106  break;
107  case TIM_Channel_4:
108  TIM_OC4Init(tim, &TIM_OCInitStructure);
109  TIM_OC4PreloadConfig(tim, tim_oc_preload);
110  break;
111  }
112 }
113 
114 static void pwmICConfig(TIM_TypeDef *tim, uint8_t channel, uint16_t polarity)
115 {
116  TIM_ICInitTypeDef TIM_ICInitStructure;
117 
118  TIM_ICStructInit(&TIM_ICInitStructure);
119  TIM_ICInitStructure.TIM_Channel = channel;
120  TIM_ICInitStructure.TIM_ICPolarity = polarity;
121  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
122  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
123  TIM_ICInitStructure.TIM_ICFilter = pwmFilter;
124 
125  TIM_ICInit(tim, &TIM_ICInitStructure);
126 }
127 
128 static void pwmGPIOConfig(GPIO_TypeDef *gpio, uint32_t pin, GPIO_Mode mode)
129 {
130  gpio_config_t cfg;
131 
132  cfg.pin = pin;
133  cfg.mode = mode;
134  cfg.speed = Speed_2MHz;
135  gpioInit(gpio, &cfg);
136 }
137 
138 static pwmPortData_t *pwmOutConfig(uint8_t port, uint8_t mhz, uint16_t period, uint16_t value)
139 {
140  pwmPortData_t *p = &pwmPorts[port];
141  configTimeBase(timerHardware[port].tim, period, mhz);
142  pwmGPIOConfig(timerHardware[port].gpio, timerHardware[port].pin, Mode_AF_PP);
143  pwmOCConfig(timerHardware[port].tim, timerHardware[port].channel, value);
144  // Needed only on TIM1
145  if (timerHardware[port].outputEnable)
147  TIM_Cmd(timerHardware[port].tim, ENABLE);
148 
149  p->cr1 = &timerHardware[port].tim->CR1;
150  p->cnt = &timerHardware[port].tim->CNT;
151 
152  switch (timerHardware[port].channel) {
153  case TIM_Channel_1:
154  p->ccr = &timerHardware[port].tim->CCR1;
155  break;
156  case TIM_Channel_2:
157  p->ccr = &timerHardware[port].tim->CCR2;
158  break;
159  case TIM_Channel_3:
160  p->ccr = &timerHardware[port].tim->CCR3;
161  break;
162  case TIM_Channel_4:
163  p->ccr = &timerHardware[port].tim->CCR4;
164  break;
165  }
166  p->period = period;
167  return p;
168 }
169 
170 static pwmPortData_t *pwmInConfig(uint8_t port, timerCCCallbackPtr callback, uint8_t channel)
171 {
172  pwmPortData_t *p = &pwmPorts[port];
173  const timerHardware_t *timerHardwarePtr = &(timerHardware[port]);
174 
175  p->channel = channel;
176 
177  pwmGPIOConfig(timerHardwarePtr->gpio, timerHardwarePtr->pin, Mode_IPD);
178  pwmICConfig(timerHardwarePtr->tim, timerHardwarePtr->channel, TIM_ICPolarity_Rising);
179 
180  timerConfigure(timerHardwarePtr, 0xFFFF, PWM_TIMER_MHZ);
181  configureTimerCaptureCompareInterrupt(timerHardwarePtr, port, callback);
182 
183  return p;
184 }
185 
186 uint32_t pwmLastUpdate()
187 {
188  return pwmLastUpdateTime_ms;
189 }
190 
191 static void ppmCallback(uint8_t port, uint16_t capture)
192 {
194  new_data = true;
195 
196  (void)port;
197  uint16_t diff;
198  static uint16_t now;
199  static uint16_t last = 0;
200  static uint8_t chan = 0;
201 
202  last = now;
203  now = capture;
204  diff = now - last;
205 
206  if (diff > 2700) { // Per http://www.rcgroups.com/forums/showpost.php?p=21996147&postcount=3960 "So, if you use 2.5ms or higher as being the reset for the PPM stream start, you will be fine. I use 2.7ms just to be safe."
207  chan = 0;
208  } else {
209  if (diff > PULSE_MIN && diff < PULSE_MAX && chan < MAX_INPUTS) { // 750 to 2250 ms is our 'valid' channel range
210  captures[chan] = diff;
211  }
212  chan++;
213  }
214 }
215 
216 static void pwmCallback(uint8_t port, uint16_t capture)
217 {
219  new_data = true;
220 
221  if (pwmPorts[port].state == 0) {
222  pwmPorts[port].rise = capture;
223  pwmPorts[port].state = 1;
225  } else {
226  pwmPorts[port].fall = capture;
227  // compute capture
228  pwmPorts[port].capture = pwmPorts[port].fall - pwmPorts[port].rise;
229  if (pwmPorts[port].capture > PULSE_MIN && pwmPorts[port].capture < PULSE_MAX) { // valid pulse width
230  captures[pwmPorts[port].channel] = pwmPorts[port].capture;
231  }
232  // switch state
233  pwmPorts[port].state = 0;
235  }
236 }
237 
238 static void sonarCallback(uint8_t port, uint16_t capture)
239 {
240  if (pwmPorts[port].state == 0) {
241  // switch state
242  pwmPorts[port].rise = capture;
243  pwmPorts[port].state = 1;
245  } else {
246  pwmPorts[port].fall = capture;
247  // compute capture
248  pwmPorts[port].capture = pwmPorts[port].fall - pwmPorts[port].rise;
249 // if (pwmPorts[port].capture > 880 && pwmPorts[port].capture < 65000) { // valid pulse width
250  sonar_reads[pwmPorts[port].channel - 8] = pwmPorts[port].capture;
251  sonar_present = true;
252 // }
253  // switch state
254  pwmPorts[port].state = 0;
256  }
257 }
258 
259 static void configureSonar(uint8_t port)
260 {
261  // Initialize as a trigger for sonar ring
262  sonar_trigger_port = port;
263 
264  gpio_config_t sonar_trigger_config;
265  sonar_trigger_config.mode = Mode_Out_PP;
266  sonar_trigger_config.pin = timerHardware[port].pin;
267  sonar_trigger_config.speed = Speed_50MHz;
268 
269  gpioInit(timerHardware[sonar_trigger_port].gpio, &sonar_trigger_config);
270 }
271 
272 // ===========================================================================
273 
274 enum {
275  TYPE_IP = 0x10,
276  TYPE_IW = 0x20,
277  TYPE_M = 0x40,
278  TYPE_S = 0x80,
279 };
280 
281 enum {
284 };
285 
286 static const uint16_t multiPPM[] = {
287  PWM1 | TYPE_IP, // PPM input
288  PWM9 | TYPE_M, // Swap to servo if needed
289  PWM10 | TYPE_M, // Swap to servo if needed
290  PWM11 | TYPE_M,
291  PWM12 | TYPE_M,
292  PWM13 | TYPE_M,
293  PWM14 | TYPE_M,
294  PWM5 | TYPE_M, // Swap to servo if needed
295  PWM6 | TYPE_M, // Swap to servo if needed
296  PWM7 | TYPE_M, // Swap to servo if needed
297  PWM8 | TYPE_M, // Swap to servo if needed
298  0xFFF
299 };
300 
301 static const uint16_t multiPPMSONAR[] = {
302  PWM1 | TYPE_IP, // PPM input
303  PWM2 | TYPE_S, // Read Sonar Input
304  PWM3 | TYPE_S,
305  PWM4 | TYPE_S,
306  PWM5 | TYPE_S,
307  PWM6 | TYPE_S,
308  PWM7 | TYPE_S,
309  PWM8 | TYPE_S,
310 
311  PWM9 | TYPE_M,
312  PWM10 | TYPE_M,
313  PWM11 | TYPE_M,
314  PWM12 | TYPE_M,
315  PWM13 | TYPE_M,
316  PWM14 | TYPE_M,
317  0xFFF
318 };
319 
320 static const uint16_t multiPWM[] = {
321  PWM1 | TYPE_IW, // input #1
322  PWM2 | TYPE_IW,
323  PWM3 | TYPE_IW,
324  PWM4 | TYPE_IW,
325  PWM5 | TYPE_IW,
326  PWM6 | TYPE_IW,
327  PWM7 | TYPE_IW,
328  PWM8 | TYPE_IW, // input #8
329  PWM9 | TYPE_M, // motor #1 or servo #1 (swap to servo if needed)
330  PWM10 | TYPE_M, // motor #2 or servo #2 (swap to servo if needed)
331  PWM11 | TYPE_M, // motor #1 or #3
332  PWM12 | TYPE_M,
333  PWM13 | TYPE_M,
334  PWM14 | TYPE_M, // motor #4 or #6
335  0xFFF
336 };
337 
338 
340 static uint8_t numMotors = 0;
341 static uint8_t numInputs = 0;
342 
343 static void pwmWriteBrushed(uint8_t index, uint16_t value)
344 {
345  *motors[index]->ccr = (value - 1000) * motors[index]->period / 1000;
346 }
347 
348 static void pwmWriteStandard(uint8_t index, uint16_t value)
349 {
350  *motors[index]->ccr = value;
351 }
352 
353 void pwmInit(bool useCPPM, bool usePwmFilter, bool fastPWM, uint32_t motorPwmRate, uint16_t idlePulseUsec)
354 {
355  const uint16_t *setup;
356 
357  // pwm filtering on input
358  pwmFilter = usePwmFilter ? 1 : 0;
359 
360  numMotors = 0;
361  setup = useCPPM ? multiPPMSONAR : multiPWM;
362 
363  int i;
364  for (i = 0; i < MAX_PORTS; i++) {
365 
366  uint8_t port = setup[i] & 0x0F;
367  uint8_t mask = setup[i] & 0xF0;
368  uint16_t output = setup[i] &0x0F00;
369 
370  if (setup[i] == 0x0FFF) // terminator
371  break;
372 
373  if(output & TYPE_GPIO_OUTPUT){
374  configureSonar(port);
375  }
376 
377  if (mask & TYPE_IP) {
378  pwmInConfig(port, ppmCallback, 0);
379  numInputs = 8;
380  } else if (mask & TYPE_IW) {
382  numInputs++;
383  } else if (mask & TYPE_S) {
385  numInputs++;
386  } else if (mask & TYPE_M) {
387  uint32_t mhz = (motorPwmRate > 500 || fastPWM) ? PWM_TIMER_8_MHZ : PWM_TIMER_MHZ;
388  uint32_t hz = mhz * 1000000;
389 
390  uint16_t period = hz / (fastPWM ? 4000 : motorPwmRate);
391 
392  motors[numMotors++] = pwmOutConfig(port, mhz, period, idlePulseUsec);
393  }
394  }
395 
396  // determine motor writer function
398  if (motorPwmRate > 500) {
400  }
401 }
402 
403 void pwmWriteMotor(uint8_t index, uint16_t value)
404 {
405  if (index < numMotors)
406  pwmWritePtr(index, value);
407 }
408 
409 uint16_t pwmRead(uint8_t channel)
410 {
411  if(millis() > pwmLastUpdateTime_ms + 40)
412  {
413  return 0;
414  }
415  else
416  {
417  new_data = false;
418  return captures[channel];
419  }
420 }
421 
423 {
424  return new_data;
425 }
426 
427 float sonarRead(uint8_t channel)
428 {
429  return (float)sonar_reads[channel] / 5787.405;
430 }
431 
433 {
434  return sonar_present;
435 }
436 
Definition: drv_pwm.h:39
static uint16_t captures[MAX_INPUTS]
Definition: drv_pwm.c:68
#define PWM_TIMER_8_MHZ
Definition: drv_pwm.c:77
uint16_t TIM_OutputNState
Definition: stm32f4xx_tim.h:92
void pwmInit(bool useCPPM, bool usePwmFilter, bool fastPWM, uint32_t motorPwmRate, uint16_t idlePulseUsec)
Definition: drv_pwm.c:353
GPIO_Mode mode
Definition: drv_gpio.h:63
static pwmPortData_t * motors[MAX_PORTS]
Definition: drv_pwm.c:339
void TIM_OC4Init(TIM_TypeDef *TIMx, TIM_OCInitTypeDef *TIM_OCInitStruct)
Initializes the TIMx Channel4 according to the specified parameters in the TIM_OCInitStruct.
Definition: drv_pwm.h:40
#define TIM_ICSelection_DirectTI
static uint8_t sonar_trigger_port
Definition: drv_pwm.c:63
volatile uint16_t * cr1
Definition: drv_pwm.c:51
static void ppmCallback(uint8_t port, uint16_t capture)
Definition: drv_pwm.c:191
GPIO_Speed speed
Definition: drv_gpio.h:64
void TIM_ICInit(TIM_TypeDef *TIMx, TIM_ICInitTypeDef *TIM_ICInitStruct)
Initializes the TIM peripheral according to the specified parameters in the TIM_ICInitStruct.
void TIM_OC4PreloadConfig(TIM_TypeDef *TIMx, uint16_t TIM_OCPreload)
Enables or disables the TIMx peripheral Preload register on CCR4.
#define TIM_Channel_3
volatile uint32_t millis(void)
Definition: system.c:50
float sonarRead(uint8_t channel)
Definition: drv_pwm.c:427
Definition: drv_pwm.h:31
#define PWM_TIMER_MHZ
Definition: drv_pwm.c:76
static void pwmWriteStandard(uint8_t index, uint16_t value)
Definition: drv_pwm.c:348
#define TIM_OCMode_PWM2
#define TIM_OutputNState_Disable
static uint16_t sonar_reads[MAX_INPUTS]
Definition: drv_pwm.c:69
#define TIM_ICPolarity_Falling
Definition: drv_pwm.h:44
#define TIM_Channel_2
void setup(void)
Definition: accelgyro.c:40
Definition: drv_pwm.h:36
void timerCCCallbackPtr(uint8_t port, uint16_t capture)
Definition: drv_timer.h:24
static bool sonar_present
Definition: drv_pwm.c:74
#define PULSE_MAX
Definition: drv_pwm.h:27
static void pwmCallback(uint8_t port, uint16_t capture)
Definition: drv_pwm.c:216
static void pwmICConfig(TIM_TypeDef *tim, uint8_t channel, uint16_t polarity)
Definition: drv_pwm.c:114
Definition: drv_pwm.h:33
uint16_t TIM_ICPolarity
#define TIM_Channel_4
static void pwmGPIOConfig(GPIO_TypeDef *gpio, uint32_t pin, GPIO_Mode mode)
Definition: drv_pwm.c:128
__IO uint32_t CCR3
Definition: stm32f4xx.h:1694
void gpioInit(GPIO_TypeDef *gpio, gpio_config_t *config)
Definition: drv_gpio.c:56
void(* pwmWriteFuncPtr)(uint8_t index, uint16_t value)
Definition: drv_pwm.c:65
static void pwmWriteBrushed(uint8_t index, uint16_t value)
Definition: drv_pwm.c:343
#define TIM_OCPolarity_Low
static pwmWriteFuncPtr pwmWritePtr
Definition: drv_pwm.c:70
__IO uint32_t CCR1
Definition: stm32f4xx.h:1692
uint32_t pwmLastUpdate()
Definition: drv_pwm.c:186
uint16_t TIM_ICPrescaler
uint16_t rise
Definition: drv_pwm.c:58
const timerHardware_t timerHardware[]
Definition: drv_timer.c:74
uint16_t TIM_OCPolarity
Definition: stm32f4xx_tim.h:99
void TIM_Cmd(TIM_TypeDef *TIMx, FunctionalState NewState)
Enables or disables the specified TIM peripheral.
static pwmPortData_t * pwmInConfig(uint8_t port, timerCCCallbackPtr callback, uint8_t channel)
Definition: drv_pwm.c:170
uint16_t pin
Definition: drv_gpio.h:62
TIM_TypeDef * tim
Definition: drv_timer.h:27
uint8_t channel
Definition: drv_timer.h:30
void configureTimerCaptureCompareInterrupt(const timerHardware_t *timerHardwarePtr, uint8_t reference, timerCCCallbackPtr *callback)
Definition: drv_timer.c:181
General Purpose I/O.
Definition: stm32f4xx.h:1281
#define TIM_OCPreload_Enable
bool sonarPresent()
Definition: drv_pwm.c:432
Definition: drv_pwm.h:41
Definition: drv_pwm.h:37
static pwmPortData_t pwmPorts[MAX_PORTS]
Definition: drv_pwm.c:67
void TIM_CtrlPWMOutputs(TIM_TypeDef *TIMx, FunctionalState NewState)
Enables or disables the TIM peripheral Main Outputs.
uint16_t fall
Definition: drv_pwm.c:59
static uint8_t numMotors
Definition: drv_pwm.c:340
uint16_t period
Definition: drv_pwm.c:53
Definition: drv_pwm.h:38
#define TIM_Channel_1
#define TIM_ICPolarity_Rising
uint8_t state
Definition: drv_pwm.c:57
GPIO_Mode
Definition: drv_gpio.h:24
void timerConfigure(const timerHardware_t *timerHardwarePtr, uint16_t period, uint8_t mhz)
Definition: drv_timer.c:215
void TIM_ICStructInit(TIM_ICInitTypeDef *TIM_ICInitStruct)
Fills each TIM_ICInitStruct member with its default value.
Definition: drv_pwm.h:34
uint16_t pwmRead(uint8_t channel)
Definition: drv_pwm.c:409
Definition: drv_pwm.h:43
void TIM_OC1PreloadConfig(TIM_TypeDef *TIMx, uint16_t TIM_OCPreload)
Enables or disables the TIMx peripheral Preload register on CCR1.
static bool new_data
Definition: drv_pwm.c:73
Definition: drv_pwm.h:35
#define TIM_ICPSC_DIV1
static const uint16_t multiPPM[]
Definition: drv_pwm.c:286
TIM Input Capture Init structure definition.
void TIM_OC2Init(TIM_TypeDef *TIMx, TIM_OCInitTypeDef *TIM_OCInitStruct)
Initializes the TIMx Channel2 according to the specified parameters in the TIM_OCInitStruct.
void TIM_OC2PreloadConfig(TIM_TypeDef *TIMx, uint16_t TIM_OCPreload)
Enables or disables the TIMx peripheral Preload register on CCR2.
static const uint16_t multiPPMSONAR[]
Definition: drv_pwm.c:301
uint8_t channel
Definition: drv_pwm.c:56
__IO uint32_t CCR4
Definition: stm32f4xx.h:1695
uint16_t TIM_OutputState
Definition: stm32f4xx_tim.h:89
#define TIM_OutputState_Enable
static pwmPortData_t * pwmOutConfig(uint8_t port, uint8_t mhz, uint16_t period, uint16_t value)
Definition: drv_pwm.c:138
void TIM_OC1Init(TIM_TypeDef *TIMx, TIM_OCInitTypeDef *TIM_OCInitStruct)
Initializes the TIMx Channel1 according to the specified parameters in the TIM_OCInitStruct.
void TIM_OC3Init(TIM_TypeDef *TIMx, TIM_OCInitTypeDef *TIM_OCInitStruct)
Initializes the TIMx Channel3 according to the specified parameters in the TIM_OCInitStruct.
static void sonarCallback(uint8_t port, uint16_t capture)
Definition: drv_pwm.c:238
#define PULSE_MIN
Definition: drv_pwm.h:26
bool pwmNewData()
Definition: drv_pwm.c:422
uint16_t TIM_ICSelection
static uint32_t pwmLastUpdateTime_ms
Definition: drv_pwm.c:72
#define TIM_OCIdleState_Set
uint32_t pin
Definition: drv_timer.h:29
static uint8_t pwmFilter
Definition: drv_pwm.c:71
void TIM_OC3PreloadConfig(TIM_TypeDef *TIMx, uint16_t TIM_OCPreload)
Enables or disables the TIMx peripheral Preload register on CCR3.
uint16_t capture
Definition: drv_pwm.c:60
#define NULL
Definition: usbd_def.h:50
__IO uint16_t CR1
Definition: stm32f4xx.h:1668
Definition: drv_pwm.h:42
static void configureSonar(uint8_t port)
Definition: drv_pwm.c:259
static uint8_t numInputs
Definition: drv_pwm.c:341
TIM Output Compare Init structure definition.
Definition: stm32f4xx_tim.h:84
void TIM_OCStructInit(TIM_OCInitTypeDef *TIM_OCInitStruct)
Fills each TIM_OCInitStruct member with its default value.
GPIO_TypeDef * gpio
Definition: drv_timer.h:28
void configTimeBase(TIM_TypeDef *tim, uint16_t period, uint8_t mhz)
Definition: drv_timer.c:198
__IO uint32_t CNT
Definition: stm32f4xx.h:1686
static const uint16_t multiPWM[]
Definition: drv_pwm.c:320
__IO uint32_t CCR2
Definition: stm32f4xx.h:1693
Definition: drv_pwm.h:32
void pwmWriteMotor(uint8_t index, uint16_t value)
Definition: drv_pwm.c:403
static void pwmOCConfig(TIM_TypeDef *tim, uint8_t channel, uint16_t value)
Definition: drv_pwm.c:79
uint16_t TIM_OCIdleState
volatile uint16_t * ccr
Definition: drv_pwm.c:50
#define MAX_INPUTS
Definition: drv_pwm.h:25
volatile uint16_t * cnt
Definition: drv_pwm.c:52


rosflight_firmware
Author(s): Daniel Koch , James Jackson
autogenerated on Thu Apr 15 2021 05:07:46