基于STM32的简易计算器proteus仿真设计(仿真+程序+设计报告+讲解视频)
基于STM32的简易计算器proteus仿真设计
- 讲解视频
- 1.主要功能
- 2. 仿真
- 3. 程序
- 4. 设计报告
- 5. 资料清单&下载链接
基于STM32的简易计算器proteus仿真设计(仿真+程序+设计报告+讲解视频)
仿真图proteus 8.9
程序编译器:keil 5
编程语言:C语言
设计编号:C0089
讲解视频
基于STM32的简易计算器proteus仿真+程序+设计报告+讲解视频
1.主要功能
功能说明:
本次嵌入式课程设计综合实验的内容为基于STM32单片机的简易计算器仿真设计系统。完成LCD1602液晶显示、矩阵按键扫描、LCD1602显示等多项任务。
一、该简易计算器设计硬件电路采用三部分电路模块构成:
1、键盘模块电路,采用 4*4 矩阵式键盘作为输入电路;
2、LCD1602 液晶显示模块;
3、以 STM32单片机作为控制核心。
二、软件程序主要由三部分组成: 主程序、按键扫描程序和 LCD1602 显示程序。
三、 性能指标
(1) 用STM32单片机设计一个简易计算器, 并用 1602 液晶显示相应的数据。
(2) 可以进行简单的整数加减乘除运算,具有清零功能。
(3) 最大可以 9999*9999。
(4) 可以通过 proteus 仿真。
主要硬件设备:STM32F103R6单片机 矩阵按键 LCD1602
2. 仿真
打开仿真工程,双击proteus中的单片机,选择hex文件路径,然后开始仿真。
然后开始仿真。
加法验证:


减法验证:


除法验证:


乘法验证:


3. 程序
程序是用keil5 mdk版本打开的,如果打开有问题,核实下keil的版本。程序是标准库版本编写的,有注释可以结合讲解视频理解。

