2016-01-26 11 views
10

Próbuję zrobić "Blinky" dla STM32F1xx w Rust. Wiem, że są na to libs, ale chcę zrobić własną "lib" dla celów edukacyjnych.Jak mogę napisać na adres mapowany w pamięci w Rust?

mogę uzyskać dostęp do „rejestrów” STM32 poprzez ich adresy, jak to w C:

*(uint32_t*)(0x40021000 + 0x018) |= 0x10; 
*(uint32_t*)(0x40011000 + 0x004) |= 0x33; 
*(uint32_t*)(0x40011000 + 0x004) &= ~0xCC; 
*(uint32_t*)(0x40011000 + 0x10) |= 0x300; 

while(1) {} 

To pisze kilka bitów w rejestrze RCC_APB2ENR umożliwić taktowanie portu C, konfiguruje szpilki i umożliwia diody na moim odkryciu.

Muszę to napisać ponownie w Rust, żeby stworzyć konstelacje, fns i zacząć pisać ładny Rusty code. Czy jest możliwe w Rust bez FFI wywoływania kodu C? Czy mogę to osiągnąć za pomocą makra asm!?

Odpowiedz

8

W C należy zadeklarować wskaźniki jak volatile kiedy dostęp do rejestrów sprzętowych, tak aby kompilator robił dostęp dokładnie tak, jak je programowałeś. W przeciwnym razie może zmienić kolejność lub wyeliminować duplikaty dostępu do tego samego rejestru.

Od rdzy 1.9 (dzięki temu RFC) można używać core::ptr::read_volatile i core::ptr::write_volatile do odczytywania i zapisywania w takiej pamięci.

Jeśli masz starszą wersję Rust, są dostępne jako volatile_read i volatile_store w core::intrinsics, które jednak są trwale niestabilny, a tym samym wymagają nocną wersję Rust do nich dostęp.

+0

Czy zachowanie się kompilatora dla wskaźników nieulotnych jest takie samo dla C i rdzy? – fevgenym

+0

Tak, kompilator Rust może wykonywać te same optymalizacje. Obsługa LLVM, która to robi, jest używana w obu językach. – starblue

1

rdza ma moduł std::ptr w standardowej bibliotece. Oferuje funkcje takie jak ptr::read i ptr::write, które są znacznie bardziej wyraźne niż dereferencje.

Więc Przykładem może być

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); 
} 

Wersja bardziej zwięzły jest użycie dereferencing, ale to działa tylko dla Copy typów:

*A |= 0x10; 
*B |= 0x33; 
*B &= !0xCC; 
*C |= 0x300; 
3

Funkcje read_volatile i write_volatile są stabilne od wersji 1.9, więc powinieneś ich używać. tłumaczone próbka ZACIĄGANIA @ ker dla demonstracji:

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); 
} 

Ponadto volatile paka zapewnia rodzajów otoki wokół wartości lotnych dostępu.

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); 
} 
+0

świetne wieści! Dzięki – fevgenym

Powiązane problemy