没有合适的资源?快使用搜索试试~ 我知道了~
c语言嵌入式系统编程修炼之道.pdf
需积分: 50 19 下载量 9 浏览量
2007-10-19
22:31:49
上传
评论 1
收藏 446KB PDF 举报
温馨提示
试读
35页
c语言嵌入式系统编程修炼之道.pdf
资源详情
资源评论
资源推荐
C 语言嵌入式系统编程修炼之道
作者: 宋宝华
出处: 天极网
责任编辑: 方舟
不同于一般形式的软件编程,嵌入式系统编程建立在特定的硬件平台上,势必要求其编程语
言具备较强的硬件直接操作能力。无疑,
汇编语言具备这样的特质。但是,归因于汇编语言
开发过程的复杂性,它并不是
嵌入式系统开发的一般选择。而与之相比,C语言--一种"高级
的低级"语言,则成为嵌入式系统开发的最佳选择。笔者在嵌入式系统项目的开发过程中,
一次又一次感受到
C语言的精妙,沉醉于C语言给嵌入式开发带来的便利。
图1 给出了本文的讨论所基于的硬件平台,实际上,这也是大多数嵌入式系统的硬件平
台。它包括两部分:
(1) 以通用处理器为中心的协议处理模块,用于网络控制协议的处理;
(2) 以数字信号处理器(DSP)为中心的信号处理模块,用于调制、解调和数/模信
号转换。
本文的讨论主要围绕以通用处理器为中心的协议处理模块进行,因为它更多地牵涉到具
体的
C语言编程技巧。而DSP编程则重点关注具体的数字信号处理算法,主要涉及通信领域
的知识,不是本文的讨论重点。
着眼于讨论普遍的嵌入式系统C编程技巧,系统的协议处理模块没有选择特别的CPU,
而是选择了众所周知的CPU芯片--80186,每一位学习过《微机
原理》的读者都应该对此芯
片有一个基本的认识,且对其指令集比较熟悉。80186 的字长是 16 位,可以
寻址到的内存
空间为 1MB,只有实地址模式。C语言编译生成的指针为 32 位(双字),高 16 位为段地址,
低 16 位为段内编译,一段最多 64KB。
图 1 系统硬件架构
协议处理模块中的FLASH和RAM几乎是每个嵌入式系统的必备设备,前者用于存储程
序,后者则是程序运行时指令及数据的存放位置。系统所选择的FLASH和RAM的位宽都为
16 位,与CPU一致。
实时钟芯片可以为系统定时,给出当前的年、月、日及具体时间(小时、分、秒及毫秒),
可以设定其经过一段时间即向CPU提出中断或设定报警时间到来时向CPU提出中断(类似
闹
钟功能)。
NVRAM(非易失去性RAM)具有掉电不丢失数据的特性,可以用于保存系统的设置信
息,譬如网络协议参数等。在系统掉电或重新启动后,仍然可以读取先前的设置信息。其位
宽为 8 位,比CPU字长小。文章特意选择一个与CPU字长不一致的存储芯片,为后文中一节
的讨论创造条件。
UART则完成CPU并行数据传输与RS-232 串行数据传输的转换,它可以在接收到
[1~
MAX_BUFFER]字节后向CPU提出中断,MAX_BUFFER为UART芯片存储接收到字节的
最大缓冲区。
键盘控制器和显示控制器则完成系统人机界面的控制。
以上提供的是一个较完备的嵌入式系统硬件架构,实际的系统可能包含更少的外设。之
所以选择一个完备的系统,是为了后文更全面的讨论嵌入式系统C语言编程技巧的方方面
面,所有设备都会成为后文的分析目标。
嵌入式系统需要良好的软件开发环境的支持,由于嵌入式系统的目标机资源受限,不可
能在其上建立庞大、复杂的开发环境,因而其开发环境和目标运行环境相互分离。因此,
嵌
入式应用软件的开发方式一般是,在宿主机(Host)上建立开发环境,进行应用程序编码和交
叉编译,然后宿主机同目标机(Target)建立连接,将应用程序下载到目标机上进行交叉调试,
经过调试和优化,最后将应用程序固化到目标机中实际运行。
CAD-UL是适用于x86 处理器的嵌入式应用软件开发环境,它运行在Windows操作系统
之上,可生成x86 处理器的目标代码并通过PC机的COM口(RS-232串口)或以太网口下载
到目标机上运行,如图 2。其驻留于目标机
FLASH存储器中的monitor程序可以监控宿主机
Windows调试平台上的用户调试指令,获取CPU寄存器的值及目标机存储空间、I/O空间的
内容。
图 2 交叉开发环境
后续章节将从
软件架构、内存操作、屏幕操作、键盘操作、性能优化等多方面阐述C语
言嵌入式系统的编程技巧。软件架构是一个宏观概念,与具体硬件的联系不大;内存操作主
要涉及系统中的FLASH、RAM和NVRAM芯片;屏幕操作则涉及显示控制器和实时钟;键
盘操作主要涉及键盘控制器;性能优化则给出一些具体的减小程序时间、空间消耗的技巧。
在我们的修炼旅途中将经过 25 个关口,这些关口主分为两类,一类是技巧型,有很强
的适用性;一类则是常识型,在理论上有些意义。
C语言嵌入式系统编程修炼之软件架构篇
模块划分
模块划分的"划"是规划的意思,意指怎样合理的将一个很大的软件划分为一系列功能独
立的部分合作完成系统的需求。C语言作为一种结构化的程序设计语言,在模块的划分上主
要依据功能(依功能进行划分在面向对象设计中成为一个错误,牛顿定律遇到了相对论),
C语言模块化程序设计需理解如下概念:
(1) 模块即是一个.c文件和一个.h文件的结合,头文件(.h)中是对于该模块接口的声明;
(2) 某模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字
声明;
(3) 模块内的函数和全局变量需在.c文件开头冠以static关键字声明;
(4) 永远不要在.h文件中定义变量!定义变量和声明变量的区别在于定义会产生内存
分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模
块寻找外部函数和变量。如:
/*module1.h*/
int a = 5; /* 在模块 1 的.h文件中定义int a */
/*module1 .c*/
#include "module1.h" /* 在模块 1 中包含模块 1 的.h文件 */
/*module2 .c*/
#include "module1.h" /* 在模块 2 中包含模块 1 的.h文件 */
/*module3 .c*/
#include "module1.h" /* 在模块 3 中包含模块 1 的.h文件 */
以上程序的结果是在模块 1、2、3 中都定义了整型变量 a,a 在不同的模块中对应不同
的地址单元,这个世界上从来不需要这样的程序。正确的做法是:
/*module1.h*/
extern int a; /* 在模块 1 的.h 文件中声明 int a */
/*module1 .c*/
#include "module1.h" /* 在模块 1 中包含模块 1 的.h 文件 */
int a = 5; /* 在模块 1 的.c 文件中定义 int a */
/*module2 .c*/
#include "module1.h" /* 在模块 2 中包含模块 1 的.h 文件 */
/*module3 .c*/
#include "module1.h" /* 在模块 3 中包含模块 1 的.h 文件 */
这样如果模块 1、2、3 操作a的话,对应的是同一片内存单元。
一个嵌入式系统通常包括两类模块:
(1)硬件驱动模块,一种特定硬件对应一个模块;
(2)软件功能模块,其模块的划分应满足低偶合、高内聚的要求。
多任务还是单任务
所谓"单任务系统"是指该系统不能支持多任务并发操作,宏观串行地执行一个任务。而
多任务系统则可以宏观并行(微观上可能串行)地"同时"执行多个任务。
多任务的并发执行通常依赖于一个多任务操作系统(OS),多任务OS的核心是系统调
度器,它使用任务控制块(TCB)来管理任务调度功能。TCB包括任务的当前状态、优先级、
要等待的事件或资源、任务程序码的起始地址、初始堆栈指针等信息。调度器在任务被激活
时,要用到这些信息。此外,TCB还被用来存放任务的"上下文"(context)。任务的上下文
就是当一个执行中的任务被停止时,所要保存的所有信息。通常,上下文就是计算机当前的
状态,也即各个寄存器的内容。当发生任务切换时,当前运行的任务的上下文被存入TCB,
并将要被执行的任务的上下文从它的TCB中取出,放入各个寄存器中。
嵌入式多任务OS的典型例子有Vxworks、ucLinux等。嵌入式OS并非遥不可及的神坛之
物,我们可以用不到 1000 行代码实现一个针对 80186 处理器的功能最简单的OS内核,作者
正准备进行此项工作,希望能将心得贡献给大家。
究竟选择多任务还是单任务方式,依赖于软件的体系是否庞大。例如,绝大多数手机程
序都是多任务的,但也有一些小灵通的协议栈是单任务的,没有操作系统,它们的主程序轮
流调用各个软件模块的处理程序,模拟多任务环境。
单任务程序典型架构
(1)从CPU复位时的指定地址开始执行;
(2)跳转至汇编代码startup处执行;
(3)跳转至用户主程序main执行,在main中完成:
a.初试化各硬件设备;
b.初始化各软件模块;
c.进入死循环(无限循环),调用各模块的处理函数
用户主程序和各模块的处理函数都以C语言完成。用户主程序最后都进入了一个死循
环,其首选方案是:
剩余34页未读,继续阅读
boyzeng
- 粉丝: 4
- 资源: 27
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0