diff options
Diffstat (limited to 'examples')
31 files changed, 1399 insertions, 24 deletions
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",]} | |||
| 29 | serde = { version = "1.0.203", default-features = false, features = ["derive"] } | 29 | serde = { version = "1.0.203", default-features = false, features = ["derive"] } |
| 30 | serde-json-core = "0.5.1" | 30 | serde-json-core = "0.5.1" |
| 31 | 31 | ||
| 32 | # for assign resources example | ||
| 33 | assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" } | ||
| 34 | |||
| 32 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 35 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 33 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } | 36 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } |
| 34 | cortex-m-rt = "0.7.0" | 37 | 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..ff6eff4a2 --- /dev/null +++ b/examples/rp/src/bin/assign_resources.rs | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | //! This example demonstrates how to assign resources to multiple tasks by splitting up the peripherals. | ||
| 2 | //! 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) | ||
| 3 | //! Of course splitting up resources and sharing resources can be combined, yet this example is only about splitting up resources. | ||
| 4 | //! | ||
| 5 | //! There are basically two ways we demonstrate here: | ||
| 6 | //! 1) Assigning resources to a task by passing parts of the peripherals | ||
| 7 | //! 2) Assigning resources to a task by passing a struct with the split up peripherals, using the assign-resources macro | ||
| 8 | //! | ||
| 9 | //! using four LEDs on Pins 10, 11, 20 and 21 | ||
| 10 | |||
| 11 | #![no_std] | ||
| 12 | #![no_main] | ||
| 13 | |||
| 14 | use assign_resources::assign_resources; | ||
| 15 | use defmt::*; | ||
| 16 | use embassy_executor::Spawner; | ||
| 17 | use embassy_rp::gpio::{Level, Output}; | ||
| 18 | use embassy_rp::peripherals::{self, PIN_20, PIN_21}; | ||
| 19 | use embassy_time::Timer; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(spawner: Spawner) { | ||
| 24 | // initialize the peripherals | ||
| 25 | let p = embassy_rp::init(Default::default()); | ||
| 26 | |||
| 27 | // 1) Assigning a resource to a task by passing parts of the peripherals. | ||
| 28 | spawner | ||
| 29 | .spawn(double_blinky_manually_assigned(spawner, p.PIN_20, p.PIN_21)) | ||
| 30 | .unwrap(); | ||
| 31 | |||
| 32 | // 2) Using the assign-resources macro to assign resources to a task. | ||
| 33 | // we perform the split, see further below for the definition of the resources struct | ||
| 34 | let r = split_resources!(p); | ||
| 35 | // and then we can use them | ||
| 36 | spawner.spawn(double_blinky_macro_assigned(spawner, r.leds)).unwrap(); | ||
| 37 | } | ||
| 38 | |||
| 39 | // 1) Assigning a resource to a task by passing parts of the peripherals. | ||
| 40 | #[embassy_executor::task] | ||
| 41 | async fn double_blinky_manually_assigned(_spawner: Spawner, pin_20: PIN_20, pin_21: PIN_21) { | ||
| 42 | let mut led_20 = Output::new(pin_20, Level::Low); | ||
| 43 | let mut led_21 = Output::new(pin_21, Level::High); | ||
| 44 | |||
| 45 | loop { | ||
| 46 | info!("toggling leds"); | ||
| 47 | led_20.toggle(); | ||
| 48 | led_21.toggle(); | ||
| 49 | Timer::after_secs(1).await; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | // 2) Using the assign-resources macro to assign resources to a task. | ||
| 54 | // first we define the resources we want to assign to the task using the assign_resources! macro | ||
| 55 | // basically this will split up the peripherals struct into smaller structs, that we define here | ||
| 56 | // naming is up to you, make sure your future self understands what you did here | ||
| 57 | assign_resources! { | ||
| 58 | leds: Leds{ | ||
| 59 | led_10: PIN_10, | ||
| 60 | led_11: PIN_11, | ||
| 61 | } | ||
| 62 | // add more resources to more structs if needed, for example defining one struct for each task | ||
| 63 | } | ||
| 64 | // this could be done in another file and imported here, but for the sake of simplicity we do it here | ||
| 65 | // see https://github.com/adamgreig/assign-resources for more information | ||
| 66 | |||
| 67 | // 2) Using the split resources in a task | ||
| 68 | #[embassy_executor::task] | ||
| 69 | async fn double_blinky_macro_assigned(_spawner: Spawner, r: Leds) { | ||
| 70 | let mut led_10 = Output::new(r.led_10, Level::Low); | ||
| 71 | let mut led_11 = Output::new(r.led_11, Level::High); | ||
| 72 | |||
| 73 | loop { | ||
| 74 | info!("toggling leds"); | ||
| 75 | led_10.toggle(); | ||
| 76 | led_11.toggle(); | ||
| 77 | Timer::after_secs(1).await; | ||
| 78 | } | ||
| 79 | } | ||
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) { | |||
| 63 | w5500_int, | 63 | w5500_int, |
| 64 | w5500_reset, | 64 | w5500_reset, |
| 65 | ) | 65 | ) |
| 66 | .await; | 66 | .await |
| 67 | .unwrap(); | ||
| 67 | unwrap!(spawner.spawn(ethernet_task(runner))); | 68 | unwrap!(spawner.spawn(ethernet_task(runner))); |
| 68 | 69 | ||
| 69 | // Generate random seed | 70 | // 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) { | |||
| 66 | w5500_int, | 66 | w5500_int, |
| 67 | w5500_reset, | 67 | w5500_reset, |
| 68 | ) | 68 | ) |
| 69 | .await; | 69 | .await |
| 70 | .unwrap(); | ||
| 70 | unwrap!(spawner.spawn(ethernet_task(runner))); | 71 | unwrap!(spawner.spawn(ethernet_task(runner))); |
| 71 | 72 | ||
| 72 | // Generate random seed | 73 | // 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) { | |||
| 65 | w5500_int, | 65 | w5500_int, |
| 66 | w5500_reset, | 66 | w5500_reset, |
| 67 | ) | 67 | ) |
| 68 | .await; | 68 | .await |
| 69 | .unwrap(); | ||
| 69 | unwrap!(spawner.spawn(ethernet_task(runner))); | 70 | unwrap!(spawner.spawn(ethernet_task(runner))); |
| 70 | 71 | ||
| 71 | // Generate random seed | 72 | // 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) { | |||
| 63 | w5500_int, | 63 | w5500_int, |
| 64 | w5500_reset, | 64 | w5500_reset, |
| 65 | ) | 65 | ) |
| 66 | .await; | 66 | .await |
| 67 | .unwrap(); | ||
| 67 | unwrap!(spawner.spawn(ethernet_task(runner))); | 68 | unwrap!(spawner.spawn(ethernet_task(runner))); |
| 68 | 69 | ||
| 69 | // Generate random seed | 70 | // Generate random seed |
diff --git a/examples/rp/src/bin/sharing.rs b/examples/rp/src/bin/sharing.rs new file mode 100644 index 000000000..5416e20ce --- /dev/null +++ b/examples/rp/src/bin/sharing.rs | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | //! This example shows some common strategies for sharing resources between tasks. | ||
| 2 | //! | ||
| 3 | //! We demonstrate five different ways of sharing, covering different use cases: | ||
| 4 | //! - Atomics: This method is used for simple values, such as bool and u8..u32 | ||
| 5 | //! - Blocking Mutex: This is used for sharing non-async things, using Cell/RefCell for interior mutability. | ||
| 6 | //! - Async Mutex: This is used for sharing async resources, where you need to hold the lock across await points. | ||
| 7 | //! The async Mutex has interior mutability built-in, so no RefCell is needed. | ||
| 8 | //! - Cell: For sharing Copy types between tasks running on the same executor. | ||
| 9 | //! - RefCell: When you want &mut access to a value shared between tasks running on the same executor. | ||
| 10 | //! | ||
| 11 | //! More information: https://embassy.dev/book/#_sharing_peripherals_between_tasks | ||
| 12 | |||
| 13 | #![no_std] | ||
| 14 | #![no_main] | ||
| 15 | |||
| 16 | use core::cell::{Cell, RefCell}; | ||
| 17 | use core::sync::atomic::{AtomicU32, Ordering}; | ||
| 18 | |||
| 19 | use cortex_m_rt::entry; | ||
| 20 | use defmt::info; | ||
| 21 | use embassy_executor::{Executor, InterruptExecutor}; | ||
| 22 | use embassy_rp::clocks::RoscRng; | ||
| 23 | use embassy_rp::interrupt::{InterruptExt, Priority}; | ||
| 24 | use embassy_rp::peripherals::UART0; | ||
| 25 | use embassy_rp::uart::{self, InterruptHandler, UartTx}; | ||
| 26 | use embassy_rp::{bind_interrupts, interrupt}; | ||
| 27 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 28 | use embassy_sync::{blocking_mutex, mutex}; | ||
| 29 | use embassy_time::{Duration, Ticker}; | ||
| 30 | use rand::RngCore; | ||
| 31 | use static_cell::{ConstStaticCell, StaticCell}; | ||
| 32 | use {defmt_rtt as _, panic_probe as _}; | ||
| 33 | |||
| 34 | type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>; | ||
| 35 | |||
| 36 | struct MyType { | ||
| 37 | inner: u32, | ||
| 38 | } | ||
| 39 | |||
| 40 | static EXECUTOR_HI: InterruptExecutor = InterruptExecutor::new(); | ||
| 41 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); | ||
| 42 | |||
| 43 | // Use Atomics for simple values | ||
| 44 | static ATOMIC: AtomicU32 = AtomicU32::new(0); | ||
| 45 | |||
| 46 | // Use blocking Mutex with Cell/RefCell for sharing non-async things | ||
| 47 | static MUTEX_BLOCKING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<MyType>> = | ||
| 48 | blocking_mutex::Mutex::new(RefCell::new(MyType { inner: 0 })); | ||
| 49 | |||
| 50 | bind_interrupts!(struct Irqs { | ||
| 51 | UART0_IRQ => InterruptHandler<UART0>; | ||
| 52 | }); | ||
| 53 | |||
| 54 | #[interrupt] | ||
| 55 | unsafe fn SWI_IRQ_0() { | ||
| 56 | EXECUTOR_HI.on_interrupt() | ||
| 57 | } | ||
| 58 | |||
| 59 | #[entry] | ||
| 60 | fn main() -> ! { | ||
| 61 | let p = embassy_rp::init(Default::default()); | ||
| 62 | info!("Here we go!"); | ||
| 63 | |||
| 64 | let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, uart::Config::default()); | ||
| 65 | // Use the async Mutex for sharing async things (built-in interior mutability) | ||
| 66 | static UART: StaticCell<UartAsyncMutex> = StaticCell::new(); | ||
| 67 | let uart = UART.init(mutex::Mutex::new(uart)); | ||
| 68 | |||
| 69 | // High-priority executor: runs in interrupt mode | ||
| 70 | interrupt::SWI_IRQ_0.set_priority(Priority::P3); | ||
| 71 | let spawner = EXECUTOR_HI.start(interrupt::SWI_IRQ_0); | ||
| 72 | spawner.must_spawn(task_a(uart)); | ||
| 73 | |||
| 74 | // Low priority executor: runs in thread mode | ||
| 75 | let executor = EXECUTOR_LOW.init(Executor::new()); | ||
| 76 | executor.run(|spawner| { | ||
| 77 | // No Mutex needed when sharing between tasks running on the same executor | ||
| 78 | |||
| 79 | // Use Cell for Copy-types | ||
| 80 | static CELL: ConstStaticCell<Cell<[u8; 4]>> = ConstStaticCell::new(Cell::new([0; 4])); | ||
| 81 | let cell = CELL.take(); | ||
| 82 | |||
| 83 | // Use RefCell for &mut access | ||
| 84 | static REF_CELL: ConstStaticCell<RefCell<MyType>> = ConstStaticCell::new(RefCell::new(MyType { inner: 0 })); | ||
| 85 | let ref_cell = REF_CELL.take(); | ||
| 86 | |||
| 87 | spawner.must_spawn(task_b(uart, cell, ref_cell)); | ||
| 88 | spawner.must_spawn(task_c(cell, ref_cell)); | ||
| 89 | }); | ||
| 90 | } | ||
| 91 | |||
| 92 | #[embassy_executor::task] | ||
| 93 | async fn task_a(uart: &'static UartAsyncMutex) { | ||
| 94 | let mut ticker = Ticker::every(Duration::from_secs(1)); | ||
| 95 | loop { | ||
| 96 | let random = RoscRng.next_u32(); | ||
| 97 | |||
| 98 | { | ||
| 99 | let mut uart = uart.lock().await; | ||
| 100 | uart.write(b"task a").await.unwrap(); | ||
| 101 | // The uart lock is released when it goes out of scope | ||
| 102 | } | ||
| 103 | |||
| 104 | ATOMIC.store(random, Ordering::Relaxed); | ||
| 105 | |||
| 106 | MUTEX_BLOCKING.lock(|x| x.borrow_mut().inner = random); | ||
| 107 | |||
| 108 | ticker.next().await; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | #[embassy_executor::task] | ||
| 113 | async fn task_b(uart: &'static UartAsyncMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) { | ||
| 114 | let mut ticker = Ticker::every(Duration::from_secs(1)); | ||
| 115 | loop { | ||
| 116 | let random = RoscRng.next_u32(); | ||
| 117 | |||
| 118 | uart.lock().await.write(b"task b").await.unwrap(); | ||
| 119 | |||
| 120 | cell.set(random.to_be_bytes()); | ||
| 121 | |||
| 122 | ref_cell.borrow_mut().inner = random; | ||
| 123 | |||
| 124 | ticker.next().await; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | #[embassy_executor::task] | ||
| 129 | async fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) { | ||
| 130 | let mut ticker = Ticker::every(Duration::from_secs(1)); | ||
| 131 | loop { | ||
| 132 | info!("======================="); | ||
| 133 | |||
| 134 | let atomic_val = ATOMIC.load(Ordering::Relaxed); | ||
| 135 | info!("atomic: {}", atomic_val); | ||
| 136 | |||
| 137 | MUTEX_BLOCKING.lock(|x| { | ||
| 138 | let val = x.borrow().inner; | ||
| 139 | info!("blocking mutex: {}", val); | ||
| 140 | }); | ||
| 141 | |||
| 142 | let cell_val = cell.get(); | ||
| 143 | info!("cell: {:?}", cell_val); | ||
| 144 | |||
| 145 | let ref_cell_val = ref_cell.borrow().inner; | ||
| 146 | info!("ref_cell: {:?}", ref_cell_val); | ||
| 147 | |||
| 148 | ticker.next().await; | ||
| 149 | } | ||
| 150 | } | ||
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) { | |||
| 23 | // Startup delay can be combined to the maximum of either | 23 | // Startup delay can be combined to the maximum of either |
| 24 | delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); | 24 | delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); |
| 25 | 25 | ||
| 26 | let vrefint_sample = adc.read(&mut vrefint); | 26 | let vrefint_sample = adc.blocking_read(&mut vrefint); |
| 27 | 27 | ||
| 28 | let convert_to_millivolts = |sample| { | 28 | let convert_to_millivolts = |sample| { |
| 29 | // From http://www.st.com/resource/en/datasheet/DM00071990.pdf | 29 | // From http://www.st.com/resource/en/datasheet/DM00071990.pdf |
| @@ -50,16 +50,16 @@ async fn main(_spawner: Spawner) { | |||
| 50 | 50 | ||
| 51 | loop { | 51 | loop { |
| 52 | // Read pin | 52 | // Read pin |
| 53 | let v = adc.read(&mut pin); | 53 | let v = adc.blocking_read(&mut pin); |
| 54 | info!("PC1: {} ({} mV)", v, convert_to_millivolts(v)); | 54 | info!("PC1: {} ({} mV)", v, convert_to_millivolts(v)); |
| 55 | 55 | ||
| 56 | // Read internal temperature | 56 | // Read internal temperature |
| 57 | let v = adc.read(&mut temp); | 57 | let v = adc.blocking_read(&mut temp); |
| 58 | let celcius = convert_to_celcius(v); | 58 | let celcius = convert_to_celcius(v); |
| 59 | info!("Internal temp: {} ({} C)", v, celcius); | 59 | info!("Internal temp: {} ({} C)", v, celcius); |
| 60 | 60 | ||
| 61 | // Read internal voltage reference | 61 | // Read internal voltage reference |
| 62 | let v = adc.read(&mut vrefint); | 62 | let v = adc.blocking_read(&mut vrefint); |
| 63 | info!("VrefInt: {}", v); | 63 | info!("VrefInt: {}", v); |
| 64 | 64 | ||
| 65 | Timer::after_millis(100).await; | 65 | Timer::after_millis(100).await; |
diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs new file mode 100644 index 000000000..43a761e6d --- /dev/null +++ b/examples/stm32f4/src/bin/adc_dma.rs | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | use cortex_m::singleton; | ||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::adc::{Adc, RingBufferedAdc, SampleTime, Sequence}; | ||
| 7 | use embassy_stm32::Peripherals; | ||
| 8 | use embassy_time::Instant; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(spawner: Spawner) { | ||
| 13 | let p = embassy_stm32::init(Default::default()); | ||
| 14 | spawner.must_spawn(adc_task(p)); | ||
| 15 | } | ||
| 16 | |||
| 17 | #[embassy_executor::task] | ||
| 18 | async fn adc_task(mut p: Peripherals) { | ||
| 19 | const ADC_BUF_SIZE: usize = 1024; | ||
| 20 | let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); | ||
| 21 | let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); | ||
| 22 | |||
| 23 | let adc = Adc::new(p.ADC1); | ||
| 24 | let adc2 = Adc::new(p.ADC2); | ||
| 25 | |||
| 26 | let mut adc: RingBufferedAdc<embassy_stm32::peripherals::ADC1> = adc.into_ring_buffered(p.DMA2_CH0, adc_data); | ||
| 27 | let mut adc2: RingBufferedAdc<embassy_stm32::peripherals::ADC2> = adc2.into_ring_buffered(p.DMA2_CH2, adc_data2); | ||
| 28 | |||
| 29 | adc.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112); | ||
| 30 | adc.set_sample_sequence(Sequence::Two, &mut p.PA2, SampleTime::CYCLES112); | ||
| 31 | adc2.set_sample_sequence(Sequence::One, &mut p.PA1, SampleTime::CYCLES112); | ||
| 32 | adc2.set_sample_sequence(Sequence::Two, &mut p.PA3, SampleTime::CYCLES112); | ||
| 33 | |||
| 34 | // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around | ||
| 35 | // 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 | ||
| 36 | // 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 | ||
| 37 | // a reduction of performance as each time the buffer is reset, the adc & dma buffer must be restarted. | ||
| 38 | |||
| 39 | // 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 | ||
| 40 | // frequently. | ||
| 41 | let mut tic = Instant::now(); | ||
| 42 | let mut buffer1 = [0u16; 512]; | ||
| 43 | let mut buffer2 = [0u16; 512]; | ||
| 44 | let _ = adc.start(); | ||
| 45 | let _ = adc2.start(); | ||
| 46 | loop { | ||
| 47 | match adc.read(&mut buffer1).await { | ||
| 48 | Ok(_data) => { | ||
| 49 | let toc = Instant::now(); | ||
| 50 | info!( | ||
| 51 | "\n adc1: {} dt = {}, n = {}", | ||
| 52 | buffer1[0..16], | ||
| 53 | (toc - tic).as_micros(), | ||
| 54 | _data | ||
| 55 | ); | ||
| 56 | tic = toc; | ||
| 57 | } | ||
| 58 | Err(e) => { | ||
| 59 | warn!("Error: {:?}", e); | ||
| 60 | buffer1 = [0u16; 512]; | ||
| 61 | let _ = adc.start(); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | match adc2.read(&mut buffer2).await { | ||
| 66 | Ok(_data) => { | ||
| 67 | let toc = Instant::now(); | ||
| 68 | info!( | ||
| 69 | "\n adc2: {} dt = {}, n = {}", | ||
| 70 | buffer2[0..16], | ||
| 71 | (toc - tic).as_micros(), | ||
| 72 | _data | ||
| 73 | ); | ||
| 74 | tic = toc; | ||
| 75 | } | ||
| 76 | Err(e) => { | ||
| 77 | warn!("Error: {:?}", e); | ||
| 78 | buffer2 = [0u16; 512]; | ||
| 79 | let _ = adc2.start(); | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
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) -> ! { | |||
| 80 | let mac_addr = [0x02, 234, 3, 4, 82, 231]; | 80 | let mac_addr = [0x02, 234, 3, 4, 82, 231]; |
| 81 | static STATE: StaticCell<State<2, 2>> = StaticCell::new(); | 81 | static STATE: StaticCell<State<2, 2>> = StaticCell::new(); |
| 82 | let state = STATE.init(State::<2, 2>::new()); | 82 | let state = STATE.init(State::<2, 2>::new()); |
| 83 | let (device, runner) = embassy_net_wiznet::new(mac_addr, state, spi, w5500_int, w5500_reset).await; | 83 | let (device, runner) = embassy_net_wiznet::new(mac_addr, state, spi, w5500_int, w5500_reset) |
| 84 | .await | ||
| 85 | .unwrap(); | ||
| 84 | unwrap!(spawner.spawn(ethernet_task(runner))); | 86 | unwrap!(spawner.spawn(ethernet_task(runner))); |
| 85 | 87 | ||
| 86 | let config = embassy_net::Config::dhcpv4(Default::default()); | 88 | let config = embassy_net::Config::dhcpv4(Default::default()); |
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) { | |||
| 16 | let mut pin = p.PA3; | 16 | let mut pin = p.PA3; |
| 17 | 17 | ||
| 18 | let mut vrefint = adc.enable_vrefint(); | 18 | let mut vrefint = adc.enable_vrefint(); |
| 19 | let vrefint_sample = adc.read(&mut vrefint); | 19 | let vrefint_sample = adc.blocking_read(&mut vrefint); |
| 20 | let convert_to_millivolts = |sample| { | 20 | let convert_to_millivolts = |sample| { |
| 21 | // From http://www.st.com/resource/en/datasheet/DM00273119.pdf | 21 | // From http://www.st.com/resource/en/datasheet/DM00273119.pdf |
| 22 | // 6.3.27 Reference voltage | 22 | // 6.3.27 Reference voltage |
| @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) { | |||
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | loop { | 28 | loop { |
| 29 | let v = adc.read(&mut pin); | 29 | let v = adc.blocking_read(&mut pin); |
| 30 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | 30 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); |
| 31 | Timer::after_millis(100).await; | 31 | Timer::after_millis(100).await; |
| 32 | } | 32 | } |
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) { | |||
| 17 | let mut pin = p.PA1; | 17 | let mut pin = p.PA1; |
| 18 | 18 | ||
| 19 | let mut vrefint = adc.enable_vrefint(); | 19 | let mut vrefint = adc.enable_vrefint(); |
| 20 | let vrefint_sample = adc.read(&mut vrefint); | 20 | let vrefint_sample = adc.blocking_read(&mut vrefint); |
| 21 | let convert_to_millivolts = |sample| { | 21 | let convert_to_millivolts = |sample| { |
| 22 | // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf | 22 | // From https://www.st.com/resource/en/datasheet/stm32g031g8.pdf |
| 23 | // 6.3.3 Embedded internal reference voltage | 23 | // 6.3.3 Embedded internal reference voltage |
| @@ -27,7 +27,7 @@ async fn main(_spawner: Spawner) { | |||
| 27 | }; | 27 | }; |
| 28 | 28 | ||
| 29 | loop { | 29 | loop { |
| 30 | let v = adc.read(&mut pin); | 30 | let v = adc.blocking_read(&mut pin); |
| 31 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | 31 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); |
| 32 | Timer::after_millis(100).await; | 32 | Timer::after_millis(100).await; |
| 33 | } | 33 | } |
diff --git a/examples/stm32g0/src/bin/adc_dma.rs b/examples/stm32g0/src/bin/adc_dma.rs new file mode 100644 index 000000000..3713e5a21 --- /dev/null +++ b/examples/stm32g0/src/bin/adc_dma.rs | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; | ||
| 7 | use embassy_time::Timer; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | static mut DMA_BUF: [u16; 2] = [0; 2]; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let mut read_buffer = unsafe { &mut DMA_BUF[..] }; | ||
| 15 | |||
| 16 | let p = embassy_stm32::init(Default::default()); | ||
| 17 | |||
| 18 | info!("Hello World!"); | ||
| 19 | |||
| 20 | let mut adc = Adc::new(p.ADC1); | ||
| 21 | |||
| 22 | let mut dma = p.DMA1_CH1; | ||
| 23 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); | ||
| 24 | let mut pa0 = p.PA0.degrade_adc(); | ||
| 25 | |||
| 26 | loop { | ||
| 27 | adc.read( | ||
| 28 | &mut dma, | ||
| 29 | [ | ||
| 30 | (&mut vrefint_channel, SampleTime::CYCLES160_5), | ||
| 31 | (&mut pa0, SampleTime::CYCLES160_5), | ||
| 32 | ] | ||
| 33 | .into_iter(), | ||
| 34 | &mut read_buffer, | ||
| 35 | ) | ||
| 36 | .await; | ||
| 37 | |||
| 38 | let vrefint = read_buffer[0]; | ||
| 39 | let measured = read_buffer[1]; | ||
| 40 | info!("vrefint: {}", vrefint); | ||
| 41 | info!("measured: {}", measured); | ||
| 42 | Timer::after_millis(500).await; | ||
| 43 | } | ||
| 44 | } | ||
diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs new file mode 100644 index 000000000..9c5dd872a --- /dev/null +++ b/examples/stm32g0/src/bin/adc_oversampling.rs | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | //! adc oversampling example | ||
| 2 | //! | ||
| 3 | //! This example uses adc oversampling to achieve 16bit data | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_stm32::adc::{Adc, SampleTime}; | ||
| 11 | use embassy_time::Timer; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | #[embassy_executor::main] | ||
| 15 | async fn main(_spawner: Spawner) { | ||
| 16 | let p = embassy_stm32::init(Default::default()); | ||
| 17 | info!("Adc oversample test"); | ||
| 18 | |||
| 19 | let mut adc = Adc::new(p.ADC1); | ||
| 20 | adc.set_sample_time(SampleTime::CYCLES1_5); | ||
| 21 | let mut pin = p.PA1; | ||
| 22 | |||
| 23 | // From https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf | ||
| 24 | // page373 15.8 Oversampler | ||
| 25 | // Table 76. Maximum output results vs N and M. Grayed values indicates truncation | ||
| 26 | // 0x00 oversampling ratio X2 | ||
| 27 | // 0x01 oversampling ratio X4 | ||
| 28 | // 0x02 oversampling ratio X8 | ||
| 29 | // 0x03 oversampling ratio X16 | ||
| 30 | // 0x04 oversampling ratio X32 | ||
| 31 | // 0x05 oversampling ratio X64 | ||
| 32 | // 0x06 oversampling ratio X128 | ||
| 33 | // 0x07 oversampling ratio X256 | ||
| 34 | adc.set_oversampling_ratio(0x03); | ||
| 35 | adc.set_oversampling_shift(0b0000); | ||
| 36 | adc.oversampling_enable(true); | ||
| 37 | |||
| 38 | loop { | ||
| 39 | let v = adc.blocking_read(&mut pin); | ||
| 40 | info!("--> {} ", v); //max 65520 = 0xFFF0 | ||
| 41 | Timer::after_millis(100).await; | ||
| 42 | } | ||
| 43 | } | ||
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) { | |||
| 32 | adc.set_sample_time(SampleTime::CYCLES24_5); | 32 | adc.set_sample_time(SampleTime::CYCLES24_5); |
| 33 | 33 | ||
| 34 | loop { | 34 | loop { |
| 35 | let measured = adc.read(&mut p.PA7); | 35 | let measured = adc.blocking_read(&mut p.PA7); |
| 36 | info!("measured: {}", measured); | 36 | info!("measured: {}", measured); |
| 37 | Timer::after_millis(500).await; | 37 | Timer::after_millis(500).await; |
| 38 | } | 38 | } |
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" | |||
| 34 | embedded-storage = "0.3.1" | 34 | embedded-storage = "0.3.1" |
| 35 | static_cell = "2" | 35 | static_cell = "2" |
| 36 | chrono = { version = "^0.4", default-features = false } | 36 | chrono = { version = "^0.4", default-features = false } |
| 37 | grounded = "0.2.0" | ||
| 37 | 38 | ||
| 38 | # cargo build/run | 39 | # cargo build/run |
| 39 | [profile.dev] | 40 | [profile.dev] |
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) { | |||
| 51 | let mut vrefint_channel = adc.enable_vrefint(); | 51 | let mut vrefint_channel = adc.enable_vrefint(); |
| 52 | 52 | ||
| 53 | loop { | 53 | loop { |
| 54 | let vrefint = adc.read(&mut vrefint_channel); | 54 | let vrefint = adc.blocking_read(&mut vrefint_channel); |
| 55 | info!("vrefint: {}", vrefint); | 55 | info!("vrefint: {}", vrefint); |
| 56 | let measured = adc.read(&mut p.PC0); | 56 | let measured = adc.blocking_read(&mut p.PC0); |
| 57 | info!("measured: {}", measured); | 57 | info!("measured: {}", measured); |
| 58 | Timer::after_millis(500).await; | 58 | Timer::after_millis(500).await; |
| 59 | } | 59 | } |
diff --git a/examples/stm32h7/src/bin/adc_dma.rs b/examples/stm32h7/src/bin/adc_dma.rs new file mode 100644 index 000000000..0b905d227 --- /dev/null +++ b/examples/stm32h7/src/bin/adc_dma.rs | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; | ||
| 7 | use embassy_stm32::Config; | ||
| 8 | use embassy_time::Timer; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[link_section = ".ram_d3"] | ||
| 12 | static mut DMA_BUF: [u16; 2] = [0; 2]; | ||
| 13 | |||
| 14 | #[embassy_executor::main] | ||
| 15 | async fn main(_spawner: Spawner) { | ||
| 16 | let mut read_buffer = unsafe { &mut DMA_BUF[..] }; | ||
| 17 | |||
| 18 | let mut config = Config::default(); | ||
| 19 | { | ||
| 20 | use embassy_stm32::rcc::*; | ||
| 21 | config.rcc.hsi = Some(HSIPrescaler::DIV1); | ||
| 22 | config.rcc.csi = true; | ||
| 23 | config.rcc.pll1 = Some(Pll { | ||
| 24 | source: PllSource::HSI, | ||
| 25 | prediv: PllPreDiv::DIV4, | ||
| 26 | mul: PllMul::MUL50, | ||
| 27 | divp: Some(PllDiv::DIV2), | ||
| 28 | divq: Some(PllDiv::DIV8), // SPI1 cksel defaults to pll1_q | ||
| 29 | divr: None, | ||
| 30 | }); | ||
| 31 | config.rcc.pll2 = Some(Pll { | ||
| 32 | source: PllSource::HSI, | ||
| 33 | prediv: PllPreDiv::DIV4, | ||
| 34 | mul: PllMul::MUL50, | ||
| 35 | divp: Some(PllDiv::DIV8), // 100mhz | ||
| 36 | divq: None, | ||
| 37 | divr: None, | ||
| 38 | }); | ||
| 39 | config.rcc.sys = Sysclk::PLL1_P; // 400 Mhz | ||
| 40 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 41 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 42 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 43 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 44 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 45 | config.rcc.voltage_scale = VoltageScale::Scale1; | ||
| 46 | config.rcc.mux.adcsel = mux::Adcsel::PLL2_P; | ||
| 47 | } | ||
| 48 | let p = embassy_stm32::init(config); | ||
| 49 | |||
| 50 | info!("Hello World!"); | ||
| 51 | |||
| 52 | let mut adc = Adc::new(p.ADC3); | ||
| 53 | |||
| 54 | let mut dma = p.DMA1_CH1; | ||
| 55 | let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); | ||
| 56 | let mut pc0 = p.PC0.degrade_adc(); | ||
| 57 | |||
| 58 | loop { | ||
| 59 | adc.read( | ||
| 60 | &mut dma, | ||
| 61 | [ | ||
| 62 | (&mut vrefint_channel, SampleTime::CYCLES387_5), | ||
| 63 | (&mut pc0, SampleTime::CYCLES810_5), | ||
| 64 | ] | ||
| 65 | .into_iter(), | ||
| 66 | &mut read_buffer, | ||
| 67 | ) | ||
| 68 | .await; | ||
| 69 | |||
| 70 | let vrefint = read_buffer[0]; | ||
| 71 | let measured = read_buffer[1]; | ||
| 72 | info!("vrefint: {}", vrefint); | ||
| 73 | info!("measured: {}", measured); | ||
| 74 | Timer::after_millis(500).await; | ||
| 75 | } | ||
| 76 | } | ||
diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs new file mode 100644 index 000000000..f6735e235 --- /dev/null +++ b/examples/stm32h7/src/bin/sai.rs | |||
| @@ -0,0 +1,186 @@ | |||
| 1 | //! Daisy Seed rev.7(with PCM3060 codec) | ||
| 2 | //! https://electro-smith.com/products/daisy-seed | ||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | |||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use grounded::uninit::GroundedArrayCell; | ||
| 8 | use hal::rcc::*; | ||
| 9 | use hal::sai::*; | ||
| 10 | use hal::time::Hertz; | ||
| 11 | use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _}; | ||
| 12 | |||
| 13 | const BLOCK_LENGTH: usize = 32; // 32 samples | ||
| 14 | const HALF_DMA_BUFFER_LENGTH: usize = BLOCK_LENGTH * 2; // 2 channels | ||
| 15 | const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks | ||
| 16 | const SAMPLE_RATE: u32 = 48000; | ||
| 17 | |||
| 18 | //DMA buffer must be in special region. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions | ||
| 19 | #[link_section = ".sram1_bss"] | ||
| 20 | static mut TX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); | ||
| 21 | #[link_section = ".sram1_bss"] | ||
| 22 | static mut RX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit(); | ||
| 23 | |||
| 24 | #[embassy_executor::main] | ||
| 25 | async fn main(_spawner: Spawner) { | ||
| 26 | let mut config = hal::Config::default(); | ||
| 27 | config.rcc.pll1 = Some(Pll { | ||
| 28 | source: PllSource::HSE, | ||
| 29 | prediv: PllPreDiv::DIV4, | ||
| 30 | mul: PllMul::MUL200, | ||
| 31 | divp: Some(PllDiv::DIV2), | ||
| 32 | divq: Some(PllDiv::DIV5), | ||
| 33 | divr: Some(PllDiv::DIV2), | ||
| 34 | }); | ||
| 35 | config.rcc.pll3 = Some(Pll { | ||
| 36 | source: PllSource::HSE, | ||
| 37 | prediv: PllPreDiv::DIV6, | ||
| 38 | mul: PllMul::MUL295, | ||
| 39 | divp: Some(PllDiv::DIV16), | ||
| 40 | divq: Some(PllDiv::DIV4), | ||
| 41 | divr: Some(PllDiv::DIV32), | ||
| 42 | }); | ||
| 43 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 44 | config.rcc.mux.sai1sel = hal::pac::rcc::vals::Saisel::PLL3_P; | ||
| 45 | config.rcc.ahb_pre = AHBPrescaler::DIV2; // 200 Mhz | ||
| 46 | config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 47 | config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 48 | config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 49 | config.rcc.apb4_pre = APBPrescaler::DIV2; // 100 Mhz | ||
| 50 | config.rcc.hse = Some(Hse { | ||
| 51 | freq: Hertz::mhz(16), | ||
| 52 | mode: HseMode::Oscillator, | ||
| 53 | }); | ||
| 54 | |||
| 55 | let p = hal::init(config); | ||
| 56 | |||
| 57 | let (sub_block_tx, sub_block_rx) = hal::sai::split_subblocks(p.SAI1); | ||
| 58 | let kernel_clock = hal::rcc::frequency::<hal::peripherals::SAI1>().0; | ||
| 59 | let mclk_div = mclk_div_from_u8((kernel_clock / (SAMPLE_RATE * 256)) as u8); | ||
| 60 | |||
| 61 | let mut tx_config = hal::sai::Config::default(); | ||
| 62 | tx_config.mode = Mode::Master; | ||
| 63 | tx_config.tx_rx = TxRx::Transmitter; | ||
| 64 | tx_config.sync_output = true; | ||
| 65 | tx_config.clock_strobe = ClockStrobe::Falling; | ||
| 66 | tx_config.master_clock_divider = mclk_div; | ||
| 67 | tx_config.stereo_mono = StereoMono::Stereo; | ||
| 68 | tx_config.data_size = DataSize::Data24; | ||
| 69 | tx_config.bit_order = BitOrder::MsbFirst; | ||
| 70 | tx_config.frame_sync_polarity = FrameSyncPolarity::ActiveHigh; | ||
| 71 | tx_config.frame_sync_offset = FrameSyncOffset::OnFirstBit; | ||
| 72 | tx_config.frame_length = 64; | ||
| 73 | tx_config.frame_sync_active_level_length = embassy_stm32::sai::word::U7(32); | ||
| 74 | tx_config.fifo_threshold = FifoThreshold::Quarter; | ||
| 75 | |||
| 76 | let mut rx_config = tx_config.clone(); | ||
| 77 | rx_config.mode = Mode::Slave; | ||
| 78 | rx_config.tx_rx = TxRx::Receiver; | ||
| 79 | rx_config.sync_input = SyncInput::Internal; | ||
| 80 | rx_config.clock_strobe = ClockStrobe::Rising; | ||
| 81 | rx_config.sync_output = false; | ||
| 82 | |||
| 83 | let tx_buffer: &mut [u32] = unsafe { | ||
| 84 | TX_BUFFER.initialize_all_copied(0); | ||
| 85 | let (ptr, len) = TX_BUFFER.get_ptr_len(); | ||
| 86 | core::slice::from_raw_parts_mut(ptr, len) | ||
| 87 | }; | ||
| 88 | |||
| 89 | let mut sai_transmitter = Sai::new_asynchronous_with_mclk( | ||
| 90 | sub_block_tx, | ||
| 91 | p.PE5, | ||
| 92 | p.PE6, | ||
| 93 | p.PE4, | ||
| 94 | p.PE2, | ||
| 95 | p.DMA1_CH0, | ||
| 96 | tx_buffer, | ||
| 97 | tx_config, | ||
| 98 | ); | ||
| 99 | |||
| 100 | let rx_buffer: &mut [u32] = unsafe { | ||
| 101 | RX_BUFFER.initialize_all_copied(0); | ||
| 102 | let (ptr, len) = RX_BUFFER.get_ptr_len(); | ||
| 103 | core::slice::from_raw_parts_mut(ptr, len) | ||
| 104 | }; | ||
| 105 | |||
| 106 | let mut sai_receiver = Sai::new_synchronous(sub_block_rx, p.PE3, p.DMA1_CH1, rx_buffer, rx_config); | ||
| 107 | |||
| 108 | sai_receiver.start(); | ||
| 109 | sai_transmitter.start(); | ||
| 110 | |||
| 111 | let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; | ||
| 112 | |||
| 113 | loop { | ||
| 114 | sai_receiver.read(&mut buf).await.unwrap(); | ||
| 115 | sai_transmitter.write(&buf).await.unwrap(); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | const fn mclk_div_from_u8(v: u8) -> MasterClockDivider { | ||
| 120 | match v { | ||
| 121 | 1 => MasterClockDivider::Div1, | ||
| 122 | 2 => MasterClockDivider::Div2, | ||
| 123 | 3 => MasterClockDivider::Div3, | ||
| 124 | 4 => MasterClockDivider::Div4, | ||
| 125 | 5 => MasterClockDivider::Div5, | ||
| 126 | 6 => MasterClockDivider::Div6, | ||
| 127 | 7 => MasterClockDivider::Div7, | ||
| 128 | 8 => MasterClockDivider::Div8, | ||
| 129 | 9 => MasterClockDivider::Div9, | ||
| 130 | 10 => MasterClockDivider::Div10, | ||
| 131 | 11 => MasterClockDivider::Div11, | ||
| 132 | 12 => MasterClockDivider::Div12, | ||
| 133 | 13 => MasterClockDivider::Div13, | ||
| 134 | 14 => MasterClockDivider::Div14, | ||
| 135 | 15 => MasterClockDivider::Div15, | ||
| 136 | 16 => MasterClockDivider::Div16, | ||
| 137 | 17 => MasterClockDivider::Div17, | ||
| 138 | 18 => MasterClockDivider::Div18, | ||
| 139 | 19 => MasterClockDivider::Div19, | ||
| 140 | 20 => MasterClockDivider::Div20, | ||
| 141 | 21 => MasterClockDivider::Div21, | ||
| 142 | 22 => MasterClockDivider::Div22, | ||
| 143 | 23 => MasterClockDivider::Div23, | ||
| 144 | 24 => MasterClockDivider::Div24, | ||
| 145 | 25 => MasterClockDivider::Div25, | ||
| 146 | 26 => MasterClockDivider::Div26, | ||
| 147 | 27 => MasterClockDivider::Div27, | ||
| 148 | 28 => MasterClockDivider::Div28, | ||
| 149 | 29 => MasterClockDivider::Div29, | ||
| 150 | 30 => MasterClockDivider::Div30, | ||
| 151 | 31 => MasterClockDivider::Div31, | ||
| 152 | 32 => MasterClockDivider::Div32, | ||
| 153 | 33 => MasterClockDivider::Div33, | ||
| 154 | 34 => MasterClockDivider::Div34, | ||
| 155 | 35 => MasterClockDivider::Div35, | ||
| 156 | 36 => MasterClockDivider::Div36, | ||
| 157 | 37 => MasterClockDivider::Div37, | ||
| 158 | 38 => MasterClockDivider::Div38, | ||
| 159 | 39 => MasterClockDivider::Div39, | ||
| 160 | 40 => MasterClockDivider::Div40, | ||
| 161 | 41 => MasterClockDivider::Div41, | ||
| 162 | 42 => MasterClockDivider::Div42, | ||
| 163 | 43 => MasterClockDivider::Div43, | ||
| 164 | 44 => MasterClockDivider::Div44, | ||
| 165 | 45 => MasterClockDivider::Div45, | ||
| 166 | 46 => MasterClockDivider::Div46, | ||
| 167 | 47 => MasterClockDivider::Div47, | ||
| 168 | 48 => MasterClockDivider::Div48, | ||
| 169 | 49 => MasterClockDivider::Div49, | ||
| 170 | 50 => MasterClockDivider::Div50, | ||
| 171 | 51 => MasterClockDivider::Div51, | ||
| 172 | 52 => MasterClockDivider::Div52, | ||
| 173 | 53 => MasterClockDivider::Div53, | ||
| 174 | 54 => MasterClockDivider::Div54, | ||
| 175 | 55 => MasterClockDivider::Div55, | ||
| 176 | 56 => MasterClockDivider::Div56, | ||
| 177 | 57 => MasterClockDivider::Div57, | ||
| 178 | 58 => MasterClockDivider::Div58, | ||
| 179 | 59 => MasterClockDivider::Div59, | ||
| 180 | 60 => MasterClockDivider::Div60, | ||
| 181 | 61 => MasterClockDivider::Div61, | ||
| 182 | 62 => MasterClockDivider::Div62, | ||
| 183 | 63 => MasterClockDivider::Div63, | ||
| 184 | _ => panic!(), | ||
| 185 | } | ||
| 186 | } | ||
diff --git a/examples/stm32h7/src/bin/spi_bdma.rs b/examples/stm32h7/src/bin/spi_bdma.rs index b2e941078..43fb6b41c 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; | |||
| 10 | use embassy_stm32::mode::Async; | 10 | use embassy_stm32::mode::Async; |
| 11 | use embassy_stm32::time::mhz; | 11 | use embassy_stm32::time::mhz; |
| 12 | use embassy_stm32::{spi, Config}; | 12 | use embassy_stm32::{spi, Config}; |
| 13 | use grounded::uninit::GroundedArrayCell; | ||
| 13 | use heapless::String; | 14 | use heapless::String; |
| 14 | use static_cell::StaticCell; | 15 | use static_cell::StaticCell; |
| 15 | use {defmt_rtt as _, panic_probe as _}; | 16 | use {defmt_rtt as _, panic_probe as _}; |
| 16 | 17 | ||
| 17 | // Defined in memory.x | 18 | // Defined in memory.x |
| 18 | #[link_section = ".ram_d3"] | 19 | #[link_section = ".ram_d3"] |
| 19 | static mut RAM_D3: [u8; 64 * 1024] = [0u8; 64 * 1024]; | 20 | static mut RAM_D3: GroundedArrayCell<u8, 256> = GroundedArrayCell::uninit(); |
| 20 | 21 | ||
| 21 | #[embassy_executor::task] | 22 | #[embassy_executor::task] |
| 22 | async fn main_task(mut spi: spi::Spi<'static, Async>) { | 23 | async fn main_task(mut spi: spi::Spi<'static, Async>) { |
| 23 | let read_buffer = unsafe { &mut RAM_D3[0..128] }; | 24 | let (read_buffer, write_buffer) = unsafe { |
| 24 | let write_buffer = unsafe { &mut RAM_D3[128..256] }; | 25 | RAM_D3.initialize_all_copied(0); |
| 26 | ( | ||
| 27 | RAM_D3.get_subslice_mut_unchecked(0, 128), | ||
| 28 | RAM_D3.get_subslice_mut_unchecked(128, 128), | ||
| 29 | ) | ||
| 30 | }; | ||
| 25 | 31 | ||
| 26 | for n in 0u32.. { | 32 | for n in 0u32.. { |
| 27 | let mut write: String<128> = String::new(); | 33 | let mut write: String<128> = String::new(); |
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 @@ | |||
| 1 | [target.thumbv7em-none-eabihf] | ||
| 2 | runner = 'probe-rs run --chip STM32H735IGKx' | ||
| 3 | |||
| 4 | [build] | ||
| 5 | target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) | ||
| 6 | |||
| 7 | [env] | ||
| 8 | 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 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-stm32h735-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } | ||
| 9 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 10 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } | ||
| 11 | embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | ||
| 12 | embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | ||
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 14 | |||
| 15 | defmt = "0.3" | ||
| 16 | defmt-rtt = "0.4" | ||
| 17 | |||
| 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 19 | cortex-m-rt = "0.7.0" | ||
| 20 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 21 | heapless = { version = "0.8", default-features = false } | ||
| 22 | embedded-graphics = { version = "0.8.1" } | ||
| 23 | tinybmp = { version = "0.5" } | ||
| 24 | |||
| 25 | # cargo build/run | ||
| 26 | [profile.dev] | ||
| 27 | codegen-units = 1 | ||
| 28 | debug = 2 | ||
| 29 | debug-assertions = true # <- | ||
| 30 | incremental = false | ||
| 31 | opt-level = 3 # <- | ||
| 32 | overflow-checks = true # <- | ||
| 33 | |||
| 34 | # cargo test | ||
| 35 | [profile.test] | ||
| 36 | codegen-units = 1 | ||
| 37 | debug = 2 | ||
| 38 | debug-assertions = true # <- | ||
| 39 | incremental = false | ||
| 40 | opt-level = 3 # <- | ||
| 41 | overflow-checks = true # <- | ||
| 42 | |||
| 43 | # cargo build/run --release | ||
| 44 | [profile.release] | ||
| 45 | codegen-units = 1 | ||
| 46 | debug = 2 | ||
| 47 | debug-assertions = false # <- | ||
| 48 | incremental = false | ||
| 49 | lto = 'fat' | ||
| 50 | opt-level = 3 # <- | ||
| 51 | overflow-checks = false # <- | ||
| 52 | |||
| 53 | # cargo test --release | ||
| 54 | [profile.bench] | ||
| 55 | codegen-units = 1 | ||
| 56 | debug = 2 | ||
| 57 | debug-assertions = false # <- | ||
| 58 | incremental = false | ||
| 59 | lto = 'fat' | ||
| 60 | opt-level = 3 # <- | ||
| 61 | 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 @@ | |||
| 1 | //! This build script copies the `memory.x` file from the crate root into | ||
| 2 | //! a directory where the linker can always find it at build time. | ||
| 3 | //! For many projects this is optional, as the linker always searches the | ||
| 4 | //! project root directory -- wherever `Cargo.toml` is. However, if you | ||
| 5 | //! are using a workspace or have a more complicated build setup, this | ||
| 6 | //! build script becomes required. Additionally, by requesting that | ||
| 7 | //! Cargo re-run the build script whenever `memory.x` is changed, | ||
| 8 | //! updating `memory.x` ensures a rebuild of the application with the | ||
| 9 | //! new memory settings. | ||
| 10 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn main() { | ||
| 17 | // Put `memory.x` in our output directory and ensure it's | ||
| 18 | // on the linker search path. | ||
| 19 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 20 | File::create(out.join("memory.x")) | ||
| 21 | .unwrap() | ||
| 22 | .write_all(include_bytes!("memory.x")) | ||
| 23 | .unwrap(); | ||
| 24 | println!("cargo:rustc-link-search={}", out.display()); | ||
| 25 | |||
| 26 | // By default, Cargo will re-run a build script whenever | ||
| 27 | // any file in the project changes. By specifying `memory.x` | ||
| 28 | // here, we ensure the build script is only re-run when | ||
| 29 | // `memory.x` is changed. | ||
| 30 | println!("cargo:rerun-if-changed=memory.x"); | ||
| 31 | |||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 34 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 35 | } | ||
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 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | FLASH : ORIGIN = 0x08000000, LENGTH = 1024K | ||
| 4 | RAM : ORIGIN = 0x24000000, LENGTH = 320K | ||
| 5 | } \ 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 --- /dev/null +++ b/examples/stm32h735/src/bin/ferris.bmp | |||
| Binary files differ | |||
diff --git a/examples/stm32h735/src/bin/ltdc.rs b/examples/stm32h735/src/bin/ltdc.rs new file mode 100644 index 000000000..a36fdef2c --- /dev/null +++ b/examples/stm32h735/src/bin/ltdc.rs | |||
| @@ -0,0 +1,467 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![macro_use] | ||
| 4 | #![allow(static_mut_refs)] | ||
| 5 | |||
| 6 | /// 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") | ||
| 7 | /// 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. | ||
| 8 | /// For this reason a 256 color lookup table had to be used to keep the memory requirement down to an acceptable level. | ||
| 9 | /// The example bounces a ferris crab bitmap around the screen while blinking an led on another task | ||
| 10 | /// | ||
| 11 | use bouncy_box::BouncyBox; | ||
| 12 | use defmt::{info, unwrap}; | ||
| 13 | use embassy_executor::Spawner; | ||
| 14 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 15 | use embassy_stm32::ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge}; | ||
| 16 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 17 | use embassy_time::{Duration, Timer}; | ||
| 18 | use embedded_graphics::draw_target::DrawTarget; | ||
| 19 | use embedded_graphics::geometry::{OriginDimensions, Point, Size}; | ||
| 20 | use embedded_graphics::image::Image; | ||
| 21 | use embedded_graphics::pixelcolor::raw::RawU24; | ||
| 22 | use embedded_graphics::pixelcolor::Rgb888; | ||
| 23 | use embedded_graphics::prelude::*; | ||
| 24 | use embedded_graphics::primitives::Rectangle; | ||
| 25 | use embedded_graphics::Pixel; | ||
| 26 | use heapless::{Entry, FnvIndexMap}; | ||
| 27 | use tinybmp::Bmp; | ||
| 28 | use {defmt_rtt as _, panic_probe as _}; | ||
| 29 | |||
| 30 | const DISPLAY_WIDTH: usize = 480; | ||
| 31 | const DISPLAY_HEIGHT: usize = 272; | ||
| 32 | const MY_TASK_POOL_SIZE: usize = 2; | ||
| 33 | |||
| 34 | // the following two display buffers consume 261120 bytes that just about fits into axis ram found on the mcu | ||
| 35 | pub static mut FB1: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT]; | ||
| 36 | pub static mut FB2: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT]; | ||
| 37 | |||
| 38 | bind_interrupts!(struct Irqs { | ||
| 39 | LTDC => ltdc::InterruptHandler<peripherals::LTDC>; | ||
| 40 | }); | ||
| 41 | |||
| 42 | const NUM_COLORS: usize = 256; | ||
| 43 | |||
| 44 | #[embassy_executor::main] | ||
| 45 | async fn main(spawner: Spawner) { | ||
| 46 | let p = rcc_setup::stm32h735g_init(); | ||
| 47 | |||
| 48 | // blink the led on another task | ||
| 49 | let led = Output::new(p.PC3, Level::High, Speed::Low); | ||
| 50 | unwrap!(spawner.spawn(led_task(led))); | ||
| 51 | |||
| 52 | // numbers from STMicroelectronics/STM32CubeH7 STM32H735G-DK C-based example | ||
| 53 | const RK043FN48H_HSYNC: u16 = 41; // Horizontal synchronization | ||
| 54 | const RK043FN48H_HBP: u16 = 13; // Horizontal back porch | ||
| 55 | const RK043FN48H_HFP: u16 = 32; // Horizontal front porch | ||
| 56 | const RK043FN48H_VSYNC: u16 = 10; // Vertical synchronization | ||
| 57 | const RK043FN48H_VBP: u16 = 2; // Vertical back porch | ||
| 58 | const RK043FN48H_VFP: u16 = 2; // Vertical front porch | ||
| 59 | |||
| 60 | let ltdc_config = LtdcConfiguration { | ||
| 61 | active_width: DISPLAY_WIDTH as _, | ||
| 62 | active_height: DISPLAY_HEIGHT as _, | ||
| 63 | h_back_porch: RK043FN48H_HBP - 11, // -11 from MX_LTDC_Init | ||
| 64 | h_front_porch: RK043FN48H_HFP, | ||
| 65 | v_back_porch: RK043FN48H_VBP, | ||
| 66 | v_front_porch: RK043FN48H_VFP, | ||
| 67 | h_sync: RK043FN48H_HSYNC, | ||
| 68 | v_sync: RK043FN48H_VSYNC, | ||
| 69 | h_sync_polarity: PolarityActive::ActiveLow, | ||
| 70 | v_sync_polarity: PolarityActive::ActiveLow, | ||
| 71 | data_enable_polarity: PolarityActive::ActiveHigh, | ||
| 72 | pixel_clock_polarity: PolarityEdge::FallingEdge, | ||
| 73 | }; | ||
| 74 | |||
| 75 | info!("init ltdc"); | ||
| 76 | let mut ltdc = Ltdc::new_with_pins( | ||
| 77 | 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, | ||
| 78 | 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, | ||
| 79 | ); | ||
| 80 | ltdc.init(<dc_config); | ||
| 81 | |||
| 82 | // we only need to draw on one layer for this example (not to be confused with the double buffer) | ||
| 83 | info!("enable bottom layer"); | ||
| 84 | let layer_config = LtdcLayerConfig { | ||
| 85 | pixel_format: ltdc::PixelFormat::L8, // 1 byte per pixel | ||
| 86 | layer: LtdcLayer::Layer1, | ||
| 87 | window_x0: 0, | ||
| 88 | window_x1: DISPLAY_WIDTH as _, | ||
| 89 | window_y0: 0, | ||
| 90 | window_y1: DISPLAY_HEIGHT as _, | ||
| 91 | }; | ||
| 92 | |||
| 93 | let ferris_bmp: Bmp<Rgb888> = Bmp::from_slice(include_bytes!("./ferris.bmp")).unwrap(); | ||
| 94 | let color_map = build_color_lookup_map(&ferris_bmp); | ||
| 95 | let clut = build_clut(&color_map); | ||
| 96 | |||
| 97 | // enable the bottom layer with a 256 color lookup table | ||
| 98 | ltdc.init_layer(&layer_config, Some(&clut)); | ||
| 99 | |||
| 100 | // Safety: the DoubleBuffer controls access to the statically allocated frame buffers | ||
| 101 | // and it is the only thing that mutates their content | ||
| 102 | let mut double_buffer = DoubleBuffer::new( | ||
| 103 | unsafe { FB1.as_mut() }, | ||
| 104 | unsafe { FB2.as_mut() }, | ||
| 105 | layer_config, | ||
| 106 | color_map, | ||
| 107 | ); | ||
| 108 | |||
| 109 | // this allows us to perform some simple animation for every frame | ||
| 110 | let mut bouncy_box = BouncyBox::new( | ||
| 111 | ferris_bmp.bounding_box(), | ||
| 112 | Rectangle::new(Point::zero(), Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)), | ||
| 113 | 2, | ||
| 114 | ); | ||
| 115 | |||
| 116 | loop { | ||
| 117 | // cpu intensive drawing to the buffer that is NOT currently being copied to the LCD screen | ||
| 118 | double_buffer.clear(); | ||
| 119 | let position = bouncy_box.next_point(); | ||
| 120 | let ferris = Image::new(&ferris_bmp, position); | ||
| 121 | unwrap!(ferris.draw(&mut double_buffer)); | ||
| 122 | |||
| 123 | // perform async dma data transfer to the lcd screen | ||
| 124 | unwrap!(double_buffer.swap(&mut ltdc).await); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | /// builds the color look-up table from all unique colors found in the bitmap. This should be a 256 color indexed bitmap to work. | ||
| 129 | fn build_color_lookup_map(bmp: &Bmp<Rgb888>) -> FnvIndexMap<u32, u8, NUM_COLORS> { | ||
| 130 | let mut color_map: FnvIndexMap<u32, u8, NUM_COLORS> = heapless::FnvIndexMap::new(); | ||
| 131 | let mut counter: u8 = 0; | ||
| 132 | |||
| 133 | // add black to position 0 | ||
| 134 | color_map.insert(Rgb888::new(0, 0, 0).into_storage(), counter).unwrap(); | ||
| 135 | counter += 1; | ||
| 136 | |||
| 137 | for Pixel(_point, color) in bmp.pixels() { | ||
| 138 | let raw = color.into_storage(); | ||
| 139 | if let Entry::Vacant(v) = color_map.entry(raw) { | ||
| 140 | v.insert(counter).expect("more than 256 colors detected"); | ||
| 141 | counter += 1; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | color_map | ||
| 145 | } | ||
| 146 | |||
| 147 | /// builds the color look-up table from the color map provided | ||
| 148 | fn build_clut(color_map: &FnvIndexMap<u32, u8, NUM_COLORS>) -> [ltdc::RgbColor; NUM_COLORS] { | ||
| 149 | let mut clut = [ltdc::RgbColor::default(); NUM_COLORS]; | ||
| 150 | for (color, index) in color_map.iter() { | ||
| 151 | let color = Rgb888::from(RawU24::new(*color)); | ||
| 152 | clut[*index as usize] = ltdc::RgbColor { | ||
| 153 | red: color.r(), | ||
| 154 | green: color.g(), | ||
| 155 | blue: color.b(), | ||
| 156 | }; | ||
| 157 | } | ||
| 158 | |||
| 159 | clut | ||
| 160 | } | ||
| 161 | |||
| 162 | #[embassy_executor::task(pool_size = MY_TASK_POOL_SIZE)] | ||
| 163 | async fn led_task(mut led: Output<'static>) { | ||
| 164 | let mut counter = 0; | ||
| 165 | loop { | ||
| 166 | info!("blink: {}", counter); | ||
| 167 | counter += 1; | ||
| 168 | |||
| 169 | // on | ||
| 170 | led.set_low(); | ||
| 171 | Timer::after(Duration::from_millis(50)).await; | ||
| 172 | |||
| 173 | // off | ||
| 174 | led.set_high(); | ||
| 175 | Timer::after(Duration::from_millis(450)).await; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | pub type TargetPixelType = u8; | ||
| 180 | |||
| 181 | // A simple double buffer | ||
| 182 | pub struct DoubleBuffer { | ||
| 183 | buf0: &'static mut [TargetPixelType], | ||
| 184 | buf1: &'static mut [TargetPixelType], | ||
| 185 | is_buf0: bool, | ||
| 186 | layer_config: LtdcLayerConfig, | ||
| 187 | color_map: FnvIndexMap<u32, u8, NUM_COLORS>, | ||
| 188 | } | ||
| 189 | |||
| 190 | impl DoubleBuffer { | ||
| 191 | pub fn new( | ||
| 192 | buf0: &'static mut [TargetPixelType], | ||
| 193 | buf1: &'static mut [TargetPixelType], | ||
| 194 | layer_config: LtdcLayerConfig, | ||
| 195 | color_map: FnvIndexMap<u32, u8, NUM_COLORS>, | ||
| 196 | ) -> Self { | ||
| 197 | Self { | ||
| 198 | buf0, | ||
| 199 | buf1, | ||
| 200 | is_buf0: true, | ||
| 201 | layer_config, | ||
| 202 | color_map, | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | pub fn current(&mut self) -> (&FnvIndexMap<u32, u8, NUM_COLORS>, &mut [TargetPixelType]) { | ||
| 207 | if self.is_buf0 { | ||
| 208 | (&self.color_map, self.buf0) | ||
| 209 | } else { | ||
| 210 | (&self.color_map, self.buf1) | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | pub async fn swap<T: ltdc::Instance>(&mut self, ltdc: &mut Ltdc<'_, T>) -> Result<(), ltdc::Error> { | ||
| 215 | let (_, buf) = self.current(); | ||
| 216 | let frame_buffer = buf.as_ptr(); | ||
| 217 | self.is_buf0 = !self.is_buf0; | ||
| 218 | ltdc.set_buffer(self.layer_config.layer, frame_buffer as *const _).await | ||
| 219 | } | ||
| 220 | |||
| 221 | /// Clears the buffer | ||
| 222 | pub fn clear(&mut self) { | ||
| 223 | let (color_map, buf) = self.current(); | ||
| 224 | let black = Rgb888::new(0, 0, 0).into_storage(); | ||
| 225 | let color_index = color_map.get(&black).expect("no black found in the color map"); | ||
| 226 | |||
| 227 | for a in buf.iter_mut() { | ||
| 228 | *a = *color_index; // solid black | ||
| 229 | } | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | // Implement DrawTarget for | ||
| 234 | impl DrawTarget for DoubleBuffer { | ||
| 235 | type Color = Rgb888; | ||
| 236 | type Error = (); | ||
| 237 | |||
| 238 | /// Draw a pixel | ||
| 239 | fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error> | ||
| 240 | where | ||
| 241 | I: IntoIterator<Item = Pixel<Self::Color>>, | ||
| 242 | { | ||
| 243 | let size = self.size(); | ||
| 244 | let width = size.width as i32; | ||
| 245 | let height = size.height as i32; | ||
| 246 | let (color_map, buf) = self.current(); | ||
| 247 | |||
| 248 | for pixel in pixels { | ||
| 249 | let Pixel(point, color) = pixel; | ||
| 250 | |||
| 251 | if point.x >= 0 && point.y >= 0 && point.x < width && point.y < height { | ||
| 252 | let index = point.y * width + point.x; | ||
| 253 | let raw_color = color.into_storage(); | ||
| 254 | |||
| 255 | match color_map.get(&raw_color) { | ||
| 256 | Some(x) => { | ||
| 257 | buf[index as usize] = *x; | ||
| 258 | } | ||
| 259 | None => panic!("color not found in color map: {}", raw_color), | ||
| 260 | }; | ||
| 261 | } else { | ||
| 262 | // Ignore invalid points | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | Ok(()) | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | impl OriginDimensions for DoubleBuffer { | ||
| 271 | /// Return the size of the display | ||
| 272 | fn size(&self) -> Size { | ||
| 273 | Size::new( | ||
| 274 | (self.layer_config.window_x1 - self.layer_config.window_x0) as _, | ||
| 275 | (self.layer_config.window_y1 - self.layer_config.window_y0) as _, | ||
| 276 | ) | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | mod rcc_setup { | ||
| 281 | |||
| 282 | use embassy_stm32::rcc::{Hse, HseMode, *}; | ||
| 283 | use embassy_stm32::time::Hertz; | ||
| 284 | use embassy_stm32::{Config, Peripherals}; | ||
| 285 | |||
| 286 | /// Sets up clocks for the stm32h735g mcu | ||
| 287 | /// change this if you plan to use a different microcontroller | ||
| 288 | pub fn stm32h735g_init() -> Peripherals { | ||
| 289 | /* | ||
| 290 | https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Projects/STM32H735G-DK/Examples/GPIO/GPIO_EXTI/Src/main.c | ||
| 291 | @brief System Clock Configuration | ||
| 292 | The system Clock is configured as follow : | ||
| 293 | System Clock source = PLL (HSE) | ||
| 294 | SYSCLK(Hz) = 520000000 (CPU Clock) | ||
| 295 | HCLK(Hz) = 260000000 (AXI and AHBs Clock) | ||
| 296 | AHB Prescaler = 2 | ||
| 297 | D1 APB3 Prescaler = 2 (APB3 Clock 130MHz) | ||
| 298 | D2 APB1 Prescaler = 2 (APB1 Clock 130MHz) | ||
| 299 | D2 APB2 Prescaler = 2 (APB2 Clock 130MHz) | ||
| 300 | D3 APB4 Prescaler = 2 (APB4 Clock 130MHz) | ||
| 301 | HSE Frequency(Hz) = 25000000 | ||
| 302 | PLL_M = 5 | ||
| 303 | PLL_N = 104 | ||
| 304 | PLL_P = 1 | ||
| 305 | PLL_Q = 4 | ||
| 306 | PLL_R = 2 | ||
| 307 | VDD(V) = 3.3 | ||
| 308 | Flash Latency(WS) = 3 | ||
| 309 | */ | ||
| 310 | |||
| 311 | // setup power and clocks for an stm32h735g-dk run from an external 25 Mhz external oscillator | ||
| 312 | let mut config = Config::default(); | ||
| 313 | config.rcc.hse = Some(Hse { | ||
| 314 | freq: Hertz::mhz(25), | ||
| 315 | mode: HseMode::Oscillator, | ||
| 316 | }); | ||
| 317 | config.rcc.hsi = None; | ||
| 318 | config.rcc.csi = false; | ||
| 319 | config.rcc.pll1 = Some(Pll { | ||
| 320 | source: PllSource::HSE, | ||
| 321 | prediv: PllPreDiv::DIV5, // PLL_M | ||
| 322 | mul: PllMul::MUL104, // PLL_N | ||
| 323 | divp: Some(PllDiv::DIV1), | ||
| 324 | divq: Some(PllDiv::DIV4), | ||
| 325 | divr: Some(PllDiv::DIV2), | ||
| 326 | }); | ||
| 327 | // numbers adapted from Drivers/BSP/STM32H735G-DK/stm32h735g_discovery_ospi.c | ||
| 328 | // MX_OSPI_ClockConfig | ||
| 329 | config.rcc.pll2 = Some(Pll { | ||
| 330 | source: PllSource::HSE, | ||
| 331 | prediv: PllPreDiv::DIV5, // PLL_M | ||
| 332 | mul: PllMul::MUL80, // PLL_N | ||
| 333 | divp: Some(PllDiv::DIV5), | ||
| 334 | divq: Some(PllDiv::DIV2), | ||
| 335 | divr: Some(PllDiv::DIV2), | ||
| 336 | }); | ||
| 337 | // numbers adapted from Drivers/BSP/STM32H735G-DK/stm32h735g_discovery_lcd.c | ||
| 338 | // MX_LTDC_ClockConfig | ||
| 339 | config.rcc.pll3 = Some(Pll { | ||
| 340 | source: PllSource::HSE, | ||
| 341 | prediv: PllPreDiv::DIV5, // PLL_M | ||
| 342 | mul: PllMul::MUL160, // PLL_N | ||
| 343 | divp: Some(PllDiv::DIV2), | ||
| 344 | divq: Some(PllDiv::DIV2), | ||
| 345 | divr: Some(PllDiv::DIV83), | ||
| 346 | }); | ||
| 347 | config.rcc.voltage_scale = VoltageScale::Scale0; | ||
| 348 | config.rcc.supply_config = SupplyConfig::DirectSMPS; | ||
| 349 | config.rcc.sys = Sysclk::PLL1_P; | ||
| 350 | config.rcc.ahb_pre = AHBPrescaler::DIV2; | ||
| 351 | config.rcc.apb1_pre = APBPrescaler::DIV2; | ||
| 352 | config.rcc.apb2_pre = APBPrescaler::DIV2; | ||
| 353 | config.rcc.apb3_pre = APBPrescaler::DIV2; | ||
| 354 | config.rcc.apb4_pre = APBPrescaler::DIV2; | ||
| 355 | embassy_stm32::init(config) | ||
| 356 | } | ||
| 357 | } | ||
| 358 | |||
| 359 | mod bouncy_box { | ||
| 360 | use embedded_graphics::geometry::Point; | ||
| 361 | use embedded_graphics::primitives::Rectangle; | ||
| 362 | |||
| 363 | enum Direction { | ||
| 364 | DownLeft, | ||
| 365 | DownRight, | ||
| 366 | UpLeft, | ||
| 367 | UpRight, | ||
| 368 | } | ||
| 369 | |||
| 370 | pub struct BouncyBox { | ||
| 371 | direction: Direction, | ||
| 372 | child_rect: Rectangle, | ||
| 373 | parent_rect: Rectangle, | ||
| 374 | current_point: Point, | ||
| 375 | move_by: usize, | ||
| 376 | } | ||
| 377 | |||
| 378 | // This calculates the coordinates of a chile rectangle bounced around inside a parent bounded box | ||
| 379 | impl BouncyBox { | ||
| 380 | pub fn new(child_rect: Rectangle, parent_rect: Rectangle, move_by: usize) -> Self { | ||
| 381 | let center_box = parent_rect.center(); | ||
| 382 | let center_img = child_rect.center(); | ||
| 383 | let current_point = Point::new(center_box.x - center_img.x / 2, center_box.y - center_img.y / 2); | ||
| 384 | Self { | ||
| 385 | direction: Direction::DownRight, | ||
| 386 | child_rect, | ||
| 387 | parent_rect, | ||
| 388 | current_point, | ||
| 389 | move_by, | ||
| 390 | } | ||
| 391 | } | ||
| 392 | |||
| 393 | pub fn next_point(&mut self) -> Point { | ||
| 394 | let direction = &self.direction; | ||
| 395 | let img_height = self.child_rect.size.height as i32; | ||
| 396 | let box_height = self.parent_rect.size.height as i32; | ||
| 397 | let img_width = self.child_rect.size.width as i32; | ||
| 398 | let box_width = self.parent_rect.size.width as i32; | ||
| 399 | let move_by = self.move_by as i32; | ||
| 400 | |||
| 401 | match direction { | ||
| 402 | Direction::DownLeft => { | ||
| 403 | self.current_point.x -= move_by; | ||
| 404 | self.current_point.y += move_by; | ||
| 405 | |||
| 406 | let x_out_of_bounds = self.current_point.x < 0; | ||
| 407 | let y_out_of_bounds = (self.current_point.y + img_height) > box_height; | ||
| 408 | |||
| 409 | if x_out_of_bounds && y_out_of_bounds { | ||
| 410 | self.direction = Direction::UpRight | ||
| 411 | } else if x_out_of_bounds && !y_out_of_bounds { | ||
| 412 | self.direction = Direction::DownRight | ||
| 413 | } else if !x_out_of_bounds && y_out_of_bounds { | ||
| 414 | self.direction = Direction::UpLeft | ||
| 415 | } | ||
| 416 | } | ||
| 417 | Direction::DownRight => { | ||
| 418 | self.current_point.x += move_by; | ||
| 419 | self.current_point.y += move_by; | ||
| 420 | |||
| 421 | let x_out_of_bounds = (self.current_point.x + img_width) > box_width; | ||
| 422 | let y_out_of_bounds = (self.current_point.y + img_height) > box_height; | ||
| 423 | |||
| 424 | if x_out_of_bounds && y_out_of_bounds { | ||
| 425 | self.direction = Direction::UpLeft | ||
| 426 | } else if x_out_of_bounds && !y_out_of_bounds { | ||
| 427 | self.direction = Direction::DownLeft | ||
| 428 | } else if !x_out_of_bounds && y_out_of_bounds { | ||
| 429 | self.direction = Direction::UpRight | ||
| 430 | } | ||
| 431 | } | ||
| 432 | Direction::UpLeft => { | ||
| 433 | self.current_point.x -= move_by; | ||
| 434 | self.current_point.y -= move_by; | ||
| 435 | |||
| 436 | let x_out_of_bounds = self.current_point.x < 0; | ||
| 437 | let y_out_of_bounds = self.current_point.y < 0; | ||
| 438 | |||
| 439 | if x_out_of_bounds && y_out_of_bounds { | ||
| 440 | self.direction = Direction::DownRight | ||
| 441 | } else if x_out_of_bounds && !y_out_of_bounds { | ||
| 442 | self.direction = Direction::UpRight | ||
| 443 | } else if !x_out_of_bounds && y_out_of_bounds { | ||
| 444 | self.direction = Direction::DownLeft | ||
| 445 | } | ||
| 446 | } | ||
| 447 | Direction::UpRight => { | ||
| 448 | self.current_point.x += move_by; | ||
| 449 | self.current_point.y -= move_by; | ||
| 450 | |||
| 451 | let x_out_of_bounds = (self.current_point.x + img_width) > box_width; | ||
| 452 | let y_out_of_bounds = self.current_point.y < 0; | ||
| 453 | |||
| 454 | if x_out_of_bounds && y_out_of_bounds { | ||
| 455 | self.direction = Direction::DownLeft | ||
| 456 | } else if x_out_of_bounds && !y_out_of_bounds { | ||
| 457 | self.direction = Direction::UpLeft | ||
| 458 | } else if !x_out_of_bounds && y_out_of_bounds { | ||
| 459 | self.direction = Direction::DownRight | ||
| 460 | } | ||
| 461 | } | ||
| 462 | } | ||
| 463 | |||
| 464 | self.current_point | ||
| 465 | } | ||
| 466 | } | ||
| 467 | } | ||
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" | |||
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | # Change stm32l072cz to your chip name, if necessary. | 8 | # Change stm32l072cz to your chip name, if necessary. |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } |
| 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } | 10 | embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } |
| 11 | embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } | 11 | embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } |
| 12 | embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 12 | 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..a54b28a93 --- /dev/null +++ b/examples/stm32l0/src/bin/dds.rs | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::option::Option::Some; | ||
| 5 | |||
| 6 | use defmt::info; | ||
| 7 | use defmt_rtt as _; // global logger | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_stm32::gpio::OutputType; | ||
| 10 | use embassy_stm32::rcc::*; | ||
| 11 | use embassy_stm32::time::hz; | ||
| 12 | use embassy_stm32::timer::low_level::{Timer as LLTimer, *}; | ||
| 13 | use embassy_stm32::timer::simple_pwm::PwmPin; | ||
| 14 | use embassy_stm32::timer::Channel; | ||
| 15 | use embassy_stm32::{interrupt, pac, Config}; | ||
| 16 | use panic_probe as _; | ||
| 17 | |||
| 18 | const DDS_SINE_DATA: [u8; 256] = [ | ||
| 19 | 0x80, 0x83, 0x86, 0x89, 0x8c, 0x8f, 0x92, 0x95, 0x98, 0x9c, 0x9f, 0xa2, 0xa5, 0xa8, 0xab, 0xae, 0xb0, 0xb3, 0xb6, | ||
| 20 | 0xb9, 0xbc, 0xbf, 0xc1, 0xc4, 0xc7, 0xc9, 0xcc, 0xce, 0xd1, 0xd3, 0xd5, 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, | ||
| 21 | 0xe6, 0xe8, 0xea, 0xec, 0xed, 0xef, 0xf0, 0xf2, 0xf3, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, | ||
| 22 | 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfc, 0xfc, 0xfb, | ||
| 23 | 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf3, 0xf2, 0xf0, 0xef, 0xed, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe2, 0xe0, 0xde, | ||
| 24 | 0xdc, 0xda, 0xd8, 0xd5, 0xd3, 0xd1, 0xce, 0xcc, 0xc9, 0xc7, 0xc4, 0xc1, 0xbf, 0xbc, 0xb9, 0xb6, 0xb3, 0xb0, 0xae, | ||
| 25 | 0xab, 0xa8, 0xa5, 0xa2, 0x9f, 0x9c, 0x98, 0x95, 0x92, 0x8f, 0x8c, 0x89, 0x86, 0x83, 0x80, 0x7c, 0x79, 0x76, 0x73, | ||
| 26 | 0x70, 0x6d, 0x6a, 0x67, 0x63, 0x60, 0x5d, 0x5a, 0x57, 0x54, 0x51, 0x4f, 0x4c, 0x49, 0x46, 0x43, 0x40, 0x3e, 0x3b, | ||
| 27 | 0x38, 0x36, 0x33, 0x31, 0x2e, 0x2c, 0x2a, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x12, | ||
| 28 | 0x10, 0x0f, 0x0d, 0x0c, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, | ||
| 29 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, | ||
| 30 | 0x0a, 0x0c, 0x0d, 0x0f, 0x10, 0x12, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x21, 0x23, 0x25, 0x27, 0x2a, 0x2c, | ||
| 31 | 0x2e, 0x31, 0x33, 0x36, 0x38, 0x3b, 0x3e, 0x40, 0x43, 0x46, 0x49, 0x4c, 0x4f, 0x51, 0x54, 0x57, 0x5a, 0x5d, 0x60, | ||
| 32 | 0x63, 0x67, 0x6a, 0x6d, 0x70, 0x73, 0x76, 0x79, 0x7c, | ||
| 33 | ]; | ||
| 34 | |||
| 35 | // frequency: 15625/(256/(DDS_INCR/2**24)) = 999,99999Hz | ||
| 36 | static mut DDS_INCR: u32 = 0x10624DD2; | ||
| 37 | |||
| 38 | // fractional phase accumulator | ||
| 39 | static mut DDS_AKKU: u32 = 0x00000000; | ||
| 40 | |||
| 41 | #[interrupt] | ||
| 42 | fn TIM2() { | ||
| 43 | unsafe { | ||
| 44 | // get next value of DDS | ||
| 45 | DDS_AKKU = DDS_AKKU.wrapping_add(DDS_INCR); | ||
| 46 | let value = (DDS_SINE_DATA[(DDS_AKKU >> 24) as usize] as u16) << 3; | ||
| 47 | |||
| 48 | // set new output compare value | ||
| 49 | pac::TIM2.ccr(2).modify(|w| w.set_ccr(value)); | ||
| 50 | |||
| 51 | // reset interrupt flag | ||
| 52 | pac::TIM2.sr().modify(|r| r.set_uif(false)); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | #[embassy_executor::main] | ||
| 57 | async fn main(_spawner: Spawner) { | ||
| 58 | info!("Hello World!"); | ||
| 59 | |||
| 60 | // configure for 32MHz (HSI16 * 6 / 3) | ||
| 61 | let mut config = Config::default(); | ||
| 62 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 63 | config.rcc.hsi = true; | ||
| 64 | config.rcc.pll = Some(Pll { | ||
| 65 | source: PllSource::HSI, | ||
| 66 | div: PllDiv::DIV3, | ||
| 67 | mul: PllMul::MUL6, | ||
| 68 | }); | ||
| 69 | |||
| 70 | let p = embassy_stm32::init(config); | ||
| 71 | |||
| 72 | // setup PWM pin in AF mode | ||
| 73 | let _ch3 = PwmPin::new_ch3(p.PA2, OutputType::PushPull); | ||
| 74 | |||
| 75 | // initialize timer | ||
| 76 | // we cannot use SimplePWM here because the Time is privately encapsulated | ||
| 77 | let timer = LLTimer::new(p.TIM2); | ||
| 78 | |||
| 79 | // set counting mode | ||
| 80 | timer.set_counting_mode(CountingMode::EdgeAlignedUp); | ||
| 81 | |||
| 82 | // set pwm sample frequency | ||
| 83 | timer.set_frequency(hz(15625)); | ||
| 84 | |||
| 85 | // enable outputs | ||
| 86 | timer.enable_outputs(); | ||
| 87 | |||
| 88 | // start timer | ||
| 89 | timer.start(); | ||
| 90 | |||
| 91 | // set output compare mode | ||
| 92 | timer.set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); | ||
| 93 | |||
| 94 | // set output compare preload | ||
| 95 | timer.set_output_compare_preload(Channel::Ch3, true); | ||
| 96 | |||
| 97 | // set output polarity | ||
| 98 | timer.set_output_polarity(Channel::Ch3, OutputPolarity::ActiveHigh); | ||
| 99 | |||
| 100 | // set compare value | ||
| 101 | timer.set_compare_value(Channel::Ch3, timer.get_max_compare_value() / 2); | ||
| 102 | |||
| 103 | // enable pwm channel | ||
| 104 | timer.enable_channel(Channel::Ch3, true); | ||
| 105 | |||
| 106 | // enable timer interrupts | ||
| 107 | timer.enable_update_interrupt(true); | ||
| 108 | unsafe { cortex_m::peripheral::NVIC::unmask(interrupt::TIM2) }; | ||
| 109 | |||
| 110 | async { | ||
| 111 | loop { | ||
| 112 | embassy_time::Timer::after_millis(5000).await; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | .await; | ||
| 116 | } | ||
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() -> ! { | |||
| 23 | let mut channel = p.PC0; | 23 | let mut channel = p.PC0; |
| 24 | 24 | ||
| 25 | loop { | 25 | loop { |
| 26 | let v = adc.read(&mut channel); | 26 | let v = adc.blocking_read(&mut channel); |
| 27 | info!("--> {}", v); | 27 | info!("--> {}", v); |
| 28 | } | 28 | } |
| 29 | } | 29 | } |
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() -> ! { | |||
| 23 | let mut channel = p.PC0; | 23 | let mut channel = p.PC0; |
| 24 | 24 | ||
| 25 | loop { | 25 | loop { |
| 26 | let v = adc.read(&mut channel); | 26 | let v = adc.blocking_read(&mut channel); |
| 27 | info!("--> {}", v); | 27 | info!("--> {}", v); |
| 28 | embassy_time::block_for(Duration::from_millis(200)); | 28 | embassy_time::block_for(Duration::from_millis(200)); |
| 29 | } | 29 | } |
diff --git a/examples/stm32u5/src/bin/tsc.rs b/examples/stm32u5/src/bin/tsc.rs index f5593d1c4..eb15d275a 100644 --- a/examples/stm32u5/src/bin/tsc.rs +++ b/examples/stm32u5/src/bin/tsc.rs | |||
| @@ -2,10 +2,15 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_stm32::bind_interrupts; | ||
| 5 | use embassy_stm32::tsc::{self, *}; | 6 | use embassy_stm32::tsc::{self, *}; |
| 6 | use embassy_time::Timer; | 7 | use embassy_time::Timer; |
| 7 | use {defmt_rtt as _, panic_probe as _}; | 8 | use {defmt_rtt as _, panic_probe as _}; |
| 8 | 9 | ||
| 10 | bind_interrupts!(struct Irqs { | ||
| 11 | TSC => InterruptHandler<embassy_stm32::peripherals::TSC>; | ||
| 12 | }); | ||
| 13 | |||
| 9 | #[cortex_m_rt::exception] | 14 | #[cortex_m_rt::exception] |
| 10 | unsafe fn HardFault(_: &cortex_m_rt::ExceptionFrame) -> ! { | 15 | unsafe fn HardFault(_: &cortex_m_rt::ExceptionFrame) -> ! { |
| 11 | cortex_m::peripheral::SCB::sys_reset(); | 16 | cortex_m::peripheral::SCB::sys_reset(); |
| @@ -45,7 +50,7 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 45 | g7.set_io2(context.PE3, PinType::Sample); | 50 | g7.set_io2(context.PE3, PinType::Sample); |
| 46 | g7.set_io3(context.PE4, PinType::Channel); | 51 | g7.set_io3(context.PE4, PinType::Channel); |
| 47 | 52 | ||
| 48 | let mut touch_controller = tsc::Tsc::new( | 53 | let mut touch_controller = tsc::Tsc::new_async( |
| 49 | context.TSC, | 54 | context.TSC, |
| 50 | Some(g1), | 55 | Some(g1), |
| 51 | Some(g2), | 56 | Some(g2), |
| @@ -56,6 +61,7 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 56 | Some(g7), | 61 | Some(g7), |
| 57 | None, | 62 | None, |
| 58 | config, | 63 | config, |
| 64 | Irqs, | ||
| 59 | ); | 65 | ); |
| 60 | 66 | ||
| 61 | touch_controller.discharge_io(true); | 67 | touch_controller.discharge_io(true); |
| @@ -67,7 +73,7 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 67 | let mut group_seven_val = 0; | 73 | let mut group_seven_val = 0; |
| 68 | info!("Starting touch_controller interface"); | 74 | info!("Starting touch_controller interface"); |
| 69 | loop { | 75 | loop { |
| 70 | touch_controller.poll_for_acquisition(); | 76 | touch_controller.pend_for_acquisition().await; |
| 71 | touch_controller.discharge_io(true); | 77 | touch_controller.discharge_io(true); |
| 72 | Timer::after_millis(1).await; | 78 | Timer::after_millis(1).await; |
| 73 | 79 | ||
