单片机模块化多输出值C函数的编程实现

2016-06-24 02:38李加明
单片机与嵌入式系统应用 2016年6期
关键词:模块化

李加明

(南京航空航天大学 2015级工科研究试验班,南京 211106)

李加明

(南京航空航天大学 2015级工科研究试验班,南京 211106)

摘要:单片机C标准编程不能实现函数的多个输出值,目前常用的全局变量法虽可变通实现,但破坏了函数的模块化原则。本文突破一些常规思维,探索出两种类型的多种模块化多输出值C函数的编程方法,并提出了新式的C函数语法规则,实现了较为规范通用的模块化多输出值C函数的编程。本方法编程思路新颖,具有较高的技术实用价值。

关键词:C函数;多输出值;模块化;MCU

引言

函数是单片机与嵌入式系统C语言程序的核心,标准C函数定义遵循y=f(x1,x2,…,xn)的数学形式,其中x1,x2,…,xn为代表函数各输入值的形式参数,y为函数唯一的输出值。函数调用以输入值实际参数代替形式参数,经运算后由return语句返回一个输出值。

但在实际应用中,往往希望函数能有多个输出值,例如用于工业控制的西门子S7-300/400可编程控制器程序中,其具有函数功能的FC、FB、SFC、SFB块均可直接实现多输出值。相比之下,标准C函数不能直接实现多输出值,其功能存在明显的不足。

当需要C函数有多输出值时,可将一个函数分为几个函数实现,但从算法和效率考虑显然极不合理。目前通常采用全局变量法变通实现:由return语句正常返回一个输出值,其余输出值均由另设的全局变量获得,但函数的模块化原则遭到了极大破坏。

实现模块化原则的任意多输出值的C函数有很大的技术需求价值,值得探索。笔者在C语言编程学习中,摸索并试验成功了两大类多种模块化多输出值C函数的编程方法。各例程分别在软件Keil C51V9.00与8位机STC90C516RD+、软件CCS5.2.1.00018与16位机MSP430F6638、软件Keil MDK5.14.0.0与32位机STM32F103RBT6平台编译下载并运行通过。其中,16位机MSP430F6638和32位机STM32F103RBT6芯片均具备直接的硬件在线运行调试功能,文中选取了对应的CCS5.2.1.00018和Keil MDK5.14.0.0软件观察硬件运行结果画面。

51核8位机STC90C516RD+芯片自身不具备硬件在线运行调试功能,故在Keil C51V9.00软件仿真运行成功基础上添加代码,通过其串行口向PC机发送硬件运行结果,由PC机侧串行口软件观察硬件运行结果。对于大多数不具备硬件在线运行调试功能的8位单片机芯片,这样的硬件运行调试方法是非常实用且必要的。文中给出了添加串行口功能的程序和PC机侧串行口软件XCOM观察的硬件运行结果画面。

1标准C函数的定义与调用

标准C函数返回一个输出值的程序如下:

int func(int a,int b){/*函数定义*/

int y; y=a+b; return(y); }/*返回一个输出值*/

int main(void){

int n1,n2;/*输入*/

int sum;/*输出*/

while(1){

n1=-132;n2=-12; sum=func(n1,n2);}

}/*函数调用*/

此例是标准C函数的定义与调用:函数形式参数表仅包括各输入量,唯一输出量由函数体return语句返回。函数采取“输出量=函数名(输入量表)”的格式定义与调用。

全局变量法实现标准C函数的多个输出值如下:

int sum; /*全局变量sum:函数的另一输出值*/

int func(int a,int b){/*函数定义*/

int subs;

subs=a-b;/*一个输出值为局部变量subs*/

sum=a+b;/*另一输出值为全局变量sum*/

return(subs);

}/*正常返回一个输出值subs*/

int main(void){

int n1, n2;/*输入*/

int sub;/*输出*/

while(1){

n1=-132;n2=-12; sub=func(n1,n2);}

}/*由函数调用获得一个输出值sub,另一输出值由全局变量sum获得*/

此例为目前实现标准C函数多输出值常用的全局变量法:函数func由return返回一个输出值sub,另一输出值sum也需由函数func计算,但函数只能返回一个输出值,故只得将sum定义为在函数func与调用func的main程序中均有效的全局变量,变通实现函数func的两个输出值。

全局变量法可变通实现函数的多输出值,因而应用广泛。但此方法的函数体内包括了作为输出量的全局变量,破坏了函数“模块自身内聚,模块间不耦合”的模块化原则,致使函数无法独立、难以移植共用,且极大降低了编程的可靠性与清晰性。

