STM8 bootloader(boot和app皆可使用中断)

STM8 In Application Programming

IAP编写的三个要点:

  1. 分析STM8启动过程和C运行时环境建立
  2. 规划bootloader和application以及各自向量表在内存中的分布
  3. 如何重定位STM8中断向量表

思路:

要编写IAP程序首先需要解决的问题是程序在运行过程中,当发生异常时,如何保证程序正常的跳转到相应的异常服务函数(不论程序是运行在bootloader还是application)

但STM8没有类似NVIC之类的中断控制器管理中断向量的地址,STM8的向量表固定在0x008000,因此在IAP中需要重定位向量表来实现(为了bootLoader和application都可以使用中断,因此,选择将向量表重定位到RAM中)

规划内存分布

针对于STM8L052C6(2K RAM/32K FLASH)

FLASH分布

0x8000bootloader_start
bootloader
0xBFFFbootloader_end
0xC000app_start
app
0xFFFFapp_end

在0x8000开始的前128个字节,放置着bootloader重定位过的向量表(bootloader的真正的向量表放置在另外的地方)

在0xC000开始的前128个字节,放置着application的向量表

RAM分布

0x0000
reload_vector
0x0080
.data/.bss/.textrw
0x07FF

在0x000000开始的128个字节,放置着真正的向量表。(因此,不论在bootloader还是application都要在编译阶段告知链接器保留0x00~0x80这段内存空间)

利用RAM的特性(rwx),当bootloader运行时,放置bootloader的向量表,当application运行时,放置application的向量表。

详细的分布规则,请参考config目录下的lnkstm8l052c6.icf文件

链接脚本语法请参考IARforSTM8/stm8/doc目录下的EWSTM8_DevelopmentGuide.pdf文件

重定位STM8中断向量表

由于STM8的向量表固定在0x008000~0x008080的位置,想要实现重定位向量表,则必须在固定的向量表中填入真正的向量表地址,方法如下:(参考src目录下的stm8l15x_interrupt.s文件)

/*
 * The interrupt vector table.
 */


        SECTION `.intvec`:CONST

define_vector MACRO
        DC8     0x82
        DC24    _interrupt_\1
        ENDM

        PUBLIC  __intvec
        EXTERN   __iar_program_start
        


__intvec:
        DC8     0x82
        DC24    __iar_program_start          ;; RESET    0x8000
        DC8     0x82
        DC24    0x0004
        DC8     0x82
        DC24    0x0008
        DC8     0x82
        DC24    0x000C
        DC8     0x82
        DC24    0x0010
        DC8     0x82
        DC24    0x0014
        DC8     0x82
        DC24    0x0018
        DC8     0x82
        DC24    0x001C
        DC8     0x82
        DC24    0x0020
        DC8     0x82
        DC24    0x0024
        DC8     0x82
        DC24    0x0028
        DC8     0x82
        DC24    0x002C
        DC8     0x82
        DC24    0x0030
        DC8     0x82
        DC24    0x0034
        DC8     0x82
        DC24    0x0038
        DC8     0x82
        DC24    0x003C
        DC8     0x82
        DC24    0x0040
        DC8     0x82
        DC24    0x0044
        DC8     0x82
        DC24    0x0048
        DC8     0x82
        DC24    0x004C
        DC8     0x82
        DC24    0x0050
        DC8     0x82
        DC24    0x0054
        DC8     0x82
        DC24    0x0058
        DC8     0x82
        DC24    0x005C
        DC8     0x82
        DC24    0x0060
        DC8     0x82
        DC24    0x0064
        DC8     0x82
        DC24    0x0068
        DC8     0x82
        DC24    0x006C
        DC8     0x82
        DC24    0x0070
        DC8     0x82
        DC24    0x0074
        DC8     0x82
        DC24    0x0078
        DC8     0x82
    	DC24    0x007C

汇编语法请参考IARforSTM8/stm8/doc目录下的EWSTM8_AssemblerReference.pdf文件

说明:

