07、点亮第一个LED

不论学习什么单片机,最简单的外设莫过于 IO 口的高低电平控制,如何在创建好的工程模板上,通过控制 51 单片机的 GPIO 使开发板上的 LED 灯点亮。即如何在程序中操作 51 单片机 的 GPIO 口输出高低电平。

一、51 单片机 GPIO 介绍

1.1、GPIO 概念

GPIO(general purpose intput output)是通用输入输出端口的简称,可 以通过软件来控制其输入和输出。51 单片机芯片的 GPIO 引脚与外部设备连接 起来,从而实现与外部通讯、控制以及数据采集的功能。不过 GPIO 最简单的应 用还属点亮 LED 灯了,只需通过软件控制 GPIO 输出高低电平即可。当然 GPIO 还可以作为输入控制,比如在引脚上接入一个按键,通过电平的高低判断按键是否按下。
开发板上使用的 51 单片机型号是 STC89C52 或 STC89C516,此芯片共有
40 引脚。

是不是所有引脚都是 GPIO 呢?当然不是,51 单片机引脚可以分为这么
几大类:
(1)电源引脚:引脚图中的 VCC、 GND 都属于电源引脚。
(2)晶振引脚:引脚图中的 XTAL1、XTAL2 都属于晶振引脚。
(3)复位引脚:引脚图中的 RST/VPD 属于复位引脚,不做其他功能使用。
(4)下载引脚:51 单片机的串口功能引脚(TXD、RXD)可以作为下载引脚
使用。
(5) GPIO 引脚:引脚图中带有 Px.x 等字样的均属于 GPIO 引脚。从引脚图可以看出,GPIO 占用了芯片大部分的引脚,共达 32 个,分为了 4 组,P0、P1、P2、P3,每组为 8 个 IO,而且在 P3 组中每个 IO 都具备额外功能,只要通过相 应的寄存器设置即可配置对应的附加功能,同一时刻,每个引脚只能使用该引脚的一个功能。

1.2、GPIO 结构框图与工作原理

1)P0 端口物理结构

P0 端口含有 8 位引脚, 下图为其中一个, 其它几个与之完全一致, 因此只需了解当中一个即可。 如下图所示

P0 端口由锁存器、 输入缓冲器、 切换开关、 一个非门、 一个与非门及场效应管驱动电路构成。

  1. 输入缓冲器
    在 P0 口中, 有两个三态的缓冲器,三态门有三个状态, 即在输出端可以是高电平、 低电平, 同时还有一种就是高阻状态( 或称为禁止状态),上面一个是读锁存器的缓冲器, 也就是说, 要读取 D 锁存器输出端 Q 的数据, 那就得使读锁存器的这个缓冲器的三态控制端( 上图中标号为' 读锁存器' 端) 有效。 下面一个是读引脚的缓冲器, 要读取 P0.x引脚上的数据, 也要使标号为' 读引脚' 的这个三态缓冲器的控制端有效, 引脚上的数据才会传输到我们单片机的内部数据总线上。
  2. D 锁存器
    构成一个锁存器, 通常要用一个时序电路,一个触发器可以保存一位的二进制数( 即具有保持功能) , 在 51单片机的 32 根 I/O 口线中都是用一个 D 触发器来构成锁存器的。图中
    的 D 锁存器, D 端是数据输入端, CP( CLK) 是控制端( 也就是时序控制信号输入端) , Q 是输出端, Q 非是反向输出端。
    对于 D 触发器来讲, 当 D 输入端有一个输入信号, 如果这时控制端 CP 没有信号( 也就是时序脉冲没有到来) , 这时输入端 D 的数据是无法传输到输出端 Q及反向输出端 Q 非的。 如果时序控制端 CP 的时序脉冲一旦到了, 这时 D 端输入的数据就会传输到 Q 及 Q 非端。 数据传送过来后, 当 CP 时序控制端的时序信号消失了, 这时, 输出端还会保持着上次输入端 D 的数据( 即把上次的数据锁存起来了) 。 如果下一个时序控制脉冲信号来了, 这时 D 端的数据才再次传送到 Q端, 从而改变 Q 端的状态。
  3. 多路开关
    在 51 单片机中, 当内部的存储器够用( 也就是不需要外扩展存储器时, 这里讲的存储器包括数据存储器及程序存储器) 时, P0 口可以作为通用的输入输出端口( 即 I/O) 使用, 对于 8031( 内部没有 ROM) 的单片机或者编写的程序超过了单片机内部的存储器容量, 需要外扩存储器时, P0 口就作为 ' 地址/数据' 总线使用。 那么这个多路选择开关就是用于选择是做为普通 I/O 口使用还是作为 ' 数据/地址' 总线使用的选择开关了。
    当多路开关与下面接通时,P0 口是作为普通的 I/O 口使用的, 当多路开关是与上面接通时, P0 口是作为' 地址/数据' 总线使用的。
  4. 场效应管输出驱动
    P0 口的输出是由两个 MOS 管组成的推拉式结构, 也就是说, 这两个 MOS 管一次只能导通一个,当 V1 导通时, V2 就截止, 当 V2 导通时,V1 截止。
  5. 与非门、 非门

