cancel
Showing results for 
Search instead for 
Did you mean: 

Controlling servo with tim1 close but it does not work

michaelmccartyeng
Associate II
Posted on April 04, 2014 at 16:00

Hello All,

I'm trying to control a servo. I found code on a site that was in Russian, and google translate did not help me much to understand it. Basically we want a period of 20ms and we want to be high during at least 1ms of that 20ms for start position up to 2ms for end position of the servo. I'm using tim1 and using PA.7. The original example was using a pin that I'm using for something else. My systemclock is ''uint32_t SystemCoreClock = 168000000;''. I dont know why they enable the int, I didnt find the int handler anywhere. Any help would be appreciated, I just discovered how servos work this week and cant believe I didn't use them before. Now I just have to add support for at least one to my project.

void initServo(void){
/*
* Servo min(1ms) = 1580
* Servo max(2ms) = 3160
*
*/
TIM_BDTRInitTypeDef bdtr;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* TIM1 clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
/* GPIOE clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* TIM1 channel 1 pin (PA.7) configuration */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Connect TIM pins to AF2 */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_TIM1);
TIM_TimeBaseStructInit (&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock/5000000; // pers 6
TIM_TimeBaseStructure.TIM_Period = 31600; // 20ms for servo period
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1 , &TIM_TimeBaseStructure);
// PWM1 Mode configuration: Channel1
// Edge -aligned; not single pulse mode
TIM_OCStructInit (& TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC1Init(TIM1 , &TIM_OCInitStructure);
TIM_BDTRStructInit(&bdtr);
bdtr.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(TIM1, &bdtr);
// Enable Timer Interrupt and Timer
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); //
TIM_Cmd(TIM1 , ENABLE);
TIM_SetCompare1(TIM1, 2370); // 2370 = 1.5ms - for Servo
}

16 REPLIES 16
michaelmccartyeng
Associate II
Posted on April 04, 2014 at 16:09


 /**
* @brief 
* @param None
* @retval None
*/
void setServo(int pos){
// Servo min(1ms) = 1580
// Servo max(2ms) = 3160
if(pos > 3160){
pos = 3160;
}
if(pos < 1580){
pos = 1580;
}
TIM_SetCompare1(TIM1, pos);
}

Setting it back to use PE.9 it seems to work on in initServo(Above). But the setServo function does not update it. 
 So I guess its just something to do with me changing the pin to A7, and I'm not calling some required re init function for the TIM1. 

Posted on April 04, 2014 at 18:31

/* TIM1 Main Output Enable */

TIM_CtrlPWMOutputs(TIM1, ENABLE);

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
michaelmccartyeng
Associate II
Posted on April 04, 2014 at 19:02

Hey Clive,

I abandoned the code I was using that was hard to understand for the code you linked to. That code is awesome and easy to understand. I had already set aside tim3 for 4 more pwm channels so I adapted your code to TIM3. All four channels are working ! The only thing I'm having issues with now is my system uses all unsigned 32 bit integers. So instead of mapping -90 to 90 I would rather map 0 to I try to subtract 90 from whatever value I input which should work but the float is always zero. With the mapping it should be -90 = 0, 0 = 90, 90 = 180, with your +/- 90 on the left mapped to 0to180 on the right. Any idea why it does not work ?

// value is my u32 input 0 to 180
float v=0; 
v = 90-(float)value;
servo_angle[0] = v;

I guess I should just refactor everything to work with 0 to 180 u32 ? Hate to mess something up though.
michaelmccartyeng
Associate II
Posted on April 04, 2014 at 19:04

Never mind it does seem to be working, just the debugger seems to always show the float as 0.

Thanks Clive !

Posted on April 04, 2014 at 19:40

Yes, I tried to make it clean and concise, I think my original F1 version used integers. People want/need different things, so adapt to suit

For 0..180.0

TIM_SetCompare1(TIM4, 600 + (int)(servo_angle[0] * 10.0f) ); // PD.12

integer

TIM_SetCompare1(TIM4, 600 + (servo_angle_int[0] * 10) ); // PD.12

integer, tenths of degrees, ie 0 .. 1800

TIM_SetCompare1(TIM4, 600 + servo_angle_tenths[0] ); // PD.12

[DEAD LINK /public/STe2ecommunities/mcu/Lists/STM32VLDiscovery/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/STM32VLDiscovery/Servo%20control%20using%20STM32VLDiscovery&FolderCTID=0x01200200770978C69A1141439FE559EB459D758000491D59B8574F8049B5DFA3E8B21CBA51&currentviews=2099]https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32VLDiscovery/Flat.aspx?RootFolder=%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2FSTM32VLDiscovery%2FServo%20control%20using%20STM32VLDiscovery&FolderCTID=0x01200200770978C69A1141439FE559EB459D758000491D59B8574F8049B5DFA3E8B21CBA51¤tviews=2099

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
michaelmccartyeng
Associate II
Posted on April 04, 2014 at 20:15

