嵌入式面试题目
1、 I²C 总线协议的起始条件:起始条件是 I²C 通信中主设备用来发起一次传输的特定信号序列。它告诉总线上的所有从设备:“请注意,一次通信即将开始,接下来是地址信息”。在起始条件之后,主设备会发送从设备地址和读写方向位,从而选中一个特定的从设备进行后续的数据传输。起始条件的信号定义起始条件在物理信号线上的定义非常明确:2、字符设备文件类型的标志是?在 Linux/Unix 系统中,字符设备文件
一、CVTE(笔试题目)
1、 I²C 总线协议的起始条件:
起始条件是 I²C 通信中主设备用来发起一次传输的特定信号序列。它告诉总线上的所有从设备:“请注意,一次通信即将开始,接下来是地址信息”。
在起始条件之后,主设备会发送从设备地址和读写方向位,从而选中一个特定的从设备进行后续的数据传输。
起始条件的信号定义
起始条件在物理信号线上的定义非常明确:
在 SCL 线为高电平期间,SDA 线发生一个从高电平到低电平的下跳变。
2、字符设备文件类型的标志是?
在 Linux/Unix 系统中,字符设备文件(Character Device File)是与字符设备(如键盘、鼠标、串口、打印机等,以字节流方式逐字符读写数据的设备)对应的文件,其核心标志通过文件类型标识符和设备号来体现。



3、不可能在用户态发生的事件是?
在操作系统中,用户态(User Mode)是应用程序运行的环境,具有较低的权限级别,不能直接访问硬件或执行特权指令。而内核态(Kernel Mode)是操作系统内核运行的环境,具有最高权限,可以处理所有硬件和系统资源。
不可能在用户态发生的事件主要是指那些需要特权级别或直接硬件访问的事件:
-
硬件中断处理:中断是由硬件设备(如键盘、鼠标、定时器)产生的事件,其处理必须由内核态的中断服务程序(ISR)来完成。用户态程序无法直接响应或处理硬件中断。
-
异常处理:异常是程序执行过程中的错误或特殊情况(如除零错误、页面故障、非法指令)。这些事件的处理由内核态的异常处理程序负责,用户态程序无法自行处理。
-
直接执行特权指令:特权指令(如修改页表寄存器、切换CPU模式、直接I/O操作)只能在内核态执行。如果用户态程序尝试执行这些指令,会触发异常,并由内核处理。
-
系统调用的执行:虽然系统调用是由用户态程序发起的(通过软中断或syscall指令),但实际的处理过程(如文件读写、进程创建)是在内核态完成的。因此,系统调用的执行阶段不可能在用户态发生。
4、如何将a.c的文件从'rwx-----权限改为r--rw--wx
要将文件 a.c 的权限从 rwx------ 改为 r--rw--wx,您可以使用 Linux 或 Unix 系统中的 chmod 命令。以下是两种方法:使用数字模式或符号模式。
方法一:使用数字模式
权限数字表示法:
-
读(r) = 4
-
写(w) = 2
-
执行(x) = 1
目标权限 r--rw--wx 分解:
-
所有者(user):r-- = 4
-
组(group):rw- = 4 + 2 = 6
-
其他(other):--wx = 2 + 1 = 3
因此,数字模式为 463。执行以下命令:
chmod 463 a.c
方法二:使用符号模式
直接设置各部分权限:
-
所有者:只读(u=r)
-
组:读和写(g=rw)
-
其他:写和执行(o=wx)
执行以下命令:
chmod u=r,g=rw,o=wx a.c
5、SPI和IIC串行总线




6、IIC理论上可以挂载多少个从器件

7、GPIO可以用于哪些场景?

8、存储器类型有哪些?

9、串口通讯需要配置哪些?

二、各种串口的特点。
1、GPIO
上拉输入VS浮空输入

典型场景选择
浮空输入:
外部信号已自带明确驱动(如数字传感器输出、带外部电阻的按键)
选上拉输入:
需默认高电平且低电平触发的场景(如按键接地设计、I²C总线空闲状态维持)
GPOI初始化

GPIO_Mode_IPU模式借助内部上拉电阻,让引脚在无外部输入时保持高电平,非常适合检测低电平触发的信号,是按键检测的理想选择。

按键中断控制LED






2、USART(通用同步/异步收发器)
核心特点
通信模式:支持全双工异步(UART)和同步模式(需SCLK时钟线)
引脚定义:TX(发送)、RX(接收)、SCLK(同步时钟,可选)
数据帧格式:起始位(1位低电平) + 数据位(5–9位) + 校验位(可选) + 停止位(1/1.5/2位高电平)
波特率范围:最高4.5 Mbps(异步模式),支持精确分频
应用场景:串口调试、蓝牙透传、GPS模块通信
初始化配置


3、SPI(串行外设接口)
核心特点
通信模式:全双工同步通信,主从架构,四线制(SCLK、MOSI、MISO、NSS)•
速率:可达数十MHz(如STM32F4系列)
工作模式:由CPOL(时钟极性)和CPHA(时钟相位)组合成4种模式(模式0最常用)
应用场景:Flash存储器(W25Q128)、OLED屏幕驱动(SSD1306)
4、IIC(集成电路总线)
核心特点
通信模式:半双工同步通信,两线制(SCL时钟线、SDA数据线)
地址寻址:支持多从设备(7位/10位地址),主设备控制总线
速率:标准模式100kbps,快速模式400kbps
应用场景:温湿度传感器(BMP280)、EEPROM(AT24C02)


