智能门禁的项目

news2025/7/21 15:27:54

项目需求

  1. 矩阵键盘输入密码,正确开锁,错误提示,三次错误后蜂鸣器响三秒;
  2. 按下#号确认输入,按下*号修改密码;
  3. 密码保存在W25Q128里;
  4. OLED屏幕显示信息。

硬件清单

矩阵键盘OLED显示屏继电器蜂鸣器W25Q128存储器
开发板ST-LinkUSB转TTL杜邦线------

其中,OLED显示器利用的是I2C的通信协议,W25Q128用的是SPI的通信协议 ;

矩阵键盘这节课进行介绍。

硬件接线

 简图:

模块:矩阵键盘(原理+驱动) 

         学了了独立按键之后,发现使用一个按键就要浪费一个IO口。那有没有一种节约GPIO的方法,比如要用16个按键,怎么样才能节省IO口?那我们就可以用矩阵键盘。16个按键,只需8IO口。

矩阵键盘介绍

我们矩阵键盘的型号多种多样,有 1x43x33x44x4,甚至是 8x8 的;有薄膜的,有按键的

矩阵键盘常见用途:

1. 电子密码锁

2. ATM 的键盘

3. POS机 的键盘

4. 家用电器的控制面板(微波炉、洗衣机等)

工作参数及引脚介绍 

引脚介绍:

  • 按键式的有的有引脚标识,R 是行,C 是列。有的没有引脚标识,可以找商家问下,或者自己看原理图。

  • 薄膜式的都没有引脚标识。键盘正对自己,引脚是先行后列,从小到大。

 矩阵键盘工作原理

 这是矩阵键盘(4x4)内部电路示意图:

矩阵键盘识别常用——逐行逐列扫描法 

  1. 使列线连接的I/O引脚输出低电平,四条行线所连接的I/O引脚输出高电平,当没有按键按下时,四条行线所连接的I/O引脚读取的将全部是高电平;当按键按下时,由于按键所在的行线与列线相通,行线将被下拉到低电阻。此时读取行线所连接的引脚,将不再是高电平。
  2. 逐列将列线依次置低电平,读取行线,如果某一条行线为低电平,则说明该行线与当前的行线为低电平的列线交叉点处的按键被按下,从判断按键按下。

中断来实现识别

  1. 把所有行引脚设成中断引脚,默认是上拉输入,下降沿触发,GPIO引脚输出高电平。
  2. 把所有列引脚连接的GPIO引脚设成输入内部下拉。(GPIO引脚输入,默认是下拉)
  3. 当有中断触发的时候就可以知道哪一行有按键按下,再逐列看哪一列变成高电平,就是哪个按键被触发了。

小实验:串口输出矩阵键盘按下的按键 

硬件清单:

STM32F103C8T6、矩阵键盘(3x4,薄膜)、USB转TTL、ST-Link

硬件接线: 

文件代码:

  • keyboard.c
#include "keyboard.h"
#include "delay.h"

uint8_t key_value;

void keyboard_init(void){
    
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    //调用GPIO的初始化函数
    GPIO_InitTypeDef GPIO_Initstruct;
    
    GPIO_Initstruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_10;
    GPIO_Initstruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_Initstruct.Pull = GPIO_PULLUP;
    GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_Initstruct);
    
    GPIO_Initstruct.Pin = GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14;
    GPIO_Initstruct.Mode = GPIO_MODE_INPUT;
    GPIO_Initstruct.Pull = GPIO_PULLDOWN;    //配置成下拉模式,当导通时,会变成高电平
    GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_Initstruct); 
    
    HAL_NVIC_SetPriority(EXTI0_IRQn,2,2);
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
    
    HAL_NVIC_SetPriority(EXTI1_IRQn,2,2);
    HAL_NVIC_EnableIRQ(EXTI1_IRQn);
    
    HAL_NVIC_SetPriority(EXTI2_IRQn,2,2);
    HAL_NVIC_EnableIRQ(EXTI2_IRQn);
    
    HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,2);
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
    
}

void EXTI0_IRQHandler(void){
    
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
    
}

void EXTI1_IRQHandler(void){
    
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
    
}
void EXTI2_IRQHandler(void){
    
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2); 
}
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) == GPIO_PIN_SET);
        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) == GPIO_PIN_SET);
        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) == GPIO_PIN_SET);
        column = 0x03;
    }
     else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_14) == GPIO_PIN_SET){
        delay_ms(10);
        while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_14) == GPIO_PIN_SET);
        column = 0x04;
    }
     
    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 == 0x14) ch = 'A';
        
        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 == 0x24) ch = 'B';
        
        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 == 0x34) ch = 'C';
        
        else if(key_value == 0x41) ch = '*';
        else if(key_value == 0x42) ch = '0';
        else if(key_value == 0x43) ch = '#';
        else if(key_value == 0x44) ch = 'D';
        delay_ms(400);
        key_value = 0x00;
    }
    return ch;
}

  •  keyboard.h
#ifndef __KEYBOARD_H__
#define __KEYBOARD_H__
#include "stm32f1xx.h"

void keyboard_init(void);
uint8_t keyboard_get_value(void);

#endif

  • main.c文件代码 
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "keyboard.h"


int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    led_init();                         /* LED初始化 */
    uart1_init(115200);
    printf("hello,world");
    keyboard_init();
    
    uint8_t key_value = 0;
    
    while(1)
    { 
        key_value = keyboard_get_value();
        if(key_value != 0){
            printf("按下了:%c \r\n",key_value);
            key_value = 0;
        }
    }
}

注意:这个代码采用的是配置中断来实现,要回顾关于中断的配置流程。 

 项目框图

(要养成习惯,在做项目的时候一定要配置流程图)

项目代码

输出设备

  • OLED

oled.c文件代码:

#include "oled.h"
#include "delay.h"
#include "font.h"

void oled_gpio_init(void){
    GPIO_InitTypeDef gpio_initstruct;
    OLED_I2C_SCL_CLK();
    OLED_I2C_SDA_CLK();
    
    gpio_initstruct.Pin = OLED_I2C_SCL_PIN;
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_OD;
    gpio_initstruct.Pull = GPIO_PULLUP;
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OLED_I2C_SCL_PORT,&gpio_initstruct);
    
    gpio_initstruct.Pin = OLED_I2C_SDA_PIN;
    HAL_GPIO_Init(OLED_I2C_SDA_PORT,&gpio_initstruct);
    
}

