1、版权所有,转载必须注明出处,注明:来自 字样。第 1 页 共 409 页 关于本书 这是一本工程师与工程师之间交流经验的书,本书的作者是一位嵌入式系统工程师,毕业以来,一直从事嵌入式系统开发,从未中断。本书中,作者将和广大读者共享其多年积累的经验。 当你捧起这本书,笔者为此感到荣幸,为有机会和你交流而兴奋,在工程技术领域,交流是提高的重要途径,希望能从你获得有益的建议。本书有经验的总结,也提出了一些自己的观点,就像上帝不会造出两张一摸一样的脸一样,也不可能有两个观点一摸一样的人,笔者不强求你认同本书的所有观点,但至少希望能拓展你的视野。如果你对本书涉及的问题有不同的看法,请务必给我写信,em
2、ail:lstpr 。 设计可靠的产品和可移植的技术是本书的两大主题。 怎样设计可靠的产品?笔者多年的职业生涯中, 涉及研制的都是电力和电信行业相关的工业类产品,这种产品的特点是可靠性要求高,使用时效长。它不像消费类民用产品,更新淘汰得很快,在使用中,即使出现故障,也不会造成严重的损失,顶多是看不成电视,听不到音响而已,维修时也可以随时关闭电源、开机拆解,而且维修也是以更换部件为主,而不会深入分析故障原因。而许多工业监控类产品需要长期不间断运行,出现故障即有可能造成严重损失,设计中要防止局部故障导致严重损失。这些产品的服务期很长,动辄 5 年、8年,甚至超过 10 年也很常见。在这么长的运行期
3、中,生产厂家需要分析每一起故障,分析可能存在的隐患,不但要改进新产品,还要升级正在服役的产品,防止出现新的故障。更有甚者,在维修过程中可能还不能关闭电源,不能退出运行。从而使笔者能够长时间跟踪产品,充分暴露、发现隐含的问题,从中总结出宝贵的经验,笔者最近一年的主要工作职责之一,就是产品失效分析,分析产品的每一起售前或者售后故障,找出改进点。 设计可移植的技术,是本书的又一主题。由于工业现场条件复杂多变,使得工业类嵌入式产品的衍生型号很多,如何使产品能够适应不同的环境,或者使技术在不同产品间具有最大的可移植性,是一门艺术。有人说, “我的软件是用 C 语言写的,是可移植的。 ”这句话看起来很有道
4、理,实际上谬之大矣!等于是说: “我用钢笔写字,我的书法具有可移植性。 ”灵活的系统结构,一致的模块接口,清晰的软硬件脉络,才是可移植的关键。也有人认为移植只是换个平台这么简单,其实能满足不同平台只是可移植软件的最低要求,可移植的软件最重要的是在当用户的需求发生变化时,能够迅速调整,重新组合,满足用户的需求,即使软件有较大的规模,船大也要好掉头才行。 要完成可靠性和可移植性目标, 不是某一个模块性能优越能达到的, 最优化设计的模块,不一定能组装出一个整体性能优越的产品; 而整体性能优越的产品, 其局部模块可能很粗糙,这是一个系统工程的。 本书以笔者自己开发的 djyos 操作系统为范例讲述产品
5、设计的系统性思想,djyos 是一个软件,但本书并不是一本软件教科书,djyos 专门为软硬件综合进行系统架构优化提供了支持。 计算机技术发展到现在, 已经有很多技术积累, 开发一个复杂的嵌入式产品, 要懂得 “拿来主义” ,不要完全从 0 开发全部软件和硬件。但是,怎样拿来呢?开发高可靠的产品,对使用外来代码会非常谨慎,甚至只使用有商业支持的外来代码。任何外来代码,都要经过通读、仔细分析,完全测试过以后才允许使用,有商业支持的代码,供应商会提供支持,这个过程进行得就快些;而没有商业支持的所谓开源代码,这些代码甚至没有完整的注释,读懂这样的代码,所花费的时间甚至不会比自己开发少多少。所以,高可
6、靠产品的“拿来主义” ,主要靠继承企业自身积累的软件模块,来降低开发工作量。要使新产品开发时有东西可以拿来用,企业产品开发过程只就要非常注重“可拿去性” ,一句话,不拿去,何以拿来,而可拿去性,实质上就是可移植性!djyos 系统支持下,在系统设计时强调“可拿来性” ,即系版权所有,转载必须注明出处,注明:来自 字样。第 2 页 共 409 页 统融合现成的软件模块的能力;在模块设计方面,强调“可拿去性” ,即模块应该能适应不同的运行环境。djyos 系统融合现成模块的能力,重点不在于支持并发运行,而在于支持程序设计者组合现有模块成为一个新产品,以及设计可组合模块的能力。 可靠性和可移植性并
7、不是孤立的指标,他们是互相促进的。可移植性是可靠性的基石,产品的可靠性来自于 3个方面: 1 正确设计以减少缺陷。 2 充分测试以修正缺陷。 3 充分使用中暴露缺陷。 开发工作是一个创新性很强的工作, 没有一个设计师敢声称自己设计的产品是没有缺陷的,即使有人这样说,也不会有人相信。同样,没有一个测试师敢声称能找出所有 bug。反复使用是暴露 bug 的一个重要手段, 同时也是暴露设计缺陷的手段。 只有可移植性强的模块,才有机会在不同的产品、不同的使用环境中反复使用。 “拿来”一个可移植的、经过反复使用考验的模块,显然是设计高可靠性产品的最好的保证。 版权所有,转载必须注明出处,注明:来自 字
8、样。第 3 页 共 409 页 目录 关于本书 1 目录 3 第 1 章 设计嵌入式产品 .11 1.1 嵌入式系统的特点 .12 1.1.1 模拟电路和数字系统紧密结合 .12 1.1.2 内存受限 .13 1.1.3 可靠性要求高 .13 1.1.4 外设非标准 .13 1.1.5 CPU速度受限且非标准 .14 1.1.6 硬件平台不固定 .14 1.1.7 可确定的系统 .14 1.1.8 软件平台变化很大 .15 1.1.9 软件被固化 .15 1.2 传统的设计方法 .16 1.3 软硬件联合设计方法 .19 1.3.1 系统方案设计阶段 .22 1.3.2 软硬件分工设计阶段 .
9、24 1.3.3 反馈软硬件分工调整阶段 .25 1.3.4 联合设计会影响进度吗? .26 1.4 软硬件联合设计范例 .26 1.4.1 需求 .26 1.4.2 设计方案 .27 1.4.3 产品功能升级 .29 1.5 软硬件协同分析问题 .30 1.5.1 软硬件相互转化 .30 1.5.2 软硬件互存互容 .32 1.6 软硬件协作提高可靠性 .33 1.6.1 硬件措施 .33 1.6.2 软件措施 .33 1.7 可靠性分级设计 .35 1.7.1 防错还是容错 .35 1.7.2 确定关键模块 .36 1.7.3 cehw系数应用电梯控制器设计 .37 1.7.3.1 非 c
10、ehw设计方案 37 1.7.3.2 cehw设计方案 39 1.7.4 echw系数应用手机通话模块 .41 第 2 章 认识内存 .43 2.1 嵌入式系统的存储设备 .43 2.2 存储器保护组件: MPU简介 47 2.3 存储器多重映射组件: MMU简介 .49 2.4 栈 .52 版权所有,转载必须注明出处,注明:来自 字样。第 4 页 共 409 页 2.5 堆 .56 2.6 汇编语言程序中使用栈 .57 2.6.1 全局变量局部化 .57 2.6.2 在栈中定义局部变量 .59 2.7 堆与栈的关系 .61 2.7.1 单线程的堆和栈 .61 2.7.2 多线程的堆和栈 .
11、63 2.8 该为系统配置多少内存 .64 2.8.1 图解多线程环境栈溢出 .64 2.8.2 测算线程需要多大的栈 .65 2.9 内存分配 .66 2.9.1 静态分配和动态分配 .66 2.9.2 任意长度分配法 .68 2.9.3 固定块分配法 .69 2.9.4 块相联分配法 .69 2.9.5 适时分配和释放堆内存 .69 2.10 动态分配与内存枯竭 .70 2.11 提高栈的复用率 .72 2.12 windows内存粉碎机 74 2.13 数据对齐 .77 第 3 章 嵌入式实时操作系统基础 .83 3.1 实时系统 .83 3.1.1 实时系统的实时性指标 .83 3.1
12、.2 非实时操作系统多道程序设计 .86 3.1.3 实时操作系统多道程序设计 .87 3.1.4 在实时操作系统中运行多个应用程序 .90 3.1.5 实时性、紧急性与可靠性 .93 3.1.6 案例:降低速度,提高实时性 .94 3.2 友好组软件界的“智子疑邻”游戏 .95 3.2.1 友好组划分 .96 3.2.2 友好组保护 .97 3.3 友好组隔离的实现 .99 3.3.1 虚拟机 .99 3.3.2 超级虚拟机 .100 3.3.3 进程虚拟机 .101 3.3.4 线程虚拟机 .103 第 4 章 djyos崭新的操作系统 104 4.1 术语 .104 4.2 djyos系
13、统的基本元素 104 4.2.1 事件 .105 4.2.2 事件类型 .106 4.2.2.1 mark标记 .107 4.2.3 线程 .107 4.2.4 事件、事件类型与线程 .108 版权所有,转载必须注明出处,注明:来自 字样。第 5 页 共 409 页 4.3 事件调度 .109 4.3.1 调度的实质分配 CPU时间 .109 4.3.2 不允许直接控制线程和进程 .110 4.3.3 数据结构说明 . 111 4.3.3.1 事件类型控制块数据结构定义 .112 4.3.3.2 登记事件类型 .113 4.3.3.3 删除事件类型 .115 4.3.3.4 事件控制块数据结
14、构定义 .116 4.3.3.5 空闲事件控制块队列 .118 4.3.3.6 就绪事件队列 .119 4.3.3.7 不能删除事件 .122 4.3.3.8 线程虚拟机数据结构定义 .122 4.3.3.9 创建线程虚拟机 .123 4.3.3.10 删除线程虚拟机 .125 4.3.4 虚拟机分配与暂存 .125 4.3.5 永久性线程虚拟机资源 .125 4.3.6 线程虚拟机操作函数 .126 4.3.7 事件状态 .127 4.3.7.1 就绪态 .127 4.3.7.2 阻塞态 .127 4.3.7.3 状态变化 .128 4.3.8 事件优先级体系 .129 4.3.9 事件切换
15、 .130 4.3.9.1 何时执行事件切换 .130 4.3.9.2 事件到事件切换 .131 4.3.9.3 异步信号 ISR中执行事件切换 .133 4.3.9.4 事件处理完成后切换到就绪事件 .134 4.3.10 事件重复弹出 .134 4.3.11 事件生命周期 .135 4.3.11.1 弹出事件 .137 4.3.11.2 事件开始处理 .142 4.3.11.3 事件处理完成 .145 4.3.12 允许和禁止调度 .150 4.3.13 mark型事件 .150 4.3.14 事件与事件类型小结 .153 4.4 线程的栈 .154 4.5 时钟嘀嗒 .155 4.6 运
16、行模式 .157 4.6.1 单映像模式( si模式) 157 4.6.2 动态加载单进程模式( dlsp模式) 158 4.6.3 多进程模式( mp模式) 159 4.7 系统启动 .159 4.7.1 系统启动过程各模式公共部分 .160 4.7.1.1 CPU初始化 .160 版权所有,转载必须注明出处,注明:来自 字样。第 6 页 共 409 页 4.7.1.2 预加载操作系统 .161 4.7.1.3 初始化中断系统 .163 4.7.1.4 调用安全钩子函数 .163 4.7.1.5 加载操作系统 .164 4.7.1.6 准静态内存分配初始化 .165 4.7.1.7 操作系
17、统内核初始化 .165 4.7.1.8 内核组件初始化 .165 4.7.2 系统启动过程 si模式部分 166 4.7.2.1 操作系统外围组件初始化 .166 4.7.2.2 加载应用程序 .166 4.7.2.3 用户应用程序初始化 .167 4.7.2.4 动态内存分配初始化 .167 4.7.2.5 启动多线程管理 .167 4.7.3 系统启动过程 dlsp模式部分 168 4.7.3.1 初始化文件系统 .168 4.7.3.2 安全钩子模块 .168 4.7.3.3 操作系统外围组件初始化 .169 4.7.3.4 动态内存分配初始化 .169 4.7.3.5 加载可执行文件
18、.169 4.7.3.6 从文件系统加载可执行文件 .169 4.7.3.7 从固定地址获取可执行文件 .171 4.7.3.8 从 DROM中读取可执行文件 .171 4.7.3.9 调试模式加载可执行文件 .172 九九加一原则 .172 第 5 章 同步 .173 5.1 闹钟同步 .173 5.2 事件同步 .178 5.3 事件类型弹出同步 .178 5.4 事件类型完成同步 .179 5.5 异步信号同步 .180 5.6 内存同步 .180 5.7 锁同步 .181 5.7.1 数据结构定义 .181 5.7.2 锁模块初始化 .182 5.7.3 创建和删除锁 .182 5.7
19、.4 请求和释放锁 .183 5.7.5 使用锁保护资源 .184 5.7.6 信号量和互斥量的区别 .185 5.7.7 优先级继承 .185 5.8 阻塞超时 .188 第 6 章 中断 .190 6.1 中断与硬件设备 .190 6.1.1 硬件操作 .190 6.1.2 中断只是异步事件 .192 版权所有,转载必须注明出处,注明:来自 字样。第 7 页 共 409 页 6.2 中断的软件模型 .194 6.2.1 实时中断与异步事件 .194 6.2.1.1 异步事件的优先级模型 .194 6.2.1.1.1 理想模型:优先级混合 .195 6.2.1.1.2 djyos模型: I
20、SR引擎 196 6.2.1.2 中断控制数据结构 .199 6.2.1.3 初始化 .201 6.2.1.4 异步信号 .202 6.2.1.5 实时中断 .205 6.2.2 禁止和允许中断 .206 6.2.3 建议的中断配置策略 .208 6.2.4 ISR函数 .209 第 7 章 内存管理 .211 7.1 准静态内存分配 .211 7.2 块相联分配法 .213 7.2.1 内存组织 .213 7.2.2 块相联分配模块初始化 .216 7.2.3 分配一块内存 .217 7.2.4 释放一块内存 .220 7.3 固定块分配法 .221 7.3.1 固定块分配模块初始化 .22
21、1 7.3.2 创建内存池 .222 7.3.3 分配内存 .223 7.3.4 释放内存 .224 7.4 堆和内存池 .224 7.5 内存同步 .225 7.6 局部内存回收 .226 7.7 djyos内存管理的特点 227 第 8 章 资源管理 .228 8.1 资源管理模型 .228 8.2 宿主数据结构 .230 8.3 使用资源链表 .231 第 9 章 泛设备驱动 .233 9.1 模块泛设备化 .233 9.2 泛设备 driver分类 .238 9.2.1 设备分类原则 .238 9.2.2 内核泛设备 driver .239 9.2.3 独立泛设备 driver .23
22、9 9.2.4 私有泛设备 driver .240 9.3 谈谈硬件驱动 .240 9.4 实现泛设备驱动程序 .241 9.4.1 辅助组件 .241 9.4.1.1 环形缓冲区 .241 9.4.1.2 线性缓冲区 .245 版权所有,转载必须注明出处,注明:来自 字样。第 8 页 共 409 页 9.4.1.3 栈缓冲区 .246 9.4.2 泛设备驱动程序数据结构 .246 9.4.2.1 struct dev_handle结构 .251 9.4.2.2 struct pan_device结构 .252 9.4.2.3 struct dev_io结构 252 9.4.2.4 内存池
23、.253 9.4.3 初始化泛设备管理模块 .254 9.4.4 建立和使用设备 .254 9.4.4.1 创建新设备 .255 9.4.4.2 打开设备 .257 9.4.4.3 关闭设备 .262 9.4.4.4 快速打开设备在实时系统中的应用 .265 9.4.4.5 防止资源泄漏 .265 9.4.4.6 设备的读、写、控制 .266 9.5 内窥镜泛设备 .269 第 10 章 看门狗 .270 10.1 看门狗的硬件 .270 10.2 看门狗的软件原理 .270 10.3 看门狗设计的常见误区 .272 10.3.1 为了喂狗而喂狗 .272 10.3.2 过度喂狗 .273 1
24、0.3.3 过分倚重看门狗 .273 10.3.4 定时器中断喂狗的特例 .273 10.4 djyos系统的看门狗模块 274 10.4.1 看门狗模块初始化 .274 10.4.2 看门狗服务执行流程 .275 10.4.3 创建和删除看门狗 .276 10.4.4 使用看门狗示例 .277 第 11 章 组件化开发(本章未完成) .278 11.1 可移植是软件的灵魂 .278 11.2 组件化的困惑 .279 11.3 耦合 .280 11.3.1 时间耦合 .282 11.3.2 CPU运行速度耦合 .282 11.3.3 临界资源耦合 .285 11.3.4 内存耦合 .286 1
25、1.3.5 全局变量耦合 .287 11.3.6 功能性耦合 .288 11.3.7 解释性耦合 .289 11.3.8 编译器耦合 .290 11.4 不要图一时之快 .290 11.5 组件的接口与实现 .290 11.6 组件化的好处 .291 11.6.1 解放专业设计者 .291 版权所有,转载必须注明出处,注明:来自 字样。第 9 页 共 409 页 11.6.2 升级维护,得心应手 .291 11.6.3 系统规划,性能优越 .293 11.7 划分模块的原则 .293 11.7.1 一致性原则 .294 11.7.2 可维护性原则 .295 11.7.3 可移植性原则 .29
26、6 11.7.4 功能独立实现原则 .296 11.7.5 可靠性分级原则 .296 11.7.6 实时性分级原则 .297 11.8 djyos为组件化开发提供的支持 297 第 12 章 文件系统 .298 12.1 djyfs与 ANSI C文件 IO的差异 298 12.2 约定规范 .299 12.3 文件系统整体结构 .300 12.4 文件系统设备和初始化文件系统 .303 12.5 文件柜设备 .304 12.6 格式化文件柜 .308 12.7 文件资源 .310 12.7.1 文件资源树 .310 12.7.2 创建文件 .314 12.7.3 打开文件 .314 12.7
27、.4 删除文件(目录) .322 12.7.5 读、写文件 .323 12.7.6 关闭文件 .325 12.7.7 其他文件操作 .326 12.8 数据结构访问权限 .327 12.9 存储介质接口函数 .328 12.9.1 format格式化文件柜 328 12.9.2 write写文件 .328 12.9.3 read读文件 329 12.9.4 flush刷新文件 .329 12.9.5 query_file_stocks查文件库存 330 12.9.6 query_file_ cubage查文件库容 330 12.9.7 set_file_size设置文件长度 .330 12.9
28、.8 seek_file设置文件指针 331 12.9.9 create_item创建文件或目录 331 12.9.10 remove_item删除文件或目录 331 12.9.11 open_item打开文件或目录 332 12.9.12 close_item关闭文件或目录 332 12.9.13 lookfor_item查找文件或目录 332 12.9.14 rename_item修改文件或目录的名字 333 12.9.15 check_folder查询子项目数量 333 12.10 dfsmd部分 .333 第 13 章 flash文件系统驱动程序 .335 版权所有,转载必须注明出处,
29、注明:来自 字样。第 10 页 共 409 页 13.1 芯片存储体布局 .335 13.2 数据结构 .339 13.3 DFFSD初始化与挂载芯片 341 13.4 掉电恢复块 .342 13.5 磨损平衡算法 .342 13.5.1 数据结构设计 .343 13.5.2 跳跃换区法 .346 13.5.3 分配一块 .347 13.6 对 djyfs的接口函数 .347 13.7 芯片驱动接口 .347 13.8 nand flash芯片范例: 2808U0B.348 13.9 nor flash芯片范例: SST39VF1601.348 第 14 章 其他文件系统驱动程序 .349
30、14.1 RAM文件系统驱动程序 349 14.2 串口文件系统驱动程序 .349 14.3 简易文件系统驱动程序 .349 第 15 章 djyos移植(本章未完成) 350 15.1 可移植性的考虑 .350 15.1.1 充分注释 .350 15.1.2 平台无关 .350 15.1.3 严格遵守 ANSI C351 15.1.4 慎用汇编 .351 15.1.5 便于应用程序迁移 .352 15.2 需要移植的部分 .353 15.2.1 CPU初始化文件 .353 15.2.2 配置文件 .353 15.2.3 makefile连接脚本文件 .355 15.2.4 线程相关函数 .3
31、56 15.2.4.1 创建线程虚拟机 .356 15.2.4.2 复位线程虚拟机 .357 15.2.4.3 复位老线程,切入新线程 .357 15.2.4.4 上下文切入 .357 15.2.4.5 上下文切换 .358 15.2.4.6 从异步信号 ISR中返回时的上下文切换 .359 15.2.5 中断系统 .359 15.2.6 系统定时 .359 15.2.7 CPU的自留地 .360 15.3 ARM7 版本 S3C44B0X 360 15.3.1 ARM的自留地 360 15.3.2 中断设计 .360 15.3.3 线程栈结构设计 .360 15.3.4 常量配置 .361
32、15.3.5 线程相关函数 .362 15.3.5.1 复位线程虚拟机 .362 版权所有,转载必须注明出处,注明:来自 字样。第 11 页 共 409 页 15.3.5.2 重置老线程,切换到新线程 .363 15.3.5.3 直接切入上下文 .364 15.3.5.4 上下文切换 .364 15.3.5.5 从异步信号 ISR返回时的线程切换 .365 15.3.6 中断系统 .368 15.4 cortex-M3 版本 .368 15.5 DSP版本 TMS320F2808 .368 第 16 章 嵌入式 C语言编程杂谈 369 16.1 对齐及数据长度 .370 16.2 强制类型转
33、换 .371 16.3 慎用 union371 16.4 适应运行环境 .373 16.5 软件效率 .374 16.6 定长数据类型 .374 16.7 数据完整性 .376 16.8 精简代码与阅读困难 .378 16.9 用户习惯 .378 16.10 空函数指针 .379 16.11 BOOL变量的第三值 379 第 17 章 api参考手册 381 17.1 事件与事件类型 .381 17.2 资源管理 .384 17.3 泛设备管理 .388 17.4 中断 .392 17.5 内存分配(从堆中分配) .396 17.6 内存分配(从内存池中分配) .397 17.7 定时 .39
34、8 17.8 锁(信号量和互斥量) .399 17.9 看门狗 .401 17.10 文件系统 .402 z 附录一 编码规范 .405 一、 排版 .405 二、 注释 .405 三、 命名规则 .407 四、 代码约定 .408 第 1章 设计嵌入式产品 本书讲述嵌入式产品的软硬件设计,开讲之前,我们先来认识一下什么是嵌入式系统的问题,做到有的放矢。给嵌入式系统下定义是个困难的事,不同的人对嵌入式系统有不同的理解,有很多讲嵌入式系统的书,对嵌入式系统的定义也各不相同,没有统一的定义。因此,本书不企求对嵌入式系统下一个统一的定义,只是要交代一下本书所讨论的中心议题。嵌入式产品是以嵌入式系统为
35、核心的电子产品, 本书讨论如何设计优秀的嵌入式系统并把它组成版权所有,转载必须注明出处,注明:来自 字样。第 12 页 共 409 页 优越的产品。嵌入式系统是电子产品的一个功能模块, 图 1-1 所示是一个典型的嵌入式产品组成结构图,它典型地包含以下模块, z 计算机核心系统模块,与通用计算机不同,这是为专门用途设计的计算模块。硬件设计时,也会根据用途采取专门的设计,例如使用 DSP 处理大量数学运算需求。 z 包含专用软件,并且可以通过修改软件适应新的功能。 z 人机界面模块,这个模块也是为专门用途设计。 z 输入和输出模块,与通用计算机的 I/O 不同,嵌入式系统的 I/O 没有确定的
36、地址,而且与具体硬件紧密相关,还可能包括模拟量 I/O。 z 有许多嵌入式系统还包含通信模块,这些模块往往用来连接成一个专用现场网络,像汽车上常用的 CAN 网一样;也可能用来与 PC 机连接,用于设定工作参数,转移采集的数据等。 图 1-1 典型的嵌入式产品 1.1 嵌入式系统的特点 1.1.1 模拟电路和数字系统紧密结合 很多人都知道,传感器检测到的信号需要经过放大、滤波、 A/D 转换后,才能被数字系统处理; CPU 输出的控制信号,也要经过与传感器检测相反的信号路径,才能实现功能。如何设计模拟电路与数字系统的实现方法息息相关,即使简单的模拟 RC 一阶低通滤波电路设计,使用精密电阻和电
37、容,软件就不用进行复杂的补偿,而使用普通电阻和电容,降低了物料成本,但可能导致需要用软件做复杂的补偿。两种方案对软件运算的需求差异,甚至可以影响到 CPU 的选择,这单纯从软件设计和硬件设计方面都是很难想象的。 版权所有,转载必须注明出处,注明:来自 字样。第 13 页 共 409 页 1.1.2 内存受限 桌面系统中,内存动辄几十几百 M,而且可以把磁盘作为虚拟内存交换使用,使程序员看起来拥有无限大的内存,一般的嵌入式系统,内存只有几十 K 至几 M,一般没有磁盘,即使有磁盘,也几乎没有硬盘页面交换能力,内存对系统的限制非常大。同时,为了保证系统的实时性,一般都没有内存碎片侦测和收集功能,
38、所有内存使用都要由设计者直接干涉,一个由多人共同开发的系统, 如何使用有限的内存, 系统设计师应该在设计初期就做出决定,避免程序员在编程过程中随意申请内存,这样会导致混乱。 1.1.3 可靠性要求高 PC 软件是允许死机的,偶尔出点错,用户也不会较真,我们常戏称经常死机的软件为“盖茨标准” 。但嵌入式控制系统经常用于关键的工业控制系统中,是不允许出错的,任何软件的或者是硬件的错误都可能是致命的。一个从技术角度讲并不算很严重的错误,如果发生在工业控制现场,就有可能是一场灾难性的事故。而很多工程师经常对一些小错误不以为然,比如当输出高电平时却输出了低电平,工程师常常一句“笔误而已”便敷衍过去了,但
39、嵌入式工业控制系统中这种错误可能比系统崩溃更为严重。这样的“笔误” ,如果出现在炼钢厂的高炉控制设备中,可能会造成一个价值数百万的设备损毁;如果出现在发电机保护设备中,可能使价值数亿的发电机报废并造成大面积停电;如果出现在生命维持设备中,那可真是人命关天的大事。 1.1.4 外设非标准 对于桌面系统,无论是台式机还是笔记本电脑,从早期的 XT 总线到后来的 ISA 总线到PCI 总线, CPU 均用标准总线与外设相连。无论是串行口还是并行打印口还是 USB 总线,最后都转换成这些标准总线供 CPU 访问。 PC 开发商甚至还对外设进行事先归类,不同种类的外设的接入地址和中断号都已经事先分配好,
40、 比如并口的 I/O 地址必然是 0x278 或 0x378,中断号必然是 IRQ5 或 IRQ7。外设供应商开发产品时,也总是使产品与某一种总线兼容,否则将无法接入计算机。或者说,桌面系统可以拒绝不符合自己要求的硬件接入。桌面系统的软件工程师,在开发硬件驱动程序或者硬件相关的产品时,只要使用操作系统提供的标准函数操作总线就可以了,剩下的就是按设定的格式解释数据。因此,桌面系统的硬件接口环境,对于嵌入式系统开发者来说,简直就是天堂!可怜的嵌入式开发工程师,他们面对的CPU 种类超过 1000 种,操作系统也超过 1000 种。他们面前根本就没有总线的说法,要说有,也就是 CPU 的数据总线或者
41、地址总线。嵌入式系统是用于解决某一个具体的问题而存在的,其硬件都是针对专用系统定制的,硬件的配置五花八门,它无需具备太多的通用性,自身浑然一体。只要你喜欢,你可以随意把一个外设安排在任意一个端口上,甚至可以用 IO口线去操作内存,事实上,有些 MPU 根本就没有外部存储器接口总线,当内部存储器不够时,往往要设计专门的接口来扩充存储器,比如 I2C 接口的存储器。同时,软件需要处理各种硬件异常情况。如果软件处理不好,硬件的变化会对软件产生很大的干扰,使其升级维护极其痛苦。 还有一个容易被产品开发阶段忽略的问题,就是芯片停产改型带来的问题。工业及现场控制类的产品往往有较长的生命周期,一个产品开发定
42、型后,连续销售 10 年甚至更长时版权所有,转载必须注明出处,注明:来自 字样。第 14 页 共 409 页 间都是有可能的。芯片厂商总是会不断地更新产品,停产老产品,工业控制类产品的产量往往不高,企业缺乏向芯片厂商叫板的筹码,遇到停产只能自己修改产品。在漫长的生命周期中, CPU 以及外围器件都有可能遭遇停产。人们往往只在硬件选型时考虑持续供货问题,而在软件开发中却缺乏考虑。当因器件供货问题局部修改设计时,往往会碰到新旧版本软件兼容升级的问题,不但增加了设计更新的工作量,也给软件版本管理带来极大的压力。 因此,嵌入式产品设计中一个很重要的问题就是统一外设,至少在一个企业范围内,要限制硬件设
43、计的随意性,限制 cpu 种类,统一安排硬件地址,这样可以有效降解软件设计的复杂性, 提高软件模块在一个企业内部不同产品之间的可移植性, 要预估可能变化的硬件,对其相关代码组件化,当发生硬件配置变化时,单独修改相应的组件就可以了。在产品升级时,可以最大限度继承老版本的软件。 1.1.5 CPU 速度受限且非标准 嵌入式系统空间狭小,散热空间也小,出于可靠性的考虑,绝大多数没有散热风扇,这直接限制了 CPU 运算速度的提高。 所有的计算系统都一样,不同的配置间速度差别很大,但桌面计算机一般不应用于关键控制,速度快慢差别并不是致命的,顶多影响用户的等待时间;而大部分嵌入式系统用于实时控制环境,有些
44、操作对定时要求相当严格,过快或者过慢都不行。 CPU 运行快慢有可能对系统造成致命影响,编写软件时就应该考虑到运行速度的差别,防止留下隐患,优秀的软件,可以自动地适应不同的 CPU 运行速度。虽然同一个产品在硬件平台一般是不变的,但如果不注意系统速度差别的话,可以轻松地保证你的软件是不可移植的。 1.1.6 硬件平台不固定 嵌入式系统一般都是为专门用途设计,同一个产品在其生命周期内硬件平台一般是不变的,但是,一个企业一般都有系列化的产品,而且可能不止一个系列。同一个系列内的产品,可能会有一个公共的 CPU 平台,配以不同外设,衍生不同的产品;不同系列的产品,则 CPU 平台也可能不一样。更不幸
45、的是,嵌入式系统工程师面对的还不仅仅是一个企业的产品,就算相似的应用,不同的公司使用的硬件配置也可能不一样,外设配置甚至 CPU 都会发生改变。为了避免重复劳动,特别是减少重复的 bug,嵌入式软件的跨软硬件平台移植的要求很高。 设计者应该考虑到, 当硬件和操作系统平台发生变化时, 尽可能重用软件代码;当产品功能要求发生变化时,尽量保持硬件和操作系统平台的稳定。软件和硬件重用的意义主要有两方面,一方面可以缩短产品开发周期,加快产品推出时间,另一方面可以使产品越来越稳定,我们知道,软件是可能存在 bug 的,使用时间越长,则 bug 暴露得越充分,只要发现 bug 时及时修改,就能使 bug 越
46、来越少,产品的可靠性也越来越高。 1.1.7 可确定的系统 计算机系统的可确定性包含两层意思,一是时间可确定,二是结果可确定。嵌入式系统的可确定性需求来自于两个方面,一是嵌入式系统可能用于实时控制,二是它是它可能用于无人值守或者无控制界面的环境。 实时系统对时间要求非常高, 它要求计算机在限定的时间内完成计算任务并给出正确的版权所有,转载必须注明出处,注明:来自 字样。第 15 页 共 409 页 输出。通用计算机系统往往要求尽量高效地使用计算机,只要求计算机尽可能公平地干尽量多的活。嵌入式系统往往直接控制硬件,很多硬件有严格的定时要求,例如在自动剥线机控制中,要求精确控制刀口动作的时间以保
47、证剥线的长度;在大米自动筛选机中,传感器首先感知到有不良大米颗粒到达筛选口,然后计算机通过采样和运算获知不良颗粒到达,再命令机械装置剔除不良颗粒,整个过程都要求快速而且精确定时,检测的速度和准确度直接影响筛选效果和生产效率。因此,许多嵌入式系统是时间关键系统,要求在指定的时间内完成指定的操作,而效率是否最高反而是次要的。在桌面系统中,对应用程序的定时需求,一般使用“尽量满足”的策略,而在嵌入式系统中,却要求“绝对满足” 。这些区别使这嵌入式系统和桌面系统的设计观念上有很大的区别,他们的作业调度算法几乎完全不一样。 靠自己,别指望上帝!无人值守环境或者无控制界面环境意味着,如果计算机遇到异常情况
48、下,这些异常情况包括外部错误的输入、计算机自身软件和硬件 bug 导致的错误,嵌入式系统应该自己做出决定,而不是等待人工干预。在桌面系统中,软件在遇到异常情况而不知道下一步该怎么做时,往往弹出对话框,让用户决定下一步该怎么做,把包袱扔给用户,而嵌入式系统却可能没有这种待遇,它必须自己做出决定。嵌入式系统的异常情况有两类,一类是可以预见但不可避免的,比如,硬件虚焊或者元器件损坏导致部分电路永久性失效,或者通信数据包由于干扰或者对方计算机出错导致错误的数据, 这些都是可预见但不可避免的,嵌入式软件需要有确定的策略处理。另一类是可以避免的异常,比如内存不足,在支持动态内存分配的环境中,可能出现内存不
49、足的情况,嵌入式系统也不例外。在桌面系统中,遇到内存不足时,操作系统可以提示用户关闭一些应用程序,或者关闭一些端口,而嵌入式环境呢,最好仔细计算好各计算任务的内存需求,按最大可能配置内存,或者在设计之初限制可能支持的通信端口数量、或者可打开的文件数量,以限制内存需求量,不要使关键任务在运行中得不到内存。 1.1.8 软件平台变化很大 与桌面平台 windows 系统和 unix 系操作系统一统天下相比,嵌入式系统的平台可谓是五花八门,全世界嵌入式操作系统有好几百种,流行较广的就不止 10 种,还有许多系统根本就不用操作系统。与嵌入式硬件和软件平台相对应,其开发平台也不一致,不同的平台有不同的编译器,这些编译器又大多根据特定平台有所扩展,以产生在特定平台上最优化的目标代码。但一般而言,我们尽量少使用编译器的特定扩展部分,最好只使用 C89