From 1a12942f530df6b3dbd316ca29daf0b9d83ec36d Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Fri, 16 May 2025 23:22:34 +0200 Subject: embassy-rp (rp2040): Rtc wait_for_alarm --- examples/rp/src/bin/rtc.rs | 8 ++++- examples/rp/src/bin/rtc_alarm.rs | 69 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 examples/rp/src/bin/rtc_alarm.rs (limited to 'examples') diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs index e9a5e43a8..1692bdf36 100644 --- a/examples/rp/src/bin/rtc.rs +++ b/examples/rp/src/bin/rtc.rs @@ -5,16 +5,22 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; +// Bind the RTC interrupt to the handler +bind_interrupts!(struct Irqs { + RTC_IRQ => embassy_rp::rtc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); info!("Wait for 20s"); - let mut rtc = Rtc::new(p.RTC); + let mut rtc = Rtc::new(p.RTC, Irqs); if !rtc.is_running() { info!("Start RTC"); diff --git a/examples/rp/src/bin/rtc_alarm.rs b/examples/rp/src/bin/rtc_alarm.rs new file mode 100644 index 000000000..83421014f --- /dev/null +++ b/examples/rp/src/bin/rtc_alarm.rs @@ -0,0 +1,69 @@ +//! This example shows how to use RTC (Real Time Clock) for scheduling alarms and reacting to them. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::select::{select, Either}; +use embassy_rp::bind_interrupts; +use embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +// Bind the RTC interrupt to the handler +bind_interrupts!(struct Irqs { + RTC_IRQ => embassy_rp::rtc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rtc = Rtc::new(p.RTC, Irqs); + + if !rtc.is_running() { + info!("Start RTC"); + let now = DateTime { + year: 2000, + month: 1, + day: 1, + day_of_week: DayOfWeek::Saturday, + hour: 0, + minute: 0, + second: 0, + }; + rtc.set_datetime(now).unwrap(); + } + + loop { + // Wait for 5 seconds or until the alarm is triggered + match select(Timer::after_secs(5), rtc.wait_for_alarm()).await { + // Timer expired + Either::First(_) => { + let dt = rtc.now().unwrap(); + info!( + "Now: {}-{:02}-{:02} {}:{:02}:{:02}", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, + ); + + // See if the alarm is already scheduled, if not, schedule it + match rtc.alarm_scheduled() { + None => { + info!("Scheduling alarm for 30 seconds from now"); + let next_30s = if dt.second == 59 { 0 } else { dt.second + 30 }; + let dtf = DateTimeFilter::default().second(next_30s); + + rtc.schedule_alarm(dtf); + + info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap()); + } + Some(_) => {} + } + } + // Alarm triggered + Either::Second(_) => { + info!("ALARM TRIGGERED!"); + } + } + } +} -- cgit From eb685574601d98c44faed9a3534d056199b46e20 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Tue, 20 May 2025 15:55:31 +0200 Subject: simplify alarm scheduling logic in RTC example --- examples/rp/src/bin/rtc_alarm.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/rp/src/bin/rtc_alarm.rs b/examples/rp/src/bin/rtc_alarm.rs index 83421014f..dccf911e3 100644 --- a/examples/rp/src/bin/rtc_alarm.rs +++ b/examples/rp/src/bin/rtc_alarm.rs @@ -50,10 +50,7 @@ async fn main(_spawner: Spawner) { match rtc.alarm_scheduled() { None => { info!("Scheduling alarm for 30 seconds from now"); - let next_30s = if dt.second == 59 { 0 } else { dt.second + 30 }; - let dtf = DateTimeFilter::default().second(next_30s); - - rtc.schedule_alarm(dtf); + rtc.schedule_alarm(DateTimeFilter::default().second((dt.second + 30) % 60)); info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap()); } -- cgit From 8965a13da4149a6f1a56c5abcf879ff8ad822844 Mon Sep 17 00:00:00 2001 From: Magnus Nordlander Date: Tue, 5 Aug 2025 09:53:16 +0200 Subject: Interface changes and added example --- examples/rp235x/src/bin/psram.rs | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 examples/rp235x/src/bin/psram.rs (limited to 'examples') diff --git a/examples/rp235x/src/bin/psram.rs b/examples/rp235x/src/bin/psram.rs new file mode 100644 index 000000000..c0e41dd9e --- /dev/null +++ b/examples/rp235x/src/bin/psram.rs @@ -0,0 +1,49 @@ +//! This example tests an APS6404L PSRAM chip connected to the RP235x +//! It fills the PSRAM with alternating patterns and reads back a value +//! +//! In this example, the PSRAM CS is connected to Pin 0. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; +use core::slice; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let config = embassy_rp::config::Config::default(); + let p = embassy_rp::init(config); + let psram_config = embassy_rp::psram::Config::aps6404l(); + let psram = embassy_rp::psram::Psram::new(embassy_rp::qmi_cs1::QmiCs1::new(p.QMI_CS1, p.PIN_0), psram_config); + + let Ok(psram) = psram else { + error!("PSRAM not found"); + loop { + Timer::after_secs(1).await; + }; + }; + + let psram_slice = unsafe { + let psram_ptr = psram.base_address(); + let slice: &'static mut [u8] = + slice::from_raw_parts_mut(psram_ptr, psram.size() as usize); + slice + }; + + loop { + psram_slice.fill(0x55); + info!("PSRAM filled with 0x55"); + let at_addr = psram_slice[0x100]; + info!("Read from PSRAM at address 0x100: 0x{:02x}", at_addr); + Timer::after_secs(1).await; + + psram_slice.fill(0xAA); + info!("PSRAM filled with 0xAA"); + let at_addr = psram_slice[0x100]; + info!("Read from PSRAM at address 0x100: 0x{:02x}", at_addr); + Timer::after_secs(1).await; + } +} -- cgit From 1e918331184f6fb11c08e5c5c7019d50452239dc Mon Sep 17 00:00:00 2001 From: Magnus Nordlander Date: Tue, 5 Aug 2025 09:58:33 +0200 Subject: Apply rustfmt --- examples/rp235x/src/bin/psram.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/rp235x/src/bin/psram.rs b/examples/rp235x/src/bin/psram.rs index c0e41dd9e..b2ddf91c9 100644 --- a/examples/rp235x/src/bin/psram.rs +++ b/examples/rp235x/src/bin/psram.rs @@ -6,11 +6,11 @@ #![no_std] #![no_main] +use core::slice; use defmt::*; use embassy_executor::Spawner; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -use core::slice; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -23,13 +23,12 @@ async fn main(_spawner: Spawner) { error!("PSRAM not found"); loop { Timer::after_secs(1).await; - }; + } }; let psram_slice = unsafe { let psram_ptr = psram.base_address(); - let slice: &'static mut [u8] = - slice::from_raw_parts_mut(psram_ptr, psram.size() as usize); + let slice: &'static mut [u8] = slice::from_raw_parts_mut(psram_ptr, psram.size() as usize); slice }; -- cgit From 0e913319f240a96cef43fd0662f1759fbca8ac07 Mon Sep 17 00:00:00 2001 From: Magnus Nordlander Date: Tue, 5 Aug 2025 09:59:34 +0200 Subject: Manual rustfmt fix --- examples/rp235x/src/bin/psram.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'examples') diff --git a/examples/rp235x/src/bin/psram.rs b/examples/rp235x/src/bin/psram.rs index b2ddf91c9..716ac7695 100644 --- a/examples/rp235x/src/bin/psram.rs +++ b/examples/rp235x/src/bin/psram.rs @@ -7,6 +7,7 @@ #![no_main] use core::slice; + use defmt::*; use embassy_executor::Spawner; use embassy_time::Timer; -- cgit From 47e45c982126d52559353308ebe328301ebbf1c6 Mon Sep 17 00:00:00 2001 From: Florian Grandel Date: Thu, 3 Apr 2025 22:46:35 +0200 Subject: rtos-trace: upgraded feature support Upgrade rtos-trace for start/stop and marker support. These methods are not used in embassy code but can be useful in client code. Signed-off-by: Florian Grandel --- examples/nrf-rtos-trace/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index a2dc0c7ad..c9eeaaac7 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -25,8 +25,8 @@ cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-sing cortex-m-rt = "0.7.0" panic-probe = "1.0.0" serde = { version = "1.0.136", default-features = false } -rtos-trace = "0.1.3" -systemview-target = { version = "0.1.2", features = ["callbacks-app", "callbacks-os", "log", "cortex-m"] } +rtos-trace = "0.2" +systemview-target = { version = "0.2", features = ["callbacks-app", "callbacks-os", "log", "cortex-m"] } log = { version = "0.4.17", optional = true } [[bin]] -- cgit From a548d7efe3ad963b95183d92c2841fde744cc373 Mon Sep 17 00:00:00 2001 From: Per Rosengren Date: Mon, 25 Aug 2025 23:05:30 +0200 Subject: Add Adc::new_with_clock() to configure analog clock Required on STM32WL with default HAL initialization. The function is only available for adc_g0, but all that have clock config should add implementations. --- examples/stm32g0/src/bin/adc.rs | 4 +-- examples/stm32g0/src/bin/adc_dma.rs | 4 +-- examples/stm32g0/src/bin/adc_oversampling.rs | 4 +-- examples/stm32wl/src/bin/adc.rs | 39 ++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 examples/stm32wl/src/bin/adc.rs (limited to 'examples') diff --git a/examples/stm32g0/src/bin/adc.rs b/examples/stm32g0/src/bin/adc.rs index 6c7f3b48a..7d8653ef2 100644 --- a/examples/stm32g0/src/bin/adc.rs +++ b/examples/stm32g0/src/bin/adc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::adc::{Adc, Clock, Presc, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -12,7 +12,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut adc = Adc::new(p.ADC1); + let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); adc.set_sample_time(SampleTime::CYCLES79_5); let mut pin = p.PA1; diff --git a/examples/stm32g0/src/bin/adc_dma.rs b/examples/stm32g0/src/bin/adc_dma.rs index d7515933c..8266a6d83 100644 --- a/examples/stm32g0/src/bin/adc_dma.rs +++ b/examples/stm32g0/src/bin/adc_dma.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; +use embassy_stm32::adc::{Adc, AdcChannel as _, Clock, Presc, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut adc = Adc::new(p.ADC1); + let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); let mut dma = p.DMA1_CH1; let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs index 9c5dd872a..bc49fac83 100644 --- a/examples/stm32g0/src/bin/adc_oversampling.rs +++ b/examples/stm32g0/src/bin/adc_oversampling.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::adc::{Adc, Clock, Presc, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Adc oversample test"); - let mut adc = Adc::new(p.ADC1); + let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); adc.set_sample_time(SampleTime::CYCLES1_5); let mut pin = p.PA1; diff --git a/examples/stm32wl/src/bin/adc.rs b/examples/stm32wl/src/bin/adc.rs new file mode 100644 index 000000000..118f02ae1 --- /dev/null +++ b/examples/stm32wl/src/bin/adc.rs @@ -0,0 +1,39 @@ +#![no_std] +#![no_main] + +use core::mem::MaybeUninit; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, CkModePclk, Clock, SampleTime}; +use embassy_stm32::SharedData; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA); + info!("Hello World!"); + + let mut adc = Adc::new_with_clock(p.ADC1, Clock::Sync { div: CkModePclk::DIV1 }); + adc.set_sample_time(SampleTime::CYCLES79_5); + let mut pin = p.PB2; + + let mut vrefint = adc.enable_vrefint(); + let vrefint_sample = adc.blocking_read(&mut vrefint); + let convert_to_millivolts = |sample| { + // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf + // 6.3.3 Embedded internal reference voltage + const VREFINT_MV: u32 = 1212; // mV + + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 + }; + + loop { + let v = adc.blocking_read(&mut pin); + info!("--> {} - {} mV", v, convert_to_millivolts(v)); + Timer::after_millis(100).await; + } +} -- cgit From 160ee7f8054b374ce4c14b6877e6a05f5a5614b7 Mon Sep 17 00:00:00 2001 From: Patrick Gansterer Date: Mon, 1 Sep 2025 17:26:41 +0200 Subject: stm32/l0: Add usb serial example --- examples/stm32l0/Cargo.toml | 2 + examples/stm32l0/src/bin/usb_serial.rs | 95 ++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 examples/stm32l0/src/bin/usb_serial.rs (limited to 'examples') diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 90f57a2d8..d42cdac15 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -11,6 +11,8 @@ embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [" embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } +embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/stm32l0/src/bin/usb_serial.rs b/examples/stm32l0/src/bin/usb_serial.rs new file mode 100644 index 000000000..fdb1aeb59 --- /dev/null +++ b/examples/stm32l0/src/bin/usb_serial.rs @@ -0,0 +1,95 @@ +#![no_std] +#![no_main] + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_stm32::usb::{self, Driver, Instance}; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + USB => usb::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = true; + config.rcc.pll = Some(Pll { + source: PllSource::HSI, + mul: PllMul::MUL6, // PLLVCO = 16*6 = 96Mhz + div: PllDiv::DIV3, // 32Mhz clock (16 * 6 / 3) + }); + config.rcc.sys = Sysclk::PLL1_R; + } + + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); + + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-Serial Example"); + config.serial_number = Some("123456"); + + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut config_descriptor, + &mut bos_descriptor, + &mut [], // no msos descriptors + &mut control_buf, + ); + + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + let mut usb = builder.build(); + + let usb_fut = usb.run(); + + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} -- cgit From d1429868cefa319217089f6501cbafa503d6d6ba Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 6 Dec 2024 11:34:30 +0100 Subject: nrf: add NFCT NDEF example. --- examples/nrf52840/src/bin/nfct.rs | 274 +++++++++++++++++++++++++++++++++++--- 1 file changed, 259 insertions(+), 15 deletions(-) (limited to 'examples') diff --git a/examples/nrf52840/src/bin/nfct.rs b/examples/nrf52840/src/bin/nfct.rs index d559d006a..0b128c3bd 100644 --- a/examples/nrf52840/src/bin/nfct.rs +++ b/examples/nrf52840/src/bin/nfct.rs @@ -1,11 +1,12 @@ #![no_std] #![no_main] -use defmt::*; +use defmt::{todo, *}; use embassy_executor::Spawner; use embassy_nrf::config::HfclkSource; use embassy_nrf::nfct::{Config as NfcConfig, NfcId, NfcT}; use embassy_nrf::{bind_interrupts, nfct}; +use iso14443_4::{Card, IsoDep}; use {defmt_rtt as _, embassy_nrf as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -30,12 +31,28 @@ async fn main(_spawner: Spawner) { let mut buf = [0u8; 256]; + let cc = &[ + 0x00, 0x0f, /* CCEN_HI, CCEN_LOW */ + 0x20, /* VERSION */ + 0x00, 0x7f, /* MLe_HI, MLe_LOW */ + 0x00, 0x7f, /* MLc_HI, MLc_LOW */ + /* TLV */ + 0x04, 0x06, 0xe1, 0x04, 0x00, 0x7f, 0x00, 0x00, + ]; + + let ndef = &[ + 0x00, 0x10, 0xd1, 0x1, 0xc, 0x55, 0x4, 0x65, 0x6d, 0x62, 0x61, 0x73, 0x73, 0x79, 0x2e, 0x64, 0x65, 0x76, + ]; + let mut selected: &[u8] = cc; + loop { info!("activating"); nfc.activate().await; + info!("activated!"); + + let mut nfc = IsoDep::new(iso14443_3::Logger(&mut nfc)); loop { - info!("rxing"); let n = match nfc.receive(&mut buf).await { Ok(n) => n, Err(e) => { @@ -44,25 +61,51 @@ async fn main(_spawner: Spawner) { } }; let req = &buf[..n]; - info!("received frame {:02x}", req); + info!("iso-dep rx {:02x}", req); - let mut deselect = false; - let resp = match req { - [0xe0, ..] => { - info!("Got RATS, tx'ing ATS"); - &[0x06, 0x77, 0x77, 0x81, 0x02, 0x80][..] + let Ok(apdu) = Apdu::parse(req) else { + error!("apdu parse error"); + break; + }; + + info!("apdu: {:?}", apdu); + + let resp = match (apdu.cla, apdu.ins, apdu.p1, apdu.p2) { + (0, 0xa4, 4, 0) => { + info!("select app"); + &[0x90, 0x00][..] } - [0xc2] => { - info!("Got deselect!"); - deselect = true; - &[0xc2] + (0, 0xa4, 0, 12) => { + info!("select df"); + match apdu.data { + [0xe1, 0x03] => { + selected = cc; + &[0x90, 0x00][..] + } + [0xe1, 0x04] => { + selected = ndef; + &[0x90, 0x00][..] + } + _ => todo!(), // return NOT FOUND + } + } + (0, 0xb0, p1, p2) => { + info!("read"); + let offs = u16::from_be_bytes([p1 & 0xef, p2]) as usize; + let len = if apdu.le == 0 { usize::MAX } else { apdu.le as usize }; + let n = len.min(selected.len() - offs); + buf[..n].copy_from_slice(&selected[offs..][..n]); + buf[n..][..2].copy_from_slice(&[0x90, 0x00]); + &buf[..n + 2] } _ => { info!("Got unknown command!"); - &[0xFF] + &[0xFF, 0xFF] } }; + info!("iso-dep tx {:02x}", resp); + match nfc.transmit(resp).await { Ok(()) => {} Err(e) => { @@ -70,10 +113,211 @@ async fn main(_spawner: Spawner) { break; } } + } + } +} - if deselect { - break; +#[derive(Debug, Clone, defmt::Format)] +struct Apdu<'a> { + pub cla: u8, + pub ins: u8, + pub p1: u8, + pub p2: u8, + pub data: &'a [u8], + pub le: u16, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)] +struct ApduParseError; + +impl<'a> Apdu<'a> { + pub fn parse(apdu: &'a [u8]) -> Result { + if apdu.len() < 4 { + return Err(ApduParseError); + } + + let (data, le) = match apdu.len() - 4 { + 0 => (&[][..], 0), + 1 => (&[][..], apdu[4]), + n if n == 1 + apdu[4] as usize && apdu[4] != 0 => (&apdu[5..][..apdu[4] as usize], 0), + n if n == 2 + apdu[4] as usize && apdu[4] != 0 => (&apdu[5..][..apdu[4] as usize], apdu[apdu.len() - 1]), + _ => return Err(ApduParseError), + }; + + Ok(Apdu { + cla: apdu[0], + ins: apdu[1], + p1: apdu[2], + p2: apdu[3], + data, + le: le as _, + }) + } +} + +mod iso14443_3 { + use core::future::Future; + + use defmt::info; + use embassy_nrf::nfct::{Error, NfcT}; + + pub trait Card { + type Error; + async fn receive(&mut self, buf: &mut [u8]) -> Result; + async fn transmit(&mut self, buf: &[u8]) -> Result<(), Self::Error>; + } + + impl<'a, T: Card> Card for &'a mut T { + type Error = T::Error; + + fn receive(&mut self, buf: &mut [u8]) -> impl Future> { + T::receive(self, buf) + } + + fn transmit(&mut self, buf: &[u8]) -> impl Future> { + T::transmit(self, buf) + } + } + + impl<'a> Card for NfcT<'a> { + type Error = Error; + + fn receive(&mut self, buf: &mut [u8]) -> impl Future> { + self.receive(buf) + } + + fn transmit(&mut self, buf: &[u8]) -> impl Future> { + self.transmit(buf) + } + } + + pub struct Logger(pub T); + + impl Card for Logger { + type Error = T::Error; + + async fn receive(&mut self, buf: &mut [u8]) -> Result { + let n = T::receive(&mut self.0, buf).await?; + info!("<- {:02x}", &buf[..n]); + Ok(n) + } + + fn transmit(&mut self, buf: &[u8]) -> impl Future> { + info!("-> {:02x}", buf); + T::transmit(&mut self.0, buf) + } + } +} + +mod iso14443_4 { + use defmt::info; + + use crate::iso14443_3; + + #[derive(defmt::Format)] + pub enum Error { + Deselected, + Protocol, + Lower(T), + } + + pub trait Card { + type Error; + async fn receive(&mut self, buf: &mut [u8]) -> Result; + async fn transmit(&mut self, buf: &[u8]) -> Result<(), Self::Error>; + } + + pub struct IsoDep { + nfc: T, + + /// Block count spin bit: 0 or 1 + block_num: u8, + + /// true if deselected. This is permanent, you must create another IsoDep + /// instance if we get selected again. + deselected: bool, + + /// last response, in case we need to retransmit. + resp: [u8; 256], + resp_len: usize, + } + + impl IsoDep { + pub fn new(nfc: T) -> Self { + Self { + nfc, + block_num: 1, + deselected: false, + resp: [0u8; 256], + resp_len: 0, } } } + + impl Card for IsoDep { + type Error = Error; + + async fn receive(&mut self, buf: &mut [u8]) -> Result { + if self.deselected { + return Err(Error::Deselected); + } + + let mut temp = [0u8; 256]; + + loop { + let n = self.nfc.receive(&mut temp).await.map_err(Error::Lower)?; + assert!(n != 0); + match temp[0] { + 0x02 | 0x03 => { + self.block_num ^= 0x01; + assert!(temp[0] == 0x02 | self.block_num); + buf[..n - 1].copy_from_slice(&temp[1..n]); + return Ok(n - 1); + } + 0xb2 | 0xb3 => { + if temp[0] & 0x01 != self.block_num { + info!("Got NAK, transmitting ACK."); + let resp = &[0xA2 | self.block_num]; + self.nfc.transmit(resp).await.map_err(Error::Lower)?; + } else { + info!("Got NAK, retransmitting."); + let resp: &[u8] = &self.resp[..self.resp_len]; + self.nfc.transmit(resp).await.map_err(Error::Lower)?; + } + } + 0xe0 => { + info!("Got RATS, tx'ing ATS"); + let resp = &[0x06, 0x77, 0x77, 0x81, 0x02, 0x80]; + self.nfc.transmit(resp).await.map_err(Error::Lower)?; + } + 0xc2 => { + info!("Got deselect!"); + self.deselected = true; + let resp = &[0xC2]; + self.nfc.transmit(resp).await.map_err(Error::Lower)?; + return Err(Error::Deselected); + } + _ => { + info!("Got unknown command {:02x}!", temp[0]); + return Err(Error::Protocol); + } + }; + } + } + + async fn transmit(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + if self.deselected { + return Err(Error::Deselected); + } + + self.resp[0] = 0x02 | self.block_num; + self.resp[1..][..buf.len()].copy_from_slice(buf); + self.resp_len = 1 + buf.len(); + + let resp: &[u8] = &self.resp[..self.resp_len]; + self.nfc.transmit(resp).await.map_err(Error::Lower)?; + + Ok(()) + } + } } -- cgit From 9ac8944478d43693da7dce41762c92e9d5777e06 Mon Sep 17 00:00:00 2001 From: wackazong Date: Fri, 10 Jan 2025 11:50:40 +0100 Subject: Fix offset calculation --- examples/nrf52840/src/bin/nfct.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/nrf52840/src/bin/nfct.rs b/examples/nrf52840/src/bin/nfct.rs index 0b128c3bd..fafa37f48 100644 --- a/examples/nrf52840/src/bin/nfct.rs +++ b/examples/nrf52840/src/bin/nfct.rs @@ -91,7 +91,7 @@ async fn main(_spawner: Spawner) { } (0, 0xb0, p1, p2) => { info!("read"); - let offs = u16::from_be_bytes([p1 & 0xef, p2]) as usize; + let offs = u16::from_be_bytes([p1 & 0x7f, p2]) as usize; let len = if apdu.le == 0 { usize::MAX } else { apdu.le as usize }; let n = len.min(selected.len() - offs); buf[..n].copy_from_slice(&selected[offs..][..n]); -- cgit From 40eb5576824d45dfbe2a0609e69743a230475253 Mon Sep 17 00:00:00 2001 From: Adrian Figueroa Date: Mon, 25 Aug 2025 21:10:59 +0200 Subject: feat: SAI example --- examples/stm32h5/src/bin/sai.rs | 53 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 examples/stm32h5/src/bin/sai.rs (limited to 'examples') diff --git a/examples/stm32h5/src/bin/sai.rs b/examples/stm32h5/src/bin/sai.rs new file mode 100644 index 000000000..086a847f7 --- /dev/null +++ b/examples/stm32h5/src/bin/sai.rs @@ -0,0 +1,53 @@ +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; + +use embassy_stm32::{sai, Config}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello world."); + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + + config.rcc.pll2 = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV16, + mul: PllMul::MUL32, + divp: Some(PllDiv::DIV16), // 8 MHz SAI clock + divq: None, + divr: None, + }); + + config.rcc.mux.sai1sel = mux::Saisel::PLL2_P; + } + let p = embassy_stm32::init(config); + + let mut write_buffer = [0u16; 1024]; + let (_, sai_b) = sai::split_subblocks(p.SAI1); + + let mut sai_b = sai::Sai::new_asynchronous( + sai_b, + p.PF8, + p.PE3, + p.PF9, + p.GPDMA1_CH0, + &mut write_buffer, + Default::default(), + ); + + // Populate arbitrary data. + let mut data = [0u16; 256]; + for (index, sample) in data.iter_mut().enumerate() { + *sample = index as u16; + } + + loop { + sai_b.write(&data).await.unwrap(); + } +} -- cgit From c487034dc707282497f4ff2450493a07f76d3027 Mon Sep 17 00:00:00 2001 From: Adrian Figueroa Date: Mon, 25 Aug 2025 21:21:54 +0200 Subject: style: formatting --- examples/stm32h5/src/bin/sai.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'examples') diff --git a/examples/stm32h5/src/bin/sai.rs b/examples/stm32h5/src/bin/sai.rs index 086a847f7..0e182f9cf 100644 --- a/examples/stm32h5/src/bin/sai.rs +++ b/examples/stm32h5/src/bin/sai.rs @@ -3,7 +3,6 @@ use defmt::info; use embassy_executor::Spawner; - use embassy_stm32::{sai, Config}; use {defmt_rtt as _, panic_probe as _}; -- cgit From 60b640bd977ac2d056061e0c0b7a497f815417f4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 25 Aug 2025 17:31:38 +0200 Subject: stm32/sai: update for new metapac, simplify cfgs. --- examples/stm32h7/src/bin/sai.rs | 72 ++--------------------------------- examples/stm32h723/src/bin/spdifrx.rs | 2 +- 2 files changed, 5 insertions(+), 69 deletions(-) (limited to 'examples') diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs index 01937593a..847b70c85 100644 --- a/examples/stm32h7/src/bin/sai.rs +++ b/examples/stm32h7/src/bin/sai.rs @@ -63,7 +63,7 @@ async fn main(_spawner: Spawner) { tx_config.tx_rx = TxRx::Transmitter; tx_config.sync_output = true; tx_config.clock_strobe = ClockStrobe::Falling; - tx_config.master_clock_divider = mclk_div; + tx_config.master_clock_divider = Some(mclk_div); tx_config.stereo_mono = StereoMono::Stereo; tx_config.data_size = DataSize::Data24; tx_config.bit_order = BitOrder::MsbFirst; @@ -119,71 +119,7 @@ async fn main(_spawner: Spawner) { } } -const fn mclk_div_from_u8(v: u8) -> MasterClockDivider { - match v { - 1 => MasterClockDivider::Div1, - 2 => MasterClockDivider::Div2, - 3 => MasterClockDivider::Div3, - 4 => MasterClockDivider::Div4, - 5 => MasterClockDivider::Div5, - 6 => MasterClockDivider::Div6, - 7 => MasterClockDivider::Div7, - 8 => MasterClockDivider::Div8, - 9 => MasterClockDivider::Div9, - 10 => MasterClockDivider::Div10, - 11 => MasterClockDivider::Div11, - 12 => MasterClockDivider::Div12, - 13 => MasterClockDivider::Div13, - 14 => MasterClockDivider::Div14, - 15 => MasterClockDivider::Div15, - 16 => MasterClockDivider::Div16, - 17 => MasterClockDivider::Div17, - 18 => MasterClockDivider::Div18, - 19 => MasterClockDivider::Div19, - 20 => MasterClockDivider::Div20, - 21 => MasterClockDivider::Div21, - 22 => MasterClockDivider::Div22, - 23 => MasterClockDivider::Div23, - 24 => MasterClockDivider::Div24, - 25 => MasterClockDivider::Div25, - 26 => MasterClockDivider::Div26, - 27 => MasterClockDivider::Div27, - 28 => MasterClockDivider::Div28, - 29 => MasterClockDivider::Div29, - 30 => MasterClockDivider::Div30, - 31 => MasterClockDivider::Div31, - 32 => MasterClockDivider::Div32, - 33 => MasterClockDivider::Div33, - 34 => MasterClockDivider::Div34, - 35 => MasterClockDivider::Div35, - 36 => MasterClockDivider::Div36, - 37 => MasterClockDivider::Div37, - 38 => MasterClockDivider::Div38, - 39 => MasterClockDivider::Div39, - 40 => MasterClockDivider::Div40, - 41 => MasterClockDivider::Div41, - 42 => MasterClockDivider::Div42, - 43 => MasterClockDivider::Div43, - 44 => MasterClockDivider::Div44, - 45 => MasterClockDivider::Div45, - 46 => MasterClockDivider::Div46, - 47 => MasterClockDivider::Div47, - 48 => MasterClockDivider::Div48, - 49 => MasterClockDivider::Div49, - 50 => MasterClockDivider::Div50, - 51 => MasterClockDivider::Div51, - 52 => MasterClockDivider::Div52, - 53 => MasterClockDivider::Div53, - 54 => MasterClockDivider::Div54, - 55 => MasterClockDivider::Div55, - 56 => MasterClockDivider::Div56, - 57 => MasterClockDivider::Div57, - 58 => MasterClockDivider::Div58, - 59 => MasterClockDivider::Div59, - 60 => MasterClockDivider::Div60, - 61 => MasterClockDivider::Div61, - 62 => MasterClockDivider::Div62, - 63 => MasterClockDivider::Div63, - _ => panic!(), - } +fn mclk_div_from_u8(v: u8) -> MasterClockDivider { + assert!((1..=63).contains(&v)); + MasterClockDivider::from_bits(v) } diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs index 6d29e8a4d..b75a03ae8 100644 --- a/examples/stm32h723/src/bin/spdifrx.rs +++ b/examples/stm32h723/src/bin/spdifrx.rs @@ -168,7 +168,7 @@ fn new_sai_transmitter<'d>( sai_config.slot_enable = 0xFFFF; // All slots sai_config.data_size = sai::DataSize::Data32; sai_config.frame_length = (CHANNEL_COUNT * 32) as u8; - sai_config.master_clock_divider = hal::sai::MasterClockDivider::MasterClockDisabled; + sai_config.master_clock_divider = None; let (sub_block_tx, _) = hal::sai::split_subblocks(sai); Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config) -- cgit From 236662c748eab43a701bf4f43570b191ec2c1158 Mon Sep 17 00:00:00 2001 From: Adrian Wowk Date: Wed, 2 Jul 2025 20:53:26 -0500 Subject: rp: add pio spi examples --- examples/rp/src/bin/pio_spi.rs | 63 ++++++++++++++++++++++++++++++++++ examples/rp/src/bin/pio_spi_async.rs | 65 ++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 examples/rp/src/bin/pio_spi.rs create mode 100644 examples/rp/src/bin/pio_spi_async.rs (limited to 'examples') diff --git a/examples/rp/src/bin/pio_spi.rs b/examples/rp/src/bin/pio_spi.rs new file mode 100644 index 000000000..1fbe235e5 --- /dev/null +++ b/examples/rp/src/bin/pio_spi.rs @@ -0,0 +1,63 @@ +//! This example shows how to use a PIO state machine as an additional SPI +//! (Serial Peripheral Interface) on the RP2040 chip. No specific hardware is +//! specified in this example. +//! +//! If you connect pin 6 and 7 you should get the same data back. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::{ + bind_interrupts, + peripherals::PIO0, + pio, + pio_programs::spi::{Config, PioSpiProgram, Spi}, + spi::Phase, +}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => pio::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + // These pins are routed to differnet hardware SPI peripherals, but we can + // use them together regardless + let mosi = p.PIN_6; // SPI0 SCLK + let miso = p.PIN_7; // SPI0 MOSI + let clk = p.PIN_8; // SPI1 MISO + + let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); + + // The PIO program must be configured with the clock phase + let program = PioSpiProgram::new(&mut common, Phase::CaptureOnFirstTransition); + + // Construct an SPI driver backed by a PIO state machine + let mut spi = Spi::new_blocking( + &mut common, + sm0, + clk, + mosi, + miso, + &program, + // Only the frequency and polarity are set here + Config::default(), + ); + + loop { + let tx_buf = [1_u8, 2, 3, 4, 5, 6]; + let mut rx_buf = [0_u8; 6]; + + spi.blocking_transfer(&mut rx_buf, &tx_buf).unwrap(); + info!("{:?}", rx_buf); + + Timer::after_secs(1).await; + } +} diff --git a/examples/rp/src/bin/pio_spi_async.rs b/examples/rp/src/bin/pio_spi_async.rs new file mode 100644 index 000000000..5abf6fc71 --- /dev/null +++ b/examples/rp/src/bin/pio_spi_async.rs @@ -0,0 +1,65 @@ +//! This example shows how to use a PIO state machine as an additional SPI +//! (Serial Peripheral Interface) on the RP2040 chip. No specific hardware is +//! specified in this example. +//! +//! If you connect pin 6 and 7 you should get the same data back. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::{ + bind_interrupts, + peripherals::PIO0, + pio, + pio_programs::spi::{Config, PioSpiProgram, Spi}, + spi::Phase, +}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => pio::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + // These pins are routed to differnet hardware SPI peripherals, but we can + // use them together regardless + let mosi = p.PIN_6; // SPI0 SCLK + let miso = p.PIN_7; // SPI0 MOSI + let clk = p.PIN_8; // SPI1 MISO + + let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); + + // The PIO program must be configured with the clock phase + let program = PioSpiProgram::new(&mut common, Phase::CaptureOnFirstTransition); + + // Construct an SPI driver backed by a PIO state machine + let mut spi = Spi::new( + &mut common, + sm0, + clk, + mosi, + miso, + p.DMA_CH0, + p.DMA_CH1, + &program, + // Only the frequency and polarity are set here + Config::default(), + ); + + loop { + let tx_buf = [1_u8, 2, 3, 4, 5, 6]; + let mut rx_buf = [0_u8; 6]; + + spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); + info!("{:?}", rx_buf); + + Timer::after_secs(1).await; + } +} -- cgit From 83b42e0db620b7fc2364763b452b346b711e8d9f Mon Sep 17 00:00:00 2001 From: Adrian Wowk Date: Wed, 2 Jul 2025 21:18:44 -0500 Subject: style: cleanup with rustfmt --- examples/rp/src/bin/pio_spi.rs | 11 ++++------- examples/rp/src/bin/pio_spi_async.rs | 13 +++++-------- 2 files changed, 9 insertions(+), 15 deletions(-) (limited to 'examples') diff --git a/examples/rp/src/bin/pio_spi.rs b/examples/rp/src/bin/pio_spi.rs index 1fbe235e5..0164e4c81 100644 --- a/examples/rp/src/bin/pio_spi.rs +++ b/examples/rp/src/bin/pio_spi.rs @@ -9,13 +9,10 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::{ - bind_interrupts, - peripherals::PIO0, - pio, - pio_programs::spi::{Config, PioSpiProgram, Spi}, - spi::Phase, -}; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio_programs::spi::{Config, PioSpiProgram, Spi}; +use embassy_rp::spi::Phase; +use embassy_rp::{bind_interrupts, pio}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/pio_spi_async.rs b/examples/rp/src/bin/pio_spi_async.rs index 5abf6fc71..1dbdff609 100644 --- a/examples/rp/src/bin/pio_spi_async.rs +++ b/examples/rp/src/bin/pio_spi_async.rs @@ -9,13 +9,10 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::{ - bind_interrupts, - peripherals::PIO0, - pio, - pio_programs::spi::{Config, PioSpiProgram, Spi}, - spi::Phase, -}; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio_programs::spi::{Config, PioSpiProgram, Spi}; +use embassy_rp::spi::Phase; +use embassy_rp::{bind_interrupts, pio}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -32,7 +29,7 @@ async fn main(_spawner: Spawner) { // use them together regardless let mosi = p.PIN_6; // SPI0 SCLK let miso = p.PIN_7; // SPI0 MOSI - let clk = p.PIN_8; // SPI1 MISO + let clk = p.PIN_8; // SPI1 MISO let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); -- cgit From 62ff0194f4b7413b17dbc69813ec205638248aa7 Mon Sep 17 00:00:00 2001 From: Adrian Wowk Date: Fri, 18 Jul 2025 19:19:27 -0500 Subject: rp: add pio spi runtime reconfiguration --- examples/rp/src/bin/pio_spi.rs | 20 ++++---------------- examples/rp/src/bin/pio_spi_async.rs | 10 +++------- 2 files changed, 7 insertions(+), 23 deletions(-) (limited to 'examples') diff --git a/examples/rp/src/bin/pio_spi.rs b/examples/rp/src/bin/pio_spi.rs index 0164e4c81..4218327ec 100644 --- a/examples/rp/src/bin/pio_spi.rs +++ b/examples/rp/src/bin/pio_spi.rs @@ -10,8 +10,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio_programs::spi::{Config, PioSpiProgram, Spi}; -use embassy_rp::spi::Phase; +use embassy_rp::pio_programs::spi::Spi; +use embassy_rp::spi::Config; use embassy_rp::{bind_interrupts, pio}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); info!("Hello World!"); - // These pins are routed to differnet hardware SPI peripherals, but we can + // These pins are routed to different hardware SPI peripherals, but we can // use them together regardless let mosi = p.PIN_6; // SPI0 SCLK let miso = p.PIN_7; // SPI0 MOSI @@ -33,20 +33,8 @@ async fn main(_spawner: Spawner) { let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); - // The PIO program must be configured with the clock phase - let program = PioSpiProgram::new(&mut common, Phase::CaptureOnFirstTransition); - // Construct an SPI driver backed by a PIO state machine - let mut spi = Spi::new_blocking( - &mut common, - sm0, - clk, - mosi, - miso, - &program, - // Only the frequency and polarity are set here - Config::default(), - ); + let mut spi = Spi::new_blocking(&mut common, sm0, clk, mosi, miso, Config::default()); loop { let tx_buf = [1_u8, 2, 3, 4, 5, 6]; diff --git a/examples/rp/src/bin/pio_spi_async.rs b/examples/rp/src/bin/pio_spi_async.rs index 1dbdff609..74a2dd11b 100644 --- a/examples/rp/src/bin/pio_spi_async.rs +++ b/examples/rp/src/bin/pio_spi_async.rs @@ -10,8 +10,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio_programs::spi::{Config, PioSpiProgram, Spi}; -use embassy_rp::spi::Phase; +use embassy_rp::pio_programs::spi::Spi; +use embassy_rp::spi::Config; use embassy_rp::{bind_interrupts, pio}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); info!("Hello World!"); - // These pins are routed to differnet hardware SPI peripherals, but we can + // These pins are routed to different hardware SPI peripherals, but we can // use them together regardless let mosi = p.PIN_6; // SPI0 SCLK let miso = p.PIN_7; // SPI0 MOSI @@ -33,9 +33,6 @@ async fn main(_spawner: Spawner) { let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); - // The PIO program must be configured with the clock phase - let program = PioSpiProgram::new(&mut common, Phase::CaptureOnFirstTransition); - // Construct an SPI driver backed by a PIO state machine let mut spi = Spi::new( &mut common, @@ -46,7 +43,6 @@ async fn main(_spawner: Spawner) { p.DMA_CH0, p.DMA_CH1, &program, - // Only the frequency and polarity are set here Config::default(), ); -- cgit From 451625ff559661c0cc30ca8a70dd0ccee79ba07b Mon Sep 17 00:00:00 2001 From: Adrian Wowk Date: Fri, 18 Jul 2025 19:23:49 -0500 Subject: rp: fix pio spi async example --- examples/rp/src/bin/pio_spi_async.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'examples') diff --git a/examples/rp/src/bin/pio_spi_async.rs b/examples/rp/src/bin/pio_spi_async.rs index 74a2dd11b..18b57d26e 100644 --- a/examples/rp/src/bin/pio_spi_async.rs +++ b/examples/rp/src/bin/pio_spi_async.rs @@ -42,7 +42,6 @@ async fn main(_spawner: Spawner) { miso, p.DMA_CH0, p.DMA_CH1, - &program, Config::default(), ); -- cgit From 241129c569023dc71d0025cdc41bcfe40418e7b8 Mon Sep 17 00:00:00 2001 From: Thor McAvenia Date: Fri, 16 May 2025 00:09:10 -0700 Subject: Add PioI2sIn, PioI2sInProgram, and example binary --- examples/rp235x/src/bin/pio_i2s_rx.rs | 81 +++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 examples/rp235x/src/bin/pio_i2s_rx.rs (limited to 'examples') diff --git a/examples/rp235x/src/bin/pio_i2s_rx.rs b/examples/rp235x/src/bin/pio_i2s_rx.rs new file mode 100644 index 000000000..c3f505b13 --- /dev/null +++ b/examples/rp235x/src/bin/pio_i2s_rx.rs @@ -0,0 +1,81 @@ +//! This example shows receiving audio from a connected I2S microphone (or other audio source) +//! using the PIO module of the RP235x. +//! +//! +//! Connect the i2s microphone as follows: +//! bclk : GPIO 18 +//! lrc : GPIO 19 +//! din : GPIO 20 +//! Then hold down the boot select button to begin receiving audio. Received I2S words will be written to +//! buffers for the left and right channels for use in your application, whether that's storage or +//! further processing +//! +//! Note the const USE_ONBOARD_PULLDOWN is by default set to false, meaning an external +//! pull-down resistor is being used on the data pin if required by the mic being used. + +#![no_std] +#![no_main] +use core::mem; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::i2s::{PioI2sIn, PioI2sInProgram}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +const SAMPLE_RATE: u32 = 48_000; +const BIT_DEPTH: u32 = 16; +const CHANNELS: u32 = 2; +const USE_ONBOARD_PULLDOWN: bool = false; // whether or not to use the onboard pull-down resistor, + // which has documented issues on many RP235x boards +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + + // Setup pio state machine for i2s input + let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); + + let bit_clock_pin = p.PIN_18; + let left_right_clock_pin = p.PIN_19; + let data_pin = p.PIN_20; + + let program = PioI2sInProgram::new(&mut common); + let mut i2s = PioI2sIn::new( + &mut common, + sm0, + p.DMA_CH0, + USE_ONBOARD_PULLDOWN, + data_pin, + bit_clock_pin, + left_right_clock_pin, + SAMPLE_RATE, + BIT_DEPTH, + CHANNELS, + &program, + ); + + // create two audio buffers (back and front) which will take turns being + // filled with new audio data from the PIO fifo using DMA + const BUFFER_SIZE: usize = 960; + static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new(); + let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]); + let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); + + loop { + // trigger transfer of front buffer data to the pio fifo + // but don't await the returned future, yet + let dma_future = i2s.read(front_buffer); + // now await the dma future. once the dma finishes, the next buffer needs to be queued + // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us + dma_future.await; + info!("Received I2S data word: {:?}", &front_buffer); + mem::swap(&mut back_buffer, &mut front_buffer); + } +} -- cgit From 7419b398bf7cc5c1ff164c504f4a4027cd6bcd3b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 5 Sep 2025 23:00:31 +0200 Subject: stm32/afio: use type inference for timer remaps as well. --- examples/stm32f1/src/bin/input_capture.rs | 5 +++-- examples/stm32f1/src/bin/pwm_input.rs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/stm32f1/src/bin/input_capture.rs b/examples/stm32f1/src/bin/input_capture.rs index d747a43c2..b5b26938d 100644 --- a/examples/stm32f1/src/bin/input_capture.rs +++ b/examples/stm32f1/src/bin/input_capture.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::gpio::{AfioRemap, Level, Output, Pull, Speed}; use embassy_stm32::time::khz; use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; use embassy_stm32::timer::{self, Channel}; @@ -40,7 +40,8 @@ async fn main(spawner: Spawner) { spawner.spawn(unwrap!(blinky(p.PC13))); let ch3 = CapturePin::new(p.PA2, Pull::None); - let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); + let mut ic = + InputCapture::new::>(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); loop { info!("wait for rising edge"); diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index 63b899767..9ae747018 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::gpio::{AfioRemap, Level, Output, Pull, Speed}; use embassy_stm32::time::khz; use embassy_stm32::timer::pwm_input::PwmInput; use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; @@ -38,7 +38,7 @@ async fn main(spawner: Spawner) { spawner.spawn(unwrap!(blinky(p.PC13))); - let mut pwm_input = PwmInput::new_ch1(p.TIM2, p.PA0, Pull::None, khz(10)); + let mut pwm_input = PwmInput::new_ch1::>(p.TIM2, p.PA0, Pull::None, khz(10)); pwm_input.enable(); loop { -- cgit From 224d6b03125dd9c799611883914dd99c5431e749 Mon Sep 17 00:00:00 2001 From: Remmirad Date: Sat, 6 Sep 2025 11:32:23 +0200 Subject: nrf: 802.15.4 embassy-net-driver --- examples/nrf52840/Cargo.toml | 4 +- examples/nrf52840/src/bin/sixlowpan.rs | 120 +++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 examples/nrf52840/src/bin/sixlowpan.rs (limited to 'examples') diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index a9339bcd3..452e83b7e 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -10,8 +10,8 @@ embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } -embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } +embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time", "net-driver"] } +embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet","udp", "medium-ieee802154", "proto-ipv6"] } embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } diff --git a/examples/nrf52840/src/bin/sixlowpan.rs b/examples/nrf52840/src/bin/sixlowpan.rs new file mode 100644 index 000000000..00a597366 --- /dev/null +++ b/examples/nrf52840/src/bin/sixlowpan.rs @@ -0,0 +1,120 @@ +#![no_std] +#![no_main] + +use core::net::Ipv6Addr; + +use defmt::{info, unwrap, warn}; +use embassy_executor::Spawner; +use embassy_net::udp::{PacketMetadata, UdpMetadata, UdpSocket}; +use embassy_net::{IpAddress, IpEndpoint, IpListenEndpoint, Ipv6Cidr, StackResources, StaticConfigV6}; +use embassy_nrf::config::{Config, HfclkSource}; +use embassy_nrf::rng::Rng; +use embassy_nrf::{bind_interrupts, embassy_net_802154_driver as net, peripherals, radio}; +use embassy_time::Delay; +use embedded_hal_async::delay::DelayNs; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + RADIO => radio::InterruptHandler; + RNG => embassy_nrf::rng::InterruptHandler; +}); + +#[embassy_executor::task] +async fn ieee802154_task(runner: net::Runner<'static, peripherals::RADIO>) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(mut runner: embassy_net::Runner<'static, net::Device<'static>>) -> ! { + runner.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let mut config = Config::default(); + // Necessary to run the radio nrf52840 v1.11 5.4.1 + config.hfclk_source = HfclkSource::ExternalXtal; + let p = embassy_nrf::init(config); + + let mac_addr: [u8; 8] = [2, 3, 4, 5, 6, 7, 8, 9]; + static NRF802154_STATE: StaticCell> = StaticCell::new(); + let (device, runner) = net::new(mac_addr, p.RADIO, Irqs, NRF802154_STATE.init(net::State::new())) + .await + .unwrap(); + + spawner.spawn(unwrap!(ieee802154_task(runner))); + + // Swap these when flashing a second board + let peer = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xd701, 0xda3f, 0x3955, 0x82a4); + let local = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xd701, 0xda3f, 0x3955, 0x82a5); + + let config = embassy_net::Config::ipv6_static(StaticConfigV6 { + address: Ipv6Cidr::new(local, 64), + gateway: None, + dns_servers: Default::default(), + }); + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + rng.blocking_fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + static RESOURCES: StaticCell> = StaticCell::new(); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed); + + spawner.spawn(unwrap!(net_task(runner))); + + let mut rx_buffer = [0; 2096]; + let mut tx_buffer = [0; 2096]; + let mut tx_m_buffer = [PacketMetadata::EMPTY; 5]; + let mut rx_m_buffer = [PacketMetadata::EMPTY; 5]; + + let mut delay = Delay; + loop { + let mut socket = UdpSocket::new( + stack, + &mut tx_m_buffer, + &mut rx_buffer, + &mut rx_m_buffer, + &mut tx_buffer, + ); + socket + .bind(IpListenEndpoint { + addr: Some(IpAddress::Ipv6(local)), + port: 1234, + }) + .unwrap(); + let rep = UdpMetadata { + endpoint: IpEndpoint { + addr: IpAddress::Ipv6(peer), + port: 1234, + }, + local_address: Some(IpAddress::Ipv6(local)), + meta: Default::default(), + }; + + info!("Listening on {:?} UDP:1234...", local); + + let mut recv_buf = [0; 12]; + loop { + delay.delay_ms(2000).await; + if socket.may_recv() { + let n = match socket.recv_from(&mut recv_buf).await { + Ok((0, _)) => panic!(), + Ok((n, _)) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + info!("Received {:02x}", &recv_buf[..n]); + } + + info!("Sending"); + socket.send_to(b"Hello World", rep).await.unwrap(); + } + } +} -- cgit From bbcaab13bc074d8223b43d8e05682b708c192d78 Mon Sep 17 00:00:00 2001 From: crispaudio Date: Mon, 8 Sep 2025 09:10:16 +0200 Subject: mspm0-adc: add adc with examples --- examples/mspm0g3507/src/bin/adc.rs | 48 ++++++++++++++++++++++++++++++++++++++ examples/mspm0l1306/src/bin/adc.rs | 48 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 examples/mspm0g3507/src/bin/adc.rs create mode 100644 examples/mspm0l1306/src/bin/adc.rs (limited to 'examples') diff --git a/examples/mspm0g3507/src/bin/adc.rs b/examples/mspm0g3507/src/bin/adc.rs new file mode 100644 index 000000000..fed8b9dd3 --- /dev/null +++ b/examples/mspm0g3507/src/bin/adc.rs @@ -0,0 +1,48 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_mspm0::adc::{self, Adc, AdcChannel, AdcConfig, Resolution, Vrsel}; +use embassy_mspm0::{bind_interrupts, peripherals, Config}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_halt as _}; + +bind_interrupts!(struct Irqs { + ADC0 => adc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + info!("Hello world!"); + let p = embassy_mspm0::init(Config::default()); + + let adc_config = AdcConfig { + resolution: Resolution::BIT12, + vr_select: Vrsel::VddaVssa, + sample_time: 50, + }; + + // Configure adc with sequence 0 to 1 + let mut adc = Adc::new_async(p.ADC0, adc_config, Irqs); + let pin1 = p.PA22.degrade_adc(); + let pin2 = p.PB20.degrade_adc(); + let sequence = [(&pin1, Vrsel::VddaVssa), (&pin2, Vrsel::VddaVssa)]; + let mut readings = [0u16; 2]; + let mut pin3 = p.PA27; + + loop { + let r = adc.read_channel(&mut pin3).await; + info!("Raw adc PA27: {}", r); + // With a voltage range of 0-3.3V and a resolution of 12 bits, the raw value can be + // approximated to voltage (~0.0008 per step). + let mut x = r as u32; + x = x * 8; + info!("Adc voltage PA27: {},{:#04}", x / 10_000, x % 10_000); + // Read a sequence of channels + adc.read_sequence(sequence.into_iter(), &mut readings).await; + info!("Raw adc sequence: {}", readings); + + Timer::after_millis(400).await; + } +} diff --git a/examples/mspm0l1306/src/bin/adc.rs b/examples/mspm0l1306/src/bin/adc.rs new file mode 100644 index 000000000..9ede31fed --- /dev/null +++ b/examples/mspm0l1306/src/bin/adc.rs @@ -0,0 +1,48 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_mspm0::adc::{self, Adc, AdcChannel, AdcConfig, Resolution, Vrsel}; +use embassy_mspm0::{bind_interrupts, peripherals, Config}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_halt as _}; + +bind_interrupts!(struct Irqs { + ADC0 => adc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + info!("Hello world!"); + let p = embassy_mspm0::init(Config::default()); + + let adc_config = AdcConfig { + resolution: Resolution::BIT12, + vr_select: Vrsel::VddaVssa, + sample_time: 50, + }; + + // Configure adc with sequence 0 to 1 + let mut adc = Adc::new_async(p.ADC0, adc_config, Irqs); + let pin1 = p.PA22.degrade_adc(); + let pin2 = p.PA20.degrade_adc(); + let sequence = [(&pin1, Vrsel::VddaVssa), (&pin2, Vrsel::VddaVssa)]; + let mut readings = [0u16; 2]; + let mut pin3 = p.PA27; + + loop { + let r = adc.read_channel(&mut pin3).await; + info!("Raw adc PA27: {}", r); + // With a voltage range of 0-3.3V and a resolution of 12 bits, the raw value can be + // approximated to voltage (~0.0008 per step). + let mut x = r as u32; + x = x * 8; + info!("Adc voltage PA27: {},{:#04}", x / 10_000, x % 10_000); + // Read a sequence of channels + adc.read_sequence(sequence.into_iter(), &mut readings).await; + info!("Raw adc sequence: {}", readings); + + Timer::after_millis(400).await; + } +} -- cgit From 7b9fe7e6398a8bce236da904b251b4cb424150fb Mon Sep 17 00:00:00 2001 From: crispaudio Date: Tue, 9 Sep 2025 22:21:10 +0200 Subject: mspm0-adc: remove dynamic vrsel and cleanup --- examples/mspm0g3507/src/bin/adc.rs | 10 ++-------- examples/mspm0l1306/src/bin/adc.rs | 10 ++-------- 2 files changed, 4 insertions(+), 16 deletions(-) (limited to 'examples') diff --git a/examples/mspm0g3507/src/bin/adc.rs b/examples/mspm0g3507/src/bin/adc.rs index fed8b9dd3..73711c75c 100644 --- a/examples/mspm0g3507/src/bin/adc.rs +++ b/examples/mspm0g3507/src/bin/adc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::adc::{self, Adc, AdcChannel, AdcConfig, Resolution, Vrsel}; +use embassy_mspm0::adc::{self, Adc, AdcChannel, Vrsel}; use embassy_mspm0::{bind_interrupts, peripherals, Config}; use embassy_time::Timer; use {defmt_rtt as _, panic_halt as _}; @@ -17,14 +17,8 @@ async fn main(_spawner: Spawner) -> ! { info!("Hello world!"); let p = embassy_mspm0::init(Config::default()); - let adc_config = AdcConfig { - resolution: Resolution::BIT12, - vr_select: Vrsel::VddaVssa, - sample_time: 50, - }; - // Configure adc with sequence 0 to 1 - let mut adc = Adc::new_async(p.ADC0, adc_config, Irqs); + let mut adc = Adc::new_async(p.ADC0, Default::default(), Irqs); let pin1 = p.PA22.degrade_adc(); let pin2 = p.PB20.degrade_adc(); let sequence = [(&pin1, Vrsel::VddaVssa), (&pin2, Vrsel::VddaVssa)]; diff --git a/examples/mspm0l1306/src/bin/adc.rs b/examples/mspm0l1306/src/bin/adc.rs index 9ede31fed..a0c2c0cff 100644 --- a/examples/mspm0l1306/src/bin/adc.rs +++ b/examples/mspm0l1306/src/bin/adc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::adc::{self, Adc, AdcChannel, AdcConfig, Resolution, Vrsel}; +use embassy_mspm0::adc::{self, Adc, AdcChannel, Vrsel}; use embassy_mspm0::{bind_interrupts, peripherals, Config}; use embassy_time::Timer; use {defmt_rtt as _, panic_halt as _}; @@ -17,14 +17,8 @@ async fn main(_spawner: Spawner) -> ! { info!("Hello world!"); let p = embassy_mspm0::init(Config::default()); - let adc_config = AdcConfig { - resolution: Resolution::BIT12, - vr_select: Vrsel::VddaVssa, - sample_time: 50, - }; - // Configure adc with sequence 0 to 1 - let mut adc = Adc::new_async(p.ADC0, adc_config, Irqs); + let mut adc = Adc::new_async(p.ADC0, Default::default(), Irqs); let pin1 = p.PA22.degrade_adc(); let pin2 = p.PA20.degrade_adc(); let sequence = [(&pin1, Vrsel::VddaVssa), (&pin2, Vrsel::VddaVssa)]; -- cgit From 0cb1ffe0258380a3dd25c1037fdfb6ceca3149d9 Mon Sep 17 00:00:00 2001 From: James Munns Date: Wed, 16 Apr 2025 17:58:45 +0200 Subject: Add EDF example --- examples/nrf52840-edf/.cargo/config.toml | 9 ++ examples/nrf52840-edf/Cargo.toml | 21 ++++ examples/nrf52840-edf/build.rs | 35 ++++++ examples/nrf52840-edf/memory.x | 12 ++ examples/nrf52840-edf/src/bin/basic.rs | 191 +++++++++++++++++++++++++++++++ 5 files changed, 268 insertions(+) create mode 100644 examples/nrf52840-edf/.cargo/config.toml create mode 100644 examples/nrf52840-edf/Cargo.toml create mode 100644 examples/nrf52840-edf/build.rs create mode 100644 examples/nrf52840-edf/memory.x create mode 100644 examples/nrf52840-edf/src/bin/basic.rs (limited to 'examples') diff --git a/examples/nrf52840-edf/.cargo/config.toml b/examples/nrf52840-edf/.cargo/config.toml new file mode 100644 index 000000000..e0b9ce59e --- /dev/null +++ b/examples/nrf52840-edf/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip nRF52840_xxAA" + +[build] +target = "thumbv7em-none-eabi" + +[env] +DEFMT_LOG = "debug" diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml new file mode 100644 index 000000000..c7147d1af --- /dev/null +++ b/examples/nrf52840-edf/Cargo.toml @@ -0,0 +1,21 @@ +[package] +edition = "2021" +name = "embassy-nrf52840-edf-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +# NOTE: "edf-scheduler" feature is enabled +embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "edf-scheduler"] } +embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } + +[profile.release] +debug = 2 diff --git a/examples/nrf52840-edf/build.rs b/examples/nrf52840-edf/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf52840-edf/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/nrf52840-edf/memory.x b/examples/nrf52840-edf/memory.x new file mode 100644 index 000000000..15b492bce --- /dev/null +++ b/examples/nrf52840-edf/memory.x @@ -0,0 +1,12 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K + + /* These values correspond to the NRF52840 with Softdevices S140 7.3.0 */ + /* + FLASH : ORIGIN = 0x00027000, LENGTH = 868K + RAM : ORIGIN = 0x20020000, LENGTH = 128K + */ +} diff --git a/examples/nrf52840-edf/src/bin/basic.rs b/examples/nrf52840-edf/src/bin/basic.rs new file mode 100644 index 000000000..6a7eb3c4b --- /dev/null +++ b/examples/nrf52840-edf/src/bin/basic.rs @@ -0,0 +1,191 @@ +//! Basic side-by-side example of the Earliest Deadline First scheduler +//! +//! This test spawns a number of background "ambient system load" workers +//! that are constantly working, and runs two sets of trials. +//! +//! The first trial runs with no deadline set, so our trial task is at the +//! same prioritization level as the background worker tasks. +//! +//! The second trial sets a deadline, meaning that it will be given higher +//! scheduling priority than background tasks, that have no deadline set + +#![no_std] +#![no_main] + +use core::sync::atomic::{compiler_fence, Ordering}; +use embassy_executor::{raw::Deadline, Spawner}; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + embassy_nrf::init(Default::default()); + + // Enable flash cache to remove some flash latency jitter + compiler_fence(Ordering::SeqCst); + embassy_nrf::pac::NVMC.icachecnf().write(|w| { + w.set_cacheen(true); + }); + compiler_fence(Ordering::SeqCst); + + // + // Baseline system load tunables + // + + // how many load tasks? More load tasks means more tasks contending + // for the runqueue + let tasks = 32; + // how long should each task work for? The longer the working time, + // the longer the max jitter possible, even when a task is prioritized, + // as EDF is still cooperative and not pre-emptive + // + // 33 ticks ~= 1ms + let work_time_ticks = 33; + // what fraction, 1/denominator, should the system be busy? + // bigger number means **less** busy + // + // 2 => 50% + // 4 => 25% + // 10 => 10% + let denominator = 2; + + // Total time window, so each worker is working 1/denominator + // amount of the total time + let time_window = work_time_ticks * u64::from(tasks) * denominator; + + // Spawn all of our load workers! + for i in 0..tasks { + spawner.must_spawn(load_task(i, work_time_ticks, time_window)); + } + + // Let all the tasks spin up + defmt::println!("Spinning up load tasks..."); + Timer::after_secs(1).await; + + // + // Trial task worker tunables + // + + // How many steps should the workers under test run? + // More steps means more chances to have to wait for other tasks + // in line ahead of us. + let num_steps = 100; + + // How many ticks should the worker take working on each step? + // + // 33 ticks ~= 1ms + let work_ticks = 33; + // How many ticks should the worker wait on each step? + // + // 66 ticks ~= 2ms + let idle_ticks = 66; + + // How many times to repeat each trial? + let trials = 3; + + // The total time a trial would take, in a perfect unloaded system + let theoretical = (num_steps * work_ticks) + (num_steps * idle_ticks); + + defmt::println!(""); + defmt::println!("Starting UNPRIORITIZED worker trials"); + for _ in 0..trials { + // + // UNPRIORITIZED worker + // + defmt::println!(""); + defmt::println!("Starting unprioritized worker"); + let start = Instant::now(); + for _ in 0..num_steps { + let now = Instant::now(); + while now.elapsed().as_ticks() < work_ticks {} + Timer::after_ticks(idle_ticks).await; + } + let elapsed = start.elapsed().as_ticks(); + defmt::println!( + "Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}", + theoretical, + elapsed + ); + let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0; + defmt::println!("Took {=f32}% of ideal time", ratio); + Timer::after_millis(500).await; + } + + Timer::after_secs(1).await; + + defmt::println!(""); + defmt::println!("Starting PRIORITIZED worker trials"); + for _ in 0..trials { + // + // PRIORITIZED worker + // + defmt::println!(""); + defmt::println!("Starting prioritized worker"); + let start = Instant::now(); + // Set the deadline to ~2x the theoretical time. In practice, setting any deadline + // here elevates the current task above all other worker tasks. + Deadline::set_current_task_deadline_after(theoretical * 2).await; + + // Perform the trial + for _ in 0..num_steps { + let now = Instant::now(); + while now.elapsed().as_ticks() < work_ticks {} + Timer::after_ticks(idle_ticks).await; + } + + let elapsed = start.elapsed().as_ticks(); + defmt::println!( + "Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}", + theoretical, + elapsed + ); + let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0; + defmt::println!("Took {=f32}% of ideal time", ratio); + + // Unset the deadline, deadlines are not automatically cleared, and if our + // deadline is in the past, then we get very high priority! + Deadline::clear_current_task_deadline().await; + + Timer::after_millis(500).await; + } + + defmt::println!(""); + defmt::println!("Trials Complete."); +} + +#[embassy_executor::task(pool_size = 32)] +async fn load_task(id: u32, ticks_on: u64, ttl_ticks: u64) { + let mut last_print = Instant::now(); + let mut last_tick = last_print; + let mut variance = 0; + let mut max_variance = 0; + loop { + let tgt = last_tick + Duration::from_ticks(ttl_ticks); + assert!(tgt > Instant::now(), "fell too behind!"); + + Timer::at(tgt).await; + let now = Instant::now(); + // How late are we from the target? + let var = now.duration_since(tgt).as_ticks(); + max_variance = max_variance.max(var); + variance += var; + + // blocking work + while now.elapsed().as_ticks() < ticks_on {} + + if last_print.elapsed() >= Duration::from_secs(1) { + defmt::trace!( + "Task {=u32} variance ticks (1s): {=u64}, max: {=u64}, act: {=u64}", + id, + variance, + max_variance, + ticks_on, + ); + max_variance = 0; + variance = 0; + last_print = Instant::now(); + } + + last_tick = tgt; + } +} -- cgit From 67ed473973dae6d60aed8b88b8b854b224660e8d Mon Sep 17 00:00:00 2001 From: James Munns Date: Wed, 16 Apr 2025 18:01:11 +0200 Subject: rustfmt --- examples/nrf52840-edf/src/bin/basic.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/nrf52840-edf/src/bin/basic.rs b/examples/nrf52840-edf/src/bin/basic.rs index 6a7eb3c4b..c270c67b9 100644 --- a/examples/nrf52840-edf/src/bin/basic.rs +++ b/examples/nrf52840-edf/src/bin/basic.rs @@ -13,7 +13,9 @@ #![no_main] use core::sync::atomic::{compiler_fence, Ordering}; -use embassy_executor::{raw::Deadline, Spawner}; + +use embassy_executor::raw::Deadline; +use embassy_executor::Spawner; use embassy_time::{Duration, Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; -- cgit From 99209accb5b37f236691e060474bfc4823d2e123 Mon Sep 17 00:00:00 2001 From: diondokter Date: Fri, 29 Aug 2025 14:44:32 +0200 Subject: Add edf example to CI and fix the example --- examples/nrf52840-edf/Cargo.toml | 12 ++++++------ examples/nrf52840-edf/src/bin/basic.rs | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'examples') diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml index c7147d1af..9efdff7c4 100644 --- a/examples/nrf52840-edf/Cargo.toml +++ b/examples/nrf52840-edf/Cargo.toml @@ -6,16 +6,16 @@ license = "MIT OR Apache-2.0" [dependencies] # NOTE: "edf-scheduler" feature is enabled -embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "edf-scheduler"] } -embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.3.1", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "edf-scheduler"] } +embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } -defmt = "0.3" -defmt-rtt = "0.4" +defmt = "1.0.1" +defmt-rtt = "1.0.0" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" -panic-probe = { version = "0.3", features = ["print-defmt"] } +panic-probe = { version = "1.0.0", features = ["print-defmt"] } [profile.release] debug = 2 diff --git a/examples/nrf52840-edf/src/bin/basic.rs b/examples/nrf52840-edf/src/bin/basic.rs index c270c67b9..8a8e46449 100644 --- a/examples/nrf52840-edf/src/bin/basic.rs +++ b/examples/nrf52840-edf/src/bin/basic.rs @@ -14,6 +14,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; +use defmt::unwrap; use embassy_executor::raw::Deadline; use embassy_executor::Spawner; use embassy_time::{Duration, Instant, Timer}; @@ -57,7 +58,7 @@ async fn main(spawner: Spawner) { // Spawn all of our load workers! for i in 0..tasks { - spawner.must_spawn(load_task(i, work_time_ticks, time_window)); + spawner.spawn(unwrap!(load_task(i, work_time_ticks, time_window))); } // Let all the tasks spin up -- cgit From d04dc2bc35435602bb153d684dd547b2ffc57fbc Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Fri, 29 Aug 2025 15:37:00 +0200 Subject: Add cargo metadata --- examples/nrf52840-edf/Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'examples') diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml index 9efdff7c4..4c0d910ea 100644 --- a/examples/nrf52840-edf/Cargo.toml +++ b/examples/nrf52840-edf/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "embassy-nrf52840-edf-examples" version = "0.1.0" license = "MIT OR Apache-2.0" +publish = false [dependencies] # NOTE: "edf-scheduler" feature is enabled @@ -19,3 +20,8 @@ panic-probe = { version = "1.0.0", features = ["print-defmt"] } [profile.release] debug = 2 + +[package.metadata.embassy] +build = [ + { target = "thumbv7em-none-eabi", artifact-dir = "out/examples/nrf52840-edf" } +] -- cgit From 09701a339d9085d86a69bf271299d7b59eda9fdc Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Mon, 8 Sep 2025 12:33:04 +0200 Subject: Fix example --- examples/nrf52840-edf/Cargo.toml | 4 ++-- examples/nrf52840-edf/src/bin/basic.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml index 4c0d910ea..1e8803233 100644 --- a/examples/nrf52840-edf/Cargo.toml +++ b/examples/nrf52840-edf/Cargo.toml @@ -6,8 +6,8 @@ license = "MIT OR Apache-2.0" publish = false [dependencies] -# NOTE: "edf-scheduler" feature is enabled -embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "edf-scheduler"] } +# NOTE: "scheduler-deadline" and "embassy-time-driver" features are enabled +embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "scheduler-deadline", "embassy-time-driver"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf52840-edf/src/bin/basic.rs b/examples/nrf52840-edf/src/bin/basic.rs index 8a8e46449..d888e17d1 100644 --- a/examples/nrf52840-edf/src/bin/basic.rs +++ b/examples/nrf52840-edf/src/bin/basic.rs @@ -15,7 +15,6 @@ use core::sync::atomic::{compiler_fence, Ordering}; use defmt::unwrap; -use embassy_executor::raw::Deadline; use embassy_executor::Spawner; use embassy_time::{Duration, Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -127,7 +126,8 @@ async fn main(spawner: Spawner) { let start = Instant::now(); // Set the deadline to ~2x the theoretical time. In practice, setting any deadline // here elevates the current task above all other worker tasks. - Deadline::set_current_task_deadline_after(theoretical * 2).await; + let meta = embassy_executor::Metadata::for_current_task().await; + meta.set_deadline_after(theoretical * 2); // Perform the trial for _ in 0..num_steps { @@ -147,7 +147,7 @@ async fn main(spawner: Spawner) { // Unset the deadline, deadlines are not automatically cleared, and if our // deadline is in the past, then we get very high priority! - Deadline::clear_current_task_deadline().await; + meta.unset_deadline(); Timer::after_millis(500).await; } -- cgit From 55b3c5c6e8fb5e55a0e507c43db5d9ef32114f64 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 11 Sep 2025 21:27:02 +0200 Subject: ci: use devtool to build. --- examples/mspm0c1104/Cargo.toml | 3 +-- examples/nrf52840-rtic/src/bin/blinky.rs | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/mspm0c1104/Cargo.toml b/examples/mspm0c1104/Cargo.toml index 4daddbbb4..21434106a 100644 --- a/examples/mspm0c1104/Cargo.toml +++ b/examples/mspm0c1104/Cargo.toml @@ -33,7 +33,6 @@ lto = true codegen-units = 1 [package.metadata.embassy] -skip = true # TODO: remove when we find a way to decrease the defmt buffer size in ci. build = [ - { target = "thumbv6m-none-eabi", artifact-dir = "out/examples/mspm0c1104" } + { target = "thumbv6m-none-eabi", artifact-dir = "out/examples/mspm0c1104", env = { DEFMT_RTT_BUFFER_SIZE = "72" }} ] diff --git a/examples/nrf52840-rtic/src/bin/blinky.rs b/examples/nrf52840-rtic/src/bin/blinky.rs index 719e22729..2adac7e0a 100644 --- a/examples/nrf52840-rtic/src/bin/blinky.rs +++ b/examples/nrf52840-rtic/src/bin/blinky.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(type_alias_impl_trait)] use {defmt_rtt as _, panic_probe as _}; -- cgit From 547a52103b3c30506dc981fa89faa6c12765e97a Mon Sep 17 00:00:00 2001 From: Roi Bachynskyi Date: Wed, 10 Sep 2025 12:21:04 +0300 Subject: lpc55: added lpc55-core0 feature Co-authored-by: Irina Chiorean --- examples/lpc55s69/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index 79b27f269..579748595 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" publish = false [dependencies] -embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55", "rt", "defmt", "time-driver-rtc"] } +embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["lpc55-core0", "rt", "defmt", "time-driver-rtc"] } embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] } embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } -- cgit From 0ea3478fb5e4fcdcd86e439186794d126ed2eca4 Mon Sep 17 00:00:00 2001 From: Riceman2000 Date: Fri, 12 Sep 2025 12:47:47 -0400 Subject: Fix typo in PIO SPI examples --- examples/rp/src/bin/pio_spi.rs | 6 +++--- examples/rp/src/bin/pio_spi_async.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'examples') diff --git a/examples/rp/src/bin/pio_spi.rs b/examples/rp/src/bin/pio_spi.rs index 4218327ec..c45aeac7d 100644 --- a/examples/rp/src/bin/pio_spi.rs +++ b/examples/rp/src/bin/pio_spi.rs @@ -27,9 +27,9 @@ async fn main(_spawner: Spawner) { // These pins are routed to different hardware SPI peripherals, but we can // use them together regardless - let mosi = p.PIN_6; // SPI0 SCLK - let miso = p.PIN_7; // SPI0 MOSI - let clk = p.PIN_8; // SPI1 MISO + let mosi = p.PIN_6; + let miso = p.PIN_7; + let clk = p.PIN_8; let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); diff --git a/examples/rp/src/bin/pio_spi_async.rs b/examples/rp/src/bin/pio_spi_async.rs index 18b57d26e..e7d9b0ecc 100644 --- a/examples/rp/src/bin/pio_spi_async.rs +++ b/examples/rp/src/bin/pio_spi_async.rs @@ -27,9 +27,9 @@ async fn main(_spawner: Spawner) { // These pins are routed to different hardware SPI peripherals, but we can // use them together regardless - let mosi = p.PIN_6; // SPI0 SCLK - let miso = p.PIN_7; // SPI0 MOSI - let clk = p.PIN_8; // SPI1 MISO + let mosi = p.PIN_6; + let miso = p.PIN_7; + let clk = p.PIN_8; let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); -- cgit From f829ddd3b236b146701951004b41525de4633c9a Mon Sep 17 00:00:00 2001 From: Riceman2000 Date: Fri, 12 Sep 2025 12:47:55 -0400 Subject: Example first draft --- examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs | 145 +++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs (limited to 'examples') diff --git a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs new file mode 100644 index 000000000..6f4ba4a70 --- /dev/null +++ b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs @@ -0,0 +1,145 @@ +//! This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. +//! +//! Example written for the [`WIZnet W55RP20-EVB-Pico`](https://docs.wiznet.io/Product/ioNIC/W55RP20/w55rp20-evb-pico) board. +//! Note: the W55RP20 is a single package that contains both a RP2040 and the Wiznet W5500 ethernet +//! controller + +#![no_std] +#![no_main] + +use core::str::FromStr; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::yield_now; +use embassy_net::{Stack, StackResources}; +use embassy_net_wiznet::chip::W5500; +use embassy_net_wiznet::*; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio_programs::spi::Spi; +use embassy_rp::spi::{Async, Config as SpiConfig}; +use embassy_rp::{bind_interrupts, pio}; +use embassy_time::{Delay, Duration, Timer}; +use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_io_async::Write; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => pio::InterruptHandler; +}); + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + W5500, + ExclusiveDevice, Output<'static>, Delay>, + Input<'static>, + Output<'static>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! { + runner.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + let mut led = Output::new(p.PIN_19, Level::Low); + + // The W55RP20 uses a PIO unit for SPI communication, once the SPI bus has been formed using a + // PIO statemachine everything else is generally unchanged from the other examples that use the W5500 + let mosi = p.PIN_23; + let miso = p.PIN_22; + let clk = p.PIN_21; + + let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); + + // Construct an SPI driver backed by a PIO state machine + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let spi = Spi::new(&mut common, sm0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + + // Further control pins + let cs = Output::new(p.PIN_20, Level::High); + let w5500_int = Input::new(p.PIN_24, Pull::Up); + let w5500_reset = Output::new(p.PIN_25, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + static STATE: StaticCell> = StaticCell::new(); + let state = STATE.init(State::<8, 8>::new()); + let (device, runner) = embassy_net_wiznet::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await + .unwrap(); + spawner.spawn(unwrap!(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + static RESOURCES: StaticCell> = StaticCell::new(); + let (stack, runner) = embassy_net::new( + device, + embassy_net::Config::dhcpv4(Default::default()), + RESOURCES.init(StackResources::new()), + seed, + ); + + // Launch network task + spawner.spawn(unwrap!(net_task(runner))); + + info!("Waiting for DHCP..."); + let cfg = wait_for_config(stack).await; + let local_addr = cfg.address.address(); + info!("IP address: {:?}", local_addr); + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + loop { + let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + led.set_low(); + info!("Connecting..."); + let host_addr = embassy_net::Ipv4Address::from_str("192.168.1.110").unwrap(); + if let Err(e) = socket.connect((host_addr, 1234)).await { + warn!("connect error: {:?}", e); + continue; + } + info!("Connected to {:?}", socket.remote_endpoint()); + led.set_high(); + + let msg = b"Hello world!\n"; + loop { + if let Err(e) = socket.write_all(msg).await { + warn!("write error: {:?}", e); + break; + } + info!("txd: {}", core::str::from_utf8(msg).unwrap()); + Timer::after_secs(1).await; + } + } +} + +async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 { + loop { + if let Some(config) = stack.config_v4() { + return config.clone(); + } + yield_now().await; + } +} -- cgit From 139ee907755bfa7817001c3ebc4a38eaf31cf243 Mon Sep 17 00:00:00 2001 From: riceman2000 Date: Fri, 12 Sep 2025 17:17:24 -0400 Subject: Updated example --- examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs | 34 +++++++++++++++------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'examples') diff --git a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs index 6f4ba4a70..17dc40aff 100644 --- a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs @@ -1,4 +1,5 @@ -//! This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. +//! This example implements a TCP echo server on port 1234 and using DHCP. +//! Send it some data, you should see it echoed back and printed in the console. //! //! Example written for the [`WIZnet W55RP20-EVB-Pico`](https://docs.wiznet.io/Product/ioNIC/W55RP20/w55rp20-evb-pico) board. //! Note: the W55RP20 is a single package that contains both a RP2040 and the Wiznet W5500 ethernet @@ -65,7 +66,8 @@ async fn main(spawner: Spawner) { // Construct an SPI driver backed by a PIO state machine let mut spi_cfg = SpiConfig::default(); - spi_cfg.frequency = 50_000_000; + spi_cfg.frequency = 10_000_000; // The PIO SPI program is much less stable than the actual SPI + // peripheral, use higher speeds at your peril let spi = Spi::new(&mut common, sm0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); // Further control pins @@ -109,28 +111,38 @@ async fn main(spawner: Spawner) { let mut rx_buffer = [0; 4096]; let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; loop { let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); socket.set_timeout(Some(Duration::from_secs(10))); led.set_low(); - info!("Connecting..."); - let host_addr = embassy_net::Ipv4Address::from_str("192.168.1.110").unwrap(); - if let Err(e) = socket.connect((host_addr, 1234)).await { - warn!("connect error: {:?}", e); + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); continue; } - info!("Connected to {:?}", socket.remote_endpoint()); + info!("Received connection from {:?}", socket.remote_endpoint()); led.set_high(); - let msg = b"Hello world!\n"; loop { - if let Err(e) = socket.write_all(msg).await { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("{:?}", e); + break; + } + }; + info!("rxd {}", core::str::from_utf8(&buf[..n]).unwrap()); + + if let Err(e) = socket.write_all(&buf[..n]).await { warn!("write error: {:?}", e); break; } - info!("txd: {}", core::str::from_utf8(msg).unwrap()); - Timer::after_secs(1).await; } } } -- cgit From 6beb7e35a6bf2ae0e72a389b2dac6bde08e5dcd2 Mon Sep 17 00:00:00 2001 From: riceman2000 Date: Fri, 12 Sep 2025 22:52:53 -0400 Subject: Remove unused imports --- examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs index 17dc40aff..0d69b66c4 100644 --- a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs @@ -8,8 +8,6 @@ #![no_std] #![no_main] -use core::str::FromStr; - use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; @@ -22,7 +20,7 @@ use embassy_rp::peripherals::PIO0; use embassy_rp::pio_programs::spi::Spi; use embassy_rp::spi::{Async, Config as SpiConfig}; use embassy_rp::{bind_interrupts, pio}; -use embassy_time::{Delay, Duration, Timer}; +use embassy_time::{Delay, Duration}; use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io_async::Write; use static_cell::StaticCell; -- cgit From 31b5a3f0a4fafd425aef34b9d6fb93ead851b4c6 Mon Sep 17 00:00:00 2001 From: crispaudio Date: Sun, 14 Sep 2025 01:34:49 +0200 Subject: mspm0-adc: implement From for AnyAdcChannel --- examples/mspm0g3507/src/bin/adc.rs | 9 +++------ examples/mspm0l1306/src/bin/adc.rs | 9 +++------ 2 files changed, 6 insertions(+), 12 deletions(-) (limited to 'examples') diff --git a/examples/mspm0g3507/src/bin/adc.rs b/examples/mspm0g3507/src/bin/adc.rs index 73711c75c..ceccc7c02 100644 --- a/examples/mspm0g3507/src/bin/adc.rs +++ b/examples/mspm0g3507/src/bin/adc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::adc::{self, Adc, AdcChannel, Vrsel}; +use embassy_mspm0::adc::{self, Adc, Vrsel}; use embassy_mspm0::{bind_interrupts, peripherals, Config}; use embassy_time::Timer; use {defmt_rtt as _, panic_halt as _}; @@ -19,14 +19,11 @@ async fn main(_spawner: Spawner) -> ! { // Configure adc with sequence 0 to 1 let mut adc = Adc::new_async(p.ADC0, Default::default(), Irqs); - let pin1 = p.PA22.degrade_adc(); - let pin2 = p.PB20.degrade_adc(); - let sequence = [(&pin1, Vrsel::VddaVssa), (&pin2, Vrsel::VddaVssa)]; + let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PB20.into(), Vrsel::VddaVssa)]; let mut readings = [0u16; 2]; - let mut pin3 = p.PA27; loop { - let r = adc.read_channel(&mut pin3).await; + let r = adc.read_channel(&p.PA27).await; info!("Raw adc PA27: {}", r); // With a voltage range of 0-3.3V and a resolution of 12 bits, the raw value can be // approximated to voltage (~0.0008 per step). diff --git a/examples/mspm0l1306/src/bin/adc.rs b/examples/mspm0l1306/src/bin/adc.rs index a0c2c0cff..2806b98cc 100644 --- a/examples/mspm0l1306/src/bin/adc.rs +++ b/examples/mspm0l1306/src/bin/adc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::adc::{self, Adc, AdcChannel, Vrsel}; +use embassy_mspm0::adc::{self, Adc, Vrsel}; use embassy_mspm0::{bind_interrupts, peripherals, Config}; use embassy_time::Timer; use {defmt_rtt as _, panic_halt as _}; @@ -19,14 +19,11 @@ async fn main(_spawner: Spawner) -> ! { // Configure adc with sequence 0 to 1 let mut adc = Adc::new_async(p.ADC0, Default::default(), Irqs); - let pin1 = p.PA22.degrade_adc(); - let pin2 = p.PA20.degrade_adc(); - let sequence = [(&pin1, Vrsel::VddaVssa), (&pin2, Vrsel::VddaVssa)]; + let sequence = [(&p.PA22.into(), Vrsel::VddaVssa), (&p.PA20.into(), Vrsel::VddaVssa)]; let mut readings = [0u16; 2]; - let mut pin3 = p.PA27; loop { - let r = adc.read_channel(&mut pin3).await; + let r = adc.read_channel(&p.PA27).await; info!("Raw adc PA27: {}", r); // With a voltage range of 0-3.3V and a resolution of 12 bits, the raw value can be // approximated to voltage (~0.0008 per step). -- cgit From 8f10e3638d77cadf058b9083de09fc7189048b0b Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 14 Sep 2025 16:30:31 +0800 Subject: rp/pio: Add onewire strong pullups, parasite power DS18B20 sensors require a strong pullup to be applied for the duration of the temperature conversion, within 10us of the command. The rp2xxx pins have sufficient drive strength to use as the pullup (no external mosfet needed). Add a new write_bytes_pullup() that will apply the pullup after bytes are written. Existing read_bytes()/write_bytes() has no change to onewire timing. A pio_onewire_parasite example reads multiple sensors individually, applying the strong pullup. --- examples/rp/src/bin/pio_onewire.rs | 1 + examples/rp/src/bin/pio_onewire_parasite.rs | 89 +++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 examples/rp/src/bin/pio_onewire_parasite.rs (limited to 'examples') diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs index 379e2b8f9..102f13c45 100644 --- a/examples/rp/src/bin/pio_onewire.rs +++ b/examples/rp/src/bin/pio_onewire.rs @@ -1,4 +1,5 @@ //! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors. +//! This uses externally powered sensors. For parasite power, see the pio_onewire_parasite.rs example. #![no_std] #![no_main] diff --git a/examples/rp/src/bin/pio_onewire_parasite.rs b/examples/rp/src/bin/pio_onewire_parasite.rs new file mode 100644 index 000000000..fd076dee0 --- /dev/null +++ b/examples/rp/src/bin/pio_onewire_parasite.rs @@ -0,0 +1,89 @@ +//! This example shows how you can use PIO to read one or more `DS18B20` +//! one-wire temperature sensors using parasite power. +//! It applies a strong pullup during conversion, see "Powering the DS18B20" in the datasheet. +//! For externally powered sensors, use the pio_onewire.rs example. + +#![no_std] +#![no_main] +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch}; +use embassy_time::Duration; +use heapless::Vec; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut pio = Pio::new(p.PIO0, Irqs); + + let prg = PioOneWireProgram::new(&mut pio.common); + let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); + + info!("Starting onewire search"); + + let mut devices = Vec::::new(); + let mut search = PioOneWireSearch::new(); + for _ in 0..10 { + if !search.is_finished() { + if let Some(address) = search.next(&mut onewire).await { + if crc8(&address.to_le_bytes()) == 0 { + info!("Found address: {:x}", address); + let _ = devices.push(address); + } else { + warn!("Found invalid address: {:x}", address); + } + } + } + } + + info!("Search done, found {} devices", devices.len()); + + loop { + // Read all devices one by one + for device in &devices { + onewire.reset().await; + onewire.write_bytes(&[0x55]).await; // Match rom + onewire.write_bytes(&device.to_le_bytes()).await; + // 750 ms delay required for default 12-bit resolution. + onewire.write_bytes_pullup(&[0x44], Duration::from_millis(750)).await; + + onewire.reset().await; + onewire.write_bytes(&[0x55]).await; // Match rom + onewire.write_bytes(&device.to_le_bytes()).await; + onewire.write_bytes(&[0xBE]).await; // Read scratchpad + + let mut data = [0; 9]; + onewire.read_bytes(&mut data).await; + if crc8(&data) == 0 { + let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; + info!("Read device {:x}: {} deg C", device, temp); + } else { + warn!("Reading device {:x} failed. {:02x}", device, data); + } + } + } +} + +fn crc8(data: &[u8]) -> u8 { + let mut crc = 0; + for b in data { + let mut data_byte = *b; + for _ in 0..8 { + let temp = (crc ^ data_byte) & 0x01; + crc >>= 1; + if temp != 0 { + crc ^= 0x8C; + } + data_byte >>= 1; + } + } + crc +} -- cgit From 7c551b4fdfd7fcf410423355a3a1b3f92d5f65a6 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 14 Sep 2025 16:38:20 +0800 Subject: rp/pio: Copy onewire examples from rp to rp235x The rp pio_onewire example was updated on cd27a8a06b0160d654ebed7b89ca473041710235 but not rp235x. Copy them to be the same. --- examples/rp235x/src/bin/pio_onewire.rs | 103 +++++++++++++----------- examples/rp235x/src/bin/pio_onewire_parasite.rs | 89 ++++++++++++++++++++ 2 files changed, 143 insertions(+), 49 deletions(-) create mode 100644 examples/rp235x/src/bin/pio_onewire_parasite.rs (limited to 'examples') diff --git a/examples/rp235x/src/bin/pio_onewire.rs b/examples/rp235x/src/bin/pio_onewire.rs index 991510851..102f13c45 100644 --- a/examples/rp235x/src/bin/pio_onewire.rs +++ b/examples/rp235x/src/bin/pio_onewire.rs @@ -1,4 +1,5 @@ -//! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor. +//! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors. +//! This uses externally powered sensors. For parasite power, see the pio_onewire_parasite.rs example. #![no_std] #![no_main] @@ -6,9 +7,10 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{self, InterruptHandler, Pio}; -use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram}; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch}; use embassy_time::Timer; +use heapless::Vec; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -21,63 +23,66 @@ async fn main(_spawner: Spawner) { let mut pio = Pio::new(p.PIO0, Irqs); let prg = PioOneWireProgram::new(&mut pio.common); - let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); + let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); - let mut sensor = Ds18b20::new(onewire); + info!("Starting onewire search"); - loop { - sensor.start().await; // Start a new measurement - Timer::after_secs(1).await; // Allow 1s for the measurement to finish - match sensor.temperature().await { - Ok(temp) => info!("temp = {:?} deg C", temp), - _ => error!("sensor error"), + let mut devices = Vec::::new(); + let mut search = PioOneWireSearch::new(); + for _ in 0..10 { + if !search.is_finished() { + if let Some(address) = search.next(&mut onewire).await { + if crc8(&address.to_le_bytes()) == 0 { + info!("Found addres: {:x}", address); + let _ = devices.push(address); + } else { + warn!("Found invalid address: {:x}", address); + } + } } - Timer::after_secs(1).await; } -} -/// DS18B20 temperature sensor driver -pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { - wire: PioOneWire<'d, PIO, SM>, -} + info!("Search done, found {} devices", devices.len()); -impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { - pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self { - Self { wire } - } + loop { + onewire.reset().await; + // Skip rom and trigger conversion, we can trigger all devices on the bus immediately + onewire.write_bytes(&[0xCC, 0x44]).await; - /// Calculate CRC8 of the data - fn crc8(data: &[u8]) -> u8 { - let mut temp; - let mut data_byte; - let mut crc = 0; - for b in data { - data_byte = *b; - for _ in 0..8 { - temp = (crc ^ data_byte) & 0x01; - crc >>= 1; - if temp != 0 { - crc ^= 0x8C; - } - data_byte >>= 1; + Timer::after_secs(1).await; // Allow 1s for the measurement to finish + + // Read all devices one by one + for device in &devices { + onewire.reset().await; + onewire.write_bytes(&[0x55]).await; // Match rom + onewire.write_bytes(&device.to_le_bytes()).await; + onewire.write_bytes(&[0xBE]).await; // Read scratchpad + + let mut data = [0; 9]; + onewire.read_bytes(&mut data).await; + if crc8(&data) == 0 { + let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; + info!("Read device {:x}: {} deg C", device, temp); + } else { + warn!("Reading device {:x} failed", device); } } - crc - } - - /// Start a new measurement. Allow at least 1000ms before getting `temperature`. - pub async fn start(&mut self) { - self.wire.write_bytes(&[0xCC, 0x44]).await; + Timer::after_secs(1).await; } +} - /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. - pub async fn temperature(&mut self) -> Result { - self.wire.write_bytes(&[0xCC, 0xBE]).await; - let mut data = [0; 9]; - self.wire.read_bytes(&mut data).await; - match Self::crc8(&data) == 0 { - true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), - false => Err(()), +fn crc8(data: &[u8]) -> u8 { + let mut crc = 0; + for b in data { + let mut data_byte = *b; + for _ in 0..8 { + let temp = (crc ^ data_byte) & 0x01; + crc >>= 1; + if temp != 0 { + crc ^= 0x8C; + } + data_byte >>= 1; } } + crc } diff --git a/examples/rp235x/src/bin/pio_onewire_parasite.rs b/examples/rp235x/src/bin/pio_onewire_parasite.rs new file mode 100644 index 000000000..fd076dee0 --- /dev/null +++ b/examples/rp235x/src/bin/pio_onewire_parasite.rs @@ -0,0 +1,89 @@ +//! This example shows how you can use PIO to read one or more `DS18B20` +//! one-wire temperature sensors using parasite power. +//! It applies a strong pullup during conversion, see "Powering the DS18B20" in the datasheet. +//! For externally powered sensors, use the pio_onewire.rs example. + +#![no_std] +#![no_main] +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch}; +use embassy_time::Duration; +use heapless::Vec; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut pio = Pio::new(p.PIO0, Irqs); + + let prg = PioOneWireProgram::new(&mut pio.common); + let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); + + info!("Starting onewire search"); + + let mut devices = Vec::::new(); + let mut search = PioOneWireSearch::new(); + for _ in 0..10 { + if !search.is_finished() { + if let Some(address) = search.next(&mut onewire).await { + if crc8(&address.to_le_bytes()) == 0 { + info!("Found address: {:x}", address); + let _ = devices.push(address); + } else { + warn!("Found invalid address: {:x}", address); + } + } + } + } + + info!("Search done, found {} devices", devices.len()); + + loop { + // Read all devices one by one + for device in &devices { + onewire.reset().await; + onewire.write_bytes(&[0x55]).await; // Match rom + onewire.write_bytes(&device.to_le_bytes()).await; + // 750 ms delay required for default 12-bit resolution. + onewire.write_bytes_pullup(&[0x44], Duration::from_millis(750)).await; + + onewire.reset().await; + onewire.write_bytes(&[0x55]).await; // Match rom + onewire.write_bytes(&device.to_le_bytes()).await; + onewire.write_bytes(&[0xBE]).await; // Read scratchpad + + let mut data = [0; 9]; + onewire.read_bytes(&mut data).await; + if crc8(&data) == 0 { + let temp = ((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.; + info!("Read device {:x}: {} deg C", device, temp); + } else { + warn!("Reading device {:x} failed. {:02x}", device, data); + } + } + } +} + +fn crc8(data: &[u8]) -> u8 { + let mut crc = 0; + for b in data { + let mut data_byte = *b; + for _ in 0..8 { + let temp = (crc ^ data_byte) & 0x01; + crc >>= 1; + if temp != 0 { + crc ^= 0x8C; + } + data_byte >>= 1; + } + } + crc +} -- cgit From daae1fe5c9357ae97b897defae3d149eeafcc49f Mon Sep 17 00:00:00 2001 From: riceman2000 Date: Sun, 14 Sep 2025 11:30:22 -0400 Subject: Up SPI freq --- examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs index 0d69b66c4..f51df2df9 100644 --- a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs @@ -64,7 +64,7 @@ async fn main(spawner: Spawner) { // Construct an SPI driver backed by a PIO state machine let mut spi_cfg = SpiConfig::default(); - spi_cfg.frequency = 10_000_000; // The PIO SPI program is much less stable than the actual SPI + spi_cfg.frequency = 12_500_000; // The PIO SPI program is much less stable than the actual SPI // peripheral, use higher speeds at your peril let spi = Spi::new(&mut common, sm0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); -- cgit From 1c080559fd20eb250e76278ff92d23432b5e0ce8 Mon Sep 17 00:00:00 2001 From: riceman2000 Date: Sun, 14 Sep 2025 14:14:59 -0400 Subject: Fix removed comments --- examples/rp/src/bin/pio_spi.rs | 6 +++--- examples/rp/src/bin/pio_spi_async.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'examples') diff --git a/examples/rp/src/bin/pio_spi.rs b/examples/rp/src/bin/pio_spi.rs index c45aeac7d..4218327ec 100644 --- a/examples/rp/src/bin/pio_spi.rs +++ b/examples/rp/src/bin/pio_spi.rs @@ -27,9 +27,9 @@ async fn main(_spawner: Spawner) { // These pins are routed to different hardware SPI peripherals, but we can // use them together regardless - let mosi = p.PIN_6; - let miso = p.PIN_7; - let clk = p.PIN_8; + let mosi = p.PIN_6; // SPI0 SCLK + let miso = p.PIN_7; // SPI0 MOSI + let clk = p.PIN_8; // SPI1 MISO let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); diff --git a/examples/rp/src/bin/pio_spi_async.rs b/examples/rp/src/bin/pio_spi_async.rs index e7d9b0ecc..18b57d26e 100644 --- a/examples/rp/src/bin/pio_spi_async.rs +++ b/examples/rp/src/bin/pio_spi_async.rs @@ -27,9 +27,9 @@ async fn main(_spawner: Spawner) { // These pins are routed to different hardware SPI peripherals, but we can // use them together regardless - let mosi = p.PIN_6; - let miso = p.PIN_7; - let clk = p.PIN_8; + let mosi = p.PIN_6; // SPI0 SCLK + let miso = p.PIN_7; // SPI0 MOSI + let clk = p.PIN_8; // SPI1 MISO let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs); -- cgit From 5ee77055a1d0073c3e5f312764acd566b1b92f84 Mon Sep 17 00:00:00 2001 From: Süha Ünüvar <87157627+phycrax@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:43:23 +0800 Subject: fix examples --- examples/stm32f7/src/bin/qspi.rs | 16 ++++++++-------- examples/stm32h742/src/bin/qspi.rs | 16 ++++++++-------- examples/stm32l432/src/bin/qspi_mmap.rs | 16 ++++++++-------- 3 files changed, 24 insertions(+), 24 deletions(-) (limited to 'examples') diff --git a/examples/stm32f7/src/bin/qspi.rs b/examples/stm32f7/src/bin/qspi.rs index ab29ddeff..80652b865 100644 --- a/examples/stm32f7/src/bin/qspi.rs +++ b/examples/stm32f7/src/bin/qspi.rs @@ -273,14 +273,14 @@ async fn main(_spawner: Spawner) -> ! { let p = embassy_stm32::init(config); info!("Embassy initialized"); - let config = QspiCfg { - memory_size: MemorySize::_8MiB, - address_size: AddressSize::_24bit, - prescaler: 16, - cs_high_time: ChipSelectHighTime::_1Cycle, - fifo_threshold: FIFOThresholdLevel::_16Bytes, - sample_shifting: SampleShifting::None, - }; + let mut config = QspiCfg::default(); + config.memory_size = MemorySize::_8MiB; + config.address_size = AddressSize::_24bit; + config.prescaler = 16; + config.cs_high_time = ChipSelectHighTime::_1Cycle; + config.fifo_threshold = FIFOThresholdLevel::_16Bytes; + config.sample_shifting = SampleShifting::None; + let driver = Qspi::new_bank1( p.QUADSPI, p.PF8, p.PF9, p.PE2, p.PF6, p.PF10, p.PB10, p.DMA2_CH7, config, ); diff --git a/examples/stm32h742/src/bin/qspi.rs b/examples/stm32h742/src/bin/qspi.rs index 50e37ec52..9e79d7089 100644 --- a/examples/stm32h742/src/bin/qspi.rs +++ b/examples/stm32h742/src/bin/qspi.rs @@ -266,14 +266,14 @@ async fn main(_spawner: Spawner) -> ! { let p = embassy_stm32::init(config); info!("Embassy initialized"); - let config = QspiCfg { - memory_size: MemorySize::_8MiB, - address_size: AddressSize::_24bit, - prescaler: 16, - cs_high_time: ChipSelectHighTime::_1Cycle, - fifo_threshold: FIFOThresholdLevel::_16Bytes, - sample_shifting: SampleShifting::None, - }; + let mut config = QspiCfg::default(); + config.memory_size = MemorySize::_8MiB; + config.address_size = AddressSize::_24bit; + config.prescaler = 16; + config.cs_high_time = ChipSelectHighTime::_1Cycle; + config.fifo_threshold = FIFOThresholdLevel::_16Bytes; + config.sample_shifting = SampleShifting::None; + let driver = Qspi::new_blocking_bank1(p.QUADSPI, p.PD11, p.PD12, p.PE2, p.PD13, p.PB2, p.PB10, config); let mut flash = FlashMemory::new(driver); let flash_id = flash.read_id(); diff --git a/examples/stm32l432/src/bin/qspi_mmap.rs b/examples/stm32l432/src/bin/qspi_mmap.rs index 075458fe5..feabdd532 100644 --- a/examples/stm32l432/src/bin/qspi_mmap.rs +++ b/examples/stm32l432/src/bin/qspi_mmap.rs @@ -246,14 +246,14 @@ const MEMORY_ADDR: u32 = 0x00000000 as u32; async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let config = qspi::Config { - memory_size: MemorySize::_16MiB, - address_size: AddressSize::_24bit, - prescaler: 200, - cs_high_time: ChipSelectHighTime::_1Cycle, - fifo_threshold: FIFOThresholdLevel::_16Bytes, - sample_shifting: SampleShifting::None, - }; + let mut config = qspi::Config::default(); + config.memory_size = MemorySize::_16MiB; + config.address_size = AddressSize::_24bit; + config.prescaler = 200; + config.cs_high_time = ChipSelectHighTime::_1Cycle; + config.fifo_threshold = FIFOThresholdLevel::_16Bytes; + config.sample_shifting = SampleShifting::None; + let driver = qspi::Qspi::new_bank1(p.QUADSPI, p.PB1, p.PB0, p.PA7, p.PA6, p.PA3, p.PA2, p.DMA2_CH7, config); let mut flash = FlashMemory::new(driver); let mut wr_buf = [0u8; 256]; -- cgit From ac32f43c3dc915b78e71328855189b8aacfec8c3 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Mon, 15 Sep 2025 21:10:22 +0200 Subject: alarm handling to poll hardware status directly; add ci test --- examples/rp/src/bin/rtc_alarm.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/rp/src/bin/rtc_alarm.rs b/examples/rp/src/bin/rtc_alarm.rs index dccf911e3..e935dbf34 100644 --- a/examples/rp/src/bin/rtc_alarm.rs +++ b/examples/rp/src/bin/rtc_alarm.rs @@ -47,14 +47,10 @@ async fn main(_spawner: Spawner) { ); // See if the alarm is already scheduled, if not, schedule it - match rtc.alarm_scheduled() { - None => { - info!("Scheduling alarm for 30 seconds from now"); - rtc.schedule_alarm(DateTimeFilter::default().second((dt.second + 30) % 60)); - - info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap()); - } - Some(_) => {} + if rtc.alarm_scheduled().is_none() { + info!("Scheduling alarm for 30 seconds from now"); + rtc.schedule_alarm(DateTimeFilter::default().second((dt.second + 30) % 60)); + info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap()); } } // Alarm triggered -- cgit From 362624ca5fa0a979b45e5828abc338c85835537c Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Mon, 15 Sep 2025 21:57:49 +0200 Subject: Log current date and time when alarm is triggered --- examples/rp/src/bin/rtc_alarm.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/rp/src/bin/rtc_alarm.rs b/examples/rp/src/bin/rtc_alarm.rs index e935dbf34..94b5fbd27 100644 --- a/examples/rp/src/bin/rtc_alarm.rs +++ b/examples/rp/src/bin/rtc_alarm.rs @@ -55,7 +55,11 @@ async fn main(_spawner: Spawner) { } // Alarm triggered Either::Second(_) => { - info!("ALARM TRIGGERED!"); + let dt = rtc.now().unwrap(); + info!( + "ALARM TRIGGERED! Now: {}-{:02}-{:02} {}:{:02}:{:02}", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, + ); } } } -- cgit From 07e86b23f1149440d022614b04916edeafeccfa5 Mon Sep 17 00:00:00 2001 From: Per Rosengren Date: Tue, 16 Sep 2025 19:11:13 +0200 Subject: Re-export API enums and update examples --- examples/stm32g0/src/bin/adc_oversampling.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs index bc49fac83..d27e6f582 100644 --- a/examples/stm32g0/src/bin/adc_oversampling.rs +++ b/examples/stm32g0/src/bin/adc_oversampling.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, Clock, Presc, SampleTime}; +use embassy_stm32::adc::{Adc, Clock, Ovsr, Ovss, Presc, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -31,8 +31,8 @@ async fn main(_spawner: Spawner) { // 0x05 oversampling ratio X64 // 0x06 oversampling ratio X128 // 0x07 oversampling ratio X256 - adc.set_oversampling_ratio(0x03); - adc.set_oversampling_shift(0b0000); + adc.set_oversampling_ratio(Ovsr::MUL16); + adc.set_oversampling_shift(Ovss::NO_SHIFT); adc.oversampling_enable(true); loop { -- cgit From 4e1e4249556e20907839f09a65ae9283a8490cac Mon Sep 17 00:00:00 2001 From: Per Rosengren Date: Tue, 16 Sep 2025 19:14:00 +0200 Subject: Cleanup --- examples/stm32g0/src/bin/adc_oversampling.rs | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'examples') diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs index d27e6f582..834d1cd4a 100644 --- a/examples/stm32g0/src/bin/adc_oversampling.rs +++ b/examples/stm32g0/src/bin/adc_oversampling.rs @@ -20,17 +20,6 @@ async fn main(_spawner: Spawner) { adc.set_sample_time(SampleTime::CYCLES1_5); let mut pin = p.PA1; - // From https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf - // page373 15.8 Oversampler - // Table 76. Maximum output results vs N and M. Grayed values indicates truncation - // 0x00 oversampling ratio X2 - // 0x01 oversampling ratio X4 - // 0x02 oversampling ratio X8 - // 0x03 oversampling ratio X16 - // 0x04 oversampling ratio X32 - // 0x05 oversampling ratio X64 - // 0x06 oversampling ratio X128 - // 0x07 oversampling ratio X256 adc.set_oversampling_ratio(Ovsr::MUL16); adc.set_oversampling_shift(Ovss::NO_SHIFT); adc.oversampling_enable(true); -- cgit From c69d17e5fb11852ba14ddc3369a0c2dbfff4e29d Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sun, 21 Sep 2025 13:27:54 +1000 Subject: examples/rp235x: Add multicore stack overflow example --- .../rp235x/src/bin/multicore_stack_overflow.rs | 72 ++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 examples/rp235x/src/bin/multicore_stack_overflow.rs (limited to 'examples') diff --git a/examples/rp235x/src/bin/multicore_stack_overflow.rs b/examples/rp235x/src/bin/multicore_stack_overflow.rs new file mode 100644 index 000000000..dba44aa23 --- /dev/null +++ b/examples/rp235x/src/bin/multicore_stack_overflow.rs @@ -0,0 +1,72 @@ +//! This example tests stack overflow handling on core1 of the RP235x chip. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Executor; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_time::Timer; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +const CORE1_STACK_LENGTH: usize = 4096; + +static mut CORE1_STACK: Stack = Stack::new(); +static EXECUTOR0: StaticCell = StaticCell::new(); +static EXECUTOR1: StaticCell = StaticCell::new(); + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + let led = Output::new(p.PIN_25, Level::Low); + + spawn_core1( + p.CORE1, + unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) }, + move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| spawner.spawn(unwrap!(core1_task()))); + }, + ); + + let executor0 = EXECUTOR0.init(Executor::new()); + executor0.run(|spawner| spawner.spawn(unwrap!(core0_task(led)))); +} + +#[embassy_executor::task] +async fn core0_task(mut led: Output<'static>) { + info!("Hello from core 0"); + loop { + info!("core 0 still alive"); + led.set_high(); + Timer::after_millis(500).await; + led.set_low(); + Timer::after_millis(500).await; + } +} + +fn blow_my_stack() { + // Allocating an array a little larger than our stack should ensure a stack overflow when it is used. + let t = [0u8; CORE1_STACK_LENGTH + 64]; + + info!("Array initialised without error"); + // We need to use black_box to otherwise the compiler is too smart and will optimise all of this away. + // We shouldn't get to this code - the initialisation above will touch the stack guard. + for ref i in t { + let _data = core::hint::black_box(*i) + 1; + } +} + +#[embassy_executor::task] +async fn core1_task() { + info!("Hello from core 1"); + + blow_my_stack(); + + loop { + info!("core 1 still alive"); + Timer::after_millis(1000).await; + } +} -- cgit From f7c3005345df07bad5c42612fd73974bd569affb Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 19 Sep 2025 17:38:24 +0200 Subject: add basic RTC driver for nRF --- examples/nrf52840/Cargo.toml | 1 + examples/nrf52840/src/bin/rtc.rs | 57 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 examples/nrf52840/src/bin/rtc.rs (limited to 'examples') diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 452e83b7e..ca3c6f863 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -35,6 +35,7 @@ embedded-hal-async = { version = "1.0" } embedded-hal-bus = { version = "0.1", features = ["async"] } num-integer = { version = "0.1.45", default-features = false } microfft = "0.5.0" +portable-atomic = "1" [profile.release] debug = 2 diff --git a/examples/nrf52840/src/bin/rtc.rs b/examples/nrf52840/src/bin/rtc.rs new file mode 100644 index 000000000..a3df7da14 --- /dev/null +++ b/examples/nrf52840/src/bin/rtc.rs @@ -0,0 +1,57 @@ +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::interrupt; +use embassy_nrf::rtc::Rtc; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use portable_atomic::AtomicU64; +use {defmt_rtt as _, panic_probe as _}; + +// 64 bit counter which will never overflow. +static TICK_COUNTER: AtomicU64 = AtomicU64::new(0); +static RTC: Mutex>>> = + Mutex::new(RefCell::new(None)); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + defmt::println!("nRF52840 RTC example"); + let p = embassy_nrf::init(Default::default()); + let mut led = Output::new(p.P0_13, Level::High, OutputDrive::Standard); + // Counter resolution is 125 ms. + let mut rtc = Rtc::new(p.RTC0, (1 << 12) - 1).unwrap(); + rtc.enable_interrupt(embassy_nrf::rtc::Interrupt::Tick, true); + rtc.enable_event(embassy_nrf::rtc::Interrupt::Tick); + rtc.enable(); + RTC.lock(|r| { + let mut rtc_borrow = r.borrow_mut(); + *rtc_borrow = Some(rtc); + }); + + let mut last_counter_val = 0; + loop { + let current = TICK_COUNTER.load(core::sync::atomic::Ordering::Relaxed); + if current != last_counter_val { + led.toggle(); + last_counter_val = current; + } + } +} + +#[interrupt] +fn RTC0() { + // For 64-bit, we do not need to worry about overflowing, at least not for realistic program + // lifetimes. + TICK_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed); + RTC.lock(|r| { + let mut rtc_borrow = r.borrow_mut(); + rtc_borrow + .as_mut() + .unwrap() + .reset_event(embassy_nrf::rtc::Interrupt::Tick); + }); +} -- cgit From 4d71f432ad05cd8cce50b13cf6de6a1422f3b401 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 22 Sep 2025 00:47:08 +0200 Subject: Update manifests to satisfy new checks. --- examples/mimxrt1011/Cargo.toml | 2 +- examples/mimxrt1062-evk/Cargo.toml | 2 +- examples/mimxrt6/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/mimxrt1011/Cargo.toml b/examples/mimxrt1011/Cargo.toml index 488f3167b..3038f5d4d 100644 --- a/examples/mimxrt1011/Cargo.toml +++ b/examples/mimxrt1011/Cargo.toml @@ -2,7 +2,7 @@ name = "embassy-imxrt1011-examples" version = "0.1.0" edition = "2021" -license = "MIT or Apache-2.0" +license = "MIT OR Apache-2.0" publish = false [dependencies] diff --git a/examples/mimxrt1062-evk/Cargo.toml b/examples/mimxrt1062-evk/Cargo.toml index ec6c5c872..82a24490d 100644 --- a/examples/mimxrt1062-evk/Cargo.toml +++ b/examples/mimxrt1062-evk/Cargo.toml @@ -2,7 +2,7 @@ name = "embassy-imxrt1062-evk-examples" version = "0.1.0" edition = "2021" -license = "MIT or Apache-2.0" +license = "MIT OR Apache-2.0" publish = false [dependencies] diff --git a/examples/mimxrt6/Cargo.toml b/examples/mimxrt6/Cargo.toml index 28de9d273..3f7ad8485 100644 --- a/examples/mimxrt6/Cargo.toml +++ b/examples/mimxrt6/Cargo.toml @@ -2,7 +2,7 @@ name = "embassy-imxrt-examples" version = "0.1.0" edition = "2021" -license = "MIT or Apache-2.0" +license = "MIT OR Apache-2.0" publish = false [dependencies] -- cgit