大纲
在《51单片机编程学习笔记——管脚输出》一文中,我们了解了管脚和寄存器之间的关系。我们可以给寄存器的一些位设置值,然后控制管脚的电平,以达到给器件传输信号的目的。
实际上,这些管脚除了有输出功能,还有输入功能。即我们可以通过器件影响寄存器的值,从而给单片机获取输入信息的机会。
我们可以使用开发板上的“独立按键模块”来学习该知识点。
在学习之前,我们需要知道P0~P3管脚在复位情况下的电平状态。这将影响到后续编码时我们对默认状态的考虑和理解。
- P0 :在复位后,P0 口的管脚处于高阻态,既不是高电平也不是低电平。这是因为 P0 口在作为通用 I/O 口使用时,内部没有上拉电阻,其电平状态取决于外部电路的连接情况。当 P0 口作为地址 / 数据总线复用口时,在不进行数据传输时,其状态不确定。
- P1:复位后,P1 口的管脚默认输出高电平。这是因为 P1 口内部具有上拉电阻,在上电复位后,寄存器初始值为全 1,使得管脚呈现高电平状态。
- P2:与 P1 口类似,P2 口复位后管脚默认输出高电平。因为 P2 口内部也有上拉电阻,上电复位后,其端口寄存器初始化为全 1,所以管脚为高电平。
- P3:P3 口同样内部有上拉电阻,复位后管脚默认输出高电平。其端口寄存器在上电复位后为全 1,使得管脚处于高电平状态,并且 P3 口的各个管脚还具有第二功能,在没有使用第二功能时,默认呈现高电平。
由上可见,除了P0,其他端口默认状态时都是高电平。
让我们回到“独立按键模块”的电路原理图。
图中4个按键的电路连接的都是P3系列的管脚。它们的另一端连接着GND。
GND 是英文 “Ground” 的缩写,在电子电路等领域中表示接地。它代表着电路中的参考电位点,通常被定义为零电位。
然后按键被按下,管脚接地会导致其对应的寄存器位中值变成0。当按键被松开,管脚不接地,会自动上电,对应的寄存器位的值恢复成1。
基于这样的认知,我们可以通过判断P30~P31的位变量值是否为0,来得知对应按键是否被按下。
我们可以设置如下规则:
- K1被按下,LED的亮灯会左移1位
- K2被按下,LED的亮灯会右移1位
- K3被按下,LED的亮灯会熄灭,之前熄灭的灯会亮起
- K4被按下,LED的灯会跑一轮马灯
对应代码如下
#include <REG52.H>
#include <intrins.h>
void Delay1ms() //@11.0592MHz
{
unsigned char i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
void Delay(unsigned int millisecond) {
unsigned int i = 0;
for (i = 0; i < millisecond; i++) {
Delay1ms();
}
}
void marquee(unsigned char from) {
unsigned char i;
for(i=0;i<8;i++) {
// P2=~(0x01<<((from+i)%8));
P2 = _cror_(P2, 1);
Delay(200);
}
}
sbit K1 = P3^0;
sbit K2 = P3^1;
sbit K3 = P3^2;
sbit K4 = P3^3;
// K1被按下,LED的亮灯会左移1位
// K2被按下,LED的亮灯会右移1位
// K3被按下,LED的亮灯会熄灭,之前熄灭的灯会亮起
// K4被按下,LED的灯会跑一轮马灯
void main() {
P2 = 0xfe;
while(1) {
if(K1 == 0) {
P2 = _crol_(P2, 1);
Delay(200);
}
if(K2 == 0) {
P2 = _cror_(P2, 1);
Delay(200);
}
if(K3 == 0) {
P2 = ~P2;
Delay(200);
}
if(K4 == 0) {
// 从当前位置开始跑马灯
marquee(j);
}
}
}
需要说明的是,电路图中给的K1和K2对应的管脚是错的。实际上,我购买的开发板上,K1对应P30,K2对应P31。
这儿有两个函数需要解释下:
_cror_
功能:对一个无符号字符型(unsigned char)变量进行循环右移操作,同样是将二进制位整体右移,移出的位补到最左边。_iror_
功能:对一个无符号整型(unsigned int)变量进行循环右移操作。循环右移意味着将变量的二进制位整体向右移动指定的位数,最右边移出的位会移到最左边。