泰凌微嵌入式一面:24 问覆盖 RTOS 与通信
- 轮次
- 一面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 实习中 10ms、100ms 的任务如何保证准确?1ms 的任务怎么保证准?10ms 和 100ms 任务之间有通信吗?
- 实习用到 FreeRTOS 了吗?
- 中断与共享内存是如何实现的?core0 和 core1 的通信方式是什么?
- 怎么确保任务栈够用?
- 单元测试和静态测试怎么做的、怎么测的?
- CAN 和 CAN-FD 熟悉吗?CAN 的多设备怎么仲裁?ID 号大的数据会丢吗?
- ADC 采集电源电压,12V 电源会烧吗?热敏电阻采集的是电压还是电阻?
- SPI 的特点是什么?看门狗怎么用?
- 10ms 任务被其他任务抢占了导致没法喂狗怎么处理?如果 100ms 的任务代码写得不好、死循环也不触发喂狗呢?
- 有没有统计每个任务的执行时间、1s 内执行了几次?
- TCP 以太网的移植是系统自带的还是自己搭的?
- 芯片的启动流程是什么?
- 上电了但是芯片无法运行,怎么排查?
- FIFO、FMC、DMA 的实现
- EEPROM 的使用,硬件 I2C 还是软件 I2C,为什么不用 flash 模拟 EEPROM?
- UART 的大小端和字节序,0x55 在总线上是怎么样的?UART 通信没收到怎么排查?
- 为什么需要使用硬件看门狗和软件看门狗?
- CPU 利用率是怎么统计的?
- Bootloader 的设计、断电保护设计
- 低功耗设计
- 大循环和小循环,break、continue、return 的区别
- 大端和小端的区别,0x12345678 在地址上怎么分布?
- 英语口语:介绍大学生活,学到了什么
- 英语口语:介绍一门课程
《参考解析》
周期任务怎么保证准时
RTOS 里不要用 vTaskDelay 去凑周期(它会累积漂移),要用 vTaskDelayUntil 以绝对时间为基准。更准的做法是靠硬件定时器中断触发,任务只在信号量或任务通知上等;1ms 级的任务最好放在定时器中断里做最轻量的部分,重活交给任务。优先级分配上按周期单调调度:周期越短优先级越高,1ms > 10ms > 100ms,这样短周期任务不会被长任务阻塞。10ms 和 100ms 任务之间的通信走队列、任务通知或事件组,不要用全局变量裸传。
喂狗和任务抢占
喂狗本身是一个强实时动作,不能让低优先级任务负责。做法是把喂狗放在最高优先级的任务里,或者对喂狗做「所有关键任务都打卡才算喂」的看门狗矩阵:每个任务有自己的计数位,喂狗任务检查所有位都被置位后才喂狗并清零,这样任何一个任务卡死都会让狗超时复位。任务被抢占导致喂不上狗,本质是优先级或时间片分配不合理,要缩短临界区和长耗时操作。另一个坑是用硬件看门狗加独立时钟源,避免系统时钟挂了连狗都停摆。更细的场景可以用窗口看门狗,喂太早也会复位,能抓出「循环跑飞但每次都喂狗」的情况。
核间通信与中断
多核(如 core0 和 core1)通信的通用方案是共享内存加核间中断:先在内存里约定一块区域和数据结构(环形缓冲或邮箱),发送方写入数据、加内存屏障保证顺序,然后触发对方的核间中断(IPI)或写 mailbox 寄存器;接收方在中断里只取走指针、真正的解析放到任务里做。同步要靠自旋锁或硬件信号量保护共享区,并注意 cache 一致性(必要时用非 cache 区域或手动 clean/invalidate)。中断和共享内存的关系在于:中断是通知机制,共享内存是数据通道,两者配合才能做到「中断里做最少的事」。
任务栈和 CPU 利用率怎么算
任务栈大小不能靠猜。可以先静态估算(函数调用深度、局部变量、嵌套中断的上下文),再用运行时的水位线验证:FreeRTOS 的 uxTaskGetStackHighWaterMark 返回历史最小剩余量,或者创建任务时把栈填成固定字节(如 0xA5)再扫描未被改动的区域。留 20% 到 30% 余量,中断和 printf 这类函数吃栈很凶。
CPU 利用率有两种统计口径:一是空闲任务计数器(vTaskGetRunTimeStats 打开后统计每个任务占用 CPU 的 tick 数),二是硬件周期计数器(DWT->CYCCNT 或定时器)测量每个任务的执行时间。要回答「1s 内执行了几次」,就在任务入口做计数并每秒清零。
CAN 仲裁与 CAN-FD
CAN 2.0 用非破坏性逐位仲裁:总线上显性位(0)会覆盖隐性位(1),所以 ID 越小、越早出现显性位的报文优先级越高。仲裁失败的节点会自动退让并在总线空闲时重发,因此ID 大的数据不会丢,只是延迟变大;真正会丢的是发送错误计数溢出导致节点进入错误被动或总线关闭,需要恢复流程。CAN-FD 的区别是数据段可以切到更高的速率、单帧数据最长 64 字节(不再有远程帧),并改用更强的 CRC,仲裁段速率保持兼容。CAN 没收到数据的排查顺序:终端电阻(两端各 120Ω)、波特率与采样点、收发器供电与 TX/RX 是否接反、总线电平、错误计数器与总线关闭状态。
大小端与字节序
大端是高位字节放在低地址,小端是低位字节放在低地址。0x12345678 在大端内存里从低地址到高地址是 12 34 56 78,小端是 78 56 34 12。UART 是串行逐位发送,通常先发 LSB(最低位在前),所以「大小端」在 UART 上主要影响的是多字节数据的先后顺序而不是单个字节的位序;协议里要明确约定先发高字节还是低字节,跨平台通信时统一按网络字节序(大端)处理。作者复盘时提到的 LSB、MSB 没搞清楚,指的就是最低有效位和最高有效位,以及串行先发哪一位、多字节先发哪一字节这两件事。
Bootloader 与低功耗
Bootloader 的基本结构是上电后先判断升级标志与固件校验(CRC/签名),合法就跳转到 app,不合法就进升级模式收固件,收完写 flash 并回写标志。断电保护靠「状态机 + 标志位」:升级前擦状态页写标记、写入完成后再写成功标记,重启时只认完整且校验通过的镜像,失败则回滚或停在升级模式;flash 驱动本身要按页对齐、考虑磨损,关键区域可以双份存。低功耗设计从时钟、外设、模式三处下手:关闭不用的外设时钟,降低主频,用 tickless idle 让空闲时进 sleep,用 RTC 或外部中断唤醒,注意唤醒源的配置和唤醒后的重新初始化,以及 IO 口的上下拉漏电。