简介:本人使用的是arduino mega2560(因为手头上没有uno板了),但是初学者普遍使用的是arduino uno,所以我写文章的时候还是写的arduino uno(仿真除外),mega上我用的是uno兼容的引脚,所以代码是既可以在mega上跑也可以在uno上跑的。
目录 一.学习目标 2 知识目标: 2 技能目标: 2 整体思路 2 二.知识储备 2 4*4按键屏(keypad) 2 OLED屏 7 舵机 12 面包板 13 EEPROM 14 三.案例实现 15 案例描述 16 四.密码锁实验程序 16 程序编写 16 整体硬件搭建 19 五.知识拓展 20
一.学习目标
知识目标:
- 了解OLED显示屏工作原理
- 了解舵机原理
- 了解44按键屏工作原理
技能目标: 设计一个44按键的密码锁,可以更改密码,将设置的密码保存在eeprom,密码正确时控制舵机开关
整体思路
二.知识储备
4*4按键屏(keypad)
工作原理
本人参考的是这位大佬的这篇博客,这是位宝藏级博主,本人跟着学了很久很久了。 博客链接 https://blog.csdn.net/weixin_41659040/article/details/119881718 接线图
Keypad从左往右的8个引脚分别接arduino的9-2号引脚
示范简单程序
#include <Keypad.h>
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};
int secret_lenth;
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
void setup(){
Serial.begin(9600);
}
void loop(){
char key = keypad.getKey();
if (key != NO_KEY){
secret_lenth=int(key)-48;
Serial.print(key);
Serial.print(" ");
Serial.println(secret_lenth);
}
}
代码分析效果图 我将char类型变成了int型(方便后续工作),他们对应的ASCLL码不一样,结合ASCLL码表知0对应48,1对应49…,所以程序中我减去了48保证了数字量的一样,字母和字符我都没处理所以就会是这种效果A=17;*=-13;
ASCLL码数字部分表
Keypad.h库下载方式 1.在github上搜索这个库切记是第一个别下错了
Github链接:https://github.com/Chris–A/Keypad
2.找到你的下载文件后,我建议保存到一个专门放arduino外源库的地方方便今后寻找。
例如新建一个文件夹专门存放这些东西,养成好习惯
3.打开arduino添加库
- 成功后你的头文件就会变红
OLED屏 工作原理 OLED特点 (1)功耗低——与LCD相比,OLED不需要背光源,而背光源在LCD中是比较耗能的一部分,所以OLED是比较节能的。例如,24in的AMOLED模块功耗仅仅为440mw,而24in的多晶硅LCD模块达到了605mw。 (2)响应速度快——OLED技术与其他技术相比,其响应速度快,响应时间可以达到微秒级别。较高的响应速度更好的实现了运动的图像。根据有关的数据分析,其响应速度达到了液晶显示器响应速度的1000倍左右。 (3)较宽的视角——与其他显示相比,由于OLED是主动发光的,所以在很大视角范围内画面是不会显示失真的。其上下,左右的视角宽度超过170度。 (4)能实现高分辨率显示——大多高分辨率的OLED显示采用的是有源矩阵也就是AMOLED,它的发光层可以是吸纳26万真彩色的高分辨率,并且随着科学技术的发展,其分辨率在以后会得到更高的提升。 (5)宽温度特性——与LCD相比,OLED可以在很大的温度范围内进行工作,根据有关的技术分析,温度在-40摄氏度到80摄氏度都是可以正常运行的。这样就可以降低地域限制,在极寒地带也可以正常使用。 (6)OLED能够实现软屏——OLED可以在塑料、树脂等不同的柔性衬底材料上进行生产,将有机层蒸镀或涂布在塑料基衬上,就可以实现软屏。 (7)OLED成品的质量比较轻——与其他产品相比,OLED的质量比较小,厚度与LCD相比是比较小的,其抗震系数较高,能够适应较大的加速度,振动等比较恶劣的环境。 接线图
SDA接SDA,SCL接SCL,切记线不能接错,我画错了,你也不能接错
示例程序
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH 16
static const unsigned char PROGMEM str_1[] =
{0x00,0xFE,0x02,0x12,0x92,0x92,0x92,0xF2,0x92,0x92,0x92,0x12,0x02,0xFE,0x00,0x00,
0x00,0xFF,0x40,0x48,0x48,0x48,0x48,0x4F,0x48,0x4A,0x4C,0x48,0x40,0xFF,0x00,0x00};
static const unsigned char PROGMEM str_2[] =
{
0x00,0x7F,0x40,0x48,0x49,0x49,0x49,0x4F,0x49,0x49,0x49,0x48,0x40,0x7F,0x00,0x00,
0x00,0xFF,0x02,0x12,0x12,0x12,0x12,0xF2,0x12,0x52,0x32,0x12,0x02,0xFF,0x00,0x00,
};
#if (SSD1306_LCDHEIGHT != 64)
#endif
void setup() {
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println("Hello, world!");
display.setTextColor(BLACK, WHITE);
display.println(3.141592);
display.setTextSize(2);
display.setTextColor(WHITE);
display.print("0x");
display.println(0xDEADBEEF, HEX);
delay(2000);
display.display();
display.clearDisplay();
delay(2000);
}
void loop() {
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println("Hello, world!");
display.setTextColor(BLACK, WHITE);
display.println(3.88);
display.display();
}
程序分析效果图 注释写的很清楚了,我就不细讲了。我见过很多oled的库,但是我独爱这个库
Adafruit_GFX,Adafruit_SSD1306库下载方式
舵机 工作原理 指在自动驾驶仪中操纵飞机舵面(操纵面)转动的一种执行部件。分有:①电动舵机,由电动机、传动部件和离合器组成。接受自动驾驶仪的指令信号而工作,当人工驾驶飞机时,由于离合器保持脱开而传动部件不发生作用。②液压舵机,由液压作动器和旁通活门组成。当人工驾驶飞机时,旁通活门打开,由于作动器活塞两边的液压互相连通而不妨害人工操纵。此外,还有电动液压舵机,简称“电液舵机”。舵机的组成 一般来讲,舵机主要由以下几个部分组成,舵盘、减速齿轮组、位置反馈电位计、直流电机、控制电路等, 舵机的输入线共有三条,如图6所示,红色中间,是电源线,一边黑色的是地线,这辆根线给舵机提供最基本的能源保证,主要是电机的转动消耗。电源有两种规格,一是4.8V,一是6.0V,分别对应不同的转矩标准,即输出力矩不同,6.0V对应的要大一些,具体看应用条件;另外一根线是控制信号线,Futaba的一般为白色,JR的一般为桔黄色。另外要注意一点,SANWA的某些型号的舵机引线电源线在边上而不是中间,需要辨认。但记住红色为电源,黑色为地线,一般不会搞错。
示例程序
面包板 面包板是由于板子上有很多小插孔,专为电子电路的无焊接实验设计制造的。由于各种电子元器件可根据需要随意插入或拔出,免去了焊接,节省了电路的组装时间,而且元件可以重复使用,所以非常适合电子调试和训练。
EEPROM 工作原理 在实际的应用中,保存在单片机RAM中的数据,掉电后就丢失了,保存在单片机的FLASH中的数据,又不能随意改变,也就是不能用它来记录变化的数值。但是在某些场合,我们又确实需要记录下某些数据,而它们还时常需要改变或更新,掉电之后数据还不能丢失。比如,我们的家用电表度数,电视机里边的频道记忆,一般都是使用EEPROM来保存数据,特点就是掉电后存储的数据不丢失。 一般情况下,EEPROM拥有30万到100万次的寿命,也就是它可以反复写入30-100万次,而读取次数是无限的。 通常,单片机里的Flash都用于存放运行代码,在运行过程中不能改;EEPROM是用来保存用户数据,运行过程中可以改变,比如一个时钟的闹铃时间初始化设定为12:00,后来在运行中改为6:00,这是保存在EEPROM里,不怕掉电,就算重新上电也不需要重新调整到6:00。但最大区别是其实是:FLASH按扇区操作,EEPROM则按字节操作,二者寻址方法不同,存储单元的结构也不同,FLASH的电路结构较简单,同样容量占芯片面积较小,成本自然比EEPROM低,因而适合用作程序存储器,EEPROM则更多的用作非易失的数据存储器。当然用FLASH做数据存储器也行,但操作比EEPROM麻烦的多,所以更“人性化”的MCU设计会集成FLASH和EEPROM两种非易失性存储器,而廉价型设计往往只有 FLASH,早期可电擦写型MCU则都是EEPRM结构,现在已基本上停产了。 示范简单程序 arduino已经为我们准备好了EEPROM类库”EEPROM.h”。 我们要使用得先调用EEPROM.h,然后使用write和read方法,即可操作EEPROM。
参考链接: https://blog.csdn.net/sdlgq/article/details/51931900
#include <EEPROM.h>
int addr = 0;
void setup()
{
}
void loop()
{
int val = analogRead(0) / 4;
EEPROM.write(addr, val);
addr = addr + 1;
if (addr == 512)
addr = 0;
delay(100);
}
案例实现 把以上硬件部分的代码再结合一点点软件功能方面的代码就可以了。
名称 数量 Arduino mega开发板 1 杜邦线 1扎 OLED屏 1 面包板 1 Keypad 1 Servo 1 充电宝 1 案例描述
- 按照电路图原理图连
红色引线表示5V电源,白色引线(SDA除外)表示接地口,黄色引线和橙色引线表示不同的信号线连接的引脚口也会不同,下文会对不同装置不同的信号引脚口进行分析。
四.密码锁实验程序
程序编写 总体流程
细分流程代码设计思路
6位密码程序
#include <Keypad.h>
#include <EEPROM.h>
#include <Servo.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
#define SSD1306_LCDHEIGHT 64
Adafruit_SSD1306 display(OLED_RESET);
#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH 16
#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
Servo myservo;
int pos = 0;
int D_break=1,A_break=1,ZhuYe=0;
char secret_num[6]={};
char secret_input[6]={};
void setup(){
Serial.begin(9600);
myservo.attach(10);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextColor(WHITE);
display.clearDisplay();
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,5);
display.print("ShuRu: A");
display.setCursor(0,25);
display.print("SheZhi:D");
display.display();
myservo.write(0);
delay(1000);
for(int i = 0;i<6;i++)
{
secret_num[i]=EEPROM.read(i);
delay(10);
}
}
void loop(){
char key = keypad.getKey();
if(ZhuYe==1)
{
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,5);
display.print("ShuRu: A");
display.setCursor(0,25);
display.print("SheZhi:D");
display.display();
ZhuYe=0;
}
if (key != NO_KEY)
{
if(key=='B'||key=='C')
{
myservo.write(0);
}
if(key=='D')
{
while(D_break)
{
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,5);
display.print("shezhi");
display.display();
int num_break=0;
while(1)
{
char key = keypad.getKey();
if(key != NO_KEY)
{
secret_num[num_break]=key;
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,5);
display.print("shezhi");
display.setCursor(0,45);
display.print(num_break+1);
display.display();
num_break++;
}
if(num_break>5)
{
for(int i = 0;i<6;i++)
{
EEPROM.write(i,secret_num[i]);
delay(10);
}
display.clearDisplay();
for(int i=0;i<6;i++)
{
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(i*15,30);
display.print(secret_num[i]);
display.display();
}
num_break=0;
delay(2000);
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,25);
display.print("OK!");
display.display();
delay(1000);
display.clearDisplay();
ZhuYe=1;
goto start;
}
}
}
}
if(key=='A')
{
while(A_break)
{
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,15);
display.print("ShuRu:");
display.display();
int num_break2=0;
while(1)
{
char key = keypad.getKey();
if(key != NO_KEY)
{
secret_input[num_break2]=key;
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,15);
display.print("ShuRu:");
display.setCursor(num_break2*15,45);
display.print(secret_input[num_break2]);
display.display();
num_break2++;
}
if(num_break2>5)
{
int num_True=0;
for(int i = 0;i<6;i++)
{
if(secret_input[i]==secret_num[i])
{
num_True++;
}
delay(10);
}
if(num_True==6)
{
Serial.print("密码正确!");
num_True=0;
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,25);
display.print("success");
display.display();
delay(2000);
display.clearDisplay();
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,25);
myservo.write(100);
delay(1500);
ZhuYe=1;
goto start;
}
else
{
Serial.print("密码错误!");
num_True=0;
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,25);
display.print("fail");
display.display();
delay(2000);
display.clearDisplay();
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,25);
ZhuYe=1;
goto start;
}
}
}
}
}
}
start:{}
}
初始页面
输入页面
成功登录
任意长度密码程序
#include <Keypad.h>
#include <EEPROM.h>
#include <Servo.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
#define SSD1306_LCDHEIGHT 64
Adafruit_SSD1306 display(OLED_RESET);
#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH 16
#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
Servo myservo;
int pos = 0;
int D_break=1,A_break=1,ZhuYe=0;
char secret_num[6]={};
char secret_input[6]={};
int secret_lenth;
void setup(){
Serial.begin(9600);
myservo.attach(10);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextColor(WHITE);
display.clearDisplay();
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,5);
display.print("ShuRu: A");
display.setCursor(0,25);
display.print("SheZhi:D");
display.display();
myservo.write(0);
delay(1000);
secret_lenth=EEPROM.read(10);
for(int i = 0;i<secret_lenth;i++)
{
secret_num[i]=EEPROM.read(i);
delay(10);
}
}
void loop(){
char key = keypad.getKey();
if(ZhuYe==1)
{
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,5);
display.print("ShuRu: A");
display.setCursor(0,25);
display.print("SheZhi:D");
display.display();
ZhuYe=0;
}
if (key != NO_KEY)
{
if(key=='B'||key=='C')
{
myservo.write(0);
}
if(key=='D')
{
int lenth_num=0;
while(D_break)
{
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,5);
display.print("shezhi");
display.setCursor(0,35);
display.print("lenth");
display.display();
char key = keypad.getKey();
if(key != NO_KEY)
{
secret_lenth=int(key)-48;
lenth_num=1;
display.setCursor(80,35);
display.print(secret_lenth);
display.display();
}
int num_break=0;
while(lenth_num)
{
char key = keypad.getKey();
if(key != NO_KEY)
{
secret_num[num_break]=key;
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,5);
display.print("shezhi");
display.setCursor(0,45);
display.print(num_break+1);
display.display();
num_break++;
}
if(num_break>secret_lenth-1)
{
for(int i = 0;i<secret_lenth;i++)
{
EEPROM.write(i,secret_num[i]);
delay(10);
}
EEPROM.write(10,secret_lenth);
delay(10);
display.clearDisplay();
for(int i=0;i<secret_lenth;i++)
{
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(i*15,30);
display.print(secret_num[i]);
display.display();
}
num_break=0;
delay(2000);
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,25);
display.print("OK!");
display.display();
delay(1000);
display.clearDisplay();
ZhuYe=1;
goto start;
}
}
}
}
if(key=='A')
{
while(A_break)
{
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,15);
display.print("ShuRu:");
display.display();
int num_break2=0;
while(1)
{
char key = keypad.getKey();
if(key != NO_KEY)
{
secret_input[num_break2]=key;
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,15);
display.print("ShuRu:");
display.setCursor(num_break2*15,45);
display.print(secret_input[num_break2]);
display.display();
num_break2++;
}
if(num_break2>secret_lenth-1)
{
int num_True=0;
for(int i = 0;i<secret_lenth;i++)
{
if(secret_input[i]==secret_num[i])
{
num_True++;
}
delay(10);
}
if(num_True==secret_lenth)
{
Serial.print("密码正确!");
num_True=0;
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,25);
display.print("success");
display.display();
delay(2000);
display.clearDisplay();
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,25);
myservo.write(100);
delay(1500);
ZhuYe=1;
goto start;
}
else
{
Serial.print("密码错误!");
num_True=0;
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,25);
display.print("fail");
display.display();
delay(2000);
display.clearDisplay();
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE,BLACK);
display.setCursor(0,25);
ZhuYe=1;
goto start;
}
}
}
}
}
}
start:{}
}
设置密码任意长度
整体硬件搭建 演示视频
任意长度密码锁
五.知识拓展
拓展有点arduino仿真的知识proteus可以去了解一下,它支持C51,stm32,arduino等多种MCU,切记只有proteus8.12才有OLED屏,仿真没有keypad所以用了个类似的代替了
arduino官网有许多的proteus文件
生成仿真hex文件
如何导入开发板仿真参考这位博主(都是宝藏级博客呀!) https://blog.csdn.net/laoxue123456/article/details/121558607
如何下载proteuse请参考 https://blog.csdn.net/Matcha_ice_cream/article/details/118639073
本文借鉴了许多大佬的文章,我写文章的目的不单是为了记录,更多的是分享吧,把别人好的文章推荐出去,毕竟我的知识也是源于大佬们。文章如有不足之处请私聊,感激不尽!
百度网盘链接 链接:https://pan.baidu.com/s/12I9oS5PIepb5PwxH3CfUiw 提取码:81d6
|