From db25706ae73bae1284a091e49ea90c022703e805 Mon Sep 17 00:00:00 2001 From: Kevin Thomas Date: Wed, 25 Mar 2026 18:10:00 -0400 Subject: [PATCH] refactor(drivers): add board.rs module, slim main.rs, fix docstrings across all 8 Rust drivers - Add board.rs to all 8 drivers: constants, type aliases, init functions, and HAL-specific helpers with full docstrings and pub(crate) visibility - Slim main.rs to boilerplate + main() only, zero helper functions - Fix i2c.rs: add file header, full docstrings on all functions - Fix lcd1602.rs: add file header, full docstrings on all functions - Fix lib.rs headers for 0x07 and 0x08 - All 8 drivers build and all 75 tests pass --- drivers/0x01_uart_rust/src/board.rs | 94 +++++++ drivers/0x01_uart_rust/src/main.rs | 65 +---- drivers/0x02_blink_rust/src/board.rs | 169 +++++++++++ drivers/0x02_blink_rust/src/main.rs | 136 +-------- drivers/0x03_button_rust/src/board.rs | 172 ++++++++++++ drivers/0x03_button_rust/src/main.rs | 139 +--------- drivers/0x04_pwm_rust/src/board.rs | 256 +++++++++++++++++ drivers/0x04_pwm_rust/src/main.rs | 234 +--------------- drivers/0x05_servo_rust/src/board.rs | 258 +++++++++++++++++ drivers/0x05_servo_rust/src/main.rs | 222 +-------------- drivers/0x06_adc_rust/src/board.rs | 215 ++++++++++++++ drivers/0x06_adc_rust/src/main.rs | 180 +----------- drivers/0x07_i2c_rust/src/board.rs | 188 +++++++++++++ drivers/0x07_i2c_rust/src/i2c.rs | 62 +++++ drivers/0x07_i2c_rust/src/lib.rs | 5 + drivers/0x07_i2c_rust/src/main.rs | 154 +--------- drivers/0x08_lcd1602_rust/src/board.rs | 339 +++++++++++++++++++++++ drivers/0x08_lcd1602_rust/src/lcd1602.rs | 72 +++++ drivers/0x08_lcd1602_rust/src/lib.rs | 5 + drivers/0x08_lcd1602_rust/src/main.rs | 230 +-------------- 20 files changed, 1902 insertions(+), 1293 deletions(-) create mode 100644 drivers/0x01_uart_rust/src/board.rs create mode 100644 drivers/0x02_blink_rust/src/board.rs create mode 100644 drivers/0x03_button_rust/src/board.rs create mode 100644 drivers/0x04_pwm_rust/src/board.rs create mode 100644 drivers/0x05_servo_rust/src/board.rs create mode 100644 drivers/0x06_adc_rust/src/board.rs create mode 100644 drivers/0x07_i2c_rust/src/board.rs create mode 100644 drivers/0x08_lcd1602_rust/src/board.rs diff --git a/drivers/0x01_uart_rust/src/board.rs b/drivers/0x01_uart_rust/src/board.rs new file mode 100644 index 0000000..35cc046 --- /dev/null +++ b/drivers/0x01_uart_rust/src/board.rs @@ -0,0 +1,94 @@ +//! @file board.rs +//! @brief Board-level initialisation helpers for the UART demo +//! @author Kevin Thomas +//! @date 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +#[cfg(rp2350)] +use rp235x_hal as hal; + +#[cfg(rp2040)] +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +// End of file diff --git a/drivers/0x01_uart_rust/src/main.rs b/drivers/0x01_uart_rust/src/main.rs index 3edad50..70425c4 100644 --- a/drivers/0x01_uart_rust/src/main.rs +++ b/drivers/0x01_uart_rust/src/main.rs @@ -40,6 +40,7 @@ #![no_std] #![no_main] +mod board; mod uart; use defmt_rtt as _; @@ -66,64 +67,6 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; #[cfg(rp2350)] pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); -const XTAL_FREQ_HZ: u32 = 12_000_000u32; - -const UART_BAUD: u32 = 115_200; - -/// Initialise system clocks and PLLs from the external 12 MHz crystal. -/// -/// # Arguments -/// -/// * `xosc` - XOSC peripheral singleton. -/// * `clocks` - CLOCKS peripheral singleton. -/// * `pll_sys` - PLL_SYS peripheral singleton. -/// * `pll_usb` - PLL_USB peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `watchdog` - Mutable reference to the watchdog timer. -/// -/// # Returns -/// -/// Configured clocks manager. -/// -/// # Panics -/// -/// Panics if clock initialisation fails. -fn init_clocks( - xosc: hal::pac::XOSC, - clocks: hal::pac::CLOCKS, - pll_sys: hal::pac::PLL_SYS, - pll_usb: hal::pac::PLL_USB, - resets: &mut hal::pac::RESETS, - watchdog: &mut hal::Watchdog, -) -> hal::clocks::ClocksManager { - hal::clocks::init_clocks_and_plls( - XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, - ) - .unwrap() -} - -/// Unlock the GPIO bank and return the pin set. -/// -/// # Arguments -/// -/// * `io_bank0` - IO_BANK0 peripheral singleton. -/// * `pads_bank0` - PADS_BANK0 peripheral singleton. -/// * `sio` - SIO peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// -/// # Returns -/// -/// GPIO pin set for the entire bank. -fn init_pins( - io_bank0: hal::pac::IO_BANK0, - pads_bank0: hal::pac::PADS_BANK0, - sio: hal::pac::SIO, - resets: &mut hal::pac::RESETS, -) -> hal::gpio::Pins { - let sio = hal::Sio::new(sio); - hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) -} - /// Application entry point for the UART uppercase echo demo. /// /// Initializes UART0 and enters an infinite loop that reads incoming @@ -136,12 +79,12 @@ fn init_pins( fn main() -> ! { let mut pac = hal::pac::Peripherals::take().unwrap(); let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); - let clocks = init_clocks( + let clocks = board::init_clocks( pac.XOSC, pac.CLOCKS, pac.PLL_SYS, pac.PLL_USB, &mut pac.RESETS, &mut watchdog, ); - let pins = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let pins = board::init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); let mut drv = uart::UartDriver::init( - pac.UART0, pins.gpio0, pins.gpio1, UART_BAUD, &mut pac.RESETS, &clocks, + pac.UART0, pins.gpio0, pins.gpio1, board::UART_BAUD, &mut pac.RESETS, &clocks, ); drv.puts(b"UART driver ready (115200 8N1)\r\n"); drv.puts(b"Type characters to echo them back in UPPERCASE:\r\n"); diff --git a/drivers/0x02_blink_rust/src/board.rs b/drivers/0x02_blink_rust/src/board.rs new file mode 100644 index 0000000..7307136 --- /dev/null +++ b/drivers/0x02_blink_rust/src/board.rs @@ -0,0 +1,169 @@ +//! @file board.rs +//! @brief Board-level initialisation helpers for the blink demo +//! @author Kevin Thomas +//! @date 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +use fugit::RateExtU32; +use hal::Clock; +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; + +#[cfg(rp2350)] +use rp235x_hal as hal; + +#[cfg(rp2040)] +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Delay between LED toggles in milliseconds. +pub(crate) const BLINK_DELAY_MS: u32 = 500; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX pins. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +// End of file diff --git a/drivers/0x02_blink_rust/src/main.rs b/drivers/0x02_blink_rust/src/main.rs index d3ecfe5..7f9ecd7 100644 --- a/drivers/0x02_blink_rust/src/main.rs +++ b/drivers/0x02_blink_rust/src/main.rs @@ -37,6 +37,7 @@ #![no_std] #![no_main] +mod board; #[allow(dead_code)] mod blink; @@ -46,11 +47,7 @@ use panic_halt as _; #[cfg(target_arch = "arm")] use panic_probe as _; -use fugit::RateExtU32; use hal::entry; -use hal::Clock; -use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; -use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; #[cfg(rp2350)] use rp235x_hal as hal; @@ -68,127 +65,6 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; #[cfg(rp2350)] pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); -const XTAL_FREQ_HZ: u32 = 12_000_000u32; - -const UART_BAUD: u32 = 115_200; - -const BLINK_DELAY_MS: u32 = 500; - -type TxPin = Pin; -type RxPin = Pin; -type TxPinDefault = Pin; -type RxPinDefault = Pin; -type EnabledUart = UartPeripheral; - -/// Initialise system clocks and PLLs from the external 12 MHz crystal. -/// -/// # Arguments -/// -/// * `xosc` - XOSC peripheral singleton. -/// * `clocks` - CLOCKS peripheral singleton. -/// * `pll_sys` - PLL_SYS peripheral singleton. -/// * `pll_usb` - PLL_USB peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `watchdog` - Mutable reference to the watchdog timer. -/// -/// # Returns -/// -/// Configured clocks manager. -/// -/// # Panics -/// -/// Panics if clock initialisation fails. -fn init_clocks( - xosc: hal::pac::XOSC, - clocks: hal::pac::CLOCKS, - pll_sys: hal::pac::PLL_SYS, - pll_usb: hal::pac::PLL_USB, - resets: &mut hal::pac::RESETS, - watchdog: &mut hal::Watchdog, -) -> hal::clocks::ClocksManager { - hal::clocks::init_clocks_and_plls( - XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, - ) - .unwrap() -} - -/// Unlock the GPIO bank and return the pin set. -/// -/// # Arguments -/// -/// * `io_bank0` - IO_BANK0 peripheral singleton. -/// * `pads_bank0` - PADS_BANK0 peripheral singleton. -/// * `sio` - SIO peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// -/// # Returns -/// -/// GPIO pin set for the entire bank. -fn init_pins( - io_bank0: hal::pac::IO_BANK0, - pads_bank0: hal::pac::PADS_BANK0, - sio: hal::pac::SIO, - resets: &mut hal::pac::RESETS, -) -> hal::gpio::Pins { - let sio = hal::Sio::new(sio); - hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) -} - -/// Initialise UART0 for serial output (stdio equivalent). -/// -/// Configures UART0 at the requested baud rate with 8N1 framing on -/// GPIO 0 (TX) and GPIO 1 (RX). -/// -/// # Arguments -/// -/// * `uart0` - PAC UART0 peripheral singleton. -/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). -/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Enabled UART0 peripheral ready for blocking writes. -/// -/// # Panics -/// -/// Panics if the HAL cannot achieve the requested baud rate. -fn init_uart( - uart0: hal::pac::UART0, - tx_pin: TxPinDefault, - rx_pin: RxPinDefault, - resets: &mut hal::pac::RESETS, - clocks: &hal::clocks::ClocksManager, -) -> EnabledUart { - let pins = ( - tx_pin.reconfigure::(), - rx_pin.reconfigure::(), - ); - let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); - UartPeripheral::new(uart0, pins, resets) - .enable(cfg, clocks.peripheral_clock.freq()) - .unwrap() -} - -/// Create a blocking delay timer from the ARM SysTick peripheral. -/// -/// # Arguments -/// -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Blocking delay provider. -/// -/// # Panics -/// -/// Panics if the cortex-m core peripherals have already been taken. -fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { - let core = cortex_m::Peripherals::take().unwrap(); - cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) -} - /// Application entry point for the LED blink demo. /// /// Initializes the onboard LED and enters an infinite loop that @@ -200,13 +76,13 @@ fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { #[entry] fn main() -> ! { let mut pac = hal::pac::Peripherals::take().unwrap(); - let clocks = init_clocks( + let clocks = board::init_clocks( pac.XOSC, pac.CLOCKS, pac.PLL_SYS, pac.PLL_USB, &mut pac.RESETS, &mut hal::Watchdog::new(pac.WATCHDOG), ); - let pins = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); - let uart = init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); - let mut delay = init_delay(&clocks); + let pins = board::init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = board::init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); + let mut delay = board::init_delay(&clocks); let mut led = blink::BlinkDriver::init(pins.gpio25.into_push_pull_output()); uart.write_full_blocking(b"Blink driver initialized on GPIO 25\r\n"); loop { @@ -216,7 +92,7 @@ fn main() -> ! { } else { uart.write_full_blocking(b"LED: OFF\r\n"); } - delay.delay_ms(BLINK_DELAY_MS); + delay.delay_ms(board::BLINK_DELAY_MS); } } diff --git a/drivers/0x03_button_rust/src/board.rs b/drivers/0x03_button_rust/src/board.rs new file mode 100644 index 0000000..47220a3 --- /dev/null +++ b/drivers/0x03_button_rust/src/board.rs @@ -0,0 +1,172 @@ +//! @file board.rs +//! @brief Board-level initialisation helpers for the button demo +//! @author Kevin Thomas +//! @date 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +use fugit::RateExtU32; +use hal::Clock; +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; + +#[cfg(rp2350)] +use rp235x_hal as hal; + +#[cfg(rp2040)] +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Debounce settling time in milliseconds. +pub(crate) const DEBOUNCE_MS: u32 = 20; + +/// Main-loop polling interval in milliseconds. +pub(crate) const POLL_MS: u32 = 10; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX pins. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +// End of file diff --git a/drivers/0x03_button_rust/src/main.rs b/drivers/0x03_button_rust/src/main.rs index 1ef974a..76c8b41 100644 --- a/drivers/0x03_button_rust/src/main.rs +++ b/drivers/0x03_button_rust/src/main.rs @@ -39,6 +39,7 @@ #![no_std] #![no_main] +mod board; #[allow(dead_code)] mod button; @@ -49,11 +50,7 @@ use panic_halt as _; use panic_probe as _; use core::cell::RefCell; -use fugit::RateExtU32; use hal::entry; -use hal::Clock; -use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; -use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; #[cfg(rp2350)] use rp235x_hal as hal; @@ -71,128 +68,6 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; #[cfg(rp2350)] pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); -const XTAL_FREQ_HZ: u32 = 12_000_000u32; - -const UART_BAUD: u32 = 115_200; - -const DEBOUNCE_MS: u32 = 20; -const POLL_MS: u32 = 10; - -type TxPin = Pin; -type RxPin = Pin; -type TxPinDefault = Pin; -type RxPinDefault = Pin; -type EnabledUart = UartPeripheral; - -/// Initialise system clocks and PLLs from the external 12 MHz crystal. -/// -/// # Arguments -/// -/// * `xosc` - XOSC peripheral singleton. -/// * `clocks` - CLOCKS peripheral singleton. -/// * `pll_sys` - PLL_SYS peripheral singleton. -/// * `pll_usb` - PLL_USB peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `watchdog` - Mutable reference to the watchdog timer. -/// -/// # Returns -/// -/// Configured clocks manager. -/// -/// # Panics -/// -/// Panics if clock initialisation fails. -fn init_clocks( - xosc: hal::pac::XOSC, - clocks: hal::pac::CLOCKS, - pll_sys: hal::pac::PLL_SYS, - pll_usb: hal::pac::PLL_USB, - resets: &mut hal::pac::RESETS, - watchdog: &mut hal::Watchdog, -) -> hal::clocks::ClocksManager { - hal::clocks::init_clocks_and_plls( - XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, - ) - .unwrap() -} - -/// Unlock the GPIO bank and return the pin set. -/// -/// # Arguments -/// -/// * `io_bank0` - IO_BANK0 peripheral singleton. -/// * `pads_bank0` - PADS_BANK0 peripheral singleton. -/// * `sio` - SIO peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// -/// # Returns -/// -/// GPIO pin set for the entire bank. -fn init_pins( - io_bank0: hal::pac::IO_BANK0, - pads_bank0: hal::pac::PADS_BANK0, - sio: hal::pac::SIO, - resets: &mut hal::pac::RESETS, -) -> hal::gpio::Pins { - let sio = hal::Sio::new(sio); - hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) -} - -/// Initialise UART0 for serial output (stdio equivalent). -/// -/// Configures UART0 at the requested baud rate with 8N1 framing on -/// GPIO 0 (TX) and GPIO 1 (RX). -/// -/// # Arguments -/// -/// * `uart0` - PAC UART0 peripheral singleton. -/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). -/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Enabled UART0 peripheral ready for blocking writes. -/// -/// # Panics -/// -/// Panics if the HAL cannot achieve the requested baud rate. -fn init_uart( - uart0: hal::pac::UART0, - tx_pin: TxPinDefault, - rx_pin: RxPinDefault, - resets: &mut hal::pac::RESETS, - clocks: &hal::clocks::ClocksManager, -) -> EnabledUart { - let pins = ( - tx_pin.reconfigure::(), - rx_pin.reconfigure::(), - ); - let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); - UartPeripheral::new(uart0, pins, resets) - .enable(cfg, clocks.peripheral_clock.freq()) - .unwrap() -} - -/// Create a blocking delay timer from the ARM SysTick peripheral. -/// -/// # Arguments -/// -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Blocking delay provider. -/// -/// # Panics -/// -/// Panics if the cortex-m core peripherals have already been taken. -fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { - let core = cortex_m::Peripherals::take().unwrap(); - cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) -} - /// Application entry point for the button debounce demo. /// /// Initializes button and LED, then continuously polls button state @@ -204,16 +79,16 @@ fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { #[entry] fn main() -> ! { let mut pac = hal::pac::Peripherals::take().unwrap(); - let clocks = init_clocks( + let clocks = board::init_clocks( pac.XOSC, pac.CLOCKS, pac.PLL_SYS, pac.PLL_USB, &mut pac.RESETS, &mut hal::Watchdog::new(pac.WATCHDOG), ); - let pins = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); - let uart = init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); - let delay = RefCell::new(init_delay(&clocks)); + let pins = board::init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = board::init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); + let delay = RefCell::new(board::init_delay(&clocks)); let btn_pin = pins.gpio15.into_pull_up_input(); let led_pin = pins.gpio25.into_push_pull_output(); - let mut btn = button::ButtonDriver::init(btn_pin, DEBOUNCE_MS, |ms| { + let mut btn = button::ButtonDriver::init(btn_pin, board::DEBOUNCE_MS, |ms| { delay.borrow_mut().delay_ms(ms); }); let mut led = button::ButtonLed::init(led_pin); @@ -230,7 +105,7 @@ fn main() -> ! { } last_state = pressed; } - delay.borrow_mut().delay_ms(POLL_MS); + delay.borrow_mut().delay_ms(board::POLL_MS); } } diff --git a/drivers/0x04_pwm_rust/src/board.rs b/drivers/0x04_pwm_rust/src/board.rs new file mode 100644 index 0000000..145701d --- /dev/null +++ b/drivers/0x04_pwm_rust/src/board.rs @@ -0,0 +1,256 @@ +//! @file board.rs +//! @brief Board-level initialisation helpers for the PWM demo +//! @author Kevin Thomas +//! @date 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +use embedded_hal::pwm::SetDutyCycle; +use fugit::RateExtU32; +use hal::Clock; +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; + +#[cfg(rp2350)] +use rp235x_hal as hal; + +#[cfg(rp2040)] +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Desired PWM output frequency in Hz. +pub(crate) const PWM_FREQ_HZ: u32 = 1000; + +/// PWM counter wrap value (period - 1). +pub(crate) const PWM_WRAP: u32 = 10000 - 1; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX pins. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Format a duty percentage into a fixed byte buffer as "Duty: NNN%\r\n". +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice (must be at least 16 bytes). +/// * `duty` - Duty cycle percentage to format. +/// +/// # Returns +/// +/// Number of bytes written into the buffer. +pub(crate) fn format_duty(buf: &mut [u8], duty: u8) -> usize { + let prefix = b"Duty: "; + buf[..6].copy_from_slice(prefix); + let mut pos = 6; + if duty >= 100 { + buf[pos] = b'1'; pos += 1; + buf[pos] = b'0'; pos += 1; + buf[pos] = b'0'; pos += 1; + } else if duty >= 10 { + buf[pos] = b' '; pos += 1; + buf[pos] = b'0' + duty / 10; pos += 1; + buf[pos] = b'0' + duty % 10; pos += 1; + } else { + buf[pos] = b' '; pos += 1; + buf[pos] = b' '; pos += 1; + buf[pos] = b'0' + duty; pos += 1; + } + buf[pos] = b'%'; pos += 1; + buf[pos] = b'\r'; pos += 1; + buf[pos] = b'\n'; pos += 1; + pos +} + +/// Sweep the PWM duty cycle from 0% to 100% in steps of 5. +/// +/// # Arguments +/// +/// * `uart` - UART peripheral for serial output. +/// * `channel` - PWM channel to set duty on. +/// * `delay` - Delay provider for 50 ms pauses. +/// * `buf` - Scratch buffer for formatting output. +pub(crate) fn sweep_up( + uart: &EnabledUart, + channel: &mut impl SetDutyCycle, + delay: &mut cortex_m::delay::Delay, + buf: &mut [u8; 16], +) { + let mut duty: u8 = 0; + while duty <= 100 { + let level = crate::pwm::duty_to_level(duty, PWM_WRAP) as u16; + channel.set_duty_cycle(level).ok(); + let n = format_duty(buf, duty); + uart.write_full_blocking(&buf[..n]); + delay.delay_ms(50u32); + duty += 5; + } +} + +/// Sweep the PWM duty cycle from 100% to 0% in steps of 5. +/// +/// # Arguments +/// +/// * `uart` - UART peripheral for serial output. +/// * `channel` - PWM channel to set duty on. +/// * `delay` - Delay provider for 50 ms pauses. +/// * `buf` - Scratch buffer for formatting output. +pub(crate) fn sweep_down( + uart: &EnabledUart, + channel: &mut impl SetDutyCycle, + delay: &mut cortex_m::delay::Delay, + buf: &mut [u8; 16], +) { + let mut duty: i8 = 100; + while duty >= 0 { + let level = crate::pwm::duty_to_level(duty as u8, PWM_WRAP) as u16; + channel.set_duty_cycle(level).ok(); + let n = format_duty(buf, duty as u8); + uart.write_full_blocking(&buf[..n]); + delay.delay_ms(50u32); + duty -= 5; + } +} + +// End of file diff --git a/drivers/0x04_pwm_rust/src/main.rs b/drivers/0x04_pwm_rust/src/main.rs index 3cb06ee..d8bb514 100644 --- a/drivers/0x04_pwm_rust/src/main.rs +++ b/drivers/0x04_pwm_rust/src/main.rs @@ -40,6 +40,7 @@ #![no_std] #![no_main] +mod board; #[allow(dead_code)] mod pwm; @@ -49,12 +50,8 @@ use panic_halt as _; #[cfg(target_arch = "arm")] use panic_probe as _; -use embedded_hal::pwm::SetDutyCycle; -use fugit::RateExtU32; -use hal::entry; use hal::Clock; -use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; -use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +use hal::entry; #[cfg(rp2350)] use rp235x_hal as hal; @@ -72,157 +69,6 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; #[cfg(rp2350)] pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); -const XTAL_FREQ_HZ: u32 = 12_000_000u32; - -const UART_BAUD: u32 = 115_200; -const PWM_FREQ_HZ: u32 = 1000; -const PWM_WRAP: u32 = 10000 - 1; - -type TxPin = Pin; -type RxPin = Pin; -type TxPinDefault = Pin; -type RxPinDefault = Pin; -type EnabledUart = UartPeripheral; - -/// Initialise system clocks and PLLs from the external 12 MHz crystal. -/// -/// # Arguments -/// -/// * `xosc` - XOSC peripheral singleton. -/// * `clocks` - CLOCKS peripheral singleton. -/// * `pll_sys` - PLL_SYS peripheral singleton. -/// * `pll_usb` - PLL_USB peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `watchdog` - Mutable reference to the watchdog timer. -/// -/// # Returns -/// -/// Configured clocks manager. -/// -/// # Panics -/// -/// Panics if clock initialisation fails. -fn init_clocks( - xosc: hal::pac::XOSC, - clocks: hal::pac::CLOCKS, - pll_sys: hal::pac::PLL_SYS, - pll_usb: hal::pac::PLL_USB, - resets: &mut hal::pac::RESETS, - watchdog: &mut hal::Watchdog, -) -> hal::clocks::ClocksManager { - hal::clocks::init_clocks_and_plls( - XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, - ) - .unwrap() -} - -/// Unlock the GPIO bank and return the pin set. -/// -/// # Arguments -/// -/// * `io_bank0` - IO_BANK0 peripheral singleton. -/// * `pads_bank0` - PADS_BANK0 peripheral singleton. -/// * `sio` - SIO peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// -/// # Returns -/// -/// GPIO pin set for the entire bank. -fn init_pins( - io_bank0: hal::pac::IO_BANK0, - pads_bank0: hal::pac::PADS_BANK0, - sio: hal::pac::SIO, - resets: &mut hal::pac::RESETS, -) -> hal::gpio::Pins { - let sio = hal::Sio::new(sio); - hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) -} - -/// Initialise UART0 for serial output (stdio equivalent). -/// -/// # Arguments -/// -/// * `uart0` - PAC UART0 peripheral singleton. -/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). -/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Enabled UART0 peripheral ready for blocking writes. -/// -/// # Panics -/// -/// Panics if the HAL cannot achieve the requested baud rate. -fn init_uart( - uart0: hal::pac::UART0, - tx_pin: TxPinDefault, - rx_pin: RxPinDefault, - resets: &mut hal::pac::RESETS, - clocks: &hal::clocks::ClocksManager, -) -> EnabledUart { - let pins = ( - tx_pin.reconfigure::(), - rx_pin.reconfigure::(), - ); - let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); - UartPeripheral::new(uart0, pins, resets) - .enable(cfg, clocks.peripheral_clock.freq()) - .unwrap() -} - -/// Create a blocking delay timer from the ARM SysTick peripheral. -/// -/// # Arguments -/// -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Blocking delay provider. -/// -/// # Panics -/// -/// Panics if the cortex-m core peripherals have already been taken. -fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { - let core = cortex_m::Peripherals::take().unwrap(); - cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) -} - -/// Format a duty percentage into a fixed byte buffer as "Duty: NNN%\r\n". -/// -/// # Arguments -/// -/// * `buf` - Mutable byte slice (must be at least 16 bytes). -/// * `duty` - Duty cycle percentage to format. -/// -/// # Returns -/// -/// Number of bytes written into the buffer. -fn format_duty(buf: &mut [u8], duty: u8) -> usize { - let prefix = b"Duty: "; - buf[..6].copy_from_slice(prefix); - let mut pos = 6; - if duty >= 100 { - buf[pos] = b'1'; pos += 1; - buf[pos] = b'0'; pos += 1; - buf[pos] = b'0'; pos += 1; - } else if duty >= 10 { - buf[pos] = b' '; pos += 1; - buf[pos] = b'0' + duty / 10; pos += 1; - buf[pos] = b'0' + duty % 10; pos += 1; - } else { - buf[pos] = b' '; pos += 1; - buf[pos] = b' '; pos += 1; - buf[pos] = b'0' + duty; pos += 1; - } - buf[pos] = b'%'; pos += 1; - buf[pos] = b'\r'; pos += 1; - buf[pos] = b'\n'; pos += 1; - pos -} - /// Application entry point for the PWM LED breathing demo. /// /// Initializes PWM at 1 kHz on the onboard LED and enters an infinite @@ -235,78 +81,28 @@ fn format_duty(buf: &mut [u8], duty: u8) -> usize { #[entry] fn main() -> ! { let mut pac = hal::pac::Peripherals::take().unwrap(); - let clocks = init_clocks( + let clocks = board::init_clocks( pac.XOSC, pac.CLOCKS, pac.PLL_SYS, pac.PLL_USB, &mut pac.RESETS, &mut hal::Watchdog::new(pac.WATCHDOG), ); - let pins = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); - let uart = init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); - let mut delay = init_delay(&clocks); + let pins = board::init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = board::init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); + let mut delay = board::init_delay(&clocks); uart.write_full_blocking(b"PWM initialized: GPIO25 @ 1000 Hz\r\n"); let pwm_slices = hal::pwm::Slices::new(pac.PWM, &mut pac.RESETS); - let mut pwm = pwm_slices.pwm4; + let mut pwm_slice = pwm_slices.pwm4; let sys_hz = clocks.system_clock.freq().to_Hz(); - let div = pwm::calc_clk_div(sys_hz, PWM_FREQ_HZ, PWM_WRAP); + let div = pwm::calc_clk_div(sys_hz, board::PWM_FREQ_HZ, board::PWM_WRAP); let div_int = div as u8; - pwm.set_div_int(div_int); - pwm.set_div_frac((((div - div_int as f32) * 16.0) as u8).min(15)); - pwm.set_top(PWM_WRAP as u16); - pwm.enable(); - pwm.channel_b.output_to(pins.gpio25); + pwm_slice.set_div_int(div_int); + pwm_slice.set_div_frac((((div - div_int as f32) * 16.0) as u8).min(15)); + pwm_slice.set_top(board::PWM_WRAP as u16); + pwm_slice.enable(); + pwm_slice.channel_b.output_to(pins.gpio25); let mut buf = [0u8; 16]; loop { - sweep_up(&uart, &mut pwm.channel_b, &mut delay, &mut buf); - sweep_down(&uart, &mut pwm.channel_b, &mut delay, &mut buf); - } -} - -/// Sweep the PWM duty cycle from 0% to 100% in steps of 5. -/// -/// # Arguments -/// -/// * `uart` - UART peripheral for serial output. -/// * `channel` - PWM channel to set duty on. -/// * `delay` - Delay provider for 50 ms pauses. -/// * `buf` - Scratch buffer for formatting output. -fn sweep_up( - uart: &EnabledUart, - channel: &mut impl SetDutyCycle, - delay: &mut cortex_m::delay::Delay, - buf: &mut [u8; 16], -) { - let mut duty: u8 = 0; - while duty <= 100 { - let level = pwm::duty_to_level(duty, PWM_WRAP) as u16; - channel.set_duty_cycle(level).ok(); - let n = format_duty(buf, duty); - uart.write_full_blocking(&buf[..n]); - delay.delay_ms(50u32); - duty += 5; - } -} - -/// Sweep the PWM duty cycle from 100% to 0% in steps of 5. -/// -/// # Arguments -/// -/// * `uart` - UART peripheral for serial output. -/// * `channel` - PWM channel to set duty on. -/// * `delay` - Delay provider for 50 ms pauses. -/// * `buf` - Scratch buffer for formatting output. -fn sweep_down( - uart: &EnabledUart, - channel: &mut impl SetDutyCycle, - delay: &mut cortex_m::delay::Delay, - buf: &mut [u8; 16], -) { - let mut duty: i8 = 100; - while duty >= 0 { - let level = pwm::duty_to_level(duty as u8, PWM_WRAP) as u16; - channel.set_duty_cycle(level).ok(); - let n = format_duty(buf, duty as u8); - uart.write_full_blocking(&buf[..n]); - delay.delay_ms(50u32); - duty -= 5; + board::sweep_up(&uart, &mut pwm_slice.channel_b, &mut delay, &mut buf); + board::sweep_down(&uart, &mut pwm_slice.channel_b, &mut delay, &mut buf); } } diff --git a/drivers/0x05_servo_rust/src/board.rs b/drivers/0x05_servo_rust/src/board.rs new file mode 100644 index 0000000..9ad8261 --- /dev/null +++ b/drivers/0x05_servo_rust/src/board.rs @@ -0,0 +1,258 @@ +//! @file board.rs +//! @brief Board-level initialisation helpers for the servo demo +//! @author Kevin Thomas +//! @date 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +use embedded_hal::pwm::SetDutyCycle; +use fugit::RateExtU32; +use hal::Clock; +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; + +#[cfg(rp2350)] +use rp235x_hal as hal; + +#[cfg(rp2040)] +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Angle increment per sweep step in degrees. +pub(crate) const STEP_DEGREES: i32 = 10; + +/// Delay between sweep steps in milliseconds. +pub(crate) const STEP_DELAY_MS: u32 = 150; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX pins. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Format an angle into "Angle: NNN deg\r\n". +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice (must be at least 20 bytes). +/// * `angle` - Angle in degrees (0..180). +/// +/// # Returns +/// +/// Number of bytes written into the buffer. +pub(crate) fn format_angle(buf: &mut [u8], angle: i32) -> usize { + let prefix = b"Angle: "; + buf[..7].copy_from_slice(prefix); + let mut pos = 7; + let a = if angle < 0 { 0 } else { angle as u32 }; + if a >= 100 { + buf[pos] = b'0' + (a / 100) as u8; pos += 1; + buf[pos] = b'0' + ((a / 10) % 10) as u8; pos += 1; + buf[pos] = b'0' + (a % 10) as u8; pos += 1; + } else if a >= 10 { + buf[pos] = b' '; pos += 1; + buf[pos] = b'0' + (a / 10) as u8; pos += 1; + buf[pos] = b'0' + (a % 10) as u8; pos += 1; + } else { + buf[pos] = b' '; pos += 1; + buf[pos] = b' '; pos += 1; + buf[pos] = b'0' + a as u8; pos += 1; + } + let suffix = b" deg\r\n"; + buf[pos..pos + 6].copy_from_slice(suffix); + pos + 6 +} + +/// Sweep the servo angle upward from 0 to 180 in STEP_DEGREES increments. +/// +/// # Arguments +/// +/// * `uart` - UART peripheral for serial output. +/// * `channel` - PWM channel implementing SetDutyCycle. +/// * `delay` - Delay provider for pause between steps. +/// * `buf` - Scratch buffer for formatting output. +pub(crate) fn sweep_angle_up( + uart: &EnabledUart, + channel: &mut impl SetDutyCycle, + delay: &mut cortex_m::delay::Delay, + buf: &mut [u8; 20], +) { + let mut angle: i32 = 0; + while angle <= 180 { + let pulse = crate::servo::angle_to_pulse_us(angle as f32, crate::servo::SERVO_DEFAULT_MIN_US, crate::servo::SERVO_DEFAULT_MAX_US); + let level = crate::servo::pulse_us_to_level(pulse as u32, crate::servo::SERVO_WRAP, crate::servo::SERVO_HZ) as u16; + channel.set_duty_cycle(level).ok(); + let n = format_angle(buf, angle); + uart.write_full_blocking(&buf[..n]); + delay.delay_ms(STEP_DELAY_MS); + angle += STEP_DEGREES; + } +} + +/// Sweep the servo angle downward from 180 to 0 in STEP_DEGREES decrements. +/// +/// # Arguments +/// +/// * `uart` - UART peripheral for serial output. +/// * `channel` - PWM channel implementing SetDutyCycle. +/// * `delay` - Delay provider for pause between steps. +/// * `buf` - Scratch buffer for formatting output. +pub(crate) fn sweep_angle_down( + uart: &EnabledUart, + channel: &mut impl SetDutyCycle, + delay: &mut cortex_m::delay::Delay, + buf: &mut [u8; 20], +) { + let mut angle: i32 = 180; + while angle >= 0 { + let pulse = crate::servo::angle_to_pulse_us(angle as f32, crate::servo::SERVO_DEFAULT_MIN_US, crate::servo::SERVO_DEFAULT_MAX_US); + let level = crate::servo::pulse_us_to_level(pulse as u32, crate::servo::SERVO_WRAP, crate::servo::SERVO_HZ) as u16; + channel.set_duty_cycle(level).ok(); + let n = format_angle(buf, angle); + uart.write_full_blocking(&buf[..n]); + delay.delay_ms(STEP_DELAY_MS); + angle -= STEP_DEGREES; + } +} + +// End of file diff --git a/drivers/0x05_servo_rust/src/main.rs b/drivers/0x05_servo_rust/src/main.rs index e00aa52..da6c297 100644 --- a/drivers/0x05_servo_rust/src/main.rs +++ b/drivers/0x05_servo_rust/src/main.rs @@ -39,6 +39,7 @@ #![no_std] #![no_main] +mod board; #[allow(dead_code)] mod servo; @@ -48,12 +49,8 @@ use panic_halt as _; #[cfg(target_arch = "arm")] use panic_probe as _; -use embedded_hal::pwm::SetDutyCycle; -use fugit::RateExtU32; -use hal::entry; use hal::Clock; -use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; -use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +use hal::entry; #[cfg(rp2350)] use rp235x_hal as hal; @@ -71,209 +68,6 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; #[cfg(rp2350)] pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); -const XTAL_FREQ_HZ: u32 = 12_000_000u32; - -const UART_BAUD: u32 = 115_200; -const STEP_DEGREES: i32 = 10; -const STEP_DELAY_MS: u32 = 150; - -type TxPin = Pin; -type RxPin = Pin; -type TxPinDefault = Pin; -type RxPinDefault = Pin; -type EnabledUart = UartPeripheral; - -/// Initialise system clocks and PLLs from the external 12 MHz crystal. -/// -/// # Arguments -/// -/// * `xosc` - XOSC peripheral singleton. -/// * `clocks` - CLOCKS peripheral singleton. -/// * `pll_sys` - PLL_SYS peripheral singleton. -/// * `pll_usb` - PLL_USB peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `watchdog` - Mutable reference to the watchdog timer. -/// -/// # Returns -/// -/// Configured clocks manager. -/// -/// # Panics -/// -/// Panics if clock initialisation fails. -fn init_clocks( - xosc: hal::pac::XOSC, - clocks: hal::pac::CLOCKS, - pll_sys: hal::pac::PLL_SYS, - pll_usb: hal::pac::PLL_USB, - resets: &mut hal::pac::RESETS, - watchdog: &mut hal::Watchdog, -) -> hal::clocks::ClocksManager { - hal::clocks::init_clocks_and_plls( - XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, - ) - .unwrap() -} - -/// Unlock the GPIO bank and return the pin set. -/// -/// # Arguments -/// -/// * `io_bank0` - IO_BANK0 peripheral singleton. -/// * `pads_bank0` - PADS_BANK0 peripheral singleton. -/// * `sio` - SIO peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// -/// # Returns -/// -/// GPIO pin set for the entire bank. -fn init_pins( - io_bank0: hal::pac::IO_BANK0, - pads_bank0: hal::pac::PADS_BANK0, - sio: hal::pac::SIO, - resets: &mut hal::pac::RESETS, -) -> hal::gpio::Pins { - let sio = hal::Sio::new(sio); - hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) -} - -/// Initialise UART0 for serial output (stdio equivalent). -/// -/// # Arguments -/// -/// * `uart0` - PAC UART0 peripheral singleton. -/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). -/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Enabled UART0 peripheral ready for blocking writes. -/// -/// # Panics -/// -/// Panics if the HAL cannot achieve the requested baud rate. -fn init_uart( - uart0: hal::pac::UART0, - tx_pin: TxPinDefault, - rx_pin: RxPinDefault, - resets: &mut hal::pac::RESETS, - clocks: &hal::clocks::ClocksManager, -) -> EnabledUart { - let pins = ( - tx_pin.reconfigure::(), - rx_pin.reconfigure::(), - ); - let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); - UartPeripheral::new(uart0, pins, resets) - .enable(cfg, clocks.peripheral_clock.freq()) - .unwrap() -} - -/// Create a blocking delay timer from the ARM SysTick peripheral. -/// -/// # Arguments -/// -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Blocking delay provider. -/// -/// # Panics -/// -/// Panics if the cortex-m core peripherals have already been taken. -fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { - let core = cortex_m::Peripherals::take().unwrap(); - cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) -} - -/// Format an angle into "Angle: NNN deg\r\n". -/// -/// # Arguments -/// -/// * `buf` - Mutable byte slice (must be at least 20 bytes). -/// * `angle` - Angle in degrees (0..180). -/// -/// # Returns -/// -/// Number of bytes written into the buffer. -fn format_angle(buf: &mut [u8], angle: i32) -> usize { - let prefix = b"Angle: "; - buf[..7].copy_from_slice(prefix); - let mut pos = 7; - let a = if angle < 0 { 0 } else { angle as u32 }; - if a >= 100 { - buf[pos] = b'0' + (a / 100) as u8; pos += 1; - buf[pos] = b'0' + ((a / 10) % 10) as u8; pos += 1; - buf[pos] = b'0' + (a % 10) as u8; pos += 1; - } else if a >= 10 { - buf[pos] = b' '; pos += 1; - buf[pos] = b'0' + (a / 10) as u8; pos += 1; - buf[pos] = b'0' + (a % 10) as u8; pos += 1; - } else { - buf[pos] = b' '; pos += 1; - buf[pos] = b' '; pos += 1; - buf[pos] = b'0' + a as u8; pos += 1; - } - let suffix = b" deg\r\n"; - buf[pos..pos + 6].copy_from_slice(suffix); - pos + 6 -} - -/// Sweep the servo angle upward from 0 to 180 in STEP_DEGREES increments. -/// -/// # Arguments -/// -/// * `uart` - UART peripheral for serial output. -/// * `channel` - PWM channel implementing SetDutyCycle. -/// * `delay` - Delay provider for pause between steps. -/// * `buf` - Scratch buffer for formatting output. -fn sweep_angle_up( - uart: &EnabledUart, - channel: &mut impl SetDutyCycle, - delay: &mut cortex_m::delay::Delay, - buf: &mut [u8; 20], -) { - let mut angle: i32 = 0; - while angle <= 180 { - let pulse = servo::angle_to_pulse_us(angle as f32, servo::SERVO_DEFAULT_MIN_US, servo::SERVO_DEFAULT_MAX_US); - let level = servo::pulse_us_to_level(pulse as u32, servo::SERVO_WRAP, servo::SERVO_HZ) as u16; - channel.set_duty_cycle(level).ok(); - let n = format_angle(buf, angle); - uart.write_full_blocking(&buf[..n]); - delay.delay_ms(STEP_DELAY_MS); - angle += STEP_DEGREES; - } -} - -/// Sweep the servo angle downward from 180 to 0 in STEP_DEGREES decrements. -/// -/// # Arguments -/// -/// * `uart` - UART peripheral for serial output. -/// * `channel` - PWM channel implementing SetDutyCycle. -/// * `delay` - Delay provider for pause between steps. -/// * `buf` - Scratch buffer for formatting output. -fn sweep_angle_down( - uart: &EnabledUart, - channel: &mut impl SetDutyCycle, - delay: &mut cortex_m::delay::Delay, - buf: &mut [u8; 20], -) { - let mut angle: i32 = 180; - while angle >= 0 { - let pulse = servo::angle_to_pulse_us(angle as f32, servo::SERVO_DEFAULT_MIN_US, servo::SERVO_DEFAULT_MAX_US); - let level = servo::pulse_us_to_level(pulse as u32, servo::SERVO_WRAP, servo::SERVO_HZ) as u16; - channel.set_duty_cycle(level).ok(); - let n = format_angle(buf, angle); - uart.write_full_blocking(&buf[..n]); - delay.delay_ms(STEP_DELAY_MS); - angle -= STEP_DEGREES; - } -} - /// Application entry point for the servo sweep demo. /// /// Initializes the servo on GPIO 6 and continuously sweeps 0-180-0 @@ -285,13 +79,13 @@ fn sweep_angle_down( #[entry] fn main() -> ! { let mut pac = hal::pac::Peripherals::take().unwrap(); - let clocks = init_clocks( + let clocks = board::init_clocks( pac.XOSC, pac.CLOCKS, pac.PLL_SYS, pac.PLL_USB, &mut pac.RESETS, &mut hal::Watchdog::new(pac.WATCHDOG), ); - let pins = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); - let uart = init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); - let mut delay = init_delay(&clocks); + let pins = board::init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = board::init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); + let mut delay = board::init_delay(&clocks); let pwm_slices = hal::pwm::Slices::new(pac.PWM, &mut pac.RESETS); let mut pwm = pwm_slices.pwm3; let sys_hz = clocks.system_clock.freq().to_Hz(); @@ -306,8 +100,8 @@ fn main() -> ! { uart.write_full_blocking(b"Sweeping 0 -> 180 -> 0 degrees in 10-degree steps\r\n"); let mut buf = [0u8; 20]; loop { - sweep_angle_up(&uart, &mut pwm.channel_a, &mut delay, &mut buf); - sweep_angle_down(&uart, &mut pwm.channel_a, &mut delay, &mut buf); + board::sweep_angle_up(&uart, &mut pwm.channel_a, &mut delay, &mut buf); + board::sweep_angle_down(&uart, &mut pwm.channel_a, &mut delay, &mut buf); } } diff --git a/drivers/0x06_adc_rust/src/board.rs b/drivers/0x06_adc_rust/src/board.rs new file mode 100644 index 0000000..1867e2a --- /dev/null +++ b/drivers/0x06_adc_rust/src/board.rs @@ -0,0 +1,215 @@ +//! @file board.rs +//! @brief Board-level initialisation helpers for the ADC demo +//! @author Kevin Thomas +//! @date 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +use fugit::RateExtU32; +use hal::Clock; +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; + +#[cfg(rp2350)] +use rp235x_hal as hal; + +#[cfg(rp2040)] +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// Main-loop polling interval in milliseconds. +pub(crate) const POLL_MS: u32 = 500; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX pins. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Format a millivolt value into "ADC0: NNNN mV | Chip temp: NN.N C\r\n". +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice (must be at least 48 bytes). +/// * `mv` - Voltage in millivolts. +/// * `temp_int` - Integer part of temperature. +/// * `temp_frac` - Single decimal digit of temperature fraction. +/// +/// # Returns +/// +/// Number of bytes written into the buffer. +pub(crate) fn format_adc_line(buf: &mut [u8], mv: u32, temp_int: i32, temp_frac: u8) -> usize { + let prefix = b"ADC0: "; + buf[..6].copy_from_slice(prefix); + let mut pos = 6; + let thousands = ((mv / 1000) % 10) as u8; + let hundreds = ((mv / 100) % 10) as u8; + let tens = ((mv / 10) % 10) as u8; + let ones = (mv % 10) as u8; + buf[pos] = b'0' + thousands; pos += 1; + buf[pos] = b'0' + hundreds; pos += 1; + buf[pos] = b'0' + tens; pos += 1; + buf[pos] = b'0' + ones; pos += 1; + let mid = b" mV | Chip temp: "; + buf[pos..pos + 19].copy_from_slice(mid); + pos += 19; + let abs_temp = if temp_int < 0 { -temp_int } else { temp_int } as u32; + if temp_int < 0 { + buf[pos] = b'-'; pos += 1; + } + if abs_temp >= 100 { + buf[pos] = b'0' + ((abs_temp / 100) % 10) as u8; pos += 1; + } + if abs_temp >= 10 { + buf[pos] = b'0' + ((abs_temp / 10) % 10) as u8; pos += 1; + } + buf[pos] = b'0' + (abs_temp % 10) as u8; pos += 1; + buf[pos] = b'.'; pos += 1; + buf[pos] = b'0' + temp_frac; pos += 1; + let suffix = b" C\r\n"; + buf[pos..pos + 4].copy_from_slice(suffix); + pos += 4; + pos +} + +// End of file diff --git a/drivers/0x06_adc_rust/src/main.rs b/drivers/0x06_adc_rust/src/main.rs index 6ebedc7..4cd8c5a 100644 --- a/drivers/0x06_adc_rust/src/main.rs +++ b/drivers/0x06_adc_rust/src/main.rs @@ -39,6 +39,7 @@ #![no_std] #![no_main] +mod board; #[allow(dead_code)] mod adc; @@ -49,11 +50,7 @@ use panic_halt as _; use panic_probe as _; use cortex_m::prelude::_embedded_hal_adc_OneShot; -use fugit::RateExtU32; use hal::entry; -use hal::Clock; -use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; -use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; #[cfg(rp2350)] use rp235x_hal as hal; @@ -71,169 +68,6 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; #[cfg(rp2350)] pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); -const XTAL_FREQ_HZ: u32 = 12_000_000u32; - -const UART_BAUD: u32 = 115_200; -const POLL_MS: u32 = 500; - -type TxPin = Pin; -type RxPin = Pin; -type TxPinDefault = Pin; -type RxPinDefault = Pin; -type EnabledUart = UartPeripheral; - -/// Initialise system clocks and PLLs from the external 12 MHz crystal. -/// -/// # Arguments -/// -/// * `xosc` - XOSC peripheral singleton. -/// * `clocks` - CLOCKS peripheral singleton. -/// * `pll_sys` - PLL_SYS peripheral singleton. -/// * `pll_usb` - PLL_USB peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `watchdog` - Mutable reference to the watchdog timer. -/// -/// # Returns -/// -/// Configured clocks manager. -/// -/// # Panics -/// -/// Panics if clock initialisation fails. -fn init_clocks( - xosc: hal::pac::XOSC, - clocks: hal::pac::CLOCKS, - pll_sys: hal::pac::PLL_SYS, - pll_usb: hal::pac::PLL_USB, - resets: &mut hal::pac::RESETS, - watchdog: &mut hal::Watchdog, -) -> hal::clocks::ClocksManager { - hal::clocks::init_clocks_and_plls( - XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, - ) - .unwrap() -} - -/// Unlock the GPIO bank and return the pin set. -/// -/// # Arguments -/// -/// * `io_bank0` - IO_BANK0 peripheral singleton. -/// * `pads_bank0` - PADS_BANK0 peripheral singleton. -/// * `sio` - SIO peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// -/// # Returns -/// -/// GPIO pin set for the entire bank. -fn init_pins( - io_bank0: hal::pac::IO_BANK0, - pads_bank0: hal::pac::PADS_BANK0, - sio: hal::pac::SIO, - resets: &mut hal::pac::RESETS, -) -> hal::gpio::Pins { - let sio = hal::Sio::new(sio); - hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) -} - -/// Initialise UART0 for serial output (stdio equivalent). -/// -/// # Arguments -/// -/// * `uart0` - PAC UART0 peripheral singleton. -/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). -/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Enabled UART0 peripheral ready for blocking writes. -/// -/// # Panics -/// -/// Panics if the HAL cannot achieve the requested baud rate. -fn init_uart( - uart0: hal::pac::UART0, - tx_pin: TxPinDefault, - rx_pin: RxPinDefault, - resets: &mut hal::pac::RESETS, - clocks: &hal::clocks::ClocksManager, -) -> EnabledUart { - let pins = ( - tx_pin.reconfigure::(), - rx_pin.reconfigure::(), - ); - let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); - UartPeripheral::new(uart0, pins, resets) - .enable(cfg, clocks.peripheral_clock.freq()) - .unwrap() -} - -/// Create a blocking delay timer from the ARM SysTick peripheral. -/// -/// # Arguments -/// -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Blocking delay provider. -/// -/// # Panics -/// -/// Panics if the cortex-m core peripherals have already been taken. -fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { - let core = cortex_m::Peripherals::take().unwrap(); - cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) -} - -/// Format a millivolt value into "ADC0: NNNN mV | Chip temp: ". -/// -/// # Arguments -/// -/// * `buf` - Mutable byte slice (must be at least 48 bytes). -/// * `mv` - Voltage in millivolts. -/// * `temp_int` - Integer part of temperature. -/// * `temp_frac` - Single decimal digit of temperature fraction. -/// -/// # Returns -/// -/// Number of bytes written into the buffer. -fn format_adc_line(buf: &mut [u8], mv: u32, temp_int: i32, temp_frac: u8) -> usize { - let prefix = b"ADC0: "; - buf[..6].copy_from_slice(prefix); - let mut pos = 6; - let thousands = ((mv / 1000) % 10) as u8; - let hundreds = ((mv / 100) % 10) as u8; - let tens = ((mv / 10) % 10) as u8; - let ones = (mv % 10) as u8; - buf[pos] = b'0' + thousands; pos += 1; - buf[pos] = b'0' + hundreds; pos += 1; - buf[pos] = b'0' + tens; pos += 1; - buf[pos] = b'0' + ones; pos += 1; - let mid = b" mV | Chip temp: "; - buf[pos..pos + 19].copy_from_slice(mid); - pos += 19; - let abs_temp = if temp_int < 0 { -temp_int } else { temp_int } as u32; - if temp_int < 0 { - buf[pos] = b'-'; pos += 1; - } - if abs_temp >= 100 { - buf[pos] = b'0' + ((abs_temp / 100) % 10) as u8; pos += 1; - } - if abs_temp >= 10 { - buf[pos] = b'0' + ((abs_temp / 10) % 10) as u8; pos += 1; - } - buf[pos] = b'0' + (abs_temp % 10) as u8; pos += 1; - buf[pos] = b'.'; pos += 1; - buf[pos] = b'0' + temp_frac; pos += 1; - let suffix = b" C\r\n"; - buf[pos..pos + 4].copy_from_slice(suffix); - pos += 4; - pos -} - /// Application entry point for the ADC voltage and temperature demo. /// /// Initializes the ADC on GPIO26 channel 0 and prints readings @@ -245,13 +79,13 @@ fn format_adc_line(buf: &mut [u8], mv: u32, temp_int: i32, temp_frac: u8) -> usi #[entry] fn main() -> ! { let mut pac = hal::pac::Peripherals::take().unwrap(); - let clocks = init_clocks( + let clocks = board::init_clocks( pac.XOSC, pac.CLOCKS, pac.PLL_SYS, pac.PLL_USB, &mut pac.RESETS, &mut hal::Watchdog::new(pac.WATCHDOG), ); - let pins = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); - let uart = init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); - let mut delay = init_delay(&clocks); + let pins = board::init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = board::init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); + let mut delay = board::init_delay(&clocks); let mut adc_hw = hal::Adc::new(pac.ADC, &mut pac.RESETS); let mut adc_pin = hal::adc::AdcPin::new(pins.gpio26).unwrap(); let mut temp_sensor = adc_hw.take_temp_sensor().unwrap(); @@ -264,9 +98,9 @@ fn main() -> ! { let temp = adc::raw_to_celsius(raw_t); let temp_int = temp as i32; let temp_frac = (((temp - temp_int as f32) * 10.0) as u8).min(9); - let n = format_adc_line(&mut buf, mv, temp_int, temp_frac); + let n = board::format_adc_line(&mut buf, mv, temp_int, temp_frac); uart.write_full_blocking(&buf[..n]); - delay.delay_ms(POLL_MS); + delay.delay_ms(board::POLL_MS); } } diff --git a/drivers/0x07_i2c_rust/src/board.rs b/drivers/0x07_i2c_rust/src/board.rs new file mode 100644 index 0000000..2b2ca12 --- /dev/null +++ b/drivers/0x07_i2c_rust/src/board.rs @@ -0,0 +1,188 @@ +//! @file board.rs +//! @brief Board-level initialisation helpers for the I2C scanner demo +//! @author Kevin Thomas +//! @date 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +use embedded_hal::i2c::I2c; +use fugit::RateExtU32; +use hal::Clock; +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; + +#[cfg(rp2350)] +use rp235x_hal as hal; + +#[cfg(rp2040)] +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// I2C bus speed in Hz (100 kHz standard mode). +pub(crate) const I2C_BAUD: u32 = 100_000; + +/// Delay between scan cycles in milliseconds. +pub(crate) const SCAN_DELAY_MS: u32 = 5_000; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX pins. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Probe a 7-bit I2C address by attempting a 1-byte read. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address to probe. +/// +/// # Returns +/// +/// `true` if a device acknowledged, `false` otherwise. +pub(crate) fn probe_addr(i2c: &mut impl I2c, addr: u8) -> bool { + let mut dummy = [0u8; 1]; + i2c.read(addr, &mut dummy).is_ok() +} + +// End of file diff --git a/drivers/0x07_i2c_rust/src/i2c.rs b/drivers/0x07_i2c_rust/src/i2c.rs index ef4c495..f846333 100644 --- a/drivers/0x07_i2c_rust/src/i2c.rs +++ b/drivers/0x07_i2c_rust/src/i2c.rs @@ -1,3 +1,30 @@ +//! @file i2c.rs +//! @brief Implementation of the I2C bus scanner driver +//! @author Kevin Thomas +//! @date 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + /// Lowest valid (non-reserved) 7-bit I2C address. pub const SCAN_ADDR_MIN: u8 = 0x08; @@ -5,15 +32,40 @@ pub const SCAN_ADDR_MIN: u8 = 0x08; pub const SCAN_ADDR_MAX: u8 = 0x77; /// Return `true` when `addr` falls in a reserved 7-bit I2C range. +/// +/// # Arguments +/// +/// * `addr` - 7-bit I2C address to check. +/// +/// # Returns +/// +/// `true` if the address is in the reserved low or high range. pub fn is_reserved(addr: u8) -> bool { addr < SCAN_ADDR_MIN || addr > SCAN_ADDR_MAX } +/// Convert a 4-bit value to its uppercase ASCII hex digit. +/// +/// # Arguments +/// +/// * `val` - Value in the range 0..=15. +/// +/// # Returns +/// +/// ASCII byte `b'0'`..`b'9'` or `b'A'`..`b'F'`. fn hex_digit(val: u8) -> u8 { if val < 10 { b'0' + val } else { b'A' + val - 10 } } /// Write the scan-table header into `buf` and return the byte count. +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice (must be at least 56 bytes). +/// +/// # Returns +/// +/// Number of bytes written into the buffer. pub fn format_scan_header(buf: &mut [u8]) -> usize { let h = b"\r\nI2C bus scan:\r\n 0 1 2 3 4 5 6 7 8 9 A B C D E F\r\n"; buf[..h.len()].copy_from_slice(h); @@ -24,6 +76,16 @@ pub fn format_scan_header(buf: &mut [u8]) -> usize { /// /// Prepends the row label when `addr` is at a 16-byte boundary and /// appends `\r\n` when `addr` is the last column of a row. +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice for formatted output. +/// * `addr` - 7-bit I2C address being reported. +/// * `found` - `true` if a device acknowledged at this address. +/// +/// # Returns +/// +/// Number of bytes written into the buffer. pub fn format_scan_entry(buf: &mut [u8], addr: u8, found: bool) -> usize { let mut pos = 0; if addr % 16 == 0 { diff --git a/drivers/0x07_i2c_rust/src/lib.rs b/drivers/0x07_i2c_rust/src/lib.rs index 6073f23..56f0ade 100644 --- a/drivers/0x07_i2c_rust/src/lib.rs +++ b/drivers/0x07_i2c_rust/src/lib.rs @@ -1,3 +1,8 @@ +//! @file lib.rs +//! @brief Library root for the I2C driver crate +//! @author Kevin Thomas +//! @date 2025 + #![no_std] pub mod i2c; diff --git a/drivers/0x07_i2c_rust/src/main.rs b/drivers/0x07_i2c_rust/src/main.rs index 89439cf..d089136 100644 --- a/drivers/0x07_i2c_rust/src/main.rs +++ b/drivers/0x07_i2c_rust/src/main.rs @@ -41,6 +41,7 @@ #![no_std] #![no_main] +mod board; #[allow(dead_code)] mod i2c; @@ -50,12 +51,10 @@ use panic_halt as _; #[cfg(target_arch = "arm")] use panic_probe as _; -use embedded_hal::i2c::I2c; use fugit::RateExtU32; -use hal::entry; use hal::Clock; -use hal::gpio::{FunctionI2C, FunctionNull, FunctionUart, Pin, PullDown, PullNone, PullUp}; -use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +use hal::entry; +use hal::gpio::{FunctionI2C, PullUp}; #[cfg(rp2350)] use rp235x_hal as hal; @@ -73,139 +72,6 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; #[cfg(rp2350)] pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); -const XTAL_FREQ_HZ: u32 = 12_000_000u32; - -const UART_BAUD: u32 = 115_200; -const I2C_BAUD: u32 = 100_000; -const SCAN_DELAY_MS: u32 = 5_000; - -type TxPin = Pin; -type RxPin = Pin; -type TxPinDefault = Pin; -type RxPinDefault = Pin; -type EnabledUart = UartPeripheral; - -/// Initialise system clocks and PLLs from the external 12 MHz crystal. -/// -/// # Arguments -/// -/// * `xosc` - XOSC peripheral singleton. -/// * `clocks` - CLOCKS peripheral singleton. -/// * `pll_sys` - PLL_SYS peripheral singleton. -/// * `pll_usb` - PLL_USB peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `watchdog` - Mutable reference to the watchdog timer. -/// -/// # Returns -/// -/// Configured clocks manager. -/// -/// # Panics -/// -/// Panics if clock initialisation fails. -fn init_clocks( - xosc: hal::pac::XOSC, - clocks: hal::pac::CLOCKS, - pll_sys: hal::pac::PLL_SYS, - pll_usb: hal::pac::PLL_USB, - resets: &mut hal::pac::RESETS, - watchdog: &mut hal::Watchdog, -) -> hal::clocks::ClocksManager { - hal::clocks::init_clocks_and_plls( - XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, - ) - .unwrap() -} - -/// Unlock the GPIO bank and return the pin set. -/// -/// # Arguments -/// -/// * `io_bank0` - IO_BANK0 peripheral singleton. -/// * `pads_bank0` - PADS_BANK0 peripheral singleton. -/// * `sio` - SIO peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// -/// # Returns -/// -/// GPIO pin set for the entire bank. -fn init_pins( - io_bank0: hal::pac::IO_BANK0, - pads_bank0: hal::pac::PADS_BANK0, - sio: hal::pac::SIO, - resets: &mut hal::pac::RESETS, -) -> hal::gpio::Pins { - let sio = hal::Sio::new(sio); - hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) -} - -/// Initialise UART0 for serial output (stdio equivalent). -/// -/// # Arguments -/// -/// * `uart0` - PAC UART0 peripheral singleton. -/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). -/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Enabled UART0 peripheral ready for blocking writes. -/// -/// # Panics -/// -/// Panics if the HAL cannot achieve the requested baud rate. -fn init_uart( - uart0: hal::pac::UART0, - tx_pin: TxPinDefault, - rx_pin: RxPinDefault, - resets: &mut hal::pac::RESETS, - clocks: &hal::clocks::ClocksManager, -) -> EnabledUart { - let pins = ( - tx_pin.reconfigure::(), - rx_pin.reconfigure::(), - ); - let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); - UartPeripheral::new(uart0, pins, resets) - .enable(cfg, clocks.peripheral_clock.freq()) - .unwrap() -} - -/// Create a blocking delay timer from the ARM SysTick peripheral. -/// -/// # Arguments -/// -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Blocking delay provider. -/// -/// # Panics -/// -/// Panics if the cortex-m core peripherals have already been taken. -fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { - let core = cortex_m::Peripherals::take().unwrap(); - cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) -} - -/// Probe a 7-bit I2C address by attempting a 1-byte read. -/// -/// # Arguments -/// -/// * `i2c` - Mutable reference to the I2C bus. -/// * `addr` - 7-bit I2C address to probe. -/// -/// # Returns -/// -/// `true` if a device acknowledged, `false` otherwise. -fn probe_addr(i2c: &mut impl I2c, addr: u8) -> bool { - let mut dummy = [0u8; 1]; - i2c.read(addr, &mut dummy).is_ok() -} - /// Application entry point for the I2C bus scanner demo. /// /// Initializes I2C1 at 100 kHz on SDA=GPIO2 / SCL=GPIO3 and prints @@ -218,17 +84,17 @@ fn probe_addr(i2c: &mut impl I2c, addr: u8) -> bool { #[entry] fn main() -> ! { let mut pac = hal::pac::Peripherals::take().unwrap(); - let clocks = init_clocks( + let clocks = board::init_clocks( pac.XOSC, pac.CLOCKS, pac.PLL_SYS, pac.PLL_USB, &mut pac.RESETS, &mut hal::Watchdog::new(pac.WATCHDOG), ); - let pins = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); - let uart = init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); - let mut delay = init_delay(&clocks); + let pins = board::init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = board::init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); + let mut delay = board::init_delay(&clocks); let sda_pin = pins.gpio2.reconfigure::(); let scl_pin = pins.gpio3.reconfigure::(); let mut i2c = hal::I2C::i2c1( - pac.I2C1, sda_pin, scl_pin, I2C_BAUD.Hz(), + pac.I2C1, sda_pin, scl_pin, board::I2C_BAUD.Hz(), &mut pac.RESETS, clocks.system_clock.freq(), ); uart.write_full_blocking(b"I2C driver initialized: I2C1 @ 100000 Hz SDA=GPIO2 SCL=GPIO3\r\n"); @@ -237,11 +103,11 @@ fn main() -> ! { let n = i2c::format_scan_header(&mut buf); uart.write_full_blocking(&buf[..n]); for addr in 0u8..128 { - let found = !i2c::is_reserved(addr) && probe_addr(&mut i2c, addr); + let found = !i2c::is_reserved(addr) && board::probe_addr(&mut i2c, addr); let n = i2c::format_scan_entry(&mut buf, addr, found); uart.write_full_blocking(&buf[..n]); } - delay.delay_ms(SCAN_DELAY_MS); + delay.delay_ms(board::SCAN_DELAY_MS); } } diff --git a/drivers/0x08_lcd1602_rust/src/board.rs b/drivers/0x08_lcd1602_rust/src/board.rs new file mode 100644 index 0000000..c6f4400 --- /dev/null +++ b/drivers/0x08_lcd1602_rust/src/board.rs @@ -0,0 +1,339 @@ +//! @file board.rs +//! @brief Board-level initialisation and LCD hardware helpers +//! @author Kevin Thomas +//! @date 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + +use embedded_hal::i2c::I2c; +use fugit::RateExtU32; +use hal::Clock; +use hal::gpio::{FunctionNull, FunctionUart, Pin, PullDown, PullNone}; +use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; + +#[cfg(rp2350)] +use rp235x_hal as hal; + +#[cfg(rp2040)] +use rp2040_hal as hal; + +/// External crystal frequency in Hz (12 MHz). +pub(crate) const XTAL_FREQ_HZ: u32 = 12_000_000u32; + +/// UART baud rate in bits per second. +pub(crate) const UART_BAUD: u32 = 115_200; + +/// I2C bus speed in Hz (100 kHz standard mode). +pub(crate) const I2C_BAUD: u32 = 100_000; + +/// 7-bit I2C address of the PCF8574 LCD backpack. +pub(crate) const LCD_I2C_ADDR: u8 = 0x27; + +/// Number of bit positions to shift a 4-bit nibble. +pub(crate) const NIBBLE_SHIFT: u8 = 4; + +/// PCF8574 backlight enable mask. +pub(crate) const BACKLIGHT_MASK: u8 = 0x08; + +/// Delay between counter updates in milliseconds. +pub(crate) const COUNTER_DELAY_MS: u32 = 1_000; + +/// Type alias for the configured TX pin (GPIO 0, UART function, no pull). +pub(crate) type TxPin = Pin; + +/// Type alias for the configured RX pin (GPIO 1, UART function, no pull). +pub(crate) type RxPin = Pin; + +/// Type alias for the default TX pin state from `Pins::new()`. +pub(crate) type TxPinDefault = Pin; + +/// Type alias for the default RX pin state from `Pins::new()`. +pub(crate) type RxPinDefault = Pin; + +/// Type alias for the fully-enabled UART0 peripheral with TX/RX pins. +pub(crate) type EnabledUart = UartPeripheral; + +/// Initialise system clocks and PLLs from the external 12 MHz crystal. +/// +/// # Arguments +/// +/// * `xosc` - XOSC peripheral singleton. +/// * `clocks` - CLOCKS peripheral singleton. +/// * `pll_sys` - PLL_SYS peripheral singleton. +/// * `pll_usb` - PLL_USB peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `watchdog` - Mutable reference to the watchdog timer. +/// +/// # Returns +/// +/// Configured clocks manager. +/// +/// # Panics +/// +/// Panics if clock initialisation fails. +pub(crate) fn init_clocks( + xosc: hal::pac::XOSC, + clocks: hal::pac::CLOCKS, + pll_sys: hal::pac::PLL_SYS, + pll_usb: hal::pac::PLL_USB, + resets: &mut hal::pac::RESETS, + watchdog: &mut hal::Watchdog, +) -> hal::clocks::ClocksManager { + hal::clocks::init_clocks_and_plls( + XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, + ) + .unwrap() +} + +/// Unlock the GPIO bank and return the pin set. +/// +/// # Arguments +/// +/// * `io_bank0` - IO_BANK0 peripheral singleton. +/// * `pads_bank0` - PADS_BANK0 peripheral singleton. +/// * `sio` - SIO peripheral singleton. +/// * `resets` - Mutable reference to the RESETS peripheral. +/// +/// # Returns +/// +/// GPIO pin set for the entire bank. +pub(crate) fn init_pins( + io_bank0: hal::pac::IO_BANK0, + pads_bank0: hal::pac::PADS_BANK0, + sio: hal::pac::SIO, + resets: &mut hal::pac::RESETS, +) -> hal::gpio::Pins { + let sio = hal::Sio::new(sio); + hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) +} + +/// Initialise UART0 for serial output (stdio equivalent). +/// +/// # Arguments +/// +/// * `uart0` - PAC UART0 peripheral singleton. +/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). +/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). +/// * `resets` - Mutable reference to the RESETS peripheral. +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Enabled UART0 peripheral ready for blocking writes. +/// +/// # Panics +/// +/// Panics if the HAL cannot achieve the requested baud rate. +pub(crate) fn init_uart( + uart0: hal::pac::UART0, + tx_pin: TxPinDefault, + rx_pin: RxPinDefault, + resets: &mut hal::pac::RESETS, + clocks: &hal::clocks::ClocksManager, +) -> EnabledUart { + let pins = ( + tx_pin.reconfigure::(), + rx_pin.reconfigure::(), + ); + let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); + UartPeripheral::new(uart0, pins, resets) + .enable(cfg, clocks.peripheral_clock.freq()) + .unwrap() +} + +/// Create a blocking delay timer from the ARM SysTick peripheral. +/// +/// # Arguments +/// +/// * `clocks` - Reference to the initialised clock configuration. +/// +/// # Returns +/// +/// Blocking delay provider. +/// +/// # Panics +/// +/// Panics if the cortex-m core peripherals have already been taken. +pub(crate) fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { + let core = cortex_m::Peripherals::take().unwrap(); + cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) +} + +/// Write one raw byte to the PCF8574 expander over I2C. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `data` - Byte to write. +fn pcf_write_byte(i2c: &mut impl I2c, addr: u8, data: u8) { + let _ = i2c.write(addr, &[data]); +} + +/// Toggle EN to latch a nibble into the LCD controller. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `data` - Nibble byte without EN asserted. +/// * `delay` - Delay provider for timing. +fn pcf_pulse_enable(i2c: &mut impl I2c, addr: u8, data: u8, delay: &mut cortex_m::delay::Delay) { + pcf_write_byte(i2c, addr, crate::lcd1602::nibble_with_en(data)); + delay.delay_us(1); + pcf_write_byte(i2c, addr, crate::lcd1602::nibble_without_en(data)); + delay.delay_us(50); +} + +/// Write one 4-bit nibble to the LCD. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `nibble` - 4-bit value to send. +/// * `mode` - Register select: 0 for command, 1 for data. +/// * `delay` - Delay provider for timing. +fn lcd_write4(i2c: &mut impl I2c, addr: u8, nibble: u8, mode: u8, delay: &mut cortex_m::delay::Delay) { + let data = crate::lcd1602::build_nibble(nibble, NIBBLE_SHIFT, mode, BACKLIGHT_MASK); + pcf_pulse_enable(i2c, addr, data, delay); +} + +/// Send one full 8-bit command/data value as two nibbles. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `value` - 8-bit value to send. +/// * `mode` - Register select: 0 for command, 1 for data. +/// * `delay` - Delay provider for timing. +fn lcd_send(i2c: &mut impl I2c, addr: u8, value: u8, mode: u8, delay: &mut cortex_m::delay::Delay) { + lcd_write4(i2c, addr, (value >> 4) & 0x0F, mode, delay); + lcd_write4(i2c, addr, value & 0x0F, mode, delay); +} + +/// Execute the HD44780 4-bit mode power-on reset sequence. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `delay` - Delay provider for timing. +fn lcd_hd44780_reset(i2c: &mut impl I2c, addr: u8, delay: &mut cortex_m::delay::Delay) { + lcd_write4(i2c, addr, 0x03, 0, delay); + delay.delay_ms(5); + lcd_write4(i2c, addr, 0x03, 0, delay); + delay.delay_us(150); + lcd_write4(i2c, addr, 0x03, 0, delay); + delay.delay_us(150); + lcd_write4(i2c, addr, 0x02, 0, delay); + delay.delay_us(150); +} + +/// Send post-reset configuration commands to the HD44780. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `delay` - Delay provider for timing. +fn lcd_hd44780_configure(i2c: &mut impl I2c, addr: u8, delay: &mut cortex_m::delay::Delay) { + lcd_send(i2c, addr, 0x28, 0, delay); + lcd_send(i2c, addr, 0x0C, 0, delay); + lcd_send(i2c, addr, 0x01, 0, delay); + delay.delay_ms(2); + lcd_send(i2c, addr, 0x06, 0, delay); +} + +/// Set the LCD cursor position. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `line` - Display row (0 or 1). +/// * `position` - Column offset. +/// * `delay` - Delay provider for timing. +fn lcd_set_cursor(i2c: &mut impl I2c, addr: u8, line: u8, position: u8, delay: &mut cortex_m::delay::Delay) { + lcd_send(i2c, addr, crate::lcd1602::cursor_address(line, position), 0, delay); +} + +/// Write a byte slice as character data to the LCD. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `addr` - 7-bit I2C address of the PCF8574. +/// * `s` - Byte slice of ASCII characters to display. +/// * `delay` - Delay provider for timing. +fn lcd_puts(i2c: &mut impl I2c, addr: u8, s: &[u8], delay: &mut cortex_m::delay::Delay) { + for &ch in s { + lcd_send(i2c, addr, ch, 1, delay); + } +} + +/// Initialize the LCD, display the title, and log over UART. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `uart` - UART peripheral for serial log output. +/// * `delay` - Delay provider for timing. +pub(crate) fn setup_display( + i2c: &mut impl I2c, + uart: &EnabledUart, + delay: &mut cortex_m::delay::Delay, +) { + lcd_hd44780_reset(i2c, LCD_I2C_ADDR, delay); + lcd_hd44780_configure(i2c, LCD_I2C_ADDR, delay); + lcd_set_cursor(i2c, LCD_I2C_ADDR, 0, 0, delay); + lcd_puts(i2c, LCD_I2C_ADDR, b"Reverse Eng.", delay); + uart.write_full_blocking(b"LCD 1602 driver initialized at I2C addr 0x27\r\n"); +} + +/// Format and display the next counter value on LCD line 1. +/// +/// # Arguments +/// +/// * `i2c` - Mutable reference to the I2C bus. +/// * `uart` - UART peripheral for serial log output. +/// * `delay` - Delay provider for timing. +/// * `count` - Mutable reference to the counter state. +pub(crate) fn update_counter( + i2c: &mut impl I2c, + uart: &EnabledUart, + delay: &mut cortex_m::delay::Delay, + count: &mut u32, +) { + let mut buf = [0u8; 16]; + let n = crate::lcd1602::format_counter(&mut buf, *count); + *count += 1; + lcd_set_cursor(i2c, LCD_I2C_ADDR, 1, 0, delay); + lcd_puts(i2c, LCD_I2C_ADDR, &buf[..n], delay); + uart.write_full_blocking(&buf[..n]); + uart.write_full_blocking(b"\r\n"); + delay.delay_ms(COUNTER_DELAY_MS); +} + +// End of file diff --git a/drivers/0x08_lcd1602_rust/src/lcd1602.rs b/drivers/0x08_lcd1602_rust/src/lcd1602.rs index fc3d114..82569d6 100644 --- a/drivers/0x08_lcd1602_rust/src/lcd1602.rs +++ b/drivers/0x08_lcd1602_rust/src/lcd1602.rs @@ -1,3 +1,30 @@ +//! @file lcd1602.rs +//! @brief Implementation of the HD44780 16x2 LCD (PCF8574 backpack) driver +//! @author Kevin Thomas +//! @date 2025 +//! +//! MIT License +//! +//! Copyright (c) 2025 Kevin Thomas +//! +//! Permission is hereby granted, free of charge, to any person obtaining a copy +//! of this software and associated documentation files (the "Software"), to deal +//! in the Software without restriction, including without limitation the rights +//! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//! copies of the Software, and to permit persons to whom the Software is +//! furnished to do so, subject to the following conditions: +//! +//! The above copyright notice and this permission notice shall be included in +//! all copies or substantial portions of the Software. +//! +//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//! SOFTWARE. + /// PCF8574 -> LCD control pin: Register Select. pub const PIN_RS: u8 = 0x01; @@ -8,6 +35,17 @@ pub const PIN_RW: u8 = 0x02; pub const PIN_EN: u8 = 0x04; /// Build a PCF8574 output byte for a 4-bit LCD nibble. +/// +/// # Arguments +/// +/// * `nibble` - 4-bit data value (0x00..0x0F). +/// * `nibble_shift` - Number of bits to shift the nibble left. +/// * `mode` - Register select mode (0 = command, non-zero = data). +/// * `backlight_mask` - Bitmask to enable the backlight LED. +/// +/// # Returns +/// +/// Assembled PCF8574 output byte. pub fn build_nibble(nibble: u8, nibble_shift: u8, mode: u8, backlight_mask: u8) -> u8 { let mut data = (nibble & 0x0F) << nibble_shift; if mode != 0 { data |= PIN_RS; } @@ -16,11 +54,27 @@ pub fn build_nibble(nibble: u8, nibble_shift: u8, mode: u8, backlight_mask: u8) } /// Build the PCF8574 byte with EN asserted. +/// +/// # Arguments +/// +/// * `nibble_byte` - PCF8574 output byte before EN assertion. +/// +/// # Returns +/// +/// Byte with the EN bit set. pub fn nibble_with_en(nibble_byte: u8) -> u8 { nibble_byte | PIN_EN } /// Build the PCF8574 byte with EN de-asserted. +/// +/// # Arguments +/// +/// * `nibble_byte` - PCF8574 output byte before EN de-assertion. +/// +/// # Returns +/// +/// Byte with the EN bit cleared. pub fn nibble_without_en(nibble_byte: u8) -> u8 { nibble_byte & !PIN_EN } @@ -29,12 +83,30 @@ pub fn nibble_without_en(nibble_byte: u8) -> u8 { const ROW_OFFSETS: [u8; 2] = [0x00, 0x40]; /// Compute the DDRAM address byte for `lcd_set_cursor`. +/// +/// # Arguments +/// +/// * `line` - Display row (0 or 1; values > 1 are clamped to 1). +/// * `position` - Column offset within the row. +/// +/// # Returns +/// +/// HD44780 set-DDRAM-address command byte (0x80 | offset). pub fn cursor_address(line: u8, position: u8) -> u8 { let row = if line > 1 { 1 } else { line as usize }; 0x80 | (position + ROW_OFFSETS[row]) } /// Format a counter value as `"Count: NNNNNN"` (right-justified, 6 digits). +/// +/// # Arguments +/// +/// * `buf` - Mutable byte slice (must be at least 13 bytes). +/// * `count` - Counter value to format (0..999999). +/// +/// # Returns +/// +/// Number of bytes written into the buffer. pub fn format_counter(buf: &mut [u8], count: u32) -> usize { let prefix = b"Count: "; buf[..7].copy_from_slice(prefix); diff --git a/drivers/0x08_lcd1602_rust/src/lib.rs b/drivers/0x08_lcd1602_rust/src/lib.rs index 6f93728..4672ce8 100644 --- a/drivers/0x08_lcd1602_rust/src/lib.rs +++ b/drivers/0x08_lcd1602_rust/src/lib.rs @@ -1,3 +1,8 @@ +//! @file lib.rs +//! @brief Library root for the LCD 1602 driver crate +//! @author Kevin Thomas +//! @date 2025 + #![no_std] pub mod lcd1602; diff --git a/drivers/0x08_lcd1602_rust/src/main.rs b/drivers/0x08_lcd1602_rust/src/main.rs index 4b0586a..347f8fb 100644 --- a/drivers/0x08_lcd1602_rust/src/main.rs +++ b/drivers/0x08_lcd1602_rust/src/main.rs @@ -41,6 +41,7 @@ #![no_std] #![no_main] +mod board; #[allow(dead_code)] mod lcd1602; @@ -50,12 +51,10 @@ use panic_halt as _; #[cfg(target_arch = "arm")] use panic_probe as _; -use embedded_hal::i2c::I2c; use fugit::RateExtU32; -use hal::entry; use hal::Clock; -use hal::gpio::{FunctionI2C, FunctionNull, FunctionUart, Pin, PullDown, PullNone, PullUp}; -use hal::uart::{DataBits, Enabled, StopBits, UartConfig, UartPeripheral}; +use hal::entry; +use hal::gpio::{FunctionI2C, PullUp}; #[cfg(rp2350)] use rp235x_hal as hal; @@ -73,215 +72,6 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; #[cfg(rp2350)] pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe(); -const XTAL_FREQ_HZ: u32 = 12_000_000u32; - -const UART_BAUD: u32 = 115_200; -const I2C_BAUD: u32 = 100_000; -const LCD_I2C_ADDR: u8 = 0x27; -const NIBBLE_SHIFT: u8 = 4; -const BACKLIGHT_MASK: u8 = 0x08; -const COUNTER_DELAY_MS: u32 = 1_000; - -type TxPin = Pin; -type RxPin = Pin; -type TxPinDefault = Pin; -type RxPinDefault = Pin; -type EnabledUart = UartPeripheral; - -/// Initialise system clocks and PLLs from the external 12 MHz crystal. -/// -/// # Arguments -/// -/// * `xosc` - XOSC peripheral singleton. -/// * `clocks` - CLOCKS peripheral singleton. -/// * `pll_sys` - PLL_SYS peripheral singleton. -/// * `pll_usb` - PLL_USB peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `watchdog` - Mutable reference to the watchdog timer. -/// -/// # Returns -/// -/// Configured clocks manager. -/// -/// # Panics -/// -/// Panics if clock initialisation fails. -fn init_clocks( - xosc: hal::pac::XOSC, - clocks: hal::pac::CLOCKS, - pll_sys: hal::pac::PLL_SYS, - pll_usb: hal::pac::PLL_USB, - resets: &mut hal::pac::RESETS, - watchdog: &mut hal::Watchdog, -) -> hal::clocks::ClocksManager { - hal::clocks::init_clocks_and_plls( - XTAL_FREQ_HZ, xosc, clocks, pll_sys, pll_usb, resets, watchdog, - ) - .unwrap() -} - -/// Unlock the GPIO bank and return the pin set. -/// -/// # Arguments -/// -/// * `io_bank0` - IO_BANK0 peripheral singleton. -/// * `pads_bank0` - PADS_BANK0 peripheral singleton. -/// * `sio` - SIO peripheral singleton. -/// * `resets` - Mutable reference to the RESETS peripheral. -/// -/// # Returns -/// -/// GPIO pin set for the entire bank. -fn init_pins( - io_bank0: hal::pac::IO_BANK0, - pads_bank0: hal::pac::PADS_BANK0, - sio: hal::pac::SIO, - resets: &mut hal::pac::RESETS, -) -> hal::gpio::Pins { - let sio = hal::Sio::new(sio); - hal::gpio::Pins::new(io_bank0, pads_bank0, sio.gpio_bank0, resets) -} - -/// Initialise UART0 for serial output (stdio equivalent). -/// -/// # Arguments -/// -/// * `uart0` - PAC UART0 peripheral singleton. -/// * `tx_pin` - GPIO pin to use as UART0 TX (GPIO 0). -/// * `rx_pin` - GPIO pin to use as UART0 RX (GPIO 1). -/// * `resets` - Mutable reference to the RESETS peripheral. -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Enabled UART0 peripheral ready for blocking writes. -/// -/// # Panics -/// -/// Panics if the HAL cannot achieve the requested baud rate. -fn init_uart( - uart0: hal::pac::UART0, - tx_pin: TxPinDefault, - rx_pin: RxPinDefault, - resets: &mut hal::pac::RESETS, - clocks: &hal::clocks::ClocksManager, -) -> EnabledUart { - let pins = ( - tx_pin.reconfigure::(), - rx_pin.reconfigure::(), - ); - let cfg = UartConfig::new(UART_BAUD.Hz(), DataBits::Eight, None, StopBits::One); - UartPeripheral::new(uart0, pins, resets) - .enable(cfg, clocks.peripheral_clock.freq()) - .unwrap() -} - -/// Create a blocking delay timer from the ARM SysTick peripheral. -/// -/// # Arguments -/// -/// * `clocks` - Reference to the initialised clock configuration. -/// -/// # Returns -/// -/// Blocking delay provider. -/// -/// # Panics -/// -/// Panics if the cortex-m core peripherals have already been taken. -fn init_delay(clocks: &hal::clocks::ClocksManager) -> cortex_m::delay::Delay { - let core = cortex_m::Peripherals::take().unwrap(); - cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()) -} - -/// Write one raw byte to the PCF8574 expander over I2C. -fn pcf_write_byte(i2c: &mut impl I2c, addr: u8, data: u8) { - let _ = i2c.write(addr, &[data]); -} - -/// Toggle EN to latch a nibble into the LCD controller. -fn pcf_pulse_enable(i2c: &mut impl I2c, addr: u8, data: u8, delay: &mut cortex_m::delay::Delay) { - pcf_write_byte(i2c, addr, lcd1602::nibble_with_en(data)); - delay.delay_us(1); - pcf_write_byte(i2c, addr, lcd1602::nibble_without_en(data)); - delay.delay_us(50); -} - -/// Write one 4-bit nibble to the LCD. -fn lcd_write4(i2c: &mut impl I2c, addr: u8, nibble: u8, mode: u8, delay: &mut cortex_m::delay::Delay) { - let data = lcd1602::build_nibble(nibble, NIBBLE_SHIFT, mode, BACKLIGHT_MASK); - pcf_pulse_enable(i2c, addr, data, delay); -} - -/// Send one full 8-bit command/data value as two nibbles. -fn lcd_send(i2c: &mut impl I2c, addr: u8, value: u8, mode: u8, delay: &mut cortex_m::delay::Delay) { - lcd_write4(i2c, addr, (value >> 4) & 0x0F, mode, delay); - lcd_write4(i2c, addr, value & 0x0F, mode, delay); -} - -/// Execute the HD44780 4-bit mode power-on reset sequence. -fn lcd_hd44780_reset(i2c: &mut impl I2c, addr: u8, delay: &mut cortex_m::delay::Delay) { - lcd_write4(i2c, addr, 0x03, 0, delay); - delay.delay_ms(5); - lcd_write4(i2c, addr, 0x03, 0, delay); - delay.delay_us(150); - lcd_write4(i2c, addr, 0x03, 0, delay); - delay.delay_us(150); - lcd_write4(i2c, addr, 0x02, 0, delay); - delay.delay_us(150); -} - -/// Send post-reset configuration commands to the HD44780. -fn lcd_hd44780_configure(i2c: &mut impl I2c, addr: u8, delay: &mut cortex_m::delay::Delay) { - lcd_send(i2c, addr, 0x28, 0, delay); - lcd_send(i2c, addr, 0x0C, 0, delay); - lcd_send(i2c, addr, 0x01, 0, delay); - delay.delay_ms(2); - lcd_send(i2c, addr, 0x06, 0, delay); -} - -/// Set the LCD cursor position. -fn lcd_set_cursor(i2c: &mut impl I2c, addr: u8, line: u8, position: u8, delay: &mut cortex_m::delay::Delay) { - lcd_send(i2c, addr, lcd1602::cursor_address(line, position), 0, delay); -} - -/// Write a byte slice as character data to the LCD. -fn lcd_puts(i2c: &mut impl I2c, addr: u8, s: &[u8], delay: &mut cortex_m::delay::Delay) { - for &ch in s { - lcd_send(i2c, addr, ch, 1, delay); - } -} - -/// Initialize the LCD, display the title, and log over UART. -fn setup_display( - i2c: &mut impl I2c, - uart: &EnabledUart, - delay: &mut cortex_m::delay::Delay, -) { - lcd_hd44780_reset(i2c, LCD_I2C_ADDR, delay); - lcd_hd44780_configure(i2c, LCD_I2C_ADDR, delay); - lcd_set_cursor(i2c, LCD_I2C_ADDR, 0, 0, delay); - lcd_puts(i2c, LCD_I2C_ADDR, b"Reverse Eng.", delay); - uart.write_full_blocking(b"LCD 1602 driver initialized at I2C addr 0x27\r\n"); -} - -/// Format and display the next counter value on LCD line 1. -fn update_counter( - i2c: &mut impl I2c, - uart: &EnabledUart, - delay: &mut cortex_m::delay::Delay, - count: &mut u32, -) { - let mut buf = [0u8; 16]; - let n = lcd1602::format_counter(&mut buf, *count); - *count += 1; - lcd_set_cursor(i2c, LCD_I2C_ADDR, 1, 0, delay); - lcd_puts(i2c, LCD_I2C_ADDR, &buf[..n], delay); - uart.write_full_blocking(&buf[..n]); - uart.write_full_blocking(b"\r\n"); - delay.delay_ms(COUNTER_DELAY_MS); -} - /// Application entry point for the LCD 1602 counter demo. /// /// Initializes the LCD over I2C with a static title on line 0 and @@ -293,23 +83,23 @@ fn update_counter( #[entry] fn main() -> ! { let mut pac = hal::pac::Peripherals::take().unwrap(); - let clocks = init_clocks( + let clocks = board::init_clocks( pac.XOSC, pac.CLOCKS, pac.PLL_SYS, pac.PLL_USB, &mut pac.RESETS, &mut hal::Watchdog::new(pac.WATCHDOG), ); - let pins = init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); - let uart = init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); - let mut delay = init_delay(&clocks); + let pins = board::init_pins(pac.IO_BANK0, pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let uart = board::init_uart(pac.UART0, pins.gpio0, pins.gpio1, &mut pac.RESETS, &clocks); + let mut delay = board::init_delay(&clocks); let sda_pin = pins.gpio2.reconfigure::(); let scl_pin = pins.gpio3.reconfigure::(); let mut i2c = hal::I2C::i2c1( - pac.I2C1, sda_pin, scl_pin, I2C_BAUD.Hz(), + pac.I2C1, sda_pin, scl_pin, board::I2C_BAUD.Hz(), &mut pac.RESETS, clocks.system_clock.freq(), ); - setup_display(&mut i2c, &uart, &mut delay); + board::setup_display(&mut i2c, &uart, &mut delay); let mut count: u32 = 0; loop { - update_counter(&mut i2c, &uart, &mut delay, &mut count); + board::update_counter(&mut i2c, &uart, &mut delay, &mut count); } }