From 1a37ff58ac46bba0ea2c7f3bd26e830b95fc2132 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:15:46 -0800 Subject: chore(deps): bump actions/download-artifact from 4 to 6 (#10) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 6. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/cargo-vet-pr-comment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cargo-vet-pr-comment.yml b/.github/workflows/cargo-vet-pr-comment.yml index fafb21641..66f27ceab 100644 --- a/.github/workflows/cargo-vet-pr-comment.yml +++ b/.github/workflows/cargo-vet-pr-comment.yml @@ -31,7 +31,7 @@ jobs: if: github.event.workflow_run.event == 'pull_request' steps: - name: 'Download artifact' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: github-token: ${{ secrets.GITHUB_TOKEN }} name: pr -- cgit From f53d4975774dd0c6009ad72692f394dca1083c0b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Nov 2025 09:52:32 -0800 Subject: Add status badges (#14) --- .github/workflows/rolling.yml | 18 ++---------------- README.md | 4 ++++ 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/.github/workflows/rolling.yml b/.github/workflows/rolling.yml index 386c0df5c..1d93cfe25 100644 --- a/.github/workflows/rolling.yml +++ b/.github/workflows/rolling.yml @@ -40,22 +40,8 @@ jobs: strategy: fail-fast: false matrix: - msrv: ["1.85"] # We're relying on namespaced-features, which - # was released in 1.60 - # - # We also depend on `fixed' which requires rust - # 1.71 - # - # Additionally, we depend on embedded-hal-async - # which requires 1.75 - # - # embassy-time requires 1.79 due to - # collapse_debuginfo - # - # embassy upstream switched to rust 1.83 - # - # embedded-services (storage bus) dependency - # requires 1.85 + msrv: ["1.91"] + name: ubuntu / ${{ matrix.msrv }} (${{ matrix.commit }}) steps: - uses: actions/checkout@v5 diff --git a/README.md b/README.md index 8a93b5f4a..86c0413f0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Embassy MCXA276 HAL +[![check](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/check.yml/badge.svg)](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/check.yml) +[![no-std](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/nostd.yml/badge.svg)](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/nostd.yml) +[![rolling](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/rolling.yml/badge.svg)](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/rolling.yml) + A Hardware Abstraction Layer (HAL) for the NXP MCXA276 microcontroller using the Embassy async framework. This HAL provides safe, idiomatic Rust interfaces for GPIO, UART, and OSTIMER peripherals. -- cgit From f4b8ae36bec40a15bedd3c0493e4822f9c5238dd Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Nov 2025 09:58:21 -0800 Subject: Remove a lot of text from README.md (#15) New text will be added as necessary. Co-authored-by: Felipe Balbi --- README.md | 357 +------------------------------------------------------------- 1 file changed, 2 insertions(+), 355 deletions(-) diff --git a/README.md b/README.md index 86c0413f0..6e7d61c0e 100644 --- a/README.md +++ b/README.md @@ -1,366 +1,13 @@ -# Embassy MCXA276 HAL +# Embassy MCXA256 HAL [![check](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/check.yml/badge.svg)](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/check.yml) [![no-std](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/nostd.yml/badge.svg)](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/nostd.yml) [![rolling](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/rolling.yml/badge.svg)](https://github.com/OpenDevicePartnership/embassy-mcxa/actions/workflows/rolling.yml) -A Hardware Abstraction Layer (HAL) for the NXP MCXA276 microcontroller +A Hardware Abstraction Layer (HAL) for the NXP MCXA256 microcontroller using the Embassy async framework. This HAL provides safe, idiomatic Rust interfaces for GPIO, UART, and OSTIMER peripherals. -## Prerequisites - -### Ubuntu/Debian Setup - -```bash -# Install Rust toolchain -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -source ~/.cargo/env - -# Add target for MCXA276 (ARM Cortex-M33) -rustup target add thumbv8m.main-none-eabihf - -# Install required tools -sudo apt update -sudo apt install -y gdb-multiarch curl wget - -# Install probe-rs for running and debugging -cargo install probe-rs --features cli -``` - -### Windows Setup - -- Install Rust via https://rustup.rs (default options are fine) -- Add the MCXA276 target: - ```powershell - rustup target add thumbv8m.main-none-eabihf - ``` -- Install probe-rs CLI (we will use it directly; no GDB required): - ```powershell - cargo install probe-rs --features cli - ``` -- Install a serial terminal (e.g., Tera Term): https://ttssh2.osdn.jp/ -- USB drivers: Windows 10/11 usually picks up the board as a USB CDC device automatically (COM port) - -### Hardware Requirements - -- NXP FRDM-MCXA276 development board -- Debug probe (CMSIS-DAP compatible) -- USB cable for power and programming - -## Examples - -This HAL includes several examples demonstrating different peripherals: - -### GPIO Examples - -#### `blink` -Blinks an LED connected to GPIO pin. Demonstrates basic GPIO output operations. - -### UART Examples - -#### `hello` -Interactive UART2 demo: prints a banner and supports `help`, `echo `, `hex `. - -### OSTIMER Examples - -#### `ostimer_alarm` - -Demonstrates setting and waiting for OSTIMER alarms. - -#### `ostimer_async` -Shows asynchronous OSTIMER operations with Embassy's async runtime. - -#### `ostimer_counter` -Demonstrates OSTIMER counter functionality. - -#### `ostimer_race_test` -Advanced example testing OSTIMER race conditions and synchronization. - -### RTC Example - -#### `rtc_alarm` -Demonstrates how to enable and use the RTC to generate an interrupt after 10seconds. - -## Build and Run - -### Using probe-rs - -All examples require specifying your debug probe. First, find your probe ID: - -```bash -probe-rs list -``` - -Then run examples with your probe ID (replace `1fc9:0143:H3AYDQVQMTROB` with your actual probe): - -```bash -# GPIO blink example -PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "gpio ostimer0" --example blink - - - -# UART hello example -PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example hello - -# OSTIMER examples -PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example ostimer_alarm -PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example ostimer_async -PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example ostimer_counter -PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example ostimer_race_test - -# RTC example -PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 rtc0" --example rtc_alarm -``` - -**Note:** All examples run from RAM, not flash memory. They are loaded directly into RAM for faster development iteration. - -**Important:** After pressing the RESET button on the board, the first `cargo run` attempt may fail with a connection error. This is expected - simply run the command again and it will work. The run.sh script now properly sets the Vector Table Offset Register (VTOR) to point to the RAM-based vector table, ensuring the correct stack pointer and reset vector are used. - -```console -smw016108@smw016108:~/Downloads/nxp/rust/uart/embassy-mcxa276$ PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --release --features "gpio ostimer0" --example blink - Finished `release` profile [optimized + debuginfo] target(s) in 0.07s - Running `/home/smw016108/Downloads/nxp/rust/uart/embassy-mcxa276/./run.sh target/thumbv8m.main-none-eabihf/release/examples/blink` -probe-rs gdb server failed to connect to target. Log: ------ probe-rs gdb log ----- - Error: Connecting to the chip was unsuccessful. - - Caused by: - 0: An ARM specific error occurred. - 1: Error using access port FullyQualifiedApAddress { dp: Default, ap: V1(0) }. - 2: Failed to read register DRW at address 0xd0c - 3: An error occurred in the communication with an access port or debug port. - 4: Target device responded with a FAULT response to the request. -smw016108@smw016108:~/Downloads/nxp/rust/uart/embassy-mcxa276$ PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --release --features "gpio ostimer0" --example blink - Finished `release` profile [optimized + debuginfo] target(s) in 0.02s - Running `/home/smw016108/Downloads/nxp/rust/uart/embassy-mcxa276/./run.sh target/thumbv8m.main-none-eabihf/release/examples/blink` -``` - -### Additional UART Examples - -#### `uart_interrupt` -Interrupt-driven UART2 echo. Type in the serial terminal; each byte is echoed back from the IRQ handler path. - -#### `lpuart_polling` -Blocking TX/RX echo over UART2 using the simple polling driver. - -#### `lpuart_buffered` -Async buffered driver with separate TX/RX tasks; echoes typed characters in chunks. - -Pins: UART2 TX=P2_2, RX=P2_3 (ALT3), 115200 8N1. - -### ADC Examples - -#### `adc_polling` -Configures ADC1 channel A8 (pin P1_10) and prints conversion values to UART2 periodically. - -#### `adc_interrupt` -Triggers a conversion and signals completion via ADC1 interrupt, printing a notification on UART2. - -```console -0x20002040 in ?? () -Supported Commands: - - info - print session information - reset - reset target - reset halt - reset target and halt afterwards - -Loading section .vector_table, size 0x224 lma 0x20000000 -Loading section .text, size 0x97e lma 0x20000224 -Loading section .Reset, size 0x58 lma 0x20000ba4 -Loading section .rodata, size 0x28 lma 0x20000bfc -Start address 0x20000ba4, load size 3106 -Transfer rate: 13 KB/sec, 776 bytes/write. -``` - -then I see the LED blinking. I press CTRL+C to exit. It will show me ^C - -```console -Program received signal SIGINT, Interrupt. -0x20000880 in embassy_executor::arch::thread::Executor::run (self=0x200027e8, init=...) at /home/smw016108/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/embassy-executor-0.9.1/src/arch/cortex_m.rs:106 -106 asm!("wfe"); -[Inferior 1 (process 1) detached] -Program loaded and started (no reset) -smw016108@smw016108:~/Downloads/nxp/rust/uart/embassy-mcxa276$ \ - -Then I press RESET again and I want to run another example, like ostimer_alarm. I open the console using sudo picocom -b 115200 /dev/ttyACM0 and I start running the example: - -smw016108@smw016108:~/Downloads/nxp/rust/uart/embassy-mcxa276$ PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example ostimer_alarm - Finished `dev` profile [optimized + debuginfo] target(s) in 0.02s - Running `/home/smw016108/Downloads/nxp/rust/uart/embassy-mcxa276/./run.sh target/thumbv8m.main-none-eabihf/debug/examples/ostimer_alarm` -probe-rs gdb server failed to connect to target. Log: ------ probe-rs gdb log ----- - Error: Connecting to the chip was unsuccessful. - - Caused by: - 0: An ARM specific error occurred. - 1: Error using access port FullyQualifiedApAddress { dp: Default, ap: V1(0) }. - 2: Failed to read register DRW at address 0xd0c - 3: An error occurred in the communication with an access port or debug port. - 4: Target device responded with a FAULT response to the request. -smw016108@smw016108:~/Downloads/nxp/rust/uart/embassy-mcxa276$ PROBE=1fc9:0143:H3AYDQVQMTROB cargo run --features "lpuart2 ostimer0" --example ostimer_alarm - Finished `dev` profile [optimized + debuginfo] target(s) in 0.02s - Running `/home/smw016108/Downloads/nxp/rust/uart/embassy-mcxa276/./run.sh target/thumbv8m.main-none-eabihf/debug/examples/ostimer_alarm` -0x20002040 in core::panicking::panic_const::panic_const_mul_overflow () at library/core/src/panicking.rs:175 -warning: 175 library/core/src/panicking.rs: No such file or directory -Supported Commands: - - info - print session information - reset - reset target - reset halt - reset target and halt afterwards - -Loading section .vector_table, size 0x224 lma 0x20000000 -Loading section .text, size 0x2226 lma 0x20000224 -Loading section .Reset, size 0x58 lma 0x2000244c -Loading section .rodata, size 0x6dc lma 0x200024a4 -Start address 0x2000244c, load size 11134 -Transfer rate: 16 KB/sec, 1855 bytes/write. -``` - -I can see in the console - -```console -OSTIMER Alarm Example -Scheduling alarm for 2 seconds... -Alarm scheduled successfully -Alarm expired! Callback executed. -Scheduling another alarm for 3 seconds... -Alarm scheduled. Waiting 1 second then canceling... -Alarm canceled -Alarm was successfully canceled -Example complete -``` - -then I press CTRL+C to stop running - -```console -^C -Program received signal SIGINT, Interrupt. -0x20000e64 in embassy_executor::arch::thread::Executor::run (self=0x200027e8, init=...) at /home/smw016108/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/embassy-executor-0.9.1/src/arch/cortex_m.rs:106 -106 asm!("wfe"); -[Inferior 1 (process 1) detached] -Program loaded and started (no reset) -smw016108@smw016108:~/Downloads/nxp/rust/uart/embassy-mcxa276$ -``` - -### Windows: Running examples (RAM, no RTT/defmt) - -Important: On Windows, do not use `cargo run` because `.cargo/config.toml` sets a Linux-only runner (`./run.sh`). Instead, use `probe-rs run` directly. - -1) Find your probe and COM port -- List probes: - - ```console - probe-rs list - ``` -- If multiple probes are attached, set the specific one (replace with your ID): - - ```console - $env:PROBE_RS_PROBE = "1366:0101:000600110607" - ``` - -- Check Windows Device Manager → Ports (COM & LPT) for the board’s COM port. - -2) Build the example - -```console -cargo build --example hello --features "lpuart2" -``` - -3) Run from RAM with probe-rs - -```console -probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/hello -``` -You will see a short probe-rs warning like "unknown variant, try to set watch point"; it’s harmless. - -4) View output in Tera Term -- Open Tera Term, select the board’s COMx port, 115200 8N1 -- Expected behavior per example: - - hello: prints a banner; simple UART output - - lpuart_polling / lpuart_buffered / uart_interrupt: echo typed characters - - adc_polling: prints ADC values periodically (ADC1 channel A8 on P1_10) - - adc_interrupt: prints "*** ADC interrupt TRIGGERED! ***" upon conversion completion - - blink: LED on PIO3_18 blinks "SOS" pattern - - rtc_alarm: schedules, cancels and reports alarm events on UART - -Notes -- All examples run from RAM (not flashed). Reset clears the program. -- If the first attempt after a reset fails to connect, just run the command again. -- UART2 pins: TX=P2_2, RX=P2_3 (ALT3), 115200 8N1. - -Quick commands for other examples: - -```console -# Build -cargo build --example blink --features "gpio ostimer0" -cargo build --example lpuart_polling --features "lpuart2 ostimer0" -cargo build --example lpuart_buffered --features "lpuart2 ostimer0" -cargo build --example uart_interrupt --features "lpuart2 ostimer0" -cargo build --example rtc_alarm --features "lpuart2 rtc0" -cargo build --example adc_polling --features "adc1 lpuart2" -cargo build --example adc_interrupt --features "adc1 lpuart2" - -# Run (RAM) -probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/blink -probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/lpuart_polling -probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/lpuart_buffered -probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/uart_interrupt -probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/rtc_alarm -probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/adc_polling -probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/adc_interrupt -probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/ostimer_alarm -probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/ostimer_async -probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/ostimer_counter -probe-rs run --chip MCXA276 --protocol swd --speed 1000 target/thumbv8m.main-none-eabihf/debug/examples/ostimer_race_test -``` - -How I tested on Windows -- Windows 11; Rust stable; probe-rs 0.29.x -- Built each example as above; ran with `probe-rs run` (RAM execution) -- Observed UART output in Tera Term at 115200 8N1; all examples behaved as expected -- No RTT/defmt used; purely UART or LED observation - -### Build Only - -To build without running: - -```console -cargo build --features "gpio ostimer0" --example blink -cargo build --features "lpuart2 ostimer0" --example hello -cargo build --features "lpuart2 ostimer0" --example ostimer_alarm -cargo build --features "lpuart2 rtc0" --example rtc_alarm -# etc. -``` - -## Development Notes - -### Critical Fix: MCXA276 Interrupt Vector Table - - -Update (SVD 25.06.00, mcxa-pac a9dd33): No manual PAC edits are required anymore. OS_EVENT and WAKETIMER0 are present and the vector table is correct. The section below is kept for historical context. -**Problem:** The OSTIMER examples crashed during interrupt handling with a hardfault (SP=0x00000000). Investigation revealed the OS_EVENT interrupt vector was NULL in the vector table, causing the CPU to jump to address 0 when OSTIMER interrupts fired. - -**Root Cause:** The `mcxa276-pac/src/lib.rs` file (generated from the SVD file) was missing the `WAKETIMER0` interrupt handler declaration. This caused the `__INTERRUPTS` array to have an off-by-one error, placing OS_EVENT at IRQ 58 instead of the correct IRQ 57 position. - -**Solution:** Manually edited `mcxa276-pac/src/lib.rs` to add the missing WAKETIMER0 interrupt: - -1. Added `fn WAKETIMER0()` to the `extern "C"` block -2. Fixed the `__INTERRUPTS: [Vector; 122]` array sequence: - - Changed from: `LPTMR0, _reserved, _reserved, OS_EVENT, _reserved, UTICK0, ...` - - Changed to: `LPTMR0, _reserved, OS_EVENT, WAKETIMER0, UTICK0, WWDT0, _reserved, ADC0, ...` -3. Added `WAKETIMER0 = 58` to the `Interrupt` enum - -**Verification:** Binary analysis confirmed OS_EVENT is now at the correct position: -- IRQ 57 = word 73 = offset 0x124 in vector table -- OS_EVENT handler: 0x20000BB1 (verified with `arm-none-eabi-objdump`) - -**Note:** This is likely an issue with the NXP MCXA276.svd file or svd2rust generation. The WAKETIMER0 peripheral exists in the PAC but the interrupt handler was missing. Future regeneration of the PAC from SVD may require reapplying this fix. - -### Warning: Avoid `#[inline(always)]` in Performance-Critical Code - -Using `#[inline(always)]` can cause the Rust compiler to generate incorrect assembly, leading to register corruption or unexpected behavior. For example, in tight polling loops like those in the OSTIMER driver, this attribute may result in invalid instructions that zero registers (e.g., `movs r1, r0` causing r1=0), triggering hardfaults. - ## License This project is licensed under MIT OR Apache-2.0. -- cgit From 77b2c602a60e41c7c977003a6d40367ac285930e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Nov 2025 13:17:44 -0800 Subject: Move examples to a package of their own (#16) * Move examples to a package of their own * cargo +nightly fmt * Add missing safety doc * cargo clippy examples * fmt again --------- Co-authored-by: Felipe Balbi --- .cargo/config.toml | 19 -- Cargo.lock | 24 +- Cargo.toml | 61 +--- build.rs | 20 -- examples/.cargo/config.toml | 17 + examples/.gitignore | 1 + examples/Cargo.lock | 625 ++++++++++++++++++++++++++++++++++ examples/Cargo.toml | 21 ++ examples/adc_interrupt.rs | 89 ----- examples/adc_polling.rs | 79 ----- examples/blink.rs | 84 ----- examples/build.rs | 20 ++ examples/common/mod.rs | 45 --- examples/hello.rs | 114 ------- examples/lpuart_buffered.rs | 77 ----- examples/lpuart_polling.rs | 53 --- examples/memory.x | 5 + examples/ostimer_alarm.rs | 111 ------ examples/ostimer_async.rs | 57 ---- examples/ostimer_counter.rs | 128 ------- examples/ostimer_race_test.rs | 396 --------------------- examples/rtc_alarm.rs | 87 ----- examples/src/bin/adc_interrupt.rs | 86 +++++ examples/src/bin/adc_polling.rs | 76 +++++ examples/src/bin/blink.rs | 78 +++++ examples/src/bin/hello.rs | 111 ++++++ examples/src/bin/lpuart_buffered.rs | 76 +++++ examples/src/bin/lpuart_polling.rs | 51 +++ examples/src/bin/ostimer_alarm.rs | 106 ++++++ examples/src/bin/ostimer_async.rs | 52 +++ examples/src/bin/ostimer_counter.rs | 125 +++++++ examples/src/bin/ostimer_race_test.rs | 393 +++++++++++++++++++++ examples/src/bin/rtc_alarm.rs | 84 +++++ examples/src/bin/uart_interrupt.rs | 66 ++++ examples/src/common/mod.rs | 45 +++ examples/src/lib.rs | 63 ++++ examples/uart_interrupt.rs | 69 ---- memory.x | 10 - src/lib.rs | 44 +-- 39 files changed, 2127 insertions(+), 1541 deletions(-) delete mode 100644 .cargo/config.toml delete mode 100644 build.rs create mode 100644 examples/.cargo/config.toml create mode 100644 examples/.gitignore create mode 100644 examples/Cargo.lock create mode 100644 examples/Cargo.toml delete mode 100644 examples/adc_interrupt.rs delete mode 100644 examples/adc_polling.rs delete mode 100644 examples/blink.rs create mode 100644 examples/build.rs delete mode 100644 examples/common/mod.rs delete mode 100644 examples/hello.rs delete mode 100644 examples/lpuart_buffered.rs delete mode 100644 examples/lpuart_polling.rs create mode 100644 examples/memory.x delete mode 100644 examples/ostimer_alarm.rs delete mode 100644 examples/ostimer_async.rs delete mode 100644 examples/ostimer_counter.rs delete mode 100644 examples/ostimer_race_test.rs delete mode 100644 examples/rtc_alarm.rs create mode 100644 examples/src/bin/adc_interrupt.rs create mode 100644 examples/src/bin/adc_polling.rs create mode 100644 examples/src/bin/blink.rs create mode 100644 examples/src/bin/hello.rs create mode 100644 examples/src/bin/lpuart_buffered.rs create mode 100644 examples/src/bin/lpuart_polling.rs create mode 100644 examples/src/bin/ostimer_alarm.rs create mode 100644 examples/src/bin/ostimer_async.rs create mode 100644 examples/src/bin/ostimer_counter.rs create mode 100644 examples/src/bin/ostimer_race_test.rs create mode 100644 examples/src/bin/rtc_alarm.rs create mode 100644 examples/src/bin/uart_interrupt.rs create mode 100644 examples/src/common/mod.rs create mode 100644 examples/src/lib.rs delete mode 100644 examples/uart_interrupt.rs delete mode 100644 memory.x diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 3286be01c..000000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,19 +0,0 @@ -[build] -target = "thumbv8m.main-none-eabihf" - -[target.thumbv8m.main-none-eabihf] -runner = "./run.sh" -# Use our custom ram.ld for RAM-execution -rustflags = ["-C", "link-arg=-Tdefmt.x", "-C", "link-arg=-Tram.ld"] - -[alias] -# Build examples with defmt+RTT and LPUART2 enabled -build-defmt = ["build", "--features", "defmt defmt-rtt lpuart2", "--examples"] -# Blocking run (uses run.sh). Good for one-terminal flash+run. -run-defmt = ["run", "--features", "defmt defmt-rtt lpuart2", "--example", "hello"] -# Non-blocking flash to RAM via J-Link; frees probe for RTT attach -flash-nb = ["!./tools/run_jlink_noblock.sh", "target/thumbv8m.main-none-eabihf/debug/examples/hello", "1366:0101:000600110607", "MCXA276", "500"] -# Attach-only viewer that decodes defmt over RTT using J-Link -# Requires PROBE_RS_PROBE exported (or it will prompt) -defmt-attach = ["embed", "--features", "defmt defmt-rtt lpuart2", "--example", "hello"] - diff --git a/Cargo.lock b/Cargo.lock index 953a71ee0..e9f4d3165 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,16 +147,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "defmt-rtt" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" -dependencies = [ - "critical-section", - "defmt", -] - [[package]] name = "document-features" version = "0.2.12" @@ -232,14 +222,13 @@ dependencies = [ ] [[package]] -name = "embassy-mcxa276" +name = "embassy-mcxa" version = "0.1.0" dependencies = [ "cortex-m", "cortex-m-rt", "critical-section", "defmt", - "defmt-rtt", "embassy-embedded-hal", "embassy-executor", "embassy-hal-internal", @@ -255,7 +244,6 @@ dependencies = [ "heapless", "mcxa-pac", "nb 1.1.0", - "panic-probe", "paste", ] @@ -446,16 +434,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "panic-probe" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" -dependencies = [ - "cortex-m", - "defmt", -] - [[package]] name = "paste" version = "1.0.15" diff --git a/Cargo.toml b/Cargo.toml index 19d1c7174..3913104fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,12 @@ [package] -name = "embassy-mcxa276" +name = "embassy-mcxa" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" -description = "Embassy Hardware Abstraction Layer (HAL) for NXP MCXA276" -keywords = ["embedded", "hal", "nxp", "mcxa276", "embassy"] +description = "Embassy Hardware Abstraction Layer (HAL) for NXP MCXA series of MCUs" +keywords = ["embedded", "hal", "nxp", "mcxa", "embassy"] categories = ["embedded", "hardware-support", "no-std"] -[lib] -name = "embassy_mcxa276" - [dependencies] cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m-rt = { version = "0.7", features = ["device"] } @@ -35,11 +32,6 @@ embedded-hal-nb = { version = "1.0" } embedded-io-async = { version = "0.6.1" } nb = "1.1.0" -[dev-dependencies] -defmt = "1.0" -defmt-rtt = "1.0" -panic-probe = { version = "1.0", features = ["print-defmt"] } - [features] default = [] @@ -50,50 +42,3 @@ defmt = ["dep:defmt"] rt = [] unstable-pac = [] - -[[example]] -name = "hello" - -[[example]] -name = "blink" - -[[example]] -name = "uart_interrupt" - -[[example]] -name = "ostimer_alarm" - -[[example]] -name = "ostimer_async" - -[[example]] -name = "ostimer_counter" - -[[example]] -name = "ostimer_race_test" - -[[example]] -name = "lpuart_polling" - -[[example]] -name = "lpuart_buffered" - -[[example]] -name = "rtc_alarm" - -[[example]] -name = "adc_polling" - -[[example]] -name = "adc_interrupt" - -[profile.release] -debug = 2 -lto = true -opt-level = "s" - -[profile.dev] -debug = 2 -opt-level = 1 - - diff --git a/build.rs b/build.rs deleted file mode 100644 index 645843590..000000000 --- a/build.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::env; -use std::fs::File; -use std::io::Write; -use std::path::PathBuf; - -fn main() { - let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); - - // Generate memory.x - put "FLASH" at start of RAM, RAM after "FLASH" - // cortex-m-rt expects FLASH for code, RAM for data/bss/stack - // Both are in RAM, but separated to satisfy cortex-m-rt's expectations - // MCXA276 has 128KB RAM total - File::create(out.join("memory.x")) - .unwrap() - .write_all(b"/* MCXA276 RAM-execution: FLASH region holds code, RAM region for data/stack */\nMEMORY { FLASH : ORIGIN = 0x20000000, LENGTH = 64K\n RAM : ORIGIN = 0x20010000, LENGTH = 64K }\n") - .unwrap(); - - println!("cargo:rustc-link-search={}", out.display()); - println!("cargo:rerun-if-changed=memory.x"); -} diff --git a/examples/.cargo/config.toml b/examples/.cargo/config.toml new file mode 100644 index 000000000..ecb11be64 --- /dev/null +++ b/examples/.cargo/config.toml @@ -0,0 +1,17 @@ +[target.thumbv8m.main-none-eabihf] +runner = 'probe-rs run --chip MCXA276 --preverify --verify' + +rustflags = [ + "-C", "linker=flip-link", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + "-C", "link-arg=--nmagic", +] + +[build] +target = "thumbv8m.main-none-eabihf" # Cortex-M33 + +[env] +DEFMT_LOG = "trace" diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 000000000..2f7896d1d --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/examples/Cargo.lock b/examples/Cargo.lock new file mode 100644 index 000000000..b774aff97 --- /dev/null +++ b/examples/Cargo.lock @@ -0,0 +1,625 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield", + "critical-section", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d5a25c99d89c40f5676bec8cefe0614f17f0f40e916f98e345dae941807f9e" +dependencies = [ + "critical-section", + "defmt", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "embassy-embedded-hal" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "554e3e840696f54b4c9afcf28a0f24da431c927f4151040020416e7393d6d0d8" +dependencies = [ + "embassy-futures", + "embassy-hal-internal", + "embassy-sync", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-storage", + "embedded-storage-async", + "nb 1.1.0", +] + +[[package]] +name = "embassy-executor" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06070468370195e0e86f241c8e5004356d696590a678d47d6676795b2e439c6b" +dependencies = [ + "cortex-m", + "critical-section", + "document-features", + "embassy-executor-macros", + "embassy-executor-timer-queue", +] + +[[package]] +name = "embassy-executor-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfdddc3a04226828316bf31393b6903ee162238576b1584ee2669af215d55472" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "embassy-executor-timer-queue" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc328bf943af66b80b98755db9106bf7e7471b0cf47dc8559cd9a6be504cc9c" + +[[package]] +name = "embassy-futures" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" + +[[package]] +name = "embassy-hal-internal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95285007a91b619dc9f26ea8f55452aa6c60f7115a4edc05085cd2bd3127cd7a" +dependencies = [ + "cortex-m", + "critical-section", + "num-traits", +] + +[[package]] +name = "embassy-mcxa" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "defmt", + "embassy-embedded-hal", + "embassy-executor", + "embassy-hal-internal", + "embassy-sync", + "embassy-time", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "embedded-io-async", + "heapless 0.8.0", + "mcxa-pac", + "nb 1.1.0", + "paste", +] + +[[package]] +name = "embassy-mcxa-examples" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "critical-section", + "defmt", + "defmt-rtt", + "embassy-embedded-hal", + "embassy-executor", + "embassy-mcxa", + "embassy-sync", + "embassy-time", + "embassy-time-driver", + "embedded-io-async", + "heapless 0.9.2", + "panic-probe", +] + +[[package]] +name = "embassy-sync" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b" +dependencies = [ + "cfg-if", + "critical-section", + "embedded-io-async", + "futures-core", + "futures-sink", + "heapless 0.8.0", +] + +[[package]] +name = "embassy-time" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65" +dependencies = [ + "cfg-if", + "critical-section", + "document-features", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-core", +] + +[[package]] +name = "embassy-time-driver" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6" +dependencies = [ + "document-features", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "embedded-io-async" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" +dependencies = [ + "embedded-io", +] + +[[package]] +name = "embedded-storage" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" + +[[package]] +name = "embedded-storage-async" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1763775e2323b7d5f0aa6090657f5e21cfa02ede71f5dc40eead06d64dcd15cc" +dependencies = [ + "embedded-storage", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "mcxa-pac" +version = "0.1.0" +source = "git+https://github.com/OpenDevicePartnership/mcxa-pac?rev=3ab4c868f75a9240bb8fdce24982d34f2273aabf#3ab4c868f75a9240bb8fdce24982d34f2273aabf" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "vcell", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "panic-probe" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" +dependencies = [ + "cortex-m", + "defmt", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] diff --git a/examples/Cargo.toml b/examples/Cargo.toml new file mode 100644 index 000000000..6b100de42 --- /dev/null +++ b/examples/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "embassy-mcxa-examples" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + +[dependencies] +cortex-m = { version = "0.7", features = ["critical-section-single-core"] } +cortex-m-rt = { version = "0.7", features = ["device"] } +critical-section = "1.2.0" +defmt = "1.0" +defmt-rtt = "1.0" +embassy-embedded-hal = "0.5.0" +embassy-executor = { version = "0.9.0", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"], default-features = false } +embassy-mcxa = { path = "../", features = ["defmt", "rt", "unstable-pac"] } +embassy-sync = "0.7.2" +embassy-time = "0.5.0" +embassy-time-driver = "0.2.1" +embedded-io-async = "0.6.1" +heapless = "0.9.2" +panic-probe = { version = "1.0", features = ["print-defmt"] } diff --git a/examples/adc_interrupt.rs b/examples/adc_interrupt.rs deleted file mode 100644 index dc82cfd30..000000000 --- a/examples/adc_interrupt.rs +++ /dev/null @@ -1,89 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; -use hal::uart; -use mcxa_pac::adc1::cfg::{Pwrsel, Refsel}; -use mcxa_pac::adc1::cmdl1::{Adch, Mode}; -use mcxa_pac::adc1::ctrl::CalAvgs; -use mcxa_pac::adc1::tctrl::Tcmd; -use {cortex_m, embassy_mcxa276 as hal}; -mod common; - -use hal::{bind_interrupts, InterruptExt}; -use {defmt_rtt as _, panic_probe as _}; - -bind_interrupts!(struct Irqs { - ADC1 => hal::adc::AdcHandler; -}); - -#[used] -#[no_mangle] -static KEEP_ADC: unsafe extern "C" fn() = ADC1; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - unsafe { - common::init_uart2(hal::pac()); - } - - let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; - let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); - - uart.write_str_blocking("\r\n=== ADC interrupt Example ===\r\n"); - - unsafe { - common::init_adc(hal::pac()); - } - - let adc_config = LpadcConfig { - enable_in_doze_mode: true, - conversion_average_mode: CalAvgs::Average128, - enable_analog_preliminary: true, - power_up_delay: 0x80, - reference_voltage_source: Refsel::Option3, - power_level_mode: Pwrsel::Lowest, - trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, - enable_conv_pause: false, - conv_pause_delay: 0, - fifo_watermark: 0, - }; - let adc = hal::adc::Adc::::new(p.ADC1, adc_config); - - adc.do_offset_calibration(); - adc.do_auto_calibration(); - - let mut conv_command_config = adc.get_default_conv_command_config(); - conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; - conv_command_config.conversion_resolution_mode = Mode::Data16Bits; - adc.set_conv_command_config(1, &conv_command_config); - - let mut conv_trigger_config = adc.get_default_conv_trigger_config(); - conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; - conv_trigger_config.enable_hardware_trigger = false; - adc.set_conv_trigger_config(0, &conv_trigger_config); - - uart.write_str_blocking("\r\n=== ADC configuration done... ===\r\n"); - - adc.enable_interrupt(0x1); - - unsafe { - hal::interrupt::ADC1.enable(); - } - - unsafe { - cortex_m::interrupt::enable(); - } - - loop { - adc.do_software_trigger(1); - while !adc.is_interrupt_triggered() { - // Wait until the interrupt is triggered - } - uart.write_str_blocking("\r\n*** ADC interrupt TRIGGERED! ***\r\n"); - //TBD need to print the value - } -} diff --git a/examples/adc_polling.rs b/examples/adc_polling.rs deleted file mode 100644 index 4b5f9422d..000000000 --- a/examples/adc_polling.rs +++ /dev/null @@ -1,79 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa276 as hal; -use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; -use hal::uart; -use mcxa_pac::adc1::cfg::{Pwrsel, Refsel}; -use mcxa_pac::adc1::cmdl1::{Adch, Mode}; -use mcxa_pac::adc1::ctrl::CalAvgs; -use mcxa_pac::adc1::tctrl::Tcmd; - -mod common; - -use core::fmt::Write; - -use heapless::String; -use {defmt_rtt as _, panic_probe as _}; - -const G_LPADC_RESULT_SHIFT: u32 = 0; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - unsafe { - common::init_uart2(hal::pac()); - } - - let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; - let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); - - uart.write_str_blocking("\r\n=== ADC polling Example ===\r\n"); - - unsafe { - common::init_adc(hal::pac()); - } - - let adc_config = LpadcConfig { - enable_in_doze_mode: true, - conversion_average_mode: CalAvgs::Average128, - enable_analog_preliminary: true, - power_up_delay: 0x80, - reference_voltage_source: Refsel::Option3, - power_level_mode: Pwrsel::Lowest, - trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, - enable_conv_pause: false, - conv_pause_delay: 0, - fifo_watermark: 0, - }; - let adc = hal::adc::Adc::::new(p.ADC1, adc_config); - - adc.do_offset_calibration(); - adc.do_auto_calibration(); - - let mut conv_command_config = adc.get_default_conv_command_config(); - conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; - conv_command_config.conversion_resolution_mode = Mode::Data16Bits; - adc.set_conv_command_config(1, &conv_command_config); - - let mut conv_trigger_config = adc.get_default_conv_trigger_config(); - conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; - conv_trigger_config.enable_hardware_trigger = false; - adc.set_conv_trigger_config(0, &conv_trigger_config); - - uart.write_str_blocking("\r\n=== ADC configuration done... ===\r\n"); - - loop { - adc.do_software_trigger(1); - let mut result: Option = None; - while result.is_none() { - result = hal::adc::get_conv_result(); - } - let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT; - let mut buf: String<16> = String::new(); // adjust size as needed - write!(buf, "\r\nvalue: {}\r\n", value).unwrap(); - uart.write_str_blocking(&buf); - } -} diff --git a/examples/blink.rs b/examples/blink.rs deleted file mode 100644 index 564353d5c..000000000 --- a/examples/blink.rs +++ /dev/null @@ -1,84 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa276 as hal; -use embassy_time::{Duration, Timer}; -use hal::gpio::pins::PIO3_18; -use hal::gpio::{Level, Output}; - -mod common; - -use embassy_mcxa276::bind_interrupts; - -// Bind only OS_EVENT for timer interrupts -bind_interrupts!(struct Irqs { - OS_EVENT => hal::ostimer::time_driver::OsEventHandler; -}); - -#[used] -#[no_mangle] -static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let _p = hal::init(hal::config::Config::default()); - - // Board-style init: enable LED GPIO/PORT clocks used by blink - unsafe { - common::init_led(hal::pac()); - } - // Initialize OSTIMER for async timing - unsafe { - common::init_ostimer0(hal::pac()); - } - - // Initialize embassy-time global driver backed by OSTIMER0 - hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000); - - // Configure LED pin for GPIO mode - PIO3_18::set_mux_gpio(); - - let mut led = Output::new(PIO3_18::degrade(), Level::High); - - // Complex blinking pattern: SOS in Morse code - // S: ... (3 short) - // O: --- (3 long) - // S: ... (3 short) - // With pauses between letters and words - - loop { - // S: three short blinks - for _ in 0..3 { - led.set_low(); - Timer::after(Duration::from_millis(150)).await; - led.set_high(); - Timer::after(Duration::from_millis(150)).await; - } - - // Pause between letters - Timer::after(Duration::from_millis(300)).await; - - // O: three long blinks - for _ in 0..3 { - led.set_low(); - Timer::after(Duration::from_millis(450)).await; - led.set_high(); - Timer::after(Duration::from_millis(150)).await; - } - - // Pause between letters - Timer::after(Duration::from_millis(300)).await; - - // S: three short blinks - for _ in 0..3 { - led.set_low(); - Timer::after(Duration::from_millis(150)).await; - led.set_high(); - Timer::after(Duration::from_millis(150)).await; - } - - // Long pause between words (SOS repeats) - Timer::after(Duration::from_millis(1000)).await; - } -} diff --git a/examples/build.rs b/examples/build.rs new file mode 100644 index 000000000..f076bba9f --- /dev/null +++ b/examples/build.rs @@ -0,0 +1,20 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + // Generate memory.x - put "FLASH" at start of RAM, RAM after "FLASH" + // cortex-m-rt expects FLASH for code, RAM for data/bss/stack + // Both are in RAM, but separated to satisfy cortex-m-rt's expectations + // MCXA256 has 128KB RAM total + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=memory.x"); +} diff --git a/examples/common/mod.rs b/examples/common/mod.rs deleted file mode 100644 index 8c52c8e86..000000000 --- a/examples/common/mod.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! Shared board-specific helpers for the FRDM-MCXA276 examples. -//! These live with the examples so the HAL stays generic. - -use hal::{clocks, pins, reset}; -use {embassy_mcxa276 as hal, panic_probe as _}; - -/// Initialize clocks and pin muxing for UART2 debug console. -/// Safe to call multiple times; writes are idempotent for our use. -#[allow(dead_code)] -pub unsafe fn init_uart2(p: &mcxa_pac::Peripherals) { - clocks::ensure_frolf_running(p); - clocks::enable_uart2_port2(p); - reset::release_reset_port2(p); - reset::release_reset_lpuart2(p); - pins::configure_uart2_pins_port2(); - clocks::select_uart2_clock(p); -} - -/// Initialize clocks for the LED GPIO/PORT used by the blink example. -#[allow(dead_code)] -pub unsafe fn init_led(p: &mcxa_pac::Peripherals) { - clocks::enable_led_port(p); - reset::release_reset_gpio3(p); - reset::release_reset_port3(p); -} - -/// Initialize clocks for OSTIMER0 (1 MHz source). -#[allow(dead_code)] -pub unsafe fn init_ostimer0(p: &mcxa_pac::Peripherals) { - clocks::ensure_frolf_running(p); - clocks::enable_ostimer0(p); - reset::release_reset_ostimer0(p); - clocks::select_ostimer0_clock_1m(p); -} - -/// Initialize clocks and pin muxing for ADC. -#[allow(dead_code)] -pub unsafe fn init_adc(p: &mcxa_pac::Peripherals) { - clocks::ensure_frolf_running(p); - clocks::enable_adc(p); - reset::release_reset_port1(p); - reset::release_reset_adc1(p); - pins::configure_adc_pins(); - clocks::select_adc_clock(p); -} diff --git a/examples/hello.rs b/examples/hello.rs deleted file mode 100644 index e39adaced..000000000 --- a/examples/hello.rs +++ /dev/null @@ -1,114 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa276 as hal; -use hal::uart; - -mod common; - -use {defmt_rtt as _, panic_probe as _}; - -/// Simple helper to write a byte as hex to UART -fn write_hex_byte(uart: &hal::uart::Uart, byte: u8) { - const HEX_DIGITS: &[u8] = b"0123456789ABCDEF"; - uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]); - uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - defmt::info!("boot"); - - // Board-level init for UART2 clocks and pins. - unsafe { - common::init_uart2(hal::pac()); - } - - // Get UART source frequency from clock configuration - // Using hardcoded frequency for now - dynamic detection may have issues - let src = 12_000_000; // FRO_LF_DIV at 12MHz with DIV=0 - let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); - - // Print welcome message before any async delays to guarantee early console output - uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n"); - uart.write_str_blocking("Available commands:\r\n"); - uart.write_str_blocking(" help - Show this help\r\n"); - uart.write_str_blocking(" echo - Echo back the text\r\n"); - uart.write_str_blocking(" hex - Display byte in hex (0-255)\r\n"); - uart.write_str_blocking("Type a command: "); - - let mut buffer = [0u8; 64]; - let mut buf_idx = 0; - - loop { - // Read a byte from UART - let byte = uart.read_byte_blocking(); - - // Echo the character back - if byte == b'\r' || byte == b'\n' { - // Enter pressed - process command - uart.write_str_blocking("\r\n"); - - if buf_idx > 0 { - let command = &buffer[0..buf_idx]; - - if command == b"help" { - uart.write_str_blocking("Available commands:\r\n"); - uart.write_str_blocking(" help - Show this help\r\n"); - uart.write_str_blocking(" echo - Echo back the text\r\n"); - uart.write_str_blocking(" hex - Display byte in hex (0-255)\r\n"); - } else if command.starts_with(b"echo ") && command.len() > 5 { - uart.write_str_blocking("Echo: "); - uart.write_str_blocking(core::str::from_utf8(&command[5..]).unwrap_or("")); - uart.write_str_blocking("\r\n"); - } else if command.starts_with(b"hex ") && command.len() > 4 { - // Parse the byte value - let num_str = &command[4..]; - if let Ok(num) = parse_u8(num_str) { - uart.write_str_blocking("Hex: 0x"); - write_hex_byte(&uart, num); - uart.write_str_blocking("\r\n"); - } else { - uart.write_str_blocking("Invalid number for hex command\r\n"); - } - } else if command.len() > 0 { - uart.write_str_blocking("Unknown command: "); - uart.write_str_blocking(core::str::from_utf8(command).unwrap_or("")); - uart.write_str_blocking("\r\n"); - } - } - - // Reset buffer and prompt - buf_idx = 0; - uart.write_str_blocking("Type a command: "); - } else if byte == 8 || byte == 127 { - // Backspace - if buf_idx > 0 { - buf_idx -= 1; - uart.write_str_blocking("\x08 \x08"); // Erase character - } - } else if buf_idx < buffer.len() - 1 { - // Regular character - buffer[buf_idx] = byte; - buf_idx += 1; - uart.write_byte(byte); - } - } -} - -/// Simple parser for u8 from ASCII bytes -fn parse_u8(bytes: &[u8]) -> Result { - let mut result = 0u8; - for &b in bytes { - if b >= b'0' && b <= b'9' { - result = result.checked_mul(10).ok_or(())?; - result = result.checked_add(b - b'0').ok_or(())?; - } else { - return Err(()); - } - } - Ok(result) -} diff --git a/examples/lpuart_buffered.rs b/examples/lpuart_buffered.rs deleted file mode 100644 index 35d311143..000000000 --- a/examples/lpuart_buffered.rs +++ /dev/null @@ -1,77 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa276 as hal; -use embassy_mcxa276::interrupt::typelevel::Handler; -use embassy_mcxa276::lpuart::buffered::BufferedLpuart; -use embassy_mcxa276::{bind_interrupts, lpuart}; -use embedded_io_async::{Read, Write}; - -mod common; - -// Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver -bind_interrupts!(struct Irqs { - LPUART2 => lpuart::buffered::BufferedInterruptHandler::; -}); - -// Wrapper function for the interrupt handler -unsafe extern "C" fn lpuart2_handler() { - lpuart::buffered::BufferedInterruptHandler::::on_interrupt(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let _p = hal::init(hal::config::Config::default()); - let p2 = lpuart::lib::init(); - - unsafe { - hal::interrupt::install_irq_handler(mcxa_pac::Interrupt::LPUART2, lpuart2_handler); - } - - // Configure NVIC for LPUART2 - hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3); - - unsafe { - common::init_uart2(hal::pac()); - common::init_ostimer0(hal::pac()); - } - - // UART configuration (enable both TX and RX) - let config = lpuart::Config { - baudrate_bps: 115_200, - enable_tx: true, - enable_rx: true, - rx_fifo_watermark: 0, - tx_fifo_watermark: 0, - ..Default::default() - }; - - let mut tx_buf = [0u8; 256]; - let mut rx_buf = [0u8; 256]; - - // Create a buffered LPUART2 instance with both TX and RX - let mut uart = BufferedLpuart::new( - p2.LPUART2, - p2.PIO2_2, // TX pin - p2.PIO2_3, // RX pin - Irqs, - &mut tx_buf, - &mut rx_buf, - config, - ) - .unwrap(); - - // Split into TX and RX parts - let (tx, rx) = uart.split_ref(); - - tx.write(b"Hello buffered LPUART.\r\n").await.unwrap(); - tx.write(b"Type characters to echo them back.\r\n").await.unwrap(); - - // Echo loop - let mut buf = [0u8; 4]; - loop { - rx.read_exact(&mut buf[..]).await.unwrap(); - tx.write_all(&buf[..]).await.unwrap(); - } -} diff --git a/examples/lpuart_polling.rs b/examples/lpuart_polling.rs deleted file mode 100644 index 067c7eb53..000000000 --- a/examples/lpuart_polling.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use {defmt_rtt as _, embassy_mcxa276 as hal, panic_probe as _}; - -use crate::hal::lpuart::{lib, Config, Lpuart}; - -mod common; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let _p = hal::init(hal::config::Config::default()); - let p2 = lib::init(); - - defmt::info!("boot"); - - // Board-level init for UART2 clocks and pins. - unsafe { - common::init_uart2(hal::pac()); - } - - // Create UART configuration - let config = Config { - baudrate_bps: 115_200, - enable_tx: true, - enable_rx: true, - ..Default::default() - }; - - // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX - let lpuart = Lpuart::new_blocking( - p2.LPUART2, // Peripheral - p2.PIO2_2, // TX pin - p2.PIO2_3, // RX pin - config, - ) - .unwrap(); - - // Split into separate TX and RX parts - let (mut tx, mut rx) = lpuart.split(); - - // Write hello messages - tx.blocking_write(b"Hello world.\r\n").unwrap(); - tx.blocking_write(b"Echoing. Type characters...\r\n").unwrap(); - - // Echo loop - loop { - let mut buf = [0u8; 1]; - rx.blocking_read(&mut buf).unwrap(); - tx.blocking_write(&buf).unwrap(); - } -} diff --git a/examples/memory.x b/examples/memory.x new file mode 100644 index 000000000..f4263a412 --- /dev/null +++ b/examples/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x20000000, LENGTH = 64K + RAM : ORIGIN = 0x20010000, LENGTH = 64K +} diff --git a/examples/ostimer_alarm.rs b/examples/ostimer_alarm.rs deleted file mode 100644 index 78ca4bbc5..000000000 --- a/examples/ostimer_alarm.rs +++ /dev/null @@ -1,111 +0,0 @@ -#![no_std] -#![no_main] - -use core::sync::atomic::{AtomicBool, Ordering}; - -use embassy_executor::Spawner; -use hal::uart; -use {cortex_m, embassy_mcxa276 as hal}; - -mod common; - -use embassy_mcxa276::bind_interrupts; -use {defmt_rtt as _, panic_probe as _}; - -// Bind only OS_EVENT, and retain the symbol explicitly so it can't be GC'ed. -bind_interrupts!(struct Irqs { - OS_EVENT => hal::ostimer::time_driver::OsEventHandler; -}); - -#[used] -#[no_mangle] -static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; - -// Global flag for alarm callback -static ALARM_FLAG: AtomicBool = AtomicBool::new(false); - -// Alarm callback function -fn alarm_callback() { - ALARM_FLAG.store(true, Ordering::Release); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - // Enable/clock OSTIMER0 and UART2 before touching their registers - unsafe { - common::init_ostimer0(hal::pac()); - } - unsafe { - common::init_uart2(hal::pac()); - } - let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; - let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); - uart.write_str_blocking("OSTIMER Alarm Example\n"); - - // Initialize embassy-time global driver backed by OSTIMER0 - hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000); - - // Create OSTIMER instance - let config = hal::ostimer::Config { - init_match_max: true, - clock_frequency_hz: 1_000_000, // 1MHz - }; - let ostimer = hal::ostimer::Ostimer::::new(p.OSTIMER0, config, hal::pac()); - - // Create alarm with callback - let alarm = hal::ostimer::Alarm::new() - .with_callback(alarm_callback) - .with_flag(&ALARM_FLAG); - - uart.write_str_blocking("Scheduling alarm for 2 seconds...\n"); - - // Schedule alarm to expire in 2 seconds (2,000,000 microseconds) - let scheduled = ostimer.schedule_alarm_delay(&alarm, 2_000_000); - if scheduled { - uart.write_str_blocking("Alarm scheduled successfully\n"); - } else { - uart.write_str_blocking("Failed to schedule alarm (would exceed timer capacity)\n"); - return; - } - - // Wait for alarm to expire - loop { - // Check if alarm has expired - if ALARM_FLAG.load(Ordering::Acquire) { - uart.write_str_blocking("Alarm expired! Callback executed.\n"); - break; - } - - // Busy wait - don't use Timer::after_millis as it interferes with alarm MATCH - for _ in 0..100000 { - cortex_m::asm::nop(); - } - } - - // Demonstrate canceling an alarm - uart.write_str_blocking("Scheduling another alarm for 3 seconds...\n"); - ALARM_FLAG.store(false, Ordering::Release); // Reset flag - - let scheduled = ostimer.schedule_alarm_delay(&alarm, 3_000_000); - if scheduled { - uart.write_str_blocking("Alarm scheduled. Waiting 1 second then canceling...\n"); - - // Wait 1 second - embassy_time::Timer::after_millis(1000).await; - - // Cancel the alarm - ostimer.cancel_alarm(&alarm); - uart.write_str_blocking("Alarm canceled\n"); - - // Check immediately if alarm flag is set - if !ALARM_FLAG.load(Ordering::Acquire) { - uart.write_str_blocking("Alarm was successfully canceled\n"); - } else { - uart.write_str_blocking("Alarm fired despite cancellation\n"); - } - } - - uart.write_str_blocking("Example complete\n"); -} diff --git a/examples/ostimer_async.rs b/examples/ostimer_async.rs deleted file mode 100644 index 27e14e022..000000000 --- a/examples/ostimer_async.rs +++ /dev/null @@ -1,57 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa276 as hal; -use hal::uart; - -mod common; - -use embassy_mcxa276::bind_interrupts; -use embassy_time::{Duration, Timer}; -use {defmt_rtt as _, panic_probe as _}; - -// Bind only OS_EVENT, and retain the symbol explicitly so it can’t be GC’ed. -bind_interrupts!(struct Irqs { - OS_EVENT => hal::ostimer::time_driver::OsEventHandler; -}); - -#[used] -#[no_mangle] -static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let _p = hal::init(hal::config::Config::default()); - - // Enable/clock OSTIMER0 and UART2 before touching their registers - unsafe { - common::init_ostimer0(hal::pac()); - } - unsafe { - common::init_uart2(hal::pac()); - } - let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; - let uart = uart::Uart::::new(_p.LPUART2, uart::Config::new(src)); - uart.write_str_blocking("boot\n"); - - // Avoid mass NVIC writes here; DefaultHandler now safely returns. - - // Initialize embassy-time global driver backed by OSTIMER0 (re-enables OS_EVENT with priority) - // The bind_interrupts! macro handles handler binding automatically - - // Initialize OSTIMER with default 1MHz frequency - // Adjust this value to match your actual OSTIMER clock frequency - hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000); - - // Removed force-pend; rely on real hardware match to trigger OS_EVENT. - - // Log using defmt if enabled - defmt::info!("OSTIMER async example starting..."); - - loop { - defmt::info!("tick"); - uart.write_str_blocking("tick\n"); - Timer::after(Duration::from_millis(1000)).await; - } -} diff --git a/examples/ostimer_counter.rs b/examples/ostimer_counter.rs deleted file mode 100644 index e95140a88..000000000 --- a/examples/ostimer_counter.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! # OSTIMER Counter Reading and Reset Example -//! -//! This example demonstrates the new timer counter reading and reset functionality -//! of the OSTIMER driver. - -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_time::{Duration, Timer}; -use hal::bind_interrupts; -use {defmt_rtt as _, embassy_mcxa276 as hal, panic_probe as _}; - -mod common; - -bind_interrupts!(struct Irqs { - OS_EVENT => hal::ostimer::time_driver::OsEventHandler; -}); - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(Default::default()); - - // Enable/clock OSTIMER0 and UART2 before touching their registers - unsafe { - common::init_ostimer0(hal::pac()); - } - unsafe { - common::init_uart2(hal::pac()); - } - let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; - let mut uart = hal::uart::Uart::::new(p.LPUART2, hal::uart::Config::new(src)); - - uart.write_str_blocking("OSTIMER Counter Reading and Reset Example\n"); - - // Initialize the OSTIMER time driver - hal::ostimer::time_driver::init( - hal::interrupt::Priority::from(3), - 1_000_000, // 1MHz clock - ); - - // Create OSTIMER instance - let ostimer = hal::ostimer::Ostimer::::new( - p.OSTIMER0, - hal::ostimer::Config { - init_match_max: true, - clock_frequency_hz: 1_000_000, - }, - hal::pac(), - ); - - // Read initial counter value - let initial_counter = ostimer.now(); - uart.write_str_blocking("Initial counter value: "); - write_u64(&mut uart, initial_counter); - uart.write_str_blocking("\n"); - - // Wait a bit to let counter increment - Timer::after(Duration::from_millis(100)).await; - - // Read counter again - let counter_after_wait = ostimer.now(); - uart.write_str_blocking("Counter after 100ms wait: "); - write_u64(&mut uart, counter_after_wait); - uart.write_str_blocking("\n"); - uart.write_str_blocking("Difference: "); - write_u64(&mut uart, counter_after_wait - initial_counter); - uart.write_str_blocking(" ticks\n"); - - // Reset the timer - uart.write_str_blocking("Resetting timer...\n"); - ostimer.reset(hal::pac()); - - // Read counter after reset - let counter_after_reset = ostimer.now(); - uart.write_str_blocking("Counter after reset: "); - write_u64(&mut uart, counter_after_reset); - uart.write_str_blocking("\n"); - - // Wait again to verify timer is working - Timer::after(Duration::from_millis(50)).await; - - let final_counter = ostimer.now(); - uart.write_str_blocking("Counter after another 50ms: "); - write_u64(&mut uart, final_counter); - uart.write_str_blocking("\n"); - uart.write_str_blocking("Difference after reset: "); - write_u64(&mut uart, final_counter - counter_after_reset); - uart.write_str_blocking(" ticks\n"); - - uart.write_str_blocking("Example complete\n"); -} - -// Helper function to write a u64 value as decimal string -fn write_u64(uart: &mut hal::uart::Uart, value: u64) { - if value == 0 { - uart.write_str_blocking("0"); - return; - } - - let mut buffer = [0u8; 20]; // Enough for max u64 - let mut i = 0; - let mut v = value; - - while v > 0 { - buffer[i] = b'0' + (v % 10) as u8; - v /= 10; - i += 1; - } - - // Write digits in reverse order - while i > 0 { - i -= 1; - match buffer[i] { - b'0' => uart.write_str_blocking("0"), - b'1' => uart.write_str_blocking("1"), - b'2' => uart.write_str_blocking("2"), - b'3' => uart.write_str_blocking("3"), - b'4' => uart.write_str_blocking("4"), - b'5' => uart.write_str_blocking("5"), - b'6' => uart.write_str_blocking("6"), - b'7' => uart.write_str_blocking("7"), - b'8' => uart.write_str_blocking("8"), - b'9' => uart.write_str_blocking("9"), - _ => uart.write_str_blocking("?"), - } - } -} diff --git a/examples/ostimer_race_test.rs b/examples/ostimer_race_test.rs deleted file mode 100644 index 368a3e52f..000000000 --- a/examples/ostimer_race_test.rs +++ /dev/null @@ -1,396 +0,0 @@ -//! # OSTIMER Race Condition Test -//! -//! This example tests for race conditions in the OSTIMER driver by: -//! - Scheduling alarms sequentially (hardware limitation: only one at a time) -//! - Reading the counter during interrupt-heavy periods -//! - Testing concurrent timer operations -//! - Stress testing interrupt handling - -#![no_std] -#![no_main] - -use core::sync::atomic::{AtomicU32, Ordering}; - -use embassy_executor::Spawner; -use embassy_time::{Duration, Timer}; -use hal::bind_interrupts; -use {defmt_rtt as _, embassy_mcxa276 as hal, panic_probe as _}; - -mod common; - -bind_interrupts!(struct Irqs { - OS_EVENT => hal::ostimer::time_driver::OsEventHandler; -}); - -#[used] -#[no_mangle] -static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; - -// Global counters for race condition detection -static ALARM_CALLBACK_COUNT: AtomicU32 = AtomicU32::new(0); -static INTERRUPT_COUNT: AtomicU32 = AtomicU32::new(0); -static RACE_DETECTED: AtomicU32 = AtomicU32::new(0); - -// Alarm callback function -fn alarm_callback() { - let _count = ALARM_CALLBACK_COUNT.fetch_add(1, Ordering::SeqCst); - INTERRUPT_COUNT.fetch_add(1, Ordering::SeqCst); - - // Simulate some work in the callback to increase chance of races - for _ in 0..10 { - cortex_m::asm::nop(); - } -} - -fn report_default_handler(uart: &mut hal::uart::Uart) { - let snapshot = hal::interrupt::default_handler_snapshot(); - if snapshot.count == 0 { - return; - } - - uart.write_str_blocking("WARNING: DefaultHandler executed "); - write_u32(uart, snapshot.count); - uart.write_str_blocking(" time(s). Vector="); - write_u32(uart, snapshot.vector as u32); - uart.write_str_blocking(" CFSR=0x"); - write_hex32(uart, snapshot.cfsr); - uart.write_str_blocking(" HFSR=0x"); - write_hex32(uart, snapshot.hfsr); - uart.write_str_blocking(" PC=0x"); - write_hex32(uart, snapshot.stacked_pc); - uart.write_str_blocking(" LR=0x"); - write_hex32(uart, snapshot.stacked_lr); - uart.write_str_blocking(" SP=0x"); - write_hex32(uart, snapshot.stacked_sp); - uart.write_str_blocking("\n"); - - hal::interrupt::clear_default_handler_snapshot(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(Default::default()); - - // Enable/clock OSTIMER0 and UART2 before touching their registers - unsafe { - common::init_ostimer0(hal::pac()); - } - unsafe { - common::init_uart2(hal::pac()); - } - let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; - let mut uart = hal::uart::Uart::::new(p.LPUART2, hal::uart::Config::new(src)); - - uart.write_str_blocking("OSTIMER Race Condition Test Starting...\n"); - - // The bind_interrupts! macro handles handler binding automatically - - // Initialize the OSTIMER time driver FIRST - hal::ostimer::time_driver::init( - hal::interrupt::Priority::from(3), - 1_000_000, // 1MHz clock - ); - - uart.write_str_blocking("Time driver initialized\n"); - - // Create OSTIMER instance - let ostimer = hal::ostimer::Ostimer::::new( - p.OSTIMER0, - hal::ostimer::Config { - init_match_max: true, - clock_frequency_hz: 1_000_000, - }, - hal::pac(), - ); - - uart.write_str_blocking("OSTIMER instance created\n"); - - // Test 1: Sequential alarm scheduling (OSTIMER only supports one alarm at a time) - uart.write_str_blocking("Test 1: Sequential alarm scheduling...\n"); - test_rapid_alarms(&ostimer, &mut uart).await; - report_default_handler(&mut uart); - - // Test 2: Counter reading during interrupts - uart.write_str_blocking("Test 2: Counter reading during interrupts...\n"); - test_counter_reading_during_interrupts(&ostimer, &mut uart).await; - report_default_handler(&mut uart); - - // Test 3: Concurrent timer operations - uart.write_str_blocking("Test 3: Concurrent timer operations...\n"); - test_concurrent_operations(&ostimer, &mut uart).await; - report_default_handler(&mut uart); - - // Test 4: Timer reset during operation - uart.write_str_blocking("Test 4: Timer reset during operation...\n"); - test_reset_during_operation(&ostimer, &mut uart, hal::pac()).await; - report_default_handler(&mut uart); - - // Report results - uart.write_str_blocking("Race condition test complete\n"); - uart.write_str_blocking("Callback count: "); - write_u32(&mut uart, ALARM_CALLBACK_COUNT.load(Ordering::SeqCst)); - uart.write_str_blocking("\nInterrupt count: "); - write_u32(&mut uart, INTERRUPT_COUNT.load(Ordering::SeqCst)); - uart.write_str_blocking("\nRaces detected: "); - write_u32(&mut uart, RACE_DETECTED.load(Ordering::SeqCst)); - uart.write_str_blocking("\n"); -} - -// Test rapid alarm scheduling to stress interrupt handling -async fn test_rapid_alarms( - ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, - uart: &mut hal::uart::Uart, -) { - let initial_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst); - - // Schedule 10 alarms sequentially (OSTIMER only supports one alarm at a time) - for _i in 0..10 { - let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback); - let delay_us = 1000; // 1ms delay for each alarm - if ostimer.schedule_alarm_delay(&alarm, delay_us) { - // Wait for this alarm to complete before scheduling the next - Timer::after(Duration::from_micros(delay_us + 100)).await; - report_default_handler(uart); - } else { - RACE_DETECTED.fetch_add(1, Ordering::SeqCst); - uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm (match not ready)\n"); - } - } - - // All alarms should have completed by now - let final_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst); - let expected_count = initial_count + 10; - - if final_count != expected_count { - RACE_DETECTED.fetch_add(1, Ordering::SeqCst); - uart.write_str_blocking("ERROR: Expected "); - write_u32(uart, expected_count); - uart.write_str_blocking(" callbacks, got "); - write_u32(uart, final_count); - uart.write_str_blocking("\n"); - } else { - uart.write_str_blocking("PASS: All rapid alarms executed\n"); - } -} - -// Test reading counter while interrupts are firing -async fn test_counter_reading_during_interrupts( - ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, - uart: &mut hal::uart::Uart, -) { - let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); - - // Schedule an alarm that will fire soon - let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback); - if !ostimer.schedule_alarm_delay(&alarm, 500) { - RACE_DETECTED.fetch_add(1, Ordering::SeqCst); - uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before counter stress\n"); - } - - // While alarm is pending, read the counter many times rapidly - // This tests if counter reading is atomic and doesn't get corrupted by interrupts - let mut last_counter = ostimer.now(); - let mut consistent_reads = 0; - let mut total_reads = 0; - - for _ in 0..1000 { - let current_counter = ostimer.now(); - total_reads += 1; - - // Check if counter is monotonically increasing (basic sanity check) - if current_counter >= last_counter { - consistent_reads += 1; - } - last_counter = current_counter; - - // Small delay between reads - for _ in 0..10 { - cortex_m::asm::nop(); - } - - report_default_handler(uart); - } - - // Wait for alarm to complete - Timer::after(Duration::from_millis(1)).await; - - let final_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); - - if consistent_reads == total_reads { - uart.write_str_blocking("PASS: Counter reading consistent during interrupts\n"); - } else { - RACE_DETECTED.fetch_add(1, Ordering::SeqCst); - uart.write_str_blocking("ERROR: Counter reading inconsistent: "); - write_u32(uart, consistent_reads); - uart.write_str_blocking("/"); - write_u32(uart, total_reads); - uart.write_str_blocking(" consistent\n"); - } - - if final_interrupt_count > initial_interrupt_count { - uart.write_str_blocking("PASS: Interrupt fired during counter reading test\n"); - } else { - uart.write_str_blocking("WARNING: No interrupt fired during counter reading test\n"); - } -} - -// Test concurrent timer operations (embassy-time + alarms) -async fn test_concurrent_operations( - ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, - uart: &mut hal::uart::Uart, -) { - let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); - - // Start an embassy-time timer - let timer_future = Timer::after(Duration::from_millis(2)); - - // Schedule an alarm that should fire before the timer - let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback); - if !ostimer.schedule_alarm_delay(&alarm, 1000) { - RACE_DETECTED.fetch_add(1, Ordering::SeqCst); - uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before concurrent operations\n"); - } - - // Wait for both to complete - timer_future.await; - - let final_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); - - if final_interrupt_count > initial_interrupt_count { - uart.write_str_blocking("PASS: Concurrent operations completed\n"); - } else { - uart.write_str_blocking("WARNING: No interrupts during concurrent operations\n"); - } -} - -// Test timer reset during active operations -async fn test_reset_during_operation( - ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, - uart: &mut hal::uart::Uart, - peripherals: &mcxa_pac::Peripherals, -) { - let initial_counter = ostimer.now(); - - // Schedule an alarm - let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback); - if !ostimer.schedule_alarm_delay(&alarm, 2000) { - RACE_DETECTED.fetch_add(1, Ordering::SeqCst); - uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before reset test\n"); - } - - // Wait a bit then reset the timer - Timer::after(Duration::from_millis(1)).await; - ostimer.reset(peripherals); - - // Check counter after reset - let counter_after_reset = ostimer.now(); - - // Wait to see if the alarm still fires (it shouldn't after reset) - Timer::after(Duration::from_millis(2)).await; - - let final_counter = ostimer.now(); - - if counter_after_reset < initial_counter { - uart.write_str_blocking("PASS: Timer reset successful\n"); - } else { - RACE_DETECTED.fetch_add(1, Ordering::SeqCst); - uart.write_str_blocking("ERROR: Timer reset may have failed\n"); - } - - uart.write_str_blocking("Counter progression after reset: "); - write_u64(uart, initial_counter); - uart.write_str_blocking(" -> "); - write_u64(uart, counter_after_reset); - uart.write_str_blocking(" -> "); - write_u64(uart, final_counter); - uart.write_str_blocking("\n"); -} - -// Helper function to write a u32 value as decimal string -fn write_u32(uart: &mut hal::uart::Uart, value: u32) { - if value == 0 { - uart.write_str_blocking("0"); - return; - } - - let mut buffer = [0u8; 10]; // Enough for max u32 - let mut i = 0; - let mut v = value; - - while v > 0 { - buffer[i] = b'0' + (v % 10) as u8; - v /= 10; - i += 1; - } - - // Write digits in reverse order - while i > 0 { - i -= 1; - match buffer[i] { - b'0' => uart.write_str_blocking("0"), - b'1' => uart.write_str_blocking("1"), - b'2' => uart.write_str_blocking("2"), - b'3' => uart.write_str_blocking("3"), - b'4' => uart.write_str_blocking("4"), - b'5' => uart.write_str_blocking("5"), - b'6' => uart.write_str_blocking("6"), - b'7' => uart.write_str_blocking("7"), - b'8' => uart.write_str_blocking("8"), - b'9' => uart.write_str_blocking("9"), - _ => uart.write_str_blocking("?"), - } - } -} - -fn write_hex32(uart: &mut hal::uart::Uart, value: u32) { - let mut buf = [b'0'; 8]; - let mut tmp = value; - for i in (0..8).rev() { - let digit = (tmp & 0xF) as u8; - buf[i] = match digit { - 0..=9 => b'0' + digit, - 10..=15 => b'A' + (digit - 10), - _ => b'?', - }; - tmp >>= 4; - } - for b in &buf { - uart.write_byte(*b); - } -} - -// Helper function to write a u64 value as decimal string -fn write_u64(uart: &mut hal::uart::Uart, value: u64) { - if value == 0 { - uart.write_str_blocking("0"); - return; - } - - let mut buffer = [0u8; 20]; // Enough for max u64 - let mut i = 0; - let mut v = value; - - while v > 0 { - buffer[i] = b'0' + (v % 10) as u8; - v /= 10; - i += 1; - } - - // Write digits in reverse order - while i > 0 { - i -= 1; - match buffer[i] { - b'0' => uart.write_str_blocking("0"), - b'1' => uart.write_str_blocking("1"), - b'2' => uart.write_str_blocking("2"), - b'3' => uart.write_str_blocking("3"), - b'4' => uart.write_str_blocking("4"), - b'5' => uart.write_str_blocking("5"), - b'6' => uart.write_str_blocking("6"), - b'7' => uart.write_str_blocking("7"), - b'8' => uart.write_str_blocking("8"), - b'9' => uart.write_str_blocking("9"), - _ => uart.write_str_blocking("?"), - } - } -} diff --git a/examples/rtc_alarm.rs b/examples/rtc_alarm.rs deleted file mode 100644 index c27fd4c55..000000000 --- a/examples/rtc_alarm.rs +++ /dev/null @@ -1,87 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use hal::rtc::{RtcDateTime, RtcInterruptEnable}; -use hal::{uart, InterruptExt}; -use {cortex_m, embassy_mcxa276 as hal}; - -mod common; - -type MyRtc = hal::rtc::Rtc; - -use embassy_mcxa276::bind_interrupts; -use {defmt_rtt as _, panic_probe as _}; - -bind_interrupts!(struct Irqs { - RTC => hal::rtc::RtcHandler; -}); - -#[used] -#[no_mangle] -static KEEP_RTC: unsafe extern "C" fn() = RTC; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); - - unsafe { - common::init_uart2(hal::pac()); - } - - let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; - let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); - - uart.write_str_blocking("\r\n=== RTC Alarm Example ===\r\n"); - - unsafe { hal::clocks::init_fro16k(hal::pac()) }; - - let rtc_config = hal::rtc::get_default_config(); - - let rtc = MyRtc::new(p.RTC0, rtc_config); - - let now = RtcDateTime { - year: 2025, - month: 10, - day: 15, - hour: 14, - minute: 30, - second: 0, - }; - - rtc.stop(); - - uart.write_str_blocking("Time set to: 2025-10-15 14:30:00\r\n"); - rtc.set_datetime(now); - - let mut alarm = now; - alarm.second += 10; - - rtc.set_alarm(alarm); - uart.write_str_blocking("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)\r\n"); - - rtc.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE); - - unsafe { - hal::interrupt::RTC.enable(); - } - - unsafe { - cortex_m::interrupt::enable(); - } - - rtc.start(); - - uart.write_str_blocking("RTC started, waiting for alarm...\r\n"); - - loop { - if rtc.is_alarm_triggered() { - uart.write_str_blocking("\r\n*** ALARM TRIGGERED! ***\r\n"); - break; - } - } - - uart.write_str_blocking("Example complete - Test PASSED!\r\n"); - - loop {} -} diff --git a/examples/src/bin/adc_interrupt.rs b/examples/src/bin/adc_interrupt.rs new file mode 100644 index 000000000..e174a5272 --- /dev/null +++ b/examples/src/bin/adc_interrupt.rs @@ -0,0 +1,86 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa_examples::{init_adc, init_uart2}; +use hal::adc::{LpadcConfig, TriggerPriorityPolicy}; +use hal::pac::adc1::cfg::{Pwrsel, Refsel}; +use hal::pac::adc1::cmdl1::{Adch, Mode}; +use hal::pac::adc1::ctrl::CalAvgs; +use hal::pac::adc1::tctrl::Tcmd; +use hal::{bind_interrupts, uart, InterruptExt}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +bind_interrupts!(struct Irqs { + ADC1 => hal::adc::AdcHandler; +}); + +#[used] +#[no_mangle] +static KEEP_ADC: unsafe extern "C" fn() = ADC1; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + unsafe { + init_uart2(hal::pac()); + } + + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); + + uart.write_str_blocking("\r\n=== ADC interrupt Example ===\r\n"); + + unsafe { + init_adc(hal::pac()); + } + + let adc_config = LpadcConfig { + enable_in_doze_mode: true, + conversion_average_mode: CalAvgs::Average128, + enable_analog_preliminary: true, + power_up_delay: 0x80, + reference_voltage_source: Refsel::Option3, + power_level_mode: Pwrsel::Lowest, + trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, + enable_conv_pause: false, + conv_pause_delay: 0, + fifo_watermark: 0, + }; + let adc = hal::adc::Adc::::new(p.ADC1, adc_config); + + adc.do_offset_calibration(); + adc.do_auto_calibration(); + + let mut conv_command_config = adc.get_default_conv_command_config(); + conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; + conv_command_config.conversion_resolution_mode = Mode::Data16Bits; + adc.set_conv_command_config(1, &conv_command_config); + + let mut conv_trigger_config = adc.get_default_conv_trigger_config(); + conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; + conv_trigger_config.enable_hardware_trigger = false; + adc.set_conv_trigger_config(0, &conv_trigger_config); + + uart.write_str_blocking("\r\n=== ADC configuration done... ===\r\n"); + + adc.enable_interrupt(0x1); + + unsafe { + hal::interrupt::ADC1.enable(); + } + + unsafe { + cortex_m::interrupt::enable(); + } + + loop { + adc.do_software_trigger(1); + while !adc.is_interrupt_triggered() { + // Wait until the interrupt is triggered + } + uart.write_str_blocking("\r\n*** ADC interrupt TRIGGERED! ***\r\n"); + //TBD need to print the value + } +} diff --git a/examples/src/bin/adc_polling.rs b/examples/src/bin/adc_polling.rs new file mode 100644 index 000000000..741551d49 --- /dev/null +++ b/examples/src/bin/adc_polling.rs @@ -0,0 +1,76 @@ +#![no_std] +#![no_main] + +use core::fmt::Write; + +use embassy_executor::Spawner; +use embassy_mcxa_examples::{init_adc, init_uart2}; +use hal::adc::{ConvResult, LpadcConfig, TriggerPriorityPolicy}; +use hal::pac::adc1::cfg::{Pwrsel, Refsel}; +use hal::pac::adc1::cmdl1::{Adch, Mode}; +use hal::pac::adc1::ctrl::CalAvgs; +use hal::pac::adc1::tctrl::Tcmd; +use hal::uart; +use heapless::String; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +const G_LPADC_RESULT_SHIFT: u32 = 0; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + unsafe { + init_uart2(hal::pac()); + } + + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); + + uart.write_str_blocking("\r\n=== ADC polling Example ===\r\n"); + + unsafe { + init_adc(hal::pac()); + } + + let adc_config = LpadcConfig { + enable_in_doze_mode: true, + conversion_average_mode: CalAvgs::Average128, + enable_analog_preliminary: true, + power_up_delay: 0x80, + reference_voltage_source: Refsel::Option3, + power_level_mode: Pwrsel::Lowest, + trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, + enable_conv_pause: false, + conv_pause_delay: 0, + fifo_watermark: 0, + }; + let adc = hal::adc::Adc::::new(p.ADC1, adc_config); + + adc.do_offset_calibration(); + adc.do_auto_calibration(); + + let mut conv_command_config = adc.get_default_conv_command_config(); + conv_command_config.channel_number = Adch::SelectCorrespondingChannel8; + conv_command_config.conversion_resolution_mode = Mode::Data16Bits; + adc.set_conv_command_config(1, &conv_command_config); + + let mut conv_trigger_config = adc.get_default_conv_trigger_config(); + conv_trigger_config.target_command_id = Tcmd::ExecuteCmd1; + conv_trigger_config.enable_hardware_trigger = false; + adc.set_conv_trigger_config(0, &conv_trigger_config); + + uart.write_str_blocking("\r\n=== ADC configuration done... ===\r\n"); + + loop { + adc.do_software_trigger(1); + let mut result: Option = None; + while result.is_none() { + result = hal::adc::get_conv_result(); + } + let value = result.unwrap().conv_value >> G_LPADC_RESULT_SHIFT; + let mut buf: String<16> = String::new(); // adjust size as needed + write!(buf, "\r\nvalue: {}\r\n", value).unwrap(); + uart.write_str_blocking(&buf); + } +} diff --git a/examples/src/bin/blink.rs b/examples/src/bin/blink.rs new file mode 100644 index 000000000..d8a57c546 --- /dev/null +++ b/examples/src/bin/blink.rs @@ -0,0 +1,78 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa as hal; +use embassy_mcxa::bind_interrupts; +use embassy_mcxa_examples::{init_led, init_ostimer0}; +use embassy_time::{Duration, Timer}; +use hal::gpio::pins::PIO3_18; +use hal::gpio::{Level, Output}; + +// Bind only OS_EVENT for timer interrupts +bind_interrupts!(struct Irqs { + OS_EVENT => hal::ostimer::time_driver::OsEventHandler; +}); + +#[used] +#[no_mangle] +static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = hal::init(hal::config::Config::default()); + + unsafe { + init_led(hal::pac()); + init_ostimer0(hal::pac()); + } + + // Initialize embassy-time global driver backed by OSTIMER0 + hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000); + + // Configure LED pin for GPIO mode + PIO3_18::set_mux_gpio(); + + let mut led = Output::new(PIO3_18::degrade(), Level::High); + + // Complex blinking pattern: SOS in Morse code + // S: ... (3 short) + // O: --- (3 long) + // S: ... (3 short) + // With pauses between letters and words + + loop { + // S: three short blinks + for _ in 0..3 { + led.set_low(); + Timer::after(Duration::from_millis(150)).await; + led.set_high(); + Timer::after(Duration::from_millis(150)).await; + } + + // Pause between letters + Timer::after(Duration::from_millis(300)).await; + + // O: three long blinks + for _ in 0..3 { + led.set_low(); + Timer::after(Duration::from_millis(450)).await; + led.set_high(); + Timer::after(Duration::from_millis(150)).await; + } + + // Pause between letters + Timer::after(Duration::from_millis(300)).await; + + // S: three short blinks + for _ in 0..3 { + led.set_low(); + Timer::after(Duration::from_millis(150)).await; + led.set_high(); + Timer::after(Duration::from_millis(150)).await; + } + + // Long pause between words (SOS repeats) + Timer::after(Duration::from_millis(1000)).await; + } +} diff --git a/examples/src/bin/hello.rs b/examples/src/bin/hello.rs new file mode 100644 index 000000000..5c4336d50 --- /dev/null +++ b/examples/src/bin/hello.rs @@ -0,0 +1,111 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa_examples::init_uart2; +use hal::uart; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +/// Simple helper to write a byte as hex to UART +fn write_hex_byte(uart: &hal::uart::Uart, byte: u8) { + const HEX_DIGITS: &[u8] = b"0123456789ABCDEF"; + uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]); + uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + defmt::info!("boot"); + + // Board-level init for UART2 clocks and pins. + unsafe { + init_uart2(hal::pac()); + } + + // Get UART source frequency from clock configuration + // Using hardcoded frequency for now - dynamic detection may have issues + let src = 12_000_000; // FRO_LF_DIV at 12MHz with DIV=0 + let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); + + // Print welcome message before any async delays to guarantee early console output + uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n"); + uart.write_str_blocking("Available commands:\r\n"); + uart.write_str_blocking(" help - Show this help\r\n"); + uart.write_str_blocking(" echo - Echo back the text\r\n"); + uart.write_str_blocking(" hex - Display byte in hex (0-255)\r\n"); + uart.write_str_blocking("Type a command: "); + + let mut buffer = [0u8; 64]; + let mut buf_idx = 0; + + loop { + // Read a byte from UART + let byte = uart.read_byte_blocking(); + + // Echo the character back + if byte == b'\r' || byte == b'\n' { + // Enter pressed - process command + uart.write_str_blocking("\r\n"); + + if buf_idx > 0 { + let command = &buffer[0..buf_idx]; + + if command == b"help" { + uart.write_str_blocking("Available commands:\r\n"); + uart.write_str_blocking(" help - Show this help\r\n"); + uart.write_str_blocking(" echo - Echo back the text\r\n"); + uart.write_str_blocking(" hex - Display byte in hex (0-255)\r\n"); + } else if command.starts_with(b"echo ") && command.len() > 5 { + uart.write_str_blocking("Echo: "); + uart.write_str_blocking(core::str::from_utf8(&command[5..]).unwrap_or("")); + uart.write_str_blocking("\r\n"); + } else if command.starts_with(b"hex ") && command.len() > 4 { + // Parse the byte value + let num_str = &command[4..]; + if let Ok(num) = parse_u8(num_str) { + uart.write_str_blocking("Hex: 0x"); + write_hex_byte(&uart, num); + uart.write_str_blocking("\r\n"); + } else { + uart.write_str_blocking("Invalid number for hex command\r\n"); + } + } else if !command.is_empty() { + uart.write_str_blocking("Unknown command: "); + uart.write_str_blocking(core::str::from_utf8(command).unwrap_or("")); + uart.write_str_blocking("\r\n"); + } + } + + // Reset buffer and prompt + buf_idx = 0; + uart.write_str_blocking("Type a command: "); + } else if byte == 8 || byte == 127 { + // Backspace + if buf_idx > 0 { + buf_idx -= 1; + uart.write_str_blocking("\x08 \x08"); // Erase character + } + } else if buf_idx < buffer.len() - 1 { + // Regular character + buffer[buf_idx] = byte; + buf_idx += 1; + uart.write_byte(byte); + } + } +} + +/// Simple parser for u8 from ASCII bytes +fn parse_u8(bytes: &[u8]) -> Result { + let mut result = 0u8; + for &b in bytes { + if b.is_ascii_digit() { + result = result.checked_mul(10).ok_or(())?; + result = result.checked_add(b - b'0').ok_or(())?; + } else { + return Err(()); + } + } + Ok(result) +} diff --git a/examples/src/bin/lpuart_buffered.rs b/examples/src/bin/lpuart_buffered.rs new file mode 100644 index 000000000..480d8e1f7 --- /dev/null +++ b/examples/src/bin/lpuart_buffered.rs @@ -0,0 +1,76 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa as hal; +use embassy_mcxa::interrupt::typelevel::Handler; +use embassy_mcxa::lpuart::buffered::BufferedLpuart; +use embassy_mcxa::{bind_interrupts, lpuart}; +use embassy_mcxa_examples::{init_ostimer0, init_uart2}; +use embedded_io_async::{Read, Write}; + +// Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver +bind_interrupts!(struct Irqs { + LPUART2 => lpuart::buffered::BufferedInterruptHandler::; +}); + +// Wrapper function for the interrupt handler +unsafe extern "C" fn lpuart2_handler() { + lpuart::buffered::BufferedInterruptHandler::::on_interrupt(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = hal::init(hal::config::Config::default()); + let p2 = lpuart::lib::init(); + + unsafe { + hal::interrupt::install_irq_handler(hal::pac::Interrupt::LPUART2, lpuart2_handler); + } + + // Configure NVIC for LPUART2 + hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3); + + unsafe { + init_uart2(hal::pac()); + init_ostimer0(hal::pac()); + } + + // UART configuration (enable both TX and RX) + let config = lpuart::Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: true, + rx_fifo_watermark: 0, + tx_fifo_watermark: 0, + ..Default::default() + }; + + let mut tx_buf = [0u8; 256]; + let mut rx_buf = [0u8; 256]; + + // Create a buffered LPUART2 instance with both TX and RX + let mut uart = BufferedLpuart::new( + p2.LPUART2, + p2.PIO2_2, // TX pin + p2.PIO2_3, // RX pin + Irqs, + &mut tx_buf, + &mut rx_buf, + config, + ) + .unwrap(); + + // Split into TX and RX parts + let (tx, rx) = uart.split_ref(); + + tx.write(b"Hello buffered LPUART.\r\n").await.unwrap(); + tx.write(b"Type characters to echo them back.\r\n").await.unwrap(); + + // Echo loop + let mut buf = [0u8; 4]; + loop { + rx.read_exact(&mut buf[..]).await.unwrap(); + tx.write_all(&buf[..]).await.unwrap(); + } +} diff --git a/examples/src/bin/lpuart_polling.rs b/examples/src/bin/lpuart_polling.rs new file mode 100644 index 000000000..215714569 --- /dev/null +++ b/examples/src/bin/lpuart_polling.rs @@ -0,0 +1,51 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa_examples::init_uart2; +use hal::lpuart::{lib, Config, Lpuart}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = hal::init(hal::config::Config::default()); + let p2 = lib::init(); + + defmt::info!("boot"); + + // Board-level init for UART2 clocks and pins. + unsafe { + init_uart2(hal::pac()); + } + + // Create UART configuration + let config = Config { + baudrate_bps: 115_200, + enable_tx: true, + enable_rx: true, + ..Default::default() + }; + + // Create UART instance using LPUART2 with PIO2_2 as TX and PIO2_3 as RX + let lpuart = Lpuart::new_blocking( + p2.LPUART2, // Peripheral + p2.PIO2_2, // TX pin + p2.PIO2_3, // RX pin + config, + ) + .unwrap(); + + // Split into separate TX and RX parts + let (mut tx, mut rx) = lpuart.split(); + + // Write hello messages + tx.blocking_write(b"Hello world.\r\n").unwrap(); + tx.blocking_write(b"Echoing. Type characters...\r\n").unwrap(); + + // Echo loop + loop { + let mut buf = [0u8; 1]; + rx.blocking_read(&mut buf).unwrap(); + tx.blocking_write(&buf).unwrap(); + } +} diff --git a/examples/src/bin/ostimer_alarm.rs b/examples/src/bin/ostimer_alarm.rs new file mode 100644 index 000000000..953f98c01 --- /dev/null +++ b/examples/src/bin/ostimer_alarm.rs @@ -0,0 +1,106 @@ +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicBool, Ordering}; + +use embassy_executor::Spawner; +use embassy_mcxa::bind_interrupts; +use embassy_mcxa_examples::{init_ostimer0, init_uart2}; +use hal::uart; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind only OS_EVENT, and retain the symbol explicitly so it can't be GC'ed. +bind_interrupts!(struct Irqs { + OS_EVENT => hal::ostimer::time_driver::OsEventHandler; +}); + +#[used] +#[no_mangle] +static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; + +// Global flag for alarm callback +static ALARM_FLAG: AtomicBool = AtomicBool::new(false); + +// Alarm callback function +fn alarm_callback() { + ALARM_FLAG.store(true, Ordering::Release); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + // Enable/clock OSTIMER0 and UART2 before touching their registers + unsafe { + init_ostimer0(hal::pac()); + init_uart2(hal::pac()); + } + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); + uart.write_str_blocking("OSTIMER Alarm Example\n"); + + // Initialize embassy-time global driver backed by OSTIMER0 + hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000); + + // Create OSTIMER instance + let config = hal::ostimer::Config { + init_match_max: true, + clock_frequency_hz: 1_000_000, // 1MHz + }; + let ostimer = hal::ostimer::Ostimer::::new(p.OSTIMER0, config, hal::pac()); + + // Create alarm with callback + let alarm = hal::ostimer::Alarm::new() + .with_callback(alarm_callback) + .with_flag(&ALARM_FLAG); + + uart.write_str_blocking("Scheduling alarm for 2 seconds...\n"); + + // Schedule alarm to expire in 2 seconds (2,000,000 microseconds) + let scheduled = ostimer.schedule_alarm_delay(&alarm, 2_000_000); + if scheduled { + uart.write_str_blocking("Alarm scheduled successfully\n"); + } else { + uart.write_str_blocking("Failed to schedule alarm (would exceed timer capacity)\n"); + return; + } + + // Wait for alarm to expire + loop { + // Check if alarm has expired + if ALARM_FLAG.load(Ordering::Acquire) { + uart.write_str_blocking("Alarm expired! Callback executed.\n"); + break; + } + + // Busy wait - don't use Timer::after_millis as it interferes with alarm MATCH + for _ in 0..100000 { + cortex_m::asm::nop(); + } + } + + // Demonstrate canceling an alarm + uart.write_str_blocking("Scheduling another alarm for 3 seconds...\n"); + ALARM_FLAG.store(false, Ordering::Release); // Reset flag + + let scheduled = ostimer.schedule_alarm_delay(&alarm, 3_000_000); + if scheduled { + uart.write_str_blocking("Alarm scheduled. Waiting 1 second then canceling...\n"); + + // Wait 1 second + embassy_time::Timer::after_millis(1000).await; + + // Cancel the alarm + ostimer.cancel_alarm(&alarm); + uart.write_str_blocking("Alarm canceled\n"); + + // Check immediately if alarm flag is set + if !ALARM_FLAG.load(Ordering::Acquire) { + uart.write_str_blocking("Alarm was successfully canceled\n"); + } else { + uart.write_str_blocking("Alarm fired despite cancellation\n"); + } + } + + uart.write_str_blocking("Example complete\n"); +} diff --git a/examples/src/bin/ostimer_async.rs b/examples/src/bin/ostimer_async.rs new file mode 100644 index 000000000..34862b61f --- /dev/null +++ b/examples/src/bin/ostimer_async.rs @@ -0,0 +1,52 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::bind_interrupts; +use embassy_mcxa_examples::{init_ostimer0, init_uart2}; +use embassy_time::{Duration, Timer}; +use hal::uart; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind only OS_EVENT, and retain the symbol explicitly so it can’t be GC’ed. +bind_interrupts!(struct Irqs { + OS_EVENT => hal::ostimer::time_driver::OsEventHandler; +}); + +#[used] +#[no_mangle] +static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = hal::init(hal::config::Config::default()); + + // Enable/clock OSTIMER0 and UART2 before touching their registers + unsafe { + init_ostimer0(hal::pac()); + init_uart2(hal::pac()); + } + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let uart = uart::Uart::::new(_p.LPUART2, uart::Config::new(src)); + uart.write_str_blocking("boot\n"); + + // Avoid mass NVIC writes here; DefaultHandler now safely returns. + + // Initialize embassy-time global driver backed by OSTIMER0 (re-enables OS_EVENT with priority) + // The bind_interrupts! macro handles handler binding automatically + + // Initialize OSTIMER with default 1MHz frequency + // Adjust this value to match your actual OSTIMER clock frequency + hal::ostimer::time_driver::init(hal::config::Config::default().time_interrupt_priority, 1_000_000); + + // Removed force-pend; rely on real hardware match to trigger OS_EVENT. + + // Log using defmt if enabled + defmt::info!("OSTIMER async example starting..."); + + loop { + defmt::info!("tick"); + uart.write_str_blocking("tick\n"); + Timer::after(Duration::from_millis(1000)).await; + } +} diff --git a/examples/src/bin/ostimer_counter.rs b/examples/src/bin/ostimer_counter.rs new file mode 100644 index 000000000..20044760a --- /dev/null +++ b/examples/src/bin/ostimer_counter.rs @@ -0,0 +1,125 @@ +//! # OSTIMER Counter Reading and Reset Example +//! +//! This example demonstrates the new timer counter reading and reset functionality +//! of the OSTIMER driver. + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa_examples::{init_ostimer0, init_uart2}; +use embassy_time::{Duration, Timer}; +use hal::bind_interrupts; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +bind_interrupts!(struct Irqs { + OS_EVENT => hal::ostimer::time_driver::OsEventHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(Default::default()); + + // Enable/clock OSTIMER0 and UART2 before touching their registers + unsafe { + init_ostimer0(hal::pac()); + init_uart2(hal::pac()); + } + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let mut uart = hal::uart::Uart::::new(p.LPUART2, hal::uart::Config::new(src)); + + uart.write_str_blocking("OSTIMER Counter Reading and Reset Example\n"); + + // Initialize the OSTIMER time driver + hal::ostimer::time_driver::init( + hal::interrupt::Priority::from(3), + 1_000_000, // 1MHz clock + ); + + // Create OSTIMER instance + let ostimer = hal::ostimer::Ostimer::::new( + p.OSTIMER0, + hal::ostimer::Config { + init_match_max: true, + clock_frequency_hz: 1_000_000, + }, + hal::pac(), + ); + + // Read initial counter value + let initial_counter = ostimer.now(); + uart.write_str_blocking("Initial counter value: "); + write_u64(&mut uart, initial_counter); + uart.write_str_blocking("\n"); + + // Wait a bit to let counter increment + Timer::after(Duration::from_millis(100)).await; + + // Read counter again + let counter_after_wait = ostimer.now(); + uart.write_str_blocking("Counter after 100ms wait: "); + write_u64(&mut uart, counter_after_wait); + uart.write_str_blocking("\n"); + uart.write_str_blocking("Difference: "); + write_u64(&mut uart, counter_after_wait - initial_counter); + uart.write_str_blocking(" ticks\n"); + + // Reset the timer + uart.write_str_blocking("Resetting timer...\n"); + ostimer.reset(hal::pac()); + + // Read counter after reset + let counter_after_reset = ostimer.now(); + uart.write_str_blocking("Counter after reset: "); + write_u64(&mut uart, counter_after_reset); + uart.write_str_blocking("\n"); + + // Wait again to verify timer is working + Timer::after(Duration::from_millis(50)).await; + + let final_counter = ostimer.now(); + uart.write_str_blocking("Counter after another 50ms: "); + write_u64(&mut uart, final_counter); + uart.write_str_blocking("\n"); + uart.write_str_blocking("Difference after reset: "); + write_u64(&mut uart, final_counter - counter_after_reset); + uart.write_str_blocking(" ticks\n"); + + uart.write_str_blocking("Example complete\n"); +} + +// Helper function to write a u64 value as decimal string +fn write_u64(uart: &mut hal::uart::Uart, value: u64) { + if value == 0 { + uart.write_str_blocking("0"); + return; + } + + let mut buffer = [0u8; 20]; // Enough for max u64 + let mut i = 0; + let mut v = value; + + while v > 0 { + buffer[i] = b'0' + (v % 10) as u8; + v /= 10; + i += 1; + } + + // Write digits in reverse order + while i > 0 { + i -= 1; + match buffer[i] { + b'0' => uart.write_str_blocking("0"), + b'1' => uart.write_str_blocking("1"), + b'2' => uart.write_str_blocking("2"), + b'3' => uart.write_str_blocking("3"), + b'4' => uart.write_str_blocking("4"), + b'5' => uart.write_str_blocking("5"), + b'6' => uart.write_str_blocking("6"), + b'7' => uart.write_str_blocking("7"), + b'8' => uart.write_str_blocking("8"), + b'9' => uart.write_str_blocking("9"), + _ => uart.write_str_blocking("?"), + } + } +} diff --git a/examples/src/bin/ostimer_race_test.rs b/examples/src/bin/ostimer_race_test.rs new file mode 100644 index 000000000..720a058d5 --- /dev/null +++ b/examples/src/bin/ostimer_race_test.rs @@ -0,0 +1,393 @@ +//! # OSTIMER Race Condition Test +//! +//! This example tests for race conditions in the OSTIMER driver by: +//! - Scheduling alarms sequentially (hardware limitation: only one at a time) +//! - Reading the counter during interrupt-heavy periods +//! - Testing concurrent timer operations +//! - Stress testing interrupt handling + +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicU32, Ordering}; + +use embassy_executor::Spawner; +use embassy_mcxa_examples::{init_ostimer0, init_uart2}; +use embassy_time::{Duration, Timer}; +use hal::bind_interrupts; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +bind_interrupts!(struct Irqs { + OS_EVENT => hal::ostimer::time_driver::OsEventHandler; +}); + +#[used] +#[no_mangle] +static KEEP_OS_EVENT: unsafe extern "C" fn() = OS_EVENT; + +// Global counters for race condition detection +static ALARM_CALLBACK_COUNT: AtomicU32 = AtomicU32::new(0); +static INTERRUPT_COUNT: AtomicU32 = AtomicU32::new(0); +static RACE_DETECTED: AtomicU32 = AtomicU32::new(0); + +// Alarm callback function +fn alarm_callback() { + let _count = ALARM_CALLBACK_COUNT.fetch_add(1, Ordering::SeqCst); + INTERRUPT_COUNT.fetch_add(1, Ordering::SeqCst); + + // Simulate some work in the callback to increase chance of races + for _ in 0..10 { + cortex_m::asm::nop(); + } +} + +fn report_default_handler(uart: &mut hal::uart::Uart) { + let snapshot = hal::interrupt::default_handler_snapshot(); + if snapshot.count == 0 { + return; + } + + uart.write_str_blocking("WARNING: DefaultHandler executed "); + write_u32(uart, snapshot.count); + uart.write_str_blocking(" time(s). Vector="); + write_u32(uart, snapshot.vector as u32); + uart.write_str_blocking(" CFSR=0x"); + write_hex32(uart, snapshot.cfsr); + uart.write_str_blocking(" HFSR=0x"); + write_hex32(uart, snapshot.hfsr); + uart.write_str_blocking(" PC=0x"); + write_hex32(uart, snapshot.stacked_pc); + uart.write_str_blocking(" LR=0x"); + write_hex32(uart, snapshot.stacked_lr); + uart.write_str_blocking(" SP=0x"); + write_hex32(uart, snapshot.stacked_sp); + uart.write_str_blocking("\n"); + + hal::interrupt::clear_default_handler_snapshot(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(Default::default()); + + // Enable/clock OSTIMER0 and UART2 before touching their registers + unsafe { + init_ostimer0(hal::pac()); + init_uart2(hal::pac()); + } + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let mut uart = hal::uart::Uart::::new(p.LPUART2, hal::uart::Config::new(src)); + + uart.write_str_blocking("OSTIMER Race Condition Test Starting...\n"); + + // The bind_interrupts! macro handles handler binding automatically + + // Initialize the OSTIMER time driver FIRST + hal::ostimer::time_driver::init( + hal::interrupt::Priority::from(3), + 1_000_000, // 1MHz clock + ); + + uart.write_str_blocking("Time driver initialized\n"); + + // Create OSTIMER instance + let ostimer = hal::ostimer::Ostimer::::new( + p.OSTIMER0, + hal::ostimer::Config { + init_match_max: true, + clock_frequency_hz: 1_000_000, + }, + hal::pac(), + ); + + uart.write_str_blocking("OSTIMER instance created\n"); + + // Test 1: Sequential alarm scheduling (OSTIMER only supports one alarm at a time) + uart.write_str_blocking("Test 1: Sequential alarm scheduling...\n"); + test_rapid_alarms(&ostimer, &mut uart).await; + report_default_handler(&mut uart); + + // Test 2: Counter reading during interrupts + uart.write_str_blocking("Test 2: Counter reading during interrupts...\n"); + test_counter_reading_during_interrupts(&ostimer, &mut uart).await; + report_default_handler(&mut uart); + + // Test 3: Concurrent timer operations + uart.write_str_blocking("Test 3: Concurrent timer operations...\n"); + test_concurrent_operations(&ostimer, &mut uart).await; + report_default_handler(&mut uart); + + // Test 4: Timer reset during operation + uart.write_str_blocking("Test 4: Timer reset during operation...\n"); + test_reset_during_operation(&ostimer, &mut uart, hal::pac()).await; + report_default_handler(&mut uart); + + // Report results + uart.write_str_blocking("Race condition test complete\n"); + uart.write_str_blocking("Callback count: "); + write_u32(&mut uart, ALARM_CALLBACK_COUNT.load(Ordering::SeqCst)); + uart.write_str_blocking("\nInterrupt count: "); + write_u32(&mut uart, INTERRUPT_COUNT.load(Ordering::SeqCst)); + uart.write_str_blocking("\nRaces detected: "); + write_u32(&mut uart, RACE_DETECTED.load(Ordering::SeqCst)); + uart.write_str_blocking("\n"); +} + +// Test rapid alarm scheduling to stress interrupt handling +async fn test_rapid_alarms( + ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, + uart: &mut hal::uart::Uart, +) { + let initial_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst); + + // Schedule 10 alarms sequentially (OSTIMER only supports one alarm at a time) + for _i in 0..10 { + let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback); + let delay_us = 1000; // 1ms delay for each alarm + if ostimer.schedule_alarm_delay(&alarm, delay_us) { + // Wait for this alarm to complete before scheduling the next + Timer::after(Duration::from_micros(delay_us + 100)).await; + report_default_handler(uart); + } else { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm (match not ready)\n"); + } + } + + // All alarms should have completed by now + let final_count = ALARM_CALLBACK_COUNT.load(Ordering::SeqCst); + let expected_count = initial_count + 10; + + if final_count != expected_count { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Expected "); + write_u32(uart, expected_count); + uart.write_str_blocking(" callbacks, got "); + write_u32(uart, final_count); + uart.write_str_blocking("\n"); + } else { + uart.write_str_blocking("PASS: All rapid alarms executed\n"); + } +} + +// Test reading counter while interrupts are firing +async fn test_counter_reading_during_interrupts( + ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, + uart: &mut hal::uart::Uart, +) { + let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); + + // Schedule an alarm that will fire soon + let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback); + if !ostimer.schedule_alarm_delay(&alarm, 500) { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before counter stress\n"); + } + + // While alarm is pending, read the counter many times rapidly + // This tests if counter reading is atomic and doesn't get corrupted by interrupts + let mut last_counter = ostimer.now(); + let mut consistent_reads = 0; + let mut total_reads = 0; + + for _ in 0..1000 { + let current_counter = ostimer.now(); + total_reads += 1; + + // Check if counter is monotonically increasing (basic sanity check) + if current_counter >= last_counter { + consistent_reads += 1; + } + last_counter = current_counter; + + // Small delay between reads + for _ in 0..10 { + cortex_m::asm::nop(); + } + + report_default_handler(uart); + } + + // Wait for alarm to complete + Timer::after(Duration::from_millis(1)).await; + + let final_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); + + if consistent_reads == total_reads { + uart.write_str_blocking("PASS: Counter reading consistent during interrupts\n"); + } else { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Counter reading inconsistent: "); + write_u32(uart, consistent_reads); + uart.write_str_blocking("/"); + write_u32(uart, total_reads); + uart.write_str_blocking(" consistent\n"); + } + + if final_interrupt_count > initial_interrupt_count { + uart.write_str_blocking("PASS: Interrupt fired during counter reading test\n"); + } else { + uart.write_str_blocking("WARNING: No interrupt fired during counter reading test\n"); + } +} + +// Test concurrent timer operations (embassy-time + alarms) +async fn test_concurrent_operations( + ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, + uart: &mut hal::uart::Uart, +) { + let initial_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); + + // Start an embassy-time timer + let timer_future = Timer::after(Duration::from_millis(2)); + + // Schedule an alarm that should fire before the timer + let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback); + if !ostimer.schedule_alarm_delay(&alarm, 1000) { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before concurrent operations\n"); + } + + // Wait for both to complete + timer_future.await; + + let final_interrupt_count = INTERRUPT_COUNT.load(Ordering::SeqCst); + + if final_interrupt_count > initial_interrupt_count { + uart.write_str_blocking("PASS: Concurrent operations completed\n"); + } else { + uart.write_str_blocking("WARNING: No interrupts during concurrent operations\n"); + } +} + +// Test timer reset during active operations +async fn test_reset_during_operation( + ostimer: &hal::ostimer::Ostimer<'_, hal::ostimer::Ostimer0>, + uart: &mut hal::uart::Uart, + peripherals: &hal::pac::Peripherals, +) { + let initial_counter = ostimer.now(); + + // Schedule an alarm + let alarm = hal::ostimer::Alarm::new().with_callback(alarm_callback); + if !ostimer.schedule_alarm_delay(&alarm, 2000) { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Failed to program OSTIMER alarm before reset test\n"); + } + + // Wait a bit then reset the timer + Timer::after(Duration::from_millis(1)).await; + ostimer.reset(peripherals); + + // Check counter after reset + let counter_after_reset = ostimer.now(); + + // Wait to see if the alarm still fires (it shouldn't after reset) + Timer::after(Duration::from_millis(2)).await; + + let final_counter = ostimer.now(); + + if counter_after_reset < initial_counter { + uart.write_str_blocking("PASS: Timer reset successful\n"); + } else { + RACE_DETECTED.fetch_add(1, Ordering::SeqCst); + uart.write_str_blocking("ERROR: Timer reset may have failed\n"); + } + + uart.write_str_blocking("Counter progression after reset: "); + write_u64(uart, initial_counter); + uart.write_str_blocking(" -> "); + write_u64(uart, counter_after_reset); + uart.write_str_blocking(" -> "); + write_u64(uart, final_counter); + uart.write_str_blocking("\n"); +} + +// Helper function to write a u32 value as decimal string +fn write_u32(uart: &mut hal::uart::Uart, value: u32) { + if value == 0 { + uart.write_str_blocking("0"); + return; + } + + let mut buffer = [0u8; 10]; // Enough for max u32 + let mut i = 0; + let mut v = value; + + while v > 0 { + buffer[i] = b'0' + (v % 10) as u8; + v /= 10; + i += 1; + } + + // Write digits in reverse order + while i > 0 { + i -= 1; + match buffer[i] { + b'0' => uart.write_str_blocking("0"), + b'1' => uart.write_str_blocking("1"), + b'2' => uart.write_str_blocking("2"), + b'3' => uart.write_str_blocking("3"), + b'4' => uart.write_str_blocking("4"), + b'5' => uart.write_str_blocking("5"), + b'6' => uart.write_str_blocking("6"), + b'7' => uart.write_str_blocking("7"), + b'8' => uart.write_str_blocking("8"), + b'9' => uart.write_str_blocking("9"), + _ => uart.write_str_blocking("?"), + } + } +} + +fn write_hex32(uart: &mut hal::uart::Uart, value: u32) { + let mut buf = [b'0'; 8]; + let mut tmp = value; + for i in (0..8).rev() { + let digit = (tmp & 0xF) as u8; + buf[i] = match digit { + 0..=9 => b'0' + digit, + 10..=15 => b'A' + (digit - 10), + _ => b'?', + }; + tmp >>= 4; + } + for b in &buf { + uart.write_byte(*b); + } +} + +// Helper function to write a u64 value as decimal string +fn write_u64(uart: &mut hal::uart::Uart, value: u64) { + if value == 0 { + uart.write_str_blocking("0"); + return; + } + + let mut buffer = [0u8; 20]; // Enough for max u64 + let mut i = 0; + let mut v = value; + + while v > 0 { + buffer[i] = b'0' + (v % 10) as u8; + v /= 10; + i += 1; + } + + // Write digits in reverse order + while i > 0 { + i -= 1; + match buffer[i] { + b'0' => uart.write_str_blocking("0"), + b'1' => uart.write_str_blocking("1"), + b'2' => uart.write_str_blocking("2"), + b'3' => uart.write_str_blocking("3"), + b'4' => uart.write_str_blocking("4"), + b'5' => uart.write_str_blocking("5"), + b'6' => uart.write_str_blocking("6"), + b'7' => uart.write_str_blocking("7"), + b'8' => uart.write_str_blocking("8"), + b'9' => uart.write_str_blocking("9"), + _ => uart.write_str_blocking("?"), + } + } +} diff --git a/examples/src/bin/rtc_alarm.rs b/examples/src/bin/rtc_alarm.rs new file mode 100644 index 000000000..dc07b5757 --- /dev/null +++ b/examples/src/bin/rtc_alarm.rs @@ -0,0 +1,84 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa as hal; +use embassy_mcxa_examples::init_uart2; +use hal::rtc::{RtcDateTime, RtcInterruptEnable}; +use hal::{uart, InterruptExt}; + +type MyRtc = hal::rtc::Rtc; + +use embassy_mcxa::bind_interrupts; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + RTC => hal::rtc::RtcHandler; +}); + +#[used] +#[no_mangle] +static KEEP_RTC: unsafe extern "C" fn() = RTC; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = hal::init(hal::config::Config::default()); + + unsafe { + init_uart2(hal::pac()); + } + + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let uart = uart::Uart::::new(p.LPUART2, uart::Config::new(src)); + + uart.write_str_blocking("\r\n=== RTC Alarm Example ===\r\n"); + + unsafe { hal::clocks::init_fro16k(hal::pac()) }; + + let rtc_config = hal::rtc::get_default_config(); + + let rtc = MyRtc::new(p.RTC0, rtc_config); + + let now = RtcDateTime { + year: 2025, + month: 10, + day: 15, + hour: 14, + minute: 30, + second: 0, + }; + + rtc.stop(); + + uart.write_str_blocking("Time set to: 2025-10-15 14:30:00\r\n"); + rtc.set_datetime(now); + + let mut alarm = now; + alarm.second += 10; + + rtc.set_alarm(alarm); + uart.write_str_blocking("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)\r\n"); + + rtc.set_interrupt(RtcInterruptEnable::RTC_ALARM_INTERRUPT_ENABLE); + + unsafe { + hal::interrupt::RTC.enable(); + } + + unsafe { + cortex_m::interrupt::enable(); + } + + rtc.start(); + + uart.write_str_blocking("RTC started, waiting for alarm...\r\n"); + + loop { + if rtc.is_alarm_triggered() { + uart.write_str_blocking("\r\n*** ALARM TRIGGERED! ***\r\n"); + break; + } + } + + uart.write_str_blocking("Example complete - Test PASSED!\r\n"); +} diff --git a/examples/src/bin/uart_interrupt.rs b/examples/src/bin/uart_interrupt.rs new file mode 100644 index 000000000..100588727 --- /dev/null +++ b/examples/src/bin/uart_interrupt.rs @@ -0,0 +1,66 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_mcxa::bind_interrupts; +use embassy_mcxa_examples::init_uart2; +use hal::interrupt::typelevel::Handler; +use hal::uart; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +// Bind LPUART2 interrupt to our handler +bind_interrupts!(struct Irqs { + LPUART2 => hal::uart::UartInterruptHandler; +}); + +#[used] +#[no_mangle] +static KEEP_LPUART2: unsafe extern "C" fn() = LPUART2; + +// Wrapper function for the interrupt handler +unsafe extern "C" fn lpuart2_handler() { + hal::uart::UartInterruptHandler::on_interrupt(); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = hal::init(hal::config::Config::default()); + + // Enable/clock UART2 before touching its registers + unsafe { + init_uart2(hal::pac()); + } + let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; + let uart = uart::Uart::::new(_p.LPUART2, uart::Config::new(src)); + + // Configure LPUART2 interrupt for UART operation BEFORE any UART usage + hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::from(3)); + + // Manually install the interrupt handler and enable RX IRQs in the peripheral + unsafe { + hal::interrupt::LPUART2.install_handler(lpuart2_handler); + // Enable RX interrupts so the handler actually fires on incoming bytes + uart.enable_rx_interrupts(); + } + + // Print welcome message + uart.write_str_blocking("UART interrupt echo demo starting...\r\n"); + uart.write_str_blocking("Type characters to echo them back.\r\n"); + + // Log using defmt if enabled + defmt::info!("UART interrupt echo demo starting..."); + + loop { + // Check if we have received any data + if uart.rx_data_available() { + if let Some(byte) = uart.try_read_byte() { + // Echo it back + uart.write_byte(byte); + uart.write_str_blocking(" (received)\r\n"); + } + } else { + // No data available, wait a bit before checking again + cortex_m::asm::delay(12_000_000); // ~1 second at 12MHz + } + } +} diff --git a/examples/src/common/mod.rs b/examples/src/common/mod.rs new file mode 100644 index 000000000..8cb4590f8 --- /dev/null +++ b/examples/src/common/mod.rs @@ -0,0 +1,45 @@ +//! Shared board-specific helpers for the FRDM-MCXA276 examples. +//! These live with the examples so the HAL stays generic. + +use hal::{clocks, pins, reset}; +use {embassy_mcxa as hal, panic_probe as _}; + +/// Initialize clocks and pin muxing for UART2 debug console. +/// Safe to call multiple times; writes are idempotent for our use. +#[allow(dead_code)] +pub unsafe fn init_uart2(p: &hal::pac::Peripherals) { + clocks::ensure_frolf_running(p); + clocks::enable_uart2_port2(p); + reset::release_reset_port2(p); + reset::release_reset_lpuart2(p); + pins::configure_uart2_pins_port2(); + clocks::select_uart2_clock(p); +} + +/// Initialize clocks for the LED GPIO/PORT used by the blink example. +#[allow(dead_code)] +pub unsafe fn init_led(p: &hal::pac::Peripherals) { + clocks::enable_led_port(p); + reset::release_reset_gpio3(p); + reset::release_reset_port3(p); +} + +/// Initialize clocks for OSTIMER0 (1 MHz source). +#[allow(dead_code)] +pub unsafe fn init_ostimer0(p: &hal::pac::Peripherals) { + clocks::ensure_frolf_running(p); + clocks::enable_ostimer0(p); + reset::release_reset_ostimer0(p); + clocks::select_ostimer0_clock_1m(p); +} + +/// Initialize clocks and pin muxing for ADC. +#[allow(dead_code)] +pub unsafe fn init_adc(p: &hal::pac::Peripherals) { + clocks::ensure_frolf_running(p); + clocks::enable_adc(p); + reset::release_reset_port1(p); + reset::release_reset_adc1(p); + pins::configure_adc_pins(); + clocks::select_adc_clock(p); +} diff --git a/examples/src/lib.rs b/examples/src/lib.rs new file mode 100644 index 000000000..cf4194559 --- /dev/null +++ b/examples/src/lib.rs @@ -0,0 +1,63 @@ +#![no_std] + +//! Shared board-specific helpers for the FRDM-MCXA276 examples. +//! These live with the examples so the HAL stays generic. + +use hal::{clocks, pins, reset}; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +/// Initialize clocks and pin muxing for UART2 debug console. +/// Safe to call multiple times; writes are idempotent for our use. +/// +/// # Safety +/// +/// Called only once to initialize the peripheral +#[allow(dead_code)] +pub unsafe fn init_uart2(p: &hal::pac::Peripherals) { + clocks::ensure_frolf_running(p); + clocks::enable_uart2_port2(p); + reset::release_reset_port2(p); + reset::release_reset_lpuart2(p); + pins::configure_uart2_pins_port2(); + clocks::select_uart2_clock(p); +} + +/// Initialize clocks for the LED GPIO/PORT used by the blink example. +/// +/// # Safety +/// +/// Called only once to initialize the peripheral +#[allow(dead_code)] +pub unsafe fn init_led(p: &hal::pac::Peripherals) { + clocks::enable_led_port(p); + reset::release_reset_gpio3(p); + reset::release_reset_port3(p); +} + +/// Initialize clocks for OSTIMER0 (1 MHz source). +/// +/// # Safety +/// +/// Called only once to initialize the peripheral +#[allow(dead_code)] +pub unsafe fn init_ostimer0(p: &hal::pac::Peripherals) { + clocks::ensure_frolf_running(p); + clocks::enable_ostimer0(p); + reset::release_reset_ostimer0(p); + clocks::select_ostimer0_clock_1m(p); +} + +/// Initialize clocks and pin muxing for ADC. +/// +/// # Safety +/// +/// Called only once to initialize the peripheral +#[allow(dead_code)] +pub unsafe fn init_adc(p: &hal::pac::Peripherals) { + clocks::ensure_frolf_running(p); + clocks::enable_adc(p); + reset::release_reset_port1(p); + reset::release_reset_adc1(p); + pins::configure_adc_pins(); + clocks::select_adc_clock(p); +} diff --git a/examples/uart_interrupt.rs b/examples/uart_interrupt.rs deleted file mode 100644 index bd734f859..000000000 --- a/examples/uart_interrupt.rs +++ /dev/null @@ -1,69 +0,0 @@ -#![no_std] -#![no_main] - -use embassy_executor::Spawner; -use embassy_mcxa276 as hal; -use hal::interrupt::typelevel::Handler; -use hal::uart; - -mod common; - -use embassy_mcxa276::bind_interrupts; -use {defmt_rtt as _, panic_probe as _}; - -// Bind LPUART2 interrupt to our handler -bind_interrupts!(struct Irqs { - LPUART2 => hal::uart::UartInterruptHandler; -}); - -#[used] -#[no_mangle] -static KEEP_LPUART2: unsafe extern "C" fn() = LPUART2; - -// Wrapper function for the interrupt handler -unsafe extern "C" fn lpuart2_handler() { - hal::uart::UartInterruptHandler::on_interrupt(); -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let _p = hal::init(hal::config::Config::default()); - - // Enable/clock UART2 before touching its registers - unsafe { - common::init_uart2(hal::pac()); - } - let src = unsafe { hal::clocks::uart2_src_hz(hal::pac()) }; - let uart = uart::Uart::::new(_p.LPUART2, uart::Config::new(src)); - - // Configure LPUART2 interrupt for UART operation BEFORE any UART usage - hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::from(3)); - - // Manually install the interrupt handler and enable RX IRQs in the peripheral - unsafe { - hal::interrupt::LPUART2.install_handler(lpuart2_handler); - // Enable RX interrupts so the handler actually fires on incoming bytes - uart.enable_rx_interrupts(); - } - - // Print welcome message - uart.write_str_blocking("UART interrupt echo demo starting...\r\n"); - uart.write_str_blocking("Type characters to echo them back.\r\n"); - - // Log using defmt if enabled - defmt::info!("UART interrupt echo demo starting..."); - - loop { - // Check if we have received any data - if uart.rx_data_available() { - if let Some(byte) = uart.try_read_byte() { - // Echo it back - uart.write_byte(byte); - uart.write_str_blocking(" (received)\r\n"); - } - } else { - // No data available, wait a bit before checking again - cortex_m::asm::delay(12_000_000); // ~1 second at 12MHz - } - } -} diff --git a/memory.x b/memory.x deleted file mode 100644 index b47534f18..000000000 --- a/memory.x +++ /dev/null @@ -1,10 +0,0 @@ -/* Memory layout for MCXA276 - RAM execution with cortex-m-rt */ -MEMORY -{ - /* FLASH and RAM overlap for RAM-execution experiments. */ - FLASH : ORIGIN = 0x20000000, LENGTH = 128K - /* RAM overlaps FLASH */ - RAM : ORIGIN = 0x20000000, LENGTH = 128K -} - -/* Leave symbol and section boundary definitions to cortex-m-rt's link.x. */ diff --git a/src/lib.rs b/src/lib.rs index 9899564d8..4e5ac0109 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,29 +62,29 @@ pub fn init(cfg: crate::config::Config) -> Peripherals { peripherals } -/// Optional hook called by cortex-m-rt before RAM init. -/// We proactively mask and clear all NVIC IRQs to avoid wedges from stale state -/// left by soft resets/debug sessions. -/// -/// NOTE: Manual VTOR setup is required for RAM execution. The cortex-m-rt 'set-vtor' -/// feature is incompatible with our setup because it expects __vector_table to be -/// defined differently than how our RAM-based linker script arranges it. -#[no_mangle] -pub unsafe extern "C" fn __pre_init() { - // Set the VTOR to point to the interrupt vector table in RAM - // This is required since code runs from RAM on this MCU - crate::interrupt::vtor_set_ram_vector_base(0x2000_0000 as *const u32); +// /// Optional hook called by cortex-m-rt before RAM init. +// /// We proactively mask and clear all NVIC IRQs to avoid wedges from stale state +// /// left by soft resets/debug sessions. +// /// +// /// NOTE: Manual VTOR setup is required for RAM execution. The cortex-m-rt 'set-vtor' +// /// feature is incompatible with our setup because it expects __vector_table to be +// /// defined differently than how our RAM-based linker script arranges it. +// #[no_mangle] +// pub unsafe extern "C" fn __pre_init() { +// // Set the VTOR to point to the interrupt vector table in RAM +// // This is required since code runs from RAM on this MCU +// crate::interrupt::vtor_set_ram_vector_base(0x2000_0000 as *const u32); - // Mask and clear pending for all NVIC lines (0..127) to avoid stale state across runs. - let nvic = &*cortex_m::peripheral::NVIC::PTR; - for i in 0..4 { - // 4 words x 32 = 128 IRQs - nvic.icer[i].write(0xFFFF_FFFF); - nvic.icpr[i].write(0xFFFF_FFFF); - } - // Do NOT touch peripheral registers here: clocks may be off and accesses can fault. - crate::interrupt::clear_default_handler_snapshot(); -} +// // Mask and clear pending for all NVIC lines (0..127) to avoid stale state across runs. +// let nvic = &*cortex_m::peripheral::NVIC::PTR; +// for i in 0..4 { +// // 4 words x 32 = 128 IRQs +// nvic.icer[i].write(0xFFFF_FFFF); +// nvic.icpr[i].write(0xFFFF_FFFF); +// } +// // Do NOT touch peripheral registers here: clocks may be off and accesses can fault. +// crate::interrupt::clear_default_handler_snapshot(); +// } /// Internal helper to dispatch a type-level interrupt handler. #[inline(always)] -- cgit