diff options
| author | Raul Alimbekov <[email protected]> | 2025-12-16 09:05:22 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-16 09:05:22 +0300 |
| commit | c9a04b4b732b7a3b696eb8223664c1a7942b1875 (patch) | |
| tree | 6dbe5c02e66eed8d8762f13f95afd24f8db2b38c /examples/mcxa/src | |
| parent | cde24a3ef1117653ba5ed4184102b33f745782fb (diff) | |
| parent | 5ae6e060ec1c90561719aabdc29d5b6e7b8b0a82 (diff) | |
Merge branch 'main' into main
Diffstat (limited to 'examples/mcxa/src')
26 files changed, 2531 insertions, 0 deletions
diff --git a/examples/mcxa/src/bin/adc_interrupt.rs b/examples/mcxa/src/bin/adc_interrupt.rs new file mode 100644 index 000000000..d2cda631c --- /dev/null +++ b/examples/mcxa/src/bin/adc_interrupt.rs | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_time::{Duration, Ticker}; | ||
| 6 | use hal::adc::{Adc, InterruptHandler, LpadcConfig, TriggerPriorityPolicy}; | ||
| 7 | use hal::bind_interrupts; | ||
| 8 | use hal::clocks::PoweredClock; | ||
| 9 | use hal::clocks::config::Div8; | ||
| 10 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; | ||
| 11 | use hal::config::Config; | ||
| 12 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; | ||
| 13 | use hal::pac::adc1::cmdl1::Mode; | ||
| 14 | use hal::pac::adc1::ctrl::CalAvgs; | ||
| 15 | use hal::peripherals::ADC1; | ||
| 16 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 17 | |||
| 18 | bind_interrupts!(struct Irqs { | ||
| 19 | ADC1 => InterruptHandler<ADC1>; | ||
| 20 | }); | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 24 | let mut config = Config::default(); | ||
| 25 | config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); | ||
| 26 | |||
| 27 | let p = hal::init(config); | ||
| 28 | |||
| 29 | defmt::info!("ADC interrupt Example"); | ||
| 30 | |||
| 31 | let adc_config = LpadcConfig { | ||
| 32 | enable_in_doze_mode: true, | ||
| 33 | conversion_average_mode: CalAvgs::Average128, | ||
| 34 | enable_analog_preliminary: true, | ||
| 35 | power_up_delay: 0x80, | ||
| 36 | reference_voltage_source: Refsel::Option3, | ||
| 37 | power_level_mode: Pwrsel::Lowest, | ||
| 38 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, | ||
| 39 | enable_conv_pause: false, | ||
| 40 | conv_pause_delay: 0, | ||
| 41 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 42 | source: AdcClockSel::FroLfDiv, | ||
| 43 | div: Div4::no_div(), | ||
| 44 | }; | ||
| 45 | let mut adc = Adc::new_async(p.ADC1, p.P1_10, Irqs, adc_config).unwrap(); | ||
| 46 | |||
| 47 | adc.do_offset_calibration(); | ||
| 48 | adc.do_auto_calibration(); | ||
| 49 | adc.set_resolution(Mode::Data16Bits); | ||
| 50 | |||
| 51 | defmt::info!("ADC configuration done..."); | ||
| 52 | let mut ticker = Ticker::every(Duration::from_millis(100)); | ||
| 53 | |||
| 54 | loop { | ||
| 55 | ticker.next().await; | ||
| 56 | match adc.read().await { | ||
| 57 | Ok(value) => { | ||
| 58 | defmt::info!("ADC value: {}", value); | ||
| 59 | } | ||
| 60 | Err(e) => { | ||
| 61 | defmt::error!("ADC read error: {:?}", e); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
diff --git a/examples/mcxa/src/bin/adc_polling.rs b/examples/mcxa/src/bin/adc_polling.rs new file mode 100644 index 000000000..5c4d5524c --- /dev/null +++ b/examples/mcxa/src/bin/adc_polling.rs | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::adc::{ConvCommandConfig, ConvTriggerConfig}; | ||
| 6 | use embassy_time::{Duration, Ticker}; | ||
| 7 | use hal::adc::{Adc, LpadcConfig, TriggerPriorityPolicy}; | ||
| 8 | use hal::clocks::PoweredClock; | ||
| 9 | use hal::clocks::config::Div8; | ||
| 10 | use hal::clocks::periph_helpers::{AdcClockSel, Div4}; | ||
| 11 | use hal::config::Config; | ||
| 12 | use hal::pac::adc1::cfg::{Pwrsel, Refsel}; | ||
| 13 | use hal::pac::adc1::cmdl1::Mode; | ||
| 14 | use hal::pac::adc1::ctrl::CalAvgs; | ||
| 15 | use hal::pac::adc1::tctrl::Tcmd; | ||
| 16 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 17 | |||
| 18 | const G_LPADC_RESULT_SHIFT: u32 = 0; | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | let mut config = Config::default(); | ||
| 23 | config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); | ||
| 24 | |||
| 25 | let p = hal::init(config); | ||
| 26 | |||
| 27 | defmt::info!("=== ADC polling Example ==="); | ||
| 28 | |||
| 29 | let adc_config = LpadcConfig { | ||
| 30 | enable_in_doze_mode: true, | ||
| 31 | conversion_average_mode: CalAvgs::Average128, | ||
| 32 | enable_analog_preliminary: true, | ||
| 33 | power_up_delay: 0x80, | ||
| 34 | reference_voltage_source: Refsel::Option3, | ||
| 35 | power_level_mode: Pwrsel::Lowest, | ||
| 36 | trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed, | ||
| 37 | enable_conv_pause: false, | ||
| 38 | conv_pause_delay: 0, | ||
| 39 | power: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 40 | source: AdcClockSel::FroLfDiv, | ||
| 41 | div: Div4::no_div(), | ||
| 42 | }; | ||
| 43 | let adc = Adc::new_blocking(p.ADC1, p.P1_10, adc_config).unwrap(); | ||
| 44 | |||
| 45 | adc.do_offset_calibration(); | ||
| 46 | adc.do_auto_calibration(); | ||
| 47 | |||
| 48 | let conv_command_config = ConvCommandConfig { | ||
| 49 | conversion_resolution_mode: Mode::Data16Bits, | ||
| 50 | ..ConvCommandConfig::default() | ||
| 51 | }; | ||
| 52 | adc.set_conv_command_config(1, &conv_command_config).unwrap(); | ||
| 53 | |||
| 54 | let conv_trigger_config = ConvTriggerConfig { | ||
| 55 | target_command_id: Tcmd::ExecuteCmd1, | ||
| 56 | enable_hardware_trigger: false, | ||
| 57 | ..Default::default() | ||
| 58 | }; | ||
| 59 | adc.set_conv_trigger_config(0, &conv_trigger_config).unwrap(); | ||
| 60 | |||
| 61 | defmt::info!("=== ADC configuration done... ==="); | ||
| 62 | let mut tick = Ticker::every(Duration::from_millis(100)); | ||
| 63 | |||
| 64 | loop { | ||
| 65 | tick.next().await; | ||
| 66 | adc.do_software_trigger(1).unwrap(); | ||
| 67 | let result = loop { | ||
| 68 | match adc.get_conv_result() { | ||
| 69 | Ok(res) => break res, | ||
| 70 | Err(_) => { | ||
| 71 | // Conversion not ready, continue polling | ||
| 72 | } | ||
| 73 | } | ||
| 74 | }; | ||
| 75 | let value = result.conv_value >> G_LPADC_RESULT_SHIFT; | ||
| 76 | defmt::info!("ADC value: {=u16}", value); | ||
| 77 | } | ||
| 78 | } | ||
diff --git a/examples/mcxa/src/bin/blinky.rs b/examples/mcxa/src/bin/blinky.rs new file mode 100644 index 000000000..dd08ec0d9 --- /dev/null +++ b/examples/mcxa/src/bin/blinky.rs | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_time::Timer; | ||
| 6 | use hal::gpio::{DriveStrength, Level, Output, SlewRate}; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: Spawner) { | ||
| 11 | let p = hal::init(hal::config::Config::default()); | ||
| 12 | |||
| 13 | defmt::info!("Blink example"); | ||
| 14 | |||
| 15 | let mut red = Output::new(p.P3_18, Level::High, DriveStrength::Normal, SlewRate::Fast); | ||
| 16 | let mut green = Output::new(p.P3_19, Level::High, DriveStrength::Normal, SlewRate::Fast); | ||
| 17 | let mut blue = Output::new(p.P3_21, Level::High, DriveStrength::Normal, SlewRate::Fast); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | defmt::info!("Toggle LEDs"); | ||
| 21 | |||
| 22 | red.toggle(); | ||
| 23 | Timer::after_millis(250).await; | ||
| 24 | |||
| 25 | red.toggle(); | ||
| 26 | green.toggle(); | ||
| 27 | Timer::after_millis(250).await; | ||
| 28 | |||
| 29 | green.toggle(); | ||
| 30 | blue.toggle(); | ||
| 31 | Timer::after_millis(250).await; | ||
| 32 | blue.toggle(); | ||
| 33 | |||
| 34 | Timer::after_millis(250).await; | ||
| 35 | } | ||
| 36 | } | ||
diff --git a/examples/mcxa/src/bin/button.rs b/examples/mcxa/src/bin/button.rs new file mode 100644 index 000000000..943edbb15 --- /dev/null +++ b/examples/mcxa/src/bin/button.rs | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_time::Timer; | ||
| 6 | use hal::gpio::{Input, Pull}; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: Spawner) { | ||
| 11 | let p = hal::init(hal::config::Config::default()); | ||
| 12 | |||
| 13 | defmt::info!("Button example"); | ||
| 14 | |||
| 15 | // This button is labeled "WAKEUP" on the FRDM-MCXA276 | ||
| 16 | // The board already has a 10K pullup | ||
| 17 | let monitor = Input::new(p.P1_7, Pull::Disabled); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | defmt::info!("Pin level is {:?}", monitor.get_level()); | ||
| 21 | Timer::after_millis(1000).await; | ||
| 22 | } | ||
| 23 | } | ||
diff --git a/examples/mcxa/src/bin/button_async.rs b/examples/mcxa/src/bin/button_async.rs new file mode 100644 index 000000000..6cc7b62cd --- /dev/null +++ b/examples/mcxa/src/bin/button_async.rs | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_time::Timer; | ||
| 6 | use hal::gpio::{Input, Pull}; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: Spawner) { | ||
| 11 | let p = hal::init(hal::config::Config::default()); | ||
| 12 | |||
| 13 | defmt::info!("GPIO interrupt example"); | ||
| 14 | |||
| 15 | // This button is labeled "WAKEUP" on the FRDM-MCXA276 | ||
| 16 | // The board already has a 10K pullup | ||
| 17 | let mut pin = Input::new(p.P1_7, Pull::Disabled); | ||
| 18 | |||
| 19 | let mut press_count = 0u32; | ||
| 20 | |||
| 21 | loop { | ||
| 22 | pin.wait_for_falling_edge().await; | ||
| 23 | |||
| 24 | press_count += 1; | ||
| 25 | |||
| 26 | defmt::info!("Button pressed! Count: {}", press_count); | ||
| 27 | Timer::after_millis(50).await; | ||
| 28 | } | ||
| 29 | } | ||
diff --git a/examples/mcxa/src/bin/clkout.rs b/examples/mcxa/src/bin/clkout.rs new file mode 100644 index 000000000..1e52912d3 --- /dev/null +++ b/examples/mcxa/src/bin/clkout.rs | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; | ||
| 6 | use embassy_mcxa::clocks::PoweredClock; | ||
| 7 | use embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate}; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 10 | |||
| 11 | /// Demonstrate CLKOUT, using Pin P4.2 | ||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let p = hal::init(hal::config::Config::default()); | ||
| 15 | let mut pin = p.P4_2; | ||
| 16 | let mut clkout = p.CLKOUT; | ||
| 17 | |||
| 18 | loop { | ||
| 19 | defmt::info!("Set Low..."); | ||
| 20 | let mut output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); | ||
| 21 | Timer::after_millis(500).await; | ||
| 22 | |||
| 23 | defmt::info!("Set High..."); | ||
| 24 | output.set_high(); | ||
| 25 | Timer::after_millis(400).await; | ||
| 26 | |||
| 27 | defmt::info!("Set Low..."); | ||
| 28 | output.set_low(); | ||
| 29 | Timer::after_millis(500).await; | ||
| 30 | |||
| 31 | defmt::info!("16k..."); | ||
| 32 | // Run Clock Out with the 16K clock | ||
| 33 | let _clock_out = ClockOut::new( | ||
| 34 | clkout.reborrow(), | ||
| 35 | pin.reborrow(), | ||
| 36 | Config { | ||
| 37 | sel: ClockOutSel::Clk16K, | ||
| 38 | div: Div4::no_div(), | ||
| 39 | level: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 40 | }, | ||
| 41 | ) | ||
| 42 | .unwrap(); | ||
| 43 | |||
| 44 | Timer::after_millis(3000).await; | ||
| 45 | |||
| 46 | defmt::info!("Set Low..."); | ||
| 47 | drop(_clock_out); | ||
| 48 | |||
| 49 | let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); | ||
| 50 | Timer::after_millis(500).await; | ||
| 51 | |||
| 52 | // Run Clock Out with the 12M clock, divided by 3 | ||
| 53 | defmt::info!("4M..."); | ||
| 54 | let _clock_out = ClockOut::new( | ||
| 55 | clkout.reborrow(), | ||
| 56 | pin.reborrow(), | ||
| 57 | Config { | ||
| 58 | sel: ClockOutSel::Fro12M, | ||
| 59 | div: const { Div4::from_divisor(3).unwrap() }, | ||
| 60 | level: PoweredClock::NormalEnabledDeepSleepDisabled, | ||
| 61 | }, | ||
| 62 | ) | ||
| 63 | .unwrap(); | ||
| 64 | |||
| 65 | // Let it run for 3 seconds... | ||
| 66 | Timer::after_millis(3000).await; | ||
| 67 | } | ||
| 68 | } | ||
diff --git a/examples/mcxa/src/bin/crc.rs b/examples/mcxa/src/bin/crc.rs new file mode 100644 index 000000000..0125e625c --- /dev/null +++ b/examples/mcxa/src/bin/crc.rs | |||
| @@ -0,0 +1,154 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use hal::config::Config; | ||
| 6 | use hal::crc::Crc; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | const CCITT_FALSE: crc::Algorithm<u16> = crc::Algorithm { | ||
| 10 | width: 16, | ||
| 11 | poly: 0x1021, | ||
| 12 | init: 0xffff, | ||
| 13 | refin: false, | ||
| 14 | refout: false, | ||
| 15 | xorout: 0, | ||
| 16 | check: 0x29b1, | ||
| 17 | residue: 0x0000, | ||
| 18 | }; | ||
| 19 | |||
| 20 | const POSIX: crc::Algorithm<u32> = crc::Algorithm { | ||
| 21 | width: 32, | ||
| 22 | poly: 0x04c1_1db7, | ||
| 23 | init: 0, | ||
| 24 | refin: false, | ||
| 25 | refout: false, | ||
| 26 | xorout: 0xffff_ffff, | ||
| 27 | check: 0x765e_7680, | ||
| 28 | residue: 0x0000, | ||
| 29 | }; | ||
| 30 | |||
| 31 | #[embassy_executor::main] | ||
| 32 | async fn main(_spawner: Spawner) { | ||
| 33 | let config = Config::default(); | ||
| 34 | let mut p = hal::init(config); | ||
| 35 | |||
| 36 | defmt::info!("CRC example"); | ||
| 37 | |||
| 38 | let buf_u8 = [0x00u8, 0x11, 0x22, 0x33]; | ||
| 39 | let buf_u16 = [0x0000u16, 0x1111, 0x2222, 0x3333]; | ||
| 40 | let buf_u32 = [0x0000_0000u32, 0x1111_1111, 0x2222_2222, 0x3333_3333]; | ||
| 41 | |||
| 42 | // CCITT False | ||
| 43 | |||
| 44 | let sw_crc = crc::Crc::<u16>::new(&CCITT_FALSE); | ||
| 45 | let mut digest = sw_crc.digest(); | ||
| 46 | digest.update(&buf_u8); | ||
| 47 | let sw_sum = digest.finalize(); | ||
| 48 | |||
| 49 | let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); | ||
| 50 | crc.feed(&buf_u8); | ||
| 51 | let sum = crc.finalize(); | ||
| 52 | assert_eq!(sum, sw_sum); | ||
| 53 | |||
| 54 | let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); | ||
| 55 | crc.feed(&buf_u16); | ||
| 56 | let sum = crc.finalize(); | ||
| 57 | assert_eq!(sum, 0xa467); | ||
| 58 | |||
| 59 | let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); | ||
| 60 | crc.feed(&buf_u32); | ||
| 61 | let sum = crc.finalize(); | ||
| 62 | assert_eq!(sum, 0xe5c7); | ||
| 63 | |||
| 64 | // Maxim | ||
| 65 | |||
| 66 | let sw_crc = crc::Crc::<u16>::new(&crc::CRC_16_MAXIM_DOW); | ||
| 67 | let mut digest = sw_crc.digest(); | ||
| 68 | digest.update(&buf_u8); | ||
| 69 | let sw_sum = digest.finalize(); | ||
| 70 | |||
| 71 | let mut crc = Crc::new_maxim(p.CRC0.reborrow()); | ||
| 72 | crc.feed(&buf_u8); | ||
| 73 | let sum = crc.finalize(); | ||
| 74 | assert_eq!(sum, sw_sum); | ||
| 75 | |||
| 76 | let mut crc = Crc::new_maxim(p.CRC0.reborrow()); | ||
| 77 | crc.feed(&buf_u16); | ||
| 78 | let sum = crc.finalize(); | ||
| 79 | assert_eq!(sum, 0x2afe); | ||
| 80 | |||
| 81 | let mut crc = Crc::new_maxim(p.CRC0.reborrow()); | ||
| 82 | crc.feed(&buf_u32); | ||
| 83 | let sum = crc.finalize(); | ||
| 84 | assert_eq!(sum, 0x17d7); | ||
| 85 | |||
| 86 | // Kermit | ||
| 87 | |||
| 88 | let sw_crc = crc::Crc::<u16>::new(&crc::CRC_16_KERMIT); | ||
| 89 | let mut digest = sw_crc.digest(); | ||
| 90 | digest.update(&buf_u8); | ||
| 91 | let sw_sum = digest.finalize(); | ||
| 92 | |||
| 93 | let mut crc = Crc::new_kermit(p.CRC0.reborrow()); | ||
| 94 | crc.feed(&buf_u8); | ||
| 95 | let sum = crc.finalize(); | ||
| 96 | assert_eq!(sum, sw_sum); | ||
| 97 | |||
| 98 | let mut crc = Crc::new_kermit(p.CRC0.reborrow()); | ||
| 99 | crc.feed(&buf_u16); | ||
| 100 | let sum = crc.finalize(); | ||
| 101 | assert_eq!(sum, 0x66eb); | ||
| 102 | |||
| 103 | let mut crc = Crc::new_kermit(p.CRC0.reborrow()); | ||
| 104 | crc.feed(&buf_u32); | ||
| 105 | let sum = crc.finalize(); | ||
| 106 | assert_eq!(sum, 0x75ea); | ||
| 107 | |||
| 108 | // ISO HDLC | ||
| 109 | |||
| 110 | let sw_crc = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC); | ||
| 111 | let mut digest = sw_crc.digest(); | ||
| 112 | digest.update(&buf_u8); | ||
| 113 | let sw_sum = digest.finalize(); | ||
| 114 | |||
| 115 | let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); | ||
| 116 | crc.feed(&buf_u8); | ||
| 117 | let sum = crc.finalize(); | ||
| 118 | assert_eq!(sum, sw_sum); | ||
| 119 | |||
| 120 | let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); | ||
| 121 | crc.feed(&buf_u16); | ||
| 122 | let sum = crc.finalize(); | ||
| 123 | assert_eq!(sum, 0x8a61_4178); | ||
| 124 | |||
| 125 | let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); | ||
| 126 | crc.feed(&buf_u32); | ||
| 127 | let sum = crc.finalize(); | ||
| 128 | assert_eq!(sum, 0xfab5_d04e); | ||
| 129 | |||
| 130 | // POSIX | ||
| 131 | |||
| 132 | let sw_crc = crc::Crc::<u32>::new(&POSIX); | ||
| 133 | let mut digest = sw_crc.digest(); | ||
| 134 | digest.update(&buf_u8); | ||
| 135 | let sw_sum = digest.finalize(); | ||
| 136 | |||
| 137 | let mut crc = Crc::new_posix(p.CRC0.reborrow()); | ||
| 138 | crc.feed(&buf_u8); | ||
| 139 | let sum = crc.finalize(); | ||
| 140 | |||
| 141 | assert_eq!(sum, sw_sum); | ||
| 142 | |||
| 143 | let mut crc = Crc::new_posix(p.CRC0.reborrow()); | ||
| 144 | crc.feed(&buf_u16); | ||
| 145 | let sum = crc.finalize(); | ||
| 146 | assert_eq!(sum, 0x6d76_4f58); | ||
| 147 | |||
| 148 | let mut crc = Crc::new_posix(p.CRC0.reborrow()); | ||
| 149 | crc.feed(&buf_u32); | ||
| 150 | let sum = crc.finalize(); | ||
| 151 | assert_eq!(sum, 0x2a5b_cb90); | ||
| 152 | |||
| 153 | defmt::info!("CRC successful"); | ||
| 154 | } | ||
diff --git a/examples/mcxa/src/bin/dma_mem_to_mem.rs b/examples/mcxa/src/bin/dma_mem_to_mem.rs new file mode 100644 index 000000000..b38baccb5 --- /dev/null +++ b/examples/mcxa/src/bin/dma_mem_to_mem.rs | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | //! DMA memory-to-memory transfer example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! This example demonstrates using DMA to copy data between memory buffers | ||
| 4 | //! using the Embassy-style async API with type-safe transfers. | ||
| 5 | //! | ||
| 6 | //! # Embassy-style features demonstrated: | ||
| 7 | //! - `TransferOptions` for configuration | ||
| 8 | //! - Type-safe `mem_to_mem<u32>()` method with async `.await` | ||
| 9 | //! - `Transfer` Future that can be `.await`ed | ||
| 10 | //! - `Word` trait for automatic transfer width detection | ||
| 11 | //! - `memset()` method for filling memory with a pattern | ||
| 12 | |||
| 13 | #![no_std] | ||
| 14 | #![no_main] | ||
| 15 | |||
| 16 | use embassy_executor::Spawner; | ||
| 17 | use embassy_mcxa::clocks::config::Div8; | ||
| 18 | use embassy_mcxa::dma::{DmaChannel, TransferOptions}; | ||
| 19 | use static_cell::ConstStaticCell; | ||
| 20 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 21 | |||
| 22 | const BUFFER_LENGTH: usize = 4; | ||
| 23 | |||
| 24 | // Buffers in RAM (static mut is automatically placed in .bss/.data) | ||
| 25 | static SRC_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([1, 2, 3, 4]); | ||
| 26 | static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); | ||
| 27 | static MEMSET_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); | ||
| 28 | |||
| 29 | #[embassy_executor::main] | ||
| 30 | async fn main(_spawner: Spawner) { | ||
| 31 | // Small delay to allow probe-rs to attach after reset | ||
| 32 | for _ in 0..100_000 { | ||
| 33 | cortex_m::asm::nop(); | ||
| 34 | } | ||
| 35 | |||
| 36 | let mut cfg = hal::config::Config::default(); | ||
| 37 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 38 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 39 | let p = hal::init(cfg); | ||
| 40 | |||
| 41 | defmt::info!("DMA memory-to-memory example starting..."); | ||
| 42 | |||
| 43 | defmt::info!("EDMA memory to memory example begin."); | ||
| 44 | |||
| 45 | let src = SRC_BUFFER.take(); | ||
| 46 | let dst = DEST_BUFFER.take(); | ||
| 47 | let mst = MEMSET_BUFFER.take(); | ||
| 48 | |||
| 49 | defmt::info!("Source Buffer: {=[?]}", src.as_slice()); | ||
| 50 | defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); | ||
| 51 | defmt::info!("Configuring DMA with Embassy-style API..."); | ||
| 52 | |||
| 53 | // Create DMA channel | ||
| 54 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | ||
| 55 | |||
| 56 | // Configure transfer options (Embassy-style) | ||
| 57 | // TransferOptions defaults to: complete_transfer_interrupt = true | ||
| 58 | let options = TransferOptions::default(); | ||
| 59 | |||
| 60 | // ========================================================================= | ||
| 61 | // Part 1: Embassy-style async API demonstration (mem_to_mem) | ||
| 62 | // ========================================================================= | ||
| 63 | // | ||
| 64 | // Use the new type-safe `mem_to_mem<u32>()` method: | ||
| 65 | // - Automatically determines transfer width from buffer element type (u32) | ||
| 66 | // - Returns a `Transfer` future that can be `.await`ed | ||
| 67 | // - Uses TransferOptions for consistent configuration | ||
| 68 | // | ||
| 69 | // Using async `.await` - the executor can run other tasks while waiting! | ||
| 70 | |||
| 71 | // Perform type-safe memory-to-memory transfer using Embassy-style async API | ||
| 72 | // Using async `.await` - the executor can run other tasks while waiting! | ||
| 73 | let transfer = dma_ch0.mem_to_mem(src, dst, options).unwrap(); | ||
| 74 | transfer.await.unwrap(); | ||
| 75 | |||
| 76 | defmt::info!("DMA mem-to-mem transfer complete!"); | ||
| 77 | defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); | ||
| 78 | |||
| 79 | // Verify data | ||
| 80 | if src != dst { | ||
| 81 | defmt::error!("FAIL: mem_to_mem mismatch!"); | ||
| 82 | } else { | ||
| 83 | defmt::info!("PASS: mem_to_mem verified."); | ||
| 84 | } | ||
| 85 | |||
| 86 | // ========================================================================= | ||
| 87 | // Part 2: memset() demonstration | ||
| 88 | // ========================================================================= | ||
| 89 | // | ||
| 90 | // The `memset()` method fills a buffer with a pattern value: | ||
| 91 | // - Fixed source address (pattern is read repeatedly) | ||
| 92 | // - Incrementing destination address | ||
| 93 | // - Uses the same Transfer future pattern | ||
| 94 | |||
| 95 | defmt::info!("--- Demonstrating memset() feature ---"); | ||
| 96 | |||
| 97 | defmt::info!("Memset Buffer (before): {=[?]}", mst.as_slice()); | ||
| 98 | |||
| 99 | // Fill buffer with a pattern value using DMA memset | ||
| 100 | let pattern: u32 = 0xDEADBEEF; | ||
| 101 | defmt::info!("Filling with pattern 0xDEADBEEF..."); | ||
| 102 | |||
| 103 | // Using blocking_wait() for demonstration - also shows non-async usage | ||
| 104 | let transfer = dma_ch0.memset(&pattern, mst, options); | ||
| 105 | transfer.blocking_wait(); | ||
| 106 | |||
| 107 | defmt::info!("DMA memset complete!"); | ||
| 108 | defmt::info!("Memset Buffer (after): {=[?]}", mst.as_slice()); | ||
| 109 | |||
| 110 | // Verify memset result | ||
| 111 | if !mst.iter().all(|&v| v == pattern) { | ||
| 112 | defmt::error!("FAIL: memset mismatch!"); | ||
| 113 | } else { | ||
| 114 | defmt::info!("PASS: memset verified."); | ||
| 115 | } | ||
| 116 | |||
| 117 | defmt::info!("=== All DMA tests complete ==="); | ||
| 118 | } | ||
diff --git a/examples/mcxa/src/bin/dma_scatter_gather_builder.rs b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs new file mode 100644 index 000000000..30ce20c96 --- /dev/null +++ b/examples/mcxa/src/bin/dma_scatter_gather_builder.rs | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | //! DMA Scatter-Gather Builder example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! This example demonstrates using the new `ScatterGatherBuilder` API for | ||
| 4 | //! chaining multiple DMA transfers with a type-safe builder pattern. | ||
| 5 | //! | ||
| 6 | //! # Features demonstrated: | ||
| 7 | //! - `ScatterGatherBuilder::new()` for creating a builder | ||
| 8 | //! - `add_transfer()` for adding memory-to-memory segments | ||
| 9 | //! - `build()` to start the chained transfer | ||
| 10 | //! - Automatic TCD linking and ESG bit management | ||
| 11 | //! | ||
| 12 | //! # Comparison with manual scatter-gather: | ||
| 13 | //! The manual approach (see `dma_scatter_gather.rs`) requires: | ||
| 14 | //! - Manual TCD pool allocation and alignment | ||
| 15 | //! - Manual CSR/ESG/INTMAJOR bit manipulation | ||
| 16 | //! - Manual dlast_sga address calculations | ||
| 17 | //! | ||
| 18 | //! The builder approach handles all of this automatically! | ||
| 19 | |||
| 20 | #![no_std] | ||
| 21 | #![no_main] | ||
| 22 | |||
| 23 | use embassy_executor::Spawner; | ||
| 24 | use embassy_mcxa::clocks::config::Div8; | ||
| 25 | use embassy_mcxa::dma::{DmaChannel, ScatterGatherBuilder}; | ||
| 26 | use static_cell::ConstStaticCell; | ||
| 27 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 28 | |||
| 29 | // Source buffers (multiple segments) | ||
| 30 | static SRC1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0x11111111, 0x22222222, 0x33333333, 0x44444444]); | ||
| 31 | static SRC2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD]); | ||
| 32 | static SRC3: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210]); | ||
| 33 | |||
| 34 | // Destination buffers (one per segment) | ||
| 35 | static DST1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 36 | static DST2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 37 | static DST3: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 38 | |||
| 39 | #[embassy_executor::main] | ||
| 40 | async fn main(_spawner: Spawner) { | ||
| 41 | // Small delay to allow probe-rs to attach after reset | ||
| 42 | for _ in 0..100_000 { | ||
| 43 | cortex_m::asm::nop(); | ||
| 44 | } | ||
| 45 | |||
| 46 | let mut cfg = hal::config::Config::default(); | ||
| 47 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 48 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 49 | let p = hal::init(cfg); | ||
| 50 | |||
| 51 | defmt::info!("DMA Scatter-Gather Builder example starting..."); | ||
| 52 | |||
| 53 | defmt::info!("DMA Scatter-Gather Builder Example"); | ||
| 54 | defmt::info!("==================================="); | ||
| 55 | let src1 = SRC1.take(); | ||
| 56 | let src2 = SRC2.take(); | ||
| 57 | let src3 = SRC3.take(); | ||
| 58 | let dst1 = DST1.take(); | ||
| 59 | let dst2 = DST2.take(); | ||
| 60 | let dst3 = DST3.take(); | ||
| 61 | |||
| 62 | // Show source buffers | ||
| 63 | defmt::info!("Source buffers:"); | ||
| 64 | defmt::info!(" SRC1: {=[?]}", src1.as_slice()); | ||
| 65 | defmt::info!(" SRC2: {=[?]}", src2.as_slice()); | ||
| 66 | defmt::info!(" SRC3: {=[?]}", src3.as_slice()); | ||
| 67 | |||
| 68 | defmt::info!("Destination buffers (before):"); | ||
| 69 | defmt::info!(" DST1: {=[?]}", dst1.as_slice()); | ||
| 70 | defmt::info!(" DST2: {=[?]}", dst2.as_slice()); | ||
| 71 | defmt::info!(" DST3: {=[?]}", dst3.as_slice()); | ||
| 72 | |||
| 73 | // Create DMA channel | ||
| 74 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | ||
| 75 | |||
| 76 | defmt::info!("Building scatter-gather chain with builder API..."); | ||
| 77 | |||
| 78 | // ========================================================================= | ||
| 79 | // ScatterGatherBuilder API demonstration | ||
| 80 | // ========================================================================= | ||
| 81 | // | ||
| 82 | // The builder pattern makes scatter-gather transfers much easier: | ||
| 83 | // 1. Create a builder | ||
| 84 | // 2. Add transfer segments with add_transfer() | ||
| 85 | // 3. Call build() to start the entire chain | ||
| 86 | // No manual TCD manipulation required! | ||
| 87 | |||
| 88 | let mut builder = ScatterGatherBuilder::<u32>::new(); | ||
| 89 | |||
| 90 | // Add three transfer segments - the builder handles TCD linking automatically | ||
| 91 | builder.add_transfer(src1, dst1); | ||
| 92 | builder.add_transfer(src2, dst2); | ||
| 93 | builder.add_transfer(src3, dst3); | ||
| 94 | |||
| 95 | defmt::info!("Added 3 transfer segments to chain."); | ||
| 96 | defmt::info!("Starting scatter-gather transfer with .await..."); | ||
| 97 | |||
| 98 | // Build and execute the scatter-gather chain | ||
| 99 | // The build() method: | ||
| 100 | // - Links all TCDs together with ESG bit | ||
| 101 | // - Sets INTMAJOR on all TCDs | ||
| 102 | // - Loads the first TCD into hardware | ||
| 103 | // - Returns a Transfer future | ||
| 104 | let transfer = builder.build(&dma_ch0).expect("Failed to build scatter-gather"); | ||
| 105 | transfer.blocking_wait(); | ||
| 106 | |||
| 107 | defmt::info!("Scatter-gather transfer complete!"); | ||
| 108 | |||
| 109 | // Show results | ||
| 110 | defmt::info!("Destination buffers (after):"); | ||
| 111 | defmt::info!(" DST1: {=[?]}", dst1.as_slice()); | ||
| 112 | defmt::info!(" DST2: {=[?]}", dst2.as_slice()); | ||
| 113 | defmt::info!(" DST3: {=[?]}", dst3.as_slice()); | ||
| 114 | |||
| 115 | let comps = [(src1, dst1), (src2, dst2), (src3, dst3)]; | ||
| 116 | |||
| 117 | // Verify all three segments | ||
| 118 | let mut all_ok = true; | ||
| 119 | for (src, dst) in comps { | ||
| 120 | all_ok &= src == dst; | ||
| 121 | } | ||
| 122 | |||
| 123 | if all_ok { | ||
| 124 | defmt::info!("PASS: All segments verified!"); | ||
| 125 | } else { | ||
| 126 | defmt::error!("FAIL: Mismatch detected!"); | ||
| 127 | } | ||
| 128 | |||
| 129 | defmt::info!("=== Scatter-Gather Builder example complete ==="); | ||
| 130 | } | ||
diff --git a/examples/mcxa/src/bin/dma_wrap_transfer.rs b/examples/mcxa/src/bin/dma_wrap_transfer.rs new file mode 100644 index 000000000..acfd29f08 --- /dev/null +++ b/examples/mcxa/src/bin/dma_wrap_transfer.rs | |||
| @@ -0,0 +1,184 @@ | |||
| 1 | //! DMA wrap transfer example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! This example demonstrates using DMA with modulo addressing to wrap around | ||
| 4 | //! a source buffer, effectively repeating the source data in the destination. | ||
| 5 | //! | ||
| 6 | //! # Embassy-style features demonstrated: | ||
| 7 | //! - `DmaChannel::is_done()` and `clear_done()` helper methods | ||
| 8 | //! - No need to pass register block around | ||
| 9 | |||
| 10 | #![no_std] | ||
| 11 | #![no_main] | ||
| 12 | |||
| 13 | use core::fmt::Write as _; | ||
| 14 | |||
| 15 | use embassy_executor::Spawner; | ||
| 16 | use embassy_mcxa::clocks::config::Div8; | ||
| 17 | use embassy_mcxa::dma::DmaChannel; | ||
| 18 | use embassy_mcxa::lpuart::{Blocking, Config, Lpuart, LpuartTx}; | ||
| 19 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 20 | |||
| 21 | // Source buffer: 4 words (16 bytes), aligned to 16 bytes for modulo | ||
| 22 | #[repr(align(16))] | ||
| 23 | struct AlignedSrc([u32; 4]); | ||
| 24 | |||
| 25 | static mut SRC: AlignedSrc = AlignedSrc([0; 4]); | ||
| 26 | static mut DST: [u32; 8] = [0; 8]; | ||
| 27 | |||
| 28 | /// Helper to print a buffer to UART | ||
| 29 | fn print_buffer(tx: &mut LpuartTx<'_, Blocking>, buf_ptr: *const u32, len: usize) { | ||
| 30 | write!(tx, "{:?}", unsafe { core::slice::from_raw_parts(buf_ptr, len) }).ok(); | ||
| 31 | } | ||
| 32 | |||
| 33 | #[embassy_executor::main] | ||
| 34 | async fn main(_spawner: Spawner) { | ||
| 35 | // Small delay to allow probe-rs to attach after reset | ||
| 36 | for _ in 0..100_000 { | ||
| 37 | cortex_m::asm::nop(); | ||
| 38 | } | ||
| 39 | |||
| 40 | let mut cfg = hal::config::Config::default(); | ||
| 41 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 42 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 43 | let p = hal::init(cfg); | ||
| 44 | |||
| 45 | defmt::info!("DMA wrap transfer example starting..."); | ||
| 46 | |||
| 47 | let config = Config { | ||
| 48 | baudrate_bps: 115_200, | ||
| 49 | ..Default::default() | ||
| 50 | }; | ||
| 51 | |||
| 52 | let lpuart = Lpuart::new_blocking(p.LPUART2, p.P2_2, p.P2_3, config).unwrap(); | ||
| 53 | let (mut tx, _rx) = lpuart.split(); | ||
| 54 | |||
| 55 | tx.blocking_write(b"EDMA wrap transfer example begin.\r\n\r\n").unwrap(); | ||
| 56 | |||
| 57 | // Initialize buffers | ||
| 58 | unsafe { | ||
| 59 | SRC.0 = [1, 2, 3, 4]; | ||
| 60 | DST = [0; 8]; | ||
| 61 | } | ||
| 62 | |||
| 63 | tx.blocking_write(b"Source Buffer: ").unwrap(); | ||
| 64 | print_buffer(&mut tx, unsafe { core::ptr::addr_of!(SRC.0) } as *const u32, 4); | ||
| 65 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 66 | |||
| 67 | tx.blocking_write(b"Destination Buffer (before): ").unwrap(); | ||
| 68 | print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); | ||
| 69 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 70 | |||
| 71 | tx.blocking_write(b"Configuring DMA with Embassy-style API...\r\n") | ||
| 72 | .unwrap(); | ||
| 73 | |||
| 74 | // Create DMA channel using Embassy-style API | ||
| 75 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | ||
| 76 | |||
| 77 | // Configure wrap transfer using direct TCD access: | ||
| 78 | // SRC is 16 bytes (4 * u32). We want to transfer 32 bytes (8 * u32). | ||
| 79 | // SRC modulo is 16 bytes (2^4 = 16) - wraps source address. | ||
| 80 | // DST modulo is 0 (disabled). | ||
| 81 | // This causes the source address to wrap around after 16 bytes, | ||
| 82 | // effectively repeating the source data. | ||
| 83 | unsafe { | ||
| 84 | let t = dma_ch0.tcd(); | ||
| 85 | |||
| 86 | // Reset channel state | ||
| 87 | t.ch_csr().write(|w| { | ||
| 88 | w.erq() | ||
| 89 | .disable() | ||
| 90 | .earq() | ||
| 91 | .disable() | ||
| 92 | .eei() | ||
| 93 | .no_error() | ||
| 94 | .ebw() | ||
| 95 | .disable() | ||
| 96 | .done() | ||
| 97 | .clear_bit_by_one() | ||
| 98 | }); | ||
| 99 | t.ch_es().write(|w| w.bits(0)); | ||
| 100 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 101 | |||
| 102 | // Source/destination addresses | ||
| 103 | t.tcd_saddr() | ||
| 104 | .write(|w| w.saddr().bits(core::ptr::addr_of!(SRC.0) as u32)); | ||
| 105 | t.tcd_daddr() | ||
| 106 | .write(|w| w.daddr().bits(core::ptr::addr_of_mut!(DST) as u32)); | ||
| 107 | |||
| 108 | // Offsets: both increment by 4 bytes | ||
| 109 | t.tcd_soff().write(|w| w.soff().bits(4)); | ||
| 110 | t.tcd_doff().write(|w| w.doff().bits(4)); | ||
| 111 | |||
| 112 | // Attributes: 32-bit transfers (size = 2) | ||
| 113 | // SMOD = 4 (2^4 = 16 byte modulo for source), DMOD = 0 (disabled) | ||
| 114 | t.tcd_attr().write(|w| { | ||
| 115 | w.ssize() | ||
| 116 | .bits(2) | ||
| 117 | .dsize() | ||
| 118 | .bits(2) | ||
| 119 | .smod() | ||
| 120 | .bits(4) // Source modulo: 2^4 = 16 bytes | ||
| 121 | .dmod() | ||
| 122 | .bits(0) // Dest modulo: disabled | ||
| 123 | }); | ||
| 124 | |||
| 125 | // Transfer 32 bytes total in one minor loop | ||
| 126 | let nbytes = 32u32; | ||
| 127 | t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); | ||
| 128 | |||
| 129 | // Source wraps via modulo, no adjustment needed | ||
| 130 | t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); | ||
| 131 | // Reset dest address after major loop | ||
| 132 | t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32)); | ||
| 133 | |||
| 134 | // Major loop count = 1 | ||
| 135 | t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); | ||
| 136 | t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); | ||
| 137 | |||
| 138 | // Enable interrupt on major loop completion | ||
| 139 | t.tcd_csr().write(|w| w.intmajor().set_bit()); | ||
| 140 | |||
| 141 | cortex_m::asm::dsb(); | ||
| 142 | |||
| 143 | tx.blocking_write(b"Triggering transfer...\r\n").unwrap(); | ||
| 144 | dma_ch0.trigger_start(); | ||
| 145 | } | ||
| 146 | |||
| 147 | // Wait for completion using channel helper method | ||
| 148 | while !dma_ch0.is_done() { | ||
| 149 | cortex_m::asm::nop(); | ||
| 150 | } | ||
| 151 | unsafe { | ||
| 152 | dma_ch0.clear_done(); | ||
| 153 | } | ||
| 154 | |||
| 155 | tx.blocking_write(b"\r\nEDMA wrap transfer example finish.\r\n\r\n") | ||
| 156 | .unwrap(); | ||
| 157 | tx.blocking_write(b"Destination Buffer (after): ").unwrap(); | ||
| 158 | print_buffer(&mut tx, core::ptr::addr_of!(DST) as *const u32, 8); | ||
| 159 | tx.blocking_write(b"\r\n\r\n").unwrap(); | ||
| 160 | |||
| 161 | // Verify: DST should be [1, 2, 3, 4, 1, 2, 3, 4] | ||
| 162 | let expected = [1u32, 2, 3, 4, 1, 2, 3, 4]; | ||
| 163 | let mut mismatch = false; | ||
| 164 | unsafe { | ||
| 165 | for i in 0..8 { | ||
| 166 | if DST[i] != expected[i] { | ||
| 167 | mismatch = true; | ||
| 168 | break; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | if mismatch { | ||
| 174 | tx.blocking_write(b"FAIL: Mismatch detected!\r\n").unwrap(); | ||
| 175 | defmt::error!("FAIL: Mismatch detected!"); | ||
| 176 | } else { | ||
| 177 | tx.blocking_write(b"PASS: Data verified.\r\n").unwrap(); | ||
| 178 | defmt::info!("PASS: Data verified."); | ||
| 179 | } | ||
| 180 | |||
| 181 | loop { | ||
| 182 | cortex_m::asm::wfe(); | ||
| 183 | } | ||
| 184 | } | ||
diff --git a/examples/mcxa/src/bin/hello.rs b/examples/mcxa/src/bin/hello.rs new file mode 100644 index 000000000..e371d9413 --- /dev/null +++ b/examples/mcxa/src/bin/hello.rs | |||
| @@ -0,0 +1,119 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::clocks::config::Div8; | ||
| 6 | use hal::lpuart::{Blocking, Config, Lpuart}; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | /// Simple helper to write a byte as hex to UART | ||
| 10 | fn write_hex_byte(uart: &mut Lpuart<'_, Blocking>, byte: u8) { | ||
| 11 | const HEX_DIGITS: &[u8] = b"0123456789ABCDEF"; | ||
| 12 | let _ = uart.write_byte(HEX_DIGITS[(byte >> 4) as usize]); | ||
| 13 | let _ = uart.write_byte(HEX_DIGITS[(byte & 0xF) as usize]); | ||
| 14 | } | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | let mut cfg = hal::config::Config::default(); | ||
| 19 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 20 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 21 | let p = hal::init(cfg); | ||
| 22 | |||
| 23 | defmt::info!("boot"); | ||
| 24 | |||
| 25 | // Create UART configuration | ||
| 26 | let config = Config { | ||
| 27 | baudrate_bps: 115_200, | ||
| 28 | ..Default::default() | ||
| 29 | }; | ||
| 30 | |||
| 31 | // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX | ||
| 32 | let mut uart = Lpuart::new_blocking( | ||
| 33 | p.LPUART2, // Peripheral | ||
| 34 | p.P2_2, // TX pin | ||
| 35 | p.P2_3, // RX pin | ||
| 36 | config, | ||
| 37 | ) | ||
| 38 | .unwrap(); | ||
| 39 | |||
| 40 | // Print welcome message before any async delays to guarantee early console output | ||
| 41 | uart.write_str_blocking("\r\n=== MCXA276 UART Echo Demo ===\r\n"); | ||
| 42 | uart.write_str_blocking("Available commands:\r\n"); | ||
| 43 | uart.write_str_blocking(" help - Show this help\r\n"); | ||
| 44 | uart.write_str_blocking(" echo <text> - Echo back the text\r\n"); | ||
| 45 | uart.write_str_blocking(" hex <byte> - Display byte in hex (0-255)\r\n"); | ||
| 46 | uart.write_str_blocking("Type a command: "); | ||
| 47 | |||
| 48 | let mut buffer = [0u8; 64]; | ||
| 49 | let mut buf_idx = 0; | ||
| 50 | |||
| 51 | loop { | ||
| 52 | // Read a byte from UART | ||
| 53 | let byte = uart.read_byte_blocking(); | ||
| 54 | |||
| 55 | // Echo the character back | ||
| 56 | if byte == b'\r' || byte == b'\n' { | ||
| 57 | // Enter pressed - process command | ||
| 58 | uart.write_str_blocking("\r\n"); | ||
| 59 | |||
| 60 | if buf_idx > 0 { | ||
| 61 | let command = &buffer[0..buf_idx]; | ||
| 62 | |||
| 63 | if command == b"help" { | ||
| 64 | uart.write_str_blocking("Available commands:\r\n"); | ||
| 65 | uart.write_str_blocking(" help - Show this help\r\n"); | ||
| 66 | uart.write_str_blocking(" echo <text> - Echo back the text\r\n"); | ||
| 67 | uart.write_str_blocking(" hex <byte> - Display byte in hex (0-255)\r\n"); | ||
| 68 | } else if command.starts_with(b"echo ") && command.len() > 5 { | ||
| 69 | uart.write_str_blocking("Echo: "); | ||
| 70 | uart.write_str_blocking(core::str::from_utf8(&command[5..]).unwrap_or("")); | ||
| 71 | uart.write_str_blocking("\r\n"); | ||
| 72 | } else if command.starts_with(b"hex ") && command.len() > 4 { | ||
| 73 | // Parse the byte value | ||
| 74 | let num_str = &command[4..]; | ||
| 75 | if let Ok(num) = parse_u8(num_str) { | ||
| 76 | uart.write_str_blocking("Hex: 0x"); | ||
| 77 | write_hex_byte(&mut uart, num); | ||
| 78 | uart.write_str_blocking("\r\n"); | ||
| 79 | } else { | ||
| 80 | uart.write_str_blocking("Invalid number for hex command\r\n"); | ||
| 81 | } | ||
| 82 | } else if !command.is_empty() { | ||
| 83 | uart.write_str_blocking("Unknown command: "); | ||
| 84 | uart.write_str_blocking(core::str::from_utf8(command).unwrap_or("")); | ||
| 85 | uart.write_str_blocking("\r\n"); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | // Reset buffer and prompt | ||
| 90 | buf_idx = 0; | ||
| 91 | uart.write_str_blocking("Type a command: "); | ||
| 92 | } else if byte == 8 || byte == 127 { | ||
| 93 | // Backspace | ||
| 94 | if buf_idx > 0 { | ||
| 95 | buf_idx -= 1; | ||
| 96 | uart.write_str_blocking("\x08 \x08"); // Erase character | ||
| 97 | } | ||
| 98 | } else if buf_idx < buffer.len() - 1 { | ||
| 99 | // Regular character | ||
| 100 | buffer[buf_idx] = byte; | ||
| 101 | buf_idx += 1; | ||
| 102 | let _ = uart.write_byte(byte); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | /// Simple parser for u8 from ASCII bytes | ||
| 108 | fn parse_u8(bytes: &[u8]) -> Result<u8, ()> { | ||
| 109 | let mut result = 0u8; | ||
| 110 | for &b in bytes { | ||
| 111 | if b.is_ascii_digit() { | ||
| 112 | result = result.checked_mul(10).ok_or(())?; | ||
| 113 | result = result.checked_add(b - b'0').ok_or(())?; | ||
| 114 | } else { | ||
| 115 | return Err(()); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | Ok(result) | ||
| 119 | } | ||
diff --git a/examples/mcxa/src/bin/i2c-async.rs b/examples/mcxa/src/bin/i2c-async.rs new file mode 100644 index 000000000..edcfd5f22 --- /dev/null +++ b/examples/mcxa/src/bin/i2c-async.rs | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_time::Timer; | ||
| 6 | use hal::bind_interrupts; | ||
| 7 | use hal::clocks::config::Div8; | ||
| 8 | use hal::config::Config; | ||
| 9 | use hal::i2c::InterruptHandler; | ||
| 10 | use hal::i2c::controller::{self, I2c, Speed}; | ||
| 11 | use hal::peripherals::LPI2C3; | ||
| 12 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!( | ||
| 15 | struct Irqs { | ||
| 16 | LPI2C3 => InterruptHandler<LPI2C3>; | ||
| 17 | } | ||
| 18 | ); | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | let mut config = Config::default(); | ||
| 23 | config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); | ||
| 24 | |||
| 25 | let p = hal::init(config); | ||
| 26 | |||
| 27 | defmt::info!("I2C example"); | ||
| 28 | |||
| 29 | let mut config = controller::Config::default(); | ||
| 30 | config.speed = Speed::Standard; | ||
| 31 | let mut i2c = I2c::new_async(p.LPI2C3, p.P3_27, p.P3_28, Irqs, config).unwrap(); | ||
| 32 | let mut buf = [0u8; 2]; | ||
| 33 | |||
| 34 | loop { | ||
| 35 | i2c.async_write_read(0x48, &[0x00], &mut buf).await.unwrap(); | ||
| 36 | defmt::info!("Buffer: {:02x}", buf); | ||
| 37 | Timer::after_secs(1).await; | ||
| 38 | } | ||
| 39 | } | ||
diff --git a/examples/mcxa/src/bin/i2c-blocking.rs b/examples/mcxa/src/bin/i2c-blocking.rs new file mode 100644 index 000000000..0f6c8cbae --- /dev/null +++ b/examples/mcxa/src/bin/i2c-blocking.rs | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_time::Timer; | ||
| 6 | use hal::clocks::config::Div8; | ||
| 7 | use hal::config::Config; | ||
| 8 | use hal::i2c::controller::{self, I2c, Speed}; | ||
| 9 | use tmp108::Tmp108; | ||
| 10 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let mut config = Config::default(); | ||
| 15 | config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); | ||
| 16 | |||
| 17 | let p = hal::init(config); | ||
| 18 | |||
| 19 | defmt::info!("I2C example"); | ||
| 20 | |||
| 21 | let mut config = controller::Config::default(); | ||
| 22 | config.speed = Speed::Standard; | ||
| 23 | let i2c = I2c::new_blocking(p.LPI2C3, p.P3_27, p.P3_28, config).unwrap(); | ||
| 24 | let mut tmp = Tmp108::new_with_a0_gnd(i2c); | ||
| 25 | |||
| 26 | loop { | ||
| 27 | let temperature = tmp.temperature().unwrap(); | ||
| 28 | defmt::info!("Temperature: {}C", temperature); | ||
| 29 | Timer::after_secs(1).await; | ||
| 30 | } | ||
| 31 | } | ||
diff --git a/examples/mcxa/src/bin/i2c-scan-blocking.rs b/examples/mcxa/src/bin/i2c-scan-blocking.rs new file mode 100644 index 000000000..bd706d712 --- /dev/null +++ b/examples/mcxa/src/bin/i2c-scan-blocking.rs | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::gpio::{Input, Pull}; | ||
| 6 | use embassy_time::Timer; | ||
| 7 | use hal::clocks::config::Div8; | ||
| 8 | use hal::config::Config; | ||
| 9 | use hal::i2c::controller::{self, I2c, Speed}; | ||
| 10 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let mut config = Config::default(); | ||
| 15 | config.clock_cfg.sirc.fro_lf_div = Div8::from_divisor(1); | ||
| 16 | |||
| 17 | let p = hal::init(config); | ||
| 18 | |||
| 19 | defmt::info!("I2C example"); | ||
| 20 | |||
| 21 | let mut config = controller::Config::default(); | ||
| 22 | config.speed = Speed::Standard; | ||
| 23 | |||
| 24 | // Note: P0_2 is connected to P1_8 on the FRDM_MCXA276 via a resistor, and | ||
| 25 | // defaults to SWO on the debug peripheral. Explicitly make it a high-z | ||
| 26 | // input. | ||
| 27 | let _pin = Input::new(p.P0_2, Pull::Disabled); | ||
| 28 | let mut i2c = I2c::new_blocking(p.LPI2C2, p.P1_9, p.P1_8, config).unwrap(); | ||
| 29 | |||
| 30 | for addr in 0x01..=0x7f { | ||
| 31 | let result = i2c.blocking_write(addr, &[]); | ||
| 32 | if result.is_ok() { | ||
| 33 | defmt::info!("Device found at addr {:02x}", addr); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | loop { | ||
| 38 | Timer::after_secs(10).await; | ||
| 39 | } | ||
| 40 | } | ||
diff --git a/examples/mcxa/src/bin/lpuart_buffered.rs b/examples/mcxa/src/bin/lpuart_buffered.rs new file mode 100644 index 000000000..47b56b7c7 --- /dev/null +++ b/examples/mcxa/src/bin/lpuart_buffered.rs | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::clocks::config::Div8; | ||
| 6 | use embassy_mcxa::lpuart::Config; | ||
| 7 | use embassy_mcxa::lpuart::buffered::BufferedLpuart; | ||
| 8 | use embassy_mcxa::{bind_interrupts, lpuart}; | ||
| 9 | use embedded_io_async::Write; | ||
| 10 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 11 | |||
| 12 | // Bind OS_EVENT for timers plus LPUART2 IRQ for the buffered driver | ||
| 13 | bind_interrupts!(struct Irqs { | ||
| 14 | LPUART2 => lpuart::buffered::BufferedInterruptHandler::<hal::peripherals::LPUART2>; | ||
| 15 | }); | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let mut cfg = hal::config::Config::default(); | ||
| 20 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 21 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 22 | let p = hal::init(cfg); | ||
| 23 | |||
| 24 | // Configure NVIC for LPUART2 | ||
| 25 | hal::interrupt::LPUART2.configure_for_uart(hal::interrupt::Priority::P3); | ||
| 26 | |||
| 27 | // UART configuration (enable both TX and RX) | ||
| 28 | let config = Config { | ||
| 29 | baudrate_bps: 115_200, | ||
| 30 | rx_fifo_watermark: 0, | ||
| 31 | tx_fifo_watermark: 0, | ||
| 32 | ..Default::default() | ||
| 33 | }; | ||
| 34 | |||
| 35 | let mut tx_buf = [0u8; 256]; | ||
| 36 | let mut rx_buf = [0u8; 256]; | ||
| 37 | |||
| 38 | // Create a buffered LPUART2 instance with both TX and RX | ||
| 39 | let mut uart = BufferedLpuart::new( | ||
| 40 | p.LPUART2, | ||
| 41 | p.P2_2, // TX pin | ||
| 42 | p.P2_3, // RX pin | ||
| 43 | Irqs, | ||
| 44 | &mut tx_buf, | ||
| 45 | &mut rx_buf, | ||
| 46 | config, | ||
| 47 | ) | ||
| 48 | .unwrap(); | ||
| 49 | |||
| 50 | // Split into TX and RX parts | ||
| 51 | let (tx, rx) = uart.split_ref(); | ||
| 52 | |||
| 53 | tx.write(b"Hello buffered LPUART.\r\n").await.unwrap(); | ||
| 54 | tx.write(b"Type characters to echo them back.\r\n").await.unwrap(); | ||
| 55 | |||
| 56 | // Echo loop | ||
| 57 | let mut buf = [0u8; 4]; | ||
| 58 | loop { | ||
| 59 | let used = rx.read(&mut buf).await.unwrap(); | ||
| 60 | tx.write_all(&buf[..used]).await.unwrap(); | ||
| 61 | } | ||
| 62 | } | ||
diff --git a/examples/mcxa/src/bin/lpuart_dma.rs b/examples/mcxa/src/bin/lpuart_dma.rs new file mode 100644 index 000000000..cc86f6a40 --- /dev/null +++ b/examples/mcxa/src/bin/lpuart_dma.rs | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | //! LPUART DMA example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! This example demonstrates using DMA for UART TX and RX operations. | ||
| 4 | //! It sends a message using DMA, then waits for 16 characters to be received | ||
| 5 | //! via DMA and echoes them back. | ||
| 6 | //! | ||
| 7 | //! The DMA request sources are automatically derived from the LPUART instance type. | ||
| 8 | //! DMA clock/reset/init is handled automatically by the HAL. | ||
| 9 | |||
| 10 | #![no_std] | ||
| 11 | #![no_main] | ||
| 12 | |||
| 13 | use embassy_executor::Spawner; | ||
| 14 | use embassy_mcxa::clocks::config::Div8; | ||
| 15 | use embassy_mcxa::lpuart::{Config, LpuartDma}; | ||
| 16 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | let mut cfg = hal::config::Config::default(); | ||
| 21 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 22 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 23 | let p = hal::init(cfg); | ||
| 24 | |||
| 25 | defmt::info!("LPUART DMA example starting..."); | ||
| 26 | |||
| 27 | // Create UART configuration | ||
| 28 | let config = Config { | ||
| 29 | baudrate_bps: 115_200, | ||
| 30 | ..Default::default() | ||
| 31 | }; | ||
| 32 | |||
| 33 | // Create UART instance with DMA channels | ||
| 34 | let mut lpuart = LpuartDma::new( | ||
| 35 | p.LPUART2, // Instance | ||
| 36 | p.P2_2, // TX pin | ||
| 37 | p.P2_3, // RX pin | ||
| 38 | p.DMA_CH0, // TX DMA channel | ||
| 39 | p.DMA_CH1, // RX DMA channel | ||
| 40 | config, | ||
| 41 | ) | ||
| 42 | .unwrap(); | ||
| 43 | |||
| 44 | // Send a message using DMA (DMA request source is automatically derived from LPUART2) | ||
| 45 | let tx_msg = b"Hello from LPUART2 DMA TX!\r\n"; | ||
| 46 | lpuart.write_dma(tx_msg).await.unwrap(); | ||
| 47 | |||
| 48 | defmt::info!("TX DMA complete"); | ||
| 49 | |||
| 50 | // Send prompt | ||
| 51 | let prompt = b"Type 16 characters to echo via DMA:\r\n"; | ||
| 52 | lpuart.write_dma(prompt).await.unwrap(); | ||
| 53 | |||
| 54 | // Receive 16 characters using DMA | ||
| 55 | let mut rx_buf = [0u8; 16]; | ||
| 56 | lpuart.read_dma(&mut rx_buf).await.unwrap(); | ||
| 57 | |||
| 58 | defmt::info!("RX DMA complete"); | ||
| 59 | |||
| 60 | // Echo back the received data | ||
| 61 | let echo_prefix = b"\r\nReceived: "; | ||
| 62 | lpuart.write_dma(echo_prefix).await.unwrap(); | ||
| 63 | lpuart.write_dma(&rx_buf).await.unwrap(); | ||
| 64 | let done_msg = b"\r\nDone!\r\n"; | ||
| 65 | lpuart.write_dma(done_msg).await.unwrap(); | ||
| 66 | |||
| 67 | defmt::info!("Example complete"); | ||
| 68 | } | ||
diff --git a/examples/mcxa/src/bin/lpuart_polling.rs b/examples/mcxa/src/bin/lpuart_polling.rs new file mode 100644 index 000000000..b80668834 --- /dev/null +++ b/examples/mcxa/src/bin/lpuart_polling.rs | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::clocks::config::Div8; | ||
| 6 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 7 | |||
| 8 | use crate::hal::lpuart::{Config, Lpuart}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) { | ||
| 12 | let mut cfg = hal::config::Config::default(); | ||
| 13 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 14 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 15 | let p = hal::init(cfg); | ||
| 16 | |||
| 17 | defmt::info!("boot"); | ||
| 18 | |||
| 19 | // Create UART configuration | ||
| 20 | let config = Config { | ||
| 21 | baudrate_bps: 115_200, | ||
| 22 | ..Default::default() | ||
| 23 | }; | ||
| 24 | |||
| 25 | // Create UART instance using LPUART2 with P2_2 as TX and P2_3 as RX | ||
| 26 | let lpuart = Lpuart::new_blocking( | ||
| 27 | p.LPUART2, // Peripheral | ||
| 28 | p.P2_2, // TX pin | ||
| 29 | p.P2_3, // RX pin | ||
| 30 | config, | ||
| 31 | ) | ||
| 32 | .unwrap(); | ||
| 33 | |||
| 34 | // Split into separate TX and RX parts | ||
| 35 | let (mut tx, mut rx) = lpuart.split(); | ||
| 36 | |||
| 37 | // Write hello messages | ||
| 38 | tx.blocking_write(b"Hello world.\r\n").unwrap(); | ||
| 39 | tx.blocking_write(b"Echoing. Type characters...\r\n").unwrap(); | ||
| 40 | |||
| 41 | // Echo loop | ||
| 42 | loop { | ||
| 43 | let mut buf = [0u8; 1]; | ||
| 44 | rx.blocking_read(&mut buf).unwrap(); | ||
| 45 | tx.blocking_write(&buf).unwrap(); | ||
| 46 | } | ||
| 47 | } | ||
diff --git a/examples/mcxa/src/bin/lpuart_ring_buffer.rs b/examples/mcxa/src/bin/lpuart_ring_buffer.rs new file mode 100644 index 000000000..be7fd4534 --- /dev/null +++ b/examples/mcxa/src/bin/lpuart_ring_buffer.rs | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | //! LPUART Ring Buffer DMA example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! This example demonstrates using the high-level `LpuartRxDma::setup_ring_buffer()` | ||
| 4 | //! API for continuous circular DMA reception from a UART peripheral. | ||
| 5 | //! | ||
| 6 | //! # Features demonstrated: | ||
| 7 | //! - `LpuartRxDma::setup_ring_buffer()` for continuous peripheral-to-memory DMA | ||
| 8 | //! - `RingBuffer` for async reading of received data | ||
| 9 | //! - Handling of potential overrun conditions | ||
| 10 | //! - Half-transfer and complete-transfer interrupts for timely wakeups | ||
| 11 | //! | ||
| 12 | //! # How it works: | ||
| 13 | //! 1. Create an `LpuartRxDma` driver with a DMA channel | ||
| 14 | //! 2. Call `setup_ring_buffer()` which handles all low-level DMA configuration | ||
| 15 | //! 3. Application asynchronously reads data as it arrives via `ring_buf.read()` | ||
| 16 | //! 4. Both half-transfer and complete-transfer interrupts wake the reader | ||
| 17 | |||
| 18 | #![no_std] | ||
| 19 | #![no_main] | ||
| 20 | |||
| 21 | use embassy_executor::Spawner; | ||
| 22 | use embassy_mcxa::clocks::config::Div8; | ||
| 23 | use embassy_mcxa::lpuart::{Config, LpuartDma, LpuartTxDma}; | ||
| 24 | use static_cell::ConstStaticCell; | ||
| 25 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 26 | |||
| 27 | // Ring buffer for RX - power of 2 is ideal for modulo efficiency | ||
| 28 | static RX_RING_BUFFER: ConstStaticCell<[u8; 64]> = ConstStaticCell::new([0; 64]); | ||
| 29 | |||
| 30 | /// Helper to write a byte as hex to UART | ||
| 31 | fn write_hex<T: embassy_mcxa::lpuart::Instance, C: embassy_mcxa::dma::Channel>( | ||
| 32 | tx: &mut LpuartTxDma<'_, T, C>, | ||
| 33 | byte: u8, | ||
| 34 | ) { | ||
| 35 | const HEX: &[u8; 16] = b"0123456789ABCDEF"; | ||
| 36 | let buf = [HEX[(byte >> 4) as usize], HEX[(byte & 0x0F) as usize]]; | ||
| 37 | tx.blocking_write(&buf).ok(); | ||
| 38 | } | ||
| 39 | |||
| 40 | #[embassy_executor::main] | ||
| 41 | async fn main(_spawner: Spawner) { | ||
| 42 | // Small delay to allow probe-rs to attach after reset | ||
| 43 | for _ in 0..100_000 { | ||
| 44 | cortex_m::asm::nop(); | ||
| 45 | } | ||
| 46 | |||
| 47 | let mut cfg = hal::config::Config::default(); | ||
| 48 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 49 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 50 | let p = hal::init(cfg); | ||
| 51 | |||
| 52 | defmt::info!("LPUART Ring Buffer DMA example starting..."); | ||
| 53 | |||
| 54 | // Create UART configuration | ||
| 55 | let config = Config { | ||
| 56 | baudrate_bps: 115_200, | ||
| 57 | ..Default::default() | ||
| 58 | }; | ||
| 59 | |||
| 60 | // Create LPUART with DMA support for both TX and RX, then split | ||
| 61 | // This is the proper Embassy pattern - create once, split into TX and RX | ||
| 62 | let lpuart = LpuartDma::new(p.LPUART2, p.P2_2, p.P2_3, p.DMA_CH1, p.DMA_CH0, config).unwrap(); | ||
| 63 | let (mut tx, rx) = lpuart.split(); | ||
| 64 | |||
| 65 | tx.blocking_write(b"LPUART Ring Buffer DMA Example\r\n").unwrap(); | ||
| 66 | tx.blocking_write(b"==============================\r\n\r\n").unwrap(); | ||
| 67 | |||
| 68 | tx.blocking_write(b"Setting up circular DMA for UART RX...\r\n") | ||
| 69 | .unwrap(); | ||
| 70 | |||
| 71 | let buf = RX_RING_BUFFER.take(); | ||
| 72 | // Set up the ring buffer with circular DMA | ||
| 73 | let mut ring_buf = rx.into_ring_dma_rx(buf); | ||
| 74 | |||
| 75 | tx.blocking_write(b"Ring buffer ready! Type characters to see them echoed.\r\n") | ||
| 76 | .unwrap(); | ||
| 77 | tx.blocking_write(b"The DMA continuously receives in the background.\r\n\r\n") | ||
| 78 | .unwrap(); | ||
| 79 | |||
| 80 | // Main loop: read from ring buffer and echo back | ||
| 81 | let mut read_buf = [0u8; 16]; | ||
| 82 | let mut total_received: usize = 0; | ||
| 83 | |||
| 84 | loop { | ||
| 85 | // Async read - waits until data is available | ||
| 86 | match ring_buf.read(&mut read_buf).await { | ||
| 87 | Ok(n) if n > 0 => { | ||
| 88 | total_received += n; | ||
| 89 | |||
| 90 | // Echo back what we received | ||
| 91 | tx.blocking_write(b"RX[").unwrap(); | ||
| 92 | for (i, &byte) in read_buf.iter().enumerate().take(n) { | ||
| 93 | write_hex(&mut tx, byte); | ||
| 94 | if i < n - 1 { | ||
| 95 | tx.blocking_write(b" ").unwrap(); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | tx.blocking_write(b"]: ").unwrap(); | ||
| 99 | tx.blocking_write(&read_buf[..n]).unwrap(); | ||
| 100 | tx.blocking_write(b"\r\n").unwrap(); | ||
| 101 | |||
| 102 | defmt::info!("Received {} bytes, total: {}", n, total_received); | ||
| 103 | } | ||
| 104 | Ok(_) => { | ||
| 105 | // No data, shouldn't happen with async read | ||
| 106 | } | ||
| 107 | Err(_) => { | ||
| 108 | // Overrun detected | ||
| 109 | tx.blocking_write(b"ERROR: Ring buffer overrun!\r\n").unwrap(); | ||
| 110 | defmt::error!("Ring buffer overrun!"); | ||
| 111 | ring_buf.clear(); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
diff --git a/examples/mcxa/src/bin/raw_dma_channel_link.rs b/examples/mcxa/src/bin/raw_dma_channel_link.rs new file mode 100644 index 000000000..74785e4f3 --- /dev/null +++ b/examples/mcxa/src/bin/raw_dma_channel_link.rs | |||
| @@ -0,0 +1,278 @@ | |||
| 1 | //! DMA channel linking example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have | ||
| 4 | //! a high-level and safe API for. It should not be taken as typical, recommended, or | ||
| 5 | //! stable usage! | ||
| 6 | //! | ||
| 7 | //! This example demonstrates DMA channel linking (minor and major loop linking): | ||
| 8 | //! - Channel 0: Transfers SRC_BUFFER to DEST_BUFFER0, with: | ||
| 9 | //! - Minor Link to Channel 1 (triggers CH1 after each minor loop) | ||
| 10 | //! - Major Link to Channel 2 (triggers CH2 after major loop completes) | ||
| 11 | //! - Channel 1: Transfers SRC_BUFFER to DEST_BUFFER1 (triggered by CH0 minor link) | ||
| 12 | //! - Channel 2: Transfers SRC_BUFFER to DEST_BUFFER2 (triggered by CH0 major link) | ||
| 13 | //! | ||
| 14 | //! # Embassy-style features demonstrated: | ||
| 15 | //! - `DmaChannel::new()` for channel creation | ||
| 16 | //! - `DmaChannel::is_done()` and `clear_done()` helper methods | ||
| 17 | //! - Channel linking with `set_minor_link()` and `set_major_link()` | ||
| 18 | |||
| 19 | #![no_std] | ||
| 20 | #![no_main] | ||
| 21 | |||
| 22 | use embassy_executor::Spawner; | ||
| 23 | use embassy_mcxa::clocks::config::Div8; | ||
| 24 | use embassy_mcxa::dma::DmaChannel; | ||
| 25 | use embassy_mcxa::pac; | ||
| 26 | use static_cell::ConstStaticCell; | ||
| 27 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 28 | |||
| 29 | // Buffers | ||
| 30 | static SRC_BUFFER: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([1, 2, 3, 4]); | ||
| 31 | static DEST_BUFFER0: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 32 | static DEST_BUFFER1: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 33 | static DEST_BUFFER2: ConstStaticCell<[u32; 4]> = ConstStaticCell::new([0; 4]); | ||
| 34 | |||
| 35 | #[embassy_executor::main] | ||
| 36 | async fn main(_spawner: Spawner) { | ||
| 37 | // Small delay to allow probe-rs to attach after reset | ||
| 38 | for _ in 0..100_000 { | ||
| 39 | cortex_m::asm::nop(); | ||
| 40 | } | ||
| 41 | |||
| 42 | let mut cfg = hal::config::Config::default(); | ||
| 43 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 44 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 45 | let p = hal::init(cfg); | ||
| 46 | |||
| 47 | defmt::info!("DMA channel link example starting..."); | ||
| 48 | |||
| 49 | // DMA is initialized during hal::init() - no need to call ensure_init() | ||
| 50 | |||
| 51 | let pac_periphs = unsafe { pac::Peripherals::steal() }; | ||
| 52 | let dma0 = &pac_periphs.dma0; | ||
| 53 | let edma = unsafe { &*pac::Edma0Tcd0::ptr() }; | ||
| 54 | |||
| 55 | // Clear any residual state | ||
| 56 | for i in 0..3 { | ||
| 57 | let t = edma.tcd(i); | ||
| 58 | t.ch_csr().write(|w| w.erq().disable().done().clear_bit_by_one()); | ||
| 59 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 60 | t.ch_es().write(|w| w.err().clear_bit_by_one()); | ||
| 61 | t.ch_mux().write(|w| unsafe { w.bits(0) }); | ||
| 62 | } | ||
| 63 | |||
| 64 | // Clear Global Halt/Error state | ||
| 65 | dma0.mp_csr().modify(|_, w| { | ||
| 66 | w.halt() | ||
| 67 | .normal_operation() | ||
| 68 | .hae() | ||
| 69 | .normal_operation() | ||
| 70 | .ecx() | ||
| 71 | .normal_operation() | ||
| 72 | .cx() | ||
| 73 | .normal_operation() | ||
| 74 | }); | ||
| 75 | |||
| 76 | defmt::info!("EDMA channel link example begin."); | ||
| 77 | |||
| 78 | // Initialize buffers | ||
| 79 | let src = SRC_BUFFER.take(); | ||
| 80 | let dst0 = DEST_BUFFER0.take(); | ||
| 81 | let dst1 = DEST_BUFFER1.take(); | ||
| 82 | let dst2 = DEST_BUFFER2.take(); | ||
| 83 | |||
| 84 | defmt::info!("Source Buffer: {=[?]}", src.as_slice()); | ||
| 85 | defmt::info!("DEST0 (before): {=[?]}", dst0.as_slice()); | ||
| 86 | defmt::info!("DEST1 (before): {=[?]}", dst1.as_slice()); | ||
| 87 | defmt::info!("DEST2 (before): {=[?]}", dst2.as_slice()); | ||
| 88 | |||
| 89 | defmt::info!("Configuring DMA channels with Embassy-style API..."); | ||
| 90 | |||
| 91 | let ch0 = DmaChannel::new(p.DMA_CH0); | ||
| 92 | let ch1 = DmaChannel::new(p.DMA_CH1); | ||
| 93 | let ch2 = DmaChannel::new(p.DMA_CH2); | ||
| 94 | |||
| 95 | // Configure channels using direct TCD access (advanced feature demo) | ||
| 96 | // This example demonstrates channel linking which requires direct TCD manipulation | ||
| 97 | |||
| 98 | // Helper to configure TCD for memory-to-memory transfer | ||
| 99 | // Parameters: channel, src, dst, width, nbytes (minor loop), count (major loop), interrupt | ||
| 100 | #[allow(clippy::too_many_arguments)] | ||
| 101 | unsafe fn configure_tcd( | ||
| 102 | edma: &embassy_mcxa::pac::edma_0_tcd0::RegisterBlock, | ||
| 103 | ch: usize, | ||
| 104 | src: u32, | ||
| 105 | dst: u32, | ||
| 106 | width: u8, | ||
| 107 | nbytes: u32, | ||
| 108 | count: u16, | ||
| 109 | enable_int: bool, | ||
| 110 | ) { | ||
| 111 | let t = edma.tcd(ch); | ||
| 112 | |||
| 113 | // Reset channel state | ||
| 114 | t.ch_csr().write(|w| { | ||
| 115 | w.erq() | ||
| 116 | .disable() | ||
| 117 | .earq() | ||
| 118 | .disable() | ||
| 119 | .eei() | ||
| 120 | .no_error() | ||
| 121 | .ebw() | ||
| 122 | .disable() | ||
| 123 | .done() | ||
| 124 | .clear_bit_by_one() | ||
| 125 | }); | ||
| 126 | t.ch_es().write(|w| w.bits(0)); | ||
| 127 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 128 | |||
| 129 | // Source/destination addresses | ||
| 130 | t.tcd_saddr().write(|w| w.saddr().bits(src)); | ||
| 131 | t.tcd_daddr().write(|w| w.daddr().bits(dst)); | ||
| 132 | |||
| 133 | // Offsets: increment by width | ||
| 134 | t.tcd_soff().write(|w| w.soff().bits(width as u16)); | ||
| 135 | t.tcd_doff().write(|w| w.doff().bits(width as u16)); | ||
| 136 | |||
| 137 | // Attributes: size = log2(width) | ||
| 138 | let size = match width { | ||
| 139 | 1 => 0, | ||
| 140 | 2 => 1, | ||
| 141 | 4 => 2, | ||
| 142 | _ => 0, | ||
| 143 | }; | ||
| 144 | t.tcd_attr().write(|w| w.ssize().bits(size).dsize().bits(size)); | ||
| 145 | |||
| 146 | // Number of bytes per minor loop | ||
| 147 | t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); | ||
| 148 | |||
| 149 | // Major loop: reset source address after major loop | ||
| 150 | let total_bytes = nbytes * count as u32; | ||
| 151 | t.tcd_slast_sda() | ||
| 152 | .write(|w| w.slast_sda().bits(-(total_bytes as i32) as u32)); | ||
| 153 | t.tcd_dlast_sga() | ||
| 154 | .write(|w| w.dlast_sga().bits(-(total_bytes as i32) as u32)); | ||
| 155 | |||
| 156 | // Major loop count | ||
| 157 | t.tcd_biter_elinkno().write(|w| w.biter().bits(count)); | ||
| 158 | t.tcd_citer_elinkno().write(|w| w.citer().bits(count)); | ||
| 159 | |||
| 160 | // Control/status: enable interrupt if requested | ||
| 161 | if enable_int { | ||
| 162 | t.tcd_csr().write(|w| w.intmajor().set_bit()); | ||
| 163 | } else { | ||
| 164 | t.tcd_csr().write(|w| w.intmajor().clear_bit()); | ||
| 165 | } | ||
| 166 | |||
| 167 | cortex_m::asm::dsb(); | ||
| 168 | } | ||
| 169 | |||
| 170 | unsafe { | ||
| 171 | // Channel 0: Transfer 16 bytes total (8 bytes per minor loop, 2 major iterations) | ||
| 172 | // Minor Link -> Channel 1 | ||
| 173 | // Major Link -> Channel 2 | ||
| 174 | configure_tcd( | ||
| 175 | edma, | ||
| 176 | 0, | ||
| 177 | src.as_ptr() as u32, | ||
| 178 | dst0.as_mut_ptr() as u32, | ||
| 179 | 4, // src width | ||
| 180 | 8, // nbytes (minor loop = 2 words) | ||
| 181 | 2, // count (major loop = 2 iterations) | ||
| 182 | false, // no interrupt | ||
| 183 | ); | ||
| 184 | ch0.set_minor_link(1); // Link to CH1 after each minor loop | ||
| 185 | ch0.set_major_link(2); // Link to CH2 after major loop | ||
| 186 | |||
| 187 | // Channel 1: Transfer 16 bytes (triggered by CH0 minor link) | ||
| 188 | configure_tcd( | ||
| 189 | edma, | ||
| 190 | 1, | ||
| 191 | src.as_ptr() as u32, | ||
| 192 | dst1.as_mut_ptr() as u32, | ||
| 193 | 4, | ||
| 194 | 16, // full buffer in one minor loop | ||
| 195 | 1, // 1 major iteration | ||
| 196 | false, | ||
| 197 | ); | ||
| 198 | |||
| 199 | // Channel 2: Transfer 16 bytes (triggered by CH0 major link) | ||
| 200 | configure_tcd( | ||
| 201 | edma, | ||
| 202 | 2, | ||
| 203 | src.as_ptr() as u32, | ||
| 204 | dst2.as_mut_ptr() as u32, | ||
| 205 | 4, | ||
| 206 | 16, // full buffer in one minor loop | ||
| 207 | 1, // 1 major iteration | ||
| 208 | true, // enable interrupt | ||
| 209 | ); | ||
| 210 | } | ||
| 211 | |||
| 212 | defmt::info!("Triggering Channel 0 (1st minor loop)..."); | ||
| 213 | |||
| 214 | // Trigger first minor loop of CH0 | ||
| 215 | unsafe { | ||
| 216 | ch0.trigger_start(); | ||
| 217 | } | ||
| 218 | |||
| 219 | // Wait for CH1 to complete (triggered by CH0 minor link) | ||
| 220 | while !ch1.is_done() { | ||
| 221 | cortex_m::asm::nop(); | ||
| 222 | } | ||
| 223 | unsafe { | ||
| 224 | ch1.clear_done(); | ||
| 225 | } | ||
| 226 | |||
| 227 | defmt::info!("CH1 done (via minor link)."); | ||
| 228 | defmt::info!("Triggering Channel 0 (2nd minor loop)..."); | ||
| 229 | |||
| 230 | // Trigger second minor loop of CH0 | ||
| 231 | unsafe { | ||
| 232 | ch0.trigger_start(); | ||
| 233 | } | ||
| 234 | |||
| 235 | // Wait for CH0 major loop to complete | ||
| 236 | while !ch0.is_done() { | ||
| 237 | cortex_m::asm::nop(); | ||
| 238 | } | ||
| 239 | unsafe { | ||
| 240 | ch0.clear_done(); | ||
| 241 | } | ||
| 242 | |||
| 243 | defmt::info!("CH0 major loop done."); | ||
| 244 | |||
| 245 | // Wait for CH2 to complete (triggered by CH0 major link) | ||
| 246 | // Using is_done() instead of AtomicBool - the standard interrupt handler | ||
| 247 | // clears the interrupt flag and wakes wakers, but DONE bit remains set | ||
| 248 | while !ch2.is_done() { | ||
| 249 | cortex_m::asm::nop(); | ||
| 250 | } | ||
| 251 | unsafe { | ||
| 252 | ch2.clear_done(); | ||
| 253 | } | ||
| 254 | |||
| 255 | defmt::info!("CH2 done (via major link)."); | ||
| 256 | |||
| 257 | defmt::info!("EDMA channel link example finish."); | ||
| 258 | |||
| 259 | defmt::info!("DEST0 (after): {=[?]}", dst0.as_slice()); | ||
| 260 | defmt::info!("DEST1 (after): {=[?]}", dst1.as_slice()); | ||
| 261 | defmt::info!("DEST2 (after): {=[?]}", dst2.as_slice()); | ||
| 262 | |||
| 263 | // Verify all buffers match source | ||
| 264 | let mut success = true; | ||
| 265 | for sli in [dst0, dst1, dst2] { | ||
| 266 | success &= sli == src; | ||
| 267 | } | ||
| 268 | |||
| 269 | if success { | ||
| 270 | defmt::info!("PASS: Data verified."); | ||
| 271 | } else { | ||
| 272 | defmt::error!("FAIL: Mismatch detected!"); | ||
| 273 | } | ||
| 274 | |||
| 275 | loop { | ||
| 276 | cortex_m::asm::wfe(); | ||
| 277 | } | ||
| 278 | } | ||
diff --git a/examples/mcxa/src/bin/raw_dma_interleave_transfer.rs b/examples/mcxa/src/bin/raw_dma_interleave_transfer.rs new file mode 100644 index 000000000..a383b6cf4 --- /dev/null +++ b/examples/mcxa/src/bin/raw_dma_interleave_transfer.rs | |||
| @@ -0,0 +1,141 @@ | |||
| 1 | //! DMA interleaved transfer example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have | ||
| 4 | //! a high-level and safe API for. It should not be taken as typical, recommended, or | ||
| 5 | //! stable usage! | ||
| 6 | //! | ||
| 7 | //! This example demonstrates using DMA with custom source/destination offsets | ||
| 8 | //! to interleave data during transfer. | ||
| 9 | //! | ||
| 10 | //! # Embassy-style features demonstrated: | ||
| 11 | //! - `TransferOptions::default()` for configuration (used internally) | ||
| 12 | //! - DMA channel with `DmaChannel::new()` | ||
| 13 | |||
| 14 | #![no_std] | ||
| 15 | #![no_main] | ||
| 16 | |||
| 17 | use embassy_executor::Spawner; | ||
| 18 | use embassy_mcxa::clocks::config::Div8; | ||
| 19 | use embassy_mcxa::dma::DmaChannel; | ||
| 20 | use static_cell::ConstStaticCell; | ||
| 21 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 22 | |||
| 23 | const BUFFER_LENGTH: usize = 16; | ||
| 24 | const HALF_BUFF_LENGTH: usize = BUFFER_LENGTH / 2; | ||
| 25 | |||
| 26 | // Buffers in RAM | ||
| 27 | static SRC_BUFFER: ConstStaticCell<[u32; HALF_BUFF_LENGTH]> = ConstStaticCell::new([0; HALF_BUFF_LENGTH]); | ||
| 28 | static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); | ||
| 29 | |||
| 30 | #[embassy_executor::main] | ||
| 31 | async fn main(_spawner: Spawner) { | ||
| 32 | // Small delay to allow probe-rs to attach after reset | ||
| 33 | for _ in 0..100_000 { | ||
| 34 | cortex_m::asm::nop(); | ||
| 35 | } | ||
| 36 | |||
| 37 | let mut cfg = hal::config::Config::default(); | ||
| 38 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 39 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 40 | let p = hal::init(cfg); | ||
| 41 | |||
| 42 | defmt::info!("DMA interleave transfer example starting..."); | ||
| 43 | |||
| 44 | defmt::info!("EDMA interleave transfer example begin."); | ||
| 45 | |||
| 46 | // Initialize buffers | ||
| 47 | let src = SRC_BUFFER.take(); | ||
| 48 | *src = [1, 2, 3, 4, 5, 6, 7, 8]; | ||
| 49 | let dst = DEST_BUFFER.take(); | ||
| 50 | |||
| 51 | defmt::info!("Source Buffer: {=[?]}", src.as_slice()); | ||
| 52 | defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); | ||
| 53 | |||
| 54 | defmt::info!("Configuring DMA with Embassy-style API..."); | ||
| 55 | |||
| 56 | // Create DMA channel using Embassy-style API | ||
| 57 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | ||
| 58 | |||
| 59 | // Configure interleaved transfer using direct TCD access: | ||
| 60 | // - src_offset = 4: advance source by 4 bytes after each read | ||
| 61 | // - dst_offset = 8: advance dest by 8 bytes after each write | ||
| 62 | // This spreads source data across every other word in destination | ||
| 63 | unsafe { | ||
| 64 | let t = dma_ch0.tcd(); | ||
| 65 | |||
| 66 | // Reset channel state | ||
| 67 | t.ch_csr().write(|w| { | ||
| 68 | w.erq() | ||
| 69 | .disable() | ||
| 70 | .earq() | ||
| 71 | .disable() | ||
| 72 | .eei() | ||
| 73 | .no_error() | ||
| 74 | .ebw() | ||
| 75 | .disable() | ||
| 76 | .done() | ||
| 77 | .clear_bit_by_one() | ||
| 78 | }); | ||
| 79 | t.ch_es().write(|w| w.bits(0)); | ||
| 80 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 81 | |||
| 82 | // Source/destination addresses | ||
| 83 | t.tcd_saddr().write(|w| w.saddr().bits(src.as_ptr() as u32)); | ||
| 84 | t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32)); | ||
| 85 | |||
| 86 | // Custom offsets for interleaving | ||
| 87 | t.tcd_soff().write(|w| w.soff().bits(4)); // src: +4 bytes per read | ||
| 88 | t.tcd_doff().write(|w| w.doff().bits(8)); // dst: +8 bytes per write | ||
| 89 | |||
| 90 | // Attributes: 32-bit transfers (size = 2) | ||
| 91 | t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); | ||
| 92 | |||
| 93 | // Transfer entire source buffer in one minor loop | ||
| 94 | let nbytes = (HALF_BUFF_LENGTH * 4) as u32; | ||
| 95 | t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); | ||
| 96 | |||
| 97 | // Reset source address after major loop | ||
| 98 | t.tcd_slast_sda().write(|w| w.slast_sda().bits(-(nbytes as i32) as u32)); | ||
| 99 | // Destination uses 2x offset, so adjust accordingly | ||
| 100 | let dst_total = (HALF_BUFF_LENGTH * 8) as u32; | ||
| 101 | t.tcd_dlast_sga() | ||
| 102 | .write(|w| w.dlast_sga().bits(-(dst_total as i32) as u32)); | ||
| 103 | |||
| 104 | // Major loop count = 1 | ||
| 105 | t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); | ||
| 106 | t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); | ||
| 107 | |||
| 108 | // Enable interrupt on major loop completion | ||
| 109 | t.tcd_csr().write(|w| w.intmajor().set_bit()); | ||
| 110 | |||
| 111 | cortex_m::asm::dsb(); | ||
| 112 | |||
| 113 | defmt::info!("Triggering transfer..."); | ||
| 114 | dma_ch0.trigger_start(); | ||
| 115 | } | ||
| 116 | |||
| 117 | // Wait for completion using channel helper method | ||
| 118 | while !dma_ch0.is_done() { | ||
| 119 | cortex_m::asm::nop(); | ||
| 120 | } | ||
| 121 | unsafe { | ||
| 122 | dma_ch0.clear_done(); | ||
| 123 | } | ||
| 124 | |||
| 125 | defmt::info!("EDMA interleave transfer example finish."); | ||
| 126 | defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); | ||
| 127 | |||
| 128 | // Verify: Even indices should match SRC_BUFFER[i/2], odd indices should be 0 | ||
| 129 | let mut mismatch = false; | ||
| 130 | let diter = dst.chunks_exact(2); | ||
| 131 | let siter = src.iter(); | ||
| 132 | for (ch, src) in diter.zip(siter) { | ||
| 133 | mismatch |= !matches!(ch, [a, 0] if a == src); | ||
| 134 | } | ||
| 135 | |||
| 136 | if mismatch { | ||
| 137 | defmt::error!("FAIL: Mismatch detected!"); | ||
| 138 | } else { | ||
| 139 | defmt::info!("PASS: Data verified."); | ||
| 140 | } | ||
| 141 | } | ||
diff --git a/examples/mcxa/src/bin/raw_dma_memset.rs b/examples/mcxa/src/bin/raw_dma_memset.rs new file mode 100644 index 000000000..7b3c06ffa --- /dev/null +++ b/examples/mcxa/src/bin/raw_dma_memset.rs | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | //! DMA memset example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have | ||
| 4 | //! a high-level and safe API for. It should not be taken as typical, recommended, or | ||
| 5 | //! stable usage! | ||
| 6 | //! | ||
| 7 | //! This example demonstrates using DMA to fill a buffer with a repeated pattern. | ||
| 8 | //! The source address stays fixed while the destination increments. | ||
| 9 | //! | ||
| 10 | //! # Embassy-style features demonstrated: | ||
| 11 | //! - `DmaChannel::is_done()` and `clear_done()` helper methods | ||
| 12 | //! - No need to pass register block around | ||
| 13 | |||
| 14 | #![no_std] | ||
| 15 | #![no_main] | ||
| 16 | |||
| 17 | use embassy_executor::Spawner; | ||
| 18 | use embassy_mcxa::clocks::config::Div8; | ||
| 19 | use embassy_mcxa::dma::DmaChannel; | ||
| 20 | use static_cell::ConstStaticCell; | ||
| 21 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 22 | |||
| 23 | const BUFFER_LENGTH: usize = 4; | ||
| 24 | |||
| 25 | // Buffers in RAM | ||
| 26 | static PATTERN: u32 = 0xDEADBEEF; | ||
| 27 | static DEST_BUFFER: ConstStaticCell<[u32; BUFFER_LENGTH]> = ConstStaticCell::new([0; BUFFER_LENGTH]); | ||
| 28 | |||
| 29 | #[embassy_executor::main] | ||
| 30 | async fn main(_spawner: Spawner) { | ||
| 31 | // Small delay to allow probe-rs to attach after reset | ||
| 32 | for _ in 0..100_000 { | ||
| 33 | cortex_m::asm::nop(); | ||
| 34 | } | ||
| 35 | |||
| 36 | let mut cfg = hal::config::Config::default(); | ||
| 37 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 38 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 39 | let p = hal::init(cfg); | ||
| 40 | |||
| 41 | defmt::info!("DMA memset example starting..."); | ||
| 42 | defmt::info!("EDMA memset example begin."); | ||
| 43 | |||
| 44 | // Initialize buffers | ||
| 45 | let pat = &PATTERN; | ||
| 46 | let dst = DEST_BUFFER.take(); | ||
| 47 | defmt::info!("Pattern Value: {=u32}", pat); | ||
| 48 | defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); | ||
| 49 | defmt::info!("Configuring DMA with Embassy-style API..."); | ||
| 50 | |||
| 51 | // Create DMA channel using Embassy-style API | ||
| 52 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | ||
| 53 | |||
| 54 | // Configure memset transfer using direct TCD access: | ||
| 55 | // Source stays fixed (soff = 0, reads same pattern repeatedly) | ||
| 56 | // Destination increments (doff = 4) | ||
| 57 | unsafe { | ||
| 58 | let t = dma_ch0.tcd(); | ||
| 59 | |||
| 60 | // Reset channel state | ||
| 61 | t.ch_csr().write(|w| { | ||
| 62 | w.erq() | ||
| 63 | .disable() | ||
| 64 | .earq() | ||
| 65 | .disable() | ||
| 66 | .eei() | ||
| 67 | .no_error() | ||
| 68 | .ebw() | ||
| 69 | .disable() | ||
| 70 | .done() | ||
| 71 | .clear_bit_by_one() | ||
| 72 | }); | ||
| 73 | t.ch_es().write(|w| w.bits(0)); | ||
| 74 | t.ch_int().write(|w| w.int().clear_bit_by_one()); | ||
| 75 | |||
| 76 | // Source address (pattern) - fixed | ||
| 77 | t.tcd_saddr().write(|w| w.saddr().bits(pat as *const _ as u32)); | ||
| 78 | // Destination address - increments | ||
| 79 | t.tcd_daddr().write(|w| w.daddr().bits(dst.as_mut_ptr() as u32)); | ||
| 80 | |||
| 81 | // Source offset = 0 (stays fixed), Dest offset = 4 (increments) | ||
| 82 | t.tcd_soff().write(|w| w.soff().bits(0)); | ||
| 83 | t.tcd_doff().write(|w| w.doff().bits(4)); | ||
| 84 | |||
| 85 | // Attributes: 32-bit transfers (size = 2) | ||
| 86 | t.tcd_attr().write(|w| w.ssize().bits(2).dsize().bits(2)); | ||
| 87 | |||
| 88 | // Transfer entire buffer in one minor loop | ||
| 89 | let nbytes = (BUFFER_LENGTH * 4) as u32; | ||
| 90 | t.tcd_nbytes_mloffno().write(|w| w.nbytes().bits(nbytes)); | ||
| 91 | |||
| 92 | // Source doesn't need adjustment (stays fixed) | ||
| 93 | t.tcd_slast_sda().write(|w| w.slast_sda().bits(0)); | ||
| 94 | // Reset dest address after major loop | ||
| 95 | t.tcd_dlast_sga().write(|w| w.dlast_sga().bits(-(nbytes as i32) as u32)); | ||
| 96 | |||
| 97 | // Major loop count = 1 | ||
| 98 | t.tcd_biter_elinkno().write(|w| w.biter().bits(1)); | ||
| 99 | t.tcd_citer_elinkno().write(|w| w.citer().bits(1)); | ||
| 100 | |||
| 101 | // Enable interrupt on major loop completion | ||
| 102 | t.tcd_csr().write(|w| w.intmajor().set_bit()); | ||
| 103 | |||
| 104 | cortex_m::asm::dsb(); | ||
| 105 | |||
| 106 | defmt::info!("Triggering transfer..."); | ||
| 107 | dma_ch0.trigger_start(); | ||
| 108 | } | ||
| 109 | |||
| 110 | // Wait for completion using channel helper method | ||
| 111 | while !dma_ch0.is_done() { | ||
| 112 | cortex_m::asm::nop(); | ||
| 113 | } | ||
| 114 | unsafe { | ||
| 115 | dma_ch0.clear_done(); | ||
| 116 | } | ||
| 117 | |||
| 118 | defmt::info!("EDMA memset example finish."); | ||
| 119 | defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); | ||
| 120 | |||
| 121 | // Verify: All elements should equal PATTERN | ||
| 122 | let mismatch = dst.iter().any(|i| *i != *pat); | ||
| 123 | |||
| 124 | if mismatch { | ||
| 125 | defmt::error!("FAIL: Mismatch detected!"); | ||
| 126 | } else { | ||
| 127 | defmt::info!("PASS: Data verified."); | ||
| 128 | } | ||
| 129 | } | ||
diff --git a/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs new file mode 100644 index 000000000..80df40449 --- /dev/null +++ b/examples/mcxa/src/bin/raw_dma_ping_pong_transfer.rs | |||
| @@ -0,0 +1,244 @@ | |||
| 1 | //! DMA ping-pong/double-buffer transfer example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have | ||
| 4 | //! a high-level and safe API for. It should not be taken as typical, recommended, or | ||
| 5 | //! stable usage! | ||
| 6 | //! | ||
| 7 | //! This example demonstrates two approaches for ping-pong/double-buffering: | ||
| 8 | //! | ||
| 9 | //! ## Approach 1: Scatter/Gather with linked TCDs (manual) | ||
| 10 | //! - Two TCDs link to each other for alternating transfers | ||
| 11 | //! - Uses custom handler that delegates to on_interrupt() then signals completion | ||
| 12 | //! - Note: With ESG=1, DONE bit is cleared by hardware when next TCD loads, | ||
| 13 | //! so we need an AtomicBool to track completion | ||
| 14 | //! | ||
| 15 | //! ## Approach 2: Half-transfer interrupt with wait_half() (NEW!) | ||
| 16 | //! - Single continuous transfer over entire buffer | ||
| 17 | //! - Uses half-transfer interrupt to know when first half is ready | ||
| 18 | //! - Application can process first half while second half is being filled | ||
| 19 | //! | ||
| 20 | //! # Embassy-style features demonstrated: | ||
| 21 | //! - `DmaChannel::new()` for channel creation | ||
| 22 | //! - Scatter/gather with linked TCDs | ||
| 23 | //! - Custom handler that delegates to HAL's `on_interrupt()` (best practice) | ||
| 24 | //! - Standard `DmaCh1InterruptHandler` with `bind_interrupts!` macro | ||
| 25 | //! - NEW: `wait_half()` for half-transfer interrupt handling | ||
| 26 | |||
| 27 | #![no_std] | ||
| 28 | #![no_main] | ||
| 29 | |||
| 30 | use embassy_executor::Spawner; | ||
| 31 | use embassy_mcxa::clocks::config::Div8; | ||
| 32 | use embassy_mcxa::dma::{DmaChannel, Tcd, TransferOptions}; | ||
| 33 | use embassy_mcxa::pac; | ||
| 34 | use static_cell::ConstStaticCell; | ||
| 35 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 36 | |||
| 37 | // Source and destination buffers for Approach 1 (scatter/gather) | ||
| 38 | static SRC: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8]); | ||
| 39 | static DST: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]); | ||
| 40 | |||
| 41 | // Source and destination buffers for Approach 2 (wait_half) | ||
| 42 | static SRC2: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0xA1, 0xA2, 0xA3, 0xA4, 0xB1, 0xB2, 0xB3, 0xB4]); | ||
| 43 | static DST2: ConstStaticCell<[u32; 8]> = ConstStaticCell::new([0; 8]); | ||
| 44 | |||
| 45 | // TCD pool for scatter/gather - must be 32-byte aligned | ||
| 46 | #[repr(C, align(32))] | ||
| 47 | struct TcdPool([Tcd; 2]); | ||
| 48 | |||
| 49 | static TCD_POOL: ConstStaticCell<TcdPool> = ConstStaticCell::new(TcdPool( | ||
| 50 | [Tcd { | ||
| 51 | saddr: 0, | ||
| 52 | soff: 0, | ||
| 53 | attr: 0, | ||
| 54 | nbytes: 0, | ||
| 55 | slast: 0, | ||
| 56 | daddr: 0, | ||
| 57 | doff: 0, | ||
| 58 | citer: 0, | ||
| 59 | dlast_sga: 0, | ||
| 60 | csr: 0, | ||
| 61 | biter: 0, | ||
| 62 | }; 2], | ||
| 63 | )); | ||
| 64 | |||
| 65 | #[embassy_executor::main] | ||
| 66 | async fn main(_spawner: Spawner) { | ||
| 67 | // Small delay to allow probe-rs to attach after reset | ||
| 68 | for _ in 0..100_000 { | ||
| 69 | cortex_m::asm::nop(); | ||
| 70 | } | ||
| 71 | |||
| 72 | let mut cfg = hal::config::Config::default(); | ||
| 73 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 74 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 75 | let p = hal::init(cfg); | ||
| 76 | |||
| 77 | defmt::info!("DMA ping-pong transfer example starting..."); | ||
| 78 | |||
| 79 | defmt::info!("EDMA ping-pong transfer example begin."); | ||
| 80 | |||
| 81 | // Initialize buffers | ||
| 82 | let src = SRC.take(); | ||
| 83 | let dst = DST.take(); | ||
| 84 | |||
| 85 | defmt::info!("Source Buffer: {=[?]}", src.as_slice()); | ||
| 86 | defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); | ||
| 87 | |||
| 88 | defmt::info!("Configuring ping-pong DMA with Embassy-style API..."); | ||
| 89 | |||
| 90 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | ||
| 91 | |||
| 92 | // Configure ping-pong transfer using direct TCD access: | ||
| 93 | // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. | ||
| 94 | // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), links to TCD1. | ||
| 95 | // TCD1 transfers second half (SRC[4..8] -> DST[4..8]), links to TCD0. | ||
| 96 | let tcds = &mut TCD_POOL.take().0; | ||
| 97 | |||
| 98 | let half_len = 4usize; | ||
| 99 | let half_bytes = (half_len * 4) as u32; | ||
| 100 | |||
| 101 | unsafe { | ||
| 102 | let tcd0_addr = &tcds[0] as *const _ as u32; | ||
| 103 | let tcd1_addr = &tcds[1] as *const _ as u32; | ||
| 104 | |||
| 105 | // TCD0: First half -> Links to TCD1 | ||
| 106 | tcds[0] = Tcd { | ||
| 107 | saddr: src.as_ptr() as u32, | ||
| 108 | soff: 4, | ||
| 109 | attr: 0x0202, // 32-bit src/dst | ||
| 110 | nbytes: half_bytes, | ||
| 111 | slast: 0, | ||
| 112 | daddr: dst.as_mut_ptr() as u32, | ||
| 113 | doff: 4, | ||
| 114 | citer: 1, | ||
| 115 | dlast_sga: tcd1_addr as i32, | ||
| 116 | csr: 0x0012, // ESG | INTMAJOR | ||
| 117 | biter: 1, | ||
| 118 | }; | ||
| 119 | |||
| 120 | // TCD1: Second half -> Links to TCD0 | ||
| 121 | tcds[1] = Tcd { | ||
| 122 | saddr: src.as_ptr().add(half_len) as u32, | ||
| 123 | soff: 4, | ||
| 124 | attr: 0x0202, | ||
| 125 | nbytes: half_bytes, | ||
| 126 | slast: 0, | ||
| 127 | daddr: dst.as_mut_ptr().add(half_len) as u32, | ||
| 128 | doff: 4, | ||
| 129 | citer: 1, | ||
| 130 | dlast_sga: tcd0_addr as i32, | ||
| 131 | csr: 0x0012, | ||
| 132 | biter: 1, | ||
| 133 | }; | ||
| 134 | |||
| 135 | // Load TCD0 into hardware registers | ||
| 136 | dma_ch0.load_tcd(&tcds[0]); | ||
| 137 | } | ||
| 138 | |||
| 139 | defmt::info!("Triggering first half transfer..."); | ||
| 140 | |||
| 141 | // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) | ||
| 142 | unsafe { | ||
| 143 | dma_ch0.trigger_start(); | ||
| 144 | } | ||
| 145 | |||
| 146 | let tcd = dma_ch0.tcd(); | ||
| 147 | // Wait for first half | ||
| 148 | loop { | ||
| 149 | if tcd.tcd_saddr().read().bits() != src.as_ptr() as u32 { | ||
| 150 | break; | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | defmt::info!("First half transferred."); | ||
| 155 | defmt::info!("Triggering second half transfer..."); | ||
| 156 | |||
| 157 | // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) | ||
| 158 | unsafe { | ||
| 159 | dma_ch0.trigger_start(); | ||
| 160 | } | ||
| 161 | |||
| 162 | // Wait for second half | ||
| 163 | loop { | ||
| 164 | if tcd.tcd_saddr().read().bits() != unsafe { src.as_ptr().add(half_len) } as u32 { | ||
| 165 | break; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | defmt::info!("Second half transferred."); | ||
| 170 | |||
| 171 | defmt::info!("EDMA ping-pong transfer example finish."); | ||
| 172 | defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); | ||
| 173 | |||
| 174 | // Verify: DST should match SRC | ||
| 175 | let mismatch = src != dst; | ||
| 176 | |||
| 177 | if mismatch { | ||
| 178 | defmt::error!("FAIL: Approach 1 mismatch detected!"); | ||
| 179 | } else { | ||
| 180 | defmt::info!("PASS: Approach 1 data verified."); | ||
| 181 | } | ||
| 182 | |||
| 183 | // ========================================================================= | ||
| 184 | // Approach 2: Half-Transfer Interrupt with wait_half() (NEW!) | ||
| 185 | // ========================================================================= | ||
| 186 | // | ||
| 187 | // This approach uses a single continuous DMA transfer with half-transfer | ||
| 188 | // interrupt enabled. The wait_half() method allows you to be notified | ||
| 189 | // when the first half of the buffer is complete, so you can process it | ||
| 190 | // while the second half is still being filled. | ||
| 191 | // | ||
| 192 | // Benefits: | ||
| 193 | // - Simpler setup (no TCD pool needed) | ||
| 194 | // - True async/await support | ||
| 195 | // - Good for streaming data processing | ||
| 196 | |||
| 197 | defmt::info!("--- Approach 2: wait_half() demo ---"); | ||
| 198 | |||
| 199 | // Enable DMA CH1 interrupt | ||
| 200 | unsafe { | ||
| 201 | cortex_m::peripheral::NVIC::unmask(pac::Interrupt::DMA_CH1); | ||
| 202 | } | ||
| 203 | |||
| 204 | // Initialize approach 2 buffers | ||
| 205 | let src2 = SRC2.take(); | ||
| 206 | let dst2 = DST2.take(); | ||
| 207 | |||
| 208 | defmt::info!("SRC2: {=[?]}", src2.as_slice()); | ||
| 209 | |||
| 210 | let dma_ch1 = DmaChannel::new(p.DMA_CH1); | ||
| 211 | |||
| 212 | // Configure transfer with half-transfer interrupt enabled | ||
| 213 | let mut options = TransferOptions::default(); | ||
| 214 | options.half_transfer_interrupt = true; // Enable half-transfer interrupt | ||
| 215 | options.complete_transfer_interrupt = true; | ||
| 216 | |||
| 217 | defmt::info!("Starting transfer with half_transfer_interrupt..."); | ||
| 218 | |||
| 219 | // Create the transfer | ||
| 220 | let mut transfer = dma_ch1.mem_to_mem(src2, dst2, options).unwrap(); | ||
| 221 | |||
| 222 | // Wait for half-transfer (first 4 elements) | ||
| 223 | defmt::info!("Waiting for first half..."); | ||
| 224 | let _ok = transfer.wait_half().await.unwrap(); | ||
| 225 | |||
| 226 | defmt::info!("Half-transfer complete!"); | ||
| 227 | |||
| 228 | // Wait for complete transfer | ||
| 229 | defmt::info!("Waiting for second half..."); | ||
| 230 | transfer.await.unwrap(); | ||
| 231 | |||
| 232 | defmt::info!("Transfer complete! Full DST2: {=[?]}", dst2.as_slice()); | ||
| 233 | |||
| 234 | // Verify approach 2 | ||
| 235 | let mismatch2 = src2 != dst2; | ||
| 236 | |||
| 237 | if mismatch2 { | ||
| 238 | defmt::error!("FAIL: Approach 2 mismatch!"); | ||
| 239 | } else { | ||
| 240 | defmt::info!("PASS: Approach 2 verified."); | ||
| 241 | } | ||
| 242 | |||
| 243 | defmt::info!("=== All ping-pong demos complete ==="); | ||
| 244 | } | ||
diff --git a/examples/mcxa/src/bin/raw_dma_scatter_gather.rs b/examples/mcxa/src/bin/raw_dma_scatter_gather.rs new file mode 100644 index 000000000..eb9960764 --- /dev/null +++ b/examples/mcxa/src/bin/raw_dma_scatter_gather.rs | |||
| @@ -0,0 +1,165 @@ | |||
| 1 | //! DMA scatter-gather transfer example for MCXA276. | ||
| 2 | //! | ||
| 3 | //! NOTE: this is a "raw dma" example! It exists as a proof of concept, as we don't have | ||
| 4 | //! a high-level and safe API for. It should not be taken as typical, recommended, or | ||
| 5 | //! stable usage! | ||
| 6 | //! | ||
| 7 | //! This example demonstrates using DMA with scatter/gather to chain multiple | ||
| 8 | //! transfer descriptors. The first TCD transfers the first half of the buffer, | ||
| 9 | //! then automatically loads the second TCD to transfer the second half. | ||
| 10 | //! | ||
| 11 | //! # Embassy-style features demonstrated: | ||
| 12 | //! - `DmaChannel::new()` for channel creation | ||
| 13 | //! - Scatter/gather with chained TCDs | ||
| 14 | //! - Custom handler that delegates to HAL's `on_interrupt()` (best practice) | ||
| 15 | |||
| 16 | #![no_std] | ||
| 17 | #![no_main] | ||
| 18 | |||
| 19 | use embassy_executor::Spawner; | ||
| 20 | use embassy_mcxa::clocks::config::Div8; | ||
| 21 | use embassy_mcxa::dma::{DmaChannel, Tcd}; | ||
| 22 | use static_cell::ConstStaticCell; | ||
| 23 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 24 | |||
| 25 | // Source and destination buffers | ||
| 26 | static SRC: ConstStaticCell<[u32; 12]> = ConstStaticCell::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); | ||
| 27 | static DST: ConstStaticCell<[u32; 12]> = ConstStaticCell::new([0; 12]); | ||
| 28 | |||
| 29 | // TCD pool for scatter/gather - must be 32-byte aligned | ||
| 30 | #[repr(C, align(32))] | ||
| 31 | struct TcdPool([Tcd; 2]); | ||
| 32 | |||
| 33 | static TCD_POOL: ConstStaticCell<TcdPool> = ConstStaticCell::new(TcdPool( | ||
| 34 | [Tcd { | ||
| 35 | saddr: 0, | ||
| 36 | soff: 0, | ||
| 37 | attr: 0, | ||
| 38 | nbytes: 0, | ||
| 39 | slast: 0, | ||
| 40 | daddr: 0, | ||
| 41 | doff: 0, | ||
| 42 | citer: 0, | ||
| 43 | dlast_sga: 0, | ||
| 44 | csr: 0, | ||
| 45 | biter: 0, | ||
| 46 | }; 2], | ||
| 47 | )); | ||
| 48 | |||
| 49 | #[embassy_executor::main] | ||
| 50 | async fn main(_spawner: Spawner) { | ||
| 51 | // Small delay to allow probe-rs to attach after reset | ||
| 52 | for _ in 0..100_000 { | ||
| 53 | cortex_m::asm::nop(); | ||
| 54 | } | ||
| 55 | |||
| 56 | let mut cfg = hal::config::Config::default(); | ||
| 57 | cfg.clock_cfg.sirc.fro_12m_enabled = true; | ||
| 58 | cfg.clock_cfg.sirc.fro_lf_div = Some(Div8::no_div()); | ||
| 59 | let p = hal::init(cfg); | ||
| 60 | |||
| 61 | defmt::info!("DMA scatter-gather transfer example starting..."); | ||
| 62 | |||
| 63 | defmt::info!("EDMA scatter-gather transfer example begin."); | ||
| 64 | |||
| 65 | // Initialize buffers | ||
| 66 | let src = SRC.take(); | ||
| 67 | let dst = DST.take(); | ||
| 68 | |||
| 69 | defmt::info!("Source Buffer: {=[?]}", src.as_slice()); | ||
| 70 | defmt::info!("Destination Buffer (before): {=[?]}", dst.as_slice()); | ||
| 71 | defmt::info!("Configuring scatter-gather DMA with Embassy-style API..."); | ||
| 72 | |||
| 73 | let dma_ch0 = DmaChannel::new(p.DMA_CH0); | ||
| 74 | let src_ptr = src.as_ptr(); | ||
| 75 | let dst_ptr = dst.as_mut_ptr(); | ||
| 76 | |||
| 77 | // Configure scatter-gather transfer using direct TCD access: | ||
| 78 | // This sets up TCD0 and TCD1 in RAM, and loads TCD0 into the channel. | ||
| 79 | // TCD0 transfers first half (SRC[0..4] -> DST[0..4]), then loads TCD1. | ||
| 80 | // TCD1 transfers second half (SRC[4..12] -> DST[4..12]), last TCD. | ||
| 81 | unsafe { | ||
| 82 | let tcds = &mut TCD_POOL.take().0; | ||
| 83 | |||
| 84 | // In the first transfer, copy | ||
| 85 | tcds[0] = Tcd { | ||
| 86 | saddr: src_ptr as u32, | ||
| 87 | soff: 4, | ||
| 88 | attr: 0x0202, // 32-bit src/dst | ||
| 89 | nbytes: 4 * 4, | ||
| 90 | slast: 0, | ||
| 91 | daddr: dst_ptr as u32, | ||
| 92 | doff: 4, | ||
| 93 | citer: 1, | ||
| 94 | dlast_sga: tcds.as_ptr().add(1) as i32, | ||
| 95 | // ESG (scatter/gather) for non-last, INTMAJOR for all | ||
| 96 | csr: 0x0012, | ||
| 97 | biter: 1, | ||
| 98 | }; | ||
| 99 | |||
| 100 | tcds[1] = Tcd { | ||
| 101 | saddr: src_ptr.add(4) as u32, | ||
| 102 | soff: 4, | ||
| 103 | attr: 0x0202, // 32-bit src/dst | ||
| 104 | nbytes: 8 * 4, | ||
| 105 | slast: 0, | ||
| 106 | daddr: dst_ptr.add(4) as u32, | ||
| 107 | doff: 4, | ||
| 108 | citer: 1, | ||
| 109 | dlast_sga: 0, | ||
| 110 | // ESG (scatter/gather) for non-last, INTMAJOR for all | ||
| 111 | csr: 0x0002, | ||
| 112 | biter: 1, | ||
| 113 | }; | ||
| 114 | |||
| 115 | // Load TCD0 into hardware registers | ||
| 116 | dma_ch0.load_tcd(&tcds[0]); | ||
| 117 | } | ||
| 118 | |||
| 119 | defmt::info!("Triggering first half transfer..."); | ||
| 120 | |||
| 121 | let tcd = dma_ch0.tcd(); | ||
| 122 | |||
| 123 | // Trigger first transfer (first half: SRC[0..4] -> DST[0..4]) | ||
| 124 | // TCD0 is currently loaded. | ||
| 125 | unsafe { | ||
| 126 | dma_ch0.trigger_start(); | ||
| 127 | } | ||
| 128 | |||
| 129 | // Wait for first half | ||
| 130 | loop { | ||
| 131 | if tcd.tcd_saddr().read().bits() != src_ptr as u32 { | ||
| 132 | defmt::info!("saddr: {=u32}", tcd.tcd_saddr().read().bits()); | ||
| 133 | defmt::info!("srptr: {=u32}", src_ptr as u32); | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | defmt::info!("First half transferred."); | ||
| 139 | defmt::info!("Triggering second half transfer..."); | ||
| 140 | |||
| 141 | // Trigger second transfer (second half: SRC[4..8] -> DST[4..8]) | ||
| 142 | // TCD1 should have been loaded by the scatter/gather engine. | ||
| 143 | unsafe { | ||
| 144 | dma_ch0.trigger_start(); | ||
| 145 | } | ||
| 146 | |||
| 147 | // Wait for second half | ||
| 148 | while !dma_ch0.is_done() { | ||
| 149 | cortex_m::asm::nop(); | ||
| 150 | } | ||
| 151 | |||
| 152 | defmt::info!("Second half transferred."); | ||
| 153 | |||
| 154 | defmt::info!("EDMA scatter-gather transfer example finish."); | ||
| 155 | defmt::info!("Destination Buffer (after): {=[?]}", dst.as_slice()); | ||
| 156 | |||
| 157 | // Verify: DST should match SRC | ||
| 158 | let mismatch = src != dst; | ||
| 159 | |||
| 160 | if mismatch { | ||
| 161 | defmt::error!("FAIL: Mismatch detected!"); | ||
| 162 | } else { | ||
| 163 | defmt::info!("PASS: Data verified."); | ||
| 164 | } | ||
| 165 | } | ||
diff --git a/examples/mcxa/src/bin/reset-reason.rs b/examples/mcxa/src/bin/reset-reason.rs new file mode 100644 index 000000000..c244fbe04 --- /dev/null +++ b/examples/mcxa/src/bin/reset-reason.rs | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use hal::config::Config; | ||
| 6 | use hal::reset_reason::reset_reason; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | #[embassy_executor::main] | ||
| 10 | async fn main(_spawner: Spawner) { | ||
| 11 | let config = Config::default(); | ||
| 12 | let _p = hal::init(config); | ||
| 13 | |||
| 14 | defmt::info!("Reset Reason: '{}'", reset_reason()); | ||
| 15 | } | ||
diff --git a/examples/mcxa/src/bin/rtc_alarm.rs b/examples/mcxa/src/bin/rtc_alarm.rs new file mode 100644 index 000000000..fe355888b --- /dev/null +++ b/examples/mcxa/src/bin/rtc_alarm.rs | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use embassy_mcxa::bind_interrupts; | ||
| 6 | use hal::rtc::{InterruptHandler, Rtc, RtcDateTime}; | ||
| 7 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 8 | |||
| 9 | bind_interrupts!(struct Irqs { | ||
| 10 | RTC => InterruptHandler<hal::rtc::Rtc0>; | ||
| 11 | }); | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) { | ||
| 15 | let p = hal::init(hal::config::Config::default()); | ||
| 16 | |||
| 17 | defmt::info!("=== RTC Alarm Example ==="); | ||
| 18 | |||
| 19 | let rtc_config = hal::rtc::get_default_config(); | ||
| 20 | |||
| 21 | let mut rtc = Rtc::new(p.RTC0, Irqs, rtc_config); | ||
| 22 | |||
| 23 | let now = RtcDateTime { | ||
| 24 | year: 2025, | ||
| 25 | month: 10, | ||
| 26 | day: 15, | ||
| 27 | hour: 14, | ||
| 28 | minute: 30, | ||
| 29 | second: 0, | ||
| 30 | }; | ||
| 31 | |||
| 32 | rtc.stop(); | ||
| 33 | |||
| 34 | defmt::info!("Time set to: 2025-10-15 14:30:00"); | ||
| 35 | rtc.set_datetime(now); | ||
| 36 | |||
| 37 | let mut alarm = now; | ||
| 38 | alarm.second += 10; | ||
| 39 | |||
| 40 | defmt::info!("Alarm set for: 2025-10-15 14:30:10 (+10 seconds)"); | ||
| 41 | defmt::info!("RTC started, waiting for alarm..."); | ||
| 42 | |||
| 43 | rtc.wait_for_alarm(alarm).await; | ||
| 44 | defmt::info!("*** ALARM TRIGGERED! ***"); | ||
| 45 | |||
| 46 | defmt::info!("Example complete - Test PASSED!"); | ||
| 47 | } | ||
diff --git a/examples/mcxa/src/bin/trng.rs b/examples/mcxa/src/bin/trng.rs new file mode 100644 index 000000000..5f6e2408c --- /dev/null +++ b/examples/mcxa/src/bin/trng.rs | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use embassy_executor::Spawner; | ||
| 5 | use hal::bind_interrupts; | ||
| 6 | use hal::config::Config; | ||
| 7 | use hal::trng::{self, InterruptHandler, Trng}; | ||
| 8 | use rand_core::RngCore; | ||
| 9 | use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; | ||
| 10 | |||
| 11 | bind_interrupts!( | ||
| 12 | struct Irqs { | ||
| 13 | TRNG0 => InterruptHandler; | ||
| 14 | } | ||
| 15 | ); | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let config = Config::default(); | ||
| 20 | let mut p = hal::init(config); | ||
| 21 | |||
| 22 | defmt::info!("TRNG example"); | ||
| 23 | |||
| 24 | let mut trng = Trng::new_blocking_128(p.TRNG0.reborrow()); | ||
| 25 | let rand = trng.blocking_next_u32(); | ||
| 26 | defmt::info!("128-bit {}", rand); | ||
| 27 | |||
| 28 | drop(trng); | ||
| 29 | |||
| 30 | let mut trng = Trng::new_blocking_256(p.TRNG0.reborrow()); | ||
| 31 | let rand = trng.blocking_next_u32(); | ||
| 32 | defmt::info!("256-bit {}", rand); | ||
| 33 | |||
| 34 | drop(trng); | ||
| 35 | |||
| 36 | let mut trng = Trng::new_blocking_512(p.TRNG0.reborrow()); | ||
| 37 | let rand = trng.blocking_next_u32(); | ||
| 38 | defmt::info!("512-bit {}", rand); | ||
| 39 | |||
| 40 | drop(trng); | ||
| 41 | |||
| 42 | let config = trng::Config::default(); | ||
| 43 | let mut trng = Trng::new_blocking_with_custom_config(p.TRNG0.reborrow(), config); | ||
| 44 | |||
| 45 | defmt::info!("========== BLOCKING =========="); | ||
| 46 | |||
| 47 | defmt::info!("Generate 10 u32"); | ||
| 48 | for _ in 0..10 { | ||
| 49 | let rand = trng.blocking_next_u32(); | ||
| 50 | defmt::info!("{}", rand); | ||
| 51 | } | ||
| 52 | |||
| 53 | defmt::info!("Generate 10 u64"); | ||
| 54 | for _ in 0..10 { | ||
| 55 | let rand = trng.blocking_next_u64(); | ||
| 56 | defmt::info!("{}", rand); | ||
| 57 | } | ||
| 58 | |||
| 59 | let mut buf = [0_u8; 256]; | ||
| 60 | |||
| 61 | defmt::info!("Generate 10 256-byte buffers"); | ||
| 62 | for _ in 0..10 { | ||
| 63 | trng.blocking_fill_bytes(&mut buf); | ||
| 64 | defmt::info!("{:02x}", buf); | ||
| 65 | } | ||
| 66 | |||
| 67 | defmt::info!("RngCore"); | ||
| 68 | |||
| 69 | for _ in 0..10 { | ||
| 70 | defmt::info!("u32: {}", trng.next_u32()); | ||
| 71 | defmt::info!("u64: {}", trng.next_u64()); | ||
| 72 | } | ||
| 73 | |||
| 74 | drop(trng); | ||
| 75 | |||
| 76 | defmt::info!("========== ASYNC =========="); | ||
| 77 | |||
| 78 | let mut trng = Trng::new_with_custom_config(p.TRNG0.reborrow(), Irqs, config); | ||
| 79 | |||
| 80 | defmt::info!("Generate 10 u32"); | ||
| 81 | for _ in 0..10 { | ||
| 82 | let rand = trng.async_next_u32().await.unwrap(); | ||
| 83 | defmt::info!("{}", rand); | ||
| 84 | } | ||
| 85 | |||
| 86 | defmt::info!("Generate 10 u64"); | ||
| 87 | for _ in 0..10 { | ||
| 88 | let rand = trng.async_next_u64().await.unwrap(); | ||
| 89 | defmt::info!("{}", rand); | ||
| 90 | } | ||
| 91 | |||
| 92 | let mut buf = [0_u8; 256]; | ||
| 93 | |||
| 94 | defmt::info!("Generate 10 256-byte buffers"); | ||
| 95 | for _ in 0..10 { | ||
| 96 | trng.async_fill_bytes(&mut buf).await.unwrap(); | ||
| 97 | defmt::info!("{:02x}", buf); | ||
| 98 | } | ||
| 99 | |||
| 100 | defmt::info!("RngCore"); | ||
| 101 | |||
| 102 | for _ in 0..10 { | ||
| 103 | defmt::info!("u32: {}", trng.next_u32()); | ||
| 104 | defmt::info!("u64: {}", trng.next_u64()); | ||
| 105 | } | ||
| 106 | } | ||
