libs/kaptan-stm32f7: add clock GPIO and timer drivers

This commit is contained in:
Onur Özkan committed 2026-07-19 22:40:04 +03:00
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
}
+102
View File
@@ -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());
}
}
}
+53
View File
@@ -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());
}
}