打印
[学习资料]

PIC32输出比较(PWM)

[复制链接]
1286|21
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zljiu|  楼主 | 2019-6-21 14:14 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
一.输出比较初始化

步骤:

1.复用端口映射为OCx     

例如:RPA0Rbits.RPA0R=0b0101; 即RPA0引脚作为外设OC1使用             见附一

2.OCM<2:0>:输出比较模式选择位

例如:OC1CON=0X06;      //输出比较端口1配置为PWM故障禁止模式。

3.OC32<5>:32 位比较模式位

例如:OC1CONCLR=0X010;  //将第五位清零,设置为16位单定时器模式。

4.OCTSEL<3>:输出比较定时器选择位

例如:OC1CONSET=0X08;  //将第三位设置为一,选择定时器3为基时钟

5.定时器使能初始化

例如:OpenTimer3(T1_ON|T1_SOURCE_INT|T1_PS_1_1,pwmn);

Pwmn周期数={ FB外设/pwmfp频率 } - 1;

6.ON<15>:输出比较外设使能位

例如:OC1CONSET=0X8000;         //将第15位置一,输出比较使能。


使用特权

评论回复
沙发
zljiu|  楼主 | 2019-6-21 14:14 | 只看该作者
二.中断触发条件

单比较模式

• 比较匹配事件强制OCx 引脚为高电平;该引脚的初始状态为低电平。在发生单比较匹配事件

时,产生中断。

• 比较匹配事件强制OCx 引脚为低电平;该引脚的初始状态为高电平。在发生单比较匹配事件时,产生中断。

• 比较匹配事件使OCx 引脚电平翻转。翻转事件是连续的,且每次翻转事件都会产生一次中断。

双比较模式

当OCx引脚被驱动为低电平(单脉冲的下降沿)时,相应通道的中断标志OCxIF会置为有效。

PWM模式

TyIF 中断标志在每个PWM 周期边界处置为有效。

当使能了具有故障保护输入模式的PWM 时,必须通过将相应的TRIS SFR 位置1 以将OCFx 故

障引脚配置为输入。选择PWM 故障模式时, OCFx 故障输入引脚不会自动配置为输入。


使用特权

评论回复
板凳
zljiu|  楼主 | 2019-6-21 14:14 | 只看该作者
三.计算各项值

所需的PWM通过写入OCxRS 寄存器来指定PWM 占空比。可以在任何时候写OCxRS 寄存器,但是在PRy和TMRy 发生匹配(即周期结束)前占空比值不会被锁存到OCxR 中。



PWM 周期 = [(PR 1) • TPB • (TMR 预分频值)]  或T=PR 1/(Fpb/PS)

PWM 频率 = 1/[PWM 周期]

最大PWM 分辨率:在一个PWM周期内有n个时基(PR),为2的x次方,分辨率为x。

以频率为52.08 kHz为例

FPB = 10 MHz

Timer2 预分频比设置: 1:1

1/52.08 kHz = (PR2 1) • TPB • (Timer2 预分频值)=(PR2 1)/(FPB/Timer2预分频)

19.20 us = (PR2 1) • 0.1 us • (1)

PR2 = 191

确定可用于52.08 kHz PWM 频率和10 MHz 外设总线时钟速率的占空比的最大分辨率。

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 位


使用特权

评论回复
地板
zljiu|  楼主 | 2019-6-21 14:15 | 只看该作者
四.拓展

#用PWM绘制新波形

峰峰值最大为PR定时器数。

每个PWM周期为一个样本

所需波形频率:

所需频率=1/(n个样本*PWM周期)

每个样本波形的幅度值(占空比*PR):

波形/n,将n个值列入数组表格,OCxRS引用。



每个PWM周期输出通过RC滤波电路转换为模拟信号,约为一条幅值为 高电平*占空比 的直线,

通过改变占空比控制赋值y轴,再通过控制周期数控制产生的x轴





正弦波波形产生,占空比计算值 OC1RS=偏移量 振幅*SIN(2*pi/周期样本值),但如此短的时间无法

来的及计算sin,最好制成表格数组引用。



#发出声音

绘制相应的频率可发出相应的音高(do,re,me…),再调整波形幅度决定音色(不同的乐器)。


使用特权

评论回复
5
zljiu|  楼主 | 2019-6-21 14:16 | 只看该作者
举例说明

一.   用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;


使用特权

评论回复
6
zljiu|  楼主 | 2019-6-21 14:16 | 只看该作者
void PWMinint()

