51单片机学习笔记-3模块化编程

news2024/11/16 8:21:57

3 模块化编程

[toc]

注:笔记主要参考B站江科大自化协教学视频“51单片机入门教程-2020版 程序全程纯手打 从零开始入门”。


3.1 模块化编程

传统方式编程:所有的函数均放在main.c里,若使用的模块比较多,则一个文件内会有很多的代码,不利于代码的组织和管理,而且很影响编程者的思路。
模块化编程: 把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include "XXX.h"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等。

比如下面这张图,将Dealy函数进行了头文件声明、源文件定义,便可以在其他的源文件中调用了。

图3-1 模块化编程示意图

注意事项:

  • .c文件:函数、变量的定义。
  • .h文件:可被外部调用的函数、变量的声明。
  • 任何自定义的变量、函数,在某个.c文件中被调用前必须有定义或声明。
  • 使用到的自定义函数的.c文件必须添加到工程参与编译。
  • 使用到的.h文件必须要放在编译器可寻找到的地方(工程文件夹根目录、安装目录、自定义)。
表3-1 C语言预编译举例
预编译意义
#include <REGX52.H>把REGX52.H文件的内容搬到此处。
< >一般是用在系统自带,“ ”一般是用在自己写的。
#define PI 3.14定义PI,将PI替换为3.14
#define ABC定义ABC
#ifndef __XX_H__ 如果没有定义__XX_H__
#endif与#ifndef,#if匹配,组成“括号”

C语言的预编译以#开头,作用是在真正的编译开始之前,对代码做一些处理(预编译),如上表所示。此外还有#ifdef,#if,#else,#elif,#undef等。这些预编译指令最重要的作用就是避免头文件被多次包含而失效(保证头文件只被包含一次)。而最常用的预编译指令结构如下(如上图3-1演示的那样):

#ifndef MY_HEADER_FILE _H_
#define MY_HEADER_FILE _H_
    //头文件内容
#endif

3.2 LCD1602调试工具

3.2.1 LCD1602液晶屏介绍
1602液晶也叫1602字符型液晶,能显示 16个字符*2行 的信息。它是由若干个 5x7 或者 5x10 的点阵字符位组成,每个点阵字符位都可以用显示一个字符,每位之间有一个点距的间隔,每行之间也有间隔,起到了字符间距和行间距的作用。正因如此,它不能很好的显示图片,而专门用来显示字母、数字、符号。

使用LCD1602液晶屏作为调试窗口,提供类似printf函数的功能,可实时观察单片机内部数据的变换情况,便于调试和演示。本视频提供的LCD1602代码属于模块化的代码,使用者只需要知道所提供函数的作用和使用方法就可以很容易的使用LCD1602。
注:当然也可以通过数码管来进行显示,但是数码管需要不断扫描,占用CPU资源较多所以不推荐。另外也可以通过串口通信的方式进行调试,但是其原理更加复杂且需要外接电脑不是很方便,所以也不推荐。

图3-2 LCD1602接口原理图
  • LCD RS:数据/命令选择端(H/L)。
  • LCD WR:写读选择端(L/H)。
  • LCD EN:使能信号。
  • 在第12部分会详细的介绍其工作原理及相应的代码编写。
表3-2 由UP主提供的LCD相关函数
函数作用
LCD_Init();初始化
LCD_ShowChar(1,1,'A');在指定位置显示一个字符
LCD_ShowString(1,3,"Hello");在指定位置显示字符串
LCD_ShowNum(1,9,123,3);在指定位置显示十进制数字
LCD_ShowSignedNum(1,13,-66,3);在指定位置显示有符号十进制数字
LCD_ShowHexNum(2,1,0xA8,2);在指定位置显示十六进制数字
LCD_ShowBinNum(2,4,0xAA,8);在指定位置显示二进制数字

注:Keil5在创建头文件后,不会自动将其添加进工程中来,但是文件会放在当前目录下,可以右键手动添加,以方便管理。

3.2.2 代码展示
需求:试一试表3-1中所有的功能,然后使用液晶屏观察中间变量的变化。

图3-3 函数调用关系

上图给出了本工程的函数调用关系,下面是代码展示:
- main源文件

#include <REGX52.H>
#include"LCD1602.h"

