2 个版本

0.1.2 2024 年 1 月 15 日
0.1.1 2020 年 7 月 21 日

#2022嵌入式开发

每月 45 次下载
2 个软件包中使用 (通过 nrf-softdevice)

自定义许可证

680KB
16K SLoC

nrf-softdevice

Rust 为 Nordic Semiconductor nRF 系列SoftDevices 提供的绑定。

SoftDevices 是 Nordic 为其微控制器编写的闭源 C 二进制文件,位于闪存底部,在启动时首先调用。然后软设备调用您的应用程序或引导加载程序或位于闪存中直接在其后面的任何内容。

它们功能齐全,经过实战检验,并已预先通过蓝牙认证,因此在与 Rust 绑定时成为宝贵的蓝牙堆栈——至少直到我们获得一个经过认证的、可以商业发行的 Rust 蓝牙堆栈。不同的 SoftDevices 支持特定的芯片以及某些功能,例如仅作为外围设备工作,或同时作为外围设备和中心设备,甚至提供类似 ant 的备用无线电配置。

除了闭源的限制外,SoftDevices 的成本在于它们会占用您的应用程序的资源,如 RAM 和闪存,以及定时器外设和多个中断优先级。

高级绑定

nrf-softdevice 软件包包含用于 Softdevice 的高级易用 Rust async/await 绑定。

工作

  • 安全的中断管理
  • 异步闪存 API
  • 蓝牙中心(扫描和连接)
  • 蓝牙外围设备(目前仅为可连接的)
  • GATT 客户端
  • GATT 服务器
  • L2CAP 连接型通道
  • 数据长度扩展
  • ATT MTU 扩展
  • 获取/设置自己的 BLE 地址

要使用它,您必须指定以下 Cargo 功能

  • 确切的软设备模型,例如功能 s140
  • 确切的受支持的 nRF 芯片模型,例如功能 nrf52840

以下软设备受到支持。

  • S112(仅外围设备)
  • S113(仅外围设备)
  • S122(仅中心设备)
  • S132(中心和外围设备)
  • S140 v7.x.x(中心和外围设备)

以下nRF芯片受支持:

  • nRF52805
  • nRF52810
  • nRF52811
  • nRF52820
  • nRF52832
  • nRF52833
  • nRF52840

一些软设备只支持某些芯片,有关详细信息,请查看Nordic的文档。

设置您的构建环境

该项目过去需要夜间工具链功能,但这些功能最近已经稳定。因此,请确保您的工具链是最新的,可以通过获取最新稳定工具链来实现

rustup update

您还需要 probe-rs - 一个实用程序,用于启用 cargo run 在设备上运行嵌入式应用程序。按照 probe-rs 网站上的说明 安装它。

运行示例

以下说明适用于S140和nRF52840-DK。您可能需要相应地调整,并且可以通过修改示例文件夹中的 cargo.toml 来实现 - 请查看 nrf-softdevicenrf-softdevice-s140 依赖项声明。

需要烧写软设备。它不是构建二进制文件的一部分。您只需在开始时或在进行完全擦除芯片后进行一次即可。

  • 从Nordic的网站 此处 下载SoftDevice S140。支持的版本是7.x.x
  • 解压
  • 如果您是调试客户端,并且使用
    • probe-rs
      • 使用 probe-rs erase --chip nrf52840_xxAA 擦除闪存(您可能需要提供额外的 --allow-erase-all 参数)。
      • 使用 probe-rs download --verify --binary-format hex --chip nRF52840_xxAA s140_nrf52_7.X.X_softdevice.hex
    • nrfjprog
      • 使用 nrfjprog --family NRF52 --chiperase --verify --program s140_nrf52_7.0.1_softdevice.hex

要运行示例,只需从 examples 文件夹使用 cargo run 即可。

  • cdexamples && cargorun --binble_bas_peripheral --featuresnrf52840-dk

示例还可以为针对S132软设备的目标nrf52832开发套件构建(功能标志 nrf52832-dk),或者为BBC micro:bit v2上的S140软设备的目标nrf52833构建(功能标志 microbit-v2)。在这些情况下,根据需要编辑 .cargo/config.toml

配置SoftDevice

首先,找出您选择的SoftDevice使用的闪存量。查看发布说明,或在网上搜索您的SoftDevice版本和“内存映射”。对于s132 v7.3,其列出的值为0x26000,或以人类可读的数字为152K(十六进制中的0x26000等于十进制中的155648 / 1024字节 = 152K)

