From 2655426cd80b828593c8cd60930da3ebbd60e85c Mon Sep 17 00:00:00 2001 From: Eekle <96976531+Eekle@users.noreply.github.com> Date: Sun, 23 Jun 2024 16:43:12 +0200 Subject: Add async wait to TSC --- examples/stm32u5/src/bin/tsc.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/stm32u5/src/bin/tsc.rs b/examples/stm32u5/src/bin/tsc.rs index f5593d1c4..642bbeaca 100644 --- a/examples/stm32u5/src/bin/tsc.rs +++ b/examples/stm32u5/src/bin/tsc.rs @@ -2,10 +2,17 @@ #![no_main] use defmt::*; -use embassy_stm32::tsc::{self, *}; +use embassy_stm32::{ + bind_interrupts, + tsc::{self, *}, +}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + TSC => InterruptHandler; +}); + #[cortex_m_rt::exception] unsafe fn HardFault(_: &cortex_m_rt::ExceptionFrame) -> ! { cortex_m::peripheral::SCB::sys_reset(); @@ -47,6 +54,7 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut touch_controller = tsc::Tsc::new( context.TSC, + Irqs, Some(g1), Some(g2), None, @@ -67,7 +75,7 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut group_seven_val = 0; info!("Starting touch_controller interface"); loop { - touch_controller.poll_for_acquisition(); + touch_controller.pend_for_acquisition().await; touch_controller.discharge_io(true); Timer::after_millis(1).await; -- cgit From 7eb605d1165eaf4cdf90453d1ed2d6976dd514af Mon Sep 17 00:00:00 2001 From: Eekle <96976531+Eekle@users.noreply.github.com> Date: Sun, 23 Jun 2024 16:54:10 +0200 Subject: fmt --- examples/stm32u5/src/bin/tsc.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/stm32u5/src/bin/tsc.rs b/examples/stm32u5/src/bin/tsc.rs index 642bbeaca..db85fb158 100644 --- a/examples/stm32u5/src/bin/tsc.rs +++ b/examples/stm32u5/src/bin/tsc.rs @@ -2,10 +2,8 @@ #![no_main] use defmt::*; -use embassy_stm32::{ - bind_interrupts, - tsc::{self, *}, -}; +use embassy_stm32::bind_interrupts; +use embassy_stm32::tsc::{self, *}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -- cgit From f64dd8228b7b8a570546ffa9b522ae85145cfdef Mon Sep 17 00:00:00 2001 From: seth Date: Mon, 24 Jun 2024 17:09:43 -0700 Subject: new PR, taking Dirbao's advice to make the DMA impl in a separate struct that consumes Adc to make RingBufferedAdc. Handling overrun similar to RingBufferedUart --- examples/stm32f4/src/bin/adc_dma.rs | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 examples/stm32f4/src/bin/adc_dma.rs (limited to 'examples') diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs new file mode 100644 index 000000000..a2611bb6f --- /dev/null +++ b/examples/stm32f4/src/bin/adc_dma.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] +use cortex_m::singleton; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::RingBufferedAdc; +use embassy_stm32::adc::{Adc, SampleTime, Sequence}; +use embassy_time::{Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + const ADC_BUF_SIZE: usize = 1024; + let mut p = embassy_stm32::init(Default::default()); + + let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); + + let adc = Adc::new(p.ADC1); + + let mut adc: RingBufferedAdc = adc.into_ring_buffered(p.DMA2_CH0, adc_data); + + adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112); + adc.set_sample_sequence(Sequence::Two, &mut p.PA2, SampleTime::CYCLES112); + adc.set_sample_sequence(Sequence::Three, &mut p.PA1, SampleTime::CYCLES112); + adc.set_sample_sequence(Sequence::Four, &mut p.PA3, SampleTime::CYCLES112); + + // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around + // to the adc.read() call before the DMA buffer is wrapped around > 1 time. At this point, the overrun is so significant that the context of + // what channel is at what index is lost. The buffer must be cleared and reset. This *is* handled here, but allowing this to happen will cause + // a reduction of performance as each time the buffer is reset, the adc & dma buffer must be restarted. + + // An interrupt executor with a higher priority than other tasks may be a good approach here, allowing this task to wake and read the buffer most + // frequently. + let mut tic = Instant::now(); + let mut buffer1: [u16; 256] = [0u16; 256]; + let _ = adc.start(); + loop { + match adc.read(&mut buffer1).await { + Ok(_data) => { + let toc = Instant::now(); + info!( + "\n adc1: {} dt = {}, n = {}", + buffer1[0..16], + (toc - tic).as_micros(), + _data + ); + tic = toc; + } + Err(e) => { + warn!("Error: {:?}", e); + buffer1 = [0u16; 256]; + let _ = adc.start(); + continue; + } + } + + Timer::after_micros(300).await; + } +} -- cgit From 27b83fdbcfe7e9e64a8b65fdcb8dc82d6dc3e58c Mon Sep 17 00:00:00 2001 From: seth Date: Mon, 24 Jun 2024 17:15:16 -0700 Subject: fmt --- examples/stm32f4/src/bin/adc_dma.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs index a2611bb6f..88822a507 100644 --- a/examples/stm32f4/src/bin/adc_dma.rs +++ b/examples/stm32f4/src/bin/adc_dma.rs @@ -3,8 +3,7 @@ use cortex_m::singleton; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::RingBufferedAdc; -use embassy_stm32::adc::{Adc, SampleTime, Sequence}; +use embassy_stm32::adc::{Adc, RingBufferedAdc, SampleTime, Sequence}; use embassy_time::{Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -14,7 +13,7 @@ async fn main(_spawner: Spawner) { let mut p = embassy_stm32::init(Default::default()); let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); - + let adc = Adc::new(p.ADC1); let mut adc: RingBufferedAdc = adc.into_ring_buffered(p.DMA2_CH0, adc_data); -- cgit From 7056783fa23eb25629e1e57da0021916a073a432 Mon Sep 17 00:00:00 2001 From: seth Date: Mon, 24 Jun 2024 17:53:59 -0700 Subject: second adc added to example + API todos completed --- examples/stm32f4/src/bin/adc_dma.rs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'examples') diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs index 88822a507..dd19caf1d 100644 --- a/examples/stm32f4/src/bin/adc_dma.rs +++ b/examples/stm32f4/src/bin/adc_dma.rs @@ -13,15 +13,18 @@ async fn main(_spawner: Spawner) { let mut p = embassy_stm32::init(Default::default()); let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); + let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); let adc = Adc::new(p.ADC1); + let adc2 = Adc::new(p.ADC2); let mut adc: RingBufferedAdc = adc.into_ring_buffered(p.DMA2_CH0, adc_data); + let mut adc2: RingBufferedAdc = adc2.into_ring_buffered(p.DMA2_CH2, adc_data2); adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112); adc.set_sample_sequence(Sequence::Two, &mut p.PA2, SampleTime::CYCLES112); - adc.set_sample_sequence(Sequence::Three, &mut p.PA1, SampleTime::CYCLES112); - adc.set_sample_sequence(Sequence::Four, &mut p.PA3, SampleTime::CYCLES112); + adc2.set_sample_sequence(Sequence::One, &mut p.PA1, SampleTime::CYCLES112); + adc2.set_sample_sequence(Sequence::Two, &mut p.PA3, SampleTime::CYCLES112); // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around // to the adc.read() call before the DMA buffer is wrapped around > 1 time. At this point, the overrun is so significant that the context of @@ -31,10 +34,12 @@ async fn main(_spawner: Spawner) { // An interrupt executor with a higher priority than other tasks may be a good approach here, allowing this task to wake and read the buffer most // frequently. let mut tic = Instant::now(); - let mut buffer1: [u16; 256] = [0u16; 256]; + let mut buffer1 = [0u16; 256]; + let mut buffer2 = [0u16; 256]; let _ = adc.start(); + let _ = adc2.start(); loop { - match adc.read(&mut buffer1).await { + match adc.read_exact(&mut buffer1).await { Ok(_data) => { let toc = Instant::now(); info!( @@ -49,10 +54,25 @@ async fn main(_spawner: Spawner) { warn!("Error: {:?}", e); buffer1 = [0u16; 256]; let _ = adc.start(); - continue; } } - Timer::after_micros(300).await; + match adc2.read_exact(&mut buffer2).await { + Ok(_data) => { + let toc = Instant::now(); + info!( + "\n adc2: {} dt = {}, n = {}", + buffer2[0..16], + (toc - tic).as_micros(), + _data + ); + tic = toc; + } + Err(e) => { + warn!("Error: {:?}", e); + buffer2 = [0u16; 256]; + let _ = adc2.start(); + } + } } } -- cgit From 6926e9e07154c72d68ef099d5f28178274f86032 Mon Sep 17 00:00:00 2001 From: seth Date: Mon, 24 Jun 2024 23:15:00 -0700 Subject: CI --- examples/stm32f4/src/bin/adc_dma.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs index dd19caf1d..992bed573 100644 --- a/examples/stm32f4/src/bin/adc_dma.rs +++ b/examples/stm32f4/src/bin/adc_dma.rs @@ -4,14 +4,19 @@ use cortex_m::singleton; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, RingBufferedAdc, SampleTime, Sequence}; -use embassy_time::{Instant, Timer}; +use embassy_stm32::Peripherals; +use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] -async fn main(_spawner: Spawner) { - const ADC_BUF_SIZE: usize = 1024; - let mut p = embassy_stm32::init(Default::default()); +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + spawner.must_spawn(adc_task(p)); +} +#[embassy_executor::task] +async fn adc_task(mut p: Peripherals) { + const ADC_BUF_SIZE: usize = 1024; let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); @@ -34,8 +39,8 @@ async fn main(_spawner: Spawner) { // An interrupt executor with a higher priority than other tasks may be a good approach here, allowing this task to wake and read the buffer most // frequently. let mut tic = Instant::now(); - let mut buffer1 = [0u16; 256]; - let mut buffer2 = [0u16; 256]; + let mut buffer1 = [0u16; 512]; + let mut buffer2 = [0u16; 512]; let _ = adc.start(); let _ = adc2.start(); loop { @@ -52,7 +57,7 @@ async fn main(_spawner: Spawner) { } Err(e) => { warn!("Error: {:?}", e); - buffer1 = [0u16; 256]; + buffer1 = [0u16; 512]; let _ = adc.start(); } } @@ -70,7 +75,7 @@ async fn main(_spawner: Spawner) { } Err(e) => { warn!("Error: {:?}", e); - buffer2 = [0u16; 256]; + buffer2 = [0u16; 512]; let _ = adc2.start(); } } -- cgit From a0799bf270010f4e91b0c3eebe487d8bc6fb54fc Mon Sep 17 00:00:00 2001 From: Chen Yuheng <1016867898@qq.com> Date: Thu, 27 Jun 2024 17:04:26 +0800 Subject: Add adc oversampling support --- examples/stm32g0/src/bin/adc_oversampling.rs | 43 ++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 examples/stm32g0/src/bin/adc_oversampling.rs (limited to 'examples') diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs new file mode 100644 index 000000000..3c31eb206 --- /dev/null +++ b/examples/stm32g0/src/bin/adc_oversampling.rs @@ -0,0 +1,43 @@ +//! adc oversampling example +//! +//! This example uses adc oversampling to achieve 16bit data + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Adc oversample test"); + + let mut adc = Adc::new(p.ADC1); + adc.set_sample_time(SampleTime::CYCLES1_5); + let mut pin = p.PA1; + + // From https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf + // page373 15.8 Oversampler + // Table 76. Maximum output results vs N and M. Grayed values indicates truncation + // 0x00 oversampling ratio X2 + // 0x01 oversampling ratio X4 + // 0x02 oversampling ratio X8 + // 0x03 oversampling ratio X16 + // 0x04 oversampling ratio X32 + // 0x05 oversampling ratio X64 + // 0x06 oversampling ratio X128 + // 0x07 oversampling ratio X256 + adc.set_oversampling_ratio(0x03); + adc.set_oversampling_shift(0b0000); + adc.oversampling_enable(true); + + loop { + let v = adc.read(&mut pin); + info!("--> {} ", v); //max 65520 = 0xFFF0 + Timer::after_millis(100).await; + } +} -- cgit From 0e84bd8a91095c035b1b2fdf0c0d8d95b736cc9f Mon Sep 17 00:00:00 2001 From: David Haig Date: Thu, 27 Jun 2024 20:13:20 +0100 Subject: Add support for the stm32 ltdc display peripheral --- examples/stm32h7/Cargo.toml | 2 + examples/stm32h7/src/bin/ferris.bmp | Bin 0 -> 6794 bytes examples/stm32h7/src/bin/ltdc.rs | 484 ++++++++++++++++++++++++++++++++++++ 3 files changed, 486 insertions(+) create mode 100644 examples/stm32h7/src/bin/ferris.bmp create mode 100644 examples/stm32h7/src/bin/ltdc.rs (limited to 'examples') diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 0584f3916..6f3007492 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -34,6 +34,8 @@ stm32-fmc = "0.3.0" embedded-storage = "0.3.1" static_cell = "2" chrono = { version = "^0.4", default-features = false } +embedded-graphics = { version = "0.8.1" } +tinybmp = { version = "0.5" } # cargo build/run [profile.dev] diff --git a/examples/stm32h7/src/bin/ferris.bmp b/examples/stm32h7/src/bin/ferris.bmp new file mode 100644 index 000000000..7a222ab84 Binary files /dev/null and b/examples/stm32h7/src/bin/ferris.bmp differ diff --git a/examples/stm32h7/src/bin/ltdc.rs b/examples/stm32h7/src/bin/ltdc.rs new file mode 100644 index 000000000..3bd307012 --- /dev/null +++ b/examples/stm32h7/src/bin/ltdc.rs @@ -0,0 +1,484 @@ +#![no_std] +#![no_main] +#![macro_use] +#![allow(static_mut_refs)] + +/// This example demonstrates the LTDC lcd display peripheral and was tested to run on an stm32h735g-dk (embassy-stm32 feature "stm32h735ig" and probe-rs chip "STM32H735IGKx") +/// Even though the dev kit has 16MB of attached PSRAM this example uses the 320KB of internal AXIS RAM found on the mcu itself to make the example more standalone and portable. +/// For this reason a 256 color lookup table had to be used to keep the memory requirement down to an acceptable level. +/// The example bounces a ferris crab bitmap around the screen while blinking an led on another task +/// +use bouncy_box::BouncyBox; +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_stm32::{ + bind_interrupts, + gpio::{Level, Output, Speed}, + ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge}, + peripherals, +}; +use embassy_time::{Duration, Timer}; +use embedded_graphics::{ + draw_target::DrawTarget, + geometry::{OriginDimensions, Point, Size}, + image::Image, + pixelcolor::{raw::RawU24, Rgb888}, + prelude::*, + primitives::Rectangle, + Pixel, +}; +use heapless::{Entry, FnvIndexMap}; +use tinybmp::Bmp; +use {defmt_rtt as _, panic_probe as _}; + +const DISPLAY_WIDTH: usize = 480; +const DISPLAY_HEIGHT: usize = 272; +const MY_TASK_POOL_SIZE: usize = 2; + +// the following two display buffers consume 261120 bytes that just about fits into axis ram found on the mcu +// see memory.x linker script +pub static mut FB1: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT]; +pub static mut FB2: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT]; + +// a basic memory.x linker script for the stm32h735g-dk is as follows: +/* +MEMORY +{ + FLASH : ORIGIN = 0x08000000, LENGTH = 1024K + RAM : ORIGIN = 0x24000000, LENGTH = 320K +} +*/ + +bind_interrupts!(struct Irqs { + LTDC => ltdc::InterruptHandler; +}); + +const NUM_COLORS: usize = 256; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = rcc_setup::stm32h735g_init(); + + // blink the led on another task + let led = Output::new(p.PC3, Level::High, Speed::Low); + unwrap!(spawner.spawn(led_task(led))); + + // numbers from STMicroelectronics/STM32CubeH7 STM32H735G-DK C-based example + const RK043FN48H_HSYNC: u16 = 41; // Horizontal synchronization + const RK043FN48H_HBP: u16 = 13; // Horizontal back porch + const RK043FN48H_HFP: u16 = 32; // Horizontal front porch + const RK043FN48H_VSYNC: u16 = 10; // Vertical synchronization + const RK043FN48H_VBP: u16 = 2; // Vertical back porch + const RK043FN48H_VFP: u16 = 2; // Vertical front porch + + let ltdc_config = LtdcConfiguration { + active_width: DISPLAY_WIDTH as _, + active_height: DISPLAY_HEIGHT as _, + h_back_porch: RK043FN48H_HBP - 11, // -11 from MX_LTDC_Init + h_front_porch: RK043FN48H_HFP, + v_back_porch: RK043FN48H_VBP, + v_front_porch: RK043FN48H_VFP, + h_sync: RK043FN48H_HSYNC, + v_sync: RK043FN48H_VSYNC, + h_sync_polarity: PolarityActive::ActiveLow, + v_sync_polarity: PolarityActive::ActiveLow, + data_enable_polarity: PolarityActive::ActiveHigh, + pixel_clock_polarity: PolarityEdge::FallingEdge, + }; + + info!("init ltdc"); + let mut ltdc = Ltdc::new( + p.LTDC, Irqs, p.PG7, p.PC6, p.PA4, p.PG14, p.PD0, p.PD6, p.PA8, p.PE12, p.PA3, p.PB8, p.PB9, p.PB1, p.PB0, + p.PA6, p.PE11, p.PH15, p.PH4, p.PC7, p.PD3, p.PE0, p.PH3, p.PH8, p.PH9, p.PH10, p.PH11, p.PE1, p.PE15, + ); + ltdc.init(<dc_config); + + // we only need to draw on one layer for this example (not to be confused with the double buffer) + info!("enable bottom layer"); + let layer_config = LtdcLayerConfig { + pixel_format: ltdc::PixelFormat::L8, // 1 byte per pixel + layer: LtdcLayer::Layer1, + window_x0: 0, + window_x1: DISPLAY_WIDTH as _, + window_y0: 0, + window_y1: DISPLAY_HEIGHT as _, + }; + + let ferris_bmp: Bmp = Bmp::from_slice(include_bytes!("./ferris.bmp")).unwrap(); + let color_map = build_color_lookup_map(&ferris_bmp); + let clut = build_clut(&color_map); + + // enable the bottom layer with a 256 color lookup table + ltdc.enable_layer(&layer_config, Some(&clut)); + + // Safety: the DoubleBuffer controls access to the statically allocated frame buffers + // and it is the only thing that mutates their content + let mut double_buffer = DoubleBuffer::new( + unsafe { FB1.as_mut() }, + unsafe { FB2.as_mut() }, + layer_config, + color_map, + ); + + // this allows us to perform some simple animation for every frame + let mut bouncy_box = BouncyBox::new( + ferris_bmp.bounding_box(), + Rectangle::new(Point::zero(), Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)), + 2, + ); + + loop { + // cpu intensive drawing to the buffer that is NOT currently being copied to the LCD screen + double_buffer.clear(); + let position = bouncy_box.next_point(); + let ferris = Image::new(&ferris_bmp, position); + unwrap!(ferris.draw(&mut double_buffer)); + + // perform async dma data transfer to the lcd screen + unwrap!(double_buffer.swap(&mut ltdc).await); + } +} + +/// builds the color look-up table from all unique colors found in the bitmap. This should be a 256 color indexed bitmap to work. +fn build_color_lookup_map(bmp: &Bmp) -> FnvIndexMap { + let mut color_map: FnvIndexMap = heapless::FnvIndexMap::new(); + let mut counter: u8 = 0; + + // add black to position 0 + color_map.insert(Rgb888::new(0, 0, 0).into_storage(), counter).unwrap(); + counter += 1; + + for Pixel(_point, color) in bmp.pixels() { + let raw = color.into_storage(); + if let Entry::Vacant(v) = color_map.entry(raw) { + v.insert(counter).expect("more than 256 colors detected"); + counter += 1; + } + } + color_map +} + +/// builds the color look-up table from the color map provided +fn build_clut(color_map: &FnvIndexMap) -> [ltdc::RgbColor; NUM_COLORS] { + let mut clut = [ltdc::RgbColor::default(); NUM_COLORS]; + for (color, index) in color_map.iter() { + let color = Rgb888::from(RawU24::new(*color)); + clut[*index as usize] = ltdc::RgbColor { + red: color.r(), + green: color.g(), + blue: color.b(), + }; + } + + clut +} + +#[embassy_executor::task(pool_size = MY_TASK_POOL_SIZE)] +async fn led_task(mut led: Output<'static>) { + let mut counter = 0; + loop { + info!("blink: {}", counter); + counter += 1; + + // on + led.set_low(); + Timer::after(Duration::from_millis(50)).await; + + // off + led.set_high(); + Timer::after(Duration::from_millis(450)).await; + } +} + +pub type TargetPixelType = u8; + +// A simple double buffer +pub struct DoubleBuffer { + buf0: &'static mut [TargetPixelType], + buf1: &'static mut [TargetPixelType], + is_buf0: bool, + layer_config: LtdcLayerConfig, + color_map: FnvIndexMap, +} + +impl DoubleBuffer { + pub fn new( + buf0: &'static mut [TargetPixelType], + buf1: &'static mut [TargetPixelType], + layer_config: LtdcLayerConfig, + color_map: FnvIndexMap, + ) -> Self { + Self { + buf0, + buf1, + is_buf0: true, + layer_config, + color_map, + } + } + + pub fn current(&mut self) -> (&FnvIndexMap, &mut [TargetPixelType]) { + if self.is_buf0 { + (&self.color_map, self.buf0) + } else { + (&self.color_map, self.buf1) + } + } + + pub async fn swap(&mut self, ltdc: &mut Ltdc<'_, T>) -> Result<(), ltdc::Error> { + let (_, buf) = self.current(); + let frame_buffer = buf.as_ptr(); + self.is_buf0 = !self.is_buf0; + ltdc.set_buffer(self.layer_config.layer, frame_buffer as *const _).await + } + + /// Clears the buffer + pub fn clear(&mut self) { + let (color_map, buf) = self.current(); + let black = Rgb888::new(0, 0, 0).into_storage(); + let color_index = color_map.get(&black).expect("no black found in the color map"); + + for a in buf.iter_mut() { + *a = *color_index; // solid black + } + } +} + +// Implement DrawTarget for +impl DrawTarget for DoubleBuffer { + type Color = Rgb888; + type Error = (); + + /// Draw a pixel + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + let size = self.size(); + let width = size.width as i32; + let height = size.height as i32; + let (color_map, buf) = self.current(); + + for pixel in pixels { + let Pixel(point, color) = pixel; + + if point.x >= 0 && point.y >= 0 && point.x < width && point.y < height { + let index = point.y * width + point.x; + let raw_color = color.into_storage(); + + match color_map.get(&raw_color) { + Some(x) => { + buf[index as usize] = *x; + } + None => panic!("color not found in color map: {}", raw_color), + }; + } else { + // Ignore invalid points + } + } + + Ok(()) + } +} + +impl OriginDimensions for DoubleBuffer { + /// Return the size of the display + fn size(&self) -> Size { + Size::new( + (self.layer_config.window_x1 - self.layer_config.window_x0) as _, + (self.layer_config.window_y1 - self.layer_config.window_y0) as _, + ) + } +} + +mod rcc_setup { + + use embassy_stm32::{rcc::*, Peripherals}; + use embassy_stm32::{ + rcc::{Hse, HseMode}, + time::Hertz, + Config, + }; + + /// Sets up clocks for the stm32h735g mcu + /// change this if you plan to use a different microcontroller + pub fn stm32h735g_init() -> Peripherals { + /* + https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Projects/STM32H735G-DK/Examples/GPIO/GPIO_EXTI/Src/main.c + @brief System Clock Configuration + The system Clock is configured as follow : + System Clock source = PLL (HSE) + SYSCLK(Hz) = 520000000 (CPU Clock) + HCLK(Hz) = 260000000 (AXI and AHBs Clock) + AHB Prescaler = 2 + D1 APB3 Prescaler = 2 (APB3 Clock 130MHz) + D2 APB1 Prescaler = 2 (APB1 Clock 130MHz) + D2 APB2 Prescaler = 2 (APB2 Clock 130MHz) + D3 APB4 Prescaler = 2 (APB4 Clock 130MHz) + HSE Frequency(Hz) = 25000000 + PLL_M = 5 + PLL_N = 104 + PLL_P = 1 + PLL_Q = 4 + PLL_R = 2 + VDD(V) = 3.3 + Flash Latency(WS) = 3 + */ + + // setup power and clocks for an stm32h735g-dk run from an external 25 Mhz external oscillator + let mut config = Config::default(); + config.rcc.hse = Some(Hse { + freq: Hertz::mhz(25), + mode: HseMode::Oscillator, + }); + config.rcc.hsi = None; + config.rcc.csi = false; + config.rcc.pll1 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV5, // PLL_M + mul: PllMul::MUL104, // PLL_N + divp: Some(PllDiv::DIV1), + divq: Some(PllDiv::DIV4), + divr: Some(PllDiv::DIV2), + }); + // numbers adapted from Drivers/BSP/STM32H735G-DK/stm32h735g_discovery_ospi.c + // MX_OSPI_ClockConfig + config.rcc.pll2 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV5, // PLL_M + mul: PllMul::MUL80, // PLL_N + divp: Some(PllDiv::DIV5), + divq: Some(PllDiv::DIV2), + divr: Some(PllDiv::DIV2), + }); + // numbers adapted from Drivers/BSP/STM32H735G-DK/stm32h735g_discovery_lcd.c + // MX_LTDC_ClockConfig + config.rcc.pll3 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV5, // PLL_M + mul: PllMul::MUL160, // PLL_N + divp: Some(PllDiv::DIV2), + divq: Some(PllDiv::DIV2), + divr: Some(PllDiv::DIV83), + }); + config.rcc.voltage_scale = VoltageScale::Scale0; + config.rcc.supply_config = SupplyConfig::DirectSMPS; + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV2; + config.rcc.apb1_pre = APBPrescaler::DIV2; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.apb3_pre = APBPrescaler::DIV2; + config.rcc.apb4_pre = APBPrescaler::DIV2; + let p = embassy_stm32::init(config); + p + } +} + +mod bouncy_box { + use embedded_graphics::{geometry::Point, primitives::Rectangle}; + + enum Direction { + DownLeft, + DownRight, + UpLeft, + UpRight, + } + + pub struct BouncyBox { + direction: Direction, + child_rect: Rectangle, + parent_rect: Rectangle, + current_point: Point, + move_by: usize, + } + + // This calculates the coordinates of a chile rectangle bounced around inside a parent bounded box + impl BouncyBox { + pub fn new(child_rect: Rectangle, parent_rect: Rectangle, move_by: usize) -> Self { + let center_box = parent_rect.center(); + let center_img = child_rect.center(); + let current_point = Point::new(center_box.x - center_img.x / 2, center_box.y - center_img.y / 2); + Self { + direction: Direction::DownRight, + child_rect, + parent_rect, + current_point, + move_by, + } + } + + pub fn next_point(&mut self) -> Point { + let direction = &self.direction; + let img_height = self.child_rect.size.height as i32; + let box_height = self.parent_rect.size.height as i32; + let img_width = self.child_rect.size.width as i32; + let box_width = self.parent_rect.size.width as i32; + let move_by = self.move_by as i32; + + match direction { + Direction::DownLeft => { + self.current_point.x -= move_by; + self.current_point.y += move_by; + + let x_out_of_bounds = self.current_point.x < 0; + let y_out_of_bounds = (self.current_point.y + img_height) > box_height; + + if x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::UpRight + } else if x_out_of_bounds && !y_out_of_bounds { + self.direction = Direction::DownRight + } else if !x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::UpLeft + } + } + Direction::DownRight => { + self.current_point.x += move_by; + self.current_point.y += move_by; + + let x_out_of_bounds = (self.current_point.x + img_width) > box_width; + let y_out_of_bounds = (self.current_point.y + img_height) > box_height; + + if x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::UpLeft + } else if x_out_of_bounds && !y_out_of_bounds { + self.direction = Direction::DownLeft + } else if !x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::UpRight + } + } + Direction::UpLeft => { + self.current_point.x -= move_by; + self.current_point.y -= move_by; + + let x_out_of_bounds = self.current_point.x < 0; + let y_out_of_bounds = self.current_point.y < 0; + + if x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::DownRight + } else if x_out_of_bounds && !y_out_of_bounds { + self.direction = Direction::UpRight + } else if !x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::DownLeft + } + } + Direction::UpRight => { + self.current_point.x += move_by; + self.current_point.y -= move_by; + + let x_out_of_bounds = (self.current_point.x + img_width) > box_width; + let y_out_of_bounds = self.current_point.y < 0; + + if x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::DownLeft + } else if x_out_of_bounds && !y_out_of_bounds { + self.direction = Direction::UpLeft + } else if !x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::DownRight + } + } + } + + self.current_point + } + } +} -- cgit From ece6203a996207f25db737032d3e0dae3df80b64 Mon Sep 17 00:00:00 2001 From: shufps Date: Fri, 28 Jun 2024 14:42:19 +0200 Subject: added DDS example --- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l0/src/bin/dds.rs | 117 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 examples/stm32l0/src/bin/dds.rs (limited to 'examples') diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 2c599e7a3..5b0519ac4 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32l072cz to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32l0/src/bin/dds.rs b/examples/stm32l0/src/bin/dds.rs new file mode 100644 index 000000000..9759fc8f3 --- /dev/null +++ b/examples/stm32l0/src/bin/dds.rs @@ -0,0 +1,117 @@ +#![no_std] +#![no_main] + +use core::option::Option::Some; +use defmt::info; +use defmt_rtt as _; // global logger +use embassy_executor::Spawner; +use embassy_stm32::gpio::OutputType; +use embassy_stm32::interrupt; +use embassy_stm32::pac; +use embassy_stm32::rcc::*; +use embassy_stm32::time::hz; +use embassy_stm32::timer::low_level::Timer as LLTimer; +use embassy_stm32::timer::low_level::*; +use embassy_stm32::timer::simple_pwm::PwmPin; +use embassy_stm32::timer::Channel; +use embassy_stm32::Config; +use panic_probe as _; + +const DDS_SINE_DATA: [u8; 256] = [ + 0x80, 0x83, 0x86, 0x89, 0x8c, 0x8f, 0x92, 0x95, 0x98, 0x9c, 0x9f, 0xa2, 0xa5, 0xa8, 0xab, 0xae, 0xb0, 0xb3, 0xb6, + 0xb9, 0xbc, 0xbf, 0xc1, 0xc4, 0xc7, 0xc9, 0xcc, 0xce, 0xd1, 0xd3, 0xd5, 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, + 0xe6, 0xe8, 0xea, 0xec, 0xed, 0xef, 0xf0, 0xf2, 0xf3, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, 0xfb, + 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf3, 0xf2, 0xf0, 0xef, 0xed, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe2, 0xe0, 0xde, + 0xdc, 0xda, 0xd8, 0xd5, 0xd3, 0xd1, 0xce, 0xcc, 0xc9, 0xc7, 0xc4, 0xc1, 0xbf, 0xbc, 0xb9, 0xb6, 0xb3, 0xb0, 0xae, + 0xab, 0xa8, 0xa5, 0xa2, 0x9f, 0x9c, 0x98, 0x95, 0x92, 0x8f, 0x8c, 0x89, 0x86, 0x83, 0x80, 0x7c, 0x79, 0x76, 0x73, + 0x70, 0x6d, 0x6a, 0x67, 0x63, 0x60, 0x5d, 0x5a, 0x57, 0x54, 0x51, 0x4f, 0x4c, 0x49, 0x46, 0x43, 0x40, 0x3e, 0x3b, + 0x38, 0x36, 0x33, 0x31, 0x2e, 0x2c, 0x2a, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x12, + 0x10, 0x0f, 0x0d, 0x0c, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0c, 0x0d, 0x0f, 0x10, 0x12, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x21, 0x23, 0x25, 0x27, 0x2a, 0x2c, + 0x2e, 0x31, 0x33, 0x36, 0x38, 0x3b, 0x3e, 0x40, 0x43, 0x46, 0x49, 0x4c, 0x4f, 0x51, 0x54, 0x57, 0x5a, 0x5d, 0x60, + 0x63, 0x67, 0x6a, 0x6d, 0x70, 0x73, 0x76, 0x79, 0x7c, +]; + +// frequency: 15625/(256/(DDS_INCR/2**24)) = 999,99999Hz +static mut DDS_INCR: u32 = 0x10624DD2; + +// fractional phase accumulator +static mut DDS_AKKU: u32 = 0x00000000; + +#[interrupt] +fn TIM2() { + unsafe { + // get next value of DDS + DDS_AKKU = DDS_AKKU.wrapping_add(DDS_INCR); + let value = (DDS_SINE_DATA[(DDS_AKKU >> 24) as usize] as u16) << 3; + + // set new output compare value + pac::TIM2.ccr(2).modify(|w| w.set_ccr(value)); + + // reset interrupt flag + pac::TIM2.sr().modify(|r| r.set_uif(false)); + } +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + // configure for 32MHz (HSI16 * 6 / 3) + let mut config = Config::default(); + config.rcc.sys = Sysclk::PLL1_R; + config.rcc.hsi = true; + config.rcc.pll = Some(Pll { + source: PllSource::HSI, + div: PllDiv::DIV3, + mul: PllMul::MUL6, + }); + + let p = embassy_stm32::init(config); + + // setup PWM pin in AF mode + let _ch3 = PwmPin::new_ch3(p.PA2, OutputType::PushPull); + + // initialize timer + let timer = LLTimer::new(p.TIM2); + + // set counting mode + timer.set_counting_mode(CountingMode::EdgeAlignedUp); + + // set pwm sample frequency + timer.set_frequency(hz(15625)); + + // enable outputs + timer.enable_outputs(); + + // start timer + timer.start(); + + // set output compare mode + timer.set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); + + // set output compare preload + timer.set_output_compare_preload(Channel::Ch3, true); + + // set output polarity + timer.set_output_polarity(Channel::Ch3, OutputPolarity::ActiveHigh); + + // set compare value + timer.set_compare_value(Channel::Ch3, timer.get_max_compare_value() / 2); + + // enable pwm channel + timer.enable_channel(Channel::Ch3, true); + + // enable timer interrupts + timer.enable_update_interrupt(true); + unsafe { cortex_m::peripheral::NVIC::unmask(interrupt::TIM2) }; + + async { + loop { + embassy_time::Timer::after_millis(5000).await; + } + } + .await; +} -- cgit From b2a0eb3cb4b9d0f9b4ecc12b6e5437e4616b4aa6 Mon Sep 17 00:00:00 2001 From: shufps Date: Fri, 28 Jun 2024 14:43:39 +0200 Subject: added comment --- examples/stm32l0/src/bin/dds.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'examples') diff --git a/examples/stm32l0/src/bin/dds.rs b/examples/stm32l0/src/bin/dds.rs index 9759fc8f3..21647a68d 100644 --- a/examples/stm32l0/src/bin/dds.rs +++ b/examples/stm32l0/src/bin/dds.rs @@ -75,6 +75,7 @@ async fn main(_spawner: Spawner) { let _ch3 = PwmPin::new_ch3(p.PA2, OutputType::PushPull); // initialize timer + // we cannot use SimplePWM here because the Time is privately encapsulated let timer = LLTimer::new(p.TIM2); // set counting mode -- cgit From 2462a22140834e80e63465dabcf8a783e1adb94f Mon Sep 17 00:00:00 2001 From: shufps Date: Fri, 28 Jun 2024 14:52:21 +0200 Subject: format --- examples/stm32l0/src/bin/dds.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/stm32l0/src/bin/dds.rs b/examples/stm32l0/src/bin/dds.rs index 21647a68d..a54b28a93 100644 --- a/examples/stm32l0/src/bin/dds.rs +++ b/examples/stm32l0/src/bin/dds.rs @@ -2,19 +2,17 @@ #![no_main] use core::option::Option::Some; + use defmt::info; use defmt_rtt as _; // global logger use embassy_executor::Spawner; use embassy_stm32::gpio::OutputType; -use embassy_stm32::interrupt; -use embassy_stm32::pac; use embassy_stm32::rcc::*; use embassy_stm32::time::hz; -use embassy_stm32::timer::low_level::Timer as LLTimer; -use embassy_stm32::timer::low_level::*; +use embassy_stm32::timer::low_level::{Timer as LLTimer, *}; use embassy_stm32::timer::simple_pwm::PwmPin; use embassy_stm32::timer::Channel; -use embassy_stm32::Config; +use embassy_stm32::{interrupt, pac, Config}; use panic_probe as _; const DDS_SINE_DATA: [u8; 256] = [ -- cgit From 1123e3fd41e7b23dd0507816f1ff67fc0de6b5d1 Mon Sep 17 00:00:00 2001 From: David Haig Date: Fri, 28 Jun 2024 15:12:17 +0100 Subject: Get dsi_bsp example to compile again --- examples/stm32h7/src/bin/ltdc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/stm32h7/src/bin/ltdc.rs b/examples/stm32h7/src/bin/ltdc.rs index 3bd307012..3b56bbb6c 100644 --- a/examples/stm32h7/src/bin/ltdc.rs +++ b/examples/stm32h7/src/bin/ltdc.rs @@ -87,7 +87,7 @@ async fn main(spawner: Spawner) { }; info!("init ltdc"); - let mut ltdc = Ltdc::new( + let mut ltdc = Ltdc::new_with_pins( p.LTDC, Irqs, p.PG7, p.PC6, p.PA4, p.PG14, p.PD0, p.PD6, p.PA8, p.PE12, p.PA3, p.PB8, p.PB9, p.PB1, p.PB0, p.PA6, p.PE11, p.PH15, p.PH4, p.PC7, p.PD3, p.PE0, p.PH3, p.PH8, p.PH9, p.PH10, p.PH11, p.PE1, p.PE15, ); @@ -109,7 +109,7 @@ async fn main(spawner: Spawner) { let clut = build_clut(&color_map); // enable the bottom layer with a 256 color lookup table - ltdc.enable_layer(&layer_config, Some(&clut)); + ltdc.init_layer(&layer_config, Some(&clut)); // Safety: the DoubleBuffer controls access to the statically allocated frame buffers // and it is the only thing that mutates their content -- cgit From 79f00e54cc4d13a28c7ccc9a13345bf2f3730f42 Mon Sep 17 00:00:00 2001 From: David Haig Date: Fri, 28 Jun 2024 18:11:34 +0100 Subject: Moved ltdc example to its own crate --- examples/stm32h7/Cargo.toml | 2 - examples/stm32h7/src/bin/ferris.bmp | Bin 6794 -> 0 bytes examples/stm32h7/src/bin/ltdc.rs | 484 ---------------------------------- examples/stm32h735/.cargo/config.toml | 8 + examples/stm32h735/Cargo.toml | 61 +++++ examples/stm32h735/build.rs | 35 +++ examples/stm32h735/memory.x | 5 + examples/stm32h735/src/bin/ferris.bmp | Bin 0 -> 6794 bytes examples/stm32h735/src/bin/ltdc.rs | 473 +++++++++++++++++++++++++++++++++ 9 files changed, 582 insertions(+), 486 deletions(-) delete mode 100644 examples/stm32h7/src/bin/ferris.bmp delete mode 100644 examples/stm32h7/src/bin/ltdc.rs create mode 100644 examples/stm32h735/.cargo/config.toml create mode 100644 examples/stm32h735/Cargo.toml create mode 100644 examples/stm32h735/build.rs create mode 100644 examples/stm32h735/memory.x create mode 100644 examples/stm32h735/src/bin/ferris.bmp create mode 100644 examples/stm32h735/src/bin/ltdc.rs (limited to 'examples') diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 6f3007492..0584f3916 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -34,8 +34,6 @@ stm32-fmc = "0.3.0" embedded-storage = "0.3.1" static_cell = "2" chrono = { version = "^0.4", default-features = false } -embedded-graphics = { version = "0.8.1" } -tinybmp = { version = "0.5" } # cargo build/run [profile.dev] diff --git a/examples/stm32h7/src/bin/ferris.bmp b/examples/stm32h7/src/bin/ferris.bmp deleted file mode 100644 index 7a222ab84..000000000 Binary files a/examples/stm32h7/src/bin/ferris.bmp and /dev/null differ diff --git a/examples/stm32h7/src/bin/ltdc.rs b/examples/stm32h7/src/bin/ltdc.rs deleted file mode 100644 index 3b56bbb6c..000000000 --- a/examples/stm32h7/src/bin/ltdc.rs +++ /dev/null @@ -1,484 +0,0 @@ -#![no_std] -#![no_main] -#![macro_use] -#![allow(static_mut_refs)] - -/// This example demonstrates the LTDC lcd display peripheral and was tested to run on an stm32h735g-dk (embassy-stm32 feature "stm32h735ig" and probe-rs chip "STM32H735IGKx") -/// Even though the dev kit has 16MB of attached PSRAM this example uses the 320KB of internal AXIS RAM found on the mcu itself to make the example more standalone and portable. -/// For this reason a 256 color lookup table had to be used to keep the memory requirement down to an acceptable level. -/// The example bounces a ferris crab bitmap around the screen while blinking an led on another task -/// -use bouncy_box::BouncyBox; -use defmt::{info, unwrap}; -use embassy_executor::Spawner; -use embassy_stm32::{ - bind_interrupts, - gpio::{Level, Output, Speed}, - ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge}, - peripherals, -}; -use embassy_time::{Duration, Timer}; -use embedded_graphics::{ - draw_target::DrawTarget, - geometry::{OriginDimensions, Point, Size}, - image::Image, - pixelcolor::{raw::RawU24, Rgb888}, - prelude::*, - primitives::Rectangle, - Pixel, -}; -use heapless::{Entry, FnvIndexMap}; -use tinybmp::Bmp; -use {defmt_rtt as _, panic_probe as _}; - -const DISPLAY_WIDTH: usize = 480; -const DISPLAY_HEIGHT: usize = 272; -const MY_TASK_POOL_SIZE: usize = 2; - -// the following two display buffers consume 261120 bytes that just about fits into axis ram found on the mcu -// see memory.x linker script -pub static mut FB1: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT]; -pub static mut FB2: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT]; - -// a basic memory.x linker script for the stm32h735g-dk is as follows: -/* -MEMORY -{ - FLASH : ORIGIN = 0x08000000, LENGTH = 1024K - RAM : ORIGIN = 0x24000000, LENGTH = 320K -} -*/ - -bind_interrupts!(struct Irqs { - LTDC => ltdc::InterruptHandler; -}); - -const NUM_COLORS: usize = 256; - -#[embassy_executor::main] -async fn main(spawner: Spawner) { - let p = rcc_setup::stm32h735g_init(); - - // blink the led on another task - let led = Output::new(p.PC3, Level::High, Speed::Low); - unwrap!(spawner.spawn(led_task(led))); - - // numbers from STMicroelectronics/STM32CubeH7 STM32H735G-DK C-based example - const RK043FN48H_HSYNC: u16 = 41; // Horizontal synchronization - const RK043FN48H_HBP: u16 = 13; // Horizontal back porch - const RK043FN48H_HFP: u16 = 32; // Horizontal front porch - const RK043FN48H_VSYNC: u16 = 10; // Vertical synchronization - const RK043FN48H_VBP: u16 = 2; // Vertical back porch - const RK043FN48H_VFP: u16 = 2; // Vertical front porch - - let ltdc_config = LtdcConfiguration { - active_width: DISPLAY_WIDTH as _, - active_height: DISPLAY_HEIGHT as _, - h_back_porch: RK043FN48H_HBP - 11, // -11 from MX_LTDC_Init - h_front_porch: RK043FN48H_HFP, - v_back_porch: RK043FN48H_VBP, - v_front_porch: RK043FN48H_VFP, - h_sync: RK043FN48H_HSYNC, - v_sync: RK043FN48H_VSYNC, - h_sync_polarity: PolarityActive::ActiveLow, - v_sync_polarity: PolarityActive::ActiveLow, - data_enable_polarity: PolarityActive::ActiveHigh, - pixel_clock_polarity: PolarityEdge::FallingEdge, - }; - - info!("init ltdc"); - let mut ltdc = Ltdc::new_with_pins( - p.LTDC, Irqs, p.PG7, p.PC6, p.PA4, p.PG14, p.PD0, p.PD6, p.PA8, p.PE12, p.PA3, p.PB8, p.PB9, p.PB1, p.PB0, - p.PA6, p.PE11, p.PH15, p.PH4, p.PC7, p.PD3, p.PE0, p.PH3, p.PH8, p.PH9, p.PH10, p.PH11, p.PE1, p.PE15, - ); - ltdc.init(<dc_config); - - // we only need to draw on one layer for this example (not to be confused with the double buffer) - info!("enable bottom layer"); - let layer_config = LtdcLayerConfig { - pixel_format: ltdc::PixelFormat::L8, // 1 byte per pixel - layer: LtdcLayer::Layer1, - window_x0: 0, - window_x1: DISPLAY_WIDTH as _, - window_y0: 0, - window_y1: DISPLAY_HEIGHT as _, - }; - - let ferris_bmp: Bmp = Bmp::from_slice(include_bytes!("./ferris.bmp")).unwrap(); - let color_map = build_color_lookup_map(&ferris_bmp); - let clut = build_clut(&color_map); - - // enable the bottom layer with a 256 color lookup table - ltdc.init_layer(&layer_config, Some(&clut)); - - // Safety: the DoubleBuffer controls access to the statically allocated frame buffers - // and it is the only thing that mutates their content - let mut double_buffer = DoubleBuffer::new( - unsafe { FB1.as_mut() }, - unsafe { FB2.as_mut() }, - layer_config, - color_map, - ); - - // this allows us to perform some simple animation for every frame - let mut bouncy_box = BouncyBox::new( - ferris_bmp.bounding_box(), - Rectangle::new(Point::zero(), Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)), - 2, - ); - - loop { - // cpu intensive drawing to the buffer that is NOT currently being copied to the LCD screen - double_buffer.clear(); - let position = bouncy_box.next_point(); - let ferris = Image::new(&ferris_bmp, position); - unwrap!(ferris.draw(&mut double_buffer)); - - // perform async dma data transfer to the lcd screen - unwrap!(double_buffer.swap(&mut ltdc).await); - } -} - -/// builds the color look-up table from all unique colors found in the bitmap. This should be a 256 color indexed bitmap to work. -fn build_color_lookup_map(bmp: &Bmp) -> FnvIndexMap { - let mut color_map: FnvIndexMap = heapless::FnvIndexMap::new(); - let mut counter: u8 = 0; - - // add black to position 0 - color_map.insert(Rgb888::new(0, 0, 0).into_storage(), counter).unwrap(); - counter += 1; - - for Pixel(_point, color) in bmp.pixels() { - let raw = color.into_storage(); - if let Entry::Vacant(v) = color_map.entry(raw) { - v.insert(counter).expect("more than 256 colors detected"); - counter += 1; - } - } - color_map -} - -/// builds the color look-up table from the color map provided -fn build_clut(color_map: &FnvIndexMap) -> [ltdc::RgbColor; NUM_COLORS] { - let mut clut = [ltdc::RgbColor::default(); NUM_COLORS]; - for (color, index) in color_map.iter() { - let color = Rgb888::from(RawU24::new(*color)); - clut[*index as usize] = ltdc::RgbColor { - red: color.r(), - green: color.g(), - blue: color.b(), - }; - } - - clut -} - -#[embassy_executor::task(pool_size = MY_TASK_POOL_SIZE)] -async fn led_task(mut led: Output<'static>) { - let mut counter = 0; - loop { - info!("blink: {}", counter); - counter += 1; - - // on - led.set_low(); - Timer::after(Duration::from_millis(50)).await; - - // off - led.set_high(); - Timer::after(Duration::from_millis(450)).await; - } -} - -pub type TargetPixelType = u8; - -// A simple double buffer -pub struct DoubleBuffer { - buf0: &'static mut [TargetPixelType], - buf1: &'static mut [TargetPixelType], - is_buf0: bool, - layer_config: LtdcLayerConfig, - color_map: FnvIndexMap, -} - -impl DoubleBuffer { - pub fn new( - buf0: &'static mut [TargetPixelType], - buf1: &'static mut [TargetPixelType], - layer_config: LtdcLayerConfig, - color_map: FnvIndexMap, - ) -> Self { - Self { - buf0, - buf1, - is_buf0: true, - layer_config, - color_map, - } - } - - pub fn current(&mut self) -> (&FnvIndexMap, &mut [TargetPixelType]) { - if self.is_buf0 { - (&self.color_map, self.buf0) - } else { - (&self.color_map, self.buf1) - } - } - - pub async fn swap(&mut self, ltdc: &mut Ltdc<'_, T>) -> Result<(), ltdc::Error> { - let (_, buf) = self.current(); - let frame_buffer = buf.as_ptr(); - self.is_buf0 = !self.is_buf0; - ltdc.set_buffer(self.layer_config.layer, frame_buffer as *const _).await - } - - /// Clears the buffer - pub fn clear(&mut self) { - let (color_map, buf) = self.current(); - let black = Rgb888::new(0, 0, 0).into_storage(); - let color_index = color_map.get(&black).expect("no black found in the color map"); - - for a in buf.iter_mut() { - *a = *color_index; // solid black - } - } -} - -// Implement DrawTarget for -impl DrawTarget for DoubleBuffer { - type Color = Rgb888; - type Error = (); - - /// Draw a pixel - fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> - where - I: IntoIterator>, - { - let size = self.size(); - let width = size.width as i32; - let height = size.height as i32; - let (color_map, buf) = self.current(); - - for pixel in pixels { - let Pixel(point, color) = pixel; - - if point.x >= 0 && point.y >= 0 && point.x < width && point.y < height { - let index = point.y * width + point.x; - let raw_color = color.into_storage(); - - match color_map.get(&raw_color) { - Some(x) => { - buf[index as usize] = *x; - } - None => panic!("color not found in color map: {}", raw_color), - }; - } else { - // Ignore invalid points - } - } - - Ok(()) - } -} - -impl OriginDimensions for DoubleBuffer { - /// Return the size of the display - fn size(&self) -> Size { - Size::new( - (self.layer_config.window_x1 - self.layer_config.window_x0) as _, - (self.layer_config.window_y1 - self.layer_config.window_y0) as _, - ) - } -} - -mod rcc_setup { - - use embassy_stm32::{rcc::*, Peripherals}; - use embassy_stm32::{ - rcc::{Hse, HseMode}, - time::Hertz, - Config, - }; - - /// Sets up clocks for the stm32h735g mcu - /// change this if you plan to use a different microcontroller - pub fn stm32h735g_init() -> Peripherals { - /* - https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Projects/STM32H735G-DK/Examples/GPIO/GPIO_EXTI/Src/main.c - @brief System Clock Configuration - The system Clock is configured as follow : - System Clock source = PLL (HSE) - SYSCLK(Hz) = 520000000 (CPU Clock) - HCLK(Hz) = 260000000 (AXI and AHBs Clock) - AHB Prescaler = 2 - D1 APB3 Prescaler = 2 (APB3 Clock 130MHz) - D2 APB1 Prescaler = 2 (APB1 Clock 130MHz) - D2 APB2 Prescaler = 2 (APB2 Clock 130MHz) - D3 APB4 Prescaler = 2 (APB4 Clock 130MHz) - HSE Frequency(Hz) = 25000000 - PLL_M = 5 - PLL_N = 104 - PLL_P = 1 - PLL_Q = 4 - PLL_R = 2 - VDD(V) = 3.3 - Flash Latency(WS) = 3 - */ - - // setup power and clocks for an stm32h735g-dk run from an external 25 Mhz external oscillator - let mut config = Config::default(); - config.rcc.hse = Some(Hse { - freq: Hertz::mhz(25), - mode: HseMode::Oscillator, - }); - config.rcc.hsi = None; - config.rcc.csi = false; - config.rcc.pll1 = Some(Pll { - source: PllSource::HSE, - prediv: PllPreDiv::DIV5, // PLL_M - mul: PllMul::MUL104, // PLL_N - divp: Some(PllDiv::DIV1), - divq: Some(PllDiv::DIV4), - divr: Some(PllDiv::DIV2), - }); - // numbers adapted from Drivers/BSP/STM32H735G-DK/stm32h735g_discovery_ospi.c - // MX_OSPI_ClockConfig - config.rcc.pll2 = Some(Pll { - source: PllSource::HSE, - prediv: PllPreDiv::DIV5, // PLL_M - mul: PllMul::MUL80, // PLL_N - divp: Some(PllDiv::DIV5), - divq: Some(PllDiv::DIV2), - divr: Some(PllDiv::DIV2), - }); - // numbers adapted from Drivers/BSP/STM32H735G-DK/stm32h735g_discovery_lcd.c - // MX_LTDC_ClockConfig - config.rcc.pll3 = Some(Pll { - source: PllSource::HSE, - prediv: PllPreDiv::DIV5, // PLL_M - mul: PllMul::MUL160, // PLL_N - divp: Some(PllDiv::DIV2), - divq: Some(PllDiv::DIV2), - divr: Some(PllDiv::DIV83), - }); - config.rcc.voltage_scale = VoltageScale::Scale0; - config.rcc.supply_config = SupplyConfig::DirectSMPS; - config.rcc.sys = Sysclk::PLL1_P; - config.rcc.ahb_pre = AHBPrescaler::DIV2; - config.rcc.apb1_pre = APBPrescaler::DIV2; - config.rcc.apb2_pre = APBPrescaler::DIV2; - config.rcc.apb3_pre = APBPrescaler::DIV2; - config.rcc.apb4_pre = APBPrescaler::DIV2; - let p = embassy_stm32::init(config); - p - } -} - -mod bouncy_box { - use embedded_graphics::{geometry::Point, primitives::Rectangle}; - - enum Direction { - DownLeft, - DownRight, - UpLeft, - UpRight, - } - - pub struct BouncyBox { - direction: Direction, - child_rect: Rectangle, - parent_rect: Rectangle, - current_point: Point, - move_by: usize, - } - - // This calculates the coordinates of a chile rectangle bounced around inside a parent bounded box - impl BouncyBox { - pub fn new(child_rect: Rectangle, parent_rect: Rectangle, move_by: usize) -> Self { - let center_box = parent_rect.center(); - let center_img = child_rect.center(); - let current_point = Point::new(center_box.x - center_img.x / 2, center_box.y - center_img.y / 2); - Self { - direction: Direction::DownRight, - child_rect, - parent_rect, - current_point, - move_by, - } - } - - pub fn next_point(&mut self) -> Point { - let direction = &self.direction; - let img_height = self.child_rect.size.height as i32; - let box_height = self.parent_rect.size.height as i32; - let img_width = self.child_rect.size.width as i32; - let box_width = self.parent_rect.size.width as i32; - let move_by = self.move_by as i32; - - match direction { - Direction::DownLeft => { - self.current_point.x -= move_by; - self.current_point.y += move_by; - - let x_out_of_bounds = self.current_point.x < 0; - let y_out_of_bounds = (self.current_point.y + img_height) > box_height; - - if x_out_of_bounds && y_out_of_bounds { - self.direction = Direction::UpRight - } else if x_out_of_bounds && !y_out_of_bounds { - self.direction = Direction::DownRight - } else if !x_out_of_bounds && y_out_of_bounds { - self.direction = Direction::UpLeft - } - } - Direction::DownRight => { - self.current_point.x += move_by; - self.current_point.y += move_by; - - let x_out_of_bounds = (self.current_point.x + img_width) > box_width; - let y_out_of_bounds = (self.current_point.y + img_height) > box_height; - - if x_out_of_bounds && y_out_of_bounds { - self.direction = Direction::UpLeft - } else if x_out_of_bounds && !y_out_of_bounds { - self.direction = Direction::DownLeft - } else if !x_out_of_bounds && y_out_of_bounds { - self.direction = Direction::UpRight - } - } - Direction::UpLeft => { - self.current_point.x -= move_by; - self.current_point.y -= move_by; - - let x_out_of_bounds = self.current_point.x < 0; - let y_out_of_bounds = self.current_point.y < 0; - - if x_out_of_bounds && y_out_of_bounds { - self.direction = Direction::DownRight - } else if x_out_of_bounds && !y_out_of_bounds { - self.direction = Direction::UpRight - } else if !x_out_of_bounds && y_out_of_bounds { - self.direction = Direction::DownLeft - } - } - Direction::UpRight => { - self.current_point.x += move_by; - self.current_point.y -= move_by; - - let x_out_of_bounds = (self.current_point.x + img_width) > box_width; - let y_out_of_bounds = self.current_point.y < 0; - - if x_out_of_bounds && y_out_of_bounds { - self.direction = Direction::DownLeft - } else if x_out_of_bounds && !y_out_of_bounds { - self.direction = Direction::UpLeft - } else if !x_out_of_bounds && y_out_of_bounds { - self.direction = Direction::DownRight - } - } - } - - self.current_point - } - } -} diff --git a/examples/stm32h735/.cargo/config.toml b/examples/stm32h735/.cargo/config.toml new file mode 100644 index 000000000..95536c6a8 --- /dev/null +++ b/examples/stm32h735/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.thumbv7em-none-eabihf] +runner = 'probe-rs run --chip STM32H735IGKx' + +[build] +target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml new file mode 100644 index 000000000..fc21cc894 --- /dev/null +++ b/examples/stm32h735/Cargo.toml @@ -0,0 +1,61 @@ +[package] +edition = "2021" +name = "embassy-stm32h735-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } +embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } +embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } +heapless = { version = "0.8", default-features = false } +embedded-graphics = { version = "0.8.1" } +tinybmp = { version = "0.5" } + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- diff --git a/examples/stm32h735/build.rs b/examples/stm32h735/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/stm32h735/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/stm32h735/memory.x b/examples/stm32h735/memory.x new file mode 100644 index 000000000..3a70d24d2 --- /dev/null +++ b/examples/stm32h735/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x08000000, LENGTH = 1024K + RAM : ORIGIN = 0x24000000, LENGTH = 320K +} \ No newline at end of file diff --git a/examples/stm32h735/src/bin/ferris.bmp b/examples/stm32h735/src/bin/ferris.bmp new file mode 100644 index 000000000..7a222ab84 Binary files /dev/null and b/examples/stm32h735/src/bin/ferris.bmp differ diff --git a/examples/stm32h735/src/bin/ltdc.rs b/examples/stm32h735/src/bin/ltdc.rs new file mode 100644 index 000000000..5c75a7db1 --- /dev/null +++ b/examples/stm32h735/src/bin/ltdc.rs @@ -0,0 +1,473 @@ +#![no_std] +#![no_main] +#![macro_use] +#![allow(static_mut_refs)] + +/// This example demonstrates the LTDC lcd display peripheral and was tested to run on an stm32h735g-dk (embassy-stm32 feature "stm32h735ig" and probe-rs chip "STM32H735IGKx") +/// Even though the dev kit has 16MB of attached PSRAM this example uses the 320KB of internal AXIS RAM found on the mcu itself to make the example more standalone and portable. +/// For this reason a 256 color lookup table had to be used to keep the memory requirement down to an acceptable level. +/// The example bounces a ferris crab bitmap around the screen while blinking an led on another task +/// +use bouncy_box::BouncyBox; +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_stm32::{ + bind_interrupts, + gpio::{Level, Output, Speed}, + ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge}, + peripherals, +}; +use embassy_time::{Duration, Timer}; +use embedded_graphics::{ + draw_target::DrawTarget, + geometry::{OriginDimensions, Point, Size}, + image::Image, + pixelcolor::{raw::RawU24, Rgb888}, + prelude::*, + primitives::Rectangle, + Pixel, +}; +use heapless::{Entry, FnvIndexMap}; +use tinybmp::Bmp; +use {defmt_rtt as _, panic_probe as _}; + +const DISPLAY_WIDTH: usize = 480; +const DISPLAY_HEIGHT: usize = 272; +const MY_TASK_POOL_SIZE: usize = 2; + +// the following two display buffers consume 261120 bytes that just about fits into axis ram found on the mcu +pub static mut FB1: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT]; +pub static mut FB2: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT]; + +bind_interrupts!(struct Irqs { + LTDC => ltdc::InterruptHandler; +}); + +const NUM_COLORS: usize = 256; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = rcc_setup::stm32h735g_init(); + + // blink the led on another task + let led = Output::new(p.PC3, Level::High, Speed::Low); + unwrap!(spawner.spawn(led_task(led))); + + // numbers from STMicroelectronics/STM32CubeH7 STM32H735G-DK C-based example + const RK043FN48H_HSYNC: u16 = 41; // Horizontal synchronization + const RK043FN48H_HBP: u16 = 13; // Horizontal back porch + const RK043FN48H_HFP: u16 = 32; // Horizontal front porch + const RK043FN48H_VSYNC: u16 = 10; // Vertical synchronization + const RK043FN48H_VBP: u16 = 2; // Vertical back porch + const RK043FN48H_VFP: u16 = 2; // Vertical front porch + + let ltdc_config = LtdcConfiguration { + active_width: DISPLAY_WIDTH as _, + active_height: DISPLAY_HEIGHT as _, + h_back_porch: RK043FN48H_HBP - 11, // -11 from MX_LTDC_Init + h_front_porch: RK043FN48H_HFP, + v_back_porch: RK043FN48H_VBP, + v_front_porch: RK043FN48H_VFP, + h_sync: RK043FN48H_HSYNC, + v_sync: RK043FN48H_VSYNC, + h_sync_polarity: PolarityActive::ActiveLow, + v_sync_polarity: PolarityActive::ActiveLow, + data_enable_polarity: PolarityActive::ActiveHigh, + pixel_clock_polarity: PolarityEdge::FallingEdge, + }; + + info!("init ltdc"); + let mut ltdc = Ltdc::new_with_pins( + p.LTDC, Irqs, p.PG7, p.PC6, p.PA4, p.PG14, p.PD0, p.PD6, p.PA8, p.PE12, p.PA3, p.PB8, p.PB9, p.PB1, p.PB0, + p.PA6, p.PE11, p.PH15, p.PH4, p.PC7, p.PD3, p.PE0, p.PH3, p.PH8, p.PH9, p.PH10, p.PH11, p.PE1, p.PE15, + ); + ltdc.init(<dc_config); + + // we only need to draw on one layer for this example (not to be confused with the double buffer) + info!("enable bottom layer"); + let layer_config = LtdcLayerConfig { + pixel_format: ltdc::PixelFormat::L8, // 1 byte per pixel + layer: LtdcLayer::Layer1, + window_x0: 0, + window_x1: DISPLAY_WIDTH as _, + window_y0: 0, + window_y1: DISPLAY_HEIGHT as _, + }; + + let ferris_bmp: Bmp = Bmp::from_slice(include_bytes!("./ferris.bmp")).unwrap(); + let color_map = build_color_lookup_map(&ferris_bmp); + let clut = build_clut(&color_map); + + // enable the bottom layer with a 256 color lookup table + ltdc.init_layer(&layer_config, Some(&clut)); + + // Safety: the DoubleBuffer controls access to the statically allocated frame buffers + // and it is the only thing that mutates their content + let mut double_buffer = DoubleBuffer::new( + unsafe { FB1.as_mut() }, + unsafe { FB2.as_mut() }, + layer_config, + color_map, + ); + + // this allows us to perform some simple animation for every frame + let mut bouncy_box = BouncyBox::new( + ferris_bmp.bounding_box(), + Rectangle::new(Point::zero(), Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)), + 2, + ); + + loop { + // cpu intensive drawing to the buffer that is NOT currently being copied to the LCD screen + double_buffer.clear(); + let position = bouncy_box.next_point(); + let ferris = Image::new(&ferris_bmp, position); + unwrap!(ferris.draw(&mut double_buffer)); + + // perform async dma data transfer to the lcd screen + unwrap!(double_buffer.swap(&mut ltdc).await); + } +} + +/// builds the color look-up table from all unique colors found in the bitmap. This should be a 256 color indexed bitmap to work. +fn build_color_lookup_map(bmp: &Bmp) -> FnvIndexMap { + let mut color_map: FnvIndexMap = heapless::FnvIndexMap::new(); + let mut counter: u8 = 0; + + // add black to position 0 + color_map.insert(Rgb888::new(0, 0, 0).into_storage(), counter).unwrap(); + counter += 1; + + for Pixel(_point, color) in bmp.pixels() { + let raw = color.into_storage(); + if let Entry::Vacant(v) = color_map.entry(raw) { + v.insert(counter).expect("more than 256 colors detected"); + counter += 1; + } + } + color_map +} + +/// builds the color look-up table from the color map provided +fn build_clut(color_map: &FnvIndexMap) -> [ltdc::RgbColor; NUM_COLORS] { + let mut clut = [ltdc::RgbColor::default(); NUM_COLORS]; + for (color, index) in color_map.iter() { + let color = Rgb888::from(RawU24::new(*color)); + clut[*index as usize] = ltdc::RgbColor { + red: color.r(), + green: color.g(), + blue: color.b(), + }; + } + + clut +} + +#[embassy_executor::task(pool_size = MY_TASK_POOL_SIZE)] +async fn led_task(mut led: Output<'static>) { + let mut counter = 0; + loop { + info!("blink: {}", counter); + counter += 1; + + // on + led.set_low(); + Timer::after(Duration::from_millis(50)).await; + + // off + led.set_high(); + Timer::after(Duration::from_millis(450)).await; + } +} + +pub type TargetPixelType = u8; + +// A simple double buffer +pub struct DoubleBuffer { + buf0: &'static mut [TargetPixelType], + buf1: &'static mut [TargetPixelType], + is_buf0: bool, + layer_config: LtdcLayerConfig, + color_map: FnvIndexMap, +} + +impl DoubleBuffer { + pub fn new( + buf0: &'static mut [TargetPixelType], + buf1: &'static mut [TargetPixelType], + layer_config: LtdcLayerConfig, + color_map: FnvIndexMap, + ) -> Self { + Self { + buf0, + buf1, + is_buf0: true, + layer_config, + color_map, + } + } + + pub fn current(&mut self) -> (&FnvIndexMap, &mut [TargetPixelType]) { + if self.is_buf0 { + (&self.color_map, self.buf0) + } else { + (&self.color_map, self.buf1) + } + } + + pub async fn swap(&mut self, ltdc: &mut Ltdc<'_, T>) -> Result<(), ltdc::Error> { + let (_, buf) = self.current(); + let frame_buffer = buf.as_ptr(); + self.is_buf0 = !self.is_buf0; + ltdc.set_buffer(self.layer_config.layer, frame_buffer as *const _).await + } + + /// Clears the buffer + pub fn clear(&mut self) { + let (color_map, buf) = self.current(); + let black = Rgb888::new(0, 0, 0).into_storage(); + let color_index = color_map.get(&black).expect("no black found in the color map"); + + for a in buf.iter_mut() { + *a = *color_index; // solid black + } + } +} + +// Implement DrawTarget for +impl DrawTarget for DoubleBuffer { + type Color = Rgb888; + type Error = (); + + /// Draw a pixel + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + let size = self.size(); + let width = size.width as i32; + let height = size.height as i32; + let (color_map, buf) = self.current(); + + for pixel in pixels { + let Pixel(point, color) = pixel; + + if point.x >= 0 && point.y >= 0 && point.x < width && point.y < height { + let index = point.y * width + point.x; + let raw_color = color.into_storage(); + + match color_map.get(&raw_color) { + Some(x) => { + buf[index as usize] = *x; + } + None => panic!("color not found in color map: {}", raw_color), + }; + } else { + // Ignore invalid points + } + } + + Ok(()) + } +} + +impl OriginDimensions for DoubleBuffer { + /// Return the size of the display + fn size(&self) -> Size { + Size::new( + (self.layer_config.window_x1 - self.layer_config.window_x0) as _, + (self.layer_config.window_y1 - self.layer_config.window_y0) as _, + ) + } +} + +mod rcc_setup { + + use embassy_stm32::{rcc::*, Peripherals}; + use embassy_stm32::{ + rcc::{Hse, HseMode}, + time::Hertz, + Config, + }; + + /// Sets up clocks for the stm32h735g mcu + /// change this if you plan to use a different microcontroller + pub fn stm32h735g_init() -> Peripherals { + /* + https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Projects/STM32H735G-DK/Examples/GPIO/GPIO_EXTI/Src/main.c + @brief System Clock Configuration + The system Clock is configured as follow : + System Clock source = PLL (HSE) + SYSCLK(Hz) = 520000000 (CPU Clock) + HCLK(Hz) = 260000000 (AXI and AHBs Clock) + AHB Prescaler = 2 + D1 APB3 Prescaler = 2 (APB3 Clock 130MHz) + D2 APB1 Prescaler = 2 (APB1 Clock 130MHz) + D2 APB2 Prescaler = 2 (APB2 Clock 130MHz) + D3 APB4 Prescaler = 2 (APB4 Clock 130MHz) + HSE Frequency(Hz) = 25000000 + PLL_M = 5 + PLL_N = 104 + PLL_P = 1 + PLL_Q = 4 + PLL_R = 2 + VDD(V) = 3.3 + Flash Latency(WS) = 3 + */ + + // setup power and clocks for an stm32h735g-dk run from an external 25 Mhz external oscillator + let mut config = Config::default(); + config.rcc.hse = Some(Hse { + freq: Hertz::mhz(25), + mode: HseMode::Oscillator, + }); + config.rcc.hsi = None; + config.rcc.csi = false; + config.rcc.pll1 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV5, // PLL_M + mul: PllMul::MUL104, // PLL_N + divp: Some(PllDiv::DIV1), + divq: Some(PllDiv::DIV4), + divr: Some(PllDiv::DIV2), + }); + // numbers adapted from Drivers/BSP/STM32H735G-DK/stm32h735g_discovery_ospi.c + // MX_OSPI_ClockConfig + config.rcc.pll2 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV5, // PLL_M + mul: PllMul::MUL80, // PLL_N + divp: Some(PllDiv::DIV5), + divq: Some(PllDiv::DIV2), + divr: Some(PllDiv::DIV2), + }); + // numbers adapted from Drivers/BSP/STM32H735G-DK/stm32h735g_discovery_lcd.c + // MX_LTDC_ClockConfig + config.rcc.pll3 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV5, // PLL_M + mul: PllMul::MUL160, // PLL_N + divp: Some(PllDiv::DIV2), + divq: Some(PllDiv::DIV2), + divr: Some(PllDiv::DIV83), + }); + config.rcc.voltage_scale = VoltageScale::Scale0; + config.rcc.supply_config = SupplyConfig::DirectSMPS; + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV2; + config.rcc.apb1_pre = APBPrescaler::DIV2; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.apb3_pre = APBPrescaler::DIV2; + config.rcc.apb4_pre = APBPrescaler::DIV2; + embassy_stm32::init(config) + } +} + +mod bouncy_box { + use embedded_graphics::{geometry::Point, primitives::Rectangle}; + + enum Direction { + DownLeft, + DownRight, + UpLeft, + UpRight, + } + + pub struct BouncyBox { + direction: Direction, + child_rect: Rectangle, + parent_rect: Rectangle, + current_point: Point, + move_by: usize, + } + + // This calculates the coordinates of a chile rectangle bounced around inside a parent bounded box + impl BouncyBox { + pub fn new(child_rect: Rectangle, parent_rect: Rectangle, move_by: usize) -> Self { + let center_box = parent_rect.center(); + let center_img = child_rect.center(); + let current_point = Point::new(center_box.x - center_img.x / 2, center_box.y - center_img.y / 2); + Self { + direction: Direction::DownRight, + child_rect, + parent_rect, + current_point, + move_by, + } + } + + pub fn next_point(&mut self) -> Point { + let direction = &self.direction; + let img_height = self.child_rect.size.height as i32; + let box_height = self.parent_rect.size.height as i32; + let img_width = self.child_rect.size.width as i32; + let box_width = self.parent_rect.size.width as i32; + let move_by = self.move_by as i32; + + match direction { + Direction::DownLeft => { + self.current_point.x -= move_by; + self.current_point.y += move_by; + + let x_out_of_bounds = self.current_point.x < 0; + let y_out_of_bounds = (self.current_point.y + img_height) > box_height; + + if x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::UpRight + } else if x_out_of_bounds && !y_out_of_bounds { + self.direction = Direction::DownRight + } else if !x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::UpLeft + } + } + Direction::DownRight => { + self.current_point.x += move_by; + self.current_point.y += move_by; + + let x_out_of_bounds = (self.current_point.x + img_width) > box_width; + let y_out_of_bounds = (self.current_point.y + img_height) > box_height; + + if x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::UpLeft + } else if x_out_of_bounds && !y_out_of_bounds { + self.direction = Direction::DownLeft + } else if !x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::UpRight + } + } + Direction::UpLeft => { + self.current_point.x -= move_by; + self.current_point.y -= move_by; + + let x_out_of_bounds = self.current_point.x < 0; + let y_out_of_bounds = self.current_point.y < 0; + + if x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::DownRight + } else if x_out_of_bounds && !y_out_of_bounds { + self.direction = Direction::UpRight + } else if !x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::DownLeft + } + } + Direction::UpRight => { + self.current_point.x += move_by; + self.current_point.y -= move_by; + + let x_out_of_bounds = (self.current_point.x + img_width) > box_width; + let y_out_of_bounds = self.current_point.y < 0; + + if x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::DownLeft + } else if x_out_of_bounds && !y_out_of_bounds { + self.direction = Direction::UpLeft + } else if !x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::DownRight + } + } + } + + self.current_point + } + } +} -- cgit From 6edf7b4688361e165c2ea9af03df9725a89a853e Mon Sep 17 00:00:00 2001 From: David Haig Date: Fri, 28 Jun 2024 18:17:17 +0100 Subject: Applied formatting --- examples/stm32h735/src/bin/ltdc.rs | 42 ++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 24 deletions(-) (limited to 'examples') diff --git a/examples/stm32h735/src/bin/ltdc.rs b/examples/stm32h735/src/bin/ltdc.rs index 5c75a7db1..a36fdef2c 100644 --- a/examples/stm32h735/src/bin/ltdc.rs +++ b/examples/stm32h735/src/bin/ltdc.rs @@ -11,22 +11,18 @@ use bouncy_box::BouncyBox; use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{ - bind_interrupts, - gpio::{Level, Output, Speed}, - ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge}, - peripherals, -}; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge}; +use embassy_stm32::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; -use embedded_graphics::{ - draw_target::DrawTarget, - geometry::{OriginDimensions, Point, Size}, - image::Image, - pixelcolor::{raw::RawU24, Rgb888}, - prelude::*, - primitives::Rectangle, - Pixel, -}; +use embedded_graphics::draw_target::DrawTarget; +use embedded_graphics::geometry::{OriginDimensions, Point, Size}; +use embedded_graphics::image::Image; +use embedded_graphics::pixelcolor::raw::RawU24; +use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::prelude::*; +use embedded_graphics::primitives::Rectangle; +use embedded_graphics::Pixel; use heapless::{Entry, FnvIndexMap}; use tinybmp::Bmp; use {defmt_rtt as _, panic_probe as _}; @@ -100,7 +96,7 @@ async fn main(spawner: Spawner) { // enable the bottom layer with a 256 color lookup table ltdc.init_layer(&layer_config, Some(&clut)); - + // Safety: the DoubleBuffer controls access to the statically allocated frame buffers // and it is the only thing that mutates their content let mut double_buffer = DoubleBuffer::new( @@ -283,12 +279,9 @@ impl OriginDimensions for DoubleBuffer { mod rcc_setup { - use embassy_stm32::{rcc::*, Peripherals}; - use embassy_stm32::{ - rcc::{Hse, HseMode}, - time::Hertz, - Config, - }; + use embassy_stm32::rcc::{Hse, HseMode, *}; + use embassy_stm32::time::Hertz; + use embassy_stm32::{Config, Peripherals}; /// Sets up clocks for the stm32h735g mcu /// change this if you plan to use a different microcontroller @@ -359,12 +352,13 @@ mod rcc_setup { config.rcc.apb2_pre = APBPrescaler::DIV2; config.rcc.apb3_pre = APBPrescaler::DIV2; config.rcc.apb4_pre = APBPrescaler::DIV2; - embassy_stm32::init(config) + embassy_stm32::init(config) } } mod bouncy_box { - use embedded_graphics::{geometry::Point, primitives::Rectangle}; + use embedded_graphics::geometry::Point; + use embedded_graphics::primitives::Rectangle; enum Direction { DownLeft, -- cgit From 67f5b8d974c23854676cff3a6797cf4aaf41a402 Mon Sep 17 00:00:00 2001 From: Eekle <96976531+Eekle@users.noreply.github.com> Date: Sun, 30 Jun 2024 11:06:32 +0200 Subject: Update example --- examples/stm32u5/src/bin/tsc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/stm32u5/src/bin/tsc.rs b/examples/stm32u5/src/bin/tsc.rs index db85fb158..eb15d275a 100644 --- a/examples/stm32u5/src/bin/tsc.rs +++ b/examples/stm32u5/src/bin/tsc.rs @@ -50,9 +50,8 @@ async fn main(_spawner: embassy_executor::Spawner) { g7.set_io2(context.PE3, PinType::Sample); g7.set_io3(context.PE4, PinType::Channel); - let mut touch_controller = tsc::Tsc::new( + let mut touch_controller = tsc::Tsc::new_async( context.TSC, - Irqs, Some(g1), Some(g2), None, @@ -62,6 +61,7 @@ async fn main(_spawner: embassy_executor::Spawner) { Some(g7), None, config, + Irqs, ); touch_controller.discharge_io(true); -- cgit From c4b88b57812da85b6952300509736fd02a4640fa Mon Sep 17 00:00:00 2001 From: Josh Junon Date: Sat, 29 Jun 2024 01:17:27 +0200 Subject: wiznet: add version check to initialization sequence --- examples/rp/src/bin/ethernet_w5500_multisocket.rs | 3 ++- examples/rp/src/bin/ethernet_w5500_tcp_client.rs | 3 ++- examples/rp/src/bin/ethernet_w5500_tcp_server.rs | 3 ++- examples/rp/src/bin/ethernet_w5500_udp.rs | 3 ++- examples/stm32f4/src/bin/eth_w5500.rs | 4 +++- 5 files changed, 11 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index bd52cadca..def26b53d 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -63,7 +63,8 @@ async fn main(spawner: Spawner) { w5500_int, w5500_reset, ) - .await; + .await + .unwrap(); unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index 3e4fbd2e6..6c4a78361 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -66,7 +66,8 @@ async fn main(spawner: Spawner) { w5500_int, w5500_reset, ) - .await; + .await + .unwrap(); unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 5532851f3..30a3a7463 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -65,7 +65,8 @@ async fn main(spawner: Spawner) { w5500_int, w5500_reset, ) - .await; + .await + .unwrap(); unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index adb1d8941..1613ed887 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -63,7 +63,8 @@ async fn main(spawner: Spawner) { w5500_int, w5500_reset, ) - .await; + .await + .unwrap(); unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed diff --git a/examples/stm32f4/src/bin/eth_w5500.rs b/examples/stm32f4/src/bin/eth_w5500.rs index c51111110..3c770a873 100644 --- a/examples/stm32f4/src/bin/eth_w5500.rs +++ b/examples/stm32f4/src/bin/eth_w5500.rs @@ -80,7 +80,9 @@ async fn main(spawner: Spawner) -> ! { let mac_addr = [0x02, 234, 3, 4, 82, 231]; static STATE: StaticCell> = StaticCell::new(); let state = STATE.init(State::<2, 2>::new()); - let (device, runner) = embassy_net_wiznet::new(mac_addr, state, spi, w5500_int, w5500_reset).await; + let (device, runner) = embassy_net_wiznet::new(mac_addr, state, spi, w5500_int, w5500_reset) + .await + .unwrap(); unwrap!(spawner.spawn(ethernet_task(runner))); let config = embassy_net::Config::dhcpv4(Default::default()); -- cgit From 70061e74b2b4e4ca513bcefa5c3bebbb52538e5d Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Wed, 26 Jun 2024 19:06:20 +0300 Subject: add async dma read example --- examples/stm32h7/src/bin/adc_dma.rs | 76 +++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 examples/stm32h7/src/bin/adc_dma.rs (limited to 'examples') diff --git a/examples/stm32h7/src/bin/adc_dma.rs b/examples/stm32h7/src/bin/adc_dma.rs new file mode 100644 index 000000000..6c0240453 --- /dev/null +++ b/examples/stm32h7/src/bin/adc_dma.rs @@ -0,0 +1,76 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; +use embassy_stm32::Config; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[link_section = ".ram_d3"] +static mut DMA_BUF: [u16; 2] = [0; 2]; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut read_buffer = unsafe { &mut DMA_BUF[..] }; + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(HSIPrescaler::DIV1); + config.rcc.csi = true; + config.rcc.pll1 = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL50, + divp: Some(PllDiv::DIV2), + divq: Some(PllDiv::DIV8), // SPI1 cksel defaults to pll1_q + divr: None, + }); + config.rcc.pll2 = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL50, + divp: Some(PllDiv::DIV8), // 100mhz + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + config.rcc.mux.adcsel = mux::Adcsel::PLL2_P; + } + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC3); + + let mut dma = p.DMA1_CH1; + let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); + let mut pc0 = p.PC0.degrade_adc(); + + loop { + adc.read_async( + &mut dma, + [ + (&mut vrefint_channel, SampleTime::CYCLES387_5), + (&mut pc0, SampleTime::CYCLES810_5), + ] + .into_iter(), + &mut read_buffer, + ) + .await; + + let vrefint = read_buffer[0]; + let measured = read_buffer[1]; + info!("vrefint: {}", vrefint); + info!("measured: {}", measured); + Timer::after_millis(500).await; + } +} -- cgit From 09cfa28a218a65cf756ffde4403806aa7e2613ed Mon Sep 17 00:00:00 2001 From: Andres Vahter Date: Wed, 3 Jul 2024 10:59:20 +0300 Subject: stm32g0: add adc_dma example --- examples/stm32g0/src/bin/adc_dma.rs | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 examples/stm32g0/src/bin/adc_dma.rs (limited to 'examples') diff --git a/examples/stm32g0/src/bin/adc_dma.rs b/examples/stm32g0/src/bin/adc_dma.rs new file mode 100644 index 000000000..42d1e729b --- /dev/null +++ b/examples/stm32g0/src/bin/adc_dma.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +static mut DMA_BUF: [u16; 2] = [0; 2]; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut read_buffer = unsafe { &mut DMA_BUF[..] }; + + let p = embassy_stm32::init(Default::default()); + + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC1); + + let mut dma = p.DMA1_CH1; + let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); + let mut pa0 = p.PA0.degrade_adc(); + + loop { + adc.read_async( + &mut dma, + [ + (&mut vrefint_channel, SampleTime::CYCLES160_5), + (&mut pa0, SampleTime::CYCLES160_5), + ] + .into_iter(), + &mut read_buffer, + ) + .await; + + let vrefint = read_buffer[0]; + let measured = read_buffer[1]; + info!("vrefint: {}", vrefint); + info!("measured: {}", measured); + Timer::after_millis(500).await; + } +} -- cgit From f851081e09982a3edefd23082bb23143a0172464 Mon Sep 17 00:00:00 2001 From: Andres Vahter Date: Wed, 3 Jul 2024 14:39:10 +0300 Subject: stm32 adc: introduce blocking_read --- examples/stm32f4/src/bin/adc.rs | 8 ++++---- examples/stm32f4/src/bin/adc_dma.rs | 4 ++-- examples/stm32f7/src/bin/adc.rs | 4 ++-- examples/stm32g0/src/bin/adc.rs | 4 ++-- examples/stm32g0/src/bin/adc_dma.rs | 2 +- examples/stm32g0/src/bin/adc_oversampling.rs | 2 +- examples/stm32g4/src/bin/adc.rs | 2 +- examples/stm32h7/src/bin/adc.rs | 4 ++-- examples/stm32h7/src/bin/adc_dma.rs | 2 +- examples/stm32l4/src/bin/adc.rs | 2 +- examples/stm32u0/src/bin/adc.rs | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) (limited to 'examples') diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 9473b7b7f..423d29225 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) { // Startup delay can be combined to the maximum of either delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); - let vrefint_sample = adc.read(&mut vrefint); + let vrefint_sample = adc.blocking_read(&mut vrefint); let convert_to_millivolts = |sample| { // From http://www.st.com/resource/en/datasheet/DM00071990.pdf @@ -50,16 +50,16 @@ async fn main(_spawner: Spawner) { loop { // Read pin - let v = adc.read(&mut pin); + let v = adc.blocking_read(&mut pin); info!("PC1: {} ({} mV)", v, convert_to_millivolts(v)); // Read internal temperature - let v = adc.read(&mut temp); + let v = adc.blocking_read(&mut temp); let celcius = convert_to_celcius(v); info!("Internal temp: {} ({} C)", v, celcius); // Read internal voltage reference - let v = adc.read(&mut vrefint); + let v = adc.blocking_read(&mut vrefint); info!("VrefInt: {}", v); Timer::after_millis(100).await; diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs index 992bed573..43a761e6d 100644 --- a/examples/stm32f4/src/bin/adc_dma.rs +++ b/examples/stm32f4/src/bin/adc_dma.rs @@ -44,7 +44,7 @@ async fn adc_task(mut p: Peripherals) { let _ = adc.start(); let _ = adc2.start(); loop { - match adc.read_exact(&mut buffer1).await { + match adc.read(&mut buffer1).await { Ok(_data) => { let toc = Instant::now(); info!( @@ -62,7 +62,7 @@ async fn adc_task(mut p: Peripherals) { } } - match adc2.read_exact(&mut buffer2).await { + match adc2.read(&mut buffer2).await { Ok(_data) => { let toc = Instant::now(); info!( diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index 641157960..6689e3b5d 100644 --- a/examples/stm32f7/src/bin/adc.rs +++ b/examples/stm32f7/src/bin/adc.rs @@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) { let mut pin = p.PA3; let mut vrefint = adc.enable_vrefint(); - let vrefint_sample = adc.read(&mut vrefint); + let vrefint_sample = adc.blocking_read(&mut vrefint); let convert_to_millivolts = |sample| { // From http://www.st.com/resource/en/datasheet/DM00273119.pdf // 6.3.27 Reference voltage @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) { }; loop { - let v = adc.read(&mut pin); + let v = adc.blocking_read(&mut pin); info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after_millis(100).await; } diff --git a/examples/stm32g0/src/bin/adc.rs b/examples/stm32g0/src/bin/adc.rs index a35119e3d..6c7f3b48a 100644 --- a/examples/stm32g0/src/bin/adc.rs +++ b/examples/stm32g0/src/bin/adc.rs @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { let mut pin = p.PA1; let mut vrefint = adc.enable_vrefint(); - let vrefint_sample = adc.read(&mut vrefint); + let vrefint_sample = adc.blocking_read(&mut vrefint); let convert_to_millivolts = |sample| { // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf // 6.3.3 Embedded internal reference voltage @@ -27,7 +27,7 @@ async fn main(_spawner: Spawner) { }; loop { - let v = adc.read(&mut pin); + let v = adc.blocking_read(&mut pin); info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after_millis(100).await; } diff --git a/examples/stm32g0/src/bin/adc_dma.rs b/examples/stm32g0/src/bin/adc_dma.rs index 42d1e729b..3713e5a21 100644 --- a/examples/stm32g0/src/bin/adc_dma.rs +++ b/examples/stm32g0/src/bin/adc_dma.rs @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { let mut pa0 = p.PA0.degrade_adc(); loop { - adc.read_async( + adc.read( &mut dma, [ (&mut vrefint_channel, SampleTime::CYCLES160_5), diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs index 3c31eb206..9c5dd872a 100644 --- a/examples/stm32g0/src/bin/adc_oversampling.rs +++ b/examples/stm32g0/src/bin/adc_oversampling.rs @@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) { adc.oversampling_enable(true); loop { - let v = adc.read(&mut pin); + let v = adc.blocking_read(&mut pin); info!("--> {} ", v); //max 65520 = 0xFFF0 Timer::after_millis(100).await; } diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index 3de38cbd6..adca846d8 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs @@ -32,7 +32,7 @@ async fn main(_spawner: Spawner) { adc.set_sample_time(SampleTime::CYCLES24_5); loop { - let measured = adc.read(&mut p.PA7); + let measured = adc.blocking_read(&mut p.PA7); info!("measured: {}", measured); Timer::after_millis(500).await; } diff --git a/examples/stm32h7/src/bin/adc.rs b/examples/stm32h7/src/bin/adc.rs index e9a857a74..98504ddf6 100644 --- a/examples/stm32h7/src/bin/adc.rs +++ b/examples/stm32h7/src/bin/adc.rs @@ -51,9 +51,9 @@ async fn main(_spawner: Spawner) { let mut vrefint_channel = adc.enable_vrefint(); loop { - let vrefint = adc.read(&mut vrefint_channel); + let vrefint = adc.blocking_read(&mut vrefint_channel); info!("vrefint: {}", vrefint); - let measured = adc.read(&mut p.PC0); + let measured = adc.blocking_read(&mut p.PC0); info!("measured: {}", measured); Timer::after_millis(500).await; } diff --git a/examples/stm32h7/src/bin/adc_dma.rs b/examples/stm32h7/src/bin/adc_dma.rs index 6c0240453..0b905d227 100644 --- a/examples/stm32h7/src/bin/adc_dma.rs +++ b/examples/stm32h7/src/bin/adc_dma.rs @@ -56,7 +56,7 @@ async fn main(_spawner: Spawner) { let mut pc0 = p.PC0.degrade_adc(); loop { - adc.read_async( + adc.read( &mut dma, [ (&mut vrefint_channel, SampleTime::CYCLES387_5), diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index 7a89334e0..c557ac6d7 100644 --- a/examples/stm32l4/src/bin/adc.rs +++ b/examples/stm32l4/src/bin/adc.rs @@ -23,7 +23,7 @@ fn main() -> ! { let mut channel = p.PC0; loop { - let v = adc.read(&mut channel); + let v = adc.blocking_read(&mut channel); info!("--> {}", v); } } diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs index 4410448f1..c8252e4e1 100644 --- a/examples/stm32u0/src/bin/adc.rs +++ b/examples/stm32u0/src/bin/adc.rs @@ -23,7 +23,7 @@ fn main() -> ! { let mut channel = p.PC0; loop { - let v = adc.read(&mut channel); + let v = adc.blocking_read(&mut channel); info!("--> {}", v); embassy_time::block_for(Duration::from_millis(200)); } -- cgit From 4ecdf31f9b887c4427ff06b70744ef438502f989 Mon Sep 17 00:00:00 2001 From: Dickless <37750743+Dicklessgreat@users.noreply.github.com> Date: Fri, 5 Jul 2024 02:33:30 +0900 Subject: add SAI example --- examples/stm32h7/Cargo.toml | 1 + examples/stm32h7/src/bin/sai.rs | 189 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 examples/stm32h7/src/bin/sai.rs (limited to 'examples') diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 0584f3916..78343b74f 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -34,6 +34,7 @@ stm32-fmc = "0.3.0" embedded-storage = "0.3.1" static_cell = "2" chrono = { version = "^0.4", default-features = false } +grounded = "0.2.0" # cargo build/run [profile.dev] diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs new file mode 100644 index 000000000..ef8479979 --- /dev/null +++ b/examples/stm32h7/src/bin/sai.rs @@ -0,0 +1,189 @@ +//! Daisy Seed rev.7(with PCM3060 codec) +//! https://electro-smith.com/products/daisy-seed +#![no_std] +#![no_main] + +use defmt_rtt as _; +use embassy_executor::Spawner; +use embassy_stm32 as hal; +use grounded::uninit::GroundedArrayCell; +use hal::rcc::*; +use hal::sai::*; +use hal::time::Hertz; +use panic_probe as _; + +const BLOCK_LENGTH: usize = 32; // 32 samples +const HALF_DMA_BUFFER_LENGTH: usize = BLOCK_LENGTH * 2; // 2 channels +const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks +const SAMPLE_RATE: u32 = 48000; + +//DMA buffer must be in special region. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions +#[link_section = ".sram1_bss"] +static mut TX_BUFFER: GroundedArrayCell = GroundedArrayCell::uninit(); +#[link_section = ".sram1_bss"] +static mut RX_BUFFER: GroundedArrayCell = GroundedArrayCell::uninit(); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = hal::Config::default(); + config.rcc.pll1 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL200, + divp: Some(PllDiv::DIV2), + divq: Some(PllDiv::DIV5), + divr: Some(PllDiv::DIV2), + }); + config.rcc.pll3 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV6, + mul: PllMul::MUL295, + divp: Some(PllDiv::DIV16), + divq: Some(PllDiv::DIV4), + divr: Some(PllDiv::DIV32), + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.mux.sai1sel = hal::pac::rcc::vals::Saisel::PLL3_P; + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.hse = Some(Hse { + freq: Hertz::mhz(16), + mode: HseMode::Oscillator, + }); + + let p = hal::init(config); + + let (sub_block_tx, sub_block_rx) = hal::sai::split_subblocks(p.SAI1); + let kernel_clock = hal::rcc::frequency::().0; + let mclk_div = mclk_div_from_u8((kernel_clock / (SAMPLE_RATE * 256)) as u8); + + let mut tx_config = hal::sai::Config::default(); + tx_config.mode = Mode::Master; + tx_config.tx_rx = TxRx::Transmitter; + tx_config.sync_output = true; + tx_config.clock_strobe = ClockStrobe::Falling; + tx_config.master_clock_divider = mclk_div; + tx_config.stereo_mono = StereoMono::Stereo; + tx_config.data_size = DataSize::Data24; + tx_config.bit_order = BitOrder::MsbFirst; + tx_config.frame_sync_polarity = FrameSyncPolarity::ActiveHigh; + tx_config.frame_sync_offset = FrameSyncOffset::OnFirstBit; + tx_config.frame_length = 64; + tx_config.frame_sync_active_level_length = embassy_stm32::sai::word::U7(32); + tx_config.fifo_threshold = FifoThreshold::Quarter; + + let mut rx_config = tx_config.clone(); + rx_config.mode = Mode::Slave; + rx_config.tx_rx = TxRx::Receiver; + rx_config.sync_input = SyncInput::Internal; + rx_config.clock_strobe = ClockStrobe::Rising; + rx_config.sync_output = false; + + let tx_buffer: &mut [u32] = unsafe { + TX_BUFFER.initialize_all_copied(0); + let (ptr, len) = TX_BUFFER.get_ptr_len(); + core::slice::from_raw_parts_mut(ptr, len) + }; + + let mut sai_transmitter = Sai::new_asynchronous_with_mclk( + sub_block_tx, + p.PE5, + p.PE6, + p.PE4, + p.PE2, + p.DMA1_CH0, + tx_buffer, + tx_config, + ); + + let rx_buffer: &mut [u32] = unsafe { + RX_BUFFER.initialize_all_copied(0); + let (ptr, len) = RX_BUFFER.get_ptr_len(); + core::slice::from_raw_parts_mut(ptr, len) + }; + + let mut sai_receiver = + Sai::new_synchronous(sub_block_rx, p.PE3, p.DMA1_CH1, rx_buffer, rx_config); + + sai_receiver.start(); + sai_transmitter.start(); + + let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; + + loop { + sai_receiver.read(&mut buf).await.unwrap(); + sai_transmitter.write(&buf).await.unwrap(); + } +} + +const fn mclk_div_from_u8(v: u8) -> MasterClockDivider { + match v { + 1 => MasterClockDivider::Div1, + 2 => MasterClockDivider::Div2, + 3 => MasterClockDivider::Div3, + 4 => MasterClockDivider::Div4, + 5 => MasterClockDivider::Div5, + 6 => MasterClockDivider::Div6, + 7 => MasterClockDivider::Div7, + 8 => MasterClockDivider::Div8, + 9 => MasterClockDivider::Div9, + 10 => MasterClockDivider::Div10, + 11 => MasterClockDivider::Div11, + 12 => MasterClockDivider::Div12, + 13 => MasterClockDivider::Div13, + 14 => MasterClockDivider::Div14, + 15 => MasterClockDivider::Div15, + 16 => MasterClockDivider::Div16, + 17 => MasterClockDivider::Div17, + 18 => MasterClockDivider::Div18, + 19 => MasterClockDivider::Div19, + 20 => MasterClockDivider::Div20, + 21 => MasterClockDivider::Div21, + 22 => MasterClockDivider::Div22, + 23 => MasterClockDivider::Div23, + 24 => MasterClockDivider::Div24, + 25 => MasterClockDivider::Div25, + 26 => MasterClockDivider::Div26, + 27 => MasterClockDivider::Div27, + 28 => MasterClockDivider::Div28, + 29 => MasterClockDivider::Div29, + 30 => MasterClockDivider::Div30, + 31 => MasterClockDivider::Div31, + 32 => MasterClockDivider::Div32, + 33 => MasterClockDivider::Div33, + 34 => MasterClockDivider::Div34, + 35 => MasterClockDivider::Div35, + 36 => MasterClockDivider::Div36, + 37 => MasterClockDivider::Div37, + 38 => MasterClockDivider::Div38, + 39 => MasterClockDivider::Div39, + 40 => MasterClockDivider::Div40, + 41 => MasterClockDivider::Div41, + 42 => MasterClockDivider::Div42, + 43 => MasterClockDivider::Div43, + 44 => MasterClockDivider::Div44, + 45 => MasterClockDivider::Div45, + 46 => MasterClockDivider::Div46, + 47 => MasterClockDivider::Div47, + 48 => MasterClockDivider::Div48, + 49 => MasterClockDivider::Div49, + 50 => MasterClockDivider::Div50, + 51 => MasterClockDivider::Div51, + 52 => MasterClockDivider::Div52, + 53 => MasterClockDivider::Div53, + 54 => MasterClockDivider::Div54, + 55 => MasterClockDivider::Div55, + 56 => MasterClockDivider::Div56, + 57 => MasterClockDivider::Div57, + 58 => MasterClockDivider::Div58, + 59 => MasterClockDivider::Div59, + 60 => MasterClockDivider::Div60, + 61 => MasterClockDivider::Div61, + 62 => MasterClockDivider::Div62, + 63 => MasterClockDivider::Div63, + _ => panic!(), + } +} -- cgit From 49546abfec8dd8a9a610c82ec5ef11adba8b5f04 Mon Sep 17 00:00:00 2001 From: Dickless <37750743+Dicklessgreat@users.noreply.github.com> Date: Fri, 5 Jul 2024 03:17:04 +0900 Subject: rustfmt --- examples/stm32h7/src/bin/sai.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs index ef8479979..7f57c4949 100644 --- a/examples/stm32h7/src/bin/sai.rs +++ b/examples/stm32h7/src/bin/sai.rs @@ -105,8 +105,7 @@ async fn main(_spawner: Spawner) { core::slice::from_raw_parts_mut(ptr, len) }; - let mut sai_receiver = - Sai::new_synchronous(sub_block_rx, p.PE3, p.DMA1_CH1, rx_buffer, rx_config); + let mut sai_receiver = Sai::new_synchronous(sub_block_rx, p.PE3, p.DMA1_CH1, rx_buffer, rx_config); sai_receiver.start(); sai_transmitter.start(); -- cgit From 4f649caa8155c5031592d74c2b5e94f400332a88 Mon Sep 17 00:00:00 2001 From: Dickless <37750743+Dicklessgreat@users.noreply.github.com> Date: Fri, 5 Jul 2024 03:29:25 +0900 Subject: cargo +nightly fmt --- examples/stm32h7/src/bin/sai.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs index 7f57c4949..f6735e235 100644 --- a/examples/stm32h7/src/bin/sai.rs +++ b/examples/stm32h7/src/bin/sai.rs @@ -3,14 +3,12 @@ #![no_std] #![no_main] -use defmt_rtt as _; use embassy_executor::Spawner; -use embassy_stm32 as hal; use grounded::uninit::GroundedArrayCell; use hal::rcc::*; use hal::sai::*; use hal::time::Hertz; -use panic_probe as _; +use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _}; const BLOCK_LENGTH: usize = 32; // 32 samples const HALF_DMA_BUFFER_LENGTH: usize = BLOCK_LENGTH * 2; // 2 channels -- cgit From b90eef293ba36bfeaba85ec5f8396021fd282130 Mon Sep 17 00:00:00 2001 From: Dickless <37750743+Dicklessgreat@users.noreply.github.com> Date: Sat, 6 Jul 2024 17:36:22 +0900 Subject: [#2905 #2904] Replaced static raw array with GroundedArrayCell --- examples/stm32h7/src/bin/spi_bdma.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/stm32h7/src/bin/spi_bdma.rs b/examples/stm32h7/src/bin/spi_bdma.rs index b2e941078..65f498506 100644 --- a/examples/stm32h7/src/bin/spi_bdma.rs +++ b/examples/stm32h7/src/bin/spi_bdma.rs @@ -10,18 +10,24 @@ use embassy_executor::Executor; use embassy_stm32::mode::Async; use embassy_stm32::time::mhz; use embassy_stm32::{spi, Config}; +use grounded::uninit::GroundedArrayCell; use heapless::String; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; // Defined in memory.x #[link_section = ".ram_d3"] -static mut RAM_D3: [u8; 64 * 1024] = [0u8; 64 * 1024]; +static mut RAM_D3: GroundedArrayCell = GroundedArrayCell::uninit(); #[embassy_executor::task] async fn main_task(mut spi: spi::Spi<'static, Async>) { - let read_buffer = unsafe { &mut RAM_D3[0..128] }; - let write_buffer = unsafe { &mut RAM_D3[128..256] }; + let (read_buffer, write_buffer) = unsafe { + RAM_D3.initialize_all_copied(0); + ( + RAM_D3.get_subslice_mut_unchecked(0, 128), + RAM_D3.get_subslice_mut_unchecked(128, 128), + ) + }; for n in 0u32.. { let mut write: String<128> = String::new(); -- cgit From 3408e1ddbf7076e119307ae04dce4a9ddf922f34 Mon Sep 17 00:00:00 2001 From: Dickless <37750743+Dicklessgreat@users.noreply.github.com> Date: Sun, 7 Jul 2024 04:39:39 +0900 Subject: Fixed to reserve as much space as it uses. --- examples/stm32h7/src/bin/spi_bdma.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/stm32h7/src/bin/spi_bdma.rs b/examples/stm32h7/src/bin/spi_bdma.rs index 65f498506..43fb6b41c 100644 --- a/examples/stm32h7/src/bin/spi_bdma.rs +++ b/examples/stm32h7/src/bin/spi_bdma.rs @@ -17,7 +17,7 @@ use {defmt_rtt as _, panic_probe as _}; // Defined in memory.x #[link_section = ".ram_d3"] -static mut RAM_D3: GroundedArrayCell = GroundedArrayCell::uninit(); +static mut RAM_D3: GroundedArrayCell = GroundedArrayCell::uninit(); #[embassy_executor::task] async fn main_task(mut spi: spi::Spi<'static, Async>) { -- cgit From 87f66343493a5ae99f0f9b27602b96524111c94a Mon Sep 17 00:00:00 2001 From: kalkyl Date: Mon, 8 Jul 2024 13:58:36 +0200 Subject: Add example for sharing things between tasks --- examples/rp/src/bin/sharing.rs | 140 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 examples/rp/src/bin/sharing.rs (limited to 'examples') diff --git a/examples/rp/src/bin/sharing.rs b/examples/rp/src/bin/sharing.rs new file mode 100644 index 000000000..0761500ef --- /dev/null +++ b/examples/rp/src/bin/sharing.rs @@ -0,0 +1,140 @@ +//! This example shows some common strategies for sharing resources between tasks. + +#![no_std] +#![no_main] + +use core::cell::{Cell, RefCell}; +use core::sync::atomic::{AtomicU32, Ordering}; + +use cortex_m_rt::entry; +use defmt::info; +use embassy_executor::{Executor, InterruptExecutor}; +use embassy_rp::clocks::RoscRng; +use embassy_rp::interrupt::{InterruptExt, Priority}; +use embassy_rp::peripherals::UART0; +use embassy_rp::uart::{self, InterruptHandler, UartTx}; +use embassy_rp::{bind_interrupts, interrupt}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::{blocking_mutex, mutex}; +use embassy_time::{Duration, Ticker}; +use rand::RngCore; +use static_cell::{ConstStaticCell, StaticCell}; +use {defmt_rtt as _, panic_probe as _}; + +type UartMutex = mutex::Mutex>; + +struct MyType { + inner: u32, +} + +static EXECUTOR_HI: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_LOW: StaticCell = StaticCell::new(); + +// Use Atomics for simple values +static ATOMIC: AtomicU32 = AtomicU32::new(0); + +// Use blocking Mutex with Cell/RefCell for sharing non-async things +static MUTEX_BLOCKING: blocking_mutex::Mutex> = + blocking_mutex::Mutex::new(RefCell::new(MyType { inner: 0 })); + +bind_interrupts!(struct Irqs { + UART0_IRQ => InterruptHandler; +}); + +#[interrupt] +unsafe fn SWI_IRQ_0() { + EXECUTOR_HI.on_interrupt() +} + +#[entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + info!("Here we go!"); + + let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, uart::Config::default()); + // Use the async Mutex for sharing async things (built-in interior mutability) + static UART: StaticCell = StaticCell::new(); + let uart = UART.init(mutex::Mutex::new(uart)); + + // High-priority executor: runs in interrupt mode + interrupt::SWI_IRQ_0.set_priority(Priority::P3); + let spawner = EXECUTOR_HI.start(interrupt::SWI_IRQ_0); + spawner.must_spawn(task_a(uart)); + + // Low priority executor: runs in thread mode + let executor = EXECUTOR_LOW.init(Executor::new()); + executor.run(|spawner| { + // No Mutex needed when sharing between tasks running on the same executor + + // Use Cell for Copy-types + static CELL: ConstStaticCell> = ConstStaticCell::new(Cell::new([0; 4])); + let cell = CELL.take(); + + // Use RefCell for &mut access + static REF_CELL: ConstStaticCell> = ConstStaticCell::new(RefCell::new(MyType { inner: 0 })); + let ref_cell = REF_CELL.take(); + + spawner.must_spawn(task_b(uart, cell, ref_cell)); + spawner.must_spawn(task_c(cell, ref_cell)); + }); +} + +#[embassy_executor::task] +async fn task_a(uart: &'static UartMutex) { + let mut ticker = Ticker::every(Duration::from_secs(1)); + loop { + let random = RoscRng.next_u32(); + + { + let mut uart = uart.lock().await; + uart.write(b"task a").await.unwrap(); + // The uart lock is released when it goes out of scope + } + + ATOMIC.store(random, Ordering::Relaxed); + + MUTEX_BLOCKING.lock(|x| x.borrow_mut().inner = random); + + ticker.next().await; + } +} + +#[embassy_executor::task] +async fn task_b(uart: &'static UartMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell) { + let mut ticker = Ticker::every(Duration::from_secs(1)); + loop { + let random = RoscRng.next_u32(); + + uart.lock().await.write(b"task b").await.unwrap(); + + cell.set(random.to_be_bytes()); + + ref_cell.borrow_mut().inner = random; + + ticker.next().await; + } +} + +#[embassy_executor::task] +async fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell) { + let mut ticker = Ticker::every(Duration::from_secs(1)); + loop { + info!("======================="); + + let atomic = ATOMIC.load(Ordering::Relaxed); + info!("atomic: {}", atomic); + + MUTEX_BLOCKING.lock(|x| { + let val = x.borrow().inner; + info!("blocking mutex: {}", val); + }); + + let cell_val = cell.get(); + info!("cell: {:?}", cell_val); + + let ref_cell_val = ref_cell.borrow().inner; + info!("ref_cell: {:?}", ref_cell_val); + + ticker.next().await; + } +} -- cgit From 028ca55f9ca3bfa2e4aa99b16bc0e0e29241fe70 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Mon, 8 Jul 2024 17:16:35 +0200 Subject: Add more docs and cross-links --- examples/rp/src/bin/sharing.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'examples') diff --git a/examples/rp/src/bin/sharing.rs b/examples/rp/src/bin/sharing.rs index 0761500ef..5416e20ce 100644 --- a/examples/rp/src/bin/sharing.rs +++ b/examples/rp/src/bin/sharing.rs @@ -1,4 +1,14 @@ //! This example shows some common strategies for sharing resources between tasks. +//! +//! We demonstrate five different ways of sharing, covering different use cases: +//! - Atomics: This method is used for simple values, such as bool and u8..u32 +//! - Blocking Mutex: This is used for sharing non-async things, using Cell/RefCell for interior mutability. +//! - Async Mutex: This is used for sharing async resources, where you need to hold the lock across await points. +//! The async Mutex has interior mutability built-in, so no RefCell is needed. +//! - Cell: For sharing Copy types between tasks running on the same executor. +//! - RefCell: When you want &mut access to a value shared between tasks running on the same executor. +//! +//! More information: https://embassy.dev/book/#_sharing_peripherals_between_tasks #![no_std] #![no_main] @@ -21,7 +31,7 @@ use rand::RngCore; use static_cell::{ConstStaticCell, StaticCell}; use {defmt_rtt as _, panic_probe as _}; -type UartMutex = mutex::Mutex>; +type UartAsyncMutex = mutex::Mutex>; struct MyType { inner: u32, @@ -53,7 +63,7 @@ fn main() -> ! { let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, uart::Config::default()); // Use the async Mutex for sharing async things (built-in interior mutability) - static UART: StaticCell = StaticCell::new(); + static UART: StaticCell = StaticCell::new(); let uart = UART.init(mutex::Mutex::new(uart)); // High-priority executor: runs in interrupt mode @@ -80,7 +90,7 @@ fn main() -> ! { } #[embassy_executor::task] -async fn task_a(uart: &'static UartMutex) { +async fn task_a(uart: &'static UartAsyncMutex) { let mut ticker = Ticker::every(Duration::from_secs(1)); loop { let random = RoscRng.next_u32(); @@ -100,7 +110,7 @@ async fn task_a(uart: &'static UartMutex) { } #[embassy_executor::task] -async fn task_b(uart: &'static UartMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell) { +async fn task_b(uart: &'static UartAsyncMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell) { let mut ticker = Ticker::every(Duration::from_secs(1)); loop { let random = RoscRng.next_u32(); @@ -121,8 +131,8 @@ async fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell loop { info!("======================="); - let atomic = ATOMIC.load(Ordering::Relaxed); - info!("atomic: {}", atomic); + let atomic_val = ATOMIC.load(Ordering::Relaxed); + info!("atomic: {}", atomic_val); MUTEX_BLOCKING.lock(|x| { let val = x.borrow().inner; -- cgit From 376f65e1d388a7eff83e0c12deeb4d556daefea5 Mon Sep 17 00:00:00 2001 From: rafael Date: Mon, 8 Jul 2024 22:07:38 +0200 Subject: add assign_resources example --- examples/rp/Cargo.toml | 3 ++ examples/rp/src/bin/assign_resources.rs | 82 +++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 examples/rp/src/bin/assign_resources.rs (limited to 'examples') diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 9bd403f02..d06ab5e5a 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -29,6 +29,9 @@ reqwless = { version = "0.12.0", features = ["defmt",]} serde = { version = "1.0.203", default-features = false, features = ["derive"] } serde-json-core = "0.5.1" +# for assign resources example +assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" } + #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m = { version = "0.7.6", features = ["inline-asm"] } cortex-m-rt = "0.7.0" diff --git a/examples/rp/src/bin/assign_resources.rs b/examples/rp/src/bin/assign_resources.rs new file mode 100644 index 000000000..38e730498 --- /dev/null +++ b/examples/rp/src/bin/assign_resources.rs @@ -0,0 +1,82 @@ +//! This example demonstrates how to assign resources to multiple tasks by splitting up the peripherals. +//! It is not about sharing the same resources between tasks, see sharing.rs for that or head to https://embassy.dev/book/#_sharing_peripherals_between_tasks) +//! Of course splitting up resources and sharing resources can be combined, yet this example is only about splitting up resources. +//! +//! There are basically two ways we demonstrate here: +//! 1) Assigning resources to a task by passing parts of the peripherals +//! 2) Assigning resources to a task by passing a struct with the split up peripherals, using the assign-resources macro +//! +//! using four LEDs on Pins 10, 11, 20 and 21 + +#![no_std] +#![no_main] + +use assign_resources::assign_resources; +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::{ + gpio, + peripherals::{self, PIN_20, PIN_21}, +}; +use embassy_time::Timer; +use gpio::{Level, Output}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + // initialize the peripherals + let p = embassy_rp::init(Default::default()); + + // 1) Assigning a resource to a task by passing parts of the peripherals. + spawner + .spawn(double_blinky_manually_assigned(spawner, p.PIN_20, p.PIN_21)) + .unwrap(); + + // 2) Using the assign-resources macro to assign resources to a task. + // we perform the split, see further below for the definition of the resources struct + let r = split_resources!(p); + // and then we can use them + spawner.spawn(double_blinky_macro_assigned(spawner, r.leds)).unwrap(); +} + +// 1) Assigning a resource to a task by passing parts of the peripherals. +#[embassy_executor::task] +async fn double_blinky_manually_assigned(_spawner: Spawner, pin_20: PIN_20, pin_21: PIN_21) { + let mut led_20 = Output::new(pin_20, Level::Low); + let mut led_21 = Output::new(pin_21, Level::High); + + loop { + info!("toggling leds"); + led_20.toggle(); + led_21.toggle(); + Timer::after_secs(1).await; + } +} + +// 2) Using the assign-resources macro to assign resources to a task. +// first we define the resources we want to assign to the task using the assign_resources! macro +// basically this will split up the peripherals struct into smaller structs, that we define here +// naming is up to you, make sure your future self understands what you did here +assign_resources! { + leds: Leds{ + led_10: PIN_10, + led_11: PIN_11, + } + // add more resources to more structs if needed, for example defining one struct for each task +} +// this could be done in another file and imported here, but for the sake of simplicity we do it here +// see https://github.com/adamgreig/assign-resources for more information + +// 2) Using the split resources in a task +#[embassy_executor::task] +async fn double_blinky_macro_assigned(_spawner: Spawner, r: Leds) { + let mut led_10 = Output::new(r.led_10, Level::Low); + let mut led_11 = Output::new(r.led_11, Level::High); + + loop { + info!("toggling leds"); + led_10.toggle(); + led_11.toggle(); + Timer::after_secs(1).await; + } +} -- cgit From 91e6aa31660d89de0b8a7f382d807e66907aceaa Mon Sep 17 00:00:00 2001 From: rafael Date: Mon, 8 Jul 2024 22:20:23 +0200 Subject: rustfmt --- examples/rp/src/bin/assign_resources.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/rp/src/bin/assign_resources.rs b/examples/rp/src/bin/assign_resources.rs index 38e730498..37b2c20bc 100644 --- a/examples/rp/src/bin/assign_resources.rs +++ b/examples/rp/src/bin/assign_resources.rs @@ -5,7 +5,7 @@ //! There are basically two ways we demonstrate here: //! 1) Assigning resources to a task by passing parts of the peripherals //! 2) Assigning resources to a task by passing a struct with the split up peripherals, using the assign-resources macro -//! +//! //! using four LEDs on Pins 10, 11, 20 and 21 #![no_std] -- cgit From 52c43f024939956d3b8d45cada70aaaa35dfbebd Mon Sep 17 00:00:00 2001 From: rafael Date: Mon, 8 Jul 2024 22:26:32 +0200 Subject: rustfmt --- examples/rp/src/bin/assign_resources.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/rp/src/bin/assign_resources.rs b/examples/rp/src/bin/assign_resources.rs index 37b2c20bc..ff6eff4a2 100644 --- a/examples/rp/src/bin/assign_resources.rs +++ b/examples/rp/src/bin/assign_resources.rs @@ -14,12 +14,9 @@ use assign_resources::assign_resources; use defmt::*; use embassy_executor::Spawner; -use embassy_rp::{ - gpio, - peripherals::{self, PIN_20, PIN_21}, -}; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::peripherals::{self, PIN_20, PIN_21}; use embassy_time::Timer; -use gpio::{Level, Output}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] -- cgit