51单片机-独立按键,矩阵按键,点阵
独立按键
独立按键:实现原理是通过轻触按键内部的 金属弹片受力弹动来实现接通和断开
“1,2”和“3,4”管脚之间距离短,初始不导通,“1,3”和“2,4”管脚之间距离长,初始值导通。
当按键按下时, 距离短的会变为导通,距离长的会变为不导通
,所以就可以利用按键这一特性来控制其他的事物。
例如管脚1接单片机的一个引脚,管脚2接地。当按键被按下时,就会给这个引脚一个 低电平
。如果不按,单片机的这个引脚默认的是 高电平
。
但是按键一般都会抖动,所以要进行消抖: 硬件消抖和软件消抖
硬件消抖是通过 充放电延时时间
来进行消抖,但成本高,一个按键就需要(一个电阻与一个电源),所以 一般选择软件消抖,软件消抖时间一般为 10ms
。
代码如下:
# include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
//定义4个按键管脚
sbit K1=P3^1;
sbit K2=P3^0;
sbit K3=P3^2;
sbit K4=P3^3;
sbit LED1=P2^0;
//宏定义如果按下则返回
# define K1_PRESS 1
# define K2_PRESS 2
# define K3_PRESS 3
# define K4_PRESS 4
//没按下
# define K_UNPRESS 0
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
u8 Detection_Key(u8 mode)
{
static u8 key=1;
if(mode)
key=1;
if(key==1 && (K1==0 || K2==0 || K3==0 || K4==0))
{
key=0;
delay_10us(1000);//消抖
if(K1==0)
return K1_PRESS;
else if(K2==0)
return K2_PRESS;
else if(K3==0)
return K3_PRESS;
else if(K4==0)
return K4_PRESS;
}
else if(K1==1 && K2==1 && K3==1 && K4==1)
{
key=1;
}
return K_UNPRESS;
}
void main()
{
while(1)
{
u8 key=Detection_Key(0);//1代表长按和0代表只按一次
if(key==K1_PRESS)
{
LED1=!LED1;
}
}
while(1);
}
备注
刚刚还试了加个蜂鸣器,只要按下按键LED1就亮并且蜂鸣器响,结果发现不仅LED1亮了连LED6也随着蜂鸣器响而在闪,问了客服才知道原来蜂鸣器和LED6共用IO口,原本就查查开发手册的事我还搞半天,我还以为是代码还是板子有问题,这件事让我懂得查手册是多么重要!
实验现象
矩阵按键
行列描述法
行列描述法:
先让一列为 低电平
,其余几列全为 高电平
(此时我们确定了列数),然后立即轮流检测一次各行是否有 低电平
,若检测到某一行为 低电平
(这时我们又确定了行数),则我们便可确认当前被按下的键是哪一行哪一列的,用同样方法轮流送各列一次 低电平
,再轮流检测一次各行是否变为 低电平
,这样即可检测完所有的按键,当有键被按下时便可判断出按下的键是哪一个键。当然我们也可以将行线置 低电平
,扫描列是否有 低电平
。从而达到整个键盘的检测。
代码如下:
# include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
# define KEY_Matrix_PORT P1//定义矩阵管脚
# define SMG_PORT P0//数码管管脚
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
/****************************************************************************************
* 使用4*4按键
****************************************************************************************/
//行列描述法
u8 key_matrix_ranks(void)
{
u8 key=0;//键值
KEY_Matrix_PORT=0xf7;//第一列按键
if(KEY_Matrix_PORT!=0xf7)
{
delay_10us(1000);//消抖
switch(KEY_Matrix_PORT)
{
case 0x77:key=1;break;//S1
case 0xb7:key=5;break;//S5
case 0xd7:key=9;break;//S9
case 0xe7:key=13;break;//S13
}
}
KEY_Matrix_PORT=0xfb;//第二列按键
if(KEY_Matrix_PORT!=0xfb)
{
delay_10us(1000);//消抖
switch(KEY_Matrix_PORT)
{
case 0x7b:key=2;break;//S2
case 0xbb:key=6;break;//S6
case 0xdb:key=10;break;//S10
case 0xeb:key=14;break;//S14
}
}
KEY_Matrix_PORT=0xfd;//第三列按键
if(KEY_Matrix_PORT!=0xfd)
{
delay_10us(1000);//消抖
switch(KEY_Matrix_PORT)
{
case 0x7d:key=3;break;//S3
case 0xbd:key=7;break;//S7
case 0xdd:key=11;break;//S11
case 0xed:key=15;break;//S15
}
}
KEY_Matrix_PORT=0xfe;//第四列按键
if(KEY_Matrix_PORT!=0xfe)
{
delay_10us(1000);//消抖
switch(KEY_Matrix_PORT)
{
case 0x7e:key=4;break;//S4
case 0xbe:key=8;break;//S8
case 0xde:key=12;break;//S12
case 0xee:key=16;break;//S16
}
}
return key;
}
u8 gsmg_code[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,
0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//定义数组存放0-F段码,gsmg中g代表全局变量
void main()
{
u8 key=0;
while(1)
{
key=key_matrix_ranks();//接收键值
if(key!=0)//要确保key不能为0
{
SMG_PORT=gsmg_code[key-1];//数码管显示
}
}
while(1);
}
线翻转法
线翻转法:
使所有行线为 低电平
时,检测所有列线是否有 低电平
,如果有,就 记录列线值
;然后再 翻转
,使所有列线都为 低电平
,检测所有行线的值, 由于有按键按下,行线的值也会有变化,记录行线的值。从而就可以检测到全部 按键。
//线翻转法
u8 key_matrix_overturn(void)
{
u8 key=0;//键值
KEY_Matrix_PORT=0x0f;//行为低电平,测试列
if(KEY_Matrix_PORT!=0x0f)
{
delay_10us(1000);
if(KEY_Matrix_PORT!=0x0f)
{
switch(KEY_Matrix_PORT)
{
case 0x07:key=1;break;//第一列
case 0x0b:key=2;break;//第二列
case 0x0d:key=3;break;//第三列
case 0x0e:key=4;break;//第四列
}
KEY_Matrix_PORT=0xf0;//列为低电平,测试行
if( KEY_Matrix_PORT!=0xf0)
{
switch(KEY_Matrix_PORT)
{
case 0x70:key=key;break;//第一行
case 0xb0:key+=4;break;//第二行
case 0xd0:key+=8;break;//第三行
case 0xe0:key+=12;break;//第四行
}
}
}
}
else
{
key=0;
}
return key;
}
74HC595芯片
74HC595是一个8位串行输入、并行输出的位移缓存器,其中并行输出为三态输出(即 高电平、低电平和高阻抗
)
注意:74HC595是 先传输字节的高位后传输低位
,所以需要 将字节低位移动到高位传输
,在传输数据时,要注意 移位寄存器时钟和存储寄存器时钟的先后顺序
,将要写入的数据先传输到74HC595寄存器中,即在准备好每位数据时要将 SRCLK进行一个上升沿变化
,此时即可将数据传输到寄存器内, 待循环8次即一个字节传输到寄存器中时
,就可以来一个 存储时钟上升沿
,此时就可以将74HC595寄存器中的数据全部一次 传输到595端口输出
。【要注意清除寄存器缓存的数据】
代码如下:
# include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
//定义74HC595控制管脚
sbit rclk=P3^5; //存储寄存器时钟输入
sbit srclk=P3^6; //移位寄存器时钟输入
sbit ser=P3^4; //串行数据输入
# define LEDDZ_COL_PORT P0 //控制点阵列的端口
u8 ghc595_buf[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
//延时函数
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void hc595_write_data(u8 dat)
{
u8 i=0;
for(i=0;i<8;i++)
{
ser=dat>>7;//优先传输一个字节中的高位
dat<<=1;//将低位移动到高位
srclk=0;
delay_10us(1);
srclk=1;
delay_10us(1);//移位寄存器时钟上升沿将端口数据送入寄存器中
}
rclk=0;
delay_10us(1);
rclk=1;//存储寄存器时钟上升沿将前面写入到寄存器的数据输出
}
void main()
{
u8 i=0;
LEDDZ_COL_PORT=0x00;//全部IO口给低电平
while(1)
{
for(i=0;i<8;i++)
{
hc595_write_data(0x00);//清除上一次数据
hc595_write_data(ghc595_buf[i]);//写入数据
delay_10us(50000);//延时
}
}
while(1);
}
实验现象
备注
上面是下往上移动,下面代码是右往左边(定时器)
main.c
# include<reg51.h>
//宏定义
# define uchar unsigned char
# define LED P0
//全局变量
uchar Time;
void main(void)
{
uchar data1=0xfe;
TMOD = 0x01; //选择工作方式1
TH0 = 0x3C; //设置初始值,定时50MS
TL0 = 0xB0;
EA = 1; //打开总中断
ET0 = 1; //打开定时器0中断
TR0 = 1; //启动定时器0
while(1)
{
LED = data1;
if(Time==10) //0.5s进入if内进行移位
{
Time = 0;
data1 = data1<<1|0x01;
if(data1==0xff)data1=0xfe;
}
}
}
void Timer0() interrupt 1
{
TH0 = 0x3C; //设置初始值
TL0 = 0xB0;
Time++;
}
想显示其他图案或者字符的话可以用 取模软件生成行的数据复制
就行了!
软件
显示数字0的代码:
# include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
//定义74HC595控制管脚
sbit rclk=P3^5; //存储寄存器时钟输入
sbit srclk=P3^6; //移位寄存器时钟输入
sbit ser=P3^4; //串行数据输入
# define LEDDZ_COL_PORT P0 //控制点阵列的端口
u8 gled_row[8]={0x00,0x7E,0x81,0x81,0x81,0x7E,0x00,0x00};//点阵行数据
u8 gled_cols[8]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//点阵列
//延时函数
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void hc595_write_data(u8 dat)
{
u8 i=0;
for(i=0;i<8;i++)
{
ser=dat>>7;
dat<<=1;
srclk=0;
delay_10us(1);
srclk=1;
delay_10us(1);
}
rclk=0;
delay_10us(1);
rclk=1;
}
void main()
{
u8 i=0;
while(1)
{
for(i=0;i<8;i++)
{
LEDDZ_COL_PORT=gled_cols[i];
hc595_write_data(gled_row[i]);
delay_10us(100);
hc595_write_data(0x00);
}
}
while(1);
}
备注
爱心同理,改一下数组就行了
爱心的代码:
u8 gled_row[8]={0x38,0x7C,0x7E,0x3F,0x3F,0x7E,0x7C,0x38};//爱心点阵行数据
16*16点阵
4片74HC595芯片,数据传入是 第一个字节最终传到第4片,第二个字节最终传到第3片,第三个字节最终传到第2片,第四个字节最终传到第1片上。
(因为第一片8位满了后再传入的就会把前面的挤出去)
汉字取模:横向取模,字节倒序,字体小四
- 16*16延时函数用:
_nop_();
(需要include<intrins.h>)