2024软考5月嵌入式系统设计师案例
2024软考嵌入式系统设计师下午案例解析
第一题
某企业大型配送控制中心一般采用自动化方式存取仓库内的货品,配送控制中心对货品的存取由两部分构成:其一,配送控制中心管理和调度多台配送车;其二,配送车根据指令从仓库货架上存取货品。
下图为某企业大型仓库货品存取示意图。图中配送车上安装有智能控制设备,通过视频接口接受图像数据,实行对货架位置的定位识别,然后将识别信息发送到配送控制中心。配送控制中心向配送车发送控制命令,将配送车上的货品放置到指定的货架位置,或从指定的货架位置上取出货品,装载到该配送车。


【问题1】
假设图1-1中的智能设备采用8051微处理器,该微处理器的定时器主频为12MHz。
该智能设备中的数据采集周期分别为10ms、15ms、1s,请给出:
(1)设备中定时器应设置的最大计时单位;
(2)8051定时器计数寄存器的初始值;
(3)若8051采用外部时钟接入方式,请完成下图中的连接方式。

解析:
(1)设备中定时器应设置的最大计时单位

(2)8051定时器计数寄存器的初始值


(3)若8051采用外部时钟接入方式,请完成下图中的连接方式
8051 的定时器 0/1 可以工作在 外部计数模式(即 C/T=1),此时 T0 或 T1 引脚上的外部脉冲被计数,脉冲周期由外部信号决定。
若采用外部时钟输入,需将 C/T 位设为 1(选择计数器模式),并通过 T0(P3.4)或 T1(P3.5)引入外部脉冲。图 1-3 应画出:外部时钟信号接到 T0 或 T1 引脚,配合 GATE 等控制位的连接(GATE=0 时,TRx 直接启动计数;GATE=1 时,还要 INTx 引脚为高电平才能启动)。
由于题干没给具体外部信号,但连接方式应是:
外部时钟信号接至 P3.4/T0 或 P3.5/T1
根据需要选 GATE 是否由 INTx 控制启动,但一般题目默认简单启动(GATE=0),只需 TRx=1 就计数
连接图示可示意为:
外部时钟源 CLKextCLKext → T0 引脚
相关控制位在程序中设定
【问题 2】
根据下图所示的配送车取货的工作过程示意图,得到图 1-4 所示的配送车取货软件流程图,请在图中的_(n) 处填入合适的内容。

解析:
(1)分析取货指令
(2)根据位置信息定位
(3)到达目标位置吗?
【问题3】
按照你对8051微处理器的理解,填写表1-1中的(1)~(5)空格, 完成表中给出的5种寻址方式的指令格式。
表: 8051寻址方式

注:
MOV:将存储器的内容取到累加器中
A:累加器
Ri:通用寄存器
PC:程序计数器
DPTR:间址寄存器
解析:
根据 8051 微处理器的指令格式,表中所列各寻址方式的正确填写如下:
(1)直接寻址
MOV A, 30H(或任意直接地址,如 direct)
对应 (1):direct(例如 30H)
(2)寄存器寻址
MOV A, R0(或 R1~R7)
对应 (2):R0(或 Rn)
(3)寄存器间接寻址
MOV A, @R0(或 @R1)
对应 (3):@R0(或 @Ri)
立即寻址
(4)MOV A, #30H(或 #data)
对应 (4):#data(或 #30H 格式)
(5)变址寻址
MOVC A, @A+PC(或 MOVC A, @A+DPTR,题中已写 +PC)
对应 (5):@A(即 MOVC A, @A+PC 中的 @A)

