单片机模块化编程方法

发布者:tmgouzi最新更新时间:2016-12-12 来源: eefocus关键字:单片机  模块化  编程方法 手机看文章 扫描二维码
随时随地手机看文章

  目前我们在学习和开发单片机时广泛采用 c 语言进行编程,当我们开发的单片机项目较小时,或者我们所写的练习程序很小时,我们总是习惯于将所有代码编写在同一个 c 文件下,由于程序代码量较少,通常为几十行或者上百行, 此时这种操作是可行方便的, 也没有什么问题。但如果要开发的项目较大,代码量上千行或者上万行甚至更大,如果你还继续将所有代码全部编写在仅有的一个 c 文件下, 这种方式的弊病会凸显出来, 它会给代码调试、更改及后期维护都会带来极大的不便。试想一下,当你尝试着从几千几万行代码中定位到某一位置或者去寻找某一错误点,上下拉动巨长的滚动条慢慢地、一点点地浏览整个 c 文件, 是件多么令人眼花缭乱,头昏脑胀的事。 模块化编程可解决这个问题,我们只要根据实际需要使用模块化编程的思维将具有不同功能的程序封装在不同模块中,将各个不同模块存放在不同的 c 文件中。 模块化编程后的程序不但使整体的程序功能结构清晰明了,同时也提高程序代码的利用率,有些模块代码我们可以直接进行移植或者经简单修改就可另作他用,好比封装好的函数。
  那么什么是模块化呢?首先我们来简单来聊聊模块概念,我们可能听说过电源模块,通信模块,这些是硬件模块,它们都提供一些接口,譬如电源模块会有输出额定电压电流的接口,通信模块可能提供了 RS232、 USB 等接口。那么对软件来说模块是怎样的呢?软件里的模块跟硬件模块类似,抽象地说就像一个黑盒子,盒子内部细节我们可以不予理会,我们只关心盒子给我们提供什么东西,即提供了什么接口,利用这些接口我们能实现什么功能。
我们把相对独立,具有独立功能用代码编写在一个 c 文件下,把需要对外的函数或变量进行声明供外部使用, 把不需要的细节尽可能对外部屏蔽起来,这就是软件模块化编程的思维。
这样不同的模块占用不同 c 文件,一个个 c 文件将整个项目串接起来实现所有的功能。

  1. 模块化编程的原则:
模块化编程通常要遵循以下几个原则:
   每一个 c 文件对应一个同名的 h 头文件
  一个 h 文件伴随相应 c 文件存在, 头文件是为了声明对外公开的接口。如果一个 c 文件不需要对外公布任何接口,则其就不应当存在,除非它是程序的入口,如 main 函数所在的文件,同时 main 函数所在文件可以没有对应的头文件。 如有一个 led.c,那应该同时制作一个 led.h 头文件。
   头文件中适合放置接口的声明,不适合放置实现

  头文件是模块的对外的接口,供外部程序调用。头文件中应放置对外部的声明,如对外提供的函数声明、宏定义、变量类型声明等。 函数的实现、变量的赋值、语句的操作等决不能放在头文件中。因为头文件的功能是向外提供接口,譬如函数,变量,具体如何实现是在 c 文件中进行,头文件仅是进行了描述声明。
   任意一个 c 文件只要使用了其他 c 文件提供的接口, 都要同时包含其对应的头文件,每个 c 文件应该头文件自包含

  任意一个 c 文件只要使用了其他 c 文件提供的接口, 都要将其对应的头文件包含到该 c文件中,没有使用到其他 c 文件的接口就不应该将其匹配的头文件包含,并且每个 c 文件都应该包含自己的头文件。
   防止头文件被重复包含
  避免一个头文件被重复包含,通常使用条件编译命令#ifndef--#endif,如下示例:
示例 1:

#ifndef TIME_H#define TIME_H……#endif

 


示例 2:

#ifndef LED_H#define LED_H……#endif

 

  其中#define FILENAME_H 为基本格式, FILENAME_H 为头文件名称,但要全部使用大写形式,使用单下划线后紧跟一个 H 表明是头文件。 不要在宏名最前面加上“ _"或“ __” ,即使用 FILENAME_H 代替_FILENAME_H_, 因为一般以"_"和” __"开头的标识符为系统保留或者标准库使用。

2. 模块化编程实例
  我们使用 AT89C52 单片机,在编程软件 keil 环境下实施一个工程,来说明模块化编程具体操作的方法和步骤。例子要实现的功能:和 P1 相连的 8 个 LED 灯每 500ms 亮灭交替闪烁,通过串口将数字 0-9 发送给单片机并显示在一个数码管上。 LED 闪烁的时间使用定时器 0 中断方式来控制, T0 每 50ms 溢出产生中断,定义一个计数器,每次 T0 中断就计数一次,累计计数 10 次,那么时长为 500ms,作为 LED 闪烁时间间隔。 单片机的时钟为11.0592MHz。那么使用模块化编程的方法, 整个项目将会有如下表中的文件。
