(嵌入式面经)第11章 20+公司面经杂谈(三):腾讯、科大讯飞、阳光电源、蔚来
本篇涉及的所有问题概要:大家可以试试在看参考答案前,提前尝试解答,以便明确自身知识点的不足部分!
1.用过交叉编译器吗,简单介绍一下?
2.说一下memcpy()和strcpy()的区别?
3.你写代码时用过多条件if else if else吗,如何进行优化呢?
4.在STM32里,串口接收哪几种模式,你比较常用什么模式,为什么呢?它是接受断如何判断数据接受结束的?
5.IIC接口地址位有几位,只有7位地址模式吗,理论上能接多少外设,实际上能接多少,是什么因素影响的?
6.说一说FreeRTOS如何去实现任务调度的?
7.如果实际中,你碰到数据传入速度大于数据处理速度,你会怎么解决?
8.在你的项目中,是如何考虑控制的实时性?
9.看你有涉及Linux跟FreeRTOS,说一说这两个操作系统的区别吧?
10. 专栏订阅奖励(支持模仿)——个人创新点问答:说一说你所设计的FreeRTOS PLUS中是如何在应对小数据量存储下,增加Flash单扇区存储的使用寿命的?
---------------------------------------------------------------------------------------------------
1.用过交叉编译器吗,简单介绍一下?
1. 交叉编译器(Cross Compiler)介绍
交叉编译器 是指在 一种 CPU 架构(如 x86)上编译可在 另一种 CPU 架构(如 ARM、RISC-V)上运行的代码的编译器。它主要用于 嵌入式系统、操作系统移植 和 跨平台开发。
2. 交叉编译的应用场景
✅ 嵌入式开发(如 STM32、ESP32、ARM Linux)
✅ Linux 内核编译(使用 arm-linux-gcc
交叉编译 ARM 设备的 Linux 内核)
✅ Android 开发(Android NDK 使用交叉编译器生成 ARM 兼容的应用)
✅ 操作系统移植(移植 FreeRTOS、U-Boot 等)
3. 交叉编译器的常见工具链
4. 如何使用交叉编译器
(1)使用交叉编译器编译 C 代码
使用 arm-none-eabi-gcc
编译:
arm-none-eabi-gcc -o hello.elf hello.c
编译 ARM Linux 可执行文件:
arm-linux-gnueabihf-gcc -o hello_arm hello.c
然后可以拷贝到 ARM 设备(如树莓派)运行:
scp hello_arm user@raspberrypi:/home/user/
ssh user@raspberrypi ./hello_arm
(2)编译 FreeRTOS 代码
arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -o main.elf main.c
-mcpu=cortex-m4
指定 Cortex-M4 处理器-mthumb
使能 Thumb 指令集5. 交叉编译 vs 本地编译
6. 交叉编译的挑战
❌ 库兼容性问题(如 glibc
版本不同)
❌ 调试难度高(需借助 GDB 远程调试)
❌ 架构指令集差异(如 ARM
vs RISC-V
)
解决方案:
qemu
模拟目标环境总结
🚀 交叉编译器 在 嵌入式开发 和 多平台支持 方面至关重要。
常见工具链如 arm-none-eabi-gcc
、arm-linux-gnueabihf-gcc
可用于不同场景。
通过合理的工具配置,我们可以在 PC 端开发代码,并将其部署到 ARM、RISC-V 设备,极大提高开发效率!
2.说一下memcpy()和strcpy()的区别?
memcpy
和 strcpy
都是用于内存操作的 C 语言库函数,但它们的功能、用法和适用场景有明显的不同。
1. memcpy 与 strcpy 的主要区别
2. memcpy 详解
函数原型
void *memcpy(void *dest, const void *src, size_t n);
dest
:目标地址src
:源地址n
:拷贝的字节数\0
结束符适用场景
✅ 适用于 二进制数据、结构体、数组、内存块 拷贝,例如:
int
、double
、char
数组)示例
⚠️ memcpy
可能会导致溢出,如果 n
超过目标缓冲区大小,就会引发 缓冲区溢出,影响程序稳定性。
3. strcpy 详解
函数原型
char *strcpy(char *dest, const char *src);
dest
:目标字符串src
:源字符串(必须以\0
结尾)- 注意:会自动拷贝
\0
结束符
适用场景
✅ 适用于 字符串(char
数组)拷贝,例如:
char
数组示例
⚠️ strcpy
可能导致缓冲区溢出,如果 dest
的大小 小于 src
,则会发生 越界写入,可能导致程序崩溃。
4. 关键区别示例
memcpy
可拷贝二进制数据,而 strcpy
仅适用于字符串
memcpy(dest1, src, 5);
- 仅仅复制
"Hello"
的 5 个字节,但不会自动添加\0
结尾。 dest1
之前的内容是"XXXXXXXXXX"
,所以输出可能会出现乱码,或者包含之前的内容。- 如果目标数组中原本有数据,则
\0
终止符不会自动加上,可能导致不受控的输出!
strcpy(dest2, src);
strcpy
会自动拷贝\0
结束符,因此dest2
仅包含"Hello"
并正确终止。- 不会残留
XXXXXX
,保证输出是正确的字符串!
memcpy(dest3, src, 6);
- 这里
memcpy
手动指定 6 个字节拷贝,包含了\0
终止符,因此dest3
也是正确的字符串。 - 但如果
src
不是字符串,而是结构体或二进制数据,memcpy
仍然不会做\0
处理。
5. 区别总结
6. 何时使用?
✅ 使用 memcpy
- 复制 非字符串(二进制数据、结构体、数组)
- 需要拷贝 精确的字节数
- 数据大小已知,且不会重叠
✅ 使用 strcpy
- 仅 复制字符串
- 保证目标有足够空间
- 需要 自动拷贝
\0
结尾
🚨 避免常见错误
memcpy
不会 添加\0
,要手动处理strcpy
不检查 目标空间,可能 越界- 推荐
strncpy
限制strcpy
的拷贝大小
结论
memcpy
更通用,适用于结构体、二进制数据strcpy
适用于字符串,但要注意越界- 优先使用
strncpy
或memmove
以提高安全性
3.你写代码时用过多条件if else if else吗,如何进行优化呢?
当 if
语句包含多个条件时,优化代码不仅能提升可读性,还能提高执行效率。
1. 提前 return,让主干代码更清晰
适用于: 代码逻辑有多个分支,但主要执行路径明确的情况。
示例:未优化代码
优化后(提前 return
):
2. 使用 && 和 || 组合条件
适用于: 条件逻辑较多但可以合并的情况。
示例:未优化代码
优化后(合并条件):
- 逻辑更直观,减少
if
嵌套。
3. 使用 switch 代替多个 if-else
适用于: 多个 if-else
语句用于匹配固定值的情况。
示例:未优化代码
优化后(switch
替换 if-else
):
switch
比多个 if-else
更高效(编译器可能优化为跳转表)。4. 使用 三目运算符 使代码更简洁
适用于: 简单 if-else
赋值逻辑。
示例:未优化代码
优化后(? :
三目运算符):
- 代码简洁,可读性更强。
5. 逻辑表驱动法
适用于: 复杂的 if-else
判断逻辑,可用数据结构存储匹配规则。
示例:
- 便于扩展,可减少
if-else
的复杂性。
总结
4.在STM32里,串口接收哪几种模式,你比较常用什么模式,为什么呢?它是接受断如何判断数据接受结束的?
STM32 的串口(USART/UART)支持 三种数据接收模式:
- 轮询模式(Polling Mode)
- 中断模式(Interrupt Mode)
- DMA 模式(Direct Memory Access Mode)
1. 轮询模式(Polling Mode)
工作方式:
代码示例:
优点:
- 代码逻辑简单,不需要额外的中断管理。
缺点:
- CPU 需要不断检查状态,占用 CPU 资源,效率较低。
- 适用于低速率、少量数据的场景。
2. 中断模式(Interrupt Mode)
工作方式:
- 启用 USART 接收完成中断(USART_IT_RXNE)。
- 当数据到达时,触发中断,在中断服务函数中读取数据。
代码示例(基于 HAL 库):
优点:
- 释放 CPU 资源,不需要频繁轮询。
- 适用于数据量适中的场景,如 命令控制、低速数据传输。
缺点:
- 当数据量较大时,会频繁进入中断,影响系统性能。
3. DMA 模式(DMA Mode)
工作方式:
- DMA(直接存储器访问) 负责数据搬运,CPU 无需干预。
- 串口接收到数据后,自动存入 指定的内存缓冲区。
- 可通过 DMA 完成中断 或 定时器 触发数据处理。
代码示例(基于 HAL 库):
优点:
- 适用于大数据量传输,如传感器数据流、日志记录。
- CPU 负担最小,提高系统实时性。
缺点:
- 代码实现较复杂,需要配置 DMA。
- 需要 额外的方式判断数据接收完成。
4. 如何判断数据接收结束?
不同模式下,数据接收完成的判断方式不同:
5. DMA 模式:不定长数据的接收终止
方法 1:启用 IDLE
中断
USART IDLE 中断(空闲中断):当串口在一个帧的最后一个字节接收完成后,RX 线在一定时间内未收到新的数据,触发 IDLE
中断,可用于判断数据传输结束。
示例代码:
方法 2:使用定时器超时
如果设备不支持 IDLE
中断,可以在接收到数据后启动一个定时器,如果超时时间内未接收到新的数据,则认为数据接收完成。
IDLE
中断的 MCU(如部分低端 STM32)。6. 常用模式选择
✅ 常用选择:
- 中断模式:适用于 短数据包通信(如 AT 指令、短指令)。
- DMA + IDLE 中断:适用于 高频率/长数据包(如 UART 传感器、日志传输)。
- DMA + 定时器:适用于 硬件不支持 IDLE 的 MCU。
总结
STM32 串口支持轮询、中断、DMA 三种模式:
如何判断数据接收完成?
RXNE
标志位。USART_IRQHandler()
触发。RxCpltCallback
。IDLE
中断,若不支持则用定时器超时判断。5.IIC接口地址位有几位,只有7位地址模式吗,理论上能接多少外设,实际上能接多少,是什么因素影响的?
I²C(Inter-Integrated Circuit)是一种多主多从的串行通信协议,它支持7位和10位地址模式,并且理论上可以连接多个从设备,但受限于多种实际因素。
1. I²C 地址位有几种模式?
I²C 设备地址是用于标识不同从设备的,常见的地址模式有两种:
(1) 7位地址模式
- 设备地址占7位,数据传输时再加上 1 位 R/W 位(读写标志位)。
- 地址范围:
0x00
~0x7F
(0~127) - 理论可连接设备数量:
127
个(但0x00
和0x7F
为保留地址,实际可用0x01
~0x7E
,共126
个)
示例:
- 设备 A 地址:
0x1A
- 设备 B 地址:
0x2F
- 通信帧格式(读):
0x1A << 1 | 1
(发送0x35
,即00110101
) - 通信帧格式(写):
0x1A << 1 | 0
(发送0x34
,即00110100
)
(2) 10位地址模式
- 设备地址占 10 位,可扩展设备地址范围。
- 地址范围:
0x0000
~0x03FF
(0~1023) - 理论可连接设备数量:
1023
个 - 使用两个字节传输地址,前 5 位为
11110
,后 10 位存放设备地址。
示例:
- 设备地址
0x1F3
(十进制499
) - 通信帧格式(写):
11110XXX 0XXXXXXX
11110000
(高 5 位固定 11110
,低 3 位 000
)01111111
2. 理论上能连接多少个设备?
3. 实际上能接多少个设备?影响因素有哪些?
虽然 I²C 理论上可以连接上百个设备,但在实际应用中受到以下因素的限制:
(1) 地址冲突
- 原因:如果多个 I²C 设备具有相同的 7 位地址,则会发生冲突,无法正常通信。
- 解决方案:
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
作者简介:仅用几个月时间0基础天坑急转嵌入式开发,逆袭成功拿下华为、vivo、小米等15个offer,面试经验100+,收藏20+面经,分享求职历程与学习心得。 专栏内容:这是一份覆盖嵌入式求职过程中99%问题指南,详细讲解了嵌入式开发的学习路径、项目经验分享、简历优化技巧、面试心得及实习经验,从技术面,HR面,AI面,主管面,谈薪一站式服务,助你突破技术瓶颈、打破信息差,争取更多大厂offer。