2013-04-11 02:07 PM
hello,
reading around the forum i comed up with the following code. my question are in the code comments, if someone can point me in the right direction i'll be glad.This code will be used to move 4 ESC, so the generated pulse should be similar to analogic servo, PWM @50Hz, duty cicle high from 1 to 2ms with ~10us precision{code}volatile uint16_t position[] = { 1000, 1000, 1000, 1000 }; //from 1000 to 2000void 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 TIM3->CCR1 = position[0]; //PB0 TIM3->CCR2 = position[1]; //PB1 TIM3->CCR3 = position[2]; //PB4 TIM3->CCR4 = position[3]; //PB5 }}void startPWM(){ /* * BASED ON https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32VLDiscovery/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/STM32VLDiscovery/Servo%20control%20using%20STM32VLDiscovery&FolderCTID=0x01200200770978C69A1141439FE559EB459D758000491D59B8574F8049B5DFA3E8B21CBA51&TopicsView=https://my.st.co... * THANKS to Clive1 * */ // clock for GPIO and AFIO (Remap) - WHY THIS? RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); // clock for TIM3 - HERE DO WE ENABLE TIM3 HW? RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // PIN SETUP GPIO_InitTypeDef GPIO_InitStructure; GPIO_StructInit(&GPIO_InitStructure); //Reset init structure GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); //THIS? GPIO_PinRemapConfig( GPIO_FullRemap_TIM3, ENABLE ); // Map TIM3_CH3 to GPIOB.Pin0, GPIOB.Pin1, GPIOB.Pin4, GPIOB.Pin5 //OR THIS? //GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_TIM3); //GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, GPIO_AF_TIM3); //GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3); //GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_TIM3); //TIMER INTERRUPT SETUP NVIC_InitTypeDef NVIC_InitStructure; // Configure the NVIC Preemption Priority Bits NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); // Enable the TIM3 Interrupt NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //TIMER SETUP TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructInit( &TIM_TimeBaseStructure ); // Reset init structure // Simplify the math here so TIM_Pulse has 1 us units // PWMFreq = SystemCoreClock / ((TIM_Prescaler + 1) * (TIM_Period + 1)); // NOT VERY SURE THE TIM3 RUN AT 24MHz.... TIM_TimeBaseStructure.TIM_Prescaler = 24 - 1; // 24 MHz / 24 = 1 MHz TIM_TimeBaseStructure.TIM_Period = 20000 - 1; // 1 MHz / 20000 = 50 Hz (20 ms) TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit( &TIM_OCInitStructure ); // PWM1: Channel1, PB0 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_Pulse = position[0]; TIM_OC1Init(TIM3, &TIM_OCInitStructure); // PWM1: Channel2, PB1 TIM_OCInitStructure.TIM_Pulse = position[1]; TIM_OC2Init(TIM3, &TIM_OCInitStructure); // PWM1: Channel3, PB4 TIM_OCInitStructure.TIM_Pulse = position[2]; TIM_OC3Init(TIM3, &TIM_OCInitStructure); // PWM1: Channel4, PB5 TIM_OCInitStructure.TIM_Pulse = position[3]; TIM_OC4Init(TIM3, &TIM_OCInitStructure); // ENABLE TIMER TIM_Cmd( TIM3, ENABLE ); TIM_CtrlPWMOutputs(TIM3, ENABLE); // TIM IT enable TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);}{/code}ps. this is my very first code for this chip family, please be kind #stm32f4-discovery #pwm2013-04-11 02:29 PM
Yeah, porting code from the F1 to F4 is fun, the GPIO set up is different, and you have to divide down the clock which won't be running at 24 MHz. The code is supposed to clock the time base at 1 MHz, which gives a 1 us granularity.
This is an STM32F4-Discovery Servo version, I pulled this from the work-in-progress directory, but think it's workable.// STM32 4-channel Servo Demo for 168 MHz STM32F4 Discovery - sourcer32@gmail.com
#include ''stm32f4_discovery.h''
/**************************************************************************************/
// Integers, or scaled integers (*10) will be more efficient here
volatile float servo_angle[4] = { 0.0, 0.0, 0.0, 0.0 }; // +/- 0 degrees
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, 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(TIM4, 1500 + (int)(servo_angle[0] * 0f) ); // PD.12
TIM_SetCompare2(TIM4, 1500 + (int)(servo_angle[1] * 0f) ); // PD.13
TIM_SetCompare3(TIM4, 1500 + (int)(servo_angle[2] * 0f) ); // PD.14
TIM_SetCompare4(TIM4, 1500 + (int)(servo_angle[3] * 0f) ); // PD.15
}
}
/**************************************************************************************/
void RCC_Configuration(void)
{
/* --------------------------- System Clocks Configuration -----------------*/
/* TIM4 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
/* GPIOD clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
}
/**************************************************************************************/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the TIM4 gloabal Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**************************************************************************************/
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*-------------------------- GPIO Configuration ----------------------------*/
/* GPIOD Configuration: Pins 12, 13, 14 and 15 in output push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
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(GPIOD, &GPIO_InitStructure);
/* Connect TIM4 pins to AF2 */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_TIM4);
}
/**************************************************************************************/
void TIM4_Configuration(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(TIM4, &TIM_TimeBaseStructure);
/* Enable TIM4 Preload register on ARR */
TIM_ARRPreloadConfig(TIM4, 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 PD.12 */
TIM_OC1Init(TIM4, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);
/* Output Compare PWM1 Mode configuration: Channel2 PD.13 */
TIM_OC2Init(TIM4, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);
/* Output Compare PWM1 Mode configuration: Channel3 PD.14 */
TIM_OC3Init(TIM4, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);
/* Output Compare PWM1 Mode configuration: Channel4 PD.15 */
TIM_OC4Init(TIM4, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
/* TIM Interrupts enable */
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
/* TIM4 enable counter */
TIM_Cmd(TIM4, ENABLE);
}
/**************************************************************************************/
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
NVIC_Configuration();
TIM4_Configuration();
while(1)
{
// Add code to stroke servos here
}
}
2013-04-11 03:20 PM
Thank you very much!
ok, so what is the difference between//THIS?
GPIO_PinRemapConfig( GPIO_FullRemap_TIM3, ENABLE ); // Map TIM3_CH3 to GPIOB.Pin0, GPIOB.Pin1, GPIOB.Pin4, GPIOB.Pin5
//OR THIS?
//GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_TIM3);
//GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, GPIO_AF_TIM3);
//GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3);
//GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_TIM3);
There is no remap in stm32f4? (uhhm i was sure the code was compiling)if i understand the difference between the GPIO initialization, simply are the init struct different?the XXXPreloadConfig
is because STM32f4 use to buffer into shoadow register the real register value (i've rad the DataS, but i'm a bit confused about this)sorry if some question are stupid, i'm tired and i'm going to sleep.2013-04-11 03:55 PM
The F2, F4, L1 have a better multiplexing fabric for the alternate function mapping. It's been moved to the GPIO unit, and can now permit pin level reconfiguration, instead of blocks of pins (peripheral remap) that the F1 provided. This makes it easier to escape the functions you require.
The ARR normally changes at the current update value, whatever that happens to be, I set it immediately for consistency, and the fact whatever it was before is irrelevant.2013-04-23 04:32 AM
Thank you very much, yesterday i've corrected the code and now it is working.
I don't have here the code, but basically it is yours with some correction (just some define are different, and systemClock appear to be 72MHz in my system).I just can't understand your prescaler calculation: you divide clock by 2.. this give you a ''pwm clock'' of 0.5MHz, not 1MHz as expected2013-04-23 04:48 AM
The code makes presumptions about the APB1 clock divider.
For a APB1 Prescaler of 4 (the usual default), the TIMCLK is DIV22014-01-27 10:31 PM
Hi,
I have modified your code of Servo Example STM32F4-Discovery to generate PWM to control my Blue Bird servoBMS-621DMG+HS
, but I still cannot make it move. The attached picture summaries the characteristics of PWM output. The period is 34ms, duty cycle changes 2.8% ~ 1% slowly up and down. The speed at which the duty cycle changes from the min value to the max one is about 8s, long enough for the response of the servo. STM32F4-Discovery board provides the real output voltage at about 4.5V, not enough for the servo requirement 4.8~6.0V. So I use an external voltage 4.9~5.9V. Could you please give me some hints. ________________ Attachments : scope_3.bmp : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I05r&d=%2Fa%2F0X0000000bTN%2FKiORAjwqXxeVhTnlWpnJsnbJQEtmJ8Z28A4P7VOExxA&asPdf=false