PWM(Pulse Width Modulation)意为脉冲宽度调制,简称脉宽调制 。在工控行业,PWM信号可以用来调节电机转速、调节变频器以及BLDC电机驱动等;在LED照明行业,可以通过PWM来控制LED的亮暗变化;还可以通过PWM信号来控制无源蜂鸣器发出简单的声音等。
WB32也可输出PWM,需配置WB32上的定时器。
在WB32中有4个定时器,其中TIM1为高级定时器,TIMx(2-4)为通用定时器,这四个定时器均挂载在APB1总线上,每个定时器都可以同时输出4路PWM。
本章需要使用固件库TIM例程中的 TIM_PWM_Output 工程,以配置TIM1为例讲解如何配置TIM使之产生四路占空比不同的PWM,请大家打开固件库例程对应学习;理解此例程后,亦可配置通用定时器TIMx输出PWM。
9.1 TIM输出PWM配置
本例程中使用TIM1来输出4路PWM,通过查询WB32f10x数据表,可知TIM1通道1到通道4(TIM_CH1-TIM_CH4)依次为PA8、PA9、PA10、PA11。
9.1.1 开启对应端口和功能时钟
void RCC_Configuration(void)
{
/* 使能TIM1 GPIOA AFIO 时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BMX1 |
RCC_APB1Periph_TIM1 |
RCC_APB1Periph_GPIOA |
RCC_APB1Periph_AFIO ,
ENABLE);
}
9.1.2 初始化端口引脚
void GPIO_Configuration(void)
{
GPIO_Init(GPIOA, GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11, GPIO_MODE_AF | GPIO_AF1);
}
注意:
1)将对应引脚作为TIM功能使用时,需要使用引脚复用模式,且需与上GPIO_AF1。(上一章,UART引脚复用模式与上GPIO_AF7)
2)void GPIO_Configuration(void)与void RCC_Configuration(void)属于用户自己编写的函数,在使用时需要将其函数名称放在主函数前进行声明,否则在主函数中调用时可能会报错。
9.1.3 定时器基本结构体配置
/* 初始化定时器参数 */
TIM_TimeBaseStructure.TIM_Period = 0xFFFF; //配置定时器周期为0xFFFF,即65535,可设置范围为0~65535。
//自动重装载寄存器(ARR)的值累计TIM_Period个频率后产生一个更新或中断。
TIM_TimeBaseStructure.TIM_Prescaler = 0; //配置预分频器为0,可设置范围为0x0000~0xFFFF。
//驱动CNT计数器的时钟CK_CNT = CK_INT /(TIM_Prescaler+1)
//例:本例程中 CK_CNT = 72M/(0+1) = 72M
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //配置时钟分频因子为0,可选择1、2、4分频。配置死区时用到,故此处设置为0。
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //配置定时器计数模式为向上计数
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //配置重复计数器为0
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //初始化定时器基本结构体
格外注意,在这一部分配置当中,部分成员配置关乎输出PWM的两个重要特征:周期和频率。
1)周期T = arr* (1/CK_CNT)
根据公式。本例程中PWM周期 T = 65535*(1/72M)= 910.208us
2)频率f = CK_INT/[(arr+1)(psc+1)]
根据公式。本例程中PWM频率 f = 72M / [(65535+1) (0+1) ] = 1098.6Hz
注意:
1)在上述两个公式当中,大家可以将arr看作定时器基本结构体中成员TIM_Period的值。可以将psc看作定时器基本结构体中成员TIM_Prescaler的值。
2)CK_INT和CK_CNT的关系大家可以在代码注释中找到,其中CK_INT为系统内部时钟,默认为72M。想要深入了解CK_INT和CK_CNT的关系可以在WB32参考手册中第十六章高级控制定时器中的高级控制定时器框图中找到。
3)上述算得的PWM周期与频率可使用示波器验证,后续实验可依自己的需求,按照上述公式进行配置。
9.1.4 定时器输出比较结构体配置
/* PWM1 模式设置: 通道1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //比较输出模式选择为PWM1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = CCR1_Val; //脉冲宽度设置为CCR1_Val。此处决定着PWM的占空比。
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //配置比较输出极性为高电平有效。它决定着定时器通道的有效电平。
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //TIM通道1输出比较结构体初始化
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //TIM通道1输出比较预加载设置
/* PWM1 模式设置: 通道2 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = CCR2_Val; //脉冲宽度设置为CCR2_Val。
TIM_OC2Init(TIM1, &TIM_OCInitStructure); //TIM通道2输出比较结构体初始化
TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable); //TIM通道2输出比较预加载设置
准确的说,想要输出PWM,就必须使用到TIM的输出比较功能(Output Compare),在配置输出比较结构体中的成员时,还涉及到PWM第三个重要参数:占空比。
占空比 = CCRx_Val / arr
例:本例中,
TIM1_CH1的占空比 = CCR1_Val / arr = 0x7FFF / 0xFFFF = 32767 / 65535 = 0.4999 约为50%
TIM1_CH2的占空比 = CCR2_Val / arr = 0x3FFF / 0xFFFF = 16383 / 65535 = 0.2499 约为25%
注意:
1)CCRx_Val中,x可取(1、2、3、4),此部分通过宏定义取值,可以在main.c中第13行处找到。
2)本处仅展示TIM通道1与TIM通道2的配置代码,可以注意到,相较于配置TIM通道1输出PWM的代码,在配置TIM通道2输出PWM的代码时少了比较输出模式和比较输出极性的配置。
这是因为在结构体配置中,某些代码配置后其他代码也可共享此配置;但是建议新手在使用结构体时不要简化,还是按照配置配置TIM通道1一样配置其他通道。
3)结合定时器基础结构体配置,重点理解占空比的计算方法,并自己测算出TIM通道3与TIM通道4的占空比。
9.1.5 使能输出PWM配置
TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIM1预加载ARR
TIM_Cmd(TIM1, ENABLE); //使能TIM1计数器
TIM_CtrlPWMOutputs(TIM1, ENABLE); //主输出使能。当使用的是定时器功能时不需要此句。
9.2 实验现象
观察主函数部分,除去TIM配置函数。仅有:while (1) { }
将程序编译后烧录到开发板上,将示波器探头夹夹住开发板GND,此时我们可直接使用示波器来检测对应通道:
首先看TIM1通道1,用示波器探头的探针勾住PA8后,可看到PWM波形和信息如下:
再看TIM1通道2,用示波器探头的探针勾住PA9后,可看到PWM波形和信息如下:
注意:
1)请观察图片中TIM1通道1与TIM1通道2的波形信息是否对应例程代码的设置。
2)请简单了解并学会使用示波器,示波器是我们在使用WB32进行电子设计时必不可少的工具。