STM8的C语言编程.pdf
第 1 页 共 32 页献给迷茫中的STM8初学者本文内容整理与网络,适用于对单片机有一定的了解和熟悉的人.希望你们能够快速上手并应用 STM8.本文所有例程适用于 COSMIC 和 IAR 编译器,更多学习资料可以登录我们的 官网 和 论坛 查找。QIYONG第 2 页 共 32 页目 录STM8 的 C 语言编程(1)基本程序与启动代码分析.1STM8 的 C 语言编程(2)变量空间的分配.5STM8 的 C 语言编程(3)GPIO 输出.6STM8 的 C 语言编程(4)GPIO 输出和输入.8STM8 的 C 语言编程(5)8 位定时器应用之一.9STM8 的 C 语言编程(6)8 位定时器应用之二.10STM8 的 C 语言编程(7)16 位定时器的中断应用.14STM8 的 C 语言编程(8)UART 应用.16STM8 的 C 语言编程(9)EEPROM 应用.20STM8 的 C 语言编程(10)修改 CPU 的时钟.22STM8 的 C 语言编程(11)切换时钟源.25STM8 的 C 语言编程(12)AD 转换.27STM8 的 C 语言编程(13)蜂鸣器.29STM8 的 C 语言编程(14)PWM.30第 1 页 共 32 页STM8STM8STM8STM8 的的 C C C C 语言编程(语言编程(1 1 1 1)基本程序与启动代码分析)基本程序与启动代码分析现在几乎所有的单片机都能用 C 语言编程了,采用 C 语言编程确实能带来很多好处,至少可读性比汇编语言强多了。在 STM8 的开发环境中,可以通过新建一个工程,自动地建立起一个 C 语言的框架,生成后开发环境会自动生成 2 个 C 语言的程序,一个是 main.c,另一个是 stm8_interrupt_vector.c。main.c 中就是一个空的 main()函数,如下所示:/*MAIN.C file*Copyright(c)2002-2005 STMicroelectronics*/main()while(1);而在 stm8_interrupt_vector.c 中,就是声明了对应该芯片的中断向量,如下所示:/*BASIC INTERRUPT VECTOR TABLE FOR STM8 devices*Copyright(c)2007 STMicroelectronics*/typedef void far(*interrupt_handler_t)(void);struct interrupt_vectorunsigned 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=0 x82,(interrupt_handler_t)_stext,/*reset*/0 x82,NonHandledInterrupt,/*trap*/0 x82,NonHandledInterrupt,/*irq0*/第 2 页 共 32 页0 x82,NonHandledInterrupt,/*irq1*/0 x82,NonHandledInterrupt,/*irq2*/0 x82,NonHandledInterrupt,/*irq3*/0 x82,NonHandledInterrupt,/*irq4*/0 x82,NonHandledInterrupt,/*irq5*/0 x82,NonHandledInterrupt,/*irq6*/0 x82,NonHandledInterrupt,/*irq7*/0 x82,NonHandledInterrupt,/*irq8*/0 x82,NonHandledInterrupt,/*irq9*/0 x82,NonHandledInterrupt,/*irq10*/0 x82,NonHandledInterrupt,/*irq11*/0 x82,NonHandledInterrupt,/*irq12*/0 x82,NonHandledInterrupt,/*irq13*/0 x82,NonHandledInterrupt,/*irq14*/0 x82,NonHandledInterrupt,/*irq15*/0 x82,NonHandledInterrupt,/*irq16*/0 x82,NonHandledInterrupt,/*irq17*/0 x82,NonHandledInterrupt,/*irq18*/0 x82,NonHandledInterrupt,/*irq19*/0 x82,NonHandledInterrupt,/*irq20*/0 x82,NonHandledInterrupt,/*irq21*/0 x82,NonHandledInterrupt,/*irq22*/0 x82,NonHandledInterrupt,/*irq23*/0 x82,NonHandledInterrupt,/*irq24*/0 x82,NonHandledInterrupt,/*irq25*/0 x82,NonHandledInterrupt,/*irq26*/0 x82,NonHandledInterrupt,/*irq27*/0 x82,NonHandledInterrupt,/*irq28*/0 x82,NonHandledInterrupt,/*irq29*/;在 stm8_interrupt_vector.c 中,除了定义了中断向量表外,还定义了空的中断服务程序,用于那些不用的中断。当然在自动建立时,所有的中断服务都是空的,因此,除了第 1 个复位的向量外,其它都指向那个空的中断服务函数。生成框架后,就可以用 Build 菜单下的 Rebuild All 对项目进行编译和连接,生成所需的目标文件,然后就可以加载到 STM8 的芯片中,这里由于 main()函数是一个空函数,因此没有任何实际的功能。不过我们可以把这个框架对应的汇编代码反出来,看看 C 语言生成的代码,这样可以更深入地了解 C 语言编程的特点。生成的代码包括 4 个部分,如图 1、图 2、图 3、图 4 所示。第 3 页 共 32 页图 1图 2第 4 页 共 32 页图 3图 4图 1 显示的是从内存地址 8000H 开始的中断向量表,中断向量表中的第 1 行 82008083H 为复位后单片机运行的第 1 跳指令的地址。从表中可以看出,单片机复位后,将从 8083H 开始运行。其它行的中断向量都指向同一个位置的中断服务程序 80D0H。图 2 显示的是 3 个字节,前 2 个字节 8083H 为复位后的第 1 条指令的地址,第 3 个字节是一个常量0,后面的启动代码要用到。图 3 显示的是启动代码,启动代码中除了初始化堆栈指针外,就是初始化 RAM 单元。由于目前是一个空的框架,因此在初始化完堆栈指针(设置成 0FFFH)后,由于 8082H 单元的内容为 0,因此程序就跳到了 80B1H,此处是一个循环,将 RAM 单元从 0 到 5 初始化成 0。然后由于寄存器 X 设置成 0100H,就直接通过 CALL main 进入 C 的 main()函数。图 4 显示的是 main()函数和中断服务函数,main()函数对应的代码就是一个无限的循环,而中断服务函数就一条指令,即中断返回指令。通过分析,可以看出用 C 语言编程时,比汇编语言编程时,就是多出了一段启动代码。第 5 页 共 32 页STM8STM8STM8STM8 的的 C C C C 语言编程(语言编程(2 2 2 2)变量空间的分配)变量空间的分配采用 C 这样的高级语言,其实可以不用关心变量在存储器空间中是如何具体分配的。但如果了解如何分配,对编程还是有好处的,尤其是在调试时。例如下面的程序定义了全局变量数组 buffer 和一个局部变量 i,在 RAM 中如何分配的呢?/*MAIN.C file*Copyright(c)2002-2005 STMicroelectronics*/unsigned char buffer10;/定义全局变量main()unsigned char i;/定义局部变量for(i=0;i10;i+)bufferi=0 x55;我们可以通过 DEBUG 中的反汇编窗口,看到如下的对应代码:从这段代码中可以看到,全局变量 buffer 被分配到空间从地址 0000H 到 0009H。而局部变量 i 则在堆栈空间中分配,通过 PUSH A 指令,将堆栈指针减 1,腾出一个字节的空间,而 SP+1 指向的空间就是分配给局部变量使用的空间。由此可以得出初步的结论,对于全局变量,内存分配是从低地址 0000H 开始向上分配的。而局部变量则是在堆栈空间中分配。另外从上一篇文章中,可以知道堆栈指针初始化时为 0FFFH。而根据 PUSH 指令的定义,当压栈后堆栈指针减 1。因此堆栈是从上往下使用的。因此根据内存分配和堆栈使用规则,我们在程序设计时,不能定义过多的变量,免得没有空间给堆栈使用。换句话说,当定义变量时,一定要考虑到堆栈空间,尤其是那些复杂的系统,程序调用层数多,这样就会占用大量的堆栈空间。总之,在单片机的程序设计时,由于 RAM 空间非常有限,要充分考虑到全局变量、局部变量、程序调用层数和中断服务调用对空间的占用。第 6 页 共 32 页STM8STM8STM8STM8 的的 C C C C 语言编程(语言编程(3 3 3 3)GPIOGPIOGPIOGPIO 输出输出与前些日子写的用汇编语言进行的实验一样,从今天开始,要在 ST 的三合一开发板上,用 C 语言编写程序,进行一系列的实验。首先当然从最简单的 LED 指示灯闪烁的实验开始。开发板上的 LED1 接在 STM8 的 PD3 上,因此要将 PD3 设置成输出模式,为了提高高电平时的输出电流,要将其设置成推挽输出方式。这主要通过设置对应的 DDR/CR1/CR2 寄存器实现。利用 ST 的开发工具,先生成一个 C 语言程序的框架,然后修改其中的 main.c,修改后的代码如下。编译通过后,下载到开发板,运行程序,可以看到 LED1 在闪烁,且闪烁的频率为 5HZ。/*MAIN.C file*Copyright(c)2002-2005 STMicroelectronics*/#include STM8S207C_S.h/函数功能:延时函数/输入参数:ms-要延时的毫秒数,这里假设 CPU 的主频为 2MHZ/输出参数:无/返 回 值:无/备注:无void DelayMS(unsigned int ms)unsigned char i;while(ms!=0)for(i=0;i250;i+);for(i=0;i75;i+);ms-;/函数功能:主函数/初始化 GPIO 端口 PD3,驱动 PD3 为高电平和低电平/输入参数:ms-要延时的毫秒数,这里假设 CPU 的主频为 2MHZ/输出参数:无/返 回 值:无/备注:无main()PD_DDR=0 x08;PD_CR1=0 x08;/将 PD3 设置成推挽输出第 7 页 共 32 页PD_CR2=0 x00;while(1)PD_ODR=PD_ODR|0 x08;/将 PD3 的输出设置成 1DelayMS(100);/延时 100MSPD_ODR=PD_ODR&0 xF7;/将 PD3 的输出设置成 0DelayMS(100);/延时 100MS需要注意的是,当生成完框架后,为了能方便使用 STM8 的寄存器名字,必须包括 STM8S207C_S.h,最好将该文件拷贝到 C:Program FilesSTMicroelectronicsst_toolsetinclude 目录下,拷贝到工程目录下。或者将该路径填写到该工程的 Settings中的 C Compiler 选项 Preprocessor 的 Additional include 中,这样编译时才会找到该文件。第 8 页 共 32 页STM8STM8STM8STM8 的的 C C C C 语言编程(语言编程(4 4 4 4)GPIOGPIOGPIOGPIO 输出和输入输出和输入今天要进行的实验,是利用 GPIO 进行输入和输出。在 ST 的三合一开发板上,按键接在 GPIO 的 PD7 上,LED 接在 GPIO 的 PD3 上,因此我们要将 GPIO 的 PD7 初始化成输入,PD3 初始化成输出。关于 GPIO 的引脚设置,主要是要初始化方向寄存器 DDR,控制寄存器 1(CR1)和控制寄存器 2(CR2),寄存器的每一位对应 GPIO 的每一个引脚。具体的设置功能定义如下:DDRCR1CR2引脚设置000悬浮输入001上拉输入010中断悬浮输入011中断上拉输入100开漏输出110推挽输出1X1输出(最快速度为 10MHZ)另外,输出引脚对应的寄存器为 ODR,输入引脚对应的寄存器为 IDR。下面的程序是检测按键的状态,当按键按下时,点亮 LED,当按键抬起时,熄灭 LED。同样也是利用 ST 的开发工具,先生成一个 C 语言程序的框架,然后修改其中的 main.c,修改后的代码如下。编译通过后,下载到开发板,运行程序,按下按键,LED 就点亮,抬起按键,LED 就熄灭了。另外,要注意,将 STM8S207C_S.h 拷贝到当前项目的目录下。/程序描述:检测开发板上的按键,若按下,则点亮 LED,若抬起,则熄灭 LED/按键接在 MCU 的 GPIO 的 PD7 上,LED 接在 MCU 的 GPIO 的 PD3 上/#include STM8S207C_S.hmain()PD_DDR=0 x08;PD_CR1=0 x08;/将 PD3 设置成推挽输出PD_CR2=0 x00;while(1)/进入无限循环if(PD_IDR&0 x80)=0 x80)/读入 PD7 的引脚信号PD_ODR=PD_ODR&0 xF7;/如果 PD7 为 1,则将 PD3 的输出设置成 0,熄灭 LEDelsePD_ODR=PD_ODR|0 x08;/否则,将 PD3 的输出设置成 1,点亮 LED第 9 页 共 32 页STM8STM8STM8STM8 的的 C C C C 语言编程(语言编程(5 5 5 5)8 8 8 8 位定时器应用之一位定时器应用之一在 STM8 单片机中,有多种定时器资源,既有 8 位的定时器,也有普通的 16 位定时器,还有高级的定时器。今天的实验是用最简单的 8 位定时器 TIM4 来进行延时,然后驱动 LED 闪烁。为了简单起见,这里是通过程序查询定时器是否产生更新事件,来判断定时器的延时是否结束。同样还是利用 ST 的开发工具,生成一个 C 程序的框架,然后修改其中的 main.c,修改后的代码如下。编译通过后,下载到开发板,运行程序,可以看到 LED 在闪烁,或者用示波器可以在 LED 引脚上看到方波。在这里要特别提醒的是,从 ST 给的手册上看,这个定时器中的计数器是一个加 1 计数器,但本人在实验过程中感觉不太对,经过反复的实验,我认为应该是一个减 1 计数器(也许是我拿的手册不对,或许是理解上有误)。例如,当给定时器中的自动装载寄存器装入 255 时,产生的方波频率最小,就象下面代码中计算的那样,产生的方波频率为 30HZ 左右。若初始化时给自动装载寄存器装入 1,则产生的方波频率最大,大约为 3.9K 左右。也就是说实际的分频数为 ARR 寄存器的值+1。/程序描述:通过初始化定时器 4,进行延时,驱动 LED 闪烁/LED 接在 MCU 的 GPIO 的 PD3 上#include STM8S207C_S.hmain()/首先初始化 GPIOPD_DDR=0 x08;PD_CR1=0 x08;/将 PD3 设置成推挽输出PD_CR2=0 x00;/然后初始化定时器 4TIM4_IER=0 x00;/禁止中断TIM4_EGR=0 x01;/允许产生更新事件TIM4_PSCR=0 x07;/计数器时钟=主时钟/128=2MHZ/128/相当于计数器周期为 64uSTIM4_ARR=255;/设定重装载时的寄存器值,255 是最大值TIM4_CNTR=255;/设定计数器的初值/定时周期=(ARR+1)*64=16320uSTIM4_CR1=0 x01;/b0=1,允许计数器工作,b1=0,允许更新/设置控制器,启动定时器while(1)/进入无限循环while(TIM4_SR1&0 x81)=0 x00);/等待更新标志TIM4_SR1=0 x00;/清除更新标志PD_ODR=PD_ODR 0 x08;/LED 驱动信号取反 LED 闪烁频率=2MHZ/128/255/2=30.63第 10 页 共 32 页STM8STM8STM8STM8 的的 C C C C 语言编程(语言编程(6 6 6 6)8 8 8 8 位定时器应用之二位定时器应用之二今天进行的实验依然是用定时器 4,只不过改成了用中断方式来实现,由定时器 4 的中断服务程序来驱动 LED 的闪烁。实现中断方式的关键点有几个,第一个关键点就是要打开定时器 4 的中断允许位,在定时器 4 的 IER寄存器中有定义。第二个关键点,就是打开 CPU 的全局中断允许位,在汇编语言中,就是执行 RIM 指令,在 C 语言中,用下列语句实现:_asm(rim);第 3 个关键点就是中断服务程序的框架或写法,中断服务程序的写法如下:far interrupt void TIM4_UPD_OVF_IRQHandler(void)/下面是中断服务程序的实体第 4 个关键点就是要设置中断向量,即将中断服务程序的入口填写到中断向量表中,如下所示,将 IRQ23 对应的中断服务程序的入口填写成 TIM4_UPD_OVF_IRQHandlerstruct interrupt_vector const _vectab=0 x82,(interrupt_handler_t)_stext,/*reset*/0 x82,NonHandledInterrupt,/*trap*/0 x82,NonHandledInterrupt,/*irq0*/0 x82,NonHandledInterrupt,/*irq1*/0 x82,NonHandledInterrupt,/*irq2*/0 x82,NonHandledInterrupt,/*irq3*/0 x82,NonHandledInterrupt,/*irq4*/0 x82,NonHandledInterrupt,/*irq5*/0 x82,NonHandledInterrupt,/*irq6*/0 x82,NonHandledInterrupt,/*irq7*/0 x82,NonHandledInterrupt,/*irq8*/0 x82,NonHandledInterrupt,/*irq9*/0 x82,NonHandledInterrupt,/*irq10*/0 x82,NonHandledInterrupt,/*irq11*/0 x82,NonHandledInterrupt,/*irq12*/0 x82,NonHandledInterrupt,/*irq13*/0 x82,NonHandledInterrupt,/*irq14*/0 x82,NonHandledInterrupt,/*irq15*/0 x82,NonHandledInterrupt,/*irq16*/0 x82,NonHandledInterrupt,/*irq17*/0 x82,NonHandledInterrupt,/*irq18*/0 x82,NonHandledInterrupt,/*irq19*/0 x82,NonHandledInterrupt,/*irq20*/第 11 页 共 32 页0 x82,NonHandledInterrupt,/*irq21*/0 x82,NonHandledInterrupt,/*irq22*/0 x82,TIM4_UPD_OVF_IRQHandler,/*irq23*/0 x82,NonHandledInterrupt,/*irq24*/0 x82,NonHandledInterrupt,/*irq25*/0 x82,NonHandledInterrupt,/*irq26*/0 x82,NonHandledInterrupt,/*irq27*/0 x82,NonHandledInterrupt,/*irq28*/0 x82,NonHandledInterrupt,/*irq29*/;解决了以上 4 个关键点,我们就能很轻松地用 C 语言实现中断服务了。同样还是利用 ST 的开发工具,生成一个 C 程序的框架,然后修改其中的 main.c,修改后的代码如下。另外还要修改 stm8_interrupt_vector.c。编译通过后,下载到开发板,运行程序,可以看到 LED 在闪烁,或者用示波器可以在 LED 引脚上看到方波。修改后的 main.c 如下:/程序描述:通过初始化定时器 4,以中断方式驱动 LED 闪烁/LED 接在 MCU 的 GPIO 的 PD3 上#include STM8S207C_S.hmain()/首先初始化 GPIOPD_DDR=0 x08;PD_CR1=0 x08;/将 PD3 设置成推挽输出PD_CR2=0 x00;/然后初始化定时器 4TIM4_IER=0 x00;/禁止中断TIM4_EGR=0 x01;/允许产生更新事件TIM4_PSCR=0 x07;/计数器时钟=主时钟/128=2MHZ/128/相当于计数器周期为 64uSTIM4_ARR=255;/设定重装载时的寄存器值,255 是最大值TIM4_CNTR=255;/设定计数器的初值/定时周期=(ARR+1)*64=16320uSTIM4_CR1=0 x01;/b0=1,允许计数器工作/b1=0,允许更新/设置控制器,启动定时器TIM4_IER=0 x01;/允许更新中断第 12 页 共 32 页_asm(rim);/允许 CPU 全局中断while(1)/进入无限循环/函数功能:定时器 4 的更新中断服务程序/输入参数:无/输出参数:无/返 回 值:无far interrupt void TIM4_UPD_OVF_IRQHandler(void)TIM4_SR1=0 x00;/清除更新标志PD_ODR=PD_ODR 0 x08;/LED 驱动信号取反/LED 闪烁频率=2MHZ/128/255/2=30.63修改后的 stm8_interrupt_vector.c 如下:/*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*/extern far interrupt void TIM4_UPD_OVF_IRQHandler(void);第 13 页 共 32 页struct interrupt_vector const _vectab=0 x82,(interrupt_handler_t)_stext,/*reset*/0 x82,NonHandledInterrupt,/*trap*/0 x82,NonHandledInterrupt,/*irq0*/0 x82,NonHandledInterrupt,/*irq1*/0 x82,NonHandledInterrupt,/*irq2*/0 x82,NonHandledInterrupt,/*irq3*/0 x82,NonHandledInterrupt,/*irq4*/0 x82,NonHandledInterrupt,/*irq5*/0 x82,NonHandledInterrupt,/*irq6*/0 x82,NonHandledInterrupt,/*irq7*/0 x82,NonHandledInterrupt,/*irq8*/0 x82,NonHandledInterrupt,/*irq9*/0 x82,NonHandledInterrupt,/*irq10*/0 x82,NonHandledInterrupt,/*irq11*/0 x82,NonHandledInterrupt,/*irq12*/0 x82,NonHandledInterrupt,/*irq13*/0 x82,NonHandledInterrupt,/*irq14*/0 x82,NonHandledInterrupt,/*irq15*/0 x82,NonHandledInterrupt,/*irq16*/0 x82,NonHandledInterrupt,/*irq17*/0 x82,NonHandledInterrupt,/*irq18*/0 x82,NonHandledInterrupt,/*irq19*/0 x82,NonHandledInterrupt,/*irq20*/0 x82,NonHandledInterrupt,/*irq21*/0 x82,NonHandledInterrupt,/*irq22*/0 x82,TIM4_UPD_OVF_IRQHandler,/*irq23*/0 x82,NonHandledInterrupt,/*irq24*/0 x82,NonHandledInterrupt,/*irq25*/0 x82,NonHandledInterrupt,/*irq26*/0 x82,NonHandledInterrupt,/*irq27*/0 x82,NonHandledInterrupt,/*irq28*/0 x82,NonHandledInterrupt,/*irq29*/;第 14 页 共 32 页STM8STM8STM8STM8 的的 C C C C 语言编程(语言编程(7 7 7 7)16161616 位定时器的中断应用位定时器的中断应用在 STM8 中,除了有 8 位的定时器外,还有 16 位的定时器。今天进行的实验就是针对 16 位定时器 2来进行的。除了计数单元为 16 位的,其它设置与前面 8 位的定时器基本一样。下面的程序也是采样中断方式,由定时器 2 的中断服务程序来驱动 LED 的闪烁。具体的程序代码如下,其它注意点见上一篇,另外要注意别忘了修改相应的中断向量。/程序描述:通过初始化定时器 2,以中断方式驱动 LED 闪烁/LED 接在 MCU 的 GPIO 的 PD3 上#include STM8S207C_S.hmain()/首先初始化 GPIOPD_DDR=0 x08;PD_CR1=0 x08;/将 PD3 设置成推挽输出PD_CR2=0 x00;/然后初始化定时器 4TIM2_IER=0 x00;/禁止中断TIM2_EGR=0 x01;/允许产生更新事件TIM2_PSCR=0 x01;/计数器时钟=主时钟/128=2MHZ/2/相当于计数器周期为 1uS/设定重装载时的寄存器值/注意必须保证先写入高 8 位,再写入低 8 位TIM2_ARRH=0 xEA;/设定重装载时的寄存器的高 8 位TIM2_ARRL=0 x60;TIM2_CNTRH=0 xEA;/设定计数器的初值TIM2_CNTRL=0 x60;/定时周期=1*60000=60000uS=60msTIM2_CR1=0 x01;/b0=1,允许计数器工作/b1=0,允许更新/设置控制器,启动定时器TIM2_IER=0 x01;/允许更新中断_asm(rim);/允许 CPU 全局中断while(1)/进入无限循环第 15 页 共 32 页/函数功能:定时器 4 的更新中断服务程序/输入参数:无/输出参数:无/返 回 值:无far interrupt void TIM2_UPD_IRQHandler(void)TIM2_SR1=0 x00;/清除更新标志PD_ODR=PD_ODR 0 x08;/LED 驱动信号取反/LED 闪烁频率=2MHZ/2/60000/2=8.3第 16 页 共 32 页STM8STM8STM8STM8 的的 C C C C 语言编程(语言编程(8 8 8 8)UARTUARTUARTUART 应用应用串口通讯也是单片机应用中经常要用到,今天的实验就是利用 STM8 的 UART 资源,来进行串口通讯的实验。实验程序的功能是以中断方式接收串口数据,然后将接收到的数据以查询方式发送到串口。程序代码如下,首先要对 STM8 的 UART 进行初始化,初始化时要注意的是波特率寄存器的设置,当求出一个波特率的分频系数(一个 16 位的数)后,要将高 4 位和低 4 位写到 BRR2 中,而将中间的 8 位写到 BRR1 中,并且必须是先写 BRR2,再写 BRR1。同样也是利用 ST 的开发工具,生成一个 C 语言的框架,然后修改其中的 main.c,同时由于需要用到中断服务,因此还要修改 stm8_interrupt_vector.c。修改后,编译连接,然后下载到开发板上,再做一根与 PC 机相连的线,把开发板的串口与 PC 机的串口连接起来,注意,2、3 脚要交叉。在 PC 机上运行超级终端,设置波特率为 9600,然后每按下一个按键,屏幕上就显示对应的字符。修改后的 main.c 和 stm8_interrupt_vector.c 如下:/程序描述:初始化 UART,以中断方式接收字符,以查询方式发送/UART 通讯参数:9600bps,8 位数据,1 位停止位,无校验#include STM8S207C_S.h/函数功能:初始化 UART/输入参数:无/输出参数:无/返 回 值:无/备注:无void UART3_Init(void)LINUART_CR2=0;/禁止 UART 发送和接收LINUART_CR1=0;/b5=0,允许 UART,b2=0,禁止校验LINUART_CR3=0;/b5,b4=00,1 个停止位/设置波特率,必须注意以下几点:/(1)必须先写 BRR2/(2)BRR1 存放的是分频系数的第 11 位到第 4 位,/(3)BRR2 存放的是分频系数的第 15 位到第 12 位,和第 3 位到第 0 位/例如对于波特率位 9600 时,分频系数=2000000/9600=208/对应的十六进制数为 00D0,BBR1=0D,BBR2=00LINUART_BRR2=0;LINUART_BRR1=0 x0d;/实际的波特率分频系数为 00D0(208)/对应的波特率为 2000000/208=9600LINUART_CR2=0 x2C;/b3=1,允许发送/b2=1,允许接收,b5=1,允许产生接收中断第 17 页 共 32 页/函数功能:从 UART3 发送一个字符/输入参数:ch-要发送的字符/输出参数:无/返 回 值:无/备注:无void UART3_SendChar(unsigned char ch)while(LINUART_SR&0 x80)=0 x00);/若发送寄存器不空,则等待LINUART_DR=ch;/将要发送的字符送到数据寄存器main()/首先初始化 UART3UART3_Init();_asm(rim);/允许 CPU 全局中断while(1)/进入无限循环/函数功能:UART3 的接收中断服务程序/输入参数:无/输出参数:无/返 回 值:无far interrupt void UART3_Recv_IRQHandler(void)unsigned char ch;ch=LINUART_DR;/读入接收到的字符UART3_SendChar(ch);/将字符发送出去/*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;第 18 页 共 32 页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*/extern far interrupt void UART3_Recv_IRQHandler();struct interrupt_vector const _vectab=0 x82,(interrupt_handler_t)_stext,/*reset*/0 x82,NonHandledInterrupt,/*trap*/0 x82,NonHandledInterrupt,/*irq0*/0 x82,NonHandledInterrupt,/*irq1*/0 x82,NonHandledInterrupt,/*irq2*/0 x82,NonHandledInterrupt,/*irq3*/0 x82,NonHandledInterrupt,/*irq4*/0 x82,NonHandledInterrupt,/*irq5*/0 x82,NonHandledInterrupt,/*irq6*/0 x82,NonHandledInterrupt,/*irq7*/0 x82,NonHandledInterrupt,/*irq8*/0 x82,NonHandledInterrupt,/*irq9*/0 x82,NonHandledInterrupt,/*irq10*/0 x82,NonHandledInterrupt,/*irq11*/0 x82,NonHandledInterrupt,/*irq12*/0 x82,NonHandledInterrupt,/*irq13*/0 x82,NonHandledI