《2022年AVR学习笔记十三、模块化程序设计 .pdf》由会员分享,可在线阅读,更多相关《2022年AVR学习笔记十三、模块化程序设计 .pdf(7页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、AVR 学习笔记十三、模块化程序设计-基于 LT_Mini_M16 13.1 程序模块化设计13.1.1、实例功能经过一段时间学习,我们应该已经对AVR 单片机的功能特点有了初步的了解。现在是时候进行一些总结了。首先我们来回顾一下我们所编写的那些程序。我们发现随着学习的深入,我们编写的程序是越来越复杂了,程序的长度是疯狂地增加。现在我们看看DS18B20 学习实验的第三个程序,这个程序里面有很多函数,这些函数的声明和定义在整个程序里面占了很大的比例。我们在编写和使用这些函数的时候,需要不停的翻找相关的程序部分。显得很麻烦。而且整个程序显得有些乱。那么能不能把程序精简一下,能不能把具有相关功能的
2、函数放在一起,能不能向我们使用头文件的时候,直接使用一个包含命令就把一些相关功能包含到程序里,而我们在程序里只要调用我们用到的函数就能够实现我们想要达到的目的呢?答案是肯定的!今天我们就学习一下模块化程序设计。本实例有两个功能模块:了解模块化程序设计的思路和方法。编写程序,LCD1602液晶的模块化程序设计步骤。通过本实例的学习,掌握以下知识点:初步掌握模块化程序设计的方法。13.1.2 模块化程序设计的简单介绍1、模块化程序设计简介用模块化方法进行程序设计的技术在20 世纪 50 年代就出现雏形。在进行程序设计时把一个大的程序按照功能划分为若干小的程序,每个小的程序完成一个确定的功能,在这些
3、小的程序之间建立必要的联系,互相协作完成整个程序要完成的功能。我们称这些小的程序为程序的模块。通常规定模块只有一个入口和出口,使用模块的约束条件是入口参数和出口参数。用模块化的方法设计程序,其过程犹如搭积木的过程,选择不同的积木块或采用积木块不同的组合就可以搭出不同的造型来。同样,选择不同的程序块或程序模块的不同组合就可以完成不同的系统架构和功能来。将一个大的程序划分为若干不同的相对独立的小程序模块,正是体现了抽象的原则,这种方法已经被人们接受。把程序设计中的抽象结果转化成模块,不仅可以保证设计的逻辑正确性,而且更适合项目的集体开发。各个模块分别由不同的程序员编制,只要明确模块之间的接口关系,
4、模块内部细节的具体实现可以由程序员自己随意设计,而模块之间不受影响。具体到程序来说,模块通常是指可以用一个名字调用的一个程序段。对于不同的程序设计语言,模块的实现和名称也不相同,在 BASIC,FORTRAN 语言中的模块称作子程序;PASCAL 语言中的模块称为过程;C 语言中的模块叫函数2、模块化程序设计思路模块化程序设计的思路是这样的:将一个大的程序按功能分割成一些小模块;即:把具有相同功能的函数放在一个文件中,然后再主程序里面把这个文件作为像编译器里面的那些函数文件一样用#include 指令把这个文件包含到主程序文件中,那么在主程序中就可以直接调用这个文件中定义好的函数来实现特定的功
5、能,而在主程序中不用声明和定义这些函数。这样就使主程序显得更加精炼,可读性也会增强。同时,我们把具有相同功能的函数放在同一个文件中,这样有一个很大的优点是便于移植,我们可以将这个模块化的函数文件很轻松的移植到别的程序中。如果电路的管脚排列情况一样的话,我们甚至可以不用进行任何更改就能移植到别的程序中。移植的时候只需要一条简单的#include 指令就可以了。综合上述,模块化程序设计的优点是:各模块相对独立,功能单一,结构清晰,接口简单.控制了程序设计的复杂性.缩短了开发周期.避免程序开发的重复劳动.名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 7 页 -易于维护和功能扩充.3、
6、模块化程序设计的实现模块化程序的实现是:将具有相同功能的函数编译成一个此语言文件,然后再主程序中包含该文件,这样在主程序中就可以调用这个文件中的函数了。一般的做法是:将不同模块(如LCD1602,DS1302 等)都封装成一个文件,然后再主程序中包含这些文件。通常我们将一个模块的端口定义,初始化设置以及函数声明放在一个扩展名为“.h”的头文件中,而将具体的函数定义(函数体)放在一个扩展名为“.c”的 c 语言文件中。在编写主程序的时候,用预编译指令“#include”将“.h”文件包含到主程序中(就像我们调用编译器中的各种应用库文件一样,比如在WINA VR(GCC)中要调用I/O 定义头文件
7、是我们要使用“#include avr/io.h”这条指令将I/O 头文件包含到主程序中)。同时将“.c”文件添加到主程序所在的项目组中。这样做的好处是:我们在调用模块中的函数实现特定功能时,只需要在“.h”文件中查找到相关的函数即可,并不需要关心具体的函数体的内容。在下面的例子中我们将会看到“.h”文件文件中的内容比“.c”文件的内容要少很多。4、LCD1602 模块化设计的实现1)“lcd1602.h”头文件的编写首先我们将LCD1602 的管脚排列,以及实现LCD1602 显示的函数声明部分放在“lcd1602.h”文件中。在计算机里面新建一个文件夹(我们接下来建立的“.h”,”.c“以
8、及主程序文件都将保存在这个文件夹中),在 WINA VR(GCC)编译环境下,新建一个文件,编写LCD1602 的管脚排列以及函数声明等内容,然后将该文件保存,名称保存为“lcd1602.h”。“lcd1602.h”的内容如下:/*LCD1602.h 文件引脚连接mcu lcd GND VSS Vcc Vcc 偏压信号,接可调电阻 VEE PA4 RS PA5 R/W PA6 E PB0 D0 PB1 D1 PB2 D2 PB3 D3 PB4 D4 PB5 D5 PB6 D6 PB7 D7 Vcc VCC GND GND*/端口位定义#define RS PA4 /数据/命令控制端 0 命令,
9、1数据名师资料总结-精品资料欢迎下载-名师精心整理-第 2 页,共 7 页 -#define RW PA5 /读/写选择控制端 0 写,1读#define E PA6 /使能端下降沿读,高电平写/函数声明void LCDPort_Init(void);/液晶端口初始化void LCD_Init(void);/LCD初始化void Write_Com(unsigned char LCD_Com);/LCD写指令void Write_Data_Byte(unsigned char LCD_Data);/LCD写字节数据void Write_Place_xy(unsigned char x,unsi
10、gned char y);/指定当前写字符的位置void Write_Data_String(unsigned char x,unsigned char y,unsigned char*data);/在指定的(x,y)位置上写入字符串数据void Write_Data_Char(unsigned char x,unsigned char y,unsigned char data);/在指定的(x,y)位置上写入字符数据void Check_Busy(void);/读写检测函数,每次对液晶操作前都要进行读写检测2)“lcd1602.c”函数模块文件的编写在 WINA VR(GCC)编译环境下,新
11、建一个文件,编写要实现LCD1602 进行显示的各个函数的具体内容,然后将该文件保存在相同文件夹下面,名称保存为“lcd1602.c”。“lcd1602.h”的内容如下:/*LCD1602.c 文件*/#include#include#include LCD1602.h void LCDPort_Init()/LCD 数据端口设置PORTB=0X00;/DDRB=0XFF;/配置端口PB 全部为输出口,LCD 数据端口/LCD 控制端口设置PORTA&=(1 RS)|(1 RW)|(1 E);/DDRA|=(1 RS)|(1 RW)|(1 E);/DDRA|=(1 RS)|(1 RW)|(1
12、E);/配置端口PA 的第 4、5、6 为输出口/LCD 初始化void LCD_Init()Write_Com(0X01);/清屏_delay_ms(5);Write_Com(0X38);/显示模式设置16x2 显示,5x7 点阵,8 位数据接口_delay_ms(5);/Write_Com(0X0f);/显示开关控制,开显示,光标显示,光标闪烁Write_Com(0X0c);/显示开关控制,开显示,光标不显示,光标不闪烁名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 7 页 -_delay_ms(5);Write_Com(0X06);/光标设置,读或写一个字符后,地址指针加一
13、,光标加一,整屏不移动_delay_ms(5);/LCD 写指令void Write_Com(unsigned char LCD_Com)Check_Busy();PORTA&=(1 RS);/RS=0,写命令PORTA&=(1 RW);/RW=0,写指令PORTA|=(1 E);/E=1,写操作_delay_ms(5);PORTB=LCD_Com;/指令送数据端口PORTA&=(1 E);/E=0,停止写操作_delay_ms(5);/LCD 写字节数据void Write_Data_Byte(unsigned char LCD_Data)Check_Busy();PORTA|=(1 RS)
14、;/RS=1,写数据PORTA&=(1 RW);/RW=0,写指令PORTA|=(1 E);/E=1,写操作_delay_ms(5);PORTB=LCD_Data;/数据送数据端口PORTA&=(1 E);/E=0,停止写操作_delay_ms(5);/指定当前写字符的位置void Write_Place_xy(unsigned char x,unsigned char y)unsigned char address;if(y=0)/行位置 address=0 x80+x;/列位置,else address=0 x80+0 x40+x;/Write_Com(address);/写地址 名师资料
15、总结-精品资料欢迎下载-名师精心整理-第 4 页,共 7 页 -/在指定的(x,y)位置上写入字符串数据void Write_Data_String(unsigned char x,unsigned char y,unsigned char*data)Write_Place_xy(x,y);/写地址while(*data)Write_Data_Byte(*data);/写字符串data+;/指针地址加1 /在指定的(x,y)位置上写入字符数据x 列位置,y 行位置void Write_Data_Char(unsigned char x,unsigned char y,unsigned char
16、 data)Write_Place_xy(x,y);/Write_Data_Byte(data);/读写检测函数,每次对液晶操作前都要进行读写检测void Check_Busy()DDRB=0X00;/PB 口置为输入口,准备读取数据PORTA&=(1 RS);/RS=0,读命令PORTA|=(1 RW);/RW=1,读指令PORTA|=(1 E);/E=1,使能while(0X80&PINB);/监测忙信号,直到忙信号为0,才能进行读写操作PORTA&=(1 E);/E=0 DDRB=0XFF;/PB 口置为输出口,准备向端口发送数据 3)主程序文件的编写在 WINA VR(GCC)编译环境
17、下,新建一个文件,编写主程序的内容,在主程序的开始部分,使用预编译指令”#include”将我们刚才编写的LCD1602.h 文件包含进来:#include“LCD1602.h”,然后将该文件保存在相同文件夹下面,名称保存为“main.c”。4)makefile 文件的修改在 makefile 文件中找到如下图所示的位置,在 SRC=$(TARGET).c 后面空一格,然后加上我们所定义的“.c”文件的名字。5)编译整个文件完成上面的步骤后,我们直接在WINA VR(GCC)环境下按”F9“按键进行编译,编译成功后,将生成的”.hex“文名师资料总结-精品资料欢迎下载-名师精心整理-第 5 页
18、,共 7 页 -件烧入单片机即可。我们可以看到,现在我们的主程序显得十分“干净“。整个程序显得十分精炼。结构明晰,读起来十分方便。12.1.3 电路和连接本实例主要讲述的是模块化程序设计的方法,并且通过对LCD1602 液晶的程序进行模块化设计。所以本例中的电路依旧是LCD 与单片机的连接电路,这个电路我们在前面讲1602 液晶的时候已经描述过,在这里就不再重复说明了。12.1.4 程序设计1、程序功能程序的功能是完成讲LCD1602液晶的相关功能使用模块化程序设计的方法写到LCD1602.h 和“LCD1602.c“两个文件中,在编写主程序文件的时候,使用#include LCD1602.h
19、 这条指令将模块文件包含到主程序文件中,而在编译的时候,将“LCD1602.c“文件放到makefile 文件中一起编译。2 函数说明本程序包含三个模块文件LCD1602.h、“LCD1602.c“以及主程序文件。3、使用 WINA VR 开发环境,使用的是内部1M 的晶振,所以需要将makefile 文件中的时钟频率修改为内部1M 振荡器。另外在程序烧录到单片机的时候,熔丝位也要选择为内部1M 晶振(注意是内部RC 振荡器,一定不要选择错了,否则会导致单片机不能再烧写程序)。4、程序代码/*AVR 使用范例1602 液晶显示模块化设计*MCU:ATmega16*作者:maweili*编译器:
20、usbisp*2008.12.19*/头文件包含#include /io 端口寄存器配置文件,必须包含#include /GCC 中的延时函数头文件#include LCD1602.h/全局变量声明unsigned char LCD_DispBuff1=Lets study A VR;unsigned char LCD_DispBuff2=WWW.LT430.COM;int main(void)unsigned char Disp_Number;LCDPort_Init();/端口初始化LCD_Init();/LCD 初始化/*/在任意位置处显示字符Write_Com(0X80+0X05);/
21、发送字符显示位置指令。0 x05 可以换成00-0f,40-4f 之间的任意值Write_Data_Byte(a);/发送要显示的字符名师资料总结-精品资料欢迎下载-名师精心整理-第 6 页,共 7 页 -/下面这段程序演示在LCD 上显示两行字符Write_Com(0X80+0X00);/指定写数据的位置,从第一行第0 列开始for(Disp_Number=0;Disp_Number 15;Disp_Number+)Write_Data_Byte(LCD_DispBuff1Disp_Number);/写入要显示的数据_delay_ms(5);/一定要延时一会,因为液晶的速度比较慢 Write
22、_Com(0X80+0X40);/指定写数据的位置,第二行第0 列开始for(Disp_Number=0;Disp_Number 14;Disp_Number+)Write_Data_Byte(LCD_DispBuff2Disp_Number);/写入要显示的数据_delay_ms(5);*/下面这段程序演示在LCD 上显示两行字符,字符从屏幕右边移动到屏幕左边/*Write_Com(0X80+0X10);/指定写数据的位置,第一行第17 个字符开始for(Disp_Number=0;Disp_Number 15;Disp_Number+)Write_Data_Byte(LCD_DispBuf
23、f1Disp_Number);/写入要显示的数据_delay_ms(5);Write_Com(0X80+0X50);/指定写数据的位置,第二行第17 个字符开始for(Disp_Number=0;Disp_Number 14;Disp_Number+)Write_Data_Byte(LCD_DispBuff2Disp_Number);/写入要显示的数据_delay_ms(5);for(Disp_Number=0;Disp_Number 16;Disp_Number+)Write_Com(0X18);/发送数据移位指令,移动16 位_delay_ms(200);*/Write_Data_String(1,0,LCD1602 DEMO);/在第一行第1 列开始显示Write_Data_String(0,1,Welcome to here!);/在第二行第0 列开始显示Write_Data_Char(14,0,a);/在第一行,第14 列显示一个字符while(1)名师资料总结-精品资料欢迎下载-名师精心整理-第 7 页,共 7 页 -