2维持标准C函数语法规则的改进编程

标准C函数只能由return直接返回一个输出值,可对这个输出值进行思路拓展:C语言有一种构造数据类型“结构”,能包含多个不同类型的数据成员,但对外却可以作为一个数据单位出现。若运用return直接返回一个结构变量,也符合标准C函数的语法规则。函数定义时可将所需的多个输出量汇集到一个专用结构变量中,调用后逐个拆分读取返回的结构变量的数据成员,则可间接获得函数的多个输出值。

如下面的例程中,函数定义将各输出量汇集定义到结构中,仍采用标准C函数的格式调用:OUT=calc(n1,n2),多个输出值可由结构OUT的成员OUT.sum、OUT.sub、OUT.mul、OUT.div间接获得。CCS5.21.00018与Keil MDK5.14.0.0观察的硬件在线调试运行结果略——编者注。

typedef struct{

int sum; int sub;

unsigned int mul; unsigned char div;

}result;

/*定义专用结构result,汇集4个输出量*/

result calc(int a,int b){/*函数定义*/

result c;

c.sum=a+b; c.sub=a-b; c.mul=a*b; c.div=a/b;

return(c); }/*返回一个结构变量*/

int main(void){

int n1,n2;/*输入*/

volatile result OUT;

/*输出,默认条件,CCS和KeilMDK编译时此处需添加volatile,而KeilC51编译时此处无需volatile,详细见后文叙述*/

while(1){

n1=-132;n2=-12;

OUT=calc(n1,n2);}/*函数调用*/

}

对于51核8位机STC90C516RD+,由于芯片自身不支持硬件在线运行调试功能,故在Keil C51V9.00软件仿真运行成功基础上添加代码,通过其串行口及printf()函数向PC机发送硬件运行结果,程序略——编者注。

由PC机侧串口软件XCOM观察的硬件运行结果略——编者注。

本方法是维持标准C函数语法规则的改进方法,通过结构间接实现函数的多输出值,结果正确、实用。标准C函数return不能直接返回多个输出值,所以函数定义时需另行定义一个专用结构变量并将期望的多个输出量以成员方式汇集定义到其中,函数调用后还需对返回的唯一结构变量进行成员拆分读取才可获得期望的多输出值,编程与数据操作均显复杂繁琐。

3重新设计新式C函数语法规则的编程

标准C函数的形式参数表仅包括输入量,输出量只能依靠return返回,这正是其不能直接实现多输出值的根源。为了从根本上解决问题,突破常规思维,放弃标准C函数的语法规则,重新设计可直接实现多输出值的新式的函数定义与调用语法规则:①函数体中完全取消return语句,再不用return返回输出值;②将函数期望的多个输出量与输入量共同纳入形式参数表。

新式C函数的定义与调用形如:f(x1,x2,…,xn,y1,y2,…,yn),即“函数名(输入量表,输出量表)”格式,函数体内完全取消return语句。采用新式函数语法规则,没有原标准语法规则的不足限制,可灵活自由地实现多种通用规范的结构化多输出值C函数的编程方法。

3.1数组法

下面的例程中,函数的多个输出量用数组result[]定义,函数以calc(n1,n2,res)格式调用,输出的res数组的元素res[0]、res[1]、res[2]、res[3]即为函数的多个输出值。CCS5.21.00018与Keil MDK5.14.0.0观察的硬件在线调试运行结果略——编者注。

void calc(int a,int b,int result[]){

/*新语法规则函数定义:输入输出量均纳入形式参数表,取消return语句*/

result[0]=a+b; result[1]=a-b;

result[2]=a*b; result[3]=a/b;}

int main(void){

int n1,n2; /*输入*/

int res[4]; /*输出*/

while(1)

{ n1=-132;n2=-12; calc(n1,n2,res);}

} /*新语法规则函数调用*/

对于51核8位机STC90C516RD+,由于芯片自身不支持硬件在线运行调试功能,故在Keil C51V9.00软件仿真运行成功基础上添加代码,通过其串行口及printf()函数向PC机发送硬件运行结果,程序略——编者注。

由PC机侧串口软件XCOM观察的硬件运行结果略——编者注。

函数多个输出值的数据类型相同时,只需定义一个数组;如多个输出值的数据类型不相同,只需依据其数据类型归类定义多个数组即可,数组的个数不受限制。