5、CAN(控制器局域网)
核心特点
通信模式:半双工异步通信,差分信号(CAN_H/CAN_L),抗干扰强
拓扑结构:总线型,支持多主多从(如汽车ECU网络)
错误检测:内置CRC校验、帧错误检测
应用场景:汽车电子、工业控制网络




6、DMA(Direct Memory Access)直接存储器存取
DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源

ROM就是只读存储器,是一种非易失性、掉电不丢失的存储器。
第一块是程序存储器Flash,也就是主闪存,它的用途就是,存储C语言编译后的程序代码,运行程序,一般也是从主闪存里面开始运行的。你之后如果在软件里看到,某个数据的地址是0800开头的,那你就可以确定,它是属于主闪存的数据。在STM32中,使用const定义的变量,是存储在flash里面的
RAM就是随机存储器,是一种易失性、掉电丢失的存储器。
运行内存SRAM,也就是我们在程序中定义变量、数组、结构体的地方。

总线矩阵左边是主动单元,拥有存储器的访问权限;总线右边的是被动单元,它们的存储器只能被左边的主动单元读写。
主动单元这里,内核有DCode和系统总线,可以访问右边的存储器。其中DCode总线是专门访问Flash的,系统总线是访问其他东西的。另外,由于DMA要转运数据,所以DMA也必须要有访问的主动权。那主动单元,除了内核CPU,剩下的就是DMA总线了。

由于Flash是只读的,所以DMA不可以进行SRAM到Flash,或者Flash到Flash的转运操作。如果要进行存储器到存储器的数据转运,那我们就需要把其中一个存储器的地址,放在外设的这个站点,这样就能进行存储器到存储器的转运了。只要你在外设起始地址里写Flash或者SRAM的地址,那它就会去Flash或SRAM找数据。
那这个软件触发和循环模式,不能同时用,因为软件触发就是想把传输计数器清零,循环模式是清零后自动重装,如果同时用的话,那DMA就停不下来了。所以上面这里,M2M位给1,就是软件触发,就是应用在存储器到存储器转运的情况。当M2M位给0,那就是使用硬件触发了,硬件触发源可以选择ADC、串口、定时器等等。使用硬件触发的转运,一般都是与外设有关的转运。
触发一次,转运一次,传输计数器自减一次。当传输计数器等于0,且没有自动重装时,这时无论是否触发,DMA都不会再进行转运了。此时就需要DMA_Cmd,给DISABLE,关闭DMA。再为传输计数器写入一个大于0的数,再DMA_Cmd,给ENABLE,开启DMA。
二、CVTE(嵌软开一面题目)
1、volatile关键字的作用?
回答:volatile 是 C/C++ 中一个非常重要的关键字,它的核心作用是指示编译器,这个变量的值可能会以编译器无法预知的方式发生改变,从而禁止编译器对该变量的访问进行一些激进的优化。
主要应用场景可以归纳为以下三个方面:
第一,也是最重要的场景:访问内存映射的硬件寄存器。
在嵌入式开发中,我们通过读写特定内存地址来配置和控制硬件。这些寄存器的值会随着硬件状态的改变而改变,与程序的执行流无关。存储器映射的硬件寄存器应添加volatile修饰,确保编译器不会优化掉与硬件通信的关键代码
第二:在中断服务程序 (ISR) 中修改的全局变量。
当一个全局变量既被主循环读取,又被 ISR 写入时,编译器可能察觉不到主循环中的变量会被 ISR 修改。该变量通常应声明为volatile,因为ISR可以在主程序执行的任意时刻修改该变量的值。
第三:多线程应用中的共享变量。
在多任务或RTOS环境中,一个全局变量可能被多个任务读写。与中断场景类似,编译器不知道何时会发生任务切换,从而可能对变量访问做出错误的优化假设。
共享变量应使用volatile。这保证了每次访问变量时都会重新从内存中读取其最新值,而非使用寄存器中缓存的值。

volatile 关键字就是用来告诉编译器:“这个变量是易变的,不要对它做任何假设和优化”。

2.struct字节对齐,给了几个变量计算所占字节数?
结构体的字节对齐(或称内存对齐)是编译器为了提升CPU访问内存的效率而采取的一种策略。基本原则是:结构体中每个成员的起始地址必须是其自身类型大小或编译器指定对齐模数(两者中较小的那个)的整数倍。整个结构体的大小也必须是其所有成员中对齐模数最大的那个的整数倍。
对齐模数(Alignment Modulus),有时也称为对齐边界(Alignment Boundary) 或对齐系数,是内存对齐规则中的基准单位。
您可以把它理解为一把“尺子”上的刻度。这把尺子用来度量内存地址。编译器规定:特定类型的数据的起始内存地址,必须是其“对齐模数”的整数倍。
对齐模数 = Min(编译器默认对齐值, 该类型本身的大小)


3、static局部变量存储位置?
static 局部变量的存储位置是 “静态存储区”(也叫全局数据区、静态数据区)。在 C/C++ 等语言中,程序的内存空间通常分为代码区、栈区、堆区、静态数据区和常量区。
代码区存指令,栈区存临时数据(自动管理),堆区存动态数据(手动管理),静态数据区存全局 / 静态变量,常量区存不可修改的常量。这些区域的划分本质是为了更高效地管理内存,适配不同数据的生命周期和访问需求


4、数组越界有什么危害?
最直接的危害是导致程序运行异常甚至崩溃。数组越界可能引发数据安全漏洞。部分场景下越界可能暂时 “无明显异常”,留下隐性隐患
5、栈溢出有什么危害?如何解决?堆和栈的区别?
我们常说的 “栈泄漏相关危害”,本质是栈内存的异常消耗或非法使用。
最直接危害:触发程序崩溃,导致运行中断。
核心危害:破坏数据完整性,导致逻辑错乱
![]()
隐性危害:调试难度极高,故障定位成本高
栈相关的异常(本质是 “栈空间被非法使用”)不像堆泄漏那样有明确的 “未释放内存记录”,其危害往往具有 “非即时性” 和 “关联性弱” 的特点,导致调试难度极大。
危害:栈溢出会覆盖栈中存储的函数返回地址、参数和局部变量,导致程序跳转至非法地址引发崩溃;还会破坏相邻内存区域(如全局变量区),造成数据错乱或逻辑异常,甚至被利用进行缓冲区溢出攻击。
解决方法:从开发层面,避免定义超大局部数组、控制递归深度;从系统层面,可设置栈空间大小上限、启用栈保护机制(如 GCC 的 - fstack-protector);运行时通过栈边界检测工具(如 Valgrind)提前发现溢出风险。
堆和栈的核心区别主要体现在内存管理方式、用途、特性三个维度:
从管理方式看,栈由编译器自动分配释放(如函数调用时的局部变量),遵循 “先进后出” 规则;堆需程序员手动申请释放(如 C 中的 malloc/free),分配释放顺序灵活,需程序员控制。
从用途和特性看,栈用于存储函数参数、局部变量和调用上下文,空间较小(通常几 MB),速度快;堆用于动态分配内存(如动态数组、对象),空间较大(可达 GB 级),但分配释放效率较低,易产生内存碎片。
6、递归在单片机中使用会有什么问题?
递归会持续消耗单片机有限的栈空间,极易导致栈溢出。
递归可能破坏单片机的实时性,导致任务响应延迟。
递归可能引入隐蔽的稳定性问题,增加调试难度。
单片机系统通常没有完善的内存保护机制,栈溢出等问题的表现往往具有随机性。
7、GPIO推挽和开漏输出的区别?

1. 外部电路依赖不同
推挽输出:无需外部电阻辅助。因为内部晶体管可直接驱动高 / 低电平
开漏输出:必须外接上拉电阻才能输出高电平。若无上拉电阻,引脚在高电平时处于悬空状态(高阻态),无法提供高电平信号,甚至可能受干扰出现不稳定电平。

2. 驱动能力与应用场景适配不同
推挽输出:驱动能力更强,且双向驱动(高电平向外供电、低电平吸收电流),适合需要 “强驱动、快速电平切换” 的场景
开漏输出:
驱动能力受限于 NPN 管和上拉电阻,但有两个独特优势:“线与” 逻辑和电平匹配,适合特定场景


8、epwm的设置,给一个场景如何手动实现epwm?
手动实现 EPWM(增强型脉冲宽度调制)需要结合定时器、比较器和 GPIO 的协同控制,核心是通过精确控制高低电平的持续时间来生成指定占空比和频率的脉冲信号。
在 STM32 上用标准库手动实现 EPWM 控制 LED 呼吸灯,核心是通过定时器中断配合 GPIO 电平翻转,动态调整占空比来实现亮度渐变

9、上升沿下降沿触发

10、数码管扫描频率?共阴极和共阳极的区别?
数码管阴极和共阳极数码管的区别在于公共端接法与点亮逻辑:共阴公共端接 GND,高电平点亮段;共阳公共端接 VCC,低电平点亮段,段码互为反码。
数码管扫描频率需在 24-100Hz 之间,既避免闪烁(低于 24Hz),又不过度占用 CPU 资源(高于 100Hz)。

