目录
- [1 项目需求](#1 项目需求)
- [2 硬件清单](#2 硬件清单)
- [3 硬件接线](#3 硬件接线)
- [4 完整代码](#4 完整代码)
- [5 实物图](#5 实物图)
1 项目需求
- 矩阵键盘输入密码,正确则开锁,错误则提示,三次错误蜂鸣器响3秒;
- 按下#号确认输入,按下*号修改密码;
- 密码保存在 W25Q128 里;
- OLED 屏幕显示信息。
2 硬件清单
- 矩阵键盘
- OLED 屏幕
- 蜂鸣器
- W25Q128
- 继电器
- 杜邦线
- STM32
- ST-Link
- USB转TTL
3 硬件接线


4 完整代码
main.c
c
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "beep.h"
#include "keyboard.h"
#include "lock.h"
#include "oled.h"
#include "w25q128.h"
#include "password.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
//串口1初始化
uart1_init(115200);
//蜂鸣器初始化
beep_init();
//矩阵键盘初始化
keyboard_init();
//电磁锁初始化
lock_init();
//OLED初始化
oled_init();
//密码模块初始化
password_init();
//串口打印测试
printf("打印测试:hello world\r\n");
//检查密码是否存在
password_check();
//按键最后一次输入值
uint8_t key_last = 0;
while(1)
{
//OLED显示输入
oled_show_input();
//获取最后一次输入值
key_last = password_get_input();
//如果最后一次输入值为#
if(key_last == POUND_KEY){
//密码比对
if(password_compare() == TRUE){
password_input_right_action();
}else{
password_input_wrong_action();
}
//如果最后一次输入值为*
}else if(key_last == START_KEY){
//显示输入旧密码
oled_show_old();
//获取输入
password_get_input();
//密码比对
if(password_compare() == TRUE){
password_old_right_action();
}else{
password_old_wrong_action();
}
}
}
}
beep.c
c
#include "beep.h"
#include "sys.h"
//初始化GBIO口函数
void beep_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//使能GPIOC时钟
__HAL_RCC_GPIOC_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_13; //beep对应引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //上拉
gpio_initstruct.Pull = GPIO_PULLUP; //高速
HAL_GPIO_Init(GPIOC, &gpio_initstruct);
//关闭beep
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
}
//开启beep的函数
void beep_on(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); //拉低beep引脚,开启beep
}
//关闭beep的函数
void beep_off(void)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); //拉高beep引脚,关闭beep
}
//翻转beep的函数
void beep_toggle(void)
{
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13); //翻转beep引脚电平
}
beep.h
c
#ifndef __BEEP_H__
#define __BEEP_H__
//初始化GBIO口函数
void beep_init(void);
//开启beep的函数
void beep_on(void);
//关闭beep的函数
void beep_off(void);
//翻转beep的函数
void beep_toggle(void);
#endif
keyboard.c
c
#include "keyboard.h"
#include "delay.h"
static uint8_t key_value = 0;
//矩阵键盘初始化
void keyboard_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//使能GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_10; //行对应引脚
gpio_initstruct.Mode = GPIO_MODE_IT_FALLING; //中断下降沿触发
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速
gpio_initstruct.Pull = GPIO_PULLUP; //上拉
HAL_GPIO_Init(GPIOB, &gpio_initstruct);
gpio_initstruct.Pin = GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13; //列对应引脚
gpio_initstruct.Mode = GPIO_MODE_INPUT; //中断下降沿触发
gpio_initstruct.Pull = GPIO_PULLDOWN; //下拉
HAL_GPIO_Init(GPIOB, &gpio_initstruct);
//配置中断
HAL_NVIC_SetPriority(EXTI0_IRQn, 3, 0);//配置中断线
HAL_NVIC_EnableIRQ(EXTI0_IRQn); //使能中断
HAL_NVIC_SetPriority(EXTI1_IRQn, 3, 0);//配置中断线
HAL_NVIC_EnableIRQ(EXTI1_IRQn); //使能中断
HAL_NVIC_SetPriority(EXTI2_IRQn, 3, 0);//配置中断线
HAL_NVIC_EnableIRQ(EXTI2_IRQn); //使能中断
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 3, 0);//配置中断线
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); //使能中断
}
//中断线0的中断服务函数
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}
//中断线1的中断服务函数
void EXTI1_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
}
//中断线2的中断服务函数
void EXTI2_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
}
//中断线10的中断服务函数
void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10);
}
//中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
uint8_t row = 0, column = 0;
//如果扫描过程中再次按下则直接返回
if(key_value != 0){
return;
}
//确认行
if(GPIO_Pin == GPIO_PIN_0){
row = 0x10;
}else if(GPIO_Pin == GPIO_PIN_1){
row = 0x20;
}else if(GPIO_Pin == GPIO_PIN_2){
row = 0x30;
}else if(GPIO_Pin == GPIO_PIN_10){
row = 0x40;
}
//确认列
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11) == GPIO_PIN_SET){
delay_ms(10);
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11)){
column = 0x01;
}
}else if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == GPIO_PIN_SET){
delay_ms(10);
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12)){
column = 0x02;
}
}else if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == GPIO_PIN_SET){
delay_ms(10);
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13)){
column = 0x03;
}
}
if(row != 0 && column != 0){
key_value = row | column;
}
}
//获取矩阵键盘值
uint8_t keyboard_get_value(void)
{
uint8_t ch = 0;
if(key_value != 0){
if(key_value == 0x11) ch = '1';
else if(key_value == 0x12) ch = '2';
else if(key_value == 0x13) ch = '3';
else if(key_value == 0x21) ch = '4';
else if(key_value == 0x22) ch = '5';
else if(key_value == 0x23) ch = '6';
else if(key_value == 0x31) ch = '7';
else if(key_value == 0x32) ch = '8';
else if(key_value == 0x33) ch = '9';
else if(key_value == 0x41) ch = '*';
else if(key_value == 0x42) ch = '0';
else if(key_value == 0x43) ch = '#';
delay_ms(400);
key_value = 0x00;
}
return ch;
}
keyboard.h
c
#ifndef __KEYBOARD_H__
#define __KEYBOARD_H__
#include "sys.h"
//矩阵键盘初始化
void keyboard_init(void);
//获取矩阵键盘值
uint8_t keyboard_get_value(void);
#endif
lock.c
c
#include "lock.h"
#include "sys.h"
//初始化GBIO口函数
void lock_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//使能GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_7; //LOCK对应引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //上拉
gpio_initstruct.Pull = GPIO_PULLUP; //高速
HAL_GPIO_Init(GPIOB, &gpio_initstruct);
//关闭LOCK
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
}
//打开LOCK的函数
void lock_on(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); //拉低LOCK引脚,打开LOCK
}
//关闭LOCK的函数
void lock_off(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); //拉高LOCK引脚,关闭LOCK
}
//翻转LOCK的函数
void lock_toggle(void)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_7); //翻转LED1引脚电平
}
//返回LOCK的状态
uint8_t lock_status_get(void)
{
return (uint8_t)HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7);
}
lock.h
c
#ifndef __LOCK_H__
#define __LOCK_H__
#include "stdint.h"
#define LCOK_STATUS_ON 0
#define LOCK_STATUS_OFF 0
//初始化GBIO口函数
void lock_init(void);
//点亮lock的函数
void lock_on(void);
//熄灭lock的函数
void lock_off(void);
//翻转lock的函数
void lock_toggle(void);
//返回继电器的状态
uint8_t lock_status_get(void);
#endif
oled.c
c
#include "oled.h"
#include "delay.h"
#include "font.h"
//OLED相关GPIO初始化
void oled_gpio_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//使能SCL和SDA引脚时钟
OLED_I2C_SCL_CLK();
OLED_I2C_SDA_CLK();
//GPIO初始化配置
gpio_initstruct.Pin = OLED_I2C_SCL_PIN; //SCL对应引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速
gpio_initstruct.Pull = GPIO_PULLUP; //上拉
HAL_GPIO_Init(OLED_I2C_SCL_PORT, &gpio_initstruct);
gpio_initstruct.Pin = OLED_I2C_SDA_PIN; //SDA对应引脚
HAL_GPIO_Init(OLED_I2C_SDA_PORT, &gpio_initstruct);
}
//I2C起始信号
void oled_i2c_start(void)
{
OLED_SCL_SET();
OLED_SDA_SET();
OLED_SDA_RESET();
OLED_SCL_RESET();
}
//I2C停止信号
void oled_i2c_stop(void)
{
OLED_SCL_SET();
OLED_SDA_RESET();
OLED_SDA_SET();
}
//I2C应答信号
void oled_i2c_ack(void)
{
OLED_SCL_SET();
OLED_SCL_RESET();
}
//I2C写字节
void oled_i2c_write_byte(uint8_t data)
{
uint8_t i, tmp;
tmp = data;
//往SDA总线上循环写数据位,高位先行
for(i = 0; i <8 ;i++){
//取出最高位
if((tmp & 0x80) == 0x80){
OLED_SDA_SET();
}else{
OLED_SDA_RESET();
}
//逻辑左移一位,去除次高位
tmp = tmp << 1;
OLED_SCL_SET();
OLED_SCL_RESET();
}
}
//OLED写命令
void oled_write_cmd(uint8_t cmd)
{
oled_i2c_start();
oled_i2c_write_byte(0x78);
oled_i2c_ack();
oled_i2c_write_byte(0x00);
oled_i2c_ack();
oled_i2c_write_byte(cmd);
oled_i2c_ack();
oled_i2c_stop();
}
//OLED写数据
void oled_write_data(uint8_t data)
{
oled_i2c_start();
oled_i2c_write_byte(0x78);
oled_i2c_ack();
oled_i2c_write_byte(0x40);
oled_i2c_ack();
oled_i2c_write_byte(data);
oled_i2c_ack();
oled_i2c_stop();
}
//OLED初始化
void oled_init(void)
{
oled_gpio_init();
delay_ms(100);
oled_write_cmd(0xAE); //设置显示开启/关闭,0xAE关闭,0xAF开启
oled_write_cmd(0xD5); //设置显示时钟分频比/振荡器频率
oled_write_cmd(0x80); //0x00~0xFF
oled_write_cmd(0xA8); //设置多路复用率
oled_write_cmd(0x3F); //0x0E~0x3F
oled_write_cmd(0xD3); //设置显示偏移
oled_write_cmd(0x00); //0x00~0x7F
oled_write_cmd(0x40); //设置显示开始行,0x40~0x7F
oled_write_cmd(0xA1); //设置左右方向,0xA1正常,0xA0左右反置
oled_write_cmd(0xC8); //设置上下方向,0xC8正常,0xC0上下反置
oled_write_cmd(0xDA); //设置COM引脚硬件配置
oled_write_cmd(0x12);
oled_write_cmd(0x81); //设置对比度
oled_write_cmd(0xCF); //0x00~0xFF
oled_write_cmd(0xD9); //设置预充电周期
oled_write_cmd(0xF1);
oled_write_cmd(0xDB); //设置VCOMH取消选择级别
oled_write_cmd(0x30);
oled_write_cmd(0xA4); //设置整个显示打开/关闭
oled_write_cmd(0xA6); //设置正常/反色显示,0xA6正常,0xA7反色
oled_write_cmd(0x8D); //设置充电泵
oled_write_cmd(0x14);
oled_write_cmd(0xAF); //开启显示
oled_fill(0x00); //清空屏幕
}
//设置坐标
void oled_set_cursor(uint8_t x, uint8_t y)
{
//指定待写入页
oled_write_cmd(0xB0 + y);
//指定待写入列
oled_write_cmd((x & 0x0F) | 0x00);
oled_write_cmd((x & 0xF0) >> 4 | 0x10);
}
//循环填充
void oled_fill(uint8_t data)
{
uint8_t i,j;
for(i = 0;i < 8;i++){
oled_set_cursor(0, i); //指定一次页,写一次列自动往后偏移
for(j = 0;j < 128;j++){
oled_write_data(data);
}
}
}
//OLED显示一个字符
void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size)
{
uint8_t i,j, page;
//ASCII码相对第一个空格字符偏移
num = num - ' ';
//确定字符所占页数
page = size / 8;
if(size % 8){
page++;
}
//循环刷新屏幕
for(j = 0;j < page;j++){
//设定字符坐标
oled_set_cursor(x, y + j);
//分行写数据
for(i = size/2 * j;i < size/2 * (j+1);i++){
if(size == 12){
oled_write_data(ascii_6X12[num][i]);
}else if(size == 16){
oled_write_data(ascii_8X16[num][i]);
}else if(size == 24){
oled_write_data(ascii_12X24[num][i]);
}
}
}
}
//OLED显示字符串
void oled_show_string(uint8_t x, uint8_t y, char *p, uint8_t size)
{
while(*p != '\0'){
oled_show_char(x, y, *p, size);
x += size/2;
p++;
}
}
//OLED显示汉字
//void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N, uint8_t size)
//{
// uint8_t i,j, page;
//
// //确定汉字所占页数
// page = size / 8;
// if(size % 8){
// page++;
// }
// //循环刷新屏幕
// for(j = 0;j < page;j++){
// //设定字符坐标
// oled_set_cursor(x, y + j);
// //分行写数据
// for(i = size * j;i < size * (j+1);i++){
// if(size == 16){
// oled_write_data(chinese_16x16[N][i]);
// }else if(size == 24){
// oled_write_data(chinese_24x24[N][i]);
// }
// }
// }
//}
//OLED显示汉字
void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N, uint8_t message_type)
{
uint16_t i,j;
for(j = 0;j < 2;j++){
oled_set_cursor(x, y + j);
for(i = 16 * j;i < 16 * (j + 1);i++){
switch(message_type){
case SHOW_INPUT_PWD:
oled_write_data(chinese_enter_password[N][i]);
break;
case SHOW_PWD_RIGHT:
oled_write_data(chinese_password_right[N][i]);
break;
case SHOW_PWD_WRONG:
oled_write_data(chinese_password_wrong[N][i]);
break;
case SHOW_INPUT_OLD_PWD:
oled_write_data(chinese_enter_old_password[N][i]);
break;
case SHOW_INPUT_NEW_PWD:
oled_write_data(chinese_enter_new_password[N][i]);
break;
case SHOW_PWD_CHANGED:
oled_write_data(chinese_password_changed[N][i]);
break;
case SHOW_SET_PWD:
oled_write_data(chinese_set_password[N][i]);
break;
}
}
}
}
//请输入密码
void oled_show_input(void)
{
oled_fill(0x00); //清空屏幕
oled_show_chinese(10, 1, 0, SHOW_INPUT_PWD);
oled_show_chinese(30, 1, 1, SHOW_INPUT_PWD);
oled_show_chinese(50, 1, 2, SHOW_INPUT_PWD);
oled_show_chinese(70, 1, 3, SHOW_INPUT_PWD);
oled_show_chinese(90, 1, 4, SHOW_INPUT_PWD);
oled_show_char(110, 1, ':', 16);
}
//密码正确
void oled_show_right(void)
{
oled_fill(0x00); //清空屏幕
oled_show_chinese(10, 1, 0, SHOW_PWD_RIGHT);
oled_show_chinese(30, 1, 1, SHOW_PWD_RIGHT);
oled_show_chinese(50, 1, 2, SHOW_PWD_RIGHT);
oled_show_chinese(70, 1, 3, SHOW_PWD_RIGHT);
}
//密码错误
void oled_show_wrong(void)
{
oled_fill(0x00); //清空屏幕
oled_show_chinese(10, 1, 0, SHOW_PWD_WRONG);
oled_show_chinese(30, 1, 1, SHOW_PWD_WRONG);
oled_show_chinese(50, 1, 2, SHOW_PWD_WRONG);
oled_show_chinese(70, 1, 3, SHOW_PWD_WRONG);
}
//请输入旧密码
void oled_show_old(void)
{
oled_fill(0x00); //清空屏幕
oled_show_chinese(10, 1, 0, SHOW_INPUT_OLD_PWD);
oled_show_chinese(30, 1, 1, SHOW_INPUT_OLD_PWD);
oled_show_chinese(50, 1, 2, SHOW_INPUT_OLD_PWD);
oled_show_chinese(70, 1, 3, SHOW_INPUT_OLD_PWD);
oled_show_chinese(90, 1, 4, SHOW_INPUT_OLD_PWD);
oled_show_chinese(110, 1, 5, SHOW_INPUT_OLD_PWD);
}
//请输入新密码
void oled_show_new(void)
{
oled_fill(0x00); //清空屏幕
oled_show_chinese(10, 1, 0, SHOW_INPUT_NEW_PWD);
oled_show_chinese(30, 1, 1, SHOW_INPUT_NEW_PWD);
oled_show_chinese(50, 1, 2, SHOW_INPUT_NEW_PWD);
oled_show_chinese(70, 1, 3, SHOW_INPUT_NEW_PWD);
oled_show_chinese(90, 1, 4, SHOW_INPUT_NEW_PWD);
oled_show_chinese(110, 1, 5, SHOW_INPUT_NEW_PWD);
}
//密码修改成功
void oled_show_changed(void)
{
oled_fill(0x00); //清空屏幕
oled_show_chinese(10, 1, 0, SHOW_PWD_CHANGED);
oled_show_chinese(30, 1, 1, SHOW_PWD_CHANGED);
oled_show_chinese(50, 1, 2, SHOW_PWD_CHANGED);
oled_show_chinese(70, 1, 3, SHOW_PWD_CHANGED);
oled_show_chinese(90, 1, 4, SHOW_PWD_CHANGED);
oled_show_chinese(110, 1, 5, SHOW_PWD_CHANGED);
}
//请设定密码
void oled_show_set(void)
{
oled_fill(0x00); //清空屏幕
oled_show_chinese(10, 1, 0, SHOW_SET_PWD);
oled_show_chinese(30, 1, 1, SHOW_SET_PWD);
oled_show_chinese(50, 1, 2, SHOW_SET_PWD);
oled_show_chinese(70, 1, 3, SHOW_SET_PWD);
oled_show_chinese(90, 1, 4, SHOW_SET_PWD);
oled_show_char(110, 1, ':', 16);
}
//OLED显示图片
void oled_show_image(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t *bmp)
{
uint8_t i, j;
for(j = 0; j < height; j++)
{
oled_set_cursor(x, y + j);
for(i = 0; i < width; i++)
oled_write_data(bmp[width * j + i]);
}
}
oled.h
c
#ifndef __OLED_H__
#define __OLED_H__
#include "sys.h"
//定义密码处理显示字符枚举类型
enum message{
SHOW_INPUT_PWD = 0,
SHOW_PWD_RIGHT,
SHOW_PWD_WRONG,
SHOW_INPUT_OLD_PWD,
SHOW_INPUT_NEW_PWD,
SHOW_PWD_CHANGED,
SHOW_SET_PWD
};
#define OLED_I2C_SCL_CLK() __HAL_RCC_GPIOB_CLK_ENABLE()
#define OLED_I2C_SCL_PORT GPIOB
#define OLED_I2C_SCL_PIN GPIO_PIN_8
#define OLED_I2C_SDA_CLK() __HAL_RCC_GPIOB_CLK_ENABLE()
#define OLED_I2C_SDA_PORT GPIOB
#define OLED_I2C_SDA_PIN GPIO_PIN_9
#define OLED_SCL_RESET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SCL_PIN,GPIO_PIN_RESET)
#define OLED_SCL_SET() HAL_GPIO_WritePin(OLED_I2C_SCL_PORT,OLED_I2C_SCL_PIN,GPIO_PIN_SET)
#define OLED_SDA_RESET() HAL_GPIO_WritePin(OLED_I2C_SDA_PORT,OLED_I2C_SDA_PIN,GPIO_PIN_RESET)
#define OLED_SDA_SET() HAL_GPIO_WritePin(OLED_I2C_SDA_PORT,OLED_I2C_SDA_PIN,GPIO_PIN_SET)
//OLED初始化
void oled_init(void);
//I2C写命令
void oled_write_cmd(uint8_t cmd);
//I2C写数据
void oled_write_data(uint8_t data);
//循环填充
void oled_fill(uint8_t data);
//设置坐标
void oled_set_cursor(uint8_t x, uint8_t y);
//OLED显示一个字符
void oled_show_char(uint8_t x, uint8_t y, uint8_t num, uint8_t size);
//OLED显示字符串
void oled_show_string(uint8_t x, uint8_t y, char *p, uint8_t size);
//OLED显示汉字
void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N, uint8_t size);
//OLED显示图片
void oled_show_image(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t *bmp);
//请输入密码
void oled_show_input(void);
//密码正确
void oled_show_right(void);
//密码错误
void oled_show_wrong(void);
//请输入旧密码
void oled_show_old(void);
//请输入新密码
void oled_show_new(void);
//密码修改成功
void oled_show_changed(void);
//请设定密码
void oled_show_set(void);
#endif
w25q128.c
c
#include "w25q128.h"
SPI_HandleTypeDef spi_handle = {0};
//SPI初始化
void w25q128_spi_init(void)
{
spi_handle.Instance = SPI1; //使用SPI1
spi_handle.Init.Mode = SPI_MODE_MASTER; //作为主设备
spi_handle.Init.Direction = SPI_DIRECTION_2LINES; //全双工传输
spi_handle.Init.DataSize = SPI_DATASIZE_8BIT; //数据位长度位8bit
spi_handle.Init.CLKPolarity = SPI_POLARITY_LOW; //时钟极性为低电平
spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE; //奇数边沿采样
spi_handle.Init.NSS = SPI_NSS_SOFT; //软件控制
spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; //波特率256分频
spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB; //高位先行
spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;
spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
spi_handle.Init.CRCPolynomial = 7;
HAL_SPI_Init(&spi_handle);
}
//SPI硬件相关初始化
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance == SPI1){
GPIO_InitTypeDef gpio_initstruct;
//使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//使能SPI1时钟
__HAL_RCC_SPI1_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_4; //NSS对应引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速
gpio_initstruct.Pull = GPIO_PULLUP; //上拉
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
gpio_initstruct.Pin = GPIO_PIN_5 | GPIO_PIN_7; //CLK、MOSI对应引脚
gpio_initstruct.Mode = GPIO_MODE_AF_PP; //复用推挽输出
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
gpio_initstruct.Pin = GPIO_PIN_6; //MISO对应引脚
gpio_initstruct.Mode = GPIO_MODE_INPUT; //输入模式
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
}
}
//SPI字节数据交换(数据收发)
uint8_t w25q128_spi_swap_byte(uint8_t data)
{
uint8_t recv_data = 0;
HAL_SPI_TransmitReceive(&spi_handle, &data, &recv_data, 1, 1000); //数据收发
return recv_data;
}
//W25Q128初始化
void w25q128_init(void)
{
w25q128_spi_init();
}
//读取设备ID
uint16_t w25q128_read_id(void)
{
uint16_t device_id = 0;
//拉低片选
W25Q128_CS(0);
//写指令
w25q128_spi_swap_byte(FLASH_ManufactDeviceID);
w25q128_spi_swap_byte(0x00);
w25q128_spi_swap_byte(0x00);
w25q128_spi_swap_byte(0x00);
//获取device_id
device_id = w25q128_spi_swap_byte(FLASH_DummyByte) << 8;
device_id |= w25q128_spi_swap_byte(FLASH_DummyByte);
//拉高片选
W25Q128_CS(1);
return device_id;
}
//W25Q128写使能
void w25q128_write_enable(void)
{
//拉低片选
W25Q128_CS(0);
//写入指令
w25q128_spi_swap_byte(FLASH_WriteEnable);
//拉高片选
W25Q128_CS(1);
}
//W25Q128读取SR1寄存器
uint8_t w25q128_read_sr1(void)
{
uint8_t recv_data = 0;
//拉低片选
W25Q128_CS(0);
//发送指令
w25q128_spi_swap_byte(FLASH_ReadStatusReg1);
//读取SR1
recv_data = w25q128_spi_swap_byte(FLASH_DummyByte);
//拉高片选
W25Q128_CS(1);
return recv_data;
}
//发送地址
void w25q128_send_address(uint32_t address)
{
//从高位到低位依次发送
w25q128_spi_swap_byte(address >> 16);
w25q128_spi_swap_byte(address >> 8);
w25q128_spi_swap_byte(address);
}
//W25Q128读数据
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size)
{
uint32_t i = 0;
//拉低片选
W25Q128_CS(0);
//发送指令
w25q128_spi_swap_byte(FLASH_ReadData);
//发送地址
w25q128_send_address(address);
//读取SR1
for(i = 0;i < size;i++){
data[i] = w25q128_spi_swap_byte(FLASH_DummyByte);
}
//拉高片选
W25Q128_CS(1);
}
//忙等待
void w25q128_wait_busy(void)
{
while((w25q128_read_sr1() & 0x01) == 0x01);
}
//W25Q128页写
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size)
{
uint16_t i = 0;
//写使能
w25q128_write_enable();
//拉低片选
W25Q128_CS(0);
//发送指令
w25q128_spi_swap_byte(FLASH_PageProgram);
//发送地址
w25q128_send_address(address);
//写入数据
for(i = 0;i < size;i++){
w25q128_spi_swap_byte(data[i]);
}
//拉高片选
W25Q128_CS(1);
//忙等待
w25q128_wait_busy();
}
//W25Q128擦除扇区
void w25q128_erase_sector(uint32_t address)
{
//写使能
w25q128_write_enable();
//忙等待
w25q128_wait_busy();
//拉低片选
W25Q128_CS(0);
//发送指令
w25q128_spi_swap_byte(FLASH_SectorErase);
//发送地址
w25q128_send_address(address);
//拉高片选
W25Q128_CS(1);
//忙等待
w25q128_wait_busy();
}
w25q128.h
c
#ifndef __W25Q128_H__
#define __W25Q128_H__
#include "sys.h"
#define W25Q128_CS(x) do{ x ? \
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); \
}while(0)
//指令表
#define FLASH_ManufactDeviceID 0x90
#define FLASH_WriteEnable 0x06
#define FLASH_ReadStatusReg1 0x05
#define FLASH_ReadData 0x03
#define FLASH_PageProgram 0x02
#define FLASH_SectorErase 0x20
#define FLASH_DummyByte 0xFF
//W25Q128初始化
void w25q128_init(void);
//读取设备ID
uint16_t w25q128_read_id(void);
//W25Q128读数据
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size);
//忙等待
void w25q128_wait_busy(void);
//W25Q128页写
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size);
//W25Q128擦除扇区
void w25q128_erase_sector(uint32_t address);
#endif
password.c
c
#include "password.h"
#include "w25q128.h"
#include "oled.h"
#include "keyboard.h"
#include "string.h"
#include "stdio.h"
#include "lock.h"
#include "beep.h"
#include "delay.h"
//密码长度宏定义
#define PASSWORD_SIZE 10
//输入字符串
uint8_t pwd_input[PASSWORD_SIZE] = {0};
//读出字符串
uint8_t pwd_read[PASSWORD_SIZE] = {0};
//矩阵键盘输入缓存
uint8_t key_value = 0;
//定义数组索引
uint8_t i = 0;
//密码输入错误次数
uint8_t try_times = 0;
//初始化密码
void password_init(void)
{
//W25Q128初始化
w25q128_init();
}
//清空输入缓存
void password_input_clear(void)
{
memset(pwd_input, 0, PASSWORD_SIZE);
i = 0;
}
//保存密码
void password_save(void)
{
//擦除待写入扇区
w25q128_erase_sector(0x000000);
//写入输入的密码
w25q128_write_page(0x000000, pwd_input, PASSWORD_SIZE);
//OLED屏幕显示密码已修改
oled_show_changed();
}
//获取键盘输入
uint8_t password_get_input(void)
{
//清空输入缓存
password_input_clear();
while(1){
//获取输入字符
key_value = keyboard_get_value();
//判断输入字符逻辑
if(key_value == POUND_KEY){
printf("按下了#键,input: %s\r\n", pwd_input);
return POUND_KEY;
}else if(key_value == START_KEY){
printf("按下了*键\r\n");
return START_KEY;
}else if(key_value != 0){
printf("按下了 %c\r\n", key_value);
//OLED逐个字符显示
oled_show_char(20 + i * 10, 4, key_value, 16);
//存入输入缓存区
pwd_input[i++] = key_value;
}
}
}
//密码比对
uint8_t password_compare(void)
{
uint8_t i = 0;
//读出W25Q128中的密码
w25q128_read_data(0x000000, pwd_read, PASSWORD_SIZE);
//比对密码
//判断长度
if(strlen((char *)pwd_input) != strlen((char *)pwd_read)){
return FALSE;
}
//逐位比较
for(i = 0;i < strlen((char *)pwd_read); i++){
if(pwd_input[i] != pwd_read[i]){
return FALSE;
}
}
return TRUE;
}
//密码输入正确的操作
void password_input_right_action(void)
{
oled_show_right();
lock_on();
beep_on();
delay_ms(500);
beep_off();
delay_ms(1000);
lock_off();
//错误次数清零
try_times = 0;
}
//密码输入错误的操作
void password_input_wrong_action(void)
{
oled_show_wrong();
try_times++;
//错误超过三次
if(try_times >= 3){
beep_on();
delay_ms(1000);
beep_off();
try_times = 0;
}
delay_ms(1000);
}
//旧密码输入正确的操作
void password_old_right_action(void)
{
oled_show_new();
password_get_input();
password_save();
beep_on();
delay_ms(500);
beep_off();
delay_ms(500);
}
//旧密码输入错误的操作
void password_old_wrong_action(void)
{
oled_show_wrong();
delay_ms(1000);
}
//检查密码文件是否存在
void password_check(void)
{
w25q128_read_data(0x000000, pwd_read, PASSWORD_SIZE);
printf("读出密码:%s\r\n", pwd_read);
//判断密码是否存在
if(pwd_read[0] == '\0' || pwd_read[0] == 0xFF){
oled_show_set();
password_get_input();
password_save();
}
}
password.h
c
#ifndef __PASSWORD_H__
#define __PASSWORD_H__
#include "sys.h"
#define POUND_KEY '#'
#define START_KEY '*'
#define TRUE 1
#define FALSE 0
//初始化密码
void password_init(void);
//清空输入缓存
void password_input_clear(void);
//保存密码
void password_save(void);
//获取键盘输入
uint8_t password_get_input(void);
//密码比对
uint8_t password_compare(void);
//密码输入正确的操作
void password_input_right_action(void);
//密码输入错误的操作
void password_input_wrong_action(void);
//旧密码输入正确的操作
void password_old_right_action(void);
//旧密码输入错误的操作
void password_old_wrong_action(void);
//检查密码文件是否存在
void password_check(void);
#endif
5 实物图