3.2“宏函数”法

本方法以带参数的宏来定义类似函数功能的“宏函数”,宏的参数表包括全部输入与输出量。例程中,宏以CALC(n1,n2,sum,sub,mul,div)格式调用后即可直接获得所需的多个输出值sum、sub、mul、div。CCS5.21.00018与Keil MDK5.14.0.0观察的硬件在线调试运行结果略——编者注。

#define CALC(a,b,summ,subs,mult,divi){

(summ)=(a)+(b); (subs)=(a)-(b);

(mult)=(a)*(b); (divi)=(a)/(b);}

/*新语法规则“宏函数”定义:输入输出量均纳入形式参数表,取消return语句*/

int main(void){

int n1,n2;/*输入*/

volatile int sum, sub, mul;

volatile unsigned char div;/*输出,默认条件,CCS和KeilMDK编译时此处需添加volatile,而Keil C51编译时此处无需volatile,详细见后文叙述*/

while(1){

n1=-132; n2=-11;

CALC(n1,n2,sum,sub,mul,div);}

} /*新语法规则“宏函数”调用*/

对于51核8位机STC90C516RD+,由于芯片自身不支持硬件在线运行调试功能,故在Keil C51V9.00软件仿真运行成功基础上添加代码,通过其串行口及printf()函数向PC机发送硬件运行结果,程序略——编者注。

由PC机侧串口软件XCOM观察的硬件运行结果略——编者注。

用宏代替函数有如下优点:①函数的形式参数类型在定义时即已固定,实际参数类型必须由编程定义与之保持一致,不能自动匹配,编程不便;而宏的参数类型完全由调用时的实际参数确定,数据类型自然自动匹配,编程简便。②函数调用要付出为形式参数分配临时单元及现场保护恢复等额外代码的时间开销;而宏调用的实质是在编译阶段就已完成的代码中展开,运行时完全不存在函数上述的额外时间开销,故运算速度明显快于函数。

宏的不足是:宏多次调用时,每次均会展开一套大部分相互重复的代码,最终的程序代码比函数调用方式的大。但目前单片机的程序存储器足够大,速度更为重要,所以,“宏函数”在运算速度及参数类型自动匹配方面具备独特应用价值。

3.3指针法

研究C语言的指针,总结并成功验证本方法:函数定义时,各输出量形式参数均冠以符号*,以指针方式定义;函数调用时,各输出量实际参数前均冠以地址符&。例程中,函数各输出量形式参数为summ、subs、mult、divi,故定义格式为:

void calc(int a,int b,int *summ,int *subs,unsigned int *mult,unsigned char *divi)

函数各输出量实际参数为sum、sub、mul、div,故调用格式为:

calc(n1,n2,&sum,&sub,&mul,&div)

CCS5.21.00018与Keil MDK5.14.0.0观察的硬件在线调试运行结果略——编者注。

void calc(int a,int b,int *summ,int *subs,unsigned int *mult,unsigned char *divi){

*summ=a+b; *subs=a-b; *mult=a*b; *divi=a/b;}

/*新语法规则:输入输出量均纳入形式参数表,取消return语句。函数各输出量形式参数均以其指针定义;函数体内采用对指针形式参数指向的值赋值的方式实现对各输出量的赋值*/

int main(void) {

int n1,n2;/*输入*/

int sum,sub;

unsigned int mul;

unsigned char div;/*输出*/

while(1){

n1=-132; n2=-12;

calc(n1,n2,&sum,&sub,&mul,&div);}

}/*函数调用:各输出值的实际参数sum,sub,mul,div前均冠以地址符& */

对于51核8位机STC90C516RD+,由于芯片自身不支持硬件在线运行调试功能,故在Keil C51V9.00软件仿真运行成功基础上添加代码,通过其串行口及printf()函数向PC机发送硬件运行结果,程序略——编者注。

由PC机侧串口软件XCOM观察的硬件运行结果略——编者注。

本方法概括为,当函数输入为x1,x2,…,xn、输出为y1,y2,…,yn、函数名为f 时,函数定义格式为:

f(x1,x2…xn,*y1,*y2…*yn)

调用格式为:

f(x1,x2…xn,&y1,&y2…&yn)

与数组法比较,不同数据类型的多个输出量不需归类定义;与“宏函数”法比较,是完全规范的函数定义与调用。相比前述的模块化多输出值C函数的编程方法,在函数定义与调用方面,本方法的编程风格最为规范化。

