找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 8425|回復(fù): 2
打印 上一主題 下一主題
收起左側(cè)

PIC32輸出比較(PWM)

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:51090 發(fā)表于 2014-9-22 15:52 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
原文出自枯葉之碟的博客


一.輸出比較初始化
步驟:
1.復(fù)用端口映射為OCx     
例如:RPA0Rbits.RPA0R=0b0101; 即RPA0引腳作為外設(shè)OC1使用             見(jiàn)附一
2.OCM<2:0>:輸出比較模式選擇位
例如:OC1CON=0X06;      //輸出比較端口1配置為PWM故障禁止模式。
3.OC32<5>:32 位比較模式位
例如:OC1CONCLR=0X010;  //將第五位清零,設(shè)置為16位單定時(shí)器模式。
4.OCTSEL<3>:輸出比較定時(shí)器選擇位
例如:OC1CONSET=0X08;  //將第三位設(shè)置為一,選擇定時(shí)器3為基時(shí)鐘
5.定時(shí)器使能初始化
例如:OpenTimer3(T1_ON|T1_SOURCE_INT|T1_PS_1_1,pwmn);
Pwmn周期數(shù)={ FB外設(shè)/pwmfp頻率 } - 1;
6.ON<15>:輸出比較外設(shè)使能位
例如:OC1CONSET=0X8000;         //將第15位置一,輸出比較使能。
詳細(xì)初始化控制寄存器見(jiàn)附二
二.中斷觸發(fā)條件
單比較模式
?? 比較匹配事件強(qiáng)制OCx 引腳為高電平;該引腳的初始狀態(tài)為低電平。在發(fā)生單比較匹配事件
時(shí),產(chǎn)生中斷。
?? 比較匹配事件強(qiáng)制OCx 引腳為低電平;該引腳的初始狀態(tài)為高電平。在發(fā)生單比較匹配事件時(shí),產(chǎn)生中斷。
?? 比較匹配事件使OCx 引腳電平翻轉(zhuǎn)。翻轉(zhuǎn)事件是連續(xù)的,且每次翻轉(zhuǎn)事件都會(huì)產(chǎn)生一次中斷。
雙比較模式
當(dāng)OCx引腳被驅(qū)動(dòng)為低電平(單脈沖的下降沿)時(shí),相應(yīng)通道的中斷標(biāo)志OCxIF會(huì)置為有效。
PWM模式
TyIF 中斷標(biāo)志在每個(gè)PWM 周期邊界處置為有效。
當(dāng)使能了具有故障保護(hù)輸入模式的PWM 時(shí),必須通過(guò)將相應(yīng)的TRIS SFR 位置1 以將OCFx 故
障引腳配置為輸入。選擇PWM 故障模式時(shí), OCFx 故障輸入引腳不會(huì)自動(dòng)配置為輸入。
三.計(jì)算各項(xiàng)值
所需的PWM通過(guò)寫(xiě)入OCxRS 寄存器來(lái)指定PWM 占空比。可以在任何時(shí)候?qū)慜CxRS 寄存器,但是在PRy和TMRy 發(fā)生匹配(即周期結(jié)束)前占空比值不會(huì)被鎖存到OCxR 中。
PWM 周期 = [(PR 1) ?? TPB ?? (TMR 預(yù)分頻值)]  或T=PR 1/(Fpb/PS)
PWM 頻率 = 1/[PWM 周期]
最大PWM 分辨率:在一個(gè)PWM周期內(nèi)有n個(gè)時(shí)基(PR),為2的x次方,分辨率為x。
以頻率為52.08 kHz為例
FPB = 10 MHz
Timer2 預(yù)分頻比設(shè)置: 1:1
1/52.08 kHz = (PR2 1) ?? TPB ?? (Timer2 預(yù)分頻值)=(PR2 1)/(FPB/Timer2預(yù)分頻)
19.20 us = (PR2 1) ?? 0.1 us ?? (1)
PR2 = 191
確定可用于52.08 kHz PWM 頻率和10 MHz 外設(shè)總線時(shí)鐘速率的占空比的最大分辨率。
1/52.08 kHz = 2^PWM 分辨率?? 1/10 MHz ?? 1
19.20 us = 2^PWM 分辨率?? 100 ns ?? 1
192 = 2^PWM 分辨率
log10(192) = (PWM 分辨率) ?? log10(2)
PWM 分辨率 = 7.6 位
四.拓展
#用PWM繪制新波形
峰峰值最大為PR定時(shí)器數(shù)。
每個(gè)PWM周期為一個(gè)樣本
所需波形頻率:
所需頻率=1/(n個(gè)樣本*PWM周期)
每個(gè)樣本波形的幅度值(占空比*PR):
波形/n,將n個(gè)值列入數(shù)組表格,OCxRS引用。
每個(gè)PWM周期輸出通過(guò)RC濾波電路轉(zhuǎn)換為模擬信號(hào),約為一條幅值為 高電平*占空比 的直線,
通過(guò)改變占空比控制賦值y軸,再通過(guò)控制周期數(shù)控制產(chǎn)生的x軸
正弦波波形產(chǎn)生,占空比計(jì)算值 OC1RS=偏移量 振幅*SIN(2*pi/周期樣本值),但如此短的時(shí)間無(wú)法
來(lái)的及計(jì)算sin,最好制成表格數(shù)組引用。
#發(fā)出聲音
繪制相應(yīng)的頻率可發(fā)出相應(yīng)的音高(do,re,me…),再調(diào)整波形幅度決定音色(不同的樂(lè)器)。
舉例說(shuō)明
一.   用PWM制作呼吸燈效果
#include
#pragma config FPLLIDIV = DIV_2         // PLL Input Divider (2x Divider)
#pragma config FPLLMUL = MUL_24
#pragma config FPLLODIV = DIV_2
#pragma config FPBDIV = DIV_1
#pragma config FNOSC = FRCPLL
#pragma config FUSBIDIO = OFF
#pragma config FWDTEN = OFF
#pragma config JTAGEN = OFF
int pwm1,pwmn,pwmfp,count,pwm_g;
void PWMinint()
{
OC1CON=0;              //關(guān)閉 初始都為零,定時(shí)器二,16位模式
OC1CON=0x06;           //PWM無(wú)故障模式
OC1R=10000;            //初始占空比為10000
OC1RS=10000;           
pwmn=48000000/pwmfp-1;          PWM周期數(shù)
OpenTimer2(T2_ON|T2_SOURCE_INT|T2_PS_1_1,pwmn);  //初始定時(shí)器2
OC1CONSET=0x8000;              //開(kāi)啟輸出比較使能
}
void __ISR(_TIMER_2_VECTOR,ipl3) Timer2hander(void)   //中斷
{
    mT2ClearIntFlag();
if(pwm_g==0)
    count ;            //當(dāng)count越大亮度越低反之詳見(jiàn)電路圖
    else
    count--;
    if(count>4410)
    pwm_g=1;
    if(count==0)
    pwm_g=0;
    OC1RS=count*pwmn/4410;       //4410/4410=1s一翻轉(zhuǎn)
}
int main()
{
    RPB7Rbits.RPB7R=0b0101;
    pwmfp=4410;
    PWMinint();
    mT2SetIntPriority(3);
    mT2IntEnable(1);
    INTEnableSystemMultiVectoredInt();
    while(1);
}
二.按鍵實(shí)時(shí)控制PWM
#include
// Configuration Bit settings
// SYSCLK = 48 MHz (8MHz Crystal / FPLLIDIV * FPLLMUL / FPLLODIV)
// PBCLK = 48 MHz (SYSCLK / FPBDIV)
// Primary Osc w/PLL (XT ,HS ,EC PLL)
// WDT OFF
#pragma config FPLLMUL = MUL_24, FPLLIDIV = DIV_2, FPLLODIV = DIV_2, FWDTEN = OFF
#pragma config POSCMOD = OFF, FNOSC = FRCPLL, FPBDIV = DIV_1,FSOSCEN = OFF
#pragma config FUSBIDIO = OFF           //FUSBIDIO?????
#pragma config FVBUSONIO = OFF
#pragma config JTAGEN   = OFF           //JTAG disable
#pragma config CP       = OFF
#pragma config DEBUG    = ON
// Period needed for timer 1 to trigger an interrupt every 0.1 second
// (48MHz PBCLK / 1 = 48000000KHz Timer 1 clock)
#define PERIOD  48000       //48000/48000000 = 0.001s = 1ms
#define BTN_DELAY   5 //2*1=2ms
#define SYS_FREQ (48000000L)
//????????
typedef enum //PRO_Status
{
    SHOW_PERIOD = 0,
    SHOW_DUTY,
    SHOW_SET_PERIOD,
    SHOW_SET_DUTY,
    SET_PERIOD,
    SET_DUTY
}PRO_STATUS;
PRO_STATUS g_status=SHOW_PERIOD;
UINT16 g_period=40,g_duty=20; //初始周期40,占空比20
UINT16 g_set_period=40,g_set_duty=20;
int    g_led_cnt=0,g_led_flag=0,g_btn_cnt=0,g_btn_flag=0,flag=0;
//顯示于按鍵標(biāo)志,用于周期性定時(shí)處理LED顯示和按鍵
unsigned char Led_lib[] = {
    0x42, 0xf3, 0x86, 0xa2, 0x33, 0x2a, 0x0a, 0xf2, 0x02, 0x22, //0-9
    0x40, 0xf1, 0x84, 0xa0, 0x31, 0x28, 0x08, 0xf0, 0x00, 0x20, //0.-9.
    0x1e, 0x2a, 0x0e, 0x0f, 0xbf, 0x23, 0x9b, 0x8b}; //FSEt-yno