第二题
阅读如下有关嵌入式软件测试的论述,回答问题1至问题3,将解答填入答题纸的对应栏内。
某嵌入式软件主要用于控制飞机起落架。飞机起落架的可靠性直接关系着机载人员的人身安全。根据载机设备对软件可靠性规定,一般将软件分为3级:关键级软件、重要级软件和一般软件。由于该嵌入式软件被定义为关键软件,规定按关键级软件进行测试。
【例题1】
请根据测试规定,简要阐明语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、条件组合覆盖的含义。
语句覆盖
设计若干测试用例,使得程序中的每条可执行语句至少被执行一次。
优点:简单。
缺点:覆盖准则弱,可能遗漏分支逻辑错误。
判定覆盖(分支覆盖)
设计测试用例,使得每个判定的真分支和假分支都至少执行一次。
覆盖了分支结果,但不一定覆盖每个条件的所有取值。
条件覆盖
设计测试用例,使得每个条件的可能取值(真/假)至少出现一次。
注意:不一定满足判定覆盖。
判定/条件覆盖
同时满足判定覆盖和条件覆盖,即每个判定的所有可能结果都出现一次,且每个条件的所有可能结果也都出现一次。
但仍可能遗漏条件的组合情况。
条件组合覆盖
设计测试用例,使得每个判定中所有条件的各种可能组合都出现至少一次。
是逻辑覆盖中覆盖程度最高的。
【例题2】
根据本题所示的软件关键级别,回答该软件需要做哪几项覆盖测试?规定的覆盖率指标是多少?假如是一般级软件,应做哪几项覆盖测试?
根据常见航空/嵌入式关键软件标准(如 DO-178B/C),不同软件等级(A级为关键,B级为重要,C/D/E 为一般)有不同结构覆盖要求:
关键级软件(A级):
结构覆盖要求最高,通常需要 修改条件/判定覆盖(MC/DC)。
若题目选项里没有 MC/DC 而只有经典五类覆盖,则选 条件组合覆盖 为最高要求。
覆盖率指标:100%。
一般级软件:
可以只需要 语句覆盖 或 判定覆盖 加上一些其他分析。
若题目没有明确区分更细的等级,则“一般级”按题中“一般软件”定为:语句覆盖 + 判定覆盖(或仅语句覆盖),覆盖率指标可能是 100% 或根据标准降低要求,但结合本题答案格式,一般软件也要做到 100% 语句覆盖。
【例题3】
在软件单元测试中,重要测试对象是软件模块,假如被测程序中有多处调用了其他过程代码,测试中应怎样处理这些功能的引用?软件的性能测试在测试工作的哪个阶段进行?
第一问:
单元测试时,如果被测单元调用了其他过程(函数/子程序),一般用 桩模块(Stub) 来代替被调用的过程。
桩模块模拟被调用过程的功能,可以返回指定的数据,以便隔离被测单元、测试其逻辑。
第二问:
性能测试通常在 集成测试阶段 或 系统测试阶段 进行。
单元测试阶段通常不进行性能测试,因为功能未完整集成,环境不足以反映真实性能。
不过具体到本题,常见软件测试流程中:性能测试在系统测试阶段进行。
第三题
某计算机中断系统有4级中断I₁、I₂、I₃和I₄,中断响应的优先次序为I₁→I₂→I₃→I₄,即先响应I₁,再响应I₂,……,最后响应I₄。每级中断对应一个屏蔽码,屏蔽码中某位为"1"表示禁止中断(关中断),若为"0"则表示允许中断(开中断)。各级中断处理程序与屏蔽码的关系如表所示。

【问题1】
若 ti 时刻 I1、I2 和 I4 级同时产生中断,在各中断处理完毕后,tj (ti<tj) 时刻发出 I3 级中断申请,CPU 为 I3 服务时,I1 级发出请求,在 CPU 为 I1 服务时,I2 级发出请求。请参照下图所示的格式,画出 CPU 的运行轨迹。

