电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
【dsPIC33】PWM系列之PWM触发ADC
分 享
扫描二维码分享
【dsPIC33】PWM系列之PWM触发ADC
dsPIC
PWM
ADC
KVIN
关注
发布时间: 2019-11-12
丨
阅读: 4770
# 前言 PWM触发ADC进行采样,是对于电机控制来说非常重要的一环,做矢量控制,能否运行,电流采样是重中之重,因此电流采样的时刻控制非常重要,本节就接着上篇文章的中心对齐模式,讲解,中心对齐模式下的PWM触发ADC进行采样。 先来看PWM触发的手册解释: ![](https://cf05.ickimg.com/bbsimages/201911/07dfc1d2c4034fab9673a71d9fbdff19.png) 上面就是说:dsPIC33E芯片有一个专门的寄存器TRIGx 来控制触发时刻,这一点功能,我觉得还是比较方便的,比ST的要方便一些,其他的一些延时,采样时间什么的都比较好理解。这个是PWM手册中的,我们要做这个功能,是PWM与ADC混合的,因此还要看一下ADC手册中对于此功能有什么补充。 ADC手册中对于此功能的描述,主要是在寄存器的解释中: ![](https://cf05.ickimg.com/bbsimages/201911/fe83cfaef74c52bcb33ba347e4ae1298.png) 首先,可以配置n次AD转换后再产生ADC中断,这一点是在ST芯片中没有的,如果使用中心对齐模式,因为在一个中心对齐模式的PWM周期中会有两次比较捕获事件,因此需要想办法处理,这一点在我之前的文章中有讲:https://www.icxbk.com/article/detail/953.html 在dsPIC芯片中,那就简单多了,直接配置每完成n次转换产生ADC中断即可。 ![](https://cf05.ickimg.com/bbsimages/201911/77c7993f7abbd439ef75022874217ee3.png) 然后就是触发源的选择,我们是使用PWM触发ADC,配置相应的两个寄存器即可。 ![](https://cf05.ickimg.com/bbsimages/201911/72e44ff52b8ecbbbb800d92e9469bc80.png) ADC转换时间计算如下: ![](https://cf05.ickimg.com/bbsimages/201911/5b1de6de151c977bc1bc577eb221f731.png) 再继续看一下手册上的示意图: ![](https://cf05.ickimg.com/bbsimages/201911/7fde248ed54b95c27c4a0e223b07dcd4.png) 我在图中加了标注,对于PWM触发寄存器与周期寄存器值的关系,标注还是比较清晰易懂的。 其他一些IO端口设置成输入啥的,这种就不再赘述,我们现在先使用最后一种情况试一下,也就是在触发寄存器与周期寄存器相同,然后再PWM末尾触发一次,然后ADC配置成每发生一次转换就产生ADC中断,即SMPI位置0。 ```c AD1CON2bits.SMPI = 0x00; ``` 在ADC中断中翻转LED电平: ```c 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; } } ``` 完整代码如下: ```c //振荡器配置 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模块 /@@*中央对齐模式的周期由 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的时刻!!!注意代码不要全抄!!! 示波器如图: ![](https://cf05.ickimg.com/bbsimages/201911/3a2cc657757fcfc7d71f78c9a3840836.png) 可以看到波形正确,之后我们试一下,触发时间寄存器改成3000,也就是比周期寄存器的4000小,然后改成每2个AD转换产生一次中断,代码就不重复贴了,就改一下那些相应寄存器即可,波形如图: ![](https://cf05.ickimg.com/bbsimages/201911/f1b5d7ba2161b22f454158784772f9ff.png) 可以看到由于我设置的触发时间为3000,周期为4000,所以偏转了一些,而且我设置的2次AD触发一次中断,因此触发数量还是相同的。 最后,依然要再次提醒,本节只是讲了PWM触发ADC的时刻,没有讲具体ADC的同时采样,因此上面关于ADC通道的配置,是直接给0,所以跟硬件管脚不对应的,关于ADC的配置,在之后的文章再详细解读。这部分代码不要全抄!!!
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
KVIN
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交