/**
* @breif 配置I2C协议的时序图:开始,读写,应答,结束。
* @note     在配置读写时序的时候,要注意。。。。。
*
*/
void oled_i2c_start(void){
    OLED_SCL_SET();
    OLED_SDA_SET();
    OLED_SDA_RESET();
    OLED_SCL_RESET();
}

void oled_i2c_stop(void){
    OLED_SCL_RESET();
    OLED_SDA_RESET();
    OLED_SCL_SET();
    OLED_SDA_SET();
}
void oled_i2c_ack(void){
    OLED_SCL_SET();
    OLED_SCL_RESET();
}

void oled_i2c_write_byte(uint8_t data){
    uint8_t i,temp;
    temp = data;
    for(i = 0;i < 8 ;i++){
        if((temp & 0x80) == 0x80)
            OLED_SDA_SET();
        else
            OLED_SDA_RESET();
        temp = temp << 1;
        OLED_SCL_SET();
        OLED_SCL_RESET();
    }
}


/**
* @breif    利用I2C的时序图,配置OLED相关的写命令和写指令的函数
* @note     
*
*/
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();
}

void oled_write_date(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();
    
}


/**
* @breif    初始化OLED显示屏的函数
* @note     包括初始化GPIO引脚和初始化OLED的1相关命令
*
*/
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);
    
}

/**
* @breif    设置oled显示屏显示的坐标
* @note     
*/
void oled_set_cursor(uint8_t x,uint8_t y){
    oled_write_cmd(0xB0 + x);
    oled_write_cmd((y & 0x0F) | 0x00);   //取x的低位
    oled_write_cmd(((y & 0xF0) >> 4) | 0x10);  //取高位
}


/**
* @breif    设置oled显示屏清屏函数
* @note     
*/
void oled_fill(uint8_t data){
    uint8_t i,j;
    for (i = 0;i < 8 ;i++){
        oled_set_cursor(i,0);
        for(j = 0;j < 128;j++)
            oled_write_date(data);
    } 
}

/**
* @breif    封装oled显示屏显示字符、字符串、汉字、图像的函数
* @note     
*/

void oled_show_char(uint8_t x ,uint8_t y,uint8_t number ,uint8_t size){         //size代表的是高度
    
    number = number - ' ';
    uint8_t i,j,page;                       //page:表示输入的字符总共需要的PAGE
    
    page = size / 8;
    if(size % 8 != 0)
        page ++;
 
    for (i = 0;i < page; i++)
    {
        oled_set_cursor(x+i,y);
        for (j = size / 2 * i;j < size / 2 * (i + 1); j++){              //size/2:高度的一半是宽度
            if(size == 16)
                oled_write_date(ascii_8X16[number][j]);                   
        }
    }
}

//显示字符串
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);      //*p:取出数组中的首元素
        y += size / 2;
        p++;                               //p++:代表的是数组指针递增,指向下一个元素地址。
    }
}

//void oled_show_chinese(uint8_t x,uint8_t y, uint8_t N ,uint8_t size){

//    uint8_t i,j;
//    for (i = 0 ; i < size / 8 ;i++){
//        oled_set_cursor(x+i,y);
//        for(j = size * i;j < size * (i + 1);j++){
//            if(size == 16)
//                oled_write_date(chinese_16X16[N][j]);
            if(size == 24)
                oled_write_date(chinese_24X24[N][j]);
//        }
//    }
//}

//显示汉字的函数
void oled_show_chinese(uint8_t x,uint8_t y, uint8_t N ,uint8_t message_type){

    uint8_t i,j;
    for (i = 0 ; i < 2 ;i++){
        oled_set_cursor(x+i,y);
        for(j = 16 * i;j < 16 * (i + 1);j++){
            switch (message_type){
                case SHOW_INPUT_PWD:
                    oled_write_date(chinese_enter_password[N][j]);
                    break;
                
                case SHOW_PWD_RIGHT:
                    oled_write_date(chinese_password_right[N][j]);
                    break;
                
                case SHOW_PWD_ERROR:
                    oled_write_date(chinese_password_error[N][j]);
                    break;
                
                case SHOW_INPUT_OLD_PWD:
                    oled_write_date(chinese_enter_oldPassword[N][j]);
                    break;
                
                case SHOW_INPUT_NEW_PWD:
                    oled_write_date(chinese_enter_newPassword[N][j]);
                    break;
                
                case SHOW_PWD_CHANGE:
                    oled_write_date(modify_passWord_complete[N][j]);
                    break;
                
                case SHOW_SET_PWD:
                    oled_write_date(set_passWord[N][j]);
                    break;
                
                default:
                    break;  
            }
        }
    }
}

