Как написать адрес памяти в Rust?
Я пытаюсь сделать "Blinky" для STM32F1xx в Rust.
Я знаю, что для этого существуют библиотеки, но я хочу создать свою собственную "lib" для учебных целей.
Я могу получить доступ к регистрам STM32 по их адресам, подобным этому в C:
*(uint32_t*)(0x40021000 + 0x018) |= 0x10;
*(uint32_t*)(0x40011000 + 0x004) |= 0x33;
*(uint32_t*)(0x40011000 + 0x004) &= ~0xCC;
*(uint32_t*)(0x40011000 + 0x10) |= 0x300;
while(1) {}
Это записывает некоторые биты в регистр RCC_APB2ENR
, чтобы включить синхронизацию порта C, настраивает контакты и включает светодиоды в моем обнаружении.
Мне нужно переписать это в Rust, создать consts, fns и начать писать хороший Rusty-код. Возможно ли это в Rust без FFI, вызывающего код C? Могу ли я достичь этого с помощью макроса asm!
?
Ответы
Ответ 1
В C вы должны объявлять свои указатели как volatile
при доступе к аппаратным регистрам, чтобы компилятор выполнял обращения точно так же, как вы их программируете. В противном случае он может переупорядочить их или исключить дублирование доступа к одному регистру.
Поскольку Rust 1.9 (благодаря этому RFC), вы можете использовать core::ptr::read_volatile
и core::ptr::write_volatile
для чтения и записи на такие память.
Если у вас более старая версия Rust, они доступны как volatile_read
и volatile_store
в core:: intrinsics, которые однако постоянно неустойчивы и, следовательно, требуют ночной версии Rust для доступа к ним.
Ответ 2
Функции read_volatile
и write_volatile
являются стабильными с версии 1.9, поэтому вы должны использовать их. Заимствование @ker переводило образец для демонстрации:
use std::ptr::{read_volatile, write_volatile};
const A: *mut u32 = (0x40021000 + 0x018) as *mut u32;
const B: *mut u32 = (0x40011000 + 0x004) as *mut u32;
const C: *mut u32 = (0x40011000 + 0x10) as *mut u32;
unsafe {
write_volatile(A, read_volatile(A) | 0x10);
write_volatile(B, read_volatile(B) | 0x33);
write_volatile(B, read_volatile(B) & !0xCC);
write_volatile(C, read_volatile(C) | 0x300);
}
Кроме того, volatile
crate предоставляет типы-оболочки вокруг значений для нестабильного доступа.
use volatile::Volatile;
const A: *mut u32 = (0x40021000 + 0x018) as *mut u32;
const B: *mut u32 = (0x40011000 + 0x004) as *mut u32;
const C: *mut u32 = (0x40011000 + 0x10) as *mut u32;
const volatile_A = A as *mut Volatile<u32>;
const volatile_B = B as *mut Volatile<u32>;
const volatile_C = C as *mut Volatile<u32>;
unsafe {
(*volatile_A).update(|x| *x | 0x10);
(*volatile_B).update(|x| *x & !0xCC);
(*volatile_C).update(|x| *x | 0x300);
}
Ответ 3
rust имеет std::ptr
модуль в стандартной библиотеке. Он предлагает такие функции, как ptr::read
и ptr::write
, которые гораздо более явные, чем разыменование.
Итак, ваш пример будет
const A: *mut u32 = (0x40021000 + 0x018) as *mut u32;
const B: *mut u32 = (0x40011000 + 0x004) as *mut u32;
const C: *mut u32 = (0x40011000 + 0x10) as *mut u32;
unsafe {
ptr::write(A, ptr::read(A) | 0x10);
ptr::write(B, ptr::read(B) | 0x33);
ptr::write(B, ptr::read(B) & !0xCC);
ptr::write(C, ptr::read(C) | 0x300);
}
Более сжатой версией является использование разыменования, но это работает только для типов Copy
:
*A |= 0x10;
*B |= 0x33;
*B &= !0xCC;
*C |= 0x300;