一、用draw.io画的主要流程图如下:(主要是这个软件它开源且免费,但是个人刚用起来不太习惯;在编程过程中发现此流程图还需要优化,但是也暂时懒得改了😄)
还能优化的地方:
1、帧率不高,刷新慢;
2、密码错误设置只是响几下,oled无错误提示;输错密码无法对当前密码位清零并重新输入;
3、oled显示屏利用效率不高,数字、字母贪方便都用了show_chinese函数,占用了空间;
4、进入管理员密码设置后并没有显示当前密码;
5、掉电后重新设置的密码丢失;
6、静音状态指示灯存在逻辑问题。
效果:
①上电初始化后进入主界面
②密码界面
1、密码正确
2、密码错误
3、按下s4返回主界面
③管理员界面
1、验证失败
2、验证成功进入
3、密码锁密码设置、验证
4、管理员密码设置、验证
④静音设置(这里我给的资源包有逻辑bug的,进入验证界面,验证完成后指示灯led1直接熄灭,其实可以用状态值就可以解决了,下面代码的work.c里的if(buzzer_state==1){LED1(on);}是已经修复了一点点)
1、主界面按下s4,进入静音后led1灯亮起,表示按下按键无声音
2、静音状态下,在主界面下再次按下s4,led1闪烁几次后恢复有声音
二、相关模块主要代码
1、main.c
#include "work.h"
int main(void)
{
system_init();
while(1)
{
work();
}
}
2、work.c的oled显示函数部分
/*显示主界面*/
void Show_MainInterface(void)
{
unsigned char x,y;
for(y=10;y<17;y++)
{
x=(y-10)*16;
OLED_ShowCHinese(x,0,y);
}
for(y=17;y<25;y++)
{
x=(y-17)*16;
OLED_ShowCHinese(x,2,y);
}
OLED_ShowCHinese(0,4,25);
for(y=26;y<34;y++)
{
x=(y-26)*16;
OLED_ShowCHinese(x,6,y);
}
}
/*显示请求输入密码*/
void Show_PasswordInterface(void)
{
unsigned char x,y;
for(y=35;y<40;y++)
{
x=(y-34)*16;
OLED_ShowCHinese(x+16,0,y);
}
}
/*显示验证成功*/
void Show_Success(void)
{
unsigned char x,y;
for(y=40;y<45;y++)
{
x=(y-40)*16;
OLED_ShowCHinese(x+32,3,y);
}
}
/*显示密码界面说明*/
void Show_PassKeyList(unsigned char h,unsigned char l)
{
unsigned char x,y;
switch(h)
{
case 0:
for(y=45;y<53;y++)
{
x=(y-45)*16;
OLED_ShowCHinese(x,l,y);
}
break;
case 1:
for(y=53;y<59;y++)
{
x=(y-53)*16;
OLED_ShowCHinese(x,l,y);
}
break;
case 2:
for(y=59;y<65;y++)
{
x=(y-59)*16;
OLED_ShowCHinese(x,l,y);
}
break;
case 3:
for(y=65;y<72;y++)
{
x=(y-65)*16;
OLED_ShowCHinese(x,l,y);
}
break;
case 4:
for(y=72;y<80;y++)
{
x=(y-72)*16;
OLED_ShowCHinese(x,l,y);
}
for(y=80;y<88;y++)
{
x=(y-80)*16;
OLED_ShowCHinese(x,l+2,y);
}
OLED_ShowCHinese(0,l+4,y);
break;
}
}
/*管理员设置显示*/
void Show_User(unsigned char c)
{
unsigned char x,y;
switch(c)
{
case 0:
for(y=89;y<97;y++)
{
x=(y-89)*16;
OLED_ShowCHinese(x,0,y);
}
break;
case 1:
for(y=97;y<105;y++)
{
x=(y-97)*16;
OLED_ShowCHinese(x,0,y);
}OLED_ShowCHinese(x+16,2,105);OLED_ShowCHinese(x+32,2,106);
break;
case 2:
for(y=107;y<115;y++)
{
x=(y-107)*16;
OLED_ShowCHinese(x,4,y);
}OLED_ShowCHinese(x+16,6,115);OLED_ShowCHinese(x+32,6,116);
break;
case 3:
for(y=117;y<125;y++)
{
x=(y-117)*16;
OLED_ShowCHinese(x,6,y);
}
break;
}
}
/*显示设置成功*/
void Show_SetY()
{
unsigned char x,y;
for(y=125;y<130;y++)
{
x=(y-125)*16;
OLED_ShowCHinese(x+32,3,y);
}
}
3、work.c的主体部分
#include "work.h"
void system_init(void)
{
SysTick_Init(168);
OLED_Init(); // oled模块引脚初始化
OLED_Clear(); // oled模块清屏(清全屏)
led_init();
key_init();
buzzer_Init();
}
unsigned char key;
unsigned char main_param,face_param,pointer;
unsigned char password[]={1,2,3,4},input[4],user[]={1,1,1,1};
/*密码输入显示*/
void Show_Input(unsigned char location)
{
OLED_ShowCHinese(location*16+32,3,input[location]);
}
/*管理员密码验证*/
void user_verify()
{
unsigned char x,y,state;
pointer=0;
while(state==0)
{
Show_Input(pointer);
while(key==0)
{x=check_key();if(x!=0){key=x;}}
if(key==1)
{input[pointer]++;if(input[pointer]==10){input[pointer]=0;}key=0;}//密码加
if(key==2)
{input[pointer]--;if(input[pointer]==255){input[pointer]=9;}key=0;}//密码减
if(key==3)
{
key=0;
pointer++;
}
if(key==4)
{OLED_Clear();face_param=0;main_param=0;pointer=0;key=0;state=1;}
/*密码验证部分*/
if(pointer==4)//输入密码4个完成以后识别
{
pointer=0;y=0;
for(x=0;x<4;x++)//识别
{
if(input[x]==user[x])
{y++;}//识别完成暂存值清零
}
for(x=0;x<4;x++)
{input[x]=0;}//识别完成暂存值清零
if(y!=4){state=0;OLED_Clear();Show_PasswordInterface();red_warn();buzzer_wrong();}//密码验证失败,重新显示输入请求
if(state==0){if(y==4){OLED_Clear();Show_Success();buzzer_right();run_horse();state=1;}}//密码验证成功
}
}
}
/*密码设置*/
void set_code(unsigned char *mima)
{
unsigned char x,state;
pointer=0;
Show_PasswordInterface();//显示输入请求
while(state==0)
{
Show_Input(pointer);
while(key==0)
{x=check_key();if(x!=0){key=x;}}
if(key==1)
{input[pointer]++;if(input[pointer]==10){input[pointer]=0;}key=0;}//密码加
if(key==2)
{input[pointer]--;if(input[pointer]==255){input[pointer]=9;}key=0;}//密码减
if(key==3)
{
key=0;
pointer++;
}
if(key==4)
{OLED_Clear();face_param=0;main_param=0;pointer=0;key=0;state=1;}
/*密码输入部分*/
if(pointer==4)//输入密码4个
{
pointer=0;
for(x=0;x<4;x++)//录入
{mima[x]=input[x];}
for(x=0;x<4;x++)
{input[x]=0;}//识别完成暂存值清零
if(x==4){OLED_Clear();Show_SetY();run_horse();buzzer_right();state=1;}//密码设置成功
}
}
}
/*工作函数*/
void work(void)
{
unsigned char x,y;
/*主界面*/
if(main_param==0)//让主界面只刷新一次即可,不然一直刷新需要时间影响按键识别
{
Show_MainInterface();
while(main_param==0)
{
switch(check_key())
{
case 1:face_param=1;main_param=1;OLED_Clear();break;
case 2:face_param=2;main_param=1;OLED_Clear();break;
case 3:face_param=3;main_param=1;OLED_Clear();break;
/*第四部分:静音*/
case 4:
buzzer_state++;
if(buzzer_state==1){LED1(on);}
if(buzzer_state==2){buzzer_state=0;led_tips();}
break;
}
}
}
/*第一部分:密码界面*/
if(face_param==1 && main_param==1)
{
Show_PasswordInterface();//显示输入请求
while(face_param==1 && main_param==1)
{
Show_Input(pointer);
while(key==0)
{x=check_key();if(x!=0){key=x;}}
if(key==1)
{input[pointer]++;if(input[pointer]==10){input[pointer]=0;}key=0;}//密码加
if(key==2)
{input[pointer]--;if(input[pointer]==255){input[pointer]=9;}key=0;}//密码减
if(key==3)
{
key=0;
pointer++;
}
if(key==4)
{
OLED_Clear();face_param=0;main_param=0;pointer=0;key=0;
for(x=0;x<4;x++)
{input[x]=0;}
}
/*密码验证部分*/
if(pointer==4)//输入密码4个完成以后识别
{
pointer=0;y=0;
for(x=0;x<4;x++)//识别
{
if(input[x]==password[x])
{y++;}//识别完成暂存值清零
input[x]=0;
}
if(y!=4){red_warn();buzzer_wrong();}//密码验证失败
if(y==4){OLED_Clear();Show_Success();run_horse();buzzer_right();}//密码验证成功
OLED_Clear();Show_PasswordInterface();//重新显示输入请求
}
}
}
/*第二部分:密码界面说明*/
if(face_param==2 && main_param==1)
{
for(x=0;x<4;x++)
{Show_PassKeyList(x,2*x);}
while(face_param==2 && main_param==1)
{
while(key==0)
{x=check_key();if(x!=0){key=x;}}
if(key==1){key=0;}
if(key==2)
{
OLED_Clear();
for(x=0;x<4;x++)
{Show_PassKeyList(x,2*x);}
key=0;
}
if(key==3)
{OLED_Clear();Show_PassKeyList(4,0);key=0;}
if(key==4)
{OLED_Clear();face_param=0;main_param=0;key=0;}
}
}
/*第三部分:管理员设置*/
if(face_param==3 && main_param==1)
{
Show_User(0);user_verify();
while(face_param==3 && main_param==1)
{
x=0;
OLED_Clear();
Show_User(1);Show_User(2);
while(face_param==3 && main_param==1 && x==0)//设置x是为了密码设置完成后重新会到管理员设置界面
{
while(key==0)
{x=check_key();if(x!=0){key=x;}}
if(key==1)
{key=0;OLED_Clear();set_code(user);x=1;}//管理员密码
if(key==2)
{key=0;OLED_Clear();set_code(password);x=1;}//密码锁密码
if(key==3)
{key=0;}
if(key==4)
{OLED_Clear();face_param=0;main_param=0;pointer=0;key=0;}
}
}
}
}
4、key.c和key.h
#include "key.h"
#include "stm32f4xx.h" // Device header
#include "oled.h"
#include "delay.h"
#include "buzzer.h"
void key_init(void)
{
// GPIO信息配置结构体
GPIO_InitTypeDef GPIO_InitStructure;
// 1、GPIO硬件使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
// 2、GPIO信息配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 引脚:第1根引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; // 模式:输入模式
GPIO_Init(GPIOA, &GPIO_InitStructure); // 通过此函数,将配置的信息写入到相应的寄存器中
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; // 引脚:第1根引脚
GPIO_Init(GPIOE, &GPIO_InitStructure); // 通过此函数,将配置的信息写入到相应的寄存器中
}
unsigned char check_key(void)
{
if(key0==0)
{delay_ms(20);buzzer_di();while(key0==0);return 1;}
if(key1==0)
{delay_ms(20);buzzer_di();while(key1==0);return 2;}
if(key2==0)
{delay_ms(20);buzzer_di();while(key2==0);return 3;}
if(key3==0)
{delay_ms(20);buzzer_di();while(key3==0);return 4;}
return 0;
}
unsigned char keynum;
void show_keynum(void)
{
unsigned char s;
s=check_key();
if(s!=0)
{keynum=s;}
OLED_ShowCHinese(0,0,keynum);
}
#ifndef __key_h__
#define __key_h__
extern void key_init(void);
#define key0 GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)
#define key1 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_2)
#define key2 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3)
#define key3 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)
unsigned char check_key(void);
void show_keynum(void);
#endif
5、led.c和led.h
#include "led.h"
#include "stm32f4xx.h" // Device header
void led_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOF, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_SetBits(GPIOF,GPIO_Pin_9);
GPIO_SetBits(GPIOF,GPIO_Pin_10);
GPIO_SetBits(GPIOE,GPIO_Pin_13);
GPIO_SetBits(GPIOE,GPIO_Pin_14);
}
void run_horse(void)
{
LED1(on);
delay_ms(30);
LED2(on);
delay_ms(30);
LED3(on);
delay_ms(30);
LED4(on);
delay_ms(30);
LED4(off);
delay_ms(30);
LED3(off);
delay_ms(30);
LED2(off);
delay_ms(30);
LED1(off);
}
void red_warn(void)
{
unsigned char x;
for(x=0;x<3;x++)
{
LED1(on);LED2(on);LED3(on);LED4(on);
delay_ms(20);
LED1(off);LED2(off);LED3(off);LED4(off);
delay_ms(20);
}
}
void led_tips(void)
{
unsigned char x;
for(x=0;x<2;x++)
{
LED1(on);
delay_ms(50);
LED1(off);
delay_ms(50);
}
LED1(on);
delay_ms(50);
LED1(off);
}
#ifndef __led_h__
#define __led_h__
#include "delay.h"
void led_init(void);
#define on 1
#define off 0
#define LED1(X) X?GPIO_ResetBits(GPIOF, GPIO_Pin_9):GPIO_SetBits(GPIOF, GPIO_Pin_9) // X为真执行第一个语句,否则执行第二个语句
#define LED2(X) X?GPIO_ResetBits(GPIOF, GPIO_Pin_10):GPIO_SetBits(GPIOF, GPIO_Pin_10)
#define LED3(X) X?GPIO_ResetBits(GPIOE, GPIO_Pin_13):GPIO_SetBits(GPIOE, GPIO_Pin_13)
#define LED4(X) X?GPIO_ResetBits(GPIOE, GPIO_Pin_14):GPIO_SetBits(GPIOE, GPIO_Pin_14)
void run_horse(void);
void red_warn(void);
void led_tips(void);
#endif
6、buzzer.c
#include "buzzer.h"
#include "stm32f4xx.h" // Device header
#include "delay.h"
void buzzer_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //引脚:Px?第9根引脚,对应LED0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //引脚模式:输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //输出模式:推挽
GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed; //输出速度:高速
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //上下拉:不拉
GPIO_Init(GPIOF, &GPIO_InitStructure); //通过此函数,将配置信息写入相应的PF引脚寄存器中【proteus仿真:F改D】
}
void buzzer_voice(unsigned int voice)
{
buzzer(1);delay_ms(voice);buzzer(0);
}
unsigned char buzzer_state;
/*蜂鸣器响一下*/
void buzzer_di(void)
{
if(buzzer_state==0)
{
buzzer_voice(15);
}
}
/*蜂鸣器响一小段时间*/
void buzzer_right(void)
{
if(buzzer_state==0)
{
buzzer_voice(120);
}
}
/*蜂鸣器快速响几下*/
void buzzer_wrong(void)
{
unsigned char i;
if(buzzer_state==0)
{
for(i=0;i<5;i++)
{buzzer_voice(15);delay_ms(15);}
}
}
三、途中的问题
测试以下代码(密码输入界面)时显示不正常,不按多几下压根没反应,要按n多下才正确显示:
猜测:按键函数里有延时的东西,并且按键识别跟用老师给的oled显示函数在同一级别下,影响按键识别判断。
当时的按键代码:
可以看到是按键函数里的蜂鸣器里有延时函数。删了蜂鸣器测试之后发现还是老问题,基本可以忽略蜂鸣器延时对按键识别影响。给这显示函数上个状态值才好一点点(但是不灵敏,只识别++或者其它其中一个【其它全部屏蔽】这样才非常灵敏),开始我感觉是显示函数里面的问题。
但是后面测试发现是测试函数中多个if(check_key)按键识别判断的问题,换成以下:
主要是改了判别方式,直接是用了暂存值,保留识别的值,再对识别的值进行处理。if里面的判断还是尽量快一点的好。看来不是oled显示的问题,是我c语言不好的问题(悲)。这里就可以了,挺灵敏了,不会要按几下才有反应。