Rust嵌入式开发库nrf52840-pac的使用,为Nordic nRF52840芯片提供外设访问控制与硬件抽象功能
Rust嵌入式开发库nrf52840-pac的使用,为Nordic nRF52840芯片提供外设访问控制与硬件抽象功能
nrf52840-pac是一个Rust嵌入式开发库,专门为Nordic nRF52840芯片提供外设访问控制(Peripheral Access Crate)与硬件抽象功能。
安装
在项目目录中运行以下Cargo命令:
cargo add nrf52840-pac
或者在Cargo.toml中添加以下行:
nrf52840-pac = "0.12.2"
示例代码
以下是一个使用nrf52840-pac控制GPIO的完整示例:
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use nrf52840_pac as pac; // 导入nRF52840外设访问库
use panic_halt as _;
#[entry]
fn main() -> ! {
// 获取外设实例
let p = pac::Peripherals::take().unwrap();
// 配置P0.13引脚为输出
p.P0.pin_cnf[13].write(|w| {
w.dir().output() // 设置为输出模式
.input().disconnect() // 断开输入
.pull().disabled() // 禁用上拉/下拉
.drive().s0s1() // 标准驱动强度
.sense().disabled() // 禁用感应
});
// 无限循环,闪烁LED
loop {
// 设置P0.13为高电平
p.P0.outset.write(|w| w.p極13().set());
cortex_m::asm::delay(1_000_000); // 简单延时
// 设置P0.13为低电平
p.P0.outclr.write(|w| w.pin13().clear());
cortex_m::asm::delay(1_000_000);
}
}
更完整的UART通信示例
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use nrf52840_pac as pac;
use panic_halt as _;
#[entry]
fn main() -> ! {
let p = pac::Peripherals::take().unwrap();
// 1. 配置GPIO
// 设置P0.06为UART TX
p.P0.pin_cnf[6].write(|w| {
w.dir().output()
.input().disconnect()
.pull().disabled()
.drive().s0s1()
.sense().disabled()
});
// 设置P0.08为UART RX
p.P0.pin_cnf[8].write(|w| {
w.dir().input()
.input().connect()
.pull().disabled()
.drive().s0s1()
.sense().disabled()
});
// 2. 配置UART
// 先禁用UART
p.UART0.enable.write(|w| w.enable().disabled());
// 配置波特率为115200
p.UART0.baudrate.write(|w| w.baudrate().baud115200());
// 配置引脚选择
p.UART0.pseltxd.write(|w| unsafe { w.bits(6) });
p.UART0.pselrxd.write(|w| unsafe { w.bits(8) });
// 禁用CTS/RTS
p.UART0.pselcts.write(|w| w.bits(0xFFFFFFFF));
p.UART0.pselrts.write(|w| w.bits(0xFFFFFFFF));
// 启用UART
p.UART0.enable.write(|w| w.enable().enabled());
// 3. 发送数据
let message = b"Hello from nRF52840!\n";
for &byte in message {
// 等待发送缓冲区准备就绪
while p.UART0.events_txdrdy.read().bits() == 0 {}
p.UART0.events_txdrdy.write(|w| unsafe { w.bits(极) });
// 发送字节
p.UART0.txd.write(|w| unsafe { w.bits(u32::from(byte)) });
}
loop {}
}
SPI通信完整示例
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use nrf52840_pac as pac;
use panic_halt as _;
#[entry]
fn main() -> ! {
let p = pac::Peripherals::take().unwrap();
// 1. 配置SPI引脚
// MOSI - P0.20
p.P0.pin_cnf[20].write(|w| {
w.dir().output()
.input().disconnect()
.pull().disabled()
.drive().s0s1()
.sense().disabled()
});
// MISO - P0.21
p.P0.pin_cnf[21].write(|w| {
w.dir().input()
.input().connect()
.pull().disabled()
.drive().s0s1()
.sense().disabled()
});
// SCK - P0.19
p.P0.pin_cnf[19].write(|w| {
w.dir().output()
.input().disconnect()
.pull().disabled()
.drive().s0s1()
.sense().disabled()
});
// 2. 配置SPI外设
p.SPI0.enable.write(|w| w.enable().disabled());
// 配置SPI模式0 (CPOL=0, CPHA=0)
p.SPI0.config.write(|w| {
w.order().msb_first() // 高位在前
.cpha().leading() // 模式0
.cpol().active_high() // 模式0
});
// 设置频率为1MHz
p.SPI0.frequency.write(|w| w.frequency().m1());
// 配置引脚选择
p.SPI0.psel.mosi.write(|w| unsafe { w.bits(20) });
p.SPI0.psel.miso.write(|w| unsafe { w.bits(21) });
p.SPI0.psel.sck.write(|w| unsafe { w.bits(19) });
// 启用SPI
p.SPI0.enable.write(|w| w.enable().enabled());
// 3. SPI数据传输示例
let tx_data: [u8; 4] = [0xAA, 0xBB, 0xCC, 0xDD];
let mut rx_data: [u8; 4] = [0; 4];
for i in 0..tx_data.len() {
// 写入要发送的数据
p.SPI0.txd.write(|w| unsafe { w.bits(u32::from(tx_data[i])) });
// 等待接收完成
while p.SPI0.events_ready.read().bits() == 0 {}
p.SPI0.events_ready.write(|w| unsafe { w.bits(0) });
// 读取接收到的数据
rx_data[i] = p.SPI0.rxd.read().bits() as u8;
}
loop {}
}
nrf52840-pac提供了对nRF52840芯片所有外设的低级访问,包括GPIO、UART、SPI、I2C、PWM、ADC等。通过这个库,开发者可以安全高效地开发nRF52840嵌入式应用。
1 回复
nrf52840-pac - Nordic nRF52840芯片的Rust外设访问控制库
nrf52840-pac
是一个Rust嵌入式开发库,专门为Nordic nRF52840系列芯片提供外设访问控制(Peripheral Access Crate)和硬件抽象功能。
功能概述
- 提供对nRF52840所有外设寄存器的安全访问
- 自动生成的寄存器定义,与芯片手册完全对应
- 类型安全的寄存器操作接口
- 支持中断处理
- 与RTIC框架兼容
使用方法
添加依赖
在Cargo.toml
中添加:
[dependencies]
nrf52840-pac = "0.10.0"
cortex-m = "0.7.6"
cortex-m-rt = "0.7.1"
基本外设访问示例
use nrf52840_pac as pac;
use cortex_m::peripheral::syst;
fn main() -> ! {
// 获取外设访问权
let p = pac::Peripherals::take().unwrap();
let cp = cortex_m::Peripherals::take().unwrap();
// 配置GPIO
let gpio = p.P0;
gpio.pin_cnf[13].write(|w| w.dir().output());
// 配置系统定时器
let mut syst = cp.SYST;
syst.set_clock_source(syst::SystClkSource::Core);
syst.set_reload(16_000_000); // 1秒间隔
syst.enable_counter();
syst.enable_interrupt();
loop {
// 翻转LED引脚
gpio.out.write(|w| unsafe { w.bits(1 << 13) });
cortex_m::asm::delay(16_000_000);
gpio.out.write(|w| unsafe { w.bits(0) });
cortex_m::asm::delay(16_000_000);
}
}
中断处理示例
use cortex_m_rt::entry;
use nrf52840_pac::{interrupt, Interrupt};
#[entry]
fn main() -> ! {
let p = pac::Peripherals::take().unwrap();
// 配置TIMER0
p.TIMER0.start.write(|w| unsafe { w.bits(1_000_000) });
p.TIMER0.intenset.write(|w| w.compare0().set());
// 启用中断
unsafe {
cortex_m::peripheral::NVIC::unmask(Interrupt::TIMER0);
}
loop {}
}
#[interrupt]
fn TIMER0() {
let p = unsafe { pac::Peripherals::steal() };
// 清除中断标志
p.TIMER0.events_compare[0].write(|w| w.bits(0));
// 处理定时器中断
}
高级功能 - 使用RTIC框架
#![no_main]
#![no_std]
use rtic::app;
use nrf52840_pac as pac;
#[app(device = pac)]
const APP: () = {
#[init]
fn init(cx: init::Context) {
// 初始化外设
let timer0 = cx.device.TIMER0;
timer0.prescaler.write(|w| unsafe { w.bits(4) });
}
#[task(binds = TIMER0, priority = 1)]
fn timer_handler(cx: timer_handler::Context) {
// 定时器中断处理
}
};
完整示例代码
以下是一个完整的LED闪烁示例,结合GPIO控制和定时器中断:
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use nrf52840_pac::{interrupt, Peripherals};
use panic_halt as _;
// LED连接的引脚号
const LED_PIN: usize = 13;
#[entry]
fn main() -> ! {
let p = Peripherals::take().unwrap();
// 1. 配置GPIO
// 设置LED引脚为输出
p.P0.pin_cnf[LED_PIN].write(|w| {
w.dir().output() // 设置为输出模式
.drive().s0s1() // 标准驱动强度
.input().disconnect() // 禁止输入
.pull().disabled() // 禁止上下拉
.sense().disabled() // 禁止感应
});
// 2. 配置TIMER0
// 设置预分频器 (16MHz/16 = 1MHz)
p.TIMER0.prescaler.write(|w| unsafe { w.bits(4) });
// 设置比较值0 (500ms)
p.TIMER0.cc[0].write(|w| unsafe { w.bits(500_000) });
// 启用比较0事件
p.TIMER0.intenset.write(|w| w.compare0().set());
// 启动定时器
p.TIMER0.tasks_start.write(|w| unsafe { w.bits(1) });
// 3. 启用中断
unsafe {
cortex_m::peripheral::NVIC::unmask(interrupt::TIMER0);
}
loop {
cortex_m::asm::wfi(); // 进入低功耗模式,等待中断
}
}
#[interrupt]
fn TIMER0() {
let p = unsafe { Peripherals::steal() };
// 清除中断标志
p.TIMER0.events_compare[0].write(|w| w.bits(0));
// 切换LED状态
let current_state = p.P0.out.read().bits();
p.P0.out.write(|w| unsafe { w.bits(current_state ^ (1 << LED_PIN)) });
}
注意事项
- 寄存器访问默认是安全的,但使用
unsafe
可以绕过编译器检查 - 大多数外设需要先启用时钟才能正常工作
- 中断处理函数需要正确清除中断标志
- 在多任务环境中使用时需要适当的同步机制