文章目录
- 1、选择题
- 1.1
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6
- 1.7
- 1.8
- 1.9
- 1.10
- 2、简答题
- 2.1
- 2.2
- 2.3
- 2.4
- 2.5
- 2.6
- 2.7
- 2.8
- 3、编程题
- 3.1
岗位:云台嵌入式工程师-2025校招
题型:10 道选择题,8 道简答题,1 道编程题
1、选择题
1.1
【多选】以下关于DMA的描述哪些是准确的? (BCE)
- 控制器不能独立于 CPU 运行,需要 CPU 提供时钟信号
- DMA 控制器独立于 CPU 进行工作,将输入输出设备的数据直接送到内存,无需 CPU 参与,节省了中断处理的时间
- DMA 总是在数据准备好时立即开始,无论 CPU 状态如何
- 即使启用 DMA,CPU 仍然必须介入,将数据从输入输出设备传送到 DMA 缓冲区
- DMA 传输时,源地址和目标地址内存对齐是为了数据传输的效率和稳定性
解答:
DMA,直接内存读取,独立于 CPU 运行,无需 CPU 提供时钟信号,也无需 CPU 的介入,选项 A、D 错误
1.2
【单选】C++ 中 const
关键词的作用描述不正确的是 (D)
const
函数不会修改类的数据成员const
成员变量不能被修改const
函数只能调用 const 函数const
函数可以调用任意函数
解答:
const
关键词用于修饰常量,不可修改,选项 A 正确
const
函数不能修改类的非 mutable 成员变量,选项 B 正确
const
函数只能调用 const 函数,因为如果调用其他函数,可能会对成员变量产生修改,选项 C 正确,选项 D 错误
1.3
【单选】设有一个空栈,现有输入序列为 1,2,3,4,5
经过 push,push,pop,push,pop,push,pop,push 后,栈还存储什么值? (C)
- 1,3
- 1,2
- 1,5
- 1,4
解答:
1 -> push
2 -> push -> pop -> 2
3 -> push -> pop -> 3
4 -> push -> pop -> 4
5 -> push
1.4
【单选】当需要将一个 GPIO 引脚设置为输出模式时,以下哪个位需要设置为 1 (B)
- 中断使能寄存器(IE)
- 方向控制寄存器(DDR)
- 输入数据寄存器(PIN)
- 输出数据寄存器(OUT)
解答:
IE 用于设置 GPIO 中断是否使能
DDR 用于设置 GPIO 输入/输出
PIN 用于设置输入模式下输入的数据
OUT 用于设置输出模式下输出的数据
1.5
【单选】C 语言中以下错误的表达式为 (B)
struct {
int a;
char b;
}Q, *p=&Q;
Q.a
*p.b
p->a
(*p).b
解答:
结构体对象使用 . 访问成员变量
结构体对象指针使用 -> 访问成员变量
Q 是一个结构体对象,Q.a
,选项 A 正确
p 是一个结构体指针,*p
是 Q ,(*p).b
,p->a
,选项 C、D 正确
.
的优先级高于*
, 因此*p.b
会先进行p.b
导致选项 B 错误
1.6
【单选】执行语句 k=7>>1;
后,变量 k 的当前值是 (B)
- 15
- 3
- 1
- 31
解答:
>>
运算符优先级高于=
7 >> 1
,意味着 7 右移 1 位(相当于除 2),结果为 3
K = 3
1.7
【单选】以下关于数字滤波的作用中,错误的是 (A)
- 增加信号的带宽
- 提取特定频率成分
- 信号重建
- 去除噪声
解答:
数字滤波不能增加信号的带宽
1.8
【单选】函数 fun()
的正确定义方式为 (A)
int main(){
int a[3][4];
fun(a);
return 0;
}
void fun(int p[][4]){}
void fun(int *p[][4]){}
void fun(int *p[4]){}
void fun(int *p[3][4]){}
解答:
观察四个选项不同之处,这道题主要是考察函数参数列表
int a[3][4];
这里 a 应该是一个二维数组指针,类型为int (*)[4]
int p[][4]
类型为int (*)[4]
✅
int *p[][4]
类型为int* (*)[4]
❌
int *p[4]
类型为int **
❌
int *p[3][4]
类型为int* (*)[4]
❌
1.9
【多选】以下关于 RTOS 中优先级翻转描述中,哪些是正确的 (AC)
- 优先级反转问题可以通过优先级继承或优先级上限等方式来解决
- 优先级翻转可以通过使用信号量或互斥锁来避免
- 优先级翻转是指高优先级任务被低优先级任务阻塞的现象
- 优先级翻转可以通过增加任务的优先级来解决
解答:
优先级反转是指高优先级的任务需要等待低优先级的任务释放资源
在 FreeRTOS 中信号量存在优先级翻转的潜在问题,而互斥量在信号量的基础上增加了优先级继承机制
互斥量能够尽量的避免出现优先级翻转的情况,但不能完全杜绝
综上,选项 A、C 正确,选项 B、D 错误
1.10
【单选】适宜采用 inline 定义函数情况是 (C)
- 函数体含有递归语句
- 函数体含有循环语句
- 函数代码少、频繁调用
- 函数代码多、不常调用
2、简答题
2.1
用变量 a 给出下面的定义:
(示例)一个整型数:int a;
- 一个指向整型数的指针:1
- 一个指向指针的的指针,它指向的指针是指向一个整型:2
- 一个有 10 个整型数的数组:3
- 一个有 10 个指针的数组,该指针是指向一个整型:4
- 一个指向有 10 个整型数数组的指针:5
- 一个指向函数的指针,该函数有一个整型参数和一个浮点型参数,并返回一个整型数:6
解答:
int *a;
int **a;
int a[10];
int *a[10];
int (*a)[10];
int (*a)(int , float);
2.2
请使用宏定义,实现一个 FUN(x) 功能,其需求如下
- 当 x<0 时,返回值为 0
- 当 x=0 时,返回值为 1
- 当 x>0 时,返回值为 2
解答:
#define FUN(x) (x)<0?0:(x==0?1:2))
2.3
以下代码的运行结果是
#include <stdio.h>
int main() {
int *p1, *p2;
int arr[3] = {0};
p1 = arr;
p2 = arr;
p1--;
p2++;
printf("%d, ", p1 - p2);
printf("%d, ", (char*)p1 - (char*)p2);
printf("%d", sizeof(arr) / sizeof(*arr));
return 0;
}
解答:
两个指针相减
p1 - p2 = (指针 p1 地址 - 指针 p2 地址) / sizeof(int) = -2
(char*)p1 - (char*)p2 = (指针 p1 地址 - 指针 p2 地址) / sizeof(char) = -8
sizeof(arr) / sizeof(*arr) = 12 / 4 = 3
最终输出 -2,-8,3
2.4
在一个光滑平面上,一质量为 m 长度为 L 电阻为 R 的铁棒两端用一电阻可忽路的柔性导线连接,铁棒导线系统垂直放置于强度为 B 的磁场中,力 F 垂直作用于铁棒质心拖动铁棒使其运动,请立该系统的动力学模型?
解答(抛砖引玉,非标准答案,存疑):
根据题目可以的出如下信息:
- 光滑平面,表示平面摩擦力为 0
- 根据法拉第电磁感应定律,闭合电路在磁场中切割磁感线会产生电流 I,感应电动势 V
- 电流在磁场中会产生阻力 F2
感应电动势
V = BLv
,其中v
是移动速度
感应电流I = V/R = BLv/R
阻力F2 = BIL = B^2*L^2*v/R
则F-F2 = ma
,其中a
为加速度
也即F-B^2*L^2*v/R = m*dv/dt
2.5
采样定理要求采样率至少是信号(最高)频率的 2 倍,才能保证采样后信号频率不失真,但采样率设置不足时会导致混叠现象的出现,比如用 4Hz 的采样 3Hz 的正弦波会产生混叠,如下图所示:
请问当采样率 fs=500Hz,且原始信号频率 fa 分别是 180Hz,280Hz,580Hz 时,混叠后的频率 fd 分别是多少?
解答(抛砖引玉,非标准答案,存疑):
采样率 500 Hz,能不失真采样的最大频率为 250 Hz
采样率 500 Hz,原始信号频率 180 Hz,500 > 180*2
,满足香农采样定律,混叠后的频率为 180 Hz
采样率 560 Hz,原始信号频率 280 Hz,不满足香农采样定律,280 Hz 超过了 250 Hz,混叠后的频率为|280-500| = 220 Hz
采样率 500 Hz,原始信号频率 580 Hz,不满足香农采样定律,580 Hz 超过了 250 Hz,混叠后的频率为|580-500| = 80 Hz
2.6
描述 I2C、SPI、UART 的工作方式以及差异?
解答(抛砖引玉,非标准答案):
I2C 特点:
- 串行、半双工、近距离、低速、同步通信
- 一般需要 SDA、SCL 两根线
- 是一种多主机总线
- 从机具有唯一的 7 位地址,器件既可以为主机又可以为从机,但同一时刻只能有一个主机
- 标准模式下速率 100Kbit/s,高速模式下速率 400Kbit/s
- 采用开漏结构,通过外部上拉电阻完成逻辑 1
I2C 通信过程:
主机向从机写数据
- Start
- Salve address + Wr ->
- Salve ACK <-
- Register address ->
- Salve ACK <-
- Data ->
- Salve ACK <-
- Repeat steps 6-7 until the data transfer is complete
- Stop
主机从从机读数据
- Start
- Slave address + Wr ->
- Salve ACK <-
- Register address ->
- Salve ACK <-
- Start repeat
- Slave address + Rd ->
- Salve ACK <-
- Data <-
- NACK
- Stop
SPI 特点:
- 串行、全双工、高速、同步通信
- 一般需要 MOSI、MISO、CS、SCLK 四根线
- 无需从机地址,片选选中即可
SPI 通信过程:
- 拉低片选信号
- 通过 MISO、MOSI 两个引脚全双工传递数据
- 通信完毕拉高片选信号
UART 特点:
- UART 是一种串行,全双工,低速,异步通信方式
- UART 需要 RX、TX 两根线
- 帧格式:起始位(逻辑0) + 数据位(5到9位,常见8位) + 校验位 + 停止位(逻辑1,1到2位)
- 传输数据以字节为单位,传输速度由波特率控制(常见9600、115200)
2.7
在 51 或 32 单片机上(例如 AT89C51 或 STM32F4)实现 Bootloader 功能,措述实现的逻辑流程?
解答(抛砖引玉,非标准答案):
何为 Bootloader?
BootLoader 为当单片机上电开始工作后首先运行的程序,他会做一些初始化配置,然后重点设置 NVIC 的偏移量,最后跳转到某个应用程序入口执行用户的应用程序;个人用户一般情况下,使用单片机编写 APP 无需使用 BootLoader,因为一般而言你只需编写一个应用程序,无需引导,后续更新大可直接烧录程序;但是对于一个完整的商品来说,不应该留给用户烧录的接口,而且大多数用户也不会开发,因此需要一个 Bootloader 可以方便的更新固件,修改 BUG 等等。
如何实现 Bootloader?
通常情况下,对于 STM32F4,其程序默认烧录在 0x80000000 的内存地址处,当 MCU 上电之后,他会从该处加载程序然后执行,假设我们将整个内存空间分为几个不同的部分,将 0x80000000 开始的比如 64K 字节数据划分为一个引导区,然后后面的剩余内存空间划分为 APP 区域,当 MCU 上电之后他会先执行引导区的程序,等待一切就绪后在 goto 到用户 APP 区域的内存处开始执行。
需要注意的事情?
NVIC 中断向量表的偏移地址,在跳转到 APP 前必须设置 NVIC 中断向量表的偏移地址,否则在 APP 中发生中断会执 BootLoader 的中断服务函数,而不是 APP 的中断服务函数。
2.8
假设结构体的首地址在 STM32 上为 0x20001000,请按如下格式列出下面结构体在 STM32 中的内存分布:
- a: 0x20001000
- b: 0x2000xxxx
- c: 0x2000xxxx
typedef struct
{
int8_t a;
int16_t b;
int16_t c;
int8_t *p_a;
int16_t *p_b;
uint8_t d;
uint16_t e;
}data_align_t;
解答:
- a: 0x20001000
- b: 0x20001004
- c: 0x20001006
- p_a: 0x20001008
- p_b: 0x2000100C
- d: 0x20001010
- e: 0x20001012
3、编程题
3.1
有一个可 360 度连续测量的角度传感器,其角度输出范国是 (-π,π] ,由于传感器采样具有一定的高斯白噪声,请设计一个滤波算法滤除角度信号中的高频噪声
- 注意角度跳变点
- 滤波系数为可调变量
- 滤波算法不限
解答:
#include <stdio.h>
#include <assert.h>
// 一个匀速旋转的角度输出数据示例
float test_data[] = {
1.5568, 1.6427, 1.7804, 1.8525, 1.9416, 2.1797, 2.3703, 2.4818,
2.3664, 2.5448, 2.6128, 2.7571, 2.7795, 2.8205, 3.0309, 3.0522,
3.1078, -2.7968, -2.8540, -2.5947, -2.5545, -2.6810, -2.3792, -2.2714,
-2.1896, -2.1175, -2.1178, -1.8515, -1.9148, -1.7719, -1.5159, -1.4263,
-1.4625, -1.3878, -1.2006, -1.2393
};
int main()
{
// 请在此处书写代码
return 0;
}