【STM32】矩阵键盘门禁项目(详细注释版)

目录

  • [1 项目需求](#1 项目需求)
  • [2 硬件清单](#2 硬件清单)
  • [3 硬件接线](#3 硬件接线)
  • [4 完整代码](#4 完整代码)
  • [5 实物图](#5 实物图)

1 项目需求

  1. 矩阵键盘输入密码,正确则开锁,错误则提示,三次错误蜂鸣器响3秒;
  2. 按下#号确认输入,按下*号修改密码;
  3. 密码保存在 W25Q128 里;
  4. 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 实物图

相关推荐
SDAU20052 小时前
ESP32C3在Arduino下的串口操作集锦
单片机·嵌入式硬件
沉在嵌入式的鱼2 小时前
STM32--编码器(E6B2-CWZ1X)
stm32·单片机·嵌入式硬件·编码器·e6b2-cwz1x
bai5459362 小时前
STM32 PWM驱动电机
stm32·单片机·嵌入式硬件
cooldream200911 小时前
小智 AI 智能音箱深度体验全解析:人设、音色、记忆与多场景玩法的全面指南
人工智能·嵌入式硬件·智能音箱
聚能芯罗1803804647613 小时前
Hi8001/Hi8002高集成升压芯片2.7-40V 宽输入电压智芯一级代理聚能芯半导体原厂技术支持
嵌入式硬件
传感器与混合集成电路13 小时前
PSOC 7020运算模块解析:从放大器到ADC的片上模拟集成之道
嵌入式硬件·物联网
bai54593614 小时前
STM32单片机光敏传感器控制蜂鸣器
stm32·单片机·嵌入式硬件
米羊12115 小时前
FAT32(上)
stm32·单片机·嵌入式硬件
MARIN_shen17 小时前
Marin说PCB之电容物料的替换经验总计--03
嵌入式硬件·硬件工程·信号处理·pcb工艺