REVONANO Milestones update
[librepilot.git] / flight / pios / stm32f4xx / pios_servo.c
blob84a450b7fd986717fbf27aa53fd7562e6d20da2c
1 /**
2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
4 * @{
5 * @addtogroup PIOS_SERVO RC Servo Functions
6 * @brief Code to do set RC servo output
7 * @{
9 * @file pios_servo.c
10 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
11 * @brief RC Servo routines (STM32 dependent)
12 * @see The GNU Public License (GPL) Version 3
14 *****************************************************************************/
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 3 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 * for more details.
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
28 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include "pios.h"
33 #ifdef PIOS_INCLUDE_SERVO
35 #include "pios_servo_priv.h"
36 #include "pios_tim_priv.h"
38 /* Private Function Prototypes */
40 static const struct pios_servo_cfg *servo_cfg;
42 // determine if the related timer will work in synchronous (or OneShot/OneShot125) One Pulse mode.
43 static uint8_t pios_servo_bank_mode[PIOS_SERVO_BANKS] = { 0 };
44 // used to skip updates when pulse length is higher than update cycle
45 static uint16_t pios_servo_bank_next_update[PIOS_SERVO_BANKS] = { 0 };
46 static uint16_t pios_servo_bank_max_pulse[PIOS_SERVO_BANKS] = { 0 };
47 // timer associated to each bank
48 static TIM_TypeDef *pios_servo_bank_timer[PIOS_SERVO_BANKS] = { 0 };
50 // index of bank used for each pin
51 static uint8_t *pios_servo_pin_bank;
53 #define PIOS_SERVO_TIMER_CLOCK 1000000
54 #define PIOS_SERVO_SAFE_MARGIN 50
55 /**
56 * Initialise Servos
58 int32_t PIOS_Servo_Init(const struct pios_servo_cfg *cfg)
60 uint32_t tim_id;
62 if (PIOS_TIM_InitChannels(&tim_id, cfg->channels, cfg->num_channels, NULL, 0)) {
63 return -1;
66 /* Store away the requested configuration */
67 servo_cfg = cfg;
68 pios_servo_pin_bank = pios_malloc(sizeof(uint8_t) * cfg->num_channels);
70 uint8_t bank = 0;
71 for (uint8_t i = 0; (i < servo_cfg->num_channels); i++) {
72 const struct pios_tim_channel *chan = &servo_cfg->channels[i];
73 bool new = true;
74 /* See if any previous channels use that same timer */
75 for (uint8_t j = 0; (j < i) && new; j++) {
76 new &= chan->timer != servo_cfg->channels[j].timer;
79 if (new) {
80 PIOS_Assert(bank < PIOS_SERVO_BANKS);
81 for (uint8_t j = i; j < servo_cfg->num_channels; j++) {
82 if (servo_cfg->channels[j].timer == chan->timer) {
83 pios_servo_pin_bank[j] = bank;
86 pios_servo_bank_timer[bank] = chan->timer;
88 TIM_ARRPreloadConfig(chan->timer, ENABLE);
89 TIM_CtrlPWMOutputs(chan->timer, ENABLE);
90 TIM_Cmd(chan->timer, DISABLE);
92 bank++;
95 /* Set up for output compare function */
96 switch (chan->timer_chan) {
97 case TIM_Channel_1:
98 TIM_OC1Init(chan->timer, &servo_cfg->tim_oc_init);
99 TIM_OC1PreloadConfig(chan->timer, TIM_OCPreload_Enable);
100 break;
101 case TIM_Channel_2:
102 TIM_OC2Init(chan->timer, &servo_cfg->tim_oc_init);
103 TIM_OC2PreloadConfig(chan->timer, TIM_OCPreload_Enable);
104 break;
105 case TIM_Channel_3:
106 TIM_OC3Init(chan->timer, &servo_cfg->tim_oc_init);
107 TIM_OC3PreloadConfig(chan->timer, TIM_OCPreload_Enable);
108 break;
109 case TIM_Channel_4:
110 TIM_OC4Init(chan->timer, &servo_cfg->tim_oc_init);
111 TIM_OC4PreloadConfig(chan->timer, TIM_OCPreload_Enable);
112 break;
116 return 0;
119 void PIOS_Servo_SetBankMode(uint8_t bank, uint8_t mode)
121 PIOS_Assert(bank < PIOS_SERVO_BANKS);
122 pios_servo_bank_mode[bank] = mode;
124 if (pios_servo_bank_timer[bank]) {
125 for (uint8_t i = 0; (i < servo_cfg->num_channels); i++) {
126 if (pios_servo_pin_bank[i] == bank) {
127 const struct pios_tim_channel *chan = &servo_cfg->channels[i];
128 /* Set up for output compare function */
129 switch (chan->timer_chan) {
130 case TIM_Channel_1:
131 TIM_OC1PolarityConfig(chan->timer, TIM_OCPolarity_High);
132 break;
133 case TIM_Channel_2:
134 TIM_OC2PolarityConfig(chan->timer, TIM_OCPolarity_High);
135 break;
136 case TIM_Channel_3:
137 TIM_OC3PolarityConfig(chan->timer, TIM_OCPolarity_High);
138 break;
139 case TIM_Channel_4:
140 TIM_OC4PolarityConfig(chan->timer, TIM_OCPolarity_High);
141 break;
146 // Setup the timer accordingly
147 TIM_SelectOnePulseMode(pios_servo_bank_timer[bank], TIM_OPMode_Repetitive);
148 TIM_Cmd(pios_servo_bank_timer[bank], ENABLE);
153 void PIOS_Servo_Update()
155 for (uint8_t i = 0; (i < PIOS_SERVO_BANKS); i++) {
156 const TIM_TypeDef *timer = pios_servo_bank_timer[i];
157 if (timer && pios_servo_bank_mode[i] == PIOS_SERVO_BANK_MODE_SINGLE_PULSE) {
158 // a pulse to be generated is longer than cycle period. skip this update.
159 if (TIM_GetCounter((TIM_TypeDef *)timer) > (uint32_t)(pios_servo_bank_next_update[i] + PIOS_SERVO_SAFE_MARGIN)) {
160 TIM_GenerateEvent((TIM_TypeDef *)timer, TIM_EventSource_Update);
161 pios_servo_bank_next_update[i] = pios_servo_bank_max_pulse[i];
164 pios_servo_bank_max_pulse[i] = 0;
166 for (uint8_t i = 0; (i < servo_cfg->num_channels); i++) {
167 uint8_t bank = pios_servo_pin_bank[i];
168 uint8_t mode = pios_servo_bank_mode[bank];
169 if (mode == PIOS_SERVO_BANK_MODE_SINGLE_PULSE) {
170 /* Update the position */
171 const struct pios_tim_channel *chan = &servo_cfg->channels[i];
173 switch (chan->timer_chan) {
174 case TIM_Channel_1:
175 TIM_SetCompare1(chan->timer, 0);
176 break;
177 case TIM_Channel_2:
178 TIM_SetCompare2(chan->timer, 0);
179 break;
180 case TIM_Channel_3:
181 TIM_SetCompare3(chan->timer, 0);
182 break;
183 case TIM_Channel_4:
184 TIM_SetCompare4(chan->timer, 0);
185 break;
191 * Set the servo update rate (Max 500Hz)
192 * \param[in] array of rates in Hz
193 * \param[in] array of timer clocks in Hz
194 * \param[in] maximum number of banks
196 void PIOS_Servo_SetHz(const uint16_t *speeds, const uint32_t *clock, uint8_t banks)
198 PIOS_Assert(banks <= PIOS_SERVO_BANKS);
199 if (!servo_cfg) {
200 return;
203 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = servo_cfg->tim_base_init;
204 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
205 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
207 for (uint8_t i = 0; i < banks && i < PIOS_SERVO_BANKS; i++) {
208 const TIM_TypeDef *timer = pios_servo_bank_timer[i];
209 if (timer) {
210 uint32_t new_clock = PIOS_SERVO_TIMER_CLOCK;
211 if (clock[i]) {
212 new_clock = clock[i];
214 // Choose the correct prescaler value for the APB the timer is attached
215 if (timer == TIM1 || timer == TIM8 || timer == TIM9 || timer == TIM10 || timer == TIM11) {
216 TIM_TimeBaseStructure.TIM_Prescaler = (PIOS_PERIPHERAL_APB2_CLOCK / new_clock) - 1;
217 } else {
218 TIM_TimeBaseStructure.TIM_Prescaler = (PIOS_PERIPHERAL_APB1_CLOCK / new_clock) - 1;
220 TIM_TimeBaseStructure.TIM_Period = ((new_clock / speeds[i]) - 1);
221 TIM_TimeBaseInit((TIM_TypeDef *)timer, &TIM_TimeBaseStructure);
227 * Set servo position
228 * \param[in] Servo Servo number (0-7)
229 * \param[in] Position Servo position in microseconds
231 void PIOS_Servo_Set(uint8_t servo, uint16_t position)
233 /* Make sure servo exists */
234 if (!servo_cfg || servo >= servo_cfg->num_channels) {
235 return;
239 /* Update the position */
240 const struct pios_tim_channel *chan = &servo_cfg->channels[servo];
241 uint16_t val = position;
242 uint16_t margin = chan->timer->ARR / 50; // Leave 2% of period as margin to prevent overlaps
243 if (val > (chan->timer->ARR - margin)) {
244 val = chan->timer->ARR - margin;
247 uint8_t bank = pios_servo_pin_bank[servo];
248 if (pios_servo_bank_max_pulse[bank] < val) {
249 pios_servo_bank_max_pulse[bank] = val;
251 switch (chan->timer_chan) {
252 case TIM_Channel_1:
253 TIM_SetCompare1(chan->timer, val);
254 break;
255 case TIM_Channel_2:
256 TIM_SetCompare2(chan->timer, val);
257 break;
258 case TIM_Channel_3:
259 TIM_SetCompare3(chan->timer, val);
260 break;
261 case TIM_Channel_4:
262 TIM_SetCompare4(chan->timer, val);
263 break;
267 uint8_t PIOS_Servo_GetPinBank(uint8_t pin)
269 if (pin < servo_cfg->num_channels) {
270 return pios_servo_pin_bank[pin];
271 } else {
272 return 0;
276 #endif /* PIOS_INCLUDE_SERVO */