单片机学习中的模块化编程,例程:51单片机驱动LCD1602,开发软件:keil

    在刚接触到单片机时,代码较短,单片机功能的实现函数都写在main.c一个文件。随着学习的深入,多模块的使用和代码量的增加使得单用一个.c文件显得程序很混乱。模块化编程的使用可增强代码的可读性,可移植性。这里以51单片机驱动LCD1602的为例讲解。

 一、知识储备---#ifndef

作用:防止头文件的重复包含和编译

定义

  #ifndef x

  #define x

  ...

  #endif

  这是宏定义的一种,它可以根据是否已经定义了一个变量来进行分支选择,一般用于调试等等.实际上确切的说这应该是预处理功能中三种(宏定义,文件包含和条件编译)中的一种----条件编译。 C语言在对程序进行编译时,会先根据预处理命令进行“预处理”。C语言编译系统包括预处理,编译和链接等部分。

  #ifndef x

  //先测试x是否被宏定义过

  #define x

  //如果没有宏定义下面就宏定义x并编译下面的语句

  ...

  #endif

  //如果已经定义了则编译#endif后面的语句

  条件指示符#ifndef检查预编译常量在前面是否已经被宏定义。如果在前面没有被宏定义,则条件指示符的值为真,于是从#ifndef到#endif之间的所有语句都被包含进来进行编译处理。相反,如果#ifndef指示符的值为假,则它与#endif指示符之间的行将被忽略。条件指示符#ifndef 的最主要目的是防止头文件的重复包含和编译。

补充一些内容

  千万不要忽略了头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。

  还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:

  #ifndef <标识>

  #define <标识>

  ......

  #endif

  <标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:LCD1602.h

  #ifndef __LCD1602_H_

  #define __LCD1602_H_

  ......

#endif

#ifndef xxx//如果没有定义xxx
#define xxx//定义xxx
#endif //结束如果
这个用法主要是在头文件中,主要是为了防止类重复的include,所以在类的头文件之前加上前面两个,用类名替代xxx,在最后加上最后一句。


了解更多:https://blog.csdn.net/qq_33658067/article/details/79443014

二、keil上的实现

1、创建工程,这里不做过多叙述。

2、工程分组,c文件的添加,按图示步骤进行。

3.头文件的编写及路劲添加,LCD1602.h

#ifndef __LCD1602_H_  //检测是否被定义
#define __LCD1602_H_  //未定义则为初次调用该头文件,则定义该宏
#include "reg51.h" //51单片机特殊功能寄存器声明文件

#define LCD1602_DATAPINS P0 //管脚定义
sbit LCD1602_RS=P2^6; //管脚定义
sbit LCD1602_RW=P2^5;
sbit LCD1602_E=P2^7;

void LcdInit(void); //.c文件中的函数声明
void LCD_CLR(void);
void LcdWriteCom(unsigned char com);
void LcdWriteData(unsigned char dat);
void LCD1602_SYB(unsigned char x,unsigned char y,unsigned char *str);
void LCD1602_NUM(unsigned char x,unsigned char y,int a);
void LCD1602_FLOAT(unsigned char x,unsigned char y,double dat);
#endif //结束如果

如对应c文件有全局变量且要在其它地方使用,可在#define 后声明该变量, 如 extern int val。

如在一些情况下管脚不对应,只需更改宏定义的管脚,很方便移植性强。

按以下步骤添加头文件路径。


4、C文件的编写,LCD1602.c

