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)) });
}

注意事项

  1. 寄存器访问默认是安全的,但使用unsafe可以绕过编译器检查
  2. 大多数外设需要先启用时钟才能正常工作
  3. 中断处理函数需要正确清除中断标志
  4. 在多任务环境中使用时需要适当的同步机制
回到顶部