1、STM8S 与汇编第 1 页 共 51 页STM8 与汇编语言(1)不知是心血来潮,还是其它因素,突然又想起玩汇编语言了。这几年也没少跟单片机打交道,包括 51 系列,430 系列,ARM 系列,但都是用 C 语言来开发。不过由于使用 C语言,实际上对这些 CPU 的了解还是不够深刻,当然除了 51 之外,因为那是我多年前曾经用汇编开发过的芯片。尽管当今 C 语言已经在嵌入式产品的开发过程中成为主流,但我个人依然认为,要想真正了解 CPU 的特点,还得用汇编语言。不知道这种观点是对还是错,也许是因为自己从硬件做起,写过机器码,用汇编语言做过优化,因此对汇编语音有一种特殊的偏爱。51 系列的芯片
2、用多了,感觉有时写起程序来不太方便,因此总想寻找一些其它的 8 位单片机玩玩,正好手头有一个 ST 的三合一开发板,那是 09 年参加 ST 研讨会上买的,一直躺在那里,与其躺在那里,不如拿出来玩玩。这几年,ST 在国内推广 STM32,力度不小,不过我一直没有用过,只是初步地看看资料。原因在于在 32 位单片机方面,我一直在用 Luminary 公司的 LM3S1138,感觉不错,一直都很顺利。09 年 ST 举办的研讨会上, ST 除了介绍 STM32 外,也介绍了 STM8,当时听了以后,觉得还行。尤其是会上的低功耗演示给我留下了很深刻的印象。基于这些,我决定好好地玩一下 STM8 芯片
3、,并将玩的结果拿出来与大家共享。STM8 与汇编语言(2)第一次打开 STM8 的手册时发现,CPU 中的寄存器只有 6 个,即 A、X、Y、SP、PC和 CC。这几个寄存器,看上去特象早年苹果机使用的微处理器 6502。在眼下都是多寄存器的 RISC 潮流下,不知 ST 推出的这种 CPU 架构有什么意图?这样的芯片能否与Microchip 或者 Atmel 的 RISC 结构的 MCU 竞争呢?在此我无意做评论,我只想了解这颗芯片。通过仔细研究,我发现由于 STM8 采用了 32 位宽度的程序存储器结构,使得大部分的指令都能在一个周期内取出,并且采用了哈佛结构和流水线,相当多的指令也都是单
4、周期完成的。这样的话,虽然 CPU 是 CISC 架构的,但也基本上达到了单周期指令的效果,就像手册上说的,CPU 的性能达到了 20MISP24MHZ。就这一点来说,我个人感觉STM8 还真不错。举个例子来说,如果我们要完成内存中的 2 个 8 位无符号数相加,结果还保存到内存中,用 C 语言描述成:unsigned char a,b,c;c = a + b;这一段程序,用 STM8 汇编可以写成如下代码:LD A, $1000 ADD A, $1001 LD $1002, A这里假设 a,b,c 这 3 个变量分别存储在内存中,地址为 1000,1001 和 1002。从 STM8 的手册
5、上可以查到,这 3 条指令都是单周期的,完成一个加法,只需要 3 个时钟周期,可见STM8 的 CPU 执行速度还是相当快的。在这种传统的所谓 CISC 架构中,我特别关心累加器 A 与内存的访问速度,因为如果累加器与内存的访问速度是单周期的话,实际上我们就可以将内存当寄存器来看,这样写程序时就相当于拥有了一个大的寄存器阵列,或者说我们也就没必要再去考虑变量在内存STM8S 与汇编第 2 页 共 51 页中还是在寄存器中。也正是因为这一点,我对 STM8 越来越感兴趣了。STM8S 与汇编第 3 页 共 51 页STM8 与汇编语言(3)STM8 的开发环境用起来还是不错的,可以到 ST 的网
6、站上下载安装程序ST_Toolset.exe。利用该环境可以开发用汇编语言写的程序,而且与 ST 的三合一开发板配合起来,确实非常方便。不过如果要想用 C 语言来开发,稍微有点麻烦,得去别的公司下载 C 的编译器(CXSTM8_16K.exe) ,而且下载完以后,还得去注册,等待许可文件。实际上,我也按照ST 介绍的方法做了,但始终都没有收到许可文件,也许本人实在愚笨。但不管怎么说,我觉得 ST 这一点做得相当不好,实在有点抠门。既然是免费的,为什么不一起打包提供给客户,这么麻烦,多耽误客户使用,得少卖多少 STM8 的芯片。言归正传,还回到正题。用汇编语言开发程序,最简单的就是利用 ST 开
7、发环境中提供的汇编程序框架自动生成功能。打开开发环境后,在 File 菜单中选择 New Workspace,点击 Create workspace and project 图标,然后就可以建立项目,在工具链中选 ST Assembler Linker,最后选择 MCU 的型号,点击 OK,就完成了一个项目的建立。这个环境与微软的VC6 开发环境很象,点开项目文件中的 Source Files,能看到系统自动生成好了一个汇编语言的框架,我们编写程序只要在这框架基础上就可以了。其实不用编写任何一条指令,这个框架程序是能够编译通过,并下载运行的。自动生成的项目中包含 3 个重要的文件:mappin
8、g.inc,mapping.asm 和 main.asm。mapping.inc 文件中定义的是一些常量,mapping.asm 文件中定义的是一些内存的分配,主要的汇编代码都在 main.asm。下面是 main.asm 中的汇编代码及注释。stm8/#include “mapping.inc“segment rom; 下面是定义一个标号,ST 汇编的写法,有点不习惯; 这里的 main 标号是复位后的第一条指令,与后面的中断向量表中; 的名字是对应的main.l ; initialize SPldw X,#stack_endldw SP,X ; 设置堆栈指针 #ifdef RAM0; 如果
9、定义了 RAM0,则要汇编以下代码 ; clear RAM0ram0_start.b EQU $ram0_segment_startram0_end.b EQU $ram0_segment_endldw X,#ram0_start ;寄存器 X 指向要清除的内存起始地址 clear_ram0.l ;这是一个标号定义,用于后面的跳转指令clr (X) ;对应的内存单元清 0 STM8S 与汇编第 4 页 共 51 页incw X ;寄存器 X+1,指向下一个单元 cpw X,#ram0_end ;比较寄存器 X 是否等于内存的最后一个地址 jrule clear_ram0 ;若不等于,则循环 #
10、endif#ifdef RAM1; 如果定义了 RAM1,则要汇编以下代码,代码含义与上面完全一样 ; clear RAM1ram1_start.w EQU $ram1_segment_startram1_end.w EQU $ram1_segment_endldw X,#ram1_startclear_ram1.lclr (X)incw Xcpw X,#ram1_endjrule clear_ram1#endif; clear stack; 将堆栈区的内存单元清 0,代码含义与上面完全一样 stack_start.w EQU $stack_segment_startstack_end.w E
11、QU $stack_segment_endldw X,#stack_startclear_stack.lclr (X)incw Xcpw X,#stack_endjrule clear_stackinfinite_loop.l ; 定义一个标号;由于是一个框架,初始化内存后,进入一个死循环jra infinite_loop ;下面代码写的是一段中断服务程序,不过也是空的interrupt NonHandledInterruptNonHandledInterrupt.l; 当进入中断服务程序后,无其它动作,直接返回iret ; 下面这张表很重要,定义了 STM8 所有的硬件中断对应的中断; 服务
12、程序的入口地址STM8S 与汇编第 5 页 共 51 页segment vectitdc.l $82000000+main ; resetdc.l $82000000+NonHandledInterrupt ; trapdc.l $82000000+NonHandledInterrupt ; irq0dc.l $82000000+NonHandledInterrupt ; irq1dc.l $82000000+NonHandledInterrupt ; irq2dc.l $82000000+NonHandledInterrupt ; irq3dc.l $82000000+NonHandledI
13、nterrupt ; irq4dc.l $82000000+NonHandledInterrupt ; irq5dc.l $82000000+NonHandledInterrupt ; irq6dc.l $82000000+NonHandledInterrupt ; irq7dc.l $82000000+NonHandledInterrupt ; irq8dc.l $82000000+NonHandledInterrupt ; irq9dc.l $82000000+NonHandledInterrupt ; irq10dc.l $82000000+NonHandledInterrupt ; i
14、rq11dc.l $82000000+NonHandledInterrupt ; irq12dc.l $82000000+NonHandledInterrupt ; irq13dc.l $82000000+NonHandledInterrupt ; irq14dc.l $82000000+NonHandledInterrupt ; irq15dc.l $82000000+NonHandledInterrupt ; irq16dc.l $82000000+NonHandledInterrupt ; irq17dc.l $82000000+NonHandledInterrupt ; irq18dc
15、.l $82000000+NonHandledInterrupt ; irq19dc.l $82000000+NonHandledInterrupt ; irq20dc.l $82000000+NonHandledInterrupt ; irq21dc.l $82000000+NonHandledInterrupt ; irq22dc.l $82000000+NonHandledInterrupt ; irq23dc.l $82000000+NonHandledInterrupt ; irq24dc.l $82000000+NonHandledInterrupt ; irq25dc.l $82
16、000000+NonHandledInterrupt ; irq26dc.l $82000000+NonHandledInterrupt ; irq27dc.l $82000000+NonHandledInterrupt ; irq28dc.l $82000000+NonHandledInterrupt ; irq29end把这个项目 Build 后,点击 Debug 中的 Start Debugging 就可以将程序下载到 ST 的三合一板上了,然后点击 Run,程序就运行起来了,不过由于框架程序是一个空程序,初始化内存后就进入死循环了,因此什么效果也看不见。因此我们必须在框架程序的基础上,
17、编写自己的程序。后面的程序例子都是在这个框架程序的基础上编写的。STM8S 与汇编第 6 页 共 51 页STM8 与汇编语言(4)今天要做的实验是在 ST 的三合一开发板上,用汇编语言写一个程序,驱动板上的LED 指示灯闪烁。开发板上的 LED1 接在 STM8 的 PD3 上,因此要将 PD3 设置成输出模式,为了提高高电平时的输出电流,要将其设置成推挽输出方式。这主要通过设置对应的DDR/CR1/CR2 寄存器实现。还是利用 ST 的开发工具,先生成一个汇编程序的框架,然后修改其中的 main.asm,修改后的代码如下。编译通过后,下载到开发板,运行程序,可以看到 LED1 在闪烁,且闪
18、烁的频率为5HZ。stm8/#include “mapping.inc“; 下面定义端口 D 的寄存器地址PD_ODR EQU $500f PD_IDR EQU $5010PD_DDR EQU $5011 PD_CR1 EQU $5012PD_CR2 EQU $5013; 定义堆栈空间的起始位置和结束位置stack_start.w EQU $stack_segment_startstack_end.w EQU $stack_segment_end; 下面开始定义一个段,该段位于 ROM 中segment rom ; 定义复位后的第一条指令的标号(即入口地址)main.l ; 首先要初始化堆栈指
19、针LDW X,#stack_end LDW SP,XLD A,#08LD PD_DDR,A ; 将 PD3 设置成输出LD A,#08STM8S 与汇编第 7 页 共 51 页LD PD_CR1,A ; 将 PD3 设置成推挽输出LD A,#00LD PD_CR2,A ;MAIN_LOOP.LLD A,#08 ; LD PD_ODR,A ; 将 PD3 的输出设置成 1LD A,#100CALL DELAY_MS ; 延时 100MSLD A,#00 ; LD PD_ODR,A ; 将 PD3 的输出设置成 1LD A,#100CALL DELAY_MS ; 延时 100MSJRA MAIN_
20、LOOP ; ; 函数功能:延时; 输入参数:寄存器 A 要延时的毫秒数,这里假设 CPU 的主频为 2MHZ; 输出参数:无; 返 回 值:无; 备 注:无DELAY_MS.LPUSH A ; 将入口参数保存到堆栈中LD A,#250 ; 寄存器 A-250,作为下面的循环数 DELAY_MS_1.LNOP ; 用空操作指令进行延时 4TNOPNOPNOPNOPDEC A ; 寄存器 A-A-1,本条指令执行之间为 1TJRNE DELAY_MS_1 ; 若不等于 0,则循环,; 本条指令执行时间为 2T(跳时)或 1T(不跳时)POP A ; 从堆栈中恢复入口参数DEC A ; 将要延时的
21、 MS 数1 JRNE DELAY_MS ; 若不等于 0,则循环RET ; 函数返回 interrupt NonHandledInterruptNonHandledInterrupt.lSTM8S 与汇编第 8 页 共 51 页iret; 下面定义中断向量表 segment vectitdc.l $82000000+main ; resetdc.l $82000000+NonHandledInterrupt ; trapdc.l $82000000+NonHandledInterrupt ; irq0dc.l $82000000+NonHandledInterrupt ; irq1dc.l
22、$82000000+NonHandledInterrupt ; irq2dc.l $82000000+NonHandledInterrupt ; irq3dc.l $82000000+NonHandledInterrupt ; irq4dc.l $82000000+NonHandledInterrupt ; irq5dc.l $82000000+NonHandledInterrupt ; irq6dc.l $82000000+NonHandledInterrupt ; irq7dc.l $82000000+NonHandledInterrupt ; irq8dc.l $82000000+Non
23、HandledInterrupt ; irq9dc.l $82000000+NonHandledInterrupt ; irq10dc.l $82000000+NonHandledInterrupt ; irq11dc.l $82000000+NonHandledInterrupt ; irq12dc.l $82000000+NonHandledInterrupt ; irq13dc.l $82000000+NonHandledInterrupt ; irq14dc.l $82000000+NonHandledInterrupt ; irq15dc.l $82000000+NonHandled
24、Interrupt ; irq16dc.l $82000000+NonHandledInterrupt ; irq17dc.l $82000000+NonHandledInterrupt ; irq18dc.l $82000000+NonHandledInterrupt ; irq19dc.l $82000000+NonHandledInterrupt ; irq20dc.l $82000000+NonHandledInterrupt ; irq21dc.l $82000000+NonHandledInterrupt ; irq22dc.l $82000000+NonHandledInterr
25、upt ; irq23dc.l $82000000+NonHandledInterrupt ; irq24dc.l $82000000+NonHandledInterrupt ; irq25dc.l $82000000+NonHandledInterrupt ; irq26dc.l $82000000+NonHandledInterrupt ; irq27dc.l $82000000+NonHandledInterrupt ; irq28dc.l $82000000+NonHandledInterrupt ; irq29endSTM8S 与汇编第 9 页 共 51 页STM8 与汇编语言(5)
26、上一次的实验程序,完成了 LED 指示灯的驱动,用到了 GPIO 的输出方式,这一次要用 GPIO 的输入方式,进行按键的输入。下面的代码是读入按键值,如果按键按下,则点亮 LED,否则熄灭 LED。利用 ST 的开发工具,生成一个汇编程序的框架,然后修改其中的 main.asm,修改后的代码如下。编译通过后,下载到开发板,运行程序,可以看到当按下按键时,LED1 点亮,当抬起按键时,LED1 熄灭。stm8/#include “mapping.inc“; 涉及到的硬件资源; LED1 定义在 PD3; KEY1 定义在 PD7; 下面定义端口 D 的寄存器地址PD_ODR EQU $500f
27、 PD_IDR EQU $5010PD_DDR EQU $5011 PD_CR1 EQU $5012PD_CR2 EQU $5013; 定义堆栈空间的起始位置和结束位置stack_start.w EQU $stack_segment_startstack_end.w EQU $stack_segment_endsegment rom ; 下面开始定义一个段,该段位于 ROM 中main.l ; 定义复位后的第一条指令的标号(即入口地址); 首先要初始化堆栈指针LDW X,#stack_end LDW SP,X; 下面初始化 IO 端口; PD3 设置成推挽输出; PD7 设置成悬浮输入LD A
28、,#08LD PD_DDR,A ; 将 PD3 设置成输出,PD7 设置成输入LD A,#08LD PD_CR1,A ; 将 PD3 设置成推挽输出LD A,#00LD PD_CR2,A ;MAIN_LOOP.LSTM8S 与汇编第 10 页 共 51 页LD A,PD_IDR ; 读入端口 D 的引脚输入寄存器AND A,#$80 ; 测试最高位是否为 1JRNE MAIN_LOOP_1 ; 若最高位为 1,则跳转 LD A,#$08 ; 否则说明按键按下,PD3-1,点亮 LED1LD PD_ODR,A ; JRA MAIN_LOOPMAIN_LOOP_1.LLD A,#$00 ; 若按键
29、没按下,PD3-0, 熄灭 LED1 LD PD_ODR,A ; JRA MAIN_LOOP ; interrupt NonHandledInterruptNonHandledInterrupt.liret; 下面定义中断向量表segment vectitdc.l $82000000+main ; resetdc.l $82000000+NonHandledInterrupt ; trapdc.l $82000000+NonHandledInterrupt ; irq0dc.l $82000000+NonHandledInterrupt ; irq1dc.l $82000000+NonHand
30、ledInterrupt ; irq2dc.l $82000000+NonHandledInterrupt ; irq3dc.l $82000000+NonHandledInterrupt ; irq4dc.l $82000000+NonHandledInterrupt ; irq5dc.l $82000000+NonHandledInterrupt ; irq6dc.l $82000000+NonHandledInterrupt ; irq7dc.l $82000000+NonHandledInterrupt ; irq8dc.l $82000000+NonHandledInterrupt
31、; irq9dc.l $82000000+NonHandledInterrupt ; irq10dc.l $82000000+NonHandledInterrupt ; irq11dc.l $82000000+NonHandledInterrupt ; irq12dc.l $82000000+NonHandledInterrupt ; irq13dc.l $82000000+NonHandledInterrupt ; irq14dc.l $82000000+NonHandledInterrupt ; irq15dc.l $82000000+NonHandledInterrupt ; irq16
32、dc.l $82000000+NonHandledInterrupt ; irq17dc.l $82000000+NonHandledInterrupt ; irq18dc.l $82000000+NonHandledInterrupt ; irq19dc.l $82000000+NonHandledInterrupt ; irq20dc.l $82000000+NonHandledInterrupt ; irq21dc.l $82000000+NonHandledInterrupt ; irq22dc.l $82000000+NonHandledInterrupt ; irq23STM8S
33、与汇编第 11 页 共 51 页dc.l $82000000+NonHandledInterrupt ; irq24dc.l $82000000+NonHandledInterrupt ; irq25dc.l $82000000+NonHandledInterrupt ; irq26dc.l $82000000+NonHandledInterrupt ; irq27dc.l $82000000+NonHandledInterrupt ; irq28dc.l $82000000+NonHandledInterrupt ; irq29endSTM8S 与汇编第 12 页 共 51 页STM8 与汇
34、编语言(6)8 位定时器应用之一STM8 单片机中的外设资源是比较丰富的,定时器有 8 位的也有 16 位的,下面的实验程序,就是利用 8 位定时器 4 来进行延时,然后驱动 LED 闪烁。同样还是利用 ST 的开发工具,生成一个汇编程序的框架,然后修改其中的main.asm,修改后的代码如下。编译通过后,下载到开发板,运行程序,可以看到 LED 在闪烁,或者用示波器可以在LED 引脚上看到方波。在这里要特别提醒的是,从 ST 给的手册上看,这个定时器中的计数器是一个加 1 计数器,但本人在实验过程中感觉不太对,经过反复的实验,我认为应该是一个减 1 计数器(也许是我拿的手册不对,或许是理解上
35、有误) 。例如,当给定时器中的自动装载寄存器装入 255 时,产生的方波频率最小,就象下面代码中计算的那样,产生的方波频率为 30HZ左右。若初始化时给自动装载寄存器装入 1,则产生的方波频率最大,大约为 3.9K 左右。也就是说实际的分频数为 ARR 寄存器的值+1。stm8/#include “mapping.inc“; #include “STM8S207S8.INC“; 涉及到的硬件资源; 下面定义端口 D 的寄存器地址PD_ODR EQU $500f PD_IDR EQU $5010PD_DDR EQU $5011 PD_CR1 EQU $5012PD_CR2 EQU $5013;
36、定时器 4 的寄存器定义TIM4_CR1 EQU $5340TIM4_IER EQU $5341TIM4_SR EQU $5342TIM4_EGR EQU $5343TIM4_CNTR EQU $5344TIM4_PSCR EQU $5345TIM4_ARR EQU $5346; 定义堆栈空间的起始位置和结束位置stack_start.w EQU $stack_segment_startstack_end.w EQU $stack_segment_endsegment rom ; 下面开始定义一个段,该段位于 ROM 中main.l ; 定义复位后的第一条指令的标号(即入口地址); 首先要初始
37、化堆栈指针STM8S 与汇编第 13 页 共 51 页LDW X,#stack_end LDW SP,X; 下面初始化 IO 端口; PD3 设置成推挽输出; PD7 设置成悬浮输入LD A,#08LD PD_DDR,A ; 将 PD3 设置成输出,PD7 设置成输入LD A,#08LD PD_CR1,A ; 将 PD3 设置成推挽输出LD A,#00LD PD_CR2,A ; 下面初始化定时器 4LD A,#$00LD TIM4_IER,A ; 禁止中断LD A,#$01 LD TIM4_EGR,A ; 允许产生更新事件LD A,#$07LD TIM4_PSCR,A ; 计数器时钟=主时钟/
38、128=2MHZ/128; 相当于计数器周期为 64uSLD A,#255LD TIM4_ARR,A ; 设定重装载时的寄存器值,255 是最大值LD A,#255LD TIM4_CNTR,A ; 设定计数器的初值; 定时周期=(ARR+1)*64=16384uS; 产生方波频率=30.5HZLD A,#$01; b0 = 1,允许计数器工作; b1 = 0,允许更新LD TIM4_CR1,A ; 设置控制器,启动定时器MAIN_LOOP.LLD A,TIM4_SR ; 读入定时器 4 的状态AND A,#01 ; 判断是否产生更新标志 JREQ MAIN_LOOP ; 若没有,则等待 LD
39、A,#0 ; 清除更新标志 LD TIM4_SR,A LD A,PD_ODR ; 将 LED 驱动信号取反XOR A,#$08LD PD_ODR,A ; LED 闪烁频率=2MHZ/128/255/2=30.63JRA MAIN_LOOP ; 无限循环interrupt NonHandledInterruptSTM8S 与汇编第 14 页 共 51 页NonHandledInterrupt.liret; 下面定义中断向量表segment vectitdc.l $82000000+main ; resetdc.l $82000000+NonHandledInterrupt ; trapdc.l
40、$82000000+NonHandledInterrupt ; irq0dc.l $82000000+NonHandledInterrupt ; irq1dc.l $82000000+NonHandledInterrupt ; irq2dc.l $82000000+NonHandledInterrupt ; irq3dc.l $82000000+NonHandledInterrupt ; irq4dc.l $82000000+NonHandledInterrupt ; irq5dc.l $82000000+NonHandledInterrupt ; irq6dc.l $82000000+Non
41、HandledInterrupt ; irq7dc.l $82000000+NonHandledInterrupt ; irq8dc.l $82000000+NonHandledInterrupt ; irq9dc.l $82000000+NonHandledInterrupt ; irq10dc.l $82000000+NonHandledInterrupt ; irq11dc.l $82000000+NonHandledInterrupt ; irq12dc.l $82000000+NonHandledInterrupt ; irq13dc.l $82000000+NonHandledIn
42、terrupt ; irq14dc.l $82000000+NonHandledInterrupt ; irq15dc.l $82000000+NonHandledInterrupt ; irq16dc.l $82000000+NonHandledInterrupt ; irq17dc.l $82000000+NonHandledInterrupt ; irq18dc.l $82000000+NonHandledInterrupt ; irq19dc.l $82000000+NonHandledInterrupt ; irq20dc.l $82000000+NonHandledInterrup
43、t ; irq21dc.l $82000000+NonHandledInterrupt ; irq22dc.l $82000000+NonHandledInterrupt ; irq23dc.l $82000000+NonHandledInterrupt ; irq24dc.l $82000000+NonHandledInterrupt ; irq25dc.l $82000000+NonHandledInterrupt ; irq26dc.l $82000000+NonHandledInterrupt ; irq27dc.l $82000000+NonHandledInterrupt ; ir
44、q28dc.l $82000000+NonHandledInterrupt ; irq29endSTM8S 与汇编第 15 页 共 51 页STM8 与汇编语言(7)8 位定时器应用之二上次写的是用 STM8 单片机中的 8 位定时器作为软件延时,采用的是查询方式。在实际系统中,定时器的应用,更多的是采用中断方式,下面的代码就给出 8 位定时器在中断方式下的应用。实验程序首先初始化驱动 LED 的端口,然后初始化 8 位的定时器 4,最后启动中断允许,要记住,一定要将中断服务程序的入口地址填写到中断向量表中,并且要根据中断向量号在正确的位置上填写。同样还是利用 ST 的开发工具,生成一个汇编程
45、序的框架,然后修改其中的main.asm,修改后的代码如下。编译通过后,下载到开发板,运行程序,可以看到 LED 在闪烁,或者用示波器可以在LED 引脚上看到方波。stm8/#include “mapping.inc“; #include “STM8S207S8.INC“; 涉及到的硬件资源; LED1 定义在 PD3; 下面定义端口 D 的寄存器地址PD_ODR EQU $500f PD_IDR EQU $5010PD_DDR EQU $5011 PD_CR1 EQU $5012PD_CR2 EQU $5013; 定时器 4 的寄存器定义TIM4_CR1 EQU $5340TIM4_IER
46、EQU $5341TIM4_SR EQU $5342TIM4_EGR EQU $5343TIM4_CNTR EQU $5344TIM4_PSCR EQU $5345TIM4_ARR EQU $5346; 定义堆栈空间的起始位置和结束位置stack_start.w EQU $stack_segment_startstack_end.w EQU $stack_segment_endsegment rom ; 下面开始定义一个段,该段位于 ROM 中main.l ; 定义复位后的第一条指令的标号(即入口地址); 首先要初始化堆栈指针LDW X,#stack_end LDW SP,XSTM8S 与汇编第 16 页 共 51 页; 下面初始化 IO 端口; PD3 设置成推挽输出; PD7 设置成悬浮输入LD A,#08LD PD_DDR,A ; 将 PD3 设置成输出,PD7 设置成输入LD A,#08LD PD_CR1,A ; 将 PD3 设置成推挽输出LD A,#00LD PD_CR2,A