表 1 工程文件清单

C 文件H 头文件 描述
main.cMain 文件可以没有对应的头文件
timer0.ctimer0.h定时器 0 定时 50ms 中断
led.c led.hLed 闪烁实现
uart.cuart.h串口通信配置实现
digitron.cdigitron.h数码管显示

 

  2.1 创建工程步骤
  2.1.1 新建工程文件目录
  新建工程文件目录(如 test),在工程目录下创建 Project、 Source、 Output、 Listing 和Readme 这 5 个文件夹,并在文件夹 Readme 下创建 Readme.txt 文件。 这样做的目的是为
了增强工程文件的可读性及结构化,便于维护和管理。
 Project 存放工程文件
 Source 存放用户编写的 c 文件及 h 头文件
 Output 存放各种输出文件,如 hex 文件
 Listing 存放编译过程中产生的各种中间文件
 Readme 存放工程项目的说明文件
  2.1.2 创建 keil 工程
( 1) 启动软件 Keil μVision, 点击工具栏上的 Project,选择 NewμVision Project,新建 test工程到目录 Project 下。

( 2) 选择目标器件,点击“ OK”确认。
( 3) 出现是否添加启动文件到工程中对话框,选择否。
( 4) 目标选项设置, 点击 target option 工具进入选项配置界面。
( 5) 选中 Target 项,根据实际情况设置晶振频率。本例子频率为 11.0592MHz。
( 6) 选中 output 选项,点击“ Select Folder for Objects…”选择工程目录下的 Output 文件夹, 将“ Create HEX File”勾选中。
( 7) 选中 Listing 选项,点击“ Select Folder for Listings…”选择工程目录下的 Listing 文件夹。
( 8) 最后点击“ OK”保存各选项的设置,至此完成 Target Option 的配置。
( 9)新建 5 个文件,以 c 为后缀名保存到工程目录的 Source 文件下,并分别命名为 main.c、led.c、 uart.c、 timer0.c 和 digitron.c。右击“ Source Group1”选择“ Add Existing Flies to Group‘Source Group1’”,将以上新建的 5 个文件添加到工程中。
(10) 至此,就完成整个 keil 工程的创建。
( 11) 工程 test 目录文件结构

  3. 程序代码编写
  3.1 LED 闪烁文件
  首先编写 led.c 文件代码,主要内容就是 LED 闪烁实现的函数,由于使用到 timer0.c 中TimeCounter 变量,故需要包含 timer0.h 头文件。其程序代码如下:

复制代码

#include #include "led.h"#include "timer0.h"void Light_LED(void)
{    static unsigned char TickFlag = 0;    if (TimeCounter == 0) //判断 500ms 定时是否到    {
        TickFlag = 1;
        TimeCounter = 10;
    }    if (TickFlag == 1) // 每间隔 500ms P1 口取反    {
        P1 = ~P1;
        TickFlag = 0;
    }
}

复制代码

 


这个文件中,仅有 Light_LED()函数提供给外部使用,故在 led.h 仅声明该函数即可,声明时要使用关键字 extern 表明该函数可供外部模块使用, led.h 头文件内容如下:

#ifndef LED_H#define LED_H    extern void Light_LED(void);#endif

 


3.2 定时器 0 中断文件
定时器 0 文件 timer0.c 代码如下:

复制代码

#include #include "timer0.h"unsigned char TimeCounter = 10;void Timer0Config(void)
{
    TMOD |= 0x01; //定时器 0 工作在方式 2,即 16 位计数器
    TH0 = 0x3C;
    TL0 = 0xB0; //定时器 0 重赋初值
    ET0 = 1; //使能定时器 0 中断
    TR0 = 1; //开启定时器 0}void timer0(void) interrupt 1 //定时器 0 中断函数{
    TH0 = 0x3C;
    TL0 = 0xB0; //定时器 0 重赋初值
    if (TimeCounter != 0)
    {
        TimeCounter--;
    }
}

复制代码

 


其对应的头文要向 led.c 文件提供 TimeCounter 变量,向 main.c 提 Timer0Config 函数,因此要在头文件中进行声明,具体内容如下:

复制代码

#ifndef TIMR0_H#define TIMR0_H    extern unsigned char TimeCounter;    extern void Timer0Config(void);#endif

复制代码

 