4两类结构化多输出值函数编程的比较

4.1维持标准C函数语法规则的改进编程

此方法的函数参数表仍仅包括输入量,仍保留函数体return返回输出量,故受到标准C函数语法不足的限制,编程与数据操作均显复杂繁琐,不是函数多输出值编程的最好方法。

4.2重新设计新式C函数语法规则的编程

此类方法打破标准C函数语法的思路限制,取消了函数体return语句,将输入与输出参数全部纳入参数表,依此方法重新设计的新式函数语法规则回避了原标准C函数的固有不足。

虽然无法推翻C语言的基本语法,但源自于C语言基本语法的标准C函数的语法规则并不是不可取代的,此类方法放弃了标准C函数的语法规则,可灵活自由地实现通用规范的模块化多输出值的C函数编程,是解决函数多输出值编程困扰的最好方法。

5输出变量定义增加volatile的试验及体会

结构法和宏函数法列举的16位及32位机程序,试验中发现输出变量需要添加volatile进行定义。在8位机Keil C51V9.00软件编译时,不添加volatile完全没有任何问题,但在16位机CCS5.2.1.00018及32位机Keil MDK5.14.0.0软件编译时,如果不添加volatile,编译时会产生报警,输出变量会被意外地优化省略掉。

各类单片机C软件平台对程序编译时均会对代码进行自认为最合理的优化,但有时会优化过度,导致结果意外错误。前例程序中遇到的报警就是优化过度,经试验,对会被意外优化省略掉的输出变量增加volatile定义,问题即可解决。

volatile的功能简单地说,就是防止编译器对代码进行优化。在作为变量定义的修饰关键字时其功能为:①确保本变量不会被编译器优化而省略;②确保变量存储在内存,在生成的代码中,每次的变量访问均要重新从内存直接读值,而不是去访问寄存器里的变量备份。使用volatile能彻底回避代码过度优化导致的意外错误,确保编译结果的正确性。

同样默认设置对相同C程序编译,16位与32位机编译系统比8位机编译系统的编译优化力度要强得多,更易出现过度优化问题。观察MSP430、STM32单片机的各类例程,变量添加volatile定义的现象确实比51核单片机的多,也佐证了这个印象。试验还发现,变量添加volatile定义会微量增加存储空间占用,虽然目前单片机存储空间一般都足够且对于存储空间更多的16位与32位机影响更小,但一般还是希望volatile定义的变量越少越好。当编程难以判断变量是否需要volatile定义时,可以设想先对这些变量均添加volatile定义,再逐步取消各自的volatile进行调试验证,直至在确保结果正确前提下留下最少的volatile定义的变量为止。

结语

本文探索的编程方法具备良好通用的技术实用价值,其中,新式函数语法规则的编程较为规范,是模块化多输出值C函数编程的较好方法。

参考文献

[1] 刘同法.单片机C语言编程基础与实践[M].北京:北京航空航天大学出版社,2009.

[2] 何钦铭.C语言程序设计[M].3版.北京:科学出版社,2010.

[3] 廖常初.S7-300/400 PLC应用技术[M].3版.北京:机械工业出版社,2011.

[4] 西门子.西门子S7-300/400编程手册[EB/OL].[2016-01].http://www.gongkong.com/download/200709/63764.html.

李加明(本科),机电与控制方向。

(责任编辑:薛士然收修改稿日期:2016-01-05)

Li Jiaming

(2015 Engineering Research Experimental Class,Nanjing Aeronautics Astronautics University,Nanjing 211106,China)

Abstract:MCU C standard programming can not achieve multiple output value function.Although it can be solved by the global variate at present,but the function modular principle is destroyed.In the paper,two kinds of modular multiple output C function programming method are explored,and a new type of C function syntax rules is proposed.Then the more standardized and versatile programming for modular multiple output C function is achieved.The method has novel programming idea and excellent practical technic value.

Key words:C function;multiple output value;modularity;MCU

中图分类号:TP368.1

文献标识码:A

猜你喜欢
模块化
模块化自主水下机器人开发与应用
模块化策略在建筑设计中的应用研究
模块化住宅
马勒推出新型模块化混动系统
考虑模块化和退货率的供应链大规模定制模型
考虑模块化和退货率的供应链大规模定制模型
基于ARM+FPGA的模块化同步控制算法研究
ACP100模块化小型堆研发进展
办公室模块化设计
机械制造技术模块化教学改革研究