目录
前言
用 TIM3 的通道 2,把通道 2 重映射到 PB5,产生 PWM 来控制 LED0 的亮度。
一、PWM 简介
脉冲宽度调制(PWM),“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制。
而PWM输出就是对外输出脉宽(即占空比)可调的方波信号,信号频率由自动重装寄存器ARR 的值决定,占空比由比较寄存器 CCR 的值决定。
STM32 的定时器除了 TIM6、7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1、TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4 路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出。
1.1、PWM输出模式—分类
PWM 模式分为两种,PWM1 和 PWM2;
以PWM1模式,计数器CNT计数的方向不同还分为边沿对齐模式和中心对齐模式。PWM信号主要都是用来控制电机,一般的电机控制用的都是边沿对齐模式,FOC 电机一般用中心对齐模式。
边沿对齐时,CNT只工作在递增或者递减。中心对齐时,CNT工作在递增和递减。
1.2、PWM边沿对齐模式
在递增计数模式下,计数器从 0 计数到自动重载值(TIMx_ARR 寄存器的内容),然后重新从 0 开始计数并生成计数器上溢事件。
在边沿对齐模式下,计数器 CNT 只工作在一种模式,递增或者递减模式。以 CNT工作在递增模式为例,在中ARR=8,CCR=4,CNT 从 0 开始计数,当 CNT<CCR的值时,OCxREF 为有效的高电平,于此同时,比较中断寄存器 CCxIF 置位。当 CCR=CNT≤ARR时,OCxREF 为无效的低电平。然后 CNT 又从 0 开始计数并生成计数器上溢事件,以此循环往复。
1.3、PWM 中心对齐模式
在中心对齐模式下,计数器 CNT 是工作做递增/递减模式下。开始的时候,计数器 CNT 从 0 开始计数到自动重载值减 1(ARR-1),生成计数器上溢事件;然后从自动重载值开始向下计数到 1 并生成计数器下溢事件。之后从 0 开始重新计数。
图 PWM1 模式的中心对齐波形是PWM1模式的中心对齐波形,ARR=8,CCR=4。
第一阶段计数器 CNT 工作在递增模式下,从 0 开始计数,当 CNT<CCR 时,OCxREF为有效的高电平,当 CCR≤CNT<<ARR时,OCxREF 为无效的低电平。
第二阶段计数器CNT工作在递减模式下,从ARR的值开始递减,当CNT>CCR时,OCxREF为无效低电平,当CCR≥CNT≥1时,OCxREF为有效的高电平。
在波形图上我们把波形分为两个阶段:
第一个阶段是计数器 CNT 工作在递增模式的波形,这个阶段我们又分为 ① 和 ② 两个阶段;
第二个阶段是计数器 CNT 工作在递减模式的波形,这个阶段我们又分为 ③ 和 ④ 两个阶段。
中心对齐模式下的波形特征是 ① 和 ③ 阶 段的时间相等,② 和 ④ 阶段的时间相等。
中心对齐模式又分为中心对齐模式1、2、3,三种:具体由寄存器 CR1 位 CMS[1:0] 配置。
区别就是比较中断中断标志位 CCxIF 在何时置 1:中心模式 1 在 CNT 递减计数的时候置 1,中心对齐模式 2 在 CNT 递增计数时置 1,中心模式 3 在 CNT 递增和递减计数时都置1。
二、PWM工作过程
2.1、通道一输出工作过程
CCR1:捕获比较(值)寄存器(x=1,2,3,4):设置比较值。
CCMR1: OC1M[2:0]位:
对于PWM方式下,用于设置PWM模式1【110】或者PWM模式2【111】
CCER:CC1P位:输入/捕获1输出极性。0:高电平有效,1:低电平有效。
CCER:CC1E位:输入/捕获1输出使能。0:关闭,1:打开。
三、PWM寄存器配置
捕获 /比较模式寄存器 (TIMx_CCMR1/2)、捕获/比较使能寄存器(TIMx_CCER)、捕获/比较寄存器(TIMx_CCR1~4)。
3.1、捕获/比较模式寄存器(TIMx_CCMR1/2)
该寄存器分别是TIMx _CCMR1 和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2 控制 CH3 和 4。
该寄存器的有些位在不同模式下,功能不一样,所以在图 14.4.8 中,我们把寄存器分了 2 层,上面一层对应输出而下面的则对应输入。
模式设置位 OCxM,此部分由 3 位组成。 总共可以配置成 7 种模式,我们使用的是 PWM 模式,所以这 3 位必须设置为 110/111。这两种 PWM 模式的区别就是输出电平的极性相反。
3.2、捕获/比较使能寄存器(TIMx_CCER)
该寄存器控制着各个输入输出通道的开关。
这里只用到了 CC2E 位,该位是输入/捕获 2 输出使能位,要想 PWM 从 IO 口输出,这个位必须设置为 1,所以我们需要设置该位为 1。
3.3、捕获/比较寄存器(TIMx_CCR1~4)
该寄存器总共有 4 个,对应 4 个输通道 CH1~4;这 4 个寄存器都差不多。
在输出模式下,该寄存器的值与 CNT 的值比较,根据比较结果产生相应动作。利用这点, 我们通过修改这个寄存器的值,就可以控制 PWM 的输出脉宽了。
四、重映射功能
利用 TIM3 的 CH2 输出 PWM 来控制 DS0 的亮度,但是 TIM3_CH2 默认是接在 PA7 上面的,而我们的 DS0 接在 PB5 上面,如果普通 MCU,可能就只能用飞线把 PA7 飞到 PB5 上来实现了。但是 STM32,可以通过重映射功能,把 TIM3_CH2 映射到 PB5 上。
STM32 的重映射控制是由复用重映射和调试 IO 配置寄存器(AFIO_MAPR)控制的
TIM3_REMAP 是由[11:10]这 2 个位控制的;
默认条件下,TIM3_REMAP[1:0]为 00,是没有重映射的,所以 TIM3_CH1~TIM3_CH4 分别是接在 PA6、PA7、PB0 和 PB1 上的,而我们想让 TIM3_CH2 映射到 PB5 上,则需要设置 TIM3_REMAP[1:0]=10,即部分重映射,这里需要注意,此时 TIM3_CH1 也被映射到 PB4 上了。
五、程序设计
输出步骤 :
1.使能定时器3和相关IO口时钟。
使能定时器3时钟:RCC_APB1PeriphClockCmd();
使能GPIOB时钟:RCC_APB2PeriphClockCmd();
2.初始化IO口为复用功能输出。函数:GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
3.这里我们是要把PB5用作定时器的PWM输出引脚,所以要重映射配置,
所以需要开启AFIO时钟。同时设置重映射。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
4.初始化定时器:ARR,PSC等:TIM_TimeBaseInit();
5.初始化输出比较参数:TIM_OC2Init();
6.使能预装载寄存器: TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
7.使能定时器。TIM_Cmd();
8.不断改变比较值CCRx,达到不同的占空比效果:TIM_SetCompare2();
程序设计:
time.c文件
#include "timer.h"
#include "led.h"
#include "usart.h"
/*
1,增加TIM3_PWM_Init函数。
2,增加LED0_PWM_VAL宏定义,控制TIM3_CH2脉宽
*/
/*
通用定时器3中断初始化
这里时钟选择为APB1的2倍,而APB1为36M
arr:自动重装值。
psc:时钟预分频数
这里使用的是定时器3!
*/
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外设
}
/*定时器3中断服务程序*/
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源
LED1=!LED1;
}
}
/*
TIM3 PWM部分初始化
PWM输出初始化
arr:自动重装值
psc:时钟预分频数
*/
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
//设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//初始化TIM3 Channel2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
timer.h程序如下所示:
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
void TIM3_Int_Init(u16 arr,u16 psc);
void TIM3_PWM_Init(u16 arr,u16 psc);
#endif
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
int main(void)
{
u16 led0pwmval=0;
u8 dir=1;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
TIM3_PWM_Init(899,0); //不分频。PWM频率=72000000/900=80Khz
while(1)
{
delay_ms(10);
if(dir)led0pwmval++;
else led0pwmval--;
if(led0pwmval>300)dir=0;
if(led0pwmval==0)dir=1;
TIM_SetCompare2(TIM3,led0pwmval);
}
}