★PIC16C57的PROGRAM MEMORY灵活使用★
当您使用Microchip公司的PIC16C57在设计程序时,是否被它的PROGRAM MEMORY需分PAGE使用,而PAGE之设定又影响到goto、call、addwf2、movwf2四个指令之执行结果而困扰不已呢?以下是小弟领悟出来的一点心得,在此野人献曝,与大家分享。PIC16C57之PROGRAM MEMORY共有2 K words,分为四个PAGE,每个PAGE有512个words;goto、call、addwf2、movwf2四个指令之执行,会改变PROGRAM COUNTER内容,其结果也受STATUS WORD REGISTER f3之bit 6, 5的影响,如图(一)所示;改变STATUS WORD REGISTER之bit 6, 5以下一律以"PAGE(的)控制"称呼。
除此之外,每个PAGE分为前半部及后半部,call、addwf2、movwf2三个指令执行结果只会跳到某一PAGE内的前半部,因为这三个指令执行后会使PROGRAM COUNTER的bit 8变为0,而goto指令则不受限制,可跳到一个PAGE之前、后半部。
PROGRAM MEMORY地址7FF是reset后第一个被执行的指令,正常情况下放入goto指令,而且STATUS WORD REGISTER bit 6, 5在 reset后全变为0,所以这个goto指令会使程序跳到PAGE 0去执行。至于要goto到何处?我们先以main 的label来代表,下文再继续讨论。
结构化的程序撰写是由主程序呼叫许多子程序组成的,为了能善用这四个PAGE,又不要让程序在呼叫子程序时或做goto之前也要同时注意PAGE的设定值,我对PAGE 0做以下安排:PAGE 0的前半部先存放所有的第一阶(即主程序直接呼叫的)子程序,接下来才存放主程序,上一段所提及的label—main就是安排在这个地方;整段主程序只能放在PAGE 0内,否则分置于两个PAGE内的goto指令前需有不同的PAGE控制,此举不但麻烦又易出错,尤其是尚在发展更改中的程序,更难掌握。
当上述的第一阶子程序太大或太多,以致无法全部放入PAGE 0的前半部时,或是会造成主程序分跨于PAGE 0与PAGE 1时,就要将部分子程序的"身体"移到别的PAGE中,但"头"仍需保留在PAGE 0的前半部,"头"是个重要的媒介,其任务就是要把子程序导引到正确的PAGE上执行;而被移走的子程序其"尾"(retlw之前)需加上指令,把PAGE的控制转回PAGE 0;如此安排,主程序就不用管它所呼叫的子程序"主体"究竟位于哪个PAGE上了。请参考如下范例:
LIST p=16c57
;****************************************************************
ORG 0 ;page 0前半部
;****************************************************************
init: ;子程序的"头
bsf STATUS,5 ;page selector point to page 1
goto sub1_1
;------------------------------------------------------------------------------------------------
tx: ;子程序的"头
bsf STATUS,6 ;page selector point to page 2
goto sub2_1
;------------------------------------------------------------------------------------------------
rx: ;子程序的"头
bsf STATUS,6 ;page selector point to page 2
goto sub2_2
;------------------------------------------------------------------------------------------------
rd_eeprom: ;子程序的"头
bsf STATUS,5 ;page selector point to page 3
bsf STATUS,6
goto sub3_1
;------------------------------------------------------------------------------------------------
………………
;其它的第一阶副程的"头"
;=========================================================
main:
;主程序
………………
call init ;直接呼叫,不必管PAGE问题
………………
call rd_eeprom ;直接呼叫,不必管PAGE问题
………………
call tx ;直接呼叫,不必管PAGE问题
………………
call rx ;直接呼叫,不必管PAGE问题
………………
;****************************************************************
ORG 0x200 ;page 1前半部
;此前半部可用以放置第二阶子程序,而且是仅接受PAGE 1程序的呼叫;同样的,这些子程序进入点也需全部位于PAGE 1的前半部,否则会被呼叫不到;如此规划,对PAGE的控制才会单纯。
;------------------------------------------------------------------------------------------------
rout1_1:
………………
retlw 0 ;如此规划不必更改PAGE控制
;------------------------------------------------------------------------------------------------
rout1_2:
………………
retlw 0 ;如此规划不必更改PAGE控制
;=========================================================
sub1_1: ;子程序的"身体"
………………
call rout1_1
………………
call rout1_2
………………
bcf STATUS,5 ;page selector point to page 0
retlw 0
;****************************************************************
ORG 0x400 ;page 2前半部
;此前半部的规划与PAGE 1同
;------------------------------------------------------------------------------------------------
sub2_1: ;子程序的"身体"
………………
bcf STATUS,6 ;page selector point to page 0
retlw 0
;------------------------------------------------------------------------------------------------
sub2_2: ;子程序的"身体"
………………
bcf STATUS,6 ;page selector point to page 0
retlw 0
;****************************************************************
ORG 0x600 ;page 3前半部
;此前半部的规划与PAGE 1同
;------------------------------------------------------------------------------------------------
sub3_1: ;子程序的"身体"
………………
bcf STATUS,5 ;page selector point to page 0
bcf STATUS,6
retlw 0
;****************************************************************
ORG 0x7FF ;reset vector
goto main
;****************************************************************
END
上例中将PAGE 1、2、3的前半部优先放置第二阶子程序,而且此子程序只由同一PAGE中的第一阶子程序所呼叫使用;若其它PAGE也有第一阶子程序需用到此第二阶子程序,那就把有此需要的第一阶子程序搬来同一PAGE吧。如此用心良苦有以下几个好处:1、第一阶子程序可直接call第二阶子程序,不用改变PAGE的控制,2、第二阶子程序执行结束时只需retlw,也不用改变PAGE的控制,3、PAGE的管理单纯,不易出错,4、整个程序代码可减少bcfSATUS,5、bcfSTATUS,6、bsfSTATUS,5、bsfSTATUS,6的数量;当主程序也有必要直接呼叫此第二阶子程序时,可以有两种做法,第一种做法可以保持程序PAGE管理的一致性,就是为主程序另外设计一个第一阶子程序,来间接呼叫这个第二阶子程序,这会增加5个(若这个第二阶子程序在PAGE 3则需再加2个)指令及其执行时间,第二个做法就是破坏原则,由主程序直接呼叫,但在call指令的前后需加上PAGE控制,这种做法有其缺点,就是当此第二阶子程序被移到别的PAGE时,主程序内所有为呼叫该第二阶子程序所做之PAGE控制,全需因应修改,而且呼叫的次数愈多,PAGE控制也跟着多,修改起来也更费事且易出错;两种做法各有其利弊,何者适用则全由阁下视情况自行判断选择了。
程序组译(assemble)完一定要开启listing檔(扩展名.lst),看看有无跨PAGE的现象,有的话就要调整该PAGE内一部分的子程序到别的PAGE,同时被搬动过的子程序的"头"及"尾"也需因应修改,同时要注意的是,是否把相对应的第二阶子程序也一齐搬动;另外要看看子程序(第一阶或第二阶)之进入点,有没有落于每个PAGE的前半部(即Address的bit 8为0的区域),没有的话也要调整以消除此现象,不然会造成呼叫不到的bug。
现在将以上程序PAGE安排原则总结如下:
一、PAGE 0的前半部先放第一阶子程序的"头"(若空间足够也可放整个子程序),"头"负责把PAGE控制转到该子程序的"身体"所座落之PAGE。
二、接下来放置主程序,主程序需全部置于PAGE 0内。
三、第一阶子程序的"身体"可置于任一PAGE内,该子程序的"尾"负责把PAGE控制转回PAGE 0。
四、第二阶子程序与呼叫它的第一阶子程序放在同一PAGE内。
五、呼叫第二阶子程序不用改变PAGE控制(例外:主程序的呼叫);第二阶子程序的"尾"也不用改变PAGE控制。
六、所有的子程序,不管是第一阶或第二阶,其进入点皆需位于每一PAGE的前半部。
如此安排后,对子程序的呼叫均不需处理PAGE控制,此控制是由子程序的"头"及"尾"处理掉了;而主、子程序当中的goto指令就可安心使用,不虞会GOTO到错误的PAGE上。
以上安排是不是会让您安心撰写程序而不用担心PAGE的控制呢?如果您发现到更好的方法也请不吝指教,谢谢!
页:
[1]