在嵌入式编程中,可以通过放置在正确地址的结构访问存储器映射的外设。如何确保在Rust中的总线上确实执行了对外设的存储器访问?在C中,这可以通过创建变量或字段volatile
来完成。
考虑这个例子:
#[repr(C)]
struct Periph {
sr: u32, /* Status */
cr: u32, /* Control */
dr: u32, /* Data */
}
const PERIPH1: *mut Periph = 0x10001200 as *mut Periph;
const PERIPH2: *mut Periph = 0x10001400 as *mut Periph;
fn useperiph(p: &mut Periph) -> i32 {
p.cr = CR_CONSTANT;
if p.cr != CR_CONSTANT {
/* Peripheral was not enabled */
return -1;
}
/* Loop a few micro seconds until ready */
while p.sr != SR_CONSTANT {}
/* Write values to hardware FIFO */
p.dr = DATA1;
p.dr = DATA2;
p.dr = DATA3;
0
}
我需要确保编译器不会删除对控制寄存器的检查,将保留状态寄存器的所有负载,并且不会将三个存储器折叠到数据寄存器。我该怎么做?
答案 0 :(得分:0)
我所知道的最好的方法是使用类似于Zinc的volatile_cell库的东西。内存映射外设寄存器的结构具有VolatileCell<u32>
类型的寄存器,而不是普通的u32
。 VolatileCell
包含一个值,具有相同的大小,并提供使用core::ptr::read_volatile
和core::ptr::write_volatile
的访问者。简而言之,VolatileCell
看起来像这样:
#[repr(C)]
pub struct VolatileCell<T> {
value: T,
}
impl<T> VolatileCell<T> {
/// Get register value.
#[inline]
pub fn get(&self) -> T {
unsafe {
read_volatile(&self.value)
}
}
/// Set register value.
#[inline]
pub fn set(&self, value: T) {
unsafe {
write_volatile(&self.value as *const T as *mut T, value)
}
}
}
然后使用VolatileCell<u32>
字段写入外设寄存器结构:
#[repr(C)]
struct Periph {
sr: VolatileCell<u32>, /* Status */
cr: VolatileCell<u32>, /* Control */
dr: VolatileCell<u32>, /* Data */
}
const PERIPH1: *mut Periph = 0x10001200 as *mut Periph;
const PERIPH2: *mut Periph = 0x10001400 as *mut Periph;
fn useperiph(p: &mut Periph) -> i32 {
p.cr.set(CR_CONSTANT);
if p.cr.get() != CR_CONSTANT {
/* Peripheral was not enabled */
return -1;
}
/* Loop a few micro seconds until ready */
while p.sr.get() != SR_CONSTANT {}
p.dr.set(DATA1);
p.dr.set(DATA2);
p.dr.set(DATA3);
0
}
生成的代码具有正确的语义,并且比直接使用u32
稍微冗长一些。显而易见可以说是一个好处。