diff --git a/tests/mcxa/.cargo/config.toml b/tests/mcxa/.cargo/config.toml new file mode 100644 index 0000000000..d2a2878b1a --- /dev/null +++ b/tests/mcxa/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "teleprobe local run --chip MCXA276 --protocol swd --elf" + +[build] +target = "thumbv8m.main-none-eabihf" # Cortex-M33 + +[env] +DEFMT_LOG = "trace" diff --git a/tests/mcxa/Cargo.toml b/tests/mcxa/Cargo.toml new file mode 100644 index 0000000000..fc0f1f989f --- /dev/null +++ b/tests/mcxa/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "embassy-mcxa-examples" +version = "0.1.0" +edition = "2024" +license = "MIT OR Apache-2.0" +publish = false + +[dependencies] +teleprobe-meta = "1.1" + +cortex-m = { version = "0.7", features = ["critical-section-single-core"] } +cortex-m-rt = { version = "0.7", features = ["set-sp", "set-vtor"] } +crc = "3.4.0" +critical-section = "1.2.0" +defmt = "1.0" +defmt-rtt = "1.0" + +embassy-embedded-hal = { version = "0.5.0", path = "../../embassy-embedded-hal" } +embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"], default-features = false } +embassy-mcxa = { version = "0.1.0", path = "../../embassy-mcxa", features = ["defmt", "unstable-pac"] } +embassy-sync = { version = "0.7.2", path = "../../embassy-sync" } +embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-time-driver = { version = "0.2.1", path = "../../embassy-time-driver", optional = true } + +embedded-io-async = "0.7.0" +panic-probe = { version = "1.0", features = ["print-defmt"] } +static_cell = "2.1.1" + +[profile.dev] +debug = 2 +debug-assertions = true +opt-level = 's' +overflow-checks = true + +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false +incremental = false +lto = "fat" +opt-level = 's' +overflow-checks = false + +# do not optimize proc-macro crates = faster builds from scratch +[profile.dev.build-override] +codegen-units = 8 +debug = false +debug-assertions = false +opt-level = 0 +overflow-checks = false + +[profile.release.build-override] +codegen-units = 8 +debug = false +debug-assertions = false +opt-level = 0 +overflow-checks = false + +[package.metadata.embassy] +build = [ + { target = "thumbv8m.main-none-eabihf", artifact-dir = "out/tests/frdm-mcx-a266" } +] diff --git a/tests/mcxa/build.rs b/tests/mcxa/build.rs new file mode 100644 index 0000000000..3f69a82ab4 --- /dev/null +++ b/tests/mcxa/build.rs @@ -0,0 +1,20 @@ +use std::env; +use std::fs; +use std::path::PathBuf; + +fn main() { + let out = PathBuf::from(env::var("OUT_DIR").unwrap()); + + let memory_x = include_bytes!("memory.x"); + fs::write(out.join("memory.x"), memory_x).unwrap(); + + // copy main linker script. + fs::write(out.join("link_ram.x"), include_bytes!("../link_ram_cortex_m.x")).unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=link_ram.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + println!("cargo:rustc-link-arg-bins=-Tteleprobe.x"); +} diff --git a/tests/mcxa/memory.x b/tests/mcxa/memory.x new file mode 100644 index 0000000000..315ced58a3 --- /dev/null +++ b/tests/mcxa/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 1M + RAM : ORIGIN = 0x20000000, LENGTH = 128K +} diff --git a/tests/mcxa/src/bin/adc.rs b/tests/mcxa/src/bin/adc.rs new file mode 100644 index 0000000000..30775f639b --- /dev/null +++ b/tests/mcxa/src/bin/adc.rs @@ -0,0 +1,76 @@ +#![no_std] +#![no_main] + +teleprobe_meta::target!(b"frdm-mcx-a266"); + +use embassy_executor::Spawner; +use embassy_mcxa::bind_interrupts; +use embassy_mcxa::gpio::Output; +use hal::adc::{self, Adc, TriggerPriorityPolicy}; +use hal::clocks::PoweredClock; +use hal::clocks::config::Div8; +use hal::clocks::periph_helpers::{AdcClockSel, Div4}; +use hal::config::Config; +use hal::pac::adc1::cfg::{Pwrsel, Refsel}; +use hal::pac::adc1::cmdl1::Mode; +use hal::pac::adc1::ctrl::CalAvgs; +use hal::peripherals::ADC0; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +bind_interrupts!(struct Irqs { + ADC0 => adc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); + + let p = hal::init(config); + + let mut output = Output::new( + p.P1_8, + embassy_mcxa::gpio::Level::Low, + embassy_mcxa::gpio::DriveStrength::Normal, + embassy_mcxa::gpio::SlewRate::Slow, + ); + + let adc_config = adc::Config { + 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, + power: PoweredClock::NormalEnabledDeepSleepDisabled, + source: AdcClockSel::FroLfDiv, + div: Div4::no_div(), + }; + let mut adc = Adc::new_async(p.ADC0, p.P2_4, Irqs, adc_config).unwrap(); + + adc.do_offset_calibration(); + adc.do_auto_calibration(); + adc.set_resolution(Mode::Data16Bits); + + // Set output low. ADC should measure (close to) GND + + output.set_low(); + embassy_time::Timer::after_millis(10).await; + + let val = adc.read().await.unwrap(); + assert!(val < 0x1000); + + // Set output high, so ADC should measure (close to) VDD + + output.set_high(); + embassy_time::Timer::after_millis(10).await; + + let val = adc.read().await.unwrap(); + assert!(val > 0xE000); + + defmt::info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/mcxa/src/bin/crc.rs b/tests/mcxa/src/bin/crc.rs new file mode 100644 index 0000000000..bbb1f1286b --- /dev/null +++ b/tests/mcxa/src/bin/crc.rs @@ -0,0 +1,157 @@ +#![no_std] +#![no_main] + +teleprobe_meta::target!(b"frdm-mcx-a266"); + +use embassy_executor::Spawner; +use hal::config::Config; +use hal::crc::Crc; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +const CCITT_FALSE: crc::Algorithm = crc::Algorithm { + width: 16, + poly: 0x1021, + init: 0xffff, + refin: false, + refout: false, + xorout: 0, + check: 0x29b1, + residue: 0x0000, +}; + +const POSIX: crc::Algorithm = crc::Algorithm { + width: 32, + poly: 0x04c1_1db7, + init: 0, + refin: false, + refout: false, + xorout: 0xffff_ffff, + check: 0x765e_7680, + residue: 0x0000, +}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let config = Config::default(); + let mut p = hal::init(config); + + defmt::info!("CRC example"); + + let buf_u8 = [0x00u8, 0x11, 0x22, 0x33]; + let buf_u16 = [0x0000u16, 0x1111, 0x2222, 0x3333]; + let buf_u32 = [0x0000_0000u32, 0x1111_1111, 0x2222_2222, 0x3333_3333]; + + // CCITT False + + let sw_crc = crc::Crc::::new(&CCITT_FALSE); + let mut digest = sw_crc.digest(); + digest.update(&buf_u8); + let sw_sum = digest.finalize(); + + let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); + crc.feed(&buf_u8); + let sum = crc.finalize(); + assert_eq!(sum, sw_sum); + + let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); + crc.feed(&buf_u16); + let sum = crc.finalize(); + assert_eq!(sum, 0xa467); + + let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); + crc.feed(&buf_u32); + let sum = crc.finalize(); + assert_eq!(sum, 0xe5c7); + + // Maxim + + let sw_crc = crc::Crc::::new(&crc::CRC_16_MAXIM_DOW); + let mut digest = sw_crc.digest(); + digest.update(&buf_u8); + let sw_sum = digest.finalize(); + + let mut crc = Crc::new_maxim(p.CRC0.reborrow()); + crc.feed(&buf_u8); + let sum = crc.finalize(); + assert_eq!(sum, sw_sum); + + let mut crc = Crc::new_maxim(p.CRC0.reborrow()); + crc.feed(&buf_u16); + let sum = crc.finalize(); + assert_eq!(sum, 0x2afe); + + let mut crc = Crc::new_maxim(p.CRC0.reborrow()); + crc.feed(&buf_u32); + let sum = crc.finalize(); + assert_eq!(sum, 0x17d7); + + // Kermit + + let sw_crc = crc::Crc::::new(&crc::CRC_16_KERMIT); + let mut digest = sw_crc.digest(); + digest.update(&buf_u8); + let sw_sum = digest.finalize(); + + let mut crc = Crc::new_kermit(p.CRC0.reborrow()); + crc.feed(&buf_u8); + let sum = crc.finalize(); + assert_eq!(sum, sw_sum); + + let mut crc = Crc::new_kermit(p.CRC0.reborrow()); + crc.feed(&buf_u16); + let sum = crc.finalize(); + assert_eq!(sum, 0x66eb); + + let mut crc = Crc::new_kermit(p.CRC0.reborrow()); + crc.feed(&buf_u32); + let sum = crc.finalize(); + assert_eq!(sum, 0x75ea); + + // ISO HDLC + + let sw_crc = crc::Crc::::new(&crc::CRC_32_ISO_HDLC); + let mut digest = sw_crc.digest(); + digest.update(&buf_u8); + let sw_sum = digest.finalize(); + + let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); + crc.feed(&buf_u8); + let sum = crc.finalize(); + assert_eq!(sum, sw_sum); + + let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); + crc.feed(&buf_u16); + let sum = crc.finalize(); + assert_eq!(sum, 0x8a61_4178); + + let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); + crc.feed(&buf_u32); + let sum = crc.finalize(); + assert_eq!(sum, 0xfab5_d04e); + + // POSIX + + let sw_crc = crc::Crc::::new(&POSIX); + let mut digest = sw_crc.digest(); + digest.update(&buf_u8); + let sw_sum = digest.finalize(); + + let mut crc = Crc::new_posix(p.CRC0.reborrow()); + crc.feed(&buf_u8); + let sum = crc.finalize(); + + assert_eq!(sum, sw_sum); + + let mut crc = Crc::new_posix(p.CRC0.reborrow()); + crc.feed(&buf_u16); + let sum = crc.finalize(); + assert_eq!(sum, 0x6d76_4f58); + + let mut crc = Crc::new_posix(p.CRC0.reborrow()); + crc.feed(&buf_u32); + let sum = crc.finalize(); + assert_eq!(sum, 0x2a5b_cb90); + + defmt::info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/mcxa/src/bin/gpio.rs b/tests/mcxa/src/bin/gpio.rs new file mode 100644 index 0000000000..8270ddfa7e --- /dev/null +++ b/tests/mcxa/src/bin/gpio.rs @@ -0,0 +1,77 @@ +#![no_std] +#![no_main] + +teleprobe_meta::target!(b"frdm-mcx-a266"); + +use embassy_executor::Spawner; +use embassy_mcxa::gpio::{Input, Output}; +use hal::config::Config; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let config = Config::default(); + let p = hal::init(config); + + defmt::info!("Gpio test"); + + let mut output = Output::new( + p.P1_8, + embassy_mcxa::gpio::Level::Low, + embassy_mcxa::gpio::DriveStrength::Normal, + embassy_mcxa::gpio::SlewRate::Slow, + ); + + spawner.spawn(wait(Input::new(p.P2_4, embassy_mcxa::gpio::Pull::Down)).unwrap()); + + embassy_time::Timer::after_millis(40).await; + output.set_high(); + embassy_time::Timer::after_millis(40).await; + output.set_low(); + embassy_time::Timer::after_millis(40).await; + output.set_high(); + embassy_time::Timer::after_millis(40).await; + output.set_low(); + embassy_time::Timer::after_millis(40).await; + output.set_high(); + embassy_time::Timer::after_millis(40).await; + + unreachable!("The wait task failed to see the output values"); +} + +#[embassy_executor::task] +async fn wait(mut input: Input<'static>) { + assert!(input.is_low()); + + input.wait_for_high().await; + + embassy_time::Timer::after_millis(10).await; + assert!(input.is_high()); + embassy_time::Timer::after_millis(10).await; + + input.wait_for_low().await; + + embassy_time::Timer::after_millis(10).await; + assert!(input.is_low()); + embassy_time::Timer::after_millis(10).await; + + input.wait_for_rising_edge().await; + + embassy_time::Timer::after_millis(10).await; + assert!(input.is_high()); + embassy_time::Timer::after_millis(10).await; + + input.wait_for_falling_edge().await; + + embassy_time::Timer::after_millis(10).await; + assert!(input.is_low()); + embassy_time::Timer::after_millis(10).await; + + input.wait_for_any_edge().await; + + embassy_time::Timer::after_millis(10).await; + assert!(input.is_high()); + + defmt::info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/mcxa/src/bin/i3c.rs b/tests/mcxa/src/bin/i3c.rs new file mode 100644 index 0000000000..d3264132b7 --- /dev/null +++ b/tests/mcxa/src/bin/i3c.rs @@ -0,0 +1,76 @@ +#![no_std] +#![no_main] + +// TODO: Come back after https://github.com/embassy-rs/embassy/issues/5359 is fixed to check if everything fully runs + +teleprobe_meta::target!(b"frdm-mcx-a266"); + +use embassy_executor::Spawner; +use embassy_mcxa::bind_interrupts; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::i3c::controller::{self, BusType, I3c}; +use embassy_time::Timer; +use hal::config::Config; +use hal::peripherals::I3C0; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +bind_interrupts!( + struct Irqs { + I3C0 => embassy_mcxa::i3c::InterruptHandler; + } +); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); + + let p = hal::init(config); + + defmt::info!("I3C test"); + + let config = controller::Config::default(); + let mut i3c = I3c::new_async(p.I3C0, p.P1_9, p.P1_8, Irqs, config).unwrap(); + let mut buf = [0u8; 2]; + + // ~~~~~~~~ // + // I2C mode // + // ~~~~~~~~ // + + // RSTDAA: reset first to make sure device responds to I2c requests. + i3c.async_write(0x7e, &[0x06], BusType::I3cSdr).await.unwrap(); + + i3c.async_write_read(0x48, &[0x00], &mut buf, BusType::I2c) + .await + .unwrap(); + let raw = f32::from(i16::from_be_bytes(buf) / 16); + let temp_i2c = raw * 0.0625; + defmt::info!("P3T1755 via I2C: {}C", temp_i2c); + Timer::after_millis(10).await; + + // ~~~~~~~~~~~~ // + // I3C SDR mode // + // ~~~~~~~~~~~~ // + + // RSTDAA + i3c.async_write(0x7e, &[0x06], BusType::I3cSdr).await.unwrap(); + + Timer::after_micros(100).await; + + // ENTDAA + i3c.async_write(0x7e, &[0x07], BusType::I3cSdr).await.unwrap(); + + // P3T1755 temperature register = 0x00 + i3c.async_write_read(0x48, &[0x00], &mut buf, BusType::I3cSdr) + .await + .unwrap(); + let raw = f32::from(i16::from_be_bytes(buf) / 16); + let temp_i3c_sdr = raw * 0.0625; + defmt::info!("P3T1755 via I3C SDR: {}C", temp_i3c_sdr); + + assert!((-40.0..120.0).contains(&temp_i2c)); + assert!((-40.0..120.0).contains(&temp_i3c_sdr)); + + defmt::info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/mcxa/src/bin/lpuart.rs b/tests/mcxa/src/bin/lpuart.rs new file mode 100644 index 0000000000..fb44938f76 --- /dev/null +++ b/tests/mcxa/src/bin/lpuart.rs @@ -0,0 +1,96 @@ +#![no_std] +#![no_main] + +// TODO: Also test ringbuffered uart +// NOTE: Blocking uart is hard to test, so maybe todo? + +teleprobe_meta::target!(b"frdm-mcx-a266"); + +use embassy_executor::Spawner; +use embassy_mcxa::bind_interrupts; +use embassy_mcxa::clocks::config::Div8; +use embassy_mcxa::lpuart::LpuartDma; +use embassy_mcxa::lpuart::buffered::BufferedLpuart; +use embedded_io_async::{Read, Write}; +use hal::config::Config; +use static_cell::ConstStaticCell; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +const MESSAGE_SIZE: usize = 69; +const MESSAGE: [u8; MESSAGE_SIZE] = *b"You've found the HIL tests for MCXA! Hope you have a wonderful day :)"; + +bind_interrupts!(struct Irqs { + LPUART3 => hal::lpuart::buffered::BufferedInterruptHandler::; +}); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let mut config = Config::default(); + config.clock_cfg.sirc.fro_12m_enabled = true; + config.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); + + let p = hal::init(config); + + defmt::info!("lpuart test"); + + let config = hal::lpuart::Config { + baudrate_bps: 115_200, + rx_fifo_watermark: 0, + tx_fifo_watermark: 0, + ..Default::default() + }; + + static TX_BUF: ConstStaticCell<[u8; 256]> = ConstStaticCell::new([0u8; 256]); + static RX_BUF: ConstStaticCell<[u8; 256]> = ConstStaticCell::new([0u8; 256]); + + let echo_uart = BufferedLpuart::new( + p.LPUART3, + p.P4_5, // TX pin + p.P4_2, // RX pin + Irqs, + TX_BUF.take(), + RX_BUF.take(), + config, + ) + .unwrap(); + + spawner.spawn(echo_plus_1(echo_uart).unwrap()); + + let mut dma_uart = LpuartDma::new( + p.LPUART2, // Peripheral + p.P2_2, // TX pin + p.P2_3, // RX pin + p.DMA_CH0, // TX DMA channel + p.DMA_CH1, // RX DMA channel + config, + ) + .unwrap(); + + let mut rx_buffer = [0; MESSAGE_SIZE]; + embassy_time::Timer::after_millis(1).await; + + defmt::info!("Sending message"); + dma_uart.write_dma(&MESSAGE).await.unwrap(); + defmt::info!("Done, waiting for response"); + dma_uart.read_dma(&mut rx_buffer).await.unwrap(); + assert_eq!(rx_buffer, MESSAGE); + + defmt::info!("Test OK"); + cortex_m::asm::bkpt(); +} + +#[embassy_executor::task] +async fn echo_plus_1(mut uart: BufferedLpuart<'static>) { + let mut buf = [0u8; MESSAGE_SIZE]; + + uart.read_exact(&mut buf).await.unwrap(); + defmt::info!("Received the message"); + + assert_eq!(buf, MESSAGE); + embassy_time::Timer::after_millis(1).await; + + defmt::info!("Sending back"); + uart.write_all(&buf).await.unwrap(); + + defmt::info!("Done"); +}