星期四, 三月 12, 2009

《电气传动系统软件设计》之三

继续上传《电气传动系统软件设计》的内容.
4.2.3 C语言/ASM语言混合编程

流行于嵌入式开发中的2种语言, ASM和C语言, 两者并非竞争或排它的关系. 混合编程的方法——即把两种语言进行代码上的结合,或者是进行编程思想上的移植, 可以更好的适应嵌入式开发中对于硬件操作,实时性和模块化的要求. 下面以TI公司的DSP硬件平台为例介绍几种混合编程方法.

4.2.3.1 带行内汇编的C语言(C with Inline Assembly)
行内汇编技术可以弥补C语言的直接操作硬件能力不强的弱点. 其语法形式为:
asm("ASMCODE");
其中, asm关键字、括号、双引号和分号都是C语言中的标准关键字或语法符号, ASMCODE是要写的汇编语句, 应当注意的是在行内汇编中同样有"除标号、注释之外都不能顶格写"的语法规则. 在CCS的汇编和C语言混合浏览(Mixed Mode)模式下, 如图4‑3所示, 行内汇编形式的C代码与其被编译后的ASM代码是完全对应的.
行内汇编短小精悍, 在对硬件进行不多的操作时, 它是C语言编程的有力补充.

图4‑3 行内汇编C语言的编译结果

4.2.3.2 宏内函数实现模块化汇编工程
相对于ASM语言, C语言以模块化性能见长. 借鉴C编程的模块化思想, 使用ASM中的宏和函数的技术, 也可实施具有良好模块化性能汇编工程.
一般情况下, 汇编中的宏可用来实现规模较小的功能块, 而规模较大的功能块则由函数来实现. 结合宏和函数的“宏内函数”可以构成接口明确的汇编功能模块. 每一个宏内函数功能模块可由两部分构成:
1) 模块实现
包括模块函数定义、变量定义、变量段分配和基本功能代码等, 在以模块命名的.asm文件中实现.
2) 模块接口
包括模块函数引用、变量引用和接口宏等, 其中接口宏完成宏参数到模块参数的传递以及对模块的调用, 这一部分在以模块命名的.h文件中实现. 在需要调用模块的主程序中只需要包含(使用.include伪指令)该模块的头文件, 就可以直接引用模块宏实现模块调用了.
图4‑4以一个PARK变换为例描述了定义和调用宏内函数的全过程. 类似的, 还可以定义CLARK、PID等一系列功能模块, 在主程序main.c中只要包含了对应模块的头文件, 就可以和在C语言中调用带参数的函数一样调用这些带参数的宏内函数了. 使用宏内函数方法可以在ASM语言的环境下很好的实现模块化(模块的调试和修改不依赖其它程序可独立完成), 同时也具有很好的可移植性(调试好的模块可以直接在其它工程中使用).

图4‑4 宏内函数实现模块化汇编工程

4.2.3.3 可被C调用的汇编(CcA, C Callable Assembly)
TI公司推出的针对数字电机的模块库(Digital Motor Control, DMC Library), 这只是它的全部数字控制算法标准体系中的一部分, 而后者也只是整个eXpressDSP体系中的一部分[1]. DMC模块库提供了各种DMC应用开发中可能用到的算法模块, 如CLARK, PARK等坐标变换模块, PID, FIR(Finite Impulse Filter, 有限冲击滤波器), SVPWM(Space Vector Pulse Width Modulation, 空间矢量脉宽调制)等算法模块, 甚至还包含MRAS(Model Reference Adaptive System, 模型参考自适应系统)模块等.
DMC模块库所提供的模块不仅提供纯汇编的实现, 而且还提供CcA(C callable Assembly)实现, 后者可以帮助DMC开发工程师在C语言系统程序框架下无缝的调用汇编语言实现的模块, 实现高效的嵌入式系统开发.
CcA通过使用系统堆栈和辅助寄存器间接寻址的技术实现了C语言主程序框架和ASM功能模块直接的参数接口, 既实现了整体系统的模块化设计, 又保证了算法模块的高效执行[12].
CcA的实现主要有以下几个工作:
1) 函数定义(用.def伪指令)、引用变量声明等(用.ref,.global伪指令);
2) 保护和恢复现场
CcA模块的首、尾部分分别完成保护和恢复现场的工作, 现场的状态量包括程序返回地址,状态寄存器ST0/ST1等;
3) 由ASM语言实现模块
CcA模块的中间部分是由ASM语言编写模块代码. 对变量的操作都是通过间接寻址访问系统堆栈来实现的, 其中借助辅助寄存器加减移位操作可大大提高单条指令的执行效率;
4) CcA模块的调用
在C语言框架的主程序中可以调用CcA模块函数, 函数的参数可以是一般变量或结构体型指针变量.
下面同样以PARK变换为例给出CcA模块定义和调用的代码.
; park.asm
; CcA(可被C调用的汇编模块)
;/////////////////////////////////////////////////////////////////////////////////////////////////////
.def _park ; 定义函数
; .global _globalx ; 引用全局变量(这里不需要全局变量所以注释掉)
; .ref _x ; 引用变量(这里不需要全局变量所以注释掉)
.text
_park:
;//////////////////保护现场
popd *+ ; 返回地址压栈保存
sar ar0,*+ ; 保存旧的帧指针(ar0)
sar ar1,* ; 保存旧的堆栈指针(ar1)
lar ar0,*+,ar1 ; 分配新的帧指针(ar0)
sst #1,*+ ; 保存系统状态寄存器1(ST1)
sst #0,*+ ; 保存系统状态寄存器0(ST0)

;//////////////////模块主程序
; park变换的主程序省略.
; 其中使用辅助寄存器实现间接寻址, 可提高程序执行效率

;//////////////////恢复现场(和保护现场逆序)
mar *,ar1 ; 准备操作堆栈指针(ar1)
mar *- ; 堆栈指针指向保护现场的最后一个数据
lst #0,*- ; 恢复系统状态寄存器0(ST0)
lst #1,*- ; 恢复系统状态寄存器1(ST1)
lar ar0,*- ; 恢复旧的帧指针(ar0)
pshd * ; 弹出返回地址
ret

4.2.3.4 汇编访问C结构体型全局变量
用C语言定义所有系统变量为结构体型变量, 在一定程度上能够保留系统的模块化性能. 如果汇编能够访问C定义的结构体型全局变量, 就可以实现C系统框架和ASM实现模块之间的接口.
实现汇编访问C定义的结构体型全局变量有如下几项工作:
1) 首先要进行变量和函数在四个文件中的定义和引用.
2) 其次要构造结构体变量成员的偏移地址.
在汇编模块的头文件中构造一套与C结构体变量成员对应的偏移地址标号, 用以实现在ASM中访问C定义结构体变量的任意成员. 同样以PARK变换为例, 汇编访问C结构体型全局变量具体实现如图4‑5所示.

图4‑5 汇编访问结构体型全局变量

对于16位的int型变量, 如果控制器的程序总线为8位, 则上面park. inc文件中的偏移地址递增量应为2, 如Microchip dsPIC30F6010芯片. park. asm中“_park+beta”对应C语言中的变量“park. beta”. 另外, 结构体数组成员也可以写成类似的汇编直接寻址形式, 如汇编中的“_sys+AD+5”对应C语言的变量“sys. AD[5]”.
采用“汇编访问结构体型全局变量”的方法, 实现了从ASM到C类型资源的主动访问, 为ASM/C混合编程构建了完整的双向的操作模式, 可以更为有效的规避大部分参数传递所对应的执行开销, 提高系统的执行效率

没有评论:

发表评论