幾個留待補充的程序:
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時間,所以我們看到的波形剛好相反了。
哎呀,想不好想,解釋起來更麻煩,感覺還沒有解釋好,就先這樣吧,如果真有人看到了,有問題再留言交流吧。