STM32第七章-脈衝寬度調製

先說在開頭啊,咱們學習定時器總感受它是很難的,這裏我就不說他的編程難度,而是對於它的理解難度。學習定時器你就必須瞭解他的前因後果。編程

好比說你如今要使用一個定時器,那麼先要決定你要用哪個定時器,是高級定時器仍是通用定時器呢?假如你要用高級定時器(TIM1 、TIM8),那他和通用定時器有啥區別呢?在程序中咱們如何來體現他們的區別呢?第二個你要關心的就是你使用的定時器是多少位的,通常有16位和32位區分。好比TIM1就是16位,那麼他的最大計數個數就是2^16=65536-1, 也就是說你的TIM_TimeBaseStructure.TIM_Period = X ;其中X最大值爲65535,你不能高於它。可是若是你使用TIM2就是32位,那麼他的最大計數個數就是2^32=4294967296-1,也就是說你的TIM_TimeBaseStructure.TIM_Period = X ;其中X最大值爲4294967295,你不能高於它。 第三就是你要清楚你使用的這個定時器的時鐘來源是哪裏,通常來講咱們高級定時器(TIM一、TIM8)的時鐘來源是來自AHB2,你能夠這樣記:高級定時器的時鐘頻率確定高於通用定時器的時鐘頻率對吧,又由於2>1,因此高級定時器的時鐘來源是來自AHB2對吧,那麼通用定時器的時鐘源就來自AHB1了對吧。markdown

在這裏還要注意的是STM32中除非APB1的時鐘分頻數設置爲1,不然通用定時器TIMx的時鐘是 APB1時鐘的2倍,當 APB1 的時鐘不分頻的時候,通用定時器 TIMx 的時鐘就等於APB1 的時鐘。這也就解釋了下面我使用TIM3是通用定時器,掛載在 APB1上,而 APB1是36MHz,通用定時器TIMx的時鐘是 APB1時鐘的2倍,因此TIM3的定時器是72MHZ 計算。只有把這些該清楚了才能續的下去,固然遠不止這一下。函數

上一章咱們說到了定時器,這一章稱熱打鐵熱說一說脈衝寬度調製-PWM。 脈衝寬度調製(PWM),是英文「Pulse Width Modulation」的縮寫,簡稱脈寬調製,是利用微處理器的數字輸出來對模擬電路進行控制的一種很是有效的技術。簡單一點,就是對脈衝寬度進行控制。oop

在這裏插入圖片描述

看到這個圖,我先想聰明的你立刻會想到,這個玩意必定有兩個寄存器ARR和CRRx,而這兩個值必定是須要本身設置的,圖上都標出來了,對吧。學習

咱們假定定時器工做在向上計數 PWM模式,且當 CNT<CCRx 時,輸出 0,當 CNT>=CCRx 時輸出 1。那麼就能夠獲得如上的 PWM示意圖:ui

  • 當 CNT 值小於 CCRx 的時候,IO 輸出低電平(0),
  • 當 CNT 值大於等於 CCRx 的時候,IO 輸出高電平(1),
  • 當 CNT 達到 ARR 值的時候,從新歸零,而後從新向上計數,依次循環。

改變 CCRx 的值,就能夠改變 PWM 輸出的佔空比,改變 ARR 的值,就能夠改變 PWM 輸出的頻率,這就是 PWM 輸出的原理。因此說對於PWM咱們只要在程序中修改這兩個值就能夠了,其餘的都同樣。spa

STM32F429 的定時器除了 TIM6 和 7。其餘的定時器均可以用來產生 PWM 輸出。 直接開搞,來看一下定時器的分類,這是F4的定時器分配圖.net

這裏還要知道一個概念就是通道3d

通道是什麼,咱們換個說法,通道=路=引腳,這樣是否是好理解一點,也就是說TIM2有4路定時器。TIM5也有4路,TIM3也有4路,TIM4也有4路。爲何要這麼多路呢? 好比咱們要產生8路週期,佔空比都不一樣的PWM信號輸出,那咱們能夠選TIM2的 CH1/CH2/CH3/CH4 還有TIM3 的CH1,CH2,CH3,CH4 這8路進行輸出,須要這麼多路,就是爲了能夠輸出/輸入 更多的信號。好比你用小舵機作一個小機器人就須要不少的定時器對應不少的通道也就是對應不少引腳。輸出的信號分別是哪些管腳呢。很明顯對F4來講就是 TIM2的 CH1/CH2/CH3/CH4 對應 PA5 /PA1/PA2/PA3 這4個管腳。 TIM5的 CH1/CH2/CH3/CH4 對應 PH10/PH11/PH12/PIO 這4個管腳。調試

