如果这篇文章对你有帮助就点点赞吧
目录
一、问题分析
在ArduinoMEGA2560开发板实现智能门禁系统,该系统有初始密码、用户密码、管理员密码,通过4×4按键矩阵输入正确密码,或者通过RFID识别进行开门操作。代码必须对数据采集和密码管理有明确的规定。
二、原理图
使用4×4按键矩阵,通过步进电机执行开门操作,蜂鸣器发出声音表示操作得到响应,RFID用于卡片的感应。
三、程序设计
3.1、功能模块
将程序分为Mjxt.ino、RFID.h、RFID.cpp、28BYJ.h、28BYJ.cpp、button.h、button.cpp、buzzer.h、buzzer.cpp等模块,具体功能如下图所示。
3.1.1RFID模块
1、RFID.h
//RFID无线射频模块
#ifndef _RFID_H_
#define _RFID_H_
#include<Arduino.h>
extern int RFID_get[4];//extern int从其他文件获取该变量,用于判断和存储卡号
#define SS_PIN 53 //53号为RFID数据控制引脚
#define RST_PIN 49 //49为RFID引脚重映射引脚
void RFID_Init();//初始化
void RFID_start();//开始识别
void RFID_IDClear();//清除卡号
#endif
RFID模块核心在于进行识别的部分,初始化和清除卡号函数是必备的。
2、RFID.cpp
//初始化
void RFID_Init(){
SPI.begin();//开启SPI通信
rfid.PCD_Init();//初始化RFID传感器
}
首先对RFID进行初始化,然后就可以开始部署识别模块的内容了。
//开始识别
void RFID_start(){
//等待ID识别(无信号返回空值)
if(!rfid.PICC_IsNewCardPresent()){
return;
}
//如果读取失败就返回空值
if(!rfid.PICC_ReadCardSerial()){
return;
}
// RFID_get 是一个数组,存储了上一次读取的 RFID 卡片的 UID。
// rfid.uid.uidByte 是当前读取的 RFID 卡片的 UID。
// 通过逐个比较两个数组的每个字节,判断是否为一张新卡片。
// 如果所有字节都不同,则说明这是一张新卡片,条件为 true。
if(RFID_get[0]!=rfid.uid.uidByte[0]&&RFID_get[1]!=rfid.uid.uidByte[1]
&&RFID_get[2]!=rfid.uid.uidByte[2]&&RFID_get[3]!=rfid.uid.uidByte[3]){
buzzer_di(2500,500);//蜂鸣器产生一个2500Hz的频率方波持续0.5秒
Serial.println("一个新的卡片已经被识别");//串口打印,其中多出的ln表示打印后换行
Serial.print("识别到的卡号为:");//串口打印
for(int i=0;i<4;i++){
RFID_get[i]=rfid.uid.uidByte[i];//用该数组存储卡号
Serial.print(rfid.uid.uidByte[i],HEX);//串口打印
Serial.print(" "); // 输出一个空格
}
}
else{
for(int i=0;i<4;i++){
RFID_get[i]=rfid.uid.uidByte[i];//用该数组存储卡号
Serial.print(rfid.uid.uidByte[i],HEX);//串口打印
Serial.print(" "); // 输出一个空格
}
}
//让 RFID 卡片进入休眠状态,停止与读卡器的通信(停止PICC)
//防止卡片在读取完成后继续与读卡器通信,避免干扰。
rfid.PICC_HaltA();
//停止 MFRC522 读卡器的 Crypto1 加密通信(停止PCD加密)
//防止后续操作受到加密通信的影响。
rfid.PCD_StopCrypto1();
}
为了提高程序运行速度, 将无用的值跳过,然后将感应到的序列和已经登记在册的序列进行比较分析,如果不相等说明是新卡片,然后登记在册,如果相等,说明这张卡已经录过了。值得注意的是,如果使用者对旧卡片的内容不感兴趣,可以在这里去掉else的部分,让代码简洁一些。
//清除卡号
void RFID_IDClear(){
for(int i=0;i<4;i++){
RFID_get[i]=0;
}
}
必要时可以调动本函数将存储卡片号码的数组清除防止对后续操作有影响。
3.1.2步进电机模块
1、28BYJ.h
//步进电机驱动模块
#ifndef _28BYJ_H_
#define _28BYJ_H_
#include<Arduino.h>
#define STEPS 32//内部输出轴旋转一周步数
//步进电机芯片输入引脚如下
#define IN1 22
#define IN2 23
#define IN3 24
#define IN4 25
void steppingMotor_start(int Speed, int Steps);//控制步进电机旋转,正值逆时针,负值顺时针。
#endif
本模块只需要实现一个开门操作。
2、28BYJ.cpp
#include"28BYJ.h"
#include<Stepper.h>
// A 相正极(IN1)激活
// B 相正极(IN3)激活
// A 相负极(IN2)激活
// B 相负极(IN4)激活
// 这种顺序会驱动电机按顺时针方向旋转。
Stepper stepper(STEPS,IN1,IN3,IN2,IN4);//配置步进电机的控制引脚,按照此相序激活可让电机顺时针旋转
void steppingMotor_start(int Speed, int Steps)//控制步进电机旋转,正值逆时针,负值顺时针。
{
stepper.setSpeed(Speed);//设置转速
stepper.step(Steps);//设置旋转步数
}
关于步进电机的使用,首先定义一个Stepper对象用于设置步进电机的默认旋转方向,按照1324相序运行可以进行顺时针旋转(逆时针1423),同时,旋转步数的正负也可以设置旋转方向,旋转步数与旋转方向的关系是正逆顺反,两者的配合让代码更加清晰可观,转速可以设置为800。
3.1.3蜂鸣器模块
1、buzzer.h
//蜂鸣器模块
#ifndef _BUZZER_H_
#define _BUZZER_H_
#include<Arduino.h>
#define BuzzerPin 9
void buzzer_Init();
void buzzer_di(int frequency,int time);//蜂鸣器响一声
#endif
蜂鸣器用于提示使用者系统有无响应,同时让门禁系统看起来有牌面一些。
2、buzzer.cpp
#include"buzzer.h"
void buzzer_Init(){
pinMode(BuzzerPin,OUTPUT);
}
void buzzer_di(int frequency,int time){
tone(BuzzerPin,frequency);//产生频率方波,frequency代表方波的频率,单位Hz
delay(time);
noTone(BuzzerPin);//停止产生方波
delay(time);
}
让蜂鸣器产生一个方波然后进行延时操作即可发出声音。
3.1.4按键模块
1、button.h
//按键模块
#ifndef _BUTTON_H_
#define _BUTTON_H_
#include<Arduino.h>
#define IRQ_PIN 2//中断控制引脚
void reset();//重置密码函数
void IRQ_Init();//初始化中断引脚
void button_Init1();//初始化按键矩阵1
void button_Init2();//初始化按键矩阵2
void analysis();////解析按键矩阵字符
void button_read();//读取按键矩阵的数字字符
void modify_password();//修改按键矩阵的密码
void input_password();//输入按键矩阵的密码
#endif
按键模块包含了密码管理系统的核心部分,所以使用到的函数比较多,为了方便管理引脚的调用,这里将蜂鸣器的引脚定义放在了.cpp部分,采用数组代替引脚。
2、button.cpp
(1)定义参数
#include"button.h"
#include"buzzer.h"
#include"28BYJ.h"
int sta[8]={40,41,42,43,44,45,46,47}; //按键矩阵控制引脚
int Num=0;//代表按键号码
String pass_input;//存储按下的按键
String password_get;//存储修改密码的值
String password ="123456#";//解锁密码
String password_input;//存储输入密码
int modify_len =0;//修改密码的长度
int input_len =0;//输入密码的长度
这里使用数组定义引脚方便后续的调用,值得注意的是,Num在这里不能只看作一个十进制数字,也应该看作是一个二进制数字,定义为0说明是0000 0000,初始化密码设置为password,最短后缀的pass_input用来表示通用的输入操作,剩下的分别有对应的工作范围。
(2)重置密码函数
//重置密码函数
void reset(){
password ="123456#";//重置密码
password_input="";//清除输入的密码
password_get="";//清除按下的字符
Serial.println("已重置密码!");//串口打印
}
这里用于防止用户忘记密码。
(3)初始化部分
//初始化中断引脚
void IRQ_Init(){
pinMode(IRQ_PIN,INPUT_PULLUP);//设为输入
//配置2号为中断引脚,reset为中断函数,低电平触发
attachInterrupt(digitalPinToInterrupt(2),reset,FALLING);
}
//初始化按键矩阵1
void button_Init1(){
for(int i=0;i<8;i++){
if(i<4){
pinMode(sta[i],INPUT_PULLUP);//40~43为输入
}
else{
pinMode(sta[i],OUTPUT);//44~47为输出
digitalWrite(sta[i],0);//44~47设置为低电平
}
}
}
//初始化按键矩阵2,与按键矩阵1反过来
void button_Init2(){
for(int i=0;i<8;i++){
if(i>3){
pinMode(sta[i],INPUT_PULLUP);//44~47为输入
}
else{
pinMode(sta[i],OUTPUT);//40~43为输出
digitalWrite(sta[i],0);//40~43设置为低电平
}
}
}
矩阵按键的初始化分为两种模式,在后面通过Num的按位操作进行判定
(4)按键矩阵解析
//解析按键矩阵字符
void analysis(){
switch (Num) {
case 0xE7:
pass_input = "1"; // 当按键矩阵值为 0xE7 时,赋值为对应的 "1"
break;
case 0xEB:
pass_input = "2"; // 当按键矩阵值为 0xEB 时,赋值为对应的 "2"
break;
case 0xED:
pass_input = "3"; // 当按键矩阵值为 0xED 时,赋值为对应的 "3"
break;
case 0xD7:
pass_input = "4"; // 当按键矩阵值为 0xD7 时,赋值为对应的 "4"
break;
case 0xDB:
pass_input = "5"; // 当按键矩阵值为 0xDB 时,赋值为对应的 "5"
break;
case 0xDD:
pass_input = "6"; // 当按键矩阵值为 0xDD 时,赋值为对应的 "6"
break;
case 0xB7:
pass_input = "7"; // 当按键矩阵值为 0xB7 时,赋值为对应的 "7"
break;
case 0xBB:
pass_input = "8"; // 当按键矩阵值为 0xBB 时,赋值为对应的 "8"
break;
case 0xBD:
pass_input = "9"; // 当按键矩阵值为 0xBD 时,赋值为对应的 "9"
break;
case 0x7B:
pass_input = "0"; // 当按键矩阵值为 0x7B 时,赋值为对应的 "0"
break;
case 0xEE:
pass_input = "A"; // 当按键矩阵值为 0xEE 时,赋值为对应的 "A"
break;
case 0xDE:
pass_input = "B"; // 当按键矩阵值为 0xDE 时,赋值为对应的 "B"
break;
case 0xBE:
pass_input = "C"; // 当按键矩阵值为 0xBE 时,赋值为对应的 "C"
break;
case 0x7E:
pass_input = "D"; // 当按键矩阵值为 0x7E 时,赋值为对应的 "D"
break;
case 0x77:
pass_input = "*"; // 当按键矩阵值为 0x77 时,赋值为对应的 "*"
break;
case 0x7D:
pass_input = "#"; // 当按键矩阵值为 0x7D 时,赋值为对应的 "#"
break;
}
}
根据原理图,由于引脚的方向是从右往左再往下,所以按键的扫描顺序也是这样,可以通过二进制数来找到对应的“0”所在位置来判断,可以利用计算机或者AI工具,比如0xE7=1110 0111,前面四位用来表示行,后面四位表示列,按从右往左的顺序,0的位置就是按键所在行(列),所以0xE7表示的就是第一行第一列的按键“1”,其他按键可以按照这个规律推出来。
(5)读取按键
//读取按键矩阵的数字字符
void button_read(){
int bit=0;//表示电平状态
button_Init1();//初始化为1号模式,40~43为输入,44~47为输出且低电平
//读取40~43引脚的电平状态存储在Num的低四位
for(int i=sta[0];i<sta[4];i++){
bit=digitalRead(i);//读取40~43引脚电平状态
bitWrite(Num,i-sta[0],bit);//Num是8位的int,这里将Num的前4位设置为低电平
//经过此步骤,Num=0000 0000
}
if((Num&0x0F)!=0x0F){
delay(30);
button_Init2();//初始化为2号模式,40~43为输出且低电平,44~47为输入
for(int i=4;i<8;i++){
bit=digitalRead(sta[i]);
bitWrite(Num,i,bit);
}
delay(500);
analysis();//解析按键矩阵字符
}
}
利用电平状态和中断程序相互呼应完成按钮的设计
(6)修改密码
//修改按键矩阵的密码
void modify_password(){
Serial.println("请输入您要修改的密码:");//串口打印
while(1){
button_read();
/*如果按键被按下(0x0F转化为二进制是00001111)
&表示两个为1取1,否则取0,Num&0x0F表示提取Num的前四位
则Num&0x0F)!=0x0F表示如果Num前四位不是高电平*/
if((Num&0x0F)!=0x0F){
password_get+=pass_input;//存储按下的字符
if(pass_input=="#"){
modify_len=password_get.length();//获取字符串长度
modify_len-=1;//长度减去"#"的长度
if(modify_len==6){
Serial.println("密码修改成功!");
password=password_get;//将新密码赋值给解锁密码
password_get="";//清除修改密码的变量
modify_len=0;
buzzer_di(2000,100);
buzzer_di(2000,100);
break;
}
else{
password_get="";
Serial.println("新密码输入错误,请重新输入!");
}
}
if(pass_input=="*"){
password_get="";
Serial.println("新密码已重置密码!");
}
Serial.println(password_get);//打印存储修改密码的变量
}
}
}
刷卡获取卡号之后,将主程序里的管理员密码改成卡号的序列号,刷卡读取到该序列号就可以修改密码。
(7)输入密码
//输入按键矩阵的密码
void input_password()
{
button_read();
if((Num&0x0F)!=0x0F)
{
password_input+=pass_input;//存储按下的字符
if(pass_input=="#")
{
input_len=password_input.length();//获取字符串长度
input_len-=1;//长度减去"#"的长度
if(input_len==6)
{
if(password_input==password)//如果输入密码和解锁密码相同
{
Serial.println("密码正确!");
password_input="";//清除存储输入密码的变量
buzzer_di(2000,500);
steppingMotor_start(800,512);//步进电机逆时针旋转90°开门
delay(5000);
steppingMotor_start(800,-512);//复位
}
else
{
password_input="";
Serial.println("密码错误,请重新输入!");
}
}
else
{
password_input="";
Serial.println("输入错误,请重新输入!");
}
}
if(pass_input=="*"){
password_input="";
Serial.println("已重置密码!");
}
Serial.println(password_input);
}
}
普通的密码输入层,输入设置好的密码password就可以开锁,初始密码是“123456#”,修改密码需要通过刷管理员卡进行。
3.1.5主程序模块
1、接入层
//接入层
#include"RFID.h"
#include"28BYJ.h"
#include"button.h"
#include"buzzer.h"
//4×4按键矩阵设计说明:
/*
列1 列2 列3 列4
行1 1 2 3 A
行2 4 5 6 B
行3 7 8 9 C
行4 * 0 # D
参考按键矩阵:
列1 列2 列3 列4
行1 E7 EB ED EE
行2 D7 DB DD DE
行3 B7 BB BD BE
行4 77 7B 7D 7E
如0xBD转化为二进制是1011 1101,
表示行=1011(第三行),表示列1101(第三列),对应数字9
由于在原理图中引脚的顺序是从右上角开始向左然后再向下(D40~D47)
所以计算方法是从右往左数,0就是所在行(列)
*/
int RFID_get[4];//RFID_get 是一个数组,存储了上一次读取的 RFID 卡片的 UID
//输入用户的ID卡号,在这里设置自己想要的卡号
int My_ID[4]={0xEB,0xDB,0xBB,0x7B};//2580
//输入管理员的ID卡号
int Gly_ID[4]={0xBD,0xBD,0xBD,0xEE};//999A
void setup() {
IRQ_Init();
Serial.begin(9600);
RFID_Init();
buzzer_Init();
}
用户可以通过接入层设置自己的密码与管理员密码,用户密码用于开门,管理员密码用于修改密码,具体的密码可以根据注释里的说明来转换成16进制写在代码里。 需要注意的是,这里的用户密码和管理员密码只用于RFID的序列号,不可手动按键输入。
2、应用层
//应用层
void loop() {
RFID_start();
//用户识别模块
if(RFID_get[0]==My_ID[0]&&RFID_get[1]==My_ID[1]
&&RFID_get[2]==My_ID[2]&&RFID_get[3]==My_ID[3]){
//steppingMoter_start(转速,旋转步数);2048步进电机的四分之一是512,旋转方向正逆负顺
steppingMotor_start(800,512);//步进电机逆时针旋转90°开门
delay(5000);
steppingMotor_start(800,-512);//步进电机顺时针旋转90°复位
RFID_IDClear();//清除已识别的ID卡号
}
//管理员识别模块
if(RFID_get[0]==Gly_ID[0]&&RFID_get[1]==Gly_ID[1]&&RFID_get[2]==Gly_ID[2]&&RFID_get[3]==Gly_ID[3]){
modify_password();//修改密码
RFID_IDClear();//清除已识别的ID卡号
}
input_password();//输入密码控制步进电机
}
应用层给出了两个解锁的方式,一个是RFID感应,一个是通过按键输入密码。
四、源程序展示
将各文件有序放在同一文件夹下,通过主程序的调用即可实现智能门禁系统。
五 、声明
爱emo的编程小白,学习Arduino在实验成功的那一刻是喜悦的,写实验报告的时候脸是苦着的,刚入坑分享学习经验这个爱好,深度不够,文章仅供参考,看完本文章喜欢的就点点赞吧,一起进步。