【dsPIC33】PWM系列之PWM触发ADC

前言

PWM触发ADC进行采样,是对于电机控制来说非常重要的一环,做矢量控制,能否运行,电流采样是重中之重,因此电流采样的时刻控制非常重要,本节就接着上篇文章的中心对齐模式,讲解,中心对齐模式下的PWM触发ADC进行采样。

先来看PWM触发的手册解释:

上面就是说:dsPIC33E芯片有一个专门的寄存器TRIGx
来控制触发时刻,这一点功能,我觉得还是比较方便的,比ST的要方便一些,其他的一些延时,采样时间什么的都比较好理解。这个是PWM手册中的,我们要做这个功能,是PWM与ADC混合的,因此还要看一下ADC手册中对于此功能有什么补充。

ADC手册中对于此功能的描述,主要是在寄存器的解释中:

首先,可以配置n次AD转换后再产生ADC中断,这一点是在ST芯片中没有的,如果使用中心对齐模式,因为在一个中心对齐模式的PWM周期中会有两次比较捕获事件,因此需要想办法处理,这一点在我之前的文章中有讲,在dsPIC芯片中,那就简单多了,直接配置每完成n次转换产生ADC中断即可。

然后就是触发源的选择,我们是使用PWM触发ADC,配置相应的两个寄存器即可。

ADC转换时间计算如下:

再继续看一下手册上的示意图:

我在图中加了标注,对于PWM触发寄存器与周期寄存器值的关系,标注还是比较清晰易懂的。

其他一些IO端口设置成输入啥的,这种就不再赘述,我们现在先使用最后一种情况试一下,也就是在触发寄存器与周期寄存器相同,然后再PWM末尾触发一次,然后ADC配置成每发生一次转换就产生ADC中断,即SMPI位置0。

AD1CON2bits.SMPI = 0x00;

在ADC中断中翻转LED电平:

void __attribute__((interrupt,no_auto_psv)) _AD1Interrupt(void)
{
    static uint8_t led_flag = 0;
    AD1CON1bits.DONE = 0; //完成标志清零
    IFS0bits.AD1IF = 0;   //清楚中断标志
    led_flag = 1 - led_flag;
    if (led_flag == 0)
    {
        PORTAbits.RA4 = 0;
    }
    else
    {
        PORTAbits.RA4 = 1;
    }
}

完整代码如下:

