1、STM32F407ZGT6的通用定时器&PWM输出简介
STM32F407ZGT6有高级定时器、通用用定时器、基本定时器,不管什么类型的定时器都有定时的功能,不管什么类型的定时器都有时基单元。通用定时有TIM2~TIM5,TIM9~TIM14。
通用定时器和基本定时器的最主要的区别是:是否有“捕获/比较通道”(capture/compare channels)。
“捕获/比较通道”顾名思义,可以是“输入捕获功能”,可以是“输出比较功能”,“捕获/比较通道”的功能只能是“捕获”或者“比较”两者一种。
2、PWM简介
占空比:IO口的输出脉冲的高电平时间宽度占整个脉冲周期的百分比。PWM的主要作用是调节占空比。
常见:手机背光调节、呼吸灯、手机马达震动强度调节。
3、STM32F407ZGT6的通用定时器TIM10/TIM11 和TIM13/TIM14的框图讲解
通用定时器TIM10/TIM11 和TIM13/TIM14 的时基单元和基本定时器一致,本章节主要分析“比较输出”功能,屏蔽掉了“捕获输入”部分的框图。
“捕获比较寄存器”也有“缓冲”。
PWM占空比调节就是属于“比较输出”中的一个功能,该功能的工作原理如下:
- 提示:“捕获/比较寄存器1”对应下图的CCR1(capture/compare register 1),该寄存器是16位的。
- CNT在时钟节拍的基础下进行自增,CNT的值小于CCR1的值时,输出一个“电平”。
- CNT在时钟节拍的基础下进行自增,CNT的值大于等于CCR1的值时,输出一个“电平”。
- CNT的值计数到ARR的值时,则溢出,从0开始从新计数自增。
总结1:“比较输出”就是CNT寄存器不断的和CCR1进行比较,同时和ARR进行比较,根据比较的结果,输出“相应的电平”。
总结2:通过调节ARR的值可以调节输出脉冲的脉冲周期
总结3:通过调节CCR1的值可以调节占空比
4、通用定时器TIM10/TIM11 和TIM13/TIM14的寄存器
通用定时器TIM10/TIM11 和TIM13/TIM14的CR1、CNT、PSC、ARR寄存器和基本定时器6/7的一样。
4.1、TIM10/11/13/14 状态寄存器 (TIMx_SR)
位 1 CC1IF 捕获/比较 1 中断标志 (Capture/compare 1 interrupt flag)
如果通道 CC1 配置为输出:
当计数器寄存器的值与捕获/比较寄存器的值匹配时,此标志由硬件置 1。但需要通过软件清零。
0:不匹配。
1: TIMx_CNT 计数器的值与 TIMx_CCR1 寄存器的值匹配。当 TIMx_CCR1 的值大于 TIMx_ARR的值时, CC1IF 位将在计数器发生上溢时变为高电平。
位 0 UIF 更新中断标志 (Update interrupt flag)
该位有硬件置1,软件清零。
0:未发生更新(即:定时时间还没到)。
1:更新中断挂起,由以下两种因素导致该位硬件置1:
①定时器计数器上溢会导致该位置1(即该位置1表示定时时间到达)
②UG位置1也会导致该位置1(前提:TIMx_CR1 寄存器中 URS = 0 且 UDIS = 0)
总结:导致产生更新中断标志位置1并且产生更新中断的因素有两个。
定时器10的捕获比较中断函数
void TIM1_UP_TIM10_IRQHandler(void)
{
if(TIM10->SR&(1<<1))
{
TIM10->SR&=~(1<<1);
}
}
4.2、TIM10/11/13/14 事件生成寄存器 (TIMx_EGR)
位 1 CC1G 捕获/比较 1 生成 (Capture/compare 1 generation)
此位由软件置 1 以生成事件,并由硬件自动清零。
0:不执行任何操作。
1:捕获比较通道 1 上生成捕获/比较事件:
如果通道 CC1 配置为输出:
使能捕获/比较 1 中断 , CC1IF 标志置 1 并发送相应的中断。
位 0 UG 更新生成 (Update generation)
该位可通过软件置 1,并由硬件自动清零。
0:不执行任何操作。
1:重新初始化定时器计数器(TIMx_CNT)并生成寄存器更新事件。
4.3、TIM10/11/13/14 捕获/比较模式寄存器 1 (TIMx_CCMR1)
位 6:4 OC1M 输出比较 1 模式 (Output compare 1 mode)
110: PWM 模式 1,只要 TIMx_CNT < TIMx_CCR1,通道 1 便为有效状态,否则为无效状态。
111: PWM 模式 2,只要 TIMx_CNT < TIMx_CCR1,通道 1 便为无效状态,否则为有效状态。
位 3 OC1PE 输出比较 1 预装载使能 (Output compare 1 preload enable)
0:CCR1的影子寄存器无效
1:CCR1的影子寄存器有效
位 2 OC1FE 输出比较 1 快速使能 (Output compare 1 fast enable) 一般置1
位 1:0 CC1S 捕获/比较 1 选择 (Capture/Compare 1 selection)
00: CC1 通道配置为输出。
01: CC1 通道配置为输入, IC1 映射到 TI1 上。
举例:定时器10捕获比较通道1的输出比较模式为PWM模式1,CCR1的影子寄存器有效,CC1配置为输出
TIM10->CCMR1 &= ~(3<<0);
TIM10->CCMR1 |= 1<<1;
TIM10->CCMR1 |= 1<<2;
TIM10->CCMR1 |= 6<<4;
4.4、TIM10/11/13/14 捕获/比较使能寄存器 (TIMx_CCER)
位 1 CC1P CC1 通道配置为输出:
0: OC1 高电平有效
1: OC1 低电平有效
位 0 CC1E 捕获/比较 1 输出使能 (Capture/Compare 1 output enable)。
CC1 通道配置为输出:
0:关闭——OC1 未激活
1:开启——在相应输出引脚上输出 OC1 信号
举例:定时器10有效电平为低电平,开启OC1输出(开启PWM输出)
TIM10->CCER |= 1<<1;
TIM10->CCER |= 1<<0;
4.5、TIM10/11/13/14 捕获/比较寄存器 1 (TIMx_CCR1)
写入该寄存器的值,主要和TIMx_CNT的值进行比较,如果匹配,则输出相应的电平。
4.6、中断使能寄存器 (TIMx_DIER)
位 1 CC1IE 捕获/比较 1 中断使能 (Capture/Compare 1 interrupt enable)
0:禁止 CC1 中断。
1:使能 CC1 中断。
位 0 UIE 更新中断使能 (Update interrupt enable)
0:禁止更新中断。
1:使能更新中断。所谓的“更新中断”可以理解为“定时器中断”。
5、TIM10/11/13/14中断函数与中断通道
该表格主要查阅startup_stm32f40_41xxx.s 和 stm32f4xx.h/
6、PWM输出与GPIO的复用功能的关系
PWM输出是属于GPIO复用功能的一种,GPIO口有很多的复用功能,具体请参考STM32F407ZGT6.pdf】的P56。
7、PWM输出的配置步骤
- 使能相应的GPIO口的时钟
- 配置相应的GPIO口为复用功能推挽输出
- 选择复用功能
- 使能定时器的相应时钟,即RCC->APB1ENR的相应位置1
- TIMx_ARR 寄存器进行缓冲
- 计数器在发生更新事件时不会停止计数(循环计数,循环定时)
- 使能更新 (UEV)
- UG位置1重新初始化定时器计数器(TIMx_CNT)
- 状态寄存器清零
- 配置预分频值
- 配置自动重装载值 (用于控制脉冲的周期)
- CCMR1配置为PWM的模式
- CCR1影子寄存器有效
- CC1 通道配置为输出。
- 配置OC1 有效电平
- 捕获/比较 1 中断使能
- 使能定时器中断中断通道,调用NVIC_EnableIRQ函数,如果要调整抢占和响应的优先级,请参考第五章的内容。
- 捕获/比较 1 输出使能
- 使能定时器
改变CCR1的值可以改变占空比
8、软件设计
PWM输出驱动PF6,实现LED1以呼吸灯的效果闪烁
寄存器版
#include "stm32f4xx.h"
void TIM10_CH1Init(u16 psc,u16 arr)
{
RCC->APB2ENR |= 1<<17;//使能定时器10的时钟(168MHZ)
RCC->AHB1ENR |= 1<<5; //使能GPIOF口时钟
//设置GPIOF6模式为:复用功能模式
GPIOF->MODER &=~(3<<12); //清零
GPIOF->MODER |= 2<<12;
//将GPIOF6选择为AF3复用功能3→TIM10_CH1
GPIOF->AFR[0] &=~(0xF<<24); //清零
GPIOF->AFR[0] |= 3<<24;
TIM10->CR1 |= 1<<7;//使用TIM10_ARR 寄存器的“影子寄存器”。
TIM10->CR1 &=~(1<<3);//选择为循环定时模式
TIM10->CR1 &=~(1<<2);//选择更新请求源:计数器上溢、UG 位置 1都可以生成更新事件。
TIM10->CR1 &=~(1<<1);//允许产生更新事件
TIM10->PSC = psc - 1;//设置预分频值
TIM10->ARR = arr;//设置重装载值
TIM10->CCMR1 &=~(7<<4); //清零
TIM10->CCMR1 |= 6<<4;//选择PWM的模式:模式1
TIM10->CCMR1 |= 1<<3;//使用CCR1寄存器的影子寄存器
TIM10->CCMR1 &=~(3<<0);//通道1配置为输出
TIM10->CCER |= 1<<1;//选择有效电平为低电平
TIM10->CCR1 = 500;//设置CCR1的值。
TIM10->EGR |= 1<<0;//UG 位置 1,主动生成更新事件,初始化计数器和影子寄存器。
TIM10->SR = 0;//状态寄存器清零。
TIM10->DIER |= 1<<1; //使能捕获/比较中断
NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);//使能中断通道
TIM10->CCER |= 1<<0;//开启PWM输出
TIM10->CR1 |= 1<<0;//使能计数器
}
void TIM1_UP_TIM10_IRQHandler(void)
{
u16 compareValue = 0; //比较值
static u8 compareFlag = 1; //标志位,1:CCR1变大,0:CCR1变小
if(TIM10->SR &(1<<1)) //判断CNT 计数器的值与 CCR1 寄存器的值是否匹配,即是否相等
{
TIM10->SR &=~(1<<1);//清零
compareValue = TIM10->CCR1;
if(1 == compareFlag )
{
if(compareValue <= 900)
{
compareValue = compareValue +3;
TIM10->CCR1 = compareValue;
}
else
{
compareFlag =0;
}
}
if(0 == compareFlag )
{
if(compareValue >=50)
{
compareValue = compareValue -3;
TIM10->CCR1 = compareValue;
}
else
{
compareFlag =1;
}
}
}
}
int main(void)
{
//实现输出PWM驱动LED1灯:PWM周期为1ms,初始状态高电平和低电平各0.5ms。
//TIM10:168MHZ
TIM10_CH1Init(1680,1000);
while(1)
{
}
}
库函数版
#include "stm32f4xx.h"
void TIM10_CH1Init(u16 psc,u16 arr)
{
GPIO_InitTypeDef GPIO_InitTypeDeS;
TIM_TimeBaseInitTypeDef TIM10_TimeBaseInitTypeDef;
TIM_OCInitTypeDef PWM_OCInitTypeDef;
/*结构体定义*/
NVIC_InitTypeDef NVIC_InitStructure;
// /*时钟初始化*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE); //GPIO 时钟初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM10,ENABLE); //TIM 时钟初始化
/*GPIO 口模式配置*/
GPIO_InitTypeDeS.GPIO_Mode =GPIO_Mode_AF; //复用功能
GPIO_InitTypeDeS.GPIO_Pin =GPIO_Pin_6; //配置 GPIOF9
GPIO_Init(GPIOF,&GPIO_InitTypeDeS);
GPIO_PinAFConfig(GPIOF,GPIO_PinSource6,GPIO_AF_TIM10); //复用
//定时器模式配置
TIM10_TimeBaseInitTypeDef.TIM_ClockDivision=TIM_CKD_DIV1; // 时钟分频
TIM10_TimeBaseInitTypeDef.TIM_Period =arr;
TIM10_TimeBaseInitTypeDef.TIM_CounterMode=TIM_CounterMode_Up; //计数模式为向上计数
TIM10_TimeBaseInitTypeDef.TIM_Prescaler=psc-1;
TIM_TimeBaseInit(TIM10,&TIM10_TimeBaseInitTypeDef);
//PWM 模式配置
PWM_OCInitTypeDef.TIM_OCMode=TIM_OCMode_PWM1; //PWM2 模式
PWM_OCInitTypeDef.TIM_OutputState= TIM_OutputState_Enable; //使能比较输出
PWM_OCInitTypeDef.TIM_OCPolarity =TIM_OCPolarity_Low; //低电平有效电平
PWM_OCInitTypeDef.TIM_Pulse = 500;
TIM_OC1Init(TIM10,&PWM_OCInitTypeDef);
TIM_ITConfig(TIM10,TIM_IT_CC1,ENABLE); //③允许定时器10 更新中断
TIM_OC1PreloadConfig(TIM10,TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM10,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel=TIM1_UP_TIM10_IRQn; //定时器10中断
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);// ④初始化 NVIC
TIM_Cmd(TIM10,ENABLE); //使能定时器 10
}
void TIM1_UP_TIM10_IRQHandler(void)
{
u16 compareValue = 0; //比较值
static u8 compareFlag = 1; //标志位,1:CCR1变大,0:CCR1变小
if(TIM_GetITStatus(TIM10, TIM_IT_CC1) != RESET) //判断CNT 计数器的值与 CCR1 寄存器的值是否匹配,即是否相等
{
TIM_ClearFlag(TIM10, TIM_IT_CC1);
compareValue = TIM_GetCapture1(TIM10);;
if(1 == compareFlag )
{
if(compareValue <= 900)
{
compareValue = compareValue +3;
TIM_SetCompare1(TIM10,compareValue);
}
else
{
compareFlag =0;
}
}
if(0 == compareFlag )
{
if(compareValue >=50)
{
compareValue = compareValue -3;
TIM_SetCompare1(TIM10,compareValue);
}
else
{
compareFlag =1;
}
}
}
}
int main(void)
{
//实现输出PWM驱动LED1灯:PWM周期为1ms,初始状态高电平和低电平各0.5ms。
//TIM10:168MHZ
TIM10_CH1Init(1680,1000);
while(1)
{
}
}