LED(Light Emitting Diode)是发光二极管的简称,在很多设备上常用他来作为一种简单的人机接口,如网卡、路由器等通过LED向用户指示设备的不同工作状态。所以,我们习惯把这种用于指示状态的LED称为LED指示灯。
IK-64D4开发板上设计了4个LED指示灯,我们可以通过编程驱动LED指示灯点亮、熄灭、闪烁,从而达到状态指示的目的,LED指示灯驱动电路如下图所示。
正在上传…重新上传取消
图1:LED指示灯驱动电路
4个LED指示灯占用的单片机的引脚如下表:
表1:LED引脚分配
LED |
颜色 |
引脚 |
说明 |
D1 |
蓝色 |
P2.6 |
非独立GPIO |
D2 |
蓝色 |
P2.7 |
非独立GPIO |
D3 |
蓝色 |
P7.2 |
独立GPIO |
D4 |
蓝色 |
P7.1 |
独立GPIO |
LED指示灯驱动电路是一个很常见、简单的电路,但他也是一个典型的单元电路,对于初学者来说,类似常用的典型电路必须要掌握,不但要知其然、还要知其所以然。
接下来,我们来分析一下这个简单的LED指示灯驱动电路。
LED驱动电路设计的时候,需要我们考虑两个方面:控制方式和限流电阻的选取。
LED指示灯控制方式分为高电平有效和低电平有效两种,高电平有效是单片机IO输出高电平时点亮LED,低电平有效是单片机IO输出低电平时点亮LED。
1. 低电平有效的控制方式
低电平有效控制方式中,当单片机的GPIO输出低电平(逻辑0)的时候,LED和电阻R上的压降等于(VCC-VCCIO = 3.3V),这时候,因为存在压降,同时,这个电路是闭合回环的,这就达到了电流产生的两个要素,LED上会有电流流过,LED被点亮。
当单片机的GPIO输出高电平(逻辑1)的时候,LED和电阻R上的压降等于0V(VCC-VCCIO = 0V),这时候,因为LED上没有压降,当然不会有电流流过,所以LED熄灭。
低电平有效控制方式中,电流经过限流电阻和LED“灌入”GPIO,对于一般单片机来说,“灌电流”比较大,即低电平有效时驱动能力较强。
2. 高电平有效的控制方式
高电平有效控制方式中,由单片机的GPIO输出电流驱动LED,当单片机的GPIO输出高电平(逻辑1)的时候,LED上存在压降,会有电流流过,这时LED被点亮,但要注意,单片机的GPIO要能提供足够的输出电流,否则,电流过小,会导致LED亮度很弱。
当单片机的GPIO输出低电平(逻辑0)的时候,LED和电阻R上的压降等于0V,这时候,LED上没有电流流过,LED熄灭。
3. 选择哪种方式来控制LED
大多数情况下,我们会选择使用低电平有效的控制方式,如开发板中的LED指示灯就是低电平有效。这是因为:单片机IO低电平时的灌入电流一般比高电平时的拉电流要大,能提供足够的电流驱动LED,另外单片机上电或复位启动时,IO口一般都是高阻输入,用低电平有效的控制方式可以确保LED在上电或复位启动时处于熄灭状态。
图4:LED限流电阻计算
由上图可以看出,LED限流电阻的计算公式如下:
其中,VCC=3.3V,VF是LED的正向压降,LED的数据手册都会给出正向电流为20mA时测试的VF的范围,下图是一款0603 LED的实物图和参数。在参数表中可以看到正向电流为20 mA时VF最小值是1.6V,典型值是2.0V,最大值是2.6V。
0603 LED
计算时VF的值可以用典型值来进行估算,对于电流,需要根据经验值和对LED亮度的要求相结合来确定,一般经验值是(2~5)mA,不过要注意,只要亮度符合自己的要求,电流低于2mA也没有任何问题。
电流为2mA时限流电阻值计算如下:
计算出的电阻是650Ω,650Ω不是一个常用的电阻值,所以我们需要选择一个电阻值为650Ω左右常用的电阻器,在IK-64D4开发板上,我们选择的电阻值是最常用的1K的电阻器,选择限流电阻后,还需要实际测试观察LED的亮度是否符合自己的需求,经过实际测试观察,IK-64D4开发板上使用1K限流电阻时亮度符合我们的要求,由此,限流电阻的阻值确定为1K。当然,如果我们觉得亮度不够,可以将电阻值适当减小一些,如使用680Ω或510Ω的电阻器作为限流电阻。
STC8A8K64D4系列单片机GPIO口数量取决于芯片的具体型号和封装,开发板使用的是STC8A8K64D4,封装为LQFP64,共有64个引脚。其中,电源和ADC参考电压使用了如下5个引脚,剩余的59个引脚均可配置为GPIO使用。
STC8A8K64D4的59个GPIO分属8个端口P0~P7,如下表所示。这里,有必要说明一下,8个端口中,P4和P5的GPIO数量不是8个,其中P4是没有P4.5、P4.6和P4.7的,而P5是没有P5.6和P5.7的,读者使用GPIO时,应注意这一点。
表2:STC8A8K64D4单片机的GPIO
端口 |
GPIO |
数量 |
P0 |
P0.0~P0.7 |
8 |
P1 |
P1.0~ P1.7 |
8 |
P2 |
P2.0~P2.7 |
8 |
P3 |
P3.0~P3.7 |
8 |
P4 |
P4.0~P4.4 |
5 |
P5 |
P5.0~P5.5 |
6 |
P6 |
P6.0~P6.7 |
8 |
P7 |
P7.0~P7.7 |
8 |
STC8A8K64D4系列单片机所有GPIO口均有4种工作模式:准双向口/弱上拉(标准8051输出口模式)、推挽输出/强上拉、高阻输入(电流既不能流入也不能流出)、开漏输出,这4种工作模式是通过寄存器PnM1和PnM0的组合配置的(n为端口号)。
本章讲解是GPIO输出,和输出相关的工作模式有准双向口/弱上拉、推挽输出/强上拉、和开漏输出,接下来,我们细看一下这3种工作模式。
准双向口(弱上拉)输出类型可用作输出和输入功能而不需重新配置端口输出状态,这是因为当端口输出为 1 时驱动能力很弱,允许外部装置将其拉低。
准双向口有3个上拉晶体管(强上拉、弱上拉和极弱上拉晶体管)适应不同的需要。为了方便描述,我们将这些晶体管分为两部分,如下图所示。
准双向口(弱上拉)在读外部状态前,要先锁存为“1”,才可读到外部正确的状态。也就是作为输入时,要先向端口锁存器写入“1”,此时,A组晶体管中的3个上拉晶体管中的一个导通,B晶体管截止。
强推挽输出配置的下拉结构与开漏输出以及准双向口的下拉结构相同,但当锁存器为1时可提供持续的强上拉。所以,推挽输出一般用于需要更大驱动电流的情况。
图6:推挽输出I/O结构图
对于LED控制电路,如果采用的是高电平有效的控制方式,则控制LED的单片机GPIO口必须配置成推挽输出才可以驱动LED。
高阻输入模式下电流既不能流入也不能流出,输入口带有一个施密特触发输入以及一个干扰抑制电路。STC8A8K64D4系列单片机的 I/O 中,除了 ISP下载脚 P3.0/P3.1 为准双向口模式外,其余的所有 I/O 口在上电后都是高阻输入模式。
图7:高阻输入I/O结构图
开漏模式既可读外部状态也可对外输出(高电平或低电平),但是需要注意的是,如要正确读外部状态或需要对外输出高电平,必须外加上拉电阻。这是因为,开漏模式下单片机虽然可以写0写1,但引脚只能输出低电平,无法输出高电平,其原理如下图所示。
图8:开漏模式I/O结构图
GPIO的使用相对比较简单,使用前先配置GPIO的工作模式,之后根据配置的模式操作即可(输出:写端口数据寄存器,输入:读端口数据寄存器)。
STC8A8K64D4提供了7个用于操作GPIO的寄存器,如下表所示:
表3:GPIO相关寄存器
序号 |
寄存器名 |
功能描述 |
1 |
端口数据寄存器(Px,x=0~7) |
读写端口状态:
|
2 |
端口模式配置寄存器(PxM0,PxM1,x=0~7) |
PxM0,PxM1的组合用来配置GPIO的工作模式(准双向口/弱上拉、推挽输出/强上拉、高阻输入、开漏输出)。 |
3 |
端口上拉电阻控制寄存器(PxPU,x=0~7) |
端口内部4.1K上拉电阻控制位(注:P3.0和P3.1口上的上拉电阻可能会略小一些)。
|
4 |
端口施密特触发控制寄存器( PxNCS,x=0~7) |
端口施密特触发控制位:
|
5 |
端口电平转换速度控制寄存器(PxSR,x=0~7) |
用于控制端口电平转换的速度:
|
6 |
端口驱动电流控制寄存器(PxDR,x=0~7) |
控制端口的驱动能力
|
7 |
端口数字信号输入使能控制寄存器(PxIE,x=0~7) |
数字信号输入使能控制:
|
STC8A8K64D4的I/O模式是通过寄存器PnM1和PnM0的组合配置的,如下表所示。
表4:GPIO模式配置
PnM1 |
PnM0 |
I/O口工作模式 |
0 |
0 |
准双向口(传统8051端口模式,弱上拉),灌电流可达20mA,拉电流为270~150μA(存在制造误差)。 |
0 |
1 |
推挽输出(强上拉输出,可达20mA, 要加限流电阻)。 |
1 |
0 |
高阻输入( 电流既不能流入也不能流出)。 |
1 |
1 |
开漏输出(Open-Drain),内部上拉电阻断开。开漏模式既可读外部状态也可对外输出(高电平或低电平)。如要正确读外部状态或需要对外输出高电平,需外加上拉电阻,否则读不到外部状态,也对外输不出高电平。 |
PnM1和PnM0中的n表示的是I/O的端口,取值范围为0~7,即P0M1和P0M0用来配置端口P0中的I/O的工作模式,P1M1和P1M0用来配置端口P1中的I/O的工作模式,以此类推。
两个寄存器中的各个位对应于端口中的I/O:
P0M1 &= 0xFE; P0M0 &= 0xFE;
P0M1 &= 0x7F; P0M0 |= 0x80;
GPIO配置为输出后,即可通过写端口数据寄存器(Px,x=0~7)中对应的位,让该GPIO输出高电平或低电平,示例如下。
P0.0 = 1; //P0.0输出高电平
P0.0 = 0; //P0.0输出低电平
GPIO配置为输入后,即可通过读端口数据寄存器(Px,x=0~7)中对应的位,从而获取该GPIO的状态,示例如下。
uint8 temp; //定义一个变量用于保存读取的GPIO P0.0的状态
temp = P0.0; //读取GPIO P0.0的状态
因为在“main.c”文件中使用了STC8的头文件“STC8.h”,所以需要引用下面的头文件。
代码清单:引用头文件
一般地,在编写程序时,为了使得程序代码清晰,便于程序阅读,通常会使用“#define”命令定义引脚,也就是给这个引脚重新取个便于理解和记忆的名字。
本例中,指示灯D3使用了GPIO P7.2驱动,因此,我们将P7.2重新定义为“LED3_P72”(即指示灯D3由GPIO P7.2驱动)。
代码清单:定义驱动LED的引脚
使用“#define”命令定义引脚,有以下两个主要的好处:
因此,我们建议读者,尤其是刚入门的读者,要养成这样的编程习惯,这会提高程序开发的效率、降低出错的几率,同时,也能让你编写的程序美观、专业。
开发板的LED驱动电路使用的是低电平驱动方式,因为,我们配置P0.3为准双向口即可,代码清单如下:
代码清单:配置引脚工作模式
驱动LED指示灯D3闪烁,只需P7.2以一定的间隔输出高低电平即可达到驱动LED指示灯闪烁的目的。编写代码的时候可以使用:重复“输出高电平à延时à输出低电平à延时”的方式实现,也可以通过:重复“翻转输出状态à延时”的方式实现。
因此,我们先编写延时函数,代码清单如下:
代码清单:软件延时函数
之后,在主循环中使驱动LED指示灯D3的引脚P7.2输出高低电平即可实现D3闪烁。
代码清单:指示灯D3闪烁
用配套的USB数据线按照下图所示将开发板的连接到电脑,本例中使用P7.2控制指示灯D3,因为D3是独立GPIO控制的,所以没有短接跳线帽的操作。
图9:硬件连接
指示灯D1、D2、D3、D4分别由GPIO P2.6、P2.7、P7.2和P7.1驱动,这里,我们使用“#define”命令定义如下。
代码清单:定义驱动LED的引脚
流水灯就是按照一定的时间间隔有规律的轮流点亮LED指示灯,也就是4个LED对应的引脚轮流输出高低电平,并加上一段时间的延时,从而实现轮流点亮的效果,代码清单如下。
代码清单:流水灯
本实验需要使用4个LED指示灯,因此需要用跳线帽短接复用引脚的指示灯(D1和D2),而指示灯D3和D4是独立引脚,没有和其他电路复用引脚,是没有短接跳线帽的操作的。
图10:跳线帽短接
当我们开发的程序比较复杂的时候,通过GPIO直接操作LED显然比较麻烦,比较好的做法是将需要用到LED操作封装为函数,并将这些函数单独放到一个“.c”文件里面,其他文件中需要用到LED功能的时候,调用这些函数实现功能即可。
接下来,我们就用这种方式来实现实验2-1-2中的流水灯,读者可以体会一下,哪种方式更好。
本例中,我们需要用到LED和延时,因此,我们新建名称为“delay.c”和“led.c”的文件,分别用于存放延时和LED操作相关的函数。另外,新建对应的头文件“delay.h”和“led.h”,以便于其他程序模块使用。
“delay.c”包含的函数分别如下表所示。
表5:延时函数
文件名称 |
函数名称 |
功能 |
delay.c |
delay_ms |
毫秒延时函数(软件延时)。 |
delay10us |
10us延时函数(软件延时)。 |
“led.c”包含的函数分别如下表所示。
表6:LED操作函数
文件名称 |
函数名称 |
功能 |
led.c |
led_on |
点亮指定的LED指示灯。 |
led_off |
熄灭指定的LED指示灯。 |
|
led_toggle |
翻转指定的LED的状态。 |
|
leds_on |
点亮所有的LED指示灯。 |
|
leds_off |
熄灭所有的LED指示灯。 |
图11:添加头文件包含路径
为了软件操作方便,我们给每个指示灯按照原理图上的定义(D1、D2、D3、D4)赋予一个编号,注意这个编号不是物理引脚定义,而是为了软件操作方便,人为在软件层面对指示灯进行的编号。
代码清单:指示灯编号
LED操作的函数代码清单如下:
代码清单:led_on函数
代码清单:led_off函数
代码清单:led_ toggle函数
代码清单:leds_on函数
代码清单:leds_off函数
下面是用LED驱动函数实现的流水灯的代码清单,他的效果和“实验2-1-2:流水灯”是一样的。
代码清单:LED驱动函数实现流水灯