嵌入式 Linux C语言(十一)——C语言模块化编程
一、C语言模块化编程
所谓模块化编程,就是指一个程序包含多个源文件(.c 文件和 .h 文件),每个模块即是一个.c文件和一个.h文件的结合,头文件(.h)中是对于该模块接口的声明。C语言模块化编程中对.c、.h文件的潜规则:
1、.c 文件主要负责实现,也就是定义函数;.h 文件主要负责声明,比如函数声明、宏定义等,结构的定义、自定义数据类型一般也放在头文件中,不能在.h文件中定义变量。将一个功能模块的代码单独编写成一个.c文件,然后把该模块的接口函数放在.h文件中。
定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。
2、引入编译器自带的头文件(包括标准头文件)用尖括号,引入自定义头文件用双引号,例如:
#include <stdio.h>
#include "myFile.h"
头文件应当是幂等的。也就是说,多次包括头文件的效果和仅包括一次的效果完全相同。
#ifndef _STDIO_H
#define _STDIO_H
/* 声明部分 */
#endif
3、模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字声明;模块内的函数和全局变量需在.c文件开头冠以static关键字声明。
二、嵌入式系统开发中的模块化编程
嵌入式系统通常包括两类模块:
(1)硬件驱动模块,一种特定硬件对应一个模块。
(2)软件功能模块,其模块的划分应满足低偶合、高内聚的要求。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差;模块内各元素(语名之间、程序段之间)联系的越紧密,内聚性就越高。
1、单任务系统
所谓"单任务系统"是指该系统不能支持多任务并发操作,宏观串行地执行一个任务。而多任务系统则可以宏观并行(微观上可能串行)地"同时"执行多个任务。
单任务程序典型架构
(1)从CPU复位时的指定地址开始执行;
(2)跳转至汇编代码startup处执行;
(3)跳转至用户主程序main执行,在main中完成:
a.初试化各硬件设备;
b.初始化各软件模块;
c.进入死循环(无限循环),调用各模块的处理函数
用户主程序和各模块的处理函数都以C语言完成。用户主程序最后都进入了一个死循环,其首选方案是:
while(1)
{
}
2、多任务系统
多任务的并发执行通常依赖于一个多任务操作系统(OS),多任务OS的核心是系统调度器,它使用任务控制块(TCB)来管理任务调度功能。TCB包括任 务的当前状态、优先级、要等待的事件或资源、任务程序码的起始地址、初始堆栈指针等信息。调度器在任务被激活时,要用到这些信息。此外,TCB还被用来存 放任务的"上下文"(context)。任务的上下文就是当一个执行中的任务被停止时,所要保存的所有信息。通常,上下文就是计算机当前的状态,也即各个寄存器的内容。当发生任务切换时,当前运行的任务的上下文被存入TCB,并将要被执行的任务的上下文从它的TCB中取出,放入各个寄存器中。
究竟选择多任务还是单任务方式,依赖于软件的体系是否庞大。例如,绝大多数手机程序都是多任务的,但也有一些小灵通的协议栈是单任务的,没有操作系统,它们的主程序轮流调用各个软件模块的处理程序,模拟多任务环境。
3、中断服务程序
中断是嵌入式系统中重要的组成部分,但是在标准C中不包含中断。许多编译开发商在标准C上增加了对中断的支持,提供新的关键字用于标示中断服务程序(ISR),类似于__interrupt、#program interrupt等。当一个函数被定义为ISR的时候,编译器会自动为该函数增加中断服务程序所需要的中断现场入栈和出栈代码。
中断服务程序需要满足如下要求:
(1)不能返回值;
(2)不能向ISR传递参数;
(3) ISR应该尽可能的短小精悍;
(4) printf(char * lpFormatString,…)函数会带来重入和性能问题,不能在ISR中采用。
4、硬件驱动模块
一个硬件驱动模块通常应包括如下函数:
(1)中断服务程序ISR
(2)硬件初始化
A、修改寄存器,设置硬件参数(如UART应设置其波特率,AD/DA设备应设置其采样速率等);
B、将中断服务程序入口地址写入中断向量表:
/* 设置中断向量表 */
m_myPtr = make_far_pointer(0l); /* 返回void far型指针void far * */
m_myPtr += ITYPE_UART; /* ITYPE_UART: uart中断服务程序 */
/* 相对于中断向量表首地址的偏移 */
*m_myPtr = &UART _Isr; /* UART _Isr:UART的中断服务程序 */
(3)设置CPU针对该硬件的控制线
A、如果控制线可作PIO(可编程I/O)和控制信号用,则设置CPU内部对应寄存器使其作为控制信号;
B、设置CPU内部的针对该设备的中断屏蔽位,设置中断方式(电平触发还是边缘触发)。
(4)提供一系列针对该设备的操作接口函数。例如,对于LCD,其驱动模块应提供绘制像素、画线、绘制矩阵、显示字符点阵等函数;而对于实时钟,其驱动模块则需提供获取时间、设置时间等函数。
本文介绍了C语言模块编程的主要思想,嵌入式系统开发中模块划分、多任务还是单任务选取、单任务程序典型架构、中断服务程序、硬件驱动模块设计等。
本文是学习网络博文并经自己思考总结整理而来,博文来源有:
C语言中文网、C语言模块化程序设计(EDN电子技术设计 wnhb)、嵌入式C语言之---模块化编程(CSDN zhzht19861011)。
由于网络博文繁杂,无法一一查明原出处,所列来源为本人学习时所查阅资料。