#include "stm32f10x.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define uchar unsigned char // 以后unsigned char就可以用uchar代替
#define uint unsigned int // 以后unsigned int 就可以用uint 代替
//=============================================================
//--- 1604 LCD驱动程序段 ---
#define LCD_RS(x) (x)?(GPIOC->ODR |= (1 << 8)):(GPIOC->ODR &=~(1 << 8))
#define LCD_EN(x) (x)?(GPIOC->ODR |= (1 << 9)):(GPIOC->ODR &=~(1 << 9))
#define LCD_PORT(x) GPIOC->ODR = (GPIOC->ODR & 0xFF00) | x
#define COM 0
#define DAT 1
/*********************************************************/
// 毫秒级的延时函数,time是要延时的毫秒数
/*********************************************************/
void DelayMs(uint time)
{uint i,j; // 定义两个无符号整型变量i和j用于循环计数for(i=0;i<time;i++) // 外层循环,根据time值进行迭代for(j=0;j<200;j++); // 内层循环,每次迭代执行200次空操作,共同实现大约time毫秒的延迟
}// 向LCD写入数据或命令的函数,rs为0时写命令,rs为1时写数据,dat是要写入的具体数据
void LCD_Write(char rs,char dat)
{
// for(int i=0;i<600;i++); // 这里原有一个延时循环被注释掉了,现在使用下面的DelayMs函数替代DelayMs(1); // 引入一个1毫秒的延迟,可能是为了确保LCD有足够的时间处理上一条指令if(0 == rs) LCD_RS(0); // 如果rs为0,则设置LCD的数据/命令选择线为命令模式else LCD_RS(1); // 如果rs为1,则设置为数据模式LCD_EN(1); // 使能LCD写入信号LCD_PORT(dat); // 通过LCD_PORT函数向LCD发送dat数据LCD_EN(0); // 写入完成后,关闭LCD使能信号
}// 在指定位置写入单个字符到LCD,x决定列位置(0-3),y决定行位置。Data是要写入的字符
void LCD_Write_Char(char x,char y,char Data)
{if(0 == x) LCD_Write(COM,0x80 + y); // 设置LCD地址指针到第一列else if(1 == x) LCD_Write(COM,0xC0 + y); // 第二列else if(2 == x) LCD_Write(COM,0x90 + y); // 第三列(假设存在)else LCD_Write(COM,0xD0 + y); // 第四列(假设存在)LCD_Write(DAT,Data); // 在设定的位置写入字符数据
}// 在指定位置写入字符串到LCD,x决定列,y决定行,*s是字符串的指针
void LCD_Write_String(char x,char y,char *s)
{if(0 == x) LCD_Write(COM,0x80 + y); // 设置LCD地址指针到第一列else if(1 == x) LCD_Write(COM,0xC0 + y); // 第二列else if(2 == x) LCD_Write(COM,0x90 + y); // 第三列(假设存在)else LCD_Write(COM,0xD0 + y); // 第四列(假设存在)while(*s) LCD_Write(DAT,*s++); // 循环写入字符串中的每个字符直到结束
}// 清除LCD显示屏内容的函数
void LCD_Clear(void)
{LCD_Write(COM,0x01); // 发送特定命令(0x01)到LCD以清屏
// for(int i=0;i<60000;i++); // 原有的延时循环被注释,使用下面的DelayMs代替DelayMs(2); // 等待2毫秒让LCD完成清屏操作
}void LCD_Init(void)
{LCD_Write(COM,0x38); //--- 显示模式设置 ---LCD_Write(COM,0x08); //--- 显示关闭 ---LCD_Write(COM,0x06); //--- 显示光标移动设置 ---LCD_Write(COM,0x0C); //--- 显示开及光标设置 ---LCD_Clear();
}void MyGPIO_Init(void)
{GPIO_InitTypeDef MyGPIO;//定义GPIO结构体初始化变量RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//打开GPIOC外设时钟MyGPIO.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 |GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8 | GPIO_Pin_9;MyGPIO.GPIO_Speed = GPIO_Speed_50MHz;//设置响应速度MyGPIO.GPIO_Mode = GPIO_Mode_Out_PP;//设置PC0~PC9为通用推挽输出GPIO_Init(GPIOC,&MyGPIO);//调用GPIO初始化函数完成PC0~PC9引脚配置
}unsigned char KEYTAB[] =
{0xD7, //00xEE,0xDE,0xBE, //1,2,30xED,0xDD,0xBD, //4,5,60xEB,0xDB,0xBB, //7,8,90x7E,0x7D,0x7B,0x77, //A,b,C,d0xE7,0xB7, //E,F
};
#define ROWOUT_COLIN() {\GPIOB->CRL = 0x44443333;\GPIOB->ODR = 0xF0;\}
#define ROWIN_COLOUT() {\GPIOB->CRL = 0x33334444;\GPIOB->ODR = 0x0F;\}
#define KEY_PORT() GPIOB->IDR
#define KEY_ROW (KEY_PORT() & 0xF0)
#define KEY_COL (KEY_PORT() & 0x0F)
int KeydlyCnt ;
int KeyBoard4X4_Scan(void)
{int i,Key = 0xFF;//读取列线引脚电平状态是否有键按下if((0xF0 != KEY_ROW) && (999999 != KeydlyCnt ) && (++KeydlyCnt > 100)){if(0xF0 != KEY_ROW)//判断是否真得有键按下{KeydlyCnt = 999999;Key = KEY_ROW;//获取列线的状态数值 KEY:1110 0000 0XE0ROWIN_COLOUT();//配置PB0~7为行线输入列线输出 KEY:0000 1110 0X0EKey |= KEY_COL; //获取行线状态并与列线状态数值合并 Key 1110 1110 0xeefor(i=0;i<sizeof(KEYTAB);i++)//查KEYTAB表是否存储该按键编码{if(KEYTAB[i] == Key)break;}//将编码值转换为数字代码存储到Key变量中if(i >= sizeof(KEYTAB))i = 0xFF;else Key = i;//key =1 if(Key < sizeof(KEYTAB)){if(Key < 10)Key += '0';else if(10 == Key)Key = '+';else if(11 == Key)Key = '-';else if(12 == Key)Key = '*';else if(13 == Key)Key = '/';else if(14 == Key)Key = 'C';else if(15 == Key)Key = '=';}ROWOUT_COLIN();//配置PB0~7为行线输出列线输入引脚}}else if(0xF0 == KEY_ROW){if(999999 == KeydlyCnt )KeydlyCnt = 0;}return Key;// key =1
}//--- LCD模块上显示数字函数 ---
char LCD_DisplayNum(int x, int y, int val)
{int i; // 循环计数器int m, nflag; // m为辅助计数器,nflag标记数值是否为负char buff[10 + 1]; // 用于存储数字字符的缓冲区,最大支持10位数字加上结束符'\0'nflag = 0; // 初始化标志位,表示数值非负if (val < 0) nflag = 1; // 如果输入的数值为负,则设置nflag为1val = abs(val); // 将输入的数值转换为其绝对值以便处理// 初始化缓冲区,填充空格字符并设置结束符for (i = 0; i < sizeof(buff); i++) buff[i] = ' ';buff[sizeof(buff) - 1] = '\0';i = sizeof(buff) - 2; // 设置缓冲区的起始写入位置(从后向前)// 将数值转换为字符形式存入缓冲区while (val){buff[i--] = val % 10 + '0'; // 取当前数值的最后一位并转换为字符val /= 10; // 数值除以10进入下一位if (0 == i) break; // 如果已到达缓冲区的最前端,则停止}// 如果原始数值是负数,在适当位置插入负号if (nflag) buff[i--] = '-';// 移动缓冲区中的字符,去除前导空格for (m = 0; m <= i; m++){for (nflag = 1; nflag < sizeof(buff); nflag++) buff[nflag - 1] = buff[nflag]; // 缓冲区内的字符向前移动}// 将处理后的数字字符串显示在LCD上指定位置LCD_Write_String(x, y, buff);// 返回实际显示的数字字符长度(不包括前导空格)return strlen(buff);
}int ch,act,i,m;
long num1,num2,result;
char str1[12],str2[2];int main(void)
{MyGPIO_Init(); // 初始化自定义的GPIO设置LCD_Init(); // 初始化LCD显示屏RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 打开GPIOB端口的外设时钟ROWOUT_COLIN(); // 配置GPIOB的0到7引脚为行输出和列输入,用于键盘扫描LCD_Write_Char(1, 1, '0'); // 在LCD第一行显示该数字键while(1) // 无限循环,等待并处理按键事件{ch = KeyBoard4X4_Scan(); // 扫描4x4键盘并获取按键值if(0xFF != ch) // 如果获取到了有效的按键(0xFF表示未按下键){if((ch == '+') || (ch == '-') || (ch == '*') || (ch == '/')) // 检查是否为运算符{LCD_Write_Char(0, i++, ch); // 在LCD的第一行显示获取到的运算符act = ch; // 记录当前运算符num1 = atof(str1); // 将之前输入的数字字符串str1转换为浮点数并赋值给num1memset(str1, 0, 12); // 清空str1准备接收新的输入result += num1; // 累加结果}else if(ch == 'C') // 如果是清除键{i = act = 0; // 重置索引和当前运算符num1 = num2 = result = 0; // 重置所有数值memset(str1, 0, 12); // 清空输入字符串LCD_Write(COM, 0x01); // 发送清屏指令给LCD}else if(ch == '=') // 如果是等于号{LCD_Write_Char(1, 0, ch); // 在LCD第二行显示等于号num2 = atof(str1); // 将输入的数字字符串转换为浮点数num2// 根据之前记录的运算符执行相应的计算switch(act){case '+': result += num2; break;case '-': result -= num2; break;case '*': result *= num2; break;case '/': result /= num2; break;}memset(str1, 0, 12); // 清空输入字符串memset(str2, 0, 2); // 清空辅助字符串// 处理并显示计算结果i = 1;if(result < 1) LCD_Write_Char(1, i++, '0'); // 如果结果小于1,先显示0else{m = (int)result; // 取结果的整数部分i += LCD_DisplayNum(1, i, m); // 显示整数部分,并更新显示位置result -= m; // 从结果中减去已显示的整数部分,保留小数部分(如果有的话)}}else // 对于其他数字键{LCD_Write_Char(0, i++, ch); // 在LCD第一行显示该数字键sprintf(str2, "%c", ch); // 将按键字符格式化到str2strcat(str1, str2); // 将str2附加到str1,累积输入的数字字符串}}}
}
4. 设计报告
8586字设计报告,内容包括硬件设计、软件设计、调试、结论等。

