欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM8(STM8S003F3) Bootloader (IAP) 升级程序

程序员文章站 2022-07-04 19:30:59
...

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中设定地址范围,如下图:

STM8(STM8S003F3) Bootloader (IAP) 升级程序

BOOT程序链接配置

STM8(STM8S003F3) Bootloader (IAP) 升级程序

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的参考手册。

四.示例源代码下载

    -> 源代码下载