本帖最后由 cruelfox 于 2017-12-30 23:32 编辑
我购买STM8S Discovery开发板的时间比买STM32开发板还早,但反而用了几年STM32后也没用起来STM8,偶尔用到的8位MCU还是AVR. 其中一个原因是我工惯了GCC,stm32, AVR都有gcc编译器工具链的支持,但STM8没有,我就没有花时间去折腾编译工具了。
ST官方提供了STM8的开发工具叫STVD,"Visual Develop"的缩写,从ST网站上可以下到。链接进去,下载的安装包其实是 st_toolset, 它支持STM8,还有更老的ST7,安装后其实还包含了ST Visual Programmer软件等。STVD的界面很像老的MS Visual Studio 6.0,它只是一个工程管理器、编辑器加调试器,并不包含汇编、编译工具。st_toolset安装后带了一套汇编工具,在 st_toolset\asm 目录下面,也包含了MCU寄存器描述使用的汇编头文件(.inc, .asm)
很难再纯用汇编来开发MCU程序了,st_toolset\include 下面提供了给C编译器用的 .h 头文件,但是没有编译器。在STVD上面新建工程的时候,可以选择 COSMIC 或者 Raisonace 的C编译器,需要指定第三方工具的路径;STVD会生成一个什么也不做的C程序模板。配置好后STVD会去调用这两家的工具来完成编译和链接。下载和调试则是STVD自己内带的功能。
本人的喜好还是用命令行工具,所以抛开STVD这个图形界面,直接找COSMIC的工具来用吧。从COSMIC的网站上可以下载到32k代码限制的免费版编译器(申请一个免费license)。IAR好象也提供有限制的免费编译器EWSTM8,我没有试过。除此之外就是著名的开源软件 SDCC 了。
好,如何从 C 代码得到最终下载到单片机的 HEX/BIN 文件呢?除了编译工具程序,还需要哪些支持的资源?
(A)
寄存器描述的文件,一般是 .h 形式。这个需要由单片机厂商提供,也就是给每个寄存器的地址起一个名字,在C语言里面能直接操作。不过定义方式又跟编译器有一定关系,因为它不是普通的内存地址。
(B)
中断向量表的定义,也是需要单片机厂商提供,不同型号不一样。它也需要结合编译器特性来书写。
(C)
初始化代码,也就是从RESET中断入口开始执行的程序。一般是一段汇编代码。
(D) C运行代码
库,包括标准库函数的头文件、编译好的库函数代码,以及软件开发时没有明写但会隐含地调用的库函数代码。这个通常是C编译工具一起提供的,也不一定全部由编译器厂商提供,例如GCC和newlib是两部分的软件。
(E)
地址空间的说明,ROM, RAM的大小和位置。
下面以我写的一个 LED 数码管显示循环计数的简单程序为例子. C源程序是这样的:
- #include "stm8s003f3.h"
- void myDelay(unsigned int value)
- {
- unsigned int i,j;
- for(i=0;i<40;i++)
- {
- for(j=0;j<value;j++);
- }
- }
- void GPIO_Config(void)
- {
- PA_DDR = 1<<3|1<<2|1<<1; // LED COMx ctrl
- PA_CR1 = 1<<3|1<<2|1<<1; // LED COMx ctrl
- PA_ODR = 1<<3|1<<2|1<<1; // initial High
- PC_DDR = 0xf0; // PC4..7 LED seg (open drain)
- PD_DDR = 1<<6|1<<5|1<<4|1<<3; // PD3..6 LED seg (open drain)
- }
- void translate(short d, char *dig)
- {
- const char table[10]={24, 95, 104, 73, 15, 137, 136, 93, 8, 9};
- const char segdp=247;
- d%=1000;
- dig[0]=table[d/100];
- d%=100;
- dig[1]=table[d/10];
- d%=10;
- dig[2]=table[d];
- }
- int main(void)
- {
- char dig[3];
- GPIO_Config();
- while(1)
- {
- int i;
- static unsigned short count=0;
- translate(count, dig);
- count++;
- if(count>999)
- count=0;
- for(i=0;i<100;i++)
- {
- PC_ODR = dig[0]&0xf0;
- PD_ODR = dig[0]<<3;
- PA_ODR = 1<<3|1<<2;
- myDelay(10);
- PC_ODR = dig[1]&0xf0;
- PD_ODR = dig[1]<<3;
- PA_ODR = 1<<3|1<<1;
- myDelay(10);
- PC_ODR = dig[2]&0xf0;
- PD_ODR = dig[2]<<3;
- PA_ODR = 1<<2|1<<1;
- myDelay(10);
- }
- }
- }
复制代码
程序里面用到 STM8 的寄存器只有 GPIO 控制的几个寄存器,它们的定义包含在 stm8s003f3.h 这个头文件中。前面提过,这个头文件在 st_toolset\include 目录下。弄到之后就可以编译这个文件(count.c)得到目标文件(count.obj)了。 COSMIC C 编译器的编译工具叫做 cxstm8, 它的命令行参数说明在COSMIC的Docs目录下用户手册里面。我用如下命令来执行编译:
cxstm8 +mods0 -i../st_toolset/include count.c
其中 +mods0 是指定Memory model(mods0 对我这个小程序使用合适),-i参数是头文件搜索路径。
接下来需要链接操作,COSMIC的链接工具叫做 clnk, 它和常见的linker用法有所不同,需要在命令行上指定一个 .lkf 文件,而不是指定刚才编译得到的 .o 文件。起初我很不习惯,思索过觉得这样也和用GNU ld需要指定一个脚本类似的。这个 .lkf 文件怎么写,找个例子参考再改改就可以了。在用STVD编译的时候,它会对应Debug和Release版本各生成一个.lkf文件,其实在 st_toolset 目录下搜索可以找到几个范本。
我改写的一个 test.lkf 文件内容是这样:
# LINK COMMAND FILE FOR TEST PROGRAM
+seg .text -b 0x8080 -n .text # program start address
+seg .const -a .text -n .const # constants follow vector
+seg .bsct -b 0x0 -m 0x100 # zero page start address
+seg .data -b 0x100 -n .data
+seg .bss -a .data -n .bss
crts0.sm8 # startup routine
count.o # application program
libis0.sm8
libm0.sm8
+seg .const -b 0x8000 -m 0x80 # vectors start address
stm8_interrupt_vector.o # interrupt vectors
# define these symbols if crtsi is used
+def __endzp=@.ubsct # end of uninitialized zpage
+def __memory=@.bss # end of bss segment
+def __stack=0x1ff
上面 count.o, stm8_interrupt_vector.o 是C文件编译得到的,另外几个 crts0.sm8, libis0.sm8, libm0.sm8 都是COSMIC提供的初始化代码及函数库。 代码段、数据段的地址安排在这个文件中定义。中断向量表由 stm8_interrupt_vector.c 文件编译产生,它在 st_toolset\stvd\builder 目录下面,内容如下
- /* BASIC INTERRUPT VECTOR TABLE FOR STM8 devices
- * Copyright (c) 2007 STMicroelectronics
- */
- typedef void @far (*interrupt_handler_t)(void);
- struct interrupt_vector {
- unsigned char interrupt_instruction;
- interrupt_handler_t interrupt_handler;
- };
- @far @interrupt void NonHandledInterrupt (void)
- {
- /* in order to detect unexpected events during development,
- it is recommended to set a breakpoint on the following instruction
- */
- return;
- }
- extern void _stext(); /* startup routine */
- struct interrupt_vector const _vectab[] = {
- {0x82, (interrupt_handler_t)_stext}, /* reset */
- {0x82, NonHandledInterrupt}, /* trap */
- {0x82, NonHandledInterrupt}, /* irq0 */
- {0x82, NonHandledInterrupt}, /* irq1 */
- {0x82, NonHandledInterrupt}, /* irq2 */
- {0x82, NonHandledInterrupt}, /* irq3 */
- {0x82, NonHandledInterrupt}, /* irq4 */
- {0x82, NonHandledInterrupt}, /* irq5 */
- {0x82, NonHandledInterrupt}, /* irq6 */
- {0x82, NonHandledInterrupt}, /* irq7 */
- {0x82, NonHandledInterrupt}, /* irq8 */
- {0x82, NonHandledInterrupt}, /* irq9 */
- {0x82, NonHandledInterrupt}, /* irq10 */
- {0x82, NonHandledInterrupt}, /* irq11 */
- {0x82, NonHandledInterrupt}, /* irq12 */
- {0x82, NonHandledInterrupt}, /* irq13 */
- {0x82, NonHandledInterrupt}, /* irq14 */
- {0x82, NonHandledInterrupt}, /* irq15 */
- {0x82, NonHandledInterrupt}, /* irq16 */
- {0x82, NonHandledInterrupt}, /* irq17 */
- {0x82, NonHandledInterrupt}, /* irq18 */
- {0x82, NonHandledInterrupt}, /* irq19 */
- {0x82, NonHandledInterrupt}, /* irq20 */
- {0x82, NonHandledInterrupt}, /* irq21 */
- {0x82, NonHandledInterrupt}, /* irq22 */
- {0x82, NonHandledInterrupt}, /* irq23 */
- {0x82, NonHandledInterrupt}, /* irq24 */
- {0x82, NonHandledInterrupt}, /* irq25 */
- {0x82, NonHandledInterrupt}, /* irq26 */
- {0x82, NonHandledInterrupt}, /* irq27 */
- {0x82, NonHandledInterrupt}, /* irq28 */
- {0x82, NonHandledInterrupt}, /* irq29 */
- };
复制代码
因为我这个例子程序不用到中断,所以只处理 RESET入口就够了,这是用默认的。
链接命令执行:
clink -o count.sm8 test.lkf
就得到整个程序机器代码文件了。不过,还要转换下格式,才可以给烧写工具用。生成HEX文件执行:
chex -fi -o count.hex count.sm8
这样得到了STVP可以用的 count.hex 文件。
对于简单的程序,可以把 cxstm8, clink, chex 这几步命令写成批一个处理文件。复杂一点可以写一个Makefile,然后用make命令来处理编译过程。
汇总一下,开发 STM8 需要的(免费)软件:
1. COSMIC C STM8 编译器
2. st toolset 这个开发工具集合,但只用到其中一部分东西
(1) st_toolset\include 下面的STM8型号相关头文件
(2) st_toolset\stvd\Example 里面能找到的 stm8_interrupt_vector.c 文件
(3) st_toolset\stvd\Example 里面能找到的 .lkf 文件
(4) STVP 烧写工具,有GUI和命令行版本
其中2 部分(1)(2)(3)也不是唯一的,比如可以下载 STM8 Standard Periphral Library 软件包,能找到功能相似,支持更丰富的代码。
此内容由EEWORLD论坛网友cruelfox原创,如需转载或用于商业用途需征得作者同意并注明出处