platforms/stm32f7: add reusable F722 firmware
This commit is contained in:
1 parent
2ae66c49a8
commit
ac54c8308d
32 files changed
+4006
-248
No files matched your search
@@ -0,0 +1,285 @@
|
||||
//! STM32F722 USART routes used by supported boards.
|
||||
|
||||
use stm32f7::stm32f722;
|
||||
|
||||
/// UART baud-rate configuration error.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum BaudRateError {
|
||||
/// Zero is not a valid baud rate.
|
||||
Zero,
|
||||
/// The requested divisor cannot fit USART BRR.
|
||||
OutOfRange,
|
||||
}
|
||||
|
||||
/// Terminal result from acknowledging a USART receive interrupt.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum ReceiveInterrupt {
|
||||
/// One received byte was removed from the UART data register.
|
||||
Byte(u8),
|
||||
/// A serial transport fault invalidated the current receive stream.
|
||||
Fault,
|
||||
/// This USART interrupt did not have a receive event pending.
|
||||
NotPending,
|
||||
}
|
||||
|
||||
/// Owns configured full-duplex USART1 hardware.
|
||||
///
|
||||
/// The family platform owns schematic-selected pin setup. Configure pins
|
||||
/// before constructing this type.
|
||||
pub struct Usart1Tx {
|
||||
peripheral: stm32f722::USART1,
|
||||
}
|
||||
|
||||
/// Owns configured full-duplex USART2 hardware.
|
||||
///
|
||||
/// The family platform owns schematic-selected pin setup. Configure pins
|
||||
/// before constructing this type.
|
||||
pub struct Usart2Tx {
|
||||
peripheral: stm32f722::USART2,
|
||||
}
|
||||
|
||||
impl Usart2Tx {
|
||||
/// Enables USART2 and configures 8N1 transmit and receive operation.
|
||||
pub fn new(
|
||||
rcc: &stm32f722::RCC,
|
||||
peripheral: stm32f722::USART2,
|
||||
peripheral_clock_hz: u32,
|
||||
baud: u32,
|
||||
) -> Result<Self, BaudRateError> {
|
||||
let brr = calculate_brr(peripheral_clock_hz, baud)?;
|
||||
|
||||
// Do not inherit a bootloader's USART2 kernel-clock selection. The
|
||||
// F722 flight clock profile supplies `peripheral_clock_hz` from PCLK1,
|
||||
// so select PCLK1 before programming BRR.
|
||||
rcc.dckcfgr2().modify(|_, write| write.usart2sel().apb1());
|
||||
rcc.apb1rstr()
|
||||
.modify(|_, write| write.usart2rst().set_bit());
|
||||
rcc.apb1rstr()
|
||||
.modify(|_, write| write.usart2rst().clear_bit());
|
||||
rcc.apb1enr().modify(|_, write| write.usart2en().set_bit());
|
||||
peripheral.brr().write(|write| {
|
||||
// SAFETY: `brr` is validated to fit the complete BRR register.
|
||||
unsafe { write.bits(u32::from(brr)) }
|
||||
});
|
||||
peripheral.cr1().write(|write| {
|
||||
write
|
||||
.te()
|
||||
.set_bit()
|
||||
.re()
|
||||
.set_bit()
|
||||
.rxneie()
|
||||
.set_bit()
|
||||
.ue()
|
||||
.set_bit()
|
||||
});
|
||||
peripheral.cr2().write(|write| write.stop().stop1());
|
||||
|
||||
Ok(Self { peripheral })
|
||||
}
|
||||
|
||||
pub(crate) fn enable_dma_transmit(&self) {
|
||||
self.peripheral.cr3().write(|write| write.dmat().set_bit());
|
||||
}
|
||||
|
||||
pub(crate) fn transmit_data_register_address(&self) -> u32 {
|
||||
self.peripheral.tdr().as_ptr() as u32
|
||||
}
|
||||
|
||||
/// Blocks until USART2 accepts one byte for transmission.
|
||||
///
|
||||
/// This is used by the platform's UART wire diagnostic. Flight data-channel traffic
|
||||
/// uses [`Usart2TxDma`](super::Usart2TxDma) instead.
|
||||
#[cfg(target_arch = "arm")]
|
||||
pub fn write_blocking(&self, byte: u8) {
|
||||
while !self.peripheral.isr().read().txe().is_not_full() {}
|
||||
self.peripheral.tdr().write(|write| {
|
||||
// SAFETY: `byte` is eight bits and therefore fits the USART's
|
||||
// documented nine-bit TDR data field.
|
||||
unsafe { write.tdr().bits(u16::from(byte)) }
|
||||
});
|
||||
}
|
||||
|
||||
/// Acknowledges one USART2 receive interrupt without accessing TX state.
|
||||
pub fn acknowledge_receive_interrupt() -> ReceiveInterrupt {
|
||||
// SAFETY: the platform binds USART2 exactly once and this function touches
|
||||
// only its receive/error registers, never the TX DMA configuration.
|
||||
let peripheral = unsafe { &*stm32f722::USART2::ptr() };
|
||||
acknowledge_receive_interrupt(peripheral)
|
||||
}
|
||||
}
|
||||
|
||||
impl Usart1Tx {
|
||||
/// Enables USART1 and configures 8N1 transmit and receive operation.
|
||||
pub fn new(
|
||||
rcc: &stm32f722::RCC,
|
||||
peripheral: stm32f722::USART1,
|
||||
peripheral_clock_hz: u32,
|
||||
baud: u32,
|
||||
) -> Result<Self, BaudRateError> {
|
||||
let brr = calculate_brr(peripheral_clock_hz, baud)?;
|
||||
|
||||
rcc.apb2rstr()
|
||||
.modify(|_, write| write.usart1rst().set_bit());
|
||||
rcc.apb2rstr()
|
||||
.modify(|_, write| write.usart1rst().clear_bit());
|
||||
rcc.apb2enr().modify(|_, write| write.usart1en().set_bit());
|
||||
peripheral.brr().write(|write| {
|
||||
// SAFETY: `brr` is validated to fit the complete BRR register.
|
||||
unsafe { write.bits(u32::from(brr)) }
|
||||
});
|
||||
peripheral.cr1().write(|write| {
|
||||
write
|
||||
.te()
|
||||
.set_bit()
|
||||
.re()
|
||||
.set_bit()
|
||||
.rxneie()
|
||||
.set_bit()
|
||||
.ue()
|
||||
.set_bit()
|
||||
});
|
||||
peripheral.cr2().write(|write| write.stop().stop1());
|
||||
|
||||
Ok(Self { peripheral })
|
||||
}
|
||||
|
||||
pub(crate) fn enable_dma_transmit(&self) {
|
||||
self.peripheral.cr3().write(|write| write.dmat().set_bit());
|
||||
}
|
||||
|
||||
pub(crate) fn transmit_data_register_address(&self) -> u32 {
|
||||
self.peripheral.tdr().as_ptr() as u32
|
||||
}
|
||||
|
||||
/// Acknowledges one USART1 receive interrupt without accessing TX state.
|
||||
///
|
||||
/// The platform invokes this only from the `USART1` interrupt. USART1 TX DMA
|
||||
/// owns its disjoint data register and DMA stream, so this static route may
|
||||
/// safely acknowledge RX while that transfer is active.
|
||||
pub fn acknowledge_receive_interrupt() -> ReceiveInterrupt {
|
||||
// SAFETY: the platform binds USART1 exactly once and this function touches
|
||||
// only its receive/error registers, never the TX DMA configuration.
|
||||
let peripheral = unsafe { &*stm32f722::USART1::ptr() };
|
||||
acknowledge_receive_interrupt(peripheral)
|
||||
}
|
||||
}
|
||||
|
||||
/// Owns configured UART4 hardware.
|
||||
///
|
||||
/// This route is intended for peripherals that continuously stream data, such
|
||||
/// as the MTF-01. It does not enable DMA state.
|
||||
pub struct Uart4 {
|
||||
_peripheral: stm32f722::UART4,
|
||||
}
|
||||
|
||||
impl Uart4 {
|
||||
/// Enables UART4 and configures 8N1 transmit and receive operation.
|
||||
pub fn new(
|
||||
rcc: &stm32f722::RCC,
|
||||
peripheral: stm32f722::UART4,
|
||||
peripheral_clock_hz: u32,
|
||||
baud: u32,
|
||||
) -> Result<Self, BaudRateError> {
|
||||
let brr = calculate_brr(peripheral_clock_hz, baud)?;
|
||||
|
||||
rcc.apb1rstr().modify(|_, write| write.uart4rst().set_bit());
|
||||
rcc.apb1rstr()
|
||||
.modify(|_, write| write.uart4rst().clear_bit());
|
||||
rcc.apb1enr().modify(|_, write| write.uart4en().set_bit());
|
||||
peripheral.brr().write(|write| {
|
||||
// SAFETY: `brr` is validated to fit the complete BRR register.
|
||||
unsafe { write.bits(u32::from(brr)) }
|
||||
});
|
||||
peripheral.cr1().write(|write| {
|
||||
write
|
||||
.te()
|
||||
.set_bit()
|
||||
.re()
|
||||
.set_bit()
|
||||
.rxneie()
|
||||
.set_bit()
|
||||
.ue()
|
||||
.set_bit()
|
||||
});
|
||||
peripheral.cr2().write(|write| write.stop().stop1());
|
||||
|
||||
Ok(Self {
|
||||
_peripheral: peripheral,
|
||||
})
|
||||
}
|
||||
|
||||
/// Acknowledges one UART4 receive interrupt.
|
||||
pub fn acknowledge_receive_interrupt() -> ReceiveInterrupt {
|
||||
// SAFETY: the platform binds UART4 exactly once and this function
|
||||
// accesses only its receive and error registers.
|
||||
let peripheral = unsafe { &*stm32f722::UART4::ptr() };
|
||||
acknowledge_receive_interrupt(peripheral)
|
||||
}
|
||||
}
|
||||
|
||||
fn acknowledge_receive_interrupt(
|
||||
peripheral: &stm32f722::usart1::RegisterBlock,
|
||||
) -> ReceiveInterrupt {
|
||||
let status = peripheral.isr().read();
|
||||
let fault = status.pe().is_error()
|
||||
|| status.fe().is_error()
|
||||
|| status.nf().is_noise()
|
||||
|| status.ore().is_overrun();
|
||||
|
||||
if status.rxne().is_data_ready() {
|
||||
let byte = peripheral.rdr().read().rdr().bits() as u8;
|
||||
if fault {
|
||||
clear_receive_errors(peripheral);
|
||||
ReceiveInterrupt::Fault
|
||||
} else {
|
||||
ReceiveInterrupt::Byte(byte)
|
||||
}
|
||||
} else if fault {
|
||||
clear_receive_errors(peripheral);
|
||||
ReceiveInterrupt::Fault
|
||||
} else {
|
||||
ReceiveInterrupt::NotPending
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_receive_errors(peripheral: &stm32f722::usart1::RegisterBlock) {
|
||||
peripheral.icr().write(|write| {
|
||||
write
|
||||
.pecf()
|
||||
.clear()
|
||||
.fecf()
|
||||
.clear()
|
||||
.ncf()
|
||||
.clear()
|
||||
.orecf()
|
||||
.clear()
|
||||
});
|
||||
}
|
||||
|
||||
const fn calculate_brr(peripheral_clock_hz: u32, baud: u32) -> Result<u16, BaudRateError> {
|
||||
if baud == 0 {
|
||||
return Err(BaudRateError::Zero);
|
||||
}
|
||||
|
||||
let divisor = (peripheral_clock_hz + (baud / 2)) / baud;
|
||||
if divisor == 0 || divisor > u16::MAX as u32 {
|
||||
Err(BaudRateError::OutOfRange)
|
||||
} else {
|
||||
Ok(divisor as u16)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{BaudRateError, calculate_brr};
|
||||
|
||||
#[test]
|
||||
fn calculates_reset_clock_brr() {
|
||||
assert_eq!(calculate_brr(16_000_000, 115_200), Ok(139));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_zero_baud() {
|
||||
assert_eq!(calculate_brr(16_000_000, 0), Err(BaudRateError::Zero));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user