虹软科技 嵌入式开发软件 二面

1. 深入聊聊你的项目,从需求分析到系统设计的完整过程

项目背景

我做的是一个智能环境监测系统,用于工业现场的温湿度、气体浓度等参数的实时监测和数据上报。系统部署了50个监测节点,每个节点每分钟采集一次数据并上报到云平台。

需求分析

项目的核心需求:

  • 实时采集多种传感器数据
  • 通过4G网络上报到云平台
  • 支持远程配置和固件升级
  • 对功耗有要求,要支持电池供电,续航至少3个月
  • 对可靠性有要求,要保证数据不丢失,设备故障要能自动恢复

硬件选型

主控芯片: STM32F407

  • 有丰富的外设接口
  • 性能足够,功耗可接受

通信模块: 4G模块

  • 支持TCP/IP和MQTT协议

传感器: 数字接口传感器

  • 使用I2C或SPI通信
  • 精度和稳定性都比较好

电源设计:

  • 采用锂电池加太阳能板
  • 支持边充电边工作
  • 使用低功耗设计,MCU大部分时间处于休眠状态,定时唤醒采集数据

软件架构设计

软件采用分层架构:

BSP层(板级支持包):

  • 时钟配置
  • GPIO初始化等

驱动层:

  • 传感器驱动
  • 4G模块驱动
  • Flash驱动等

中间层:

  • 协议栈
  • 文件系统
  • 数据管理等

应用层:

  • 数据采集
  • 数据上报
  • 远程配置等业务逻辑

任务管理(使用FreeRTOS):

  • 数据采集任务:定时读取传感器数据,存储到本地Flash
  • 数据上报任务:从Flash读取数据,通过MQTT上报到云平台
  • 通信管理任务:负责4G模块的初始化、网络连接、断线重连等

关键技术难点

难点1:低功耗设计

使用了多种低功耗技术:

  • MCU大部分时间处于Stop模式,功耗只有几十微安
  • 使用RTC定时唤醒,采集数据后立即休眠
  • 传感器使用低功耗型号,不用时断电
  • 4G模块使用PSM省电模式,不通信时进入休眠

优化效果: 整机平均功耗降到5mA以下,电池续航达到4个月

难点2:数据可靠性

使用了多重保障机制:

  • 数据采集后先存储到本地Flash,上报成功后才删除
  • 使用环形缓冲区管理数据,满了就覆盖最老的数据
  • 使用MQTT的QoS 1保证消息至少送达一次
  • 云平台收到数据后返回确认,设备收到确认才删除本地数据

难点3:远程升级

实现了Bootloader,支持通过4G网络下载固件升级:

  • Bootloader占用前32KB Flash
  • 应用程序从32KB开始
  • 升级时,Bootloader下载新固件到备份区
  • 校验通过后复制到应用区,然后跳转到应用程序
  • 如果升级失败,可以回滚到旧版本

通信协议设计

设备和云平台使用MQTT协议通信:

  • 设备订阅主题device/设备ID/config接收配置命令
  • 发布到主题device/设备ID/data上报数据
  • 数据格式使用JSON,包含设备ID、时间戳、传感器数据等
  • 为了节省流量,使用简短的字段名,比如用t表示temperature
  • 使用心跳机制保持连接,每5分钟发送一次心跳
  • 如果连接断开,自动重连,最多重试3次

测试和优化

实验室测试:

  • 进行了长时间测试,模拟各种异常情况
  • 测试网络断开、服务器故障、传感器异常等场景
  • 验证系统的可靠性

现场优化:

  • 通过日志分析发现了一些问题
  • 比如某些环境下4G信号不稳定,导致频繁重连
  • 优化了重连策略,增加了重连间隔,减少了功耗
  • 通过远程升级功能,修复了几个bug,优化了性能

用户反馈: 系统运行稳定,数据准确,满足了项目需求

项目成果

  • 系统已经稳定运行半年
  • 50个节点的数据上报成功率达到99.5%以上
  • 电池续航达到4个月,满足了设计要求
  • 远程升级功能使用了5次,都成功完成
  • 客户对系统很满意,计划扩大部署规模

2. ARM Cortex-M的异常和中断机制,NVIC的配置

异常和中断

ARM Cortex-M把所有的异常事件统称为异常,包括:

  • 复位、NMI、硬件故障等系统异常
  • 外部中断

异常号:

  • 系统异常的异常号是负数
  • 外部中断的异常号从0开始

优先级:

  • 每个异常有一个优先级,数字越小优先级越高
  • 优先级分为抢占优先级和子优先级
  • 抢占优先级决定是否可以嵌套
  • 子优先级决定同时发生时的处理顺序

NVIC(嵌套向量中断控制器)

NVIC负责管理外部中断,包括:

  • 中断的使能
  • 优先级配置
  • 挂起状态等

每个中断有独立的使能位、挂起位、活动位。

中断嵌套:

  • NVIC支持中断嵌套,高优先级中断可以打断低优先级中断
  • 但有一个例外:如果两个中断的抢占优先级相同,后来的中断不能打断先来的中断,要等它执行完

优先级分组

Cortex-M支持优先级分组,把优先级位分为抢占优先级和子优先级。

通过AIRCR寄存器的PRIGROUP字段配置分组。比如STM32有4位优先级,可以配置为:

  • 4位抢占优先级 + 0位子优先级
  • 3位抢占优先级 + 1位子优先级
  • 2位抢占优先级 + 2位子优先级(常用)
  • 等等