10、ad采样位数代表什么?什么因素影响 ADC精度和转换速率?有什么滤波算法?
-
AD 采样位数:代表 ADC 能将模拟信号量化为数字量的精细程度,位数越高,量化台阶越小(如 12 位 ADC 将满量程分为 4096 级),理论上能区分的信号差异越细微。
-
影响 ADC 精度和转换速率的因素:
- 精度:参考电压稳定性、非线性误差、温漂、输入信号噪声、PCB 布局(如电源干扰);
- 转换速率:ADC 自身时钟频率、转换模式(逐次逼近 / 流水线等)、是否启用硬件平均。
-
常用滤波算法:简单移动平均、加权平均、中位值滤波(抗脉冲干扰)、限幅滤波(抑制突变噪声)、IIR/FIR 数字滤波(适合特定频率噪声)。
11、中断如何设置?中断中任务过多有什么危害?
-
中断设置核心步骤:先使能对应外设中断(如定时器、GPIO),配置中断触发条件(如上升沿、溢出),再设置中断优先级,最后开启总中断,并编写中断服务函数(ISR)处理核心逻辑。
-
中断中任务过多的危害:会导致中断服务函数执行时间过长,可能错过其他高优先级中断(丢失中断),还会占用过多 CPU 资源,打乱主程序时序,甚至引发系统卡顿、数据处理异常。
12、波特率是什么?
面试官您好!波特率是串行通信中单位时间内传输的 “码元” 数量,单位是 “波特(Baud)”,核心作用是定义通信双方的 “数据传输速率基准”—— 只有收发双方波特率一致,才能正确同步和解码数据。
比如常用的 9600 波特率,代表每秒传输 9600 个码元;若每个码元对应 1 位二进制数(如 UART 通信),则此时数据传输速率约为 9600 位 / 秒(bps),是串行通信(如串口、RS485)中最基础的配置参数之一。
13、串口通信起始位如何判定?i2c总线仲裁?
-
串口通信起始位判定:串口空闲时为高电平,当检测到线路从高电平跳变为低电平并持续 1 个波特率周期(如 9600 波特率下约 104μs),即判定为起始位,以此同步后续数据位的采样时机。
-
I2C 总线仲裁:多主设备同时发送数据时,通过 “线与” 逻辑仲裁 —— 总线上的信号为各设备输出的逻辑与,若某设备检测到自己输出高电平 但总线实际为低,说明仲裁失败,立即转为从机模式,最终只有一个主设备保持控制。
- 总线上的所有主机同时发送数据时,每个时钟周期(SCL 高电平期间)对比 SDA 线上的电平;
- 若主机发送的电平与总线上实际检测到的电平一致,继续参与仲裁;
- 若主机发送的是高电平,但检测到总线为低电平(说明其他主机发送了低电平(SDA低电平可以通信)),则该主机立即退出仲裁(停止发送数据,转为从机模式监听总线);
- 最终只有一个主机能始终保持发送的电平与总线一致,成为获胜者,继续完成通信。
I2C 从机地址的构成(协议基础)
“7 位从机地址” 与 “读写控制位” 组合成一个 8 位字节,符合 I2C 协议对地址传输的规定。
高 7 位:从机的实际地址,
最低位(bit0):读写控制位 ——0表示 “写操作”(主机向从机发送数据),1表示 “读操作”(主机从从机接收数据)。
(slave_addr << 1):将 7 位地址左移,腾出最低位,| 0 或 | 1:设置最低位的读写控制位。
<< 1 是左移 1 位操作,将 7 位地址 “推到” 高 7 位,最低位(bit0)变为读写标志位。
14、中断函数可不可以有参数、返回值,在中断中可以进行 printf函数打印么?
-
中断函数能否有参数和返回值:
不能。中断是异步触发的(由硬件事件随机触发),而非像普通函数那样通过代码主动调用,因此无法预先传递参数;同时,中断服务函数的执行上下文是系统自动切换的,没有调用者等待其返回值,故返回值也无意义。 -
中断中能否使用 printf 打印:
不建议。printf 通常依赖串口等外设,其底层实现包含复杂的缓冲操作和中断使能 / 禁止逻辑,可能导致:① 中断服务函数执行时间过长,阻塞其他高优先级中断;② 若 printf 使用的外设中断与当前中断存在嵌套或资源冲突,可能引发死锁;③ 频繁在中断中打印会占用大量 CPU 资源,破坏实时性。
如需输出调试信息,应采用 “中断中缓存数据,主循环中打印” 的异步方式。
三、海信(软嵌笔试)

sizeof()是计算变量或者数据类型所占的字节数,注意:所有字符串后面都有一个结束符,
所以sizeof(str)= 12, “\0”是字符串结束标志
strlen 是计算字符串的长度,遇到 “\0”就停止计数。

要逐步分析 process 函数的调用过程以及静态数组 cache 的值变化:步骤 1:第一次调用 process(data, 4)。此时 cache 为 [2, 8, 18],p1 指向该 cache。
步骤 2:第二次调用 process(p1, 3),此时 arr 是第一次调用得到的 cache(即 [2, 8, 18]),n = 3。
再次计算静态数组 cache[3](静态数组会保留上一次调用的结果,本次会覆盖原有值):
此时 cache 为 [2, 16, 54],p2 指向该 cache。
步骤 3:输出 p2[2]
p2 指向的 cache 中,索引为 2 的元素是 54,因此 printf 输出 54。

函数中循环的范围是 range(1, int(sqrt(n)) + 1),即循环变量 i 从 1 遍历到 根号n (包含根号n )。
循环的执行次数与 根号n成正比,因此时间复杂度由循环次数主导。
结论该函数的时间复杂度为 O(根号n) 。


- 选项 A:
grep "ERROR" error.log | head -n 3- 首先
grep "ERROR" error.log找出所有含"ERROR"的行,然后head -n 3取这些行的前 3 行。但题目要的是含"ERROR"的最后 3 行,所以 A 不符合。
- 首先
- 选项 B:
tail -n 3 error.log | grep "ERROR"- 先
tail -n 3 error.log取error.log文件的最后 3 行,然后grep "ERROR"在这最后 3 行里找含"ERROR"的行。看文件内容,最后 3 行里只有 1 行([2023-10-10] ERROR: Network timeout)含"ERROR",得不到最后 3 行含"ERROR"的内容,所以 B 不符合。
- 先
- 选项 C:
grep "ERROR" error.log | tail 3- 先
grep "ERROR" error.log找出所有含"ERROR"的行,这里有 2 行([2023-10-10] ERROR: Disk full和[2023-10-10] ERROR: Network timeout),然后tail 3取这些行的最后 3 行(因为含"ERROR"的行只有 2 行,所以就是这 2 行),符合 “显示包含"ERROR"的最后 3 行” 的要求,所以 C 正确。
- 先
8、关于单片机中的C语言程序运用,说法正确的是?
IAR 工程支持包含多个 C 程序源文件(.c 文件),可将不同功能的代码拆分到多个源文件中,再通过工程进行整合管理,因此 B 正确。
中断服务函数通常通过编译器特定的语法(如 IAR 中的中断属性声明)来定义,而非单纯用 C 语言 “单独声明”,
9、上拉电阻和下拉电阻的特性

