0、51单片机的准双向IO口
51单片机IO属于准双向IO,准双向IO的原理简化如图所示,图中的输出信号A经过非门反转变为A',此时可以将输出分为两种情况,也就是所谓的拉高/拉低;
A、当输出A为1高电平时,A'为0低电平,MOSFET-N(N沟道mos管)在这里起到一个类似于开关的作用,A'与B(GND电平默认为0)在此时均为0时,mos管处于截止状态,即上下不导通,mos管上方与下方电路是断开的,此时输入端检测到的电压信号就是VCC,即为1,IO输出也为1;
B、当输出A为0低电平,A'为1高电平时,mos管处于导通状态,此时输入点相当于接了地,故此时输入端检测到的信号为0,IO输出也为0;
因此可以看到,拉高/拉低本质上是改变mos管的通断,拉高情况下,输入端才能有效地检测到外部的低电平信号;拉低情况下,输入端才能有效检测到外部的高电平信号
1、按键状态标志
传统的按键控制一般包含一个while()循环,即当按键按下松开前,程序都会停在这一判断是否松开的语句前,知道松开再执行其他程序,这样做是为了避免单片机混淆按下的按键和松开的按键这两种状态下的看似相同的情况;因此也可以通过设置按键状态标志的方法来区分这两种情况,设置key_state_flag,假设当key_state_flag == 1时,按键状态为“按下”,为0时状态为“松开”,下面给出一个相对稳定的官方程序:
以下为扫描四个独立按键的实现方式,加入消抖后如下:
void Scan_key(void) { static unsigned char key_press_prev = 0xff, key_state_flag = 0; unsigned char key_press; key_press = P3; switch(key_state_flag) { case 0: //未按下 if(key_press != 0xff) { delay(1); if(key_press != 0xff) { key_state_flag = 1; } } break; case 1: //按下未松开 if(key_press != key_press_prev) { key_state_flag = 0; } else { key_state_flag = 2; } break; case 2: //按下后松开 if(key_press == 0xff) { if(key_press_prev == S7) k1++; if(key_press_prev == S6) k2++; if(key_press_prev == S5) k3++; if(key_press_prev == S4) k4++; key_state_flag = 0; } break; } key_press_prev = key_press; }
三个case非常直观的显示出了三种状态