2)作为 I/O 端口输出使用时的工作原理

P0 口作为 I/O 端口使用时, 多路开关的控制信号为 0( 低电平) , 看上图中的红线部份, 多路开关的控制信号同时与与非门的一个输入端是相接的, 我们知道与门的逻辑特点是" 全 1 出 1, 有 0 出 0" 那么控制信号是 0 的话, 这时与门输出的也是一个 0( 低电平) , 与门的输出是 0, V1 管就截止, 在多路控制开关的控制信号是 0( 低电平) 时,多路开关是与锁存器的 Q 非端相接的( 即 P0 口作为 I/O 口线使用)。

P0 口用作 I/O 口线, 其由数据总线向引脚输出( 即输出状态 Output) 的工作过程: 当写锁存器信号 CP 有效, 数据总线的信号→锁存器的输入端→D 锁存器的反向输出 Q 非端→多路开关→V2 管的栅极→V2 的漏极到输出端 P0.X。 前面我们已讲了, 当多路开关的控制信号为低电平 0 时, 与门输出为低电平, V1 管是截止的, 所以作为输出口时, P0 是漏极开路输出, 类似于OC 门, 当驱动上接电流负载时, 需要外接上拉电阻。

下图就是由内部数据总线向 P0 口输出数据的流程图( 红色箭头) :

3)作为 I/O 端口输入使用的工作原理

数据输入时,读 P0 口有两种情况:

情况一:读引脚:读芯片引脚上的数据, 读引脚数时, 读引脚缓冲器打开( 即三态缓冲器的控制端要有效) , 通过内部数据总线输入, 请看下图( 红色箭头) 。

4)读锁存器

通过打开读锁存器三态缓冲器读取锁存器输出端 Q 的状态, 请看下图( 红色箭头)

5)P1 端口

P1 口的结构最简单, 用途也单一, 仅作为数据输入/输出端口使用。 输出的信息有锁存, 输入有读引脚和读锁存器之分。 P1 端口的一位结构见下图:

P1 端口与 P0 端口的主要差别在于, P1 端口用内部上拉电阻 R 代替了 P0 端口的场效应管 V1, 并且输出的信息仅来自内部总线。 由内部总线输出的数据经锁存器反相和场效应管反相后, 锁存在端口线上, 所以, P1 端口是具有输出锁存的静态口。

要正确地从引脚上读入外部信息, 必须先使场效应管关断, 以便由外部输入的信息确定引脚的状态。 为此, 在作引脚读入前, 必须先对该端口写入 l。 具有这种操作特点的输入/输出端口, 称为准双向 I/O 口。 8051 单片机的 P1、 P2、 P3 都是准双向口。 P0 端口由于输出有三态功能, 输入前, 端口线已处于高阻态, 无需先写入 l 后再作读操作。

单片机复位后, 各个端口已自动地被写入了 1, 此时, 可直接作输入操作。如果在应用端口的过程中, 已向 P1 一 P3 端口线输出过 0, 则再要输入时, 必须先写 1 后再读引脚, 才能得到正确的信息。 此外, 随输入指令的不同, P1 端口也有读锁存器与读引脚之分。

6) P2 端口

P2 端口结构将下图:

P2 端口在片内既有上拉电阻, 又有切换开关 MUX, 所以 P2 端口在功能上兼有 P0 端口和 P1 端口的特点。 这主要表现在输出功能上, 当切换开关向下接通时, 从内部总线输出的一位数据经反相器和场效应管反相后, 输出在端口引脚线上; 当多路开关向上时, 输出的一位地址信号也经反相器和场效应管反相后, 输出在端口引脚线上。

对于 8051 单片机必须外接程序存储器才能构成应用电路( 或者我们的应用电路扩展了外部存储器) , 而 P2 端口就是用来周期性地输出从外存中取指令的地址(高 8 位地址), 因此, P2 端口的多路开关总是在进行切换, 分时地输出从内部总线来的数据和从地址信号线上来的地址。 因此 P2 端口是动态的 I/O 端口。输出数据虽被锁存, 但不是稳定地出现在端口线上。 其实, 这里输出的数据往往也是一种地址, 只不过是外部 RAM 的高 8 位地址。

