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)外设
  • 等等

最佳实践

  1. 安全访问:始终使用write()modify()方法来访问寄存器,而不是直接写入
  2. 中断处理:在中断处理程序中避免长时间操作
  3. 电源管理:合理配置低功耗模式以节省能源
  4. 错误处理:对外设操作进行适当的错误检查
回到顶部