#include "reg51.h"  //51单片机特殊功能寄存器声明文件
#include "LCD1602.h" //对应的头文件
#include "delay.h"  //需用到延时函数
//--------写命令
void LcdWriteCom(unsigned char com)	 
{
	LCD1602_E = 0;     
	LCD1602_RS = 0;	  
	LCD1602_RW = 0;	 
	
	LCD1602_DATAPINS = com;    
	
	LCD1602_E = 1;	         
	delay_12us(1); 
	LCD1602_E = 0;
	delay_12us(1); 
}
//--------写数据
void LcdWriteData(unsigned char dat)			
{
	LCD1602_E = 0;	
	LCD1602_RS = 1;	
	LCD1602_RW = 0;	

	LCD1602_DATAPINS = dat; 
	
	LCD1602_E = 1;  
	delay_12us(1);  
	LCD1602_E = 0;
	delay_12us(1);
}
//--------清屏
void LCD_CLR(void)
{
	LcdWriteCom(0x01);//清屏
	delay_ms(5);
}
//--------初始化
void LcdInit(void)						 
{
 	LcdWriteCom(0x38);//设置为8总线,双行显示,5*10点阵字符
	LcdWriteCom(0x0c);//开启显示,不显示光标
	LcdWriteCom(0x06);//自左向右显示,显示内容不移动
	LCD_CLR();
}
//--------从x行y列坐标处开始显示字符串
void LCD1602_SYB(unsigned char x,unsigned char y,unsigned char *str)
{
	unsigned char addr,i;
	if(x==1)
		addr = 0x00+y-1;
	else
		addr = 0x40+y-1;
	LcdWriteCom(addr+0x80);//设置显示地址
	while(*str!='\0')
	{
		i++;
		LcdWriteData(*str++);
	}
	while(i<7)//用空格覆盖上次显示,这里写的不好
	{
		i++;
		LcdWriteData(' ');
	}
}
//--------将整型数据转换为字符串数组并显示
void LCD1602_NUM(unsigned char x,unsigned char y,signed int a)
{
	unsigned char num[7];
	if(a>=0)
	{	
		num[0]=a%10+48;
		num[1]='\0';
		num[2]=0;
		num[3]=0;
		num[4]=0;
		num[5]=0;
		if(a>=10)
		{
			num[0]=a/10%10+48;
			num[1]=a%10+48;
			num[2]='\0';
			num[3]=0;
			num[4]=0;
			num[5]=0;
		}
		if(a>=100)
		{
			num[0]=a/100%10+48;
			num[1]=a/10%10+48;
			num[2]=a%10+48;
			num[3]='\0';
			num[4]=0;
			num[5]=0;
		}
		if(a>=1000)
		{
			num[0]=a/1000%10+48;
			num[1]=a/100%10+48;
			num[2]=a/10%10+48;
			num[3]=a%10+48;
			num[4]='\0';
			num[5]=0;
		}
		if(a>=10000)
		{
			num[0]=a/10000%10+48;
			num[1]=a/1000%10+48;
			num[2]=a/100%10+48;
			num[3]=a/10%10+48;
			num[4]=a%10+48;
			num[5]='\0';
		}
	}
	if(a<0)
	{
		a=-a;
		num[0]='-';
		num[1]=a%10+48;
		num[2]='\0';
		num[3]=0;
		num[4]=0;
		num[5]=0;
		num[6]=0;
		if(a>=10)
		{
			num[1]=a/10%10+48;
			num[2]=a%10+48;
			num[3]='\0';
			num[4]=0;
			num[5]=0;
			num[6]=0;
		}
		if(a>=100)
		{
			num[1]=a/100%10+48;
			num[2]=a/10%10+48;
			num[3]=a%10+48;
			num[4]='\0';
			num[5]=0;
			num[6]=0;
		}
		if(a>=1000)
		{
			num[1]=a/1000%10+48;
			num[2]=a/100%10+48;
			num[3]=a/10%10+48;
			num[4]=a%10+48;
			num[5]='\0';
			num[6]=0;
		}
		if(a>=10000)
		{
			num[1]=a/10000%10+48;
			num[2]=a/1000%10+48;
			num[3]=a/100%10+48;
			num[4]=a/10%10+48;
			num[5]=a%10+48;
			num[6]='\0';
		}
	}
	LCD1602_SYB(x,y,num);
}
//--------显示浮点型数据
void LCD1602_FLOAT(unsigned char x,unsigned char y,double dat)
{
	int a,b;
	unsigned char c;
	a=(int)dat;
	b=(int)((dat-a)*100);//浮点型转整形会出现误差
	c=1;
	if(a<0)
	{
		a=0-a;
		b=0-b;
		if(a>10)
			c=2;
		if(a>100)
			c=3;
		if(a>1000)
			c=4;
		if(a>10000)
			c=5;
		LCD1602_SYB(x,y,"-");
		LCD1602_NUM(x,y+1,a);
		LCD1602_SYB(x,c+y+1,".");
		LCD1602_NUM(x,c+y+2,b/10);
		LCD1602_NUM(x,c+y+3,b%10);
	}
	else
	{
		if(a>10)
			c=2;
		if(a>100)
			c=3;
		if(a>1000)
			c=4;
		if(a>10000)
			c=5;
		LCD1602_NUM(x,y,a);
		LCD1602_SYB(x,c+y,".");
		LCD1602_NUM(x,c+y+1,b/10);
		LCD1602_NUM(x,c+y+2,b%10);
	}
}
	

三、效果展示

简洁的main.c文件,可读性强。

#include "reg51.h"
#include "LCD1602.h"

void main(void)
{
	LcdInit();
	//显示测试
	LCD1602_SYB(1,1,"Welcome to SWPU");
	LCD1602_NUM(2,1,12345);
	LCD1602_FLOAT(2,9,-123.02);
	while(1);
}


附:

delay.h

#ifndef __DELAY_H_
#define __DELAY_H_
#include "reg51.h"
//超级不准的延时。。。
void delay_ms(unsigned int c);
void delay_12us(unsigned char c);

#endif

delay.c

#include "reg51.h"
#include "delay.h"

void delay_ms(unsigned int c)   
{
    unsigned char a,b;
    for(c;c>0;c--)
        for(b=142;b>0;b--)
            for(a=2;a>0;a--);
}
void delay_12us(unsigned char c)   
{
    unsigned char a;
	for(c;c>0;c--)
   	 for(a=4;a>0;a--);
}


--By Young


猜你喜欢

转载自blog.csdn.net/yc5300891/article/details/80636478