STM32F4X 定时器PWM
上节说到了定时器的定时功能,这节说一下定时器跟GPIO结合,输出PWM波形的功能。
什么是PWM
PWM是Pulse Width Modulation 的缩写,也叫脉冲宽度调节,PWM被广泛应用在产品中,比如常用的有LED灯调节、电机脉冲调节等。PWM有以下几个参数需要了解,分别是周期、高电平持续时间、低电平持续时间和占空比。下面就来简单了解一下PWM的概念。
T:PWM周期,周期是指高低电平的持续时间为一个周期。
T1:高电平时间
T2:低电平时间
占空比:占空比为 (高电平时间 / 周期) * 100%
比如上图中,T为1ms,T1为600us,T2为400us,那么占空比就为 600 / (1 * 1000) * 100% = 60%
STM32F4X PWM使用
GPIO引脚复用
前面的章节讲过,STM32F4X的GPIO除了用作普通的输入输出接口之外,还可以复用成其他的功能。本节就将GPIO接口复用成定时器模式,具体可以在STM32F4的数据手册中找到GPIO的复用表。下面就以PF9为例,下面的表格可以看到,STM32F4X的PF9引脚可以复用成定时器14的通道1,所以后面的例程就用定时器14的通道1来输出PWM波形。
STM32F4X PWM配置参数
定时器PWM输出通道
在STM32F4X中,每个定时器都会有几路PWM的输出,具体是使用哪一路的PWM要根据GPIO的复用表格进行确定,像PF9在表格中就复用成了TIM14_CH1,也就是定时器的通道1,在后面的编程中要注意通道不能用错。
定时器PWM输出模式
在定时器的PWM模式中,有两种输出模式,分别是PWM1和PWM2,这两种模式有以下区别。
TIMx_CNT表示定时器当前计数器的值
TIMx_CCRn表示捕获/比较寄存器的值
定时器PWM有效电平
有效电平通常要跟上面所说的PWM模式结合来使用,在STM32F4X中的PWM有效电平可以配置成高电平有效或者低电平有效。
定时器PWM工作过程
下面来简单讲一下STM32F4X的PWM工作过程。以PWM模式2、定时器向上计数模式和有效电平为高电平的情况来进行讲解。如下图所示
CNT代表定时器当前计数值
ARR为重装载寄存器值
CCRx为捕获/比较寄存器值
通过定时器PWM输出模式的表格可以知道,在向上计数模式和PWM2模式下,当CNT<CCR时,电平无效,而我们的有效电平设了高电平有效,也就是CNT<CCR时,输出低电平。反之,当CNT>CCR时,电平有效,输出高电平。也就是上图会输出如下的PWM波形。
定时器PWM频率计算
在向上计数模式、PWM1模式和高电平有效的条件下。假设定时器时钟源为84MHZ,定时器分频系数为83(PSC),重装载值为5999(ARR),输出比较寄存器CCR为2000,通过下面公式可以算出定时器的中断频率。
Tout:定时器溢出值
Tclk:84MHZ
psc:83
arr:5999
Tout = (1 / (Tclk / (psc + 1))) * (arr + 1)
Tout等于1us中断一次
就可以算出PWM的周期为6000 * 1us = 6000us = 6ms。
而高电平的时间就为CCR * 1us = 2000us = 2ms
占空比就为2 / 6 * 100% = 33%
STM32F4X PWM配置步骤
- 使能GPIO时钟和定时器时钟
- 配置GPIO引脚为定时器模式
- 配置定时器工作频率
- 配置定时器PWM模式
- 使能定时器
- 如果需要可以修改PWM占空比
经过上面的步骤就可以使用PWM功能了。
STM32F4X PWM配置函数
/*
定时器初始化函数
TIMx:定时器索引
TIM_TimeBaseInitStruct:定时器初始化结构体
*/
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
/*
定时器通道初始化函数
TIMx:定时器索引
TIM_OCInitStruct:比较输出初始化结构体
*/
void TIM_OCXInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct)
/*
定时器比较初始化寄存器设置函数
TIMx:定时器索引
Comparex:CCR值
*/
void TIM_SetComparex(TIM_TypeDef* TIMx, uint32_t Comparex)
PWM程序例程
void bsp_pwm_init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE); // 使能定时器14时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); //使能PORTF时钟
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; //GPIOF9
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOF,&GPIO_InitStruct); //初始化PF9
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); // PF9复用成TIM14
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInitStruct.TIM_Period = 999; // 重装值为1000 1ms中断一次
TIM_TimeBaseInitStruct.TIM_Prescaler = 83; // 预分配值为84 即84000000/84 = 1MHZ 1us计一次书
TIM_TimeBaseInit(TIM14,&TIM_TimeBaseInitStruct); // 初始化定时器14
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // 有效电平为高电平
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // 使能比较输出
TIM_OCInitStruct.TIM_Pulse = 300; // CCR值为300
TIM_OC1Init(TIM14,&TIM_OCInitStruct);
TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //使能TIM14在CCR1上的预装载寄存器
TIM_ARRPreloadConfig(TIM14,ENABLE);//ARPE使能
TIM_Cmd(TIM14,ENABLE); // 使能定时器14
}
int main(void)
{
int dir,led0pwmval = 0;
NVIC_PriorityGroupConfig(2);
system_tick_init();
bsp_usart_init(115200);
bsp_pwm_init();
while(1){
delay_ms(1);
if(dir)
led0pwmval++;//dir==1 led0pwmval递增
else
led0pwmval--; //dir==0 led0pwmval递减
if(led0pwmval>1000)
dir=0;//led0pwmval到达1000后,方向为递减
if(led0pwmval==0)
dir=1; //led0pwmval递减到0后,方向改为递增
TIM_SetCompare1(TIM14,led0pwmval); //修改比较值,修改占空比
}
}