1. IO介绍
51单片机总的管脚有40个,但是其中能够作为IO使用的只有32个,每8个分为一组,共4组。单片机要想实现预定功能必然要使用到各种IO口,来完成各项功能,包括点亮LED,连接按键、键盘,各种I2C、SPI设备等。51单片机,4组IO的结构略有不同,使用时应当注意。
P0属于双向IO,内部没有上拉电阻,作为输出时,最好外加上拉电阻。
P1、P2、P3属于准双向IO。“准”体现在输入时,必须先输出“1”,才能正确读到IO的输入情况。
P3口的各个IO均有复用功能:
2. IO编程
对于IO的操作无非是读输入和写输出,通过读写相应的寄存器(P0、P1、P2、P3)就可以实现。
2.1 字节寻址
字节寻址可以一次把8个IO全部访问了,使用时把Px(x=0、1、2或3)当作无符号字符变量(unsigned char)即可。
2.2 位寻址
51单片机有一类特殊的变量——位变量(bit),可以用来保存“0”或者“1”。
为了读写某个IO,可以定义特殊位变量(sbit)指定某一个IO,再进行读写
#include "reg52.h"
sbit LED=P1^0;
bit temp;
void main()
{
temp=LED;//确保temp为位变量(bit)
LED=1;
LED=0;
LED=temp;
}
3.常用器件的驱动电路
3.1 LED
单片机能够提供的电流有限,一般采用这种方式驱动LED:
当P2^0=1是,LED两端电压差位0V,LED不发光。当P2^0=1是,LED两端存在电压差,LED发光,串联电阻的作用在于限流,阻值根据电源电压和LED工作电流决定
3.2 数码管。
数码管本质是发光二极管按照一定位置排列的显示数字的器件,可分为共阴极和共阳极两类。按照数量可分为一位,两位,四位甚至八位。
一位七段共阳极数码管:
有的数码管有八段,右下角会增加一个小数点。
当驱动两位8段数码管时,按上面的方式会占用16个IO,这种方式成为静态显示。静态显示可以控制每一个数码管,但是会极大的占用IO资源,当数码管数量较多时,这种方式明显不适合。
与静态相对应的是动态显示。每一个数码管显示一段时间,然后在切换到下一个。根据人眼的视觉暂留效应,只要刷新的频率超过24Hz,在人眼中就是连续的。把每一位数码管的a,b,c,d,e,f,g,dp接在同一组IO上,再用另一组IO来控制具体显示哪一个数码管(公共端)。
下面四位共阴极数码管示例,公共端用三极管做开关:
数码管显示段码如下
unsigned char code DUMA[]={ //共阴极显示段码,共阳极取反即可
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f, //0-9
0x77,0x7c,0x39,0x5e,0x79,0x71 //a-f
};
3.3 按键
可以用这种最简单的方式连接在IO上,为了保证能正确读到输入,先输出“1”。如下图,当按键按下,P1^0=0, 当按键松开,P1^0=1。
但实际上,由于开关接触瞬间,电压会产生不稳定的跳变,称为抖动,如下图:
抖动的时间小于10ms,因此当我们检测到电平变化是,延时10ms即可。
3.4 键盘
通过把按键组成4x4矩阵,可以使用8个IO连接16个按键。当按键数量较多时,采用这种方式可以大大节约IO资源。
下面代码是这种矩阵键盘的驱动,delay10ms()根据具体的情况定义。
#define key_port P2
//检测按键的返回值,可以检测多个按键,返回一个16位的unsigned int型变量,某一个按键按下,相应位置“1”
unsigned int KeyBoard_scan(void)
{
unsigned int ms=0,value=0;
key_port=0x0f;
while(key_port!=0x0f)
{
delay10ms();
if(key_port!=0x0f)
{
key_port=0x7f;
value |= (key_port^0x7f);
key_port=0xbf;
value |= (key_port^0xbf)<<4;
key_port=0xdf;
value |= (key_port^0xdf)<<8;
key_port=0xef;
value |= (key_port^0xef)<<12;
}
key_port=0x0f;
}
return value;
}
//检测单个按键,value参数会写回相应键值0-15,返回值表示按键按下的时间,ms计
unsigned int KeyBoard(unsigned char *value)
{
unsigned int ms=0;
key_port=0x0f;
if(key_port!=0x0f)
{
delay10ms();
if(key_port!=0x0f)
{
ms=10;
key_port=0X0f;
switch(key_port)
{
case(0X07): *value=0;break;
case(0X0b): *value=1;break;
case(0X0d): *value=2;break;
case(0X0e): *value=3;break;
default: *value=0xff;
}
key_port=0Xf0;
switch(key_port)
{
case(0X70): *value=*value;break;
case(0Xb0): *value=*value+4;break;
case(0Xd0): *value=*value+8;break;
case(0Xe0): *value=*value+12;break;
default: *value=0xff;
}
while((key_port!=0xf0))
{
delay_ms_keyboard(1);
ms++;
}
return ms;
}
else *value=0xff;
}
else *value=0xff;
return 0;
}
3.5 蜂鸣器
蜂鸣器分为有源蜂鸣器和无源蜂鸣器两种。
有源蜂鸣器用低电平就可以触发,发出的声音频率不会发生变化。
无源蜂鸣器需要用脉冲触发,脉冲的频率决定了声音的频率。
无源蜂鸣器的驱动电路如下: