前言
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的配置,在之后的文章再详细解读。这部分代码不要全抄!!!