前言
本篇主要介绍 STM8Lxxxx 如何实现在应用程序中编程(In-application programming)。
1. IAP user Flash 分配框图及中断向量表重定向
注意:
1 和 2 为 IAP 代码空间。为客户自己的 bootloader 代码,应用程序中更新 Flash 的代码。
3 和 4 为客户应用程序代码,实现产品具体操作功能。
中断向量表 1 中除了 Reset,其他存放的是指向中断向量表 2 的地址;中断向量表 2 中放的是具体的 APP 中断处理程序入口地址。发生中断后 PC 指针先指向中断向量表 1,然后再指向中断向量表 2,最后到具体的中断处理函数处。客户应用程序起始位置(上图是 0x9000)可以根据客户需要设置,比如可以改 0x9000 为 0xA000(如果客户的bootloader 程序超过 0x9000 但没有超过 0xA000 可以这样设置)。
2. IAP 对中断向量表重定向的说明
STM8 的中断向量位置是固定的,大部分是 0x8000 开始处。IAP 程序中需要手工处理中断向量表。并且 boot 程序不能使用任何中断(IAP 程序中的中断跳转(中断向量表 1)会使 PC 指到 0x900x 处,如果此时 APP 没有程序,程序就会出错。),程序中的所有中断(向量表 1)的处理最终都要跳转到用户程序的中断向量表 2 处。
IAP bootloader 参考程序为 STSW-STM8006。
下面以 STVD 工程 Cosmic 为例进行说明。
…\STSW-STM8006\AN2659-IAP_using_user-bootloader\Project\STM8L_User-Bootloader_example\STVD\CosmicStm8_interrupt_vector.c: 下面的中断向量表 1 位于 0x8000 到 0x8080 地址空间。
假设发生了 0x8001 中断,指针经硬件定向到了 0x8001,在 0x8001 里存储的是跳转到 0x9001 代码,然后程序跳转到 0x9001 位置去执行,而 0x9001 中的代码是跳转到具体的中断函数处。(具体代码中使用函数指针来实现)
0x82 为操作码,意思是跳转到后面的地址去执行。如下例:
- //*********IAP bootloader 程序中的 中断向量表 1 **************************
- extern void _stext(); /* startup routine */
- 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 */ //note:向量表代码开始位置为
- 0x8000,0x82 后面的 24bits 存放 0x9000
- {0x82, (interrupt_handler_t)(UserISR_IRQ+ 1)}, /* trap */ //note:代码位于
- 0x8001,0x82 后面的 24bits 存放 0x9001
- {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 */
- Step1 发生中断
- Step2 跳转到向量表 2
- Step3 跳转到具体的处理函数
- Step4 APP 软件发生中断
- {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 */
- };
- //*********************************************************************************
复制代码
对 0x82 的描述在文件 PM0044 有如下描述: 0x82 为内部指令:
在 main.h 里有下面的代码: MAIN_USER_RESET_ADDR 的地址为 0x9000。所以 APP 代码开始的位置在 0x9000,开始存放第二个中断向量表(中断向量表 2),此处为中断处理函数入口地址的重定向表。
- //*********************************************************************************
- //user application start (user interrupt table address)
- #define MAIN_USER_RESET_ADDR 0x9000ul
- //*********************************************************************************
复制代码
3. STVD 软件中,下图 Project/Settings 中对中断向量表位置的处理可以看到。中断向量表 1 是从 0x8000 开始存放(Vector file name and Vector addr.)。
对“Vector file name”的定义为“stm8_interrupt_vector.c”;“Vector address”定义为“0x8000”地址。如下图。
…\STSW-STM8006\AN2659-IAP_using_user-bootloader\Project\STM8L_User-Bootloader_example\STVD\Cosmic\Debug有文件“userbootloader.lkf”,有代码如下:(当这个文件中的设置同 STVD Project/Settings/Linker 对画框中的设置不一样时,优先使用对话框中的设置)
- //**********************************************************************************
- # Segment configuration - section reserved for STVD
- #<BEGIN SEGMENT_CONF>
- # Segment Code,Constants:
- +seg .const -b 0x8080 -m 0x7f80 -n .const -it
- +seg .text -a .const -n .text
- # Segment Eeprom:
- +seg .eeprom -b 0x1000 -m 0x400 -n .eeprom
- # Segment Zero Page:
- +seg .bsct -b 0x0 -m 0x100 -n .bsct
- +seg .ubsct -a .bsct -n .ubsct
- +seg .bit -a .ubsct -n .bit -id
- +seg .share -a .bit -n .share -is
- # Segment Ram:
- +seg .data -b 0x100 -m 0x4ff -n .data
- +seg .bss -a .data -n .bss
- +seg .FLASH_CODE -a .bss -n .FLASH_CODE -ic
- #<END SEGMENT_CONF
- …
- # Interrupt vectors file - section reserved for STVD
- #<BEGIN VECTOR_FILE>
- +seg .const -b 0x8000 -k
- Debug\stm8_interrupt_vector.o
- #<END VECTOR_FILE>
- //*******************************************************************************
复制代码
4. User Flash 空间烧写方式
Bootloader 程序可以烧写内部 Flash、EEPROM、RAM。适用于产品软件的更新。
5.对 _fctcpy 的说明,对 Flash 的大块区域编程的代码在 RAM 中运行的问题解决。
对 Flash 进行大块区域的编程的代码需要在 RAM 中运行,为了实现这个,需要一个自定义代码段,并将这个代码段放在RAM 中。在例程中定义了一个“.FLASH_CODE”段。 请查看下面代码红色区域。
- //*********************IAP 的 bootloader 程序*********************************
- #ifdef _COSMIC_
- #pragma section (FLASH_CODE) //定义 FLASH_CODE 代码段
- #endif /* _COSMIC */
- #ifdef _IAR_
- #pragma location = "FLASH_CODE"
- #endif /* _IAR_ */
- #ifdef _RAISONANCE_
- void Mem_ProgramBlock(u16 BlockNum, FLASH_MemType_TypeDef MemType, u8 *Buffer) inra
- {
- u16 Count = 0;
- u32 StartAddress = 0;
- u16 timeout = (u16)0x6000;
- /* Set Start address wich refers to mem_type */
- if (MemType == FLASH_MEMTYPE_PROG)
- {
- StartAddress = FLASH_START;
- }
- else
- {
- StartAddress = EEPROM_START;
- }
- /* Point to the first block address */
- StartAddress = StartAddress + ((u32)BlockNum * BLOCK_SIZE);
- /* Standard programming mode */
- FLASH->CR2 |= (u8)0x01;
- /* Copy data bytes from RAM to FLASH memory */
- for (Count = 0; Count IAPSR & 0x40) != 0x00 || (timeout == 0x00))
- {
- timeout--;
- }
- }
- #endif /* STM8S208, STM8S207, STM8S105 */
- }
- #ifdef _COSMIC_
- #pragma section () //普通默认代码段
- #endif /* __COSMIC__ */
- //*******************************************************************************
复制代码
_fctcpy 功能:
COSMIC 中的函数_fctcpy 是将 Flash 中的代码拷贝到 RAM 中并运行。_fctcpy 寻找 linker 定义的描述符(此描述符是在 RAM中定义段的第一个字符),在 STSW-STM8006 中定义了一个段 FLASH_CODE (#pragma section (FLASH_CODE))。因此第一个字符是‘F’.
在 mian 函数中调用_fctcpy('F')的作用是把 FLASH_CODE 段拷贝到 RAM 中并运行。
FLASH_CODE 是一个可以移动的段,需要在 IAP “boot 程序链接配置”的 RAM 区添加。
在 RAM 中创建一个“.FLASH_CODE”段,并在 Option 中输入“-ic”;“.FLASH_CODE”表示在 RAM 中定义一个
FLASH_CODE 的段,程序可以从此地址运行。“-ic”表示标记这个段为可移动的段,可将 Flash 中的代码移植到此。
- void main(void)
- {
- …
- _fctcpy('F'); //把 FLASH_CODE 代码拷贝到 RAM 中并运行
- …
- }
复制代码
可以在生成的*.map 文件中检查生成的段是否正确,示例代码中有如下的 map 文件
- //*******************************************************************************
- ---------------------------------------------------------------------------------
- Segments
- --------
- start 00008080 end 000080a2 length 34 segment .const
- start 00008104 end 00008be0 length 2780 segment .text
- start 00001000 end 00001000 length 0 segment .eeprom
- start 00000000 end 00000001 length 1 segment .bsct, initialized
- start 000080af end 000080b0 length 1 segment .bsct, from
- start 00000001 end 00000091 length 144 segment .ubsct
- start 00000091 end 00000091 length 0 segment .bit
- start 00000091 end 00000091 length 0 segment .share
- start 00000100 end 00000100 length 0 segment .data
- start 00000100 end 00000100 length 0 segment .bss
- start 00000100 end 00000154 length 84 segment .FLASH_CODE, initialized
- start 000080b0 end 00008104 length 84 segment .FLASH_CODE, from
- start 00000000 end 000004de length 1246 segment .info.
- start 00000000 end 00000e70 length 3696 segment .debug
- start 00008000 end 00008080 length 128 segment .const
- start 000080a2 end 000080af length 13 segment .init
- //*******************************************************************************
复制代码
“from”为存放代码的部分。“initialized”为执行代码的部分。
IAP 的 bootloader 和 APP 分开为两个独立的工程文件。上面的内容是针对 IAP 的 bootloader 的程序工程的处理说明(属于图 1 中的“1.中断变量表 1,复位后硬件 PC 指针从这里开始”和“2.客户程序中的 bootloader 代码” 段),下面来说说APP 工程段(图 1 中的 3 和 4 代码段)。
6. APP 工程说明。
在 APP 工程里,设置中断向量地址为 0x9000(上面以 0x9000 为例。如果从 0xA000 开始,就设置为 0xA000)。代码段的起始地址为 0x9080. 编译后生成的二进制文件就是需要通过 bootloader 烧写到 Flash 0x9000 之后地址的 APP 程序。
|