本方案以高性能STM32系列单片机为核心,巧妙融合了硬件电路的精妙设计与高效软件编程技术,旨在打造一款既实用又便捷的计算工具。计算器不仅实现了基础的数学运算功能——包括加法、减法、乘法和除法运算,还贴心地加入了清零功能,使用户在连续计算或需要重新开始时能够快速重置,大大提升了使用的便捷性和效率。
设计的一大亮点在于其输入系统采用了4x4矩阵式键盘,这种设计在有限的空间内实现了丰富的功能输入选项,用户只需轻触按键,即可快速录入所需计算的数据或运算符号,极大地优化了人机交互体验。而运算结果显示则依托于经典的LCD1602液晶显示屏,清晰直观地展示每一步的运算过程及最终结果,使得计算过程一目了然,即便是复杂的运算序列也能轻松跟踪。
软件层面,设计团队精心编写了三大核心程序模块:主程序、按键扫描程序和LCD1602显示程序。主程序负责系统的整体调度与初始化,确保各组件协同工作;按键扫描程序通过高效的矩阵扫描算法,准确捕获用户的按键动作,实时响应用户指令;显示程序则动态更新屏幕内容,确保计算过程的每一步都得到及时反馈。这三大程序模块的有机结合,确保了计算器功能的完整实现与流畅运行。
5. 资料清单&下载链接

