libs/kaptan-stm32f7: add clock GPIO and timer drivers
This commit is contained in:
1 parent
6d63cbcbd9
commit
1c7fccc4a0
3 files changed
+209
No files matched your search
@@ -0,0 +1,54 @@
|
||||
//! Deterministic STM32F722 clock-source selection.
|
||||
|
||||
use stm32f7::stm32f722;
|
||||
|
||||
const CLOCK_SWITCH_POLL_LIMIT: u16 = 1_024;
|
||||
|
||||
/// Failure to establish the board's HSI16 clock profile.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum ClockProfileError {
|
||||
/// HSI did not become ready within the bounded polling budget.
|
||||
HsiNotReady,
|
||||
/// The system clock did not switch to HSI within the bounded polling budget.
|
||||
SystemClockNotHsi,
|
||||
}
|
||||
|
||||
/// Selects 16 MHz HSI as SYSCLK with undivided AHB, APB1, and APB2 clocks.
|
||||
///
|
||||
/// This profile is deliberately selected even when an earlier boot stage left
|
||||
/// the device running from a PLL. It establishes the clock assumptions used by
|
||||
/// DWT timestamps, USART1 baud calculation, and TIM2 heartbeat scheduling.
|
||||
pub fn select_hsi16_profile(rcc: &stm32f722::RCC) -> Result<(), ClockProfileError> {
|
||||
rcc.cr().modify(|_, write| write.hsion().on());
|
||||
if !wait_for_hsi(rcc) {
|
||||
return Err(ClockProfileError::HsiNotReady);
|
||||
}
|
||||
|
||||
rcc.cfgr()
|
||||
.modify(|_, write| write.sw().hsi().hpre().div1().ppre1().div1().ppre2().div1());
|
||||
if !wait_for_system_clock_hsi(rcc) {
|
||||
return Err(ClockProfileError::SystemClockNotHsi);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn wait_for_hsi(rcc: &stm32f722::RCC) -> bool {
|
||||
for _ in 0..CLOCK_SWITCH_POLL_LIMIT {
|
||||
if rcc.cr().read().hsirdy().is_ready() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn wait_for_system_clock_hsi(rcc: &stm32f722::RCC) -> bool {
|
||||
for _ in 0..CLOCK_SWITCH_POLL_LIMIT {
|
||||
if rcc.cfgr().read().sws().is_hsi() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
//! Named STM32F722 GPIO routes used by supported board wiring.
|
||||
|
||||
use stm32f7::stm32f722;
|
||||
|
||||
/// Configures PA5, PA6, and PA7 for SPI1 alternate function 5.
|
||||
pub fn configure_spi1_pins_pa5_pa6_pa7(rcc: &stm32f722::RCC, port: &stm32f722::GPIOA) {
|
||||
rcc.ahb1enr().modify(|_, write| write.gpioaen().set_bit());
|
||||
port.moder().modify(|_, write| {
|
||||
write
|
||||
.moder5()
|
||||
.alternate()
|
||||
.moder6()
|
||||
.alternate()
|
||||
.moder7()
|
||||
.alternate()
|
||||
});
|
||||
port.afrl().modify(|_, write| {
|
||||
// SAFETY: AF5 selects SPI1 on PA5, PA6, and PA7 on STM32F722.
|
||||
unsafe {
|
||||
write
|
||||
.afrl5()
|
||||
.bits(0b0101)
|
||||
.afrl6()
|
||||
.bits(0b0101)
|
||||
.afrl7()
|
||||
.bits(0b0101)
|
||||
}
|
||||
});
|
||||
port.otyper()
|
||||
.modify(|_, write| write.ot5().push_pull().ot6().push_pull().ot7().push_pull());
|
||||
port.ospeedr().modify(|_, write| {
|
||||
write
|
||||
.ospeedr5()
|
||||
.high_speed()
|
||||
.ospeedr6()
|
||||
.high_speed()
|
||||
.ospeedr7()
|
||||
.high_speed()
|
||||
});
|
||||
port.pupdr().modify(|_, write| {
|
||||
write
|
||||
.pupdr5()
|
||||
.floating()
|
||||
.pupdr6()
|
||||
.floating()
|
||||
.pupdr7()
|
||||
.floating()
|
||||
});
|
||||
}
|
||||
|
||||
/// Configures PA4 as the input pin for an EXTI4 route.
|
||||
///
|
||||
/// A board must select this explicitly rather than inheriting the pin mode
|
||||
/// left by a bootloader or an earlier firmware image.
|
||||
pub fn configure_exti4_pin_pa4(port: &stm32f722::GPIOA) {
|
||||
port.moder().modify(|_, write| write.moder4().input());
|
||||
port.pupdr().modify(|_, write| write.pupdr4().floating());
|
||||
}
|
||||
|
||||
/// Configures PA9 for USART1 transmit alternate function 7.
|
||||
pub fn configure_usart1_tx_pin_pa9(port: &stm32f722::GPIOA) {
|
||||
port.moder().modify(|_, write| write.moder9().alternate());
|
||||
port.afrh().modify(|_, write| {
|
||||
// SAFETY: AF7 selects USART1 TX on PA9 on STM32F722.
|
||||
unsafe { write.afrh9().bits(0b0111) }
|
||||
});
|
||||
port.otyper().modify(|_, write| write.ot9().push_pull());
|
||||
port.ospeedr()
|
||||
.modify(|_, write| write.ospeedr9().high_speed());
|
||||
port.pupdr().modify(|_, write| write.pupdr9().floating());
|
||||
}
|
||||
|
||||
/// Active-low status LED connected to PD2.
|
||||
pub struct ActiveLowLedPd2 {
|
||||
port: stm32f722::GPIOD,
|
||||
is_on: bool,
|
||||
}
|
||||
|
||||
impl ActiveLowLedPd2 {
|
||||
/// Configures PD2 as an initially-off active-low output.
|
||||
pub fn new(rcc: &stm32f722::RCC, port: stm32f722::GPIOD) -> Self {
|
||||
rcc.ahb1enr().modify(|_, write| write.gpioden().set_bit());
|
||||
port.moder().modify(|_, write| write.moder2().output());
|
||||
port.otyper().modify(|_, write| write.ot2().push_pull());
|
||||
port.ospeedr()
|
||||
.modify(|_, write| write.ospeedr2().low_speed());
|
||||
port.pupdr().modify(|_, write| write.pupdr2().floating());
|
||||
port.bsrr().write(|write| write.bs2().set_bit());
|
||||
|
||||
Self { port, is_on: false }
|
||||
}
|
||||
|
||||
/// Toggles the LED state.
|
||||
pub fn toggle(&mut self) {
|
||||
self.is_on = !self.is_on;
|
||||
if self.is_on {
|
||||
self.port.bsrr().write(|write| write.br2().set_bit());
|
||||
} else {
|
||||
self.port.bsrr().write(|write| write.bs2().set_bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//! STM32F722 TIM2 periodic interrupt configuration.
|
||||
|
||||
use stm32f7::stm32f722;
|
||||
|
||||
/// Precomputed TIM2 periodic-update configuration.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct Tim2PeriodicConfig {
|
||||
/// Timer prescaler register value.
|
||||
pub prescaler: u16,
|
||||
/// Timer auto-reload register value.
|
||||
pub auto_reload: u32,
|
||||
}
|
||||
|
||||
/// TIM2 configured to issue periodic update interrupts.
|
||||
pub struct Tim2Periodic {
|
||||
peripheral: stm32f722::TIM2,
|
||||
}
|
||||
|
||||
impl Tim2Periodic {
|
||||
/// Enables TIM2 with the supplied board-derived period configuration.
|
||||
pub fn new(
|
||||
rcc: &stm32f722::RCC,
|
||||
peripheral: stm32f722::TIM2,
|
||||
config: Tim2PeriodicConfig,
|
||||
) -> Self {
|
||||
rcc.apb1rstr().modify(|_, write| write.tim2rst().set_bit());
|
||||
rcc.apb1rstr()
|
||||
.modify(|_, write| write.tim2rst().clear_bit());
|
||||
rcc.apb1enr().modify(|_, write| write.tim2en().set_bit());
|
||||
peripheral.cr1().modify(|_, write| write.cen().clear_bit());
|
||||
peripheral.psc().write(|write| {
|
||||
// SAFETY: `prescaler` is exactly the 16-bit PSC register width.
|
||||
unsafe { write.psc().bits(config.prescaler) }
|
||||
});
|
||||
peripheral.arr().write(|write| {
|
||||
// SAFETY: TIM2 ARR is a 32-bit register and accepts `auto_reload`.
|
||||
unsafe { write.arr().bits(config.auto_reload) }
|
||||
});
|
||||
peripheral.egr().write(|write| write.ug().set_bit());
|
||||
peripheral.sr().modify(|_, write| write.uif().clear_bit());
|
||||
peripheral.dier().modify(|_, write| write.uie().set_bit());
|
||||
peripheral.cr1().modify(|_, write| write.cen().set_bit());
|
||||
|
||||
Self { peripheral }
|
||||
}
|
||||
|
||||
/// Acknowledges a TIM2 update interrupt.
|
||||
pub fn acknowledge_update(&mut self) {
|
||||
self.peripheral
|
||||
.sr()
|
||||
.modify(|_, write| write.uif().clear_bit());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user