在为STM8S103F3微控制器搜索引导加载程序的过程中,发现可用的引导加载程序主要用“ C”编写,“窃取”了大量的FLASH存储器,并传输中断向量表。
对于某些无法将编程器连接到的设备,引导加载程序是必需的。
决定尝试自己编写具有以下要求的引导程序:
-加载程序应称为STM8uLoader;
-该代码必须用汇编器编写(法律尚未禁止汇编器的使用);
-引导加载程序应在FLASH内存中占用尽可能最小的数量,计算机中占用的数量将被视为不受限制;
-加载程序不应移动中断向量表;
-引导加载程序应具有最少的功能,所有主要功能应由计算机接管;
-如果没有连接到计算机,则引导加载程序必须在重置/开机后的合理时间内将控制权转移到应用程序。
第一个条件立即
得到满足 ,但随后的要求必须
得到满足 。
第一阶段。 闪存中有65个字节的代码
为了将向量表保存在原位,决定将代码放置在FLASH存储器的末尾,并立即从转储向量$ 8000切换到它。
在引导时,控制权转移到$ 9FC2的引导加载程序代码。 加载程序配置UART 9600 8N1,等待UART的两个字节,然后不等待,就将控制权转移到应用程序中存储在$ 9FFE:$ 9FFF对中的地址。
如果加载程序从主机程序接收到预期转储大小的高字节和低字节,则加载器将自己进行转储,将转储放入RAM内存并将控制权转移给它。
此外,所有工作都取决于计算机中的程序及其发送的转储。 它应该准确发送完成当前任务所需的转储(读/擦除/写/复制存储单元STM8)。 转储应该能够在RAM内存中相互替换,并将控制权转移到应用程序。
申请的过渡地址是$ 9FFE:$ 9FFF。
文件boot_FLASH.asm:stm8/ TITLE "boot_FLASH.asm" .NOLIST #include "STM8S103F3P.inc" .LIST MOTOROLA WORDS segment byte at 8000 'boot_start' boot_start: jp boot_FLASH_start dc.b $00 ; boot_FLASH ; ******************************************************** ; 0x8004...0x9FC1 WORDS ; segment byte at 8004 'main_FLASH' main_FLASH_start: ldw X, #$03FF ldw SP, X mov UART1_BRR1, #13 mov UART1_CR2, #%00001100 main_FLASH_cycle: callr main_delay ; bset PB_DDR,#5 bset PB_CR1,#5 ; byte1_tx: mov UART1_DR, #$80 byte1_wait_tx btjf UART1_SR, #7, byte1_wait_tx callr main_delay boot_RAM_exit1: ; bres PB_DDR,#5 ; bres PB_CR1,#5 ; ; byte2_tx: mov UART1_DR, #$08 byte2_wait_tx btjf UART1_SR, #7, byte2_wait_tx jra main_FLASH_cycle main_delay: decw X jrne main_delay ret segment byte at 9FC2 'boot_FLASH' boot_FLASH_start: mov UART1_BRR1, #13; Fmaster=16/8=2/9600/16 mov UART1_CR2, #%00001100; / ; UART1 RST_SR boot_FLASH_RST_SR_tx: mov UART1_DR, RST_SR ; , ; ; X ( 200 ) ldw X,#0 boot_FLASH_wait_byte1: decw X jreq boot_FLASH_exit; btjf UART1_SR, #5, boot_FLASH_wait_byte1 ; , , ; X ld A, UART1_DR ld XH, A ; boot_FLASH_wait_byte2: btjf UART1_SR, #5, boot_FLASH_wait_byte2 ; ld A, UART1_DR ld XL, A ; X - ; X ldw Y, #$0400 ; Y 0x0400 (RAM_END + 1) ; boot_FLASH_rx_block_wait: btjf UART1_SR, #5, boot_FLASH_rx_block_wait boot_EEPROM_rx_block_entry: decw Y ; Y ld A, UART1_DR ld (Y), A decw X ; X jrne boot_FLASH_rx_block_wait ; jp (Y) ; () boot_FLASH_exit: dc.b $CC boot_FLASH_exit_addr: dc.w main_FLASH_start end ;
第二阶段。 闪存中的代码大小为21个字节,而EEPROM存储器中的代码为52个字节
从FLASH存储器中选择65个字节(在STM8S103F3中仅为8192个字节)是不人道的。 毕竟,附近有640字节不必要的EEPROM存储器。 让我们将引导加载程序代码分为两部分:boot_FLASH和boot_EEPROM。
加载时,控制权转移到$ 9FEF的boot_FLASH代码。 boot_FLASH将boot_EEPROM代码映像从EEPROM复制到RAM存储器中,并将控制权转移给它。
现在,boot_EEPROM配置UART 9600 8N1,等待UART字节,然后不等待,就将控制权转移到应用程序(我们将地址保留在$ 9FFE:$ 9FFF的相同位置)。
如果boot_EEPROM接收到一个字节,该字节的大小与RAM存储器的预期转储大小相同,则它会接收到一个转储,将转储放在RAM存储器的另一个区域中,并将控制权转移给它。
此外,一切都与第一阶段相同。
文件boot_FLASH_EEPROM.asm: stm8/ TITLE "boot_FLASH_EEPROM.asm" .NOLIST #include "STM8S103F3P.inc" .LIST MOTOROLA WORDS segment byte at 4000 'eeprom' ; boot_EEPROM dc.b $35, $0D, $52, $32, $35, $0C, $52, $35 dc.b $35, $01, $52, $31, $5A, $27, $16, $72 dc.b $0B, $52, $30, $F8, $C6, $52, $31, $72 dc.b $0B, $52, $30, $FB, $3B, $52, $31, $4A dc.b $26, $F5, $96, $5C, $FC, $CE, $9F, $FE dc.b $2B, $FA, $90, $AE, $42, $7F, $AE, $02 dc.b $7F, $CC, $9F, $F4 segment byte at 8000 'boot_start' boot_start: jp boot_FLASH_start dc.b $01 ; boot_FLASH_EEPROM ; ******************************************************** ; 0x8004...0x9FEE segment byte at 8004 'main_FLASH' ; main_FLASH_start: ldw X, #$03FF ldw SP, X mov UART1_BRR1, #13 mov UART1_CR2, #%00001100 main_FLASH_cycle: callr main_delay ; bset PB_DDR,#5 bset PB_CR1,#5 ; byte1_tx: mov UART1_DR, #$80 byte1_wait_tx btjf UART1_SR, #7, byte1_wait_tx callr main_delay boot_RAM_exit1: ; bres PB_DDR,#5 ; bres PB_CR1,#5 ; ; byte2_tx: mov UART1_DR, #$08 byte2_wait_tx btjf UART1_SR, #7, byte2_wait_tx jra main_FLASH_cycle main_delay: decw X jrne main_delay ret ; EEPROM -> RAM segment byte at 9FEF 'boot_FLASH' boot_FLASH_start: ldw X, SP ; Y <- { EEPROM_START + RAM_END} ; Y <- { $4000 + $03FF = $43FF } ldw Y, #$43FF boot_FLASH_copy: ld A, (Y) ld (X), A decw Y decw X jrpl boot_FLASH_copy incw X jp (X) boot_FLASH_exit_address: dc.w main_FLASH_start end ;
运行文件
runSTM8uLoader.bat ,按
面板上的reset按钮,引导程序发送字节0x01。 带有main_RAM.hex文件中的代码的转储通过UART发送到RAM STM8。 评估板开始闪烁LED并发送字节0x20和0x02。 再次按下重置按钮。 FLASH存储器中的应用程序启动,LED开始更快地闪烁并发送字节0x80和0x08。
第三阶段。 闪存中的代码大小为18个字节,而选项字节为52个字节
当然,我们急于使用EEPROM存储器。 现在在哪里存放罪孽和其他桌子? 有了FLASH存储器,并非所有事情都一目了然。 谁决定将应用程序的传输控制地址存储在FLASH存储器中? 引导加载程序版本的相同字节通常一次存储在两个位置。 哪里要压缩用于EEPROM的52个字节?
光刻在这里对我们有帮助。 EEPROM存储器由10个块组成,每个块64个字节。 在这些块中添加另一个块,但是大小不同,在经济上不可行。 意法半导体(STMicroelectronics)就是这样做的,添加了另一个64字节的块,命名为该区域OPTION字节,并在其中存储重要的非易失性微控制器设置(对于STM8S103F3,它最多为11个字节)。 当然,STM忘记提及该区域还剩下53个功能单元。 显然有很多STM8型号,您需要为将来的重要设置留出空间。
我们的引导程序仅在STM8型号上声明,而没有内置引导程序。 因此,到目前为止,我们没有人看到OPTION Bytes块的备份单元。 确实,有一个很小
但解决了的不便之处。 传统的程序员不允许您将信息写入这些单元格。
加载时,控制权转移到$ 9FF2处的初始copy_boot_FLASH代码。 boot_FLASH将boot_OPTION引导加载程序映像从“选项字节”区域传输到RAM。
boot_OPTION配置UART 9600 8N1,发送带有其版本的UART字节,等待来自主机程序的UART字节,并且无需等待0.2秒就将控制权转移到应用程序中位于$ 4831:$ 4832对中的地址。
如果boot_OPTION在发送了一个带有其版本的字节后,占用了预期转储大小的一个字节,则它接受了转储本身,将转储放入RAM内存并将控制权转移给它。
此外,所有工作都取决于计算机中的程序及其发送的转储。 它应该准确发送完成当前任务所需的转储(读/擦除/写/复制存储单元STM8)。 转储应该能够在RAM内存中相互替换,并将控制权转移到应用程序。
该应用程序的过渡地址为$ 4831:$ 4832。
要在闪存中执行的加载程序和应用程序代码: stm8/ TITLE "boot_FLASH_OPTION.asm" .NOLIST #include "STM8S103F3P.inc" .LIST MOTOROLA WORDS segment byte at 4800 'boot_OPTION' ; boot_OPTION dc.b $00, $00, $FF, $00, $FF, $00, $FF, $00 dc.b $FF, $00, $FF, $35, $0D, $52, $32, $35 dc.b $0C, $52, $35, $35, $25, $52, $31, $5A dc.b $27, $16, $72, $0B, $52, $30, $F8, $C6 dc.b $52, $31, $72, $0B, $52, $30, $FB, $3B dc.b $52, $31, $4A, $26, $F5, $96, $5C, $FC dc.b $AE, $80, $04, $2B, $FA, $90, $AE, $42 dc.b $7F, $AE, $02, $7F, $CC, $9F, $F6, $00 segment byte at 8000 'boot_start' boot_start: ldw X, SP jp boot_FLASH_start ; ******************************************************** ; 0x8004...0x9FF1 segment byte at 8004 'main_FLASH' ; main_FLASH_start: ldw X, #$03FF ldw SP, X mov UART1_BRR1, #13 mov UART1_CR2, #%00001100 main_FLASH_cycle: callr main_delay ; bset PB_DDR,#5 bset PB_CR1,#5 ; byte1_tx: mov UART1_DR, #$80 byte1_wait_tx btjf UART1_SR, #7, byte1_wait_tx callr main_delay boot_RAM_exit1: ; bres PB_DDR,#5 ; bres PB_CR1,#5 ; ; byte2_tx: mov UART1_DR, #$08 byte2_wait_tx btjf UART1_SR, #7, byte2_wait_tx jra main_FLASH_cycle main_delay: decw X jrne main_delay ret ; OPTION -> RAM segment byte at 9FF2 'boot_FLASH' boot_FLASH_start: ; Y <- { OPTION_START + RAM_END} ; Y <- { $4800 + $03FF = $43FF } ldw Y, #$43FF boot_FLASH_copy: ld A, (Y) ld (X), A decw Y decw X jrpl boot_FLASH_copy incw X jp (X) boot_FLASH_exit_address: dc.w main_FLASH_start end ;
在RAM存储器中执行的应用程序代码: stm8/ TITLE “boot_RAM.asm” MOTOROLA #include "STM8S103F3P.inc" BYTES segment byte at 0000 'boot_RAM_data' boot_RAM_start: ; pull-up ( ) , , 14 ; ld A, #%01001100 ; [A6 4C] ; cpl A ; [43] ; ld PA_CR1, A ; [C7 50 03] ; ld PB_CR1, A ; [C7 50 08] ; ld PC_CR1, A ; [C7 50 0D] ; ld PD_CR1, A ; [C7 50 12] PD6(UART1_RX), PD2, PD1 ; UART1 / 9600, (8 , , 1 ) ; mov UART1_BRR2, #0 ; [35 00 52 33] Fmaster=16/8=2 9600 mov UART1_BRR1, #13 ; [35 0D 52 32] Fmaster=16/8=2 9600 mov UART1_CR2, #%00001100 ; [35 0C 52 35] UART1_CR2.TEN <- 1 UART1_CR2.REN <- 1 / ; UART1 boot_RAM_byte1_tx: mov UART1_DR, #$02 boot_RAM_byte1_wait_tx btjf UART1_SR, #7, boot_RAM_byte1_wait_tx ldw X,#0 ; [AE 00 00] boot_FLASH X boot_RAM_wait1: decw X ; [5A] jreq boot_RAM_exit1 ; jra boot_RAM_wait1 boot_RAM_exit1: ; bres PB_DDR,#5 ; bres PB_CR1,#5 ; ; UART1 boot_RAM_byte2_tx: mov UART1_DR, #$20 ; [35 11 52 31] boot_RAM_byte2_wait_tx btjf UART1_SR, #7, boot_RAM_byte2_wait_tx ldw X,#0 ; [AE 00 00] boot_FLASH X boot_RAM_wait2: decw X ; [5A] jreq boot_RAM_exit2 ; jra boot_RAM_wait2 boot_RAM_exit2: ; bset PB_DDR,#5 ; bset PB_CR1,#5 ; jra boot_RAM_byte1_tx end
运行文件
runSTM8uLoader.bat ,按
面板上的reset按钮,引导程序发送字节0x25。 带有main_RAM.hex文件中的代码的转储通过UART发送到RAM STM8。 评估板开始闪烁LED并发送字节0x20和0x02。 再次按下重置按钮。 FLASH存储器中的应用程序启动,LED开始更快地闪烁并发送字节0x80和0x08。
在最后阶段,要将引导加载程序映像写入OPTION Bytes区域,必须使用
method 。 该方法的本质是,首先您需要程序员将boot_OPTION_rev25.hex固件文件写入STM8 FLAH存储器,重新启动微控制器,“ OPTION Bytes”区域将填充必要的信息,并且LED会亮起。 然后,程序员再次从本文
boot_FLASH_OPTION.hex中写入FLASH固件文件。
添加了没有应用程序代码的“干净”引导程序代码版本0x14。 将boot_OPTION映像部署到源代码。 更正的评论。 与$ 25版本不同,应用程序的控制转移地址在$ 9FFE:$ 9FFFF FLASH单元中。 FLASH中的大小分别为20个字节。
boot_uC_rev14.asm: stm8/ TITLE "boot_uC_rev14.asm" ; boot_uC = boot_OPTION + boot_FLASH MOTOROLA .NOLIST #include "STM8S103F3P.inc" .LIST WORDS ; ******************************************************** segment byte at 4800 'boot_O_IMG' ;0000FF00FF00FF00FF00FF350D523235 ;0C5235351452315A2716720B5230F8C6 ;5231720B5230FB3B52314A26F5965CFC ;CE9FFE2BFA90AE427FAE027FCC9FF400 ; ; $4800 RAM dc.b $00, $00, $FF, $00, $FF, $00, $FF, $00, $FF, $00, $FF ; OPTION (RAM) ; $480B ($0000) boot_O boot_O_start: ; UART 96008N1 Fmaster=16/8=2/9600/16 ; mov UART1_BRR2, #0 ; [35 00 52 33] mov UART1_BRR1, #13 ; [35 0D 52 32] ; UART1_CR2.TEN <- 1 UART1_CR2.REN <- 1 / mov UART1_CR2, #%00001100 ; [35 0C 52 35] ; $4813 ($0008) boot_E_byte1_tx: ; $14 mov UART1_DR, #$14 ; [35 14 52 31] ; , ; ; X ( 200 ) ; clrw X ; [5F] X boot_F ; $4817 ($000C) boot_O_rx_wait_byte: decw X ; [5A] jreq boot_O_exit ; [27 16] btjf UART1_SR, #5, boot_O_rx_wait_byte ; [72 OB 52 30 F8] ; , , A ; $481F ($0014) ld A, UART1_DR ; [C6 52 31] ; $4822 ($0017) boot_O_rx_wait_block: btjf UART1_SR, #5, boot_O_rx_wait_block ; [72 OB 52 30 FB] push UART1_DR ; [3B 52 31] dec A ; [4A] ; A jrne boot_O_rx_wait_block ; [26 F5] ; $482D ($0022) ldw X, SP ; [96] incw X ; [5C] boot_O_exit_to_FLASH: jp (X) ; [FC] ; $4830 ($0025) boot_O_exit: ldw X, boot_F_exit_address ; [CE 9F FE] jrmi boot_O_exit_to_FLASH ; [2B FA] ; if X < $8000 $0000 ; EEPROM boot_O_exit_to_EEPROM: ; Y <- { EEPROM_END} ldw Y, #$427F ; [90 AE 42 7F] ; X <- { EEPROM_END - EEPROM_START } ; EEPROM RAM ldw X, #$027F ; [AE 02 7F] jp boot_F_copy ; [CC 9F F4] ; $483F ($0034) dc.b $00 ; boot_O_end: ; ******************************************************** segment byte at 8000 'RESET_vector' ;96CC9FF0 ldw X, SP ; [96] X <- RAM_END jp boot_F_start ; [CC 9F F0] ; ******************************************************** ; 0x8004...0x9FEF segment byte at 8004 'main_FLASH' ;20FE jra * ; [20 FE] ; ******************************************************** ; boot_FLASH segment byte at 9FF0 'boot_F' ;90AE4C0A90F6F7905A5A2AF85CFC8004 boot_F_start: ; Y <- { boot_O_START + RAM_END} { $480B + $03FF = $4C0A } ldw Y, #$4C0A ; [90 AE 4C 0A] ; ; boot_FLASH, boot_OPTION boot_F_copy: ld A, (Y) ; [90 F6] ld (X), A ; [F7] decw Y ; [90 5A] decw X ; [5A] jrpl boot_F_copy ; [2A F8] X(Y) >= RAM_START(boot_O_START) incw X ; [5C] jp (X) ; [FC] boot_F_exit_address: dc.w $8004 ; [80 04] ; dc.w $0000 ; [00 00] end ;
添加了一个没有应用程序代码的“干净的”引导程序代码版本0x25。 将boot_OPTION映像部署到源代码。 更正的评论。 与$ 14版本不同,应用程序的控制传输地址位于OPTION Bytes区域的$ 4831:$ 4832单元中。 闪存中的占用大小分别减少到18个字节。 OPTION Bytes区域中的占用大小未更改(52字节+1保留)。
boot_uC_rev14.asm: stm8/ TITLE "boot_uC_rev25.asm" ; boot_uC = boot_OPTION + boot_FLASH MOTOROLA .NOLIST #include "STM8S103F3P.inc" .LIST BYTES ; ******************************************************** ; EEPROM ; boot_O_exit_address $0000 ( <$8000) ; ; segment byte at 0000 'boot_O_IMG' main_ram: ;20FE jra * ; [20 FE] WORDS ; ******************************************************** segment byte at 4800 'boot_O_IMG' ;0000FF00FF00FF00FF00FF350D523235 ;0C5235351452315A2716720B5230F8C6 ;5231720B5230FB3B52314A26F5965CFC ;AE80042BFA90AE427FAE027FCC9FF600 ; ; $4800 RAM dc.b $00, $00, $FF, $00, $FF, $00, $FF, $00, $FF, $00, $FF ; OPTION (RAM) ; $480B ($0000) boot_OPTION boot_O_start: ; UART 96008N1 Fmaster=16/8=2/9600/16 ; mov UART1_BRR2, #0 ; [35 00 52 33] mov UART1_BRR1, #13 ; [35 0D 52 32] ; UART1_CR2.TEN <- 1 UART1_CR2.REN <- 1 / mov UART1_CR2, #%00001100 ; [35 0C 52 35] ; $4813 ($0008) boot_E_byte1_tx: ; $14 mov UART1_DR, #$14 ; [35 14 52 31] ; , ; ; X ( 200 ) ; clrw X ; [5F] X boot_F ; $4817 ($000C) boot_O_rx_wait_byte: decw X ; [5A] jreq boot_O_exit ; [27 16] btjf UART1_SR, #5, boot_O_rx_wait_byte ; [72 OB 52 30 F8] ; , , A ; $481F ($0014) ld A, UART1_DR ; [C6 52 31] ; $4822 ($0017) boot_O_rx_wait_block: btjf UART1_SR, #5, boot_O_rx_wait_block ; [72 OB 52 30 FB] push UART1_DR ; [3B 52 31] dec A ; [4A] ; A jrne boot_O_rx_wait_block ; [26 F5] ; $482D ($0022) ldw X, SP ; [96] incw X ; [5C] boot_O_exit_to_FLASH: jp (X) ; [FC] ; $4830 ($0025) boot_O_exit: dc.b $AE ; ldw X, #boot_O_exit_address ; [AE 80 04] ; $4831 ($0026) ; boot_O_exit_address: dc.w main_flash ; [80 04] ; dc.w main_ram ; [00 00] jrmi boot_O_exit_to_FLASH ; [2B FA] ; if X < $8000 $0000 ; EEPROM boot_O_exit_to_EEPROM: ; Y <- { EEPROM_END} ldw Y, #$427F ; [90 AE 42 7F] ; X <- { EEPROM_END - EEPROM_START } ; EEPROM RAM ldw X, #$027F ; [AE 02 7F] jp boot_F_copy ; [CC 9F F4] ; $483F ($0034) dc.b $00 ; boot_O_end: ; ******************************************************** segment byte at 8000-8003 'RESET_vector' ;96CC9FF2 ldw X, SP ; [96] X <- RAM_END jp boot_F_start ; [CC 9F F2] ; ******************************************************** ; 0x8004...0x9FF1 segment byte at 8004 'main_FLASH' main_flash: ;20FE jra * ; [20 FE] ; ******************************************************** ; boot_FLASH segment byte at 9FF2-9FFF 'boot_F' ;90AE4C0A90F6F7905A5A2AF85CFC boot_F_start: ; Y <- { boot_O_START + RAM_END} { $480B + $03FF = $4C0A } ldw Y, #$4C0A ; [90 AE 4C 0A] ; ; boot_FLASH, boot_OPTION boot_F_copy: ld A, (Y) ; [90 F6] ld (X), A ; [F7] decw Y ; [90 5A] decw X ; [5A] jrpl boot_F_copy ; [2A F8] X(Y) >= RAM_START(boot_O_START) incw X ; [5C] jp (X) ; [FC] end ;
可以从$ 8004 ... $ 9FF1的范围内选择FLASH存储器中应用程序的控制传输地址。 对于来自EEPROM存储器的应用代码映像,只能在RAM存储器中的地址$ 0000处进行控制传输。
主机程序可以传递任何控制传输地址作为第二个命令行参数。
主机程序的源代码可以在
这里找到。 也有用于更详细沟通的联系人。
我要求读者有针对性的批评和建议,以进一步减少代码。
我还建议阅读文章
“如何将STM8的引导程序压缩到FLASH存储器中的8个字节的大小” 。