前言
上篇文章讲到使用PWM来触发ADC,进而控制ADC采样的时刻,这在电机控制中是非常重要的一步,但上篇文章没有细讲多路ADC的同时采样,本文就单独把这一部分拉出来进行分析。
读取ADC值有两种方式,一种使用DMA,另一种不使用DMA。
使用DMA:
翻译过来就是:使能DMA模块(ADDMAEN = 1)时,每个ADC外设ADC模块仅具有1个ADC结果缓冲器(即ADC1BUF0),并且在下一次ADC转换之前,必须由CPU或DMA控制器读取ADC转换结果。 可以避免覆盖先前的值。
不使用DMA:
翻译过来就是:禁用DMA模块时(ADDMAEN = 0),ADC具有16个结果缓冲区.ADC转换结果按顺序存储在ADC1BUF0-ADC1BUFF中,无论使用哪个模拟输入取决于SMPIx位和1c中描述的条件 以上。 被测ANx输入与转换结果将放置在哪个ADC缓冲器(ADC1BUF0-ADC1BUFF)之间没有关系。
也就是ADxCON4寄存器中的ADDMAEN位为DMA使能位,如果使用了DMA,则数据都会存储在ADC1BUF0,需要在下次采样之前取出数据,避免数据覆盖掉之前的值。
那么结合我们实际的情况,对于电机电流采样,每次采样就那几个数据采集一个就行,因此不使用DMA会更方便一些,可以直接读取相应的存储寄存器即可。
大多数应用,使用分时采样,或者叫顺序采样即可,但是在一些特殊场合,比如需要同步采样来消除模拟信号之间的相位差时,就必须有多个采样电路同时工作,dsPIC内部的10位AD转换器前端一共有4个可以同时工作的采样保持电路。可以通过ADCON2中的CHPS位来配置:
看一下我的原理图:
由于项目中有6个模拟信号,而且不使用DMA,因此可以参照手册中的 “使用同时采样对8个输入进行采样(不使用DMA)” 章节:
由上图可知,需要使能同时采样:
AD1CON1bits.SIMSAM = 1; //同步采样
由上图可知,8通道同时采样实际是分为两部分,因为每个ADC只有4个通道,CH0-CH3,但是可以选择这四个通道连通的是哪四个输入引脚,第一个一个触发信号到来时,会使配置的MUXA的CH0-CH3同时采样,然后顺序转换,之后下一个触发信号到来时,会使配置的MUXB的CH0-CH3同时采样,然后顺序转换,依次存储在内存中。也就是说8通道同时采样是有两组,一组是MUXA,另一组是MUXB,虽说是8通道,但每个触发信号只能同时采样4个引脚,也就是交替采样这个概念。手册中的配置如图:
手册中的配置是4次采样后产生中断,也就是说,4个触发信号之后会产生一个ADC中断,也就是说,8个模拟输入,每个都采样了两遍,因为ADC的缓存空间有16个,因此可以存储两遍。而我在实际项目中,不需要每个通道采集两遍,因此通过配置SMPI位更改,而BUFM位也一样配置为0:
显然我是希望总是从起始地址开始填充缓冲区的。
按照手册上的配置,存储空间的示意图如下:
之后,我们可以参照手册上的配置来配置自己的,这时,需要再细看输入通道寄存器,dsPIC33E芯片中是有两个输入通道寄存器的,一个是ADxCHS0,用来进行CH0通道的配置;另一个是ADxCHS123,用来进行CH1,2,3的配置,CH1,2,3是同时配置的,由此可见对于CH0通道的使用是更加灵活的。
由上面的寄存器介绍可知:CH0通道可以配置为任意的AD引脚。
而CH1,2,3则稍微有些固定,不过也还算是灵活,也可以满足需要。
接下来就要理解交替采样的概念,手册上有一句话说的比较清楚:
就是说,第一个触发信号使用配置的MUXA通道进行采样,第二个触发信号使用配置的MUXB通道进行采样。之后再循环。
而我使用的是SVPWM,因此,只有触发时间点比PWM周期值小,便会在每个周期都会有两个触发信号:
到这里,便可以看我的实际电路图,来进行配置了。
我有6个通道,可以将MUXA的CH1,2,3通道配置为AN0,1,2,然后将MUXB的CH1,2,3通道配置为AD3,4,5,至于两个CH0,便可以随便指定。(注:这里我测试如果MUXA与MUXB的CH0通道都配置为同一个引脚的话,采集会有问题,因此避免两个CH0采集同一个通道)当然,在配置ADC之前,不要忘记将引脚配置为输入模式。
配置如下:
void sensor_SensorAdc_Init (void)
{
AD1CON1bits.DONE = 0; //确保没有任何正在进行的转换
/@@*端口初始化*/
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; // 总线电压采样
TRISAbits.TRISA0 = 1; //配置为输入
TRISAbits.TRISA1 = 1;
TRISBbits.TRISB0 = 1;
TRISBbits.TRISB1 = 1;
TRISBbits.TRISB2 = 1;
TRISBbits.TRISB3 = 1;
/@@*控制寄存器配置*/
AD1CON1 = 0x0000; //清零
AD1CON1bits.ADON = 0; //ADC关闭
AD1CON1bits.ADSIDL = 0; //在空闲模式下模块继续工作
AD1CON1bits.AD12B = 0; //10位,4通道ADC工作
AD1CON1bits.FORM = 0; //输出整数(0000 00dd dddd dddd)
AD1CON1bits.SSRC = 0x00; //当SSRCG=1时 SSRC=0即为由PWM发生器1主触发比较结束采样并启动转换
AD1CON1bits.SSRCG = 1;
AD1CON1bits.SAMP = 1; //同时采样 CH0,CH1,CH2,CH3
AD1CON1bits.ASAM = 1; //上一次转换后立即开始采样,即SAMP位自动置1
AD1CON1bits.SIMSAM = 1; //同步采样
AD1CON2 = 0x0000; //清零
AD1CON2bits.CHPS = 0x03; //多通道采样 CH0 - CH3
AD1CON2bits.VCFG = 0x00; //参考电压选择AVdd与AVss
AD1CON2bits.CSCNA = 0; //不扫描输入
AD1CON2bits.SMPI = 0x01; //每完成2次采样/转换操作后产生ADC中断a
AD1CON2bits.ALTS = 1; //MUXA与MUXB交替采样
AD1CON2bits.BUFM = 0; //总是从起始地址开始填充缓冲区
AD1CON3 = 0x0000; //清零
/@@*短暂延时,让寄存器有充分时间写入*/
Nop();
/@@*不使用DMA*/
AD1CON4 = 0x0000;
/@@**************************MUXA输入选择****************************/
/@@*输入CH0选择寄存器配置*/
AD1CHS0bits.CH0SA = 4; //CH0同向输入端选择 AN4 (偏移电压采集)
AD1CHS0bits.CH0NA = 0; //CH0反向输入端选择 Vrefl
/@@*输入CH1,CH2,CH3选择寄存器配置*/
AD1CHS123bits.CH123SA = 0; //CH1,2,3同向输入端对应 AN0,1,2
AD1CHS123bits.CH123NA = 0; //CH1,2,3反向输入端都为 Vrefl
/@@**************************MUXB输入选择***************************/
/@@*输入CH0选择寄存器配置*/
AD1CHS0bits.CH0SB = 5; //CH0同向输入端选择 AN3 (电源电压采集)
AD1CHS0bits.CH0NB = 0; //CH0反向输入端选择 Vrefl
/@@*输入CH1,CH2,CH3选择寄存器配置*/
AD1CHS123bits.CH123SB = 1; //CH1,2,3同向输入端对应 AN3,4,5
AD1CHS123bits.CH123NB = 0; //CH1,2,3反向输入端都为 Vrefl
// AD1CSSL = 0x001f; //输入扫描时跳过ANx
IFS0bits.AD1IF = 0; //清楚ADC中断标志
IEC0bits.AD1IE = 1; //使能中断
IPC3bits.AD1IP = 0x01; //ADC中断优先级设置为1
AD1CON1bits.ADON = 1; //使能ADC
}
void sensor_UpdateSensor (SENSOR_DATA *pSensor)
{
/@@*****************************************************************************************************************************************
* ADC采集程序
****************************************************************************************************************************************/
pSensor->i16_speedVol = ADC1BUF5;
pSensor->i16_current_U = ADC1BUF1;
pSensor->i16_current_V = ADC1BUF2;
pSensor->i16_current_All = ADC1BUF3;
pSensor->i16_offsetV = ADC1BUF0;
pSensor->i16_vin = ADC1BUF4;
}