//请输入密码
void oled_show_input(void){
    oled_fill(0x00);
    oled_show_chinese(1,10,0,SHOW_INPUT_PWD);
    oled_show_chinese(1,30,1,SHOW_INPUT_PWD);
    oled_show_chinese(1,50,2,SHOW_INPUT_PWD);
    oled_show_chinese(1,70,3,SHOW_INPUT_PWD);
    oled_show_chinese(1,90,4,SHOW_INPUT_PWD);
    oled_show_char(1,110,':',16);
}
//密码正确
void oled_pwd_right(void){
    oled_fill(0x00);
    oled_show_chinese(1,10,0,SHOW_PWD_RIGHT);
    oled_show_chinese(1,30,1,SHOW_PWD_RIGHT);
    oled_show_chinese(1,50,2,SHOW_PWD_RIGHT);
    oled_show_chinese(1,70,3,SHOW_PWD_RIGHT);
    oled_show_char(1,90,':',16);
}
//密码错误
void oled_pwd_error(void){
    oled_fill(0x00);
    oled_show_chinese(1,10,0,SHOW_PWD_ERROR);
    oled_show_chinese(1,30,1,SHOW_PWD_ERROR);
    oled_show_chinese(1,50,2,SHOW_PWD_ERROR);
    oled_show_chinese(1,70,3,SHOW_PWD_ERROR);
    oled_show_char(1,110,':',16);
}
//请输入旧密码
void oled_input_old_pwd(void){
    oled_fill(0x00);
    oled_show_chinese(1,10,0,SHOW_INPUT_OLD_PWD);
    oled_show_chinese(1,26,1,SHOW_INPUT_OLD_PWD);
    oled_show_chinese(1,42,2,SHOW_INPUT_OLD_PWD);
    oled_show_chinese(1,58,3,SHOW_INPUT_OLD_PWD);
    oled_show_chinese(1,74,4,SHOW_INPUT_OLD_PWD);
    oled_show_chinese(1,90,5,SHOW_INPUT_OLD_PWD);
    oled_show_char(1,106,':',16);
}
//请输入新密码
void oled_input_new_pwd(void){
    oled_fill(0x00);
    oled_show_chinese(1,10,0,SHOW_INPUT_NEW_PWD);
    oled_show_chinese(1,26,1,SHOW_INPUT_NEW_PWD);
    oled_show_chinese(1,42,2,SHOW_INPUT_NEW_PWD);
    oled_show_chinese(1,58,3,SHOW_INPUT_NEW_PWD);
    oled_show_chinese(1,74,4,SHOW_INPUT_NEW_PWD);
    oled_show_chinese(1,90,4,SHOW_INPUT_NEW_PWD);
    oled_show_char(1,106,':',16);
}
//密码修改完成
void oled_modif_pwd_complete(void){
    oled_fill(0x00);
    oled_show_chinese(1,10,0,SHOW_PWD_CHANGE);
    oled_show_chinese(1,26,1,SHOW_PWD_CHANGE);
    oled_show_chinese(1,42,2,SHOW_PWD_CHANGE);
    oled_show_chinese(1,58,3,SHOW_PWD_CHANGE);
    oled_show_chinese(1,74,4,SHOW_PWD_CHANGE);
    oled_show_chinese(1,90,5,SHOW_PWD_CHANGE);
    oled_show_char(1,106,':',16);
}
//请设置密码
void oled_set_pwd(void){
    oled_fill(0x00);
    oled_show_chinese(1,10,0,SHOW_SET_PWD);
    oled_show_chinese(1,30,1,SHOW_SET_PWD);
    oled_show_chinese(1,50,2,SHOW_SET_PWD);
    oled_show_chinese(1,70,3,SHOW_SET_PWD);
    oled_show_chinese(1,90,4,SHOW_SET_PWD);
    oled_show_char(1,110,':',16);
}

//显示图像
 void oled_show_image(uint8_t x,uint8_t y, uint8_t width ,uint8_t highth ,uint8_t * bpm){
     uint8_t i,j;
     for(i = 0; i < highth ;i++)
     {
         oled_set_cursor(x+i,j);
         for(j = 0;j < width;j++)
            oled_write_date(bpm[ width * i + j ]);
     }
 }

 oled.h文件代码:

#ifndef __OLED_H__
#define __OLED_H__
#include "stm32f1xx.h"

enum message{
    SHOW_INPUT_PWD = 0,
    SHOW_PWD_RIGHT,
    SHOW_PWD_ERROR,
    SHOW_INPUT_OLD_PWD,
    SHOW_INPUT_NEW_PWD,
    SHOW_PWD_CHANGE,
    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)


void oled_init(void);
void oled_set_cursor(uint8_t x,uint8_t y);
void oled_fill(uint8_t data);
void oled_write_date(uint8_t data);
void oled_show_char(uint8_t x ,uint8_t y,uint8_t number ,uint8_t size);
void oled_show_string(uint8_t x,uint8_t y,char *p ,uint8_t size);
void oled_show_chinese(uint8_t x, uint8_t y, uint8_t N, uint8_t size);
void oled_show_image(uint8_t x,uint8_t y, uint8_t width ,uint8_t highth,uint8_t * bpm);

void oled_show_input(void);
void oled_pwd_right(void);
void oled_pwd_error(void);
void oled_input_old_pwd(void);
void oled_input_new_pwd(void);
void oled_modif_pwd_complete(void);
void oled_set_pwd(void);


#endif

font.h文件代码 

#ifndef __FONT_H__
#define __FONT_H__

//请输入密码;
const unsigned char chinese_enter_password[][32] = {
    0x40,0x42,0xCC,0x00,0x00,0x44,0x54,0x54,0x54,0x7F,0x54,0x54,0x54,0x44,0x40,0x00,
    0x00,0x00,0x7F,0x20,0x10,0x00,0xFF,0x15,0x15,0x15,0x55,0x95,0x7F,0x00,0x00,0x00,/*"请",0*/
        
    0x88,0x68,0x1F,0xC8,0x08,0x10,0xC8,0x54,0x52,0xD1,0x12,0x94,0x08,0xD0,0x10,0x00,
    0x09,0x19,0x09,0xFF,0x05,0x00,0xFF,0x12,0x92,0xFF,0x00,0x5F,0x80,0x7F,0x00,0x00,/*"输",1*/
        
    0x00,0x00,0x00,0x00,0x00,0x01,0xE2,0x1C,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x80,0x40,0x20,0x10,0x0C,0x03,0x00,0x00,0x00,0x03,0x0C,0x30,0x40,0x80,0x80,0x00,/*"入",2*/
        
    0x10,0x8C,0x44,0x04,0xE4,0x04,0x95,0xA6,0x44,0x24,0x14,0x84,0x44,0x94,0x0C,0x00,
    0x02,0x02,0x7A,0x41,0x41,0x43,0x42,0x7E,0x42,0x42,0x42,0x43,0xF8,0x00,0x00,0x00,/*"密",3*/
        
    0x04,0x84,0xE4,0x5C,0x44,0xC4,0x00,0x02,0xF2,0x82,0x82,0x82,0xFE,0x80,0x80,0x00,
    0x02,0x01,0x7F,0x10,0x10,0x3F,0x00,0x08,0x08,0x08,0x08,0x48,0x88,0x40,0x3F,0x00,/*"码",4*/
};

