diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6117e453..3696aee4 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: @@ -231,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/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/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 */ 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..e2c761a9 --- /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) +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(); +} 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..83344e2e --- /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::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) +static mut UART_INSTANCE: Option> = None; + +/// Setup function for task to execute. +fn setup_fn() { + println!("UART Echo Setup started"); + + 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"); + } +} + +/// 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(); +} diff --git a/src/lib.rs b/src/lib.rs index 8669aeba..33a96ee6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,11 +15,20 @@ 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")] // Network setup. ports::Port::init_network(); + // Uart + #[cfg(feature = "uart")] + ports::Port::setup_uart(); } #[cfg(any(target_arch = "riscv32", target_arch = "xtensa"))] @@ -27,3 +36,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() -> ports::Uart2Type { + ports::Port::get_uart2() +} + +#[cfg(feature = "uart")] +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 83df6602..c02d776c 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(); @@ -18,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 { @@ -57,4 +61,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..aa34b999 100644 --- a/src/ports/mod.rs +++ b/src/ports/mod.rs @@ -33,16 +33,83 @@ 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() -> Self::Uart2Type; + /// 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() -> Self::IoType; // 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 @@ -90,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 5ff72b00..48afe36a 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(); } @@ -55,11 +63,27 @@ 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() { + 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/hardware_timer.rs b/src/ports/xtensa_esp32/hardware_timer.rs index b8a2376a..29dea568 100644 --- a/src/ports/xtensa_esp32/hardware_timer.rs +++ b/src/ports/xtensa_esp32/hardware_timer.rs @@ -1,22 +1,25 @@ +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); + 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 +27,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 afaef044..f12d3bbe 100644 --- a/src/ports/xtensa_esp32/mod.rs +++ b/src/ports/xtensa_esp32/mod.rs @@ -2,17 +2,26 @@ pub mod hardware_timer; pub mod memory_manager; #[cfg(feature = "network")] pub mod network; +pub mod peripherals; #[cfg(feature = "preemptive")] 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 +88,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/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..d1c2cb9e --- /dev/null +++ b/src/ports/xtensa_esp32/peripherals.rs @@ -0,0 +1,54 @@ +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; +// 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; + +/// 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); + #[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); + } + } +} + +/// 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 new file mode 100644 index 00000000..7197de29 --- /dev/null +++ b/src/ports/xtensa_esp32/uart.rs @@ -0,0 +1,61 @@ +//! UART module for ESP32 - handles UART2 and IO peripherals + +use super::peripherals::init_peripherals; +use super::peripherals::{UartPeriph, PERIFERALS_GPIO, PERIFERALS_IO_MUX, PERIFERALS_UART}; +use core::sync::atomic::{AtomicBool, Ordering}; +use esp_hal::{gpio::*, peripherals::*}; + +// Static variables for UART peripherals +pub static mut IO: Option = None; + +// Flag to ensure UART is initialized only once +static UART_INITIALIZED: AtomicBool = AtomicBool::new(false); + +/// ESP32 UART type alias (depends on arch) +pub type Uart2Type = UartPeriph; + +/// 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() + { + init_peripherals(); + unsafe { + 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); + } + } +} + +/// Get UART2 peripheral instance +/// +/// Returns the UART2 peripheral for configuration. Can only be called once. +pub fn get_uart2() -> UartPeriph { + unsafe { + PERIFERALS_UART + .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) +} 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 73abb88b..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, @@ -416,6 +423,14 @@ impl TaskManagerTrait for PreemptiveTaskManager { fn start_task_manager() -> ! { // TODO: Add idle task implementation Port::setup_interrupt(); - loop {} + loop { + core::hint::spin_loop(); + } + } +} + +impl Default for PreemptiveTaskManager { + fn default() -> Self { + Self::new() } } 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 //! 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;