以TRAP中断为例,当发送TRAP中断时,PC指针首先指向0x008004地址(FLASH/硬件自动完成)去取指,取到的指令操作码为 82 00 00 04,这段操作码对应的指令为INT 0x000004,操作为将目标地址(0x000004)加载到PC寄存器中,效果等于PC指针指向0x000004地址(RAM)去取指,同样的,在0x000004地址处(RAM)存放着指令操作码82 00 b8 db,其中在0x00b8db地址处(FLASH)存放着真正的TRAP中断服务程序,当PC指针指向0x00b8db地址后,从而执行TRAP中断服务函数。

STM8命令集及操作码请参考:
http://www.st.com/content/ccc/resource/technical/document/programming_manual/43/24/13/9a/89/df/45/ed/CD00161709.pdf/files/CD00161709.pdf/jcr:content/translations/en.CD00161709.pdf

在RAM中放置bootloader的向量表

typedef void (INTERRUPT *interrupt_handler_t)(void);
struct interrupt_vector {
    unsigned char interrupt_instruction;
    unsigned char reserve;
    interrupt_handler_t interrupt_handler;
};
struct interrupt_vector isr_handler[32] @".memvectab" = {
    {0x82, 0x00, __iar_program_start},
    {0x82, 0x00, TRAP_IRQHandler},
    {0x82, 0x00, NMI_IRQHandler},
    {0x82, 0x00, FLASH_IRQHandler},
    {0x82, 0x00, DMA1_CHANNEL0_1_IRQHandler},
    {0x82, 0x00, DMA1_CHANNEL2_3_IRQHandler},
    {0x82, 0x00, RTC_CSSLSE_IRQHandler},
    {0x82, 0x00, EXTIE_F_PVD_IRQHandler},
    {0x82, 0x00, EXTIB_G_IRQHandler},
    {0x82, 0x00, EXTID_H_IRQHandler},
    {0x82, 0x00, EXTI0_IRQHandler},
    {0x82, 0x00, EXTI1_IRQHandler},
    {0x82, 0x00, EXTI2_IRQHandler},
    {0x82, 0x00, EXTI3_IRQHandler},
    {0x82, 0x00, EXTI4_IRQHandler},
    {0x82, 0x00, EXTI5_IRQHandler},
    {0x82, 0x00, EXTI6_IRQHandler},
    {0x82, 0x00, EXTI7_IRQHandler},
    {0x82, 0x00, LCD_AES_IRQHandler},
    {0x82, 0x00, SWITCH_CSS_BREAK_DAC_IRQHandler},
    {0x82, 0x00, ADC1_COMP_IRQHandler},
    {0x82, 0x00, TIM2_UPD_OVF_TRG_BRK_USART2_TX_IRQHandler},
    {0x82, 0x00, TIM2_CC_USART2_RX_IRQHandler},
    {0x82, 0x00, TIM3_UPD_OVF_TRG_BRK_USART3_TX_IRQHandler},
    {0x82, 0x00, TIM3_CC_USART3_RX_IRQHandler},
    {0x82, 0x00, TIM1_UPD_OVF_TRG_COM_IRQHandler},
    {0x82, 0x00, TIM1_CC_IRQHandler},
    {0x82, 0x00, TIM4_UPD_OVF_TRG_IRQHandler},
    {0x82, 0x00, SPI1_IRQHandler},
    {0x82, 0x00, USART1_TX_TIM5_UPD_OVF_TRG_BRK_IRQHandler},
    {0x82, 0x00, USART1_RX_TIM5_CC_IRQHandler},
    {0x82, 0x00, I2C1_SPI2_IRQHandler}
};

同时,在链接脚本中,将.memvectab这个section 放置在RAM的0x000000地址处。

STM8启动过程

在上面两部分中介绍的,在RAM的0x00~0x80地址处会在不同阶段时放置bootloader和application的两张向量表,因此必然涉及到两张向量表的互相覆盖。

在STM8启动时,会执行__iar_program_start这个函数(此函数由IAR提供),这个函数负责建立C运行时环境和数据的拷贝,因此,在这个阶段会将bootloader真正的向量表从FLASH拷贝到RAM的0x00 ~ 0x80地址处(由链接脚本指定),当需要跳转到application时,运行以下代码将application的向量表拷贝到RAM的0x00 ~ 0x80地址处然后跳转:

void reload_interrupr_vectortable(void)
{
	uint8_t *check = (uint8_t *)APPLICATION_ADDRESS;

	if(*check == 0x82) {
        uint8_t *src = (uint8_t *)APPLICATION_ADDRESS;
        uint8_t *dst = (uint8_t *)VECTAB_RELOAD_START;
        uint16_t cnt = sizeof(isr_handler);

    	/* disable interrupt, interrupt will be enable in application */
    	sim();
        platform_peripherals_deinit();
        /* reload interrupt vector table(application) from flash to memory */
        for(; cnt > 0; cnt--) {
            *dst++ = *src++;
        }

        /* reset stack pointer (lower byte - because compiler decreases SP with some bytes) */
        asm("LDW X,  SP ");
        asm("LD  A,  $FF");
        asm("LD  XL, A  ");
        asm("LDW SP, X  ");
        asm("JPF $C000  "); /* APPLICATION_ADDRESS */
	}
}

通过汇编指令asm("JPF $C000")跳转到application的起始地址处,开始运行application。

当复位时,会重新执行__iar_program_start函数,将bootloader的向量表再次拷贝到RAM的0x00~0x80地址处,覆盖application的向量表。

至此,IAP编写的难点都已解决。

附连接脚本

/
//      Example ILINK command file for
//      STM8 IAR C/C++ Compiler and Assembler.
//
//      Copyright 2017 HinsShum.
//
//      $Revision: 1623 $
//
/

/*-Symbol-*/
define symbol __intvec_start__      = 0x000000;
define symbol __region_TINY_start__ = 0x000000;
define symbol __region_TINY_end__   = 0x0000FF;
define symbol __region_ROM_start__  = 0x008000;
define symbol __region_ROM_end__    = 0x00BFFF; /* 16K bootloader flash region */
define symbol __region_RAM_start__  = 0x000000;
define symbol __region_RAM_end__    = 0x000FFF; /* 4K RAM region */
define symbol __RAMCACHE_start__    = 0x000100;

/*-Memory Regions-*/
define memory mem with size = 16M;
define region TinyData   = mem:[from __region_TINY_start__ to __region_TINY_end__];
define region ROM_region = mem:[from __region_ROM_start__ to __region_ROM_end__];
define region RAM_region = mem:[from __region_RAM_start__ to __region_RAM_end__];

/

define block CSTACK     with size    = _CSTACK_SIZE, alignment = 1 {};
define block HEAP       with size    = _HEAP_SIZE,   alignment = 1 {};
define block INTVEC     with size    = 0x80,         alignment = 4 { ro section .intvec };
define block INTVECMEM  with size    = 0x80,         alignment = 4 { rw section .memvectab };
define block RAMCACHE   with size    = 0x40,         alignment = 1 {};
define block MD5API     with size    = 0x06,         alignment = 2 { ro section .md5_api };

initialize by copy { rw section .*.textrw, rw data, zi };
do not initialize { rw section vregs, rw section .noinit };

// Keep section
keep { section .memvectab };
keep { block RAMCACHE };
keep { section .md5_api };

// Placement
place at start of ROM_region    { block INTVEC };
place in ROM_region             { ro code, ro data };     // includes ro code and data
place at end of ROM_region      { block MD5API };         // provide md5 api to application

place at address mem: __intvec_start__ { block INTVECMEM };
place in TinyData               { rw section .vregs };
place at address mem: __RAMCACHE_start__ { block RAMCACHE };
place in RAM_region             { rw code, rw data, rw section .*.textrw };
place in RAM_region             { zi };
place in RAM_region             { block HEAP };
place at end of RAM_region      { block CSTACK };
/
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值