P2 口既可作为 I/O 口使用, 也可作为地址总线使用, 通常主要用作 I/O 口使用, 地址总线使用不作分析。

7) P3 端口

P3 口是一个多功能口, 它除了可以作为 I/O 口外, 还具有第二功能, P3 端口的一位结构见下图:

P3 端口和 Pl 端口的结构相似, 区别仅在于 P3 端口的各端口线有两种功能选择。 当处于第一功能时, 第二输出功能线为 1, 此时, 内部总线信号经锁存器和场效应管输入/输出, 其作用与 P1 端口作用相同, 也是静态准双向 I/O 端口。 当处于第二功能时, 锁存器输出 1, 通过第二输出功能线输出特定的内含信号, 在输入方面, 即可以通过缓冲器读入引脚信号, 还可以通过替代输入功能读入片内的特定第二功能信号。 由于输出信号锁存并且有双重功能, 故P3 端口为静态双功能端口。

记住几点:

  1. P0 口是漏极开路, 要使其输出高电平, 必须外接上拉电阻, 通常选择 4.7K~10K 阻值。
  2. P0、 P1、 P2 几乎都用作普通 I/O 口使用, 既可作为输入, 又可作为输出。
  3. P3 口既可用作普通 I/O 口, 又可作为第二功能使用, 比如串口、 外部中
    断、 计数器等。

二、LED 简介

LED 即发光二极管。 它具有单向导电性, 通过 5mA 左右电流即可发光, 电流越大, 其亮度越强, 但若电流过大, 会烧毁二极管, 一般我们控制在 3 mA - 20mA之间, 通常我们会在 LED 管脚上串联一个电阻, 目的就是为了限制通过发光二极管的电流,因此这些电阻又可以称为" 限流电阻" 。 当发光二极管发光时, 测量它两端电压约为 1.7V, 这个电压又叫做发光二极管的" 导通压降" 。发光二极管正极又称阳极, 负极又称阴极, 电流只能从阳极流向阴极。 直插式发光二极管长脚为阳极, 短脚为阴极。 仔细观察贴片式发光二极管正面的一端有彩色标记, 通常有标记的一端为阴极。

三、硬件设计(开发板上 LED 模块电路)

如果要想 51 单片机控制 LED, 就必须通过单片机相应管脚口上输出高电平。

四、软件设计

4.1、点亮一个LED

点亮 D1 指示灯, 即让 P2.0 管脚输出一个高电平。完成后可再控制 D1 指示灯闪烁, 即间隔一段时间点亮和熄灭 D1 指示灯。直接复制前面创建好的工程模板, 在此模板基础上进行程序开发。 为了能够与开发攻略教程对应, 将复制过来的模板文件夹重新命名为" 2-点亮一个

LED" 。 打开工程直接在 main.c 源文件内进行编程, main.c 内代码如下:

复制代码
/**********************************************************************
实验名称:点亮第一个LED
接线说明:	
实验现象:下载程序后"LED模块"的D1指示灯熄灭
注意事项:																				  
**********************************************************************/
#include "reg52.h"
sbit LED1=P2^0;	//将P2.0管脚定义为LED1

/********************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
********************************************************************/
void main()
{
	LED1=0;	//LED1端口设置为低电平,
	while(1)
	{
	}	
}

注意几个数据的意义:

  • code: 表示程序所占用 FLASH 的大小
  • data: 数据储存器内部 RAM 占用大小。
  • xdata: 数据储存器外部 RAM 占用大小。

一定要注意的是程序的大小不是.hex 文件的大小, 而是编译后的 code 和 data 之和。

4.2、LED 闪烁实验

要实现 LED 闪烁, 只需循环让 D1 指示灯先亮一会后熄灭。 这里就有一个延时问题, 如何来产生延时呢? 我们知道单片机执行每条代码指令都是需要时间的, 因此只需编写一个循环函数, 让CPU 不干其它事, 专门在那循环运行即可实现延时功能。依据人的肉眼余晖效应,延时时间不能太短, 否则就无法观察到 LED 闪烁。

