9个版本
| 新增 0.9.9 | 2024年8月26日 | 
|---|---|
| 0.9.8 | 2024年8月21日 | 
| 0.9.6 | 2023年12月13日 | 
| 0.9.3 | 2023年11月7日 | 
#7 in #registers
141 每月下载次数
在 embedded-registers 中使用
18KB
228 代码行数(不包括注释)
该软件包提供了一种过程宏,用于在嵌入式设备驱动程序中轻松定义寄存器。
目前,embedded-registers需要您使用 #![feature(generic_arg_infer)]。
属性宏
通过在bondrewd位域的定义中添加 #[register(...)] 来定义寄存器。作为一个简短的提醒,bondrewd 是另一个过程宏,允许您定义位域结构,这在处理寄存器时非常有用,因为多个值通常紧密地按位排列。
寄存器属性宏支持以下参数
| address | 与寄存器关联的虚拟地址。 | 
| read | 如果寄存器应该是可读的,则添加此参数 | 
| write | 如果寄存器应该是可写的,则添加此参数 | 
将此属性添加到bondrewd结构 Foo 将导致定义两个类型
- Foo将成为寄存器,本质上是一个具有正确大小的字节数组,为单个字段提供获取和设置函数。
- FooBitfield将成为底层的bondrewd位域,可以用来从头开始构建寄存器,或者可以通过- Foo::read_all获取,如果您想解包所有值。
这的好处是读取寄存器不会产生额外的内存和CPU成本来解包位域的所有值。您只为实际访问的成员付费。
简单示例
这个简单的例子定义了MCP9808的DeviceId寄存器。它具有虚拟地址 0b111 (0x7),使用大端字节序,结构体成员的第一个字节位于最高位,大小为2字节,且为只读。
#![feature(generic_arg_infer)]
use embedded_registers::{register, i2c::{I2cDevice, codecs::OneByteRegAddrCodec}, RegisterInterface, ReadableRegister};
#[register(address = 0b111, mode = "r")]
#[bondrewd(read_from = "msb0", default_endianness = "be", enforce_bytes = 2)]
pub struct DeviceId {
    device_id: u8,
    revision: u8,
}
// The register may now be read from an I2C bus using sync or async operations:
// Create new I2cDevice with bus, address, codec
let mut dev = I2cDevice::new(i2c, 0x24, OneByteRegAddrCodec::default());
let reg = dev.read_register::<DeviceId>().await?;
// sync: let reg = DeviceId::read_i2c_blocking(&mut i2c, address);
复杂示例
更复杂的示例可能涉及添加自己的Bondrewd支持枚举。我们还确保用 #[default] 标注正确的字段,以便轻松重建上电默认值。请看MCP9808配置寄存器的这个摘录。
#![feature(generic_arg_infer)]
use embedded_registers::{register, i2c::{I2cDevice, codecs::OneByteRegAddrCodec}, RegisterInterface, ReadableRegister, WritableRegister};
use bondrewd::BitfieldEnum;
#[derive(BitfieldEnum, Clone, Default, PartialEq, Eq, Debug, Format)]
#[bondrewd_enum(u8)]
pub enum Hysteresis {
    #[default]
    Deg_0_0C = 0b00,
    Deg_1_5C = 0b01,
    Deg_3_0C = 0b10,
    Deg_6_0C = 0b11,
}
#[derive(BitfieldEnum, Clone, Default, PartialEq, Eq, Debug, Format)]
#[bondrewd_enum(u8)]
pub enum ShutdownMode {
    #[default]
    Continuous = 0,
    Shutdown = 1,
}
#[register(address = 0b001, mode = "rw")]
#[bondrewd(read_from = "msb0", default_endianness = "be", enforce_bytes = 2)]
pub struct Config {
    // padding
    #[bondrewd(bit_length = 5, reserve)]
    #[allow(dead_code)]
    reserved: u8,
    #[bondrewd(enum_primitive = "u8", bit_length = 2)]
    pub hysteresis: Hysteresis,
    #[bondrewd(enum_primitive = "u8", bit_length = 1)]
    pub shutdown_mode: ShutdownMode,
    // ... all 16 bits must be filled
    # #[bondrewd(bit_length = 8, reserve)]
    # #[allow(dead_code)]
    # reserved2: u8,
}
// This now allows us to read and write the register, while only
// unpacking the fields we require:
let mut reg = dev.read_register::<Config>().await?;
info!("previous shutdown mode: {}", reg.read_shutdown_mode());
reg.write_shutdown_mode(ShutdownMode::Shutdown);
dev.write_register(®).await?;
// If you want to get the full decoded bitfield, you can use either `read_all`
// or `.into()`. If you need to unpack all fields anyway, this might be
// more convenient as it allows you to access the bitfield members more ergonomically.
//let bf: ConfigBitfield = reg.into();
let mut bf = reg.read_all();
info!("previous shutdown mode: {}", bf.shutdown_mode);
bf.shutdown_mode = ShutdownMode::Shutdown;
reg.write_all(bf);
依赖项
~1–1.5MB
~32K SLoC