几个留待补充的程序:
1、高级定时器实现互补PWM波
2、同一TIM不同通道,产生相位不同的PWM波
3、高级定时器的输出比较产生PWM
之前在蓝桥的单片机比赛阶段对于PWM的一次又一次深入,印象颇深。而今到了嵌入式,更感觉尤为重要,这里也算做个小结,如有错误,不吝指教。
阅读此篇博文的同时,也可参阅之前写的一篇ST32之PWM小结。
几个带着写博客的问题回答下:
1、关于PWM的上限频率呢?
跟周期有关即PWM模式下的arr和输出比较中的pulse...,也不太好说,没具体环境,具体情况再分析吧。
2、PWM模式下同时可以几个通道输出呢?
有几个通道就能输出几个呗。
一、通用和高级定时器的PWM模式产生PWM
PWM模式的特点:同一定时器中,不同的通道下,输出的频率固定,占空比可变。
通用定时器PWM模式,TIM3_CH1 和 TIM3_CH2输出,引脚为PA6 PA7...工程代码移步Github<传送门>
1、通用定时器PWM模式输出主要代码
小于Pluse为高电平,大于Pluse小于ARR为低电平。
main.c
/*******************************************************************************
* 文件名:main.c
* 描 述:
* 作 者:CLAY
* 版本号:v1.0.0
* 日 期: 2019年1月25日
* 备 注:通用定时器输出硬件PWM PA6-TIM3_CH1 PA7-TIM3_CH2
* 并通过按键4改变占空比,初始占空比40%,每次加10%
*******************************************************************************
*/
#include "config.h"
#include "led.h"
#include "key.h"
#include "timer.h"
#include "beep.h"
#include "lcd.h"
#include "stdio.h"
#include "usart.h"
#include "i2c.h"
#include "eeprom.h"
#include "PWMMode.h"
int main(void)
{
STM3210B_LCD_Init();
LCD_Clear(Blue);
LEDInit();
KeyInit();
BeepInit();
TIM2Init(2000, 72);//定时2ms
TIM3_PWMInit(1000, 40, 80);//频率2K CH1占空比40% CH2占空比80%
while(1)
{
KeyDriver();
}
}
void KeyAction(int code)
{
static u32 x = 400;
if(code == 1)//按下B1,切换灯状态,蜂鸣器鸣叫0.1s
{
GPIOC->ODR ^= (1<<8);//PC8不断取反
GPIOD->ODR |= (1<<2);//PD2置1,使能573锁存器
GPIOD->ODR &= ~(1<<2);//PD2清0,关闭573锁存器
Beep(100);
}
else if(code == 2)
{
Beep(-1);
}
else if(code == 3)
{
Beep(0);
}
else if(code == 4)
{
x += 100;
TIM_SetCompare1(TIM3, x);//改变占空比
}
}
PWMMode.c
#include "PWMMode.h"
void TIM3_IOInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//***注意这里是APB2***
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //TIM3通道1-PA6 TIM3通道2-PA7
//***复用推挽输出(PWM模式)***
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIO
}
void TIM3Init(u32 arr, u8 ch1_duty, u8 ch2_duty)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
//***注意这里是APB1***
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟
TIM_TimeBaseStructure.TIM_Period = arr-1; //设置在自动重装载周期值
TIM_TimeBaseStructure.TIM_Prescaler = 72-1; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //初始化 TIM3
}
void TIM3_PWMInit(u32 freq, u8 ch1_duty, u8 ch2_duty)
{
u32 arr;
TIM_OCInitTypeDef TIM_OCInitStructure;
arr = 1000000 / freq;
TIM3_IOInit();//TIM3通道1-PA6 TIM3通道2-PA7配置
TIM3Init(arr, ch1_duty, ch2_duty);//TIM3定时器配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM1模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性高
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = (arr - 1) * ch1_duty / 100;
//TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);//预装载使能位无所谓,不影响
TIM_OC1Init( TIM3, &TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;//PWM2模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//输出极性低
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//比较输出使能
//***(arr-1)是总的计数值,而(arr-1)*占空比是电平反转时的计数值,故控制pluse就可以控制占空比***
TIM_OCInitStructure.TIM_Pulse = (arr - 1) * ch2_duty / 100;
//TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);//预装载使能位无所谓,不影响
TIM_OC2Init( TIM3, &TIM_OCInitStructure);
//***高级定时器必须有这一句,而通用就不必了***
//TIM_CtrlPWMOutputs(TIM3, ENABLE);//使能PWM输出
TIM_Cmd(TIM3, ENABLE); //使能 TIM3
}
PWMMode.h
#ifndef _PWMMODE_H
#define _PWMMODE_H
#include "config.h"
void TIM3_PWMInit(u32 freq, u8 ch1_duty, u8 ch2_duty);
#endif
2、通用定时器PWM模式输出需要注意的地方
通用定时器的硬件PWM模式,改变占空比通过TIM_SetCompare1
函数,注意最大数值是等于arr即周期值!
①、我们要用TIM3的CH1和CH2通道,这两个可以从数据手册(P19)查阅到对应的复用引脚是PA6和PA7。
②、我们要想使用定时器的PWM模式,配置流程需要遵循 IO引脚配置 -> 定时器配置 -> PWM模式配置的顺序。
③、IO引脚为PA,对应时钟总线在APB2,而TIM3对应时钟总线是APB1。
④、定时器配置中的TIM_OCInitStructure
结构体,我们通过程序溯源可以找到其成员变量,可以发现其共有8个,但是其中4个通用定时器无需配置,高级定时器才需要配置。
⑥、PWM配置中TIM_OCInitStructure
结构体的两个成员需要注意下,TIM_OCMode
和TIM_OCPolarity
。
前者
TIM_OCMode
取值有两个分别为TIM_OCMode_PWM1
和TIM_OCMode_PWM2
。后者取值也有两个TIM_OCPolarity_High
和TIM_OCPolarity_Low
PWM1和PWM2
参考正点原子的《STM32F1开发指南-寄存器版本_V3.1 》P255....
说白了,这两个都是管PWM初始状态的,惯用的搭配是TIM_OCMode_PWM1
和TIM_OCPolarity_High
一对,TIM_OCMode_PWM2
和TIM_OCPolarity_Low
一对,这样搭配是正逻辑,否则为反逻辑。什么意思呢?看下面的图吧。
主函数调用TIM3_PWMInit(1000, 40, 80);
I、CH1为TIM_OCMode_PWM1
和TIM_OCPolarity_High
,CH2为TIM_OCMode_PWM1
和TIM_OCPolarity_High
时:
是符合主函数调用逻辑的,故称之为正逻辑。
II、CH1为TIM_OCMode_PWM2
和TIM_OCPolarity_High
时:
此时占空比主函数调用时40%,而实际是60%,故称之为反逻辑。
III、CH1为TIM_OCMode_PWM2
和TIM_OCPolarity_Low
时:
此时变又是正逻辑了,所以记住固定搭配就行了。
⑦、关于PWMMode.c
中注释的一句
//***高级定时器必须有这一句,而通用就不必了***
//TIM_CtrlPWMOutputs(TIM3, ENABLE);//使能PWM输出
参考正点原子的《STM32F1开发指南-寄存器版本_V3.1 》P318....
只有高级定时器PWM输出时,必须加上这一句,而这里我们是通用定时器,所以没有加。
⑧、关于TIM_OCInitStructure
结构体重的TIM_Pulse
理解
(arr-1)是总的计数值,而(arr-1)*duty是电平反转时的计数值
,故pluse = 占空比
⑨、关于PWMMode.c
中的TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
用来使能输出/比较预装载使能位。
输出比较预装载使能位OCxPE位于捕获/比较模式寄存器TIMx_CCMRx中。
若使能了该位,写入到TIMx_CCRx寄存器的比较值将在更新事件到来时才会传入到当前捕获/比较寄存器,否则未使能,比较值将立即写入当前捕获比较寄存器。
在PWM模式下,预装载使能位
无论使能与否都不影响。
哈,注意了,当前捕获比较寄存器是一个名词。
每个通用定时器拥有4路捕获/比较寄存器,每路通道都拥有一个捕获/比较寄存器TIMx_CCRx用于装载比较值。该寄存器同样包括两个寄存器,一个是用户可以操作的:用于写入比较值;另一个是用于和计数器CNT进行比较的当前捕获比较寄存器。
对了,关于这个函数也有要说的,我们在程序中顺着这个函数寻找它的定义会找到
也许会有一个疑问,CH2的预装载使能位在手册上写的不是第11位吗?
怎么到了库函数里面变成了8,
莫急我们继续看看TIM_OCPreload,再寻找这个的定义
看,
原来使能的时候不是0x0001啊,而是0x0008,那么这么算起来刚好不就对应11位了么?完美~
附几个寄存器名,混个眼熟
既然上面详细介绍了通用定时器的PWM输出功能,这里也再附上高级定时器的PWM输出功能吧,方便日后查阅... 工程代码移步Github<传送门>
main.c
/*******************************************************************************
* 文件名:main.c
* 描 述:
* 作 者:CLAY
* 版本号:v1.0.0
* 日 期: 2019年1月26日
* 备 注:高级定时器PWM输出 PA9-TIM_CH2 PA10-TIM_CH3
* 并通过按键4增加占空比,初始占空比为40%,每次增加10%
*******************************************************************************
*/
#include "config.h"
#include "led.h"
#include "key.h"
#include "timer.h"
#include "beep.h"
#include "lcd.h"
#include "stdio.h"
#include "usart.h"
#include "i2c.h"
#include "eeprom.h"
#include "PWMMode.h"
#include "PWMMode_Advance.h"
int main(void)
{
STM3210B_LCD_Init();
LCD_Clear(Blue);
LEDInit();
KeyInit();
BeepInit();
TIM2Init(2000, 72);//定时2ms
TIM3_PWMInit(1000, 40, 80);//频率2K CH1占空比40% CH2占空比80%
TIM1_PWMInit(1000, 40, 80);//频率2K CH2占空比40% CH3占空比80%
while(1)
{
KeyDriver();
}
}
void KeyAction(int code)
{
static u32 x = 400;
if(code == 1)//按下B1,切换灯状态,蜂鸣器鸣叫0.1s
{
GPIOC->ODR ^= (1<<8);//PC8不断取反
GPIOD->ODR |= (1<<2);//PD2置1,使能573锁存器
GPIOD->ODR &= ~(1<<2);//PD2清0,关闭573锁存器
Beep(100);
}
else if(code == 2)
{
Beep(-1);
}
else if(code == 3)
{
Beep(0);
}
else if(code == 4)
{
x += 100;
TIM_SetCompare2(TIM1, x);//改变TIM1_CH2占空比
}
}
PWMMode_Advance.c
#include "PWMMode_Advance.h"
void TIM1_IOInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//***注意这里是APB2***
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA时钟
//***根据实际电路选用了USART1的复用功能。***(要注意的是,拔掉跳线帽PA9 PA10)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10; //TIM_CH2 - PA9 //TIM_CH3 - PA10
//***复用推挽输出(PWM模式)***
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIO
}
void TIM1Init(u32 arr, u8 ch1_duty, u8 ch2_duty)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
//***注意这里是APB2***
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //使能定时器1时钟
TIM_TimeBaseStructure.TIM_Period = arr-1; //设置在自动重装载周期值
TIM_TimeBaseStructure.TIM_Prescaler = 72-1; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //初始化 TIM1
}
void TIM1_PWMInit(u32 freq, u8 ch1_duty, u8 ch2_duty)
{
u32 arr;
TIM_OCInitTypeDef TIM_OCInitStructure;
arr = 1000000/ freq;
TIM1_IOInit();//TIM1通道2-PA9 TIM1通道3-PA10配置
TIM1Init(arr, ch1_duty, ch2_duty);//TIM1定时器配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM1模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性高
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = (arr - 1) * ch1_duty / 100;
//TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);//预装载使能位无所谓,不影响
TIM_OC2Init( TIM1, &TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;//PWM2模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//输出极性低
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//比较输出使能
//***(arr-1)是总的计数值,而(arr-1)*占空比是电平反转时的计数值,故控制pluse就可以控制占空比***
TIM_OCInitStructure.TIM_Pulse = (arr - 1) * ch2_duty / 100;
//TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);//预装载使能位无所谓,不影响
TIM_OC3Init( TIM1, &TIM_OCInitStructure);
//***高级定时器必须有这一句,而通用就不必了***
TIM_CtrlPWMOutputs(TIM1, ENABLE);//使能PWM输出
TIM_Cmd(TIM1, ENABLE); //使能 TIM1
}
PWMMode_Advance.h
#ifndef _PWMMODE_ADVANCE_H
#define _PWMMODE_ADVANCE_H
#include "config.h"
void TIM1_PWMInit(u32 freq, u8 ch1_duty, u8 ch2_duty);
#endif
4、高级定时器PWM模式输出需要注意的地方
①、TIM1是高级定时器,它是挂载到APB2总线上的.
②、根据TIM的四个捕获比较通道位置:PA8/PA9/PA10/PA11。再结合实际电路,确定了选择CH2-PA9和CH3-PA10作为PWM输出,但是此时要注意,拔掉对应的跳线帽。
③、对应的通道初始化应为TIM_OC2Init
和TIM_OC3Init
,即对应的通道2和通道3。
④、高级定时器必须加这一句TIM_CtrlPWMOutputs(TIM1, ENABLE);//使能PWM输出
⑤、对应的逻辑分析仪波形
二、通用定时器输出比较产生PWM
输出比较的特点:不同通道产生不同频率不同占空比的方波
理解输出比较的一张重要图。
特别感受一种叠加的概念!总的计数值是0~0xFFFF(通用定时器是16位嘛),溢出重新从0开始,再往上加。而Pluse是我们的周期,也就是产生的波形频率,这个Pluse并不是对应计数器CNT的绝对范围,而是相对CNT的相对范围。就是Pluse加到设定值了,不管此刻对应的CNT是多少,我继续从当前CNT再计我的频率数。而占空占空比的是其中的Duty,上面这个示意图了解了,也就不难理解整个过程了。
小于Duty为高,大于Duty小于Pluse为低。
工程代码可见Github<传送门>
1、主要代码
main.c
/*******************************************************************************
* 文件名:main.c
* 描 述:
* 作 者:CLAY
* 版本号:v1.0.0
* 日 期: 2019年1月26日
* 备 注:通用定时器的输出比较 TIM4_CH1 - PB6 TIM4_CH2 - PB7
*
*******************************************************************************
*/
#include "config.h"
#include "led.h"
#include "key.h"
#include "timer.h"
#include "beep.h"
#include "lcd.h"
#include "stdio.h"
#include "usart.h"
#include "i2c.h"
#include "eeprom.h"
#include "PWMMode.h"
#include "PWMMode_Advance.h"
#include "PWM_Compare.h"
int main(void)
{
STM3210B_LCD_Init();
LCD_Clear(Blue);
LEDInit();
KeyInit();
BeepInit();
TIM2Init(2000, 72);//定时2ms
TIM3_PWMInit(2000, 40, 80);//频率2K CH1占空比40% CH2占空比80%
TIM1_PWMInit(2000, 40, 80);//频率2K CH2占空比40% CH3占空比80%
TIM4_PWMCompare(2000, 1000, 40, 80);//CH1占空比2K 40% CH2 1K占空比80%
while(1)
{
KeyDriver();
}
}
void KeyAction(int code)
{
if(code == 1)//按下B1,切换灯状态,蜂鸣器鸣叫0.1s
{
GPIOC->ODR ^= (1<<8);//PC8不断取反
GPIOD->ODR |= (1<<2);//PD2置1,使能573锁存器
GPIOD->ODR &= ~(1<<2);//PD2清0,关闭573锁存器
Beep(100);
}
else if(code == 2)
{
Beep(-1);
}
else if(code == 3)
{
Beep(0);
}
}
PWM_Compare.c
#include "PWM_Compare.h"
void TIM4_IOInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//***注意这里是APB2***
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能 GPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //TIM4通道1-PB6 TIM4通道2-PB7
//***复用推挽输出(PWM模式)***
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIO
}
void NVIC_TIM4Enable(void)
{
NVIC_InitTypeDef NVIC_initstructure;
NVIC_initstructure.NVIC_IRQChannel = TIM4_IRQn; //选择TIM4中断通道
NVIC_initstructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道
NVIC_initstructure.NVIC_IRQChannelPreemptionPriority = 0; //设定抢占优先级为0
NVIC_initstructure.NVIC_IRQChannelSubPriority = 0; //设定响应优先级为0
NVIC_Init(&NVIC_initstructure);
}
void TIM4Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
//***注意这里是APB1***
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能定时器4时钟
//***注意这里设置的是0xFFFF
TIM_TimeBaseStructure.TIM_Period = 0xFFFF; //设置计数值最大
TIM_TimeBaseStructure.TIM_Prescaler = 72-1; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //初始化 TIM4
//***别忘了设置这个***
NVIC_TIM4Enable();
}
u32 CH1_Val, CH2_Val, CH1_Duty, CH2_Duty;
void TIM4_PWMCompare(u32 ch1_freq, u32 ch2_freq, u32 ch1_duty, u32 ch2_duty)
{
// u32 CH1_Val, CH2_Val;//FUCK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
TIM_OCInitTypeDef TIM_OCInitStructure;
CH1_Val = 1000000 / ch1_freq;
CH2_Val = 1000000 / ch2_freq;
CH1_Duty = CH1_Val * ch1_duty / 100;
CH2_Duty = CH2_Val * ch2_duty / 100;
TIM4_IOInit();//TIM4通道1-PB6 TIM4通道2-PB7配置
TIM4Init();//TIM4定时器配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;//触发模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性高
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CH1_Val;
TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Disable);预装载使能位失能
TIM_OC1Init( TIM4, &TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;//触发模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性高
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//比较输出使能
TIM_OCInitStructure.TIM_Pulse = CH2_Val;
TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Disable);//预装载使能位失能
TIM_OC2Init( TIM4, &TIM_OCInitStructure);
TIM_SetCounter(TIM4, 0);//定时器计数值清0
TIM_SetCompare1(TIM4, 0);//定时器捕获比较1寄存器值清0
TIM_SetCompare2(TIM4, 0);//定时器捕获比较2寄存器值清0
TIM_ClearITPendingBit(TIM4, TIM_IT_CC1|TIM_IT_CC2);//清除中断标志位
TIM_ITConfig(TIM4, TIM_IT_CC1|TIM_IT_CC2, ENABLE);//使能通道比较中断
TIM_Cmd(TIM4, ENABLE); //使能 TIM4
}
PWM_Compare.h
#ifndef _PWM_COMPARE_H
#define _PWM_COMPARE_H
#include "config.h"
extern u32 CH1_Val, CH2_Val, CH1_Duty, CH2_Duty;
void TIM4_PWMCompare(u32 ch1_freq, u32 ch2_freq, u32 ch1_duty, u32 ch2_duty);
#endif
2、需要注意的地方
①、查阅数据手册可知,TIM4的四个输出比较通道在PB6/PB7/PB8/PB9,而结合实际电路PB6/PB7是接在EEPROM的,而PB8/PB9是用作LCD,LCD显然使用的较多,我们选择了牺牲I2C,即TIM3_CH1 - PB6和 TIM3_CH2 - PB7...
②、注意细节,局部变量和全局变量~~
③、配置好定时器后别忘了,中断向量的配置
在这里插入图片描述
④、关于TIM_OC1PreloadConfig
前面也有提及如使能了该位,写入到TIMx_CCRx寄存器的比较值将在更新事件到来时才会传入到当前捕获/比较寄存器,否则未使能,比较值将立即写入当前捕获比较寄存器。
输出比较模式,当然是立即生效了,所以这里TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);
为Disable或者不写。
⑤、TIM_ClearITPendingBit
对输出极性影响
固件库定时器初始化函数默认会产生更新事件并触发更新事件中断标志位,如果此时中断使能,程序会立即执行中断函数。
知道了这个先决条件,再结合程序就不难理解了,一起来分析分析吧。
当有TIM_ClearITPendingBit
并且设置输出极性为TIM_OCPolarity_High
的情况下,设置2K频率40%占空比和1K频率80%占空比的波形如下所示。
2k 40%
1k 80%
契合程序的要求,但是如果现在还是输出进行为TIM_OCPolarity_High
没有了清除中断标志位这一操作。对应的波形又是什么样子呢?
2K 60%
1K 20%
刚好反过来了,为什么呢?说了半天还在卖关子,别介咱不是在分析问题么,问题现象出来了,再结合程序继续来看看
定时器4的中断源是输出比较,也就是说计数器一直在和输出比较寄存器的数进行比较,一旦相等就要进入中断。
初始的时候是计数器为0,假如现在初始的时候清除了中断标志位,一开始计数器为0,比较寄存器也是0,相等要进入中断,设置高电平时间持续时间t1=当前比较值(0)+duty
,持续时间完毕后,计数器又和比较器相等,再进入中断,设置低电平持续时间t2 = 当前比较值+(pulse-duty)
由于没有使能预装载位,故在中断里一旦赋值,直接写入寄存器中。
直到再进中断,即完成了一个周期的方波输出,整个方波是先高后低(设置TIM_OCPolarity_High
的前提下),接着再赋值t3=当前比较值+duty
,就这样周而复始的循环....便产生了符合我们要求的方波信号。
说到这就不难立即为什么没有清除中断标志位就会刚好相反了,没有清除中断标志位一上来就直接进入中断(前面说了这是库函数决定了),然后瞬间又发现计数器和比较值一样,再进一次中断。整个过程当前比较值几乎没有更新还是0(整个过程想成很短的时间?),相当于直接设置了低电平输出duty时间,所以我们看到的波形刚好相反了。
哎呀,想不好想,解释起来更麻烦,感觉还没有解释好,就先这样吧,如果真有人看到了,有问题再留言交流吧。