知识回顾
本节知识需要了解通用定时器原理的前提下进行学习
我们将通用定时器分为四个部分:
1,选择时钟
2,时基电路
3,输入捕获
4,输出比较
本节定时器PWM输出主要涉及到定时器框图右下方部分,即输出比较部分
和上一讲相同,时基时钟来源于内部默认时钟
对此有疑问请参考 : 定时器中断实验 中 定时器时钟选择部分 和 定时器时钟来源部分
什么是PWM
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。
PWM工作过程
每个定时器有四个通道,每一个通道都有一个捕获比较寄存器,
将寄存器值和计数器值比较,通过比较结果输出高低电平,实现PWM信号
先简单说明一下:
如图为向上计数:
定时器重装载值为ARR,比较值CCRx
t时刻对计数器值和比较值进行比较
如果计数器值小于CCRx值,输出低电平
如果计数器值大于CCRx值,输出高电平
PWM的一个周期
定时器从0开始向上计数
当0-t1段,定时器计数器TIMx_CNT值小于CCRx值,输出低电平
t1-t2段,定时器计数器TIMx_CNT值大于CCRx值,输出高电平
当TIMx_CNT值达到ARR时,定时器溢出,重新向上计数...循环此过程
至此一个PWM周期完成
影响因素
ARR : 决定PWM周期(在时钟频率一定的情况下,当前为默认内部时钟CK_INT)
CCRx : 决定PWM占空比(高低电平所占整个周期比例)
PWM工作过程(以通道1为例)
1,TIMx_CCMR1寄存器的OC1M[2:0]位,设置输出模式控制器
110:PWM模式1
111:PWM模式2
2,计数器值TIMx_CNT与通道1捕获比较寄存器CCR1进行比较,通过比较结果输出有效电平和无效电平
OC1REF=0 无效电平
OC1REF=1 无效电平
3,通过输出模式控制器产生的信号
TIMx_CCER寄存器的CC1P位,设置输入/捕获通道1输出极性
0:高电平有效
1:低电平有效
4,TIMx_CCER:CC1E位控制输出使能电路,信号由此输出到对应引脚
0:关闭
1:开启
PWM如何输出高低电平
计数器值TIMx_CNT与捕获比较寄存器值CCRx比较后,最终输出高电平还是低电平,
由TIMx_CCMR1:OC1M位和TIMx_CCER:CC1P位共同决定
1,TIMx_CCMR1寄存器的OC1M[2:0]位,设置PWM模式1或模式2
通过设置模式1或模式2,决定了比较结果输出有效或无效电平
2,TIMx_CCER寄存器的CC1P位,设置输入/捕获通道1输出极性
通过设置输出极性,确定有效或无效电平为最终输出的高电平或低电平
总结:
模式1:
CNT<CCR为有效电平 (OC1REF = 1)
CNT>CCR为无效电平 (OC1REF = 0)
模式2:
CNT<CCR为无效电平 (OC1REF = 0)
CNT>CCR为有效电平 (OC1REF = 1)
CC1P:
0:高电平有效
1:低电平有效
PWM模式配置
TIM_OC1PreloadConfig函数:
作用:TIM_CCMRx寄存器OCxPE位使能相应的预装在寄存器
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
TIM_ARRPreloadConfig函数:
作用:操作TIMx_CR1寄存器ARPE位,使能自动重装载的预装载寄存器
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
ARPE的使能-ARR变更生效配置
ARPE=1,ARR立即生效
ARPE=0,ARR下周期生效
定时器3输出通道引脚
定时器3的4个通道对应的引脚及重映射
PWM输出库函数
1,定时器通道初始化-TIM_OC1Init
经过上面的讲解,我们知道了要想使用PWM需要配置
配置参数对应框图位置如下:
1,TIMx_CCMR1寄存器的OC1M[2:0]位,设置输出模式控制器
2,TIMx_CCER寄存器的CC1P位,设置输入/捕获通道1输出极性
3,TIMx_CCER:CC1E位控制输出使能电路,信号由此输出到对应引脚
初始化定时器输出比较通道
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
TIM_OCInitTypeDef结构体
typedef struct
{
uint16_t TIM_OCMode; // PWM模式1或者模式2
uint16_t TIM_OutputState; // 输出使能 OR失能
uint16_t TIM_OutputNState; // PWM输出不需要
uint16_t TIM_Pulse; // 比较值,写CCRx
uint16_t TIM_OCPolarity; // 比较输出极性
uint16_t TIM_OCNPolarity; // PWM输出不需要
uint16_t TIM_OCIdleState; // PWM输出不需要
uint16_t TIM_OCNIdleState; // PWM输出不需要
} TIM_OCInitTypeDef;
2,设置比较值函数-TIM_SetCompare1
作用:外部改变TIM_Pulse值,即改变CCR的值
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
3,使能输出比较预装载-TIM_OC1PreloadConfig
作用:TIM_CCMRx寄存器OCxPE位使能相应的预装在寄存器
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
4,使能自动重装载的预装载寄存器允许位-TIM_ARRPreloadConfig
作用:操作TIMx_CR1寄存器ARPE位,使能自动重装载的预装载寄存器
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
5,修改通道极性
作用:操作TIMx_CCER的CC1P位,修改通道极性
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
PWM输出实验
使用定时器3初始PWM信号,输出占空比可变的PWM波驱动LED(PB5引脚),实现LED亮度变换
LED:低电平点亮,高电平熄灭,占空比越大,一个周期中高电平持续时间越长,亮度越大,反之越暗.
查找手册PB5引脚为定时器3的通道2,需要部分重映射
PWM输出实验步骤
1,使能定时器3和相关IO时钟(LED-PB5)
使能定时器3时钟:RCC_APB1PeriphClockCmd();
使能GPIOB时钟:RCC_APB2PeriphClockCmd();
2,初始化IO口为复用功能输出 GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
3,PB5输出PWM(定时器3通道2),需要部分冲突映射
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO时钟设置
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);//部分重映射
4,初始化定时器 (重装载值ARR,与分频系数PSC等)
TIM_TimeBaseInit();//决定PWM周期
5,初始化输出比较参数:
TIM_OC2Init();//通道2输出比较初始化
6,使能预装载寄存器
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);//定时器3 通道2
7,使能定时器
TIM_Cmd();
8,不断改变比较值CCRx,达到不同的占空比效果
TIM_SetCompare2(); //通道2,改变比较值CCRx
代码实现
基于 定时器中断实验 代码进行编写
timer.h添加PWM初始化函数定义 void TIM3_PWM_Init(u16 arr,u16 psc);
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
void TIM3_PWM_Init(u16 arr,u16 psc);
#endif
time.c 实现定时器PWM初始化函数
#include "timer.h"
#include "led.h"
#include "usart.h"
//TIM3 PWM初始化
//arr 重装载值
//psc 预分频系数
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 使能定时器3时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 使能GPIOB时钟(LED在BP5引脚),使能AFIO时钟(定时器3通道2需要重映射到BP5引脚)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
// 初始化IO口为复用功能TIM3_CH2->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);
// Timer3部分重映射 TIM3_CH2->PB5
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
// 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);
// TIM3_CH2 PWM初始化 配置结果: CNT>CCR时输出高电平
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; // PWM模式2:CNT>CCR时输出有效
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;// 设置极性-有效为高电平
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
// 使能预装载
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
// 使能定时器3
TIM_Cmd(TIM3, ENABLE);
}
main.c 改变CCR值实现PWM占空比变化,小灯量暗变化
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "timer.h"
int main(void)
{
u16 led0pwmval=0; // 设置CCR值
u8 dir=1; // 设置方向 0:变暗 1:变亮
delay_init(); // 延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 定时器中断优先级分组为2
LED_Init(); // LED初始化
// 设置自动装载值899,预分频系数0(不分频)
// 使用内部时钟,不分频=72000000
// PWM时钟频率=72000000/(899+1) = 80KHZ
TIM3_PWM_Init(899,0);
while(1)
{
delay_ms(10);
if(dir)led0pwmval++; // 由暗变亮
else led0pwmval—; // 由亮变暗
if(led0pwmval>300)dir=0; // 已达到最亮,开始变暗
if(led0pwmval==0)dir=1; // 已达到最暗,开始变亮
TIM_SetCompare2(TIM3,led0pwmval); //修改定时器3通道2比较值CCR
}
}
总结:
使用定时器默认时钟-内部时钟CK_INT=72MHZ
ARR=899 PSC=0 设置