【自学51单片机】8 -- 按键的学习,单片机最小系统

最新更新时间:2021-10-20来源: eefocus关键字:51单片机  按键  最小系统 手机看文章 扫描二维码
随时随地手机看文章

1、单片机最小系统

电源

单片机中常见5v和3.3v的单片机,“5v”和“3.3v”分别只是他们正常工作的典型值,5v和3.3v单片机也是以他们正常工作典型值命名的,他们正常工作电压的标准范围要根据单片机手册查询,


晶振

晶振分无源晶振和有源晶振。实物图和原理图见下图。

无源晶振: 依靠单片机内部振荡电路才能工作,接到单片机两个振荡引脚即可,晶体两个引脚无区别,电压无要求,两侧通常有电容,手册有要求根据手册选电容,手册无要求一般选20pf。


有源晶振: 无需依靠单片机内部振荡电路,只需外部供电达到电压要求,即可产生振荡频率,接到单片机晶振输入引脚即可接受到晶振频率,单片机晶振输出引脚无需连接。

两者区别:无源晶振信号质量和精度比有源晶振差,价格比有源晶振便宜。


复位电路:

KST-51开发板复位电路如下图

STC89C52单片机:高电平复位,低电平正常工作。

上电复位:一上电,给电容充电,此时电容相当于导线,RST高电平,电容充电越多,电路电流越来越小直到电容开路电流为0,RST电压越来越小直到低电平(0v),这就是上电复位,复位时间达到要求即可复位。


手动(按键)复位:按键未按前,RST为低电平,按键按下,RST为高电平,松开按键,电源给电容充电后,电容开路,RST为低电平。18欧姆电阻作用:为抑制按键按下后,电容产生的电磁干扰。


2、独立按键和准双向IO口

用户和单片机交流信息依赖于输入设备和输出设备,前边LED小灯部分都是输出设备,现在来学习输入设备-----按键。


按键:按键电路分独立式按键和矩阵式按键两种。下面说明独立式按键,见下图原理图。

说明:四条KeyIn编号输入线连接单片机IO口,当按键k1按下,KeyIn1引脚为低电平,当按键k1松开,KeyIn1引脚为高电平,KeyIn编号IO口的电平情况由按键的状态所决定。


准双向IO口:在KST51-开发板中,按键接到P2中KeyIn编号IO口上,这些IO口上电默认是准双向IO口,下面来学习准双向IO口电路,如下图8-7。

说明:这种IO口,有输出端和输入端,要正常读取外部信号状态,必须要保证自己内部输出的是1。当内部输出为1时,经过非门后输出为0,三极管不导通,按键松开,内部输入为高电平,按键按下,内部输入为低电平。而当内部输出为0时,通过分析,按键是什么状态,内部输入都为低电平。


3、矩阵按键

矩阵按键相对于独立按键而言可以减少IO口的使用,原理图见下图。

说明:如果keyout1输出为低电平,相当于GND,而keyout2、keyout3、keyout4输出为高电平的时候,K1、K2、K3、K4相当于独立按键。


4、矩阵按键扫描程序

//矩阵按键的扫描

#include


sbit ADDR0 = P1^0;

sbit ADDR1 = P1^1;

sbit ADDR2 = P1^2;

sbit ADDR3 = P1^3;

sbit ENLED = P1^4;

sbit keyin1 = P2^4;

sbit keyin2 = P2^5;

sbit keyin3 = P2^6;

sbit keyin4 = P2^7;

sbit keyout1 = P2^3;

sbit keyout2 = P2^2;

sbit keyout3 = P2^1;

sbit keyout4 = P2^0;


unsigned char code LedChar[] = { //数码管显示字符转换表

0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,

0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E

};


unsigned char keysta[4][4]= { //全部矩阵按键的当前状态

{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}//数组不能定义成bit型

};


void main()