0、常见使用问题及解决方法–必读!!!!
1、程序代码
2、Proteus仿真
3、功能要求
4、开题报告
5、设计报告
6、讲解视频
Altium Designer 安装破解
KEIL+proteus 单片机仿真设计教程
KEIL安装破解
Proteus元器件查找
Proteus安装
Proteus简易使用教程
单片机学习资料
相关数据手册
答辩技巧
设计报告常用描述
鼠标双击打开查找嘉盛单片机51 STM32单片机课程毕业设计.url
资料下载链接:
https://pan.baidu.com/s/1eiEeurqSY2hBktaMKal3GQ?pwd=6vam
相关文章:
基于STM32的简易计算器proteus仿真设计(仿真+程序+设计报告+讲解视频)
基于STM32的简易计算器proteus仿真设计 讲解视频1.主要功能2. 仿真3. 程序4. 设计报告5. 资料清单&下载链接 基于STM32的简易计算器proteus仿真设计(仿真程序设计报告讲解视频) 仿真图proteus 8.9 程序编译器:keil 5 编程语言:C语言 …...
小程序onLoad 和 onShow
onLoad 和 onShow 是小程序页面的生命周期函数,它们在不同的时机触发,具有不同的用途和执行顺序 1.onLoad: (1)onLoad 在页面加载时触发,仅执行一次。 (2)用于页面的初始化操作,例如…...
抖音直播违规规定有哪些?(直播违禁词汇总表)
全民直播的同时也有不少新手直播玩家处处碰壁,直播间没人气,直播不知道说什么甚至直播间被封。 收到直播封禁通知的朋友,轻者封禁直播账号两三天,严重着可能永久封禁直播间! 今天我们重点来说说直播间被封是怎么回事?如何避免抖音直播间被封?抖音直播间违规规定有哪些?抖音…...
安卓 jetpack compose
以下是 Jetpack Compose 中常用的一些组件的列表: 组件名称描述Text用于显示文本内容。Button可点击的按钮组件,常用于触发事件。TextField用于输入文本的文本框组件。Image用于展示图片。Column垂直布局容器,可以在其中垂直排列子组件。Row…...
JavaWeb系列十九: jQuery的DOM操作 上
查找节点, 修改属性 查找属性节点: 查找到所需要的元素之后, 可以调用jQuery对象的attr()方法用来 设置/返回 它的各种属性值 设置属性值 $(“img”).attr(“width”, “300”);返回属性值 $(“img”).attr(“width”); 创建节点 创建节点: 使用jQuery的工厂函数$(): $(html标…...
JavaWeb系列十一: Web 开发会话技术(Cookie, Session)
韩sir Cookie技术Cookie简单示意图Cookie常用方法Cookie创建Cookie读取JSESSIONID读取指定Cookie Cookie修改Cookie生命周期Cookie的有效路径Cookie作业布置Cookie注意事项Cookie中文乱码问题 Session技术Session原理示意图Session常用方法Session底层机制Session生命周期Sessi…...
【激光雷达使用记录】—— 如何在ubuntu中利用ros自带的rviz工具实时可视化雷达点云的数据
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、查看雷达数据的 frame_id1. 查看雷达数据的话题2. 查看数据的frame_id 二、可视化雷达数据总结 前言 RViz(ROS Visualization)是机…...
一道session文件包含题
目录 环境说明 session文件包含getshell 审计源码 session包含 base64在session中的解码分析 题目: 链接:https://pan.baidu.com/s/1Q0BN08b8gWiVE4tOnirpTA?pwdcate 提取码:cate 环境说明 这里我用的是linux,也可以用p…...
vuex数据持久化
清空原因: 刷新页面vuex的数据会丢失属于正常现象,因为JS的数据都是保存在浏览器的堆栈内存里面的,刷新浏览器页面,以前堆栈申请的内存被释放,这就是浏览器的运行机制,那么堆栈里的数据自然就清空了。 解…...
MySQL之复制(十)
复制 改变主库 确定期望的日志位置 如果有备库和新主库的位置不相同,则需要找到该备库最后一条执行的时间在新主库的二进制日志中相应的位置,然后再执行CHANGE MASTER TO.可以通过mysqlbinlog工具来找到备库执行的最后一条查询,然后在主库上…...
Spring MVC数据绑定和响应——简单数据绑定(一)默认类型数据绑定
一、Spring MVC常见的默认类型 当使用Spring MVC默认支持的数据类型作为处理器的形参类型时,Spring MVC的参数处理适配器会默认识别这些类型并进行赋值。Spring MVC常见的默认类型如下所示。 • HttpServletRequest:获取请求信息。 • HttpServlet…...
短视频平台自动化插件编写需要用到的源代码分享!
随着短视频平台的蓬勃发展,自动化插件的需求也日益增长,这些插件能够帮助用户更高效地管理内容、分析数据、优化发布策略等。 一、登录验证模块 登录验证是自动化插件的基础功能之一,确保用户能够安全地访问平台并执行相关操作,…...
安卓下载以来总是要添加maven下载地址,放在哪?
放这里面的 repositories 里...
springboot多数据源应用,A服务依赖于B服务jar包,A服务和B服务业务数据分别入自己的库如何做?
上一节我们简单阐述了springboot多数据源如何配置。在实际的业务场景中我们常常遇到A服务依赖于B服务jar包,A服务和B服务业务数据分别入自己的库中。为何要这么做呢?比如B服务是日志SDK,A服务集成B服务来实现记录日志的功能,但是日…...
20240626 每日AI必读资讯
🌍警告!OpenAI宣布全面封锁中国API接入! - 7月9号开始封锁不支持的国家API - 如果在OpenAI不允许的国家使用其 API 将面临封杀 🔗 警告!OpenAI 宣布全面封锁中国 API 接入-CSDN博客 🎵索尼、环球音乐、华…...
C语言经典算法题第一题
题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔 子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数 为多少? #include <stdio.h>int main() …...
计算预卷积特征
当冻结卷积层和训练模型时,全连接层或dense层(vgg.classifier)的输入始终是相同的。为了更好地理解,让我们将卷积块(在示例中为vgg.features块)视为具有了已学习好的权重且在训练期间不会更改的函数。因此,计算卷积特征并保存下来将有助于我们…...
Python 入门 —— 描述器
Python 入门 —— 描述器 文章目录 Python 入门 —— 描述器描述器简单示例定制名称只读属性状态交互验证器类自定义验证器验证器的使用 对象关系映射 描述器 前面我们介绍了两种属性拦截的方式:特性(property)以及重载属性访问运算符&#…...
测试驱动开发TDD
如何在后端测试代码,测试一个其前端的请求,能否正常处理 以登录请求为例 package com.example.demo.login;import com.example.demo.login.pojo.User; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import…...
AMD Ryzen SMU Debug Tool完全指南:揭秘硬件级调试的三大实战场景
AMD Ryzen SMU Debug Tool完全指南:揭秘硬件级调试的三大实战场景 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址:…...
WRF-CHEM模拟翻车?可能是你的namelist.chem没设对(附MEIC数据实战配置清单)
WRF-CHEM模拟异常排查指南:MEIC数据与namelist.chem的深度适配 当WRF-CHEM模拟结果出现异常时,很多用户会第一时间怀疑MEIC数据处理环节的问题,但实际上,namelist.chem参数与MEIC特性的匹配度才是更隐蔽的关键因素。本文将带您深入…...
AI智能体评估框架AgentEval:模块化设计与自动化评测实践
1. 项目概述:AgentEval,一个为AI智能体“打分”的裁判最近在折腾AI智能体(Agent)的开发,从简单的自动化脚本到复杂的多步推理系统,我前前后后也做了不少。但每次做完一个Agent,最头疼的问题就来…...
用户为中心交互系统工程在智能制造系统中应用
用户为中心交互系统工程(User-Centered Interaction System Engineering, UCI-SE)是智能制造与 AI 时代下,重塑传统工业软件(如 MES、ERP、SCADA)和硬件控制终端(如 HMI、具身智能教导盒)的核心…...
单卡训练mmsegmentation模型?先把这个SyncBN改成BN(附完整配置文件修改指南)
单卡训练mmsegmentation模型?先解决SyncBN这个关键配置 当你第一次在个人电脑或实验室的单一GPU设备上运行mmsegmentation训练脚本时,屏幕上突然弹出的SyncBN相关错误信息可能会让兴奋的心情瞬间跌入谷底。这个看似简单的配置问题,实际上反映…...
Windows资源管理器STL缩略图革命:3D模型可视化管理的终极解决方案
Windows资源管理器STL缩略图革命:3D模型可视化管理的终极解决方案 【免费下载链接】STL-thumbnail Shellextension for Windows File Explorer to show STL thumbnails 项目地址: https://gitcode.com/gh_mirrors/st/STL-thumbnail 还在为海量STL文件的管理而…...
多语言支持秘籍:validatorjs国际化错误消息配置终极指南
多语言支持秘籍:validatorjs国际化错误消息配置终极指南 【免费下载链接】validatorjs A data validation library in JavaScript for the browser and Node.js, inspired by Laravels Validator. 项目地址: https://gitcode.com/gh_mirrors/va/validatorjs …...
射频链路中 Coupler(耦合器)的作用分析
射频链路中 Coupler(耦合器)工程解析报告 ——原理、系统作用、工程实现及 Bi‑Directional Coupler 全解 1. Coupler 在射频链路里“到底起什么作用”(工程结论) Coupler 的本质作用只有一句话: 在**“不显著影响主射频链路”的前提下,抽取一小部分、方向可控的射频能量…...
RK3566(泰山派)实战:D310T9362V1SPEC触摸屏驱动从零适配与调试(竖屏)
1. RK3566与D310T9362V1SPEC屏幕简介 RK3566是瑞芯微推出的一款高性能嵌入式处理器,采用四核Cortex-A55架构,主频可达1.8GHz。这款芯片在工业控制、智能家居和物联网设备中广泛应用,特别适合需要图形显示和触摸交互的场景。我最近在一个智能终…...
米尔i.MX 93核心板:异构计算与AI赋能入门级嵌入式开发实战
1. 项目概述:米尔NXP i.MX 93核心板如何重塑入门级嵌入式体验 在嵌入式开发领域,选型往往是一场在性能、成本和功能之间的艰难平衡。对于许多从事工业HMI、智能网关、便携式医疗设备或新能源充电桩开发的工程师来说,他们既需要一颗能流畅运行…...
