一、PWM__Mode:
当计数值小于CRR寄存器值时输出为有效电平,而有效电平要根据OCXInit来设置,设置的有效电平为高则当CNT值小于设置的CRR寄存器值时输出有效电平高电平,当CNT值大于设置的CRR寄存器值时输出有效电平低电平;
二、为什么PWM模式下要设置arr(period)和psc(prescaler)呢?
首先,ARR对应于预装载值,打个比方,当为向上计数时,计数器计数到CNT=ARR时,溢出并重新计数,所以可称ARR的值为预装载周期(此周期是在分频系数PSC下对应的周期);
其次,PSC是预分频系数,就是将机器周期的频率进行分频,STM32F407在倍频前对应的时钟频率是84MHZ, 假如,ARR=499(0~499=500)、PSC=83(0~83=84),此时有,机器周期对应的是1/84000000(单位是s,M对应的是十的六次方),分频系数是psc=83+1=84,分频后的频率为84/84=1MHZ,对应的周期为1/1000000(由式子84/84000000得来,也就是一个时钟周期【C51里面,一个时钟周期对应12个机器周期,STM32可以自己设置而已】);
好啦,到这里就好理解了。ARR是预装载值,PSC是预分频系数,在分频系数的分频之下,每一个装载值,对应的一个时钟周期为84/84000000,故定时器的定时实间为 ARR*PSC/(84MHz)=500*84/84000000s=0.0005s,频率也就是1/0.0005=2000Hz
顺便提一下,ARR可理解为计数周期,这个周期代表一次定时器溢出的计数周期,即计数达到此值时就会溢出,是在PSC分频系数下得到的机器周期对应的频率下计数的周期间。
例:在STM32F103VET6上的舵机驱动程序(270°舵机)。
舵机参数:
程序设计思路:
PWM初始化arr为20000,psc为72,计算的定时器周期为0.02秒,定时器频率为50hz,同时计数频率则为1M,计数周期为1us,最大计数周期为20000,servo函数里,直接更改ccr寄存器,在pwm模式设置下(pwm模式一和pwm模式二,其中一个是当CNT值小于ccr值时为高电平、大于时为低电平,另一个模式则相反),设置对应的ccr寄存器值为500起,对应pwm波形500us,500+2000/270*pwm,pwm取值0~270,得到输出有效脉宽电平500~2500us 对应 0°~270°。
(其他型号舵机驱动原理相同,如180°对应的ARR寄存器取值为500+2000/180*pwm,pwm取值0~180)
初始化程序:
/**********************************************/
//舵机初始化配置,TIM8的四个通道输出四路舵机
/**********************************************/
extern int s1_c,s2_c,s3_c,s4_c; //记录上一次舵机角度值
void servo_Configuration1(void) //舵机初始化
{
GPIO_InitTypeDef GPIO_InitStructrue;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructrue;
TIM_OCInitTypeDef TIM_OCInitStructrue;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8 , ENABLE);
GPIO_InitStructrue.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructrue.GPIO_Pin =GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructrue);
TIM_TimeBaseInitStructrue.TIM_ClockDivision=0;
TIM_TimeBaseInitStructrue.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructrue.TIM_Period=19999;
TIM_TimeBaseInitStructrue.TIM_Prescaler=71;
TIM_TimeBaseInit(TIM8,&TIM_TimeBaseInitStructrue);
TIM_OCInitStructrue.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructrue.TIM_OCPolarity=TIM_OCPolarity_High;
TIM_OCInitStructrue.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitStructrue.TIM_Pulse=0;
TIM_OC1Init(TIM8,&TIM_OCInitStructrue);
TIM_OC1PreloadConfig(TIM8,TIM_OCPreload_Disable);
TIM_OC2Init(TIM8,&TIM_OCInitStructrue);
TIM_OC2PreloadConfig(TIM8,TIM_OCPreload_Disable);
TIM_OC3Init(TIM8,&TIM_OCInitStructrue);
TIM_OC3PreloadConfig(TIM8,TIM_OCPreload_Disable);
TIM_OC4Init(TIM8,&TIM_OCInitStructrue);
TIM_OC4PreloadConfig(TIM8,TIM_OCPreload_Disable);
TIM_CtrlPWMOutputs(TIM8,ENABLE); //MOE 主输出使能
TIM_ARRPreloadConfig(TIM8,DISABLE);
TIM_Cmd(TIM8,ENABLE);
}
舵机角度控制程序:
/*************************************/
//TIM8的四路舵机输出,servo1、servo2、servo3和servo4
/*************************************/
void servo1(int pwm) //pwm是设定的角度,这里pwm有效值0~270,下同
{
if ((pwm >= 0)&&(pwm<=270))
{
TIM8->CCR1= 500 + (2000/270)*pwm;
}
else
{ /*****/
if(pwm <0) //限定角度最小0°,最大270°。下同。
{TIM8->CCR1 = 500;} /****/
else
{ TIM8->CCR1 = 2500;}
}
s1_c = pwm;
}
void servo2(int pwm)
{
if ((pwm >= 0)&&(pwm<=270))
{
TIM8->CCR2 = 500 + (2000/270)*pwm;
}
else
{
if(pwm <0)
{TIM8->CCR2 = 500;}
else
{ TIM8->CCR2 = 2500;}
}
s2_c = pwm;
}
void servo3(int pwm)
{
if ((pwm >= 0)&&(pwm<=270))
{
TIM8->CCR3 = 500 + (2000/270)*pwm;
}
else
{
if(pwm <0)
{TIM8->CCR3 = 500;}
else
{ TIM8->CCR3 = 2500;}
}
s3_c = pwm;
}
void servo4(int pwm)
{
if ((pwm >= 0)&&(pwm<=270))
{
TIM8->CCR4 = 500 + (2000/270)*pwm;
}
else
{
if(pwm <0)
{TIM8->CCR4 = 500;}
else
{ TIM8->CCR4 = 2500;}
}
s4_c = pwm;
}