一.动态数码管
1.数码管介绍
- 我的单片机上的显示器是液晶LED,液晶LED显示器可以分为:段显示(7段)和点阵(8*8)
- 数码管静态显示是段显示,也就是7段加一个点,看上去像一个8字(如图)
- 1-F的显示数字就根据图中a-g发光二极管的亮来调节;比如要1亮,那就b、c二极管发光,也就是1100 0000,转为16进制就是P0=0xf9
2.数码管静态显示原理
- 静态显示:必须选择一个8位数据线来保持显示的字码形;输入的字码会一直保持到下一次再输入字码为止。
-
动态显示:
- 动态驱动是将所有数码管的8个显示笔划"a,b,c,d,e,f,g,dp"的同名端连在一起,当单片机输出字形码时,所有数码管都接收到相同的字形码,但究竟是那个数码管会显示出字形,取决于单片机对位选通COM端电路的控制,所以我们只要将需要显示的数码管的选通控制打开,该位就显示出字形,没有选通的数码管就不会亮。通过分时轮流控制各个数码管的的COM端,就使各个数码管轮流受控显示,这就是动态驱动。在轮流显示过程中,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位数码管并非同时点亮,但只要扫描的速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感,动态显示的效果和静态显示是一样的,只不过动态显示的要比静态显示的暗,所以电流限制要比静态的电流大,也就是电阻阻值要比较大。(段选:P0口扫描 ,和9个LED是相接的;位选:8个共阳的的位的扫描,链接38译码器)
3.静态数码管模块电路
- 8个电阻(排阻)作用:限流,防止二极管因为电流过大而被击穿
- 每个段显示的8个二极管都是共阳的,要使某一个数码管显示某一个数字就要看下是那个二极管亮(P0口为1),然后转化为16进制令其等于P0
4.74HC138芯片
? ? ? ??
? ?
- 这个输入把它当做二进制然后转化为十进制Y0-Y7(对应1-8个数码管)?;例如:100就是2,输出就是Y2,那就是第二个数码管输出。
- 上边的A0、A1、A2对应下边原理图的A、B、C
5.代码
先位选确定哪一个数码管亮,再位选,确定亮的那个数码管要显示什么值
#include<reg52.h>
sbit LSA=P2^2; //3转8输入确定哪一个灯亮
sbit LSB=P2^3;
sbit LSC=P2^4;
char code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值,这个可以用软件直接转化
void delay(u16 i)
{
while(i--);
}
void Display()
{
u8 i;
for(i=0;i<8;i++)
{
switch(i) //位选,选择点亮的数码管,
{
case(0):
LSA=1;LSB=1;LSC=1; break;//显示第0位
case(1):
LSA=0;LSB=1;LSC=1; break;//显示第1位
case(2):
LSA=1;LSB=0;LSC=1; break;//显示第2位
case(3):
LSA=0;LSB=0;LSC=1; break;//显示第3位
case(4):
LSA=1;LSB=1;LSC=0; break;//显示第4位
case(5):
LSA=0;LSB=1;LSC=0; break;//显示第5位
case(6):
LSA=1;LSB=0;LSC=0; break;//显示第6位
case(7):
LSA=0;LSB=0;LSC=0; break;//显示第7位
}
P0=smgduan[i];//段选,发送段码
delay(100); //间隔一段时间扫描
P0=0x00;//消隐
}
}
void main()
{
while(1)
{
Display(); //数码管显示函数
}
}
二.矩阵按键
1.矩阵按键介绍
独立按键有4个占了4个 IO口,然而单片机也就只有8个IO口,如果有16个按键,那IO口就不够用了,所以就有了矩阵按键,利用8个IO口实现16个矩阵按键。
2.矩阵按键原理
行列扫描:高四位(P14-17)全部输出高电频,低四位(P10-13)全部输出低电频,当由按键按下的时候,低四位不全为低电频,然后通过接受的数值来判断那一列有按键按下;然后反过来高四位(P14-17)全部输出低电频,低四位(P10-13)全部输出高电频,然后通过接受的数值来判断那一行有按键按下;这样就确定了哪一个按键有按下。
3.代码
#include "reg52.h"
#define GPIO_DIG P0
#define GPIO_KEY P1
char KeyValue; //用来存放读取到的键值
void KeyDown(void)
{
char a=0;
GPIO_KEY=0x0f;//0000 1111
if(GPIO_KEY!=0x0f)//读取按键是否按下
{
delay(1000);//延时10ms进行消抖
if(GPIO_KEY!=0x0f)//再次检测键盘是否按下
{
//测试列
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): KeyValue=0;break;//0000 0111
case(0X0b): KeyValue=1;break;//0000 1011
case(0X0d): KeyValue=2;break;//0000 1101
case(0X0e): KeyValue=3;break;//0000 1110
}
//测试行
GPIO_KEY=0XF0;
switch(GPIO_KEY)
{
case(0X70): KeyValue=KeyValue;break;
case(0Xb0): KeyValue=KeyValue+4;break;
case(0Xd0): KeyValue=KeyValue+8;break;
case(0Xe0): KeyValue=KeyValue+12;break;
}
}
}
while((a<50)&&(GPIO_KEY!=0xf0)) //检测按键松手检测
{
delay(100);
a++;
}
}
三.作业
? ? ?1.按下独立按键1,特殊流水(此功能具体请看演示视频) ? ? ?2.按下独立按键2,数码管显示 7777 7777,再按数码管均灭(第三次按,再显示7777 7777) ? ? ?3.按下矩阵按键1,蜂鸣器响,第二次按,以第2种声音响,第三次按,以第3种声音响,(第四? ? ? ? ? ?次按,回到第一种声音) ? ? ?4.按下矩阵按键2,LED特殊闪烁(此功能具体请看演示视频) ? ? ?5.各个按键均可多次使用
?? ? ? ?1.解决独立按键和矩阵--用存放值来解决 ?? ??? ?2.解决两个特殊流水灯--直接对P0口进行赋值即可 ?? ??? ?3.解决数码管显示问题--因为显示的数值都一样,所以直接放松段码0x07即可 ?? ??? ?4.三次不同的蜂鸣器响声--利用逻辑值判断进行筛选然后赋予键值即可
1. 延迟函数的改进,使得按键更为灵敏,但是由于for循环太快,不可能等到按键按下,所以不能转化
/*本来这里的延迟如果改为下边这个的话,那么反应会更加灵敏, ?? ??? ?但是for循环在几毫秒内已经运行完了,它不会再执行里面按键检测函数 ?? ??? ?然后又不能在几秒内按下按键,所以他就不能够执行这个按键检测函数 ?? ??? ?然后你按一下的话就没有效果 ?? ??for(n=0;n<1000;n++) ?//改进:上边直接用delay延迟的话,可能按下按键的时候,它还在延迟中 ?? ??? ??? ?{? ? ? ? ? ? ? ? ? ? ? ? ?//所以设置了每10ms就进行一次判断的程序,是功能切换更为灵敏 ?? ??? ??? ? PD_DLkeyDown();? ? ? ? ? ? ? ?PD_JZKeyDown();?? ? ? ? ? ? ? ? ?if(k!=1) return; ?? ??? ??? ? delay(10); ?? ??? ??? ?} ?2. 矩阵按键的问题描述:按下矩阵按键2,执行的是3的程序,按下两次矩阵按键1,并不会执行1_2的程序 ? 矩阵按键的问题分析:在逻辑值的判断上出现问题或者单片机硬件有问题(因为之前按键1、2是相反的
矩阵按键的问题解决:*/
#include<reg52.h>
#include<intrins.h>
#define led P2
#define GPIO_DIG P0 //矩阵按键
#define GPIO_KEY P1
sbit k1=P3^1;//定义独立按键的串口
sbit k2=P3^0;
sbit beep=P1^5; //定义蜂鸣器
sbit LSA=P2^2;//定义数码管显示的3-8口
sbit LSB=P2^3;
sbit LSC=P2^4;
char key; //用来存放矩阵按键读取到的键值
char k;//用于存放独立按键和矩阵按键的值,并进行判断
char code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
/******************************延迟函数*******************************************/
void delay(int i)
{
while(i--);
}
/*************************判断是否有独立按键的按下**********************************/
void PD_DLkeyDown()
{
if(k1==0) //检测按键K1是否按下
{
delay(1000); //消除抖动 一般大约10ms
if(k1==0) //再次判断按键是否按下
{
k=1;
}
while(!k1); //检测按键是否松开
}
if(k2==0) //检测按键K1是否按下
{
delay(1000); //消除抖动 一般大约10ms
if(k2==0) //再次判断按键是否按下
{
k=2;
}
while(!k2); //检测按键是否松开
}
}
/*************************判断是否有矩阵按键的按下**********************************/
//这里的独立按键只用到1和2,所以只需要测试第一行和第一二列
//矩阵按键1按下,返回key值3;2按下的话,key的值返回4
//由于我的单片机的按键1、2是相反的,所以等下我的键值也会是相反的
void PD_JZKeyDown()
{
/*判断矩阵按键*/
GPIO_KEY = 0x0f; // 0000 1111
if(GPIO_KEY!=0x0f) //按键按下,有电位的变化
{
switch(GPIO_KEY)
{
case 0x07:key = 3; break; //第一列 0000 0111
case 0x0b: k = 4 ; break; //第二列 0000 1011
}
if(key==3)
{
if(k!=3&&k!=5)
k=3;
if(k==3)
k=5;
if(k==5)
k=6;
}
while(GPIO_KEY!=0x0f);//检测按键松开
delay(1000);
}
}
/****************************按下独立按键1后执行的内容*****************************/
void DLkey_1()
{
//int n;
while(1)
{
led=0x7f; //1111 1110
delay(50000);
/*本来这里的延迟如果改为下边这个的话,那么反应会更加灵敏,
但是for循环在几毫秒内已经运行完了,它不会再执行里面按键检测函数
然后又不能在几秒内按下按键,所以他就不能够执行这个按键检测函数
然后你按一下的话就没有效果
for(n=0;n<1000;n++) //改进:上边直接用delay延迟的话,可能按下按键的时候,它还在延迟中
{ //所以设置了每10ms就进行一次判断的程序,是功能切换更为灵敏
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=1) return;
delay(10);
}
*/
led=0xfe;
delay(50000);
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=1) return;
led=0xbf;
delay(50000);
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=1) return;
led=0xfd; //1111 1110
delay(50000);
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=1) return;
led=0xdf;
delay(50000);
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=1) return;
led=0xfb;
delay(50000);
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=1) return;
led=0xef;
delay(50000);
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=1) return;
led=0xf7;
delay(50000);
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=1) return;
}
}
/************************按下独立按键二后执行的内容*******************************/
void DLkey_2()
{
char i;
while(1)
{
for(i=0;i<8;i++)
{
switch(i) //位选,选择点亮的数码管,
{
case(0):
LSA=1;LSB=1;LSC=1; break;//显示第0位
case(1):
LSA=0;LSB=1;LSC=1; break;//显示第1位
case(2):
LSA=1;LSB=0;LSC=1; break;//显示第2位
case(3):
LSA=0;LSB=0;LSC=1; break;//显示第3位
case(4):
LSA=1;LSB=1;LSC=0; break;//显示第4位
case(5):
LSA=0;LSB=1;LSC=0; break;//显示第5位
case(6):
LSA=1;LSB=0;LSC=0; break;//显示第6位
case(7):
LSA=0;LSB=0;LSC=0; break;//显示第7位
}
P0=smgduan[7];//发送段码,显示7
delay(100); //间隔一段时间扫描
P0=0x00;//消隐
}
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=2) return;
}
}
/************************按下矩阵按键一后执行的内容*******************************/
void JZkey2()
{
while(1)
{
led=0xaa;
delay(50000);
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=3) return;
led=0x55;
delay(50000);
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=3) return;
}
}
/************************按下矩阵按键二后执行的内容*******************************/
/* 第一次按下矩阵按键2 */
void JZkey_1_1()
{
beep = ~beep;
delay(10);
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=4) return;
}
/* 第二次按下矩阵按键2 */
void JZkey_1_2()
{
beep =~beep;
delay(500);
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=5) return;
}
/* 第二次按下矩阵按键2 */
void JZkey_1_3()
{
beep =~beep;
delay(1000);
PD_DLkeyDown();
PD_JZKeyDown();
if(k!=6) return;
}
/******************************主函数*******************************************/
void main()
{
while(1)
{
PD_DLkeyDown(); //判断独立按键是否有按下
PD_JZKeyDown(); //判断矩阵按键是否有按下
switch(k)
{
case 1:
DLkey_1(); break;
case 2:
DLkey_2();break;
case 3:
JZkey_1_1();break;
case 4:
JZkey2();break;
case 5:
JZkey_1_2();break;
case 6:
JZkey_1_3();break;
}
}
}
|