void main(){
  int a = 0; // 注意若编译器较老,那么不能中途定义变量
  
//  // 注意下面的代码不要放在while(1)里面,否则LCD会闪
//  LCD_Init();
//  LCD_ShowChar(1,1,'A');
//  LCD_ShowString(1,2,"HelloWorld");
//  LCD_ShowNum(1,12,666,3);
//  LCD_ShowSignedNum(1,15,-9,2);	
//  LCD_ShowHexNum(2,1,0xA8,2);
//  LCD_ShowBinNum(2,5,0xAA,8);
  
  // 下面是实际想用的场景:展示代码运行结果
  LCD_Init();
  while(1){
    LCD_ShowNum(1,1,a++,8);
  }
}

- LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif

- LCD1602.c

 #include <REGX52.H>

//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}

/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}

/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/184794.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1604_linux环境下使用命令行把网页转换成pdf

全部学习汇总&#xff1a; GreyZhang/toolbox: 常用的工具使用查询&#xff0c;非教程&#xff0c;仅作为自我参考&#xff01; (github.com) 使用的工具很容易在彼此之间产生隔离性障碍&#xff0c;比如我最近使用的墨水屏阅读的最合适的文件格式我觉得是pdf&#xff0c;但是我…

路由工具之路由策略router-policy、acl列表与ip-prefix前缀列表的区别、过滤列表filter-policy

3.0.0 路由工具之路由策略router-policy、acl列表与ip-prefix前缀列表的区别、过滤列表filter-policy 目录IP-Prefix前缀列表前缀列表与ACLrouter-policy路由策略应用路由策略过滤路由1、环境介绍2、配置OSPF3、过滤路由&#xff08;1&#xff09;ACL匹配路由方式过滤&#xff…

带死区的PID控制算法及仿真

在计算机控制系统中&#xff0c;某些系统为了避免控制作用过于频繁&#xff0c;消除由于频繁动作所引起的振荡&#xff0c;可采用带死区的PID控制算法&#xff0c;控制算式为&#xff1a;式中&#xff0c;e(k)为位置跟踪偏差;e为一个可调参数&#xff0c;其具体数值可根据实际控…

软件测试职场六年,一个女测试工程师的自我认知

微软自动化测试二年&#xff0c;而后转入阿里做自动化测试三年&#xff0c;经历了入行时的迷茫&#xff0c;而后的笃定&#xff0c;转入移动后对自身定位和价值的怀疑&#xff0c;继而对自动化测试的重新认识&#xff0c;职场六年&#xff0c;终于敢对自动化测试有所论述了。 先…

五个好用的PDF软件推荐!

我们在工作中经常需要选择一款好用的办公软件来转换PDF文件&#xff0c;如果选择的软件不好用&#xff0c;那就回影响我们工作的效率&#xff0c;如果选对了软件&#xff0c;就可以让我们的效率越来越高&#xff0c;足以证明软件的在我们办公中的重要性&#xff0c;下面小编就来…

win远程桌面连接无显示器Ubuntu(22.04.1 LTS)

1、安装ssh server 安装虚拟显示器会导致物理显示器无法使用&#xff0c;为防止虚拟显示出现问题无法连接Ubuntu&#xff0c;在必要时可以使用SSH连接系统。 # Ubuntu Terminal sudo apt-get install openssh-server在Windows中尝试连接 # Windows PowerShell ssh UsernameU…

C语言递归函数(递归调用)详解

一个函数在它的函数体内调用它自身称为递归调用&#xff0c;这种函数称为递归函数。执行递归函数将反复调用其自身&#xff0c;每调用一次就进入新的一层&#xff0c;当最内层的函数执行完毕后&#xff0c;再一层一层地由里到外退出。递归函数不是C语言的专利&#xff0c;Java、…

css背景

1、背景颜色&#xff1a;半透明 <style>div{width: 1000px;height: 100px;/* 背景颜色半透明&#xff0c;其他文字不受影响 */background: rgba(0 ,0 ,0 ,0.3 );}</style> </head> <body><div></div> </body>2、背景图 属性名: ba…

vue前端框架课程笔记(一)

目录初识Vue演示代码模板语法插值语法指令语法使用举例数据绑定两种数据绑定方式示例el和data的两种写法el的两种写法data的两种写法MVVM模型数据代理Object.defineProperty示例参数说明关于getter和setter使用举例泛化的数据代理举例vue中的数据代理原理事件处理指令methods配…

word样式管理:如何对样式进行修改删除

