1、STM8 单片机的触角I/O 口的应用4.1 玩转 I/O 口必备的“五器”STM8S208RB 单片机的 I/O 口分布情况如 图 4-1 所示图 4-1 STM8S208RB 管脚分布图从 图 4-1 中我们可以看到,STM8S208RB 共有 64 个引脚,其中 52 个通用输入/输出口(GPIO) ,它们分别是PA1PA6、PB0PB7、PC1PC7、PD0PD7、PE0PE7、PF0、PF3PF7、PG0PG7、PI0。其中,每个端口都有一个输出数据寄存器(ODR) ,一个输入引脚寄存器(IDR) ,一个数据方向寄存器(DDR) ,一个控制寄存器 1(CR1) ,一个控制寄存器 2(
2、CR2) ,这就是 STM8单片机 I/O 的“五器” 。 “五器”到手了,该如何使用,如 表 4-1。配置模式 数据方向寄存器 DDR 控制寄存器 1CR1 控制寄存器 2CR2 配置模式0 0 0 悬浮输入0 1 0 上拉输入0 0 1 中断悬浮输入输入0 1 1 中断上拉输入1 0 0 开漏输出1 1 0 推挽输出1 x 1 输出(最快速度 10MHz)输出x x x 真正的开漏输出 (特定引脚)表 4-1 I/O 口配置表由 表 4-1 中看出,当 Px_DDRn 为“1” ,Pxn 配置为输出,否则为输入。当配置为输入时,若 Px_CR1 为“1” ,上拉电阻使能,否则为悬浮输入。而
3、 Px_CR2 为“1”时,开启当前 I/O 口的外部中断功能,为“0”时关闭外部中断功能。若想读取该I/O 引脚上的数据,只需读取相应的 Px_IDR 寄存器即可。当配置为输出时,若 Px_CR1 为“1” ,Pxn 配置为推挽输出,否则为开漏输出。而Px_CR2 为“1”时,当前 I/O 口的最大输出速率为 10MHz,否则最大输出速率为 2MHz。想要某个 I/O 口输出高电平,则向 Px_ODR 中写入 0xff,否则写入 0x00;注:针对 STM8S208RB 而言,x 为 A、B、C、D、E、F、G、I;n 为 07。下面的例子演示了如何设置 PB 口低四位为推挽输出,最快速度
4、10MHz,并输出高电平,高四位为上拉输入。unsigned char i;/*设置输出高电平*/PB_ODR = (1 3) | (1 2) | (1 1) | (1 0);/*设置端口方向*/PB_DDR = (1 3) | (1 2) | (1 1) | (1 0);/*设置推挽输出和定义上拉电阻*/PB_CR1 = 0xff;/*设置端口最大速度和关闭中断*/PB_CR2 = (1 3) | (1 2) | (1 1) | (1 0);/*读取端口输入数据*/i = PB_IDR 4.1.1 悬浮与上拉悬浮输入与上拉输入是两种输入方式,不同之处在于上拉输入时,引脚内部有个上拉电阻。当引
5、脚悬空时,上拉输入的引脚电平是确定的,即高电平;而悬浮输入则不同,它的电平时不确定的,即使外部的一个很小的信号都会使其发生改变。上拉输入最典型的应用就是外部按键,当按键未按下时,我们要保证它是高电平,当按键按下时才被拉低;而悬浮输入的典型应用就是模数转换,外部的任何一个小信号都要经过 A/D 采样转换为数字信号。4.1.2 开漏与推挽1. 开漏输出说开漏输出之前,我们先来看看什么是集电极开路输出。 Q21RK+5件0OutIn图 4-2 集电极开路集电极开路输出的结构图如 图 4-2 所示,三极管 Q1 的集电极就是单片机的 I/O 口,什么都不接,所以叫做集电极开路。当控制端输入为“0”时,
6、三极管 Q2 截止,及集电极与发射机之间断开,所以 5V 电压通过 R1 接到 Q1 的基级,Q1 导通,即相当于管脚直接接地;当控制端输入“1”时,三极管 Q2 导通,Q1 截止,输出引脚与地之间断开。我们将 图 4-2简化为 图 4-3 所示。0件1Out图 4-3 集电极开路简化图图 4-3 中的开关受软件控制, “1”时断开, “0”时闭合。很明显可以看出,当开关闭合时,输出直接接地,所以输出低电平。而当开关断开时,则输出端悬空,即引脚为高阻态。这时电平状态未知,如果后面一个电阻负载(即使很轻的负载)到地,那么输出端的电平就被这个负载拉到低电平了,所以这个电路是不能输出高电平的。 RK
7、+5图 4-4 带上拉电阻的开漏输出图 4-4 中的 10K 电阻即是上拉电阻。当开关闭合,输出管脚直接接地,输出为低电平,当开关断开,电流经过 10K 电阻流入负载,相当于管脚输出高电平。明白了集电极开路,那么开漏输出就简单了,只要把三极管换成场效应管即可,这样,集电极就变成了漏极,而原理分析是一样的。开漏输出有这么几个特点:(1) 利用外部电路的驱动能力,减少 IC 内部的驱动。当 IC 内部 MOSFET 导通时,驱动电流是从外部的 VCC 流经上拉电阻到负载,IC 内部仅需很小的栅极驱动电流。(2) 因为开漏引脚不连接外部的上拉电阻时,只能输出低电平,如果需要同时具备输出高电平的功能,
8、则需要接上拉电阻,很好的一个优点是通过改变上拉电源的电压,便可以改变传输电平。比如加上上拉电阻就可以提供 TTL/CMOS 电平输出等。 (上拉电阻的阻值决定了逻辑电平转换的沿的速度 。阻值越大,速度越低功耗越小,所以负载电阻的选择要兼顾功耗和速度。 )(3) 开漏结构提供了灵活的输出方式,但是也有其弱点,就是带来上升沿的延时。因为上升沿是通过外接上拉无源电阻对负载充电,所以当电阻选择小时延时就小,但功耗大;反之延时大功耗小。所以如果对延时有要求,则建议用下降沿输出。(4) 可以将多个开漏输出的引脚连接到一条线上。通过一只上拉电阻,在不增加任何器件的情况下,形成“与逻辑”关系。这也是 I2C,
9、SMBus 等总线判断总线占用状态的原理。2. 推挽输出我们同样以三极管为例,来看看推挽输出的结构,如 图 4-5 所示。Q12VCInOutRK图 4-5 推挽输出输入端由软件控制,当软件写“1”时,输入高电平,三极管 Q1 导通,Q2 截止;当软件写“0”时,输入低电平,三极管 Q1 截止,当输出端的电平高于 0.7V 时,Q2 导通。我们可将上面电路图简化为 图 4-6 所示。 S图 4-6 推挽输出简化图当软件写“1”时,开关 S1 闭合,S2 断开,VCC 连接到引脚上,输出高电平;当软件写“0”时,开关 S1 断开,S2 闭合,引脚接到 GND,输出低电平。4.2 LED 孤独的闪
10、着在点亮小灯之前我们先来了解一下 LED 的一些基础知识, 图 4-7 是普通发光二极管的外形图及电路符号,长脚为阳极。我们其实也可以看二极管里面大片的一侧是阴极,但是也有些黄色 LED 是相反的。图 4-7 LED 硬件结构VD30PB125689GSdio_sCAarefTM图 4-8 闪烁 LED 电路图图 4-8 是闪烁 LED 的电路原理图,PB0 口通过一个 330 电阻连接发光二极管的阴极,即低电平 LED 亮,高电平灭。程序代码如下。#include “stm8s208r.h“/*函数声明*/void delay(unsigned int time);/*主函数*/int ma
11、in(void)PB_DDR |= 0x01; /选择输出模式PB_CR1 |= 0x01; /推挽输出模式PB_CR2 |= 0x00; /低速输出模式while (1)PB_ODR / 小灯亮delay(50000); PB_ODR |= 0x01; /小灯灭delay(50000);/*延时子程序*/void delay(unsigned int time)while(time-);问:老大,这个程序不算难,原理我都懂.就是先把 IO 口的工作方式等都设置好,然后给小灯一个低电平,小灯就亮,然后延时一会,再给小灯一个高电平,小灯就灭,再延时.如此反复,就会有闪烁的效果.但是问题是为什么给
12、管脚赋值的时候要用”与等于”和”或等于”呢?我就是理解不了这个.答:放心,作为老大.我一定不会让你受苦!Relax.Everything is OK.这种赋值方法.无论是“与等于”或者“或等于”,目的都是在改变某一位或者某几位的电平的同时,保持剩余位置的电平不变.因为进行“与”运算的时候.无论 1 或者 0“与”1 后得到的结果没有发生改变(还是 1或者 0).当进行“或”运算的时候,无论 1 或者 0“或”0 后得到的结果也没有发生改变(还是 1 或者 0).讲到这里.应该能明白其中的道理了吧?很简单.当你需要对某位置高的时候就“或”1,需要保持原来的电平的位置,则“或”0.当需要对某位置低
13、的时候就“与”0,需要保持就“与”1 这就通过程序达到了“位操作”的效果. Come on baby.Go on reading the most important paragraph!1.注意,这里的位操作并不是真正意义的位操作.要注意区分两者的本质区别!2.虽然在本例中不使用这种赋值方法,直接赋值的话对程序不会造成任何影响.但是养成一个好的习惯对以后的进一步学习非常有好处,因为以后程序代码量增加之后,此类这种位操作,经常出现,如果不养成这种习惯,会很难以找出程序中的错误.对调试程序产生极大的影响.既费时又费力.所以极力推荐大家使用这种方法4.3 跑马灯是怎么跑的?4.3.1 应用 swi
14、tch-case 语句设计跑马灯大家在前面一节初步的接触了单个 IO 口寄存器设置及其使用方法,在本节我们来用PB 的 8 个 I/O 口来进行跑马灯的程序设计。我们简单的回顾一下 switch-case 的用法。如 图 4-9 所示,switch 相当于开关的拨口,i 选择被连端口,case 相当于开关的被连端口,当 i 为某值时,则有且只导通该路 case i。跑马灯的电路原理图如 图 4-10 所示.switch()ae 01234567图 4-9 switch-case 示例图DLE8PBGVSdo_CArfTMR*图 4-10 跑马灯电路原理图程序代码如下。#include“stm8
15、s208r.h“ / 开始时的头文件包含 /*函数声明*/void delay(unsigned int time); /声明延时函数/*主函数*/Into main(void)unsigned char i = 0;PB_DDR = 0XFF; /设置 IO 口 B 为输出PB_CR1 = 0XFF; /设置 IO 口 B 为推挽方式 PB_CR1 = 0X00; /输出最快速度为 2MHzPB_ODR = 0XFF; /设置输出寄存器的输出数值,初始化小灯全灭 while (1)switch (i) /让小灯从低位到高位依次亮case 0: PB_ODR = 0XFE;Delay(4000
16、0); /延时约 20mSi+; /让 i 加 1 下次执行程序时进入到 case 2break; /跳出 case 0,重新到 switch 处检查 i 值case 1: PB_ODR = 0XFD;Delay(40000);i+;break;case 2: PB_ODR = 0XFB;Delay(40000);i+;break;case 3: PB_ODR = 0XF7;Delay(40000);i+;break;case 4: PB_ODR = 0XEF;Delay(40000);i+;break;case 5: PB_ODR = 0XDF;Delay(40000);i+;break;c
17、ase 6: PB_ODR = 0XBF;Delay(40000);i+;break;case 7: PB_ODR = 0X7F;Delay(40000);i = 0;break;default : PB_ODR = 0XFF;Delay(40000);/*延时子程序*/void delay(unsigned int time)while (t-); /让 while 执行 t 次空程序达到延时的目的问:我说老大呀我又懵了,上面的程序里 IO 口必须设置为推挽输出吗?速度就只能设置为最快 2MHz 吗?答:懵啥呀有我在不用怕,其实它还可以设置为开漏方式,即 PB_CR1 = 0x00,小灯是低
18、电平点亮,电路图中我们在小灯正极外接了电源在这种情况下开漏与推挽两者的灌电流都很大所以没有什么区别,具体参考本章第一节,除此之外,还可以设置最快速度 10MHz,即 PB_CR2 = 0XFF。为了程序的规范编写,在定义函数时要完整,如 unsigned char Delay(unsigned char t),如果在没有返回值时,前面最好加 void,括号内没有定义行参时也定义 void,如 void Delay(void)。在写每个 case 时,一定不要忘了在相应的 case 语句后加跳出语句 break,否则会接着执行下个 case 语句。4.3.2 用 for 循环遛马硬件电路图与 图
19、 4-10 一样。初级骑马技巧代码如下。#include“stm8s208r.h“ /包含 stm8 头文件/*函数声明*/void delay(unsigned int x); /延时函数/*主函数*/main()unsigned char i;PB_DDR=0xff; /I/O 口初始化,将 PB 口设为输出状态PB_CR1=0xff; /PB 口(CR1) 置 1 推挽输出PB_CR2=0X00; /CR2 置 0 低速模式,初始化完成PB_ODR = 0XFE; /低电平有效,点亮第一个 LEDwhile (1) PB_ODR = 0XFE; /赋初值配合 for 循环使用for(i=
20、0;i8;i+)delay(50000); PB_ODR = 1; /左移一位, 点亮下一个 LEDPB_ODR |= 0x01; /左移后最低位自动补 0,故将最低为置高/*延时子程序*/void delay(unsigned int x) while(x-);4.4 数组与万能流水灯利用数组让你腾云驾雾但需要反复磨练方能修成正果,原理图等同上节马圈,下面是你期待已久的至高技能。#include “stm8s208r.h“ /包含头文件使软件识别特殊寄存器像 PB_DDR 等/*定义一个二维数组*/*装入使小灯花样闪亮的数据 */*/unsigned char dis58=0xfe,0xfd
21、,0xfb,0xf7,0xef,0xdf,0xbf,0x7f,0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x7f,0x3f,0x1f,0x0f,0x07,0x03,0x01,0x00,0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f;/*函数声明*/void GPIO_Init(void); / GPIO 口的初始化子程序void display(void); / 用 PB 口显示花样小灯子程void delay(unsigned int time
22、) ; / 延时子程序/*主函数*/int main(void) GPIO_Init(); /端口初始化while(1) /死循环display(); /始终让小灯显示花样/*GPIO 口的初始化*/void GPIO_Init(void)PB_ODR=0xff; /初始使小灯全灭PB_DDR=0xff; /DDR 为 0 是输入,1 是输出。此处为 PB 的 8 个端口均为输出PB_CR1=0xff; /推挽输出模式PB_CR2=0x00; /低速输出模式/*小灯花样显示子程序*/void display(void)unsigned char i,j;for(i = 0; i 5; i+)
23、/循环二维数组的行数 for(j = 0; j 8; j+) /循环二维数组每行中的数据PB_ODR = disij; /把数据给 PB 口使小灯显示delay(50000);/*延时子程序*/void delay(unsigned int time)while(time-);4.5 考验 STM8 的“臂力”STM8 推挽输出能力很强,我们就用数码管来检验一下 STM8 的“ 臂力” 。4.5.1 STM8 直接驱动一个数码管数码管是一种最常见的显示器件,可以显示一个 8 字型的数字,其内部其实是由 8 个发光二极管组成,每个发光二极管称为一个字段,分为 a、b、c、d、e、f、g、dp 八
24、段,其中 dp 为小数点,有共阴极和共阳极两种形式。下面我们就以共阳极的数码管为例作具体讲解。我们来做一个实验:用 STM8 直接驱动一个数码管,让数码管显示数字 0-9。硬件电路设计如 图 4-11。 70*912K3DP56SMGVCBvsio_ArT图 4-11 STM8 点亮一个数码管电路图程序代码如下。#include “stm8s208r.h“/* - 函数声明 -*/void delay(unsigned int delay_time); void display_SMG(void);/* SMG_table 共阳数码管段选编码*/unsigned char SMG_table1
25、0=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90;/* 对应显示数字 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9 */main() /*端口初始化-*/PB_DDR = 0XFF; /定义 PB 口为输出,用于控制数码管段选PB_ODR = 0XFF; /定义 PB 初始输出为高,数码管初始不亮PB_CR1 = 0XFF; /定义推挽输出PB_CR2 = 0XFF; /定义快速输出/*-*/while (1)display_SMG(); /用一个数码管显示数字/* 数码管显示函数 */void display_SMG(voi
26、d) unsigned int i;/*用一个数码管显示 0-9 */for(i = 0; i 10; i+) PB_ODR = SMG_tablei;delay(50000);/* 延时函数 */void delay(unsigned int delay_time)while (delay_time != 0)delay_time-;程序分析:共阳极数码管,顾名思义,就是公共端接到阳极(正极) , 改变另一端的电平高低来控制其各个段的亮灭,不同的亮灭组合就出现了不同的数字。程序中的 SMG_table 就是一个存放 09 亮灭组合的数组。用 for 语句循环十次,显示数字 09。这里要注意的
27、是,在显示一个数字时,必须保持其亮一段时间,如果数字的变化太快则肉眼无法识别,只能看到一个 8 字。4.5.2 STM8 驱动 8 个数码管显示生日4.5.3 之前,我们用 8 个 I/O 口引脚控制了 1 个数码管的显示,那我们想控制 8 个数码管,是不是要用 64 个 I/O 口引脚呢?当然不是啦,教你一招,用 16 个引脚和 8 个三极管就能控制 8 个数码管。如 图 4-12【所示。图 4-12 数码管动态显示 8 个数码管我这一招的秘诀其实是利用了人眼的“视觉暂留”效应实现的。原理是用 PB 口控制数码管显示显示的内容,用 PG 口控制哪个数码管亮,其实任意时刻只有一个数码管在亮,一
28、个灭了另一个再亮,这样轮回显示。当数码管的亮灭达到一定频率时,肉眼无法识别,认为 8 个数码管一起亮的,这就是动态显示。这里的三极管起到了两个作用,一是放大作用,二是开关作用。用 PG 口控制三极管的通断,导通的三极管对应的数码管有电流流过,就能发亮显示,并且将电流放大,将一个管脚的输出电流放大提供给 1 个数码管中的 8 段 led发光。程序代码:#include “stm8s208r.h“/* - 函数声明 -*/void delay(unsigned int delay_time); void display_SMG(void);/* SMG_table 共阳数码管段选编码*/unsig
29、ned char SMG_table10=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90;/* 对应数码管显示数字 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9 */main() /*端口初始化-*/PB_DDR = 0XFF; /定义 PB 口为输出,用于控制数码管段选PB_ODR = 0XFF; /定义 PB 初始输出为高,数码管初始不亮PB_CR1 = 0XFF; /定义推挽输出PB_CR2 = 0XFF; /定义快速输出PG_DDR = 0XFF; /定义 PG 口为输出,用于控制数码管位选PG_ODR = 0XFF;
30、/定义 PG 初始输出为高,数码管初始不亮PG_CR1 = 0XFF; /定义推挽输出PG_CR2 = 0XFF; /定义快速输出/*-*/while (1)display_SMG(); /用数码管显示数字/* 数码管显示函数 */void display_SMG(void) /* 第 8 个数码管显示 2 */PB_ODR = SMG_table2; /段选,让全部数码管显示 2PG_ODR = 0X7F; /位选,0111 1111 让第 8 个数码管亮delay(500);/* 第 7 个数码管显示 0 */PB_ODR = SMG_table0; /段选,让全部数码管显示 0PG_OD
31、R = 0XBF; /位选,1011 1111 让第 7 个数码管亮delay(500);/* 第 6 个数码管显示 1 */PB_ODR = SMG_table1; /段选,让全部数码管显示 1PG_ODR = 0XDF; /位选,1101 1111 让第 6 个数码管亮delay(500);/* 第 5 个数码管显示 1 */PB_ODR = SMG_table1; /段选,让全部数码管显示 1PG_ODR = 0XEF; /位选,1110 1111 让第 5 个数码管亮delay(500);/* 第 4 个数码管显示 0 */PB_ODR = SMG_table0; /段选,让全部数码管
32、显示 0PG_ODR = 0XF7; /位选,1111 0111 让第 4 个数码管亮delay(500);/* 第 3 个数码管显示 1 */PB_ODR = SMG_table1; /段选,让全部数码管显示 1PG_ODR = 0XFB; /位选,1111 1011 让第 3 个数码管亮delay(500);/* 第 2 个数码管显示 1 */PB_ODR = SMG_table1; /段选,让全部数码管显示 1PG_ODR = 0XFD; /位选,1111 1101 让第 2 个数码管亮delay(500);/* 第 1 个数码管显示 0 */PB_ODR = SMG_table0; /
33、段选,让全部数码管显示 0PG_ODR = 0XFE; /位选,1111 1110 让第 1 个数码管亮delay(500);/* 延时函数 */void delay(unsigned int delay_time)while (delay_time != 0)delay_time-;程序分析:与一个数码管显示不同,显示 8 个数码管不仅有段选(即显示的内容) ,还有位选(即选择让哪个数码管亮) ,位选和段选配合使用,每个数码管亮的内容都是相同的,每次只让一个数码管亮,一个数码管灭了之后另一个马上亮,同时显示内容发生变化,依次由右向左亮灭,这样 8 个数码管跑起来了。值得注意的是,这里的延时
34、delay(500)不应太长,不宜太短,若延时太长,数码管显示看起来就是一个一个分别亮的,不能达到同时亮的效果。如果延时太短,数码管显示就会很暗,效果不好。4.6 独立按键的应用独立按键在单片机中应用非常广泛,用按键输入信息,数码管显示信息,是最简单的一种人机交互方式。STM8 输入总共有 4 种输入方式,分别是:上拉输入,中断上拉输入,悬浮输入,中断悬浮输入。我们在检测按键时,为了得到稳定的电平,需要用上拉电阻,我们可以通过程序设置启用芯片内部的上拉电阻,也可以设置为悬浮输入,然后接外部上拉电阻。 表 4-2为 STM8 输入方式配置表。配置模式 数据方向寄存器 DDR 控制寄存器 1CR1
35、 控制寄存器 2CR2 配置模式0 0 0 悬浮输入0 1 0 上拉输入0 0 1 中断悬浮输入输入0 1 1 中断上拉输入表 4-2 输入模式配置4.6.1 启用内部上拉检测按键状态接下来我们就设计一个实验,用一个按键控制数码管显示数字的变化,当 PE0 所接的独立按键按下时,数码管中显示的数字加 1,显示 09 范围的数字。硬件电路:470*8f9g1ed2K3cDP5b6aSMGVCNBvsio_Ar-TE图 4-13 启用内部上拉电阻时接独立按键程序代码如下:#include “stm8s208r.h“/* - 函数声明 -*/void delay(unsigned int delay
36、_time); void display_SMG(void);void key_press(void);/*-*/* SMG_table 共阳数码管段选编码*/unsigned char SMG_table10=0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90;/* 对应显示数字 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 */unsigned char Number; /显示的数字main()/*端口初始化-*/PB_DDR = 0XFF; / 1111 1111 定义 PB 口为输出,用于控制数码管段选PB_ODR = 0XF
37、F; / 1111 1111 定义 PB 初始输出为高,数码管初始不亮PB_CR1 = 0XFF; / 1111 1111 定义推挽输出PB_CR2 = 0XFF; / 1111 1111 定义快速输出PE_DDR / 1111 1110 定义 PC 口为输入,检测按键输入PE_CR1 |= 0X01; / 0000 0001 定义为上拉输入,芯片内部启用上拉电阻PE_CR2 / 1111 1110 定义不使能中断/*-*/while (1)key_press(); /用查询法判断按键是否按下display_SMG(); /用数码管显示 Number 值/* 按键处理函数 */void key
38、_press()/* 判断按键是否按下 */if(PE_IDR /延时 10-20ms 去除抖动 if(PE_IDR else Number = 0; /* 数码管显示函数 */void display_SMG(void) /*用一个数码管显示 Number */PB_ODR = SMG_tableNumber;/* 延时函数 */void delay(unsigned int delay_time)while (delay_time != 0)delay_time-;程序分析:这个程序就是在之前用一个数码管显示数字的基础上加了独立按键控制显示内容,注意端口初始化中 PE0 口的配置,PE_D
39、DR /1111 1111 定义 PB 口为输出,用于控制数码管段选PB_ODR = 0XFF; /1111 1111 定义 PB 初始输出为高,数码管初始不亮PB_CR1 = 0XFF; /1111 1111 定义推挽输出PB_CR2 = 0XFF; /1111 1111 定义快速输出PE_DDR /1111 1110 定义 PE 口为输入,检测按键输入PE_CR1 /1111 1110 定义为浮空输入,需要外部接上拉电阻PE_CR2 /1111 1110 定义不使能中断/*-*/更改了程序,我们重新下载,实验一下,在配置浮空方式并且外部没有启用上拉电阻的情况下,独立按键非常不稳定,不能正确
40、的反应按键的状态。这时候我们就需要外加上拉电阻使 PE0 得到稳定的电平。如 图 4-14 所示 70*8f9g1ed2K3cDP5b6aSMGVCNBvsio_ArTE图 4-14 悬浮输入外部加上拉电阻在原先浮空输入的基础上,PE0 引脚接了 10k 的上拉电阻。当没有按键按下时,PE0 引脚因为有上拉而得到高电平,当按键按下时,PE0 和 GND 相连得到低电平。我们通过外部自己加上拉电阻,将原本浮空、不确定电平的引脚变成了确定的高电平或者低电平,保证了按键的可靠性和稳定性。对比总结:经过“上拉输入”和“浮空输入”的对比实验,我可以得出结论:上拉输入可以使引脚在不接任何东西的状态下得到确
41、定的电平,而浮空输入在不接任何东西的情况下电平不确定,在接按键或者一些其他需要判断引脚高低电平的场合,最好启用内部上拉电阻。4.7 使用固件库点亮神灯4.7.1 什么是固件库“世界因懒人而变化!”人们因为懒得走路而发明了汽车,因而,因而,因懒得直接操作寄存器而制作的固件库。每初始化一个 I/O 口就需要操作相应的 DDR、CR1、CR2 寄存器,使用的时候就需要操作 ODR、IDR 寄存器,有没有办法简化一些呢?有,那就是编写一个函数,每次想用 I/O 口的时候就调用这些函数,当这样的函数编多了,组合起来,就成了固件库。固件库一般由芯片生产厂商提供。接下来,我们就看看意法半导体为 STM8s
42、做的固件库(下载地址:)文件“stm8s_fwlib_um.chm”是库函数的帮助文件, “version.txt”是版本信息,文件夹“FWLib”下面又有三个文件夹:“examples”中包含的是各功能模块的例子,“library”中包含的就是今后要用到的库了, “project”则包含了一个例子工程。4.7.2 建立第一个包含库的工程第一步:按常规方法建立空工程。第二步:复制“library”文件夹复制到前面的工程文件夹中,复制“project”文件夹中的“main.c” 、 “stm8s_conf.h”到工程文件夹中。第三步:在“Source Files”上点右键,选择“Add File
43、s to Folder.”选择“librarysrc”中的“stm8s_gpio.c” 。第四步:选择“Project”下的“Settings.”或使用快捷键“Shift+F7”打开“Project Settings”对话框,选择“C Compiler”选项卡,如图选择“Preprocessor” ,在出现的页面中添加路径“libraryinc” ,如图所示。完成:点击编译或快捷键 F7,出现“0 error(s), 0 warning(s)”就成功了!4.7.3 重新点亮神灯工程建立好了,让我们重新点亮神灯吧。首先,在工程列表的“External Dependencies”文件夹中,打开“s
44、tm8s.h”确定第45 行的“#define STM8S208”未注释。45 #define STM8S20846 /* #define STM8S207 */47 /* #define STM8S105 */ 48 /* #define STM8S103 */接着,打开“stm8s_conf.h” ,去掉第 80 行的“#define _GPIO (1) ”的注释。79 /* GPIO */80 #define _GPIO (1) 8182 /* I2C */83 /* #define _I2C (1) */去掉“main.c”文件的只读属性,下面就开始编写主程序。#include “stm8s.h“void delay(unsigned int time);void main(void)/* 设置 PB0 为推挽高速输出,初始值为高电平 */GPIO_Init(GPIOB, GPIO_PIN_0, GPIO_MODE_OUT_PP_HIGH_FAST);while (1)/* 置低 PB0,点亮 LED */GPIO_WriteLow(GPIOB, GPIO_PIN_0);delay(50000);/*