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 05, 2014 at 17:30

Can I use TIM1, looking over my code I was already using TIM3. TIM1 is the only one with pins that actually work out well. I remember from the documentation that TIM1 works differently from the others, I think its more ''high speed''. I changed your code to use TIM1 and it keeps halting in startup_stm32f40_41xxx.s in the FPU_IRQHandler ? I think I'm not defining the correct Int handler, or the correct INT

void initServo(void){
RCC_Configuration_Servo();
GPIO_Configuration_Servo();
NVIC_Configuration_Servo();
TIM1_Configuration_Servo();
}
/**************************************************************************************/
// Integers, or scaled integers (*10) will be more efficient here
void TIM1_IRQHandler(void)
{
if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM1, 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(TIM1, 600 + servo_angle_tenths[0] ); // PE.9
TIM_SetCompare2(TIM1, 600 + servo_angle_tenths[1] ); // PE.11
TIM_SetCompare3(TIM1, 600 + servo_angle_tenths[2] ); // PE.13
TIM_SetCompare4(TIM1, 600 + servo_angle_tenths[3] ); // PE.14
}
}
/**************************************************************************************/
void RCC_Configuration_Servo(void)
{
/* --------------------------- System Clocks Configuration -----------------*/
/* TIM1 clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
/* GPIOD clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
}
/**************************************************************************************/
void NVIC_Configuration_Servo(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the TIM1 gloabal Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_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_9 | GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14;
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(GPIOE, &GPIO_InitStructure);
/* Connect TIM1 pins to AF2 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_TIM1);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_TIM1);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_TIM1);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_TIM1);
}
/**************************************************************************************/
void TIM1_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(TIM1, &TIM_TimeBaseStructure);
/* Enable TIM1 Preload register on ARR */
TIM_ARRPreloadConfig(TIM1, 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(TIM1, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
/* Output Compare PWM1 Mode configuration: Channel2 PB.5 */
TIM_OC2Init(TIM1, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
/* Output Compare PWM1 Mode configuration: Channel3 PB.0 */
TIM_OC3Init(TIM1, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
/* Output Compare PWM1 Mode configuration: Channel4 PB.1 */
TIM_OC4Init(TIM1, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable);
/* TIM Interrupts enable */
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
/* TIM1 enable counter */
TIM_Cmd(TIM1, ENABLE);
}

Posted on April 05, 2014 at 17:47

void TIM1_UP_TIM10_IRQHandler(void)

{

  // ...

}
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 05, 2014 at 18:26

With that ISR I see it updates the CCR1-4 now as expected. But I dont see any movement on the servo. So there must be something more to TIM1.

I will put a scope on it later, but I dont know if my scope goes up to 1MHz its one of those handheld ones.

michaelmccartyeng
Associate II
Posted on April 06, 2014 at 00:57

    /* TIM1 Main Output Enable */

    TIM_CtrlPWMOutputs(TIM1, ENABLE);

Made it output something as Clive said in this post (palm/face) and various others.

Now it seems to output on PE.14 only. And the range has been reduced to much less, plus it frequently tries to hit a location it cannot (I just hear it try really hard and ''hum'')

So, I think the timing has changed. And I have no idea why the other PortE pins are not outputting PWM (they dont have any signal on them).

Looking on the scope I see

servo_angle_tenths = 600   | Scope shows 300ms

servo_angle_tenths = 1050 | Scope shows 525ms

servo_angle_tenths = 1500 | Scope shows Almost 800ms ... I dont have much resolution.

So, Everything seems to be halved, putting 2 and 2 together TIM1 uses APB2 instead of APB1. So APB2 must be twice as fast ( I know I should know all this, sorry ).

So I modified the code to no longer divide by 2  for the prescalar and now everything works on PE.14 only.

Before

TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (((SystemCoreClock / 1000000) / 2) - 1); // Shooting for 1 MHz, (1us)

After

  TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (((SystemCoreClock / 1000000)) - 1); // Shooting for 1 MHz, (1us)

Any ideas why the other four gpio dont work as pwm outputs ? PE.9.11,13 ?

michaelmccartyeng
Associate II
Posted on April 06, 2014 at 03:00

I added three lines from my code in the first post and now it works. I dont know why but for TIM1 it seems to need the lines related to ''bdtr'' and ''TIM_CtrlPWMOutputs(TIM1,ENABLE''.

I should note I tested this also with ch1 on PA.7 and it did NOT work. However, here is working code for TIM1 using four channels on PE.9,11,13,14 to control up to 4 servos. This is working. Onto the next battle !

/**
* @brief initialize the servo 
* 
* @param None
* @retval None
*/
void initServo(void){
RCC_Configuration_Servo();
GPIO_Configuration_Servo();
NVIC_Configuration_Servo();
TIM1_Configuration_Servo();
}
/**************************************************************************************/
// Integers, or scaled integers (*10) will be more efficient here
// void TIM1_IRQHandler(void)
void TIM1_UP_TIM10_IRQHandler(void)
{
if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM1, 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(TIM1, 600 + servo_angle_tenths[0] ); // PE.9
TIM_SetCompare2(TIM1, 600 + servo_angle_tenths[1] ); // PE.11
TIM_SetCompare3(TIM1, 600 + servo_angle_tenths[2] ); // PE.13
TIM_SetCompare4(TIM1, 600 + servo_angle_tenths[3] ); // PE.14
}
}
/**************************************************************************************/
void RCC_Configuration_Servo(void)
{
/* --------------------------- System Clocks Configuration -----------------*/
/* TIM1 clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
/* GPIO clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
}
/**************************************************************************************/
void NVIC_Configuration_Servo(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the TIM1 gloabal Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_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_9 | GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14;
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(GPIOE, &GPIO_InitStructure);
/* Connect TIM1 pins to AF2 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_TIM1);// TIM1_CH1 SERVO1
GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_TIM1);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_TIM1);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_TIM1);
}
/**************************************************************************************/
void TIM1_Configuration_Servo(void)
{
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_BDTRInitTypeDef bdtr;
/* Time base configuration - SystemCoreClock = 168000000 for 168 MHz board */
TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (((SystemCoreClock / 1000000)) - 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(TIM1, &TIM_TimeBaseStructure);
/* Enable TIM1 Preload register on ARR */
TIM_ARRPreloadConfig(TIM1, ENABLE);
/* TIM PWM1 Mode configuration: Channel */
TIM_OCStructInit (& TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 600; // Servo 0 DEG
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
/* Output Compare PWM1 Mode configuration: Channel1 ? */
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
/* Output Compare PWM1 Mode configuration: Channel2 ? */
TIM_OC2Init(TIM1, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
/* Output Compare PWM1 Mode configuration: Channel3 ? */
TIM_OC3Init(TIM1, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
/* Output Compare PWM1 Mode configuration: Channel4 ? */
TIM_OC4Init(TIM1, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable);
// if using TIM1 only do these next 3 lines
TIM_BDTRStructInit(&bdtr);
bdtr.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(TIM1, &bdtr);
/* TIM Interrupts enable */
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
/* TIM1 Main Output Enable */
// only required for TIM1 
TIM_CtrlPWMOutputs(TIM1, ENABLE);
/* TIM1 enable counter */
TIM_Cmd(TIM1, ENABLE);
}

Posted on April 06, 2014 at 04:24

The OCInit has more parameters for TIM1/TIM8, but I'm not sure it needs the BDTR stuff. The Prescaler computation is dependent on the APB dividers being used. A more universal approach might be to use the RCC_GetClocks() method.

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 06, 2014 at 19:34

If your unsure imagine me ! I have no idea. All I know is before adding those lines about BDTR only channel 4 worked and afterwards all four channels worked. I understand how timers work but I had never seen that BDTR stuff before, thats why I shied away from the original code.

Guess I'll have to read up on it.

But thanks for your help Clive, would not have gotten to something that works without your help and code.