蓝牙芯片中埋伏的“微内核”:nRFSDK SoftDevice探究

[复制链接]
发表于 2025-12-17 09:57:56 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
假如你刚开始打仗 Nordic 的 nRF52 或 nRF53 系列芯片,你大概会发如今烧录步伐时,除了你自己的应用步伐(Application),通常还须要烧录一个名为 SoftDevice 的大文件。这不但是一个蓝牙协议栈库,它更像是一个霸道的“微内核”。它担当了芯片最核心的资源,以致把你(开辟者)的代码“赶”到了低特权级运行。**
为什么 Nordic 要计划这种看似复杂的布局?它又是怎样包管你的代码和协议栈宁静共处的?本日这篇博文,我们将揭开 SoftDevice 的秘密面纱。
1. 什么是 SoftDevice?不但仅是库文件

在传统的单片机开辟中,假如我们想利用一个功能(好比驱动屏幕或读取传感器),通常会包罗一个 .h 头文件,链接一个 .lib 库,然后直接调用内里的函数。
但 SoftDevice 差别。它是一个预编译好的、二进制格式的协议栈,位于芯片 Flash 的固定位置。你可以把它想象成操纵体系中的内核 (Kernel),而你的应用步伐则是运行在用户空间 (User Space) 的步伐:

  • 特权级: SoftDevice 运行在更高的 CPU 特权级,它独占了射频 (Radio) 和核心定时器。
  • 掩护性: 它掩护自己不被用户代码随意访问或粉碎。
  • 实时性: 无论你的步伐在做什么,一旦蓝牙时序到来,SoftDevice 会强行打断你,优先处理惩罚蓝牙信号。
2. 它们怎样沟通?(SVC 调用机制与 SWI 软件克制)

以蓝牙广播为例,应用步伐怎样哀求 SoftDevice 开始蓝牙广播呢?答案是:SVC (Supervisor Call,管理调用)。
在 softdevice 提供的头文件内界说了一系列 SVC 的克制号:
  1. enum BLE_GAP_SVCS
  2. {
  3.   SD_BLE_GAP_ADDR_SET                   = BLE_GAP_SVC_BASE,       /**< Set own Bluetooth Address. */
  4.   ...
  5.   SD_BLE_GAP_ADV_START                  = BLE_GAP_SVC_BASE + 7,   /**< Start Advertising. */
  6.   ...
  7. };
复制代码
想象 SoftDevice 是司理,你的 App 是员工。员工不能直接冲进司理办公室操纵电脑,必须提交一份“申请单”。

  • 发起哀求: 当你在代码中调用 sd_ble_gap_adv_start() 时,你并不是直接实验广播代码,而是实验了 SVC 中克制。
    sd_ble_gap_adv_start() 函数,把干系宏界说睁开后可以得到如下情势:
  1. _Pragma("GCC diagnostic push")  
  2. _Pragma("GCC diagnostic ignored "-Wreturn-type"")  // 由于是汇编函数,不包含return指令,需要防止编译器报错
  3. __attribute__((naked)) // 函数编译时都会自动生成push(入栈)出栈(操作)指令。naked关键字让编译器不要生成这些指令,由用户自己实现
  4. __attribute__((unused))
  5. static uint32_t sd_ble_gap_adv_start(uint8_t adv_handle, uint8_t conn_cfg_tag)
  6. {
  7.   __asm(
  8.     "svc %0\n" // 将 SD_BLE_GAP_ADV_START 编号作为立即数放入 SVC 指令
  9.     "bx r14"// 函数返回,将控制权交还给调用者
  10.     : /* 输出操作数 */
  11.     : /* 输入操作数 */ "I" (GCC_CAST_CPP SD_BLE_GAP_ADV_START) /* SVC 编号 */
  12.     : "r0" /* 临时寄存器,表示 SoftDevice 会使用 R0 寄存器作为返回值 */
  13.   );
  14. }
  15. _Pragma("GCC diagnostic pop")
复制代码

  • 特权切换: SVC 指令会触发一个硬件非常(软克制)。CPU 瞬间停息你的步伐,从用户模式切换到特权模式,进入 SoftDevice 中的 SVC_Handler。
  • 内核处理惩罚: 控制权移交给 SoftDevice 的克制处理惩罚步伐。它查抄你的哀求,确认参数无误后,操纵底层硬件开启广播。
  • 返回结果: 处理惩罚完毕后,SoftDevice 将结果返回给你,CPU 切回用户模式,你的步伐继承向下实验。
    这种机制包管了安全性:用户步伐永世无法直打仗碰底层硬件,必须通过“正规渠道”申请。
