《MCU职位》面试问题

1. 中断何如设计

  1. 中断服务函数(ISR)应该尽可能简短,尽量只做通知
  2. 避免在中断中使用阻塞操作
  3. 在中断和主程序之间共享的变量必须用 volatile
  4. 当多个中断或中断与主程序访问共享资源时,需要保护
  5. 中断优先级管理
  6. 及时清除中断标志
  7. 避免递归中断
  8. 确保足够堆栈

2. 如何通过DMA读取大量数据,避免丢包

  1. 使用循环模式:确保DMA配置为循环模式,双缓冲方案
  2. 足够大的缓冲区:根据数据速率和处理能力设置合适的缓冲区大小
  3. 及时处理数据:在主循环中及时处理DMA收集的数据
  4. 软件硬件流控制
  5. 错误检测:实现DMA错误中断处理
  6. 优先级设置:为DMA中断设置合适的优先级
  7. 内存管理:确保DMA缓冲区不在缓存行边界上,内存对齐

3. freertos 如何边界区处理

  1. 保护临界区,即那些必须完整执行、不能被打断的代码段
  2. 可以考虑使用信号量(Semaphore) 或互斥量(Mutex) 来进行任务间的同步与资源共享
  3. 任务级 taskENTER_CRITICAL() / taskEXIT_CRITICAL()
  4. 中断级 taskENTER_CRITICAL_FROM_ISR() / taskEXIT_CRITICAL_FROM_ISR()
  5. 避免阻塞,保持精简

4. 死锁

  1. 双锁,解决方法:两个任务都按照同一个顺序A-B
c 复制代码
void Task1(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(xMutexA, portMAX_DELAY);  // 先获取A
        vTaskDelay(pdMS_TO_TICKS(1));            // 增加死锁概率
        xSemaphoreTake(xMutexB, portMAX_DELAY);  // 再请求B
        
        // 访问资源A和B
        printf("Task1 critical section\n");
        
        xSemaphoreGive(xMutexB);
        xSemaphoreGive(xMutexA);
    }
}

void Task2(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(xMutexB, portMAX_DELAY);  // 先获取B
        vTaskDelay(pdMS_TO_TICKS(1));            // 增加死锁概率
        xSemaphoreTake(xMutexA, portMAX_DELAY);  // 再请求A
        
        // 访问资源A和B
        printf("Task2 critical section\n");
        
        xSemaphoreGive(xMutexA);
        xSemaphoreGive(xMutexB);
    }
}
  1. 自锁,解决方法:超时
c 复制代码
void Task(void *pvParameters)
{
    xSemaphoreTake(xMutex, portMAX_DELAY);  // 第一次获取
    
    // 某些条件下再次尝试获取同一个互斥量
    if(some_condition) {
        xSemaphoreTake(xMutex, portMAX_DELAY);  // 第二次获取 - 死锁!
    }
    
    xSemaphoreGive(xMutex);
}
  1. 获取资源组,一次获取所有的锁

5. 如何解决遇到难处理问题

  1. 看门狗是否复位
  2. 计算错误、逻辑流程、数据损坏、通信出错
  3. 时好时坏、稳定复现
  4. 日志
  5. 调试器甚至逻辑分析仪
  6. 中断、互斥量、越界、空指针、野指针
  7. 函数重入
  8. 调试器PC
  9. 时钟
  10. 中断服务函数过长、优先级、未清除中断标志、阻塞
  11. 去除不相关的功能最小排查
  12. 硬件:电源、时钟、复位、焊接、电磁

6. 项目中遇到的最难的问题,如何解决?

7. 时钟树都有什么

  1. 选择时钟源HSI/E LSI/E
  2. PLL设置
  3. 分频器设置

8. GPIO

  • 推挽输出:可以主动输出高电平(推)和低电平(挽)。驱动能力强,适合驱动LED、控制继电器等。
  • 开漏输出:只能主动拉低电平,高电平需要外部上拉电阻。方便连接不同电压等级的器件。适合I2C等总线通信。
  • 上拉/下拉输入:在引脚悬空时,通过内部电阻给一个确定的电平,防止误触发。上拉默认高电平,下拉默认低电平。

9. 程序代码、常量、全局变量、局部变量、堆栈分别存储在MCU的哪个区域?

  • Flash/ROM:存储程序代码(.text)和常量(如const变量)。
  • RAM:
  • data数据段:存储已初始化的全局变量和静态变量。
  • BSS段:存储未初始化的全局变量和静态变量(启动时清零)。
  • 堆:动态分配的内存(malloc/free),从低地址向高地址增长。
  • 栈:局部变量、函数参数、返回地址等,从高地址向低地址增长。

关键点:栈溢出会覆盖堆或其他数据区域,导致不可预知的错误,非常危险。

10. 什么是内存对齐?为什么需要内存对齐?

  • 要求数据在内存中的地址必须是其本身大小的整数倍。一个 uint32_t 变量,其地址最好是4的倍数。
  • 不对齐的内存访问可能需要多次总线操作,降低效率。Cortex-M的硬件不支持非对齐访问。

11. 如何让MCU进入低功耗模式?有什么注意事项?

  • Sleep, Stop, Standby(功耗依次降低,唤醒时间依次增长)。
  • 进入前必须配置好唤醒源(如外部中断、RTC闹钟)。
  • 进入前关闭不必要的外设时钟和功能以节省功耗。
  • 将未使用的IO引脚设置为模拟输入模式,避免漏电流。

12. 如何优化MCU程序的性能和效率?

  • 编译器优化:使用 -O2 或 -Os(优化代码大小)等级别。
  • 算法与数据结构:选择时间/空间复杂度更优的算法。
  • 减少函数调用开销:对性能关键的短小函数使用 inline 内联\宏函数。
  • 查表法代替实时计算:如CRC、三角函数等。
  • 使用DMA:将CPU从繁重的数据搬运工作中解放出来。

13. 软件版本管理的

  • 主分支main/master、开发分支develop、功能分支feature/xxx、修复分支hotfix/xxx。为发布版本打Tag

14. 在团队协作中,如何保证代码质量

  • 相互审查代码
  • 使用工具检查代码
  • 编写测试用例

15. volatile关键字有什么用

  • 告诉编译器,这个变量可能会被意想不到地改变,禁止对其进行优化(如缓存到寄存器),每次都必须从内存中读取。
  • 在中断服务程序和主循环之间共享的全局变量。
  • 在多线程环境(如RTOS)中共享的全局变量。
相关推荐
大前端helloworld5 小时前
前端梳理体系从常问问题去完善-网络篇
前端·面试
清风6666666 小时前
基于单片机的智能点滴输液速度与液位控制系统设计
单片机·嵌入式硬件·毕业设计·课程设计
绝无仅有8 小时前
某大厂跳动Java面试真题之问题与解答总结(二)
后端·面试·github
绝无仅有8 小时前
某大厂跳动Java面试真题之问题与解答总结(三)
后端·面试·架构
大前端helloworld8 小时前
前端梳理体系从常问问题去完善-框架篇(Vue2&Vue3)
前端·javascript·面试
电子工程师成长日记-C518 小时前
基于51单片机的交通灯智能调节系统
单片机·嵌入式硬件·51单片机
点灯小铭9 小时前
基于单片机的智能水瓶温度控制系统
单片机·嵌入式硬件·毕业设计·课程设计
点灯小铭9 小时前
基于单片机的四点位水位控制与报警系统设计
单片机·嵌入式硬件·毕业设计·课程设计
是大强9 小时前
肖特基二极管作用及应用
单片机·嵌入式硬件