A错误,上拉电阻和下拉电阻的作用是提高芯片输入信号的噪声容限。
10、Linux修改文件权限的命令

在 Linux 中,chmod命令用于修改文件权限。
- 选项 A:
u+x仅给文件所有者添加执行权限,不是所有用户,不符合。 - 选项 B:
-x是移除执行权限,而非 “添加”,不符合。 - 选项 C:
+x在不指定用户时,默认给所有用户(所有者、所属组、其他用户)添加执行权限,符合要求。 - 选项 D:
644对应的权限是 “所有者读 / 写,组用户 / 其他用户只读”,不含执行权限,不符合。
11、RTOS管理空调遥控器任务时,压缩机启停控制任务设置为:
在RTOS管理空调控制器任务时,压缩机启停控制任务是一个关键任务,需要及时响应和高度可靠性。选项A(动态优先级+看门狗监控)是最合适的,原因如下:
-
动态优先级:允许任务优先级根据系统状态(如温度变化或紧急情况)动态调整,确保压缩机控制能及时响应关键事件。
-
看门狗监控:提供故障检测和恢复机制,防止任务卡死或出现异常,增强系统的稳定性和安全性。
其他选项不足:
-
B(中等优先级 + 极小堆栈):极小堆栈可能导致堆栈溢出,任务崩溃,风险高。
-
C(最低优先级+大堆栈):最低优先级会导致响应延迟,影响实时性;大堆栈可能浪费内存。
-
D(固定中等优先级+互斥锁):固定优先级可能无法适应动态需求;互斥锁虽可保护共享资源,但不是压缩机控制的核心需求。
12、在EMC测试中系统因RS485通讯重启,软件上应首先优化:
在EMC测试中,系统因RS485通讯重启通常是由于电磁干扰导致通讯错误(如帧错误、噪声错误等),进而引发未处理的异常或看门狗复位。从软件优化角度,应首先启用串口帧错误中断处理(选项D),以便及时检测和处理通讯错误,避免错误累积导致系统重启。这可以通过在中断服务程序中处理错误、重置通讯状态或实施恢复机制来提高系统的鲁棒性。
其他选项虽有一定作用,但并非首选:
-
A(配置I/O口速度为低速模式)更多涉及硬件配置,且降低速度可能影响通讯速率。
-
B(增加数据包重传机制)是错误后补救措施,但无法直接防止错误引发的系统重启。
-
C(增大串口接收超时时间)可能缓解短暂干扰,但治标不治本。
13、While、switch语句


这里i++,意味着先使用i,再对i自加。

在C语言中,continue语句用于在循环中跳过当前迭代的剩余代码,并直接进入下一次迭代的条件检查。具体来说,当程序执行到continue时,它会立即跳转到循环的末尾(即跳过循环体中continue之后的语句),然后进行循环条件判断,决定是否开始下一次迭代。

14、暂存区(栈)的最小设计容量?


15、栈的特性

栈在 “需要回溯、逆序处理” 的场景中非常高效(如函数调用、括号匹配、浏览器前进后退等)


典型应用场景
栈的特性使其在计算机科学中无处不在:
-
函数调用栈 (Function Call Stack):系统使用栈来管理函数调用。每次调用新函数时,将其状态(返回地址、局部变量等)压入栈;函数返回时,弹出栈顶状态以恢复上一级函数。
-
表达式求值:编译器中用于将中缀表达式转换为后缀表达式,并基于栈进行求值(处理运算符优先级)。
-
括号匹配:检查代码中的括号(
(),[],{})是否成对正确闭合。 -
撤销 (Undo) 功能:编辑器中的撤销操作,将用户操作按顺序压入栈,撤销时弹出最近的操作。
-
浏览器前进/后退:使用两个栈(前进栈和后退栈)来记录访问历史。
-
深度优先搜索 (DFS):在图和树的遍历中,使用栈来记录待访问的节点。
16、为洗衣机电机设计PWM死区时间,主要依据是:?

根据PWM死区时间的设计原理,死区时间主要用于防止H桥电路中的上下桥臂MOSFET同时导通,避免短路现象。死区时间的设置需要基于MOSFET的开关特性,特别是关断延时,因为关断延时决定了MOSFET从关断信号发出到完全关断所需的时间。因此,设计死区时间的主要依据是H桥MOSFET的关断延时。
选项分析:
-
A. 电机线圈电感量:电感量影响电流变化率,但不是死区时间的直接依据。
-
B. ADC电流采样周期:与电流测量相关,与死区时间无关。
-
C. H桥MOSFET关断延时:直接决定死区时间的长短,以确保安全切换。
-
D. 转子位置检测频率:与电机控制相关,但非死区时间设计依据。
17、代码题





18、代码题