cpp 复制代码
/*******************************************************************************
* 函 数 名       : delay_10us
* 函数功能		 : 延时函数,ten_us=1时,大约延时10us
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
	while(ten_us--);	
}

上述代码即为延时函数, 通过 while 循环来实现。 函数入口有一个形式参数ten_us, 如果 ten_us 等于 1, 则 while 循环执行一次, 调用该函数延时时间大约 10us, 当然使用循环来实现延时, 这种延时是不精确的, 目前我们先得到个大概的时间即可。 函数形参 ten_us 是 u16 类型的, 这个似乎不是 C 语言数据类型关键字, 这是重定义的数据类型。

cpp 复制代码
typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;

关键字 typedef 对系统默认数据类型 unsigned int 和 unsigned char重新命名, 主要是方便我们代码的书写和变量类型的查看。 u16 即代表该变量是16 位的无符号整型数据, u8 代表该变量是 8 位的无符号字符型数据。 有了这个就知道参数的传送范围, 不能超过形参定义的范围。

4.3、通过 KEIL 软件仿真查看延时时间

如何来验证延时呢? 可以通过 KEIL 自带的软件仿真功能, 操作如下:

1)打开实验工程, 点击魔术棒, 选择" Target" 选项卡, 在 Xtal(MHz)文本框中输入 12M, 该值表示开发板上实际使用外部晶振大小, 如果开发板上使用外部晶振是 11.0592M, 则修改为对应值。 然后点击 OK。

2) 点击仿真按钮,进入仿真界面

3)点击 RST 按钮, 重新复位系统参数, 此时参数列表中 sec 则为 0, 然后在

所要查看调试的代码数字前面用鼠标左键双击即可出现" 红色块" , 我们称之为

断点。 如果再次双击, 即可取消该断点。 当点击红色标记 8 运行的时候就能直接

运行到我们设置的断点处。记录此时时间 t1。

4) 下一步,在此记录时间 t2,t2 - t1得到 50000 的延时时间,大概为450ms。

4.4、LED 流水灯实验

如果要实现 LED 流水灯, 只需循环让 D1-D8 指示灯逐个点亮。 同样本实验也需要延时。要实现循环点亮, 可以使用最容易理解的方法: 点亮 D1 且把 D2-D8 熄灭, 延时一段时间后再点亮 D2 且把 D1、D3-D8 熄灭, 延时一段时间后再点亮 D3 且把 D1-D2、 D4-D8 熄灭, 如此循环, 这样就可以很简单的实现 LED 流水灯实验, 当然我们不推荐此种方法。 在前面我们学习了 C 语言相关的基础知识, 里面有移位以及循环语句操作, 根据流水灯实现原理, 即 IO 口由低往高或者由高往低逐个输出低电平特点, 那么我们可以将移位操作以及循环结合进来。

方法一:移位+循环

cpp 复制代码
for(i=0;i<8;i++)
{
		LED_PORT=~(0x01<<i);	//将1右移i位,然后取反将结果赋值到LED_PORT
		delay_10us(50000);
}

方法二:使用 crolcror

cpp 复制代码
for(i=0;i<7;i++)	 //将led左移一位
		{									  
			LED_PORT=_crol_(LED_PORT,1);
			delay_10us(50000); 	
		}
		for(i=0;i<7;i++)	//将led右移一位
		{
			LED_PORT=_cror_(LED_PORT,1);
			delay_10us(50000);	
		}	

此处每个 for 循环只有 7 次, 为什么不是 8 次呢, 这是因为在进入 main 开始, 就已经将 LED_PORT 端口设置了一次状态, 即让 D1 点亮, 并且我们是想让 LED 从左至右依次点亮, 然后继续又从右至左依次点亮, 这样形成左右流水效果。 下一个循环右移时就可以从最高位开始

往低位移动, 从而实现左右流水灯效果。

五、实验现象

使用 USB 线将开发板和电脑连接成功后( 电脑能识别开发板上 CH340 串口) ,把编译后产生的.hex 文件烧入到芯片内。

相关推荐
优信电子4 小时前
ESP32 I2S音频总线学习笔记(二):I2S读取INMP441音频数据
单片机·嵌入式
第二层皮-合肥5 小时前
坦电容做电源滤波,放在陶瓷电容的前面还是后面好
单片机·嵌入式硬件
知识充实人生10 小时前
时序收敛方法一:控制集优化
stm32·单片机·fpga开发
点灯小铭17 小时前
基于单片机的多模式自动洗衣机设计与实现
数据库·单片机·嵌入式硬件·毕业设计·课程设计
La Pulga18 小时前
【STM32】RTC实时时钟
c语言·stm32·单片机·嵌入式硬件·mcu·实时音视频
m0_5557629018 小时前
STM32产品程序测试完整指南
stm32·单片机·嵌入式硬件
普中科技18 小时前
【普中Hi3861开发攻略--基于鸿蒙OS】-- 第 26 章 WIFI实验-AP 建立网络
单片机·嵌入式硬件·wifi·liteos·hi3861·普中科技
普中科技18 小时前
【普中Hi3861开发攻略--基于鸿蒙OS】-- 第 24 章 OLED液晶显示实验
单片机·嵌入式硬件·oled·liteos·hi3861·普中科技
冻结的鱼19 小时前
STM32H5 的 PB14 引脚被意外拉低的问题解析
stm32·单片机·嵌入式硬件