3.3 串口通信文件
串口配置及中断服务程序 uart.c 文件,该文件的代码如下:

复制代码

#include #include "uart.h"unsigned char tem = 0;void UartConfig(void)
{
    SCON = 0x50; //串口工作在方式 1,即 10 位收发器模式
    PCON = 0x00; //波特率不增倍
    TMOD |= 0x20; //定时器 1 做波特率发生器,工作在方式 2 即 8 位自动重装初值模式
    TH1 = 0xFD; //定器 1 赋初值
    TL1 = TH1;
    ES = 1; //使能串口中断
    TR1 = 1; //开启定时器 1}void InterruptUart(void) interrupt 4 //串口中断服务函数{    if (RI == 1)
    {
        RI = 0;
        tem = SBUF;
    }
}

复制代码

 


uart.h 头文件向 mian.c 提供变量 tem 和函数 UartConfig(),故要进行声明, 内容如下:

复制代码

#ifndef UART_H#define UART_H    extern void UartConfig(void);    extern unsigned char tem;#endif

复制代码

 


3.4 数码管显示文件
数码管显示模块文件 digitron.c 代码如下:

复制代码

#include #include "digitron.h"unsigned char code table[16] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
                   0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};void DigiShow(unsigned char num)
{
    P2 = 0xFE; //打开第一个数码管的位码
    P0 = table[num]; //将串口发送过来的数字送到数码上显示}

复制代码

 


文件 main.c 调用该文件的 DigiShow()函数,因此 digitron.h 头文件要声明这个函数,
其头文件内容如下:

#ifndef DIGITRON_H#define DIGITRON_H    extern void DigiShow(unsigned char num);#endif

 


3.5 主函数所在文件
mian.c 文件是程序代码的入口文件,并且调用其他模块接口,实现所有功能的核心文件, 故需要包含器其他 4 个头文件,其代码如下:

复制代码

#include #include "led.h"#include "uart.h"#include "timer0.h"#include "digitron.h"void main(void)
{
    Timer0Config(); //初始化定时器 0
    UartConfig(); //初始化串口,设置波特率为 9600
    EA = 1; //使能全局中断
    while (1)
    {
        Light_LED();
        DigiShow(tem);
    }
}


关键字:单片机  模块化  编程方法 引用地址:单片机模块化编程方法

上一篇:智能硬件开发如何选择低功耗MCU
下一篇:MSP430F149模拟IIC读写24C02程序

推荐阅读最新更新时间:2024-03-16 15:24

初学89c52单片机的心得
51单片机是学习单片机的入门基础,编程的软件是keil,51开发板的下载器是stc-isp。 软件安装: keil最好安装在c盘,下载器很可能安装不了,需要在32位机上安装,需要多试几次。 编程注意事项: 每次编程时,最好建立单独的文件夹,便于管理和以后运用 1,打开keil,点击new project弹出对话框,写上文件名,点击保存,弹出另一对话框 选中atmel中at89c52 点击ok 然后点击yes 2,点击new file 然后保存(save)在弹出对话框中文件名写上xxx。c 然后保存 3,在project中target的source group 点击鼠标反键 点击add 选中上面保存的文件添加,然后可以
[单片机]
51单片机软件抗干扰的技术误区
单片机 中存在这样一种广泛流传的误解:在MCS-51系列单片机中,只要用指令使程序从起始地址开始执行,就可以复位单片机,摆脱干扰。通过一个简单的实验,揭示了软件复位的可靠方法。   有的单片机(如8098)有专门的复位指令,某些增强型MCS-51系统单片机虽然没有复位指令,但片内集成了WATCHDOG电路,故抗干扰也不成问题。而普及型MCS-51系列单片机(如8031和8032)既然无复位指令,又不带硬件WATCHDOS,如果没有外接硬件WATCHDOG电路,就必须采用软件抗干扰技术。常用的软件抗干扰技术有:软件陷阱、指令冗余、软件WATCHDOG等,它们的作用是在系统受干扰时能及时发现,再用软件的方法使系统复位。所谓软件复位
[单片机]
基于C8051F040单片机的温度遥测遥控系统
  0 引言   温度遥控遥测是远程实现对温度的测量与控制,特别适合那些环境恶劣,测量人员不容易接近的场合,近年来在工农业生产中应用广泛。根据遥控遥测系统的特点,提出了对水温遥控遥测的设计方案。   1 总体方案设计   温度遥控遥测系统主要由微处理器系统、测温模块、加热模块、通信模块、液位模块以及上位机软件等组成。系统构成如图1所示。   测温模块测量液体温度后,把温度数值发送给微处理器,当温度变化达到一定值后,加热模块开始加热,首先可以在设定的时间内,加热到设定的温度并稳定在该温度一段时间。系统还可以按照设计好的各温度节点进行分段折线加热,精度很高。测液位模块实时测量液体液位,并传给微处理器。系统通过两个通信模块实
[单片机]
STM32的各个MCU性能评判
1写在前面 我们经常听见某手机发布会,安兔兔跑分多少多少,其实这个跑分就是体现手机性能的一个指标。我们使用STM32开发产品,选择MCU时,一般会综合考虑MCU各方面性能。对于MCU来说,一个重要指标是功耗,还有一个重要指标就是性能。 当我们进入ST官方微控制器网页,就会发现ST将MCU划分为不同层次,如下图: https://www.st.com/en/microcontrollers.html 从上面层次关系,大致可以了解ST各MCU的功耗和性能。 2 STM32 CoreMark跑分 STM32家族的32位微控制器基于ARM Cortex®-M处理器设计,为用户提供新的自由度。 产品集高性能、实时性、数字信号处理
[单片机]
STM32的各个<font color='red'>MCU</font>性能评判
51单片机8*8点阵LED的显示原理及程序设计
在8X8点阵LED上显示柱形,让其先从左往右平滑移动三次,其次从右往左平滑移动三次,再次从上往下平滑移动三次,最后从下往上平滑移动三次,如此循环。 1. 程序设计内容 8X8点阵LED工作原理说明 :8X8点阵共需要64个发光二极管组成,且每个发光二极管是放置在行线和列线的交叉点上,当对应的某一列置1电平,某一行置0电平,则相应的二极管就亮;因此要实现一根柱形的亮法,如图49所示,对应的一列为一根竖柱,或者对应的一行为一根横柱,因此实现柱的亮的方法如下所述: 一根竖柱:对应的列置1,而行则采用扫描的方法来实现。 一根横柱:对应的行置0,而列则采用扫描的方法来实现。 2. 硬件电路 3.汇编源程
[单片机]
51<font color='red'>单片机</font>8*8点阵LED的显示原理及程序设计
单片机外中断使用方面的问题及其解决方法
在长期使用51单片机的过程中,我们发现单片机在工业生产现场等干扰较强的环境工作时,有时确已产生定义为下降沿触发方式的外中断,然而用仪器测量已经送入单片机的INTX引脚时,单片机却不能够正常响应中断。在排除了可能由程序导致的问题之后,再检查CPU的当前状态,发现中断级别、中断允许、中断触发方式也都表明应该响应中断。后经分析及测试,发现是单片机INTX引脚内部端口锁存器被置为零所致。当端口被封锁为零时,从该引脚引入的下降沿表示的中断申请将丢失,中断当然不被响应。由于程序中并未使用可将端口锁存器置为零的指令,且这种情况的产生具有一定的随机性,并非经常发生,所以认为这种现象可能是因电路干扰或噪声造成锁存器误动作导致的。同时我们还认为这种误
[单片机]
<font color='red'>单片机</font>外中断使用方面的问题及其解决<font color='red'>方法</font>
51 单片机 C 语言:不规则的矩阵键盘
单片机的 P4.0,P2.0--P2.6 这几个 IO 上面接一个 4*4 的键盘。 这里P4.0是最低位,P2.6是最高位。 一个按键接在同一个端口上 很好操作的,但是这些 IO 在两个端口上面。 这些端口不连续应该咋写键盘程序 ? //定义键盘端口: sbit HH1 = P4^0; sbit HH2 = P2^0; sbit HH3 = P2^1; sbit HH4 = P2^2; sbit LL1 = P2^3; sbit LL2 = P2^4; sbit LL3 = P2^5; sbit LL4 = P2^6; char scan_key(void) //行列扫描 { HH1 = 0;
[单片机]
51单片机控制步进电机硬件连接部分
1、概要: 本案例讲解的内容是51单片机控制步进电机硬件连接部分。后续会分别讲解单片机程序,S曲线加减速方法,上位机等相关内容 2、功能原理图: 2.1、51单片机: ①输出脉冲到TB6600驱动器PUL端口,从而控制步进电机转动 ②控制TB6600驱动器ENA端口,从而控制步进电机使能 ③控制TB6600驱动器DIR端口,从而控制步进电机转动方向 2.2、步进电机: ①提供机械动力 2.3、稳压电源: ①为步进电机提供电源 2.4、TB6600驱动器: ①二相四线步进电机专用驱动器 3、实物图: ** ①** 、51****单片机控制板一个 ** ②** 、二相四线步进电机一个 ** ③** 、稳压电源一个 ** ④** 、
[单片机]
51<font color='red'>单片机</font>控制步进电机硬件连接部分
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved