以下内容均参考自 CXSTM8_UsersGuide.pdf
eide 插件版本需要更新到预览版本 v3.20.7
链接脚本 stm8.lkf 和 中断向量表 vector.c
在使用内置的 stm8-cosmic 模板创建项目后,工程目录下会有一个 stm8.lkf
和 vector.c
文件
这两个文件是配套使用的,在开始编写代码前,需要根据你使用的芯片来修改这两个文件
链接脚本 stm8.lkf
默认的链接脚本如下:
##
## link command file for STM8S003F3
## Copyright (c) 2008 by COSMIC Software
##
##############
# defines
##############
+def RAM_ADDR=0x0000
+def RAM_SIZE=0x400
+def SP_VALUE=0x3ff # stack top pointer value
+def ROM_ADDR=0x8000
+def ROM_SIZE=0x2000
+def EPROM_ADDR=0x4000
+def EPROM_SIZE=128
##############
# sections
##############
# vectors start address
+seg .vector -b ROM_ADDR -m ROM_SIZE -k -n .vector
+seg .const -a .vector -n .const # constants follow vectors
+seg .text -a .const -n .text # code follow constants
+seg .FLASH_CODE -a .text -n .FLASH_CODE
# internal eeprom
+seg .eeprom -b EPROM_ADDR -m EPROM_SIZE
# internal ram
+seg .bsct -b RAM_ADDR -m RAM_SIZE -n .bsct
+seg .ubsct -a .bsct -n .ubsct
+seg .bit -a .ubsct -n .bit -id
+seg .data -a .bit -n .data
+seg .bss -a .data -n .bss
###########
# objects
###########
## vectors
$<objs:**/vector.o>
## application files
$<objs:**/*>
## C runtime startup
$<libs:crts*.sm8>
## C libraries
$<libs:**/libfs*.sm8>
$<libs:**/libis*.sm8>
$<libs:**/libm*.sm8>
###########
# symbols
###########
+def __endzp=@.ubsct # end of zero page
+def __memory=@.bss # symbol used by library
+def __stack=SP_VALUE # stack pointer initial value
在这个文件的开头有这样几个定义,这是我们要修改的部分:
+def RAM_SIZE=0x400
:MCU SRAM 的大小,根据实际情况修改
+def SP_VALUE=0x3ff
:栈顶指针的值。STM8的栈是向下生长的,因此 SP 位于 SRAM 的末尾。这里就是 0x3ff
+def ROM_SIZE=0x2000
:FLASH 空间大小,根据实际情况修改
+def EPROM_SIZE=128
:EEPROM 大小(没有用到eeprom的则可以不用管)
中断向量表 vector.c
常见的中断向量表有两种格式:
cosmic 默认的向量表:
/* INTERRUPT VECTORS TABLE FOR STM8S003
* Copyright (c) 2008 by COSMIC Software
*/
extern void _stext(); /* startup routine */
#pragma section const {vector}
typedef void (*vector_func_t)(void);
const vector_func_t @vector _vectab[32] = {
_stext, /* RESET */
0, /* TRAP */
0, /* TLI */
0, /* AWU */
0, /* CLK */
0, /* EXTI0 */
0, /* EXTI1 */
0, /* EXTI2 */
0, /* EXTI3 */
0, /* EXTI4 */
0, 0, /* Reserved */
0, /* SPI */
0, /* TIMER 1 OVF */
0, /* TIMER 1 CAP */
0, /* TIMER 2 OVF */
0, /* TIMER 2 CAP */
0, 0, /* Reserved */
0, /* UART1 TX */
0, /* UART1 RX */
0, /* I2C */
0, 0, /* Reserved */
0, /* ADC1 */
0, /* TIMER 4 OVF */
0, /* EEPROM ECC */
0, 0, 0, 0, 0, /* Reserved */
};
这个默认的向量表是空的,因此需要根据实际的中断使用情况,每用到一个中断,就在将对应位置的 0
替换为中断回调函数的名字。
ST 标准库提供的向量表
/* INTERRUPT VECTORS TABLE FOR STM8S003
* Copyright (c) 2008 by COSMIC Software
*/
#include "stm8s.h"
#pragma section const {vector}
#ifdef _COSMIC_
void _stext(void); /* RESET startup routine */
INTERRUPT void NonHandledInterrupt(void);
#endif /* _COSMIC_ */
#ifndef _RAISONANCE_
INTERRUPT void TRAP_IRQHandler(void); /* TRAP */
INTERRUPT void TLI_IRQHandler(void); /* TLI */
INTERRUPT void AWU_IRQHandler(void); /* AWU */
INTERRUPT void CLK_IRQHandler(void); /* CLOCK */
INTERRUPT void EXTI_PORTA_IRQHandler(void); /* EXTI PORTA */
INTERRUPT void EXTI_PORTB_IRQHandler(void); /* EXTI PORTB */
INTERRUPT void EXTI_PORTC_IRQHandler(void); /* EXTI PORTC */
INTERRUPT void EXTI_PORTD_IRQHandler(void); /* EXTI PORTD */
INTERRUPT void EXTI_PORTE_IRQHandler(void); /* EXTI PORTE */
#ifdef STM8S903
INTERRUPT void EXTI_PORTF_IRQHandler(void); /* EXTI PORTF */
#endif /*STM8S903*/
#if defined (STM8S208) || defined (STM8AF52Ax)
INTERRUPT void CAN_RX_IRQHandler(void); /* CAN RX */
INTERRUPT void CAN_TX_IRQHandler(void); /* CAN TX/ER/SC */
#endif /* STM8S208 || STM8AF52Ax */
INTERRUPT void SPI_IRQHandler(void); /* SPI */
INTERRUPT void TIM1_CAP_COM_IRQHandler(void); /* TIM1 CAP/COM */
INTERRUPT void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void); /* TIM1 UPD/OVF/TRG/BRK */
#ifdef STM8S903
INTERRUPT void TIM5_UPD_OVF_BRK_TRG_IRQHandler(void); /* TIM5 UPD/OVF/BRK/TRG */
INTERRUPT void TIM5_CAP_COM_IRQHandler(void); /* TIM5 CAP/COM */
#else /*STM8S208, STM8S207, STM8S105 or STM8S103 or STM8AF52Ax or STM8AF62Ax or STM8A626x*/
INTERRUPT void TIM2_UPD_OVF_BRK_IRQHandler(void); /* TIM2 UPD/OVF/BRK */
INTERRUPT void TIM2_CAP_COM_IRQHandler(void); /* TIM2 CAP/COM */
#endif /*STM8S903*/
#if defined (STM8S208) || defined(STM8S207) || defined(STM8S007) || defined(STM8S105) || \
defined(STM8S005) || defined (STM8AF52Ax) || defined (STM8AF62Ax) || defined (STM8AF626x)
INTERRUPT void TIM3_UPD_OVF_BRK_IRQHandler(void); /* TIM3 UPD/OVF/BRK */
INTERRUPT void TIM3_CAP_COM_IRQHandler(void); /* TIM3 CAP/COM */
#endif /*STM8S208, STM8S207 or STM8S105 or STM8AF52Ax or STM8AF62Ax or STM8A626x */
#if defined (STM8S208) || defined(STM8S207) || defined(STM8S007) || defined(STM8S103) || \
defined(STM8S003) || defined (STM8AF52Ax) || defined (STM8AF62Ax) || defined (STM8S903)
INTERRUPT void UART1_TX_IRQHandler(void); /* UART1 TX */
INTERRUPT void UART1_RX_IRQHandler(void); /* UART1 RX */
#endif /*STM8S208, STM8S207, STM8S903 or STM8S103 or STM8AF52Ax or STM8AF62Ax */
INTERRUPT void I2C_IRQHandler(void); /* I2C */
#if defined(STM8S105) || defined(STM8S005) || defined (STM8AF626x)
INTERRUPT void UART2_RX_IRQHandler(void); /* UART2 RX */
INTERRUPT void UART2_TX_IRQHandler(void); /* UART2 TX */
#endif /* STM8S105 or STM8AF626x */
#if defined(STM8S207) || defined(STM8S007) || defined(STM8S208) || defined (STM8AF52Ax) || defined (STM8AF62Ax)
INTERRUPT void UART3_RX_IRQHandler(void); /* UART3 RX */
INTERRUPT void UART3_TX_IRQHandler(void); /* UART3 TX */
#endif /*STM8S207, STM8S208, STM8AF62Ax or STM8AF52Ax */
#if defined(STM8S207) || defined(STM8S007) || defined(STM8S208) || defined (STM8AF52Ax) || defined (STM8AF62Ax)
INTERRUPT void ADC2_IRQHandler(void); /* ADC2 */
#else /*STM8S105, STM8S103 or STM8S903*/
INTERRUPT void ADC1_IRQHandler(void); /* ADC1 */
#endif /*STM8S207, STM8S208, STM8AF62Ax or STM8AF52Ax */
#ifdef STM8S903
INTERRUPT void TIM6_UPD_OVF_TRG_IRQHandler(void); /* TIM6 UPD/OVF/TRG */
#else /*STM8S208, STM8S207, STM8S105 or STM8S103 or STM8AF62Ax or STM8AF52Ax or STM8AF626x */
INTERRUPT void TIM4_UPD_OVF_IRQHandler(void); /* TIM4 UPD/OVF */
#endif /*STM8S903*/
INTERRUPT void EEPROM_EEC_IRQHandler(void); /* EEPROM ECC CORRECTION */
#endif /* _RAISONANCE_ */
typedef void @far (*interrupt_handler_t)(void);
struct interrupt_vector {
u8 interrupt_instruction;
interrupt_handler_t interrupt_handler;
};
const struct interrupt_vector _vectab[] = {
{0x82, (interrupt_handler_t)_stext}, /* RESET */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* TRAP - Software interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq0 - External Top Level interrupt (TLI) */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq1 - Auto Wake Up from Halt interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq2 - Clock Controller interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq3 - External interrupt 0 (GPIOA) */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq4 - External interrupt 1 (GPIOB) */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq5 - External interrupt 2 (GPIOC) */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq6 - External interrupt 3 (GPIOD) */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq7 - External interrupt 4 (GPIOE) */
#if defined(STM8S208) || defined(STM8AF52Ax)
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq8 - CAN Rx interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq9 - CAN Tx/ER/SC interrupt */
#elif defined(STM8S903)
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq8 - External interrupt 5 (GPIOF) */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq9 - Reserved */
#else /*STM8S207, STM8S105 or STM8AF62Ax or STM8AF626x*/
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq8 - Reserved */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq9 - Reserved */
#endif /* STM8S208 or STM8AF52Ax */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq10 - SPI End of transfer interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq11 - TIM1 Update/Overflow/Trigger/Break interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq12 - TIM1 Capture/Compare interrupt */
#ifdef STM8S903
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq13 - TIM5 Update/Overflow/Break/Trigger interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq14 - TIM5 Capture/Compare interrupt */
#else /*STM8S208, STM8S207, STM8S105 or STM8S103 or STM8AF62Ax or STM8AF52Ax or STM8AF626x*/
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq13 - TIM2 Update/Overflow/Break interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq14 - TIM2 Capture/Compare interrupt */
#endif /*STM8S903*/
#if defined(STM8S208) || defined(STM8S207) || defined(STM8S007) || defined(STM8S105) || \
defined(STM8S005) || defined(STM8AF52Ax) || defined(STM8AF62Ax) || defined(STM8AF626x)
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq15 - TIM3 Update/Overflow/Break interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq16 - TIM3 Capture/Compare interrupt */
#else
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq15 - Reserved */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq16 - Reserved */
#endif /*STM8S208, STM8S207, STM8S105 or STM8AF62Ax or STM8AF52Ax or STM8AF626x*/
#if defined(STM8S105) || defined(STM8S005) || defined(STM8AF626x)
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq17 - Reserved */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq18 - Reserved */
#else
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq17 - UART1 Tx complete interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq18 - UART1 Rx interrupt */
#endif /*STM8S105 or STM8AF626x */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq19 - I2C interrupt */
#if defined(STM8S208) || defined(STM8S207) || defined(STM8S007) || defined(STM8AF52Ax) || defined(STM8AF62Ax)
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq20 - UART3 Tx interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq21 - UART3 Rx interrupt */
#elif defined(STM8S105) || defined(STM8S005) || defined(STM8AF626x)
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq20 - UART2 Tx interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq21 - UART2 Rx interrupt */
#else /* STM8S103, STM8S903 */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq20 - Reserved */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq21 - Reserved */
#endif /* STM8S208, STM8S207, STM8AF52Ax or STM8AF62Ax */
#if defined(STM8S208) || defined(STM8S207) || defined(STM8S007) || defined(STM8AF52Ax) || defined(STM8AF62Ax)
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq22 - ADC2 end of conversion interrupt */
#else /* STM8S105, STM8S103, STM8S903 */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq22 - ADC1 end of conversion/Analog watchdog interrupts */
#endif /* STM8S208, STM8S207, STM8AF52Ax or STM8AF62Ax */
#ifdef STM8S903
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq23 - TIM6 Update/Overflow/Trigger interrupt */
#else
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq23 - TIM4 Update/Overflow interrupt */
#endif /*STM8S903*/
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq24 - FLASH interrupt */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq25 - Reserved */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq26 - Reserved */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq27 - Reserved */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq28 - Reserved */
{0x82, (interrupt_handler_t)NonHandledInterrupt}, /* irq29 - Reserved */
};
#pragma section (text)
INTERRUPT_HANDLER(NonHandledInterrupt)
{
while (1)
{
nop();
}
}
这两种格式的区别是 ST 的向量表中的数组,每个项中多了一个操作码 0x82
,这是因为它没有使用 @vector
关键字因此需要手动添加 0x82 到数组中去。
编译器实现了特殊的修饰符 @vector,以便允许在C中直接声明中断向量表。
每个条目必须声明为函数指针,并用函数名初始化。
编译器将产生一个包含特殊操作码 0x82 的4字节条目,而不是地址常量,后面跟着要到达的中断函数的24位地址。
因此,因此在修改向量表的时候需要注意二者的区别。(一个要用 @vector
修饰。一个不用)
中断向量表必须放置到 FLASH 的最开头部分,因此需要为 vector.c 文件分配一个段,然后将这个段的地址设为flash 起始地址。
由于前面 链接脚本 stm8.lkf 中已经定义了 +seg .vector -b ROM_ADDR -m ROM_SIZE -k -n .vector
,
这句话表示在 flash 的开头创建一个段,名字为 .vector
因此我们在 vector.c 中需要在文件的开头添加这样一句话:
#pragma section const {vector}
这句话表示将本源文件中的 const 变量放入 .vector 段中,这样结合链接脚本中的定义,我们就把中断向量数组放置到了 flash 的开头。
内存模型选择(Memory Models)
插件配置项:构建器选项 -> 全局选项 -> Memory Models
在 cosmic 中有一系列关键字:@tiny
@near
@far
用于描述符号所属的 地址区域
上表中的内容表示了默认情况下编译器为变量分配的 地址区域,选择不同的 内存模型,编译器给变量分配的默认 地址区域 是不一样的,不同的区域内,编译器会对访存指令进行不同的优化,同时生成的代码大小也不一样。
内存模型分为 小于64K
和 大于64K
两种。这个 64K 是指寻址范围,多数情况下都是小于 64KB 的
一般情况下我们的选择策略:
- 如果你的程序使用的变量很多(总尺寸已超过256Bytes),就选:
Stack Long Model(Code < 64KB)
- 如果你的程序使用的变量较少,就选:
Stack Short Model(Code < 64KB)
代码尺寸小于 64K 时的内存模型(Code < 64KB)
STM8编译器为小于64K的应用程序支持两种内存模型,允许您根据处理器配置和应用程序选择最有效的行为。
所有这些模型处理的代码都小于64K,
然后函数指针默认为 @near
指针(2字节)。数据指针默认为 @near
指针(2字节)。
支持的模型有:
Stack Short Model(Code < 64KB):全局变量默认为 Short Range. (默认@tiny
)
任何 Long Range 的全局对象都必须用 @near
修饰符显式访问,除非通过指针访问。
Stack Long Model(Code < 64KB):全局变量默认为 Long Range. (默认@near
)
任何 Short Range 对象都必须使用 @tiny
修饰符显式访问。
对于给定的应用程序,应该根据全局变量的数量与内存中的可用空间的比较来选择合适的模型。
Short Range 变量比 Long Range 变量更有效,但 Short Range 的总大小限制为256字节。
代码尺寸大于 64K 时的内存模型
对于大于64K的应用程序,STM8编译器支持两种内存模型,允许您根据处理器配置和应用程序选择最有效的行为。
所有这些模型都允许代码大于64K,然后函数指针默认为@far指针(3字节)。
数据指针默认为 @near
指针(2字节),除非用 @far
修饰符显式声明。
支持的模型有:
对于给定的应用程序,应该根据全局变量的数量与内存中的可用空间的比较来选择合适的模型。
Short Range 变量比 Long Range 变量更有效,但 Short Range 的总大小限制为256字节。
运行时库
插件可以自动根据你的编译选项帮你选择合适的 C 库并链接
但是如果你使用的 链接脚本文件 不是从 eide 项目模板中的 stm8.lkf 修改而来的。
那么你需要在你的 lkf 文件中添加如下片段:
## vectors
$<objs:**/vector.o>
## application files
$<objs:**/*>
## C runtime startup
$<libs:crts*.sm8>
## C libraries
$<libs:**/libfs*.sm8>
$<libs:**/libis*.sm8>
$<libs:**/libm*.sm8>
在线调试
注意:构建器配置中需要选择 输出调试信息,否则编译后生成的 elf 是无法调试的
使用 stlink,可以使用 stm8-debug 插件进行调试
如果使用 stm8s003,则调试配置如下:
{
"type": "stm8-debug",
"request": "launch",
"serverType": "stm8-sdcc",
"name": "Debug (COSMIC)",
"executable": "build/Debug/Test.elf",
"runToMain": true,
"openOcdConfigs": [
"interface/stlink.cfg",
"target/stm8s003.cfg"
]
}
如果是其他型号的,需要自行提供 openOcdConfigs 中的 target 部分的 cfg 文件,内置的可用配置文件:
配置完成后,打开 调试控制台 面板,按 F5 启动调试,查看调试日志。
如果需要暂时为某个文件禁用优化,可以设置 -no
参数
示例工程