Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ default = []
c-library = []
cooperative = []
preemptive = []
uart = []
network = ["esp-wifi"]
mips64_timer_tests = []
cooperative_tests = []
Expand Down
14 changes: 14 additions & 0 deletions examples/rust-examples/xtensa-esp32/uart/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -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"
17 changes: 17 additions & 0 deletions examples/rust-examples/xtensa-esp32/uart/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"]
37 changes: 37 additions & 0 deletions examples/rust-examples/xtensa-esp32/uart/README.md
Original file line number Diff line number Diff line change
@@ -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
```
2 changes: 2 additions & 0 deletions examples/rust-examples/xtensa-esp32/uart/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[toolchain]
channel = "esp"
99 changes: 99 additions & 0 deletions examples/rust-examples/xtensa-esp32/uart/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<Uart<'static, esp_hal::peripherals::UART2, Blocking>> = 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();
}
15 changes: 15 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -20,10 +22,23 @@ pub fn init_system() {
#[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"))]
#[cfg(feature = "network")]
pub fn get_esp_now() -> EspNow<'static> {
return ports::Port::get_esp_now();
}

#[cfg(feature = "uart")]
pub fn get_uart2() -> <XtensaEsp32 as PortTrait>::Uart2Type {
ports::Port::get_uart2()
}

#[cfg(feature = "uart")]
pub fn get_io() -> <XtensaEsp32 as PortTrait>::IoType {
ports::Port::get_io()
}
23 changes: 23 additions & 0 deletions src/ports/mips64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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()
}
}
36 changes: 36 additions & 0 deletions src/ports/mips64/uart.rs
Original file line number Diff line number Diff line change
@@ -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;
66 changes: 66 additions & 0 deletions src/ports/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() -> 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")]
Expand Down
Loading
Loading