//振荡器配置
void System_Colck(void)
{
    //产生Fosc = 120MHz  芯片以60MIPS工作
    CLKDIVbits.PLLPRE = 0;//N1 = 2  
    PLLFBDbits.PLLDIV = 58;//M = 60  
    CLKDIVbits.PLLPOST = 0;//N2 = 2    8 * (60 / (2 + 2)) = 120M
    while (OSCCONbits.COSC!= 0b011);
    while (OSCCONbits.LOCK!= 1) {};//PLL 处于锁定状态
}
//32位定时器配置
void Pwm_Init(void)//定时器模式
{   
    /@@*先关闭PWM  在 PTEN = 0 的情况下,才能修改PWM配置*/
    PTCONbits.PTEN = 0;   //失能高速PWM<a class="decoration-color" href="https://buy.icxbk.com/index.php?ctl=Product&met=lists&category_id=1047" target="_blank">模块</a>
    /@@*中央对齐模式的周期由 PHASEx/SPHASEx决定  15K*/
    PHASE1 = PHASE2 = PHASE3  = 4000;
    /@@*初始占空比*/
    PDC1 = 1000;
    PDC2 = 2000;
    PDC3 = 3500;
    /@@*死区时间配置*/
    DTR1 = DTR2 = DTR3 = 25;//PWM 死区寄存器  控制PWMxH的死区
    ALTDTR1 = ALTDTR2 = ALTDTR3 = 25;//PWM 备用死区寄存器  控制PWMxL的死区
    /@@*PWM输出引脚控制*/
    IOCON1 = IOCON2 = IOCON3 = 0xC000; 
    /@@*设置主时基,边沿对齐模式,正死区和独立占空比 中央对齐模式*/
    PWMCON1bits.ITB = 1;
    PWMCON1bits.CAM = 1;
    PWMCON2bits.ITB = 1;
    PWMCON2bits.CAM = 1;
    PWMCON3bits.ITB = 1;
    PWMCON3bits.CAM = 1;
    /@@*配置故障*/
    FCLCON1 = FCLCON2 = FCLCON3 = 0x0000;
    /@@*最大预分频比 1分频*/
    PTCON2 = 0x0000;//PWM 时钟分频比选择寄存器
    TRIG1 = 4000;
    TRGCON1bits.TRGDIV = 0;
    TRGCON1bits.TRGSTRT = 0;
    PWMCON1bits.TRGIEN = 1;
    PTCONbits.PTEN = 1;   //使能高速PWM模块
}
/@@*Adc配置*/
void Adc_Init(void)
{
    /@@*端口初始化*/
    ANSELA = 0;
    ANSELB = 0;
    ANSELAbits.ANSA0 = 1;    // U相电流采样
    ANSELAbits.ANSA1 = 1;    // V相电流采样
    ANSELBbits.ANSB0 = 1;    // 总电流采样
    ANSELBbits.ANSB1 = 1;   // 电位器
    ANSELBbits.ANSB2 = 1;   // 偏移电压1.65V采样
    ANSELBbits.ANSB3 = 1;   // 总线电压采样
    /@@*控制寄存器配置*/
    AD1CON1bits.ADON = 0;    //ADC关闭
    AD1CON1bits.ADSIDL = 0;  //在空闲模式下模块继续工作
    AD1CON1bits.AD12B = 0;   //10位,4通道ADC工作
    AD1CON1bits.FORM = 0;    //输出整数(0000 00dd dddd dddd)
    AD1CON1bits.SSRC = 0x00; //由PWM主特殊事件触发结束采样并启动转换
    AD1CON1bits.SSRCG = 1;
    AD1CON1bits.SAMP = 1;    //同时采样 CH0,CH1,CH2,CH3
    AD1CON1bits.ASAM = 1;    //上一次转换后立即开始采样,即SAMP位自动置1
    /@@*短暂延时,让寄存器有充分时间写入*/
    Nop();
//    AD1CON1bits.SSRC = 0;    //0 for manual, 2 for Timer3, 3 for SEVTCMP
    /@@*不使用DMA*/
    AD1CON4 = 0x0000;
    /@@*输入通道0选择寄存器配置*/
    AD1CHS0 = 0x000D;   //MUX B Channel 0 反向输入为 VREF-
                        //MUX B Channel 0 同向输入为 is AN0
                        //MUX A Channel 0 反向输入为 VREF-
                        //MUX A Channel 0 同向输入为 is AN8
                        //just a startup sequence to read the POT ( AN0 )
    AD1CSSL = 0x0000;    //输入扫描时跳过ANx
    AD1CON3 = 0x0005;    //时钟由系统时钟产生
                        //自动采样时间时间位 = 0 TAD,因为PWM控制采样时间
                        //TAD = 6*TCY, TAD 约 85ns
    AD1CON2 = 0x0000;    //ADREF+ = AVDD ADREF- = AVSS
                        //Do not scan inputs
                        //00 = Converts CH0 only
                        //A/D is currently filling buffer 0x0-0x7
                        //Interrupts at the completion of conversion for each sample/convert sequence
                        //Always starts filling buffer from the beginning
                        //Always uses channel input selects for Sample A
        AD1CON2bits.SMPI = 0x00;   //每一个AD转换产生一个中断
    AD1CON1bits.DONE = 0;    //Making sure that there is not any conversion in progress
    IPC3bits.AD1IP = 5;        //Assigning ADC ISR priority
    IFS0bits.AD1IF = 0;        //清楚ADC中断标志
    IEC0bits.AD1IE = 1;        //使能中断
    AD1CON1bits.ADON = 1;    //使能ADC
}
void Led_Init()
{
    TRISA = 0xffef;   //A4端口配置为输出 
    PORTAbits.RA4 = 1;//熄灭LED
}
int main(void)
{       
    System_Colck();   //时钟振荡器配置
    Adc_Init();       //ADC配置
    Pwm_Init();       //PWM配置
    Led_Init();       //Led配置
    while(1)
    {
    }
}
void __attribute__((interrupt,no_auto_psv)) _AD1Interrupt(void)
{
    static uint8_t led_flag = 0;
    AD1CON1bits.DONE = 0; //完成标志清零
    IFS0bits.AD1IF = 0;   //清楚中断标志
    led_flag = 1 - led_flag;
    if (led_flag == 0)
    {
        PORTAbits.RA4 = 0;
    }
    else
    {
        PORTAbits.RA4 = 1;
    }
}

注意我上面的程序,触发时间寄存器与周期寄存器是相同的,而且ADC通道的配置我是先没有配置的,本节文章只讲解PWM触发ADC的时刻!!!注意代码不要全抄!!!

示波器如图:

可以看到波形正确,之后我们试一下,触发时间寄存器改成3000,也就是比周期寄存器的4000小,然后改成每2个AD转换产生一次中断,代码就不重复贴了,就改一下那些相应寄存器即可,波形如图:

可以看到由于我设置的触发时间为3000,周期为4000,所以偏转了一些,而且我设置的2次AD触发一次中断,因此触发数量还是相同的。

最后,依然要再次提醒,本节只是讲了PWM触发ADC的时刻,没有讲具体ADC的同时采样,因此上面关于ADC通道的配置,是直接给0,所以跟硬件管脚不对应的,关于ADC的配置,在之后的文章再详细解读。这部分代码不要全抄!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值