aboutsummaryrefslogtreecommitdiff
path: root/examples/stm32u5/src
diff options
context:
space:
mode:
authorOlof <[email protected]>2024-12-18 01:48:25 +0100
committerGitHub <[email protected]>2024-12-18 01:48:25 +0100
commit7cf96e4730964d085015320648c870a05fbaf431 (patch)
tree04072529b62082cb66443377b589fe08169f83be /examples/stm32u5/src
parent8678911028a591d72fd1d8418407b5885ed4c417 (diff)
parent341036a8b865609767fbf9015b482ea70ed4f23f (diff)
Merge branch 'embassy-rs:main' into u5_adc
Diffstat (limited to 'examples/stm32u5/src')
-rw-r--r--examples/stm32u5/src/bin/ferris.bmpbin0 -> 6794 bytes
-rw-r--r--examples/stm32u5/src/bin/i2c.rs2
-rw-r--r--examples/stm32u5/src/bin/ltdc.rs461
-rw-r--r--examples/stm32u5/src/bin/tsc.rs75
-rw-r--r--examples/stm32u5/src/bin/usb_hs_serial.rs129
-rw-r--r--examples/stm32u5/src/bin/usb_serial.rs4
6 files changed, 625 insertions, 46 deletions
diff --git a/examples/stm32u5/src/bin/ferris.bmp b/examples/stm32u5/src/bin/ferris.bmp
new file mode 100644
index 000000000..7a222ab84
--- /dev/null
+++ b/examples/stm32u5/src/bin/ferris.bmp
Binary files differ
diff --git a/examples/stm32u5/src/bin/i2c.rs b/examples/stm32u5/src/bin/i2c.rs
index 19a78eac9..d5f5d6f60 100644
--- a/examples/stm32u5/src/bin/i2c.rs
+++ b/examples/stm32u5/src/bin/i2c.rs
@@ -13,7 +13,7 @@ const WHOAMI: u8 = 0x0F;
13#[embassy_executor::main] 13#[embassy_executor::main]
14async fn main(_spawner: Spawner) { 14async fn main(_spawner: Spawner) {
15 let p = embassy_stm32::init(Default::default()); 15 let p = embassy_stm32::init(Default::default());
16 let mut i2c = I2c::new_blocking(p.I2C2, p.PH4, p.PH5, Hertz(100_000), Default::default()); 16 let mut i2c = I2c::new_blocking(p.I2C2, p.PF1, p.PF0, Hertz(100_000), Default::default());
17 17
18 let mut data = [0u8; 1]; 18 let mut data = [0u8; 1];
19 unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data)); 19 unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data));
diff --git a/examples/stm32u5/src/bin/ltdc.rs b/examples/stm32u5/src/bin/ltdc.rs
new file mode 100644
index 000000000..bd59a9148
--- /dev/null
+++ b/examples/stm32u5/src/bin/ltdc.rs
@@ -0,0 +1,461 @@
1#![no_std]
2#![no_main]
3#![macro_use]
4#![allow(static_mut_refs)]
5
6/// This example was derived from examples\stm32h735\src\bin\ltdc.rs
7/// 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")
8///
9use bouncy_box::BouncyBox;
10use defmt::{info, unwrap};
11use embassy_executor::Spawner;
12use embassy_stm32::gpio::{Level, Output, Speed};
13use embassy_stm32::ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge};
14use embassy_stm32::{bind_interrupts, peripherals};
15use embassy_time::{Duration, Timer};
16use embedded_graphics::draw_target::DrawTarget;
17use embedded_graphics::geometry::{OriginDimensions, Point, Size};
18use embedded_graphics::image::Image;
19use embedded_graphics::pixelcolor::raw::RawU24;
20use embedded_graphics::pixelcolor::Rgb888;
21use embedded_graphics::prelude::*;
22use embedded_graphics::primitives::Rectangle;
23use embedded_graphics::Pixel;
24use heapless::{Entry, FnvIndexMap};
25use tinybmp::Bmp;
26use {defmt_rtt as _, panic_probe as _};
27
28const DISPLAY_WIDTH: usize = 800;
29const DISPLAY_HEIGHT: usize = 480;
30const MY_TASK_POOL_SIZE: usize = 2;
31
32// the following two display buffers consume 261120 bytes that just about fits into axis ram found on the mcu
33pub static mut FB1: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT];
34pub static mut FB2: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT];
35
36bind_interrupts!(struct Irqs {
37 LTDC => ltdc::InterruptHandler<peripherals::LTDC>;
38});
39
40const NUM_COLORS: usize = 256;
41
42#[embassy_executor::main]
43async fn main(spawner: Spawner) {
44 let p = rcc_setup::stm32u5g9zj_init();
45
46 // enable ICACHE
47 embassy_stm32::pac::ICACHE.cr().write(|w| {
48 w.set_en(true);
49 });
50
51 // blink the led on another task
52 let led = Output::new(p.PD2, Level::High, Speed::Low);
53 unwrap!(spawner.spawn(led_task(led)));
54
55 // numbers from STM32U5G9J-DK2.ioc
56 const RK050HR18H_HSYNC: u16 = 5; // Horizontal synchronization
57 const RK050HR18H_HBP: u16 = 8; // Horizontal back porch
58 const RK050HR18H_HFP: u16 = 8; // Horizontal front porch
59 const RK050HR18H_VSYNC: u16 = 5; // Vertical synchronization
60 const RK050HR18H_VBP: u16 = 8; // Vertical back porch
61 const RK050HR18H_VFP: u16 = 8; // Vertical front porch
62
63 // NOTE: all polarities have to be reversed with respect to the STM32U5G9J-DK2 CubeMX parametrization
64 let ltdc_config = LtdcConfiguration {
65 active_width: DISPLAY_WIDTH as _,
66 active_height: DISPLAY_HEIGHT as _,
67 h_back_porch: RK050HR18H_HBP,
68 h_front_porch: RK050HR18H_HFP,
69 v_back_porch: RK050HR18H_VBP,
70 v_front_porch: RK050HR18H_VFP,
71 h_sync: RK050HR18H_HSYNC,
72 v_sync: RK050HR18H_VSYNC,
73 h_sync_polarity: PolarityActive::ActiveHigh,
74 v_sync_polarity: PolarityActive::ActiveHigh,
75 data_enable_polarity: PolarityActive::ActiveHigh,
76 pixel_clock_polarity: PolarityEdge::RisingEdge,
77 };
78
79 info!("init ltdc");
80 let mut ltdc_de = Output::new(p.PD6, Level::Low, Speed::High);
81 let mut ltdc_disp_ctrl = Output::new(p.PE4, Level::Low, Speed::High);
82 let mut ltdc_bl_ctrl = Output::new(p.PE6, Level::Low, Speed::High);
83 let mut ltdc = Ltdc::new_with_pins(
84 p.LTDC, // PERIPHERAL
85 Irqs, // IRQS
86 p.PD3, // CLK
87 p.PE0, // HSYNC
88 p.PD13, // VSYNC
89 p.PB9, // B0
90 p.PB2, // B1
91 p.PD14, // B2
92 p.PD15, // B3
93 p.PD0, // B4
94 p.PD1, // B5
95 p.PE7, // B6
96 p.PE8, // B7
97 p.PC8, // G0
98 p.PC9, // G1
99 p.PE9, // G2
100 p.PE10, // G3
101 p.PE11, // G4
102 p.PE12, // G5
103 p.PE13, // G6
104 p.PE14, // G7
105 p.PC6, // R0
106 p.PC7, // R1
107 p.PE15, // R2
108 p.PD8, // R3
109 p.PD9, // R4
110 p.PD10, // R5
111 p.PD11, // R6
112 p.PD12, // R7
113 );
114 ltdc.init(&ltdc_config);
115 ltdc_de.set_low();
116 ltdc_bl_ctrl.set_high();
117 ltdc_disp_ctrl.set_high();
118
119 // we only need to draw on one layer for this example (not to be confused with the double buffer)
120 info!("enable bottom layer");
121 let layer_config = LtdcLayerConfig {
122 pixel_format: ltdc::PixelFormat::L8, // 1 byte per pixel
123 layer: LtdcLayer::Layer1,
124 window_x0: 0,
125 window_x1: DISPLAY_WIDTH as _,
126 window_y0: 0,
127 window_y1: DISPLAY_HEIGHT as _,
128 };
129
130 let ferris_bmp: Bmp<Rgb888> = Bmp::from_slice(include_bytes!("./ferris.bmp")).unwrap();
131 let color_map = build_color_lookup_map(&ferris_bmp);
132 let clut = build_clut(&color_map);
133
134 // enable the bottom layer with a 256 color lookup table
135 ltdc.init_layer(&layer_config, Some(&clut));
136
137 // Safety: the DoubleBuffer controls access to the statically allocated frame buffers
138 // and it is the only thing that mutates their content
139 let mut double_buffer = DoubleBuffer::new(
140 unsafe { FB1.as_mut() },
141 unsafe { FB2.as_mut() },
142 layer_config,
143 color_map,
144 );
145
146 // this allows us to perform some simple animation for every frame
147 let mut bouncy_box = BouncyBox::new(
148 ferris_bmp.bounding_box(),
149 Rectangle::new(Point::zero(), Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)),
150 2,
151 );
152
153 loop {
154 // cpu intensive drawing to the buffer that is NOT currently being copied to the LCD screen
155 double_buffer.clear();
156 let position = bouncy_box.next_point();
157 let ferris = Image::new(&ferris_bmp, position);
158 unwrap!(ferris.draw(&mut double_buffer));
159
160 // perform async dma data transfer to the lcd screen
161 unwrap!(double_buffer.swap(&mut ltdc).await);
162 }
163}
164
165/// builds the color look-up table from all unique colors found in the bitmap. This should be a 256 color indexed bitmap to work.
166fn build_color_lookup_map(bmp: &Bmp<Rgb888>) -> FnvIndexMap<u32, u8, NUM_COLORS> {
167 let mut color_map: FnvIndexMap<u32, u8, NUM_COLORS> = heapless::FnvIndexMap::new();
168 let mut counter: u8 = 0;
169
170 // add black to position 0
171 color_map.insert(Rgb888::new(0, 0, 0).into_storage(), counter).unwrap();
172 counter += 1;
173
174 for Pixel(_point, color) in bmp.pixels() {
175 let raw = color.into_storage();
176 if let Entry::Vacant(v) = color_map.entry(raw) {
177 v.insert(counter).expect("more than 256 colors detected");
178 counter += 1;
179 }
180 }
181 color_map
182}
183
184/// builds the color look-up table from the color map provided
185fn build_clut(color_map: &FnvIndexMap<u32, u8, NUM_COLORS>) -> [ltdc::RgbColor; NUM_COLORS] {
186 let mut clut = [ltdc::RgbColor::default(); NUM_COLORS];
187 for (color, index) in color_map.iter() {
188 let color = Rgb888::from(RawU24::new(*color));
189 clut[*index as usize] = ltdc::RgbColor {
190 red: color.r(),
191 green: color.g(),
192 blue: color.b(),
193 };
194 }
195
196 clut
197}
198
199#[embassy_executor::task(pool_size = MY_TASK_POOL_SIZE)]
200async fn led_task(mut led: Output<'static>) {
201 let mut counter = 0;
202 loop {
203 info!("blink: {}", counter);
204 counter += 1;
205
206 // on
207 led.set_low();
208 Timer::after(Duration::from_millis(50)).await;
209
210 // off
211 led.set_high();
212 Timer::after(Duration::from_millis(450)).await;
213 }
214}
215
216pub type TargetPixelType = u8;
217
218// A simple double buffer
219pub struct DoubleBuffer {
220 buf0: &'static mut [TargetPixelType],
221 buf1: &'static mut [TargetPixelType],
222 is_buf0: bool,
223 layer_config: LtdcLayerConfig,
224 color_map: FnvIndexMap<u32, u8, NUM_COLORS>,
225}
226
227impl DoubleBuffer {
228 pub fn new(
229 buf0: &'static mut [TargetPixelType],
230 buf1: &'static mut [TargetPixelType],
231 layer_config: LtdcLayerConfig,
232 color_map: FnvIndexMap<u32, u8, NUM_COLORS>,
233 ) -> Self {
234 Self {
235 buf0,
236 buf1,
237 is_buf0: true,
238 layer_config,
239 color_map,
240 }
241 }
242
243 pub fn current(&mut self) -> (&FnvIndexMap<u32, u8, NUM_COLORS>, &mut [TargetPixelType]) {
244 if self.is_buf0 {
245 (&self.color_map, self.buf0)
246 } else {
247 (&self.color_map, self.buf1)
248 }
249 }
250
251 pub async fn swap<T: ltdc::Instance>(&mut self, ltdc: &mut Ltdc<'_, T>) -> Result<(), ltdc::Error> {
252 let (_, buf) = self.current();
253 let frame_buffer = buf.as_ptr();
254 self.is_buf0 = !self.is_buf0;
255 ltdc.set_buffer(self.layer_config.layer, frame_buffer as *const _).await
256 }
257
258 /// Clears the buffer
259 pub fn clear(&mut self) {
260 let (color_map, buf) = self.current();
261 let black = Rgb888::new(0, 0, 0).into_storage();
262 let color_index = color_map.get(&black).expect("no black found in the color map");
263
264 for a in buf.iter_mut() {
265 *a = *color_index; // solid black
266 }
267 }
268}
269
270// Implement DrawTarget for
271impl DrawTarget for DoubleBuffer {
272 type Color = Rgb888;
273 type Error = ();
274
275 /// Draw a pixel
276 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
277 where
278 I: IntoIterator<Item = Pixel<Self::Color>>,
279 {
280 let size = self.size();
281 let width = size.width as i32;
282 let height = size.height as i32;
283 let (color_map, buf) = self.current();
284
285 for pixel in pixels {
286 let Pixel(point, color) = pixel;
287
288 if point.x >= 0 && point.y >= 0 && point.x < width && point.y < height {
289 let index = point.y * width + point.x;
290 let raw_color = color.into_storage();
291
292 match color_map.get(&raw_color) {
293 Some(x) => {
294 buf[index as usize] = *x;
295 }
296 None => panic!("color not found in color map: {}", raw_color),
297 };
298 } else {
299 // Ignore invalid points
300 }
301 }
302
303 Ok(())
304 }
305}
306
307impl OriginDimensions for DoubleBuffer {
308 /// Return the size of the display
309 fn size(&self) -> Size {
310 Size::new(
311 (self.layer_config.window_x1 - self.layer_config.window_x0) as _,
312 (self.layer_config.window_y1 - self.layer_config.window_y0) as _,
313 )
314 }
315}
316
317mod rcc_setup {
318
319 use embassy_stm32::time::Hertz;
320 use embassy_stm32::{rcc, Config, Peripherals};
321
322 /// Sets up clocks for the stm32u5g9zj mcu
323 /// change this if you plan to use a different microcontroller
324 pub fn stm32u5g9zj_init() -> Peripherals {
325 // setup power and clocks for an STM32U5G9J-DK2 run from an external 16 Mhz external oscillator
326 let mut config = Config::default();
327 config.rcc.hse = Some(rcc::Hse {
328 freq: Hertz(16_000_000),
329 mode: rcc::HseMode::Oscillator,
330 });
331 config.rcc.pll1 = Some(rcc::Pll {
332 source: rcc::PllSource::HSE,
333 prediv: rcc::PllPreDiv::DIV1,
334 mul: rcc::PllMul::MUL10,
335 divp: None,
336 divq: None,
337 divr: Some(rcc::PllDiv::DIV1),
338 });
339 config.rcc.sys = rcc::Sysclk::PLL1_R; // 160 Mhz
340 config.rcc.pll3 = Some(rcc::Pll {
341 source: rcc::PllSource::HSE,
342 prediv: rcc::PllPreDiv::DIV4, // PLL_M
343 mul: rcc::PllMul::MUL125, // PLL_N
344 divp: None,
345 divq: None,
346 divr: Some(rcc::PllDiv::DIV20),
347 });
348 config.rcc.mux.ltdcsel = rcc::mux::Ltdcsel::PLL3_R; // 25 MHz
349 embassy_stm32::init(config)
350 }
351}
352
353mod bouncy_box {
354 use embedded_graphics::geometry::Point;
355 use embedded_graphics::primitives::Rectangle;
356
357 enum Direction {
358 DownLeft,
359 DownRight,
360 UpLeft,
361 UpRight,
362 }
363
364 pub struct BouncyBox {
365 direction: Direction,
366 child_rect: Rectangle,
367 parent_rect: Rectangle,
368 current_point: Point,
369 move_by: usize,
370 }
371
372 // This calculates the coordinates of a chile rectangle bounced around inside a parent bounded box
373 impl BouncyBox {
374 pub fn new(child_rect: Rectangle, parent_rect: Rectangle, move_by: usize) -> Self {
375 let center_box = parent_rect.center();
376 let center_img = child_rect.center();
377 let current_point = Point::new(center_box.x - center_img.x / 2, center_box.y - center_img.y / 2);
378 Self {
379 direction: Direction::DownRight,
380 child_rect,
381 parent_rect,
382 current_point,
383 move_by,
384 }
385 }
386
387 pub fn next_point(&mut self) -> Point {
388 let direction = &self.direction;
389 let img_height = self.child_rect.size.height as i32;
390 let box_height = self.parent_rect.size.height as i32;
391 let img_width = self.child_rect.size.width as i32;
392 let box_width = self.parent_rect.size.width as i32;
393 let move_by = self.move_by as i32;
394
395 match direction {
396 Direction::DownLeft => {
397 self.current_point.x -= move_by;
398 self.current_point.y += move_by;
399
400 let x_out_of_bounds = self.current_point.x < 0;
401 let y_out_of_bounds = (self.current_point.y + img_height) > box_height;
402
403 if x_out_of_bounds && y_out_of_bounds {
404 self.direction = Direction::UpRight
405 } else if x_out_of_bounds && !y_out_of_bounds {
406 self.direction = Direction::DownRight
407 } else if !x_out_of_bounds && y_out_of_bounds {
408 self.direction = Direction::UpLeft
409 }
410 }
411 Direction::DownRight => {
412 self.current_point.x += move_by;
413 self.current_point.y += move_by;
414
415 let x_out_of_bounds = (self.current_point.x + img_width) > box_width;
416 let y_out_of_bounds = (self.current_point.y + img_height) > box_height;
417
418 if x_out_of_bounds && y_out_of_bounds {
419 self.direction = Direction::UpLeft
420 } else if x_out_of_bounds && !y_out_of_bounds {
421 self.direction = Direction::DownLeft
422 } else if !x_out_of_bounds && y_out_of_bounds {
423 self.direction = Direction::UpRight
424 }
425 }
426 Direction::UpLeft => {
427 self.current_point.x -= move_by;
428 self.current_point.y -= move_by;
429
430 let x_out_of_bounds = self.current_point.x < 0;
431 let y_out_of_bounds = self.current_point.y < 0;
432
433 if x_out_of_bounds && y_out_of_bounds {
434 self.direction = Direction::DownRight
435 } else if x_out_of_bounds && !y_out_of_bounds {
436 self.direction = Direction::UpRight
437 } else if !x_out_of_bounds && y_out_of_bounds {
438 self.direction = Direction::DownLeft
439 }
440 }
441 Direction::UpRight => {
442 self.current_point.x += move_by;
443 self.current_point.y -= move_by;
444
445 let x_out_of_bounds = (self.current_point.x + img_width) > box_width;
446 let y_out_of_bounds = self.current_point.y < 0;
447
448 if x_out_of_bounds && y_out_of_bounds {
449 self.direction = Direction::DownLeft
450 } else if x_out_of_bounds && !y_out_of_bounds {
451 self.direction = Direction::UpLeft
452 } else if !x_out_of_bounds && y_out_of_bounds {
453 self.direction = Direction::DownRight
454 }
455 }
456 }
457
458 self.current_point
459 }
460 }
461}
diff --git a/examples/stm32u5/src/bin/tsc.rs b/examples/stm32u5/src/bin/tsc.rs
index eb15d275a..a85acc4c7 100644
--- a/examples/stm32u5/src/bin/tsc.rs
+++ b/examples/stm32u5/src/bin/tsc.rs
@@ -2,8 +2,8 @@
2#![no_main] 2#![no_main]
3 3
4use defmt::*; 4use defmt::*;
5use embassy_stm32::bind_interrupts;
6use embassy_stm32::tsc::{self, *}; 5use embassy_stm32::tsc::{self, *};
6use embassy_stm32::{bind_interrupts, peripherals};
7use embassy_time::Timer; 7use embassy_time::Timer;
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
@@ -33,63 +33,52 @@ async fn main(_spawner: embassy_executor::Spawner) {
33 synchro_pin_polarity: false, 33 synchro_pin_polarity: false,
34 acquisition_mode: false, 34 acquisition_mode: false,
35 max_count_interrupt: false, 35 max_count_interrupt: false,
36 channel_ios: TscIOPin::Group2Io2 | TscIOPin::Group7Io3,
37 shield_ios: TscIOPin::Group1Io3.into(),
38 sampling_ios: TscIOPin::Group1Io2 | TscIOPin::Group2Io1 | TscIOPin::Group7Io2,
39 }; 36 };
40 37
41 let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new(); 38 let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
42 g1.set_io2(context.PB13, PinType::Sample); 39 g1.set_io2::<tsc::pin_roles::Sample>(context.PB13);
43 g1.set_io3(context.PB14, PinType::Shield); 40 g1.set_io3::<tsc::pin_roles::Shield>(context.PB14);
44 41
45 let mut g2: PinGroup<embassy_stm32::peripherals::TSC, G2> = PinGroup::new(); 42 let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();
46 g2.set_io1(context.PB4, PinType::Sample); 43 g2.set_io1::<tsc::pin_roles::Sample>(context.PB4);
47 g2.set_io2(context.PB5, PinType::Channel); 44 let sensor0 = g2.set_io2(context.PB5);
48 45
49 let mut g7: PinGroup<embassy_stm32::peripherals::TSC, G7> = PinGroup::new(); 46 let mut g7: PinGroupWithRoles<peripherals::TSC, G7> = PinGroupWithRoles::default();
50 g7.set_io2(context.PE3, PinType::Sample); 47 g7.set_io2::<tsc::pin_roles::Sample>(context.PE3);
51 g7.set_io3(context.PE4, PinType::Channel); 48 let sensor1 = g7.set_io3(context.PE4);
52 49
53 let mut touch_controller = tsc::Tsc::new_async( 50 let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
54 context.TSC, 51 g1: Some(g1.pin_group),
55 Some(g1), 52 g2: Some(g2.pin_group),
56 Some(g2), 53 g7: Some(g7.pin_group),
57 None, 54 ..Default::default()
58 None, 55 };
59 None, 56
60 None, 57 let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap();
61 Some(g7),
62 None,
63 config,
64 Irqs,
65 );
66 58
67 touch_controller.discharge_io(true); 59 let acquisition_bank = touch_controller.create_acquisition_bank(AcquisitionBankPins {
68 Timer::after_millis(1).await; 60 g2_pin: Some(sensor0),
61 g7_pin: Some(sensor1),
62 ..Default::default()
63 });
69 64
70 touch_controller.start(); 65 touch_controller.set_active_channels_bank(&acquisition_bank);
71 66
72 let mut group_two_val = 0;
73 let mut group_seven_val = 0;
74 info!("Starting touch_controller interface"); 67 info!("Starting touch_controller interface");
75 loop { 68 loop {
69 touch_controller.start();
76 touch_controller.pend_for_acquisition().await; 70 touch_controller.pend_for_acquisition().await;
77 touch_controller.discharge_io(true); 71 touch_controller.discharge_io(true);
78 Timer::after_millis(1).await; 72 Timer::after_millis(1).await;
79 73
80 if touch_controller.group_get_status(Group::Two) == GroupStatus::Complete { 74 let status = touch_controller.get_acquisition_bank_status(&acquisition_bank);
81 group_two_val = touch_controller.group_get_value(Group::Two);
82 }
83 75
84 if touch_controller.group_get_status(Group::Seven) == GroupStatus::Complete { 76 if status.all_complete() {
85 group_seven_val = touch_controller.group_get_value(Group::Seven); 77 let read_values = touch_controller.get_acquisition_bank_values(&acquisition_bank);
78 let group2_reading = read_values.get_group_reading(Group::Two).unwrap();
79 let group7_reading = read_values.get_group_reading(Group::Seven).unwrap();
80 info!("group 2 value: {}", group2_reading.sensor_value);
81 info!("group 7 value: {}", group7_reading.sensor_value);
86 } 82 }
87
88 info!(
89 "Group Two value: {}, Group Seven value: {},",
90 group_two_val, group_seven_val
91 );
92
93 touch_controller.start();
94 } 83 }
95} 84}
diff --git a/examples/stm32u5/src/bin/usb_hs_serial.rs b/examples/stm32u5/src/bin/usb_hs_serial.rs
new file mode 100644
index 000000000..5549e2cbb
--- /dev/null
+++ b/examples/stm32u5/src/bin/usb_hs_serial.rs
@@ -0,0 +1,129 @@
1#![no_std]
2#![no_main]
3
4use defmt::{panic, *};
5use defmt_rtt as _; // global logger
6use embassy_executor::Spawner;
7use embassy_futures::join::join;
8use embassy_stm32::usb::{Driver, Instance};
9use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
10use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
11use embassy_usb::driver::EndpointError;
12use embassy_usb::Builder;
13use panic_probe as _;
14
15bind_interrupts!(struct Irqs {
16 OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
17});
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 info!("Hello World!");
22
23 let mut config = Config::default();
24 {
25 use embassy_stm32::rcc::*;
26 use embassy_stm32::time::Hertz;
27 config.rcc.hse = Some(Hse {
28 freq: Hertz(16_000_000),
29 mode: HseMode::Oscillator,
30 });
31 config.rcc.pll1 = Some(Pll {
32 source: PllSource::HSE,
33 prediv: PllPreDiv::DIV2, // HSE / 2 = 8MHz
34 mul: PllMul::MUL60, // 8MHz * 60 = 480MHz
35 divr: Some(PllDiv::DIV3), // 480MHz / 3 = 160MHz (sys_ck)
36 divq: Some(PllDiv::DIV10), // 480MHz / 10 = 48MHz (USB)
37 divp: Some(PllDiv::DIV15), // 480MHz / 15 = 32MHz (USBOTG)
38 });
39 config.rcc.mux.otghssel = mux::Otghssel::PLL1_P;
40 config.rcc.voltage_range = VoltageScale::RANGE1;
41 config.rcc.sys = Sysclk::PLL1_R;
42 }
43
44 let p = embassy_stm32::init(config);
45
46 // Create the driver, from the HAL.
47 let mut ep_out_buffer = [0u8; 256];
48 let mut config = embassy_stm32::usb::Config::default();
49 // Do not enable vbus_detection. This is a safe default that works in all boards.
50 // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need
51 // to enable vbus_detection to comply with the USB spec. If you enable it, the board
52 // has to support it or USB won't work at all. See docs on `vbus_detection` for details.
53 config.vbus_detection = false;
54 let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
55
56 // Create embassy-usb Config
57 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
58 config.manufacturer = Some("Embassy");
59 config.product = Some("USB-serial example");
60 config.serial_number = Some("12345678");
61
62 // Required for windows compatibility.
63 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
64 config.device_class = 0xEF;
65 config.device_sub_class = 0x02;
66 config.device_protocol = 0x01;
67 config.composite_with_iads = true;
68
69 // Create embassy-usb DeviceBuilder using the driver and config.
70 // It needs some buffers for building the descriptors.
71 let mut config_descriptor = [0; 256];
72 let mut bos_descriptor = [0; 256];
73 let mut control_buf = [0; 64];
74
75 let mut state = State::new();
76
77 let mut builder = Builder::new(
78 driver,
79 config,
80 &mut config_descriptor,
81 &mut bos_descriptor,
82 &mut [], // no msos descriptors
83 &mut control_buf,
84 );
85
86 // Create classes on the builder.
87 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
88
89 // Build the builder.
90 let mut usb = builder.build();
91
92 // Run the USB device.
93 let usb_fut = usb.run();
94
95 // Do stuff with the class!
96 let echo_fut = async {
97 loop {
98 class.wait_connection().await;
99 info!("Connected");
100 let _ = echo(&mut class).await;
101 info!("Disconnected");
102 }
103 };
104
105 // Run everything concurrently.
106 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
107 join(usb_fut, echo_fut).await;
108}
109
110struct Disconnected {}
111
112impl From<EndpointError> for Disconnected {
113 fn from(val: EndpointError) -> Self {
114 match val {
115 EndpointError::BufferOverflow => panic!("Buffer overflow"),
116 EndpointError::Disabled => Disconnected {},
117 }
118 }
119}
120
121async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
122 let mut buf = [0; 64];
123 loop {
124 let n = class.read_packet(&mut buf).await?;
125 let data = &buf[..n];
126 info!("data: {:x}", data);
127 class.write_packet(data).await?;
128 }
129}
diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs
index 4d56395da..4bb1a6079 100644
--- a/examples/stm32u5/src/bin/usb_serial.rs
+++ b/examples/stm32u5/src/bin/usb_serial.rs
@@ -13,7 +13,7 @@ use embassy_usb::Builder;
13use panic_probe as _; 13use panic_probe as _;
14 14
15bind_interrupts!(struct Irqs { 15bind_interrupts!(struct Irqs {
16 OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>; 16 OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
17}); 17});
18 18
19#[embassy_executor::main] 19#[embassy_executor::main]
@@ -48,7 +48,7 @@ async fn main(_spawner: Spawner) {
48 // to enable vbus_detection to comply with the USB spec. If you enable it, the board 48 // to enable vbus_detection to comply with the USB spec. If you enable it, the board
49 // has to support it or USB won't work at all. See docs on `vbus_detection` for details. 49 // has to support it or USB won't work at all. See docs on `vbus_detection` for details.
50 config.vbus_detection = false; 50 config.vbus_detection = false;
51 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); 51 let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
52 52
53 // Create embassy-usb Config 53 // Create embassy-usb Config
54 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); 54 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);