要使 STM32F429 的通用定時器 TIMx 產生 PWM 輸出,除了上一章介紹的寄存器外,咱們還會用到4個寄存器,來控制 PWM 的。這三個寄存器分別是: 捕獲/比較模式寄存器(TIMx_CCMR1/2) 捕獲/比較使能寄存器(TIMx_CCER) 捕獲/比較寄存器(TIMx_CCR1~4) 剎車和死區寄存器(TIMx_BDTR)這個寄存器通常在高級定時器中使用,就是TIM1和TIM8。若是你沒用到這兩個定時器就不要管這個寄存器了。脈衝寬度調製模式能夠生成一個信號,該信號頻率由 TIMx_ARR 寄存器值決定,其佔空比則由 TIMx_CCRx 寄存器值決定。

寄存器講解

捕獲/比較模式寄存器(TIMx_CCMR1/2)

該寄存器通常有 2 個:TIMx _CCMR1和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和CH2,而 TIMx_CCMR2 控制 CH3 和 CH4

在這裏插入圖片描述

這個寄存器咱們關心的是 位14 13 12位 9 8,其餘的默認就能夠了。 模式設置位 OC4M,此部分由 3位組成。總共能夠配置成 7 種模式,咱們使用的是 PWM 模式,因此這 3 位必須設置爲 110/111。這兩種 PWM 模式的區別就是輸出電平的極性相反。另外 CC4S 用於設置通道的方向(輸入/輸出)默認設置爲00,就是設置通道做爲輸出使用。

捕獲/比較使能寄存器(TIM3_CCER)

使能寄存器見名知意,固然是使能TIM3定時器的啊。使CC4E置位爲1就能夠了。

在這裏插入圖片描述

捕獲/比較寄存器(TIMx_CCR1~4)

該寄存器總共有 4 個,對應 4 個通道 CH1~4。咱們使用的是通道3。在輸出模式下,該寄存器的值與 CNT 的值比較,根據比較結果產生相應動做。利用這點,咱們經過修改這個寄存器的值,就能夠控制 PWM 的輸出脈寬了。 在這裏插入圖片描述

程序代碼配置

咱們文本的例子是定時器3的通道3,對應PB0管腳。

1.開啓TIM3時鐘,配置 PB0

要使用 TIM3,咱們必須先開啓 TIM3的時鐘,這點相信你們看了這麼多代碼,應該明白了。這裏咱們還要配置 PB0 爲複用輸出(固然還要時能 GPIOB的時鐘),這是由於 TIM3_CH3通道將使用 PB0的複用功能做爲輸出,咱們配置 PB0爲複用輸出,才能夠實現 TIM3_CH3的 PWM 通過 PB0輸出。

GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);    //使能TIM外設時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);  //使能GPIO外設時鐘 
//設置該引腳爲複用輸出功能,輸出TIM3 CH3的PWM脈衝波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;         //TIM3_CH3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //複用推輓輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
複製代碼

2.設置 TIM3的ARR和PSC

在開啓了 TIM3的時鐘以後,咱們要設置 ARR 和 PSC 兩個寄存器的值來控制輸出 PWM 的週期。這在庫函數是經過 TIM_TimeBaseInit 函數實現的,在上一節定時器中斷章節已經有講解過,這裏就不詳細講解,調用的格式爲:

TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 100;     
TIM_TimeBaseStructure.TIM_Prescaler =360-1; 	
TIM_TimeBaseStructure.TIM_ClockDivision = 0;  //設置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上計數模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位
複製代碼