//密码正确;
const unsigned char chinese_password_right[][32] = {
    0x10,0x8C,0x44,0x04,0xE4,0x04,0x95,0xA6,0x44,0x24,0x14,0x84,0x44,0x94,0x0C,0x00,
    0x02,0x02,0x7A,0x41,0x41,0x43,0x42,0x7E,0x42,0x42,0x42,0x43,0xF8,0x00,0x00,0x00,/*"密",0*/
    
    0x04,0x84,0xE4,0x5C,0x44,0xC4,0x00,0x02,0xF2,0x82,0x82,0x82,0xFE,0x80,0x80,0x00,
    0x02,0x01,0x7F,0x10,0x10,0x3F,0x00,0x08,0x08,0x08,0x08,0x48,0x88,0x40,0x3F,0x00,/*"码",1*/
    
    0x00,0x02,0x02,0xC2,0x02,0x02,0x02,0xFE,0x82,0x82,0x82,0x82,0x82,0x02,0x00,0x00,
    0x40,0x40,0x40,0x7F,0x40,0x40,0x40,0x7F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,/*"正",2*/
    
    0x04,0x84,0xE4,0x5C,0x44,0xC4,0x20,0x10,0xE8,0x27,0x24,0xE4,0x34,0x2C,0xE0,0x00,
    0x02,0x01,0x7F,0x10,0x10,0x3F,0x80,0x60,0x1F,0x09,0x09,0x3F,0x49,0x89,0x7F,0x00,/*"确",3*/
};
//密码错误;
const unsigned char chinese_password_error[][32] = {
    0x10,0x8C,0x44,0x04,0xE4,0x04,0x95,0xA6,0x44,0x24,0x14,0x84,0x44,0x94,0x0C,0x00,
    0x02,0x02,0x7A,0x41,0x41,0x43,0x42,0x7E,0x42,0x42,0x42,0x43,0xF8,0x00,0x00,0x00,/*"密",0*/
    
    0x04,0x84,0xE4,0x5C,0x44,0xC4,0x00,0x02,0xF2,0x82,0x82,0x82,0xFE,0x80,0x80,0x00,
    0x02,0x01,0x7F,0x10,0x10,0x3F,0x00,0x08,0x08,0x08,0x08,0x48,0x88,0x40,0x3F,0x00,/*"码",1*/
    
    0x40,0x30,0xEF,0x24,0x64,0x48,0x48,0x7F,0x48,0x48,0x48,0x7F,0x48,0x48,0x40,0x00,
    0x01,0x01,0x7F,0x21,0x11,0x00,0xFF,0x49,0x49,0x49,0x49,0x49,0xFF,0x00,0x00,0x00,/*"错",2*/
    
    0x40,0x42,0xCC,0x00,0x00,0x80,0x9E,0x92,0x92,0x92,0x92,0x92,0x9E,0x80,0x00,0x00,
    0x00,0x00,0x7F,0x20,0x94,0x84,0x44,0x24,0x14,0x0F,0x14,0x24,0x44,0x84,0x84,0x00,/*"误",3*/
};

//请输入旧密码;
const unsigned char chinese_enter_oldPassword[][32] = {
    0x40,0x42,0xCC,0x00,0x00,0x44,0x54,0x54,0x54,0x7F,0x54,0x54,0x54,0x44,0x40,0x00,
    0x00,0x00,0x7F,0x20,0x10,0x00,0xFF,0x15,0x15,0x15,0x55,0x95,0x7F,0x00,0x00,0x00,/*"请",0*/
    
    0x88,0x68,0x1F,0xC8,0x08,0x10,0xC8,0x54,0x52,0xD1,0x12,0x94,0x08,0xD0,0x10,0x00,
    0x09,0x19,0x09,0xFF,0x05,0x00,0xFF,0x12,0x92,0xFF,0x00,0x5F,0x80,0x7F,0x00,0x00,/*"输",1*/
    
    0x00,0x00,0x00,0x00,0x00,0x01,0xE2,0x1C,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x80,0x40,0x20,0x10,0x0C,0x03,0x00,0x00,0x00,0x03,0x0C,0x30,0x40,0x80,0x80,0x00,/*"入",2*/
    
    0x00,0x00,0xFF,0x00,0x00,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0x82,0xFE,0x00,0x00,
    0x00,0x00,0xFF,0x00,0x00,0x00,0x7F,0x20,0x20,0x20,0x20,0x20,0x20,0x7F,0x00,0x00,/*"旧",3*/
    
    0x10,0x8C,0x44,0x04,0xE4,0x04,0x95,0xA6,0x44,0x24,0x14,0x84,0x44,0x94,0x0C,0x00,
    0x02,0x02,0x7A,0x41,0x41,0x43,0x42,0x7E,0x42,0x42,0x42,0x43,0xF8,0x00,0x00,0x00,/*"密",4*/
    
    0x04,0x84,0xE4,0x5C,0x44,0xC4,0x00,0x02,0xF2,0x82,0x82,0x82,0xFE,0x80,0x80,0x00,
    0x02,0x01,0x7F,0x10,0x10,0x3F,0x00,0x08,0x08,0x08,0x08,0x48,0x88,0x40,0x3F,0x00,/*"码",5*/
};

