STM32F407ZG开发板学习(7)
PWM简介
STM32F4的PWM技术是通过利用定时器实现的。
脉冲宽度调制
脉冲宽度调制(PWM,Pulse Width Modulation),简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术 。即对脉冲宽度的控制,原理图如下:
ARR控制频率,CRR控制占空比。 如图我们用定时器模拟脉冲,CNT为计数器,设置为匀速向上递增,ARR为自增的最大限制,达到即清零CNT,这样就模拟了一个锯齿状的脉冲。用一个CRR寄存器设置一个数在0到max之间的界限,小于时输出0,大于时输出1,这样最后就能输出一个固定占空比的脉冲信号,如上图下半部分所示。
STM32F4的定时器除了TIM6 和7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器TIM1和 TIM8可以同时产生多达7路的PWM输出。而通用定时器也能同时产 生多达4路的PWM输出。
寄存器
除了上一篇文章所介绍的一些寄存器和相关位,要配置使用定时器的PWM功能,还需了解如下寄存器。
捕获比较模式寄存器( TIMx_CCMR1/2 )
该寄存器 一般 有 2 个 TIMx _CCMR1和TIMx _CCMR2,不过TIM14只有一个。TIMx_CCMR1控制控制CH1和CH2,而TIMx_CCMR2控制CH3和CH4。
寄存器分为了两部分,OCxx是输出模式,ICxx是输入模式下。本文用的就是上半部分。其中CC1S(最后两位)用于选择输入还是输出。
捕获/比较使能寄存器 (TIMx_CCER)
捕获/比较寄存器 1 (TIMx_CCR1)
16位CCR值控制占空比。
刹车和死区寄存器(高级定时器 TIMx_BDTR )
如果是通用定时器,则配置以上三个寄存器就够了,但是如果是高级定时器,则还需要配
置:刹车和死区寄存器( TIMx_BDTR ):
实验
配置TIM14设置为PWM输出模式,由开发板的原理图可以知道,TIM14的PWM输出引脚PF9是和LED0的引脚复用的,因此可以通过调节TIM14的配置来对LED0的亮度做出控制。
为什么PWM可以改变LED的亮度?
LED亮度变化的原理
LED是一个二极管,它可以实现快速开关。它的开关速度可以高达微秒以上。是任何发光器件所无法比拟的。因此,只要把电源改成脉冲恒流源,用改变脉冲宽度的方法,就可以改变其亮度。这种方法称为脉宽调制(PWM)调光法。假如脉冲的周期为tpwm,脉冲宽度为ton,那么其工作比D(或称为孔度比)就是ton/tpwm。改变恒流源脉冲的工作比就可以改变LED的亮度。
配置步骤
- 使能相关时钟AHB1的GPIOF、APB2的TIM14相关时钟。
- GPIO引脚复用设置,GPIO_PinAFConfig。
- GPIO_Init,TIM_TimeBaseInit,TIM_OCInit三个相关结构体初始化。
其中需要提一下的是Polarity,其实前面有说到。
这里设置为low,即低电平有效。
//only for TIM1 and TIM8 仅高级定时器用
//TIM_OCInitStructure.TIM_OCIdleState = ; //TIM 互补输出比较状态
//TIM_OCInitStructure.TIM_OCNIdleState = ; //Idle 状态下 TIM 输出比较引脚状态
//TIM_OCInitStructure.TIM_OCNPolarity = ; //指定互补输出极性
//TIM_OCInitStructure.TIM_OutputNState = ; //Idle 状态下 TIM 输出比较引脚状态
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //TIM 输出比较模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性低
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
//TIM_OCInitStructure.TIM_Pulse = ; //捕获/比较寄存器 CCR 的值
TIM_OC1Init(TIM14, &TIM_OCInitStructure);
- 使能OC预装载寄存器,TIM_OC1PreloadConfig
- 使能ARR预装载寄存器,TIM_ARRPreloadConfig
- 可选:高级定时器虽然和通用定时器类似,但是高级定时器要想输出 PWM ,必须还要设置一个MOE位(TIMx_BDTR的第15位),以使能主输出,否则不会输出 PWM 。库函数设置的函数为:
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState); - 使能TIM,TIM_Cmd。
代码
void TIM14_PWM_Init(uint32_t arr, uint32_t psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
//使能时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource9, GPIO_AF_TIM14); //复用
//GPIOF初始化
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOF, &GPIO_InitStructure);
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = arr;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
//TIM_TimeBaseInitStructure.TIM_RepetitionCounter = ; only TIM1 TIM8
TIM_TimeBaseInit(TIM14, &TIM_TimeBaseInitStructure);
//only for TIM1 and TIM8 仅高级定时器用
//TIM_OCInitStructure.TIM_OCIdleState = ; //TIM互补输出比较状态
//TIM_OCInitStructure.TIM_OCNIdleState = ; //Idle状态下TIM输出比较引脚状态
//TIM_OCInitStructure.TIM_OCNPolarity = ; //指定互补输出极性
//TIM_OCInitStructure.TIM_OutputNState = ; //Idle状态下TIM输出比较引脚状态
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //TIM输出比较模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性低
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
//TIM_OCInitStructure.TIM_Pulse = ; //捕获/比较寄存器CCR的值,此程序在后面设置以控制LED的亮度
TIM_OC1Init(TIM14, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //使能预装载寄存器
TIM_ARRPreloadConfig(TIM14,ENABLE); //ARPE使能
TIM_Cmd(TIM14, ENABLE); //使能TIM14
}
main函数
int main()
{
uint8_t led_dir = 1; //调整增/减,1为递增,0为递减
uint32_t led_crr = 0;
delay_init(168);
LED_init();
TIM14_PWM_Init(500 - 1, 84 - 1); //定时器时钟为 84M ,分频系数为 84 ,所以计数频率
//为 84M/84=1Mhz, 重装载值 500 ,所以 PWM 频率为 1M/500=2Khz.
while(1)
{
delay_ms(10);
if(led_dir)
{
led_crr++;
}
else {
led_crr--;
}
if(led_crr > 30)
{
led_dir = 0;
}
if(led_crr == 0)
{
led_dir = 1;
}
TIM_SetCompare1(TIM14, led_crr); //修改比较值,即修改占空比
}
}