{

unsigned char i, j;

unsigned char backup[4][4] = {//按键值备份,保存前一次的值

{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}

};


EA = 1;//使能总中断

ENLED = 0;//选择数码管DS0显示

ADDR3 = 1;

ADDR2 = 0;

ADDR1 = 0;

ADDR0 = 0;

TMOD  = 0x10; //设置T1为模式1

TH1 = 0xFC; //为T1赋值,定时1ms

TL1 = 0x67;

TR1 = 1; //开启T1

ET1 = 1; //使能T1中断

P0 = LedChar[0]; //默认数码管显示0


while(1)

{

for(i = 0; i < 4; i++) //循环检测4*4矩阵按键

{

for(j = 0; j < 4; j++)

{

if(keysta[i][j] != backup[i][j]) //检测按键动作

{

if(backup[i][j] == 0)  //前一次值为0,说明当前按键弹起

{  

P0 = LedChar[4*i+j];//将编号显示到数码管

}

backup[i][j] = keysta[i][j];//更新前一次备份值

}

}

}

}

}


void Interrupt() interrupt 3

{

unsigned char i; 

static unsigned char keyout = 0;   //矩阵按键扫描输出索引

static unsigned char keybuf[4][4] = { //矩阵按键扫描缓冲区

{0xFF,0xFF,0xFF,0xFF}, {0xFF,0xFF,0xFF,0xFF},

{0xFF,0xFF,0xFF,0xFF}, {0xFF,0xFF,0xFF,0xFF}

};


TH1 = 0xFC; //重新为T1赋值

TL1 = 0x67;

//将一行的4个按键值移入缓冲区

keybuf[keyout][0] = (keybuf[keyout][0] << 1) | keyin1;

keybuf[keyout][1] = (keybuf[keyout][1] << 1) | keyin2;

keybuf[keyout][2] = (keybuf[keyout][2] << 1) | keyin3;

keybuf[keyout][3] = (keybuf[keyout][3] << 1) | keyin4;

//消抖后更新按键状态

for(i = 0; i < 4; i++)   //每行四个按键,所以循环四次

{

if((keybuf[keyout][i] & 0x0F) == 0x00)

{   //连续扫描4次值为0,即4*4ms内都是按下状态时,可认为按键已稳定按下

keysta[keyout][i] = 0;

}else if((keybuf[keyout][i] & 0x0F) == 0x0F)

{   //连续扫描4次值为1,即4*4ms内都是弹起状态时,可认为按键已稳定弹起

keysta[keyout][i] = 1;

}

}

//执行一下次的扫描输出

keyout++; //输出索引递增

keyout = keyout & 0x03;//索引值加到4即归零

switch(keyout) //根据索引,释放当前输出引脚,拉低下次的输出引脚

{

case 0: keyout4 = 1; keyout1 = 0; break;

case 1: keyout1 = 1; keyout2 = 0; break;

case 2: keyout2 = 1; keyout3 = 0; break;

case 3: keyout3 = 1; keyout4 = 0; break;

default:break;

}

}


原理:根据上面所讲的内容和下图单片机原理图的分析,程序原理很好理解,程序利用动态扫描扫描4行按键,每隔一秒扫描一行按键,通过检测不同位置按键输入引脚的电平变化,来改变数码管的值,程序中keyin输入和keyout输出颠倒是为了让输出信号有足够时间来稳定。

5、按键抖动

按键抖动:按键闭合时间由操作人员控制决定,通常会在100ms以上,刻意快速的按,也会在40-50ms左右,因为按键有机械弹性,按下和弹起瞬间都会有一连串的抖动,如下图8-10,这种抖动直接影响了数码管值的变化,上面程序运用了软件消抖解决该问题,每个按键每隔4ms检测一次按键状态,每个按键检测4次,每个按键检测4*4ms,若4次状态相同则可确定按键的状态。这就将按键的抖动消去了。

6、综合小程序-简易加减法计算器

//支持“向上键”加,“向下键”减, 回车计算结果,但不支持连续加减操作

#include


sbit ADDR0 = P1^0;

sbit ADDR1 = P1^1;

sbit ADDR2 = P1^2;

sbit ADDR3 = P1^3;

sbit ENLED = P1^4;

sbit keyin1 = P2^4;

sbit keyin2 = P2^5;

sbit keyin3 = P2^6;

sbit keyin4 = P2^7;

sbit keyout1 = P2^3;

