Microchip公司16位单片机dsPIC33E/PIC24E系列bootloader的开发(5)

Bootloader必须百分百可靠。换句话说,bootloader永远是一个无名英雄,躲在芯片的flash里面,在必要时发挥其作用。它不能在任何时候被自己擦除或者覆盖。然而,如果你了解单片机bootloader的实现机制,你不难发现bootloader说到底也只是一段存储在flash空间里的二进制代码而已,与你其他的应用程序无二。因此,bootloader是可以被擦除或者覆盖的。一个设计优秀的bootloader可以避免绝大部分的自毁式错误,但却无法避免由于硬件异常而造成的此类错误。尽管硬件异常五花八门,有不可预测性。但我们任然可以通过一定的软件机制来保护bootloader免遭损坏。

举例来说,当单片机闪存第0页被擦除后,却由于某些原因(比如IO错误,跳电等)紧跟其后的自我重编程操作无法完成,那单片机的bootloader就丢失了位于闪存最前端最重要的跳转重置(GOTO-RESET)命令行。造成的结果就是在单片机冷启动以后无法正确找到bootloader,从而“挂死”。如果你不是很明白为什么单片机闪存0页0行对bootloader的重要意义,为什么这么重要的闪存内容必须每次都要擦除并重新编程,请阅读我本系列文章的第一篇

为了避免类似上述硬件异常所造成的bootloader损坏,我在这里设计了一种“回滚”机制,保存bootloader免遭丢失GOTO-RESET指令的危险。简而言之,一个用于“回滚”的覆盖标识变量用来监控单片机闪存中的GOTO-RESET指令是否被擦除。在每次执行bootloading之前,回滚机制首先将GOTO-RESET中的指令保存在另外一个安全的地方,然后执行擦除。一旦发生了擦除操作,覆盖标识变量就被设置为1,表明单片机原有的GOTO-RESET已被存在另外一个地方,该指令位置已被清空。而后续程序一旦发现正常的bootloader无法完成,而该变量值却任然为1,那就启动“回滚”,即将保存在他处的原GOTO-RESET指令重新写回到原来位置。那尽管这次bootloader失败了,但单片机任然可以正常启动,继续在必要的时候执行bootloader工作。显而易见,该标识变量只有在一次bootloader顺利完成之后才会被重置为0,表明无须对GOTO-RESET进行回滚。

[sourcecode language=”text”]
.bss
bt_Addr:.space 6

; 两个GOTO指令(6字节)用于指向bootloader的起始地址
; 该6字节指令位于闪存第0页第0行

[/sourcecode]

下面是我在单片机中实现的回滚机制代码,注释在代码中间。你当然可以用不用的方式来实现

[sourcecode language=”text”]
Roll_Bk:
; 读入写锁存
mov     #0xFA, W0
mov     W0, TBLPAG
mov     #0, W0

; 缓存变量bt_Addr已经保存了GOTO-RESET指令内容。
; 是“抢”在该指令被删除之前保存下来的
mov     #bt_Addr, W1
tblwth.b    [W1++], [W0]
tblwtl.b    [W1++], [W0++]
tblwtl.b    [W1++], [W0++]
tblwth.b    [W1++], [W0]
tblwtl.b    [W1++], [W0++]
tblwtl.b    [W1], [W0]

; 将bt_Addr中的内容写入地址0.
; 导入地址
mov     #0, W0
; W0此时是0,即GOTO-RESET的地址是0,位于闪存的最前端。
mov     W0, NVMADRU
mov     W0, NVMADR
; 赋值NVMCON使其开始编程。
mov     #0x4001, W0
rcall   Write
return
[/sourcecode]

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

%d 博主赞过: