实验之前要了解两个知识点:按键是否被按下的判断 和 消抖
如图所示K1健右边接地,左边与P31引脚相连并且有个上拉电阻。
如何判断按键是否被按下: 笔者的理解如下,如果有误请评论区告知我,谢谢!
当K1键未被按下,右边是断开状态,P31因为上拉电阻的存在显示为高电平。
当K1键被按下时,右边是联通状态,P31因为右边接地有通路,电压明显减少,显示为低电平。
消抖:因为按键的时候会形成一会高电平一会低电平,不好判断按键是否被按下,如下图所示:
实验一:用KEY1键翻转LED1的状态(资料代码):
#include "reg51.h"
#include "intrins.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit KEY1 = P3^1; // 定义键位
sbit KEY2 = P3^0;
sbit KEY3 = P3^2;
sbit KEY4 = P3^3;
sbit LED1 = P2^0; // 定义LED灯
#define KEY1_PRESS 1 // 宏定义一个标志 KEY1_PRESS 为KEY1的返回值
#define KEY2_PRESS 2
#define KEY3_PRESS 3
#define KEY4_PRESS 4
#define KEY_UNPRESS 0
void delay10us(u16 ten_us) // 延时程序
{
while(ten_us--);
}
u8 key_scan(u8 mode) // 扫描按键是否被按下,在主程序while中就是一直在扫描
{
static u8 key = 1; // 设置静态变量key来决定是否要扫描 静态变量的意义在于key的值是保留每一次的赋值操作 key = 1这里只是赋了个初值,只运行一次
if(mode) key = 1; // mode为1,表示为持续扫描。mode为0,表示为一次扫描。
if(key = 1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0)) // 当key为1 并且按键有被按下,执行下面的操作 返回对应的值 函数结束
{
delay10us(1000);
key = 0;
if(KEY1==0)
return KEY1_PRESS;
else if(KEY2==0)
return KEY2_PRESS;
else if(KEY3==0)
return KEY3_PRESS;
else if(KEY4==0)
return KEY4_PRESS;
}
else if(KEY1==1 && KEY2==1 && KEY3==1 && KEY4==1) // 无按键按下或者按键已经结束,此时所有的按键处于打开状态 将key复位便于下次扫描
{
key = 1;
}
return KEY_UNPRESS;
}
void main()
{
u8 key=0;
while(1)
{
key=key_scan(0);
if(key==KEY1_PRESS) // 检测KEY1是否按下
LED1=!LED1; // LED1状态翻转
}
}
实验一:用KEY1键翻转LED1的状态(简单版):
#include "reg51.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit KEY1 = P3^1;
sbit KEY2 = P3^0;
sbit KEY3 = P3^2;
sbit KEY4 = P3^3;
sbit LED1 = P2^0;
void delay10us(u16 ten_us)
{
while(ten_us--);
}
void main()
{
while(1)
{
if(KEY1==0) // 判断KEY1是否被按下
{
delay10us(1000); // 消抖
while(KEY1==0); // 循环判断
LED1=!LED1; // 翻转LED灯状态
}
}
}
实验二:用KEY1、KEY2 控制 数码管时间钟 秒(sec)的加减:
学习视频:11.按键应用调整时间之初体验_哔哩哔哩_bilibili
如何用数码管显示可以走动的时间,可参考《51单片机入门实验代码——独立按键实验》实验四:
#include "reg51.h"
#include "intrins.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit KEY1 = P3^1; // 定义KEY1键
sbit KEY2 = P3^0;
sbit KEY3 = P3^2;
sbit KEY4 = P3^3;
#define SMG_A_DP_PORT P0 // 宏定义数码管
u8 gsmg_code[18] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,
0x77,0x7c,0x39,0x5e,0x79,0x71,0x00,0x40}; // 多添加了横杠-:0x40
u8 date_code[8] = {16,16,17,16,16,17,16,16}; // 初始化晶体管
u16 hour=12, min=59, sec=22; // 设置时间变量初值
u8 count = 0;
void delay10us(u16 ten_us) // 延时程序
{
while(ten_us--);
}
jisuan() // 将时间变量计算到date_code数组中,最后输入到P0
{
date_code[0] = sec%10; // 最低两位显示 秒
date_code[1] = sec/10;
date_code[3] = min%10; // 中间两位显示 分
date_code[4] = min/10;
date_code[6] = hour%10; // 最高两位显示 时
date_code[7] = hour/10;
}
void main()
{
u8 i=0;
jisuan(); // 初始化时间初值
while(1)
{
for(i=0;i<8;i++) // 循环 位控制LED的显示位
{
P2 = i<<2; // i为几,LED第几位显示
SMG_A_DP_PORT = gsmg_code[date_code[i]]; // LED显示数值
delay10us(125);
SMG_A_DP_PORT = 0x00; // LED消除上次显示的光影
}
//count++; // 这里与实验四不同的是count++被注释掉了 所以永远不会执行后面的if(count==100)
if(count==100) // for每循环执行一次 要8*1.25ms=10ms 当count为100时,用时100*10ms=1s (我猜的理论上是这样)
{
count=0;
sec++;
if(sec==60) // 用时1s后sec+1,加到60 min+1,以此类推
{
sec=0;
min++;
if(min==60)
{
min=0;
hour++;
if(hour==24)
{
hour=0;
}
}
}
jisuan(); // sec、min、hour计算完成后保存到date_code数组,最后在while下次循环中写到数码管中
}
if(KEY1 == 0) // 通过KEY1来将秒(sec)加一 但是可以加到61 后续会优化
{
delay10us(1000); // 消抖
while(KEY1 == 0);
sec++;
jisuan();
}
if(KEY2 == 0) // 通过KEY2来将秒(sec)减一
{
delay10us(1000);
while(KEY2 == 0);
sec--;
jisuan();
}
}
}
实验三:用KEY1来移动设置位,并用KEY2 设置数码管时间钟:
学习视频:12.按键应用调整时间之完结版(时钟调整时间)_哔哩哔哩_bilibili
#include "reg51.h"
#include "intrins.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit KEY1 = P3^1; // 定义KEY1键
sbit KEY2 = P3^0;
sbit KEY3 = P3^2;
sbit KEY4 = P3^3;
#define SMG_A_DP_PORT P0 // 宏定义数码管
u8 gsmg_code[18] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,
0x77,0x7c,0x39,0x5e,0x79,0x71,0x00,0x40}; // 多添加了横杠-:0x40
u8 date_code[8] = {16,16,17,16,16,17,16,16}; // 初始化晶体管
u16 hour=12, min=59, sec=22; // 设置时间变量初值
u8 count = 0;
u8 shezhiwei = 0; // 设置设置位来控制修改数码管的哪一位
void delay10us(u16 ten_us) // 延时程序
{
while(ten_us--);
}
jisuan() // 将时间变量计算到date_code数组中,最后输入到P0
{
date_code[0] = sec%10; // 最低两位显示 秒
date_code[1] = sec/10;
date_code[3] = min%10; // 中间两位显示 分
date_code[4] = min/10;
date_code[6] = hour%10; // 最高两位显示 时
date_code[7] = hour/10;
}
void main()
{
u8 i=0;
jisuan(); // 初始化时间初值
while(1)
{
for(i=0;i<8;i++) // 循环 位控制LED的显示位
{
P2 = i<<2; // i为几,LED第几位显示
if(count < 50 && i == shezhiwei)
SMG_A_DP_PORT = 0; // 使设置位闪烁
else
SMG_A_DP_PORT = gsmg_code[date_code[i]]; // LED显示数值
delay10us(125);
SMG_A_DP_PORT = 0x00; // LED消除上次显示的光影
}
count++; // count记录执行了多少次for循环
if(count==100) // for每循环执行一次 要8*1.25ms=10ms 当count为100时,用时100*10ms=1s (我猜的理论上是这样)
{
count=0;
if(shezhiwei==-1) // 这个if是设置完成后继续时间进行
{
sec++;
if(sec==60) // 用时1s后sec+1,加到60 min+1,以此类推
{
sec=0;
min++;
if(min==60)
{
min=0;
hour++;
if(hour==24)
{
hour=0;
}
}
}
jisuan(); // sec、min、hour计算完成后保存到date_code数组,最后在while下次循环中写到数码管中
}
}
if(KEY1 == 0) // 通过KEY1来移动设置位
{
delay10us(1000); // 消抖
while(KEY1 == 0);
shezhiwei++;
if(shezhiwei==8) // 当设置位到最高位还按了一次 设置结束 ,shezhiwei=-1 ,离开设置模式,正常进行时钟
shezhiwei=-1;
else if(shezhiwei==2)
shezhiwei++;
else if(shezhiwei==5)
shezhiwei++;
}
if(KEY2 == 0) // 通过KEY2修改设置位的数值
{
delay10us(1000);
while(KEY2 == 0);
if(shezhiwei==0)
{
sec++; sec%=60;
jisuan();
}
else if(shezhiwei==1)
{
sec+=10; sec%=60;
jisuan();
}
else if(shezhiwei==3)
{
min++; min%=60;
jisuan();
}
else if(shezhiwei==4)
{
min+=10; min%=60;
jisuan();
}
else if(shezhiwei==6)
{
hour++; hour%=24;
jisuan();
}
else if(shezhiwei==7)
{
hour+=10; hour%=24;
jisuan();
}
}
}
}
以上内容,学习所用。如有侵权,Call我必删。