//请输入新密码
const unsigned char chinese_enter_newPassword[][32] = {
    0x40,0x42,0xCC,0x00,0x00,0x44,0x54,0x54,0x54,0x7F,0x54,0x54,0x54,0x44,0x40,0x00,
    0x00,0x00,0x7F,0x20,0x10,0x00,0xFF,0x15,0x15,0x15,0x55,0x95,0x7F,0x00,0x00,0x00,/*"请",0*/
    0x88,0x68,0x1F,0xC8,0x08,0x10,0xC8,0x54,0x52,0xD1,0x12,0x94,0x08,0xD0,0x10,0x00,
    0x09,0x19,0x09,0xFF,0x05,0x00,0xFF,0x12,0x92,0xFF,0x00,0x5F,0x80,0x7F,0x00,0x00,/*"输",1*/
    0x00,0x00,0x00,0x00,0x00,0x01,0xE2,0x1C,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x80,0x40,0x20,0x10,0x0C,0x03,0x00,0x00,0x00,0x03,0x0C,0x30,0x40,0x80,0x80,0x00,/*"入",2*/
    0x40,0x44,0x54,0x65,0xC6,0x64,0x54,0x44,0x00,0xFC,0x44,0x44,0xC4,0x42,0x40,0x00,
    0x20,0x12,0x4A,0x82,0x7F,0x02,0x0A,0x92,0x60,0x1F,0x00,0x00,0xFF,0x00,0x00,0x00,/*"新",3*/
    0x10,0x8C,0x44,0x04,0xE4,0x04,0x95,0xA6,0x44,0x24,0x14,0x84,0x44,0x94,0x0C,0x00,
    0x02,0x02,0x7A,0x41,0x41,0x43,0x42,0x7E,0x42,0x42,0x42,0x43,0xF8,0x00,0x00,0x00,/*"密",4*/
    0x04,0x84,0xE4,0x5C,0x44,0xC4,0x00,0x02,0xF2,0x82,0x82,0x82,0xFE,0x80,0x80,0x00,
    0x02,0x01,0x7F,0x10,0x10,0x3F,0x00,0x08,0x08,0x08,0x08,0x48,0x88,0x40,0x3F,0x00,/*"码",5*/
};
//密码修改成功
const unsigned char modify_passWord_complete[][32] = {
   0x10,0x8C,0x44,0x04,0xE4,0x04,0x95,0xA6,0x44,0x24,0x14,0x84,0x44,0x94,0x0C,0x00,
    0x02,0x02,0x7A,0x41,0x41,0x43,0x42,0x7E,0x42,0x42,0x42,0x43,0xF8,0x00,0x00,0x00,/*"密",0*/
    0x04,0x84,0xE4,0x5C,0x44,0xC4,0x00,0x02,0xF2,0x82,0x82,0x82,0xFE,0x80,0x80,0x00,
    0x02,0x01,0x7F,0x10,0x10,0x3F,0x00,0x08,0x08,0x08,0x08,0x48,0x88,0x40,0x3F,0x00,/*"码",1*/
    0x40,0x20,0xF8,0x07,0xF0,0xA0,0x90,0x4C,0x57,0x24,0xA4,0x54,0x4C,0x80,0x80,0x00,
    0x00,0x00,0xFF,0x00,0x1F,0x80,0x92,0x52,0x49,0x29,0x24,0x12,0x08,0x00,0x00,0x00,/*"修",2*/
    0x04,0x84,0x84,0x84,0x84,0xFC,0x40,0x30,0xCC,0x0B,0x08,0x08,0xF8,0x08,0x08,0x00,
    0x00,0x7F,0x20,0x10,0x10,0x08,0x80,0x40,0x21,0x16,0x08,0x16,0x21,0x40,0x80,0x00,/*"改",3*/
    0x00,0x00,0xF8,0x88,0x88,0x88,0x88,0x08,0x08,0xFF,0x08,0x09,0x0A,0xC8,0x08,0x00,
    0x80,0x60,0x1F,0x00,0x10,0x20,0x1F,0x80,0x40,0x21,0x16,0x18,0x26,0x41,0xF8,0x00,/*"成",4*/
    0x08,0x08,0x08,0xF8,0x08,0x08,0x08,0x10,0x10,0xFF,0x10,0x10,0x10,0xF0,0x00,0x00,
    0x10,0x30,0x10,0x1F,0x08,0x88,0x48,0x30,0x0E,0x01,0x40,0x80,0x40,0x3F,0x00,0x00,/*"功",5*/ 
};
//请设定密码
const unsigned char set_passWord[][32] = {
    0x40,0x42,0xCC,0x00,0x00,0x44,0x54,0x54,0x54,0x7F,0x54,0x54,0x54,0x44,0x40,0x00,
    0x00,0x00,0x7F,0x20,0x10,0x00,0xFF,0x15,0x15,0x15,0x55,0x95,0x7F,0x00,0x00,0x00,/*"请",0*/
    0x40,0x40,0x42,0xCC,0x00,0x40,0xA0,0x9E,0x82,0x82,0x82,0x9E,0xA0,0x20,0x20,0x00,
    0x00,0x00,0x00,0x3F,0x90,0x88,0x40,0x43,0x2C,0x10,0x28,0x46,0x41,0x80,0x80,0x00,/*"设",1*/
    0x10,0x0C,0x44,0x44,0x44,0x44,0x45,0xC6,0x44,0x44,0x44,0x44,0x44,0x14,0x0C,0x00,
    0x80,0x40,0x20,0x1E,0x20,0x40,0x40,0x7F,0x44,0x44,0x44,0x44,0x44,0x40,0x40,0x00,/*"定",2*/
    0x10,0x8C,0x44,0x04,0xE4,0x04,0x95,0xA6,0x44,0x24,0x14,0x84,0x44,0x94,0x0C,0x00,
    0x02,0x02,0x7A,0x41,0x41,0x43,0x42,0x7E,0x42,0x42,0x42,0x43,0xF8,0x00,0x00,0x00,/*"密",3*/
    0x04,0x84,0xE4,0x5C,0x44,0xC4,0x00,0x02,0xF2,0x82,0x82,0x82,0xFE,0x80,0x80,0x00,
    0x02,0x01,0x7F,0x10,0x10,0x3F,0x00,0x08,0x08,0x08,0x08,0x48,0x88,0x40,0x3F,0x00,/*"码",4*/
};
  • BEEP (蜂鸣器)

beep.c文件代码

#include "stm32f1xx.h"
#include "beep.h"

//初始化GPIO口
void beep_init(void){
    //打开时钟
    __HAL_RCC_GPIOC_CLK_ENABLE();
    //打开GPIO口
    GPIO_InitTypeDef GPIO_Initstruct;
    GPIO_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Initstruct.Pin = GPIO_PIN_13;
    GPIO_Initstruct.Pull = GPIO_NOPULL;
    GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOC,&GPIO_Initstruct);
    //关闭蜂鸣器
    beep_off();
}
//打开蜂鸣器函数
void beep_on(void){
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
}
//关闭蜂鸣器
void beep_off(void){
    HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
}
//反转蜂鸣器
void beep_toggle(void){
    HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);  
}
  • SWITCH(开关) 

 switch.h文件代码

#include "alarm.h"
#include "stm32f1xx.h"                  // 外设的驱动函数


//初始化GPIO口

void alarm_init(void){
    
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    //调用GPIO的初始化函数
    GPIO_InitTypeDef GPIO_Initstruct;
    GPIO_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Initstruct.Pin = GPIO_PIN_7;
    GPIO_Initstruct.Pull = GPIO_PULLUP;
    GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_Initstruct);
    //关闭继电器
    alarm_off();
}
//接通继电器的函数
void alarm_on(void){   
    
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET);   
}