一般配置为2位抢占优先级2位子优先级,这样有4个抢占级别,每个级别有4个子级别。

中断配置步骤

第一步:配置优先级分组

  • 调用NVIC_SetPriorityGrouping函数
  • 一般在系统初始化时配置一次

第二步:配置中断优先级

  • 调用NVIC_SetPriority函数,指定中断号和优先级
  • 优先级是一个8位数字,高位是抢占优先级,低位是子优先级

第三步:使能中断

  • 调用NVIC_EnableIRQ函数,指定中断号
  • 也可以用NVIC_DisableIRQ函数禁用中断

第四步:配置外设的中断使能位

  • 每个外设有自己的中断使能寄存器
  • 比如USART有RXNE中断使能位,TIM有更新中断使能位

中断处理流程

  1. 外设触发中断
  2. NVIC检查中断是否使能,是否被屏蔽
  3. 如果可以响应,CPU保存当前状态到栈,包括R0-R3、R12、LR、PC、xPSR等寄存器
  4. CPU从向量表读取中断服务程序的地址,跳转执行
  5. 中断服务程序执行完后,返回指令会自动恢复之前保存的寄存器,返回被中断的程序

中断延迟

硬件延迟:

  • 从中断触发到CPU开始执行中断服务程序的时间
  • Cortex-M3/M4大约是12个时钟周期

软件延迟:

  • 中断服务程序的执行时间
  • 要尽量缩短中断服务程序,只做必要的处理
  • 复杂的处理放到任务中

中断安全

在中断和任务之间共享数据时,要注意数据竞争:

方法1:使用临界区保护

  • 调用taskENTER_CRITICALtaskEXIT_CRITICAL

方法2:关闭中断

  • 但要尽快打开,避免影响实时性

方法3:使用FreeRTOS中断安全API

  • FreeRTOS提供了中断安全的API,以FromISR结尾
  • 比如xSemaphoreGiveFromISRxQueueSendFromISR
  • 在中断中只能调用这些API,不能调用普通的API

3. DMA的工作原理,如何配置DMA传输?

DMA概念

DMA是Direct Memory Access的缩写,直接内存访问。它可以在外设和内存之间直接传输数据,不需要CPU参与。这样可以释放CPU,让CPU做其他工作,提高系统效率。

DMA的优势

减少CPU负担:

  • 比如串口接收数据,如果用中断方式,每收到一个字节就触发一次中断,CPU要频繁响应中断
  • 如果用DMA,可以一次传输一批数据,只在传输完成时触发一次中断

传输速度快:

  • 因为不需要CPU参与,没有上下文切换的开销

降低功耗:

  • DMA可以在CPU休眠时工作,进一步降低功耗

DMA的工作模式

传输方向(三种):

  1. 外设到内存(比如ADC采集数据到内存)
  2. 内存到外设(比如内存数据发送到USART)
  3. 内存到内存(比如数据拷贝)

工作模式(两种):

  1. 正常模式:传输完成后停止,需要重新配置才能再次传输
  2. 循环模式:传输完成后自动重新开始,适合连续采集的场景

其他特性:

  • DMA支持不同的数据宽度,可以是字节、半字、字
  • 源地址和目标地址可以配置为递增、递减或固定

DMA配置步骤

第一步:使能DMA时钟

  • 调用RCC_AHBPeriphClockCmd函数

第二步:配置DMA通道

  • 填充DMA_InitTypeDef结构体
  • 包括外设地址、内存地址、传输方向、数据宽度、传输数量等

第三步:配置DMA中断

  • 如果需要在传输完成时得到通知
  • 使能传输完成中断,配置NVIC

第四步:使能DMA通道

  • 调用DMA_Cmd函数

第五步:配置外设的DMA使能位

  • 每个外设有自己的DMA使能寄存器
  • 比如USART有DMAT和DMAR位,ADC有DMA位

DMA使用示例

串口DMA接收:

  • 配置DMA从USART的数据寄存器传输到内存缓冲区,使用循环模式
  • 这样串口接收的数据会自动存储到缓冲区,不需要CPU参与

ADC DMA采集:

  • 配置DMA从ADC的数据寄存器传输到内存数组,使用循环模式
  • 配置ADC连续转换模式,每次转换完成触发DMA传输
  • 这样可以连续采集ADC数据,不需要CPU参与

DMA的注意事项

1. DMA和CPU同时访问内存的问题

  • 如果DMA正在传输数据到缓冲区,CPU同时读取缓冲区,可能读到不完整的数据
  • 可以使用双缓冲机制,DMA写一个缓冲区时,CPU读另一个缓冲区

2. 缓存一致性问题

  • 在有缓存的MCU上,DMA传输的数据可能在缓存中,没有写回内存
  • 要在DMA传输前刷新缓存,传输后使缓存失效

3. DMA传输完成的判断

  • 可以使用中断方式,在传输完成中断中处理
  • 也可以使用轮询方式,查询DMA的传输完成标志

4. Flash的读写操作,如

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

嵌入式面试八股文全集 文章被收录于专栏

这是一个全面的嵌入式面试专栏。主要内容将包括:操作系统(进程管理、内存管理、文件系统等)、嵌入式系统(启动流程、驱动开发、中断管理等)、网络通信(TCP/IP协议栈、Socket编程等)、开发工具(交叉编译、调试工具等)以及实际项目经验分享。专栏将采用理论结合实践的方式,每个知识点都会附带相关的面试真题和答案解析。

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务