将 memory.x 设置为将您的应用程序的闪存起始地址移动到SoftDevice大小之后,并从总可用大小中减去它

MEMORY
{
  /* NOTE 1 K = 1 KiBi = 1024 bytes */
  /* These values correspond to the NRF52832 with SoftDevices S132 7.3.0 */
  FLASH : ORIGIN = 0x00000000 + 152K, LENGTH = 512K - 152K
  RAM : ORIGIN = 0x20000000 + 44K, LENGTH = 64K - 44K
}

目前您可以选择大多数东西作为RAM,因为如果启用了defmt日志记录,SoftDevice会在调用enable时告诉您正确的数字。

1 INFO  softdevice RAM: 41600 bytes
└─ nrf_softdevice::softdevice::{impl#0}::enable @ /home/jacob/.cargo/git/checkouts/nrf-softdevice-03ef4aef10e777e4/fa369be/nrf-softdevice/src/fmt.rs:138
2 ERROR panicked at 'too little RAM for softdevice. Change your app's RAM start address to 2000a280'

您可以通过调整SoftDevice配置参数来控制这个数字。特别是并发连接参数。如果您不需要支持多个连接,这些参数可以显著减小您的RAM大小。

  • conn_gap.conn_count:在此配置下,应用程序可以创建的并发连接数。
  • periph_role_count:同时作为外围设备的最大连接数。
  • central_role_count:同时作为中心设备的最大连接数。

接下来,您需要找出您的板子上是否有外部振荡器(提供更好的电池寿命)。但如果不确定,只需假设它没有,并将SoftDevice设置为使用内部时钟。nRF52常见的无外部晶振配置可能如下:

        clock: Some(raw::nrf_clock_lf_cfg_t {
            source: raw::NRF_CLOCK_LF_SRC_RC as u8,
            rc_ctiv: 16,
            rc_temp_ctiv: 2,
            accuracy: raw::NRF_CLOCK_LF_ACCURACY_500_PPM as u8,
        }),

中断

SoftDevice在高优先级下执行时间关键性无线电处理。如果其定时被打断,它将引发“断言失败”错误。有两个常见的错误需要避免:(暂时)禁用软设备的中断和运行中断优先级过高。

这些错误将导致“断言失败”错误,100%保证。如果您只“稍微”这样做,例如仅短暂禁用所有中断,事情似乎可以正常工作,但在运行数小时后您将得到“断言失败”错误。请务必完全遵循。

默认情况下,Softdevice驱动程序(例如 Softdevice::run())不能从中断使用。但是,usable-from-interrupts功能启用了此功能。要使用此功能,需要一个critical-section实现。此存储库的内部实现(critical-section-impl功能)被推荐,但其他与Softdevice兼容的实现也应该可以工作。

关键部分

某些外围设备和SWI/EGU的中断被保留给SoftDevice。这些中断的处理程序由软设备保留,您的应用程序中的处理程序不会被调用。

请勿禁用软设备的 interrupts。您绝对不应该使用广泛使用的 cortex_m::interrupt::free 在“禁用所有中断”的关键部分中。相反,使用 critical-section 存储库,它允许自定义关键部分实现。

  • 确保为 nrf-softdevice 启用 critical-section-impl Cargo功能。这会使 nrf-softdevice 发出自定义关键部分实现,该实现仅禁用非SoftDevice中断。
  • 使用 critical_section::with 而不是 cortex_m::interrupt::free。这使用自定义关键部分实现。
  • 使用 embassy_sync::blocking_mutex::CriticalSectionMutex 而不是 cortex_m::interrupt::Mutex

确保您没有使用任何内部使用 cortex_m::interrupt::free 的库。

中断优先级

中断优先级0、1和4被保留给SoftDevice。请确保不要使用它们。

中断的默认优先级为0,因此对于您启用的每个中断,请确保显式设置优先级。例如

use embassy_nrf::interrupt::{self, InterruptExt};

interrupt::SPIM3.set_priority(interrupt::Priority::P3);
let mut spim = spim::Spim::new(p.SPI3, Irqs, p.P0_13, p.P0_16, p.P0_15, config);

如果您正在使用启用了 embassy-nrf 以及 gpiotetime-driver-rtc1 功能,您需要编辑您的 embassy_config 来调整这些优先级。

// 0 is Highest. Lower prio number can preempt higher prio number
// Softdevice has reserved priorities 0, 1 and 4
let mut config = embassy_nrf::config::Config::default();
config.gpiote_interrupt_priority = Priority::P2;
config.time_interrupt_priority = Priority::P2;
let peripherals = embassy_nrf::init(config);

故障排除

中断优先级

如果您确定已经正确设置了中断,但仍然遇到以下错误

[ERROR]Location<lib.rs:104>panicked at 'sd_softdevice_enable err SdmIncorrectInterruptConfiguration'

请确保在 embassy_nrf 上启用了 defmt 功能。

然后,您可以使用以下代码来打印中断是否启用以及其优先级

// NB! MAX_IRQ depends on chip used, for example: nRF52840 has 48 IRQs, nRF52832 has 38.
const MAX_IRQ: u16 = ...;

use embassy_nrf::interrupt::{Interrupt, InterruptExt};
for num in 0..=MAX_IRQ {
    let interrupt = unsafe { core::mem::transmute::<u16, Interrupt>(num) };
    let is_enabled = InterruptExt::is_enabled(interrupt);
    let priority = InterruptExt::get_priority(interrupt);

    defmt::println!("Interrupt {}: Enabled = {}, Priority = {}", num, is_enabled, priority);
}

中断号映射到 Interrupt 枚举 中的内容。

如果您的 SoftDevice 在启用时出现硬错误,并且您认为一切设置正确,请确保返回并执行完全的芯片擦除或恢复,并重新刷新 SoftDevice。SoftDevice 后需要留出一些字节为 0xFF 的空空间,但如果软设备是在现有二进制文件上刷新的,则可能不需要。

外设冲突

如果出现以下运行时错误

Softdevice memory access violation. Your program accessed registers for a peripheral reserved to the softdevice. PC=2a644 PREGION=8192

检查应用程序使用的外设。

当 Softdevice 启用(甚至禁用)时,它使用一定数量的外设来执行其功能,并因此对外设的可用性强制实施某些限制 (参见外设要求)

  1. 打开 - 外设不被 SoftDevice 使用,并且应用程序具有完全访问权限。
  2. 阻止 - 外设被 SoftDevice 使用,并且所有应用程序访问都被禁用。尽管如此,某些外设(RADIO、TIMER0、CCM 和 AAR)可以通过 Softdevice Radio Timeslot API 进行访问。
  3. 受限 - 外设被 SoftDevice 使用,但可以通过 SoftDevice API 进行有限的访问。例如 FLASHRNGTEMP 外设。

链接问题

如果出现以下链接错误

rust-lld: error: undefined symbol: _critical_section_release

确保启用了功能 critical-section-impl,并且软设备已包含在代码中,例如 use nrf_softdevice as _;

如果刷新固件后运行固件超时,请确保在链接脚本中 RAM 和 FLASH 区域的大小和位置正确。

低级原始绑定

包含低级绑定的 nrf-softdevice-s1xx 库与软设备 C 头文件一一对应。

它们使用 bindgen 生成,并进行了额外的后处理,以正确生成基于 svc 的软设备调用。

生成的代码由使用内联汇编的内联函数组成,确保尽可能低的开销。大多数时候您会在调用函数中看到它们作为单个 svc 指令的内联。以下是一个示例

#[inline(always)]
pub unsafe fn sd_ble_gap_connect(
      p_peer_addr: *const ble_gap_addr_t,
      p_scan_params: *const ble_gap_scan_params_t,
      p_conn_params: *const ble_gap_conn_params_t,
      conn_cfg_tag: u8,
) -> u32 {
    let ret: u32;
    core::arch::asm!("svc 140",
        inout("r0") p_peer_addr => res,
        inout("r1") p_scan_params => _,
        inout("r2") p_conn_params => _,
        inout("r3") conn_cfg_tag => _,
        lateout("r12") _,
    );
    ret
}

生成

使用 gen.sh 脚本从头文件生成绑定。

许可协议

此存储库包括软设备头文件,这些头文件受 Nordic 的专有许可。生成的 binding.rs 文件是头文件的派生作品,因此也受 Nordic 许可的约束。

高级绑定(nrf-softdevice)和生成代码(nrf-softdevice-gen)的许可协议为以下之一

由您选择。

无运行时依赖