反过来,SoftDevice 怎样关照应用步伐呢?

  • 产生事故并关照: 当蓝牙毗连创建时,SoftDevice 天生一个 BLE_GAP_EVT_CONNECTED 事故数据包,把它丢进一个共享的收件箱”(事故队列),然后产生一个软件克制 SD_EVT_IRQn 来关照应用步伐。
  1. // SoftDevice 内部(伪代码)
  2. void softdevice_internal_ble_event_handler(void)
  3. {
  4.     // 1. BLE 事件发生(如连接、断开、数据接收)
  5.     ble_evt_t evt;
  6.     evt.header.evt_id = BLE_GAP_EVT_CONNECTED;
  7.     // ... 填充事件数据
  8.    
  9.     // 2. 将事件放入 SoftDevice 内部队列
  10.     sd_internal_event_queue_push(&evt);
  11.    
  12.     // 3. 触发软件中断通知应用程序
  13.     NVIC_SetPendingIRQ(SD_EVT_IRQn);  // 触发 SWI2 中断
  14. }
复制代码

  • 提取处理惩罚: 你的应用步伐在主循环中醒来,调用 sd_ble_evt_get()(这也是一个 SVC 调用),从“收件箱”里取出信件,然后根据事故范例实验相应的逻辑(好比亮起 LED 灯)。
  1. // 简化的事件处理伪代码
  2. nrf_sdh_ble_evts_poll()
  3. {
  4.         while (true) {
  5.             // 获取事件
  6.             sd_ble_evt_get(p_ble_evt);
  7.        
  8.             // 检查事件
  9.             switch (p_ble_evt->header.evt_id) {
  10.                 case BLE_GAP_EVT_CONNECTED:
  11.                     // 处理连接
  12.                     break;
  13.                 case BLE_GAP_EVT_DISCONNECTED:
  14.                     // 处理断开
  15.                     break;
  16.             }
  17.         }
  18. }
复制代码
将上面的流程化成流程图如下:
sequenceDiagram    autonumber    participant App as 用户步伐 (Application)
Thread Mode / Low Priority    participant SVC as SVC 接口
(Exception Boundary)    participant SD as SoftDevice (内核)
Handler Mode / High Priority    participant HW as 硬件 (Hardware)
Radio / Timer / NVIC    %% 场景一:用户自动调用 (SVC Call)    rect rgb(230, 245, 255)    note over App, HW: 场景 1: 用户步伐哀求服务 (比方:开启广播)    App->>SVC: 调用 sd_ble_gap_adv_start()    activate SVC    note right of App: 触发 SVC 克制
CPU 切入特权模式    SVC->>SD: 转达 SVC 编号 & 参数    activate SD    SD->>HW: 设置 Radio 寄存器    SD-->>SVC: 返回实验结果 (NRF_SUCCESS)    deactivate SD    SVC-->>App: 返回函数调用    deactivate SVC    note left of App: CPU 切回用户模式
步伐继承实验    end    %% 场景二:事故关照 (Event)    rect rgb(255, 240, 230)    note over App, HW: 场景 2: SoftDevice 关照用户 (比方:创建毗连)    HW->>SD: 硬件克制 (Radio IRQ)    activate SD    SD->>SD: 天生事故 (EVT_CONNECTED)
放入事故队列    SD->>App: 触发 SD_EVT_IRQn (SWI软件克制) 关照应用    deactivate SD        note left of App: 应用被克制叫醒
进入事故处理惩罚函数    App->>SVC: 调用 sd_ble_evt_get()    activate SVC    SVC->>SD: 哀求获取队列中的事故    activate SD    SD-->>SVC: 返回事故数据布局    deactivate SD    SVC-->>App: 返回事故数据    deactivate SVC    App->>App: switch (evt_id) { case CONNECTED... }    end4. 为什么要这么贫困?

这种应用与协议栈分离的计划固然增长了学习资本,但带来了巨大的利益:

  • 极致的稳固性: 无论你的应用步伐写得多么烂(死循环、野指针),SoftDevice 都有硬件掩护机制(MPU),大概率能包管蓝牙毗连不停开。
  • 简化的认证: 由于底层的 RF 协议栈是预编译且不可修改的,Nordic 已经帮它过了许多法规认证。只要你不动射频参数,过 FCC/CE 认证会轻易得多。
  • 零时序负担: 你不须要在大循环里盘算蓝牙跳频的时间,也不消担心处理惩罚传感器数据太慢导致蓝牙掉线。SoftDevice 会自动通过高优先级克制处理惩罚全部时序敏感的任务。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表