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