课程设计:基于STM32F103RCT6的简易计算器、简易密码锁

主要参考资料:

《普中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、简易密码锁功能验证

功能

输入(按键顺序)

输出(结果)

  1. 有无按键提示音

按任意按键

有提示音

  1. 输入3次错误密码

12确定、12确定、1确定

有声光提示

  1. 输入正确密码

初始密码:123456

有声光提示

  1. 更改密码

按下更改密码按键,输入新密码: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

生成海报
点赞 0

布鲁强

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

基于8051单片机实现电子时钟+数字秒表设计

概述 电子时钟是一种利用数字电路来显示秒、分、时的计时装置,与传统的机械钟相比,它具有走时准确、显 示直观、无机械传动装置等优点,因而得到广泛应用。随着人们生活环境的不断改善和美化,在许

51小车(一)

1.准备: 借鉴: 详细介绍如何从零开始制作51单片机控制的智能小车(一)———让小车动起来_慕羽★的博客-CSDN博客_新手怎么制作单片机智能小车 2.已有模块 (1)  已有电机驱动 (2)

三相电机相电感,相电阻和极对数的测量

相电阻测量: 方法一:可使用万用表电阻档直接测量其中两端 方法二:可使用LCR数字电桥仪器测量 万用表操作: 将万用表旋转到电阻档的适当量程内 将万用表上的两表笔接触在电机的其中任意