×
嵌入式 > 嵌入式开发 > 详情

代码的移植性问题

发布时间:2020-08-28 发布时间:
|
代码移植性是对编程人员的一大考验,这个问题应该时刻考虑。甚至从你策划项目的一瞬间开始,就应该考虑到代码可能会被多次修改,或者功能模块的删减等。项目的外围硬件很多时候都会根据需求改变,到底怎么样的编写技巧能使修改量降至最小,而又能很好的完成项目呢?这就是今天研究的话题。这里先说一些小的例子,以及实际怎样运用。

(1)用宏定义代替直接IO操作

我觉得最典型的例子就是蜂鸣器,4KHZ驱动的交流蜂鸣器,如果蜂鸣器接在 P50 口,那么驱动的代码就应该是:

mov a,@0x01

xor 0x05,a

代码很简单,0x01 和 P5口异或出来的结果,就是 P50 口取反,不断的取反那么得到的是 P50 口输出方波,用C语言来描述就是:

PORT5 ^= 0x01

好了,这个代码的移植性超级差,如果我的蜂鸣器现在需要修改到 P62口,那么代码也必须相应的修改为:

mov a,@0x04

xor 0x06,a

当然了,我们这里只需要一处,还不见得麻烦,如果你的代码中有50处,乃至100处需要修改,我想你肯定要抓狂。所以这种不可移植的代码必须扔掉。

修改版1:

通过定义蜂鸣器的IO口来处理

buz_port EQU 0x05

mov a,@0x01

xor buz_port,a

这里用 buz_port 这个名字代替了蜂鸣器的输出端口,那么现在移植性有了提高,如果修改了端口的话,我们只需要重新定义 buz_port 这个宏,就完成了对代码的所有的修改。

按照上面的描述,能解决了端口的问题,但是我们发现,问题还没有完全解决,mov a,@0x01 我们还得手工计算蜂鸣器在哪个引脚上面,而且还得在一个一个的修改,所以还需要改进。

修改版2:

buz_port EQU 0x05

buz_pin EQU 0

mov a,@1<

xor buz_port,a

这个代码就真正实现了所谓的移植性。用C语言来解释一下

buz_port ^= 1<

如果C语言过关的同学应该对这个表达式很熟悉了。1<

例如

1<<0 得到的结果是 1 (00000001b)

1<<3 得到的结果是 8 (00001000b)

道理是这样,但是使用起来一点也不负责,可以说是很好用。可以直接和IO的名字对应起来,例如前面说的 P62 的话,那么定义就应该为:

buz_port EQU 0x06

buz_pin EQU 2

只需要修改一下定义,程序当中所有使用了这个IO口的部分都不需要修改,这样移植性就非常好了。这个技巧虽然简单,但是是必须学会的。

(2)动态绑定IO口

思考一种情况,我们使用 3个IO口分别发不同占空比的PWM,驱动3个不同颜色的LED发光,形成彩灯效果。要求就是3个IO口独立控制,那么很自然的想法就是,3段程序,每一段程序都生成PWM,也就是说将1路的情况扩展到3路,分别控制。但是寄存器的消耗和ROM的消耗也相对应的变成了3倍,这是很好理解的。但是3段程序惊人的类似,很可能只是IO口输出部分修改了一下,我们思考,时候可以合在一起写呢?也就是说,用1个PWM程序生成PWM,分别输出到3个不同的IO口,我建议,在看下去之前先自己思考一下,敲一下程序,就能很深刻的理解到这个问题的难点。

类似的情况也不少见,例如RC测温,如果测1路的话很好办,但是如果测很多路呢?你不会真的需要将每一路都一份对应的程序吧?那绝对不可能,处理的办法类似,也就是说,用一个程序处理多个IO的输入数据,切换。这有点类似数字电路里面的数据选择器。

答案是:动态绑定IO口输入输出,途径是R0和R4配合的间接寻址。怎么实现?其实很简单,因为间接寻址本身能访问地址 0x05 和 0x06 ,也就是能直接访问 PORT5 和 PORT6 ,这样就很容易联想到,例如 PORT6 口输出状态,然后 PORT6 的输出状态修改为 0X0F (P60~P63高电平,P64~P67低电平)一般的做法是:

mov a,@0x0f

mov PORT6,a

如果通过间接寻址的办法,那么就是

mov a,@0x06

mov R4,a

mov a,@0x0f

mov R0,a

首先将要访问的寄存器地址 0x06放到地址寄存器 R4,然后将数据 0x0f 送到 R0中,那么实际的效果就是将 0xf 送到 PORT6 当中,用仿真器跑一下就OK了,很简单的。

懂了这个原理之后,那么所谓的“动态绑定”就很容易理解了,将原来用 PORT5 PORT6访问IO口的指令,变成用 R0 R4 间接寻址来访问,这样程序就可以变得很灵活。

例如有用回之前的那个蜂鸣器的例子

系统有两个蜂鸣器,P50和P62,那么下面看看怎么通过动态绑定将方波分别输出到这两个蜂鸣器当中。

分配两个寄存器,分别保存当前需要操作的端口信息。

REG_PORT == 0x10

REG_PIN == 0x11

假如当前需要对 P50输出

Mov a,@0x05

Mov REG_PORT,a

Mov a,@1<<0

Mov REG_PIN,a

那么蜂鸣器的驱动函数就应该做相应的修改,前面说了,原来是直接对PORT口操作,现在变成间接寻址

Mov a,REG_PORT

Mov R4,a

Mov a,REG_PIN

Xor R0,a

可以理解么?其实很简单,首先确定当前需要操作的端口,前面设定了REG_PORT的值为0x05,也就是说操作 PORT5,然后将需要操作的脚 1<<0 也就是 0x01 和 R0(现在指向PORT5)异或,得出来的效果就是 P50 改变电平,多次调用就变成输出方波了。

如果现在需要对 P62输出,那么事情就变得很简单,只需要:

Mov a,@0x06

Mov REG_PORT,a

Mov a,@1<<2

Mov REG_PIN,a

那么中间的方波生成部分就通用了,我们需要操作哪个端口,直接送值就OK。

看到没有?这里就是所谓的动态绑定。如果考虑移植性,也就是说以后可能会修改端口的话,那集合第一点当中所说的,定义一个宏就OK了,反正这东西需要自己灵活的运用。

结束语:

很简单的两个东西,我用了大篇幅来描述就是为了说明问题而已,如果理解的话真的很简单,甚至是不足挂齿。但是,怎么说呢,程序就是这类小技巧一点一点的积累起来的。希望今天说的2个小技巧能对你有益。



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
基于嵌入式系统的CAN总线网络通信流程设计