独立键盘与单片机进行连接时,每一个按键都需要单片机的一个I/O口,若某单片机系统较多按键,如果用独立按键便会占用较多的I/O口资源。为了尽可能节省I/O口线,引入矩阵键盘。

以巫妖王单片机上的4×4矩阵键盘为例,讲解矩阵键盘的原理和检测方法,其原理图如上图所示。将16个按键排成4行4列,第一行将每个按键的一端连接在一起构成行线,第一列将每个按键的另一端连接在一起构成列线,这样一共有4行4列共八根线,我们将这八根线连接到单片机的8个I/O口上,通过程序扫描键盘就可检测16个键。用这种办法我们也可以实现3行3列9个键,5行5列25个键等。
无论是独立键盘还是矩阵键盘,单片机检测其是否被按下的依据都是一样的,也就是检测与该键对应的I/O口是否为低电平。独立键盘有一端固定为低电平,单片机写程序检测时比较方便,但是矩阵键盘两端都与单片机I/O口相连,因此在检测时需要人为通过单片机的I/O口送出低电平。
下面着重介绍一下扫描的概念:
数码管扫描(输出扫描)
原理:显示第1位→显示第2位→显示第3位→……,然后快速循环这个过程,最终实现所有数码管同时显示的效果
矩阵键盘扫描(输入扫描)
原理:读取第1行(列)→读取第2行(列) →读取第3行(列) → ……,然后快速循环这个过程,最终实现所有按键同时检测的效果
以按行扫描为例:
当P1.4-P1.7口赋值为1101时,此时先看P17所在的行,无论S1、S2、S3和S4谁按下,两端都是高电平,同理P15和P14同样如此,而P16所在的行因为P1.6口赋值为0,只要在此基础检测P1.0-P1.3口电平情况就可以判断究竟是S5、S6、S7还是S8被按下。具体的说,当P1.4-P1.7口赋值为1101时,如果P1.0-P1.3口赋值为:1110(P1 .3为0),则S5被按下,其他同理,并以此类推即可。
在写代码前,再介绍C51子函数带返回值的写法
//格式
类型 函数名(形参)
{
函数体;
return 数据;
}
例如:
int getSum(int num1,int num2)
{
int sum = num1 + num2;
return sum;
}
//参数是 函数接收外面传进来的
//返回值 是函数从里面扔出去的
各模块具体代码如下
主函数main()
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "MaxtrixKey.h"
unsigned char KeyNum; //定义变量 接一下返回值
void main()
{
LCD_Init();
LCD_ShowString(1,1,"Hello World!");
while(1)
{
KeyNum=MaxtrixKey();
if(KeyNum)
{
LCD_ShowNum(2,1,KeyNum,2);
}
}
}
/*
为什么需要加上if(KeyNum)进行判断,这是因为如果不加的话有一下一种情况:
当你没有按下键时,开始执行KeyNum=MaxtrixKey()语句,即KeyNum通过调用MaxtrixKey函数获取键值,此时KeyNum获取的值为0(不按下键,MaxtrixKey函数内的值就是初始化的值0)
然后开始执行LCD_ShowNum函数,这样1602上就会显示00
当你按下某个键,不松手,会卡在执行KeyNum=MaxtrixKey()语,显示0
显示的键值和显示0 间隔的时间太短 以至于人眼无法识别
*/
MaxtrixKey.h
#ifndef__MAXTRIXKRY_H__
#define__MAXTRIXKRY_H__
unsigned char MaxtrixKey();
#endif
MaxtrixKey.c
#include <REGX52.H>
#include "Delay.h"
/**
* @brief 矩阵键盘读取按键键码
* @param 无
* @retval KeyNumer 按下按键的键码值
如果按下不放,程序会停留在此函数,松手一瞬间,返回按键码,没有按键时,返回0
*/
unsigned char MaxtrixKey()
{
unsigned char KeyNumber=0;
P1=0xff;
P1_3=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=1;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=5;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=9;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=13;}
P1=0xff;
P1_2=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=2;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=6;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=10;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=14;}
P1=0xff;
P1_1=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=3;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=7;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=11;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=15;}
P1=0xff;
P1_0=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=4;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=8;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=12;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=16;}
return KeyNumber;
}
LCD1602.h定义如下
#ifndef __LCD1602_H__
#define __LCD1602_H__
//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
#endif
LCD1602.c定义如下
#include <REGX52.H>
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief 在,范围:1~2
* @param Column 起始列位LCD1602指定位置开始显示所给数字
* @param Line 起始行位置置,范围:1~16
* @param Number 要显示的数字,范围:0~65535
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-32768~32767
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line,Column);
if(Number>=0)
{
LCD_WriteData('+');
Number1=Number;
}
else
{
LCD_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以十六进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFF
* @param Length 要显示数字的长度,范围:1~4
* @retval 无
*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i,SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* @brief 在LCD1602指定位置开始以二进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
}
}
Delay.h
#ifndef __DELAY_H__ //
#define __DELAY_H__
void Delay(unsigned int xms);
#endif
Delay.c
void Delay(unsigned int xms) //@12.000MHz
{
while(xms--)
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
在上面的基础上模块化的代码上,只需要改动主函数mian()即可实现,因此,下面就只给出主函数密码
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "MaxtrixKey.h"
unsigned char KeyNum; //定义变量 接一下返回值
unsigned int PassWord,Count; //定义四位密码和计数器
void main()
{
LCD_Init();
LCD_ShowString(1,1,"PassWord:");
while(1)
{
KeyNum=MaxtrixKey();
if(KeyNum)
{
if(KeyNum<=10) //密码区,如果S1~S10按键按下,输入密码
{
if(Count<4)
{
PassWord*=10;//相当于PassWord=PassWord*10 目的:密码左移一位
PassWord+=KeyNum%10 ;//取余,1-9取余还是1-9 而10取余则为0 ,目的:获取一位密码
Count++; //计次加1
}
LCD_ShowNum(2,1,PassWord,4);
}
if(KeyNum==11)//如果S11按下,即为确认密码
{
if(PassWord==9981) //如果密码等于正确密码显示Pass,否则显示EROR
{
LCD_ShowString(1,14,"ok ");//注意OK后有空格 用于对齐ERR
PassWord=0; //密码清0
Count=0; //计次清0
LCD_ShowNum(2,1,PassWord,4);//更新显示
}
else
{
LCD_ShowString(1,14,"Err");
PassWord=0; //密码清0
Count=0; //计次清0
LCD_ShowNum(2,1,PassWord,4);//更新显示
}
}
if(KeyNum==12)//如果S12按下,取消
{
PassWord=0; //密码清0
Count=0; //计次清0
LCD_ShowNum(2,1,PassWord,4);//更新显示
}
}
}
}
需要注意的是,当第一个输入的是0的时候会存在bug,这是因为0*10h还是等于0起不到进位的效果,此外也没加退格的功能,等后续有时间我再更新添加退格功能的代码。
我正在尝试在Ruby中制作一个cli应用程序,它接受一个给定的数组,然后将其显示为一个列表,我可以使用箭头键浏览它。我觉得我已经在Ruby中看到一个库已经这样做了,但我记不起它的名字了。我正在尝试对soundcloud2000中的代码进行逆向工程做类似的事情,但他的代码与SoundcloudAPI的使用紧密耦合。我知道cursesgem,我正在考虑更抽象的东西。广告有没有人见过可以做到这一点的库或一些概念证明的Ruby代码可以做到这一点? 最佳答案 我不知道这是否是您正在寻找的,但也许您可以使用我的想法。由于我没有关于您要完成的工作
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
点向量坐标矩阵的几何意义介绍旋转矩阵的几何含义之前,先介绍一下点向量坐标矩阵的几何含义点:在一维空间下就是一个标量,如同一条直线上,以任意某一个位置为0点,以一定的尺度间隔为1,2,3...,相反方向为-1,-2,-3...;如此就形成了一维坐标系,这时候任何一个点都可以用一个数值表示,如点p1=5,即即从原点出发沿着x轴正方向移动5个尺度;点p2=-3,负方向移动3个尺度; 在一维坐标系上过原点做垂直于一维坐标系的直线,则形成了二维坐标系,此时描述一个点需要两个数值来表示点p3=(3,2),即从原点出发沿着x轴正方向移动3个尺度,在此基础上沿着y轴正方向移动两个尺度的位置就是点p3。
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复
在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定
有没有办法在Rails中为确认字段自定义消息?例如在设计中我必须输入密码和password_confirmation并且错误消息是:Passwordconfirmationdoesn'tmatchPassword我可以更改事件记录语言环境消息(“不匹配”),但它会在该语言环境消息的开头和结尾输出密码确认和密码,所以我得到如下内容:"PasswordconfirmationmustmatchPassword"有没有办法将其更改为不同的字符串?PasswordconfirmationandPasswordmustmatch.编辑另一件事是拥有完全自定义的消息,例如:'Setpassword