我们以进阶(1)中,用MCC生成的文件为基础,先编写PFC电流内环的算法代码。在进阶(1)中,已经提到了,电流内环的采样频率为100KHz,电压外环的采样频率为50KHz。而且电流内环是PWM2发生器触发,即TRIG2为触发的寄存器。而电压外环为主时基触发,是用SEVTCMP做为触发寄存器。由于是平均电流型控制,要求采集电感电流的平均值,所以要求TRIG2跟随占空比的变化而变化,电压环路没有这个要求,就在一个固定的时间触发即可。
除了MCC生成的文件,还要添加5个需要自己编写的文件,如图:
1.defines.h文件把所有需要定义的常量在该头文件中定义。
2.pfc_variables.h和pfc_variables.c,其中.c文件用来定义用到的全局变量或函数,另外PID算法用到的两个数组以及数组的初始化也在该文件中完成。.h文件是将这些全局变量做extern声明。
3.initCONTEXT.S文件是初始化context的汇编代码。
4.PID.s是PID的算法函数,其中的代码就是PID实现的过程。
1)context的初始化:
相比microchip的8位MCU只有一个工作寄存器,dspic16位MCU由16个工作寄存器阵列(w0-w15)组成一组,而且有多组,比如:dspic33ep128gs806就有4组这样的工作寄存器组。在调用函数或进入中断时,工作寄存器就不需要压栈,而是直接切换到另外一组工作寄存器组,函数返回或出中断时也不用再将寄存器弹出栈,只需要把原来的工作寄存器组再切换回来。
官方编写的smps_control_library库函数中的硬件加速函数就是应用了上述的工作寄存器组切换的特点,减少了调用函数的时间。在用这种方法之前,首先要用汇编代码初始化一组工作寄存器。代码在initCONTEXT.S文件中编写:
#include "defines.h"
.text
.global _initCTXT1
_initCTXT1:
CTXTSWP #1
mov #_pfcCurrentRef, w0
mov #_pfcCurrentSample, w1
mov #_pfcCurrPIDout, w2
mov #currentLoop_postScalar, w6
mov #currentLoop_postShift, w7
mov #_CurrentPID_Coeff, w9
mov #_CurrentLoopHistory, w10
mov #CurrPIDoutMin, w11
mov #CurrPIDoutMax, w12
mov #preShift, w13
CTXTSWP #0
return
.end
文件的开头包含预定义常量的.h文件。.text表示代码开始。然后声明一个全局函数_initCTXT1,接着用“CTXTSWP #1”切换到工作寄存器1组,随后就是工作寄存器的定义,定义完成后,还要切换回工作寄存器0组,就是dsp上电后默认的工作寄存器组。最后函数返回。以.end结束。
2)在dsp上电时初始化工作寄存器1组
笔者将context初始化的工作放在系统初始化函数中,如图:
在system.c文件中,先声明initCTXT1函数,然后就可以调用该函数了。注意在C文件中没有下划线。
3)PID参数的初始化
PID的参数Ka、Kb和Kc是在defines.h中定义,PID参数的初始化就是定义的两个数组的初始化。电流环路中的这两个数组是在pfc_variables.c中定义的。这两个数组是:
CurrentPID_Coeff[3]和CurrentLoopHistory[3]
将计算好的Ka、Kb和Kc参数放入CurrentPID_Coeff[3]中,而CurrentLoopHistory[3]元素要全部清零。
关于参数Ka、Kb和Kc的计算,笔者参考了microchip官方的一篇文章《使用dsPIC® DSC 实现能量转换应用中的功率因数校正(DS01106A_CN)》中第13页的公式,最终得到的差分方程是:
根据前面文章中介绍的方法:
Ka=26772,Kb=-25460,Kc=0,postScalar=32767,postShift=0。
4)加入PID.s文件
就是上一篇文章中修改后的smps_pid_dspic_v2.s文件。
5)ADC采样中断并计算PID差分方程
在PWM2触发ADC模块的AN0通道(mos管电流采样)中断时,进行电流环路PID计算。所以在_ADCAN0Interrupt中断时加入代码。PID的函数不是放在回调函数中,而直接放到中断函数里,这样能减少延时时间。另外,在中断函数属性中使用"no_auto_psv",还能再节省100ns的时间。
从代码中可以看到,进中断后,将AN0通道的采样值读出(如果在中断的过程中不去读ADCBUF0,就会不停的再次进入该中断,影响其它代码的执行。),然后用汇编指令手动切换到工作寄存器1组,执行PID算法。计算结果保存在pfcCurrPIDout变量中。然后更新PDC2寄存器。PDC2中的值除2就是TRIG2的值,使下一次ADC采样在占空比一半时触发。
到此,电流内环的PID算法的流程已经完成。
另外要提一下的是,最好不要使用ADC中断等级绑定的自动context切换。因为自动切换context后,要十分小心,先要在中断函数属性中要添加context属性。而且最好在一进入中断后立即调用PID算法,不要有任何C语句,否则用C编译器编译后,可能会覆盖工作寄存器1组的w0等工作寄存器,导致PID出现未知的错误。所以为了避免出现此情况,还是手动切换更为保险。
如果必需用到context自动切换,建议直接用汇编编写ADC中断代码,不要再用C编写。官方给出的参考代码中的关于ADC的电流采样中断就是用汇编语言编写的。
关于context切换的更详细内容,请参考smps_control_library中的doc目录中的帮助文件help_smps_control.pdf中的第21页。