解析:
(1)初始条件分析
中断系统有 4 级中断 I1,I2,I3,I4,响应优先级是 I1>I2>I3>I4(即 I1I最优先)。
屏蔽码:
I1 的屏蔽码:1111→ 关所有中断
I2的屏蔽码:0111 → 只允许 I1 中断(I2 执行时若 I1 来,就中断它)
I3 的屏蔽码:0011 → 允许 I1,I2 中断(I3 执行时若 I1 或 I2 来,就中断它)
I4 的屏蔽码:0001 → 允许 I1,I2,I3 中断(I4 执行时若 I1,I2,I3 来,就中断它)
注意:屏蔽码是在中断处理程序内设定的,决定了在它执行期间允许哪些更高级的中断。
第一段时间:ti 时刻 I1,I2,I4 同时请求
响应优先级是 I1>I2>I3>I4,所以:
ti 时,同时有 I1,I2,I4请求,先响应 I1,进入 I1 程序。
I1 屏蔽码 1111,意味着关所有中断,所以 I2,I4挂起。
I1执行完,返回时检测到还有挂起的 I2 和 I4,这时 I2 优先级高于 I4,所以先响应 I2。
进入 I2 程序,I2 屏蔽码 0111,只允许 I1 中断(但此时 I1 刚结束,没有再来 I1),所以 I2 执行期间不被中断,直到完成。
I2 结束后,检测到还有 I4 挂起,进入 I4 程序。
I4 屏蔽码 0001,允许 I1,I2,I3 中断,但当前没有 I1,I2,I3 请求,所以 I4 执行完。
所以第一波中断 I1,I2,I4I 处理完成,次序:
I1→I2→I4
无嵌套(因 I1 关中断,I2 时无 I1,I4 时无 I1,I2,I3I打断)。
tj 时刻 I3 请求
当 I4 结束返回正常程序后,在 tj 时刻 I3 请求。
CPU 响应 I3。
CPU 为 I3 服务时,I1 请求
I3 的屏蔽码是 0011,允许 I1,I2 中断。
所以 I1 请求(比 I3 优先级高)发生时,立即中断 I3 的执行,进入 I1 程序。
CPU 为 I1 服务时,I2 请求
I1 的屏蔽码是 1111,关所有中断,所以 I2 请求此时不会被响应,只能挂起。
I1 执行完,返回后,检测到挂起的 I2 和中断的 I3 都未完成。
不过 I3 是被中断的,必须等 I1 返回 I3 执行,但在 I1 返回时,中断返回点是在 I3 程序内,但返回后 I3 会被重新执行(因为是恢复现场),此时已无 I1 请求,但 I2 请求存在。
所以 I1 结束后,恢复 I3 前,系统会先响应更高优先级的挂起中断 I2 吗?
这里要明确:在 I1 返回后,
硬件会自动检查比当前优先级(I1 返回时回到 I3 的执行)更高或同级的挂起中断,按优先顺序响应。
当前返回点是 I3 内部,I3 优先级低于 I2,但 I2 优先级高于 I3,所以从 I1 返回到 I3 程序之前,会先响应 I2(如果 I2 挂起且未被屏蔽)。
当前屏蔽码是 I3 的 0011,允许 I1,I2 中断。
所以:
I1 完成 → 返回 I3 时,有 I2 挂起且优先级高于 I3,于是立即响应 I2,嵌套在 I3 内(I2 屏蔽码 0111),此时 I3 被二次挂起(准确说是仍然挂起没执行完)。
I2 执行完后,返回 I3,I3 继续执行到结束。
我们按横轴时间从左到右,中断请求和处理过程如下:
开始正常程序,ti 时 I1,I2,I4 同时请求 → 响应 I1 → 执行 I1 完 → 响应 I2 → 执行 I2 完 → 响应 I4 → 执行 I4 完 → 回正常程序。
正常程序到 tj,I3 请求 → 响应 I3 → 执行 I3 期间,I1 请求 → 嵌套 I1 → I1 执行期间,I2 请求(挂起) → I1 完 → 返回 I3 前,检测到 I2 挂起且优先级高于 I3,立即响应 I2 → 执行 I2 → 返回 I3 → 继续 I3 到结束 → 回正常程序。
其中第一次 I1, I2, I4 是顺序处理,无嵌套;第二次 I3, I1 嵌套,I1 时 I2 挂起,I1 结束后 I2 嵌套到 I3(即 I2 在 I3 内部执行)。
按照题干图例格式,嵌套的竖线应表示:
I3 开始时画一个中断请求的起点(竖线),然后 I1 嵌套在 I3 内(画一个从 I3 左边界到 I3 执行再向下到 I1 的弧线,表示打断),I1 的屏蔽码 1111 导致 I2 只能挂起(在 I1 内部画 I2 请求标志线在 I1 范围内,但不嵌套到 I1,而是等 I1 结束,从 I3 被打断点再进 I2)。
自己实现图形绘制,但运行轨迹结构就是:
时间轴:
正常 → 进入 I1 → 进 I2 → 进 I4 → 返回正常
正常 → 进 I3 → 进 I1(嵌套,I1 内收到 I2 请求但被屏蔽)→ 返回 I3(此时进 I2 嵌套在 I3 内)→ I2 完回 I3 → I3 完回正常
【问题 2】
若将中断优先次序设置为 I1 → I4 → I2 → I3,即先响应 I1,再响应 I4,再响应 I2,最后响应 I3。请重新设置各级的屏蔽码,填写下表 。

