STM32F4定时器与PWM
STM32F4除了基本定时器TIM6和TIM7,其他定时器都可以产生PWM输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出(TIM9-TIM14最多能产生2路)。
PWM的输出其实就是对外输出脉宽可调(即占空比调节)的方波信号,信号频率是由自动重装寄存器 ARR 的值决定,占空比由比较寄存器 CCR 的值决定。
PWM模式:
PWM输出比较模式总共有8种,最常用的两种PWM输出模式:PWM1和PWM2。
PWM模式根据计数器CNT计数方式,可分为边沿对齐模式和中心对齐模式。PWM边沿对齐模式
当 TIMx_CR1 寄存器中的 DIR 位为低时执行递增计数,计数器CNT从 0 计数到自动重载值(TIMx_ARR 寄存器的内容),然后重新从 0 开始计数并生成计数器上溢事件。以 PWM 模式 1 为例。只要TIMx_CNT < TIMx_CCRx, PWM 参考信号 OCxREF 便为 高电平,否则为低电平。如果 TIMx_CCRx 中的比较值大于自动重载值(TIMx_ARR 中),则 OCxREF 保持为“ 1”。如果比较值为 0, 则 OCxREF 保持为“ 0”。
当 TIMx_CR1 寄存器中的 DIR 位为高时执行递减计数,计数器CNT从自动重载值(TIMx_ARR 寄存器的内容)递减计数到0,然后重新从TIMx_ARR值开始计数并生成计数器下溢事件。以 PWM 模式 1 为例。只要TIMx_CNT >TIMx_CCRx, PWM 参考信号 OCxREF 便为 低电平,否则为高电平。如果 TIMx_CCRx 中的比较值大于自动重载值(TIMx_ARR 中),则 OCxREF 保持为“ 1”。此模式下不能产生0%的PWM波形。
PWM中心对齐模式
在中心对齐模式下,计数器 CNT 是工作做递增/递减模式下。开始的时候, 计数器CNT 从 0 开始计数到自动重载值减 1(ARR-1),生成计数器上溢事件;然后从自动重载值开始向下计数到 1 并生成计数器下溢事件。之后从 0 开始重新计数。
以ARR=8,CCRx=4为例进行介绍。第一阶段计数器CNT工作在递增计数方式,从0开始计数,当TIMx_CNT < TIMx_CCRx时,PWM 参考信号 OCxREF为高电平,当TIMx_CNT >= TIMx_CCRx时,PWM 参考信号 OCxREF为低电平。第二阶段计数器CNT工作在递减计数方式,从ARR开始递减计数,当TIMx_CNT > TIMx_CCRx时,PWM 参考信号 OCxREF为低电平,当TIMx_CNT <= TIMx_CCRx时,PWM 参考信号 OCxREF为高电平。
PWM输出配置步骤
(1)使能定时器及端口时钟,并设置引脚复用器映射
可选的参数在 stm32f4xx_gpio.h
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE);
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14);
(2)初始化定时器参数,包含自动重装值,分频系数,计数方式等
voidTIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
(3)初始化PWM输出参数,包含PWM模式、输出极性,使能等
void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
/typedef struct
{
uint16_t TIM_OCMode; //比较输出模式
uint16_t TIM_OutputState; //比较输出使能
uint16_t TIM_OutputNState; //比较互补输出使能
uint32_t TIM_Pulse; //脉冲宽度
uint16_t TIM_OCPolarity; //输出极性
uint16_t TIM_OCNPolarity; //互补比较输出极性
uint16_t TIM_OCIdleState; //空闲状态下比较输出状态
uint16_t TIM_OCNIdleState; //空闲状态下比较输出状态
} TIM_OCInitTypeDef; **/
(4)开启定时器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);TIM_Cmd(TIM4,ENABLE); //开启定时器
为了使代码完整可以使能定时器在CCR1上的预装载寄存器以及使能定时器ARR上的装载寄存器,也可以不用使能
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
(5)修改TIMx_CCRx的值控制占空比
通常在主函数中对其进行调用
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint32_t Compare1);
pwm源文件:
#include "pwm.h"
void TIM14_CH1_PWM_Init(u16 per,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义复用管教9结构体变量
TIM_TimeBaseInitTypeDef TIMBaseInitStructure; //定时器参数结构体
TIM_OCInitTypeDef TIM_OCINitStructure; //pwm输出参数结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE); //使能定时器14时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE); //使能端口时钟,因为定时器14的pwm在通道1,是挂接在AHB的GPIOF9上的
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); //将PF9映射到TIM4通道1的pwm功能上
//初始化PF9端口
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF; //复用输出模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//管脚设置F9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度为100M
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化结构体
//初始化定时器参数
TIMBaseInitStructure.TIM_Period =per; //自动重装载值 周期
TIMBaseInitStructure.TIM_Prescaler = psc; //预分频系数(时钟源)
TIMBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分割,时钟分频 通常不需要修改
TIMBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数模式选择向上计数
TIM_TimeBaseInit(TIM14,&TIMBaseInitStructure); //初始化定时器中断
//初始化PWM输出参数
TIM_OCINitStructure.TIM_OCMode = TIM_OCMode_PWM1; //比较输出模式:pwm模式
TIM_OCINitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性
TIM_OCINitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OC1Init(TIM14,&TIM_OCINitStructure);
TIM_OC1PreloadConfig(TIM14,ENABLE);//使能定时器14在CCR1上的预装载寄存器
TIM_ARRPreloadConfig(TIM14,ENABLE); // 使能定时器14ARR上的装载寄存器
TIM_Cmd(TIM14,ENABLE); //开启定时器TIM14
}
主函数:
#include "led.h"
#include "system.h"
#include "SysTick.h"
#include "key.h"
#include "pwm.h"
int main()
{
u8 fx = 0;
u16 i = 0;
SysTick_Init(168);
LED_Init();
TIM14_CH1_PWM_Init(500-1,84-1);//84MHz分频为84即1MHz为1us,计数500次为500us,频率为2KHz
while(1)
{
if(fx == 0){ //fx用于对亮暗改变方向
i++; //用于比较函数
if(i == 300){ //在计数到300的时候改变计数方向(增减)
fx = 1;
}
}else{
i--;
if(i == 0){
fx = 0;
}
}
TIM_SetCompare1(TIM14,i); //pwm用于比较的函数,以此来控制占空比的值
myDelay_ms(10);
}
}