PWM互補輸出及死區時間程式碼和詳解(暫存器版本)
原文: http://www.openedv.com/thread-62861-1-1.html
一直跟著原子哥的教程一直學到PWM這一章發現一頭霧水,自己也沒發過什麼分享帖,感覺原子哥對PWM這章講的比較少(mini板教程),後面自己上網找了些資料,網上關於PWM講的也不多,所以學起來也挺困難的,一直對TIM1有7路PWM不理解,自己只能輸出4路,另外三路也不知道怎麼輸出,通過網上少量資料和自己研究發現這三路用於互補輸出,互補輸出調試出來了發現又有死區時間,主要用於電機的H橋方面的控制,後面附上自己對高階定時器互補輸出和死區時間設定的程式碼和詳解。花了兩小時整理了下資料,初學者難免有錯希望指出,也希望對跟我一樣的初學者學習PWM有點幫助
先簡單瞭解下PWM和死區時間
簡介:
脈衝寬度調製(PWM),是英文“ Pulse Width Modulation” 的縮寫,簡稱脈寬調製,是利用微處理器的數字輸出來對類比電路進行控制的一種非常有效的技術。簡單一點,就是對脈衝寬度的控制,高階定時器 TIM1 和 TIM8 可以同時產生多達 7 路的 PWM 輸出。而通用定時器也能同時產生多達 4路的 PWM 輸出,這樣, STM32 最多可以同時產生 30 路 PWM 輸出!
PWM的死區時間:
死區,簡單解釋:通常,大功率電機、變頻器等,末端都是由大功率管、IGBT等元件組成的H橋或3相橋。每個橋的上半橋和下半橋是是絕對不能同時導通的,但高速的PWM驅動訊號在達到功率元件的控制極時,往往會由於各種各樣的原因產生延遲的效果,造成某個半橋元件在應該關斷時沒有關斷,造成功率元件燒燬。死區就是在上半橋關斷後,延遲一段時間再開啟下半橋或在下半橋關斷後,延遲一段時間再開啟上半橋,從而避免功率元件燒燬。這段延遲時間就是死區。(就是上、下半橋的元件都是關斷的)死區時間控制在通常的低端微控制器所配備的PWM中是沒有的。PWM的上下橋臂的三極體是不能同時導通的。如果同時導通就會是電源兩端短路。所以,兩路觸發訊號要在一段時間內都是使三極體斷開的。這個區域就叫做“死區”優點就不用說了。缺點是使諧波的含量有所增加。
7路PWM:
程式碼和詳解:
利用高階定時器TIM1輸出7路PWM進行觀察
需要配置以下暫存器:
捕獲/比較模式暫存器(TIMx_CCMR1/2)
捕獲/比較使能暫存器(TIMx_CCER)
捕獲/比較暫存器(TIMx_CCR1~4)
剎車和死區暫存器(TIMx_BDTR)
按程式碼行後面的序號解析:[C] 純文字檢視 複製程式碼?
0102030405060708091011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465 | //TIM1_CH1 PWM輸出初始化 //arr:自動重灌值 //psc:時鐘預分頻數 void TIM1_PWM_Init(u16 arr,u16 psc) { RCC->APB2ENR|=1<<11; //TIM1時鐘使能 RCC->APB2ENR|=1<<2; //使能PORTA時鐘 RCC->APB2ENR|=1<<3; //使能PORTB時鐘 RCC->APB2ENR|=1<<0; //開啟複用時鐘 GPIOA->CRH&=0XFFFF0000; //PA8,9,10,11清除之前的設定 GPIOA->CRH|=0X0000BBBB; //PA8,9,10,11複用功能輸出 GPIOB->CRH&=0X000FFFFF; //PB13,14,15清除之前的設定 GPIOB->CRH|=0XBBB00000; //PB13,14,15複用功能輸出 GPIOB->ODR|=1<<13; //PB13 輸出上拉,低電平有效 GPIOB->ODR|=1<<14; //PB14 GPIOB->ODR|=1<<15; //PB15 GPIOA->ODR|=1<<8; //PA8 GPIOA->ODR|=1<<9; //PA9 GPIOA->ODR|=1<<10; //PA10 TIM1->ARR=arr; //設定計數器自動重灌值 ①1 TIM1->PSC=psc; //預分頻器設定 ②2 TIM1->CCER|=1<<0; //TIM1CH1 輸出使能,高電平有效 ③3 TIM1->CCER|=1<<4; //TIM1CH2 輸出使能 TIM1->CCER|=1<<8; //TIM1CH3 輸出使能 TIM1->CCER|=1<<12; //TIM1CH4 輸出使能 TIM1->CCER|=1<<2; //TIM1CH1N 互補輸出使能 TIM1->CCER|=1<<6; //TIM1CH2N 互補輸出使能 TIM1->CCER|=1<<10; //TIM1CH3N 互補輸出使能 TIM1->CCMR1|=7<<4; //CH1 PWM2模式 ④4 TIM1->CCMR1|=1<<3; //CH1預裝載使能 TIM1->CCMR1|=7<<12; //CH2 PWM2模式 TIM1->CCMR1|=1<<11; //CH2預裝載使能 TIM1->CCMR2|=7<<4; //CH3 PWM2模式 TIM1->CCMR2|=1<<3; //CH3預裝載使能 TIM1->CCMR2|=7<<12; //CH4 PWM2模式 TIM1->CCMR2|=1<<11; //CH4預裝載使能 TIM1->BDTR|=0x14; //死區時間設定 ⑤5 TIM1->BDTR|=1<<15; //MOE 主輸出使能 ⑥6 TIM1->CR1 |= 0x80; //ARPE使能,開始所有輸出通道,預設向上計數 ⑦7 TIM1->CR1 |= 0x01; //使能計數器 } int main( void ) { Stm32_Clock_Init(9); //系統時鐘設定 delay_init(72); //延時初始化 TIM1_PWM_Init(499,7199); // 72M/7200=10khz, 1/10khz * 500=50ms ⑧8 while (1) { TIM1->CCR1=250; //佔空比:50% 低電平時長25ms ⑨9 TIM1->CCR2=125; //佔空比:75% 低電平時長12.5ms TIM1->CCR3=50; //佔空比:90% 低電平時長5ms TIM1->CCR4=25; //佔空比:95% 低電平時長2.5ms } } |
①自動過載,這裡過載值為500,50ms一個週期
②預分頻:7200分頻,頻率為10KHZ
③TIM1->CCER|=1<<0;//位0為使能位,位1~3都預設為0,即高電平有效,關閉輸入捕獲,互補輸出極性為“高電平有效”,所謂的”有效”跟④有關,如下
④
CCMR1的4~6位為模式設定,,由於我們上面設定為高電平“有效”,這裡又設定為PWM2模式,在⑦沒有設定TIM1_CR1的DIR位,所以預設為0,也就是向上計數,如下
可以推出TIMx_CNT從0開始往上計數,在小於CCR1(第⑨有設定)時會輸出無效電平也就是低電平,當大於的時候輸出有效電平也就是高電平,這樣就達到了設定佔空比的目的。一開始我們設ARR為500,CCR1為250,所以一開始輸出低電平佔50%,高電平也是50%
⑤死區時間設定TIM1_BDTR(位7:0),我們上面設定的是0x14
DT表示死區持續時間,Tdts為系統時鐘週期,Tdtg表示乘以倍數後死區設定時間步進值。
TIM1_BDTR = 0x14是高3位為000,也就是呼叫以下這個公式(第一個公式),高3位可以根據自己需要設定
可以看到第⑧可以得知Tdts = 10Khz,Tdtg = Tdts = 10Khz,DT = 20(0x14) * 100us(10Khz) = 2000us = 2ms,也就是死區持續時間為2ms,最後配置輸出使能⑥
下面是輸出的波形
通道1(低電平27ms,高電平23ms)
互補輸出的通道(CH1N)(高電平23ms,低電平27ms)
通道2(7ms)
通道3(14.5ms)
通道4(2.5ms)
死區時間(2ms)
由於互補輸出且有死區時間所以CH1加了2ms,而CH1N減了2ms,死區時間跟計算的一樣是2ms,而最後一個CH4沒有互補
輸出所以自然是2.5ms不變,由於我們設定的10KHZ,而手冊是以8M為例子的,大家也可以試一下,親測沒有問題,如果
是8M死區時間精度也更高可以,可以設為us和ns級別。下面附上這個例程的程式碼