文章目录[隐藏]
主要参考资料:
《普中51单片机开发攻略--A6》、《LCD1602液晶完整中文资料》、《STM32中文参考手册_V10》、《STM32F103xCDE_数据手册-英文》、《STM32库开发实战指南——基于野火MINI开发板》、《CM3权威指南》
编程语言:C语言
目录
一、设计任务目标和要求
(说明:请写清楚所实现的任务)
简易计算器 |
|
功能 |
1、矩阵键盘输入,LCD1602显示数字、符号 |
2、实现 两个 正整数 的加减乘除,结果精确至小数点后两位 |
|
|
|
缺陷 |
1、不能进行连续的运算,即每次只能进行两个数的加减乘除 |
2、不能进行负数、小数以及其它更复杂的运算 |
|
简易密码锁 |
|
功能 |
1、每次按键有按键提示音; |
2、要求密码为6位,从矩阵键盘直接输入;密码输入正确后,提供声光指示;连续3次输入错误,提供声光报警; |
|
3、密码能够被修改 |
|
缺陷 |
1、更改密码、重置前没有确认一遍密码是否正确 |
2、其它 |
二、设计方案的论证与选择
(说明:1.说明为什么这样设计,画出原理框图;2.论证各模块的方案选择)
三、系统硬件电路设计
(说明:1.各模块电路设计;2.利用AD画出电路原理图)
四、系统软件设计
(说明:1.画出各模块程序设计流程图;2.画出主程序流程图;3.画出仿真电路并仿真)
简易计算器
简易密码锁
仿真失败(硬件成功实现)
五、软硬件联调
(说明:说说具体调试步骤、出现的问题及解决方法)
一、简易计算器调试步骤:(分模块调试)
1、让LCD1602在任意位置显示一个字符:验证LCD1602显示程序模块的正确性;
2、连接矩阵键盘模块,让LCD1602模块显示矩阵键盘所按下的键位值:验证矩阵键盘程序模块的正确性;
3、简易计算器功能验证:
取特殊情况、特殊值、正常情况进行功能验证
功能 |
输入(按键顺序) |
输出(结果) |
加 |
+1=、1+=、9999+9999= |
1.00、1.00、1998.00 |
减 |
-1=、1 -=、1-9999= |
-1.00、1.00、-9998.00 |
乘 |
*1=、1*=、9999*9999= |
0.00、0.00、99980000.00 |
除 |
/1=、1/=、1666/1000= |
0.00、0.00、1.67 |
清零 |
按清零键 |
无 |
二、简易密码锁调试步骤:(分模块调试)
前两步和简易计算器相同;
3、简易密码锁功能验证:
功能 |
输入(按键顺序) |
输出(结果) |
|
按任意按键 |
有提示音 |
|
12确定、12确定、1确定 |
有声光提示 |
|
初始密码:123456 |
有声光提示 |
|
按下更改密码按键,输入新密码:123321,回到初始界面,输入新密码验证 |
若密码正确,有声光提示 |
|
|
|
简易计算器、简易密码锁 |
|
问题 |
解决方法 |
1、驱动LCD1602的GPIO端口是不连续的 |
编写一个数据处理函数,将数据移至对应位置合并后返回,再写入GPIO口 |
2、LCD1602只能写入字符型数据,直接把字符型数据按位分配至不同的引脚会出现乱码 |
先把要显示的字符型变量转化成整型变量,再用数据处理函数处理 |
3、按一下按键,LCD会显示多个字符 |
参考别人的经验解决: https://blog.csdn.net/qq_41985361/article/details/89237467 |
4、若键盘扫描函数返回的是整形值,直接写入LCD会出现乱码 |
可以定义字符型数组存储要显示的字符,通过键盘扫描函数返回的数值确定要显示的字符,再将此字符转化为整型数据进行移位处理后(因为所使用的管脚不连续)传输给LCD1602显示 |
5、若x=0,则x *= 10 + Keyvalue;的结果恒为0 |
改为:x = x * 10 + Keyvalue; |
|
|
部分代码:
简易计算器主程序
/*******************************************************************************
* 项目名称 : 简易计算器
* 项目功能 : 1、矩阵键盘输入,LCD1602显示数字、符号
2、实现 两个 正整数 的加减乘除,结果精确至小数点后两位
* 修改内容 : 2021.6.27(完成功能1、2)
* 项目缺陷 : 1、不能进行连续的运算,即每次只能进行两个数的加减乘除
2、不能进行负数、小数以及其它更复杂的运算
3、其它
* 注意事项 : 无
*******************************************************************************/
#include "calculator_main.h"
void Result(float fin);
void Yunsuan(uint dat1);
void Select(void);
uchar num[] = {'0','1','2','3','4','5','6','7','8','9'};
uchar CHAR[] = {'=','/','*','-','+'};
int Num_1=0,Num_2=0;//用以存储运算符前后的两个数
static float Total = 0;//存储运算结果
uint FLAG = 0, FLAG_X = 0;//运算符、等于号标志位
int main(void)
{
//delay_init();
LCD_Init();
KEY_Init();
while(1)
{
Select();
}
}
/*******************************************************************************
* 函 数 名 : Result()
* 函数功能 : 在LCD1602第2行显示运算结果
* 输 入 : 运算结果
* 输 出 : 无
*******************************************************************************/
void Result(float fin)
{
uint k;
char buf[16];
LCDWriteCom(0xc0); //设置数据指针起点:第二行,第一个
LCDWriteData('=');
sprintf(buf,"%.2f",fin);//将浮点型结果 fin 转化为字符串 buf,然后向LCD1602打印
for(k=0;k<strlen(buf);k++)
LCDWriteData(buf[k]);
}
/*******************************************************************************
* 函 数 名 : Yunsuan()
* 函数功能 : 按下运算符键的处理
* 输 入 : 键值
* 输 出 : 无
*******************************************************************************/
void Yunsuan(uint dat1)
{
switch( CHAR[dat1-11] )//**********************************加减乘除
{
case '/':
LCDWriteData('/');
Select();
if(FLAG==0) break;//若清零
else if(FLAG>=1 && FLAG_X!=1) Select();//若为运算符键,则继续检测按键
else if(FLAG_X==1) Yunsuan(11);//'='
break;
case '*':
LCDWriteData('*');
Select();
if(FLAG==0) break;//若清零
else if(FLAG>=1 && FLAG_X!=1) Select();//若为运算符键,继续检测按键
else if(FLAG_X==1) Yunsuan(11);//'='
break;
case '-':
LCDWriteData('-');
Select();
if(FLAG==0) break;//若清零
else if(FLAG>=1 && FLAG_X!=1) Select();//若为运算符键,继续检测按键
else if(FLAG_X==1) Yunsuan(11);//'='
break;
case '+':
LCDWriteData('+');
Select();
if(FLAG==0) break;//若清零
else if(FLAG>=1 && FLAG_X!=1) Select();//若为运算符键,继续检测按键
else if(FLAG_X==1) Yunsuan(11);//'='
break;
case '=':
if( (Num_1>0||Num_1==0) && Num_2==0 )
{
Total = (float)Num_1;
if(FLAG==1||FLAG==2) Total = 0;
}
else if(Num_1==0 && Num_2!=0 && FLAG!=0 )
{
switch(FLAG)
{
default: Total = 0; break;
case 3: Total = (float)Num_1 - (float)Num_2; break;
case 4: Total = (float)Num_1 + (float)Num_2; break;
}
}
else if(Num_1!=0 && Num_2!=0 && FLAG!=0 )
{
switch(FLAG)
{
case 1: Total = (float)Num_1 / (float)Num_2; break;
case 2: Total = (float)Num_1 * (float)Num_2; break;
case 3: Total = (float)Num_1 - (float)Num_2; break;
case 4: Total = (float)Num_1 + (float)Num_2; break;
}
}
Result(Total);
break;
}
}
/*******************************************************************************
* 函 数 名 : Select()
* 函数功能 : 判断按键的类型
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Select(void)
{
uint flag;
int Keyvalue;
while( (Keyvalue=(int)KEY_Input())==16 );//读取键值
if(Keyvalue<10)//数字键
flag = 1;
else if(Keyvalue>10 && Keyvalue<16)//符号键
flag = 2;
else if(Keyvalue==10)//清零键
flag = 3;
switch( flag )
{
case 1: //数字
LCDWriteData(num[Keyvalue]);
if(FLAG==0) //运算符前的数字
{
Num_1 = Num_1 * 10 + Keyvalue;
Select();
}
if(FLAG>=1 && FLAG_X!=1) //运算符后的数字
{
Num_2 = Num_2 * 10 + Keyvalue;
Select();
}
break;
case 2: //字符
if(Keyvalue==11) FLAG_X = 1;//'='
else if(Keyvalue==12) FLAG = 1;//'/'
else if(Keyvalue==13) FLAG = 2;//'*'
else if(Keyvalue==14) FLAG = 3;//'-'
else if(Keyvalue==15) FLAG = 4;//'+'
Yunsuan(Keyvalue);
break;
case 3: //清零
LCDWriteCom(0x01);
Num_1 = Num_2 = FLAG = FLAG_X = Total = 0;
break;
}
}
简易计算器头文件
#ifndef __CALCULATOR_MAIN_H__
#define __CALCULATOR_MAIN_H__
#include "string.h"
#include "stdio.h"
#include "stm32f10x.h"
#include "keyboard.h"
#include "LCD1602.h"
//#include "common.h"
#include "delay_systick.h"
#endif
简易密码锁主程序
/*******************************************************************************
* 项目名称 : 简易密码锁
* 项目功能 : a) 每次按键有按键提示音;
b) 要求密码为6位,从矩阵键盘直接输入;
密码输入正确后,提供声光指示;
连续3次输入错误,提供声光报警;
c)密码能够被修改。
* 修改内容 : 2021.6.28(完成功能a、b);2021.6.29(完成功能c)
* 项目缺陷 : 1、更改密码、重置前没有确认一遍密码是否正确
2、其它
* 注意事项 : 无
简易密码锁键位
1 2 3 无
4 5 6 无
7 8 9 重输
重置 0 改 确认
*******************************************************************************/
#include "combination_lock_main.h"
void Tips(uint tips_mode,char BUF[16]);
void Key_mode(void);
void JUDGE(void);
void CHA_PAS(void);
const uint Sys_password[6]={1,2,3,4,5,6};//初始密码
uint Change_Password[6] = {0};//存储用户更改的密码
uint User_password[6]={0};//存储用户输入的密码
uint Password_Bits = 0; //密码位数标志
uint Password_fault = 0;//密码错误次数标志
uint Chang = 0; //密码是(1)否(0)更改标志
int main(void)
{
//delay_init();
LCD_Init();
KEY_Init();
Buzzer_Init();
Tips(1,0);//初始界面
while(1)
{
Key_mode();
}
}
/*******************************************************************************
* 函 数 名 : Tips()
* 函数功能 : 在LCD1602上输出提示符
* 输 入 : 提示符的模式
* 输 出 : 无
*******************************************************************************/
void Tips(uint tips_mode,char BUF[16])
{
int i;
const char disp5[] = " Welcome! ";//初始界面1
const char disp1[] = "Password:";//初始界面2
const char disp2[] = "Current:";//改密码界面1
const char disp3[] = "New:";//改密码界面2
const char disp4[] = "Password Error!";//输入错误密码后的提示
const char disp6[] = " Hello world ! ";//输入密码正确后的提示
if(tips_mode!=0)//模式1:显示系统提示字符
{
switch(tips_mode)
{
case 1: //初始界面
LCDWriteCom(0x01);//先清屏,后显示
LCD_Position(0,1);
for(i=0;i<strlen(disp5);i++)
LCDWriteData(disp5[i]);
LCD_Position(0,2);
for(i=0;i<strlen(disp1);i++)
LCDWriteData(disp1[i]);
break;
case 2://改密码时,第1行显示:Current:
LCDWriteCom(0x01);
LCD_Position(0,1);
for(i=0;i<strlen(disp2);i++)
LCDWriteData(disp2[i]);
break;
case 3://改密码时,第2行显示:New:
LCDWriteCom(0x01);
LCD_Position(0,2);
for(i=0;i<strlen(disp3);i++)
LCDWriteData(disp3[i]);
break;
case 4: //输入密码错误后显示
LCDWriteCom(0x01);//先清屏,后显示
LCD_Position(0,1);
for(i=0;i<strlen(disp4);i++)
LCDWriteData(disp4[i]);
break;
case 5: //输入密码正确后的提示
LCDWriteCom(0x01);//先清屏,后显示
LCD_Position(0,1);
for(i=0;i<strlen(disp6);i++)
LCDWriteData(disp6[i]);
break;
}
}
else//模式2:最大显示长度为 16 的任意字符串
{
for(i=0;i<strlen(BUF);i++)
LCDWriteData(BUF[i]);
}
}
/*******************************************************************************
* 函 数 名 : Key_mode()
* 函数功能 : 判断按键的返回值,进入不同的 5种 模式
模式1:0~9 输入密码按键; 模式2:10 重置键; 模式3:11 改密码键;
模式4:12 重输键; 模式5:13~14 无功能键; 模式6:15 密码输入完成确认键
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Key_mode(void)
{
uint temp1,i;
while( (temp1=KEY_Input())==16 );//等待按键按下,若按下,则执行下面的语句
Buzzer(1,0);//蜂鸣器响,指示灯关
switch(temp1)
{
case 15://模式5:模式输入密码完成的确认按键
JUDGE();
LCDWriteCom(0x01);
Tips(1,0);//初始界面
break;
case 12: case 13: //无功能按键
Key_mode();
break;
case 14: //模式4:重输按键
LCDWriteCom(0x01);
Tips(1,0);//初始界面
Password_Bits = 0;
Key_mode();
break;
case 11://模式3:改密码键
if(Password_fault==0)
CHA_PAS();
break;
case 10://模式2:重置键
if(Password_fault==0)//输入正确密码后或初始密码状态下才能重置
{
LCDWriteCom(0x01);//清屏
Tips(1,0);//初始界面
Password_Bits = Password_fault = Chang = 0;//各种全局变量(标志位)清零
for(i=0;i<6;i++)
{
Change_Password[i] = User_password[i] = 0;
}
}
break;
default://模式1:0~9
if( Password_Bits<6 )
{
LCDWriteData('*');//输入密码时显示 *
User_password[Password_Bits] = temp1;//0、1、2、3、4、5
Password_Bits++;
Key_mode();
}
else
{
while( KEY_Input()!=15 ) Buzzer(1,0);//等待按下确认键,按其它按键有提示音,但仍然处于死循环
Password_Bits = 0;
Key_mode();
}
break;
}
}
/*******************************************************************************
* 函 数 名 : JUDGE()
* 函数功能 : 判断输入密码的正确与否
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void JUDGE(void)
{
uint i,k,judge=0;
Password_Bits = 0;
if(Chang==0)//若没更改过初始密码,则进行以下判断
{
for(i=0;i<6;i++)
{
if(User_password[i]!=Sys_password[i]) //若密码错误
{
judge = 1;
Password_fault++;
Tips(4,0);//LCD提示错误
if(Password_fault%3==0)//每输入 3次 错误密码,则产生声光报警
{
for(k=0;k<6;k++)//LED、蜂鸣器提示
{
Buzzer(1,0);//灯灭,蜂鸣器响
delay_ms(250);
Buzzer(0,1);//灯亮,蜂鸣器静音
delay_ms(250);
}
}
else
{
delay_ms(1000);
}
break;
}
}
if(judge==0)//若密码正确
{
Password_fault=0;//重置密码错误次数
LCDWriteCom(0x01);
Tips(5,0);
for(i=0;i<5;i++)
{
Buzzer(1,0);//灯灭,蜂鸣器响
delay_ms(100);
Buzzer(0,1);//灯亮,蜂鸣器静音
delay_ms(100);
}
}
}
else//若更改了初始密码
{
for(i=0;i<6;i++)
{
if(User_password[i]!=Change_Password[i]) //若密码错误
{
judge = 1;
Password_fault++;
Tips(4,0);//LCD提示错误
if(Password_fault%3==0)//每输入 3次 错误密码,则产生声光报警
{
for(k=0;k<6;k++)//LED、蜂鸣器提示
{
Buzzer(1,0);//灯灭,蜂鸣器响
delay_ms(250);
Buzzer(0,1);//灯亮,蜂鸣器静音
delay_ms(250);
}
}
else
{
delay_ms(1000);
}
break;
}
}
if(judge==0)//若密码正确
{
Password_fault=0;//重置密码错误次数
LCDWriteCom(0x01);
Tips(5,0);
for(i=0;i<5;i++)
{
Buzzer(1,0);//灯灭,蜂鸣器响
delay_ms(100);
Buzzer(0,1);//灯亮,蜂鸣器静音
delay_ms(100);
}
}
}
}
/*******************************************************************************
* 函 数 名 : CHA_PAS()
* 函数功能 : 更改密码
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void CHA_PAS(void)
{
uint key_temp_1;
Password_Bits = 0;
if(Password_fault==0)
{
Tips(3,0);
while( Password_Bits<6 )
{
while( (key_temp_1=KEY_Input())==16 );
Buzzer(1,0);
if(key_temp_1<10)
{
LCDWriteData('*');
Change_Password[Password_Bits] = key_temp_1;
Password_Bits++;
}
if(Password_Bits>=6) //当输入完密码后,要按确认键才退出修改密码的界面
while(KEY_Input()!=15) Buzzer(1,0);
}
Chang = 1;
Password_Bits=0;
LCDWriteCom(0x01);
Tips(1,0);
}
else
{
LCDWriteCom(0x01);
Tips(1,0);//初始界面
}
}
简易密码锁主程序头文件
#ifndef __COMBINATION_LOCK_MAIN_H__
#define __COMBINATION_LOCK_MAIN_H__
#include "string.h"
#include "stdio.h"
#include "stm32f10x.h"
#include "keyboard.h"
#include "LCD1602.h"
//#include "common.h"
#include "delay_systick.h"
#include "Buzzer.h"
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
#endif
实物视频:
简易密码锁:https://www.bilibili.com/video/BV1P44y1q7Dh/
简易计算器:https://www.bilibili.com/video/BV1o64y197QP/
所有资料:
以下程序所使用的GPIO口是不连续的,使用者应注意。
http://www.51hei.com/bbs/dpj-209546-1.html
写在最后:
如有错误,请各位前辈指出,不胜感激
版权声明:本文为CSDN博主「布鲁强」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_46757367/article/details/118395307
暂无评论