sbit keyout2 = P2^2;

sbit keyout3 = P2^1;

sbit keyout4 = P2^0;


void KeyDriver();//按键驱动函数

void KeyAction(unsigned char keycodeMap);//按键动作函数

void ShowNumber(signed long num);//显示函数

void Keyscan();//按键扫描函数

void LedScan();//数码管扫描函数


unsigned char code LedChar[] = { //数码管显示字符转换表

0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,

0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E

};


unsigned char  Ledbuff[6] = {  //数码管显示缓冲区

0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF

};


unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表

    { 0x31, 0x32, 0x33, 0x26 }, //数字键1、数字键2、数字键3、向上键

    { 0x34, 0x35, 0x36, 0x25 }, //数字键4、数字键5、数字键6、向左键

    { 0x37, 0x38, 0x39, 0x28 }, //数字键7、数字键8、数字键9、向下键

    { 0x30, 0x1B, 0x0D, 0x27 }  //数字键0、ESC键、  回车键、 向右键

};


unsigned char keysta[4][4]= { //全部矩阵按键的当前状态

{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}

};



void main()

{

ENLED = 0; //使能U3

ADDR3 = 1;

EA = 1; //使能总中断

TMOD = 0x01;//设置T0为模式1

TH0 = 0xFC;//为T0赋值,定时1ms

TL0 = 0x67;

ET0 = 1; //使能T0中断

TR0 = 1; //开启T0

Ledbuff[0] = LedChar[0]; //上电显示0


while(1)

{

KeyDriver();//调用按键驱动函数

};

}


//按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用

void KeyDriver()

{

unsigned char i, j;

static unsigned char backup[4][4]= { //按键备份值,保存前一次的值

{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}  

};

for(i = 0; i < 4; i++)    //循环检测4*4的矩阵按键

{

for(j = 0; j < 4; j++)

{

if(backup[i][j] != keysta[i][j])   //检测按键动作

{

if(backup[i][j] != 0) //按键按下时执行动作

{

KeyAction(KeyCodeMap[i][j]);  //调用按键动作函数

}

backup[i][j] = keysta[i][j];   //刷新前一次的备份值

}

}

}

}


//按键动作函数,根据键码执行相应的操作,keycode-按键按码

void KeyAction(unsigned char keycodeMap)