四、简历的相关技能提问?
1、volatile和static关键字的作用?请举一个实际项目中的例子。
-
volatile:告诉编译器这个变量可能会被程序之外的代理(如中断、DMA)修改,要求编译器禁止对其做任何优化,每次访问都必须直接从内存中读取。-
例子:在我的智能中控项目中,一个在
main循环中检查的flag变量,会在串口接收中断里被修改。这个flag必须声明为volatile,否则编译器可能优化为只从寄存器读取一次,导致主循环永远看不到中断里的改变。
-
-
static:有两个主要作用:1) 限制作用域(在函数内表示该变量只在本文件内可见);2) 延长生命周期(在函数内表示该变量在函数调用结束后依然存在,下次调用保持原值)。-
例子:在驱动模块的
.c文件里,我常将模块内部使用的全局变量和函数声明为static,避免污染全局命名空间,体现封装思想。在函数内,我曾用static变量来记录某个传感器的上次数采时间,用于计算采样间隔。
-
2、在STM32上如何排查内存泄漏?
-
监控堆空间:FreeRTOS 提供了
xPortGetFreeHeapSize()等函数,我会在任务中周期性地打印剩余堆空间大小。如果这个值持续下降,就基本断定存在泄漏。 -
代码审查:重点检查
pvPortMalloc()和vPortFree()是否成对出现。尤其是在错误处理分支上,是否也正确地释放了内存。 -
封装与记录:在项目后期,我会封装自己的内存分配函数,在其中加入统计信息,比如记录分配的内存大小、所在文件名和行号,这样就能快速定位是哪个地方的分配没有释放
4. 请描述一下一般你在哪个场景使用了DMA?它的好处是什么?配置DMA传输需要注意哪些关键点?
5. I2C总线锁死了,你如何诊断和恢复?
-
诊断:首先抓取波形,确认是哪根线被拉死。
-
软件恢复:
-
尝试软件复位I2C外设(先
DeInit再Init)。 -
如果无效,我会尝试模拟IO口的方式,手动控制SCL线产生时钟脉冲(比如产生9个时钟脉冲),目的是让从设备完成它未完成的操作(比如写完一个字节),从而释放SDA线。这是一个非常经典的恢复方法。
-
-
预防:在驱动层为所有I2C操作添加超时重试机制,并配置硬件看门狗,避免整个系统因I2C卡死而崩溃。
6. 如何为不同的外设分配中断优先级?
-
最高优先级:系统心跳(如SysTick)、看门狗、硬件错误(HardFault)。这些关系到系统存亡。
-
高优先级:通信类外设的TX/RX中断(如SPI、UART的DMA传输完成中断)。保证数据流不中断。
-
中等优先级:外部中断(如按键、传感器触发)。允许稍慢的响应。
-
低优先级:普通定时器中断。
同时,我会注意中断嵌套问题,避免高优先级中断长时间阻塞低优先级中断。
关于FreeRTOS(绝对重点)
7. 在你的项目中,具体创建了哪些任务?它们的优先级是如何规划的?

