2 ******************************************************************************
3 * @addtogroup PIOS PIOS Core hardware abstraction layer
5 * @addtogroup PIOS_SERVO RC Servo Functions
6 * @brief Code to do set RC servo output
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
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
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
58 int32_t PIOS_Servo_Init(const struct pios_servo_cfg
*cfg
)
62 if (PIOS_TIM_InitChannels(&tim_id
, cfg
->channels
, cfg
->num_channels
, NULL
, 0)) {
66 /* Store away the requested configuration */
68 pios_servo_pin_bank
= pios_malloc(sizeof(uint8_t) * cfg
->num_channels
);
71 for (uint8_t i
= 0; (i
< servo_cfg
->num_channels
); i
++) {
72 const struct pios_tim_channel
*chan
= &servo_cfg
->channels
[i
];
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
;
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
);
95 /* Set up for output compare function */
96 switch (chan
->timer_chan
) {
98 TIM_OC1Init(chan
->timer
, &servo_cfg
->tim_oc_init
);
99 TIM_OC1PreloadConfig(chan
->timer
, TIM_OCPreload_Enable
);
102 TIM_OC2Init(chan
->timer
, &servo_cfg
->tim_oc_init
);
103 TIM_OC2PreloadConfig(chan
->timer
, TIM_OCPreload_Enable
);
106 TIM_OC3Init(chan
->timer
, &servo_cfg
->tim_oc_init
);
107 TIM_OC3PreloadConfig(chan
->timer
, TIM_OCPreload_Enable
);
110 TIM_OC4Init(chan
->timer
, &servo_cfg
->tim_oc_init
);
111 TIM_OC4PreloadConfig(chan
->timer
, TIM_OCPreload_Enable
);
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
) {
131 TIM_OC1PolarityConfig(chan
->timer
, TIM_OCPolarity_High
);
134 TIM_OC2PolarityConfig(chan
->timer
, TIM_OCPolarity_High
);
137 TIM_OC3PolarityConfig(chan
->timer
, TIM_OCPolarity_High
);
140 TIM_OC4PolarityConfig(chan
->timer
, TIM_OCPolarity_High
);
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
) {
175 TIM_SetCompare1(chan
->timer
, 0);
178 TIM_SetCompare2(chan
->timer
, 0);
181 TIM_SetCompare3(chan
->timer
, 0);
184 TIM_SetCompare4(chan
->timer
, 0);
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
);
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
];
210 uint32_t new_clock
= PIOS_SERVO_TIMER_CLOCK
;
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;
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
);
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
) {
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
) {
253 TIM_SetCompare1(chan
->timer
, val
);
256 TIM_SetCompare2(chan
->timer
, val
);
259 TIM_SetCompare3(chan
->timer
, val
);
262 TIM_SetCompare4(chan
->timer
, val
);
267 uint8_t PIOS_Servo_GetPinBank(uint8_t pin
)
269 if (pin
< servo_cfg
->num_channels
) {
270 return pios_servo_pin_bank
[pin
];
276 #endif /* PIOS_INCLUDE_SERVO */