From 16bc637f304e34c6ae8ced63865b4b6c44d5b573 Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Fri, 5 Sep 2025 15:11:29 +0300 Subject: [PATCH 01/14] Add uart --- Cargo.toml | 1 + src/lib.rs | 13 +++++++ src/ports/mips64/mod.rs | 23 ++++++++++++ src/ports/mips64/uart.rs | 36 +++++++++++++++++++ src/ports/mod.rs | 66 ++++++++++++++++++++++++++++++++++ src/ports/mok/mod.rs | 23 ++++++++++++ src/ports/mok/uart.rs | 36 +++++++++++++++++++ src/ports/xtensa_esp32/mod.rs | 23 ++++++++++++ src/ports/xtensa_esp32/uart.rs | 61 +++++++++++++++++++++++++++++++ 9 files changed, 282 insertions(+) create mode 100644 src/ports/mips64/uart.rs create mode 100644 src/ports/mok/uart.rs create mode 100644 src/ports/xtensa_esp32/uart.rs diff --git a/Cargo.toml b/Cargo.toml index 106348e1..2e915649 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ default = [] c-library = [] cooperative = [] preemptive = [] +uart = [] network = ["esp-wifi"] mips64_timer_tests = [] cooperative_tests = [] diff --git a/src/lib.rs b/src/lib.rs index 8669aeba..9f844cba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,9 @@ pub fn init_system() { #[cfg(feature = "network")] // Network setup. ports::Port::init_network(); + // Uart setup. + #[cfg(feature = "uart")] + ports::Port::setup_uart(); } #[cfg(any(target_arch = "riscv32", target_arch = "xtensa"))] @@ -27,3 +30,13 @@ pub fn init_system() { pub fn get_esp_now() -> EspNow<'static> { return ports::Port::get_esp_now(); } + +#[cfg(feature = "uart")] +pub fn get_uart2() -> ::Uart2Type { + ports::Port::get_uart2() +} + +#[cfg(feature = "uart")] +pub fn get_io() -> ::IoType { + ports::Port::get_io() +} diff --git a/src/ports/mips64/mod.rs b/src/ports/mips64/mod.rs index 83df6602..3394e9d7 100644 --- a/src/ports/mips64/mod.rs +++ b/src/ports/mips64/mod.rs @@ -3,11 +3,19 @@ pub mod hardware_timer; pub mod memory_manager; #[cfg(feature = "network")] pub mod network; +#[cfg(feature = "uart")] +pub mod uart; use crate::ports::PortTrait; /// PortTrait implementation for Mips64 platform pub struct Mips64; impl PortTrait for Mips64 { + #[cfg(feature = "uart")] + type Uart2Type = uart::Uart2Type; + + #[cfg(feature = "uart")] + type IoType = uart::IoType; + fn init_heap() { #[cfg(not(feature = "mips64_timer_tests"))] memory_manager::init_heap(); @@ -57,4 +65,19 @@ impl PortTrait for Mips64 { fn init_network() { network::init_network(); } + + #[cfg(feature = "uart")] + fn setup_uart() { + uart::setup_uart(); + } + + #[cfg(feature = "uart")] + fn get_uart2() -> Self::Uart2Type { + uart::get_uart2() + } + + #[cfg(feature = "uart")] + fn get_io() -> Self::IoType { + uart::get_io() + } } diff --git a/src/ports/mips64/uart.rs b/src/ports/mips64/uart.rs new file mode 100644 index 00000000..6647761f --- /dev/null +++ b/src/ports/mips64/uart.rs @@ -0,0 +1,36 @@ +//! MIPS64 UART stub implementation + +/// MIPS64 UART2 peripheral stub +#[cfg(feature = "uart")] +pub struct Mips64Uart2; + +/// MIPS64 IO peripheral stub +#[cfg(feature = "uart")] +pub struct Mips64Io; + +/// Initialize UART subsystem (stub implementation) +#[cfg(feature = "uart")] +pub fn setup_uart() { + // MIPS64 UART not implemented yet + // Could implement real MIPS64 UART initialization here in the future +} + +/// Get MIPS64 UART2 peripheral instance +#[cfg(feature = "uart")] +pub fn get_uart2() -> Mips64Uart2 { + Mips64Uart2 +} + +/// Get MIPS64 IO peripheral instance +#[cfg(feature = "uart")] +pub fn get_io() -> Mips64Io { + Mips64Io +} + +/// Type alias for consistency with PortTrait +#[cfg(feature = "uart")] +pub type Uart2Type = Mips64Uart2; + +/// Type alias for consistency with PortTrait +#[cfg(feature = "uart")] +pub type IoType = Mips64Io; diff --git a/src/ports/mod.rs b/src/ports/mod.rs index 2a7f04d7..ab82c7d2 100644 --- a/src/ports/mod.rs +++ b/src/ports/mod.rs @@ -33,6 +33,72 @@ pub trait PortTrait { #[cfg(feature = "network")] /// Function for getting esp-now object for network. fn get_esp_now() -> EspNow<'static>; + #[cfg(feature = "uart")] + type Uart2Type; + #[cfg(feature = "uart")] + type IoType; + /// Initializes the UART subsystem for the current platform. + /// + /// This function performs platform-specific initialization of UART peripherals, + /// including clock setup and basic port configuration. + /// + /// # Safety + /// + /// This function should be called only once during system initialization. + /// Multiple calls may result in undefined behavior. + /// + /// # Availability + /// + /// Available only on ESP32 architectures (xtensa, riscv32) and only when + /// the "uart" feature is enabled. + #[cfg(feature = "uart")] + fn setup_uart(); + /// Returns a UART2 peripheral instance for configuration and use. + /// + /// This function transfers ownership of the UART2 peripheral from the system + /// pool to user code. After calling this function, attempting to retrieve + /// the same instance again will fail until system restart. + /// + /// # Returns + /// + /// Returns `esp_hal::peripherals::UART2` - a UART2 peripheral instance + /// ready for initialization with `esp_hal::uart::Uart::new()`. + /// + /// # Panics + /// + /// This function panics if: + /// - UART2 has already been retrieved previously + /// - The system was not initialized via `setup_uart()` + /// + /// # Availability + /// + /// Available only on ESP32 architectures (xtensa, riscv32) and only when + /// the "uart" feature is enabled. + #[cfg(feature = "uart")] + fn get_uart2() -> UART2; + /// Returns a GPIO/IO peripheral instance for pin configuration. + /// + /// This function transfers ownership of the GPIO peripheral from the system + /// pool to user code for configuring input/output pins. After calling this + /// function, attempting to retrieve the same instance again will fail. + /// + /// # Returns + /// + /// Returns `esp_hal::gpio::Io` - a GPIO peripheral instance that provides + /// access to individual pins through the `pins` field. + /// + /// # Panics + /// + /// This function panics if: + /// - The IO peripheral has already been retrieved previously + /// - The system was not initialized via `setup_uart()` + /// + /// # Availability + /// + /// Available only on ESP32 architectures (xtensa, riscv32) and only when + /// the "uart" feature is enabled. + #[cfg(feature = "uart")] + fn get_io() -> Io; // TODO: split to separate trait? #[cfg(feature = "preemptive")] diff --git a/src/ports/mok/mod.rs b/src/ports/mok/mod.rs index 5ff72b00..ce8d2f21 100644 --- a/src/ports/mok/mod.rs +++ b/src/ports/mok/mod.rs @@ -2,12 +2,20 @@ pub mod hardware_timer; pub mod memory_manager; #[cfg(feature = "network")] pub mod network; +#[cfg(feature = "uart")] +pub mod uart; use crate::ports::PortTrait; /// PortTrait implementation for Mok platform pub struct Mok; impl PortTrait for Mok { + #[cfg(feature = "uart")] + type Uart2Type = uart::Uart2Type; + + #[cfg(feature = "uart")] + type IoType = uart::IoType; + fn init_heap() { memory_manager::init_heap(); } @@ -60,6 +68,21 @@ impl PortTrait for Mok { fn save_ctx(thread_ctx: &mut crate::ports::TrapFrame, isr_ctx: &crate::ports::TrapFrame) {} #[cfg(feature = "preemptive")] fn load_ctx(thread_ctx: &crate::ports::TrapFrame, isr_ctx: &mut crate::ports::TrapFrame) {} + + #[cfg(feature = "uart")] + fn setup_uart() { + uart::setup_uart(); + } + + #[cfg(feature = "uart")] + fn get_uart2() -> Self::Uart2Type { + uart::get_uart2() + } + + #[cfg(feature = "uart")] + fn get_io() -> Self::IoType { + uart::get_io() + } } #[allow(dead_code)] diff --git a/src/ports/mok/uart.rs b/src/ports/mok/uart.rs new file mode 100644 index 00000000..7b66c07a --- /dev/null +++ b/src/ports/mok/uart.rs @@ -0,0 +1,36 @@ +//! Mock UART implementation for testing and simulation + +/// Mock UART2 peripheral for platforms that don't support real UART +#[cfg(feature = "uart")] +pub struct MockUart2; + +/// Mock IO peripheral for platforms that don't support real UART +#[cfg(feature = "uart")] +pub struct MockIo; + +/// Initialize UART subsystem (mock implementation) +#[cfg(feature = "uart")] +pub fn setup_uart() { + // Mock implementation - does nothing + // Could log or simulate UART initialization here +} + +/// Get mock UART2 peripheral instance +#[cfg(feature = "uart")] +pub fn get_uart2() -> MockUart2 { + MockUart2 +} + +/// Get mock IO peripheral instance +#[cfg(feature = "uart")] +pub fn get_io() -> MockIo { + MockIo +} + +/// Type alias for consistency with PortTrait +#[cfg(feature = "uart")] +pub type Uart2Type = MockUart2; + +/// Type alias for consistency with PortTrait +#[cfg(feature = "uart")] +pub type IoType = MockIo; diff --git a/src/ports/xtensa_esp32/mod.rs b/src/ports/xtensa_esp32/mod.rs index afaef044..d752093f 100644 --- a/src/ports/xtensa_esp32/mod.rs +++ b/src/ports/xtensa_esp32/mod.rs @@ -8,11 +8,19 @@ mod preempt; use crate::ports::PortTrait; #[cfg(feature = "network")] use esp_wifi::esp_now::EspNow; +#[cfg(feature = "uart")] +pub mod uart; // TODO: make it port just for esp32, not only for XtensaEsp32 /// PortTrait implementation for XtensaEsp32 platform pub struct XtensaEsp32; impl PortTrait for XtensaEsp32 { + #[cfg(feature = "uart")] + type Uart2Type = uart::Uart2Type; + + #[cfg(feature = "uart")] + type IoType = uart::IoType; + fn setup_hardware_timer() { hardware_timer::setup_hardware_timer(); } @@ -79,6 +87,21 @@ impl PortTrait for XtensaEsp32 { fn load_ctx(thread_ctx: &TrapFrame, isr_ctx: &mut TrapFrame) { preempt::load_ctx(thread_ctx, isr_ctx) } + + #[cfg(feature = "uart")] + fn setup_uart() { + uart::setup_uart(); + } + + #[cfg(feature = "uart")] + fn get_uart2() -> Self::Uart2Type { + uart::get_uart2() + } + + #[cfg(feature = "uart")] + fn get_io() -> Self::IoType { + uart::get_io() + } } #[cfg(feature = "preemptive")] diff --git a/src/ports/xtensa_esp32/uart.rs b/src/ports/xtensa_esp32/uart.rs new file mode 100644 index 00000000..4fa99277 --- /dev/null +++ b/src/ports/xtensa_esp32/uart.rs @@ -0,0 +1,61 @@ +//! UART module for ESP32 - handles UART2 and IO peripherals + +use core::sync::atomic::{AtomicBool, Ordering}; +use esp_hal::{gpio::*, peripherals::*}; + +// Static variables for UART peripherals +pub static mut PERIFERALS_UART2: Option = None; +pub static mut IO: Option = None; + +// Flag to ensure UART is initialized only once +static UART_INITIALIZED: AtomicBool = AtomicBool::new(false); + +/// ESP32 UART2 type alias +pub type Uart2Type = UART2; + +/// ESP32 IO type alias +pub type IoType = Io; + +/// Initialize UART subsystem for ESP32 +/// +/// This function initializes the UART2 and IO peripherals separately from +/// the hardware timer initialization. It calls esp_hal::init to get fresh +/// peripheral instances. +pub fn setup_uart() { + if UART_INITIALIZED + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_ok() + { + // Initialize ESP-HAL to get peripheral instances + let peripherals = esp_hal::init(esp_hal::Config::default()); + + unsafe { + PERIFERALS_UART2 = Some(peripherals.UART2); + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + IO = Some(io); + } + } +} + +/// Get UART2 peripheral instance +/// +/// Returns the UART2 peripheral for configuration. Can only be called once. +pub fn get_uart2() -> UART2 { + unsafe { + PERIFERALS_UART2.take().expect("UART2 not available - call setup_uart first") + } +} + +/// Get IO peripheral instance +/// +/// Returns the IO peripheral for pin configuration. Can only be called once. +pub fn get_io() -> Io { + unsafe { + IO.take().expect("IO not available - call setup_uart first") + } +} + +/// Check if UART is initialized +pub fn is_uart_initialized() -> bool { + UART_INITIALIZED.load(Ordering::Acquire) +} From 5b3b7d9c74af5d8ffcbeaaec02f1f880b94b980b Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Fri, 5 Sep 2025 15:16:37 +0300 Subject: [PATCH 02/14] fmt --- src/ports/mips64/mod.rs | 2 +- src/ports/mod.rs | 38 +++++++++++++++++----------------- src/ports/mok/mod.rs | 2 +- src/ports/xtensa_esp32/mod.rs | 2 +- src/ports/xtensa_esp32/uart.rs | 16 +++++++------- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/ports/mips64/mod.rs b/src/ports/mips64/mod.rs index 3394e9d7..db44183e 100644 --- a/src/ports/mips64/mod.rs +++ b/src/ports/mips64/mod.rs @@ -12,7 +12,7 @@ pub struct Mips64; impl PortTrait for Mips64 { #[cfg(feature = "uart")] type Uart2Type = uart::Uart2Type; - + #[cfg(feature = "uart")] type IoType = uart::IoType; diff --git a/src/ports/mod.rs b/src/ports/mod.rs index ab82c7d2..1e347729 100644 --- a/src/ports/mod.rs +++ b/src/ports/mod.rs @@ -38,63 +38,63 @@ pub trait PortTrait { #[cfg(feature = "uart")] type IoType; /// Initializes the UART subsystem for the current platform. - /// + /// /// This function performs platform-specific initialization of UART peripherals, /// including clock setup and basic port configuration. - /// + /// /// # Safety - /// + /// /// This function should be called only once during system initialization. /// Multiple calls may result in undefined behavior. - /// + /// /// # Availability - /// + /// /// Available only on ESP32 architectures (xtensa, riscv32) and only when /// the "uart" feature is enabled. #[cfg(feature = "uart")] fn setup_uart(); /// Returns a UART2 peripheral instance for configuration and use. - /// + /// /// This function transfers ownership of the UART2 peripheral from the system /// pool to user code. After calling this function, attempting to retrieve /// the same instance again will fail until system restart. - /// + /// /// # Returns - /// + /// /// Returns `esp_hal::peripherals::UART2` - a UART2 peripheral instance /// ready for initialization with `esp_hal::uart::Uart::new()`. - /// + /// /// # Panics - /// + /// /// This function panics if: /// - UART2 has already been retrieved previously /// - The system was not initialized via `setup_uart()` - /// + /// /// # Availability - /// + /// /// Available only on ESP32 architectures (xtensa, riscv32) and only when /// the "uart" feature is enabled. #[cfg(feature = "uart")] fn get_uart2() -> UART2; /// Returns a GPIO/IO peripheral instance for pin configuration. - /// + /// /// This function transfers ownership of the GPIO peripheral from the system /// pool to user code for configuring input/output pins. After calling this /// function, attempting to retrieve the same instance again will fail. - /// + /// /// # Returns - /// + /// /// Returns `esp_hal::gpio::Io` - a GPIO peripheral instance that provides /// access to individual pins through the `pins` field. - /// + /// /// # Panics - /// + /// /// This function panics if: /// - The IO peripheral has already been retrieved previously /// - The system was not initialized via `setup_uart()` - /// + /// /// # Availability - /// + /// /// Available only on ESP32 architectures (xtensa, riscv32) and only when /// the "uart" feature is enabled. #[cfg(feature = "uart")] diff --git a/src/ports/mok/mod.rs b/src/ports/mok/mod.rs index ce8d2f21..a3351526 100644 --- a/src/ports/mok/mod.rs +++ b/src/ports/mok/mod.rs @@ -12,7 +12,7 @@ pub struct Mok; impl PortTrait for Mok { #[cfg(feature = "uart")] type Uart2Type = uart::Uart2Type; - + #[cfg(feature = "uart")] type IoType = uart::IoType; diff --git a/src/ports/xtensa_esp32/mod.rs b/src/ports/xtensa_esp32/mod.rs index d752093f..0591149f 100644 --- a/src/ports/xtensa_esp32/mod.rs +++ b/src/ports/xtensa_esp32/mod.rs @@ -17,7 +17,7 @@ pub struct XtensaEsp32; impl PortTrait for XtensaEsp32 { #[cfg(feature = "uart")] type Uart2Type = uart::Uart2Type; - + #[cfg(feature = "uart")] type IoType = uart::IoType; diff --git a/src/ports/xtensa_esp32/uart.rs b/src/ports/xtensa_esp32/uart.rs index 4fa99277..a9ecab1c 100644 --- a/src/ports/xtensa_esp32/uart.rs +++ b/src/ports/xtensa_esp32/uart.rs @@ -17,7 +17,7 @@ pub type Uart2Type = UART2; pub type IoType = Io; /// Initialize UART subsystem for ESP32 -/// +/// /// This function initializes the UART2 and IO peripherals separately from /// the hardware timer initialization. It calls esp_hal::init to get fresh /// peripheral instances. @@ -28,7 +28,7 @@ pub fn setup_uart() { { // Initialize ESP-HAL to get peripheral instances let peripherals = esp_hal::init(esp_hal::Config::default()); - + unsafe { PERIFERALS_UART2 = Some(peripherals.UART2); let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); @@ -38,21 +38,21 @@ pub fn setup_uart() { } /// Get UART2 peripheral instance -/// +/// /// Returns the UART2 peripheral for configuration. Can only be called once. pub fn get_uart2() -> UART2 { unsafe { - PERIFERALS_UART2.take().expect("UART2 not available - call setup_uart first") + PERIFERALS_UART2 + .take() + .expect("UART2 not available - call setup_uart first") } } /// Get IO peripheral instance -/// +/// /// Returns the IO peripheral for pin configuration. Can only be called once. pub fn get_io() -> Io { - unsafe { - IO.take().expect("IO not available - call setup_uart first") - } + unsafe { IO.take().expect("IO not available - call setup_uart first") } } /// Check if UART is initialized From 099a15bbadd5404564070507cde9a409ba9e2538 Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Fri, 5 Sep 2025 15:25:14 +0300 Subject: [PATCH 03/14] Add example with uart --- .../xtensa-esp32/uart/.cargo/config.toml | 14 +++ .../xtensa-esp32/uart/Cargo.toml | 17 ++++ .../rust-examples/xtensa-esp32/uart/README.md | 37 +++++++ .../xtensa-esp32/uart/rust-toolchain.toml | 2 + .../xtensa-esp32/uart/src/main.rs | 99 +++++++++++++++++++ 5 files changed, 169 insertions(+) create mode 100644 examples/rust-examples/xtensa-esp32/uart/.cargo/config.toml create mode 100644 examples/rust-examples/xtensa-esp32/uart/Cargo.toml create mode 100644 examples/rust-examples/xtensa-esp32/uart/README.md create mode 100644 examples/rust-examples/xtensa-esp32/uart/rust-toolchain.toml create mode 100644 examples/rust-examples/xtensa-esp32/uart/src/main.rs diff --git a/examples/rust-examples/xtensa-esp32/uart/.cargo/config.toml b/examples/rust-examples/xtensa-esp32/uart/.cargo/config.toml new file mode 100644 index 00000000..992f3460 --- /dev/null +++ b/examples/rust-examples/xtensa-esp32/uart/.cargo/config.toml @@ -0,0 +1,14 @@ +[build] +rustflags = [ + "-C", "link-arg=-Tlinkall.x", + + "-C", "link-arg=-nostartfiles", +] + +target = "xtensa-esp32-none-elf" + +[unstable] +build-std = ["core", "alloc"] + +[target.'cfg(any(target_arch = "riscv32", target_arch = "xtensa"))'] +runner = "espflash flash --monitor" diff --git a/examples/rust-examples/xtensa-esp32/uart/Cargo.toml b/examples/rust-examples/xtensa-esp32/uart/Cargo.toml new file mode 100644 index 00000000..db8ed8a9 --- /dev/null +++ b/examples/rust-examples/xtensa-esp32/uart/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "example_xtensa_esp32" +version = "0.4.0" +edition = "2021" + +[profile.release] +debug = true + +[dependencies] +# Specifying current Martos version path for ci +martos = { path = "../../../../", features = ["uart"] } +esp-hal = "0.21.1" +esp-backtrace = { version = "0.14.1", features = ["esp32", "panic-handler", "exception-handler", "println"] } +esp-println = { version = "0.11.0", features = ["esp32"] } + +[features] +default = ["esp-hal/esp32", "esp-backtrace/esp32", "esp-println/esp32"] diff --git a/examples/rust-examples/xtensa-esp32/uart/README.md b/examples/rust-examples/xtensa-esp32/uart/README.md new file mode 100644 index 00000000..c9c026d4 --- /dev/null +++ b/examples/rust-examples/xtensa-esp32/uart/README.md @@ -0,0 +1,37 @@ +# Rust example for xtensa esp32 architecture + +Presented here is a straightforward UART echo Rust example utilizing Martos with UART functionality. + +Within the setup function, the UART interface is initialized once on GPIO pins 16 (RX) and 17 (TX) with 19200 baud rate, 8 data bits, no parity, and 1 stop bit. +Additionally, within the loop function, incoming UART data is continuously read byte-by-byte and immediately echoed back to the sender, with each processed byte being logged along with a running counter for debugging purposes. + +## How to install dependencies + +For comprehensive guidance on installing the necessary dependencies for developing applications targeting the Xtensa ESP32 architecture, +please refer to [the official website](https://docs.esp-rs.org/book/installation/riscv-and-xtensa.html). +Below is an illustrative example demonstrating the installation of building toolchains on a Linux (Ubuntu/Debian): +``` +apt-get -qq update +apt-get install -y -q build-essential curl +curl https://sh.rustup.rs -sSf | sh -s -- -y +cargo install espup +espup install +``` + +## How to build the example + +For a thorough guide on developing projects for the Xtensa ESP32 architecture across various operating systems, +we recommend consulting [the official website](https://docs.esp-rs.org/book/installation/riscv-and-xtensa.html#3-set-up-the-environment-variables). +Below, you will find an illustrative example showcasing the building process on a Linux system (Ubuntu/Debian): +``` +. $HOME/export-esp.sh +cargo build +``` + +## How to run the example +For detailed instructions on running projects for the Xtensa ESP32 architecture across various operating systems, +we recommend consulting [the official website](https://docs.esp-rs.org/book/tooling/espflash.html). +Below, you will find an illustrative example showcasing the running on a Linux system (Ubuntu/Debian): +``` +cargo run +``` diff --git a/examples/rust-examples/xtensa-esp32/uart/rust-toolchain.toml b/examples/rust-examples/xtensa-esp32/uart/rust-toolchain.toml new file mode 100644 index 00000000..a2f5ab50 --- /dev/null +++ b/examples/rust-examples/xtensa-esp32/uart/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "esp" diff --git a/examples/rust-examples/xtensa-esp32/uart/src/main.rs b/examples/rust-examples/xtensa-esp32/uart/src/main.rs new file mode 100644 index 00000000..cd0667dc --- /dev/null +++ b/examples/rust-examples/xtensa-esp32/uart/src/main.rs @@ -0,0 +1,99 @@ +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; +use esp_backtrace as _; +use esp_hal::{entry, uart::Uart}; +use esp_println::println; +use martos::{ + get_uart2, get_io, init_system, + task_manager::{TaskManager, TaskManagerTrait}, +}; +use esp_hal::uart::config::{Config, DataBits, StopBits}; + +/// Counter to track processed bytes +static BYTE_COUNTER: AtomicU32 = AtomicU32::new(0); + +/// UART instance (initialized in setup) +static mut UART_INSTANCE: Option> = None; + +/// Setup function for task to execute. +fn setup_fn() { + println!("UART Echo Setup started"); + + // Get UART2 and IO from Martos + let uart2 = get_uart2(); + let io = get_io(); + + // Configure UART pins + let tx_pin = io.pins.gpio17.into_push_pull_output(); + let rx_pin = io.pins.gpio16.into_pull_up_input(); + + // UART configuration: 19200 baud, 8N1 + let config = Config::default() + .baudrate(19200) + .data_bits(DataBits::DataBits8) + .parity_none() + .stop_bits(StopBits::STOP1); + + // Initialize UART + match Uart::new_with_config(uart2, config, rx_pin, tx_pin) { + Ok(uart) => { + unsafe { + UART_INSTANCE = Some(uart); + } + println!("UART Echo ready on GPIO16(RX)/GPIO17(TX) at 19200 baud"); + } + Err(_) => { + println!("Failed to initialize UART!"); + } + } +} + +/// Loop function for task to execute. +fn loop_fn() { + unsafe { + if let Some(ref mut uart) = UART_INSTANCE { + let mut buffer = [0u8; 1]; + + // Try to read a byte + if uart.read(&mut buffer).is_ok() { + let byte = buffer[0]; + let count = BYTE_COUNTER.fetch_add(1, Ordering::Relaxed) + 1; + + println!("Received byte #{}: 0x{:02X} ('{}') - echoing back", + count, + byte, + if byte.is_ascii_graphic() || byte == b' ' { + byte as char + } else { + '.' + } + ); + + // Echo the byte back + if uart.write(&buffer).is_err() { + println!("Failed to echo byte!"); + } + } + } + } +} + +/// Stop condition function for task to execute. +fn stop_condition_fn() -> bool { + // Never stop - run forever + false +} + +#[entry] +fn main() -> ! { + // Initialize Martos (including UART) + init_system(); + + // Add task to execute + TaskManager::add_task(setup_fn, loop_fn, stop_condition_fn); + + // Start task manager + TaskManager::start_task_manager(); +} From c6c5a80f0a6e0dee463de826697763639bfbc90e Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Fri, 5 Sep 2025 15:25:23 +0300 Subject: [PATCH 04/14] Ci --- .github/workflows/rust.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6117e453..71972186 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -130,6 +130,21 @@ jobs: - name: Fmt run: cd ./examples/rust-examples/xtensa-esp32/scheduler/cooperative && cargo fmt --all -- --check + xtensa-esp32-rust-example-uart: + runs-on: ubuntu-latest + env: + CARGO_HOME: /root/.cargo + RUSTUP_HOME: /root/.rustup + container: + image: arkhipovivan1/xtensa-esp32-rust:latest + options: --user root + steps: + - uses: actions/checkout@v3 + - name: Build + run: cd ./examples/rust-examples/xtensa-esp32/uart && . /root/export-esp.sh && cargo build + - name: Fmt + run: cd ./examples/rust-examples/xtensa-esp32/uart && cargo fmt --all -- --check + xtensa-esp32-static-library: runs-on: ubuntu-latest env: From 0e0db663aa80a8778f412672a2face63585ed088 Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Fri, 5 Sep 2025 15:31:38 +0300 Subject: [PATCH 05/14] Use CurrentPort --- src/lib.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9f844cba..a878e6bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,32 +11,41 @@ pub mod timer; #[cfg(feature = "network")] use esp_wifi::esp_now::EspNow; +#[cfg(target_arch = "xtensa")] +use crate::ports::xtensa_esp32::XtensaEsp32Port as CurrentPort; + +#[cfg(target_arch = "mips")] +use crate::ports::mips64::Mips64 as CurrentPort; + +#[cfg(not(any(target_arch = "xtensa", target_arch = "mips")))] +use crate::ports::mok::Mok as CurrentPort; + /// Martos initialization. Should be called before using Martos functions. pub fn init_system() { // Memory initialization. - ports::Port::init_heap(); + CurrentPort::init_heap(); // Hardware timer setup. - ports::Port::setup_hardware_timer(); + CurrentPort::setup_hardware_timer(); #[cfg(feature = "network")] // Network setup. - ports::Port::init_network(); + CurrentPort::init_network(); // Uart setup. #[cfg(feature = "uart")] - ports::Port::setup_uart(); + CurrentPort::setup_uart(); } #[cfg(any(target_arch = "riscv32", target_arch = "xtensa"))] #[cfg(feature = "network")] pub fn get_esp_now() -> EspNow<'static> { - return ports::Port::get_esp_now(); + return CurrentPort::get_esp_now(); } #[cfg(feature = "uart")] pub fn get_uart2() -> ::Uart2Type { - ports::Port::get_uart2() + CurrentPort::get_uart2() } #[cfg(feature = "uart")] pub fn get_io() -> ::IoType { - ports::Port::get_io() + CurrentPort::get_io() } From 8cd85e11ebfff8ec91fceec9f3490759f40fab60 Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Fri, 5 Sep 2025 15:40:47 +0300 Subject: [PATCH 06/14] Maybe fix? --- src/lib.rs | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a878e6bc..1973c256 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,41 +11,32 @@ pub mod timer; #[cfg(feature = "network")] use esp_wifi::esp_now::EspNow; -#[cfg(target_arch = "xtensa")] -use crate::ports::xtensa_esp32::XtensaEsp32Port as CurrentPort; - -#[cfg(target_arch = "mips")] -use crate::ports::mips64::Mips64 as CurrentPort; - -#[cfg(not(any(target_arch = "xtensa", target_arch = "mips")))] -use crate::ports::mok::Mok as CurrentPort; - /// Martos initialization. Should be called before using Martos functions. pub fn init_system() { // Memory initialization. - CurrentPort::init_heap(); + ports::Port::init_heap(); // Hardware timer setup. - CurrentPort::setup_hardware_timer(); + ports::Port::setup_hardware_timer(); #[cfg(feature = "network")] // Network setup. - CurrentPort::init_network(); - // Uart setup. + ports::Port::init_network(); + // Uart #[cfg(feature = "uart")] - CurrentPort::setup_uart(); + ports::Port::setup_uart(); } #[cfg(any(target_arch = "riscv32", target_arch = "xtensa"))] #[cfg(feature = "network")] pub fn get_esp_now() -> EspNow<'static> { - return CurrentPort::get_esp_now(); + return ports::Port::get_esp_now(); } #[cfg(feature = "uart")] -pub fn get_uart2() -> ::Uart2Type { - CurrentPort::get_uart2() +pub fn get_uart2() -> ports::Port::Uart2Type { + ports::Port::get_uart2() } #[cfg(feature = "uart")] -pub fn get_io() -> ::IoType { - CurrentPort::get_io() +pub fn get_io() -> ports::Port::IoType { + ports::Port::get_io() } From bb41a2c8cd42943f4b82265ed5a277befc5223d3 Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Fri, 5 Sep 2025 15:46:00 +0300 Subject: [PATCH 07/14] Fix? --- src/lib.rs | 4 ++-- src/ports/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1973c256..0195b27a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,11 +32,11 @@ pub fn get_esp_now() -> EspNow<'static> { } #[cfg(feature = "uart")] -pub fn get_uart2() -> ports::Port::Uart2Type { +pub fn get_uart2() -> ::Uart2Type { ports::Port::get_uart2() } #[cfg(feature = "uart")] -pub fn get_io() -> ports::Port::IoType { +pub fn get_io() -> ::IoType { ports::Port::get_io() } diff --git a/src/ports/mod.rs b/src/ports/mod.rs index 1e347729..70c283e6 100644 --- a/src/ports/mod.rs +++ b/src/ports/mod.rs @@ -75,7 +75,7 @@ pub trait PortTrait { /// Available only on ESP32 architectures (xtensa, riscv32) and only when /// the "uart" feature is enabled. #[cfg(feature = "uart")] - fn get_uart2() -> UART2; + fn get_uart2() -> Uart2Type; /// Returns a GPIO/IO peripheral instance for pin configuration. /// /// This function transfers ownership of the GPIO peripheral from the system @@ -98,7 +98,7 @@ pub trait PortTrait { /// Available only on ESP32 architectures (xtensa, riscv32) and only when /// the "uart" feature is enabled. #[cfg(feature = "uart")] - fn get_io() -> Io; + fn get_io() -> IoType; // TODO: split to separate trait? #[cfg(feature = "preemptive")] From c74d49ee6ea60d876e8802e4c91c85e49fe00b3f Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Fri, 5 Sep 2025 15:57:36 +0300 Subject: [PATCH 08/14] Fix build --- .../xtensa-esp32/uart/src/main.rs | 92 +++++++++---------- src/lib.rs | 2 + src/ports/mod.rs | 4 +- 3 files changed, 50 insertions(+), 48 deletions(-) diff --git a/examples/rust-examples/xtensa-esp32/uart/src/main.rs b/examples/rust-examples/xtensa-esp32/uart/src/main.rs index cd0667dc..84e2cb7d 100644 --- a/examples/rust-examples/xtensa-esp32/uart/src/main.rs +++ b/examples/rust-examples/xtensa-esp32/uart/src/main.rs @@ -3,50 +3,49 @@ use core::sync::atomic::{AtomicU32, Ordering}; use esp_backtrace as _; +use esp_hal::uart::config::{Config, DataBits, StopBits}; +use esp_hal::Blocking; use esp_hal::{entry, uart::Uart}; use esp_println::println; use martos::{ - get_uart2, get_io, init_system, + get_io, get_uart2, init_system, task_manager::{TaskManager, TaskManagerTrait}, }; -use esp_hal::uart::config::{Config, DataBits, StopBits}; /// Counter to track processed bytes static BYTE_COUNTER: AtomicU32 = AtomicU32::new(0); -/// UART instance (initialized in setup) -static mut UART_INSTANCE: Option> = None; +/// UART instance (initialized in setup) - исправленная сигнатура +static mut UART_INSTANCE: Option> = None; /// Setup function for task to execute. fn setup_fn() { println!("UART Echo Setup started"); - - // Get UART2 and IO from Martos - let uart2 = get_uart2(); - let io = get_io(); - - // Configure UART pins - let tx_pin = io.pins.gpio17.into_push_pull_output(); - let rx_pin = io.pins.gpio16.into_pull_up_input(); - - // UART configuration: 19200 baud, 8N1 - let config = Config::default() - .baudrate(19200) - .data_bits(DataBits::DataBits8) - .parity_none() - .stop_bits(StopBits::STOP1); - - // Initialize UART - match Uart::new_with_config(uart2, config, rx_pin, tx_pin) { - Ok(uart) => { - unsafe { - UART_INSTANCE = Some(uart); - } - println!("UART Echo ready on GPIO16(RX)/GPIO17(TX) at 19200 baud"); - } - Err(_) => { - println!("Failed to initialize UART!"); - } + + unsafe { + // Get UART2 and IO from Martos + let uart2 = get_uart2(); + let io = get_io(); + + // UART configuration: 19200 baud, 8N1 + let config = Config::default() + .baudrate(19200) + .data_bits(DataBits::DataBits8) + .parity_none() + .stop_bits(StopBits::STOP1); + + // Initialize UART + let uart = Uart::new_with_config( + uart2, + config, + io.pins.gpio16, // RX pin + io.pins.gpio17, // TX pin + ) + .expect("UART init failed"); + + UART_INSTANCE = Some(uart); + + println!("UART Echo ready on GPIO16(RX)/GPIO17(TX) at 19200 baud"); } } @@ -55,24 +54,25 @@ fn loop_fn() { unsafe { if let Some(ref mut uart) = UART_INSTANCE { let mut buffer = [0u8; 1]; - - // Try to read a byte - if uart.read(&mut buffer).is_ok() { + + // Try to read a byte using read_bytes method + if uart.read_bytes(&mut buffer).is_ok() { let byte = buffer[0]; let count = BYTE_COUNTER.fetch_add(1, Ordering::Relaxed) + 1; - - println!("Received byte #{}: 0x{:02X} ('{}') - echoing back", - count, + + println!( + "Received byte #{}: 0x{:02X} ('{}') - echoing back", + count, byte, - if byte.is_ascii_graphic() || byte == b' ' { - byte as char - } else { - '.' + if byte.is_ascii_graphic() || byte == b' ' { + byte as char + } else { + '.' } ); - - // Echo the byte back - if uart.write(&buffer).is_err() { + + // Echo the byte back using write_bytes method + if uart.write_bytes(&buffer).is_err() { println!("Failed to echo byte!"); } } @@ -90,10 +90,10 @@ fn stop_condition_fn() -> bool { fn main() -> ! { // Initialize Martos (including UART) init_system(); - + // Add task to execute TaskManager::add_task(setup_fn, loop_fn, stop_condition_fn); - + // Start task manager TaskManager::start_task_manager(); } diff --git a/src/lib.rs b/src/lib.rs index 0195b27a..3f6a78b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,8 @@ pub mod c_api; pub mod task_manager; pub mod timer; #[cfg(any(target_arch = "riscv32", target_arch = "xtensa"))] +use crate::ports::xtensa_esp32::XtensaEsp32; +#[cfg(any(target_arch = "riscv32", target_arch = "xtensa"))] #[cfg(feature = "network")] use esp_wifi::esp_now::EspNow; diff --git a/src/ports/mod.rs b/src/ports/mod.rs index 70c283e6..fd1ddee1 100644 --- a/src/ports/mod.rs +++ b/src/ports/mod.rs @@ -75,7 +75,7 @@ pub trait PortTrait { /// Available only on ESP32 architectures (xtensa, riscv32) and only when /// the "uart" feature is enabled. #[cfg(feature = "uart")] - fn get_uart2() -> Uart2Type; + fn get_uart2() -> Self::Uart2Type; /// Returns a GPIO/IO peripheral instance for pin configuration. /// /// This function transfers ownership of the GPIO peripheral from the system @@ -98,7 +98,7 @@ pub trait PortTrait { /// Available only on ESP32 architectures (xtensa, riscv32) and only when /// the "uart" feature is enabled. #[cfg(feature = "uart")] - fn get_io() -> IoType; + fn get_io() -> Self::IoType; // TODO: split to separate trait? #[cfg(feature = "preemptive")] From 750cdb510e66ddc5b9d5e7e0cf41011e47e92316 Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Wed, 1 Oct 2025 18:55:59 +0300 Subject: [PATCH 09/14] Refactor peripheral initialization and update UART handling - Introduced a new `peripherals` module to manage ESP32 peripheral initialization. - Updated `setup_hardware_timer` and `setup_uart` functions to utilize the new peripheral management. - Simplified timer index validation in `valid_timer_index`. - Adjusted trait methods in `PortTrait` implementations for better clarity and consistency. - Modified test conditions for cooperative tests to exclude preemptive feature. --- src/lib.rs | 6 ++-- src/ports/mips64/hardware_timer.rs | 21 ++++++------ src/ports/mips64/mod.rs | 6 +--- src/ports/mod.rs | 14 +++++--- src/ports/mok/mod.rs | 7 ++-- src/ports/xtensa_esp32/hardware_timer.rs | 23 +++++++------ src/ports/xtensa_esp32/mod.rs | 1 + src/ports/xtensa_esp32/network.rs | 5 +-- src/ports/xtensa_esp32/peripherals.rs | 41 ++++++++++++++++++++++++ src/ports/xtensa_esp32/uart.rs | 12 +++---- src/task_manager/preemptive.rs | 19 +++++++++-- tests/cooperative_tests.rs | 2 +- 12 files changed, 110 insertions(+), 47 deletions(-) create mode 100644 src/ports/xtensa_esp32/peripherals.rs diff --git a/src/lib.rs b/src/lib.rs index 3f6a78b5..5c895e27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,8 +8,6 @@ pub mod c_api; pub mod task_manager; pub mod timer; #[cfg(any(target_arch = "riscv32", target_arch = "xtensa"))] -use crate::ports::xtensa_esp32::XtensaEsp32; -#[cfg(any(target_arch = "riscv32", target_arch = "xtensa"))] #[cfg(feature = "network")] use esp_wifi::esp_now::EspNow; @@ -34,11 +32,11 @@ pub fn get_esp_now() -> EspNow<'static> { } #[cfg(feature = "uart")] -pub fn get_uart2() -> ::Uart2Type { +pub fn get_uart2() -> ports::Uart2Type { ports::Port::get_uart2() } #[cfg(feature = "uart")] -pub fn get_io() -> ::IoType { +pub fn get_io() -> ports::IoType { ports::Port::get_io() } diff --git a/src/ports/mips64/hardware_timer.rs b/src/ports/mips64/hardware_timer.rs index cea20921..2eb8ed40 100644 --- a/src/ports/mips64/hardware_timer.rs +++ b/src/ports/mips64/hardware_timer.rs @@ -257,8 +257,9 @@ pub fn setup_hardware_timer() { } /// Mips64 attempt to acquire timer. +#[allow(static_mut_refs)] pub fn try_acquire_timer(timer_index: u8) -> bool { - if timer_index <= 4 as u8 { + if timer_index <= 4_u8 { unsafe { let timer_block = TIMER_BLOCK.take().expect("Timer block error"); @@ -270,15 +271,9 @@ pub fn try_acquire_timer(timer_index: u8) -> bool { &timer_block.timer4.in_use, ]; - let return_value = match timers[timer_index as usize].compare_exchange( - false, - true, - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => true, - Err(_) => false, - }; + let return_value = timers[timer_index as usize] + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_ok(); TIMER_BLOCK = Some(timer_block); return_value @@ -290,6 +285,7 @@ pub fn try_acquire_timer(timer_index: u8) -> bool { /// Mips64 start harware timer. pub fn start_hardware_timer(timer_index: u8) { + #[allow(static_mut_refs)] unsafe { let mut timer_block = TIMER_BLOCK.take().expect("Timer block error"); match timer_index { @@ -306,6 +302,7 @@ pub fn start_hardware_timer(timer_index: u8) { /// Mips64 change operating mode of hardware timer. pub fn set_reload_mode(timer_index: u8, auto_reload: bool) { + #[allow(static_mut_refs)] unsafe { let mut timer_block = TIMER_BLOCK.take().expect("Timer block error"); match timer_index { @@ -323,6 +320,7 @@ pub fn set_reload_mode(timer_index: u8, auto_reload: bool) { /// Mips64 change the period of hardware timer. /// If timer was in active state, function will restart timer with a new period. pub fn change_period_timer(timer_index: u8, period: Duration) { + #[allow(static_mut_refs)] unsafe { let mut timer_block = TIMER_BLOCK.take().expect("Timer block error"); match timer_index { @@ -339,6 +337,7 @@ pub fn change_period_timer(timer_index: u8, period: Duration) { /// Mips64 getting counter value of hardware timer. pub fn get_time(timer_index: u8) -> Duration { + #[allow(static_mut_refs)] unsafe { let timer_block = TIMER_BLOCK.take().expect("Timer block error"); let tick_counter = match timer_index { @@ -357,6 +356,7 @@ pub fn get_time(timer_index: u8) -> Duration { /// Mips64 stop hardware timer. pub fn stop_hardware_timer(timer_index: u8) -> bool { + #[allow(static_mut_refs)] unsafe { let mut timer_block = TIMER_BLOCK.take().expect("Timer block error"); match timer_index { @@ -375,6 +375,7 @@ pub fn stop_hardware_timer(timer_index: u8) -> bool { /// Mips64 release hardware timer. pub fn release_hardware_timer(timer_index: u8) { + #[allow(static_mut_refs)] unsafe { let timer_block = TIMER_BLOCK.take().expect("Timer block error"); match timer_index { diff --git a/src/ports/mips64/mod.rs b/src/ports/mips64/mod.rs index db44183e..c02d776c 100644 --- a/src/ports/mips64/mod.rs +++ b/src/ports/mips64/mod.rs @@ -26,11 +26,7 @@ impl PortTrait for Mips64 { } fn valid_timer_index(timer_index: u8) -> bool { - if timer_index <= 4 { - true - } else { - false - } + timer_index <= 4 } fn try_acquire_timer(timer_index: u8) -> bool { diff --git a/src/ports/mod.rs b/src/ports/mod.rs index fd1ddee1..aa34b999 100644 --- a/src/ports/mod.rs +++ b/src/ports/mod.rs @@ -102,13 +102,14 @@ pub trait PortTrait { // TODO: split to separate trait? #[cfg(feature = "preemptive")] - fn setup_interrupt(); + fn setup_interrupt() {} #[cfg(feature = "preemptive")] - fn setup_stack(thread: &mut crate::task_manager::preemptive::Thread); + #[allow(private_interfaces)] + fn setup_stack(_thread: &mut crate::task_manager::preemptive::Thread) {} #[cfg(feature = "preemptive")] - fn save_ctx(thread_ctx: &mut TrapFrame, isr_ctx: &TrapFrame); + fn save_ctx(_thread_ctx: &mut TrapFrame, _isr_ctx: &TrapFrame) {} #[cfg(feature = "preemptive")] - fn load_ctx(thread_ctx: &TrapFrame, isr_ctx: &mut TrapFrame); + fn load_ctx(_thread_ctx: &TrapFrame, _isr_ctx: &mut TrapFrame) {} } /// Port is an alias of PortTrait implementation for a current platform @@ -156,3 +157,8 @@ mod arch { } pub use arch::*; + +#[cfg(feature = "uart")] +pub type Uart2Type = ::Uart2Type; +#[cfg(feature = "uart")] +pub type IoType = ::IoType; diff --git a/src/ports/mok/mod.rs b/src/ports/mok/mod.rs index a3351526..48afe36a 100644 --- a/src/ports/mok/mod.rs +++ b/src/ports/mok/mod.rs @@ -63,11 +63,12 @@ impl PortTrait for Mok { #[cfg(feature = "preemptive")] fn setup_interrupt() {} #[cfg(feature = "preemptive")] - fn setup_stack(thread: &mut crate::task_manager::preemptive::Thread) {} + #[allow(private_interfaces)] + fn setup_stack(_thread: &mut crate::task_manager::preemptive::Thread) {} #[cfg(feature = "preemptive")] - fn save_ctx(thread_ctx: &mut crate::ports::TrapFrame, isr_ctx: &crate::ports::TrapFrame) {} + fn save_ctx(_thread_ctx: &mut crate::ports::TrapFrame, _isr_ctx: &crate::ports::TrapFrame) {} #[cfg(feature = "preemptive")] - fn load_ctx(thread_ctx: &crate::ports::TrapFrame, isr_ctx: &mut crate::ports::TrapFrame) {} + fn load_ctx(_thread_ctx: &crate::ports::TrapFrame, _isr_ctx: &mut crate::ports::TrapFrame) {} #[cfg(feature = "uart")] fn setup_uart() { diff --git a/src/ports/xtensa_esp32/hardware_timer.rs b/src/ports/xtensa_esp32/hardware_timer.rs index b8a2376a..8dff7acf 100644 --- a/src/ports/xtensa_esp32/hardware_timer.rs +++ b/src/ports/xtensa_esp32/hardware_timer.rs @@ -1,22 +1,28 @@ +use super::peripherals::init_peripherals; +use super::peripherals::{PERIFERALS_RADIO_CLK, PERIFERALS_RNG, PERIFERALS_WIFI}; +use super::peripherals::{PERIFERALS_TIMG0, PERIFERALS_TIMG1}; use core::sync::atomic::{AtomicBool, Ordering}; use core::time::Duration; use esp_hal::timer::timg::{Timer, Timer0, TimerGroup}; use esp_hal::{peripherals::*, prelude::*}; -// TODO: initialize peripherals in separate mod +// Timer instances derived from stored raw peripherals pub static mut TIMER00: Option, esp_hal::Blocking>> = None; pub static mut TIMER10: Option, esp_hal::Blocking>> = None; -pub static mut PERIFERALS_RNG: Option = None; -pub static mut PERIFERALS_RADIO_CLK: Option = None; -pub static mut PERIFERALS_WIFI: Option = None; static TIMER_BUSY: AtomicBool = AtomicBool::new(false); /// Esp32 hardware timer setup. pub fn setup_hardware_timer() { - let peripherals = esp_hal::init(esp_hal::Config::default()); - let timer_group0 = TimerGroup::new(peripherals.TIMG0); - let timer_group1 = TimerGroup::new(peripherals.TIMG1); + init_peripherals(); + let (timg0, timg1) = unsafe { + ( + PERIFERALS_TIMG0.take().expect("TIMG0 peripherals error"), + PERIFERALS_TIMG1.take().expect("TIMG1 peripherals error"), + ) + }; + let timer_group0 = TimerGroup::new(timg0); + let timer_group1 = TimerGroup::new(timg1); let timer00 = timer_group0.timer0; let timer10 = timer_group1.timer0; @@ -24,9 +30,6 @@ pub fn setup_hardware_timer() { unsafe { TIMER00 = Some(timer00); TIMER10 = Some(timer10); - PERIFERALS_RNG = Some(peripherals.RNG); - PERIFERALS_RADIO_CLK = Some(peripherals.RADIO_CLK); - PERIFERALS_WIFI = Some(peripherals.WIFI); } } diff --git a/src/ports/xtensa_esp32/mod.rs b/src/ports/xtensa_esp32/mod.rs index 0591149f..f12d3bbe 100644 --- a/src/ports/xtensa_esp32/mod.rs +++ b/src/ports/xtensa_esp32/mod.rs @@ -2,6 +2,7 @@ pub mod hardware_timer; pub mod memory_manager; #[cfg(feature = "network")] pub mod network; +pub mod peripherals; #[cfg(feature = "preemptive")] mod preempt; diff --git a/src/ports/xtensa_esp32/network.rs b/src/ports/xtensa_esp32/network.rs index aa1a7d57..66dcb875 100644 --- a/src/ports/xtensa_esp32/network.rs +++ b/src/ports/xtensa_esp32/network.rs @@ -1,5 +1,6 @@ -use crate::ports::xtensa_esp32::hardware_timer::{ - PERIFERALS_RADIO_CLK, PERIFERALS_RNG, PERIFERALS_WIFI, TIMER10, +use crate::ports::xtensa_esp32::hardware_timer::TIMER10; +use crate::ports::xtensa_esp32::peripherals::{ + PERIFERALS_RADIO_CLK, PERIFERALS_RNG, PERIFERALS_WIFI, }; use esp_hal::rng::Rng; use esp_wifi::{esp_now::EspNow, init, EspWifiInitFor}; diff --git a/src/ports/xtensa_esp32/peripherals.rs b/src/ports/xtensa_esp32/peripherals.rs new file mode 100644 index 00000000..1195fe85 --- /dev/null +++ b/src/ports/xtensa_esp32/peripherals.rs @@ -0,0 +1,41 @@ +use core::sync::atomic::{AtomicBool, Ordering}; +use esp_hal::peripherals::*; + +// Single-shot initialization guard for peripherals +static PERIPHERALS_INITIALIZED: AtomicBool = AtomicBool::new(false); + +// Raw peripheral handles stored for later split/consumption by subsystems +pub static mut PERIFERALS_TIMG0: Option = None; +pub static mut PERIFERALS_TIMG1: Option = None; +pub static mut PERIFERALS_RNG: Option = None; +pub static mut PERIFERALS_RADIO_CLK: Option = None; +pub static mut PERIFERALS_WIFI: Option = None; +pub static mut PERIFERALS_UART2: Option = None; +pub static mut PERIFERALS_GPIO: Option = None; +pub static mut PERIFERALS_IO_MUX: Option = None; + +/// Initialize ESP32 peripherals once and store raw handles for later use +pub fn init_peripherals() { + if PERIPHERALS_INITIALIZED + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_ok() + { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + unsafe { + PERIFERALS_TIMG0 = Some(peripherals.TIMG0); + PERIFERALS_TIMG1 = Some(peripherals.TIMG1); + PERIFERALS_RNG = Some(peripherals.RNG); + PERIFERALS_RADIO_CLK = Some(peripherals.RADIO_CLK); + PERIFERALS_WIFI = Some(peripherals.WIFI); + PERIFERALS_UART2 = Some(peripherals.UART2); + PERIFERALS_GPIO = Some(peripherals.GPIO); + PERIFERALS_IO_MUX = Some(peripherals.IO_MUX); + } + } +} + +/// Check if peripherals were initialized +pub fn is_initialized() -> bool { + PERIPHERALS_INITIALIZED.load(Ordering::Acquire) +} diff --git a/src/ports/xtensa_esp32/uart.rs b/src/ports/xtensa_esp32/uart.rs index a9ecab1c..3304f155 100644 --- a/src/ports/xtensa_esp32/uart.rs +++ b/src/ports/xtensa_esp32/uart.rs @@ -1,10 +1,11 @@ //! UART module for ESP32 - handles UART2 and IO peripherals +use super::peripherals::init_peripherals; +use super::peripherals::{PERIFERALS_GPIO, PERIFERALS_IO_MUX, PERIFERALS_UART2}; use core::sync::atomic::{AtomicBool, Ordering}; use esp_hal::{gpio::*, peripherals::*}; // Static variables for UART peripherals -pub static mut PERIFERALS_UART2: Option = None; pub static mut IO: Option = None; // Flag to ensure UART is initialized only once @@ -26,12 +27,11 @@ pub fn setup_uart() { .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) .is_ok() { - // Initialize ESP-HAL to get peripheral instances - let peripherals = esp_hal::init(esp_hal::Config::default()); - + init_peripherals(); unsafe { - PERIFERALS_UART2 = Some(peripherals.UART2); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let gpio = PERIFERALS_GPIO.take().expect("GPIO peripherals error"); + let io_mux = PERIFERALS_IO_MUX.take().expect("IO_MUX peripherals error"); + let io = Io::new(gpio, io_mux); IO = Some(io); } } diff --git a/src/task_manager/preemptive.rs b/src/task_manager/preemptive.rs index 11a7eb68..c81b3b3b 100644 --- a/src/task_manager/preemptive.rs +++ b/src/task_manager/preemptive.rs @@ -8,12 +8,15 @@ use core::alloc::Layout; pub(crate) const THREAD_STACK_SIZE: usize = 1024; // TODO: +#[allow(dead_code)] pub(crate) struct Thread { /// Pointer to the memory allocated for stack + #[allow(dead_code)] pub(crate) stack: *mut u8, /// **Arch specific** state of the registers at the moment of context switch pub(crate) context: TrapFrame, /// Task that is executed by this thread + #[allow(dead_code)] pub(crate) task: Task, } @@ -34,6 +37,7 @@ impl Thread { }, } } + #[allow(dead_code)] pub(crate) fn run_task( start: TaskSetupFunctionType, loop_: TaskLoopFunctionType, @@ -43,7 +47,7 @@ impl Thread { loop { if stop() { // TODO: yield - loop {} + core::hint::spin_loop(); } else { loop_(); } @@ -66,6 +70,7 @@ impl PreemptiveTaskManager { } } + #[allow(static_mut_refs)] fn next_thread() { unsafe { TASK_MANAGER.task_to_execute_index = @@ -73,6 +78,7 @@ impl PreemptiveTaskManager { } } + #[allow(static_mut_refs)] pub fn schedule(isr_ctx: &mut TrapFrame) { if unsafe { !TASK_MANAGER.first_task } { let task = unsafe { @@ -100,6 +106,7 @@ impl PreemptiveTaskManager { } impl TaskManagerTrait for PreemptiveTaskManager { + #[allow(static_mut_refs)] fn add_task( setup_fn: TaskSetupFunctionType, loop_fn: TaskLoopFunctionType, @@ -116,6 +123,14 @@ impl TaskManagerTrait for PreemptiveTaskManager { fn start_task_manager() -> ! { // todo!("idle task?"); Port::setup_interrupt(); - loop {} + loop { + core::hint::spin_loop(); + } + } +} + +impl Default for PreemptiveTaskManager { + fn default() -> Self { + Self::new() } } diff --git a/tests/cooperative_tests.rs b/tests/cooperative_tests.rs index 128e5e19..fec3c0d2 100644 --- a/tests/cooperative_tests.rs +++ b/tests/cooperative_tests.rs @@ -1,4 +1,4 @@ -#[cfg(all(test, feature = "cooperative_tests"))] +#[cfg(all(test, feature = "cooperative_tests", not(feature = "preemptive")))] mod cooperative_tests { use martos::task_manager::{TaskManager, TaskManagerTrait}; use sequential_test::sequential; From 53e453cffe93e50eb66c056e39e28d913fa60e05 Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Wed, 1 Oct 2025 19:34:16 +0300 Subject: [PATCH 10/14] Update documentation formatting and improve peripheral handling - Changed code block formatting in documentation to use `rust,ignore` and `text` for better clarity. - Refactored UART peripheral initialization to support architecture-specific types. - Updated UART handling in the ESP32 module to use a unified `UartPeriph` type. - Enhanced task manager documentation to reflect changes in task registration examples. --- src/ports/xtensa_esp32/hardware_timer.rs | 1 - src/ports/xtensa_esp32/peripherals.rs | 17 ++++++++- src/ports/xtensa_esp32/uart.rs | 10 ++--- src/task_manager/mod.rs | 6 +-- src/task_manager/preemptive.rs | 13 +++++-- src/task_manager/task.rs | 48 ++++++++++++------------ src/timer.rs | 24 ++++++------ 7 files changed, 69 insertions(+), 50 deletions(-) diff --git a/src/ports/xtensa_esp32/hardware_timer.rs b/src/ports/xtensa_esp32/hardware_timer.rs index 8dff7acf..9d6ae08c 100644 --- a/src/ports/xtensa_esp32/hardware_timer.rs +++ b/src/ports/xtensa_esp32/hardware_timer.rs @@ -1,5 +1,4 @@ use super::peripherals::init_peripherals; -use super::peripherals::{PERIFERALS_RADIO_CLK, PERIFERALS_RNG, PERIFERALS_WIFI}; use super::peripherals::{PERIFERALS_TIMG0, PERIFERALS_TIMG1}; use core::sync::atomic::{AtomicBool, Ordering}; use core::time::Duration; diff --git a/src/ports/xtensa_esp32/peripherals.rs b/src/ports/xtensa_esp32/peripherals.rs index 1195fe85..d1c2cb9e 100644 --- a/src/ports/xtensa_esp32/peripherals.rs +++ b/src/ports/xtensa_esp32/peripherals.rs @@ -10,7 +10,13 @@ pub static mut PERIFERALS_TIMG1: Option = None; pub static mut PERIFERALS_RNG: Option = None; pub static mut PERIFERALS_RADIO_CLK: Option = None; pub static mut PERIFERALS_WIFI: Option = None; -pub static mut PERIFERALS_UART2: Option = None; +// Select UART peripheral depending on the target architecture +#[cfg(target_arch = "xtensa")] +pub type UartPeriph = UART2; +#[cfg(target_arch = "riscv32")] +pub type UartPeriph = UART0; + +pub static mut PERIFERALS_UART: Option = None; pub static mut PERIFERALS_GPIO: Option = None; pub static mut PERIFERALS_IO_MUX: Option = None; @@ -28,7 +34,14 @@ pub fn init_peripherals() { PERIFERALS_RNG = Some(peripherals.RNG); PERIFERALS_RADIO_CLK = Some(peripherals.RADIO_CLK); PERIFERALS_WIFI = Some(peripherals.WIFI); - PERIFERALS_UART2 = Some(peripherals.UART2); + #[cfg(target_arch = "xtensa")] + { + PERIFERALS_UART = Some(peripherals.UART2); + } + #[cfg(target_arch = "riscv32")] + { + PERIFERALS_UART = Some(peripherals.UART0); + } PERIFERALS_GPIO = Some(peripherals.GPIO); PERIFERALS_IO_MUX = Some(peripherals.IO_MUX); } diff --git a/src/ports/xtensa_esp32/uart.rs b/src/ports/xtensa_esp32/uart.rs index 3304f155..7197de29 100644 --- a/src/ports/xtensa_esp32/uart.rs +++ b/src/ports/xtensa_esp32/uart.rs @@ -1,7 +1,7 @@ //! UART module for ESP32 - handles UART2 and IO peripherals use super::peripherals::init_peripherals; -use super::peripherals::{PERIFERALS_GPIO, PERIFERALS_IO_MUX, PERIFERALS_UART2}; +use super::peripherals::{UartPeriph, PERIFERALS_GPIO, PERIFERALS_IO_MUX, PERIFERALS_UART}; use core::sync::atomic::{AtomicBool, Ordering}; use esp_hal::{gpio::*, peripherals::*}; @@ -11,8 +11,8 @@ pub static mut IO: Option = None; // Flag to ensure UART is initialized only once static UART_INITIALIZED: AtomicBool = AtomicBool::new(false); -/// ESP32 UART2 type alias -pub type Uart2Type = UART2; +/// ESP32 UART type alias (depends on arch) +pub type Uart2Type = UartPeriph; /// ESP32 IO type alias pub type IoType = Io; @@ -40,9 +40,9 @@ pub fn setup_uart() { /// Get UART2 peripheral instance /// /// Returns the UART2 peripheral for configuration. Can only be called once. -pub fn get_uart2() -> UART2 { +pub fn get_uart2() -> UartPeriph { unsafe { - PERIFERALS_UART2 + PERIFERALS_UART .take() .expect("UART2 not available - call setup_uart first") } diff --git a/src/task_manager/mod.rs b/src/task_manager/mod.rs index b37b7a93..d3da6105 100644 --- a/src/task_manager/mod.rs +++ b/src/task_manager/mod.rs @@ -42,7 +42,7 @@ //! //! ## Basic Task Creation //! -//! ```rust,no_run +//! ```rust,ignore //! use martos::task_manager::{TaskManager, TaskManagerTrait}; //! //! fn setup_sensor() { @@ -143,7 +143,7 @@ pub trait TaskManagerTrait { /// /// # Examples /// - /// ```rust,no_run + /// ```rust,ignore /// use martos::task_manager::{TaskManager, TaskManagerTrait}; /// /// fn init() { println!("Task starting"); } @@ -171,7 +171,7 @@ pub trait TaskManagerTrait { /// /// # Examples /// - /// ```rust,no_run + /// ```rust,ignore /// use martos::task_manager::{TaskManager, TaskManagerTrait}; /// /// // Register tasks first diff --git a/src/task_manager/preemptive.rs b/src/task_manager/preemptive.rs index 39cc18e0..b979468a 100644 --- a/src/task_manager/preemptive.rs +++ b/src/task_manager/preemptive.rs @@ -35,7 +35,7 @@ //! //! ## Basic Task Registration //! -//! ```rust,no_run +//! ```rust,ignore //! use martos::task_manager::{TaskManager, TaskManagerTrait}; //! use core::sync::atomic::{AtomicU32, Ordering}; //! @@ -126,6 +126,7 @@ pub(crate) const THREAD_STACK_SIZE: usize = 1024; /// The `stack` field is a raw pointer to dynamically allocated memory. /// Care must be taken to ensure the stack remains valid throughout /// the thread's lifetime. Currently, stack deallocation is not implemented. +#[allow(dead_code)] pub(crate) struct Thread { /// Pointer to the memory allocated for this thread's stack. /// @@ -135,6 +136,7 @@ pub(crate) struct Thread { /// /// # Safety /// Raw pointer requires careful lifetime management. + #[allow(dead_code)] pub(crate) stack: *mut u8, /// Architecture-specific saved register state. @@ -148,6 +150,7 @@ pub(crate) struct Thread { /// /// Contains the three function pointers that define the task's /// behavior: setup, main loop, and termination condition. + #[allow(dead_code)] pub(crate) task: Task, } @@ -205,6 +208,7 @@ impl Thread { /// /// # TODO /// Implement proper yielding mechanism instead of infinite loop. + #[allow(dead_code)] pub(crate) fn run_task( start: TaskSetupFunctionType, loop_: TaskLoopFunctionType, @@ -214,7 +218,7 @@ impl Thread { loop { if stop() { // TODO: yield properly instead of busy loop - loop {} + core::hint::spin_loop(); } else { loop_(); } @@ -236,7 +240,7 @@ impl Thread { /// /// # Example /// -/// ```rust,no_run +/// ```rust,ignore /// use martos::task_manager::{TaskManager, TaskManagerTrait}; /// /// fn setup() { println!("Starting task"); } @@ -293,6 +297,7 @@ impl PreemptiveTaskManager { /// # Safety /// /// Accesses the global `TASK_MANAGER` instance unsafely. + #[allow(static_mut_refs)] fn next_thread() { unsafe { TASK_MANAGER.task_to_execute_index = @@ -321,6 +326,7 @@ impl PreemptiveTaskManager { /// /// This function manipulates raw CPU context and must only be /// called from interrupt context with proper stack setup. + #[allow(static_mut_refs)] pub fn schedule(isr_ctx: &mut TrapFrame) { if unsafe { !TASK_MANAGER.first_task } { let task = unsafe { @@ -380,6 +386,7 @@ impl TaskManagerTrait for PreemptiveTaskManager { /// # TODO /// /// Implement stack deallocation when tasks terminate. + #[allow(static_mut_refs)] fn add_task( setup_fn: TaskSetupFunctionType, loop_fn: TaskLoopFunctionType, diff --git a/src/task_manager/task.rs b/src/task_manager/task.rs index 778ab94c..417ca6d1 100644 --- a/src/task_manager/task.rs +++ b/src/task_manager/task.rs @@ -18,7 +18,7 @@ //! //! ## Examples //! -//! ```rust,no_run +//! ```rust,ignore //! use martos::task_manager::{TaskManager, TaskManagerTrait}; //! //! fn my_setup() { @@ -52,7 +52,7 @@ /// /// # Examples /// -/// ``` +/// ```rust,ignore /// use martos::task_manager::{TaskManager, TaskManagerTrait}; /// /// fn initialize_sensor() { @@ -61,7 +61,7 @@ /// } /// /// TaskManager::add_task(initialize_sensor, || {}, || false); -/// ``` +/// ```rust,ignore /// /// # See Also /// @@ -83,13 +83,13 @@ pub type TaskSetupFunctionType = fn() -> (); /// /// # Examples /// -/// ``` +/// ```text /// // C code example /// void my_task_setup(void) { /// printf("Task setup from C\n"); /// // C initialization code /// } -/// ``` +/// ```text /// /// # See Also /// @@ -116,7 +116,7 @@ pub type TaskSetupFunctionType = extern "C" fn() -> (); /// /// # Examples /// -/// ``` +/// ```text /// use martos::task_manager::{TaskManager, TaskManagerTrait}; /// use core::sync::atomic::{AtomicU32, Ordering}; /// @@ -130,7 +130,7 @@ pub type TaskSetupFunctionType = extern "C" fn() -> (); /// } /// /// TaskManager::add_task(|| {}, blink_led, || false); -/// ``` +/// ```rust,ignore /// /// # See Also /// @@ -158,13 +158,13 @@ pub type TaskLoopFunctionType = fn() -> (); /// /// # Examples /// -/// ``` +/// ```rust,ignore /// // C code example /// void my_task_loop(void) { /// // Main task logic in C /// printf("Task loop iteration\n"); /// } -/// ``` +/// ```rust,ignore /// /// # See Also /// @@ -192,7 +192,7 @@ pub type TaskLoopFunctionType = extern "C" fn() -> (); /// /// # Examples /// -/// ``` +/// ```rust,ignore /// use martos::task_manager::{TaskManager, TaskManagerTrait}; /// use core::sync::atomic::{AtomicU32, Ordering}; /// @@ -204,7 +204,7 @@ pub type TaskLoopFunctionType = extern "C" fn() -> (); /// } /// /// TaskManager::add_task(|| {}, || {}, should_stop); -/// ``` +/// ```rust,ignore /// /// # See Also /// @@ -231,7 +231,7 @@ pub type TaskStopConditionFunctionType = fn() -> bool; /// /// # Examples /// -/// ``` +/// ```rust,ignore /// // C code example /// static int counter = 0; /// @@ -239,7 +239,7 @@ pub type TaskStopConditionFunctionType = fn() -> bool; /// counter++; /// return counter >= 50; // Stop after 50 iterations /// } -/// ``` +/// ```rust,ignore /// /// # See Also /// @@ -273,7 +273,7 @@ pub type TaskStopConditionFunctionType = extern "C" fn() -> bool; /// /// ## Basic Task Creation /// -/// ``` +/// ```rust,ignore /// use martos::task_manager::{TaskManager, TaskManagerTrait}; /// /// fn setup() { @@ -289,11 +289,11 @@ pub type TaskStopConditionFunctionType = extern "C" fn() -> bool; /// } /// /// TaskManager::add_task(setup, main_loop, stop_condition); -/// ``` +/// ```rust,ignore /// /// ## Task with Termination Condition /// -/// ``` +/// ```rust,ignore /// use martos::task_manager::{TaskManager, TaskManagerTrait}; /// use std::sync::atomic::{AtomicBool, Ordering}; /// @@ -314,14 +314,14 @@ pub type TaskStopConditionFunctionType = extern "C" fn() -> bool; /// } /// /// TaskManager::add_task(setup, work, is_complete); -/// ``` +/// ```rust,ignore /// /// # Integration with TaskManager /// /// Tasks are typically created and registered with the [`TaskManager`] which /// handles their execution lifecycle: /// -/// ```rust,no_run +/// ```rust,ignore /// use martos::task_manager::{TaskManager, TaskManagerTrait}; /// /// // Create task functions @@ -348,7 +348,7 @@ pub struct Task { /// /// # Examples /// - /// ``` + /// ```rust,ignore /// use martos::task_manager::{TaskManager, TaskManagerTrait}; /// /// fn my_setup() { @@ -358,8 +358,8 @@ pub struct Task { /// /// // Register task using public API /// TaskManager::add_task(my_setup, || {}, || false); - /// ``` - /// ``` + /// ```rust,ignore + /// ```rust,ignore pub(crate) setup_fn: TaskSetupFunctionType, /// Loop function called repeatedly during task execution. @@ -381,7 +381,7 @@ pub struct Task { /// /// # Examples /// - /// ``` + /// ```rust,ignore /// use martos::task_manager::{TaskManager, TaskManagerTrait}; /// use core::sync::atomic::{AtomicU32, Ordering}; /// @@ -395,7 +395,7 @@ pub struct Task { /// } /// /// TaskManager::add_task(|| {}, periodic_work, || false); - /// ``` + /// ```rust,ignore pub(crate) loop_fn: TaskLoopFunctionType, /// Stop condition function that determines task termination. @@ -417,7 +417,7 @@ pub struct Task { /// /// # Examples /// - /// ``` + /// ```rust,ignore /// use martos::task_manager::{TaskManager, TaskManagerTrait}; /// use core::sync::atomic::{AtomicBool, Ordering}; /// diff --git a/src/timer.rs b/src/timer.rs index 02eed536..b05f6b4f 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -60,7 +60,7 @@ //! //! ## Basic Timer Setup //! -//! ``` +//! ```rust,ignore //! use martos::timer::{Timer, TickType}; //! use core::time::Duration; //! @@ -76,11 +76,11 @@ //! //! // Start the timer //! timer.start_timer(); -//! ``` +//! ```rust,ignore //! //! ## Periodic Task with Tick Counting //! -//! ``` +//! ```rust,ignore //! use martos::timer::Timer; //! use core::time::Duration; //! @@ -105,11 +105,11 @@ //! //! // Clean up //! timer.release_timer(); -//! ``` +//! ```rust,ignore //! //! ## One-Shot Timer for Delays //! -//! ``` +//! ```rust,ignore //! use martos::timer::Timer; //! use core::time::Duration; //! @@ -122,11 +122,11 @@ //! //! // Wait for timeout or do other work //! // Timer will automatically stop after 5 seconds -//! ``` +//! ```rust,ignore //! //! ## High-Resolution Time Measurement //! -//! ``` +//! ```rust,ignore //! use martos::timer::Timer; //! //! let timer = Timer::get_timer(3).unwrap(); @@ -144,7 +144,7 @@ //! println!("Operation took: {:?}", end_time); //! //! timer.release_timer(); -//! ``` +//! ```rust,ignore //! //! # Integration with Martos RTOS //! @@ -157,7 +157,7 @@ //! //! ## Task Manager Integration //! -//! ``` +//! ```text //! use martos::{init_system, task_manager::{TaskManager, TaskManagerTrait}}; //! use martos::timer::Timer; //! use core::time::Duration; @@ -191,7 +191,7 @@ //! //! // Register with task manager //! TaskManager::add_task(timer_setup, timer_task, timer_stop_condition); -//! ``` +//! ```text //! //! # Performance Considerations //! @@ -223,7 +223,7 @@ //! //! ## Common Error Scenarios //! -//! ``` +//! ```text //! use martos::timer::Timer; //! //! // Handle timer acquisition failure @@ -245,7 +245,7 @@ //! println!("Platform doesn't support stopping timers"); //! // Use alternative approach (disable interrupts, etc.) //! } -//! ``` +//! ```text //! //! # Safety and Thread Safety //! From 720907538d6d2aede47473a0416a4843145c6057 Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Wed, 1 Oct 2025 19:51:45 +0300 Subject: [PATCH 11/14] Enhance ESP32 peripheral initialization in system setup - Added initialization of peripherals specifically for ESP32 platforms in the `init_system` function. - Removed direct call to `init_peripherals` from `setup_hardware_timer` to streamline hardware timer setup. --- src/lib.rs | 6 ++++++ src/ports/xtensa_esp32/hardware_timer.rs | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5c895e27..33a96ee6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,12 @@ use esp_wifi::esp_now::EspNow; pub fn init_system() { // Memory initialization. ports::Port::init_heap(); + // Initialize peripherals for ESP32 platforms + #[cfg(any(target_arch = "riscv32", target_arch = "xtensa"))] + { + use ports::xtensa_esp32::peripherals::init_peripherals; + init_peripherals(); + } // Hardware timer setup. ports::Port::setup_hardware_timer(); #[cfg(feature = "network")] diff --git a/src/ports/xtensa_esp32/hardware_timer.rs b/src/ports/xtensa_esp32/hardware_timer.rs index 9d6ae08c..29dea568 100644 --- a/src/ports/xtensa_esp32/hardware_timer.rs +++ b/src/ports/xtensa_esp32/hardware_timer.rs @@ -1,4 +1,3 @@ -use super::peripherals::init_peripherals; use super::peripherals::{PERIFERALS_TIMG0, PERIFERALS_TIMG1}; use core::sync::atomic::{AtomicBool, Ordering}; use core::time::Duration; @@ -13,7 +12,6 @@ static TIMER_BUSY: AtomicBool = AtomicBool::new(false); /// Esp32 hardware timer setup. pub fn setup_hardware_timer() { - init_peripherals(); let (timg0, timg1) = unsafe { ( PERIFERALS_TIMG0.take().expect("TIMG0 peripherals error"), From 48352fce89fd284f23a9a038b6ac4288ba379b41 Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Thu, 2 Oct 2025 14:29:09 +0300 Subject: [PATCH 12/14] Add UART echo example for ESP32-C6 - Introduced a new UART echo example demonstrating UART communication using the Martos RTOS framework. - Added necessary configuration files including `Cargo.toml`, `.cargo/config.toml`, and `rust-toolchain.toml`. - Implemented main functionality in `src/main.rs` to handle UART communication, including byte reception and echoing. - Updated GitHub Actions workflow to include build and formatting steps for the new example. --- .github/workflows/rust.yml | 15 +++ .../risc-v-esp32-c6/uart/.cargo/config.toml | 13 +++ .../risc-v-esp32-c6/uart/Cargo.toml | 17 ++++ .../risc-v-esp32-c6/uart/README.md | 73 ++++++++++++++ .../risc-v-esp32-c6/uart/rust-toolchain.toml | 2 + .../risc-v-esp32-c6/uart/src/main.rs | 99 +++++++++++++++++++ 6 files changed, 219 insertions(+) create mode 100644 examples/rust-examples/risc-v-esp32-c6/uart/.cargo/config.toml create mode 100644 examples/rust-examples/risc-v-esp32-c6/uart/Cargo.toml create mode 100644 examples/rust-examples/risc-v-esp32-c6/uart/README.md create mode 100644 examples/rust-examples/risc-v-esp32-c6/uart/rust-toolchain.toml create mode 100644 examples/rust-examples/risc-v-esp32-c6/uart/src/main.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 71972186..3696aee4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -246,6 +246,21 @@ jobs: - name: Fmt run: cd ./examples/rust-examples/risc-v-esp32-c6/wifi && cargo fmt --all -- --check + risc-v-esp32c6-rust-example-uart: + runs-on: ubuntu-latest + env: + CARGO_HOME: /root/.cargo + RUSTUP_HOME: /root/.rustup + container: + image: arkhipovivan1/xtensa-esp32-rust:latest + options: --user root + steps: + - uses: actions/checkout@v3 + - name: Build + run: cd ./examples/rust-examples/risc-v-esp32-c6/uart && . /root/export-esp.sh && cargo build + - name: Fmt + run: cd ./examples/rust-examples/risc-v-esp32-c6/uart && cargo fmt --all -- --check + risc-v-esp32c6-rust-example-scheduler: runs-on: ubuntu-latest env: diff --git a/examples/rust-examples/risc-v-esp32-c6/uart/.cargo/config.toml b/examples/rust-examples/risc-v-esp32-c6/uart/.cargo/config.toml new file mode 100644 index 00000000..5166e1a1 --- /dev/null +++ b/examples/rust-examples/risc-v-esp32-c6/uart/.cargo/config.toml @@ -0,0 +1,13 @@ +[build] +rustflags = [ + "-C", "link-arg=-Tlinkall.x", + "-C", "force-frame-pointers", +] + +target = "riscv32imac-unknown-none-elf" + +[unstable] +build-std = ["core", "alloc"] + +[target.'cfg(any(target_arch = "riscv32", target_arch = "xtensa"))'] +runner = "espflash flash --monitor" \ No newline at end of file diff --git a/examples/rust-examples/risc-v-esp32-c6/uart/Cargo.toml b/examples/rust-examples/risc-v-esp32-c6/uart/Cargo.toml new file mode 100644 index 00000000..5249900e --- /dev/null +++ b/examples/rust-examples/risc-v-esp32-c6/uart/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "example_risc_v_esp32c6" +version = "0.4.0" +edition = "2021" + +[profile.release] +debug = true + +[dependencies] +# Specifying current Martos version path for ci +martos = { path = "../../../../", features = ["uart"] } +esp-hal = "0.21.1" +esp-backtrace = { version = "0.14.1", features = ["esp32c6", "panic-handler", "exception-handler", "println"] } +esp-println = { version = "0.11.0", features = ["esp32c6"] } + +[features] +default = ["esp-hal/esp32c6", "esp-backtrace/esp32c6", "esp-println/esp32c6"] diff --git a/examples/rust-examples/risc-v-esp32-c6/uart/README.md b/examples/rust-examples/risc-v-esp32-c6/uart/README.md new file mode 100644 index 00000000..51347d9f --- /dev/null +++ b/examples/rust-examples/risc-v-esp32-c6/uart/README.md @@ -0,0 +1,73 @@ +# UART Echo Example for ESP32-C6 + +This example demonstrates UART communication on ESP32-C6 using the Martos RTOS framework. + +## Features + +- UART echo functionality +- Receives bytes via UART and echoes them back +- Uses GPIO16 (RX) and GPIO17 (TX) pins +- Baud rate: 19200 +- Configuration: 8N1 (8 data bits, no parity, 1 stop bit) + +## Hardware Setup + +Connect your ESP32-C6 to a UART-to-USB converter: +- ESP32-C6 GPIO16 → UART-to-USB RX +- ESP32-C6 GPIO17 → UART-to-USB TX +- GND → GND + +## Building and Flashing + +Make sure you have the ESP toolchain installed: + +```bash +# Install ESP toolchain +rustup toolchain install esp + +# Set the toolchain for this project +rustup override set esp +``` + +Build the project: + +```bash +cargo build --release +``` + +Flash to ESP32-C6: + +```bash +cargo run --release +``` + +Or use espflash directly: + +```bash +espflash flash target/riscv32imac-unknown-none-elf/release/example_risc_v_esp32c6 +``` + +## Usage + +1. Flash the firmware to your ESP32-C6 +2. Connect a UART-to-USB converter to GPIO16/GPIO17 +3. Open a serial terminal (e.g., minicom, screen, or Arduino IDE Serial Monitor) +4. Set the terminal to 19200 baud, 8N1 +5. Type characters - they will be echoed back with additional information + +## Expected Output + +The example will print debug information via the ESP32-C6's built-in USB serial (if available) and echo received bytes via the external UART pins. + +## Architecture Notes + +This example is specifically configured for ESP32-C6 (RISC-V architecture): +- Uses UART0 peripheral (ESP32-C6 uses UART0 instead of UART2) +- Targets `riscv32imac-unknown-none-elf` +- Uses ESP32-C6 specific HAL features + +## Troubleshooting + +- Make sure your UART-to-USB converter is set to 19200 baud +- Check that GPIO16 and GPIO17 are not being used by other peripherals +- Verify that the ESP32-C6 is properly powered and grounded \ No newline at end of file diff --git a/examples/rust-examples/risc-v-esp32-c6/uart/rust-toolchain.toml b/examples/rust-examples/risc-v-esp32-c6/uart/rust-toolchain.toml new file mode 100644 index 00000000..a2f5ab50 --- /dev/null +++ b/examples/rust-examples/risc-v-esp32-c6/uart/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "esp" diff --git a/examples/rust-examples/risc-v-esp32-c6/uart/src/main.rs b/examples/rust-examples/risc-v-esp32-c6/uart/src/main.rs new file mode 100644 index 00000000..9273afe2 --- /dev/null +++ b/examples/rust-examples/risc-v-esp32-c6/uart/src/main.rs @@ -0,0 +1,99 @@ +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; +use esp_backtrace as _; +use esp_hal::uart::config::{Config, DataBits, StopBits}; +use esp_hal::Blocking; +use esp_hal::{entry, uart::Uart}; +use esp_println::println; +use martos::{ + get_io, get_uart2, init_system, + task_manager::{TaskManager, TaskManagerTrait}, +}; + +/// Counter to track processed bytes +static BYTE_COUNTER: AtomicU32 = AtomicU32::new(0); + +/// UART instance (initialized in setup) - исправленная сигнатура для ESP32-C6 +static mut UART_INSTANCE: Option> = None; + +/// Setup function for task to execute. +fn setup_fn() { + println!("UART Echo Setup started"); + + unsafe { + // Get UART0 and IO from Martos (ESP32-C6 uses UART0) + let uart0 = get_uart2(); + let io = get_io(); + + // UART configuration: 19200 baud, 8N1 + let config = Config::default() + .baudrate(19200) + .data_bits(DataBits::DataBits8) + .parity_none() + .stop_bits(StopBits::STOP1); + + // Initialize UART + let uart = Uart::new_with_config( + uart0, + config, + io.pins.gpio16, // RX pin + io.pins.gpio17, // TX pin + ) + .expect("UART init failed"); + + UART_INSTANCE = Some(uart); + + println!("UART Echo ready on GPIO16(RX)/GPIO17(TX) at 19200 baud"); + } +} + +/// Loop function for task to execute. +fn loop_fn() { + unsafe { + if let Some(ref mut uart) = UART_INSTANCE { + let mut buffer = [0u8; 1]; + + // Try to read a byte using read_bytes method + if uart.read_bytes(&mut buffer).is_ok() { + let byte = buffer[0]; + let count = BYTE_COUNTER.fetch_add(1, Ordering::Relaxed) + 1; + + println!( + "Received byte #{}: 0x{:02X} ('{}') - echoing back", + count, + byte, + if byte.is_ascii_graphic() || byte == b' ' { + byte as char + } else { + '.' + } + ); + + // Echo the byte back using write_bytes method + if uart.write_bytes(&buffer).is_err() { + println!("Failed to echo byte!"); + } + } + } + } +} + +/// Stop condition function for task to execute. +fn stop_condition_fn() -> bool { + // Never stop - run forever + false +} + +#[entry] +fn main() -> ! { + // Initialize Martos (including UART) + init_system(); + + // Add task to execute + TaskManager::add_task(setup_fn, loop_fn, stop_condition_fn); + + // Start task manager + TaskManager::start_task_manager(); +} From 835ba777040a789fa49f4c41cd1b22f2a683c1e7 Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Thu, 2 Oct 2025 14:33:00 +0300 Subject: [PATCH 13/14] Refactor UART instance documentation in ESP32 examples - Removed unnecessary comments regarding the UART instance signature in both ESP32-C6 and Xtensa examples for clarity. - Streamlined code documentation to focus on essential information. --- examples/rust-examples/risc-v-esp32-c6/uart/src/main.rs | 2 +- examples/rust-examples/xtensa-esp32/uart/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rust-examples/risc-v-esp32-c6/uart/src/main.rs b/examples/rust-examples/risc-v-esp32-c6/uart/src/main.rs index 9273afe2..e2c761a9 100644 --- a/examples/rust-examples/risc-v-esp32-c6/uart/src/main.rs +++ b/examples/rust-examples/risc-v-esp32-c6/uart/src/main.rs @@ -15,7 +15,7 @@ use martos::{ /// Counter to track processed bytes static BYTE_COUNTER: AtomicU32 = AtomicU32::new(0); -/// UART instance (initialized in setup) - исправленная сигнатура для ESP32-C6 +/// UART instance (initialized in setup) static mut UART_INSTANCE: Option> = None; /// Setup function for task to execute. diff --git a/examples/rust-examples/xtensa-esp32/uart/src/main.rs b/examples/rust-examples/xtensa-esp32/uart/src/main.rs index 84e2cb7d..83344e2e 100644 --- a/examples/rust-examples/xtensa-esp32/uart/src/main.rs +++ b/examples/rust-examples/xtensa-esp32/uart/src/main.rs @@ -15,7 +15,7 @@ use martos::{ /// Counter to track processed bytes static BYTE_COUNTER: AtomicU32 = AtomicU32::new(0); -/// UART instance (initialized in setup) - исправленная сигнатура +/// UART instance (initialized in setup) static mut UART_INSTANCE: Option> = None; /// Setup function for task to execute. From 45800667af5891baade5b5d8ebcb6ac28187c2b3 Mon Sep 17 00:00:00 2001 From: "arkhipov.iv99@mail.ru" Date: Thu, 2 Oct 2025 14:41:09 +0300 Subject: [PATCH 14/14] Update DRAM segment length in ESP32 linker script - Increased the length of the DRAM segment in the ESP32 linker script from 0x10120 to 0x10200 to accommodate additional memory requirements. --- examples/c-examples/xtensa-esp32/ld/esp32.ld | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/c-examples/xtensa-esp32/ld/esp32.ld b/examples/c-examples/xtensa-esp32/ld/esp32.ld index 8888fad5..bab07bf3 100644 --- a/examples/c-examples/xtensa-esp32/ld/esp32.ld +++ b/examples/c-examples/xtensa-esp32/ld/esp32.ld @@ -15,7 +15,7 @@ MEMORY /* TODO: Use human-readable lengths */ /* TODO: Use the full memory map - this is just a test */ iram_seg ( RX ) : ORIGIN = 0x40080400, len = 0xFC00 - dram_seg ( RW ) : ORIGIN = 0x3FFF0000, len = 0x10120 + dram_seg ( RW ) : ORIGIN = 0x3FFF0000, len = 0x10200 } /* Define output sections */