STM8(STM8S003F3) Bootloader (IAP) 升级程序
STM8S003这种片内FLASH只有8k的单片机使用bootloader功能貌似没有必要,但如果你的项目只需要6K的程序空间那剩余的2K用作bootloader也是个非常不错的注意:)
一.环境
编译:STVD V4.3.5 + COSMIC V4.3.4
Bootloader上位机软件: CAS-BOOT
二.实现原理
1.FLASH程序存储器应用
STM8S003F3集成8K字节的FLASH程序存储器,其地址范围为0x8000 ~ 0x9fff
其中中断向量占用0x8000到0x8080 用户程序可以存储于0x8080到0x9fff
实现Bootloader时,BOOT代码占用前2K的字节,即0x8000到0x8800
用户程序占用后6K字节,即0x8800到0x9fff
笔者使用两个项目来开发这两个程序,也就是说BOOT程序和APP程序开发于不同的项目,BOOT程序编译后生成的可执行代码存储于
0x8000开始处,而用户APP程序存储于0x8800开始处。这个地址的分配需要在各自的项目中配置,配置方法:
project -> setting... -> Linker -> Category -> Input中设定地址范围,如下图:
BOOT程序链接配置
APP程序的链接配置
2.FLASH编程在RAM内执行问题
BOOT程序实现FLASH编程时,编程执行代码应在RAM内执行,为了实现这个,我们需要一个自定义代码段,将编程代码放入其中,
这里笔者新增加了一个名为FLASH_CODE的代码段,编程代码如下:
#pragma section (FLASH_CODE)
void ProgramBlock(uint16_t BlockIndex, uint8_t *Buffer)
{
uint16_t Count = 0;
uint32_t StartAddress = BlockIndex<<6;
//Standard programming mode
FLASH->CR2 |= (uint8_t)0x01;
FLASH->NCR2 &= (uint8_t)~0x01;
//Copy data bytes from RAM to FLASH memory
for (Count = 0; Count < BLOCK_SIZE; Count++)
{
*((PointerAttr uint8_t*)StartAddress + Count) = ((uint8_t)(Buffer[Count]));
}
}
#pragma section ()
FLASH_CODE是一个可移动的段,需要在“Boot程序链接配置”的RAM区添加,需要注意的是这个段的名字输入时需要前面加一个点。
另外,该段的Option需要指定为 “-ic”,以表示这是一个在RAM内执行的可移动代码段。如上面的Boot程序链接配置图所示。
但在程序初始化时这个代码段仍存储在FLASH中,未复制到RAM对应的区域,所以我们要使用到一个cosmic库函数 _fctcpy('F');
这个函数帮助我们,将ram内的代码段从FLASH复制过去,其参数为段名的第一个字符。
3.中断向量问题
STM8S的中断向量地址是固定的,也就是0x8000处,这个已经被boot程序占用了,怎么办呢,只能手工中断向量重定向了,办法就是
boot程序不使用任何中断,boot程序的所有中断都跳转到用户程序的向量处。
struct interrupt_vector const UserISR_IRQ[32] @ MAIN_USER_RESET_ADDR;
//redirected interrupt table
struct interrupt_vector const _vectab[] = {
{0x82, (interrupt_handler_t)_stext}, /* reset */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 1)}, /* trap */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 2)}, /* irq0 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 3)}, /* irq1 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 4)}, /* irq2 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 5)}, /* irq3 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 6)}, /* irq4 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 7)}, /* irq5 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 8)}, /* irq6 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+ 9)}, /* irq7 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+10)}, /* irq8 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+11)}, /* irq9 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+12)}, /* irq10 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+13)}, /* irq11 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+14)}, /* irq12 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+15)}, /* irq13 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+16)}, /* irq14 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+17)}, /* irq15 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+18)}, /* irq16 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+19)}, /* irq17 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+20)}, /* irq18 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+21)}, /* irq19 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+22)}, /* irq20 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+23)}, /* irq21 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+24)}, /* irq22 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+25)}, /* irq23 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+26)}, /* irq24 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+27)}, /* irq25 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+28)}, /* irq26 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+29)}, /* irq27 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+30)}, /* irq28 */
{0x82, (interrupt_handler_t)(UserISR_IRQ+31)}, /* irq29 */
};
其中MAIN_USER_RESET_ADDR为用户程序中断向量地址也就是0x8800。
三.Boot区保护问题
BOOT程序主要是靠ST-LINK之类的下载工具写进去的,写进去一次后即可通过BOOTLOADER程序下载用户程序了,为了BOOT区程序不被误
擦除或覆盖,可以在将其保护起来,这可以通过STM8S的OPTION BYTE来配置,OPTION BYTE保护起来的FLASH地址区域是无法再编程的。
STM8S的OPTION BYTE 是在EEPROM区,可通过ST-LINK等下载工具进行配置,如何将前2K字节的FLASH用OPTION BYTE配置进行保护,请参考
STM8S的参考手册。
四.示例源代码下载
上一篇: Nginx处理请求时的匹配规则详析
下一篇: 基于nginx设置浏览器协商缓存过程详解