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) 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
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 /* 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
];
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
;
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
);
105 /* Set up for output compare function */
106 switch (chan
->timer_chan
) {
108 TIM_OC1Init(chan
->timer
, &cfg
->tim_oc_init
);
109 TIM_OC1PreloadConfig(chan
->timer
, TIM_OCPreload_Enable
);
112 TIM_OC2Init(chan
->timer
, &cfg
->tim_oc_init
);
113 TIM_OC2PreloadConfig(chan
->timer
, TIM_OCPreload_Enable
);
116 TIM_OC3Init(chan
->timer
, &cfg
->tim_oc_init
);
117 TIM_OC3PreloadConfig(chan
->timer
, TIM_OCPreload_Enable
);
120 TIM_OC4Init(chan
->timer
, &cfg
->tim_oc_init
);
121 TIM_OC4PreloadConfig(chan
->timer
, TIM_OCPreload_Enable
);
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
);
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
];
149 uint32_t new_clock
= PIOS_SERVO_TIMER_CLOCK
;
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
);
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
) {
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
) {
188 TIM_SetCompare1(chan
->timer
, val
);
191 TIM_SetCompare2(chan
->timer
, val
);
194 TIM_SetCompare3(chan
->timer
, val
);
197 TIM_SetCompare4(chan
->timer
, val
);
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
) {
224 TIM_SetCompare1(chan
->timer
, 0);
227 TIM_SetCompare2(chan
->timer
, 0);
230 TIM_SetCompare3(chan
->timer
, 0);
233 TIM_SetCompare4(chan
->timer
, 0);
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
) {
252 TIM_OC1PolarityConfig(chan
->timer
, TIM_OCPolarity_High
);
255 TIM_OC2PolarityConfig(chan
->timer
, TIM_OCPolarity_High
);
258 TIM_OC3PolarityConfig(chan
->timer
, TIM_OCPolarity_High
);
261 TIM_OC4PolarityConfig(chan
->timer
, TIM_OCPolarity_High
);
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
];
283 #endif /* PIOS_INCLUDE_SERVO */