Microchip公司16位单片机dsPIC33E/PIC24E系列bootloader的开发(4)
如果你看过本系列文章的上一篇关于如何从编译好的二进制固件里提取数据,那你距离最后的成功——通过bootloader给单片机下载固件——仅一步之遥了。
与ICD编程器利用芯片的PCG/PCD管脚下载程序不同,Microchip在其16位单片机芯片里实现了一整套指令/寄存器专门用于对自己的flash闪存进行读写。比如:
- NVMADRU(高8位地址)和NVMADR(低16位地址)是记录flash程序空间地址的寄存器,他们所保存的24位地址指向用户所需要读/写/擦除固件程序的起始地址。
- NVMCON寄存器中的NVMOP字节用于触发不同的flash空间操作,通过不同的设置执行比如批量擦除、页擦除、行擦除等。
- TBLPAG寄存器用于保存”Write Latches”写锁存(下面有关于这个写锁存的讨论)24位地址中的高8位。对于dsPIC33E或者PIC24E的芯片,你只需要将“0xFA”赋值给这个TBLPAG寄存器,因为从下图中可以看出,“Write Latches”物理地址被硬实现从0xFA0000开始。
- TBLWTH和TBLWTL指令用于将RAM中的数据“锁”入Write Latches中,然后才能执行最终的固件程序“烧”入闪存步骤。
- 还有很多……
考虑到Microchip在其芯片上实现了“行编程”的功能,所以以“行”为单位(即每128个指令大小为单位)进行编程就显得比较方便而且快捷。“Write Latches”(写锁存)可以被理解为一个在Flash(高8位地址是0xFA)上面的临时数据存储器,用于临时存放这128个指令数据,等待最终的“烧制”过程。在行编程模式中,Microchip的disPIC33E系列似乎不支持直接将RAM中的数据直接写入用户闪存程序存储区,所有数据必须先进入RAM,然后从RAM中导入写锁存,最终再从写锁存写入目标地址的Flash中。下面是一段源代码展示了这一过程:
- 通过外围模块(peripheral,可以是UART、I2C、SPI、PMP、普通I/O等)接收固件二进制数据,并存入RAM:
[sourcecode language=”text”]
.bss
buffer: .space (#128*3) ;在RAM内存中创建128×3字节大小的数组
;通过UART/SPI/I2C接受外面传进来的二进制代码,这里详细不列举[/sourcecode]
- 将RAM中的数据“锁”如写锁存“Write Latches”:
[sourcecode language=”text”]mov #128, W1 ;要“烧”128个指令,即一个“行”的大小
mov #0xFA, W0
mov W0, TBLPAG ;上面提到的,Write Latches的地址总是从0xFA0000开始
mov #0, W2 ;Write Latches的低16位地址从0开始
mov #buffer, W3 ;上面创建的128×3大小的buffer的地址
WRLA: tblwth.b [W3++], [W2]
tblwtl.b [W3++], [W2++]
tblwtl.b [W3++], [W2++]
dec W1, W1
bra nz, WRLA[/sourcecode]
- 执行“行”编程(row program):
[sourcecode language=”text”]
;在开始最终的编程步骤之前,芯片必须知道这128个指令写到用户程序存储区的哪里。
;所以我么必须对此进行定义。
;假定W0寄存器里已经导入了地址的高8位
mov W0, NVMADRU ;将这高8位导入NVMADRU寄存器
;假定W0现在导入的是地址的低16位
mov W0, NVMADR ;现在将低16为导入NVMADR寄存器
;现在芯片知道写锁存里面128个指令应该写到flash的什么位置
;一些就绪就待最终的执行了!
mov #0x4002, W0
mov W0, NVMCON
mov #0x55, W0
mov W0, NVMKEY
mov #0xAA, W0
mov W0, NVMKEY
bset NVMCON, #WR
nop
nop
;等待写入操作完成。
WWTF: btsc NVMCON, #WR
bra WWTF[/sourcecode]
- 完成了一个“行编程”(即128个指令),重复步骤1~3直到完成整个芯片的编程。