PIC单片机的PWM与ADC的相互调用

1.前言

  为提高对控制对象的控制精度的要求,闭环控制成为了一种普遍采用的方案。闭环控制是指:使用被控对象输出误差来修正控制对象的方法。

  其常见的原理框图如图所示:

  以常见的控制系统为例,其实现的过程一般为使用单片机ADC采样,进而修改单片机输出的PWM占空比,以实现对被控对象的控制。

  在其他单片机中这一过程,往往需要软件编程来进行实现,然而dsPIC单片机可以凭借其中断实现硬件闭环。减少了软件的编写量,而且使闭环的可靠性有较大的提升。

  其原理框图如:

 

  本文将首先对PWM、ADC模块进行介绍,阐述相互调用的原理,并给出示范代码。

  采用的单片机型号为dsPIC33FJ16GS502,编辑器为MPLAB X IDE V6.0,编译器为XC16 V2.00

2.PWM模块

2.1 总体介绍

  在使用附属时钟的情况下,此款单片机的PWM模块的时钟输入频率可达到117.92MHz具有4×2路PWM输出,支持真正独立输出、冗余输出、中心对齐模式和推挽输出等多种PWM模式,并且可触发中断、ADC采样。其PWM模块功能强大,详情可参考其用户手册。

  其时钟是经由附属时钟(ALCK)提供,详情也可看时钟简介的博客:

  

  本文使用的PWM模式:完全独立的PWM输出。故仅介绍此种模式;

  何为完全独立模式呢?

  就是两路PWM的输出的周期,占空比均相互独立,不相互影响。可见下图:
  

2.2 PWM原理介绍

  原理介绍网上有很多,可以参考:

  https://zhuanlan.zhihu.com/p/379585884

  其在单片机中的实现,可以参考

  https://blog.csdn.net/weixin_48283961/article/details/122105151

  死区相关知识,可以参考:

  https://www.jianshu.com/p/61b0fdac2805

  PWM触发ADC的原理也是较为容易理解的,见图:

 

  以上半边为例,当PTMRx与TRIGx中寄存器的值相等时,便会产生一个高电平触发,经过TRGSTRT延时(可设置),可TRGDIV分频(可设置),经过或门(可设置)后便会到达ADC触发其发生中断,对数据进行采样。

  具体电平变化,如图所示

2.2 寄存器设置

  搞懂PWM的原理后,便可以配置PIC的寄存器来时单片机输出PWM波形啦!

  真正独立的PWM输出模式需要配置的寄存器很少,其名称和功能如下:

名称 功能
PTCON 周期更新设置,关断设置
PTCON2 时钟分批
PWMCON1 PWM模块1功能设置
PHASE1 PWM1H周期寄存器
SPHASE1 PWM1L周期寄存器
PDC1 PWM1H占空比寄存器
ALTDTR1 PWM1L占空比寄存器

IOCON1

PWM1模块端口寄存器

TRIG1

PWM1H触发寄存器

STRIG1

PWM1L触发寄存器

TRGCON1

PWM1模块触发设置位

  

  具体各个寄存器的作用,以及如何配置见代码及代码注释:

2.3 PWM代码

 1 void PWM1_Init(void)
 2 {
 3     PTCONbits.EIPU = 0 ;            // 周期边界处更新周期
 4     PTCON2bits.PCLKDIV = 0b110 ;    // PWM Divide is 64
 5     
 6     PWMCON1bits.ITB = 1;            // 使用独立时基模式
 7     PWMCON1bits.MDCS = 0 ;          // 使用PDC与SDC寄存器提供占空比信息
 8     PWMCON1bits.DTC = 0 ;           // 施加正死区
 9     PWMCON1bits.IUE = 1 ;           // 立即更新占空比
10     // PWMCON1bits.MTBS 
11     // 周期 1ms
12     PHASE1 = 1843;
13     SPHASE1 = 1843;
14     // 占空比 MDC
15     PDC1 = 920;
16     SDC1 = 920;                     // 初始占空比50%
17     // 死区
18     DTR1 = (int)(0.05 * PDC1);
19     ALTDTR1 = (int)(0.05 * SDC1);   // 10% 死区
20     //引脚模式
21     IOCON1bits.PENH = 1 ;           // 使能PWM模块控制PWM1H引脚
22     IOCON1bits.PENL = 1 ;           // 使能PWM模块控制PWM1L引脚
23     IOCON1bits.POLH = 0 ;
24     IOCON1bits.POLL = 0 ;           // 高电平有效
25     IOCON1bits.PMOD = 0b11 ;        // 真正独立PWM模式
26     //触发设置
27     TRIG1 = 64 ;
28     STRIG1 = 64 ;                   // 上升沿64个PWM周期后触发ADC中断
29     TRGCON1bits.TRGSTRT = 0;        // 立即触发
30     TRGCON1bits.TRGDIV = 0;         // 每次触发产生中断
31     TRGCON1bits.DTM = 1 ;           // 或逻辑输出触发
32  
33     PTCONbits.PTEN = 1 ;            // enable PWM   
34 }
按照上述配置后,便会产生周期为1ms,占空比为50%,死区为5%占空比的PWM信号。

3 ADC配置

 3.1 简介

此款单片机的ADC模块为10位,对于一般的控制系统也是十分够用的。对于具有2个SAR(Successive Approximation Register)的模块,其采样速率达到的4Msps,并且其采样方案采用的为对采样,说人话就是一次对两个通道进行采样。对于电源控制方面来说,可以一次采样电压和电流,较为方便。

其时钟也是经由附属时钟(ALCK)提供,详情也可看时钟简介的博客:

 

3.2 原理简介

关于逐次逼近ADC的原理,网上有很多,故不再再次介绍。

不太明白的小伙伴可以去看一下这个视频:

【AD/DA数模转换介绍(09)逐次逼近型ADC的工作原理-郭天祥】 https://www.bilibili.com/video/BV1ar4y1v7MT?share_source=copy_web&vd_source=2fa930b5f77508fb08968cb0ac80ba34

3.2 寄存器介绍

搞明白ADC采样的原理后,就可以来配置寄存器啦。

ADC的寄存器也不是很多,详情见表格:

寄存器名 功能
ADCON ADC模块配置寄存器
ADCFG 端口配置寄存器
ADCPC0 转换配置寄存器
IPC27 中断优先级配置
IFS6 中断标志
IEC6 中断使能

 

具体各个寄存器的作用,以及如何配置见代码及代码注释:

 1 void ADC_Init(void)
 2 {
 3     // 输入时钟设置
 4     ADCONbits.SLOWCLK = 1;              //使用附属PLL(ALCK)作为时钟源
 5     ADCONbits.ADCS = 0b101;             // ADC转换时钟6分频
 6     // 模块配置
 7     ADCONbits.ADSIDL = 0 ;              // 空闲模式下继续工作
 8     ADCONbits.SLOWCLK = 1 ;             // Use ACLK as Clock Source
 9     ADCONbits.FORM = 0 ;                // 转换完成后数据格式为整数格式
10     ADCONbits.EIE = 1 ;                 // 提前中断模式,AN0采样完成后中断
11     // 转换设置
12     ADCONbits.ORDER = 0; 
13     ADCONbits.SEQSAMP = 0;
14     ADCONbits.ASYNCSAMP = 0;            //同步采样和并行转换
15     // AN0 AN1 端口配置
16     ADPCFGbits.PCFG0 = 0;               //配置AN0 为模拟输入引脚
17     ADPCFGbits.PCFG1 = 0;               //配置AN1 为模拟输入引脚
18     // ADC 转换对控制寄存器
19     ADCPC0bits.TRGSRC0 = 0b100;         // PWM发生器1主触发0b100
20     ADCPC0bits.IRQEN0 = 1;              //转换完成后产生中断
21     // ADC中断配置
22     IPC27bits.ADCP0IP = 0x01;           // ADC转换优先级为 1 
23     IFS6bits.ADCP0IF = 0;               // 清除中断标志位
24     IEC6bits.ADCP0IE = 1;               // 使能中断
25     //开启中断转换
26     ADCONbits.ADON = 1;                 // 开启ADC转换  
27 }

比较重要的是第19行,将ADC转换触发设置为由PWM发生器1触发。

ADC初始化完成后,要读取ADC的转换值,需要在ADC的中断函数中,因此也可以在ADC中断服务函数中,来进行PWM的占空比调节;

ADC的中断服务函数如下:

 1 void __attribute__((interrupt , no_auto_psv)) _ADCP0Interrupt (void)
 2 {
 3     PDC1 = ADCBUF0;
 4     SDC1 = ADCBUF1;
 5     // 死区
 6     DTR1 = (int)(0.05 * PDC1);
 7     ALTDTR1 = (int)(0.05 * SDC1); // 5% 死区
 8     // clear ADC Pair 0 Interrupt flag
 9     IFS6bits.ADCP0IF = 0 ;
10 }

至此ADC的配置就算完成了。

4 主函数

在各模块都已经完成的情况下,主函数的编写就十分的简单了,直接见代码:

int main()
{ 
    System_Colck();//时钟设置
    ALCK_Init(7); //附属时钟初始化
    ADC_Init(); // ADC初始化
    PWM1_Init(); // PWM 时钟初始化
    while(1)
    {}
}

至此便完成了--->””通过PWM触发ADC中断修改PWM参数的硬件闭环调用”“<--- ,在外电路合适的情况下,改变ADC引脚的电压值便可实现对PWM的占空比进行改变,具体表现为LED的亮度啦。

 

 

  

 

 
posted @ 2023-03-27 19:27  巨巨巨兔  阅读(1188)  评论(0)    收藏  举报