Merge pull request #3458 from williams-one/fix-ltdc-settings-for-stm32u5
Fix LTDC registry settings for STM32U5
This commit is contained in:
		
						commit
						10c9fbcc99
					
				| @ -395,7 +395,10 @@ impl<'d, T: Instance> Ltdc<'d, T> { | |||||||
|         // framebuffer pitch and line length
 |         // framebuffer pitch and line length
 | ||||||
|         layer.cfblr().modify(|w| { |         layer.cfblr().modify(|w| { | ||||||
|             w.set_cfbp(width * bytes_per_pixel); |             w.set_cfbp(width * bytes_per_pixel); | ||||||
|  |             #[cfg(not(stm32u5))] | ||||||
|             w.set_cfbll(width * bytes_per_pixel + 7); |             w.set_cfbll(width * bytes_per_pixel + 7); | ||||||
|  |             #[cfg(stm32u5)] | ||||||
|  |             w.set_cfbll(width * bytes_per_pixel + 3); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         // framebuffer line number
 |         // framebuffer line number
 | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||||||
| # replace STM32U585AIIx with your chip as listed in `probe-rs chip list` | # replace STM32U5G9ZJTxQ with your chip as listed in `probe-rs chip list` | ||||||
| runner = "probe-rs run --chip STM32U585AIIx" | runner = "probe-rs run --chip STM32U5G9ZJTxQ" | ||||||
| 
 | 
 | ||||||
| [build] | [build] | ||||||
| target = "thumbv8m.main-none-eabihf" | target = "thumbv8m.main-none-eabihf" | ||||||
|  | |||||||
| @ -5,8 +5,8 @@ version = "0.1.0" | |||||||
| license = "MIT OR Apache-2.0" | license = "MIT OR Apache-2.0" | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| # Change stm32u585ai to your chip name, if necessary. | # Change stm32u5g9zj to your chip name, if necessary. | ||||||
| embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ]  } | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u5g9zj", "time-driver-any", "memory-x" ]  } | ||||||
| embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | ||||||
| embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | ||||||
| embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | ||||||
| @ -21,6 +21,8 @@ cortex-m-rt = "0.7.0" | |||||||
| embedded-hal = "0.2.6" | embedded-hal = "0.2.6" | ||||||
| panic-probe = { version = "0.3", features = ["print-defmt"] } | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||||||
| heapless = { version = "0.8", default-features = false } | heapless = { version = "0.8", default-features = false } | ||||||
|  | embedded-graphics = { version = "0.8.1" } | ||||||
|  | tinybmp = { version = "0.6.0" } | ||||||
| 
 | 
 | ||||||
| micromath = "2.0.0" | micromath = "2.0.0" | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								examples/stm32u5/src/bin/ferris.bmp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/stm32u5/src/bin/ferris.bmp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 6.6 KiB | 
| @ -13,7 +13,7 @@ const WHOAMI: u8 = 0x0F; | |||||||
| #[embassy_executor::main] | #[embassy_executor::main] | ||||||
| async fn main(_spawner: Spawner) { | async fn main(_spawner: Spawner) { | ||||||
|     let p = embassy_stm32::init(Default::default()); |     let p = embassy_stm32::init(Default::default()); | ||||||
|     let mut i2c = I2c::new_blocking(p.I2C2, p.PH4, p.PH5, Hertz(100_000), Default::default()); |     let mut i2c = I2c::new_blocking(p.I2C2, p.PF1, p.PF0, Hertz(100_000), Default::default()); | ||||||
| 
 | 
 | ||||||
|     let mut data = [0u8; 1]; |     let mut data = [0u8; 1]; | ||||||
|     unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data)); |     unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data)); | ||||||
|  | |||||||
							
								
								
									
										461
									
								
								examples/stm32u5/src/bin/ltdc.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										461
									
								
								examples/stm32u5/src/bin/ltdc.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,461 @@ | |||||||
|  | #![no_std] | ||||||
|  | #![no_main] | ||||||
|  | #![macro_use] | ||||||
|  | #![allow(static_mut_refs)] | ||||||
|  | 
 | ||||||
|  | /// This example was derived from examples\stm32h735\src\bin\ltdc.rs
 | ||||||
|  | /// It demonstrates the LTDC lcd display peripheral and was tested on an STM32U5G9J-DK2 demo board (embassy-stm32 feature "stm32u5g9zj" and probe-rs chip "STM32U5G9ZJTxQ")
 | ||||||
|  | ///
 | ||||||
|  | use bouncy_box::BouncyBox; | ||||||
|  | use defmt::{info, unwrap}; | ||||||
|  | use embassy_executor::Spawner; | ||||||
|  | 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; | ||||||
|  | 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 _}; | ||||||
|  | 
 | ||||||
|  | const DISPLAY_WIDTH: usize = 800; | ||||||
|  | const DISPLAY_HEIGHT: usize = 480; | ||||||
|  | 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<peripherals::LTDC>; | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const NUM_COLORS: usize = 256; | ||||||
|  | 
 | ||||||
