From ce45e63e865a8cdf7b32ea019c8f0b13d7351f73 Mon Sep 17 00:00:00 2001 From: Pawel Pyszko Date: Sun, 11 May 2025 15:26:06 +0200 Subject: [PATCH 1/8] Update Wio Terminal embedded-sdmmc dependency Update from 0.3 to 0.8 version. Sacrifice delay to embedded sdmmc. Possibly missing SPI interface baud to higher speed after controller initialization. --- boards/wio_terminal/examples/sdcard.rs | 1 + boards/wio_terminal/src/storage.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/boards/wio_terminal/examples/sdcard.rs b/boards/wio_terminal/examples/sdcard.rs index 40c9b276e3b4..92305d876cf0 100644 --- a/boards/wio_terminal/examples/sdcard.rs +++ b/boards/wio_terminal/examples/sdcard.rs @@ -28,6 +28,7 @@ use heapless::String; use embedded_sdmmc::{sdcard::Error as SdCardError, TimeSource, Timestamp, VolumeIdx}; use wio::SDCardController; +#[allow(clippy::empty_loop)] #[entry] fn main() -> ! { let mut peripherals = Peripherals::take().unwrap(); diff --git a/boards/wio_terminal/src/storage.rs b/boards/wio_terminal/src/storage.rs index 526e58148cd5..1de85b42e0f2 100644 --- a/boards/wio_terminal/src/storage.rs +++ b/boards/wio_terminal/src/storage.rs @@ -1,3 +1,5 @@ +use core::marker::PhantomData; + use atsamd_hal::{ clock::{GenericClockController, Sercom6CoreClock}, delay::Delay, From 1eff819e6cc2115bf81dcebf122209b453cf2299 Mon Sep 17 00:00:00 2001 From: Pawel Pyszko Date: Sat, 10 May 2025 22:37:43 +0200 Subject: [PATCH 2/8] async_opentherm example moved on the atsamd-hal 0.22 --- boards/wio_terminal/.cargo/config.toml | 4 +- boards/wio_terminal/Cargo.toml | 32 +- boards/wio_terminal/README.md | 34 +- boards/wio_terminal/build_async_opentherm.sh | 4 + .../wio_terminal/examples/async_opentherm.rs | 978 ++++++++++++++++++ boards/wio_terminal/memory.x | 2 +- boards/wio_terminal/openocd.gdb | 13 + boards/wio_terminal/setup_timer.gdb | 38 + boards/wio_terminal/src/buttons.rs | 2 +- boards/wio_terminal/src/display.rs | 4 +- boards/wio_terminal/src/sensors.rs | 33 +- boards/wio_terminal/src/serial.rs | 4 +- boards/wio_terminal/src/sound.rs | 29 +- boards/wio_terminal/src/storage.rs | 4 +- boards/wio_terminal/src/wifi.rs | 4 +- hal/Cargo.toml | 1 + hal/src/peripherals/mod.rs | 10 + hal/src/peripherals/pwm/d5x.rs | 58 +- hal/src/peripherals/pwm_waveform/d5x.rs | 403 ++++++++ .../peripherals/timer_capture_waveform/d5x.rs | 822 +++++++++++++++ 20 files changed, 2420 insertions(+), 59 deletions(-) create mode 100755 boards/wio_terminal/build_async_opentherm.sh create mode 100644 boards/wio_terminal/examples/async_opentherm.rs create mode 100644 boards/wio_terminal/openocd.gdb create mode 100644 boards/wio_terminal/setup_timer.gdb create mode 100644 hal/src/peripherals/pwm_waveform/d5x.rs create mode 100644 hal/src/peripherals/timer_capture_waveform/d5x.rs diff --git a/boards/wio_terminal/.cargo/config.toml b/boards/wio_terminal/.cargo/config.toml index e8f7edb3cf74..d946e04b0ce9 100644 --- a/boards/wio_terminal/.cargo/config.toml +++ b/boards/wio_terminal/.cargo/config.toml @@ -1,5 +1,7 @@ [target.thumbv7em-none-eabihf] -runner = "arm-none-eabi-gdb" +runner = "/home/cooler1989/programs/cgdb/cgdb/cgdb -d gdb-multiarch -x openocd.gdb" +# -ex "mon reset halt" --ex "load" -ex "mon reset halt" +#runner = "arm-none-eabi-gdb" #runner = 'probe-run --chip ATSAMD51P19A' [build] diff --git a/boards/wio_terminal/Cargo.toml b/boards/wio_terminal/Cargo.toml index 3cd890ad02f7..dbe3d16788c3 100644 --- a/boards/wio_terminal/Cargo.toml +++ b/boards/wio_terminal/Cargo.toml @@ -31,6 +31,8 @@ chip = "ATSAMD51P19A" [dependencies] bitfield = "0.13" +modular-bitfield = "0.12" +fugit = "0.3.7" cortex-m-rt = { version = "0.7", optional = true } embedded-hal-bus = "0.3.0" @@ -44,29 +46,51 @@ nb = "1.0" bbqueue = { version = "0.5", optional = true } generic-array = { version = "0.14", optional = true } seeed-erpc = { version = "0.1.3", optional = true } +defmt = "0.3" +defmt-rtt = "0.4" +embassy-sync = { version = "0.6" } +embassy-futures = { version = "0.1.0" } +embassy-executor = { version = "0.7.0", features = [ + "arch-cortex-m", + "executor-thread", + "task-arena-size-12288", +] } +rtic-monotonics = { version = "2.0.1", features = ["cortex-m-systick"] } + +# Opentherm project specific dependnecies: +opentherm_boiler_controller_lib = { version = "0.1.0", git = "ssh://cooler1989@bitbucket.org/learn-rust/boiler_controller_ot_lib.git", branch = "passing_around_slices_instead_of_iterators", features = [ +], optional = true } [dependencies.cortex-m] features = ["critical-section-single-core"] version = "0.7" [dependencies.atsamd-hal] -version = "0.21.0" +path = "../../hal" +version = "0.22.0" default-features = false [dev-dependencies] usbd-serial = "0.2" embedded-graphics = "0.8.1" +panic-probe = "0.3" panic-halt = "0.2" +panic-semihosting = "0.5" +cortex-m-semihosting = "0.3" oorandom = "11.1.3" nom = { version = "8.0", default-features = false } [features] -default = ["atsamd-hal/samd51p", "rt", "usb", "wifi"] +default = ["atsamd-hal/samd51p", "rt", "usb", "wifi", "dma", "async", "use_opentherm"] rt = ["atsamd-hal/samd51p-rt", "cortex-m-rt"] usb = ["atsamd-hal/usb", "usb-device"] # enable feature for RTL8720 firmware older than 2.1.2 wifi-fw-before-212 = [] wifi = ["bbqueue", "generic-array", "seeed-erpc"] +dma = ["atsamd-hal/dma"] +async = ["atsamd-hal/async"] +use_semihosting = [] +use_opentherm = ["opentherm_boiler_controller_lib"] [[example]] name = "blinky" @@ -91,6 +115,10 @@ required-features = ["usb"] [[example]] name = "sdcard" +[[example]] +name = "async_opentherm" +required-features = ["async"] + [[example]] name = "qspi" diff --git a/boards/wio_terminal/README.md b/boards/wio_terminal/README.md index c65ae39386ed..882185511714 100644 --- a/boards/wio_terminal/README.md +++ b/boards/wio_terminal/README.md @@ -1,5 +1,10 @@ # `wio-terminal` +High Level TODOs: + +- Implement capture timer driver, capable of timeouts of around 800ms, best to return after 10ms of idle activity but only if some activity has been detected. +- Add confugiration option to use second Grove port to realize Master version of the OpenTherm protocol + [![Crates.io](https://img.shields.io/crates/v/wio-terminal.svg)](https://crates.io/crates/wio-terminal) [![Docs](https://docs.rs/wio-terminal/badge.svg)](https://docs.rs/wio-terminal/) ![License](https://img.shields.io/badge/License-MIT%20OR%20Apache--2.0-blue) @@ -8,11 +13,11 @@ This project is made possible thanks to the following crates: -* [atsamd-hal](https://github.com/atsamd-rs/atsamd) -* [ili9341-rs](https://github.com/yuri91/ili9341-rs) -* [lis3dh-rs](https://github.com/BenBergman/lis3dh-rs) -* [embedded-sdmmc](https://github.com/rust-embedded-community/embedded-sdmmc-rs) -* [seeed-erpc-rs](https://github.com/twitchyliquid64/seeed-erpc-rs) +- [atsamd-hal](https://github.com/atsamd-rs/atsamd) +- [ili9341-rs](https://github.com/yuri91/ili9341-rs) +- [lis3dh-rs](https://github.com/BenBergman/lis3dh-rs) +- [embedded-sdmmc](https://github.com/rust-embedded-community/embedded-sdmmc-rs) +- [seeed-erpc-rs](https://github.com/twitchyliquid64/seeed-erpc-rs) ## [Documentation] @@ -20,9 +25,9 @@ This project is made possible thanks to the following crates: ## Resources -* [Wio Terminal product page](https://www.seeedstudio.com/Wio-Terminal-p-4509.html) -* [Wio Terminal wiki](https://wiki.seeedstudio.com/Wio-Terminal-Getting-Started/) -* [Wio Terminal user manual](https://files.seeedstudio.com/wiki/Wio-Terminal/res/Wio-Terminal-User-Manual.pdf) +- [Wio Terminal product page](https://www.seeedstudio.com/Wio-Terminal-p-4509.html) +- [Wio Terminal wiki](https://wiki.seeedstudio.com/Wio-Terminal-Getting-Started/) +- [Wio Terminal user manual](https://files.seeedstudio.com/wiki/Wio-Terminal/res/Wio-Terminal-User-Manual.pdf) ## Display @@ -42,9 +47,9 @@ For information on building and flashing the examples to your device, as well as Licensed under either of: -* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) -* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) at your option. @@ -53,3 +58,10 @@ at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +### Lessons learned + +If you want to expand macro for hal drivers go to hal/ and use: + +cargo install cargo-expand +CARGO_NET_GIT_FETCH_WITH_CLI=true cargo expand --features "samd51p, async, dma" peripherals::timer_capture_waveform > peripherals_timer_capture_waveform.rs diff --git a/boards/wio_terminal/build_async_opentherm.sh b/boards/wio_terminal/build_async_opentherm.sh new file mode 100755 index 000000000000..4c8a47386360 --- /dev/null +++ b/boards/wio_terminal/build_async_opentherm.sh @@ -0,0 +1,4 @@ +CARGO_NET_GIT_FETCH_WITH_CLI=true cargo build --example async_opentherm +objcopy -I elf32-little -O binary ../../target/thumbv7em-none-eabihf/debug/examples/async_opentherm async_opentherm.bin +export BINARY_FILE=async_opentherm.bin; gdb-multiarch --batch asyn_opentherm.bin --ex "target remote localhost:3333" --ex "mon reset halt" --ex "mon program $(readlink -f ${BINARY_FILE}) 0x00000 verify" +# export BINARY_FILE=async_opentherm.bin; /home/cooler1989/programs/cgdb/cgdb/cgdb -d gdb-multiarch ../../target/thumbv7em-none-eabihf/debug/examples/async_opentherm --ex "target remote localhost:3333" --ex "mon reset halt" -ex "mon arm semihosting enable" -ex "mon arm semihosting_redirect tcp 2999" diff --git a/boards/wio_terminal/examples/async_opentherm.rs b/boards/wio_terminal/examples/async_opentherm.rs new file mode 100644 index 000000000000..79384d53888c --- /dev/null +++ b/boards/wio_terminal/examples/async_opentherm.rs @@ -0,0 +1,978 @@ +#![no_std] +#![no_main] + +use bsp::hal::time::Hertz; +use core::time::Duration; +use defmt_rtt as _; +use hal::fugit::Hertz as FugitHertz; +use hal::fugit::MillisDuration; +use heapless::Vec; +use panic_probe as _; + +use embassy_futures::join; +use embassy_sync::channel::{Channel, Receiver}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; + +use crate::pac::Mclk; +use bsp::pac; +use crate::bsp::hal; +use hal::gpio::{Output, Input, OutputConfig, Pins, PinId, PushPull, PushPullOutput, Floating}; +use hal::gpio::{E, PB08, PB09, PA16, PA17}; +// use wio_terminal::prelude::_embedded_hal_PwmPin; +use wio_terminal::aliases::UserLed; + +use hal::{ + clock::{ClockGenId, ClockSource, GenericClockController, Tc4Tc5Clock, Tc2Tc3Clock}, + delay::Delay, + dmac, + dmac::{DmaController, PriorityLevel, Ch1, ReadyFuture}, + ehal::digital::{OutputPin, StatefulOutputPin}, + eic::{Eic, Sense}, + gpio::{Pin as GpioPin, PullUp, PullUpInterrupt}, + pwm::{TC4Pinout, TC2Pinout, PinoutNewTrait}, + pwm_wg::{PwmWg4, PwmWg2}, + timer_capture_waveform::{TimerCapture4, TimerCapture4Future, TimerCaptureFutureTrait, TimerCaptureBaseTrait, TimerCaptureResultAvailable}, +}; +use wio_terminal::prelude::_embedded_hal_blocking_delay_DelayMs; + +// use bsp::pins::UserLed; +use wio_terminal as bsp; + +use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; +use rtic_monotonics::Monotonic; + +use modular_bitfield::prelude::*; + +// use static_cell::StaticCell; + +rtic_monotonics::systick_monotonic!(Mono, 10000); + +#[cfg(feature = "use_opentherm")] +use boiler::opentherm_interface::{ + edge_trigger_capture_interface::{ + CaptureError, EdgeCaptureInterface, EdgeCaptureTransitiveToTriggerCapable, CapturedEdgePeriod, CaptureTypeEdges, + EdgeTriggerInterface, EdgeTriggerTransitiveToCaptureCapable, InitLevel, TriggerError, + }, + open_therm_message::{CHState, Temperature, OpenThermMessage}, + OpenThermEdgeTriggerBus, + SendOpenThermMessage, + ListenOpenThermMessage, +}; +#[cfg(feature = "use_opentherm")] +use boiler::{BoilerControl, Instant, TimeBaseRef}; +#[cfg(feature = "use_opentherm")] +use opentherm_boiler_controller_lib as boiler; + +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use cortex_m_semihosting::hprintln; + +atsamd_hal::bind_interrupts!(struct Irqs { + EIC_EXTINT_5 => atsamd_hal::eic::InterruptHandler; + TC4 => atsamd_hal::timer_capture_waveform::TimerCaptureInterruptHandler; +}); + +atsamd_hal::bind_multiple_interrupts!(struct DmacIrqs { + DMAC: [DMAC_0, DMAC_1, DMAC_2, DMAC_OTHER] => atsamd_hal::dmac::InterruptHandler; +}); + +enum SignalTxSimulation{Ready_} +static CHANNEL: Channel = Channel::new(); + +trait OtMode {} +struct NoneT {} +struct OtTx {} +struct OtRx {} +impl OtMode for OtTx {} +impl OtMode for OtRx {} +impl OtMode for NoneT {} + +#[cfg(feature = "use_opentherm")] +#[embassy_executor::task] +async fn boiler_task() {} + +#[cfg(feature = "use_opentherm")] +mod boiler_implementation { + use crate::dmac::ReadyFuture; + use crate::timer_data_set::PinoutSpecificDataImplTc4; + use atsamd_hal::gpio::G; + use atsamd_hal::gpio::{Alternate, PullUpInput, pin::AnyPin}; + use atsamd_hal::pac::dsu::length; + use atsamd_hal::pac::gclk::genctrl::OeR; + use atsamd_hal::pac::tcc0::per; + use atsamd_hal::pwm_wg::{PwmWgFutureTrait, PwmBaseTrait, PinoutCollapse}; + use atsamd_hal::timer_capture_waveform::{self, TimerCaptureData, TimerCaptureFutureTrait}; + use fugit::MicrosDuration; + use core::any::Any; + use core::marker::PhantomData; + + use super::*; + + trait AtsamdEdgeTriggerCaptureFactory{ + + } + + pub(super) const VEC_SIZE_CAPTURE: usize = 128; + + pub(super) trait CreatePwmPinout { + type PinTxId: PinId; + type PinRxId: PinId; + type PinTx: AnyPin; + type PinRx: AnyPin; + type DmaChannel: dmac::AnyChannel; + type Timer; + type PinoutTx: PinoutCollapse + PinoutNewTrait; + type PinoutRx: PinoutCollapse + PinoutNewTrait; + type PwmBase: PwmBaseTrait = Self::PwmWg>; + type PwmWg: PwmWgFutureTrait; + type TimerCaptureBase: TimerCaptureBaseTrait = Self::TimerCaptureFuture>; + type TimerCaptureFuture: TimerCaptureFutureTrait; + // fn new_pwm_generator<'a>(pin: Self::PinTx, tc: Self::Timer, dma: Self::DmaChannel, mclk: &mut Mclk) -> Self::PwmWg; + // fn collapse(self) -> Self::PinTx; + } + + pub(super) struct AtsamdEdgeTriggerCapture< + PinoutSpecificData: CreatePwmPinout, + M: OtMode = OtTx, + const N: usize = VEC_SIZE_CAPTURE, + > { + tx_pin: Option>>, + rx_pin: Option>>, + + // Pin>`, found `TC4Pinout`` + tx_init_duty_value: u8, + pwm: Option, // one alternative when TX operation + capture_device: Option, // one alternative when RX operation + periph_clock_freq: Hertz, + mode: PhantomData, + pinout: PhantomData, + } + + impl AtsamdEdgeTriggerCapture + where + PinoutSpecificData: CreatePwmPinout, + OtTx: OtMode, + { + pub fn new_with_default( + pin_tx: GpioPin>, + pin_rx: GpioPin>, + tc_timer: PinoutSpecificData::Timer /*pac::Tc4*/, + mclk: &mut Mclk, + input_clock_frequency: Hertz, + pinout_factory: PinoutSpecificData, + dma_channel: PinoutSpecificData::DmaChannel, + ) -> AtsamdEdgeTriggerCapture { + let pwm_tx_pin = pin_tx.into_alternate::(); + + // Enable clocks for capture timer + PinoutSpecificData::TimerCaptureBase::enable_mclk_clocks(mclk); + + let pwm_generator_future = PinoutSpecificData::PwmBase::new_waveform_generator( + input_clock_frequency, + Hertz::from_raw(32), + tc_timer, + PinoutSpecificData::PinoutTx::new_pin(pwm_tx_pin), + Some(mclk), + ).with_dma_channel(dma_channel); + + Self { + tx_pin: None, + rx_pin: Some(pin_rx), + tx_init_duty_value: 0xff, // This determines idle bus state level. TODO: add configuration + pwm: Some(pwm_generator_future), + capture_device: None, + periph_clock_freq: input_clock_frequency, + mode: PhantomData, + pinout: PhantomData, + } + } + } + + impl AtsamdEdgeTriggerCapture + where + PinoutSpecificData: CreatePwmPinout, + { + // Starting with TX as the boiler controller is more common and uses the TX command first + pub fn new( + pin_tx: GpioPin>, + pin_rx: GpioPin>, + tc_timer: PinoutSpecificData::Timer, + periph_clock_freq: Hertz, + dma_channel: PinoutSpecificData::DmaChannel, + ) -> AtsamdEdgeTriggerCapture { + let pwm_tx_pin = pin_tx.into_alternate::(); + + let pwm_generator_future = PinoutSpecificData::PwmBase::new_waveform_generator( + periph_clock_freq, + Hertz::from_raw(32), + tc_timer, + PinoutSpecificData::PinoutTx::new_pin(pwm_tx_pin), + None, + ).with_dma_channel(dma_channel); + + Self { + tx_pin: None, + rx_pin: Some(pin_rx), + tx_init_duty_value: 0xff, // This determines idle bus state level. TODO: add configuration + pwm: Some(pwm_generator_future), + capture_device: None, + periph_clock_freq: periph_clock_freq, + mode: PhantomData, + pinout: PhantomData, + } + } + } + + impl EdgeTriggerTransitiveToCaptureCapable + for AtsamdEdgeTriggerCapture + where + PinoutSpecificData: CreatePwmPinout, + { + type CaptureDevice = AtsamdEdgeTriggerCapture; + fn transition_to_capture_capable_device(self) -> Self::CaptureDevice { + let pwm = self.pwm.unwrap(); + let (dma, tc_timer, pinout) = pwm.decompose(); + let pin_tx = pinout.collapse(); + let pin_tx = pin_tx.into_push_pull_output(); + + AtsamdEdgeTriggerCapture::::new( + pin_tx, + self.rx_pin.unwrap(), + tc_timer, + self.periph_clock_freq, + dma, + ) + } + } + + impl EdgeCaptureTransitiveToTriggerCapable + for AtsamdEdgeTriggerCapture + where + PinoutSpecificData: CreatePwmPinout, + { + type TriggerDevice = AtsamdEdgeTriggerCapture; + fn transition_to_trigger_capable_device(self) -> AtsamdEdgeTriggerCapture { + let (pin_tx, capture_timer) = ( + self.tx_pin.unwrap(), + self.capture_device.unwrap(), + ); + let (dma, tc_timer, pinout_rx) = capture_timer.decompose(); + // let (dma, tc_timer, pinout) = pwm.decompose(); + let pin_rx = pinout_rx.collapse(); + let pin_rx = pin_rx.into_pull_up_input(); + + AtsamdEdgeTriggerCapture::::new( + pin_tx, + pin_rx, + tc_timer, + self.periph_clock_freq, + dma, + ) + } + } + + impl + AtsamdEdgeTriggerCapture + where + PinoutSpecificData: CreatePwmPinout, + { + pub fn new( + pin_tx: GpioPin>, + pin_rx: GpioPin>, + tc_timer: PinoutSpecificData::Timer, + periph_clock_freq: Hertz, + dma_channel: PinoutSpecificData::DmaChannel, + ) -> Self { + let pwm_rx_pin = pin_rx.into_alternate::(); + + let pinout = PinoutSpecificData::PinoutRx::new_pin(pwm_rx_pin); + + let timer_capture = + PinoutSpecificData::TimerCaptureBase::new_timer_capture( + periph_clock_freq, + Hertz::from_raw(32), + tc_timer, + pinout, + None, + ) + .with_dma_channel(dma_channel); // TODO: Channel shall be changed to channel0 later on. This is + // just for prototyping + + Self { + tx_pin: Some(pin_tx), + rx_pin: None, + tx_init_duty_value: 0xff, // This determines idle bus state level. TODO: add configuration + pwm: None, + capture_device: Some(timer_capture), // TODO: Implement the capture device + periph_clock_freq: periph_clock_freq, + mode: PhantomData, + pinout: PhantomData, + } + } + } + + impl EdgeTriggerInterface + for + AtsamdEdgeTriggerCapture + where + PinoutSpecificData: CreatePwmPinout, + { + type OutputSelf = Self; + async fn trigger( + mut self, + iterator: impl Iterator, + period: core::time::Duration, + ) -> (Self, Result<(), TriggerError>) { + + // Invert for hardware adapter compensation: + const INVERT_SIGNAL: bool = true; + // TODO: Implement the period arg usage + let response = match self.pwm.as_mut() { + Some(pwm) => { + // let mut source: [u8; N] = [self.tx_init_duty_value; N]; + // TODO: Actually use the period to set the PWM frequency + pwm.start_regular_pwm(self.tx_init_duty_value); + let dma_future = self + .pwm + .as_mut() + .unwrap() /* TODO: remove runtime panic */ + .start_timer_prepare_dma_transfer::(self.tx_init_duty_value, iterator); + dma_future.await.map_err(|_| TriggerError::GenericError) + } + None => Err(TriggerError::GenericError), + }; + (self, response) + } + } + + impl EdgeCaptureInterface for AtsamdEdgeTriggerCapture + where + PinoutSpecificData: CreatePwmPinout, + { + type OutputSelf = Self; + async fn start_capture>( + mut self, + mut container: OutputType, // fills the container with captured edges or drops it in case of error + timeout_inactive_capture: Duration, + timeout_till_active_capture: Duration, + ) -> (Self, Result<(OutputType, CaptureTypeEdges), CaptureError>) { + /// TODO: + /// 1) Timeout scenario: maybe realized with 32b timer overflow. Will require to set timer overflow event to happen at around 800ms + /// 2) Correct frame finish detection: implemented with reading the timer counter register value in a loop + /// The C++ driver does it by checking number of edges detected to be captured so far by polling in the elements of the DMA buffer array. + /// This element could be improved by reading some internal register of DMA that returns this count, as the array itself is borrowed by DMA and Rust would not allow to read it. + /// In C++ idle bus time is based on the above mentioned edge count by using the independent system timestamp capure mechanism. Maybe that can be improved as well. + // let mut capture_memory: [u32; N] = [0; N]; + let mut data_container: Vec = Vec::new(); + let init_level = self.capture_device.as_mut().unwrap().read_pin_level(); + let result = self.capture_device + .as_mut() + .unwrap() + .start_timer_execute_dma_transfer::<_, N>(data_container).await; + if let Ok(capture_result) = result { + + // let mut timestamps = Vec::::new(); + // // The start of the timer is assumed at counter value equal to zero so the lenght can be set to 0ms of relative capture time. + // let _ = timestamps.push(core::time::Duration::from_nanos(0u64)); + // for (idx, value) in capture_memory.iter().enumerate() { + // // TODO: Fix by using the dma transfer coun instead of using non-zero values condition + // // hprintln!("memory captured[{}] = {}", idx, *value).ok(); + // if *value > 0 { + // let ratio_adjusted = (*value as u64 * 4173) / 1000; // TODO: fix this by some ration compund type + // let _ = timestamps.push(core::time::Duration::from_nanos(ratio_adjusted)); + // } + // } + // let differences: Vec = timestamps + // .iter() + // .zip(timestamps.iter().skip(1)) + // .map(|(a, b)| if b > a {*b - *a} else {core::time::Duration::from_micros(0)}).collect(); + // let mut differences_reverse: Vec = Vec::new(); + // for value in differences.iter().rev() { + // let _ = differences_reverse.push(*value); + // hprintln!("timestamps difference: {}ns", value.as_nanos()).ok(); + // } + // let differences_reverse = differences_reverse; + // match capture_result { + // TimerCaptureResultAvailable::DmaPollReady(timer_value_at_termination) => { + // let _ = timestamps.push(core::time::Duration::from_micros(timer_value_at_termination.get_raw_value() as u64)); + // hprintln!("TimerCaptureResultAvailable::DmaPollReady: {}, lvl:{:?}, N={}", timer_value_at_termination.get_raw_value(), init_level, timestamps.len()).ok(); + // } + // TimerCaptureResultAvailable::TimerTimeout(timer_value_at_termination) => { + // let _ = timestamps.push(core::time::Duration::from_micros(timer_value_at_termination.get_raw_value() as u64)); + // hprintln!("TimerCaptureResultAvailable::TimerTimeout: {}, lvl:{:?}, N={}", timer_value_at_termination.get_raw_value(), init_level, timestamps.len()).ok(); + // } + // } + // // First period is skipped so, we start off with the second period and thus oposite level: + // let level = match init_level { + // true => InitLevel::Low, + // false => InitLevel::High, + // }; + // container.extend(differences_reverse.iter().map(|v| CapturedEdgePeriod::FallingToFalling(*v))); + // (self, Ok((container, CaptureTypeEdges::RisingEdge))) + + match capture_result { + TimerCaptureResultAvailable::DmaPollReady(timer_capture_data) | TimerCaptureResultAvailable::TimerTimeout(timer_capture_data) => { + let timestamps = timer_capture_data.get_data(); + if timestamps.len() < 3 { + hprintln!("TimerCaptureResultAvailable::[dma/timeout] timestamps: {:?}", timestamps).ok(); + return (self, Err(CaptureError::GenericError)); + } + container.extend(timestamps.iter().take(1).map(|v| CapturedEdgePeriod::StartToFirstRising(*v))); + // The middle part without first and last, is RisignToRising: + container.extend(timestamps.iter().skip(1).take(timestamps.len()-2).map(|v| CapturedEdgePeriod::RisingToRising(*v))); + // ... and the last one is RisingToStop: + container.extend(timestamps.iter().skip(timestamps.len()-1).map(|v| CapturedEdgePeriod::RisingToStop(*v))); + // hprintln!("TimerCaptureResultAvailable::TimerTimeout: {:?}, N={}", timestamps, timestamps.len()).ok(); + hprintln!("TimerCaptureResultAvailable::[dma/timeout] last: {:?}", timestamps.iter().last()).ok(); + } + } + + (self, Ok((container, CaptureTypeEdges::RisingEdge))) + } + else { + return (self, Err(CaptureError::GenericError)); + } + } + } + + pub struct AtsamdGpioEdgeTriggerDev { + pin_tx: GpioPin, + } + + impl AtsamdGpioEdgeTriggerDev + { + pub fn new(mut pin: GpioPin) -> Self { + pin.set_high().unwrap(); // idle state + Self{pin_tx: pin} + } + } + + impl EdgeTriggerInterface for AtsamdGpioEdgeTriggerDev { + type OutputSelf = Self; + async fn trigger( mut self, iterator: impl Iterator, + period: core::time::Duration) -> (Self, Result<(), TriggerError>) { + + for (_idx, value) in iterator.enumerate() { + if value + { + self.pin_tx.set_high().unwrap(); // idle state + } + else + { + self.pin_tx.set_low().unwrap(); // idle state + } + + Mono::delay(MicrosDuration::::from_ticks(period.as_micros().try_into().unwrap()).convert()).await; + } + // Always success: + (self, Ok(())) + } + } + + pub(super) struct AtsamdTimeDriver {} + + impl AtsamdTimeDriver { + pub(super) fn new() -> Self { + Self {} + } + } + + impl TimeBaseRef for AtsamdTimeDriver { + fn now(&self) -> Instant { + // Instant { ticks: 0 } + todo!() + } + } + +} + +#[inline] +pub fn check_and_clear_interrupts(flags: InterruptFlags) -> InterruptFlags { + let mut cleared = 0; + let tc4 = unsafe { crate::pac::Peripherals::steal().tc4 }; + tc4.count8().intflag().modify(|r, w| { + cleared = r.bits() & flags.into_bytes()[0]; + unsafe { w.bits(flags.into_bytes()[0]) } + }); + InterruptFlags::from_bytes([cleared]) +} + +#[embassy_executor::task] +async fn toggle_pin_task(mut toggle_pin: GpioPin>) { + loop { + toggle_pin.toggle().unwrap(); + Mono::delay(MillisDuration::::from_ticks(200).convert()).await; + } +} + +mod timer_data_set { +use crate::hal::pwm_wg::{PwmWg4Future, PwmWg2Future}; +// use super::bsp; +use crate::bsp::pac; +use crate::bsp::hal::{ + time::Hertz, + timer_capture_waveform::{TimerCaptureBaseTrait, TimerCapture4Future, TimerCapture4, TimerCapture2Future, TimerCapture2}, + dmac, dmac::ReadyFuture, + pwm_wg::{PwmWg4, PwmWg2, PwmBaseTrait}, + pwm::{TC4Pinout, TC2Pinout, PinoutNewTrait}, + gpio::{Pin, AnyPin, Output, Input, Alternate, OutputConfig, Pins, PushPull, PushPullOutput, Floating}, + gpio::{E, PB08, PB09, PA16, PA17}, +}; +use crate::pac::Mclk; + +pub(super) struct PinoutSpecificDataImplTc4 {} + +impl super::boiler_implementation::CreatePwmPinout for PinoutSpecificDataImplTc4 { + type PinTxId = PB09; + type PinRxId = PB08; + type PinTx = Pin>; + type PinRx = Pin>; + type PinoutTx = TC4Pinout; + type PinoutRx = TC4Pinout; + type DmaChannel = dmac::Channel; + type PwmWg = PwmWg4Future; + type TimerCaptureFuture = TimerCapture4Future; + type TimerCaptureBase = TimerCapture4; + type PwmBase = PwmWg4; + type Timer = pac::Tc4; + + } +pub(super) struct PinoutSpecificDataImplTc2 {} + +impl super::boiler_implementation::CreatePwmPinout for PinoutSpecificDataImplTc2 { + type PinTxId = PA17; + type PinRxId = PA16; + type PinTx = Pin>; + type PinRx = Pin>; + type PinoutTx = TC2Pinout; + type PinoutRx = TC2Pinout; + type DmaChannel = dmac::Channel; + type PwmWg = PwmWg2Future; + type TimerCaptureFuture = TimerCapture2Future; + type TimerCaptureBase = TimerCapture2; + type PwmBase = PwmWg2; + type Timer = pac::Tc2; + + } +} + +#[embassy_executor::main] +async fn main(spawner: embassy_executor::Spawner) { + use boiler_implementation::AtsamdEdgeTriggerCapture; + + let mut peripherals = pac::Peripherals::take().unwrap(); + let core = pac::CorePeripherals::take().unwrap(); + // let core = CorePeripherals::take().unwrap(); + + let pins = Pins::new(peripherals.port); + let dev_dependency_tx_simulation_pin: GpioPin> = pins.pa17.into_push_pull_output(); + let dev_dependency_rx_simulation_pin: GpioPin> = pins.pa16.into_pull_up_input(); + let receiver = CHANNEL.receiver(); + + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.gclk, + &mut peripherals.mclk, + &mut peripherals.osc32kctrl, + &mut peripherals.oscctrl, + &mut peripherals.nvmctrl, + ); + let gclk0 = clocks.gclk0(); + // let mut delay = Delay::new(core.SYST, &mut clocks); + let freq: FugitHertz = clocks.gclk0().into(); + Mono::start(core.SYST, freq.to_Hz()); + + // Initialize DMA Controller + let dmac = DmaController::init(peripherals.dmac, &mut peripherals.pm); + // Turn dmac into an async controller + let mut dmac = dmac.into_future(DmacIrqs); + // Get individual handles to DMA channels + let channels = dmac.split(); + // Initialize DMA Channels 0 and 1 + let mut channel0 = channels.0.init(PriorityLevel::Lvl0); + let mut channel1 = channels.1.init(PriorityLevel::Lvl0); + + let tc2_timer = peripherals.tc2; + + // #[cfg(feature = "use_opentherm")] + // spawner.spawn(full_boiler_opentherm_simulation(dev_dependency_tx_simulation_pin, + // dev_dependency_rx_simulation_pin, + // tc2_timer, channel1, + // &mut peripherals.mclk, + // &clocks.tc2_tc3(&gclk0).unwrap(), + // )).unwrap(); + // #[cfg(feature = "use_opentherm")] + // spawner.spawn(simulate_opentherm_tx(dev_dependency_tx_simulation_pin, receiver)).unwrap(); + + + let mut pwm_tx_pin = pins.pb09.into_push_pull_output(); + // Set initial level of OpenTherm bus to high: + pwm_tx_pin.set_high().unwrap(); + + let pwm_rx_pin = pins.pb08.into_pull_up_input(); + // let _pwm_tx_pin = pins.pb09.into_alternate::(); + let tc4_timer = peripherals.tc4; + + let async_lambda_delay = || async move { + Mono::delay(MillisDuration::::from_ticks(50).convert()).await; + }; + async_lambda_delay().await; + + // let mut user_led: bsp::UserLed = pin_alias!(pins.user_led).into(); + let mut user_led: UserLed = pins.pa15.into(); + // user_led.toggle().unwrap(); + + // let sets = bsp::Pins::new(peripherals.port).split(); + // let mut user_led = sets.user_led.into_push_pull_output(); + user_led.set_low().unwrap(); + + // spawner.spawn(print_capture_timer_state_task()).unwrap(); + + let pinout_specific_data = timer_data_set::PinoutSpecificDataImplTc4{}; + + #[cfg(feature = "use_opentherm")] + let mut edge_trigger_capture_dev = + boiler_implementation::AtsamdEdgeTriggerCapture::<_, _, {boiler_implementation::VEC_SIZE_CAPTURE}>::new_with_default( + pwm_tx_pin, + pwm_rx_pin, + tc4_timer, + &mut peripherals.mclk, + clocks.tc4_tc5(&gclk0).unwrap().freq(), + pinout_specific_data, // determines all the types + channel0, + ); + + let pinout_specific_data_tc2 = timer_data_set::PinoutSpecificDataImplTc2{}; + + #[cfg(feature = "use_opentherm")] + let mut edge_trigger_capture_simulation_device = + boiler_implementation::AtsamdEdgeTriggerCapture::::new_with_default( + dev_dependency_tx_simulation_pin, + dev_dependency_rx_simulation_pin, + tc2_timer, + &mut peripherals.mclk, + clocks.tc2_tc3(&gclk0).unwrap().freq(), + pinout_specific_data_tc2, + channel1, + ); + + // let _time_driver = boiler_implementation::AtsamdTimeDriver::new(); + // let mut boiler_controller = BoilerControl::new(edge_trigger_capture_dev, time_driver); + // let _ = boiler_controller.set_point(Temperature::Celsius(16)); + + // Driver bringup temporary code: + // Idea is to configure additional PIN and connect it to the TC4 timer to capture the signal + // Pin toggle can be done in independent task + // spawner.spawn(toggle_pin_task(dev_dependency_tx_simulation_pin)).unwrap(); + + hprintln!("main:: loop{} start:").ok(); + + let (mut edge_trigger_capture_dev, _) = edge_trigger_capture_dev.send_open_therm_message( + OpenThermMessage::try_new_from_u32(0b0_000_0000_00000001_00100101_00000000_u32).unwrap()).await; + + let mut edge_trigger_capture_dev = edge_trigger_capture_dev.transition_to_capture_capable_device(); + let sender_trigger_tx_sequence = CHANNEL.sender(); + + Mono::delay(MillisDuration::::from_ticks(5000).convert()).await; + + let mut count_all = 0; + let mut count_success = 0; + loop { + // Test one round of TX(simulation) -> RX(production) + let tx_async_simulation = async { + sender_trigger_tx_sequence.send(SignalTxSimulation::Ready_).await; + let (device, result) = + edge_trigger_capture_simulation_device.send_open_therm_message( + OpenThermMessage::try_new_from_u32(0b0_000_0000_00000001_00100101_00000000_u32).unwrap()).await; + device + }; + + let rx_async = async |&count_all| { + let dur = Duration::from_millis(100); + let start_time = Mono::now(); + let (tx_device, result) = + edge_trigger_capture_dev. + listen_open_therm_message()/* -> (Self, Result)*/.await; + let duration = Mono::now() - start_time; + match result { + Ok(message) => { + count_success += 1; + hprintln!("Capture finished with opentherm message: {}, took: {}, rate: {}/{}", message, duration, count_success, count_all).ok(); + }, + Err(e) => { + hprintln!("Capture finished with error on OpenThermMessage: {}, took: {}, rate: {}/{}", e, duration, count_success, count_all).ok(); } + } + tx_device + }; + + count_all += 1; + hprintln!("start: {}", count_all).ok(); + // TODO: how to join the two futures and return devices as a result after execution? + let (tx_result, rx_result) = + embassy_futures::join::join(tx_async_simulation, rx_async(&count_all)).await; + hprintln!("stop: {}", count_all).ok(); + + // TODO: Count successes and errors. + + edge_trigger_capture_dev = rx_result; + edge_trigger_capture_simulation_device = tx_result; + } + + // let mut edge_trigger_capture_dev = rx_result; + + // let time_d = core::time::Duration::from_millis(100); + // let time_d :u32 = time_d.as_millis().try_into().unwrap(); + // hprintln!("Start Capture: {}", time_d).ok(); + + // let mut count_iterations: u32 = 0; + // loop { + // Mono::delay(MillisDuration::::from_ticks(200).convert()).await; + // let device = edge_trigger_capture_dev; + // // hprintln!("Wait long before starting the capture").ok(); + // // Mono::delay(MillisDuration::::from_ticks(500).convert()).await; + // hprintln!("Start Capture {}", count_iterations).ok(); + // let dur = Duration::from_millis(100); + // // trigger gpio simulation of the OpenTherm TX message + // sender_trigger_tx_sequence.send(SignalTxSimulation::Ready_).await; + // let (rx_device, result) = + // device.start_capture(dur, dur).await; + + // if let Ok((level, vector)) = result { + // hprintln!("Capture finished with: {}", vector.len()).ok(); + // let differences: Vec = vector + // .iter() + // .zip(vector.iter().skip(1)) + // .map(|(a, b)| if b > a {b.as_micros() - a.as_micros()} else {0}).collect(); + + // // for (i, v) in differences.into_iter().enumerate() { + // // hprintln!("{}:{} us", i, v).ok(); + // // } + // } + // hprintln!("Finish Capture {}", count_iterations).ok(); + // Mono::delay(MillisDuration::::from_ticks(50).convert()).await; + + // hprintln!("Start FullOpentherm Capture {}", count_iterations).ok(); + // let dur = Duration::from_millis(100); + // // trigger gpio simulation of the OpenTherm TX message + // sender_trigger_tx_sequence.send(SignalTxSimulation::Ready_).await; + // let (rx_device, result) = + // rx_device.listen_open_therm_message().await; + // if let Ok(message) = result { + // hprintln!("Capture finished with opentherm message: {}", message).ok(); + // } + // else + // { + // // TODO: extend the analysis of what is wrong with this capture here: + // hprintln!("Capture finished with error on OpenThermMessage").ok(); + // } + // hprintln!("Finish Capture {}", count_iterations).ok(); + // Mono::delay(MillisDuration::::from_ticks(50).convert()).await; + + // hprintln!("Start Trigger {}", count_iterations).ok(); + // let tx_device = rx_device.transition_to_trigger_capable_device(); + // let (tx_device, result) = + // tx_device.send_open_therm_message(OpenThermMessage::try_new_from_u32(0b0_000_0000_00000001_00100101_00000000_u32).unwrap()).await; + + // edge_trigger_capture_dev = tx_device.transition_to_capture_capable_device(); + // // let _ = boiler_controller.process().await.unwrap(); + + // user_led.toggle().unwrap(); + // count_iterations += 1; + // } + + // let _ot_rx: GpioPin<_, PullUpInterrupt> = pins.pb08.into(); // D0 + // // let pb_09_ot_tx: GpioPin<_, PushPullOutput> = pins.pb09.into(); // D1 + // // let capture_device = RpEdgeCapture::new(async_input); + // // let mut open_therm_bus = AtsamdEdgeTriggerCapture::new(pb_09_ot_tx); + // let example_vector = heapless::Vec::::from_slice(&[ + // true, true, false, true, false, true, false, false, true, false, false, + // ]) + // .unwrap(); + // // let _ = open_therm_bus.trigger(example_vector.into_iter(), Duration::from_millis(100)); + + // #[cfg(feature = "use_opentherm")] + // let time_driver = AtsamdTimeDriver::new(); + // // let mut boiler_controller = BoilerControl::new(open_therm_bus, time_driver); + // // let _ = boiler_controller.set_point(Temperature::Celsius(16)); + // // let _ = boiler_controller.enable_ch(CHState::Enable(true)); +} + +#[bitfield] +#[repr(u8)] +#[derive(Clone, Copy)] +pub struct InterruptFlags { + /// Overflow + pub ovf: bool, + /// Err + pub err: bool, + #[skip] + _reserved: B6, +} + +impl Default for InterruptFlags { + fn default() -> Self { + Self::new() + } +} + +// Dummy Waker implementation for no_std environment. +static RAW_WAKER_VTABLE: core::task::RawWakerVTable = core::task::RawWakerVTable::new( + |ptr: *const ()| RawWaker::new(ptr, &RAW_WAKER_VTABLE), + |_: *const ()| {}, // Do nothing, for simulation + |_: *const ()| {}, // Do nothing, for simulation + |_: *const ()| {}, +); + +unsafe fn raw_waker(waker_ptr: *const ()) -> Waker { + Waker::from_raw(RawWaker::new(waker_ptr, &RAW_WAKER_VTABLE)) +} + +/// The idea here is to use simpler to implement TX driver using gpio timer to ease on +/// implementation of the OpenTherm RX driver based on timer + DMA +#[embassy_executor::task] +async fn simulate_opentherm_tx(mut tx_pin: GpioPin>, + receiver: Receiver<'static, ThreadModeRawMutex, SignalTxSimulation, 64>) +{ + let hw_dev = boiler_implementation::AtsamdGpioEdgeTriggerDev::new(tx_pin); + const DURATION_MS: u32 = 500; + + // Give it some time before fire-up the + Mono::delay(MillisDuration::::from_ticks(50).convert()).await; + // TODO: implement heapless queue receiving requests + let mut pass_dev_in_loop = hw_dev; + loop { + // Comment this out to have on demand instead of periodic transfer: + let _received = receiver.receive().await; + + // hprintln!("channel::RX").ok(); + // tx_pin.toggle().unwrap(); + // Wait for the receiver to be ready, give it some time to setup the capture + Mono::delay(MillisDuration::::from_ticks(10).convert()).await; + // The device implements the trigger interface, it shall implement send as well: + let (dev, result) = + pass_dev_in_loop.send_open_therm_message(OpenThermMessage::try_new_from_u32(0b0_000_0000_00000001_00100101_00000000_u32).unwrap()).await; + pass_dev_in_loop = dev; + // Mono::delay(MillisDuration::::from_ticks(DURATION_MS).convert()).await; + } +} + +#[embassy_executor::task] +async fn print_capture_timer_state_task(/*mut uart_tx: UartFutureTxDuplexDma, Ch1>*/) +{ + let tc4_readonly = unsafe { crate::pac::Peripherals::steal().tc4 }; + let count32 = tc4_readonly .count32(); + let dmac_readonly = unsafe { crate::pac::Peripherals::steal().dmac }; + // let mut value_cc1 = 0x00u8; + loop { + // Read this value: + // let vcc1 = tc4_readonly.count8().cc(1).read().bits(); + // if vcc1 != value_cc1 { + // hprintln!("tc4.cc1:0x{:08X}", vcc1).ok(); + // value_cc1 = vcc1; + // } + + // uart_tx.write(b"Hello, world!").await.unwrap(); + // defmt::info!("Sent 10 bytes"); + + // let mut delay = Delay::new(core.SYST, &mut clocks); + { // Read counter one by one to see if it is running: + let _ = count32.ctrlbset().write(|w| w.cmd().readsync()); + let cnt_value = count32.count().read().bits(); + let _ = count32.ctrlbset().write(|w| w.cmd().readsync()); + let cn2_value = count32.count().read().bits(); + // hprintln!("cnt:0x{:08X}, 0x{:08X}", cnt_value, cn2_value).ok(); + } + + hprintln!("tc4int:0x{:08X}", count32.intflag().read().bits()).ok(); + // hprintln!("tc4cc0:0x{:08X}", count32.cc(0).read().bits()).ok(); + //hprintln!("tc4ctrla:0x{:08X}", count32.ctrla().read().bits()).ok(); + //hprintln!("tc4evctrl:0x{:08X}", count32.evctrl().read().bits()).ok(); + // hprintln!("tc4per:0x{:08X}", tc4_readonly.count8().per().read().bits()).ok(); + // hprintln!("dmaact:0x{:08X}", dmac_readonly.active().read().bits()).ok(); + // let btcnt = dmac_readonly.active().read().btcnt() ).ok(); + // hprintln!("dmaact:0x{:08X}", dmac_readonly.active().read().btcnt().bits() ).ok(); + //hprintln!("dmactrl:0x{:08X}", dmac_readonly.ctrl().read().bits()).ok(); + //hprintln!("dmabusy:0x{:08X}", dmac_readonly.busych().read().bits()).ok(); + //hprintln!("dmachint:0x{:08X}", dmac_readonly.intstatus().read().bits()).ok(); + //hprintln!("dmachintpend:0x{:08X}", dmac_readonly.intpend().read().bits()).ok(); + //hprintln!("ch[0]chctrla:0x{:08X}", dmac_readonly.channel(0).chctrla().read().bits()).ok(); + //hprintln!("ch[0]chint:0x{:08X}", dmac_readonly.channel(0).chintflag().read().bits()).ok(); + //hprintln!("ch[0]chstat:0x{:08X}", dmac_readonly.channel(0).chstatus().read().bits()).ok(); + + // let flags_to_check = InterruptFlags::new().with_ovf(true).with_err(true); + // if check_and_clear_interrupts(flags_to_check).ovf() { + // // hprintln!("Overflow detected").ok(); + // } + + // delay.delay_ms(200u16); + Mono::delay(MillisDuration::::from_ticks(2000).convert()).await; + } +} +#[embassy_executor::task] +async fn print_timer_state_task(/*mut uart_tx: UartFutureTxDuplexDma, Ch1>*/) +{ + let tc4_readonly = unsafe { crate::pac::Peripherals::steal().tc4 }; + let dmac_readonly = unsafe { crate::pac::Peripherals::steal().dmac }; + let mut value_cc1 = 0x00u8; + loop { + // Read this value: + let vcc1 = tc4_readonly.count8().cc(1).read().bits(); + if vcc1 != value_cc1 { + hprintln!("tc4.cc1:0x{:08X}", vcc1).ok(); + value_cc1 = vcc1; + } + + // uart_tx.write(b"Hello, world!").await.unwrap(); + // defmt::info!("Sent 10 bytes"); + + // let mut delay = Delay::new(core.SYST, &mut clocks); + let _ = tc4_readonly + .count8() + .ctrlbset() + .write(|w| w.cmd().readsync()); + let cnt_value = tc4_readonly.count8().count().read().bits(); + let _ = tc4_readonly + .count8() + .ctrlbset() + .write(|w| w.cmd().readsync()); + let cn2_value = tc4_readonly.count8().count().read().bits(); + + hprintln!("cnt:0x{:08X}, 0x{:08X}", cnt_value, cn2_value).ok(); + // hprintln!( + // "tc4int:0x{:08X}, cc1:0x{:08X}", + // tc4_readonly.count8().intflag().read().bits(), + // tc4_readonly.count8().cc(1).read().bits() + // ) + // .ok(); + // hprintln!("tc4per:0x{:08X}", tc4_readonly.count8().per().read().bits()).ok(); + // hprintln!("dma:0x{:08X}", dmac_readonly.active().read().bits()).ok(); + + let flags_to_check = InterruptFlags::new().with_ovf(true).with_err(true); + if check_and_clear_interrupts(flags_to_check).ovf() { + // hprintln!("Overflow detected").ok(); + } + + // delay.delay_ms(200u16); + Mono::delay(MillisDuration::::from_ticks(500).convert()).await; + } +} + +#[embassy_executor::task] +async fn full_boiler_opentherm_simulation(tx_pin: GpioPin>, mut rx_pin: GpioPin>, + timer: pac::Tc2, dma_channel: hal::dmac::Channel, + mclk: &'static mut Mclk, + timer_clocks: &'static Tc2Tc3Clock) +{ + // let mut edge_trigger_capture_dev = + // boiler_implementation::AtsamdEdgeTriggerCapture::new_with_default( + // tx_pin, + // rx_pin, + // timer, + // &mclk, + // &timer_clocks, + // dma_channel, + // ); + loop { + Mono::delay(MillisDuration::::from_ticks(500).convert()).await; + } +} diff --git a/boards/wio_terminal/memory.x b/boards/wio_terminal/memory.x index 3ff115fb881f..aa2b9deffb71 100644 --- a/boards/wio_terminal/memory.x +++ b/boards/wio_terminal/memory.x @@ -1,7 +1,7 @@ MEMORY { /* Leave 16k for the default bootloader on the Wio Terminal */ - FLASH (rx) : ORIGIN = 0x00000000 + 16K, LENGTH = 512K - 16K + FLASH (rx) : ORIGIN = 0x00000000 + 0K, LENGTH = 512K - 16K RAM (rxw) : ORIGIN = 0x20000000, LENGTH = 192K } _stack_start = ORIGIN(RAM) + LENGTH(RAM); diff --git a/boards/wio_terminal/openocd.gdb b/boards/wio_terminal/openocd.gdb new file mode 100644 index 000000000000..98102b300991 --- /dev/null +++ b/boards/wio_terminal/openocd.gdb @@ -0,0 +1,13 @@ +target remote :3333 + +set print asm-demangle on + +load + +mon reset halt + +break DefaultHandler + +break HardFault + +break rust_begin_unwind diff --git a/boards/wio_terminal/setup_timer.gdb b/boards/wio_terminal/setup_timer.gdb new file mode 100644 index 000000000000..0d94be3b6423 --- /dev/null +++ b/boards/wio_terminal/setup_timer.gdb @@ -0,0 +1,38 @@ +printf "MCLK reg:\n" +x /xw 0x4000081c + +# enable de-assert enable signal: +mon mww 0x42001400 0x604 +shell sleep 1 +# enable assert SWRT: +mon mww 0x42001400 0x605 +shell sleep 1 +# Change mode to 32-bits +mon mww 0x42001400 0x608 +shell sleep 1 +# enable COPEN +mon mww 0x42001400 0x10608 +shell sleep 1 +# enable CAPTEN +mon mww 0x42001400 0x110608 +shell sleep 1 +# enable timer: +mon mww 0x42001400 0x11060A +shell sleep 1 +# enable timer when DBG +mon mwb 0x42001409 0x01 +# Capture data: +mon mwb 0x42001405 0x80 +# read counter: +x /xw 0x42001414 + +set $counter = 0 +while $counter < 5 + printf "iter: %d\n", $counter + # Capture data: + mon mwb 0x42001405 0x80 + # read counter: + x /xw 0x42001414 + shell sleep 0.1 + set $counter = $counter + 1 +end diff --git a/boards/wio_terminal/src/buttons.rs b/boards/wio_terminal/src/buttons.rs index f4ba9bcede55..c8e0311123ed 100644 --- a/boards/wio_terminal/src/buttons.rs +++ b/boards/wio_terminal/src/buttons.rs @@ -37,7 +37,7 @@ impl ButtonPins { ) -> ButtonController { let gclk1 = clocks.gclk1(); let eic_clock = clocks.eic(&gclk1).unwrap(); - let mut eic = eic::Eic::new(mclk, eic_clock, eic); + let mut eic = eic::Eic::new(mclk, &eic_clock, eic); // Unfortunately, the pin assigned to B1 shares the same // ExtInt line as up on the joystick. As such, we don't diff --git a/boards/wio_terminal/src/display.rs b/boards/wio_terminal/src/display.rs index d70f565e20ec..680bd648674b 100644 --- a/boards/wio_terminal/src/display.rs +++ b/boards/wio_terminal/src/display.rs @@ -7,7 +7,7 @@ use atsamd_hal::ehal_nb::serial::Write; use atsamd_hal::pac::Mclk; use atsamd_hal::sercom::spi; use atsamd_hal::sercom::spi::Spi; -use atsamd_hal::sercom::{IoSet4, Sercom7}; +use atsamd_hal::sercom::Sercom7; use atsamd_hal::time::Hertz; use atsamd_hal::typelevel::NoneT; use display_interface_spi::SPIInterface; @@ -40,7 +40,7 @@ pub struct Display { pub backlight: LcdBacklightReset, } -pub type LcdPads = spi::Pads; +pub type LcdPads = spi::Pads; pub type LcdSpi = spi::PanicOnRead, spi::Tx>>; pub type LcdDevice = bspi::ExclusiveDevice; diff --git a/boards/wio_terminal/src/sensors.rs b/boards/wio_terminal/src/sensors.rs index 0533aa294514..96a51281a037 100644 --- a/boards/wio_terminal/src/sensors.rs +++ b/boards/wio_terminal/src/sensors.rs @@ -3,7 +3,7 @@ use atsamd_hal::clock::GenericClockController; use atsamd_hal::pac::gclk::pchctrl::Genselect::Gclk11; use atsamd_hal::pac::{Adc1, Mclk}; use atsamd_hal::prelude::*; -use atsamd_hal::sercom::{i2c, IoSet3, Sercom4}; +use atsamd_hal::sercom::{i2c, Sercom4}; use lis3dh::{Lis3dh, SlaveAddr}; @@ -21,7 +21,7 @@ pub struct Accelerometer { /// I2C pads for the labelled I2C peripheral /// /// You can use these pads with other, user-defined [`i2c::Config`]urations. -pub type I2cPads = i2c::Pads; +pub type I2cPads = i2c::Pads; impl Accelerometer { /// Initialize the LIS3DH accelerometer using the correct pins and @@ -56,17 +56,18 @@ pub struct LightSensor { pub pd1: LightSensorAdcReset, } -impl LightSensor { - /// Initialize Pd1 as an ADC input, and return a Tuple containing the ADC - /// peripheral and the configured pin. - pub fn init( - self, - adc: Adc1, - clocks: &mut GenericClockController, - mclk: &mut Mclk, - ) -> (Adc, LightSensorAdc) { - let adc1 = Adc::adc1(adc, mclk, clocks, Gclk11); - - (adc1, self.pd1.into()) - } -} +// impl LightSensor { +// /// Initialize Pd1 as an ADC input, and return a Tuple containing the ADC +// /// peripheral and the configured pin. +// pub fn init( +// self, +// adc: Adc1, +// clocks: &mut GenericClockController, +// mclk: &mut Mclk, +// ) -> (Adc, LightSensorAdc) { +// todo!() +// // let adc1 = Adc::adc1(adc, mclk, clocks, Gclk11); +// +// // (adc1, self.pd1.into()) +// } +// } diff --git a/boards/wio_terminal/src/serial.rs b/boards/wio_terminal/src/serial.rs index d4ec95339691..29bafb8919fe 100644 --- a/boards/wio_terminal/src/serial.rs +++ b/boards/wio_terminal/src/serial.rs @@ -1,6 +1,6 @@ use atsamd_hal::clock::GenericClockController; use atsamd_hal::pac::{self, Mclk}; -use atsamd_hal::sercom::{uart, IoSet2, Sercom2}; +use atsamd_hal::sercom::{uart, Sercom2}; use atsamd_hal::time::Hertz; #[cfg(feature = "usb")] @@ -20,7 +20,7 @@ pub struct Uart { } /// UART pads for the labelled RX & TX pins -pub type UartPads = uart::Pads; +pub type UartPads = uart::Pads; /// UART device for the labelled RX & TX pins pub type HalUart = uart::Uart, uart::Duplex>; diff --git a/boards/wio_terminal/src/sound.rs b/boards/wio_terminal/src/sound.rs index 0072b6879908..b83f218939f7 100644 --- a/boards/wio_terminal/src/sound.rs +++ b/boards/wio_terminal/src/sound.rs @@ -43,17 +43,18 @@ pub struct Microphone { pub mic: MicOutputReset, } -impl Microphone { - /// Initialize Pd1 as an ADC input, and return a Tuple containing the ADC - /// peripheral and the configured pin. - pub fn init( - self, - adc: Adc1, - clocks: &mut GenericClockController, - mclk: &mut Mclk, - ) -> (Adc, MicOutput) { - let adc1 = Adc::adc1(adc, mclk, clocks, Gclk11); - - (adc1, self.mic.into()) - } -} +// impl Microphone { +// /// Initialize Pd1 as an ADC input, and return a Tuple containing the ADC +// /// peripheral and the configured pin. +// pub fn init( +// self, +// adc: Adc1, +// clocks: &mut GenericClockController, +// mclk: &mut Mclk, +// ) -> (Adc, MicOutput) { +// todo!() +// // let adc1 = Adc::adc1(adc, mclk, clocks, Gclk11); +// +// // (adc1, self.mic.into()) +// } +// } diff --git a/boards/wio_terminal/src/storage.rs b/boards/wio_terminal/src/storage.rs index 1de85b42e0f2..6b9f729cf34a 100644 --- a/boards/wio_terminal/src/storage.rs +++ b/boards/wio_terminal/src/storage.rs @@ -6,7 +6,7 @@ use atsamd_hal::{ pac::{Mclk, Qspi}, prelude::*, qspi, - sercom::{spi, IoSet1, Sercom6}, + sercom::{spi, Sercom6}, time::Hertz, typelevel::NoneT, }; @@ -62,7 +62,7 @@ pub struct SDCard { pub det: SdDetReset, } -pub type SdPads = spi::Pads; +pub type SdPads = spi::Pads; pub type SdSpi = spi::Spi, spi::Duplex>; type Controller = embedded_sdmmc::VolumeManager< diff --git a/boards/wio_terminal/src/wifi.rs b/boards/wio_terminal/src/wifi.rs index 41c8905dd04c..86b27e155f71 100644 --- a/boards/wio_terminal/src/wifi.rs +++ b/boards/wio_terminal/src/wifi.rs @@ -4,7 +4,7 @@ use atsamd_hal::{ ehal::digital::OutputPin, pac::{interrupt, Mclk}, prelude::*, - sercom::{uart, IoSet2, Sercom0}, + sercom::{uart, Sercom0}, }; use bbqueue::{self, BBBuffer, Consumer, Producer}; @@ -46,7 +46,7 @@ pub struct Wifi { } /// UART pads for the labelled RX & TX pins -pub type WifiUartPads = uart::Pads; +pub type WifiUartPads = uart::Pads; /// UART device for the labelled RX & TX pins pub type WifiUart = uart::Uart, uart::Duplex>; diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 44aec215315b..a2586c77ff49 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -54,6 +54,7 @@ seq-macro = "0.3" sorted-hlist = "0.2.0" typenum = "1.12.0" void = {version = "1.0", default-features = false} +pin-project = {version = "1.1.9", default-features = false} #=============================================================================== # Optional depdendencies diff --git a/hal/src/peripherals/mod.rs b/hal/src/peripherals/mod.rs index 78eb4048a08c..7982281ca385 100644 --- a/hal/src/peripherals/mod.rs +++ b/hal/src/peripherals/mod.rs @@ -31,6 +31,16 @@ pub mod usb {} )] pub mod pwm {} +#[hal_module( + "clock-d5x" => "pwm_waveform/d5x.rs", +)] +pub mod pwm_wg {} + +#[hal_module( + "clock-d5x" => "timer_capture_waveform/d5x.rs", +)] +pub mod timer_capture_waveform {} + #[hal_module( any("clock-d11", "clock-d21") => "clock/d11.rs", "clock-d5x" => "clock/d5x/mod.rs", diff --git a/hal/src/peripherals/pwm/d5x.rs b/hal/src/peripherals/pwm/d5x.rs index 6b500ea1a6bd..214c8becc51a 100644 --- a/hal/src/peripherals/pwm/d5x.rs +++ b/hal/src/peripherals/pwm/d5x.rs @@ -11,6 +11,19 @@ use crate::timer_params::TimerParams; // Timer/Counter (TCx) +pub trait PinoutCollapse { + type PinId : PinId; + // type Pin = Pin; + + fn collapse(self) -> Pin; + // fn new_pin(pin: impl AnyPin) -> Self; +} +pub trait PinoutNewTrait { + fn new_pin(pin: Pin>) -> Self; +} +pub trait PinoutReadLevel { + fn read_level(&self) -> bool; +} /// This is a major syntax hack. /// /// The previous Pinout types were enums that took specific v1::Pin types. As a @@ -40,15 +53,46 @@ macro_rules! impl_tc_pinout { _pin: Pin, } + impl PinoutReadLevel for $Type { + fn read_level(&self) -> bool { + self._pin._is_high() + } + } + + impl PinoutCollapse for $Type { + type PinId = I; + fn collapse(self) -> Pin { + self._pin + } + } + $( + // $( #[$attr] )? + // impl $Type<$Id> { // those are specializations similar to C++ template specializations + // #[inline] + // pub fn new_pin(pin: impl AnyPin) -> Self { + // let _pin = pin.into().into_alternate(); + // Self { _pin } + // } + // } + + $( #[$attr] )? - impl $Type<$Id> { + impl PinoutNewTrait<$Id> for $Type<$Id> { // those are specializations similar to C++ template specializations #[inline] - pub fn $func(pin: impl AnyPin) -> Self { - let _pin = pin.into().into_alternate(); - Self { _pin } + fn new_pin(pin: Pin<$Id, Alternate>) -> Self { + Self { _pin: pin } } } + + + // Where should this be implemented? TODO: + // $( #[$attr] )? + // impl PinoutCollapse for $Type<$Id> { // those are specializations similar to C++ template specializations + // fn collapse(self) -> Pin<$Id, AlternateE> { + // self._pin + // } + // } )+ }; } @@ -78,7 +122,9 @@ impl_tc_pinout!(TC2Pinout: [ #[hal_cfg("pa13")] (Pa13, PA13), #[hal_cfg("pa17")] - (Pa17, PA17) + (Pa17, PA17), + #[hal_cfg("pa16")] + (Pa16, PA16) ]); #[hal_cfg("tc3")] @@ -93,6 +139,8 @@ impl_tc_pinout!(TC3Pinout: [ impl_tc_pinout!(TC4Pinout: [ #[hal_cfg("pa23")] (Pa23, PA23), + #[hal_cfg("pb08")] + (Pb8, PB08), #[hal_cfg("pb09")] (Pb9, PB09), #[hal_cfg("pb13")] diff --git a/hal/src/peripherals/pwm_waveform/d5x.rs b/hal/src/peripherals/pwm_waveform/d5x.rs new file mode 100644 index 000000000000..f14f9c26465c --- /dev/null +++ b/hal/src/peripherals/pwm_waveform/d5x.rs @@ -0,0 +1,403 @@ +#![allow(non_snake_case)] + +use atsamd_hal_macros::hal_cfg; + +#[cfg(feature = "async")] +use crate::dmac::ReadyFuture; +use crate::dmac::{AnyChannel, Beat, Buffer, Error as DmacError, TriggerAction, TriggerSource}; +// use crate::gpio::*; +use crate::gpio::PinId; +use crate::pac::Mclk; +use crate::time::Hertz; +use crate::timer_params::TimerParams; + +use paste::paste; + +#[derive(Clone)] +pub struct PwmWaveformGeneratorPtr(pub(in super::super) *mut T); + +unsafe impl Buffer for PwmWaveformGeneratorPtr { + type Beat = T; + + #[inline] + fn dma_ptr(&mut self) -> *mut Self::Beat { + self.0 + } + + #[inline] + fn incrementing(&self) -> bool { + false + } + + #[inline] + fn buffer_len(&self) -> usize { + 1 + } +} + +pub use crate::pwm::PinoutCollapse; +pub trait PwmWgFutureTrait { + type DmaChannel: AnyChannel; + type TC; + type Pinout: PinoutCollapse; + + fn decompose(self) -> (Self::DmaChannel, Self::TC, Self::Pinout); + fn start_regular_pwm(&mut self, ccx_value: u8); + async fn start_timer_prepare_dma_transfer( + &mut self, + ccx_value:u8, + generation_pattern_iter: impl Iterator) + -> Result<(), DmacError>; +} +pub trait PwmBaseTrait { + type TC; + type Pinout: PinoutCollapse; + type ConvertibleToFuture: PwmWgFutureTrait + where + D: AnyChannel; + + // type Future: PwmWgFutureTrait; + + /// Create a new PWM Waveform Generator + fn new_waveform_generator( + clock_freq: Hertz, + freq: Hertz, + tc: Self::TC, + pinout: Self::Pinout, + mclk: Option<&mut Mclk>, + ) -> Self; + fn with_dma_channel(self, channel: CH) -> Self::ConvertibleToFuture + where + CH: AnyChannel; +} +// Timer/Counter (TCx) +// +macro_rules! pwm_wg { + ($($TYPE:ident: ($TC:ident, $pinout:ident, $clock:ident, $apmask:ident, $apbits:ident, $wrapper:ident, $event:ident)),+) => { + $( + +use crate::pwm::$pinout; + +pub struct $TYPE { + /// The frequency of the attached clock, not the period of the pwm. + /// Used to calculate the period of the pwm. + clock_freq: Hertz, + requested_freq: Hertz, + tc: crate::pac::$TC, + #[allow(dead_code)] + pinout: $pinout, + // _channel: Option, +} + +paste!{ +pub struct [<$TYPE Future>]>{ + base_pwm: $TYPE, + _channel: DmaCh, + _init_level: u8, +} + +impl> [<$TYPE Future>] { + fn get_init_level(&self) -> u8 { + self._init_level + } + +} + +impl> PwmWgFutureTrait for [<$TYPE Future>] { + type DmaChannel = DmaCh; + type TC = crate::pac::$TC; + type Pinout = $pinout; + + async fn start_timer_prepare_dma_transfer(&mut self, ccx_value:u8, generation_pattern_iter: impl Iterator) + -> Result<(), DmacError> { + + let init_level = self.get_init_level(); + let mut generation_pattern_dma: [u8; N] = [init_level; N]; + for (idx, value) in generation_pattern_iter.enumerate() { + // TODO: move it to the right because for a reason it is not visisble on the wire + // plus resolve the initial driver state. Before the first TX it is low instead of high. + let idx = idx + 2; + if idx >= N { + break; + } + // Implement conditional inversion of the signal: + let value = value != INVERT; + // TODO: Implement configurable idle bus state level + let level = if value { 0xffu8 } else { 0x00u8 }; + generation_pattern_dma[idx] = level; + // hprintln!("trigger::source[{}]: {}", idx, level).ok(); + } + + let count = self.base_pwm.tc.count8(); + + count.cc(0).write(|w| unsafe { w.bits(0x00u8) }); + while count.syncbusy().read().cc0().bit_is_set() {} + count.cc(1).write(|w| unsafe { w.bits(ccx_value) }); + while count.syncbusy().read().cc1().bit_is_set() {} + count.ccbuf(0).write(|w| unsafe { w.bits(0x00u8) }); + count.ccbuf(1).write(|w| unsafe { w.bits(ccx_value) }); + + let pwm_dma_address = self.base_pwm.get_dma_ptr(); + let dma_future = self._channel.as_mut().transfer_future( + &mut generation_pattern_dma, + pwm_dma_address, + TriggerSource::$event, + TriggerAction::Burst, + ); + // Rest of the setup shall go into poll method: i.e. enabling interrupts and the counter + // of the timer. + count.ctrla().modify(|_, w| w.enable().set_bit()); + while count.syncbusy().read().enable().bit_is_set() {} + + // First poll the future starts the DMA transfer. It sets enable bit of the DMA + let value_to_return = dma_future.await; + + count.cc(1).write(|w| unsafe { w.bits(ccx_value) }); + count.ccbuf(1).write(|w| unsafe { w.bits(ccx_value) }); + + value_to_return + } + fn start_regular_pwm(&mut self, ccx_value: u8) { + self._init_level = ccx_value; + let count = self.base_pwm.tc.count8(); + count.cc(0).write(|w| unsafe { w.bits(0x00u8) }); + while count.syncbusy().read().cc0().bit_is_set() {} + count.cc(1).write(|w| unsafe { w.bits(ccx_value) }); + while count.syncbusy().read().cc1().bit_is_set() {} + + count.ccbuf(0).write(|w| unsafe { w.bits(0x00u8) }); + count.ccbuf(1).write(|w| unsafe { w.bits(ccx_value) }); + + count.ctrla().modify(|_, w| w.enable().set_bit()); + while count.syncbusy().read().enable().bit_is_set() {} + } + + fn decompose(self) -> (Self::DmaChannel, Self::TC, Self::Pinout) + { + let $TYPE{clock_freq, requested_freq, tc, pinout} = self.base_pwm; + (self._channel, tc, pinout) + } +} + +/* +/// +/// PRESCALER = 6, in reference project written in C++ +/// cc = 233 +/// per = 233 +/// +/// As the timer is set to produce idle level signal, it can be started before +/// we start the DMA transfer. It will naturally pick and start from the +/// beginning of next cycle of the timer. Timer is configured to prodduce +/// constant period signal by setting PER register to a value that corresponds +/// to requested frequency of the manchester signal. Base of the working +/// principle is that the timer CCx register will be loaded with either 0x00 +/// of 0xFF to produce either full cycle high or low signal. +*/ +impl PwmBaseTrait for $TYPE { + type TC = crate::pac::$TC; + type Pinout = $pinout; + type ConvertibleToFuture = [<$TYPE Future>] where + D: AnyChannel; + + fn new_waveform_generator( + clock_freq: Hertz, + freq: Hertz, + tc: Self::TC, + pinout: Self::Pinout, + mclk: Option<&mut Mclk>, + ) -> Self { + const TIEMR_PERIOD: u8 = 233; // mclk / 256 / 233 = 1000 Hz + let count = tc.count8(); + let tc_ccbuf_dma_data_register_address = tc.count8().ccbuf(1).as_ptr() as *const (); + // let PwmWaveformGeneratorPtr()(pub(in super::super) *mut T); + + // write(|w| w.ccbuf().bits(duty as u8)); + let _params = TimerParams::new(freq.convert(), clock_freq); + // Works as a mask: you can only enable the clock not disable it. TODO: check if it is set in read only/steal method. + match mclk { + Some(mclk) => { + mclk.$apmask().modify(|_, w| w.$apbits().set_bit()); + } + None => {} + } + count.ctrla().write(|w| w.swrst().set_bit()); + while count.ctrla().read().bits() & 1 != 0 {} + count.ctrla().modify(|_, w| w.enable().clear_bit()); + while count.syncbusy().read().enable().bit_is_set() {} + count.ctrla().modify(|_, w| w.mode().count8()); + count.ctrla().modify(|_, w| { + w.prescaler().div256() + // match params.divider { + // 1 => w.prescaler().div1(), + // 2 => w.prescaler().div2(), + // 4 => w.prescaler().div4(), + // 8 => w.prescaler().div8(), + // 16 => w.prescaler().div16(), + // 64 => w.prescaler().div64(), + // 256 => w.prescaler().div256(), + // 1024 => w.prescaler().div1024(), + // _ => unreachable!(), + // } + }); + + count.count().write(|w| unsafe { w.bits(233u8) }); + count.per().write(|w| unsafe { w.bits(233u8) }); + count.perbuf().write(|w| unsafe { w.bits(233u8) }); + // while count.syncbusy().read().per().bit_is_set() {} + + count.wave().write(|w| w.wavegen().npwm()); + // while count.syncbusy().read().wave().bit_is_set() {} + + // TODO: do the test: + // prerequisites: forget DMA configuration + // 1) Set CCx to 0x00 and measure the signal value + // 2) Set CCx to 0x7F and measure the signal value and frequency + // 3) Set CCx to 0xFF and measure the signal value + count.cc(0).write(|w| unsafe { w.bits(0x00u8/*params.cycles as u8*/) }); + while count.syncbusy().read().cc0().bit_is_set() {} + count.cc(1).write(|w| unsafe { w.bits(0xffu8) }); + while count.syncbusy().read().cc1().bit_is_set() {} + + Self { + clock_freq: clock_freq, + requested_freq: freq, + tc, + pinout, + } + } + + // pub fn with_dma_channels(self, rx: R, tx: T) -> Spi + fn with_dma_channel(self, channel: CH) -> Self::ConvertibleToFuture + where + CH: AnyChannel + { + [<$TYPE Future>] { + base_pwm: self, + _channel: channel, + _init_level: 0x00u8, + } + } +} + +impl $TYPE { + + pub fn start(&mut self) { + // Rest of the setup shall go into poll method: i.e. enabling interrupts and the counter + // of the timer. + let count = self.tc.count8(); + count.ctrla().modify(|_, w| w.enable().set_bit()); + while count.syncbusy().read().enable().bit_is_set() {} + } + pub fn GetDmaPtr(tc: crate::pac::$TC) -> PwmWaveformGeneratorPtr { + PwmWaveformGeneratorPtr(tc.count8().ccbuf(1).as_ptr() as *mut _) + } + pub fn get_dma_ptr(&self) -> PwmWaveformGeneratorPtr { + PwmWaveformGeneratorPtr(self.tc.count8().ccbuf(1).as_ptr() as *mut _) + } + + pub fn get_period(&self) -> Hertz { + let count = self.tc.count8(); + let divisor = count.ctrla().read().prescaler().bits(); + let top = count.cc(0).read().cc().bits(); + self.clock_freq / divisor as u32 / (top + 1) as u32 + } + + pub fn set_period(&mut self, period: Hertz) + { + let period = period.into(); + let params = TimerParams::new(period, self.clock_freq); + let count = self.tc.count8(); + count.ctrla().modify(|_, w| w.enable().clear_bit()); + while count.syncbusy().read().enable().bit_is_set() {} + count.ctrla().modify(|_, w| { + match params.divider { + 1 => w.prescaler().div1(), + 2 => w.prescaler().div2(), + 4 => w.prescaler().div4(), + 8 => w.prescaler().div8(), + 16 => w.prescaler().div16(), + 64 => w.prescaler().div64(), + 256 => w.prescaler().div256(), + 1024 => w.prescaler().div1024(), + _ => unreachable!(), + } + }); + count.ctrla().modify(|_, w| w.enable().set_bit()); + while count.syncbusy().read().enable().bit_is_set() {} + count.cc(0).write(|w| unsafe { w.cc().bits(params.cycles as u8) }); + while count.syncbusy().read().cc0().bit_is_set() {} + } +} +} // paste!() + +impl $crate::ehal::pwm::ErrorType for$TYPE { + type Error = ::core::convert::Infallible; +} + +impl $crate::ehal::pwm::SetDutyCycle for $TYPE { + fn max_duty_cycle(&self) -> u16 { + let count = self.tc.count8(); + let top = count.cc(0).read().cc().bits(); + top as u16 + } + + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { + let count = self.tc.count8(); + unsafe { count.ccbuf(1).write(|w| w.ccbuf().bits(duty as u8)); } + Ok(()) + } +} + +impl $crate::ehal_02::PwmPin for $TYPE { + type Duty = u16; + + fn disable(&mut self) { + let count = self.tc.count8(); + count.ctrla().modify(|_, w| w.enable().clear_bit()); + while count.syncbusy().read().enable().bit_is_set() {} + } + + fn enable(&mut self) { + let count = self.tc.count8(); + count.ctrla().modify(|_, w| w.enable().set_bit()); + while count.syncbusy().read().enable().bit_is_set() {} + } + + + fn get_duty(&self) -> Self::Duty { + let count = self.tc.count8(); + let duty: u8 = count.ccbuf(1).read().ccbuf().bits(); + duty as Self::Duty + } + + fn get_max_duty(&self) -> Self::Duty { + use $crate::ehal::pwm::SetDutyCycle; + self.max_duty_cycle() + } + + fn set_duty(&mut self, duty: Self::Duty) { + use $crate::ehal::pwm::SetDutyCycle; + let _ignore_infaillible = self.set_duty_cycle(duty); + } +} + +)+}} + +#[hal_cfg("tc0")] +pwm_wg! { PwmWg0: (Tc0, TC0Pinout, Tc0Tc1Clock, apbamask, tc0_, PwmWg0Wrapper, Tc0Ovf) } +#[hal_cfg("tc1")] +pwm_wg! { PwmWg1: (Tc1, TC1Pinout, Tc0Tc1Clock, apbamask, tc1_, PwmWg1Wrapper, Tc1Ovf) } +#[hal_cfg("tc2")] +pwm_wg! { PwmWg2: (Tc2, TC2Pinout, Tc2Tc3Clock, apbbmask, tc2_, PwmWg2Wrapper, Tc2Ovf) } +#[hal_cfg("tc3")] +pwm_wg! { PwmWg3: (Tc3, TC3Pinout, Tc2Tc3Clock, apbbmask, tc3_, PwmWg3Wrapper, Tc3Ovf) } +#[hal_cfg("tc4")] +pwm_wg! { PwmWg4: (Tc4, TC4Pinout, Tc4Tc5Clock, apbcmask, tc4_, PwmWg4Wrapper, Tc4Ovf) } +#[hal_cfg("tc5")] +pwm_wg! { PwmWg5: (Tc5, TC5Pinout, Tc4Tc5Clock, apbcmask, tc5_, PwmWg5Wrapper, Tc5Ovf) } +#[hal_cfg("tc6")] +pwm_wg! { PwmWg6: (Tc6, TC6Pinout, Tc6Tc7Clock, apbdmask, tc6_, PwmWg6Wrapper, Tc6Ovf) } +#[hal_cfg("tc7")] +pwm_wg! { PwmWg7: (Tc7, TC7Pinout, Tc6Tc7Clock, apbdmask, tc7_, PwmWg7Wrapper, Tc7Ovf) } + + // ($($TYPE:ident: ($TC:ident, $pinout:ident, $clock:ident, $apmask:ident, $apbits:ident, $wrapper:ident, $event:ident)),+) => { \ No newline at end of file diff --git a/hal/src/peripherals/timer_capture_waveform/d5x.rs b/hal/src/peripherals/timer_capture_waveform/d5x.rs new file mode 100644 index 000000000000..7f7d57312d36 --- /dev/null +++ b/hal/src/peripherals/timer_capture_waveform/d5x.rs @@ -0,0 +1,822 @@ +#![allow(non_snake_case)] + +use core::future::Future; +use core::sync::atomic; +use core::iter; + +use atsamd_hal_macros::hal_cfg; + +use crate::typelevel; +use crate::pac; +use crate::async_hal::interrupts::{Handler, Interrupt}; +#[cfg(feature = "async")] +use crate::dmac::ReadyFuture; +use crate::dmac::{AnyChannel, Beat, Buffer, Error as DmacError, TriggerAction, TriggerSource}; +use crate::gpio::*; +use crate::pac::Mclk; +use crate::time::Hertz; + +use pin_project::pin_project; + +use paste::paste; + +/// Channel 0 is used to capture the counter value while channel 1 is used to trigger interrupt +/// used for timeout handling + +const TIMER_CHANNEL: usize = 0; +// Second channel may be used for timeout detection. CCx can thus be set to some value +// that will be compared with current value of the counter and trigger interrupt when the value matches. +const TIMER_TIMEOUT_CHANNEL: usize = 1; + +#[hal_cfg("tc1-d11")] +type RegBlock = pac::tc1::RegisterBlock; + +#[hal_cfg("tc3-d21")] +type RegBlock = pac::tc3::RegisterBlock; + +#[hal_cfg("tc1-d5x")] +type RegBlock = pac::tc0::RegisterBlock; + +#[derive(Clone)] +pub struct TimerCaptureWaveformSourcePtr(pub(in super::super) *mut T); + +unsafe impl Buffer for TimerCaptureWaveformSourcePtr { + type Beat = T; + + #[inline] + fn dma_ptr(&mut self) -> *mut Self::Beat { + self.0 + } + + #[inline] + fn incrementing(&self) -> bool { + false + } + + #[inline] + fn buffer_len(&self) -> usize { + 1 + } +} + +#[pin_project] +struct TimerCaptureDmaWrapper<'a, DmaFut, T> { + #[pin] + _dma_future: DmaFut, + timer_started: bool, + tc_waker_index: usize, + _timer: &'a T, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CounterValueAtTermination(u32); + +impl CounterValueAtTermination { + pub fn get_raw_value(self) -> u32 { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TimerCaptureData> { + data: Container, +} + +impl TimerCaptureData +where + Container: iter::Extend +{ + pub fn get_data(self) -> Container { + self.data + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TimerCaptureResultAvailable> { + DmaPollReady(TimerCaptureData), + TimerTimeout(TimerCaptureData), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct ExtendWithFirstZeroAndTerminationValueIterator<'a>{ + data: &'a [u32], + first_done: bool, + depleted: bool, + last_value: CounterValueAtTermination, +} + +impl<'a> ExtendWithFirstZeroAndTerminationValueIterator<'a> { + fn new(data: &'a [u32], counter_at_termination: CounterValueAtTermination) -> Self { + Self { data, first_done: false, depleted: false, last_value: counter_at_termination } + } +} + +// And only non-zero values are returned. Otherwise the iterator will return `None` +impl<'a> iter::Iterator for ExtendWithFirstZeroAndTerminationValueIterator<'a> { + type Item = u32; + fn next(&mut self) -> Option { + if self.data.len() == 0 { + if self.depleted == false { + self.depleted = true; + if let CounterValueAtTermination(value) = self.last_value { + return Some(value); + } else { + panic!("Invalid value of the counter at termination"); + } + } + return None; + } + if self.first_done == false { + self.first_done = true; + return Some(0u32); + } + if self.depleted == true { + return None; + } + + let value = self.data[0]; + self.data = &self.data[1..]; + if value == 0 { + self.depleted = true; + return None; + } + Some(value) + } +} + +#[derive(Debug, PartialEq, Eq)] +struct TimerCaptureRawData<'a> { + counter_at_termination: CounterValueAtTermination, + data: Option<&'a mut [u32]>, +} +// TODO: DmaPollReady and TimerTimeout should be merged into one trait / type to avoid processing code duplication: +#[derive(Debug, PartialEq, Eq)] +pub enum TimerCaptureRawResultAvailable<'a> { + DmaPollReady(TimerCaptureRawData<'a>), + TimerTimeout(TimerCaptureRawData<'a>), +} + +impl<'a> AsMut> for TimerCaptureRawResultAvailable<'a> { + fn as_mut(&mut self) -> &mut TimerCaptureRawData<'a> { + match self { + TimerCaptureRawResultAvailable::DmaPollReady(data) => data, + TimerCaptureRawResultAvailable::TimerTimeout(data) => data, + } + } +} + +impl<'a> TimerCaptureRawResultAvailable<'a> { + pub fn get_public_data>(self, mut container: Container) -> TimerCaptureResultAvailable { + let adjust_ratio = |x| {(x as u64 * 4173*2) / 1000}; // TODO: fix this by some ratio compund type + + match self { + TimerCaptureRawResultAvailable::DmaPollReady(data) => { + todo!() + } + TimerCaptureRawResultAvailable::TimerTimeout(data) => { + let counter_value_at_termination = data.counter_at_termination; + if let Some(data) = data.data { + // container.extend(data.iter().map(|x| core::time::Duration::from_nanos(adjust_ratio(*x as u64)))); + let data_with_preceeding_zero = ExtendWithFirstZeroAndTerminationValueIterator::new(data, counter_value_at_termination); + container.extend(data_with_preceeding_zero.clone(). + zip(data_with_preceeding_zero.skip(1)). + map(|(x, y)|{ + if y > x { + core::time::Duration::from_nanos(adjust_ratio(y as u64 - x as u64)) + } else { + // TODO: Better handling for errror here: + todo!() + } + } + )); + TimerCaptureResultAvailable::TimerTimeout(TimerCaptureData { + data: container, + }) + } + else {todo!()} + } + } + } + pub fn fill_the_capture_memory<'b>(&mut self, capture_memory: &'b mut [u32]) + where + 'b: 'a, + { + self.as_mut().data = Some(capture_memory); + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TimerCaptureFailure { + DmaFailed(DmacError), + TimerFailed, +} + +impl<'a, DmaFut, T> TimerCaptureDmaWrapper<'a, DmaFut, T> { + fn new(dma_future: DmaFut, timer: &'a T, tc_index: usize) -> Self { + Self { + _dma_future: dma_future, + timer_started: false, + tc_waker_index:tc_index, + _timer: timer, + } + } +} + +impl<'a, DmaFut, T> core::future::Future for TimerCaptureDmaWrapper<'a, DmaFut, T> +where + DmaFut: core::future::Future>, + T: TimerCounterStart, +{ + type Output = Result, TimerCaptureFailure>; + + fn poll( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + use waker::MC1_INTERRUPT_FIRED; + + // let mut pinned = core::pin::pin!(self); + let this = self.as_mut().project(); + + // First time the future is polled, it sets enable bit of DMA. Only after that the timer shall be started. + let result = this._dma_future.poll(cx); + if result == core::task::Poll::Ready(Ok(())) { + let counter_value = this._timer.counter_value(); + this._timer.stop(); + this._timer.disable_interrupt(); + let raw_result = TimerCaptureRawData { + counter_at_termination: CounterValueAtTermination(counter_value), + data: None, + }; + // TODO: add check on the mc1 flag related to timeout + return core::task::Poll::Ready(Ok(TimerCaptureRawResultAvailable::DmaPollReady(raw_result))); + } + + if *this.timer_started == false { + *this.timer_started = true; + MC1_INTERRUPT_FIRED[*this.tc_waker_index].store(false, atomic::Ordering::Relaxed); + // TODO: Enable interrupts of the timer + this._timer.start(); + // Enable the NVIC interrupt: + this._timer.enable_interrupt(); + } + + // Check if the interrupt was fired: + let result = if + MC1_INTERRUPT_FIRED[*this.tc_waker_index].load(atomic::Ordering::Relaxed) { + // counter_value + let counter_value = this._timer.counter_value(); + this._timer.stop(); + this._timer.disable_interrupt(); + let raw_result = TimerCaptureRawData { + counter_at_termination: CounterValueAtTermination(counter_value), + data: None, + }; + core::task::Poll::Ready(Ok(TimerCaptureRawResultAvailable::TimerTimeout(raw_result))) + } else { + use waker::WAKERS; + // TODO: Do I have to disable interrupt/ put registartion into critical section? + WAKERS[self.tc_waker_index].register(cx.waker()); + // TODO: poll must be called so we need to register Waker for the future. + // The problem is that we have two source of the wake-up events: DMA and Timer. + // We need to combine them into one or as simple alternative we can use the Timer to wake up at the timeout. + // This is not very efficient but it is simpler. + core::task::Poll::Pending + }; + result + } +} + +/// Trait enabling the use of a Timer/Counter in async mode. Specifically, this +/// trait enables us to register a `TC*` interrupt as a waker for timer futures. +/// +/// **⚠️ Warning** This trait should not be implemented outside of this crate! +pub trait TimerSpecificInterruptAndRegisters/* : Sealed*/ { + /// Index of this TC in the `STATE` tracker + const WAKER_ID: usize; + + /// Get a reference to the timer's register block + fn reg_block(peripherals: &pac::Peripherals) -> &RegBlock; + + /// Interrupt type for this timer + type Interrupt: Interrupt; +} + +// Timer/Counter (TCx) +// +trait TimerCounterInterrupt { + type Interrupt: Interrupt; +} + +struct TimerCounterTimeoutInterruptHandler { + _private: (), + _tc: core::marker::PhantomData, +} + +trait TimerTraitToDo { +} + +pub struct InterruptHandlerAsdf { + _private: (), + _tc: core::marker::PhantomData, +} + +// impl typelevel::Sealed for TimerCounterTimeoutInterruptHandler {} + +// impl Handler for TimerCounterTimeoutInterruptHandler { +// unsafe fn on_interrupt() { +// use waker::WAKERS; +// // WAKERS[extract_number!(I)].wake(); +// let peripheral = unsafe{ crate::pac::Peripherals::steal() }; +// } +// } + +const fn extract_number(tc_name: &str) -> usize { + let bytes = tc_name.as_bytes(); + let last_byte = bytes[bytes.len() - 1]; + let tc_number = (last_byte - b'0') as usize; + assert!(tc_number < MAX_TIMER_COUNT); + tc_number +} + +trait TimerCaptureCapable { + type Interrupt: Interrupt; + const WAKER_IDX: usize; + fn reg_block(peripherals: &pac::Peripherals) -> &RegBlock; +} + +pub struct TimerCaptureInterruptHandler { + _private: (), + _tc: core::marker::PhantomData +} + +impl typelevel::Sealed for TimerCaptureInterruptHandler {} +impl Handler for TimerCaptureInterruptHandler { + unsafe fn on_interrupt() { + let periph = unsafe { crate::pac::Peripherals::steal() }; + let tc = T::reg_block(&periph); + let intflag = &tc.count32().intflag(); + + // Wake only on the compare match channel 1, which is used for the timeout detection. + if intflag.read().mc1().bit_is_set() { + // Clear the flag + intflag.modify(|_, w| w.mc1().set_bit()); + use waker::{WAKERS, MC1_INTERRUPT_FIRED}; + MC1_INTERRUPT_FIRED[T::WAKER_IDX].store(true, atomic::Ordering::Relaxed); + WAKERS[T::WAKER_IDX].wake(); + } + } +} + +pub use crate::pwm::{PinoutCollapse, PinoutReadLevel}; + +pub trait TimerCaptureBaseTrait { + type TC; + type Pinout: PinoutCollapse; + type ConvertibleToFuture: TimerCaptureFutureTrait + where + D: AnyChannel; + + // fn get_dma_ptr(&self) -> TimerCaptureWaveformSourcePtr; + fn new_timer_capture( + clock_freq: Hertz, + freq: Hertz, + tc: Self::TC, + pinout: Self::Pinout, + mclk: Option<&mut Mclk>, + // timeout: MillisDurationU32, + ) -> Self; + fn enable_mclk_clocks(mclk: &mut Mclk); + fn with_dma_channel(self, channel: CH) -> Self::ConvertibleToFuture + where + CH: AnyChannel; +} + +pub trait TimerCaptureFutureTrait { + type DmaChannel; + type TC; + type Pinout: PinoutCollapse; + fn decompose(self) -> (Self::DmaChannel, Self::TC, Self::Pinout); + // fn start_regular_pwm(&mut self, ccx_value: u8); + async fn start_timer_execute_dma_transfer, const N: usize>(&mut self, timestamps_capture: Container) + -> Result, TimerCaptureFailure>; + fn read_pin_level(&mut self) -> bool; +} + +macro_rules! create_timer_capture { + ($($TYPE:ident: ($TC:ident, $pinout:ident, $clock:ident, $apmask:ident, $apbits:ident, $wrapper:ident, $event:ident)),+) => { + $( + +macro_rules! extract_number_macro { + ($tc_name:ident) => { + { + extract_number(stringify!{$tc_name}) + } + }; +} + +use crate::pwm::$pinout; + +pub struct $TYPE { + /// The frequency of the attached clock, not the period of the pwm. + /// Used to calculate the period of the pwm. + clock_freq: Hertz, + tc: crate::pac::$TC, + #[allow(dead_code)] + pinout: $pinout, + // _channel: Option, +} + +impl TimerCaptureBaseTrait for $TYPE { + type TC = crate::pac::$TC; + type Pinout = $pinout; + type ConvertibleToFuture = paste!{ [<$TYPE Future>] } + where + D: AnyChannel; + + // fn get_dma_ptr(&self) -> TimerCaptureWaveformSourcePtr { + // TimerCaptureWaveformSourcePtr(self.tc.count32().cc(TIMER_CHANNEL).as_ptr() as *mut _) + // } + + fn enable_mclk_clocks(mclk: &mut Mclk) + { + Self::enable_mclk_clocks(mclk); + } + fn new_timer_capture( + clock_freq: Hertz, + freq: Hertz, + tc: crate::pac::$TC, + pinout: $pinout, + mclk: Option<&mut Mclk>, + // timeout: MillisDurationU32, + ) -> Self { + Self::new_timer_capture(clock_freq, freq, tc, pinout, mclk) + } + + fn with_dma_channel(self, channel: CH) -> Self::ConvertibleToFuture + where + CH: AnyChannel, + { + self.with_dma_channel(channel) + } +} + +impl typelevel::Sealed for $TYPE {} + +paste!{ + pub struct [<$TYPE InterruptData >] { + } + impl TimerCaptureCapable for [< $TYPE InterruptData >] { + const WAKER_IDX: usize = extract_number_macro!($TC); + + type Interrupt = crate::async_hal::interrupts::[< $TC:upper >]; + + fn reg_block(peripherals: &pac::Peripherals) -> &RegBlock { + &*peripherals.[< $TC:lower >] + } + } +} + +// type Interrupt = crate::async_hal::interrupts::[< $TC:upper >]; +// paste!{ +// impl Handler]> for $TYPE { +// unsafe fn on_interrupt() { +// use waker::WAKERS; +// WAKERS[0].wake(); +// } +// } +// } + +impl TimerSpecificInterruptAndRegisters for $TYPE { + const WAKER_ID: usize = extract_number_macro!($TC); + + paste!{ + fn reg_block(peripherals: &pac::Peripherals) -> &RegBlock { + &*peripherals.[< $TC:lower >] + } + + type Interrupt = crate::async_hal::interrupts::[< $TC:upper >]; + } +} + +paste!{ +pub struct [<$TYPE Future>]>{ + base_pwm: $TYPE, + _channel: DmaCh +} + +// Implement Interrupt traits for basic timer struct: +impl TimerCounterInterrupt for $TYPE { + type Interrupt = crate::async_hal::interrupts::[< $TC:upper >]; +} + +impl> [<$TYPE Future>] { + + fn start_capture_timer(&mut self) { + let count = self.base_pwm.tc.count32(); + + count.ctrla().modify(|_, w| w.enable().set_bit()); + while count.syncbusy().read().enable().bit_is_set() {} + } + +} +impl> TimerCaptureFutureTrait for [<$TYPE Future>] { + type DmaChannel = DmaCh; + type TC = crate::pac::$TC; + type Pinout = $pinout; + fn decompose(self) -> (Self::DmaChannel, Self::TC, Self::Pinout){ + let $TYPE{clock_freq, tc, pinout} = self.base_pwm; + (self._channel, tc, pinout) + } + fn read_pin_level(&mut self) -> bool { + // let count = self.base_pwm.tc.count32(); + // let pinout = self.base_pwm.pinout; + // let pin = pinout.get_pin(); + // let level = pin.is_high().unwrap(); + // level + self.base_pwm.read_pin_level() + } + /// The capture_memorys first element will be the value of the counter at the moment of the first event. The timer starts counting from zero, which mean the first period can be assumed as the value of the first element in the memory. + async fn start_timer_execute_dma_transfer, const N: usize> + (&mut self, mut capture_container: ContainerData) + -> Result, TimerCaptureFailure> { + + let count = self.base_pwm.tc.count32(); + + let pwm_dma_address = self.base_pwm.get_dma_ptr(); + + let mut capture_memory: [u32; N] = [0; N]; + // TODO: core::pin::pin_mut!(capture_memory); + // TODO: make transfer_future method to return real number of transferred items + let dma_future = self._channel.as_mut().transfer_future( + pwm_dma_address, + &mut capture_memory, + TriggerSource::$event, + TriggerAction::Burst, + ); + + // dma_future.as_mut().poll() + + let dma_wrapped_future = TimerCaptureDmaWrapper::new(dma_future, &self.base_pwm, extract_number_macro!($TC)); + + // TODO: Change the implementation of the DMA channel so that the timer can be started before the DMA gets enabled + // First poll the future starts the DMA transfer. It sets enable bit of the DMA. + let result = dma_wrapped_future.await; + // Right after the DMA transfer is started, we can start the timer. + + if let Ok(mut result) = result { + result.fill_the_capture_memory(&mut capture_memory); + Ok(result.get_public_data(capture_container)) + }else { + // Err(TimerCaptureFailure::DmaFailed(DmacError::TransferError)) + todo!() + } + // type Output = Result, TimerCaptureFailure>; + // Rest of the setup shall go into poll method: i.e. enabling interrupts and the counter + // of the timer. + // self.start_capture_timer(); + // count.ctrla().modify(|_, w| w.enable().set_bit()); + // while count.syncbusy().read().enable().bit_is_set() {} + + // wait for the settings to be applied + // while count.syncbusy().read().cc0().bit_is_set() {} + // result + } + +} + +} // paste macrto + +/* +/// +/// To resolve the issue of the end condition for DMA transfer we probably need to use second interrupt signal from the timer +/// One possibility is to use the overflow, but considering 32 bits counter it is not very practical. +/// Another possibility is to use the compare match on timer channel 1. The compare match can be set to the maximum value of the counter. +/// +*/ +impl $TYPE { + pub fn new_timer_capture( + clock_freq: Hertz, + _freq: Hertz, + tc: crate::pac::$TC, + pinout: $pinout, + mclk: Option<&mut Mclk>, + // timeout: MillisDurationU32, + ) -> Self { + // TODO: Calculate the valuee of the compare match register: + let capture_comapre_value_timeout = 0x4000_0000_u32; + let count = tc.count32(); + // let tc_ccbuf_dma_data_register_address = count.cc(TIMER_CHANNEL).as_ptr() as *const (); + // let TimerCaptureWaveformSourcePtr()(pub(in super::super) *mut T); + + // write(|w| w.ccbuf().bits(duty as u8)); + // let params = TimerParams::new(freq.convert(), clock_freq); + + match mclk { + Some(mclk) => { + mclk.$apmask().modify(|_, w| w.$apbits().set_bit()); + // TODO: Dirty hack to allow TC4 + TC5 timers work in 32 bits. This is somewhat against + // datasheet declarations so be cerful. + mclk.apbcmask().modify(|_, w| w.tc5_().set_bit()); + // TODO: Dirty hack to allow TC2 + TC3 timers work in 32 bits. This is somewhat against + // datasheet declarations so be cerful.In the future I expect it will be added to + // take over the ownership of the other timer as well so that it is blocked to be + // used for other purposes. + mclk.apbbmask().modify(|_, w| w.tc3_().set_bit()); + } + None => {} + } + + // First disable the timer, only after that we should set SWRST bit. + count.ctrla().modify(|_, w| w.enable().clear_bit()); + while count.syncbusy().read().enable().bit_is_set() {} + + count.ctrla().write(|w| w.swrst().set_bit()); + while count.ctrla().read().bits() & 1 != 0 {} + + // Set the timer to 32-bit mode: + count.ctrla().modify(|_, w| w.mode().count32()); + count.ctrla().modify(|_, w| { + w.prescaler().div1() + // match params.divider { + // 1 => w.prescaler().div1(), + // 2 => w.prescaler().div2(), + // 4 => w.prescaler().div4(), + // 8 => w.prescaler().div8(), + // 16 => w.prescaler().div16(), + // 64 => w.prescaler().div64(), + // 256 => w.prescaler().div256(), + // 1024 => w.prescaler().div1024(), + // _ => unreachable!(), + // } + }); + // TODO: Set capten0 and clear capten1. + count.ctrla().modify(|_, w| w.capten0().set_bit().copen0().set_bit()); + + // clear all interrupt flags: + count.intflag().write(|w| w.mc1().set_bit().mc0().set_bit().ovf().set_bit().err().set_bit()); + + count.evctrl().write(|w| w.evact().stamp().mceo0().set_bit()); + // enable interrupt on the timeout side channel: + count.intenset().modify(|_, w| w.mc1().set_bit() ); + + // It is possible to set capture on falling edges by setting the INVEN bit. + // count.drvctrl().write(|w| w.inven0().set_bit()); + + // count.ccbuf(0).write(|w| unsafe { w.bits(0x00) }); + // count.ccbuf(1).write(|w| unsafe { w.bits(0x00) }); + count.cc(0).write(|w| unsafe { w.bits(0x00) }); + while count.syncbusy().read().cc0().bit_is_set() {} + count.cc(1).write(|w| unsafe { w.bits(capture_comapre_value_timeout) }); + while count.syncbusy().read().cc1().bit_is_set() {} + + Self { + clock_freq: clock_freq, + tc, + pinout, + } + } + + pub fn read_pin_level(&self) -> bool { + // let pinout = self.pinout; + // let pin = pinout.get_pin(); + // let level = pin.is_high().unwrap(); + // level + self.pinout.read_level() + } + + paste!{ + // pub fn with_dma_channels(self, rx: R, tx: T) -> Spi + pub fn with_dma_channel(self, channel: CH ) -> [<$TYPE Future>] + where + CH: AnyChannel + { + [<$TYPE Future>] { + base_pwm: self, + _channel: channel, + } + } + } + + pub fn enable_mclk_clocks(mclk: &mut Mclk) { + mclk.$apmask().modify(|_, w| w.$apbits().set_bit()); + // TODO: Dirty hack to allow TC4 + TC5 timers work in 32 bits. This is somewhat against + // datasheet declarations so be cerful. + mclk.apbcmask().modify(|_, w| w.tc5_().set_bit()); + // TODO: Dirty hack to allow TC2 + TC3 timers work in 32 bits. This is somewhat against + // datasheet declarations so be cerful.In the future I expect it will be added to + // take over the ownership of the other timer as well so that it is blocked to be + // used for other purposes. + mclk.apbbmask().modify(|_, w| w.tc3_().set_bit()); + } + + pub fn start(&self) { + // Rest of the setup shall go into poll method: i.e. enabling interrupts and the counter + // of the timer. + let count = self.tc.count32(); + count.ctrla().modify(|_, w| w.enable().set_bit()); + while count.syncbusy().read().enable().bit_is_set() {} + } + + pub fn stop(&self){ + let count = self.tc.count32(); + count.ctrla().modify(|_, w| w.enable().clear_bit()); + while count.syncbusy().read().enable().bit_is_set() {} + } + + pub fn GetDmaPtr(tc: crate::pac::$TC) -> TimerCaptureWaveformSourcePtr { + TimerCaptureWaveformSourcePtr(tc.count32().cc(TIMER_CHANNEL).as_ptr() as *mut _) + } + pub fn get_dma_ptr(&self) -> TimerCaptureWaveformSourcePtr { + TimerCaptureWaveformSourcePtr(self.tc.count32().cc(TIMER_CHANNEL).as_ptr() as *mut _) + } +} + +impl TimerCounterStart for $TYPE { + paste!{ + type Interrupt = crate::async_hal::interrupts::[< $TC:upper >]; + } + fn start(&self) + { + self.start(); + } + fn stop(&self) + { + self.stop(); + } + fn is_mc1_interrupt_active(&self) -> bool { + let count = self.tc.count32(); + count.intflag().read().mc1().bit_is_set() + // if intflag.read().mc1().bit_is_set() { + } + fn enable_interrupt(&self) + { + unsafe { + Self::Interrupt::enable(); + } + } + fn disable_interrupt(&self) + { + Self::Interrupt::disable(); + } + fn counter_value(&self) -> u32 + { + // trigger counter value read: + let _ = self.tc.count32().ctrlbset().write(|w| w.cmd().readsync()); + // return counter value: + self.tc.count32().count().read().bits() + } +} + +impl $crate::ehal::pwm::ErrorType for $TYPE { + type Error = ::core::convert::Infallible; +} + +)+}} + +#[hal_cfg("tc0")] +create_timer_capture! { TimerCapture0: (Tc0, TC0Pinout, Tc0Tc1Clock, apbamask, tc0_, TimerCapture0Wrapper, Tc0Mc0) } +#[hal_cfg("tc1")] +create_timer_capture! { TimerCapture1: (Tc1, TC1Pinout, Tc0Tc1Clock, apbamask, tc1_, TimerCapture1Wrapper, Tc1Mc0) } +#[hal_cfg("tc2")] +create_timer_capture! { TimerCapture2: (Tc2, TC2Pinout, Tc2Tc3Clock, apbbmask, tc2_, TimerCapture2Wrapper, Tc2Mc0) } +#[hal_cfg("tc3")] +create_timer_capture! { TimerCapture3: (Tc3, TC3Pinout, Tc2Tc3Clock, apbbmask, tc3_, TimerCapture3Wrapper, Tc3Mc0) } +#[hal_cfg("tc4")] +create_timer_capture! { TimerCapture4: (Tc4, TC4Pinout, Tc4Tc5Clock, apbcmask, tc4_, TimerCapture4Wrapper, Tc4Mc0) } +#[hal_cfg("tc5")] +create_timer_capture! { TimerCapture5: (Tc5, TC5Pinout, Tc4Tc5Clock, apbcmask, tc5_, TimerCapture5Wrapper, Tc5Mc0) } +#[hal_cfg("tc6")] +create_timer_capture! { TimerCapture6: (Tc6, TC6Pinout, Tc6Tc7Clock, apbdmask, tc6_, TimerCapture6Wrapper, Tc6Mc0) } +#[hal_cfg("tc7")] +create_timer_capture! { TimerCapture7: (Tc7, TC7Pinout, Tc6Tc7Clock, apbdmask, tc7_, TimerCapture7Wrapper, Tc7Mc0) } + +trait TimerCounterStart { + type Interrupt: Interrupt; + fn start(&self); + fn stop(&self); + fn is_mc1_interrupt_active(&self) -> bool; + fn enable_interrupt(&self); + fn disable_interrupt(&self); + fn counter_value(&self) -> u32; +} + +const MAX_TIMER_COUNT: usize = 8; + +#[cfg(feature = "async")] +mod waker { + use core::sync::atomic::AtomicBool; + + use embassy_sync::waitqueue::AtomicWaker; + + use super::MAX_TIMER_COUNT; + + #[allow(clippy::declare_interior_mutable_const)] + const NEW_WAKER: AtomicWaker = AtomicWaker::new(); + pub(super) static WAKERS: [AtomicWaker; MAX_TIMER_COUNT] = + [NEW_WAKER; MAX_TIMER_COUNT]; + const NEW_INTERRUP_FIRED: AtomicBool = AtomicBool::new(false); + pub(super) static MC1_INTERRUPT_FIRED: [AtomicBool; MAX_TIMER_COUNT] = + [NEW_INTERRUP_FIRED; MAX_TIMER_COUNT]; +} + From 0a2dab34aa82ccd02f85729da2d59210133f8886 Mon Sep 17 00:00:00 2001 From: Pawel Pyszko Date: Sun, 11 May 2025 11:23:41 +0200 Subject: [PATCH 3/8] cargo fmt --- .../wio_terminal/examples/async_opentherm.rs | 399 +++++++++++------- 1 file changed, 240 insertions(+), 159 deletions(-) diff --git a/boards/wio_terminal/examples/async_opentherm.rs b/boards/wio_terminal/examples/async_opentherm.rs index 79384d53888c..3f4454aa8910 100644 --- a/boards/wio_terminal/examples/async_opentherm.rs +++ b/boards/wio_terminal/examples/async_opentherm.rs @@ -10,28 +10,31 @@ use heapless::Vec; use panic_probe as _; use embassy_futures::join; -use embassy_sync::channel::{Channel, Receiver}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::{Channel, Receiver}; +use crate::bsp::hal; use crate::pac::Mclk; use bsp::pac; -use crate::bsp::hal; -use hal::gpio::{Output, Input, OutputConfig, Pins, PinId, PushPull, PushPullOutput, Floating}; -use hal::gpio::{E, PB08, PB09, PA16, PA17}; +use hal::gpio::{Floating, Input, Output, OutputConfig, PinId, Pins, PushPull, PushPullOutput}; +use hal::gpio::{E, PA16, PA17, PB08, PB09}; // use wio_terminal::prelude::_embedded_hal_PwmPin; use wio_terminal::aliases::UserLed; use hal::{ - clock::{ClockGenId, ClockSource, GenericClockController, Tc4Tc5Clock, Tc2Tc3Clock}, + clock::{ClockGenId, ClockSource, GenericClockController, Tc2Tc3Clock, Tc4Tc5Clock}, delay::Delay, dmac, - dmac::{DmaController, PriorityLevel, Ch1, ReadyFuture}, + dmac::{Ch1, DmaController, PriorityLevel, ReadyFuture}, ehal::digital::{OutputPin, StatefulOutputPin}, eic::{Eic, Sense}, gpio::{Pin as GpioPin, PullUp, PullUpInterrupt}, - pwm::{TC4Pinout, TC2Pinout, PinoutNewTrait}, - pwm_wg::{PwmWg4, PwmWg2}, - timer_capture_waveform::{TimerCapture4, TimerCapture4Future, TimerCaptureFutureTrait, TimerCaptureBaseTrait, TimerCaptureResultAvailable}, + pwm::{PinoutNewTrait, TC2Pinout, TC4Pinout}, + pwm_wg::{PwmWg2, PwmWg4}, + timer_capture_waveform::{ + TimerCapture4, TimerCapture4Future, TimerCaptureBaseTrait, TimerCaptureFutureTrait, + TimerCaptureResultAvailable, + }, }; use wio_terminal::prelude::_embedded_hal_blocking_delay_DelayMs; @@ -50,13 +53,12 @@ rtic_monotonics::systick_monotonic!(Mono, 10000); #[cfg(feature = "use_opentherm")] use boiler::opentherm_interface::{ edge_trigger_capture_interface::{ - CaptureError, EdgeCaptureInterface, EdgeCaptureTransitiveToTriggerCapable, CapturedEdgePeriod, CaptureTypeEdges, - EdgeTriggerInterface, EdgeTriggerTransitiveToCaptureCapable, InitLevel, TriggerError, + CaptureError, CaptureTypeEdges, CapturedEdgePeriod, EdgeCaptureInterface, + EdgeCaptureTransitiveToTriggerCapable, EdgeTriggerInterface, + EdgeTriggerTransitiveToCaptureCapable, InitLevel, TriggerError, }, - open_therm_message::{CHState, Temperature, OpenThermMessage}, - OpenThermEdgeTriggerBus, - SendOpenThermMessage, - ListenOpenThermMessage, + open_therm_message::{CHState, OpenThermMessage, Temperature}, + ListenOpenThermMessage, OpenThermEdgeTriggerBus, SendOpenThermMessage, }; #[cfg(feature = "use_opentherm")] use boiler::{BoilerControl, Instant, TimeBaseRef}; @@ -77,7 +79,9 @@ atsamd_hal::bind_multiple_interrupts!(struct DmacIrqs { DMAC: [DMAC_0, DMAC_1, DMAC_2, DMAC_OTHER] => atsamd_hal::dmac::InterruptHandler; }); -enum SignalTxSimulation{Ready_} +enum SignalTxSimulation { + Ready_, +} static CHANNEL: Channel = Channel::new(); trait OtMode {} @@ -97,21 +101,19 @@ mod boiler_implementation { use crate::dmac::ReadyFuture; use crate::timer_data_set::PinoutSpecificDataImplTc4; use atsamd_hal::gpio::G; - use atsamd_hal::gpio::{Alternate, PullUpInput, pin::AnyPin}; + use atsamd_hal::gpio::{pin::AnyPin, Alternate, PullUpInput}; use atsamd_hal::pac::dsu::length; use atsamd_hal::pac::gclk::genctrl::OeR; use atsamd_hal::pac::tcc0::per; - use atsamd_hal::pwm_wg::{PwmWgFutureTrait, PwmBaseTrait, PinoutCollapse}; + use atsamd_hal::pwm_wg::{PinoutCollapse, PwmBaseTrait, PwmWgFutureTrait}; use atsamd_hal::timer_capture_waveform::{self, TimerCaptureData, TimerCaptureFutureTrait}; - use fugit::MicrosDuration; use core::any::Any; use core::marker::PhantomData; + use fugit::MicrosDuration; use super::*; - trait AtsamdEdgeTriggerCaptureFactory{ - - } + trait AtsamdEdgeTriggerCaptureFactory {} pub(super) const VEC_SIZE_CAPTURE: usize = 128; @@ -124,10 +126,26 @@ mod boiler_implementation { type Timer; type PinoutTx: PinoutCollapse + PinoutNewTrait; type PinoutRx: PinoutCollapse + PinoutNewTrait; - type PwmBase: PwmBaseTrait = Self::PwmWg>; - type PwmWg: PwmWgFutureTrait; - type TimerCaptureBase: TimerCaptureBaseTrait = Self::TimerCaptureFuture>; - type TimerCaptureFuture: TimerCaptureFutureTrait; + type PwmBase: PwmBaseTrait< + TC = Self::Timer, + Pinout = Self::PinoutTx, + ConvertibleToFuture = Self::PwmWg, + >; + type PwmWg: PwmWgFutureTrait< + DmaChannel = Self::DmaChannel, + Pinout = Self::PinoutTx, + TC = Self::Timer, + >; + type TimerCaptureBase: TimerCaptureBaseTrait< + TC = Self::Timer, + Pinout = Self::PinoutRx, + ConvertibleToFuture = Self::TimerCaptureFuture, + >; + type TimerCaptureFuture: TimerCaptureFutureTrait< + DmaChannel = Self::DmaChannel, + TC = Self::Timer, + Pinout = Self::PinoutRx, + >; // fn new_pwm_generator<'a>(pin: Self::PinTx, tc: Self::Timer, dma: Self::DmaChannel, mclk: &mut Mclk) -> Self::PwmWg; // fn collapse(self) -> Self::PinTx; } @@ -147,7 +165,7 @@ mod boiler_implementation { periph_clock_freq: Hertz, mode: PhantomData, pinout: PhantomData, - } + } impl AtsamdEdgeTriggerCapture where @@ -157,7 +175,7 @@ mod boiler_implementation { pub fn new_with_default( pin_tx: GpioPin>, pin_rx: GpioPin>, - tc_timer: PinoutSpecificData::Timer /*pac::Tc4*/, + tc_timer: PinoutSpecificData::Timer, /*pac::Tc4*/ mclk: &mut Mclk, input_clock_frequency: Hertz, pinout_factory: PinoutSpecificData, @@ -174,7 +192,8 @@ mod boiler_implementation { tc_timer, PinoutSpecificData::PinoutTx::new_pin(pwm_tx_pin), Some(mclk), - ).with_dma_channel(dma_channel); + ) + .with_dma_channel(dma_channel); Self { tx_pin: None, @@ -209,7 +228,8 @@ mod boiler_implementation { tc_timer, PinoutSpecificData::PinoutTx::new_pin(pwm_tx_pin), None, - ).with_dma_channel(dma_channel); + ) + .with_dma_channel(dma_channel); Self { tx_pin: None, @@ -252,11 +272,10 @@ mod boiler_implementation { PinoutSpecificData: CreatePwmPinout, { type TriggerDevice = AtsamdEdgeTriggerCapture; - fn transition_to_trigger_capable_device(self) -> AtsamdEdgeTriggerCapture { - let (pin_tx, capture_timer) = ( - self.tx_pin.unwrap(), - self.capture_device.unwrap(), - ); + fn transition_to_trigger_capable_device( + self, + ) -> AtsamdEdgeTriggerCapture { + let (pin_tx, capture_timer) = (self.tx_pin.unwrap(), self.capture_device.unwrap()); let (dma, tc_timer, pinout_rx) = capture_timer.decompose(); // let (dma, tc_timer, pinout) = pwm.decompose(); let pin_rx = pinout_rx.collapse(); @@ -272,8 +291,7 @@ mod boiler_implementation { } } - impl - AtsamdEdgeTriggerCapture + impl AtsamdEdgeTriggerCapture where PinoutSpecificData: CreatePwmPinout, { @@ -288,8 +306,7 @@ mod boiler_implementation { let pinout = PinoutSpecificData::PinoutRx::new_pin(pwm_rx_pin); - let timer_capture = - PinoutSpecificData::TimerCaptureBase::new_timer_capture( + let timer_capture = PinoutSpecificData::TimerCaptureBase::new_timer_capture( periph_clock_freq, Hertz::from_raw(32), tc_timer, @@ -313,8 +330,7 @@ mod boiler_implementation { } impl EdgeTriggerInterface - for - AtsamdEdgeTriggerCapture + for AtsamdEdgeTriggerCapture where PinoutSpecificData: CreatePwmPinout, { @@ -324,7 +340,6 @@ mod boiler_implementation { iterator: impl Iterator, period: core::time::Duration, ) -> (Self, Result<(), TriggerError>) { - // Invert for hardware adapter compensation: const INVERT_SIGNAL: bool = true; // TODO: Implement the period arg usage @@ -337,7 +352,10 @@ mod boiler_implementation { .pwm .as_mut() .unwrap() /* TODO: remove runtime panic */ - .start_timer_prepare_dma_transfer::(self.tx_init_duty_value, iterator); + .start_timer_prepare_dma_transfer::( + self.tx_init_duty_value, + iterator, + ); dma_future.await.map_err(|_| TriggerError::GenericError) } None => Err(TriggerError::GenericError), @@ -346,14 +364,15 @@ mod boiler_implementation { } } - impl EdgeCaptureInterface for AtsamdEdgeTriggerCapture + impl EdgeCaptureInterface + for AtsamdEdgeTriggerCapture where PinoutSpecificData: CreatePwmPinout, { type OutputSelf = Self; async fn start_capture>( mut self, - mut container: OutputType, // fills the container with captured edges or drops it in case of error + mut container: OutputType, // fills the container with captured edges or drops it in case of error timeout_inactive_capture: Duration, timeout_till_active_capture: Duration, ) -> (Self, Result<(OutputType, CaptureTypeEdges), CaptureError>) { @@ -366,12 +385,13 @@ mod boiler_implementation { // let mut capture_memory: [u32; N] = [0; N]; let mut data_container: Vec = Vec::new(); let init_level = self.capture_device.as_mut().unwrap().read_pin_level(); - let result = self.capture_device + let result = self + .capture_device .as_mut() .unwrap() - .start_timer_execute_dma_transfer::<_, N>(data_container).await; + .start_timer_execute_dma_transfer::<_, N>(data_container) + .await; if let Ok(capture_result) = result { - // let mut timestamps = Vec::::new(); // // The start of the timer is assumed at counter value equal to zero so the lenght can be set to 0ms of relative capture time. // let _ = timestamps.push(core::time::Duration::from_nanos(0u64)); @@ -412,25 +432,49 @@ mod boiler_implementation { // (self, Ok((container, CaptureTypeEdges::RisingEdge))) match capture_result { - TimerCaptureResultAvailable::DmaPollReady(timer_capture_data) | TimerCaptureResultAvailable::TimerTimeout(timer_capture_data) => { + TimerCaptureResultAvailable::DmaPollReady(timer_capture_data) + | TimerCaptureResultAvailable::TimerTimeout(timer_capture_data) => { let timestamps = timer_capture_data.get_data(); if timestamps.len() < 3 { - hprintln!("TimerCaptureResultAvailable::[dma/timeout] timestamps: {:?}", timestamps).ok(); + hprintln!( + "TimerCaptureResultAvailable::[dma/timeout] timestamps: {:?}", + timestamps + ) + .ok(); return (self, Err(CaptureError::GenericError)); } - container.extend(timestamps.iter().take(1).map(|v| CapturedEdgePeriod::StartToFirstRising(*v))); + container.extend( + timestamps + .iter() + .take(1) + .map(|v| CapturedEdgePeriod::StartToFirstRising(*v)), + ); // The middle part without first and last, is RisignToRising: - container.extend(timestamps.iter().skip(1).take(timestamps.len()-2).map(|v| CapturedEdgePeriod::RisingToRising(*v))); + container.extend( + timestamps + .iter() + .skip(1) + .take(timestamps.len() - 2) + .map(|v| CapturedEdgePeriod::RisingToRising(*v)), + ); // ... and the last one is RisingToStop: - container.extend(timestamps.iter().skip(timestamps.len()-1).map(|v| CapturedEdgePeriod::RisingToStop(*v))); + container.extend( + timestamps + .iter() + .skip(timestamps.len() - 1) + .map(|v| CapturedEdgePeriod::RisingToStop(*v)), + ); // hprintln!("TimerCaptureResultAvailable::TimerTimeout: {:?}, N={}", timestamps, timestamps.len()).ok(); - hprintln!("TimerCaptureResultAvailable::[dma/timeout] last: {:?}", timestamps.iter().last()).ok(); + hprintln!( + "TimerCaptureResultAvailable::[dma/timeout] last: {:?}", + timestamps.iter().last() + ) + .ok(); } } (self, Ok((container, CaptureTypeEdges::RisingEdge))) - } - else { + } else { return (self, Err(CaptureError::GenericError)); } } @@ -440,30 +484,32 @@ mod boiler_implementation { pin_tx: GpioPin, } - impl AtsamdGpioEdgeTriggerDev - { + impl AtsamdGpioEdgeTriggerDev { pub fn new(mut pin: GpioPin) -> Self { pin.set_high().unwrap(); // idle state - Self{pin_tx: pin} + Self { pin_tx: pin } } } impl EdgeTriggerInterface for AtsamdGpioEdgeTriggerDev { type OutputSelf = Self; - async fn trigger( mut self, iterator: impl Iterator, - period: core::time::Duration) -> (Self, Result<(), TriggerError>) { - + async fn trigger( + mut self, + iterator: impl Iterator, + period: core::time::Duration, + ) -> (Self, Result<(), TriggerError>) { for (_idx, value) in iterator.enumerate() { - if value - { + if value { self.pin_tx.set_high().unwrap(); // idle state - } - else - { + } else { self.pin_tx.set_low().unwrap(); // idle state } - Mono::delay(MicrosDuration::::from_ticks(period.as_micros().try_into().unwrap()).convert()).await; + Mono::delay( + MicrosDuration::::from_ticks(period.as_micros().try_into().unwrap()) + .convert(), + ) + .await; } // Always success: (self, Ok(())) @@ -484,7 +530,6 @@ mod boiler_implementation { todo!() } } - } #[inline] @@ -507,54 +552,59 @@ async fn toggle_pin_task(mut toggle_pin: GpioPin>) { } mod timer_data_set { -use crate::hal::pwm_wg::{PwmWg4Future, PwmWg2Future}; -// use super::bsp; -use crate::bsp::pac; -use crate::bsp::hal::{ - time::Hertz, - timer_capture_waveform::{TimerCaptureBaseTrait, TimerCapture4Future, TimerCapture4, TimerCapture2Future, TimerCapture2}, - dmac, dmac::ReadyFuture, - pwm_wg::{PwmWg4, PwmWg2, PwmBaseTrait}, - pwm::{TC4Pinout, TC2Pinout, PinoutNewTrait}, - gpio::{Pin, AnyPin, Output, Input, Alternate, OutputConfig, Pins, PushPull, PushPullOutput, Floating}, - gpio::{E, PB08, PB09, PA16, PA17}, -}; -use crate::pac::Mclk; - -pub(super) struct PinoutSpecificDataImplTc4 {} - -impl super::boiler_implementation::CreatePwmPinout for PinoutSpecificDataImplTc4 { - type PinTxId = PB09; - type PinRxId = PB08; - type PinTx = Pin>; - type PinRx = Pin>; - type PinoutTx = TC4Pinout; - type PinoutRx = TC4Pinout; - type DmaChannel = dmac::Channel; - type PwmWg = PwmWg4Future; - type TimerCaptureFuture = TimerCapture4Future; - type TimerCaptureBase = TimerCapture4; - type PwmBase = PwmWg4; - type Timer = pac::Tc4; - - } -pub(super) struct PinoutSpecificDataImplTc2 {} - -impl super::boiler_implementation::CreatePwmPinout for PinoutSpecificDataImplTc2 { - type PinTxId = PA17; - type PinRxId = PA16; - type PinTx = Pin>; - type PinRx = Pin>; - type PinoutTx = TC2Pinout; - type PinoutRx = TC2Pinout; - type DmaChannel = dmac::Channel; - type PwmWg = PwmWg2Future; - type TimerCaptureFuture = TimerCapture2Future; - type TimerCaptureBase = TimerCapture2; - type PwmBase = PwmWg2; - type Timer = pac::Tc2; - - } + use crate::hal::pwm_wg::{PwmWg2Future, PwmWg4Future}; + // use super::bsp; + use crate::bsp::hal::{ + dmac, + dmac::ReadyFuture, + gpio::{ + Alternate, AnyPin, Floating, Input, Output, OutputConfig, Pin, Pins, PushPull, + PushPullOutput, + }, + gpio::{E, PA16, PA17, PB08, PB09}, + pwm::{PinoutNewTrait, TC2Pinout, TC4Pinout}, + pwm_wg::{PwmBaseTrait, PwmWg2, PwmWg4}, + time::Hertz, + timer_capture_waveform::{ + TimerCapture2, TimerCapture2Future, TimerCapture4, TimerCapture4Future, + TimerCaptureBaseTrait, + }, + }; + use crate::bsp::pac; + use crate::pac::Mclk; + + pub(super) struct PinoutSpecificDataImplTc4 {} + + impl super::boiler_implementation::CreatePwmPinout for PinoutSpecificDataImplTc4 { + type PinTxId = PB09; + type PinRxId = PB08; + type PinTx = Pin>; + type PinRx = Pin>; + type PinoutTx = TC4Pinout; + type PinoutRx = TC4Pinout; + type DmaChannel = dmac::Channel; + type PwmWg = PwmWg4Future; + type TimerCaptureFuture = TimerCapture4Future; + type TimerCaptureBase = TimerCapture4; + type PwmBase = PwmWg4; + type Timer = pac::Tc4; + } + pub(super) struct PinoutSpecificDataImplTc2 {} + + impl super::boiler_implementation::CreatePwmPinout for PinoutSpecificDataImplTc2 { + type PinTxId = PA17; + type PinRxId = PA16; + type PinTx = Pin>; + type PinRx = Pin>; + type PinoutTx = TC2Pinout; + type PinoutRx = TC2Pinout; + type DmaChannel = dmac::Channel; + type PwmWg = PwmWg2Future; + type TimerCaptureFuture = TimerCapture2Future; + type TimerCaptureBase = TimerCapture2; + type PwmBase = PwmWg2; + type Timer = pac::Tc2; + } } #[embassy_executor::main] @@ -566,8 +616,10 @@ async fn main(spawner: embassy_executor::Spawner) { // let core = CorePeripherals::take().unwrap(); let pins = Pins::new(peripherals.port); - let dev_dependency_tx_simulation_pin: GpioPin> = pins.pa17.into_push_pull_output(); - let dev_dependency_rx_simulation_pin: GpioPin> = pins.pa16.into_pull_up_input(); + let dev_dependency_tx_simulation_pin: GpioPin> = + pins.pa17.into_push_pull_output(); + let dev_dependency_rx_simulation_pin: GpioPin> = + pins.pa16.into_pull_up_input(); let receiver = CHANNEL.receiver(); let mut clocks = GenericClockController::with_external_32kosc( @@ -604,7 +656,6 @@ async fn main(spawner: embassy_executor::Spawner) { // #[cfg(feature = "use_opentherm")] // spawner.spawn(simulate_opentherm_tx(dev_dependency_tx_simulation_pin, receiver)).unwrap(); - let mut pwm_tx_pin = pins.pb09.into_push_pull_output(); // Set initial level of OpenTherm bus to high: pwm_tx_pin.set_high().unwrap(); @@ -628,21 +679,24 @@ async fn main(spawner: embassy_executor::Spawner) { // spawner.spawn(print_capture_timer_state_task()).unwrap(); - let pinout_specific_data = timer_data_set::PinoutSpecificDataImplTc4{}; + let pinout_specific_data = timer_data_set::PinoutSpecificDataImplTc4 {}; #[cfg(feature = "use_opentherm")] - let mut edge_trigger_capture_dev = - boiler_implementation::AtsamdEdgeTriggerCapture::<_, _, {boiler_implementation::VEC_SIZE_CAPTURE}>::new_with_default( - pwm_tx_pin, - pwm_rx_pin, - tc4_timer, - &mut peripherals.mclk, - clocks.tc4_tc5(&gclk0).unwrap().freq(), - pinout_specific_data, // determines all the types - channel0, - ); + let mut edge_trigger_capture_dev = boiler_implementation::AtsamdEdgeTriggerCapture::< + _, + _, + { boiler_implementation::VEC_SIZE_CAPTURE }, + >::new_with_default( + pwm_tx_pin, + pwm_rx_pin, + tc4_timer, + &mut peripherals.mclk, + clocks.tc4_tc5(&gclk0).unwrap().freq(), + pinout_specific_data, // determines all the types + channel0, + ); - let pinout_specific_data_tc2 = timer_data_set::PinoutSpecificDataImplTc2{}; + let pinout_specific_data_tc2 = timer_data_set::PinoutSpecificDataImplTc2 {}; #[cfg(feature = "use_opentherm")] let mut edge_trigger_capture_simulation_device = @@ -667,10 +721,15 @@ async fn main(spawner: embassy_executor::Spawner) { hprintln!("main:: loop{} start:").ok(); - let (mut edge_trigger_capture_dev, _) = edge_trigger_capture_dev.send_open_therm_message( - OpenThermMessage::try_new_from_u32(0b0_000_0000_00000001_00100101_00000000_u32).unwrap()).await; + let (mut edge_trigger_capture_dev, _) = edge_trigger_capture_dev + .send_open_therm_message( + OpenThermMessage::try_new_from_u32(0b0_000_0000_00000001_00100101_00000000_u32) + .unwrap(), + ) + .await; - let mut edge_trigger_capture_dev = edge_trigger_capture_dev.transition_to_capture_capable_device(); + let mut edge_trigger_capture_dev = + edge_trigger_capture_dev.transition_to_capture_capable_device(); let sender_trigger_tx_sequence = CHANNEL.sender(); Mono::delay(MillisDuration::::from_ticks(5000).convert()).await; @@ -679,28 +738,41 @@ async fn main(spawner: embassy_executor::Spawner) { let mut count_success = 0; loop { // Test one round of TX(simulation) -> RX(production) - let tx_async_simulation = async { - sender_trigger_tx_sequence.send(SignalTxSimulation::Ready_).await; - let (device, result) = - edge_trigger_capture_simulation_device.send_open_therm_message( - OpenThermMessage::try_new_from_u32(0b0_000_0000_00000001_00100101_00000000_u32).unwrap()).await; + let tx_async_simulation = async { + sender_trigger_tx_sequence + .send(SignalTxSimulation::Ready_) + .await; + let (device, result) = edge_trigger_capture_simulation_device + .send_open_therm_message( + OpenThermMessage::try_new_from_u32(0b0_000_0000_00000001_00100101_00000000_u32) + .unwrap(), + ) + .await; device }; let rx_async = async |&count_all| { let dur = Duration::from_millis(100); let start_time = Mono::now(); - let (tx_device, result) = - edge_trigger_capture_dev. - listen_open_therm_message()/* -> (Self, Result)*/.await; + let (tx_device, result) = edge_trigger_capture_dev + .listen_open_therm_message() /* -> (Self, Result)*/ + .await; let duration = Mono::now() - start_time; match result { Ok(message) => { count_success += 1; - hprintln!("Capture finished with opentherm message: {}, took: {}, rate: {}/{}", message, duration, count_success, count_all).ok(); - }, + hprintln!( + "Capture finished with opentherm message: {}, took: {}, rate: {}/{}", + message, + duration, + count_success, + count_all + ) + .ok(); + } Err(e) => { - hprintln!("Capture finished with error on OpenThermMessage: {}, took: {}, rate: {}/{}", e, duration, count_success, count_all).ok(); } + hprintln!("Capture finished with error on OpenThermMessage: {}, took: {}, rate: {}/{}", e, duration, count_success, count_all).ok(); + } } tx_device }; @@ -830,9 +902,10 @@ unsafe fn raw_waker(waker_ptr: *const ()) -> Waker { /// The idea here is to use simpler to implement TX driver using gpio timer to ease on /// implementation of the OpenTherm RX driver based on timer + DMA #[embassy_executor::task] -async fn simulate_opentherm_tx(mut tx_pin: GpioPin>, - receiver: Receiver<'static, ThreadModeRawMutex, SignalTxSimulation, 64>) -{ +async fn simulate_opentherm_tx( + mut tx_pin: GpioPin>, + receiver: Receiver<'static, ThreadModeRawMutex, SignalTxSimulation, 64>, +) { let hw_dev = boiler_implementation::AtsamdGpioEdgeTriggerDev::new(tx_pin); const DURATION_MS: u32 = 500; @@ -849,18 +922,22 @@ async fn simulate_opentherm_tx(mut tx_pin: GpioPin>, // Wait for the receiver to be ready, give it some time to setup the capture Mono::delay(MillisDuration::::from_ticks(10).convert()).await; // The device implements the trigger interface, it shall implement send as well: - let (dev, result) = - pass_dev_in_loop.send_open_therm_message(OpenThermMessage::try_new_from_u32(0b0_000_0000_00000001_00100101_00000000_u32).unwrap()).await; + let (dev, result) = pass_dev_in_loop + .send_open_therm_message( + OpenThermMessage::try_new_from_u32(0b0_000_0000_00000001_00100101_00000000_u32) + .unwrap(), + ) + .await; pass_dev_in_loop = dev; // Mono::delay(MillisDuration::::from_ticks(DURATION_MS).convert()).await; } } #[embassy_executor::task] -async fn print_capture_timer_state_task(/*mut uart_tx: UartFutureTxDuplexDma, Ch1>*/) -{ +async fn print_capture_timer_state_task(/*mut uart_tx: UartFutureTxDuplexDma, Ch1>*/ +) { let tc4_readonly = unsafe { crate::pac::Peripherals::steal().tc4 }; - let count32 = tc4_readonly .count32(); + let count32 = tc4_readonly.count32(); let dmac_readonly = unsafe { crate::pac::Peripherals::steal().dmac }; // let mut value_cc1 = 0x00u8; loop { @@ -875,7 +952,8 @@ async fn print_capture_timer_state_task(/*mut uart_tx: UartFutureTxDuplexDma>, mut rx_pin: GpioPin>, - timer: pac::Tc2, dma_channel: hal::dmac::Channel, +async fn full_boiler_opentherm_simulation( + tx_pin: GpioPin>, + mut rx_pin: GpioPin>, + timer: pac::Tc2, + dma_channel: hal::dmac::Channel, mclk: &'static mut Mclk, - timer_clocks: &'static Tc2Tc3Clock) -{ + timer_clocks: &'static Tc2Tc3Clock, +) { // let mut edge_trigger_capture_dev = // boiler_implementation::AtsamdEdgeTriggerCapture::new_with_default( // tx_pin, From 28e41bf54cb1aeae259a6ec72ebcaacecf0f8f55 Mon Sep 17 00:00:00 2001 From: Pawel Pyszko Date: Sun, 11 May 2025 11:59:21 +0200 Subject: [PATCH 4/8] Add some temporary documentation files --- boards/wio_terminal/examples/image.png | Bin 0 -> 53907 bytes .../16 MHz, 304 M Samples [8].logicdata | Bin 0 -> 3808 bytes .../Zrzut ekranu 2025-03-16 221251.jpg | Bin 0 -> 64711 bytes .../Zrzut ekranu 2025-03-16 221318.jpg | Bin 0 -> 67502 bytes .../Zrzut ekranu 2025-03-16 221338.jpg | Bin 0 -> 198993 bytes .../Zrzut ekranu 2025-03-16 221354.jpg | Bin 0 -> 154517 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 boards/wio_terminal/examples/image.png create mode 100644 development_documentation/development_atsamd_wio_2503/16 MHz, 304 M Samples [8].logicdata create mode 100644 development_documentation/development_atsamd_wio_2503/Zrzut ekranu 2025-03-16 221251.jpg create mode 100644 development_documentation/development_atsamd_wio_2503/Zrzut ekranu 2025-03-16 221318.jpg create mode 100644 development_documentation/development_atsamd_wio_2503/Zrzut ekranu 2025-03-16 221338.jpg create mode 100644 development_documentation/development_atsamd_wio_2503/Zrzut ekranu 2025-03-16 221354.jpg diff --git a/boards/wio_terminal/examples/image.png b/boards/wio_terminal/examples/image.png new file mode 100644 index 0000000000000000000000000000000000000000..c612afbc2f9baf1e7e3ff10f52c828b3bfcd7d21 GIT binary patch literal 53907 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�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%0DPQ39I4EV3{h=gD3o;M zv(9?me}+`=m;dpAf9HMu-*npk|7r*Rs~`LSzDO!VfkR9i+@!zf`hmV+0CNecLHplLPMqy2HRy`fAG3CPdu@tVoRcFCtb}n)_Pvg(hO z@Bbua@SoNGTbYLci`CeHK-Yp~=YOiym&2*4skCAsqT@tJ6J*c06aA-<01tNE9tbV~ zLMnCb{X_lU1D_AGcmS9U5EAgc0whBJ?!*^SR*rMcvAI{T$sV(=@hkpA^Lr*H(uDWn fS%6qz43nK(yFb0(-~QcxWxd;mw+e4QdG$X4*Jo9L literal 0 HcmV?d00001 diff --git a/development_documentation/development_atsamd_wio_2503/16 MHz, 304 M Samples [8].logicdata b/development_documentation/development_atsamd_wio_2503/16 MHz, 304 M Samples [8].logicdata new file mode 100644 index 0000000000000000000000000000000000000000..42b5f70a63f01a50bda66736c7a0be54ffd5ee31 GIT binary patch literal 3808 zcmcgvTWl0n7@o7cy^=^;XhVZ`CxQ_&FWv2Sw=JKQpol#1w0{3TXLhy|mzpLznKS?S zzwf`C>&%(MqEwW&>O;CZsEj@$OfW&B3J) zA1Fd(>jIapxN;SDuHwm63UZagT%|~eWtkLYukcK`g02Fk^}^_5(9s+`vN_Qhm=K&% zL`h4x9*aeyY7H#NR#BF=gA%qqHCP-MZDO6spqBOU<@Z3Z5F81F%eMC~w|8aJWm_gN z0OFyTB}cdISi7<#1`9suPeyob$H2;t7+4;Mj3}`E>Q?r{9B}_KqR{pWuIz{D60<@T zOK544?HS7FiJ20eqEj(%o*zsvO{}N2EP=$wmKBt=S9#ht-!D{z%Uo*Qa6Di- zFSdwzMa-WF-%Fm-mQd~_&uG6V&uRBd8OPH!zHWu@&wePM%zS&Lr0n}E zCD}OuU4aEl^KQ+EB{TAJO91|b7MCksQt3#oXTDVGB~n^^`A{YE>m|OKpcJh;RBeu=;8;0IjynD3Fe;oy($ z|C4#EJ+DjN>fh;Y7pn*waQIpFB~_Q0e@pU*9Cn{xOEEto@o@*=G5#g6Xcu^@_%U&vN{q0qri~D zh&6?ft)4T7ydgw3TR|-$>mV?*ZsKG^de9q7^`XV}-u_e!GUV-pjHRN8M={J;RIkse z$5KJBzeW74OeSIJls__MC zYyHp~8dShweKj>T5Vf^JTJA+D9lXJ_hpWS@K?*(Z#^zvzsgy7N!HtOenhUo09) z7k*G&rcUak2A&q;CO%%5*k^fOoOF<;JNpZT-vhq*>`s1L*^gh^qP)}SGhzqTy?VSC ZGxCCN#D%Nbhk}=?^u+&``5T}pe*@Mb5l8?4 literal 0 HcmV?d00001 diff --git a/development_documentation/development_atsamd_wio_2503/Zrzut ekranu 2025-03-16 221251.jpg b/development_documentation/development_atsamd_wio_2503/Zrzut ekranu 2025-03-16 221251.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b27081eb421f48d065656c26ad41b53ab5623823 GIT binary patch literal 64711 zcmeIb1wfSDwlMtA28bvnAfu9kh)OFm7_hl9jnas8 zNe(3`G2}2z{10y!?>Xna-+jOD{P&!5A9R!__MW}hUTf{OSCT)IM}WPmiYkf#1qB80 z4EzI-2Y?Fz6(!~NU+{+-{7bWghK8D&W)~eD?GE}~^z^%T?cU9>XWw3iJxqIc@7~M0 zmuWvU3kwT9BO5y_Gy6Vf7Uu0vD5$_T)HFM3Xm&C)>}Fv8Z~u_r157)BB!+k@iX#9e z69p9$1-S-*0RROJINI$2|Mo*c367DLZpY4DyTKO<_X3m@R8*AIRNJEl-}VHb1Jq13 z`wpKxN4sC^2Hg<{=2H(s<96_z&;7u1rG1H4%*^rO&Rwi*><2jb_yq)y3W-ZdN=eI{ zzHm`NQAt@v_3AZk9bG+r1M`~}w=Au!ZJh2pySTdDyZ`91kFVd8r~YB#&m$tEUc8Ks zPe@EkPDxEm&&w|;EGjN}S6WqFQ(ITx(D<>Vv#YzO_j6zW=-Bwgm&vK=nOW@e%Iezs z25u9-JueD?>ep#~o7o@c#RSfalA4-|nr?eu6qK&uhl+`s=I}|{edo03ZaC~ea_Yej z=JTO(xgU1&h+SD?F>`F+#mXx_%7@*a+AlNvKbzRYzcjON6Z<}|et@2e0$e;QCIAj> zN|E-evu#D0;`UmRfyet5NMCwM)Et8i2}it7kbzO^cH}!}GVp*l&4zn-M_f$p5jG*d z*F2MsZXdE!j-=Ws5ydW%ft*l65O5W2&-hz=4cs|#(*+=e*m#5 zhpk_0ZInD^vFiwwe$PLhFE&|w>Ami#)NP>66g<9|1dK6_aPOZ68uNa z93!5`Ra2(4`2$t|?Y4jMEB{(En=gYMLpEA^7o_?E6fLafslE3|)6Hp+KA=dCHeeg0 z@G5V?9{~nidT50nEF}Z)9~mN-9?ii%qXyvpQkXtD%kcHq{Q578Es{hV)H*#GScR34 z0rXB1GsT5(@ceO&-yy_{N}m_)S6>gyeX4T>;N2fAkJs{aI2P5@bE0WNcJ`A+ovKeA z^35+R`#Wa*0RJ2^aJ`iboZAZ7T6&KB{_8*ZJ~Z(WoJh9-U#z-zr%}Q;wMfi}^DXwk z6&=kzAYa-AjC+`mik$A%Y#T2^yeOaPGcI3wZQDv$$k+PF*1RVueb{u<@ERGYZ?cU3 zj7=j0PmBGKbEDuqcR7!ffm&7MpaHO)VJvB(&w5sMmRH~Sd`8SC&sqDgTOs(}CP<7W z!3JSkCPj1!Cj;jtBmiwm-P`_tdgVgpJ}aD5YsqB5auC^OutM5`kLwD??n3r$z*bqy z$v|Z*uAPW-0B4G5-TF!ff{fz5kN)_!(z<6oktPSem`{>$^aoOYdKvaZH{BlvDN)~F z7DxVTe`7YvF_9@_m{JBNLk2i>a!W%4By-(9T~;|Ih3hW;x0=d7d$~FzKhA4MI0wAX z`ar2TwU%*BlLr1B@vKO#AT7ogP7&9Ub=g|-%w!;c0X{8qj|_OCunAjo?0z_}cN4vzLNUQ##WSclwzFs~rsLx_v+*p!P=FQ%-nGR{O6c_6>ml z8%gZX&{FrSu9v>Xd_eQ#Df<_nTc^~2WH}In`Xdv5!xu3a^lmMn&hF**)|U=#eLn~_ z9a-D7=NwRb-El`Kx^A2H1~lH8sdO*yltsM;#if5^3Fx08HpG9N*LsFvdtew=X?~ql zfcekd;Qx&K{GSRj)^#7}LpA5vd~~0Ipz!IY+^+pBrmOs<2l(HjH24$RO#4|%58sme34H%Awv7LnleX*UY(gGt zfbG@J7RXw!ZJ`%Wb`5{O2LX4^evMI&_Dg zauTdLe-hCl=JW@x?FEMv)eeKq=!mY=&iDw+v#lnDNt%Pm?0nGJ@q!Gz0ql@B-gCli z(5qx1Bsm*hB{|kR>i6`Bj_d9;ic-Zv%yMg5wkNs=mUXw1lMIB}k0G~qh7eQa_i8CH zQ=G;a33>BBqj=95#6rVNFT`FwYN>?4D27@ z&7xCPYjEEE#9prROn<{c7GQ1XU`wK$ePZAu&hB zX)7u%lz~bfy@*Ydu9haoHZC{u(%@3YM zqf;zXt@pXXt6idsnVuUfMC(QPjwH!9azCe_*{DY4&lJ0|mR4jDYvX-OihM3>I-y?0 z)iJ6@w~juEuD|rIq$lfkU2I}a9?$+~4#9E}eHfd5ov1`yT%@Astc%t)E)DvZk=cyL>oyC{-Z%gjalNkf~|sO>X#rrya)X=CZ>f;*@Y_PwGsi z@Z0vgWPti(rxK4rtFSu9Gkxag6CqXJ!i`H`3I+!UW?k@)`WnzS4|%VfEjG%+s?rI1 z#>2Aj0zER8y@3&3wH&vwb>@n&gND`EnT?NJ_^>r)c-1odu_oN^~82ZXHa zNrn`UOFN`DV)s;H)Y}T0&6SrXN{ythm>SkC4=Xdi2ploD@0>BQ%rwYGWtbQq9(?<; zf$^=x%-bO=e9XH%2&>y!@%mX`o*zp}pz<-Am@LoN>0-Omonx0ktt9@b33chU zCbePekd|G7GoV%))Xh38rAb}(ha0r+QUvXj$lh0EVA`xAf8BG*w(i~70SWhtOom38jdv>(Ab4;U0DR8#yrZ_!#e=%w)h?oyEUk)Y%Vy>4glihXcu&wob-STVw zwnVjK7BA|JpXe{T=&;xOj#%JYETf1H8Sqaw#jj`(>DwJK*sIp)wL1F=A;AHX&^-;J zObY3-Rhd05b#;XdT)IU%?=3ZdJgk5A=uICx9>pdFfuS4Ih*Z#=DR>OE+ApuEhNQR46lr~ONw8@hwhGp2Q+(}B@0&jXG>du_B+mn7FX zk_&5adMGA;;B?`aq}mmUKskkU?nE~=t^SaA}_^5_UsMy-1;5=h{XFh$@|WrKtiK`stx_d^fmD<&nt;?D%^UtV51%c0=d1NKn!zFO$Bw2im^IN}&A~*Ys_Lw0>C6 zd1eYV=)nL4l5OIKkl_~&1|b843=mI*c;YVt55yB8p7>4rg^=NIkwMKWE<29;CYz?x zxcmWh-v)YR6Fjwf^@FZam`wtBoI~51#<8Lp%7uE~hK~-3hxw~)ox1NSJf`tPGFKlr zW%6)G54(9q#6EF$ZHi!knW7PCIZOsxzvK|NY~cBe$f@OC60y)2^y-5iKp=sT0U`s4 zGeA5M+714EgY-wJy|B!;wmTHz)edXoIC0i=Htaj}S)axIXXWHnP=5 zlk@!bGqKfl*Je^w3bOY-Nin-QxnD9PxhlF%OdbI_#t|25zY8n*7IVeC!Lm2 zT95pJa>L>1&vvn})Wl1TM`91LN@qvaa8sAJ*%y?Vi@VZk260Qu%X4~<&x*-&Vc6?v z&z@8Zt;gvsJ}u|GH#%fBE z-teURq88eV?nn+%uZUI(KBF>xacegBr0^R1SRrzhvC^I~Ib&b>hot2|rkYQ)_a??? zy$Gk%P^Gl!YEqL4CtIlH~T`AEvJ@PWf#mCuz@hMFm&&*;an%pIvy4XN`!vg!1i zo_N%e?lOE=TgB3FN=koUt3+bJ!8U%;=dV}%)B9~_o4-iBo;xn~<;5!(xfiiJI2pRA z)|kuE@`S5-c>T4yMl$G`3`s0Y&h-I@yLgLZq@!E*A62A|s*~m>w9&FI-j{OkA%%{V zy)mpVjeIK*%;|oNGINj71U+L*uAxS`vd*o7muB{5hl3uzUj0xDD3lEc4X=s!+s>Dt zSFsiF7i%Bpr&_5s>L5tDr`oXgEFYul5ibOybm-FEh;YFFUy$Ljh2g($b75n zqS`L};^l>B4iT*eIsB-5^o`7s;k-E;ObOSUSJ@}MkNnc0qqAHzeKslx!SH3q z$BY_mr4Gq67YwL$j4ovRZ+?9snQ}-_CaEjSB3jQ*q|XEQU>9c`U6#?1;w;dxB7xf6 zC;%f%1_**b{4Q+j%$Z6B`(Y&do%43;nEvdKJGJR;a>L}QCvz4+n~9UxiQBW$j6_!? z5$IM+0nG|e$UteKd2KTp*ss2uRMrSa8*sFgg2n{Ktqggt1r#ZX49sPKYJJ7bZgh+ncNG9U)fRymuz_j`#T6x#z#c((XJ`aV|) zT1#okKwD2m*}6Yy1y#yI4#0`*1r*qW%v$m^(C1$p2I`h%0QL;ot3YyQ`2PD{3cMKj zfcp-zSAwLyBwCZRDJXXYT3C)B!;~!A_IC} zg&l6LB$X6@_*Y)|x++i;@Bc2oDvG1_`4vI8;hQ>F+a6XQi8gAfx-a~EI(x;i?W@RI z1}>p6h$nbVL7U2(YA+2->%7(gmw00MJH@IqyWz!`){_mJo)B`6dhK;+GtXh+)Sp3q z4z+H)^-;F6n-l? z#@(_?373X%7w3wXT0FmEFn`DC@D*kJ#4arAmv$Pb;Mzn%s{XIeL8mrqqVSJwv@+*y zUY$1yxE;of8g{{7b;hvuThC>yC(VXdJMgx%SlLmDdCZ|+IFCz=4d3+2z+bU{a#t9y zNmtZ7wj-zVbE#q>BE?2g*&=#Yzs5|Vk=L#juG*T9O>UQx~qv8wsf$Q{KVOwGm|Af z$ELcIUf59^y=WO9e9hIXv9wXc+Ak7Zac7w?e^a0_2ov5gavy6%_Zl^9+7cqcsAJmV zefIvALHq61c{Sy{%7Cx-^iHgUGS5or>2fFw7bR>tdaH8>f)tNx>kQVTwf4~mmjvL1 ztA77(qip0ZU>$+x=o>Vd#hTg(yC&D%u)1-|&a_0Vz|%>+vX{@VhIM%e=i4${rIx@~ zE-d)0rcBAi>)gDF%%svRI*I${y%t@#58$ru7kun^4!645rhRvGK*cq|dgR7x~O&Y)j;BUA~vYxbI@cu8@!6 z9>%9i2!d3lxTiV%afrD70b{>nJ^hJ?M^B}1l{6e&LIgIXdVo-0VXkyUx_RBDIIeJj zg$;xy><#x{zR~LTmJHOiULXV69mr|gyaiK|Ks~WL3&x_Hl{Wq25DBkA1bWn4kj14g z%oHJDtZIE3vR4V*T7wL-!>hb_!L`#HeYfP3oN%gC+%BgM7H^u zgCX3VNbEI=5ADWN1ioQzG7z_`o(xcdJ&kSq6qHqCRuAiufswlyf;MpHt}YmOw$s%Q zJ$*hGj5VG?@y$CoyUJ9OZSF$3&z&6 zgplae!H8Tr6dCTdQGMX7O0^l4!*-uh=`g#C;_ zv3vITFa7}+mHq~!sUATC`jJ{82>r-}ARL0I?B{(E;zK`f^$@3q_Ofq+9<+5pTgUGb zJ|v()BJ58`JV?m@@f;1EI)2s>{O4>Pvj{kj@Q73@x{^Kcha+s-I3r`-Y}3e@y&oOn zoxkW)s1}6bxRpnwcR9;B54`x0jpwjzL?mdNiHlyFAZAIFv}<|Dn6W6*Ee@9)RIBfo z%uO}I7`t)Q8NPbTp%0tSr_YrUcqGs545?2b1rwx#gOrqz))dk!e>;#tns>-H@uSrF zM};|Ly@Bjo|MvzrXPqsg1&ZiVPnc^*|18$)nH=w34LV=f!wKKjKO;uW=2?G{ofzsp z)KJakB+n#;uU^XzuSPGc%Yw(l8;8k2-rslni$H?m!OL8opl8w;oVY`KSJ?3Ayk)Re zOCQhmkDfbZShPIyE@^0+x0frB0Up{EGGKZI^kBN>KXzPJCDqtSO*Y>yO!0Vgful-e zd$6h2T9eO_qnz#N=>ztg zFl;(UlwU!_yUqx4oaWj0Is-9^j0X{}&X%+4q?gIa-UgDQH|TA#V>k$mY;Hm}Y$vl@ z3Yx~O(iam#aNT2FcCowQ-*2@4{3^oWvk`_j$KIZ|Y2Sj201r zeY{pby$J@-|5;-^6%UQ3eh9EDdbwp><3+VIb>wnz+K~~`HHwIzhxOwO=Kp*KCfnX> z34hYOo26@5y&!K-s*m4@AZ0Ou=e`t?N(5Xj5}4*&DgoJc3bPC!*R;kf6eXF{40Sh8 zy*jmXhp(gmDX~DE%Aln+k8>G%GerVd+%qQo0QY2V^R?A6Z!s|O__rlWATd>Nk{(2o zflx3#Zd;ERx2WG5f+&fbRdF^O5ZHP5Q@U5>>mJ3pL__N$v7!FK;d^i6p6yIIot!ZURQ-{D=V(P)N$I{zQntQk{Bq@2 z?wLs(F}9%wopd_28Q=fgB7X63K(Mv(rn3a`O z>{m;biw1>kr@h{c?VfY2z1TC)ldPomOpr?8rWHA?pM+wj`v+pl8BCQm;$aEr$7U^A=mcz6!%t;EU<- zx0ys<&~Y+-jpgEVl{Fn zgO2xiEPPZhUf8;5J}Gk$aXt>LJY)>UYnh%T1E+!60pbmOugWN9lRrifOvm>O3~Cg9 zWxwV(XDr}{oQQTyAXR};2sdNtRRt4rD7|+H=N2w;4jNP+l|Jr|dC|;*&NKZK=$ZZ% zjMLpSQsj#<=uYvm4A&?WyBK_`ERR;1U^Dg9KE79$C26i$GIw|T-5bvoAFy-*cjW7{ z%-q;&Bn_^2Wjb@!Tc*lix0Y}D^vo%wDhYe5BC&R~0$z#C-9}{bFiqRSe8Fe&`Q-+m!+d$PgO2xdPIMW5t3Ky)dbq;Ec_fJQa`2VjXKKPK z+@0_F99l<>bYd|x_w9)uj1yK=IwvPpGl=852Z7dy&pZyr5#ql>FIcF4wr9cwnT(Vv~zZ_!<@LjAVjHpwRSeO;Rau8nr%vHjgQ zx9nNfY{EY4saQk}0zzKKHBNKUUnCZH%(z6GM5S4mJ`dM7QJ=^+7Q7NDbM}l`cAs18 zIX}|A2e$iW)}vTs6f$idzvTQfLA;}WEf;$^l+M@VtfkY|Mlsqt+0S}(*8^0Cw}|?P zE%%B_7k`KInSinkX3SBy_fLIyp3%74kzBmQ?L&)-*GQc$KB1C!v_G^i#hN;pSdv9F z9Fz1HCj)!sWzX?VxZXK!(0`0RjgfBm$F&L9c_ZKau`J2urm7RQax=%Y%CT<=sFQNf zDem`OYcwdwoQC>@wCW9lnXwAIj|*8U%AdHBnu4H zvQHRf>w$tl^gQ|6&>HVeRa|P z%KFS5d3>)3md!XHOBay6@YO=h@GT(M0e=JBjV$ho!t7py3#%3vM)!@G76`Mtwm4qC ze|O%*g?&UAh56TW^`k!d_A6T2t;1Gi9Dr4(*|P4vykb=J~lA=EtGQ+ek>Ic&X2;R@XU zYf^`;bH>h>+=kkbY*sDTA{uyMsvD*(Zj0}S_b&B5^0y z#9!J=bPfT*$TAIQsuVWzP;B>*v31`qvK|@6TTJ82KgdiKVj6|Yx>pqQxoa&L`6UyH z2VxnLM0kYa7hDc&d#cQsbdTlu8Ep8HcH_L3ch8u)>nE4Qa9V$q>~o2^qv$fy8)C)b zh04%4Wy3MS!DST}rZN(yIdv8|S8I=}+EOiALi&jtpD|IlGn83Kj?ps< zLQ9N$r3lwZ%%Bz(Gh;HB)(8e!VW&uXN`#Eb6-(>tc9LJm7ojCoI#DAm8;l)4qt(S_ z(!9rRa&w==Aunu&HLA~ajR|D-Xf>S)Fe!D@A|4M$Tq#PhtZL!3R>E*EsqfQw9G@sJ zbzakB_ID-l6vI~5KvSw0YlXUpai*KbsIIS}a%EU;N?G~JrRx@NuO4XFknkY`yC2n= zuEO3Cq6k7yrBOH~JNVS>3W*>BTM8aTwsubuaR&3~(K~KUV982&7?@obwp1c(v0WGv z)rhR}hMN3SAZFSoC*oE*XWSVVWnAKx_%7(6ve8~W%F~*3Z;(}I9KdwCx~Pp&8yRu^ z!|&ba5(Sc{ujx*khV1C{V97S_Pru;A@H$WFNbJYASI%+if}w!>B@h)(Ris^L^vi5} z{sPpem)=cgX0#6T^4u0g(1fJL8M3unfV4n%DUOqfB8KtaLf zy=oa+ltgSQj;~7Wj$V}8W2|OA#-KAvw>;&iToxup6>Me&S-BsH+SdJX93@lD`@TK(%mLwHuBZ&9Zdt5p5MzEIz5$ffsG zT(maL+a2#`FW6|6~w}Ny*35|D}lMQFM zZ|V#vcG(XtOK-s6cwppN;Cbj-9#GKsiVXR^RmNRS8jasC61Id%@lvpj8Ji(yimSh0 zSdl)Gfz+USBHo}FEZF${>U(!!300``-+=^y1;Q4Hh(L}6@fL_He%m_!eYv8qJ1oEX z7MOA<(th}9SrK) z4+{mAEYn?3xVOfFIwC%~d`6_pF1_DzCcrgSc4NQV{dO~HwgmwVu$o`YnrUOZpYKI2 zjFEVdq-Cl7D3?b@>t?e)ZSZW82WUTX-L@auG*?ZFWLWbK4)FzAmFdD{zn<)kAp zv%x{Bee88zTN)o3Z)|6fH3;hZOa|_qN_KeXO+@5j1*)+LvmAUCg-u5MiBDWlP$9T( zdL4+}19#@k$i%{`2VTMUSB4}!EmYxodeaP`d$yCPe#-y{Gp#I*>1Q}~PvAjOgExKJ zs%#srt%V2!f@GI7Y|riiVfTPnqwom6ya&q^ zuVX5^C>p?=p*H$%aD%QJC6NOf(R%ZZ`@o0eS$xHh*8-YPuCd?dbLVZDDcNhImUuh>PSLCHiPB^)AjHrSa01Uud7niN2X(?^DHBZ`Oq7Ne$h2WN6^` z(0TrNv)w{LV_dNr5b9m0ae2+9X_S;+8WeasB5_@EL%q=A5Er)JTbl7}*%oqWz$E@W zSP|6l3HQrp9&3C)Jts~XwD3?75T#f)@3kO0Ei3LiB)>|u@I7|M;gJz;#*XwMHz~us z%A$PK<>)gRF=l}eqQFdn+@)Tb36!M0@HubL3*8j#JS1##jhl5e;z+;pY6}A0LE@Y% z_07aP8Aw!ADOw00J_yL6RAXc4dwC1ry;DDZ>*3(rNO@YegYIp#r&9H5%oiRMcnb4u z39Vx$3d;53V9wlUKNArt6-%U)6g?$(Cu?5Gyn|`N=F~lQi}!)Xtv#SyH5`k?JRnJM z-PcK@&u=Kk+P!=PUr^FKai*tdJ0Wiyt;T0( z3#p?W&dLV23~?*sG3H(q&UI%!GZ3y7oE6hl0zbCx&Vp-%?F`%yySFmm&wd=>ev$F2 zVtQy%o;`X6S*t#3iYLN9R+L={Cka=#65fVv>iW)s0TV2upzUA|to}AF@wNXs3TwO{ zENf>@(q4YoSui>cnnPyuwB0niy;7PP!8D^$=(RN|QlO3CEfMGY8U#Y~cC@tc%;-|W zqySc|7)wV8L+wQN4lS;zjtT#KDb%n_Fy8wgema&~34zfXL{AVBasJI&qV!~-p)nD( z!3>&$dS?0m*CsiDWue$5@neIbRpxgpT<@{i_Jq8cN zxcw#*0W{i}M8WW9Q>mWB`wIfl=YI|c*_cr{!KVrHnL(=Nl5v)Y*+ra6 z9xO1N`=PP6-C6*DN#2p!eXQ3nbKN;%MdfmU@^YXX#RK@8`#u{r4ExTVIY6IhsDH8O z@nQBT>!E>OmT}ZTBy44qkw{=h+UX%%C*T`PnsmASZq?Pr2DS4;6HDS@hDS1tt9fe6 zc0W76^7@gMTS$HB+3uq3BHeQa)@CPnm^)L`U2#BNM>HUdkHM#wVOuys(jb^x6xD!S z9EoK#tHAmOWE-|x^}p*VQem!|YjT=o;SGJ?f{>6!+~AH%sfk*V&7GyxwWQ+%Zj@jb zgqz5~-|fjEWK8EN|Etp(pU=teD^ue8Ql5R2=48Y`8xcX z_+c&a+xKnlOX+&b^S2zUNk}jz5dAP@zKL;@;720u0@g5$xB-RESJk~&2ZamE?WrU4 zcqHF{Qg&222!EshYK%65JMNUo(32uHC1tQC={3$Lx2ehmD%@JXuMzAU@F*(c5Gsk8 z!FcE7_+?Q-rSOTXvnP}!+Hj(^I{gg6#k>n|2b-Dp#viR4t9~$3hw%Omp@i?F^FHgO zg{^yh1D#h8nvmuD2-l$h zF0c&Yd&L_H)o)F-^zLmiJ5hIS^ocxU=j%>8S(Su7+yhbb-Y;drV-ug&=QwE|+rr#= z!s~68?Z1Hs{;gB0-c{2l{kKrgvYicf5rqN~OkL0RR=Rp!LvTyyN11d+R$u9uw@q+y ziQKK?t*Ih?{j!Eh_r!P5{D4=Ppqfs7v<%61K_c<(hyDHKu53EF%DMJJ)rSoy7lhVw zLJAP3yJ~Hj@yPzK9mobXX&r{k&A-FxhgF;}zcDz6>Dd2? zx2@eQNs8(Mq{=Z+W~#OLHkh=`HR;=SBk%mzyVQ1PK7D!a03FY!CLIRzXEIiXp)N65 z_G-4Z>wV9jv?NYMYV=Xcy4ZOhIy#Bg85FDC-68JQOFZW)iVEo8zG`y&VnJW~q;W%> zqEX8@g>d{Ra;m-xjiX}uX%Vhb)V4!Ue@96O8b1;!1Purp5a0N9vw~;=q6LT+AXWz@ya*0O@DBi$Z&+iz0?a6M!XF{`%}1X3&5&` zOmRkF7Ngb>ALJ6P5HL2K-T4DtL!gHg{tysA3WI6U^UQf`i+TU~&yOcu$YV|6GaK0qo{DM4$@t#9=W41l4x%Hw9vof^+!>^U37YX4S z%*fBm$gdI2#ebV-Qj?ZXzf(38Nm1e%Doj~_U`9JN`OTa63zN9u_D^#R<@|hRdn4ek z#Yk1*$MN}J6r8}^j^T8Kt#hD@L=-`A`8uehw*sTpK=-WH)9E|;Q4R