{

    static signed char oprt = 0; //用于保存加减运算符

static signed long addend = 0;//用于保存输入的减数

static signed long result = 0;//用于保存运算结果

if((keycodeMap >= 0x30) && (keycodeMap <= 0x39))//输入0~9的数字

{

addend = (addend * 10) + (keycodeMap - 0x30);//整体十进制左移,新数字进入个位

ShowNumber(addend);   //运算结果显示到数码管

}

else if(keycodeMap == 0x26)  //向上键用作加号,执行连法运算

{

oprt = 0;  //设置运算符变量

result = addend;  //运算数存到结果中,准备进行加减

addend = 0;    //清零运算数,准备接收下一个运算数

ShowNumber(result); //刷新数码管显示

else if(keycodeMap == 0x28)  //向下键用作减号,执行减法运算

{

oprt = 1;//设置运算符变

result = addend;//运算数存到结果中,准备进行加减 

addend = 0; //清零运算数,准备接收下一个运算数

ShowNumber(result); //刷新数码管显示

else if(keycodeMap == 0x0D) //回车键执行加减法运算)

{

if(oprt == 0)  //执行加法运算

{

result += addend;

}

else //执行减法运算

{

result -= addend;

}

addend = 0;

ShowNumber(result); //运算结果显示到数码管

}

else if(keycodeMap == 0x1B)//ESC键,清零结果

{

result = 0;

addend = 0;

ShowNumber(addend); //清零后的减数显示到数码管

}

}


//将一个无符号长整形的数字显示到数码管上,num为待显示数字

void ShowNumber(signed long num)

{

unsigned char sign = 0;

unsigned char buf[6];

signed char i;

if(num < 0) //首先提取并暂存符号位

{

sign = 0;

num = -num;

}

else{

sign = 1;

}

for(i = 0; i < 6; i++) //把长整型数转换为6位十进制的数组

{

buf[i] = num % 10;

num = num / 10;

}

for(i = 5; i >= 1; i--) //从最高位起,遇到0转换为空格,遇到非0则退出循环

{

if(buf[i] == 0)

{

Ledbuff[i] = 0xFF;

}

else

{

break;

}

}

if(sign == 0)    //负数时,需在最前面添加负号

{

if(i < 5)   //当有效位数小于6位时添加负号,否则显示结果将是错的

{

Ledbuff[i+1] = 0xBF;

}

}

for(; i >= 0; i--) //剩余低位都如实转换为数码管显示字符

{

Ledbuff[i] = LedChar[buf[i]];

}

}


void InterruptTimer0() interrupt 1

{

TH0 = 0xFC;//为重新T0赋值

TL0 = 0x67;


LedScan(); //调用数码管显示扫描函数

Keyscan(); //调用按键扫描函数

}


void Keyscan()//按键扫描函数

{

unsigned char i;

static unsigned char keyout = 0;//矩阵按键扫描输出索引

static unsigned char keybuf[4][4] = {  //矩阵按键扫描缓冲区

{0xFF,0xFF,0xFF,0xFF}, {0xFF,0xFF,0xFF,0xFF},

{0xFF,0xFF,0xFF,0xFF}, {0xFF,0xFF,0xFF,0xFF}

};


//将一行的4个按键值移入缓冲区

keybuf[keyout][0] = (keybuf[keyout][0] << 1) | keyin1;

keybuf[keyout][1] = (keybuf[keyout][1] << 1) | keyin2;

keybuf[keyout][2] = (keybuf[keyout][2] << 1) | keyin3;

keybuf[keyout][3] = (keybuf[keyout][3] << 1) | keyin4;

//消抖后更新按键状态

for(i = 0; i < 4; i++)   //每行4个按键,所以循环四次

{

if((keybuf[keyout][i] & 0x0F) == 0x00)

{  //连续4次扫描值为0,即4*4ms内都是按下状态,可认为按键以稳定地按下

keysta[keyout][i] = 0;

}

else if((keybuf[keyout][i] & 0x0F) == 0x0F)

{ //连续4次扫描值为1,即4*4ms内都是弹起状态,可认为按键以稳定地弹起

keysta[keyout][i] = 1;

}

}

//执行下一次的扫描输出

keyout++; //输出索引递增

keyout = keyout & 0x03; //索引值加到4后归零

switch(keyout) //根据索引,释放当前输出引脚,拉低下次的输出引脚

{

case 0:keyout4 = 1; keyout1 = 0; break;

case 1:keyout1 = 1; keyout2 = 0; break;

case 2:keyout2 = 1; keyout3 = 0; break;

case 3:keyout3 = 1; keyout4 = 0; break;

default:break;

}

}


//数码管动态扫描刷新函数,须需在定时中断中调用

void LedScan()

{

static unsigned char i = 0;    //动态扫描的索引

P0 = 0xFF; //显示消隐

switch(i)

{

case 0: ADDR2 = 0; ADDR1 = 0; ADDR0 = 0; i++; P0 = Ledbuff[0];break;

case 1: ADDR2 = 0; ADDR1 = 0; ADDR0 = 1; i++; P0 = Ledbuff[1];break;

case 2: ADDR2 = 0; ADDR1 = 1; ADDR0 = 0; i++; P0 = Ledbuff[2];break;

case 3: ADDR2 = 0; ADDR1 = 1; ADDR0 = 1; i++; P0 = Ledbuff[3];break;

case 4: ADDR2 = 1; ADDR1 = 0; ADDR0 = 0; i++; P0 = Ledbuff[4];break;

case 5: ADDR2 = 1; ADDR1 = 0; ADDR0 = 1; i=0; P0 = Ledbuff[5];break;

default:break;

}

}


说明:程序功能上只能进行一次加或一次减,该程序分为几个模块,目的是为了让程序层次化。

关键字:51单片机  按键  最小系统 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic551877.html

上一篇:【自学51单片机】9 -- 步进电机原理、蜂鸣器原理,单片机IO
下一篇:【自学51单片机】7--LED点阵学习

推荐阅读

基于51单片机电子秒表倒计时器
刚刚咱们讲了电子秒表用的是C语言,接下来咱们再讲一讲用汇编。硬件设计(末尾附文件)电路图1:数码管显示;时间清零;启动计时;暂定计时;继续计时;电路图2程序设计TLOW    EQU  78HTHIGH   EQU  0ECHLED_WEI_Pro EQU 0f0H;数码管位选 保护位;寄存器设置COUN_INT    EQU 21H     ;5ms * 20 = 100ms = 0.1SCOUN_PASSNUM EQU 22H ;COUN_TIME_L EQU 23H ;时间累加 低位 100MS加1COUN_T
发表于 2021-11-26
基于<font color='red'>51单片机</font>电子秒表倒计时器
51单片机60秒倒计时 数码管显示
本资源适用于初学者,使用单片机型号为普中51单片机:下面展示 代码。#include"reg52.h"sbit LSA=P2^2;sbit LSB=P2^3;sbit LSC=P2^4;//38译码器控制位选char code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};char a1,a2,s=60;//s为要定时 60秒void timer0(){ TMOD|=0x01;TH0=0xfc;TL0=0x18;//¶¨定时1MSEA=1;ET0=1;TR0
发表于 2021-11-26
【C51单片机】数码管实现99秒倒计时
C51单片机之数码管实现99秒倒计时(结合定时器)—STC89C52单片机代码如下:#include <reg52.h>typedef unsigned char u8;typedef unsigned int u16;sbit b1 = P2^2;sbit b2 = P2^3;sbit b3 = P2^4;u8 duan[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};u16 count = 99;u16 digit = 0;//延时函数void delay(u16
发表于 2021-11-26
关于两块51单片机之间的串口通信(排队叫号系统)
一、项目要求叫号:按下按键叫号,数码管显示叫号的号数,蜂鸣器响。取号:按下按键取号,两个数码管分别显示取到的号数和等待的人数。(PS:推荐使用串口调试助手进行调试)二、可能使用到的工具1.串口调试助手(百度网盘自行下载)链接:https://pan.baidu.com/s/1XmCBUDD7kF8a5393fh_bBg提取码:znv3三、串口通信原理1.由51单片机引脚图可看出,P3.1(TXD)和P3.0(RXD)为串行通信口,P3.0为串行接收口,P3.1为串行发送口。要使两块单片机板可以进行串口通信,则将两块板之间的RXD/TXD交叉连接。四、设计流程图五、硬件设计六、界面实现七、代码主机:#include "
发表于 2021-11-25
关于两块<font color='red'>51单片机</font>之间的串口通信(排队叫号系统)
51单片机UART 串口通信[ 方式1 ]实现
1  51串行口结构51单片机的UART串行通信是基于其串行口的可编程硬件结构,只要用正确的程序代码通过初始化串行口对应寄存器的形式将其串行硬件结构初始化,再编写符合此串行口通信的程序代码便能够实现串行通信,其硬件结构决定了编程机制( 当然还要靠51芯片内CPU等机制 )。此结构具有UART( 通用异步收发器 )的全部功能,能同时进行数据的发送和接收,也可作为同步移位寄存器使用。此结构集成于单片机内部。Figure1:51串行口结构2  51串行口通信编程机制决定编程机制的先决条件是51单片机的硬件结构及51芯片内部CPU的执行机制。根据串行通信口硬件结构,实现UART通信可以分为两步:(1)初始化串行通信
发表于 2021-11-25
<font color='red'>51单片机</font>UART 串口通信[ 方式1 ]实现
51单片机内部定时器使用
/*------------------------------Function: generate pulse signalAuthor: Zhang KaizhouDate: 2019-5-15 22:50:43------------------------------*/#include <reg51.h>#define uchar unsigned charsbit output = P1^0;uchar period = 250;uchar high = 50;uchar tcount = 0;void timer0() interrupt 1 using 1{ TH0 = (65536 - 10000) /
发表于 2021-11-25
小广播
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2021 EEWORLD.com.cn, Inc. All rights reserved