简介:这份综合设计文档模板面向嵌入式系统课程设计、毕业设计与中小型项目开发团队,帮助在缺乏统一规范时快速搭建软件设计说明书框架,覆盖从立项到评审的文档组织需求。压缩包仅含1个doc文件,大小约64KB,正文按文档编号、版本历史、引言、软件设计约束、软件设计描述等章节展开,并细化编写目的、读者对象、项目概述、功能框图、接口说明、程序编写、源文件列表等条目,可直接替换项目信息后使用。已有2574人学习下载,说明其在嵌入式教学与工程实践中具有一定参考度。读者可借此理清设计目标、资源与时序约束、文档管理、质量与配置管理等环节的写作要点,减少格式反复,把精力放在方案论证与内容完善上,适合作为设计文档初稿和评审检查清单的底稿。
1. 到底什么是嵌入式软件设计说明书:一份让代码不跑偏的施工图
装修过房子的人都知道,师傅进场第一件事不是抡锤子,而是摊开一张施工图:哪面墙能砸、电线走哪条槽、插座离地多高,全标在上面。嵌入式系统开发里的「软件设计说明书」就是这张施工图,只不过它画的对象是程序——任务怎么分、接口怎么定、每个函数吃什么参数、吐什么结果。手边这份《综合设计-嵌入式系统开发软件设计说明书模板》,相当于把施工图该有的栏目提前画好了格子:编写目的、读者对象、软件项目概述、设计目标和原则、设计约束、功能说明、功能框图、接口说明、主程序与子程序的七栏参数、源文件列表、参考资料,一样不缺。嵌入式系统开发的特点是资源紧、时序硬、改一版要重新烧板子,图纸上含糊一句话,后面可能就是三个通宵的代价。
2. 为什么嵌入式项目非要先写说明书:几十KB内存里的取舍
嵌入式系统开发有个绕不开的现实:代码最终要装进一块资源有限的板子里。手机、掌上电脑、机电系统、工业控制系统、汽车电子、家电控制、医疗器械,这些应用对象的共同点是程序跑在具体硬件上,而硬件不讲情面。在电脑上多开一个线程、多申请一兆内存,用户顶多觉得风扇响了点;在嵌入式设备里多占 2KB 内存,可能直接导致栈溢出、设备反复重启,现场的人只会看到「它又死机了」。设计说明书存在的第一层理由,就是把这些取舍提前落在纸面上,而不是等编译器的警告和示波器的波形来告诉你。
2.1 四个组成部分,决定了文档不能只写给人看
嵌入式系统由嵌入式微处理器、外围硬件设备、嵌入式操作系统、用户应用程序四块组成,这四块之间的接缝特别多,每一道接缝都需要文字来固定。
微处理器和外围硬件之间是电气层面的约定:引脚怎么接、电平多高、要不要上拉、采样周期多少毫秒。外围硬件和操作系统之间是驱动层的约定:哪个中断号、哪个寄存器地址、数据从哪个环形缓冲区进出。操作系统和应用程序之间是任务层的约定:开几个任务、优先级怎么排、共享数据加不加互斥锁。应用程序和用户之间才是功能层面的约定:按一下键应该发生什么、屏幕多久刷新一次。
一份只写「本软件实现温度采集与显示功能」的文档,把这四道接缝全糊住了。接手的人只能靠读代码猜,猜错一次就得多烧一次板子验证。打个比方,四个组成部分像四个人抬一张桌子,说明书就是喊号子的那个人——没人喊,四个人各走各的步子,桌子迟早翻。模板把「接口说明」单列成 3.2 一节,用意就在这里:它逼着你把每条接缝写清楚,而不是用一句「详见代码」糊过去。
2.2 资源约束会把一个笔误放大成一次事故
模板 2.2 要求填写硬件平台、操作系统要求、开发语言、开发工具、容量和性能要求、灵活性和配置要求。这几栏看着像例行公事,其实每一栏都在给后文划边界。
| 约束项 | 模板里要写清的内容 | 写不清的后果 |
|---|---|---|
| 硬件平台 | 处理器型号、主频、Flash 与 RAM 容量 | 按大容量写的代码移植过去直接放不下 |
| 操作系统要求 | 有无 RTOS、任务调度方式、节拍频率 | 中断里调用了会阻塞的接口,时序全乱 |
| 开发语言 | C 版本、是否允许汇编、是否允许动态内存 | 评审时风格打架,链接阶段才发现问题 |
| 开发工具 | 编译器版本、调试器、烧录方式 | 换台机器编译出的行为不一致,排查方向跑偏 |
| 容量和性能 | 代码体积上限、单次任务耗时上限 | 功能对了但实时性不达标,等于白做 |
| 灵活性和配置 | 哪些参数要能在出厂后改、通过什么方式改 | 客户改一个阈值就要重新出固件 |
举个常见的坑:某采集模块在文档里只写了「使用缓冲区暂存采样数据」,没写缓冲区多大、由谁分配、满了怎么办。开发的人按 640 字节实现,集成的人按 64 字节去对接,一跑起来就内存越界。这类问题在桌面软件里往往表现为一次崩溃加一行日志,在嵌入式里可能是设备反复重启,现象玄学得很,查三天也未必能定位到那一行。设计说明书要求把容量和性能写成数字,就是提前把这个坑填掉。
2.3 设计目标和原则:模板开头那两栏不是客套
模板 2.1 要求写「设计目标和原则」。很多人把这两栏当填空题,随手写「满足用户需求」「代码规范清晰」。实际上这两栏是后面所有内容的裁判。
设计目标回答的是「这个软件做成什么样算成功」:完整实现用户需求、系统具备可扩充性、功耗控制在某个范围、从上电到可响应不超过多少毫秒。设计原则回答的是「为了达到目标,我们约定按什么规矩写」:中断服务程序里不做浮点运算、所有外设访问统一走一层封装、运行期不动态申请内存、错误码统一从负数返回。
原则一旦写下来,评审就有依据。有人提「这里顺手 malloc 一下更省事」,你可以翻到 2.1 那一页指给他看。没有这一页,讨论很容易变成资历和嗓门的比拼,最后代码里留下一堆风格互相打架的模块,谁也不敢动。
2.4 文档也是给三个月后的自己看的
人对代码的记忆衰减得比想象中快。今天你清楚地知道那个全局变量为什么必须在初始化前清零,三个月后回来看,只剩一句「当时好像是调出来的」。嵌入式项目周期往往横跨硬件打样、驱动调试、整机联调几个阶段,中间还夹着人员轮换,一个人手上同时压着两三个项目是常态。模板里「编写目的」「读者对象」「文档概述」「定义」这几节,看似最没用,实际是在给未来的读者留路标:这份文档写给谁、覆盖哪个版本、里面那些缩写分别是什么意思。
血泪经验通常出现在交接那一刻。人走了,代码在,文档里只有一句「模块功能见源码」,接手的人光理顺调用关系就要一周。把设计写下来,成本是几个下午;不写,成本是每个接手者的一周,还得乘上接手的人数。
3. 模板骨架里到底装了什么:从设计约束到主程序入口参数
模板第三章「软件设计描述」是整份文档最厚的部分,也是最能看出设计水平的地方。它要求从总体设计、接口说明、程序编写、源文件列表四个层次往下拆,一层比一层靠近代码。这个顺序不是随便排的:先让人看懂系统分成几块,再看清块与块之间怎么说话,最后才落到某个函数收几个参数。反过来写,读者一上来就淹没在细节里,看不到全局。
3.1 总体设计:功能说明与功能框图是一对
模板 3.1 给总体设计提了五条硬要求:完全、准确、一致、直观简洁、可追踪。翻成大白话就是——用户需求一条都不能漏;文字和图形要能互相印证;同一个东西从头到尾叫同一个名字;别写废话;每一段设计都能追溯到某条需求。
这五条里最容易被忽略的是「一致」和「可追踪」。同一个缓冲区,有人叫 rx_buf,有人叫 recv_buffer,文档里又写成「接收缓存」,三处对不上,联调时就得靠猜。可追踪则意味着文档里每一块设计都能回答「这是为了实现哪条需求」,答不上来的部分,要么是多余功能,要么是需求漏写了。
模板还给了两种表述路径:面向数据流的建模方法用层次图、HIPO 图、结构图配文字说明;面向对象的建模方法用类图、对象图、状态图、顺序图、活动图配文字说明。选哪种不重要,重要的是图和文字必须成对出现——图负责让人一眼看出结构,文字负责说清图上标不出来的条件,比如「这个状态只在初始化完成后才进入」。
功能说明和功能框图这两节,模板留了占位符。填的时候有个实用做法:先用一句话说明每个功能块的输入、处理、输出,再画框图。画完回头检查,框图上每个箭头在文字里都能找到对应描述,文字里提到的每条数据流在框图上都有箭头,两边对不上就是漏了。
3.2 接口说明:软硬件之间最容易扯皮的那条线
接口是嵌入式项目里扯皮最多的地方,因为它横跨硬件、驱动、应用三个角色,每个角色都默认对方知道某些约定。
硬件工程师认为电平定义写在原理图里就够了,驱动工程师认为时序参数应该由硬件给出,应用工程师只关心「调这个函数能不能拿到数据」。三方都没错,但三方都没写下来,联调时就会互相指着对方说「你这儿不对」。
接口说明要落的是四件事:接口的物理形式(引脚、总线、串口编号)、数据的格式(字节序、单位、有效位、校验方式)、调用的时序(谁先发起、超时多久算失败、失败后怎么重试)、异常的表现(断线、CRC 错、缓冲区满分别返回什么)。这四件事写清楚,联调现场能省掉大量「你先量一下波形」的来回。
模板把接口说明放在总体设计和程序编写之间,位置很讲究:它上承系统结构,下启函数实现。接口定不下来,函数签名就定不下来;函数签名反复改,源文件列表也跟着反复改。
3.3 主程序和子程序:六个栏目为什么值得单列
模板 3.3.1 给主程序列了七栏:功能、入口参数、出口参数、局部变量、函数调用、对象调用、流程图。3.3.2 给子程序列了六栏,少了对象调用。这些栏目看似繁琐,实际是把一个函数的「合同」拆开写清楚。
/* 读取指定通道的原始采样值,单位:毫伏 */ int read_channel_raw(uint8_t ch, uint16_t *out) { if (ch >= CH_MAX || out == NULL) { return -1; /* 出口参数之一:错误码 */ } uint16_t adc = adc_sample_once(ch); /* 局部变量,仅本函数可见 */ *out = (uint16_t)((adc * 3300UL) >> 12); /* 12 位 ADC 折算成毫伏 */ return 0; }这段代码对应模板的栏目是这样的:功能栏写「读取指定通道采样值并折算为毫伏」;入口参数栏写ch的取值范围是 0 到 CH_MAX-1;出口参数栏写out指向调用者提供的变量,函数不负责分配,同时写明返回 0 表示成功、-1 表示参数非法;局部变量栏写adc为 12 位无符号值;函数调用栏列出对adc_sample_once的依赖;流程图栏画参数检查和折算两步。
为什么要连局部变量都写?因为嵌入式里很多「灵异现象」来自局部变量:大数组放在栈上把栈撑爆、递归深度不可控、中断里改了本该只在本函数内使用的变量。把这些写进文档,评审时有人一眼就能看出「这个 256 字节的数组不该放栈上」。
函数调用和对象调用两栏的作用是提前暴露依赖。一个函数调了七八个底层接口,说明它承担的职责可能太多;反过来,一个被二十处调用的函数要改签名,影响面也一目了然。
3.4 源文件列表和参考资料:给代码留一张地图
模板 3.4 的源文件列表,很多团队会省掉,理由是「代码仓库里都有」。仓库里确实有文件,但没有「为什么这么分」的说明。一个中等规模的嵌入式项目,源文件动辄几十上百个,分层方式各有各的习惯:有的按硬件抽象分层,有的按业务模块分,有的干脆按人分。源文件列表要写的是分组依据和依赖方向——哪一层可以调哪一层,哪个文件是公共工具,哪个文件只被一个模块使用。
模板 3.5 的参考资料同样别空着。数据手册、芯片参考手册、通信协议文档、行业标准,这些都是设计决策的依据。写下参考资料的好处是,半年后有人质疑「这个超时为什么设成 200 毫秒」,你能直接指到手册第几页,而不是回答「当时试出来的」。
4. 编号、版本和参考资料:模板里那些「表格活」到底在管什么
模板前两页塞满了表格:文档编号、产品版本、密级、产品名称、作者、开发测试经理、项目经理签字与日期、文件状态、文件标识、版本历史。第一次填的人多半会嘀咕:写个设计文档,怎么跟办手续一样。这些栏目管的其实不是文字,是「这份文档在流程里的位置」——谁批准它存在、它对应哪个产品版本、它现在处于什么状态、改过几次、每次改了什么。
4.1 文档编号与密级:一行字决定这份文档的流转范围
模板里的文档编号由几段拼成,通常是项目代号、文档类型、年份序号、版本号。它的作用是让文档在系统里有唯一身份,检索时不用靠文件名。文件名谁都能改,编号改起来要登记,这就是差别。
密级那一栏,模板标注了「仅供内部使用」。这一栏不是装饰,它决定文档能被谁看到、能不能外发、归档时放哪个柜子。写设计文档的人常常只关心技术内容,觉得这些是行政的事,但真到交付或者审计环节,缺编号、缺密级、缺签字的文档是要退回重做的。
4.2 版本历史与文件状态:草稿、正式发布、正在修改
模板的文件状态有三个勾选项:草稿、正式发布、正在修改。这三个状态对应三种完全不同的使用方式。草稿意味着内容还会大改,看的人别急着照着做;正式发布意味着这份内容已经过评审,可以作为开发和测试的依据;正在修改意味着有人在动它,其他人引用前要先确认拿的是哪一版。
版本历史表要求填版本、状态、作者、参与者、起止日期、备注。备注那一栏最值得认真写,写「修改了接口说明第 3 节,补充超时重试规则」比写「若干修改」有用一百倍。项目后期出问题回溯时,往往是靠这一栏找到「哪一版引入了这个行为」。
4.3 读者对象:同一份文档,三种人看三种深度
模板 1.2 要求说明读者对象。这一节短,但它决定了后面所有内容写到什么颗粒度。
项目经理关心的是范围、进度、风险,他看的是软件项目概述和设计约束;测试人员关心的是功能边界和异常处理,他看的是功能说明、接口说明和错误码定义;接手代码的开发人员关心的是结构和依赖,他看的是总体设计、源文件列表和函数参数。
一份文档想同时讨好这三类人,结果往往是三类人都看不下去。务实的做法是:主体内容按开发人员的深度写,在关键章节前加一小段给非技术的读者看的概述,说明这一章解决什么问题、不涉及什么。
4.4 配置管理与变更:模板的留白是给流程用的
模板 1.6 参考资料、3.5 相关参考资料及文档、3.6 其他,这几节都留了空白。空白不是让你跳过,而是给流程留位置:变更记录、评审意见、遗留问题、待确认事项,都可以落在这些地方。
摘要里提到的配置管理,落到这份模板上就是三件事:文档有唯一编号,改动有版本记录,不同版本之间能对比。听起来简单,做起来最容易踩坑的是「口头变更」——会上说了一句「这个接口改成异步」,文档没改,代码改了,测试按文档写用例,最后三方对不上。文档的价值不在于写得多漂亮,而在于它和代码是同一份事实。
5. 设计说明书最容易混淆的五件事:需求、概要设计、测试用例、用户手册和代码注释
这几类文档在项目里同时存在,名字又都带「文档」两个字,初学的人经常把它们当成同一件东西的不同叫法。实际上它们各自回答不同的问题,写错位置的内容,读的人找不到,写的人白费劲。
5.1 易混一:软件设计说明书与软件需求规格说明书
易混点:两者都在项目早期产出,都在讲「系统要做什么」,格式上还都用条目编号。
为什么混:很多小团队的实践中,需求文档写完就直接进入编码,设计文档要么不写,要么把需求内容复制一遍充当设计,久而久之两者边界就糊了。
怎么区分:需求文档描述「问题空间」,说的是用户要什么、系统要满足什么条件、验收标准是什么,它不关心你用什么数据结构、开几个任务。设计说明书描述「解空间」,说的是为了满足那些需求,系统分成几个模块、模块之间怎么通信、数据结构长什么样、函数怎么分工。判断方法很简单:需求文档里出现「用链表还是数组」是越界,设计文档里出现「用户希望响应时间小于一秒」是跑题——后者应该被引用,而不是被重新定义。这份模板的 1.3 软件项目概述只是简述功能和性能要求,详细需求应该另文存放,正文里引一下编号即可。
5.2 易混二:总体设计与详细设计
易混点:模板第三章叫「软件设计描述」,里面既有总体设计,又有主程序和子程序的详细说明,看起来是一份文档干了两件事。
为什么混:概要设计和详细设计在不少流程里是两份独立文档,而这份模板把它们合在一份里,用 3.1 和 3.3 两个层级区分,不熟悉的人容易把两部分内容写串。
怎么区分:总体设计回答「系统由哪几块组成、块之间传什么」,视图是模块和连线;详细设计回答「某一块内部怎么实现」,视图是函数、参数、局部变量和流程。一个实用的检验标准:总体设计那一层不该出现具体函数名,详细设计那一层不该重新画系统框图。模板 3.1 要求从多个侧面描述总体结构,3.3 才落到主程序和子程序的七栏参数,这个顺序就是在提示你分两层写。
5.3 易混三:设计说明书与测试用例、测试报告
易混点:设计文档里写了异常处理和错误码,测试用例里也写异常场景,内容看着重叠。
为什么混:两者都在描述「出问题时会怎样」,而且测试用例的输入往往直接来自设计文档的接口说明。
怎么区分:设计文档写「系统应该怎么表现」,是期望;测试用例写「用什么输入去验证这个期望」,是方法;测试报告写「实际跑出来是什么」,是结果。三者的关系是链条,不是复制。设计文档里写「通道号越界返回 -1」,测试用例就应该设计一条越界输入去验证它返回 -1,测试报告记录实际返回值。如果设计文档里只写「做了参数检查」而不写返回什么,测试就没法写出可判定的用例——这也是模板要求把出口参数写清楚的实际价值。
5.4 易混四:设计说明书与用户手册
易混点:两者都要描述功能和操作,用户手册里的功能列表和设计文档里的功能说明常常高度相似。
为什么混:编写者图省事,把设计文档的功能说明直接搬进用户手册,或者反过来。
怎么区分:用户手册站在使用者视角,讲「按这个键会发生什么」「这个指示灯闪三下代表什么」,不谈内部结构;设计说明书站在实现视角,讲「这个按键事件由哪个任务处理、消抖放在驱动层还是应用层」。判断标准是读者:用户手册给不碰代码的人看,出现函数名就是错;设计说明书给改代码的人看,只讲现象不讲机制就是空。
5.5 易混五:设计说明书与代码注释、仓库说明文件
易混点:代码里已经有函数头注释,仓库里也有说明文件,再写一份设计文档,内容是不是重复了。
为什么混:三者确实都在解释代码,而且好的函数注释和设计文档的某些段落几乎可以互相替换。
怎么区分:代码注释跟着代码走,解释「这一行为什么这么写」,颗粒度是语句和函数;仓库说明文件解释「怎么编译、怎么跑、目录怎么分」,颗粒度是工程;设计说明书解释「为什么这样分层、接口为什么定成这样、有哪些约束不能碰」,颗粒度是决策。注释会随重构消失,决策不该消失。举个例子:某个模块为什么不用动态内存,这个理由写在代码注释里,重构时很容易被删掉;写进设计文档的 2.1 设计原则,它就一直在那儿。
6. 什么时候该掏出这份模板,什么时候别硬套
这份模板最适合的场景,是「多人协作、要交付、要维护一段时间」的嵌入式项目。典型特征是:硬件和软件由不同的人负责,代码要跑在定制板子上,交付后还有人要改需求或者查问题。这种项目里,模板的每一节都能派上用场——编号和版本管流转,设计约束管边界,总体设计管结构,接口说明管扯皮,主程序七栏管细节,源文件列表管交接。一个中等规模的机电控制项目,按这套骨架填一遍,大概需要两到三个工作日,换来的是联调阶段少开几次「到底是谁的问题」的会。
也有些场景不适合硬套。第一类是验证性原型:目标只是确认某个方案可行,代码活不过两周,这时候花三天写文档属于本末倒置,写一页纸的设计要点、把接口和约束记下来就够了。第二类是单人维护的小工具:从设计到调试都是一个人,文档的真正读者只有未来的自己,用模板全填一遍不如把关键决策写成十条备注。第三类是需求还在剧烈变化阶段:这周定的接口下周就推翻,写详细设计等于反复返工,这时候应该先稳住需求和总体结构,详细设计那部分留到结构定型后再补。
| 场景 | 建议做法 |
|---|---|
| 多人协作、有交付节点 | 按模板完整填写,重点填约束、接口、参数三块 |
| 原型验证、周期两周内 | 只写设计目标和接口约定,其余留白 |
| 单人维护的小工具 | 用简化版,保留版本历史和关键决策记录 |
| 需求剧烈变动期 | 先写总体设计和约束,详细设计延后 |
| 老项目补文档 | 从接口说明和源文件列表倒推,别追求一次写全 |
还有一种情况值得单独说:维护老项目。接手一套没有文档的代码,最省力的补文档顺序不是从第一章开始填,而是先列源文件列表、再补接口说明、最后回头写总体设计。因为源文件列表能最快建立地图感,接口说明能逼你搞清模块之间怎么通信,等这两块清楚了,总体设计的框图基本就浮出来了。反过来从「编写目的」开始写,很容易写成一篇空话。
我自己踩过的坑是在一个模拟项目上:当时觉得功能简单,跳过接口说明直接写代码,结果采样数据的单位在驱动层和应用层各按一套理解实现,驱动返回的是 ADC 原始码值,应用层当成毫伏用,显示出来的温度比实际高了十几倍。查了两天才发现是两个函数的注释各说各话。从那以后我每次开新模块,都强制先把接口说明那几行写下来——数据单位、取值范围、异常返回,写完再动手。这几行字花不了十分钟,省下的往往是两个晚上。
如果你手上正有一个嵌入式项目要开工,或者正对着一堆没有文档的代码发愁,这份《综合设计-嵌入式系统开发软件设计说明书模板》可以当成起点:它把该有的栏目都摆好了位置,你只需要按项目的实际情况往里填。希望帮到你。
本文还有配套的精品资源,点击获取