對於上面的參數解釋一下: ①頻率:咱們使用的APB1時鐘源是72MHz的,在此咱們不作分頻,經過配置相關的參數來設置輸入頻率,計算方法:輸入頻率=APB1時鐘/(預分頻係數+1)=72 000 000Hz/360=200 000Hz =200KHZ。 ②TIM_TImeBaseStructure.TIM_Period參數決定了輸出PWM波形的頻率,輸出PWM波形的頻率=定時器的輸入頻率/TIM_TImeBaseStructure.TIM_Period,這裏咱們設置設置的週期爲200 000Hz/100=2000Hz,即0.5ms一個週期。 若是你想修改你的輸出PWM波形的頻率按我上面本身配置便可。

3.設置TIM3_CH3的PWM模式及通道方向, 使能 TIM3的CH3輸出

接下來,咱們要設置 TIM3_CH3爲 PWM 模式,咱們要經過配置 TIM3_CCMR2的相關位來控制 TIM3_CH3 的模式。在庫函數中,PWM 通道設置是經過函數 TIM_OC1Init()~TIM_OC4Init()來設置的,不一樣的通道的設置函數不同,這裏咱們使用的是通道 3,因此使用的函數是 TIM_OC3Init()。這裏的結構體我就很少少了,本身看一下就能夠明白,對於普通定時器咱們只須要設置下面這四個參數就能夠了。

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //選擇定時器模式:TIM脈衝寬度調製模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
TIM_OCInitStructure.TIM_Pulse = 500;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高
TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根據TIM_OCInitStruct中指定的參數初始化外設TIMx
複製代碼

③配置佔空比:佔空比=配置佔空比的值/ TIM_TImeBaseStructure.TIM_Period,這裏的佔空比爲50/100=50% 。50%的佔空比那麼波形也必定是一半高一半低對吧,一會咱們仿真看一看。

4使能TIM3

全部的都配置完了,最後能一下TIM3。

TIM_Cmd(TIM3, ENABLE);//使能TIM3
複製代碼

5.使能基本定時器3的預裝載值。

使能TIM3在CCR3上的預裝載寄存器

TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  ////使能TIM3在CCR3上的預裝載寄存器,即TIM3_CCR3的預裝載值在更新事件到來時才能被傳送至當前寄存器中。
TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIM3在ARR上的預裝載寄存器 (影子寄存器)
複製代碼

這裏咱們來說一下影子寄存器: 1.有影子寄存器的有3個:分頻寄存器PSC,自動重裝載寄存器 ARR,自動捕獲CCRx(x是對應的通道),注意,PSC,ARR,CCRx不是影子寄存器,而是它們對應的「預裝載寄存器」; 二、影子寄存器纔是真正起做用的寄存器,可是ST沒有提供這個寄存器出來,只是提供出與之相對應的預裝載寄存器,分別爲「PSC,ARR,CCRx」 三、咱們用戶能接觸到,能修改或讀取的都是預裝載寄存器,ST只是把它們開放出來(影子寄存器並無開放給用戶),其實就是ARR寄存器,如:TIM3->ARR 四、從預裝載寄存器ARR傳送到影子寄存器,有兩種方式,一種是馬上更新,一種是等觸發事件以後更新;這兩種方式主要取決於寄存器TIMx->CR1中的「APRE」位;

  • APRE=0,當ARR值被修改時,同時立刻更新影子寄存器的值;
  • APRE=1,當ARR值被修改時,必須在下一次事件發生後才能更新影子寄存器的值;

五、怎麼樣立刻馬上更改影子寄存器的值,而不是下一個事件;方法以下:

  • 將ARPE=0,TIM_ARRPreloadConfig(TIM3, DISABLE );
  • 在ARPE=1,TIM_ARRPreloadConfig(TIM3, ENABLE);

TIM_ARRPreloadConfig設置爲DISABLE 和ENABLE的問題,他的做用只是容許或禁止在定時器工做時向ARR的緩衝器中寫入新值,以便在更新事件發生時載入覆蓋之前的值。在開始初始化的時候你已經把" TIM_TimeBaseStructure.TIM_Period=100; //ARR的值 ",後來也一直是這個值,緣由是你沒有編寫中斷服務函數或者你在中斷服務函數中根本就沒有給ARR緩衝器從新寫入新值,因此設置爲DISABLE 和ENABLE都沒有影響,可是最保險的方法就是是能一下。

彙總一下代碼:

void TIM3_PWM_Init() {  
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能TIM時鐘 
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);//使能GPIO外設時鐘 

   //設置該引腳爲複用輸出功能,輸出TIM3 CH3的PWM脈衝波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;         //TIM3_CH3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //複用推輓輸出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);	
	TIM_TimeBaseStructure.TIM_Period = 100; //輸出PWM波形的頻率=定時器的輸入頻率/TIM_TImeBaseStructure.TIM_Period,200 000Hz/100=2000Hz,即0.5ms一個週期 
	TIM_TimeBaseStructure.TIM_Prescaler =360-1;//不分頻 輸入頻率=APB1時鐘/(預分頻係數+1)=72 000 000Hz/360=200 000Hz =200K 
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設置時鐘分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上計數模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //選擇定時器模式:TIM脈衝寬度調製模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
	TIM_OCInitStructure.TIM_Pulse = 50; //佔空比=配置佔空比的值/TIM_TImeBaseStructure.TIM_Period,500/1000=50% 
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高
	TIM_OC3Init(TIM3, &TIM_OCInitStructure);  
		
    TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //CH3預裝載使能 
    TIM_ARRPreloadConfig(TIM3, ENABLE); //使能TIM3在ARR上的預裝載寄存器 (影子寄存器) 
	TIM_Cmd(TIM3, ENABLE);  //使能TIM3 
}
複製代碼

6.主函數的編寫

主函數就很簡單,一個while函數就能夠解決。

int main(void) {	
	SystemInit ();	 
	TIM3_PWM_Init();
   	while(1)
   	{
	} 
}
複製代碼

根據而上一章講到的方法keil中調試一下,能夠清楚地看到週期是0.5ms,跟咱們上面設置的同樣。佔空比也是50%,就是1/2。

在這裏插入圖片描述

固然咱們這裏設置的PWM波是死的,也就是說是固定的50%。那麼這實際項目中咱們通常是要求可變的PWM,最典型的就是控制電機的轉速。舉一個例子,下面是電機的驅動芯片就是TB6612的管腳分配圖。

TB6612引腳分配:
VM         PWMA--------->TIM3_CH3(PBO)
VCC        AIN2--------->GPIOB_12
GND        AIN1--------->GPIOB_13
AO1        STBY--------->GPIOB_14
AO2        BIN1--------->GPIOB_15
BO2        BIN2--------->GPIOA_12
BO1        PWMB--------->TIM3_CH4(PB1)
複製代碼

在這裏咱們用GPIOB_12和GPIOB_13的電平高低來控制電機的前進後退,具體的電平高低要看真值表。用TIM3_CH3(PB0)來控制電機的轉速。設置不一樣的值,電機的轉速就是有快有慢。那麼用哪個函數來控制呢?

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)複製代碼

理所固然,對於其餘通道,分別有一個函數名字,函數格式爲 TIM_SetComparex(x=1,2,3,4)。如今咱們就能夠控制 TIM3 的 CH3 輸出 PWM 波了。也就是一個輪子的轉速快慢了。這裏我用一個小小的if語句來使定時器3的通道3的值發生變化。

int main(void) {	
	u16 i=0;  
	SystemInit ();	 
	TIM3_PWM_Init();
   	while(1)
	{
		i++;
		TIM_SetCompare3(TIM3,i);	  //定時器3的通道3 
 		if(i==100) i=0;
	} 
}
複製代碼

下面看一下效果:

能夠明顯的看到PWM波在不斷地變化,那就對應電機轉速的變化或者小燈的明暗程度。

至此咱們就明白是STM32是如何輸出PWM的。這裏咱們來總結一下: 咱們輸出PWM用到了定時器3,用TIM_Period 和TIM_Prescaler來肯定輸出PWM的頻率和週期.用TIM_Pulse來肯定佔空比。 若是你單純的使用定時器3作定時功能那麼TIM_Period 和TIM_Prescaler來肯定須要定時的時間,也就是多長時間進入一次中斷服務函數執行相應的指令。只是在不一樣的地方叫法不一樣,本質仍是同樣的。 聰明的你會了嗎?

上一篇:STM32第六章-定時器詳解

相關文章
相關標籤/搜索