diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2023-05-01 21:36:10 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-01 21:36:10 +0000 |
| commit | 6096f0cf4b5ef45b97665166be41bfd490748f40 (patch) | |
| tree | 3e19fd1bacd3cb2d4276eefc224a81ed6c004ff0 /tests/stm32/src/bin | |
| parent | 855c0d1423cb1aacd4f4f45e255b02b442afde34 (diff) | |
| parent | a1d45303c336434929eb8eb7e55629c504a95b0e (diff) | |
Merge #1404
1404: feat(stm32): Add DMA based, ring-buffer based rx uart, v3 r=Dirbaio a=rmja
This PR replaces #1150. Comparing to that PR, this one has the following changes:
* The implementation now aligns with the new stm32 dma module, thanks `@Dirbaio!`
* Calls to `read()` now returns on either 1) idle line, or 2) ring buffer is at most half full. This is different from the previous pr, which would return a lot of 1 byte reads. Thank you `@chemicstry` for making me realize that it was actually not what I wanted. This is accomplished using half-transfer completed and full-transfer completed interrupts. Both seems to be supported on both dma and bdma.
The implementation still have the issue mentioned here: https://github.com/embassy-rs/embassy/pull/1150#discussion_r1094627035
Regarding the todos here: https://github.com/embassy-rs/embassy/pull/1150#issuecomment-1513905925. I have removed the exposure of ndtr from `dma::RingBuffer` to the uart so that the uart now simply calls `ringbuf::reload_position()` to align the position within the ring buffer to that of the actual running dma controller. BDMA and GPDMA is not implemented. I do not have any chips with those dma controllers, so maybe someone else should to this so that it can be tested.
The `saturate_serial` test utility inside `tests/utils` has an `--idles` switch which can be used to saturate the uart from a pc, but with random idles.
Because embassy-stm32 now can have tests, we should probably run them in ci. I do this locally to test the DmaRingBuffer: `cargo test --no-default-features --features stm32f429ig`.
cc `@chemicstry` `@Dirbaio`
Co-authored-by: Rasmus Melchior Jacobsen <[email protected]>
Co-authored-by: Dario Nieuwenhuis <[email protected]>
Diffstat (limited to 'tests/stm32/src/bin')
| -rw-r--r-- | tests/stm32/src/bin/usart_dma.rs | 27 | ||||
| -rw-r--r-- | tests/stm32/src/bin/usart_rx_ringbuffered.rs | 200 |
2 files changed, 218 insertions, 9 deletions
diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index d673df0f3..de6cd41d1 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | mod example_common; | 6 | mod example_common; |
| 7 | use defmt::assert_eq; | 7 | use defmt::assert_eq; |
| 8 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 9 | use embassy_futures::join::join; | ||
| 9 | use embassy_stm32::interrupt; | 10 | use embassy_stm32::interrupt; |
| 10 | use embassy_stm32::usart::{Config, Uart}; | 11 | use embassy_stm32::usart::{Config, Uart}; |
| 11 | use example_common::*; | 12 | use example_common::*; |
| @@ -76,18 +77,26 @@ async fn main(_spawner: Spawner) { | |||
| 76 | (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); | 77 | (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); |
| 77 | 78 | ||
| 78 | let config = Config::default(); | 79 | let config = Config::default(); |
| 79 | let mut usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); | 80 | let usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); |
| 80 | 81 | ||
| 81 | // We can't send too many bytes, they have to fit in the FIFO. | 82 | const LEN: usize = 128; |
| 82 | // This is because we aren't sending+receiving at the same time. | 83 | let mut tx_buf = [0; LEN]; |
| 83 | // For whatever reason, blocking works with 2 bytes but DMA only with 1?? | 84 | let mut rx_buf = [0; LEN]; |
| 85 | for i in 0..LEN { | ||
| 86 | tx_buf[i] = i as u8; | ||
| 87 | } | ||
| 84 | 88 | ||
| 85 | let data = [0x42]; | 89 | let (mut tx, mut rx) = usart.split(); |
| 86 | usart.write(&data).await.unwrap(); | ||
| 87 | 90 | ||
| 88 | let mut buf = [0; 1]; | 91 | let tx_fut = async { |
| 89 | usart.read(&mut buf).await.unwrap(); | 92 | tx.write(&tx_buf).await.unwrap(); |
| 90 | assert_eq!(buf, data); | 93 | }; |
| 94 | let rx_fut = async { | ||
| 95 | rx.read(&mut rx_buf).await.unwrap(); | ||
| 96 | }; | ||
| 97 | join(rx_fut, tx_fut).await; | ||
| 98 | |||
| 99 | assert_eq!(tx_buf, rx_buf); | ||
| 91 | 100 | ||
| 92 | info!("Test OK"); | 101 | info!("Test OK"); |
| 93 | cortex_m::asm::bkpt(); | 102 | cortex_m::asm::bkpt(); |
diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs new file mode 100644 index 000000000..2c4a8fdf4 --- /dev/null +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs | |||
| @@ -0,0 +1,200 @@ | |||
| 1 | // required-features: not-gpdma | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | |||
| 7 | #[path = "../example_common.rs"] | ||
| 8 | mod example_common; | ||
| 9 | use defmt::{assert_eq, panic}; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_stm32::interrupt; | ||
| 12 | use embassy_stm32::usart::{Config, DataBits, Parity, RingBufferedUartRx, StopBits, Uart, UartTx}; | ||
| 13 | use embassy_time::{Duration, Timer}; | ||
| 14 | use example_common::*; | ||
| 15 | use rand_chacha::ChaCha8Rng; | ||
| 16 | use rand_core::{RngCore, SeedableRng}; | ||
| 17 | |||
| 18 | #[cfg(feature = "stm32f103c8")] | ||
| 19 | mod board { | ||
| 20 | pub type Uart = embassy_stm32::peripherals::USART1; | ||
| 21 | pub type TxDma = embassy_stm32::peripherals::DMA1_CH4; | ||
| 22 | pub type RxDma = embassy_stm32::peripherals::DMA1_CH5; | ||
| 23 | } | ||
| 24 | #[cfg(feature = "stm32g491re")] | ||
| 25 | mod board { | ||
| 26 | pub type Uart = embassy_stm32::peripherals::USART1; | ||
| 27 | pub type TxDma = embassy_stm32::peripherals::DMA1_CH1; | ||
| 28 | pub type RxDma = embassy_stm32::peripherals::DMA1_CH2; | ||
| 29 | } | ||
| 30 | #[cfg(feature = "stm32g071rb")] | ||
| 31 | mod board { | ||
| 32 | pub type Uart = embassy_stm32::peripherals::USART1; | ||
| 33 | pub type TxDma = embassy_stm32::peripherals::DMA1_CH1; | ||
| 34 | pub type RxDma = embassy_stm32::peripherals::DMA1_CH2; | ||
| 35 | } | ||
| 36 | #[cfg(feature = "stm32f429zi")] | ||
| 37 | mod board { | ||
| 38 | pub type Uart = embassy_stm32::peripherals::USART6; | ||
| 39 | pub type TxDma = embassy_stm32::peripherals::DMA2_CH6; | ||
| 40 | pub type RxDma = embassy_stm32::peripherals::DMA2_CH1; | ||
| 41 | } | ||
| 42 | #[cfg(feature = "stm32wb55rg")] | ||
| 43 | mod board { | ||
| 44 | pub type Uart = embassy_stm32::peripherals::LPUART1; | ||
| 45 | pub type TxDma = embassy_stm32::peripherals::DMA1_CH1; | ||
| 46 | pub type RxDma = embassy_stm32::peripherals::DMA1_CH2; | ||
| 47 | } | ||
| 48 | #[cfg(feature = "stm32h755zi")] | ||
| 49 | mod board { | ||
| 50 | pub type Uart = embassy_stm32::peripherals::USART1; | ||
| 51 | pub type TxDma = embassy_stm32::peripherals::DMA1_CH0; | ||
| 52 | pub type RxDma = embassy_stm32::peripherals::DMA1_CH1; | ||
| 53 | } | ||
| 54 | #[cfg(feature = "stm32u585ai")] | ||
| 55 | mod board { | ||
| 56 | pub type Uart = embassy_stm32::peripherals::USART3; | ||
| 57 | pub type TxDma = embassy_stm32::peripherals::GPDMA1_CH0; | ||
| 58 | pub type RxDma = embassy_stm32::peripherals::GPDMA1_CH1; | ||
| 59 | } | ||
| 60 | #[cfg(feature = "stm32c031c6")] | ||
| 61 | mod board { | ||
| 62 | pub type Uart = embassy_stm32::peripherals::USART1; | ||
| 63 | pub type TxDma = embassy_stm32::peripherals::DMA1_CH1; | ||
| 64 | pub type RxDma = embassy_stm32::peripherals::DMA1_CH2; | ||
| 65 | } | ||
| 66 | |||
| 67 | const DMA_BUF_SIZE: usize = 256; | ||
| 68 | |||
| 69 | #[embassy_executor::main] | ||
| 70 | async fn main(spawner: Spawner) { | ||
| 71 | let p = embassy_stm32::init(config()); | ||
| 72 | info!("Hello World!"); | ||
| 73 | |||
| 74 | // Arduino pins D0 and D1 | ||
| 75 | // They're connected together with a 1K resistor. | ||
| 76 | #[cfg(feature = "stm32f103c8")] | ||
| 77 | let (tx, rx, usart, irq, tx_dma, rx_dma) = ( | ||
| 78 | p.PA9, | ||
| 79 | p.PA10, | ||
| 80 | p.USART1, | ||
| 81 | interrupt::take!(USART1), | ||
| 82 | p.DMA1_CH4, | ||
| 83 | p.DMA1_CH5, | ||
| 84 | ); | ||
| 85 | #[cfg(feature = "stm32g491re")] | ||
| 86 | let (tx, rx, usart, irq, tx_dma, rx_dma) = | ||
| 87 | (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); | ||
| 88 | #[cfg(feature = "stm32g071rb")] | ||
| 89 | let (tx, rx, usart, irq, tx_dma, rx_dma) = | ||
| 90 | (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); | ||
| 91 | #[cfg(feature = "stm32f429zi")] | ||
| 92 | let (tx, rx, usart, irq, tx_dma, rx_dma) = ( | ||
| 93 | p.PG14, | ||
| 94 | p.PG9, | ||
| 95 | p.USART6, | ||
| 96 | interrupt::take!(USART6), | ||
| 97 | p.DMA2_CH6, | ||
| 98 | p.DMA2_CH1, | ||
| 99 | ); | ||
| 100 | #[cfg(feature = "stm32wb55rg")] | ||
| 101 | let (tx, rx, usart, irq, tx_dma, rx_dma) = ( | ||
| 102 | p.PA2, | ||
| 103 | p.PA3, | ||
| 104 | p.LPUART1, | ||
| 105 | interrupt::take!(LPUART1), | ||
| 106 | p.DMA1_CH1, | ||
| 107 | p.DMA1_CH2, | ||
| 108 | ); | ||
| 109 | #[cfg(feature = "stm32h755zi")] | ||
| 110 | let (tx, rx, usart, irq, tx_dma, rx_dma) = | ||
| 111 | (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH0, p.DMA1_CH1); | ||
| 112 | #[cfg(feature = "stm32u585ai")] | ||
| 113 | let (tx, rx, usart, irq, tx_dma, rx_dma) = ( | ||
| 114 | p.PD8, | ||
| 115 | p.PD9, | ||
| 116 | p.USART3, | ||
| 117 | interrupt::take!(USART3), | ||
| 118 | p.GPDMA1_CH0, | ||
| 119 | p.GPDMA1_CH1, | ||
| 120 | ); | ||
| 121 | #[cfg(feature = "stm32c031c6")] | ||
| 122 | let (tx, rx, usart, irq, tx_dma, rx_dma) = | ||
| 123 | (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); | ||
| 124 | |||
| 125 | // To run this test, use the saturating_serial test utility to saturate the serial port | ||
| 126 | |||
| 127 | let mut config = Config::default(); | ||
| 128 | // this is the fastest we can go without tuning RCC | ||
| 129 | // some chips have default pclk=8mhz, and uart can run at max pclk/16 | ||
| 130 | config.baudrate = 500_000; | ||
| 131 | config.data_bits = DataBits::DataBits8; | ||
| 132 | config.stop_bits = StopBits::STOP1; | ||
| 133 | config.parity = Parity::ParityNone; | ||
| 134 | |||
| 135 | let usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); | ||
| 136 | let (tx, rx) = usart.split(); | ||
| 137 | static mut DMA_BUF: [u8; DMA_BUF_SIZE] = [0; DMA_BUF_SIZE]; | ||
| 138 | let dma_buf = unsafe { DMA_BUF.as_mut() }; | ||
| 139 | let rx = rx.into_ring_buffered(dma_buf); | ||
| 140 | |||
| 141 | info!("Spawning tasks"); | ||
| 142 | spawner.spawn(transmit_task(tx)).unwrap(); | ||
| 143 | spawner.spawn(receive_task(rx)).unwrap(); | ||
| 144 | } | ||
| 145 | |||
| 146 | #[embassy_executor::task] | ||
| 147 | async fn transmit_task(mut tx: UartTx<'static, board::Uart, board::TxDma>) { | ||
| 148 | let mut rng = ChaCha8Rng::seed_from_u64(1337); | ||
| 149 | |||
| 150 | info!("Starting random transmissions into void..."); | ||
| 151 | |||
| 152 | let mut i: u8 = 0; | ||
| 153 | loop { | ||
| 154 | let mut buf = [0; 32]; | ||
| 155 | let len = 1 + (rng.next_u32() as usize % buf.len()); | ||
| 156 | for b in &mut buf[..len] { | ||
| 157 | *b = i; | ||
| 158 | i = i.wrapping_add(1); | ||
| 159 | } | ||
| 160 | |||
| 161 | tx.write(&buf[..len]).await.unwrap(); | ||
| 162 | Timer::after(Duration::from_micros((rng.next_u32() % 1000) as _)).await; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | #[embassy_executor::task] | ||
| 167 | async fn receive_task(mut rx: RingBufferedUartRx<'static, board::Uart, board::RxDma>) { | ||
| 168 | info!("Ready to receive..."); | ||
| 169 | |||
| 170 | let mut rng = ChaCha8Rng::seed_from_u64(1337); | ||
| 171 | |||
| 172 | let mut i = 0; | ||
| 173 | let mut expected = 0; | ||
| 174 | loop { | ||
| 175 | let mut buf = [0; 100]; | ||
| 176 | let max_len = 1 + (rng.next_u32() as usize % buf.len()); | ||
| 177 | let received = match rx.read(&mut buf[..max_len]).await { | ||
| 178 | Ok(r) => r, | ||
| 179 | Err(e) => { | ||
| 180 | panic!("Test fail! read error: {:?}", e); | ||
| 181 | } | ||
| 182 | }; | ||
| 183 | |||
| 184 | for byte in &buf[..received] { | ||
| 185 | assert_eq!(*byte, expected); | ||
| 186 | expected = expected.wrapping_add(1); | ||
| 187 | } | ||
| 188 | |||
| 189 | if received < max_len { | ||
| 190 | Timer::after(Duration::from_micros((rng.next_u32() % 1000) as _)).await; | ||
| 191 | } | ||
| 192 | |||
| 193 | i += received; | ||
| 194 | |||
| 195 | if i > 100000 { | ||
| 196 | info!("Test OK!"); | ||
| 197 | cortex_m::asm::bkpt(); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | } | ||
