学习单片机最简单的外设莫过于 IO 口的高低电平控制,通过控制 51 单片机的 GPIO 使开发 板上的 LED 灯点亮,闪烁,流水灯以及双向流水灯

1、GPIO概念

GPIO(general purpose intput output)是通用输入输出端口的简称,可以通过软件来控制其 输入和输出。51 单片机芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数 据采集的功能。 GPIO 最简单的应用应该是点亮 LED 灯,只需通过软件控制 GPIO 输出高低电平 即可。当然 GPIO 还可以作为输入控制,比如在引脚上接入一个按键,通过电平的高低判断按键是 否按下。有以下几类引脚: (1)电源引脚:引脚图中的 VCC、 GND 都属于电源引脚。 (2)晶振引脚:引脚图中的 XTAL1、XTAL2 都属于晶振引脚。 (3)复位引脚:引脚图中的 RST 属于复位引脚,不做其他功能使用。 (4)下载引脚:51 单片机的串口功能引脚(TXD、RXD)可以作为下载引脚使用。 (5)GPIO 引脚:引脚图中带有 Px.x 等字样的均属于 GPIO 引脚。从STC89C52开发板的引脚图 可以看出,GPIO 占用了芯片大部分的引脚,共达 32 个,分为了 4 组,P0、P1、 P2、P3,每组 为 8 个 IO,而且在 P3 组中每个 IO 都具备额外功能,只要通过相应的寄存器设置即可配置对应的 附加功能,同一时刻,每个引脚只能使用该引脚一个功能。对于每个引脚的具体功能需要查看相关 的数据手册。下面是我在STC89C52手册上查到的部分引脚功能说明。

2、LED简介

LED 即发光二极管。它具有单向导电性,通过 5mA 左右电流即可发光,电流越大,其亮度越 强,但若电流过大,会烧毁二极管,一般我们控制在 3 mA-20mA之间,通常我们会在 LED 管脚上 串联一个电阻,目的就是为了限制通过发光二极管的电流不要太大,因此这些电阻又可以称为“限 流电阻”。单色LED只有两根引脚,这两根引脚有正、负极之分。多色的LED为 3 根引脚。发光二 极管分为直插式发光二极管和贴片式发光二极管实物图。发光二极管正极又称阳极,负极又称阴 极,电流只能从阳极流向阴极。直插式发光二极管长脚为阳极,短脚为阴极。

LED对电压也是有要求的,二极管都有导通电压,LED的供电电压如果没有达到这个导通电压值,

LED无法点亮,当LED导通时,电流才开始流过LED,且电流越大,亮度越强(但不能超过最大作电流)。

(1)OA 段。这是正向死区。UA 为开启LED发光的电压。

(2)AB 段。这是工作区。在这一区段, 一般是随着电压增加电流也跟着增加, 发光亮度也跟着增大。

(3)OC 段。这是反向死区,发光二极管加反向电压是不发光的(不工作),但有反向电流。这个反向电流通常很小,一般在几微安之内。

(4)CD 段。这是反向击穿区,LED的反向电压一般不要超过10V,最大不得超过 15V,最大反向电压。超过这个电压,就会出现反向击穿,导致发光二极管报废。

STC89C52开发板上LED模块电路如下:

可以看到图中D1-D8 连接到单片机的 P20-P27 口。并且图中 LED 采用共阳接法,即所有 LED 阳 极管脚接电源 VCC,阴极管脚通过一个限流电阻接到 P2 口上。开发板引脚上电默认为高电平1,要 让 LED 发光即对应的阴极管脚应该为低电平,若为高电平则熄灭。如果要想 51 单片机控制 LED,就必须通过单片机管脚在 P2 口上输出低电平。

3、点亮D1指示灯

查看开发板原理图LED D1指示灯对应的是引脚是P2_0,所以只需要给P2_0输出一个低电平就能使D1亮

#include sbit LED = P2^0;//将P2.0定义为LEDvoid main(){LED = 0;//点亮LED1while(1){}}

4、LED闪烁

LED已经点亮,想要它出现闪烁就是使LED一亮一灭,只需循环让 D1 指示灯先亮一会后熄灭,使

这个状态持续下去但是要想使我们人眼想要观察到需要设置一定的延时,利用人眼的余晖效应

#include sbit LED = P2^0;typedef unsigned int u16;//对系统默认数据类型进行重命名typedef unsigned char u8;void Delay(u16 ten)//延迟函数{while(ten--);}void main(){while(1){LED = 0;//点亮Delay(50000);//大约延迟450msLED = 1;//熄灭Delay(50000);}}