在前面的图文中简单为大家介绍了Word文本样式和表格样式的创建技巧。但对于已经创建好的样式&#xff0c;有时会对样式中的格式进行再次修改&#xff1b;或者是当拿到某个Word文档&#xff0c;它的样式经历多次或多人不断修改&#xff0c;可能导致样式库中的样式混乱不堪&#…

Dockerfile构建Tomcat镜像

Dockerfile构建Tomcat镜像构建步骤1 编写Dockfile文件2 dockcer build构建镜像3 docker run运行容器4 使用容器卷挂载搜索镜像的个数 docker search tomcat |wc -l因此&#xff0c;需要根据自己的项目需求来针对性的构建镜像 构建步骤 1 编写Dockfile文件 看成在编写一个纯净…

React 项目 黑马极客园

React 项目 黑马极客园git地址视频地址项目准备1.项目介绍2.项目搭建3.使用scss预处理器4.配置基础路由5.组件库antd使用6.配置别名路径8.别名路径提示9.安装dev-tools调试工具登录模块1.基本结构搭建2. 创建表单结构3. 表单校验实现4. 获取登录表单数据5. 封装http工具模块6. …

Android Studio Electric Eel | 2022.1.1 版本

前言 各位读者&#xff0c;新年快乐&#xff0c;现在是2023年了&#xff0c;新的一年&#xff0c;我们的开发也会焕然一新&#xff0c;Android开发的工具Android Studio&#xff0c;也能更新到Android Studio Electric Eel | 2022.1.1 版本了&#xff0c;短期内我将使用此版本学…

Delphi 中.deployproj文件结构

Delphi中&#xff0c;如果生成Android或者IOS等APP&#xff0c;需要在Project -> Deployment中按下Deploy图标 &#xff0c;然后就会生成和项目工程文件名称一致&#xff0c;后缀是.deployproj的文件。当然如果直接按下运行图标(RUN)也会自动生成这个文件。一、.deployproj文…

【自然语言处理】情感分析(五):基于 BERT 实现

情感分析&#xff08;五&#xff09;&#xff1a;基于 BERT 实现本文是 情感分析 系列的第 555 篇&#xff0c;也是本系列的收官之作。前四篇分别是&#xff1a; 【自然语言处理】情感分析&#xff08;一&#xff09;&#xff1a;基于 NLTK 的 Naive Bayes 实现【自然语言处理…

简答说明通用智能基础模型的基本处理逻辑

当能量多的时候 激发的是欲望 按照中庸之道 多余的能力要消耗掉 当没有可以查询的模仿行为或者是大量消耗能量行为的时候 就开始记录行为统计为一个发生概率行为表 当拥有了概率记录数据后&#xff0c;按照最大发生概率依次模仿行为 行为得到的反馈 反馈的时间越短 则判断剩余能…

【论文速递】BEVFormer: 通过时空变换器从多相机图像中学习BEV表示

【论文原文】&#xff1a;ECCV2022 - BEVFormer: Learning Bird’s-Eye-View Representation from Multi-Camera Images via Spatiotemporal Transformers 论文&#xff1a;https://arxiv.org/abs/2203.17270 代码&#xff1a;https://github.com/fundamentalvision/BEVFormer…

C++ opencv之配置环境

opencv下载官网下载连接:https://opencv.org/releases/选择相应版本下载&#xff0c;windows操作系统下载windows版本下载exe后&#xff0c;双击即可安装&#xff0c;选择好路径&#xff0c;尽量安装路径不要存在中文&#xff0c;安装好如下:vs2022配置opencv为DLL添加Path环境…

windows10搭建spark本地开发环境

windows10搭建spark本地开发环境1. spark概述2. 安装spark2.1 Windows10 安装Spark本地开发环境2.1.1 **版本说明**2.1.2 **环境准备**2.1.2.1 JDK 安装和配置2.1.2.1.1 JDK下载2.1.2.1.2 JDK安装2.1.2.1.3 JDK配置2.1.2.2 Scala 安装和配置2.1.2.2.1Scala下载2.1.2.2.2 Scala安…

51单片机学习笔记-11 DS18B02温度传感器(单总线)

11 DS18B02温度传感器 [toc] 注&#xff1a;笔记主要参考B站江科大自化协教学视频“51单片机入门教程-2020版 程序全程纯手打 从零开始入门”。 11.1 温度传感器与单总线通信 11.1.1 DS18B20温度传感器 DS18B20是一种常见的数字温度传感器&#xff0c;其控制命令和数据都是以…