//断开继电器的函数
void alarm_off(void){
    
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);
    
}

//获取继电器的状态
uint8_t alarm_status_get(void){
    
    return (uint8_t)HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7);
}
  • W25Q128(FLASH存储器) 

w25q128.c文件代码

#include "w25q128.h"

SPI_HandleTypeDef spi_handle = {0};
void w25q128_spi_init(void){
    
    spi_handle.Instance = 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;      //CPOL = 0
    spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE;          //CPHA = 奇数边沿检测
    spi_handle.Init.NSS = SPI_NSS_SOFT;               //软件控制SS引脚配置
    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);
}


void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi){
    if(hspi->Instance == SPI1){
        __HAL_RCC_GPIOA_CLK_ENABLE();
        __HAL_RCC_SPI1_CLK_ENABLE();
        
        GPIO_InitTypeDef gpio_initstruct;
        
        //NSS引脚
        gpio_initstruct.Pin = GPIO_PIN_4;
        gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;
        gpio_initstruct.Pull = GPIO_PULLUP;
        gpio_initstruct.Speed  = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOA,&gpio_initstruct);
        
        //SCL引脚和输出引脚
        gpio_initstruct.Pin = GPIO_PIN_5 |GPIO_PIN_7;
        gpio_initstruct.Mode = GPIO_MODE_AF_PP;
        HAL_GPIO_Init(GPIOA,&gpio_initstruct);
        
        //输入引脚
        gpio_initstruct.Pin = GPIO_PIN_6;
        gpio_initstruct.Mode = GPIO_MODE_INPUT;
        HAL_GPIO_Init(GPIOA,&gpio_initstruct);
        
    } 
}

uint8_t w25q128_spi_swap_byte(uint8_t data){
    
    uint8_t recv_data = 0;
    HAL_SPI_TransmitReceive(&spi_handle,&data, &recv_data,1,1000);  ///size:尺寸代表是多少个字节
    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 = w25q128_spi_swap_byte(FLASH_DummyBtye) << 8;  /* 将数据放在高8位 */
    device_id |= w25q128_spi_swap_byte(FLASH_DummyBtye);      /* 利用 |= 将数据放在低8位,并保留高8位的数据 */
    
    W25Q128_CS(1);
    
    return device_id; 
}

//写使能

void w25q128_write_enable(void)
{
    W25Q128_CS(0);
    w25q128_spi_swap_byte(FLASH_WriteEable);
    W25Q128_CS(1);
}

//读SR1寄存器
uint8_t w25q128_read_sr1(void){
    
    uint8_t recv_data = 0;
    W25Q128_CS(0);
    w25q128_spi_swap_byte(FLASH_ReadStatusReg1);
    recv_data = w25q128_spi_swap_byte(FLASH_DummyBtye);
    W25Q128_CS(1);
    return recv_data;
}

//发送地址的函数
void w25q128_send_address(uint32_t address){   //地址是3个字节,先发送高位,在发送中位,最后发送低位
    
    w25q128_spi_swap_byte(address >> 16);//高位
    w25q128_spi_swap_byte(address >> 8);  //中位:由于函数是一个8位的,因此,移动后数据后,高位自动去掉。
    w25q128_spi_swap_byte(address);
}

//忙等待的函数
void w25q128_wait_busy(void){
    
    while ((w25q128_read_sr1() & 0x01) == 0x01);  //判断最后一位是不是1
}

//读数据
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_ReadDate);
    w25q128_send_address(address);
    
    for(i = 0;i< size; i++)
       data[i] =  w25q128_spi_swap_byte(FLASH_DummyBtye);
    W25Q128_CS(1);
    
}
//页写:写的是256个字节,
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);
    
    //忙等待,写入数据是需要花费时间的;看状态寄存器的最后一位是0还是1
    w25q128_wait_busy();
}
//扇区擦除
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文件代码

#ifndef __W25Q128_H__
#define __W25Q128_H__
#include "stm32f1xx.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_WriteEable                0x06
#define FLASH_ReadStatusReg1            0x05
#define FLASH_ReadDate                  0x03
#define FLASH_PageProgram               0x02
#define FLASH_SectorErase               0x20
#define FLASH_DummyBtye                 0xFF                       

void w25q128_init(void);
uint16_t w25q128_read_id(void);
void w25q128_read_data(uint32_t address,uint8_t *data,uint32_t size);
void w25q128_write_page(uint32_t address,uint8_t *data,uint16_t size);
void w25q128_erase_sector(uint32_t address);                       
#endif

输入设备 

  • KEYBOARD(矩阵键盘

keyboard.c文件代码:

#include "keyboard.h"
#include "delay.h"

static uint8_t key_value;

void keyboard_init(void){
    
    //打开时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    //调用GPIO的初始化函数
    GPIO_InitTypeDef GPIO_Initstruct;
    
    GPIO_Initstruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_10;
    GPIO_Initstruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_Initstruct.Pull = GPIO_PULLUP;
    GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_Initstruct);
    
    GPIO_Initstruct.Pin = GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14;
    GPIO_Initstruct.Mode = GPIO_MODE_INPUT;
    GPIO_Initstruct.Pull = GPIO_PULLDOWN;    //配置成下拉模式,当导通时,会变成高电平
    GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_Initstruct); 
    
    HAL_NVIC_SetPriority(EXTI0_IRQn,2,2);
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
    
    HAL_NVIC_SetPriority(EXTI1_IRQn,2,2);
    HAL_NVIC_EnableIRQ(EXTI1_IRQn);
    
    HAL_NVIC_SetPriority(EXTI2_IRQn,2,2);
    HAL_NVIC_EnableIRQ(EXTI2_IRQn);
    
    HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,2);
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
    
}

void EXTI0_IRQHandler(void){
    
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
    
}