5、LED流水灯

LED流水灯之前我记录过一次,这次想在记录一次就当复习了。要实现 LED 流水灯,只需循环

让 D1-D8 指示灯逐个点亮。要实现循环点亮,最容易想到的方法:点亮 D1 且把 D2-D8 熄灭,延

时一段时间后再点亮 D2 且把 D1、 D3-D8 熄灭,延时一段时间后再点亮 D3 且把 D1-D2、D4-D8

熄灭,如此循环下去,IO 口由低往高或者由高往低逐个输出低电平特点。在这里有两种方法实现,一

种是利用二进制逻辑位移使不同引脚低电平,另一种是利用库函数intrins.h中的位移函数。

这种是最笨的方法,依次让每一个LED逐次亮灭

#include sbit LED1 = P2^0;sbit LED2 = P2^1;sbit LED3 = P2^2;sbit LED4 = P2^3;sbit LED5 = P2^4;sbit LED6 = P2^5;sbit LED7 = P2^6;sbit LED8 = P2^7;typedef unsigned int u16;void Delay(u16 ten){while(ten--);}void main(){while(1){//通过高低电平LED1 = 0;Delay(50000);LED1 = 1;Delay(50000);LED2 = 0;Delay(50000);LED2 = 1;Delay(50000);LED3 = 0;Delay(50000);LED3 = 1;Delay(50000);LED4 = 0;Delay(50000);LED4 = 1;Delay(50000);LED5 = 0;Delay(50000);LED5 = 1;Delay(50000);LED6 = 0;Delay(50000);LED6 = 1;Delay(50000);LED7 = 0;Delay(50000);LED7 = 1;Delay(50000);LED8 = 0;Delay(50000);LED8 = 1;Delay(50000);}}

通过位移来实现亮灭

#include #define LED_PORTP2;typedef unsigned int u16;typedef unsigned char u8;void Delay(u16 ten){while(ten--);}void main(){u8 i;while(1){//通过位移实现for(i=0;i>i); //1000 0000 -->0000 0001然后取反Delay(50000);}for(i=0;i<8;i++){P2 = ~(0x01<1000 0000然后取反Delay(50000);}}}

通过库函数intrins.h实现

#include #include "intrins.h"#define LED_PORTP2;typedef unsigned int u16;typedef unsigned char u8;void Delay(u16 ten){while(ten--);}void main(){u8 i;P2 = 0xFE;while(1){for(i=0;i<7;i++)//移动的是0的位置{P2 = _crol_(P2,1);//左移Delay(50000);}for(i=0;i<7;i++){P2 = _cror_(P2,1);//右移Delay(50000);}for(i=0;i<8;i++)//移动的是1的位置{P2 = ~(_cror_(0x01,i));//右移Delay(50000);}for(i=0;i<8;i++){P2 = ~(_crol_(0x80,i));//左移Delay(50000);}}}

使用逻辑左移高位移出(舍弃),低位的空位补0,最高位丢失,最低位补0。使用逻辑右移低位移出

(舍弃),高位的空位补0,最高位补0,最低位丢失。类似数据结构的队列一样,前面走后面来的补

上。而使用左移函数(循环左移)_crol_(移动对象,左移的位数)如果是左移,那么最高位就被移到

最低位了,次高位变为最高位。右移函数和左移函数类似。类似一个循环队列。

#include #include "intrins.h"#define LED_PORTP2;typedef unsigned int u16;typedef unsigned char u8;void Delay(u16 ten){while(ten--);}void main(){u8 i;P2 = 0xFE;while(1){for(i=0;i<7;i++)//0的位置变动{P2 = _crol_(P2,1);Delay(50000);}for(i=0;i<7;i++){P2 = _cror_(P2,1);Delay(50000);}}}

刚开始P2 = 0xFE; 对应二进制 1111 1110,最低位为0因此最开始的 D1 指示灯会点亮。循环七次

的原因是在进入 main 开始,就已经将 LED_PORT 端口设置了一次状态,即让 D1 点亮,并且我

们是想让 LED 从左至右依次点亮,然后继续又从右至左依次点亮

初始状态:LED_PORT=1111 1110

i=0:LED_PORT=1111 1101

i=1:LED_PORT=1111 1011

i=2:LED_PORT=1111 0111

i=3:LED_PORT=1110 1111

i=4:LED_PORT=1101 1111

i=5:LED_PORT=1011 1111

i=6:LED_PORT=0111 1111

i=7:LED_PORT=1111 1110

如上如有错误,谢谢指正