(P1口最简单,所以这里只介绍P1,其他IO口原理类似)
P1口原理
可见P1口的工作原理比较简单。 先用P1口做输入输出比较容易理解。
1. 内部总线:是内部P1.X位寄存器的值。 例如内部总线P1.0电压为0V,则对应P1.0=0; =1;
2. P1.X引脚:对应单片机引脚接口
3. 读锁存器:读锁存器为1,允许读锁存器。 为0时,不允许读取锁存器。
4. 读引脚:如果为0,则不允许读引脚,如果为1,则允许读引脚。
5、写锁存:提供上升沿锁存数据(向单片机IO口写入数据时自动提供脉冲)
几个核心问题:
1. 读锁存器和读引脚有什么区别?
读锁存器:读取锁存器Q的电平
读引脚:读取P1.X引脚电平
2. 读锁存器和读引脚可以同时读取吗?
不可以,同时只能打开两个输入缓冲区之一,因此只能同时读取一层。
3. 何时读取锁存器以及何时读取引脚?
所有属于读-修改-写模式的指令从锁存器读取信号,其他指令从端口引脚线读取信号。也就是说,只有遇到读指令时才会打开相应的输入缓冲区,通常处于关闭状态。
4、如果P1.0口初始设置为1,然后用按键拉低,松开按键后P1.0口会处于低电平吗?
不会,锁存器锁定1,写0之前总是输出1。按下按钮时,P1.0引脚变低,松开后仍然为高。
(有了以上知识,我们就可以轻松解决很多问题了)
按键输入
1.按键抖动
由于按钮的机械结构,按下时不可避免地会产生振动。 一般情况下,按下和松开时会出现振动。 振动时间约为10ms。
2.打开proteus仿真并画出电路
功能:一键控制一个发光二极管。
这可以说是最简单的按键输入实验了!
由于它是51单片机,它有一个内部上拉电阻,因此我们不想浪费材料将上拉电阻连接到按钮。
3.打开keil,编写以下代码
#include
sbit key=P1^0; //定义key为P1.0
sbit led=P2^0; //定义LED为P2.0
void delay10(void) //延时10ms
{
int n=1000;
while(n--);
}
void main(void)
{
while(1)
{
if(key==0) //读P1.0引脚,如果引脚为低电平,则进入if
{
delay10(); //延时10ms消抖
if(key==0) //再次判断按键是否按下,防止干扰,增强稳定
{
led = !led;//led状态改变
while(key==0);//等待按键松开,防止往下执行
}
}
}
}
博主有一个疑问,很困惑。 当“key”改为“P2^0”时,程序将无法正常运行。 有了解的人可以帮我解释一下吗?
烧录单片机后,我们会发现按下按钮时LED会熄灭,再次按下时LED会再次点亮。
4、程序升级
! ! !
不知道小伙伴们有没有发现这段代码的缺点。 好的代码不能有延迟。 对于51单片机来说,10ms的延时影响并不大,但是你可以做一句“while(key==0);//等待按钮松开”,我想说的是,如果你代码多一点,你就会成为一个傻瓜程序。 一旦按钮按下,其他子程序就基本结束了。
接下来我们看看博主是如何摆脱掉“while(key==0);//等待key被释放”这个该死的程序的
#include
sbit key=P1^0; //定义key为P1.0
sbit led1=P2^0; //定义LED为P2.0
sbit led2=P2^7; //定义LED为P2.0
void delay_ms(unsigned int t) //ms延时
{
unsigned int i,j;
for(i=0; i<t; i++)
for(j=0; j<120; j++);
}
void keyscan()
{
static int key_up=1; //按键松开标志位,
if(key==0 && key_up==1)//判断按键是否按下
{
delay_ms(10);//延时消抖
key_up=0;//按下状态,(防止循环执行按键控制程序)
if(key==0) //再次判断,排除是松开状态或外界杂波干扰
{
led1=!led1;
}
}
else if(key==1) key_up=1;
}
void main(void)
{
int count=0; //计数
while(1)
{
keyscan();
count++;
if(count==10000)
{
count=0;
led2=!led2; //程序运行led2闪烁
}
}
}
为了验证按下按钮时其他程序仍然可以运行,我添加了一个绿色LED。 闪烁表示程序运行正常。 按下按钮不会影响绿色LED,黄色LED也可以用同样的方式控制。
4*4矩阵按键输入
代码稳定!
代码1:按下按钮显示键值,松开不显示
#include
//分别显示0 1 2 3 4 5 6 7 8 9 A b c d E F -
char sg[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40};
void delayms(unsigned int t)
{
unsigned int i,j;
for(i=0; i<t; i++)
for(j=0; j<120; j++);
}
int keyscan(void)//返回键值
{
int val=16;//无按键按下默认键值16吧
P1 = 0xf0;
if(P1!=0xf0)
{
delayms(10); //按键消抖
switch(P1) //行扫描
{
case 0xe0:
val = 0; break;
case 0xd0:
val = 1; break;
case 0xb0:
val = 2; break;
case 0x70:
val = 3; break;
}
P1 = 0x0f;
switch(P1) //列扫描
{
case 0x0e:
val += 0; break;
case 0x0d:
val += 4; break;
case 0x0b:
val += 8; break;
case 0x07:
val += 12; break;
}
}
return val;
}
void main(void)
{
/*在这里定义初始化防止循环执行时循环初始化*/
char keyval=0;
while(1)
{
keyval = keyscan();//按键扫描
P0 = sg[keyval]; //数码管输出
}
}
代码效果:
代码2:按下按钮,松开按钮后显示键值
#include
//分别显示0 1 2 3 4 5 6 7 8 9 A b c d E F -
char sg[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40};
void delayms(unsigned int t)
{
unsigned int i,j;
for(i=0; i<t; i++)
for(j=0; j<120; j++);
}
int val=0;//使用全局变量定义键值
void keyscan(void)
{
P1 = 0xf0;
if(P1!=0xf0)
{
delayms(10); //按键消抖
switch(P1) //行扫描
{
case 0xe0:
val = 0; break;
case 0xd0:
val = 1; break;
case 0xb0:
val = 2; break;
case 0x70:
val = 3; break;
}
P1 = 0x0f;
switch(P1) //列扫描
{
case 0x0e:
val += 0; break;
case 0x0d:
val += 4; break;
case 0x0b:
val += 8; break;
case 0x07:
val += 12; break;
}while(P1!=0x0f); //等待按键松开
}
}
void main(void)
{
//在这里定义初始化防止循环执行时循环初始化
while(1)
{
keyscan();//按键扫描
P0 = sg[val]; //数码管输出
}
}
嵌入式物联网的学习之路很漫长,很多人因为学习路径错误或者学习内容不够专业而错失高薪offer。 不过不用担心,我给大家整理了一份150多G的学习资源,基本涵盖了嵌入式物联网学习的所有内容。 点击下方链接0元领取学习资源,让您的学习之旅更加顺利! 记得点赞、关注、收藏、转发!
点此找助手0元获取:扫码进群获取资料