//LED字庫(kù)SPI初始化
void SpiInitDevice() {
    // 8 bits/char, input data sampled at end of data output time
    SpiOpenFlags oFlags = SPI_OPEN_MSTEN | SPI_OPEN_CKP_HIGH | SPI_OPEN_MODE8 | SPI_OPEN_ON;
    PORTSetPinsDigitalOut(IOPORT_B, BIT_9);//作為鎖存,1鎖存,0開(kāi)放
    PPSOutput(2, RPB8, SDO2); // Set RB8 pin as output for SDO2
    // Open SPI module, use SPI channel 2, use flags set above, Divide Fpb by 6
    SpiChnOpen(2, oFlags, 6);
}
void SpiDoBurst(unsigned char *pBuff, unsigned char Len) {
    if (pBuff) {
        unsigned int i;
        PORTClearBits(IOPORT_B, BIT_9);
        for (i = 0; i < Len; i ) {
            SpiChnPutC(2, pBuff[ i]);
        }
     
        PORTSetBits(IOPORT_B, BIT_9);
      
    }
}
//LED初始化
void Led()
{
    static unsigned char ledBuff[4] = {0x00, 0x00, 0x00, 0x00};
    static int led = 0;
    int i,n;
    SpiDoBurst(ledBuff, 4);
   
    //LED顯示 4,1,2,3
    switch(g_status)
    {
        case SHOW_PERIOD:
            led=g_period;
            ledBuff[3]=0b00010110;//Led_lib[1];
            break;
        case SHOW_DUTY:
            led=g_duty;
            ledBuff[3]=0b01001110;//Led_lib[2];
            break;
        case SHOW_SET_PERIOD:
            led=g_set_period;
            ledBuff[3]=0b00010100;//Led_lib[3];
            break;
        case SHOW_SET_DUTY:
            led=g_set_duty;
            ledBuff[3]=0b01001100;//Led_lib[4];
            break;
        case SET_PERIOD:
            led=g_set_period;
            ledBuff[3]=0b00010100;//Led_lib[3];
            break;
        case SET_DUTY:
            led=g_set_duty;
            ledBuff[3]=0b01001100;//Led_lib[4];
            break;
    }
    i=led/100;
    i=i;
    ledBuff[0]=Led_lib[ i];
    led=led0;
    i=led/10;
    ledBuff[1]=Led_lib[ i];
    i=led;
    ledBuff[2]=Led_lib[ i];
    n ;
    if(n=2)
    flag=1;
}
void pwminit()
{
    RPB13Rbits.RPB13R=0b0101;//外設(shè)端口映射為OC4
    OC4CON=0;
    OC4R=0;
    OC4RS=0;
    OC4CON=0X06;    //PWM無(wú)故障模式
    OpenTimer2(T2_ON|T2_SOURCE_INT|T2_PS_1_1,g_period);  //基定時(shí)器初始
    OC4CONSET=0X8000;      //使能OC4
//    mT2SetIntPriority(1);
//    mT2IntEnable(1);
}
//void __ISR(_TIMER_2_VECTOR,ipl1) Timer2(void)
//{
//
//    mT2ClearIntFlag();
//    OC4RS=g_duty;   
//若由輸出比較的基定時(shí)器2刷新值,則會(huì)隨著PWM周期的太小刷新過(guò)快,會(huì)與定時(shí)器1的中斷多次沖突造成定時(shí)器1無(wú)法正常工作。
//    PR2=g_period;   
//}
void Timer1Init()
{
    // Timer1@1ms
    OpenTimer1(T1_ON | T1_SOURCE_INT | T1_PS_1_1, PERIOD);
    // Set up the timer interrupt with a priority of 2
    INTEnable(INT_T1, INT_ENABLED);
    INTSetVectorPriority(INT_TIMER_1_VECTOR, INT_PRIORITY_LEVEL_5);
    INTSetVectorSubPriority(INT_TIMER_1_VECTOR, INT_SUB_PRIORITY_LEVEL_0);
}
//??1????
void __ISR(_TIMER_1_VECTOR, ipl5) Timer1Handler(void)
{
    // Clear the interrupt flag
    INTClearFlag(INT_T1);
    OC4RS=g_duty;
    PR2=g_period;//由定時(shí)器1統(tǒng)一更新周期和占空比
    g_led_cnt ;
    if(g_led_cnt > 100)    //0.1s
    {
        g_led_cnt = 0;
        g_led_flag = 1;
    }
    g_btn_cnt ;
    if(g_btn_cnt > 5)     //5ms
    {
        g_btn_cnt = 0;
        g_btn_flag = 1;
    }
}
//設(shè)為數(shù)字端口,當(dāng)有足夠電壓改變1與0的轉(zhuǎn)換才有信號(hào)
void BtnInit()
{
    ANSELAbits.ANSA0 = 0;
    ANSELAbits.ANSA1 = 0;
//    ANSELBbits.ANSB3 = 0;
    ANSELBbits.ANSB14 = 0;
}
//????
void Button(void)
{
    static int btn0=0,btn1=0,btn2=0,btn3=0,n=0;
    if(PORTAbits.RA0 == 0) //sel
    {
        btn0 ;
        if(btn0 == BTN_DELAY)
        {
             switch(g_status)
            {
                case SHOW_PERIOD:
                    g_status=SHOW_SET_PERIOD;
                    break;
                case SHOW_DUTY:
                    g_status=SHOW_SET_DUTY;
                    break;
                case SHOW_SET_PERIOD:
                    g_status=SHOW_PERIOD;
                    break;
                case SHOW_SET_DUTY:
                     g_status=SHOW_DUTY;
                    break;
             }
//            if (g_status>=SHOW_SET_PERIOD)
//            {
//                g_status=SHOW_PERIOD;
//                OC4CONCLR=0X8000;
//            }
//            else
//            {
//                 g_status=SHOW_SET_PERIOD;
//                 g_set_period=g_period;
//                 g_set_duty=g_duty;
//               
//            }
        }
    }
    else
        btn0 = 0;
    if(PORTAbits.RA1 == 0) //
    {
        btn1 ;
        if(btn1 == BTN_DELAY)
        {
            switch(g_status)
            {
                case SHOW_PERIOD:
                    g_status=SHOW_DUTY;
                    break;
                case SHOW_DUTY:
                    g_status=SHOW_PERIOD;
                    break;
                case SHOW_SET_PERIOD:
                     if (g_set_period<999) g_set_period ;
                     g_period=g_set_period;
                    break;
//                    g_status=SHOW_SET_DUTY;
//                    break;
                case SHOW_SET_DUTY:
                     if (g_set_duty<100) g_set_duty ;
                     g_duty=g_set_duty;
                    break;
//                    g_status=SHOW_SET_PERIOD;
//                    break;
//                case SET_PERIOD:
                  
//                case SET_DUTY:
                  
            }
        }
    }
    else
        btn1 = 0;
    if(PORTBbits.RB14 == 0) //-
    {
        btn2 ;
        if(btn2 == BTN_DELAY)
        {
            switch(g_status)
            {
                case SHOW_PERIOD:
                    g_status=SHOW_DUTY;
                    break;
                case SHOW_DUTY:
                    g_status=SHOW_PERIOD;
                    break;
                case SHOW_SET_PERIOD:
                     if (g_set_period>0) g_set_period--;
                     g_period=g_set_period;
                    break;
//                    g_status=SHOW_SET_DUTY;
//                    break;
                case SHOW_SET_DUTY:
                     if (g_set_duty>0) g_set_duty--;
                     g_duty=g_set_duty;
                    break;
//                    g_status=SHOW_SET_PERIOD;
//                    break;
//                case SET_PERIOD:
//                    if (g_set_period>0) g_set_period--;
//                    break;
//                case SET_DUTY:
//                    if (g_set_duty>0) g_set_duty--;
//                    break;
            }
        }
    }
    else
        btn2 = 0;
//    if(PORTBbits.RB3 == 0) //enter
//    {
//        btn3 ;
//        if(btn3 == BTN_DELAY)
//        {
//            switch(g_status)
//            {
//                case SHOW_PERIOD:
//                     OC1CONCLR=0X8000;
//                    break;
//                case SHOW_DUTY:
//                     OC1CONCLR=0X8000;
//                    break;
//                case SHOW_SET_PERIOD:
//                    g_status=SET_PERIOD;
//                     OC1CONCLR=0X8000;
//                    break;
//                case SHOW_SET_DUTY:
//                    g_status=SET_DUTY;
//                     OC1CONCLR=0X8000;
//                    break;
//                case SET_PERIOD:
//                    g_period=g_set_period;
//                    g_status=SHOW_PERIOD;
//                    while(!flag);
//                    flag=0;
//                    pwminit();
//                    break;
//                case SET_DUTY:
//                    g_duty=g_set_duty;
//                    g_status=SHOW_PERIOD;
//                    while(!flag);
//                    flag=0;
//                    pwminit();
//                    break;
//            }
//        }
//    }
//    else
//        btn3 = 0;
}
int main(int argc, char** argv) {
    int task=0;
    SYSTEMConfig(SYS_FREQ, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);
    INTDisableInterrupts();
    INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR);
    SpiInitDevice();
    BtnInit();
    Timer1Init();
    pwminit();
    INTEnableInterrupts();
    while(1)
    {
        switch(task)
        {
            case 0:
                if(g_led_flag > 0)
                {
                    g_led_flag = 0;
                    Led();
                }
                break;
            case 1:
                if(g_btn_flag > 0)
                {
                    g_btn_flag = 0;
                    Button();
                }
            default:
                break;
        }
        task ;
        if(task > 1) task = 0;
    }
    return (EXIT_SUCCESS);
}
附一:
表11-2: 輸出引腳選擇

例如:RPA0Rbits.RPA0R=0b0101;或 PPSOutput(1,RPA0,OC1); 第1組 即RPA0引腳作為OC1使用
例如:RPB15Rbits.RPB15R=0b0011; 或 PPSOutput(1,RPB15,SS1);
輸入引腳映射選擇


INT4Rbits.INT4R=0B0001; 或 PPSInput(3,SDI2,RPB6)   第3組   //將外部中斷4映射給RPB3
SDI1Rbits.SDI1R=0B0110; 或 PPSInput(2,SDI1,RPC8)   第2組   //數(shù)據(jù)輸入1口映射給RPC8
附二
初始化相關(guān)位定義
bit 15 ON:輸出比較外設(shè)使能位
1 = 使能輸出比較外設(shè)。
0 = 禁止輸出比較外設(shè),不會(huì)消耗電流。允許進(jìn)行SFR 修改。該寄存器中其他位的狀態(tài)不會(huì)受該位置1
或清零影響。
注: 使用1:1 PBCLK 分頻比時(shí),在清零模塊ON 位的指令之后,用戶的軟件不應(yīng)立即在SYSCLK
周期中讀/ 寫(xiě)外設(shè)的SFR。
bit 14 FRZ:調(diào)試異常模式凍結(jié)位
1 = 在CPU 進(jìn)入調(diào)試異常模式時(shí)停止工作
0 = 在CPU 進(jìn)入調(diào)試異常模式時(shí)繼續(xù)工作
注: FRZ 僅在調(diào)試異常模式下可寫(xiě),在正常模式下強(qiáng)制為0。
bit 13 SIDL:IDLE (空閑)模式停止位
1 = 在CPU 進(jìn)入IDLE (空閑)模式時(shí)停止工作
0 = 在IDLE (空閑)模式下繼續(xù)工作
bit 12-6 保留:寫(xiě)入0 ;忽略讀操作
bit 5 OC32:32 位比較模式位
1 = OCxR<31:0> 和/ 或OCxRS<31:0> 用于與32 位定時(shí)器源進(jìn)行比較
0 = OCxR<15:0> 和OCxRS<15:0> 用于與16 位定時(shí)器源進(jìn)行比較
bit 4 OCFLT:PWM 故障條件狀態(tài)位(1)
1 = 發(fā)生了PWM 故障條件(僅可用硬件清零)
0 = 未發(fā)生PWM 故障條件
注: 僅當(dāng)OCM<2:0> = 111 時(shí),才使用該位。
bit 3 OCTSEL:輸出比較定時(shí)器選擇位
1 = Timer3 作為該OCMP 模塊的時(shí)鐘源
0 = Timer2 作為該OCMP 模塊的時(shí)鐘源
關(guān)于輸出比較模塊可用的特定時(shí)基,請(qǐng)參見(jiàn)器件數(shù)據(jù)手冊(cè)。
bit 2-0 OCM<2:0>:輸出比較模式選擇位
111 = OCx 處于PWM 模式;故障引腳使能
110 = OCx 處于PWM 模式;故障引腳禁止
101 = 初始化OCx 引腳為低電平;在OCx 引腳上產(chǎn)生連續(xù)輸出脈沖
100 = 初始化OCx 引腳為低電平;在OCx 引腳上產(chǎn)生單輸出脈沖
011 = 比較事件使OCx 引腳電平翻轉(zhuǎn)
010 = 初始化OCx 引腳為高電平;比較事件強(qiáng)制OCx 引腳為低電平
001 = 初始化OCx 引腳為低電平;比較事件強(qiáng)制OCx 引腳為高電平
000 = 輸出比較外設(shè)被禁止,但會(huì)繼續(xù)消耗電流
轉(zhuǎn)載請(qǐng)說(shuō)明出處謝謝
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏1 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:702600 發(fā)表于 2020-5-6 14:00 | 只看該作者
得來(lái)不費(fèi)工夫,剛好用到,謝謝分享。
回復(fù)

使用道具 舉報(bào)

板凳
ID:974733 發(fā)表于 2021-10-25 10:43 | 只看該作者
您好 請(qǐng)問(wèn)下PIC單片機(jī)怎樣發(fā)單脈沖的PWM波呢?  有例程嗎
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表