8. 任务之间是如何通信的?为什么选择消息队列而不是全局变量?
我的数据采集任务和网络发送任务之间通过消息队列(Queue) 通信。
-
流程:采集任务将传感器数据打包成一个结构体,通过
xQueueSend()发送到队列。网络任务在另一端通过xQueueReceive()阻塞地等待这个队列,有数据就取出发送。 -
为什么是队列?
-
安全性:队列自带互斥保护,避免了使用全局变量可能引发的竞态条件(Data Race)。
-
解耦性:生产者和消费者无需知道对方的存在,只需操作队列即可,实现了任务间的解耦。
-
高效性:当队列空时,接收任务会自动进入阻塞状态,让出CPU时间,而不是忙等待,节省了系统资源。
-
9. 什么是优先级反转?FreeRTOS中是如何解决这个问题的?
-
优先级反转:指一个低优先级任务(L)持有一个高优先级任务(H)所需要的资源(如互斥锁),但低优先级任务又被一个中优先级任务(M)抢占,导致高优先级任务H反而被迫等待低优先级任务L执行,仿佛其优先级被“反转”了。
-
解决方案:FreeRTOS中的互斥锁(Mutex)具有优先级继承机制。
-
当H请求被L占有的Mutex时,系统会临时将L的优先级提升到与H相同。
-
这样L就能尽快执行,释放Mutex,从而让H继续执行。
-
一旦L释放了Mutex,它的优先级又会恢复原样。
-
这个机制有效缓解了优先级反转问题,我在访问SPI Flash等共享资源时都使用了优先级继承Mutex。
-
10. 如何判断某个任务的栈大小设置是否合理?
-
观察调试器:FreeRTOS提供了
uxTaskGetStackHighWaterMark()函数,它可以返回任务运行以来剩余栈空间的最小值(即历史高水位线)。我会在任务运行一段时间后打印这个值,如果它很小(例如小于总栈空间的20%),就说明栈分配不足,需要增大。如果一直很大,则可以适当减小以节省内存。 -
经验值:函数调用层次深、局部变量多、使用了printf等耗栈函数的任务,我会给它分配更大的栈空间(如512字)。简单任务(如LED灯)则分配较小空间(如128字)。
关于工具与Linux
关于TCP网络协议
1、首先,能请你简要描述一下TCP三次握手和四次挥手的过程吗?
-
三次握手:目的是为了确认双方的发送和接收能力都正常。
-
客户端发送
SYN包(Seq=X)给服务器,进入SYN_SENT状态。 -
服务器收到后,回复
SYN+ACK包(Seq=Y, Ack=X+1),进入SYN_RCVD状态。 -
客户端再回复一个
ACK包(Ack=Y+1),双方进入established状态,连接建立成功。
-
-
四次挥手:目的是双方都同意关闭连接。
-
主动方(比如我的设备)发送
FIN包(Seq=X),进入FIN_WAIT_1状态。 -
被动方收到
FIN,回复ACK包(Ack=X+1),进入CLOSE_WAIT状态。此时,被动方可能还有数据要发送。 -
被动方数据发送完毕后,也发送一个
FIN包(Seq=Y)给主动方,进入LAST_ACK状态。 -
主动方收到
FIN后,回复ACK包(Ack=Y+1),进入TIME_WAIT状态,等待一段时间后彻底关闭。被动方收到ACK后立即关闭。”
-
2、为什么是三次?为什么关闭是四次?
因为两次握手无法防止已失效的连接请求报文突然又传送到服务器,从而产生错误。第三次握手是对服务器SYN的确认,是建立可靠通信的必要保证。
因为TCP连接是全双工的,关闭时需要双方都确认关闭。当一方发送FIN,只表示它没有数据要发送了,但还可以接收数据。另一方可能还有数据要发送,所以先回一个ACK,等自己数据发完了,再发送FIN,因此需要四步。
五、诺瓦一面
1、SPI与IIC各种优缺点?
SPI(串行外设接口)、全双工、相对简单、高速、可以主从、点对点传输。由时钟极性CPOL和相位CPHA决定模式,常见四种模式:
IIC(集成电路总线)、半双工、中低速、相对复杂、可以主从、多主从传输。拥有硬件应答机制:每个字节(8bit)传输后,接收方都必须发送一个应答位(ACK)或非应答位(NACK)。这为通信提供了基本的可靠性保障,主设备可以立刻知道从设备是否在线且接收成功。
协议包含起始条件、停止条件、应答位、地址帧、数据帧等。
2、指针能不能用volatile?
指针可以使用volatile关键字,要看volatile修饰的是指针本身还是指针所指向。
嵌入式中一般情况下 1)指针指向一个volatile的地址(最常用):volatile修饰的是指针所指向的内存单元。(比如一个硬件寄存器)的内容可能在任何时候被硬件本身改变,而不是仅由程序代码改变)语法:volatile uint32_t *pReg;
2) 指针本身是volatile的:这意味着pPtr这个变量(它存储的是一个地址值)可能会被程序本身无法预知的方式改变(例如,在一个中断服务程序中修改了它)。
语法:uint32_t *volatile pPtr;
3、GPIO的几种工作模式?
要点:输入(推挽、上拉、下拉)、输出(推挽(NPN、PNP晶体管)、开漏(NPN晶体管)、模拟输入、复用功能(推挽、开漏)。开漏输出常用于IIC总线。
4、定时器的种类?
系统定时器、高级定时器、通用定时器、基本定时器、看门狗

5、软件模拟IIC?
不使用MCU内置的硬件IIC控制器,而是通过程序控制两个普通的GPIO引脚(SDA和SCL)的电平变化,来模拟出IIC协议的时序。
极强的灵活性和可移植性、调试方便
CPU占用率高:这是最大的缺点
软件IIC驱动,本质上是实现一组最基本的GPIO操作函数,并严格按照IIC时序图来调用它们。
1) 硬件初始化:
将选定的两个GPIO引脚(如GPIOB, GPIO_PIN_6为SCL,GPIOB, GPIO_PIN_7为SDA)初始化为开漏输出模式(Open-Drain),并默认拉高。这是因为IIC总线是“线与”结构,开漏模式允许多个设备共享总线。
2)编写底层基本操作函数(核心)
3) 基于基本函数实现数据收发
6、STM32F407低速时钟上的外设?
LSI是大约32kHz的内部RC振荡器、LSE是32.768kHz的外部晶振。STM32F407的低速时钟主要服务于一些对时钟精度和功耗有特殊要求的外设模块,


