Look at some of the cases in the article, such as:
let mut dp = pac::Peripherals::take().unwrap();
dp.RCC.ahb2enr.modify(|_, w| w.gpioaen().set_bit());
In C that would be: RCC->AHBENR2 |= RCC_AHBENR2_GPIOAEN;
Okay, you only have to take ownership of the Peripherals object once, but what about when they want to set a UART register in a function? Rust wants mutable pointers to have a trail of ownership outside of 'unsafe' blocks, so now we need: pub fn setup_transmission(regs: &mut R, [...])
where R: Deref<Target = pac::usart1::RegisterBlock> {
[...]
regs.cr2.modify(|_, w| w.stop().bits(config.stop_bits as u8));
[...]
}
This is a bit of a construction, in C(++) we can just do something like: void setup_transmission([...]) {
[...]
USART1->CR2 &= ~(USART_CR2_STOP_BITS);
USART1->CR2 |= ((uint8_t)config.stop_bits << USART_CR2_STOP_BITS_Pos);
[...]
}
The Rust snippet seems like it would be safer in a multi-threaded environment like an RTOS, or in cases where interrupts might modify peripheral registers, but...there are so many syntactical details to remember.I really tried to like embedded Rust, and I still think it compares favorably with the sort of C HALs that most chip manufacturers distribute. But the syntax seems to get in the way of quick simple access to peripheral registers.