【51单片机】4. 模块化编程与LCD1602Debug
1. 什么是模块化编程
传统编程会将所有函数放在main.c
中,如果使用的模块多,一个文件内会有很多代码,不利于组织和管理
模块化编程则是将各个模块的代码放在不同的.c
文件里,在.h
文件里提供外部可调用函数声明,其他.c
文件想使用其中的代码时,只需要#include "XXX.h"
即可
模块化编程有助于极大提高代码可阅读性、可维护性和可移植性
2. 模块化编程框架
假如我们将延迟函数Delay
模块化,则代码如下:
在Delay.c
文件中:
void Delay(unsigned int ms) //@11.0592MHz
{unsigned char i, j;while (ms){_nop_();i = 2;j = 199;do{while (--j);} while (--i);ms--;}
}
在Delay.h
文件中
#ifndef __DELAY_H__
#define __DELAY_H__void Delay(unsigned int ms);#endif
在main.c
中,就可以通过include
来引用了:
#include "Delay.h"void main()
{Delay(100);
}
注意事项:
-
.c
文件放函数和变量的定义 -
.h
文件放可被外部调用的函数、变量的声明 -
任何自定义的变量和函数在调用前必须有定义或声明
-
使用到的自定义函数
.c
文件必须添加到工程参与编译 -
使用到的
.h
文件必须要放在编译器可寻找到的地方(工程文件夹根目录、安装目录、自定义) -
自定义如下:
3. 常见预编译及意义
C语言预编译以#
开头,其作用是在真正编译开始之前对代码做一些处理(预编译)
#include <REGX52.H>
:将REGX52.H文件内容搬至此处
#ifndef __XX_H__
:如果没有定义__XX_H__
#endif
:与#ifndef
和#if
匹配,组成“括号”
#define ABC
:定义ABC
#define PI 3.14
:定义PI,并将PI替换为3.14
其他类似的包括#ifdef
, #if
, #else
, #elif
, #undef
等等
4. 模块化编程调整
我原本写了一串代码如下,现需要将其模块化
#include <REGX52.H>
#include <INTRINS.H>void Delay(unsigned int ms) //@11.0592MHz
{unsigned char i, j;while (ms){_nop_();i = 2;j = 199;do{while (--j);} while (--i);ms--;}
}int LEDNum[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
int LEDDPNum[] = {0xBF, 0x86, 0xDB, 0xcF, 0xE6, 0xED, 0xFD, 0x87, 0xFF, 0xEF};void LEDShow(unsigned int LEDLocation,Number, unsigned char dp)
{switch(LEDLocation){case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;}if (dp)P0 = LEDDPNum[Number];else P0 = LEDNum[Number];Delay(1);P0 = 0x00;
}void birthday1()
{LEDShow(1,2,0);LEDShow(2,0,0);LEDShow(3,2,0);LEDShow(4,5,1);LEDShow(5,0,0);LEDShow(6,6,1);LEDShow(7,0,0);LEDShow(8,7,0);
}void birthday2()
{LEDShow(1,2,0);LEDShow(2,0,0);LEDShow(3,2,0);LEDShow(4,5,1);LEDShow(5,0,0);LEDShow(6,6,1);LEDShow(7,0,0);LEDShow(8,8,0);
}void birthday3()
{LEDShow(1,2,0);LEDShow(2,0,0);LEDShow(3,2,0);LEDShow(4,5,1);LEDShow(5,0,0);LEDShow(6,6,1);LEDShow(7,1,0);LEDShow(8,1,0);
}int main(void)
{unsigned char showNum = 0;while (1){if (P3_1 == 1){Delay(20);if (showNum == 3) showNum = 0;while (P3_1 == 1){if (showNum == 0) birthday1();else if (showNum == 1) birthday2();else birthday3();}showNum++;Delay(20);}}
}
第一步:右键【Source Group 1】→【Add New Item to Source Group 1…】,创建Delay.c
第二步:将Delay
函数放入Delay.h
中:
#include <INTRINS.H>void Delay(unsigned int ms) //@11.0592MHz
{unsigned char i, j;while (ms){_nop_();i = 2;j = 199;do{while (--j);} while (--i);ms--;}
}
第三步:同第一步创建出Delay.h
创建完后发现虽然打开了Delay.h
的窗口,但是左侧栏中没有显示Delay.h
,需要如下操作显示:
写代码:
#ifndef __DELAY_H__ // 如果没定义Delay.h
#define __DELAY_H__ // 定义Delay.hvoid Delay(unsigned int ms);#endif // 结束if
第四步:照葫芦画瓢将LEDShow的相关函数搬过来创建LEDShow.c
和LEDShow.h
LEDShow.c
如下:
#include <REGX52.H>
#include "Delay.h"int LEDNum[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
int LEDDPNum[] = {0xBF, 0x86, 0xDB, 0xcF, 0xE6, 0xED, 0xFD, 0x87, 0xFF, 0xEF};void LEDShow(unsigned int LEDLocation,Number, unsigned char dp)
{switch(LEDLocation){case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;}if (dp)P0 = LEDDPNum[Number];else P0 = LEDNum[Number];Delay(1);P0 = 0x00;
}
LEDShow.h
如下:
#ifndef __LEDSHOW_H__
#define __LEDSHOW_H__void LEDShow(unsigned int LEDLocation,Number, unsigned char dp);#endif
第五步:修改主函数,此时可以将Delay和LEDShow相关头文件引入,删去原本相关的代码,精简如下:
#include <REGX52.H>
#include "Delay.h"
#include "LEDShow.h"void birthday1()
{LEDShow(1,2,0);LEDShow(2,0,0);LEDShow(3,2,0);LEDShow(4,5,1);LEDShow(5,0,0);LEDShow(6,6,1);LEDShow(7,0,0);LEDShow(8,7,0);
}void birthday2()
{LEDShow(1,2,0);LEDShow(2,0,0);LEDShow(3,2,0);LEDShow(4,5,1);LEDShow(5,0,0);LEDShow(6,6,1);LEDShow(7,0,0);LEDShow(8,8,0);
}void birthday3()
{LEDShow(1,2,0);LEDShow(2,0,0);LEDShow(3,2,0);LEDShow(4,5,1);LEDShow(5,0,0);LEDShow(6,6,1);LEDShow(7,1,0);LEDShow(8,1,0);
}int main(void)
{unsigned char showNum = 0;while (1){if (P3_1 == 1){Delay(20);if (showNum == 3) showNum = 0;while (P3_1 == 1){if (showNum == 0) birthday1();else if (showNum == 1) birthday2();else birthday3();}showNum++;Delay(20);}}
}
编译烧录后效果如下:
5. LCD1602 Debug
江科大提供有LCD1602的头文件,可以直接导入到项目里(这部分目前还没学),导入后如下:
其次需要将LCD1602插到板子右上角(可以看结果图插的位置,插反会报警,报警记得断开电源),然后看电路图:
动态数码管模块&LCD1602&单片机核心的接口有些冲突,所以会导致插了LCD1602后动态数码管乱闪的问题。
之后就可以根据提供的函数,在LCD1602上打印想要的东西了,具体的注释在.c文件里边有,我这里打印了一个字符串:
#include "LCD1602.h"void main()
{LCD_Init();// LCD_ShowChar(1,1,'A');LCD_ShowString(1,3,"Hello World!!");while (1){}
}
效果如下:
附:编译器无法识别中文
可以通过修改编码模式解决:
-
GB2312能识别中文但会导致字体改变
-
UTF-8由于有自己的一套编码格式,以前写的一些英文注解可能也无法识别?
相关文章:

【51单片机】4. 模块化编程与LCD1602Debug
1. 什么是模块化编程 传统编程会将所有函数放在main.c中,如果使用的模块多,一个文件内会有很多代码,不利于组织和管理 模块化编程则是将各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数声明,其他.c文…...
Java并发编程实战 Day 11:并发设计模式
【Java并发编程实战 Day 11】并发设计模式 开篇 这是"Java并发编程实战"系列的第11天,今天我们聚焦于并发设计模式。并发设计模式是解决多线程环境下常见问题的经典解决方案,它们不仅提供了优雅的设计思路,还能显著提升系统的性能…...

DeepSeek越强,Kimi越慌?
被DeepSeek吊打的Kimi,还有多少人在用? 去年,月之暗面创始人杨植麟别提有多风光了。90后清华学霸,国产大模型六小虎之一,手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水,单月光是投流就花费2个亿。 疯…...

数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor
1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...
跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践
在电商行业蓬勃发展的当下,多平台运营已成为众多商家的必然选择。然而,不同电商平台在商品数据接口方面存在差异,导致商家在跨平台运营时面临诸多挑战,如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...
电脑桌面太单调,用Python写一个桌面小宠物应用。
下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡,可以响应鼠标点击,并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...
字符串哈希+KMP
P10468 兔子与兔子 #include<bits/stdc.h> using namespace std; typedef unsigned long long ull; const int N 1000010; ull a[N], pw[N]; int n; ull gethash(int l, int r){return a[r] - a[l - 1] * pw[r - l 1]; } signed main(){ios::sync_with_stdio(false), …...

ArcGIS Pro+ArcGIS给你的地图加上北回归线!
今天来看ArcGIS Pro和ArcGIS中如何给制作的中国地图或者其他大范围地图加上北回归线。 我们将在ArcGIS Pro和ArcGIS中一同介绍。 1 ArcGIS Pro中设置北回归线 1、在ArcGIS Pro中初步设置好经纬格网等,设置经线、纬线都以10间隔显示。 2、需要插入背会归线…...

MySQL体系架构解析(三):MySQL目录与启动配置全解析
MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录,这个目录下存放着许多可执行文件。与其他系统的可执行文件类似,这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中,用…...
从实验室到产业:IndexTTS 在六大核心场景的落地实践
一、内容创作:重构数字内容生产范式 在短视频创作领域,IndexTTS 的语音克隆技术彻底改变了配音流程。B 站 UP 主通过 5 秒参考音频即可克隆出郭老师音色,生成的 “各位吴彦祖们大家好” 语音相似度达 97%,单条视频播放量突破百万…...
boost::filesystem::path文件路径使用详解和示例
boost::filesystem::path 是 Boost 库中用于跨平台操作文件路径的类,封装了路径的拼接、分割、提取、判断等常用功能。下面是对它的使用详解,包括常用接口与完整示例。 1. 引入头文件与命名空间 #include <boost/filesystem.hpp> namespace fs b…...

小智AI+MCP
什么是小智AI和MCP 如果还不清楚的先看往期文章 手搓小智AI聊天机器人 MCP 深度解析:AI 的USB接口 如何使用小智MCP 1.刷支持mcp的小智固件 2.下载官方MCP的示例代码 Github:https://github.com/78/mcp-calculator 安这个步骤执行 其中MCP_ENDPOI…...
Python爬虫实战:研究Restkit库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的有价值数据。如何高效地采集这些数据并将其应用于实际业务中,成为了许多企业和开发者关注的焦点。网络爬虫技术作为一种自动化的数据采集工具,可以帮助我们从网页中提取所需的信息。而 RESTful API …...

DAY 45 超大力王爱学Python
来自超大力王的友情提示:在用tensordoard的时候一定一定要用绝对位置,例如:tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾: tensorboard的发展历史和原理tens…...

UE5 音效系统
一.音效管理 音乐一般都是WAV,创建一个背景音乐类SoudClass,一个音效类SoundClass。所有的音乐都分为这两个类。再创建一个总音乐类,将上述两个作为它的子类。 接着我们创建一个音乐混合类SoundMix,将上述三个类翻入其中,通过它管理每个音乐…...

轻量级Docker管理工具Docker Switchboard
简介 什么是 Docker Switchboard ? Docker Switchboard 是一个轻量级的 Web 应用程序,用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器,使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...
如何通过git命令查看项目连接的仓库地址?
要通过 Git 命令查看项目连接的仓库地址,您可以使用以下几种方法: 1. 查看所有远程仓库地址 使用 git remote -v 命令,它会显示项目中配置的所有远程仓库及其对应的 URL: git remote -v输出示例: origin https://…...

Linux基础开发工具——vim工具
文章目录 vim工具什么是vimvim的多模式和使用vim的基础模式vim的三种基础模式三种模式的初步了解 常用模式的详细讲解插入模式命令模式模式转化光标的移动文本的编辑 底行模式替换模式视图模式总结 使用vim的小技巧vim的配置(了解) vim工具 本文章仍然是继续讲解Linux系统下的…...

边缘计算网关提升水产养殖尾水处理的远程运维效率
一、项目背景 随着水产养殖行业的快速发展,养殖尾水的处理成为了一个亟待解决的环保问题。传统的尾水处理方式不仅效率低下,而且难以实现精准监控和管理。为了提升尾水处理的效果和效率,同时降低人力成本,某大型水产养殖企业决定…...

echarts使用graphic强行给图增加一个边框(边框根据自己的图形大小设置)- 适用于无法使用dom的样式
pdf-lib https://blog.csdn.net/Shi_haoliu/article/details/148157624?spm1001.2014.3001.5501 为了完成在pdf中导出echarts图,如果边框加在dom上面,pdf-lib导出svg的时候并不会导出边框,所以只能在echarts图上面加边框 grid的边框是在图里…...

goreplay
1.github地址 https://github.com/buger/goreplay 2.简单介绍 GoReplay 是一个开源的网络监控工具,可以记录用户的实时流量并将其用于镜像、负载测试、监控和详细分析。 3.出现背景 随着应用程序的增长,测试它所需的工作量也会呈指数级增长。GoRepl…...

麒麟系统使用-进行.NET开发
文章目录 前言一、搭建dotnet环境1.获取相关资源2.配置dotnet 二、使用dotnet三、其他说明总结 前言 麒麟系统的内核是基于linux的,如果需要进行.NET开发,则需要安装特定的应用。由于NET Framework 是仅适用于 Windows 版本的 .NET,所以要进…...
游戏开发中常见的战斗数值英文缩写对照表
游戏开发中常见的战斗数值英文缩写对照表 基础属性(Basic Attributes) 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...

GraphRAG优化新思路-开源的ROGRAG框架
目前的如微软开源的GraphRAG的工作流程都较为复杂,难以孤立地评估各个组件的贡献,传统的检索方法在处理复杂推理任务时可能不够有效,特别是在需要理解实体间关系或多跳知识的情况下。先说结论,看完后感觉这个框架性能上不会比Grap…...

Canal环境搭建并实现和ES数据同步
作者:田超凡 日期:2025年6月7日 Canal安装,启动端口11111、8082: 安装canal-deployer服务端: https://github.com/alibaba/canal/releases/1.1.7/canal.deployer-1.1.7.tar.gz cd /opt/homebrew/etc mkdir canal…...

【java面试】微服务篇
【java面试】微服务篇 一、总体框架二、Springcloud(一)Springcloud五大组件(二)服务注册和发现1、Eureka2、Nacos (三)负载均衡1、Ribbon负载均衡流程2、Ribbon负载均衡策略3、自定义负载均衡策略4、总结 …...

HTTPS证书一年多少钱?
HTTPS证书作为保障网站数据传输安全的重要工具,成为众多网站运营者的必备选择。然而,面对市场上种类繁多的HTTPS证书,其一年费用究竟是多少,又受哪些因素影响呢? 首先,HTTPS证书通常在PinTrust这样的专业平…...

Python环境安装与虚拟环境配置详解
本文档旨在为Python开发者提供一站式的环境安装与虚拟环境配置指南,适用于Windows、macOS和Linux系统。无论你是初学者还是有经验的开发者,都能在此找到适合自己的环境搭建方法和常见问题的解决方案。 快速开始 一分钟快速安装与虚拟环境配置 # macOS/…...

内窥镜检查中基于提示的息肉分割|文献速递-深度学习医疗AI最新文献
Title 题目 Prompt-based polyp segmentation during endoscopy 内窥镜检查中基于提示的息肉分割 01 文献速递介绍 以下是对这段英文内容的中文翻译: ### 胃肠道癌症的发病率呈上升趋势,且有年轻化倾向(Bray等人,2018&#x…...