7、串口空闲状态下是什么电平?
UART协议在无数据传输时,数据线(TX和RX)必须保持在高电平。需要发送一个字节的数据时,发送方会首先产生一个低电平的脉冲,这个脉冲被称为“起始位”
停止位:一个字节的数据位(通常是8位)发送完毕后,发送方会再发送至少1位(可以是1、1.5或2位)的高电平,这被称为“停止位”。
停止位有两个作用:保证每帧数据之间有明确的间隔。确保数据线在帧与帧之间回归到高电平的空闲状态,为下一帧的起始位做好准备
8、keil自带调试工具有哪些?
软件仿真模式:此模式无需连接实际硬件板卡。调试器会模拟执行ARM指令集并模拟芯片的片上外设
9、示波器怎么用,触发方式,带宽,带宽和采样频率的关系?
准备连接、通道设置、触发设置(边沿触发、脉冲触发、斜率触发、串行总线触发)、时基设置、自动测量(AutoSet 、Measure)。
带宽决定了示波器能准确测量多高频率的信号。
带宽指的是当输入一个正弦波时,示波器测量到的幅度衰减到实际幅度的-3dB(约等于70.7%)时的频率点。
奈奎斯特采样定理:采样率必须大于信号最高频率的2倍。工程:示波器带宽至少应为信号最高频率的5倍。
10、FreeRTOs的任务调度逻辑?
它主要基于优先级抢占式调度。调度器永远会选择当前处于就绪态的、优先级最高的任务来运行。
11、变量和声明定义的区别?
声明是向编译器介绍一个变量。一个变量可以被多次声明。声明不分配内存,定义分配内存。 extern int global_var;
定义是让编译器创建这个变量。一个变量有且只能有一个定义。
附加:
1、STM32的启动过程是怎么样的?
要点:从固定地址(0X800 0000)获取栈顶指针;复位中断服务程序地址;执行SystemInit函数初始化时钟;最后跳转到main函数。
2、什么是CMSIS?他的作用是什么?
要点:Cortex Micocontroller Software Interface Standard。为Cortex-M处理器提供硬件抽象层,统一了外设访问、内核函数接口,提高了代码可移植性。
3、描述一下STM32的中断处理流程?
要点:中断发生——>查找中断向量标——>保存现场——>执行中断服务程序(ISR)——>恢复现场——>返回主程序。NVIC统一管理中断优先级。
4、NVIC是什么?如何配置中断优先级?
要点:嵌套中断控制单元。STM32通过使用优先级分组(抢占优先级和子优先级)NVIC_SetPriority和NVIC_EnableIRQ进行配置
5、STM32的时钟系统(HCLK,PCLK1,PCLK2,SYSCLK分别是什么?)
要点:SYSCLK为系统时钟,HCLK为AHB总线的总线时钟,PCLK1为AHB1总线上低速外设时钟,PCLK2为AHB2总线上的高速外设时钟。有PLL倍频得到。
6、看门狗的作用是什么?STM32有哪几种看门狗?
要点:防止程序跑飞或者循环等待。独立看门狗(IWDG)和窗口看门狗(WWDG)。
7、volatile关键字的作用?
要点:禁止编译器对变量进行优化。场景:硬件寄存器、ISR中修改的全局变量、多线程应用中的共享变量
8、STM32有几种低功耗模式?区别
要点:睡眠(仅CPU停止)、停止(所有时钟停止,寄存器保持)、待机(最低功耗,仅备份域)。唤醒方式不同。
9、描述一下STM32中使用UART进行数据收发的过程?
要点:初始化GPIO和UART(波特率等参数);发送:查询或者中断方式将数据写入DR寄存器;接收:使能接收中断,在ISR中读取DR寄存器。
10、SP1协议有几根线?四种工作模式是什么,由什么决定?
要点:SCK,MOSI,MISO,CS。模式由CPOL(时钟极性)和CPHA(时钟相位)决定,共同构成模式0-3。
11、IIC协议中,如何开始一次通信?如何结束?如何应答?
要点:起始位(S):SCL为高电平,SDA从高电平变为低电平;停止位(P):SCL高时,SDA由低电平变为高电平;应答(ACK):第9个时钟周期,接收方将SDA拉低。
12、PWM输出的原理是什么?
要点:通过定时器的自动重载寄存器(ARR)决定频率,通过捕获/比较寄存器(CCR)决定占空比。模式通常为PWM模式1或2。
13、DMA的作用是什么?
要点:直接存储器访问。在外设和内存之间建立高速数据通道,传输数据无需CPU干预,传输结束后通过中断通知CPU。
14、如何使用定时器实现输入捕获功能?
要点:配置定时器时基和输入捕获通道;设置捕获边沿(上升/下降沿);在捕获中断中读取CCR寄存器的值,计算时间差(如脉冲宽度)。
15、STM32中的IIS协议主要用于什么?他和IIC的区别?
要点:用于传输音频数据,是数字音频接口。IIS是全双工音频流协议,速度快。
IIC为半双工通信协议,用于控制芯片,速度慢。
16、如何为STM32外设配置DMA?请以UART发送为例。
要点:初始化DMA流;设置外设地址(UART->DR)、内存地址、数据传输方向
数据大小;使能外设的DMA请求(UART->CR3 DMA使能位);启动DMA传输。
17、在STM32上使用FreeRTOS的原因?他解决了什么问题?
要点:实现多任务并发管理、提供任务调度、同步、通信机制(信号量、消息队列等),解决复杂应用的实时性和资源管理问题。
18、FreeRTOS的几种状态?
要点:运行态(Running)就绪态(Ready)阻塞态(Blocked)挂起态(Suspended)
19、什么是优先级反转?如何解决?
要点:低优先级任务 L 首先获取了某个共享资源(如互斥锁、全局变量),高优先级任务 H 此时就绪并需要使用同一共享资源,但资源已被 L 占用,因此 H 被迫进入阻塞状态。中等优先级任务 M直接抢占 CPU 开始执行,高优先级任务 H 在等待低优先级任务 L,而 L 被中等优先级任务 M “饿死”。
优先级继承:当高优先级任务 H 等待低优先级任务 L 持有的资源时,临时将 L 的优先级提升到与 H 相同的级别。资源释放后,L 的优先级自动恢复为原级别。
20、FreeRTOS中信号量和互斥锁的区别?
要点:互斥锁有优先级继承机制,用于临界资源保护;信号量主要用于任务同步和资源计数,没有优先级继承。
21、消息队列的工作原理和使用场景。
要点:用于任务间传递消息数据。生产者任务向队列发送消息,消费者任务从队列接收消息。适用于大数据量或不同结构体的通信。
22、如何进行STM32的硬件调试?常用的调试接口是什么?
要点:使用JTAG或SWD接口(更常用,线少),配合ST-Link、J-Link等调试器,可以设置断点、单步执行、查看变量和寄存器。
六、小米二面
1、给定一个字符数组,求它的sizeof的大小?
对于字符数组,sizeof返回的是整个数组的字节大小:



2. bin和elf的区别

7. TCP和UDP区别

更多推荐



所有评论(0)