幾個(gè)留待補(bǔ)充的程序:
1、高級(jí)定時(shí)器實(shí)現(xiàn)互補(bǔ)PWM波
2、同一TIM不同通道,產(chǎn)生相位不同的PWM波
3、高級(jí)定時(shí)器的輸出比較產(chǎn)生PWM
之前在藍(lán)橋的單片機(jī)比賽階段對(duì)于PWM的一次又一次深入,印象頗深。而今到了嵌入式,更感覺尤為重要,這里也算做個(gè)小結(jié),如有錯(cuò)誤,不吝指教。
閱讀此篇博文的同時(shí),也可參閱之前寫的一篇ST32之PWM小結(jié)。
幾個(gè)帶著寫博客的問題回答下:
1、關(guān)于PWM的上限頻率呢?
跟周期有關(guān)即PWM模式下的arr和輸出比較中的pulse...,也不太好說,沒具體環(huán)境,具體情況再分析吧。
2、PWM模式下同時(shí)可以幾個(gè)通道輸出呢?
有幾個(gè)通道就能輸出幾個(gè)唄。
一、通用和高級(jí)定時(shí)器的PWM模式產(chǎn)生PWM
PWM模式的特點(diǎn):同一定時(shí)器中,不同的通道下,輸出的頻率固定,占空比可變。
通用定時(shí)器PWM模式,TIM3_CH1 和 TIM3_CH2輸出,引腳為PA6 PA7...工程代碼移步Github<傳送門>
1、通用定時(shí)器PWM模式輸出主要代碼
小于Pluse為高電平,大于Pluse小于ARR為低電平。
main.c
/*******************************************************************************
* 文件名:main.c
* 描 述:
* 作 者:CLAY
* 版本號(hào):v1.0.0
* 日 期: 2019年1月25日
* 備 注:通用定時(shí)器輸出硬件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);//定時(shí)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,切換燈狀態(tài),蜂鳴器鳴叫0.1s
{
GPIOC->ODR ^= (1<<8);//PC8不斷取反
GPIOD->ODR |= (1<<2);//PD2置1,使能573鎖存器
GPIOD->ODR &= ~(1<<2);//PD2清0,關(guān)閉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時(shí)鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //TIM3通道1-PA6 TIM3通道2-PA7
//***復(fù)用推挽輸出(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); //使能定時(shí)器3時(shí)鐘
TIM_TimeBaseStructure.TIM_Period = arr-1; //設(shè)置在自動(dòng)重裝載周期值
TIM_TimeBaseStructure.TIM_Prescaler = 72-1; //設(shè)置預(yù)分頻值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設(shè)置時(shí)鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上計(jì)數(shù)模式
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定時(shí)器配置
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);//預(yù)裝載使能位無所謂,不影響
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)是總的計(jì)數(shù)值,而(arr-1)*占空比是電平反轉(zhuǎn)時(shí)的計(jì)數(shù)值,故控制pluse就可以控制占空比***
TIM_OCInitStructure.TIM_Pulse = (arr - 1) * ch2_duty / 100;
//TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);//預(yù)裝載使能位無所謂,不影響
TIM_OC2Init( TIM3, &TIM_OCInitStructure);
//***高級(jí)定時(shí)器必須有這一句,而通用就不必了***
//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、通用定時(shí)器PWM模式輸出需要注意的地方
通用定時(shí)器的硬件PWM模式,改變占空比通過TIM_SetCompare1
函數(shù),注意最大數(shù)值是等于arr即周期值!
①、我們要用TIM3的CH1和CH2通道,這兩個(gè)可以從數(shù)據(jù)手冊(cè)(P19)查閱到對(duì)應(yīng)的復(fù)用引腳是PA6和PA7。
②、我們要想使用定時(shí)器的PWM模式,配置流程需要遵循 IO引腳配置 -> 定時(shí)器配置 -> PWM模式配置的順序。
③、IO引腳為PA,對(duì)應(yīng)時(shí)鐘總線在APB2,而TIM3對(duì)應(yīng)時(shí)鐘總線是APB1。
④、定時(shí)器配置中的TIM_OCInitStructure
結(jié)構(gòu)體,我們通過程序溯源可以找到其成員變量,可以發(fā)現(xiàn)其共有8個(gè),但是其中4個(gè)通用定時(shí)器無需配置,高級(jí)定時(shí)器才需要配置。
⑥、PWM配置中TIM_OCInitStructure
結(jié)構(gòu)體的兩個(gè)成員需要注意下,TIM_OCMode
和TIM_OCPolarity
。
前者
TIM_OCMode
取值有兩個(gè)分別為TIM_OCMode_PWM1
和TIM_OCMode_PWM2
。后者取值也有兩個(gè)TIM_OCPolarity_High
和TIM_OCPolarity_Low
PWM1和PWM2
參考正點(diǎn)原子的《STM32F1開發(fā)指南-寄存器版本_V3.1 》P255....
說白了,這兩個(gè)都是管PWM初始狀態(tài)的,慣用的搭配是TIM_OCMode_PWM1
和TIM_OCPolarity_High
一對(duì),TIM_OCMode_PWM2
和TIM_OCPolarity_Low
一對(duì),這樣搭配是正邏輯,否則為反邏輯。什么意思呢?看下面的圖吧。
主函數(shù)調(diào)用TIM3_PWMInit(1000, 40, 80);
I、CH1為TIM_OCMode_PWM1
和TIM_OCPolarity_High
,CH2為TIM_OCMode_PWM1
和TIM_OCPolarity_High
時(shí):
是符合主函數(shù)調(diào)用邏輯的,故稱之為正邏輯。
II、CH1為TIM_OCMode_PWM2
和TIM_OCPolarity_High
時(shí):
此時(shí)占空比主函數(shù)調(diào)用時(shí)40%,而實(shí)際是60%,故稱之為反邏輯。
III、CH1為TIM_OCMode_PWM2
和TIM_OCPolarity_Low
時(shí):
此時(shí)變又是正邏輯了,所以記住固定搭配就行了。
⑦、關(guān)于PWMMode.c
中注釋的一句
//***高級(jí)定時(shí)器必須有這一句,而通用就不必了***
//TIM_CtrlPWMOutputs(TIM3, ENABLE);//使能PWM輸出
參考正點(diǎn)原子的《STM32F1開發(fā)指南-寄存器版本_V3.1 》P318....
只有高級(jí)定時(shí)器PWM輸出時(shí),必須加上這一句,而這里我們是通用定時(shí)器,所以沒有加。
⑧、關(guān)于TIM_OCInitStructure
結(jié)構(gòu)體重的TIM_Pulse
理解
(arr-1)是總的計(jì)數(shù)值,而(arr-1)*duty是電平反轉(zhuǎn)時(shí)的計(jì)數(shù)值
,故pluse = 占空比
⑨、關(guān)于PWMMode.c
中的TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
用來使能輸出/比較預(yù)裝載使能位。
輸出比較預(yù)裝載使能位OCxPE位于捕獲/比較模式寄存器TIMx_CCMRx中。
若使能了該位,寫入到TIMx_CCRx寄存器的比較值將在更新事件到來時(shí)才會(huì)傳入到當(dāng)前捕獲/比較寄存器,否則未使能,比較值將立即寫入當(dāng)前捕獲比較寄存器。
在PWM模式下,預(yù)裝載使能位
無論使能與否都不影響。
哈,注意了,當(dāng)前捕獲比較寄存器是一個(gè)名詞。
每個(gè)通用定時(shí)器擁有4路捕獲/比較寄存器,每路通道都擁有一個(gè)捕獲/比較寄存器TIMx_CCRx用于裝載比較值。該寄存器同樣包括兩個(gè)寄存器,一個(gè)是用戶可以操作的:用于寫入比較值;另一個(gè)是用于和計(jì)數(shù)器CNT進(jìn)行比較的當(dāng)前捕獲比較寄存器。
對(duì)了,關(guān)于這個(gè)函數(shù)也有要說的,我們?cè)诔绦蛑许樦@個(gè)函數(shù)尋找它的定義會(huì)找到
也許會(huì)有一個(gè)疑問,CH2的預(yù)裝載使能位在手冊(cè)上寫的不是第11位嗎?
怎么到了庫(kù)函數(shù)里面變成了8,
莫急我們繼續(xù)看看TIM_OCPreload,再尋找這個(gè)的定義
看,
原來使能的時(shí)候不是0x0001啊,而是0x0008,那么這么算起來剛好不就對(duì)應(yīng)11位了么?完美~
附幾個(gè)寄存器名,混個(gè)眼熟
既然上面詳細(xì)介紹了通用定時(shí)器的PWM輸出功能,這里也再附上高級(jí)定時(shí)器的PWM輸出功能吧,方便日后查閱... 工程代碼移步Github<傳送門>
main.c
/*******************************************************************************
* 文件名:main.c
* 描 述:
* 作 者:CLAY
* 版本號(hào):v1.0.0
* 日 期: 2019年1月26日
* 備 注:高級(jí)定時(shí)器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);//定時(shí)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,切換燈狀態(tài),蜂鳴器鳴叫0.1s
{
GPIOC->ODR ^= (1<<8);//PC8不斷取反
GPIOD->ODR |= (1<<2);//PD2置1,使能573鎖存器
GPIOD->ODR &= ~(1<<2);//PD2清0,關(guān)閉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時(shí)鐘
//***根據(jù)實(shí)際電路選用了USART1的復(fù)用功能。***(要注意的是,拔掉跳線帽PA9 PA10)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10; //TIM_CH2 - PA9 //TIM_CH3 - PA10
//***復(fù)用推挽輸出(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); //使能定時(shí)器1時(shí)鐘
TIM_TimeBaseStructure.TIM_Period = arr-1; //設(shè)置在自動(dòng)重裝載周期值
TIM_TimeBaseStructure.TIM_Prescaler = 72-1; //設(shè)置預(yù)分頻值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設(shè)置時(shí)鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上計(jì)數(shù)模式
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定時(shí)器配置
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);//預(yù)裝載使能位無所謂,不影響
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)是總的計(jì)數(shù)值,而(arr-1)*占空比是電平反轉(zhuǎn)時(shí)的計(jì)數(shù)值,故控制pluse就可以控制占空比***
TIM_OCInitStructure.TIM_Pulse = (arr - 1) * ch2_duty / 100;
//TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);//預(yù)裝載使能位無所謂,不影響
TIM_OC3Init( TIM1, &TIM_OCInitStructure);
//***高級(jí)定時(shí)器必須有這一句,而通用就不必了***
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、高級(jí)定時(shí)器PWM模式輸出需要注意的地方
①、TIM1是高級(jí)定時(shí)器,它是掛載到APB2總線上的.
②、根據(jù)TIM的四個(gè)捕獲比較通道位置:PA8/PA9/PA10/PA11。再結(jié)合實(shí)際電路,確定了選擇CH2-PA9和CH3-PA10作為PWM輸出,但是此時(shí)要注意,拔掉對(duì)應(yīng)的跳線帽。
③、對(duì)應(yīng)的通道初始化應(yīng)為TIM_OC2Init
和TIM_OC3Init
,即對(duì)應(yīng)的通道2和通道3。
④、高級(jí)定時(shí)器必須加這一句TIM_CtrlPWMOutputs(TIM1, ENABLE);//使能PWM輸出
⑤、對(duì)應(yīng)的邏輯分析儀波形
二、通用定時(shí)器輸出比較產(chǎn)生PWM
輸出比較的特點(diǎn):不同通道產(chǎn)生不同頻率不同占空比的方波
理解輸出比較的一張重要圖。
特別感受一種疊加的概念!總的計(jì)數(shù)值是0~0xFFFF(通用定時(shí)器是16位嘛),溢出重新從0開始,再往上加。而Pluse是我們的周期,也就是產(chǎn)生的波形頻率,這個(gè)Pluse并不是對(duì)應(yīng)計(jì)數(shù)器CNT的絕對(duì)范圍,而是相對(duì)CNT的相對(duì)范圍。就是Pluse加到設(shè)定值了,不管此刻對(duì)應(yīng)的CNT是多少,我繼續(xù)從當(dāng)前CNT再計(jì)我的頻率數(shù)。而占空占空比的是其中的Duty,上面這個(gè)示意圖了解了,也就不難理解整個(gè)過程了。
小于Duty為高,大于Duty小于Pluse為低。
工程代碼可見Github<傳送門>
1、主要代碼
main.c
/*******************************************************************************
* 文件名:main.c
* 描 述:
* 作 者:CLAY
* 版本號(hào):v1.0.0
* 日 期: 2019年1月26日
* 備 注:通用定時(shí)器的輸出比較 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);//定時(shí)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,切換燈狀態(tài),蜂鳴器鳴叫0.1s
{
GPIOC->ODR ^= (1<<8);//PC8不斷取反
GPIOD->ODR |= (1<<2);//PD2置1,使能573鎖存器
GPIOD->ODR &= ~(1<<2);//PD2清0,關(guān)閉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時(shí)鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //TIM4通道1-PB6 TIM4通道2-PB7
//***復(fù)用推挽輸出(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; //設(shè)定搶占優(yōu)先級(jí)為0
NVIC_initstructure.NVIC_IRQChannelSubPriority = 0; //設(shè)定響應(yīng)優(yōu)先級(jí)為0
NVIC_Init(&NVIC_initstructure);
}
void TIM4Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
//***注意這里是APB1***
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能定時(shí)器4時(shí)鐘
//***注意這里設(shè)置的是0xFFFF
TIM_TimeBaseStructure.TIM_Period = 0xFFFF; //設(shè)置計(jì)數(shù)值最大
TIM_TimeBaseStructure.TIM_Prescaler = 72-1; //設(shè)置預(yù)分頻值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設(shè)置時(shí)鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上計(jì)數(shù)模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //初始化 TIM4
//***別忘了設(shè)置這個(gè)***
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定時(shí)器配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;//觸發(fā)模式
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);預(yù)裝載使能位失能
TIM_OC1Init( TIM4, &TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;//觸發(fā)模式
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);//預(yù)裝載使能位失能
TIM_OC2Init( TIM4, &TIM_OCInitStructure);
TIM_SetCounter(TIM4, 0);//定時(shí)器計(jì)數(shù)值清0
TIM_SetCompare1(TIM4, 0);//定時(shí)器捕獲比較1寄存器值清0
TIM_SetCompare2(TIM4, 0);//定時(shí)器捕獲比較2寄存器值清0
TIM_ClearITPendingBit(TIM4, TIM_IT_CC1|TIM_IT_CC2);//清除中斷標(biāo)志位
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、需要注意的地方
①、查閱數(shù)據(jù)手冊(cè)可知,TIM4的四個(gè)輸出比較通道在PB6/PB7/PB8/PB9,而結(jié)合實(shí)際電路PB6/PB7是接在EEPROM的,而PB8/PB9是用作LCD,LCD顯然使用的較多,我們選擇了犧牲I2C,即TIM3_CH1 - PB6和 TIM3_CH2 - PB7...
②、注意細(xì)節(jié),局部變量和全局變量~~
③、配置好定時(shí)器后別忘了,中斷向量的配置
在這里插入圖片描述
④、關(guān)于TIM_OC1PreloadConfig
前面也有提及如使能了該位,寫入到TIMx_CCRx寄存器的比較值將在更新事件到來時(shí)才會(huì)傳入到當(dāng)前捕獲/比較寄存器,否則未使能,比較值將立即寫入當(dāng)前捕獲比較寄存器。
輸出比較模式,當(dāng)然是立即生效了,所以這里TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Disable);
為Disable或者不寫。
⑤、TIM_ClearITPendingBit
對(duì)輸出極性影響
固件庫(kù)定時(shí)器初始化函數(shù)默認(rèn)會(huì)產(chǎn)生更新事件并觸發(fā)更新事件中斷標(biāo)志位,如果此時(shí)中斷使能,程序會(huì)立即執(zhí)行中斷函數(shù)。
知道了這個(gè)先決條件,再結(jié)合程序就不難理解了,一起來分析分析吧。
當(dāng)有TIM_ClearITPendingBit
并且設(shè)置輸出極性為TIM_OCPolarity_High
的情況下,設(shè)置2K頻率40%占空比和1K頻率80%占空比的波形如下所示。
2k 40%
1k 80%
契合程序的要求,但是如果現(xiàn)在還是輸出進(jìn)行為TIM_OCPolarity_High
沒有了清除中斷標(biāo)志位這一操作。對(duì)應(yīng)的波形又是什么樣子呢?
2K 60%
1K 20%
剛好反過來了,為什么呢?說了半天還在賣關(guān)子,別介咱不是在分析問題么,問題現(xiàn)象出來了,再結(jié)合程序繼續(xù)來看看
定時(shí)器4的中斷源是輸出比較,也就是說計(jì)數(shù)器一直在和輸出比較寄存器的數(shù)進(jìn)行比較,一旦相等就要進(jìn)入中斷。
初始的時(shí)候是計(jì)數(shù)器為0,假如現(xiàn)在初始的時(shí)候清除了中斷標(biāo)志位,一開始計(jì)數(shù)器為0,比較寄存器也是0,相等要進(jìn)入中斷,設(shè)置高電平時(shí)間持續(xù)時(shí)間t1=當(dāng)前比較值(0)+duty
,持續(xù)時(shí)間完畢后,計(jì)數(shù)器又和比較器相等,再進(jìn)入中斷,設(shè)置低電平持續(xù)時(shí)間t2 = 當(dāng)前比較值+(pulse-duty)
由于沒有使能預(yù)裝載位,故在中斷里一旦賦值,直接寫入寄存器中。
直到再進(jìn)中斷,即完成了一個(gè)周期的方波輸出,整個(gè)方波是先高后低(設(shè)置TIM_OCPolarity_High
的前提下),接著再賦值t3=當(dāng)前比較值+duty
,就這樣周而復(fù)始的循環(huán)....便產(chǎn)生了符合我們要求的方波信號(hào)。
說到這就不難立即為什么沒有清除中斷標(biāo)志位就會(huì)剛好相反了,沒有清除中斷標(biāo)志位一上來就直接進(jìn)入中斷(前面說了這是庫(kù)函數(shù)決定了),然后瞬間又發(fā)現(xiàn)計(jì)數(shù)器和比較值一樣,再進(jìn)一次中斷。整個(gè)過程當(dāng)前比較值幾乎沒有更新還是0(整個(gè)過程想成很短的時(shí)間?),相當(dāng)于直接設(shè)置了低電平輸出duty時(shí)間,所以我們看到的波形剛好相反了。
哎呀,想不好想,解釋起來更麻煩,感覺還沒有解釋好,就先這樣吧,如果真有人看到了,有問題再留言交流吧。