嵌入式面试问题

1. volatile是否可以修饰const

  • 可以。volatileconst可以同时修饰一个变量。
  • volatile表示变量可能会被意外修改(例如硬件寄存器),编译器不应优化对该变量的访问。
  • const表示变量是只读的,不能被修改。
  • 示例:
    volatile const int *p = (volatile const int *)0x1234;
    
    这里p指向一个硬件寄存器,该寄存器的值可能会被硬件改变,但程序不能修改它。

2. 如何快速一行代码操作硬件寄存器?

  • 使用指针直接访问硬件寄存器地址。例如:
    *(volatile unsigned int *)0x1234 = 0x5678; // 将0x5678写入地址0x1234
    
  • volatile确保编译器不会优化掉该操作。

3. 如何最快比较两组寄存器里有多少位不同?

  • 使用异或操作(^)和位计数。例如:
    int diff_bits = __builtin_popcount(reg1 ^ reg2);
    
    • reg1 ^ reg2:异或操作,结果中为1的位表示不同。
    • __builtin_popcount:GCC内置函数,计算二进制中1的个数。

4. 如何降低功耗?

  • 硬件层面
    • 使用低功耗模式(如睡眠模式)。
    • 关闭未使用的外设时钟。
    • 降低CPU频率。
  • 软件层面
    • 减少CPU运行时间(如使用中断代替轮询)。
    • 优化算法,减少计算量。
    • 进入低功耗模式(如WFIWFE指令)。

5. 什么时候会用到do{}while(0)

  • 主要用于宏定义中,确保宏在多行代码中正确展开。例如:
    #define SAFE_FREE(p) do { free(p); p = NULL; } while(0)
    
  • 这样可以避免宏在if-else语句中展开时出现问题。

6. GPIO有几种状态?

  • 输入状态:
    • 高电平(1)。
    • 低电平(0)。
    • 高阻态(浮空)。
  • 输出状态:
    • 推挽输出(高电平或低电平)。
    • 开漏输出(只能拉低或高阻态)。

7. 如何用软件处理硬件管脚抖动?

  • 去抖动算法
    • 多次采样,确认电平稳定后再处理。例如:
      if (read_pin() == HIGH) {
          delay_ms(10); // 延时
          if (read_pin() == HIGH) {
              // 确认高电平
          }
      }
      

8. 如何高效处理中断?

  • 快速处理:
    • 中断服务程序(ISR)尽量简短,只做必要操作。
  • 延迟处理:
    • 在ISR中设置标志位,在主循环中处理耗时任务。
  • 避免阻塞:
    • 不要在ISR中调用可能阻塞的函数(如printf)。

9. delaysleep的区别?

  • delay:忙等待,CPU一直运行。
  • sleep:让出CPU,进入低功耗状态。

10. 中断时可否睡眠?

  • 不可以。在中断上下文中睡眠会导致系统崩溃。

11. 如何设计RAM和Flash的验证工具?

  • RAM验证
    • 写入特定模式(如0xAA0x55),然后读取验证。
  • Flash验证
    • 写入数据后读取并校验CRC或哈希值。

12. 如何合理高效静态分配内存?

  • 使用固定大小的数组或内存池。
  • 避免内存碎片化。

13. 如何跟踪内存泄漏?

  • 使用工具(如Valgrind)。
  • 在代码中记录内存分配和释放。

14. 如何实现一个ring buffer以及用途?

  • 实现
    #define BUFFER_SIZE 1024
    uint8_t buffer[BUFFER_SIZE];
    int head = 0, tail = 0;
    
    void push(uint8_t data) {
        buffer[head] = data;
        head = (head + 1) % BUFFER_SIZE;
    }
    
    uint8_t pop() {
        uint8_t data = buffer[tail];
        tail = (tail + 1) % BUFFER_SIZE;
        return data;
    }
    
  • 用途:用于数据缓冲(如串口通信)。

15. DMA和FIFO的区别?

  • DMA:直接内存访问,用于高速数据传输。
  • FIFO:先进先出队列,用于数据缓冲。

16. 如何做到统一API对接不同外设驱动?

  • 使用抽象接口(如函数指针或虚函数)。
  • 示例:
    struct Device {
        void (*read)(void);
        void (*write)(void);
    };
    
    void UART_Read() { /* UART读取 */ }
    void SPI_Read() { /* SPI读取 */ }
    
    struct Device uart = { UART_Read, NULL };
    struct Device spi = { SPI_Read, NULL };
    

17. 如何合理设计Flash分区表?

  • 分区表应包括:
    • Bootloader区。
    • 应用程序区。
    • 配置区。
    • 日志区。

18. 正常非掉电重启是否要释放内存?

  • 不需要,系统重启时会自动释放。

19. 正常掉电关机流程是否要释放内存?

  • 不需要,掉电后内存数据会丢失。

20. 非掉电异常如何处理?

  • 记录异常信息。
  • 重启系统。

21. 如何实现异常后的dump

  • 保存寄存器状态和堆栈信息到Flash或日志。

22. 非正常掉电如何保护?

  • 使用掉电检测电路。
  • 快速保存关键数据到非易失性存储器(如Flash)。

23. 如何设计一个简单的profiling工具?

  • 使用高精度计时器记录函数执行时间。
  • 示例:
    #define START_TIMER() uint32_t start_time = get_timer()
    #define STOP_TIMER() printf("Time: %lu\n", get_timer() - start_time)
    

24. 低功耗深睡眠如何唤醒后继续之前工作?

  • 保存状态到非易失性存储器。
  • 唤醒后恢复状态。

25. RTOS不能断点和打印的时候如何调试?

  • 使用日志系统。
  • 使用硬件调试器(如JTAG)。

26. 什么是交叉编译?

  • 在一种平台上编译另一种平台的代码。

27. 如何保证Makefile的增量编译?

  • 使用依赖规则:
    target: dependency
        command
    

28. 如何用一套代码支持不同硬件?

  • 使用条件编译:
    #ifdef HW_TYPE_A
    // 硬件A的代码
    #else
    // 硬件B的代码
    #endif
    

29. 不同代码编译后的存放区域有何不同?

  • 代码段(.text)、数据段(.data)、BSS段(.bss)。

30. releasedebug编译的区别?

  • debug:包含调试信息,未优化。
  • release:优化代码,去除调试信息。

31. ARM多核之间有多少通讯机制及优缺点?

  • 共享内存:速度快,但需要同步。
  • 消息传递:灵活,但开销较大。

32. 两个线程之间不同锁的区别是什么?

  • 互斥锁:同一时间只有一个线程访问资源。
  • 自旋锁:忙等待,适合短时间锁定。

33. 如何理解收益边界?

  • 投入与产出的平衡点,超过该点后收益递减。

34. 代码优化经验

  • 使用高效算法。
  • 减少内存访问。
  • 利用编译器优化选项。

35. 代码移植经验

  • 抽象硬件相关代码。
  • 使用条件编译支持不同平台。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值