{

OC1CON=0;              //关闭 初始都为零,定时器二,16位模式

OC1CON=0x06;           //PWM无故障模式

OC1R=10000;            //初始占空比为10000

OC1RS=10000;           

pwmn=48000000/pwmfp-1;          PWM周期数

OpenTimer2(T2_ON|T2_SOURCE_INT|T2_PS_1_1,pwmn);  //初始定时器2

OC1CONSET=0x8000;              //开启输出比较使能

}


使用特权

评论回复
7
zljiu|  楼主 | 2019-6-21 14:16 | 只看该作者
void __ISR(_TIMER_2_VECTOR,ipl3) Timer2hander(void)   //中断

{

    mT2ClearIntFlag();

if(pwm_g==0)

    count ;            //当count越大亮度越低反之详见电路图

    else

    count--;

    if(count>4410)

    pwm_g=1;

    if(count==0)

    pwm_g=0;

    OC1RS=count*pwmn/4410;       //4410/4410=1s一翻转

}


使用特权

评论回复
8
zljiu|  楼主 | 2019-6-21 14:17 | 只看该作者
int main()

{

    RPB7Rbits.RPB7R=0b0101;

    pwmfp=4410;

    PWMinint();

    mT2SetIntPriority(3);

    mT2IntEnable(1);

    INTEnableSystemMultiVectoredInt();

    while(1);

}


使用特权

评论回复
9
zljiu|  楼主 | 2019-6-21 14:17 | 只看该作者
二.按键实时控制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)


使用特权

评论回复
10
zljiu|  楼主 | 2019-6-21 14:18 | 只看该作者
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;

//显示于按键标志,用于周期性定时处理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


使用特权

评论回复
11
zljiu|  楼主 | 2019-6-21 14:18 | 只看该作者
//LED字库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开放

    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);

}


使用特权

评论回复
12
zljiu|  楼主 | 2019-6-21 14:19 | 只看该作者
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);



    }

}


使用特权

评论回复
13
zljiu|  楼主 | 2019-6-21 14:19 | 只看该作者
//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;

    led=led0;

    i=led/10;

    ledBuff[1]=Led_lib;

    i=led;

    ledBuff[2]=Led_lib;

    n ;

    if(n=2)

    flag=1;

}

使用特权

评论回复
14
zljiu|  楼主 | 2019-6-21 14:20 | 只看该作者
void pwminit()

{

    RPB13Rbits.RPB13R=0b0101;//外设端口映射为OC4

    OC4CON=0;

    OC4R=0;

    OC4RS=0;

    OC4CON=0X06;    //PWM无故障模式

    OpenTimer2(T2_ON|T2_SOURCE_INT|T2_PS_1_1,g_period);  //基定时器初始

    OC4CONSET=0X8000;      //使能OC4

//    mT2SetIntPriority(1);

//    mT2IntEnable(1);

}


使用特权

评论回复
15
zljiu|  楼主 | 2019-6-21 14:20 | 只看该作者
//void __ISR(_TIMER_2_VECTOR,ipl1) Timer2(void)

//{

//

//    mT2ClearIntFlag();

//    OC4RS=g_duty;   

//若由输出比较的基定时器2刷新值,则会随着PWM周期的太小刷新过快,会与定时器1的中断多次冲突造成定时器1无法正常工作。

//    PR2=g_period;   

//}


使用特权

评论回复
16
zljiu|  楼主 | 2019-6-21 14:21 | 只看该作者
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);

}


使用特权

评论回复
17
zljiu|  楼主 | 2019-6-21 14:21 | 只看该作者
void __ISR(_TIMER_1_VECTOR, ipl5) Timer1Handler(void)

{

    // Clear the interrupt flag

    INTClearFlag(INT_T1);

    OC4RS=g_duty;

    PR2=g_period;//由定时器1统一更新周期和占空比

    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;

    }

}


使用特权

评论回复
18
zljiu|  楼主 | 2019-6-21 14:22 | 只看该作者
//设为数字端口,当有足够电压改变1与0的转换才有信号

void BtnInit()

{

    ANSELAbits.ANSA0 = 0;

    ANSELAbits.ANSA1 = 0;

//    ANSELBbits.ANSB3 = 0;

    ANSELBbits.ANSB14 = 0;

}


使用特权

评论回复
19
zljiu|  楼主 | 2019-6-21 14:22 | 只看该作者
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;

}


使用特权

评论回复
20
zljiu|  楼主 | 2019-6-21 14:22 | 只看该作者
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);

}


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

51

主题

3323

帖子

3

粉丝