void EXTI1_IRQHandler(void){
    
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
    
}
void EXTI2_IRQHandler(void){
    
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2); 
}
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) == GPIO_PIN_SET);
        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) == GPIO_PIN_SET);
        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) == GPIO_PIN_SET);
        column = 0x03;
    }
     else if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_14) == GPIO_PIN_SET){
        delay_ms(10);
        while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_14) == GPIO_PIN_SET);
        column = 0x04;
    }
     
    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 == 0x14) ch = 'A';
        
        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 == 0x24) ch = 'B';
        
        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 == 0x34) ch = 'C';
        
        else if(key_value == 0x41) ch = '*';
        else if(key_value == 0x42) ch = '0';
        else if(key_value == 0x43) ch = '#';
        else if(key_value == 0x44) ch = 'D';
        delay_ms(400);
        key_value = 0x00;
    }
    return ch;
}

 PASSWORD(配置密码)

password.c文件代码:

#include "password.h"
#include "w25q128.h"
#include "oled.h"
#include "keyboard.h"
#include "string.h"
#include "stdio.h"
#include "alarm.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 i = 0,key_value = 0,try_times = 0;

                    /********** 1 *********/

//初始化函数
void password_init(void){
    w25q128_init();   
}
//读取密码:读取的是w25Q128_read_data();

//保存密码
void password_save(void){
    w25q128_erase_sector(0x000000);
    w25q128_write_page(0x000000,pwd_input,PASSWORD_SIZE);
    oled_modif_pwd_complete();
}
//清空输入缓存
void password_input_clear(void){
    memset(pwd_input, 0 ,PASSWORD_SIZE);
    i = 0;
}

//获取键盘输入:将按键输入(数字、字母)存到一个字符串数组当中,而“#”,“*”则进行返回。
uint8_t password_get_input(void){
   password_input_clear();                                  /*清除 字符数组 中的值 */
    
   while(1){
      key_value =  keyboard_get_value();
       if(key_value == '#'){
           printf("按下了 %c 键 \r\n", key_value);
           return POUND_KEY;
       }
       else if(key_value == '*'){
           printf("按下了 %c 键 \r\n", key_value);
           return STAR_KEY;
       }
       else if(key_value != 0){
           printf("按下了 %c 键 \r\n", key_value);
           oled_show_char(3,i * 10,key_value,16);  //字符的尺寸。
           pwd_input[i++] = key_value;
       }
   }
    
}
                    /********** 2 *********/
//密码比对:长度和元素。返回值:TURE和FLASE
uint8_t password_compare(void){
    uint8_t i = 0;
    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_pwd_right();
    alarm_on();
    beep_on();
    delay_ms(500);
    beep_off();
    delay_s(1);
    alarm_off();
    try_times = 0;
    
}
//密码输入错误的操作
void password_input_error_action(void){
    oled_pwd_error();
    try_times ++;
    if(try_times >= 3){
        beep_on();
        delay_ms(1000);
        beep_off();
        try_times = 0;
    }
    delay_s(1);
}

//旧密码输入正确的操作
void password_old_right_action(void){
    oled_input_new_pwd();
    password_get_input();
    password_save();
    beep_on();
    delay_ms(300);
    beep_off();
    delay_ms(700);
}

//旧密码输入错误的操作
void password_old_error_action(void){
    oled_pwd_error();
    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_set_pwd();
        password_get_input();
        password_save();
    }
}

password.h文件代码

#ifndef __PASSWORD_H__
#define __PASSWPRD_H__
#include "stm32f1xx.h"

#define FALSE               0
#define TRUE                1

#define POUND_KEY   '#'
#define STAR_KEY    '*'

void password_init(void);

