02 综合应用开发
考频:★★★★★(综合大题,官方样题 61 题全解析)
一、S3C2410 开发背景
S3C2410 是三星(Samsung)出品的 ARM920T 内核处理器,是嵌入式教学中最常见的实验平台。
| 属性 | 参数 |
|---|---|
| CPU 内核 | ARM920T(ARMv4T) |
| 工作频率 | 最高 203MHz |
| 内存接口 | SDRAM |
| GPIO | 多组通用 I/O 口(GPA、GPB、GPD、GPE…) |
| 常见外设 | LCD、键盘、串口、USB、网口 |
GPIO 寄存器操作基础
每组 GPIO 有两个核心寄存器: - GPxCON:控制寄存器,配置每个引脚的功能(输入/输出/特殊功能) - GPxDAT:数据寄存器,读取输入值或写入输出值
配置编码(2位/引脚):
| 编码 | 含义 |
|---|---|
00 |
输入(Input) |
01 |
输出(Output) |
10 |
特殊功能 |
11 |
特殊功能 |
二、裸机开发(无操作系统)
裸机开发指不运行任何操作系统,直接在硬件上跑应用程序。
裸机程序基本结构
#include "2410addr.h" // S3C2410 寄存器地址头文件
int main(void) {
/* 硬件初始化 */
InitClock(); // 初始化时钟
InitUART(); // 初始化串口(调试用)
InitMemory(); // 初始化内存
/* 应用主循环 */
while (1) {
/* 读取按键 */
/* 控制 LED */
/* 其他逻辑 */
}
return 0;
}
三、μC/OS-II 环境下开发
在 μC/OS-II 上开发,需要将任务组织成 RTOS 任务。
#include "includes.h"
OS_STK Task1Stk[512];
OS_STK Task2Stk[512];
void Task1(void *pdata) {
InitHardware(); // 在任务里初始化硬件
InitTimerTick(); // 在 OSStart() 之后激活时钟节拍!
while (1) {
DoWork1();
OSTimeDly(100); // 延时 100 个节拍,让出 CPU
}
}
void Task2(void *pdata) {
while (1) {
DoWork2();
OSTimeDly(200);
}
}
int main(void) {
OSInit();
OSTaskCreate(Task1, (void *)0, &Task1Stk[511], 5);
OSTaskCreate(Task2, (void *)0, &Task2Stk[511], 6);
OSStart();
return 0;
}
四、键盘扫描函数详解(官方样题重点!)
考频:★★★★★(综合题 13~18 空全考)
键盘矩阵原理
矩阵键盘用"行列扫描"方式检测按键: - 行线:连接 GPIO 输出引脚(逐行拉低) - 列线:连接 GPIO 输入引脚(读取哪列为低)
以 4×3 矩阵键盘(4行3列)为例: - 行线:GPE0、GPE1、GPE2、GPE3(4根输出) - 列线:GPD0、GPD1、GPD2(3根输入)
扫描思路:依次让每一行输出低电平(其余行高电平),然后读取所有列的值,如果某列为低电平,说明该行该列对应的按键被按下。
官方样题键盘扫描代码解析
INT16U ScanKey() {
int i;
INT8U temp;
/*
* 第 (13) 空:初始化 GPD0~GPD2 为输入模式
*
* GPD0~GPD2 是列线,需要配置为"输入"(编码 00)
* 每个引脚占用 GPDxCON 的 2 位:
* GPD0 → 位[1:0]
* GPD1 → 位[3:2]
* GPD2 → 位[5:4]
* 共 6 位(3引脚 × 2位)需要清零
* 清掉低 6 位的掩码:~(0x3F) = 0xFFFFFFC0
*/
rGPDCON = rGPDCON & 0xFFFFFFC0; // (13) 答案:0xFFFFFFC0
/*
* 第 (14) 空:初始化 GPE0~GPE3 为输出模式
*
* GPE0~GPE3 是行线,需要配置为"输出"(编码 01)
* 每个引脚占用 GPExCON 的 2 位:
* GPE0 → 位[1:0] = 01 → 0x01
* GPE1 → 位[3:2] = 01 → 0x04
* GPE2 → 位[5:4] = 01 → 0x10
* GPE3 → 位[7:6] = 01 → 0x40
* 合并:0x01 | 0x04 | 0x10 | 0x40 = 0x55
* 保留高位原值:掩码 0xFFFFFF00
*/
rGPECON = (rGPECON & 0xFFFFFF00) | 0x00000055;
// ↑(14)=0xFFFFFF00
/*
* 第 (15) 空:扫描循环控制值
*
* 需要扫描 4 行,i 依次取 1, 2, 4, 8(每次左移1位)
* 循环条件:i <= 8
* 即:for (i=1; i<=8; i<<=1)
*/
for (i = 1; i <= 8; i <<= 1) // (15) 答案:8
{
/* 将当前行拉低(取反后输出),其他行保持高电平 */
rGPEDAT = ~i & 0x0F;
/* 读取列线状态(GPD0~GPD2 共3位) */
temp = rGPDDAT & 0x07;
/*
* 第 (16) 空:判断无按键的条件
*
* 3根列线全为高电平 = 无按键
* 3位全1 = 0x07
* 所以 (temp & 0x07) == 0x07 表示无按键
*/
if ((temp & 0x07) != 0x07) // (16) 答案:0x07
{
/* 有按键按下!构造返回值 */
/*
* 返回值格式:高8位=行信息,低8位=列信息
* 行信息:~i(当前扫描行的行号取反)
* 列信息:temp | 0xF8(将高5位填1,只保留低3位列信息)
*/
return ((~i) << 8) | (temp | 0xF8);
}
}
/*
* 第 (17) 空:无任何按键按下时的返回值
*
* 扫描完所有行都没有检测到按键,返回 0xFFFF 表示无键
*/
return 0xFFFF; // (17) 答案:0xFFFF
}
各空答案汇总
| 空号 | 答案 | 原因说明 |
|---|---|---|
| (13) | 0xFFFFFFC0 |
清除 GPD0~GPD2 的 6 位配置(清低 6 位),配置为输入 |
| (14) | 0xFFFFFF00 |
保留 GPEDCON 的高 24 位,低 8 位重新配置(GPE0~GPE3 输出) |
| (15) | 8 |
扫描 4 行,i 依次为 1, 2, 4, 8,循环到 i<=8 |
| (16) | 0x07 |
3 列全高(0b111 = 0x07)= 无按键 |
| (17) | 0xFFFF |
无任何按键按下的特殊返回值(全1) |
第 (18) 空:计算按键返回值
例题:若第 3 行第 2 列的按键被按下,
ScanKey()的返回值是多少?
解题步骤:
Step 1:确定行号对应的 i 值
循环中 i 的取值: - 第 1 行:i = 1(0b0001) - 第 2 行:i = 2(0b0010) - 第 3 行:i = 4(0b0100) - 第 4 行:i = 8(0b1000)
所以第 3 行对应 i = 4。
Step 2:计算行信息(高 8 位)
Step 3:确定列号对应的 temp 值
列线是 GPD0、GPD1、GPD2,对应 temp 的位 0、1、2: - 第 1 列:GPD0 = 0,temp 位 0 = 0 - 第 2 列:GPD1 = 0,temp 位 1 = 0 - 第 3 列:GPD2 = 0,temp 位 2 = 0
第 2 列按下,GPD1(位1)= 0,其余列为高:
Step 4:计算列信息(低 8 位)
Step 5:合并返回值
(18) 答案:
0xFBFD
五、官方样题 61 题完整解析(综合题框架)
题目背景
以 S3C2410 为核心的嵌入式系统,运行 μC/OS-II,实现键盘控制功能。
第 19 空(μC/OS-II 创建任务函数)
int main(void) {
OSInit();
_______(TaskKey, (void *)0, &KeyStk[STK_SIZE-1], 10); // 第19空
_______(TaskDisp, (void *)0, &DispStk[STK_SIZE-1], 11);
OSStart();
return 0;
}
答案:OSTaskCreate
第 20 空(启动多任务调度)
int main(void) {
OSInit();
OSTaskCreate(TaskKey, (void *)0, &KeyStk[STK_SIZE-1], 10);
OSTaskCreate(TaskDisp, (void *)0, &DispStk[STK_SIZE-1], 11);
_______; // 第20空
return 0;
}
答案:OSStart()
六、解题技巧总结
[!TIP] 键盘扫描题解题步骤:
- 看GPIO配置:哪些脚做输入(列线),哪些脚做输出(行线)
- 算掩码:清除/设置对应位,注意每个引脚占 2 位
- 看循环条件:i 的最大值 = 最后一行对应的值(4行就是8)
- 看判断条件:全高电平 = 无键,用 0x07(3列)或 0x0F(4列)
- 算返回值:
(~行号) << 8 | (列读值 | 0xF8)[!NOTE] μC/OS-II 综合题固定套路:
OSInit()→OSTaskCreate()× N →OSStart()时钟节拍初始化写在某个任务函数里(OSStart 之后执行)。
本章小结
开发方式:
裸机开发 → 直接操作寄存器,简单循环
μC/OS-II → OSInit + OSTaskCreate + OSStart
GPIO 配置规律:
输入 = 00,输出 = 01(每引脚2位)
清N位掩码 = ~((1<<(2N))-1)
键盘扫描关键:
行线输出(逐行拉低),列线输入(读取)
无键 = 所有列为1 = 0x07(3列)
有键返回:(~行) << 8 | (列值 | 0xF8)
无键返回:0xFFFF
综合题必填:
第19空 = OSTaskCreate
第20空 = OSStart
真题演练
以下是NCRE三级嵌入式真题中与本节相关的典型考题:
1. 下列关于嵌入式系统的软件结构的描述中,错误的是( )。
- A)最简单的软件结构是轮询结构
- B)选择嵌入式系统软件结构的基本原则是:尽可能选择通用操作系统+应用软件结构,降低应用系统设计的复杂度
- C)小型的监控式嵌入式操作系统通常只包含内核,仅实现任务调度、任务间通信和中断管理等最基本功能
- D)带中断的轮询结构也称为前后台结构
答案解析
答:B 选择嵌入式系统软件结构的基本原则是:可以满足响应时间需求的最简单的结构,而不是"尽可能选择通用操作系统"。嵌入式系统设计最重要的一点是满足系统对功能、成本、功耗、尺寸等方面的具体需求,软件结构并没有优劣之分,需要根据具体应用需求选择。
2. 下面关于ADS1.2开发工具的说法中,不正确的是( )。
- A)利用ADS1.2工具建立的应用工程项目中,至少应包括一个Release生成目标
- B)ADS1.2工具完成应用程序连接时,可按照Scattered类型的文件进行地址映射
- C)ADS1.2工具把被编译后的信息,按照RO段或RW段或ZI段来存储
- D)对于地址映射关系简单的工程项目,ADS1.2可以根据Simple连接类型的参数来完成应用程序的地址映射
答案解析
答:A 在ADS1.2的集成开发环境中,一个工程项目中至少应包含一个生成目标(不是特指Release)。ARM提供的可执行输出文件的模板包括了3个生成目标:Debug、Release、DebugRel。A项说"至少应包括一个Release生成目标"表述不准确。
3. 【填空题】嵌入式系统开发过程中,需求分析阶段需要对用户的功能需求进行分析。验证模块/系统的功能和性能,发现错误是_的目的。分析所发现的错误,检查错误原因,定位故障位置和进行修改是_的目的。
答案
答:测试(test);调试(debug) 测试的目的是验证模块/系统的功能和性能,同时发现错误;调试的任务是分析测试中发现的错误,检查原因,定位故障(错误)位置,采取适当措施修改软硬件,然后返回重新进行测试。