TIM_SetCompare1(TIM3, 600 + servo_angle_tenths[0] ); // PB.4
TIM_SetCompare2(TIM3, 600 + servo_angle_tenths[1] ); // PB.5
TIM_SetCompare3(TIM3, 600 + servo_angle_tenths[2] ); // PB.0
TIM_SetCompare4(TIM3, 600 + servo_angle_tenths[3] ); // PB.1

I implemented the ''tenths'' way. Exactly what I needed ! The comments say 600us to 2400us (600+1800). I thought servos worked from 1ms to 2ms ? I can see how the 20ms is used to use time division multiplexing to span out to multiple different servos on a single line. If the output from the stm32 were split by a external IC in divisions of 2ms we could control 10 servos on a single line. I dont know if such a chip exists but that would be neat. Thanks a bunch Clive, great code. I basically just did a replace of TIM4 with TIM3 and setup the gpio and everything worked. Her it is in case anyone needs 4 servos driven from TIM3

volatile u32 servo_angle_tenths[4] = { 0,0,0,0 }; // 0 to 1800
/**
* @brief initialize the servo 
* 
* @param None
* @retval None
*/
void initServo(void){
RCC_Configuration_Servo();
GPIO_Configuration_Servo();
NVIC_Configuration_Servo();
TIM3_Configuration_Servo();
}
/**************************************************************************************/
// Integers, or scaled integers (*10) will be more efficient here
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
// minimum high of 600 us for -90 degrees, with +90 degrees at 2400 us, 10 us per degree
// timer timebase set to us units to simplify the configuration/math
TIM_SetCompare1(TIM3, 600 + servo_angle_tenths[0] ); // PB.4
TIM_SetCompare2(TIM3, 600 + servo_angle_tenths[1] ); // PB.5
TIM_SetCompare3(TIM3, 600 + servo_angle_tenths[2] ); // PB.0
TIM_SetCompare4(TIM3, 600 + servo_angle_tenths[3] ); // PB.1
}
}
/**************************************************************************************/
void RCC_Configuration_Servo(void)
{
/* --------------------------- System Clocks Configuration -----------------*/
/* TIM3 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* GPIOD clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
}
/**************************************************************************************/
void NVIC_Configuration_Servo(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the TIM3 gloabal Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**************************************************************************************/
void GPIO_Configuration_Servo(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*-------------------------- GPIO Configuration ----------------------------*/
/* GPIOD Configuration: in output push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_4 | GPIO_Pin_1 | GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Connect TIM3 pins to AF2 */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_TIM3);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, GPIO_AF_TIM3);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_TIM3);
}
/**************************************************************************************/
void TIM3_Configuration_Servo(void)
{
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/* Time base configuration - SystemCoreClock = 168000000 for 168 MHz board */
TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (((SystemCoreClock / 1000000) / 2) - 1); // Shooting for 1 MHz, (1us)
TIM_TimeBaseStructure.TIM_Period = 20000 - 1; // 1 MHz / 20000 = 50 Hz (20ms)
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* Enable TIM3 Preload register on ARR */
TIM_ARRPreloadConfig(TIM3, ENABLE);
/* TIM PWM1 Mode configuration: Channel */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 1500; // Servo Top-Center
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
/* Output Compare PWM1 Mode configuration: Channel1 PB.4 */
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
/* Output Compare PWM1 Mode configuration: Channel2 PB.5 */
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
/* Output Compare PWM1 Mode configuration: Channel3 PB.0 */
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
/* Output Compare PWM1 Mode configuration: Channel4 PB.1 */
TIM_OC4Init(TIM3, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);
/* TIM Interrupts enable */
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
/* TIM3 enable counter */
TIM_Cmd(TIM3, ENABLE);
}

michaelmccartyeng
Associate II
Posted on April 04, 2014 at 20:23

Never mind, I finally found the specs of the servo here

http://www.servodatabase.com/servo/towerpro/sg90

Basic Information
Modulation: Analog
Torque: 
4.8V:
0 oz-in (1.80 kg-cm)
Speed: 
4.8V:
0.10 sec/60°
Weight: 0.32 oz (9.0 g)
Dimensions: 
Length:
0.91 in (1 mm)
Width:
0.48 in (2 mm)
Height:
1.14 in (0 mm)
Motor Type: 3-pole
Gear Type: Plastic
Rotation/Support: Bushing
Additional Specifications
Rotational Range: 180°
Pulse Cycle: ? (add)
Pulse Width: 500-2400 µs
Connector Type: ? (add)

Posted on April 04, 2014 at 20:39

The comments say 600us to 2400us (600+1800). I thought servos worked from 1ms to 2ms ?

It clearly depends on the models in question, I'm not a user of servos, but I found several for which specification were available that fell under this criterion, and it makes a lot of sense from an implementation point of view. I'm more of a signals/data guy.

http://www.servocity.com/html/hs-325hb_bb_deluxe.html#.Uz77FSjyBaQ

This servo can operate 180° when given a pulse signal ranging from 600usec to 2400usec. Since most R/C controllers cannot generate this wide of signal range, you will need to use our

http://www.servocity.com/html/180o_servo_stretcher.html

for 180° operation.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..