Rust嵌入式开发库nrf52833-pac的使用:Nordic nRF52833芯片外设访问控制与硬件抽象层实现
Rust嵌入式开发库nrf52833-pac的使用:Nordic nRF52833芯片外设访问控制与硬件抽象层实现
nRF微控制器的PACs
这个仓库包含用于Nordic nRF系列Cortex-M微控制器的外设访问控制库(PACs)。
所有这些crate都是使用svd2rust自动生成的。
对于更用户友好的外设接口,nrf-hal crate可能更合适。
Crates
每个nRF芯片都有其自己的PAC,如下表所示:
Crate | Docs | crates.io | target |
---|---|---|---|
nrf51-pac | ![docs.rs] | ![crates.io] | thumbv6m-none-eabi |
nrf52805-pac | ![docs.rs] | ![crates.io] | thumbv7em-none-eabi |
nrf52810-pac | ![docs.rs] | ![crates.io] | thumbv7em-none-eabi |
nrf52811-pac | ![docs.rs] | ![crates.io] | thumbv7em-none-eabi |
nrf52820-pac | ![docs.rs] | ![crates.io] | thumbv7em-none-eabi |
nrf52832-pac | ![docs.rs] | ![crates.io] | thumbv7em-none-eabihf |
nrf52833-pac | ![docs.rs] | ![crates.io] | thumbv7em-none-eabihf |
nrf52840-pac | ![docs.rs] | ![crates.io] | thumbv7em-none-eabihf |
nrf5340-app-pac | ![docs.rs] | ![crates.io] | thumbv8m.main-none-eabihf |
nrf5340-net-pac | ![docs.rs] | ![crates.io] | thumbv8m.main-none-eabihf |
nrf9160-pac | ![docs.rs] | ![crates.io] | thumbv8m.main-none-eabihf |
Nordic的设备参考手册
设备 | 产品规格 | DK参考指南 |
---|---|---|
nRF52805 | v1.2 | v1.3.1* |
nRF52810 | v1.3 | v1.3.1* |
nRF52811 | v1.0 | v1.3.1* |
nRF52820 | v1.0 | v1.0.1 |
nRF52832 | v1.4 | v1.3.1* |
nRF52833 | v1.3 | v1.极1 |
nRF52840 | v1.1 | v1.2 |
nRF5340 | v1.1 | v1.0.0 |
nRF9160 | v2.0 | v0.9.3 |
*这些设备没有单独的开发套件,共享NRF52 DK
nrf52833-pac使用示例
下面是一个使用nrf52833-pac控制GPIO的完整示例:
// 导入必要的库
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use panic_halt as _;
use nrf52833_pac as pac;
#[entry]
fn main() -> ! {
// 获取外设访问实例
let p = pac::Peripherals::take().unwrap();
// 配置GPIO
let gpio = p.P0; // 获取P0 GPIO端口
// 配置P0.13引脚为输出
gpio.p极_cnf[13].write(|w| {
w.dir().output() // 设置为输出模式
.input().disconnect() // 断开输入
.pull().disabled() // 禁用上拉/下拉
.drive().s0s1() // 标准驱动强度
.sense().disabled() // 禁用感应
});
// 主循环,切换LED状态
loop {
// 设置P0.13为高电平
gpio.outset.write(|w| unsafe { w.bits(1 << 13) });
cortex_m::asm::delay(1_000_000); // 简单延时
// 设置P0.13为低电平
gpio.outclr.write(|w| unsafe { w.bits(1 << 13) });
cortex_m::asm::delay(1_000_000); // 简单延时
}
}
UART通信示例
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use panic_halt as _;
use nrf52833_pac as pac;
#[entry]
fn main() -> ! {
let p = pac::Peripherals::take().unwrap();
// 配置UARTE0
let uarte = p.UARTE0;
// 1. 先关闭UARTE以便配置
uarte.enable.write(|w| w.enable().disabled());
// 2. 配置波特率为115200
uarte.baudrate.write(|w| w.baudrate().baud115200());
// 3. 配置引脚
uarte.psel.rxd.write(|w| unsafe { w.bits(8) }); // 使用P0.08作为RXD
uarte.psel.txd.write(|w| unsafe { w.bits(6) }); // 使用P0.06作为TXD
// 4. 启用UARTE
uarte.enable.write(|w| w.enable().enabled());
// 要发送的数据
let data: [u8; 5] = [b'H', b'e', b'l', b'l', b'o'];
// 5. 发送数据
uarte.txd.ptr.write(|w| unsafe { w.bits(data.as_ptr() as u32) });
uarte.txd.maxcnt.write(|w| unsafe { w.bits(data.len() as u32) });
uarte.tasks_starttx.write(|极| unsafe { w.bits(1) });
loop {
// 等待发送完成
while uarte.events_endtx.read().bits() == 0 {}
uarte.events_endtx.write(|w| unsafe { w.bits(0) });
cortex_m::asm::delay(5_000_000);
}
}
SPI通信示例
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use panic_halt as _;
use nrf52833_pac as pac;
#[entry]
fn main() -> ! {
let p = pac::Peripherals::take().unwrap();
// 配置SPI0
let spim = p.SPIM0;
// 1. 先禁用SPIM以便配置
spim.enable.write(|w| w.enable().disabled());
// 2. 配置SPI模式
spim.config.write(|w|
w.order().msb_first() // 高位在前
.mode().mode0() // SPI模式0
);
// 3. 配置频率
spim.frequency.write(|w| w.frequency().m8());
// 4. 配置引脚
spim.psel.sck.write(|w| unsafe { w.bits(25) }); // SCK on P0.25
spim.psel.mosi.write(|w| unsafe { w.bits(23) }); // MOSI on P0.23
spim.psel.miso.write(|w| unsafe { w.bits(24) }); // MISO on P0.24
// 5. 启用SPIM
spim.enable.write(|w| w.enable().enabled());
// 要发送的数据
let tx_data: [u8; 4] = [0xAA, 0xBB, 0xCC, 0xDD];
let mut rx_data: [u8; 4] = [0; 4];
loop {
// 6. 配置发送和接收缓冲区
spim.txd.ptr.write(|w| unsafe { w.bits(tx_data.as_ptr() as u32) });
spim.txd.maxcnt.write(|w| unsafe { w.bits(tx_data.len() as u32) });
spim.rxd.ptr.write(|w| unsafe { w.bits(rx_data.as_mut_ptr() as u32) });
spim.rxd.maxcnt.write(|w| unsafe { w.bits(rx_data.len() as u32) });
// 7. 启动传输
spim.tasks_start.write(|w| unsafe { w.bits(1) });
// 等待传输完成
while spim.events_end.read().bits() == 0 {}
spim.events_end.write(|w| unsafe { w.bits(0) });
cortex_m::asm::delay(1_000_000);
}
}
这些示例展示了如何使用nrf52833-pac库直接访问和控制nRF52833芯片的外设。在实际应用中,你可能需要考虑使用更高层次的抽象库如nrf-hal来简化开发。
1 回复
nrf52833-pac库:Nordic nRF52833芯片的Rust嵌入式开发指南
概述
nrf52833-pac
是一个Rust库,为Nordic nRF52833蓝牙SoC芯片提供了外设访问控制(PAC)和硬件抽象层(HAL)实现。这个库是使用svd2rust
工具从芯片的SVD(系统视图描述)文件自动生成的,提供了对芯片所有外设寄存器的安全访问接口。
主要特性
- 类型安全的寄存器访问
- 自动生成的外设模块结构
- 符合Rust所有权模型的硬件访问控制
- 支持中断处理
- 提供硬件抽象层实现
使用方法
添加依赖
首先在Cargo.toml中添加依赖:
[dependencies]
nrf52833-pac = "0.10.0"
cortex-m = "0.7.6"
cortex-m-rt = "0.7.3"
基本外设访问示例
use cortex_m_rt::entry;
use nrf52833_pac as pac;
#[entry]
fn main() -> ! {
// 获取外设访问实例
let peripherals = pac::Peripherals::take().unwrap();
// 访问GPIO外设
let p0 = &peripherals.P0;
// 配置P0.03引脚为输出
p0.pin_cnf[3].write(|w| w.dir().output());
// 设置P0.03引脚高电平
p0.out.write(|w| w.pin3().high());
// 访问UARTE0外设
let uarte0 = &peripherals.UARTE0;
// 配置UART波特率
uarte0.baudrate.write(|w| w.baudrate().baud115200());
loop {}
}
中断处理示例
use cortex_m_rt::entry;
use nrf52833_pac::{interrupt, Peripherals};
#[entry]
fn main() -> ! {
let peripherals = Peripherals::take().unwrap();
// 配置GPIO中断
let gpio = &peripherals.P0;
gpio.pin_cnf[0].write(|w| w.input().connect().sense().high());
// 启用GPIOTE中断
peripherals.GPIOTE.intenset.write(|w| w.in0().set());
// 启用全局中断
unsafe { cortex_m::peripheral::NVIC::unmask(nrf52833_pac::Interrupt::GPIOTE); }
loop {}
}
#[interrupt]
fn GPIOTE() {
// 处理GPIO中断
let peripherals = unsafe { Peripherals::steal() };
peripherals.GPIOTE.events_in[0].write(|w| w.events_in().clear());
// 执行中断处理逻辑
}
硬件抽象层(HAL)使用示例
use nrf52833_pac as pac;
use nrf52833_hal::{
gpio::Level,
prelude::*,
timer::Timer,
};
#[entry]
fn main() -> ! {
let peripherals = pac::Peripherals::take().unwrap();
// 使用HAL初始化GPIO
let p0 = nrf52833_hal::gpio::p0::Parts::new(peripherals.P0);
let mut led = p0.p0_13.into_push_pull_output(Level::Low);
// 使用HAL初始化定时器
let mut timer = Timer::new(peripherals.TIMER0);
loop {
led.set_high().unwrap();
timer.delay_ms(500u32);
led.set_low().unwrap();
timer.delay_ms(500u32);
}
}
完整示例代码
下面是一个结合GPIO、定时器和中断处理的完整示例:
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use panic_halt as _;
use nrf52833_pac::{self as pac, interrupt};
use nrf52833_hal::{
gpio::Level,
timer::Timer,
prelude::*,
};
#[entry]
fn main() -> ! {
// 获取外设实例
let peripherals = pac::Peripherals::take().unwrap();
// 初始化GPIO
let p0 = nrf52833_hal::gpio::p0::Parts::new(peripherals.P0);
let mut led1 = p0.p0_13.into_push_pull_output(Level::Low); // LED1
let mut led2 = p0.p0_14.into_push_pull_output(Level::Low); // LED2
let button = p0.p0_11.into_pullup_input(); // 按钮
// 配置按钮中断
button.port().pin_cnf[11].write(|w|
w.sense().high()
.input().connect()
.drive().s0s1()
);
// 启用GPIOTE中断
peripherals.GPIOTE.intenset.write(|w| w.in0().set());
// 初始化定时器0
let mut timer = Timer::new(peripherals.TIMER0);
// 启用全局中断
unsafe {
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::GPIOTE);
}
// 主循环
loop {
// LED1闪烁
led1.set_high().unwrap();
timer.delay_ms(500u32);
led1.set_low().unwrap();
timer.delay_ms(500u32);
}
}
// GPIOTE中断处理
#[interrupt]
fn GPIOTE() {
// 获取外设实例
let peripherals = unsafe { pac::Peripherals::steal() };
// 清除中断事件
peripherals.GPIOTE.events_in[0].write(|w| w.events_in().clear());
// 获取GPIO端口
let p0 = unsafe { &(*pac::P0::ptr()) };
// 切换LED2状态
if p0.out.read().pin14().is_high() {
p0.out.write(|w| w.pin14().low());
} else {
p0.out.write(|w| w.pin14().high());
}
}
外设模块
nrf52833-pac
提供了对nRF52833所有外设的访问,包括:
- GPIO (P0, P1)
- UARTE (UARTE0, UARTE1)
- SPIM/SPIS (SPIM0-3, SPIS0-3)
- TWIM/TWIS (I2C接口)
- PWM (PWM0-3)
- ADC
- TIMER (TIMER0-4)
- RTC (RTC0, RTC1)
- 蓝牙低功耗(BLE)外设
- 等等
最佳实践
- 安全访问:始终使用
write()
和modify()
方法来访问寄存器,而不是直接写入 - 中断处理:在中断处理程序中避免长时间操作
- 电源管理:合理配置低功耗模式以节省能源
- 错误处理:对外设操作进行适当的错误检查