PWM簡述
PWM為Pulse Width Modulation的縮寫,對於只有high和low的數位訊號來說,如何用high,low比率調整出類似類比訊號為PWM的用處,另外大多數馬達也透過PWM來驅動轉速,LED由於只吃固定電壓,所以常用PWM來調整亮度。
PWM主要又三種方式產生:硬體內建PWM模組、中斷產生、迴圈。
硬體內建PWM模組
MCU內常內建PWM模組,以PIC系列為例,有CCP(Capture/Compare/PWM)模組、Output Compare模組和PWM模組。透過設定timer和暫存去達成PWM輸出。以下以dsPIC30F4013的Output Compare做示範。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <xc.h> | |
// FOSC | |
#pragma config FOSFPR = XT_PLL4 // Oscillator (XT w/PLL 4x) | |
#pragma config FCKSMEN = CSW_FSCM_OFF // Clock Switching and Monitor (Sw Disabled, Mon Disabled) | |
// FWDT | |
#pragma config FWPSB = WDTPSB_16 // WDT Prescaler B (1:16) | |
#pragma config FWPSA = WDTPSA_512 // WDT Prescaler A (1:512) | |
#pragma config WDT = WDT_OFF // Watchdog Timer (Disabled) | |
// FBORPOR | |
#pragma config FPWRT = PWRT_64 // POR Timer Value (64ms) | |
#pragma config BODENV = BORV20 // Brown Out Voltage (Reserved) | |
#pragma config BOREN = PBOR_ON // PBOR Enable (Enabled) | |
#pragma config MCLRE = MCLR_EN // Master Clear Enable (Enabled) | |
// FGS | |
#pragma config GWRP = GWRP_OFF // General Code Segment Write Protect (Disabled) | |
#pragma config GCP = CODE_PROT_OFF // General Segment Code Protection (Disabled) | |
// FICD | |
#pragma config ICS = ICS_PGD // Comm Channel Select (Use PGC/EMUC and PGD/EMUD) | |
#define Tcy 10000000 //10MHz oscillator with 4xPLL -> 10'000'000MIPS | |
//------------------------------------------------------------------------------ | |
// main routine | |
//------------------------------------------------------------------------------ | |
int main(int argc, char** argv) { | |
//------------------------------------------------------------------------------ | |
// IO Plan | |
TRISA = 0x0000; | |
TRISB = 0x0000; | |
TRISC = 0x0000; | |
TRISD = 0x0000; //RD1,RD2,RD3,RD4 -> PWM OC pin | |
TRISF = 0x0000; | |
ADPCFG = 0xFFFF; | |
//------------------------------------------------------------------------------ | |
// initialize PWM | |
//PR2 = 50000; 1:256-> 200 HZ | |
PR2 = 25000; | |
OC1RS = 1875; | |
OC1CON = 0x0006; //Set PWM mode on OC1,Fault pin disable,Timer2 is source | |
OC2RS = 1875; | |
OC2CON = 0x0006; //Set PWM mode on OC2,Fault pin disable,Timer2 is source | |
OC3RS = 1875; | |
OC3CON = 0x0006; //Set PWM mode on OC3,Fault pin disable,Timer2 is source | |
OC4RS = 1875; | |
OC4CON = 0x0006; //Set PWM mode on OC4,Fault pin disable,Timer2 is source | |
//IEC0bits.T2IE = 1; //enable Timer2 interrupt | |
// T2CON = 0x8000; //Configure Timer2(Timer on,continue in IDLE,not gate | |
// ,1:256 prescaler,internal clock) | |
T2CON = 0x8010; | |
TMR2 = 0; | |
while(1); | |
} | |
} |
由於每個MCU的PWM實際細節都不一樣,所以讀者請自行閱讀datasheet來應用硬體內建的PWM。
dsPIC30F系列的Output Compare PWM模組要應用時須將OCxCON 暫存器中的OCM設為'110'或'111'。設定順序為:
1.設定PRy(Period register)表示PWM的週期。
2.再來設定OCxRS暫存器表示duty cycle。
3.設定OCM。
4.設定timer,並開啟timer。
PWM 週期 = [(PRy+1)]*Tcy*(TMRx presclaer value)。 (Tcy為振盪器頻率*PLL/4)
PWM 頻率為週期倒數。
詳細設定在http://ww1.microchip.com/downloads/en/DeviceDoc/70157C.pdf中有詳細描述。
中斷產生PWM
利用Timer中斷來切換PWM進入duty cycle,有兩種方法。
一、用一個或兩個timer,先輸出high,計算PWM在high所需時間,設定好Timer 週期(中斷條件),當Timer發生中斷,將輸出切為low,並在計算PWM在low所需時間,同樣設定好Timer週期,當Timer發生中斷回到一開始重頭來過。也可用兩個Timer一個設為high所需時間,一個為period,當high的timer 中斷發生,關閉其中斷與timer,並將輸出設為low,等待period 的Timer發生中斷,在開啟high 的timer中斷。
二、用一個timer中斷,當其中斷將counter加一,一旦couner的值大於duty cycle則切為low,當counter的值大於period則歸零。以下為範例(使用PIC18F452)。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* File: newmainXC16.c | |
* Author: asus-h | |
* | |
* Created on 2015?2?9?, ?? 9:49 | |
*/ | |
#include "xc.h" | |
#pragma config OSC = HS, WDT=OFF, LVP=OFF,PWRT = OFF | |
int counter,pwm_val_1 = 20,pwm_val_2 = 20,pwm_val_3 = 20,pwm_val_4 = 20; | |
int change = 1; | |
void init_timer(); | |
void do_PWM(); | |
int main(void) { | |
TRISC = 0xEF; | |
TRISB = 0b11111111; | |
TRISD = 0x00; | |
//TRISCbits.TRISC4 = 0; | |
//unsigned int duty = 500; | |
// T3CONbits.T3CCP2=0; | |
// T3CONbits.T3CCP1=0; | |
INTCON2 = 0b00000001; | |
init_timer(); | |
return 0; | |
} | |
void interrupt ISR() | |
{ | |
if(T0IF) | |
{ | |
TMR0 = 65530; | |
counter++; | |
INTCON &= ~(1<<T0IF); | |
//INTCON = 0b11111110; //clear overflow bit; | |
T0IF = 0; | |
do_PWM(); | |
} | |
} | |
void init_timer() | |
{ | |
TMR0 = 65500; | |
TMR1 = 0; | |
T0CON = 0b10000001; | |
T1CON = 0b10000000; | |
//OPTION = 0B00001000; | |
INTCON = 0b11111110; | |
PIR1 = 0b000000011; | |
} | |
void do_PWM() | |
{ | |
TMR0 = 65530 ; | |
counter++; | |
if(counter<=pwm_val_1) | |
PORTDbits.RD4 = 1; | |
else | |
PORTDbits.RD4 = 0; | |
if(counter<=pwm_val_2) | |
PORTDbits.RD5 = 1; | |
else | |
PORTDbits.RD5 = 0; | |
if(counter<=pwm_val_3) | |
PORTDbits.RD6 = 1; | |
else | |
PORTDbits.RD6 = 0; | |
if(counter<=pwm_val_4) | |
PORTDbits.RD7 = 1; | |
else | |
PORTDbits.RD7 = 0; | |
if(counter == 100) | |
counter = 0; | |
T0IF = 0; | |
} |
此方法缺點為PWM頻率難以調整,不過對於本身PWM模組缺乏或過少之MCU,此法十分有效。
迴圈產生PWM
以迴圈直接產生PWM,可以用一個counter當作現在情況,當迴圈執行次數超過duty cycle的值時,調整high low,這方法十分簡單,但如果要有準確的頻率需要將程式編成組語計算其所需時間,並用NOP指令挑整週期。這方法會佔走整個CPU,十分無效率,但其可以結合輪詢等固定時間需要執行的功能,對於低階,簡單的系統有其應用空間。
No comments:
Post a Comment