解析:
题目新指定的中断响应优先级(即硬件排队优先级)改为:
I1→I4→I2→I3
意思是:硬件排队电路会先响应 I1(最高),然后 I4(次高),然后 I2(第三),然后 I3(最低)。
屏蔽码的作用是:在某个中断处理程序内部,如果来一个更高级的中断(按新优先级定义),CPU 是否允许它打断当前程序。
规则:屏蔽码某位为“1”表示禁止该位对应的中断(关中断),为“0”表示允许中断。一般写屏蔽码时,某中断程序的屏蔽码应该禁止所有比它优先级低的中断,允许所有比它优先级高的中断。
屏蔽码设计原则
对于中断处理程序 Ix,它的屏蔽码中的第 y 位(对应 Iy 中断)的值应该:
如果 Iy 的优先级 高于 Ix,则允许中断,即屏蔽码该位 = 0。
如果 Iy 的优先级 低于或等于 Ix,则禁止中断,即屏蔽码该位 = 1。
(因为如果 Iy 优先级低或相等,在 Ix 中不应被响应,避免同级嵌套或低优先级打断高优先级。)
按新优先级确定屏蔽码
优先级(高→低):I1>I4>I2>I3。
(1) 中断处理程序 I1
I1 优先级最高,比它高的没有,所以它对 I4,I2,I3 应该禁止(1),对自身禁止(1,因为同优先级一般不嵌套)。
屏蔽码 1111。
(2) 中断处理程序 I4
优先级:I4 比 I1 低,比 I2,I3 高。
对 I1:比它高 → 允许 (0)
对 I4:自身 → 禁止 (1)
对 I2:比它低 → 禁止 (1)
对 I3:比它低 → 禁止 (1)
所以 I4 的屏蔽码 0111。
(3) 中断处理程序 I2
优先级:比 I1 低,比 I4 高,比 I3 高。
对 I1:允许 (0)
对 I2:自身禁止 (1)
对 I3:禁止(因为比它低)(1)
对 I4:允许(因为 I4 优先级比 I2 高)(0)
所以按列顺序 (I1,I2,I3,I4):
I1:0, I2:1, I3:1, I4:0 → 码 0110
(4) 中断处理程序 I3I3
优先级最低,比它高的有 I1, I4, I2。
对 I1:允许 (0)
对 I2:允许 (0)
对 I3:自身禁止 (1)
对 I4:允许 (0)
按列顺序 (I1,I2,I3,I4):
I1:0, I2:0, I3:1, I4:0 → 码 0010