void password_check(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_error_action(void);

void password_old_right_action(void);
void password_old_error_action(void);


#endif

 主函数

main.c文件代码:

#include "sys.h"
#include "led.h"
#include "delay.h"
#include "uart1.h"
#include "oled.h"
#include "beep.h"
#include "alarm.h"
#include "w25q128.h"
#include "keyboard.h"
#include "password.h"


int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    uart1_init(115200);
    printf("hello,world!!!\r\n");
    
    beep_init();
    alarm_init();
    oled_init();
    keyboard_init();
    password_init();
//    w25q128_init();
    
    password_check();
    uint8_t key_last = 0;
    while(1)
    { 
       oled_show_input();
       key_last = password_get_input();
       if(key_last == POUND_KEY){
           if(password_compare() == TRUE)
               password_input_right_action();
           else
               password_input_error_action();
       }
       else if(key_last == STAR_KEY){
           oled_input_old_pwd();
           password_get_input();
           if(password_compare() == TRUE)
               password_old_right_action();
           else
               password_old_error_action();
       }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2393788.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《Google I/O 2025:AI浪潮下的科技革新风暴》

Google I/O 2025 盛大开幕 在科技飞速发展的时代&#xff0c;Google I/O 开发者大会一直是全球科技爱好者和开发者瞩目的焦点&#xff0c;堪称科技领域的年度盛宴。2025 年 5 月 20 日至 21 日&#xff0c;Google I/O 2025 在美国加州山景城的 Shoreline Amphitheatre 盛大举行…

职坐标IT培训:硬件嵌入式与AI芯片开发实战

课程体系以硬件嵌入式开发与AI芯片技术融合为核心&#xff0c;构建模块化知识框架。从硬件设计规范切入&#xff0c;系统讲解PCB Layout设计中的信号完整性控制、电磁兼容性&#xff08;EMC&#xff09;优化等关键要素&#xff0c;延伸至高速电路设计中阻抗匹配与电源完整性&am…

一句话开发Chrome摸鱼插件

本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴&#xfeff;。 CodeBuddy 一、CodeBuddy新功能特色 Craft智能体&#xff1a;自然语言驱动的全栈开发引擎Craft开发智能体的核心突破在于实现需求理解-任务拆解-代码生成的…

Spring Boot + OpenCSV 数据清洗实战:CSV 结构化处理与可视化

目录 摘要 演示 一、背景&#xff1a;为什么需要自动化数据清洗&#xff1f; 二、技术选型&#xff1a;为什么选择这三个工具&#xff1f; 三、核心功能实现&#xff1a;从数据读取到智能清洗 1. 配置控制器 2. 文件上传控制器 3. CSV数据处理服务接口 4. CSV数据处理…

Cmake编译glog成功并在QT中测试成功步骤

glog是开源的日志记录系统&#xff0c;下载地址GitHub - google/glog: C implementation of the Google logging module 跟gflags有点相似&#xff0c;编译和测试过程比较周折&#xff0c;所以记录下来具体的编译和测试步骤。 编译环境&#xff1a;WindowsCmakeVs2022Qt5.14.…

xhr、fetch和axios

XMLHttpRequest (XHR) XMLHttpRequest 是最早用于在浏览器中进行异步网络请求的 API。它允许网页在不刷新整个页面的情况下与服务器交换数据。 // 创建 XHR 对象 const xhr new XMLHttpRequest();// 初始化请求 xhr.open(GET, https://api.example.com/data, true);// 设置请…

2025吉林ccpc【部分题解】

文章目录 C - SSPPSPSPProblemD.互互互质质质ProblemF. Ever ForeverProblemG.石石石头头头剪剪剪刀刀刀布布布Problem J.奇偶游戏Problem L.好矩阵 C - SSPPSPSP 题目来源&#xff1a;C - SSPPSPSP !](https://i-blog.csdnimg.cn/direct/26fc1492b1724446be61cf39b718cf9b.…

PowerDesigner通过SQL反向生成类图

PowerDesigner通过SQL反向生成类图 背景操作步骤步骤1: 选择这个步骤2: 目前我是选择的这个步骤3: 选择这个 其他 背景 工作学习 操作步骤 步骤1: 选择这个 步骤2: 目前我是选择的这个 步骤3: 选择这个 其他 其他同事告诉我的, 我还没有亲自尝试, 应该问题不大. 尝试后再反…

【bug排查记录】由Redission配置引发的Satoken血案

背景 在今天服务更改服务配置的redis的database后&#xff0c;本地测试通过&#xff0c;发布到线上。出现所有用户登录状态失效&#xff0c;并且重新登录后也是失效的问题&#xff0c;由于鉴权登录框架使用的是satoken&#xff0c;线上database配置的是1&#xff0c;然后去red…

MAC程序签名遇到的问题

整体步骤 需要一个apple开发者账号&#xff0c;个人账户注册需要按年付费 申请证书 申请证书链接 command 空格打开聚焦搜索 输入钥匙串访问打开 默认情况下是没有要的证书的 点击左上角的证书助理&#xff0c;选择请求证书 根据输入创建请求文件并保存到本地 点开申请…

华为OD机试真题——宜居星球改造计划(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 200分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 本文收录于专栏:《2025华为OD真题目录+全流程解析/备考攻略/经验分享》 华为OD机试真题《宜居…

Linux下基本指令的介绍

目录 1.目标 2.快速认识指令 1.pwd 2.clear 3.ls 4.mkdir 5.cd 6.touch 3.细化每一条指令 1.ls指令 2.pwd指令 注&#xff1a; 3.cd指令 4.touch和mkdir指令 6.删除文件 4.总结 1.目标 上一篇博客中&#xff0c;我们了解了Linux的起源和发展&#xff0c;也知道…

零基础开始的网工之路第十四天------Linux程序管理

目录 一、Linux程序与进程 1、程序,进程,线程的概念 2、程序和进程的区别 3、进程和线程的区别 二、Linux进程基础(生命周期) 1、进程生命周期 2、父子进程的关系 三、程序管理 1、常见的软件包类型 四、Linux操作系统启动流程详解 1、概述 2、启动流程核心阶段 1…

SIGGRAPH 2025 | 快手可灵团队提出3D感知的电影级文本到视频生成框架CineMaster

Sora、可灵等视频生成模型令人惊艳的性能表现使得创作者仅依靠文本输入就能够创作出高质量的视频内容。然而&#xff0c;我们常见的电影片段通常是由导演在一个场景中精心布置多个目标的运动、摄像机拍摄角度后再剪辑而成的。例如&#xff0c;在拍摄赛车追逐的场景时&#xff0…

历年西安电子科技大学计算机保研上机真题

2025西安电子科技大学计算机保研上机真题 2024西安电子科技大学计算机保研上机真题 2023西安电子科技大学计算机保研上机真题 在线测评链接&#xff1a;https://pgcode.cn/school 查找不同的连续数字串个数 题目描述 给定一个数字串&#xff0c;查找其中不同的连续数字串的个…

利用openwrt路由器和随身WIFI搭建CPE

背景&#xff1a; 最近5GCPE挺火&#xff0c;各种硬件层出不穷&#xff0c;包括DY上很多商家在推的AX3000叠加展锐RM500 5G模块&#xff0c;自己组装CPE&#xff0c;成本也在300 看了下开源硬件&#xff0c;其实就是一个开源的openwrt系统&#xff0c;硬件上5G模块通过usb协议…

科学智能赋能空间科学研究(2):AI4S 范式下空间科学实验的核心挑战

中国科学院空间应用工程与技术中心在空间科学实验领域的研究覆盖了多模态空间科学实验数据模式挖掘、领域知识抽取、跨学科知识融合与认知智能等研究内容&#xff0c;有效促进了空间科学实验领域的数据应用生态的体系化建设&#xff0c;相关研究成果已正式发表于权威学术期刊《…

Axure设计案例——科技感渐变线性图

想让数据变化趋势展示告别枯燥乏味&#xff0c;成为吸引观众目光的亮点吗&#xff1f;快来看看这个Axure设计的科技感渐变线性图案例&#xff01;科技感设计风格凭借炫酷的渐变色彩打破传统线性图的单调&#xff0c;营造出一种令人过目难忘的视觉体验。每一条线条都仿佛是流动的…

【Opencv+Yolo】Day2_图像处理

目录 一、图像梯度计算 图像梯度-sobal算子&#xff1a; Scharr&#xff1a;权重变化更大&#xff08;线条更加丰富&#xff0c;比Sobel更加细致捕捉更多梯度信息&#xff09; Laplacian算子&#xff1a;对噪音点敏感&#xff08;可以和其他一起结合使用&#xff09; 二、边…

STUSB4500 PPS(PD3.0)快充SINK模块——应用 解析

0 前言 朋友参加车展&#xff0c;收获一枚很漂亮的倍思65W氮化镓快充头&#xff0c;送给我了。 我看了手中只支持33W快充的三星陷入了沉思… 快充头支持PPS协议&#xff0c;我心思这玩意适合做可调电源啊&#xff01; 上网随便一查没查到&#xff0c;都是转换成5V、9V、12V等…