fix WhatsNew for release
[librepilot.git] / flight / pios / stm32f10x / pios_servo.c
blob76da3d33a596ea223da6e03cd9b4c6494c9404ed
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) 2010.
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 /* Configure the channels to be in output compare mode */
72 for (uint8_t i = 0; i < cfg->num_channels; i++) {
73 const struct pios_tim_channel *chan = &servo_cfg->channels[i];
74 bool new = true;
75 /* See if any previous channels use that same timer */
76 for (uint8_t j = 0; (j < i) && new; j++) {
77 new &= chan->timer != servo_cfg->channels[j].timer;
80 if (new) {
81 PIOS_Assert(bank < PIOS_SERVO_BANKS);
82 for (uint8_t j = i; j < servo_cfg->num_channels; j++) {
83 if (servo_cfg->channels[j].timer == chan->timer) {
84 pios_servo_pin_bank[j] = bank;
87 pios_servo_bank_timer[bank] = chan->timer;
89 PIOS_Assert(bank < PIOS_SERVO_BANKS);
91 for (uint8_t j = i; j < servo_cfg->num_channels; j++) {
92 if (servo_cfg->channels[j].timer == chan->timer) {
93 pios_servo_pin_bank[j] = bank;
96 pios_servo_bank_timer[bank] = chan->timer;
98 TIM_ARRPreloadConfig(chan->timer, ENABLE);
99 TIM_CtrlPWMOutputs(chan->timer, ENABLE);
100 TIM_Cmd(chan->timer, ENABLE);
102 bank++;
105 /* Set up for output compare function */
106 switch (chan->timer_chan) {
107 case TIM_Channel_1:
108 TIM_OC1Init(chan->timer, &cfg->tim_oc_init);
109 TIM_OC1PreloadConfig(chan->timer, TIM_OCPreload_Enable);
110 break;
111 case TIM_Channel_2:
112 TIM_OC2Init(chan->timer, &cfg->tim_oc_init);
113 TIM_OC2PreloadConfig(chan->timer, TIM_OCPreload_Enable);
114 break;
115 case TIM_Channel_3:
116 TIM_OC3Init(chan->timer, &cfg->tim_oc_init);
117 TIM_OC3PreloadConfig(chan->timer, TIM_OCPreload_Enable);
118 break;
119 case TIM_Channel_4:
120 TIM_OC4Init(chan->timer, &cfg->tim_oc_init);
121 TIM_OC4PreloadConfig(chan->timer, TIM_OCPreload_Enable);
122 break;
126 return 0;
130 * Set the servo update rate (Max 500Hz)
131 * \param[in] array of rates in Hz
132 * \param[in] array of timer clocks in Hz
133 * \param[in] maximum number of banks
135 void PIOS_Servo_SetHz(const uint16_t *speeds, const uint32_t *clock, uint8_t banks)
137 PIOS_Assert(banks <= PIOS_SERVO_BANKS);
138 if (!servo_cfg) {
139 return;
142 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = servo_cfg->tim_base_init;
143 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
144 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
146 for (uint8_t i = 0; i < banks && i < PIOS_SERVO_BANKS; i++) {
147 const TIM_TypeDef *timer = pios_servo_bank_timer[i];
148 if (timer) {
149 uint32_t new_clock = PIOS_SERVO_TIMER_CLOCK;
150 if (clock[i]) {
151 new_clock = clock[i];
153 TIM_TimeBaseStructure.TIM_Prescaler = (PIOS_MASTER_CLOCK / new_clock) - 1;
154 TIM_TimeBaseStructure.TIM_Period = ((new_clock / speeds[i]) - 1);
156 TIM_TimeBaseInit((TIM_TypeDef *)timer, &TIM_TimeBaseStructure);
162 * Set servo position
163 * \param[in] Servo Servo number (0-7)
164 * \param[in] Position Servo position in microseconds
166 void PIOS_Servo_Set(uint8_t servo, uint16_t position)
168 /* Make sure servo exists */
169 if (!servo_cfg || servo >= servo_cfg->num_channels) {
170 return;
174 /* Update the position */
175 const struct pios_tim_channel *chan = &servo_cfg->channels[servo];
176 uint16_t val = position;
177 uint16_t margin = chan->timer->ARR / 50; // Leave 2% of period as margin to prevent overlaps
178 if (val > (chan->timer->ARR - margin)) {
179 val = chan->timer->ARR - margin;
182 uint8_t bank = pios_servo_pin_bank[servo];
183 if (pios_servo_bank_max_pulse[bank] < val) {
184 pios_servo_bank_max_pulse[bank] = val;
186 switch (chan->timer_chan) {
187 case TIM_Channel_1:
188 TIM_SetCompare1(chan->timer, val);
189 break;
190 case TIM_Channel_2:
191 TIM_SetCompare2(chan->timer, val);
192 break;
193 case TIM_Channel_3:
194 TIM_SetCompare3(chan->timer, val);
195 break;
196 case TIM_Channel_4:
197 TIM_SetCompare4(chan->timer, val);
198 break;
202 void PIOS_Servo_Update()
204 for (uint8_t i = 0; (i < PIOS_SERVO_BANKS); i++) {
205 const TIM_TypeDef *timer = pios_servo_bank_timer[i];
206 if (timer && pios_servo_bank_mode[i] == PIOS_SERVO_BANK_MODE_SINGLE_PULSE) {
207 // a pulse to be generated is longer than cycle period. skip this update.
208 if (TIM_GetCounter((TIM_TypeDef *)timer) > (uint32_t)(pios_servo_bank_next_update[i] + PIOS_SERVO_SAFE_MARGIN)) {
209 TIM_GenerateEvent((TIM_TypeDef *)timer, TIM_EventSource_Update);
210 pios_servo_bank_next_update[i] = pios_servo_bank_max_pulse[i];
213 pios_servo_bank_max_pulse[i] = 0;
215 for (uint8_t i = 0; (i < servo_cfg->num_channels); i++) {
216 uint8_t bank = pios_servo_pin_bank[i];
217 uint8_t mode = pios_servo_bank_mode[bank];
218 if (mode == PIOS_SERVO_BANK_MODE_SINGLE_PULSE) {
219 /* Update the position */
220 const struct pios_tim_channel *chan = &servo_cfg->channels[i];
222 switch (chan->timer_chan) {
223 case TIM_Channel_1:
224 TIM_SetCompare1(chan->timer, 0);
225 break;
226 case TIM_Channel_2:
227 TIM_SetCompare2(chan->timer, 0);
228 break;
229 case TIM_Channel_3:
230 TIM_SetCompare3(chan->timer, 0);
231 break;
232 case TIM_Channel_4:
233 TIM_SetCompare4(chan->timer, 0);
234 break;
240 void PIOS_Servo_SetBankMode(uint8_t bank, uint8_t mode)
242 PIOS_Assert(bank < PIOS_SERVO_BANKS);
243 pios_servo_bank_mode[bank] = mode;
245 if (pios_servo_bank_timer[bank]) {
246 for (uint8_t i = 0; (i < servo_cfg->num_channels); i++) {
247 if (pios_servo_pin_bank[i] == bank) {
248 const struct pios_tim_channel *chan = &servo_cfg->channels[i];
249 /* Set up for output compare function */
250 switch (chan->timer_chan) {
251 case TIM_Channel_1:
252 TIM_OC1PolarityConfig(chan->timer, TIM_OCPolarity_High);
253 break;
254 case TIM_Channel_2:
255 TIM_OC2PolarityConfig(chan->timer, TIM_OCPolarity_High);
256 break;
257 case TIM_Channel_3:
258 TIM_OC3PolarityConfig(chan->timer, TIM_OCPolarity_High);
259 break;
260 case TIM_Channel_4:
261 TIM_OC4PolarityConfig(chan->timer, TIM_OCPolarity_High);
262 break;
267 // Setup the timer accordingly
268 TIM_SelectOnePulseMode(pios_servo_bank_timer[bank], TIM_OPMode_Repetitive);
269 TIM_Cmd(pios_servo_bank_timer[bank], ENABLE);
274 uint8_t PIOS_Servo_GetPinBank(uint8_t pin)
276 if (pin < servo_cfg->num_channels) {
277 return pios_servo_pin_bank[pin];
278 } else {
279 return 0;
283 #endif /* PIOS_INCLUDE_SERVO */