第四题
在实时系统中,许多控制软件需要将数据封装到一个数据结构中,以节省存储空间。对于位操作,使用汇编语言实现其访问比较容易,但会增加编程难度,因此目前普遍采用 C 语言实现。使用高级语言编程要特别注意结构的存储格式以及编译器的特性。本题所使用的编译器对变量按声明顺序分配地址。分析下图所示的 C 语言代码,回答如下问题。
typedef struct
{
int A : 16; //按 16 位字对齐;
char B : 8;
char C : 8;
char D : 8;
char E : 8;
int F; //占 16 位并按 16 位字对齐;
}
radartype;
typedef struct
{
unsigned int X; //占 16 位并按 16 位字对齐;
unsigned int Y;
unsigned int Z;
unsigned int U;
}
datatranstype;
radartype myRadarData[2]={{1, 'a', 'b', 'c', 0, 512}, {2, 'x', 'y', 'z',0,1024 }};
void main(void)
{
radartype *p;
datatranstype *q;
p = myRadarData;
q = (datatranstype *)p;
q++;
}
【问题 1】
假如处理机按 16 位以大端方式 (big_endian) 编址,请在下图所示的存储器图表中填入 myRadarData 数据的存储内容 (十六进制表示) 。

解析:
1. 分析结构定义
typedef struct {
int A : 16; // 占 16 位(2 字节),按 16 位字对齐
char B : 8; // 占 8 位(1 字节)
char C : 8; // 占 8 位(1 字节)
char D : 8; // 占 8 位(1 字节)
char E : 8; // 占 8 位(1 字节)
int F; // 占 16 位(2 字节),按 16 位字对齐
} radartype;
注意:题中说“int 占 16 位”,所以 int = 2 字节。
int A : 16 也是 2 字节,按字对齐(即起始地址为偶数)。
结构大小(按成员顺序分配地址,且 16 位对齐,即每个成员地址必须是偶数):
A:2 字节,起始地址 0x5000(假设)
B:1 字节,起始地址 0x5002
(因为在 0x5001 不是 16 位对齐成员,不需要填充,但 char 在 C 中可任意字节对齐;如果按严格字对齐,char 不要求偶数,但这里 0x5002 是偶数,直接放 B)
C:1 字节,起始地址 0x5003
D:1 字节,起始地址 0x5004
E:1 字节,起始地址 0x5005
F:2 字节,但必须按 16 位字对齐,即起始地址必须是偶数。
上一个成员 E 地址是 0x5005,下一个可用偶数是 0x5006,所以 F 从 0x5006 开始,占 0x5006–0x5007。
所以 radartype 总大小 = 0x5000–0x5007 共 8 字节。
检查结构体布局(字节地址):
A: 0x5000 0x5001
B: 0x5002
C: 0x5003
D: 0x5004
E: 0x5005
F: 0x5006 0x5007
2. 初始数据
radartype myRadarData[2] = {
{1, 'a', 'b', 'c', 0, 512},
{2, 'x', 'y', 'z', 0, 1024}
};
数据值(注意 C 语言大小端影响内存内容):
int A 是 16 位,值 1 → 0x0001(高 0x00,低 0x01)
char B = 'a' → ASCII 0x61
char C = 'b' → 0x62
char D = 'c' → 0x63
char E = 0 → 0x00
int F = 512 → 0x0200(高 0x02,低 0x00)
3. 大端存储(big_endian)
大端模式下,16 位数据的高 8 位放在低地址,低 8 位放在高地址。
myRadarData[0] 字节地址(假设起始地址 0x5000):

myRadarData[1] 紧随其后,起始地址 0x5008(因为 radartype 大小为 8 字节)。
数据 {2, 'x', 'y', 'z', 0, 1024}:
int A = 2 → 0x0002(高 0x00,低 0x02)
char B = 'x' → 0x78
char C = 'y' → 0x79
char D = 'z' → 0x7A
char E = 0 → 0x00
int F = 1024 → 0x0400(高 0x04,低 0x00)
大端存储:

题目给的图是(每行是 16 位字,并排高 8 位+低 8 位):
0x5000 一行 = 0x5000 (高) + 0x5001 (低)
0x5002 一行 = 0x5002 + 0x5003
0x5004 一行 = 0x5004 + 0x5005
0x5006 一行 = 0x5006 + 0x5007
这是 myRadarData[0] 的 4 个字(共 8 字节)
0x5008 一行 = 0x5008 + 0x5009
0x500A 一行 = 0x500A + 0x500B
0x500C 一行 = 0x500C + 0x500D
0x500E 一行 = 0x500E + 0x500F
这是 myRadarData[1] 的 4 个字(共 8 字节):


