BSP分层与驱动架构问答合集
由本目录 15 篇 BSP 分层与驱动架构主题问答的答案精华按知识点重组而成,原帖全部保留,每节末尾标注出处。
1. 一条链路读懂各层:hal → driver → handler → adapter → APP
- hal 是硬件抽象层;driver 把 hal 组合起来完成基本的外设控制;handler 控制 driver 去满足运行逻辑;adapter 把 handler 挂载到 wrapper 的抽象接口上;APP 调用 platform 里的 wrapper 接口完成具体业务。
— hal是硬件抽象层,driver将hal组合起来完成基本的外设控制(Jack)
- 更直观的比喻:Driver 是材料包(木头、手柄,如 hal 库,实际没有做事,CPU 通过调库配置外设寄存器);Core 把材料拼装成锤子(含 main.c,对外设初始化后你获得了装有成功初始化外设的"外设库",如 IIC 句柄);BSP 层调用外设库里已初始化成功的外设进行通信;BSP 与操作系统层和 APP 层构成生产者消费者模型——操作系统层调 BSP 读数据到缓冲区或发给 APP,APP 不能直接调 BSP,一定要经过操作系统层。
— Driver:理解为材料包,木头,手柄,比如hal库(Jack)
2. Core 与 BSP 的边界
- BSP 是面向设备的驱动,Core 是芯片的底层驱动,BSP 是 Core 的上层结构。例:一个 IIC 接口的 MPU6050,Core 就是 IIC 的初始化,BSP 就是 MPU6050 的驱动,它需要 IIC 的支持。
— 同学提问: Core和BSP(Jack)
- Driver 层与 Core 层都提到外设初始化,区别在哪?driver 层没有做初始化操作——driver 基本都是库,不会有任何代码执行;core 把这些 driver 调用后实现最终的操作句柄,提供给 BSP 层调用。
— 对Driver层和Core层还是不太理解,为什么都提到了对外设的初始化(Jack)
- 换个角度:ADC+DMA 采集代码属不属于 BSP 层?不属于。BSP 描述的是 MCU 外部的东西和 MCU 的通信逻辑,而 ADC 是片内外设(挂载在 AHB/APB 总线上、CPU 以外的部分)。
— 老师,我写了个ADC+DMA的代码,实现了ADC数据采集以后(Jack)
3. 什么代码该放进 BSP
最优秀的 BSP 驱动文件中,只放 MCU 和板上外设的交互逻辑。
— 什么类型的函数适合放到BSP驱(Jack)
4. 私有数据保护:防脏数据
- 行业常识:驱动中私有变量的访问要有防脏数据的保护方法(图解见原帖)。
— 行业常识:驱动中私有变量的访问方法(防止脏数据系统紊乱(Jack)
- APP 想读设备 ID 怎么办?不能让 APP 直接读:如果 APP 比 BSP 先启动,直接读可能读到脏数据,若该 ID 间接引起 IO 变化会非常危险。正确做法是提供一个自己的函数获取已挂载设备 ID,函数在获取前先检查 BSP 设备是否初始化成功,确认完成后再把 ID 读给 APP。
— BSP驱动中如何处理私有数据?比如设备的ID,APP想知道设备的ID:(Jack)
5. handler 与线程模型
调用 handler inst 时会调用外部传入的线程创建接口——一个 handler 构造一个线程。因为对通用算法而言,能管理的 LED 数量绝对有上限,超过对象管理数目就得再开线程,不可能一个 handler 管理上百万个 LED。
— 老师,调用handler inst的时候会调用外部传入的线程创建接口(Jack)
6. 模块化与事件驱动:按键/LED 的队列设计
- 什么是事件驱动:以按键为例,大部分时间按键检测线程都是阻塞的(不占 CPU,因为 CPU 没有 polling);按键被按下时发出一个队列消息,让按键检测线程退出阻塞状态(开始占用 CPU)。
— 什么是事件驱动? 事件驱动简(Jack)
- 按键模块怎么实现:用事件驱动的思想,基于中断计数队列传输。如果用按键扫描方式,会造成很多空扫,为了这些空扫设备无法实现低功耗。
— 问:按键模块是怎么进行的 答:(Jack)
- 为什么 LED 也要单独建队列,而不是 key_thread 与 led_thread 共用一个队列?各干各的事:做模块化时,不可能一个驱动向另一个驱动传递自己的信息——写 LED 驱动的人,并不知道 key 的存在。
— 老师我这里不是很理解为什么要为led也创建一个队列,按道理两个线程(Jack)
7. 与 RT-Thread 设备框架的对应
RT-Thread 的设备框架是"片上外设驱动框架",对应本课程架构中的 BSP 层的 Handler 层。
— 问: 我之前用过 RT Thr(Jack)
8. 解耦与平台化的分寸
- 做解耦时,Core/driver 层觉得 CubeMX 生成的代码解耦性不强,要不要抽象?Core 以下再做抽象意义不大:这部分原厂会提供,复用度太小,换芯片时 Core 及 Core 以下全换,这是原厂 SDK 的事。
— 老师,做解耦时,core 层和 driver层感觉cubemx生成的代码解耦性不强(Jack)
- "去平台化"与"平台化"的本质:驱动与 app 完成解耦可称"去平台化";项目实际运行需要把抽象接口具体化,即把各模块"平台化"——平台化的本质就是将模块内部的抽象接口具体化。
— 关于“平台化”和“去平台化”的(Lili_iliLX 提问,Jack 确认)
- 自己设计的 handler 接口注册框架行不行?无论单元接口怎么设计,系统集成工程师都会设计 adapter 去集成你和其他模块的关系,明确接口和测试范围。
— 老师,看了你们aht21这个初级框架的课程。我讲一下我现在驱动这里怎么做(Jack)
9. HAL 库使用心态
HAL 库的有些操作(尤其中断回调)有时和预期不一致——用别人的库,一定要能通过各种方法走到最底层去调试(排查示例截图见原帖)。
— hal库的有些操作,尤其是中断回调,有时候和我们预期是不一致(Jack)
10. 更多相关问答(未答/图答)
- 如果在驱动初始化时要对设备做一些参数设置,这些设置以后是不会改(Jack,未答)——固定参数写死 BSP 还是引出接口之问
- 老师,为什么OTA的课和mpu6050驱动的课都使用buff1和buff2而不使用环(daneel.wan*,未答)——双 buffer 与环形缓冲区的选型之问
- 老师,按键这部分课程中,为什么要把task放入bsp层呢?我理解他应该是应用层(daneel.wan*,未答)——按键 task 为何放 BSP 层
- 星主,请教一个问题,我想把软件 spi 和硬件 spi 用一个对象封装起来(这是一个好听的id,未答)——软硬件 SPI/IIC 统一对象封装之问
- 想问下操作系统层对bsp层的I:O控制是怎么实现的?看了视频(爱健身的机器人,图答)——操作系统层对 BSP 的 I/O 控制实现
- AHT21驱动的调用流程参考(Jack,资源)——AHT21 驱动调用流程图 PDF
来源笔记
- hal是硬件抽象层,driver将hal组合起来完成基本的外设控制(Jack)
- Driver:理解为材料包,木头,手柄,比如hal库(Jack)
- 同学提问: Core和BSP(Jack)
- 对Driver层和Core层还是不太理解,为什么都提到了对外设的初始化(Jack)
- 老师,我写了个ADC+DMA的代码,实现了ADC数据采集以后(Jack)
- 什么类型的函数适合放到BSP驱(Jack)
- 行业常识:驱动中私有变量的访问方法(防止脏数据系统紊乱(Jack)
- BSP驱动中如何处理私有数据?比如设备的ID,APP想知道设备的ID:(Jack)
- 老师,调用handler inst的时候会调用外部传入的线程创建接口(Jack)
- 什么是事件驱动? 事件驱动简(Jack)
- 问:按键模块是怎么进行的 答:(Jack)
- 老师我这里不是很理解为什么要为led也创建一个队列,按道理两个线程(Jack)
- 问: 我之前用过 RT Thr(Jack)
- 老师,做解耦时,core 层和 driver层感觉cubemx生成的代码解耦性不强(Jack)
- 关于“平台化”和“去平台化”的(Lili_iliLX)
- 老师,看了你们aht21这个初级框架的课程。我讲一下我现在驱动这里怎么做(Jack)
- hal库的有些操作,尤其是中断回调,有时候和我们预期是不一致(Jack)