在数字电源的研发过程中,纯数字控制的闭环算法是个难点。要想达到预期的性能指标,一般要求控制环中的算法满足下面的一些条件:
1.从被控量的采样到完成占空比的计算和更新,这个过程的时间要尽可能的短。越少的时间越能提高控制环路的相位裕度,增加环路的稳定性。针对这个问题,microchip的dsp系列单片机有专用的硬件来解决。以dsp33ep128gs806举例:
首先,为缩短ADC采样的时间,该芯片内部有4个专用内核用来对被控量进行采样,以最高速运行的dsp,adc的转换时间能控制在200ns以内。在上篇文章中,用来采样mos电流和输入输出电压的ADC(AN0、AN1、AN2、AN3)都是专用内核。
其次,为减少进入ADC中断时,寄存器压栈和出栈的时间,引入context转换的功能。笔者实测能比普通方式进入ADC中断少300ns左右。
最后,为了快速解算环路中差分方程的解,引入带饱和功能的40位累加器,在一个指令周期能完成累加,相乘,两个参数的读取,累加器回写等指令。配合专用的累加器汇编指令,能加快运算速度。以笔者用的PID算法,在60MHz的工作频率下,能在1.6us的时间内完成差分方程的求解和占空比的更新。
2.环路中的差分方程的参数有很多是用小数表示的。为了满足控制的精度,并且还能有很快的运算速度,microchip的dsp33ep系列的dsp单片机没有用浮点数表示小数,而是用定点数来表示。具体到累加器中的小数表示,就是用Q15的格式。
从上面提到的这些条件来看,笔者觉得,先将累加器是如何工作,如何进行小数乘法的计算这两方面弄清楚是很有必要的。
dsp如何表示小数?dsp小数数据表示为二进制补码数,其中最高位定义为符号位,小数点隐含于符号位之后。这种格式通常被 称为1.15(或Q15)格式。其中1是用来表示数据的整数部分的位数,而15是用来表示小数部分的位数。1.15格式的dsp小数数据表示的范围是:-1.0(0x8000)至0.999969482(0x7FFF)。可见,要把纸面中的小于1的小数转换成dsp能识别的小数数据,应该用公式:,比如:
而32767的十六进制数为0x7FFF
那么0.1953125该如何表示?
6400的十六进制数为0x1900。
下面举例说明累加器如何计算小数乘法:
- 纯小数×整数:
0.1953125*2762=??
0.1953125--0.1953125*2^15=6400--0x1900
2762--0x0ACA
下图是累加器ACCA计算乘法的过程:
将0x1900赋值给w4寄存器,0x0ACA赋值给w5寄存器。mac指令是用累加器A做w4×w5的运算,并将累加器的ACCAH部分保存在w6寄存器中,下图是40位累加器寄存器的结构图:
ACCA寄存器的结果是0x00021B7400,只保存0x021B到w6中,0x021B的十进制为539。
而,所以w6的结果就是w4×w5的最终结果。其实这个计算过程手动推导一下是这样的:
而0x00010DBA00除就是右移15位,也就是0x021B,即为539。
那这个0x00010DBA00是怎么回事??其实累加器有两种工作模式:小数模式和整数模式。当处于小数模式时,会把结果自动左移一位,而整数模式工作时,则不会进行位移。所以读者可以尝试将0x00010DBA00左移一位就是0x00021B7400,因此要把0x00021B7400右移16位才是乘法运算正确的结果。而w6寄存器中的数据0x021B正是0x00021B7400右移16位的结果。
那为何小数模式会把结果自动左移一位?小数都是用1.15格式,1.15×1.15=2.30,即小数乘法的结果是2.30格式,但是累加器的小数点固定在31位和30位之间,为了对齐结果,就变成了左移一位,成为1.31格式。
从上面的分析中,可以看到,对于累加器A来说,根本就没有做小数的乘法,都是整数的乘法,小数点只存在于程序员的心里。
- 纯小数乘纯小数:
0.1953125*0.08428955078125=??
0.01953125--0.01953125*2^15=6400--0x1900
0.08428955078125--0.08428955078125*2^15=2762--0x0ACA
0.1953125*0.08428955078125=0.01646280288.......
考虑到小数取整时的误差,可以看出,累加器的计算结果在一定精度上是对的!!
- 整数乘整数:(用累加器的整数模式)
6400*2762=17676800(十六进制0x00010DBA00)
这时的正确结果是保存在ACCxH和ACCxL两个寄存器中,无法把结果保存在一个16位的寄存器中。
- 大于1的小数×整数:
以上都是对于纯小数或纯整数的乘法做的计算。那么对于大于1的小数该如何表示?大于1的小数无法表示成1.15的格式!
还是那句话,小数点只存于使用者的心里,累加器只会做整数的乘法!
举例:
3.348*2762=9247.176
3.348是大于1的小数,且小于4(2^2),所以换算成dsp可以识别的小数就应该左移(15-2)位,即13位。
2762--0x0ACA
w6寄存器保存正确的结果0x241F--十进制为9247。
手动推导计算过程:
在计算完w4×w5的结果后,还要用sftac指令将累加器结果寄存器ACCA左移2位,最终保存在w6寄存器才是正确的结果。
在microchip官方提供的PID算法库函数中,对于大于1的小数就是用这种方法来计算的,这在后面还会提到。
通过前面的几个例子,可以看到dsp能够处理任意小数的乘法运算。但是要注意结果饱和的问题。比如:
200.863*742=149040.346 > 32767,
这时再用上面的步骤,得到结果后,再左移位,将会引起累加器的饱和,这种情况就只能用整数模式,并且自己计算小数的位移,保存计算结果。
200.863<256(2^8)所以左移7位。即
742--十六进制0x02E6
可以看到累加器最终的计算结果为0x00012316D4,这时就不能再左移8位了,否则会导致累加器ACCA饱和
最终结果与正确的结果有一定的误差,这是由于取整误差造成的。
总结:
dsp的小数乘法,本质上就是整数乘法,当对于累加器的工作方式了解以后,有助于程序员选择合适的计算模式。
通过上面的方法,dsp对所有整数和小数的乘法运算都可以很好的支持,明白这些之后,才能熟练应用smps_control_library函数库,明白如何设定函数的参数。