yo#fq60_Zl4pjEy5utZ#XJKI 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 literal 0 HcmV?d00001 diff --git a/development_documentation/development_atsamd_wio_2503/Zrzut ekranu 2025-03-16 221318.jpg b/development_documentation/development_atsamd_wio_2503/Zrzut ekranu 2025-03-16 221318.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a15e05c3031c50728e39a769471aa008286344d3 GIT binary patch literal 67502 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 literal 0 HcmV?d00001 diff --git a/development_documentation/development_atsamd_wio_2503/Zrzut ekranu 2025-03-16 221338.jpg b/development_documentation/development_atsamd_wio_2503/Zrzut ekranu 2025-03-16 221338.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7ef07fc1a7fdea9d66efaf57a44cb23544437286 GIT binary patch literal 198993 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 be4mYIT`Q0S>TF17uX~t^{1M%KUiV)B>ObD? literal 0 HcmV?d00001 diff --git a/development_documentation/development_atsamd_wio_2503/Zrzut ekranu 2025-03-16 221354.jpg b/development_documentation/development_atsamd_wio_2503/Zrzut ekranu 2025-03-16 221354.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3a310a21641a7b95711f8342cf1a59568b0988b5 GIT binary patch literal 154517 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

K>fD=akfJiG+g(+I{p?cey$!$WVms`OtaJcOez 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 Date: Sun, 11 May 2025 11:59:59 +0200 Subject: [PATCH 5/8] Add some eic.rs example --- boards/wio_terminal/examples/eic.rs | 93 +++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 boards/wio_terminal/examples/eic.rs diff --git a/boards/wio_terminal/examples/eic.rs b/boards/wio_terminal/examples/eic.rs new file mode 100644 index 000000000000..1f0fb1c6e14d --- /dev/null +++ b/boards/wio_terminal/examples/eic.rs @@ -0,0 +1,93 @@ +//! Uses an external interrupt to blink an LED. +//! +//! You need to connect a button between D12 and ground. Each time the button +//! is pressed, the LED will count the total number of button presses so far. +#![no_std] +#![no_main] + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::hal; +use bsp::pac; +use wio_terminal as bsp; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::eic::{Eic, Sense}; +use hal::gpio::{Pins, Pin, PullUpInterrupt}; +use hal::prelude::*; +use pac::{interrupt, CorePeripherals, Peripherals}; +use wio_terminal::aliases::UserLed; + +use core::sync::atomic::{AtomicUsize, Ordering}; + +use cortex_m::peripheral::NVIC; + +static COUNTER: AtomicUsize = AtomicUsize::new(0); + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.gclk, + &mut peripherals.mclk, + &mut peripherals.osc32kctrl, + &mut peripherals.oscctrl, + &mut peripherals.nvmctrl, + ); + let gclk0 = clocks.gclk0(); + let pins = Pins::new(peripherals.port); + // let mut red_led: bsp::RedLed = pins.d13.into(); + let mut user_led: UserLed = pins.pa15.into(); + let mut delay = Delay::new(core.SYST, &mut clocks); + + let eic_clock = clocks.eic(&gclk0).unwrap(); + let eic_channels = Eic::new(&mut peripherals.mclk, eic_clock, peripherals.eic).split(); + + let button: Pin<_, PullUpInterrupt> = pins.pd10.into(); + let mut extint = eic_channels.5.with_pin(button); + extint.sense(Sense::Fall); + extint.enable_interrupt(); + + // Enable EIC interrupt in the NVIC + unsafe { + core.NVIC.set_priority(interrupt::EIC_EXTINT_5, 1); + NVIC::unmask(interrupt::EIC_EXTINT_5); + } + + // Blink the LED once to show that we have started up. + user_led.set_high().unwrap(); + delay.delay_ms(200u8); + user_led.set_low().unwrap(); + delay.delay_ms(200u8); + + let mut last_counter_value = COUNTER.load(Ordering::SeqCst); + loop { + let new_counter_value = COUNTER.load(Ordering::SeqCst); + if last_counter_value != new_counter_value { + last_counter_value = new_counter_value; + for _ in 0..new_counter_value { + user_led.set_high().unwrap(); + delay.delay_ms(200u8); + user_led.set_low().unwrap(); + delay.delay_ms(200u8); + } + } + } +} + +#[interrupt] +fn EIC_EXTINT_5() { + // Increase the counter and clear the interrupt. + unsafe { + // Accessing registers from interrupts context is safe + let eic = &*pac::Eic::ptr(); + eic.intflag().modify(|_, w| w.extint().bits(0x01u16 << 5)); + } + COUNTER.store(COUNTER.load(Ordering::SeqCst) + 1, Ordering::SeqCst); +} From 61b6fdb83c44ad6cd3c51c77258ba84b75e5886a Mon Sep 17 00:00:00 2001 From: Pawel Pyszko Date: Sun, 11 May 2025 12:05:39 +0200 Subject: [PATCH 6/8] async_sdcard examples: wip --- boards/wio_terminal/examples/async_sdcard.rs | 160 +++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 boards/wio_terminal/examples/async_sdcard.rs diff --git a/boards/wio_terminal/examples/async_sdcard.rs b/boards/wio_terminal/examples/async_sdcard.rs new file mode 100644 index 000000000000..ab122d588f3f --- /dev/null +++ b/boards/wio_terminal/examples/async_sdcard.rs @@ -0,0 +1,160 @@ +#![no_std] +#![no_main] + +/// Makes the wio_terminal read the SD card and print the filenames +/// of the first few entries. +use embedded_graphics as eg; +use panic_halt as _; +use wio_terminal as wio; + +use eg::mono_font::{ascii::FONT_9X15, MonoTextStyle}; +use eg::pixelcolor::Rgb565; +use eg::prelude::*; +use eg::primitives::{PrimitiveStyleBuilder, Rectangle}; +use eg::text::{Baseline, Text}; + +use wio::entry; +use wio::hal::clock::GenericClockController; +use wio::hal::delay::Delay; +use wio::pac::{CorePeripherals, Peripherals}; +use wio::prelude::*; + +use core::fmt::Write; +use heapless::String; + +use embedded_sdmmc::{TimeSource, Timestamp, VolumeIdx}; +use wio::SDCardController; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.gclk, + &mut peripherals.mclk, + &mut peripherals.osc32kctrl, + &mut peripherals.oscctrl, + &mut peripherals.nvmctrl, + ); + + let mut delay = Delay::new(core.SYST, &mut clocks); + let sets = wio::Pins::new(peripherals.port).split(); + + let (mut cont, _sd_present) = sets + .sd_card + .init( + &mut clocks, + peripherals.sercom6, + &mut peripherals.mclk, + Clock, + ) + .unwrap(); + + // Initialize the ILI9341-based LCD display. Create a black backdrop the size of + // the screen. + let (mut display, _backlight) = sets + .display + .init( + &mut clocks, + peripherals.sercom7, + &mut peripherals.mclk, + 58.MHz(), + &mut delay, + ) + .unwrap(); + + let style = MonoTextStyle::new(&FONT_9X15, Rgb565::WHITE); + + loop { + match cont.device().init() { + Ok(_) => { + // Now that we have initialized, we can run the SPI bus at + // a reasonable speed. + cont.set_baud(20.MHz()); + + let mut data = String::<128>::new(); + write!(data, "OK! ").unwrap(); + match cont.device().card_size_bytes() { + Ok(size) => writeln!(data, "{}Mb", size / 1024 / 1024).unwrap(), + Err(e) => writeln!(data, "Err: {:?}", e).unwrap(), + } + Text::with_baseline(data.as_str(), Point::new(4, 2), style, Baseline::Top) + .draw(&mut display) + .ok() + .unwrap(); + + if let Err(e) = print_contents(&mut cont, &mut display) { + let mut data = String::<128>::new(); + writeln!(data, "Err: {:?}", e).unwrap(); + Text::with_baseline(data.as_str(), Point::new(4, 20), style, Baseline::Top) + .draw(&mut display) + .ok() + .unwrap(); + } + } + Err(e) => { + let mut data = String::<128>::new(); + writeln!(data, "Error!: {:?}", e).unwrap(); + Text::with_baseline(data.as_str(), Point::new(4, 2), style, Baseline::Top) + .draw(&mut display) + .ok() + .unwrap(); + } + } + + delay.delay_ms(2500_u16); + Rectangle::with_corners(Point::new(0, 0), Point::new(320, 240)) + .into_styled( + PrimitiveStyleBuilder::new() + .fill_color(Rgb565::BLACK) + .build(), + ) + .draw(&mut display) + .ok() + .unwrap(); + } +} + +fn print_contents( + cont: &mut SDCardController, + lcd: &mut wio::LCD, +) -> Result<(), embedded_sdmmc::Error> { + let style = MonoTextStyle::new(&FONT_9X15, Rgb565::WHITE); + + let volume = cont.get_volume(VolumeIdx(0))?; + let dir = cont.open_root_dir(&volume)?; + + let mut count = 0; + let out = cont.iterate_dir(&volume, &dir, |ent| { + let mut data = String::<128>::new(); + writeln!(data, "{} - {:?}", ent.name, ent.attributes).unwrap(); + Text::with_baseline( + data.as_str(), + Point::new(4, 20 + count * 16), + style, + Baseline::Top, + ) + .draw(lcd) + .ok() + .unwrap(); + count += 1; + }); + cont.close_dir(&volume, dir); + out +} + +struct Clock; + +impl TimeSource for Clock { + fn get_timestamp(&self) -> Timestamp { + Timestamp { + year_since_1970: 0, + zero_indexed_month: 0, + zero_indexed_day: 0, + hours: 0, + minutes: 0, + seconds: 0, + } + } +} From 474ed2d89f05dc5b5c60f5355b06634af5f98f30 Mon Sep 17 00:00:00 2001 From: Pawel Pyszko Date: Sun, 11 May 2025 19:14:24 +0200 Subject: [PATCH 7/8] Bump Wio Terminal version to 7.4 --- boards/wio_terminal/src/sensors.rs | 43 +++++++++++++++++++----------- boards/wio_terminal/src/sound.rs | 30 ++++++++++----------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/boards/wio_terminal/src/sensors.rs b/boards/wio_terminal/src/sensors.rs index 96a51281a037..66c920869680 100644 --- a/boards/wio_terminal/src/sensors.rs +++ b/boards/wio_terminal/src/sensors.rs @@ -1,4 +1,5 @@ -use atsamd_hal::adc::Adc; +use atsamd_hal::adc::AdcBuilder; +use atsamd_hal::adc::{Accumulation, Adc, Prescaler, Resolution}; use atsamd_hal::clock::GenericClockController; use atsamd_hal::pac::gclk::pchctrl::Genselect::Gclk11; use atsamd_hal::pac::{Adc1, Mclk}; @@ -56,18 +57,28 @@ pub struct LightSensor { pub pd1: LightSensorAdcReset, } -// impl LightSensor { -// /// Initialize Pd1 as an ADC input, and return a Tuple containing the ADC -// /// peripheral and the configured pin. -// pub fn init( -// self, -// adc: Adc1, -// clocks: &mut GenericClockController, -// mclk: &mut Mclk, -// ) -> (Adc, LightSensorAdc) { -// todo!() -// // let adc1 = Adc::adc1(adc, mclk, clocks, Gclk11); -// -// // (adc1, self.pd1.into()) -// } -// } +impl LightSensor { + /// Initialize Pd1 as an ADC input, and return a Tuple containing the ADC + /// peripheral and the configured pin. + pub fn init( + self, + adc: Adc1, + clocks: &mut GenericClockController, + mclk: &mut Mclk, + ) -> (Adc, LightSensorAdc) { + + todo!() + // let adc1 = Adc::adc1(adc, mclk, clocks, Gclk11); + + // let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) + // .with_clock_cycles_per_sample(5) + // // Overruns if clock divider < 32 in debug mode + // .with_clock_divider(Prescaler::Div32) + // .with_vref(atsamd_hal::adc::Reference::Arefa) + // .enable(adc, apb_adc0, &pclk_adc0) + // .unwrap(); + // let mut adc_pin = pins.a0.into_alternate(); + + // (adc1, self.pd1.into()) + } +} diff --git a/boards/wio_terminal/src/sound.rs b/boards/wio_terminal/src/sound.rs index b83f218939f7..13b522c60ae2 100644 --- a/boards/wio_terminal/src/sound.rs +++ b/boards/wio_terminal/src/sound.rs @@ -43,18 +43,18 @@ pub struct Microphone { pub mic: MicOutputReset, } -// impl Microphone { -// /// Initialize Pd1 as an ADC input, and return a Tuple containing the ADC -// /// peripheral and the configured pin. -// pub fn init( -// self, -// adc: Adc1, -// clocks: &mut GenericClockController, -// mclk: &mut Mclk, -// ) -> (Adc, MicOutput) { -// todo!() -// // let adc1 = Adc::adc1(adc, mclk, clocks, Gclk11); -// -// // (adc1, self.mic.into()) -// } -// } +impl Microphone { + /// Initialize Pd1 as an ADC input, and return a Tuple containing the ADC + /// peripheral and the configured pin. + pub fn init( + self, + adc: Adc1, + clocks: &mut GenericClockController, + mclk: &mut Mclk, + ) -> (Adc, MicOutput) { + todo!() + + // let adc1 = Adc::adc1(adc, mclk, clocks, Gclk11); + // (adc1, self.mic.into()) + } +} From 4e5f98f7e80f9d5d856c1968f23617893021c9ca Mon Sep 17 00:00:00 2001 From: Pawel Pyszko Date: Sun, 10 Aug 2025 22:48:07 +0200 Subject: [PATCH 8/8] Fix or move to broken examples --- boards/wio_terminal/Cargo.toml | 6 +++--- boards/wio_terminal/examples/{ => broken}/async_sdcard.rs | 0 boards/wio_terminal/examples/{ => broken}/microphone.rs | 0 boards/wio_terminal/examples/eic.rs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) rename boards/wio_terminal/examples/{ => broken}/async_sdcard.rs (100%) rename boards/wio_terminal/examples/{ => broken}/microphone.rs (100%) diff --git a/boards/wio_terminal/Cargo.toml b/boards/wio_terminal/Cargo.toml index dbe3d16788c3..8a1a7b05951a 100644 --- a/boards/wio_terminal/Cargo.toml +++ b/boards/wio_terminal/Cargo.toml @@ -102,9 +102,9 @@ name = "buttons" name = "clock" required-features = ["usb"] -[[example]] -name = "microphone" - +# [[example]] +# name = "microphone" + [[example]] name = "orientation" diff --git a/boards/wio_terminal/examples/async_sdcard.rs b/boards/wio_terminal/examples/broken/async_sdcard.rs similarity index 100% rename from boards/wio_terminal/examples/async_sdcard.rs rename to boards/wio_terminal/examples/broken/async_sdcard.rs diff --git a/boards/wio_terminal/examples/microphone.rs b/boards/wio_terminal/examples/broken/microphone.rs similarity index 100% rename from boards/wio_terminal/examples/microphone.rs rename to boards/wio_terminal/examples/broken/microphone.rs diff --git a/boards/wio_terminal/examples/eic.rs b/boards/wio_terminal/examples/eic.rs index 1f0fb1c6e14d..43153eccf154 100644 --- a/boards/wio_terminal/examples/eic.rs +++ b/boards/wio_terminal/examples/eic.rs @@ -18,7 +18,7 @@ use bsp::entry; use hal::clock::GenericClockController; use hal::delay::Delay; use hal::eic::{Eic, Sense}; -use hal::gpio::{Pins, Pin, PullUpInterrupt}; +use hal::gpio::{Pin, Pins, PullUpInterrupt}; use hal::prelude::*; use pac::{interrupt, CorePeripherals, Peripherals}; use wio_terminal::aliases::UserLed; @@ -47,7 +47,7 @@ fn main() -> ! { let mut delay = Delay::new(core.SYST, &mut clocks); let eic_clock = clocks.eic(&gclk0).unwrap(); - let eic_channels = Eic::new(&mut peripherals.mclk, eic_clock, peripherals.eic).split(); + let eic_channels = Eic::new(&mut peripherals.mclk, &eic_clock, peripherals.eic).split(); let button: Pin<_, PullUpInterrupt> = pins.pd10.into(); let mut extint = eic_channels.5.with_pin(button);