|  | #[embassy_executor::main] | ||||||
|  | async fn main(spawner: Spawner) { | ||||||
|  |     let p = rcc_setup::stm32u5g9zj_init(); | ||||||
|  | 
 | ||||||
|  |     // enable ICACHE
 | ||||||
|  |     embassy_stm32::pac::ICACHE.cr().write(|w| { | ||||||
|  |         w.set_en(true); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // blink the led on another task
 | ||||||
|  |     let led = Output::new(p.PD2, Level::High, Speed::Low); | ||||||
|  |     unwrap!(spawner.spawn(led_task(led))); | ||||||
|  | 
 | ||||||
|  |     // numbers from STM32U5G9J-DK2.ioc
 | ||||||
|  |     const RK050HR18H_HSYNC: u16 = 5; // Horizontal synchronization
 | ||||||
|  |     const RK050HR18H_HBP: u16 = 8; // Horizontal back porch
 | ||||||
|  |     const RK050HR18H_HFP: u16 = 8; // Horizontal front porch
 | ||||||
|  |     const RK050HR18H_VSYNC: u16 = 5; // Vertical synchronization
 | ||||||
|  |     const RK050HR18H_VBP: u16 = 8; // Vertical back porch
 | ||||||
|  |     const RK050HR18H_VFP: u16 = 8; // Vertical front porch
 | ||||||
|  | 
 | ||||||
|  |     // NOTE: all polarities have to be reversed with respect to the STM32U5G9J-DK2 CubeMX parametrization
 | ||||||
|  |     let ltdc_config = LtdcConfiguration { | ||||||
|  |         active_width: DISPLAY_WIDTH as _, | ||||||
|  |         active_height: DISPLAY_HEIGHT as _, | ||||||
|  |         h_back_porch: RK050HR18H_HBP, | ||||||
|  |         h_front_porch: RK050HR18H_HFP, | ||||||
|  |         v_back_porch: RK050HR18H_VBP, | ||||||
|  |         v_front_porch: RK050HR18H_VFP, | ||||||
|  |         h_sync: RK050HR18H_HSYNC, | ||||||
|  |         v_sync: RK050HR18H_VSYNC, | ||||||
|  |         h_sync_polarity: PolarityActive::ActiveHigh, | ||||||
|  |         v_sync_polarity: PolarityActive::ActiveHigh, | ||||||
|  |         data_enable_polarity: PolarityActive::ActiveHigh, | ||||||
|  |         pixel_clock_polarity: PolarityEdge::RisingEdge, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     info!("init ltdc"); | ||||||
|  |     let mut ltdc_de = Output::new(p.PD6, Level::Low, Speed::High); | ||||||
|  |     let mut ltdc_disp_ctrl = Output::new(p.PE4, Level::Low, Speed::High); | ||||||
|  |     let mut ltdc_bl_ctrl = Output::new(p.PE6, Level::Low, Speed::High); | ||||||
|  |     let mut ltdc = Ltdc::new_with_pins( | ||||||
|  |         p.LTDC, // PERIPHERAL
 | ||||||
|  |         Irqs,   // IRQS
 | ||||||
|  |         p.PD3,  // CLK
 | ||||||
|  |         p.PE0,  // HSYNC
 | ||||||
|  |         p.PD13, // VSYNC
 | ||||||
|  |         p.PB9,  // B0
 | ||||||
|  |         p.PB2,  // B1
 | ||||||
|  |         p.PD14, // B2
 | ||||||
|  |         p.PD15, // B3
 | ||||||
|  |         p.PD0,  // B4
 | ||||||
|  |         p.PD1,  // B5
 | ||||||
|  |         p.PE7,  // B6
 | ||||||
|  |         p.PE8,  // B7
 | ||||||
|  |         p.PC8,  // G0
 | ||||||
|  |         p.PC9,  // G1
 | ||||||
|  |         p.PE9,  // G2
 | ||||||
|  |         p.PE10, // G3
 | ||||||
|  |         p.PE11, // G4
 | ||||||
|  |         p.PE12, // G5
 | ||||||
|  |         p.PE13, // G6
 | ||||||
|  |         p.PE14, // G7
 | ||||||
|  |         p.PC6,  // R0
 | ||||||
|  |         p.PC7,  // R1
 | ||||||
|  |         p.PE15, // R2
 | ||||||
|  |         p.PD8,  // R3
 | ||||||
|  |         p.PD9,  // R4
 | ||||||
|  |         p.PD10, // R5
 | ||||||
|  |         p.PD11, // R6
 | ||||||
|  |         p.PD12, // R7
 | ||||||
|  |     ); | ||||||
|  |     ltdc.init(<dc_config); | ||||||
|  |     ltdc_de.set_low(); | ||||||
|  |     ltdc_bl_ctrl.set_high(); | ||||||
|  |     ltdc_disp_ctrl.set_high(); | ||||||
|  | 
 | ||||||
|  |     // 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<Rgb888> = 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<Rgb888>) -> FnvIndexMap<u32, u8, NUM_COLORS> { | ||||||
|  |     let mut color_map: FnvIndexMap<u32, u8, NUM_COLORS> = 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<u32, u8, NUM_COLORS>) -> [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<u32, u8, NUM_COLORS>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl DoubleBuffer { | ||||||
|  |     pub fn new( | ||||||
|  |         buf0: &'static mut [TargetPixelType], | ||||||
|  |         buf1: &'static mut [TargetPixelType], | ||||||
|  |         layer_config: LtdcLayerConfig, | ||||||
|  |         color_map: FnvIndexMap<u32, u8, NUM_COLORS>, | ||||||
|  |     ) -> Self { | ||||||
|  |         Self { | ||||||
|  |             buf0, | ||||||
|  |             buf1, | ||||||
|  |             is_buf0: true, | ||||||
|  |             layer_config, | ||||||
|  |             color_map, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn current(&mut self) -> (&FnvIndexMap<u32, u8, NUM_COLORS>, &mut [TargetPixelType]) { | ||||||
|  |         if self.is_buf0 { | ||||||
|  |             (&self.color_map, self.buf0) | ||||||
|  |         } else { | ||||||
|  |             (&self.color_map, self.buf1) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub async fn swap<T: ltdc::Instance>(&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<I>(&mut self, pixels: I) -> Result<(), Self::Error> | ||||||
|  |     where | ||||||
|  |         I: IntoIterator<Item = Pixel<Self::Color>>, | ||||||
|  |     { | ||||||
|  |         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::time::Hertz; | ||||||
|  |     use embassy_stm32::{rcc, Config, Peripherals}; | ||||||
|  | 
 | ||||||
|  |     /// Sets up clocks for the stm32u5g9zj mcu
 | ||||||
|  |     /// change this if you plan to use a different microcontroller
 | ||||||
|  |     pub fn stm32u5g9zj_init() -> Peripherals { | ||||||
|  |         // setup power and clocks for an STM32U5G9J-DK2 run from an external 16 Mhz external oscillator
 | ||||||
|  |         let mut config = Config::default(); | ||||||
|  |         config.rcc.hse = Some(rcc::Hse { | ||||||
|  |             freq: Hertz(16_000_000), | ||||||
|  |             mode: rcc::HseMode::Oscillator, | ||||||
|  |         }); | ||||||
|  |         config.rcc.pll1 = Some(rcc::Pll { | ||||||
|  |             source: rcc::PllSource::HSE, | ||||||
|  |             prediv: rcc::PllPreDiv::DIV1, | ||||||
|  |             mul: rcc::PllMul::MUL10, | ||||||
|  |             divp: None, | ||||||
|  |             divq: None, | ||||||
|  |             divr: Some(rcc::PllDiv::DIV1), | ||||||
|  |         }); | ||||||
|  |         config.rcc.sys = rcc::Sysclk::PLL1_R; // 160 Mhz
 | ||||||
|  |         config.rcc.pll3 = Some(rcc::Pll { | ||||||
|  |             source: rcc::PllSource::HSE, | ||||||
|  |             prediv: rcc::PllPreDiv::DIV4, // PLL_M
 | ||||||
|  |             mul: rcc::PllMul::MUL125,     // PLL_N
 | ||||||
|  |             divp: None, | ||||||
|  |             divq: None, | ||||||
|  |             divr: Some(rcc::PllDiv::DIV20), | ||||||
|  |         }); | ||||||
|  |         config.rcc.mux.ltdcsel = rcc::mux::Ltdcsel::PLL3_R; // 25 MHz
 | ||||||
|  |         embassy_stm32::init(config) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | mod bouncy_box { | ||||||
|  |     use embedded_graphics::geometry::Point; | ||||||
|  |     use embedded_graphics::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 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -13,7 +13,7 @@ use embassy_usb::Builder; | |||||||
| use panic_probe as _; | use panic_probe as _; | ||||||
| 
 | 
 | ||||||
| bind_interrupts!(struct Irqs { | bind_interrupts!(struct Irqs { | ||||||
|     OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; |     OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>; | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| #[embassy_executor::main] | #[embassy_executor::main] | ||||||
| @ -48,7 +48,7 @@ async fn main(_spawner: Spawner) { | |||||||
|     // to enable vbus_detection to comply with the USB spec. If you enable it, the board
 |     // to enable vbus_detection to comply with the USB spec. If you enable it, the board
 | ||||||
|     // has to support it or USB won't work at all. See docs on `vbus_detection` for details.
 |     // has to support it or USB won't work at all. See docs on `vbus_detection` for details.
 | ||||||
|     config.vbus_detection = false; |     config.vbus_detection = false; | ||||||
|     let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); |     let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); | ||||||
| 
 | 
 | ||||||
|     // Create embassy-usb Config
 |     // Create embassy-usb Config
 | ||||||
|     let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); |     let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user