【问题2】

在图 所示的程序中,第 22 行的语句执行完毕后,下列语句的结果是多少?请将应填入 (n) 处的内容写在答题纸的对应栏中。
q−>X=(1)
q−>Y=(2)
q−>Z=(3)
q−>U=(4)
若再执行一次 q++,则下列语句的结果又是多少?请将应填入 (n) 处的内容写在答题纸的对应栏中。
q−>X=(5)
q−>Y=(6)
q−>Z=(7)
q−>U=(8)
解析:
radartype 大小已知为 8 字节,结构体成员在内存中的位置(按大端 16 位机,地址以字节为单位):
myRadarData[0](起始地址 0x5000):

myRadarData[1](起始地址 0x5008):

第一次 q++ 之前
p = myRadarData; // p 指向 0x5000
q = (datatranstype *)p; // q 也指向 0x5000,但按 datatranstype 解释
q++; // q 增加 1 个 datatranstype(8 字节)
所以执行后 q 指向 0x5008(即 myRadarData[1] 的起始地址)。
第一次 q++ 后的 datatranstype 视图
datatranstype 结构:
-
X: 0x5008–0x5009
-
Y: 0x500A–0x500B
-
Z: 0x500C–0x500D
-
U: 0x500E–0x500F
在大端模式下,从连续字节构造 16 位无符号整数:
(1) X:
0x5008 = 0x00, 0x5009 = 0x02 → 大端值 = 0x0002 = 2
(2) Y:
0x500A = 0x78, 0x500B = 0x79 → 大端值 = 0x7879
(3) Z:
0x500C = 0x7A, 0x500D = 0x00 → 大端值 = 0x7A00
(4) U:
0x500E = 0x04, 0x500F = 0x00 → 大端值 = 0x0400 = 1024
再执行一次 q++
q 当前指向 0x5008,再加一个 datatranstype(8 字节):
q 指向 0x5010(即 myRadarData[2] 的起始地址,如果存在)。
但在原题中,myRadarData 只有 2 个元素,所以 0x5010 是数组外的地址,指向未定义数据。
不过题目可能是想我们忽略越界,认为下一个 radartype 按同样模式存放,即 myRadarData[2] 的 A=3, B='p', C='q', D='r', E=0, F=2048 这样连续的假想数据。
最终答案
第一次 q++ 后:
(1) 2
(2) 0x7879
(3) 0x7A00
(4) 1024
第二次 q++ 后:
(5) 3
(6) 0x7071
(7) 0x7200
(8) 2048
【问题 3】
内存空间常划分代码段(text)、数据段(data)、bss 段(bss)、堆区(heap)和栈区(stack),那么图 中 myRadarData 数组的存储空间应分配在哪段中?指针变量 p、q 应分配在哪段中?
解析:
C 程序的内存布局(典型嵌入式/裸机环境)
通常:
代码段(text):存放机器指令、常量(如字符串字面量),只读。
数据段(data):存放已初始化的全局变量、静态变量。
bss 段:存放未初始化或初始化为 0 的全局变量、静态变量(程序启动时清零)。
堆(heap):动态分配(malloc / new)。
栈(stack):局部变量、函数参数、返回地址。
分析 myRadarData
radartype myRadarData[2] = {
{1, 'a', 'b', 'c', 0, 512},
{2, 'x', 'y', 'z', 0, 1024}
};
定义在函数 main 外部(全局作用域)。
已初始化(有明确的初值)。
不在任何函数内,不是 static 局部,也不是 auto。
因此 myRadarData 分配在 数据段(data)。
指针变量 p, q
void main(void)
{
radartype *p;
datatranstype *q;
...
}
定义在 main 函数内部,是局部变量。
没有 static 修饰,所以是自动存储期(auto)。
存储在栈上。
p, q 分配在 栈区(stack)。
更多推荐
所有评论(0)