aboutsummaryrefslogtreecommitdiff
path: root/examples/rp/src
diff options
context:
space:
mode:
authorchemicstry <[email protected]>2023-07-31 10:29:20 +0300
committerchemicstry <[email protected]>2023-07-31 10:29:20 +0300
commit780569c08ab089dae67c5d57dd1852d5419d4577 (patch)
tree021965b7c9ac34fb33507c3998b3a6b6e7cecae7 /examples/rp/src
parenta56ef685f3bfd9148a79c9dbbdde83a2c1642ba5 (diff)
parent6c6bd11c1a28eb8985518a10bd723c622d82f4b6 (diff)
Merge remote-tracking branch 'origin/main' into bxcan_timestamp
Diffstat (limited to 'examples/rp/src')
-rw-r--r--examples/rp/src/bin/flash.rs42
-rw-r--r--examples/rp/src/bin/pio_async.rs10
-rw-r--r--examples/rp/src/bin/pio_dma.rs4
-rw-r--r--examples/rp/src/bin/pio_hd44780.rs7
-rw-r--r--examples/rp/src/bin/pio_uart.rs394
-rw-r--r--examples/rp/src/bin/pio_ws2812.rs4
6 files changed, 439 insertions, 22 deletions
diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs
index 4c4982acc..88bb931d2 100644
--- a/examples/rp/src/bin/flash.rs
+++ b/examples/rp/src/bin/flash.rs
@@ -6,7 +6,7 @@
6 6
7use defmt::*; 7use defmt::*;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; 9use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE};
10use embassy_rp::peripherals::FLASH; 10use embassy_rp::peripherals::FLASH;
11use embassy_time::{Duration, Timer}; 11use embassy_time::{Duration, Timer};
12use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
@@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) {
25 // https://github.com/knurling-rs/defmt/pull/683 25 // https://github.com/knurling-rs/defmt/pull/683
26 Timer::after(Duration::from_millis(10)).await; 26 Timer::after(Duration::from_millis(10)).await;
27 27
28 let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); 28 let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0);
29 29
30 // Get JEDEC id 30 // Get JEDEC id
31 let jedec = flash.jedec_id().unwrap(); 31 let jedec = flash.jedec_id().unwrap();
@@ -40,10 +40,12 @@ async fn main(_spawner: Spawner) {
40 40
41 multiwrite_bytes(&mut flash, ERASE_SIZE as u32); 41 multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
42 42
43 background_read(&mut flash, (ERASE_SIZE * 2) as u32).await;
44
43 loop {} 45 loop {}
44} 46}
45 47
46fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { 48fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
47 info!(">>>> [multiwrite_bytes]"); 49 info!(">>>> [multiwrite_bytes]");
48 let mut read_buf = [0u8; ERASE_SIZE]; 50 let mut read_buf = [0u8; ERASE_SIZE];
49 defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); 51 defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf));
@@ -71,7 +73,7 @@ fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>,
71 } 73 }
72} 74}
73 75
74fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { 76fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
75 info!(">>>> [erase_write_sector]"); 77 info!(">>>> [erase_write_sector]");
76 let mut buf = [0u8; ERASE_SIZE]; 78 let mut buf = [0u8; ERASE_SIZE];
77 defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); 79 defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf));
@@ -99,3 +101,35 @@ fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE
99 defmt::panic!("unexpected"); 101 defmt::panic!("unexpected");
100 } 102 }
101} 103}
104
105async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
106 info!(">>>> [background_read]");
107
108 let mut buf = [0u32; 8];
109 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
110
111 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
112 info!("Contents start with {=u32:x}", buf[0]);
113
114 defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
115
116 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
117 info!("Contents after erase starts with {=u32:x}", buf[0]);
118 if buf.iter().any(|x| *x != 0xFFFFFFFF) {
119 defmt::panic!("unexpected");
120 }
121
122 for b in buf.iter_mut() {
123 *b = 0xDABA1234;
124 }
125
126 defmt::unwrap!(flash.write(ADDR_OFFSET + offset, unsafe {
127 core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4)
128 }));
129
130 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
131 info!("Contents after write starts with {=u32:x}", buf[0]);
132 if buf.iter().any(|x| *x != 0xDABA1234) {
133 defmt::panic!("unexpected");
134 }
135}
diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs
index c001d6440..a6d6144be 100644
--- a/examples/rp/src/bin/pio_async.rs
+++ b/examples/rp/src/bin/pio_async.rs
@@ -8,7 +8,6 @@ use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts; 8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; 10use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};
11use embassy_rp::relocate::RelocatedProgram;
12use fixed::traits::ToFixed; 11use fixed::traits::ToFixed;
13use fixed_macro::types::U56F8; 12use fixed_macro::types::U56F8;
14use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
@@ -29,9 +28,8 @@ fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a,
29 ".wrap", 28 ".wrap",
30 ); 29 );
31 30
32 let relocated = RelocatedProgram::new(&prg.program);
33 let mut cfg = Config::default(); 31 let mut cfg = Config::default();
34 cfg.use_program(&pio.load_program(&relocated), &[]); 32 cfg.use_program(&pio.load_program(&prg.program), &[]);
35 let out_pin = pio.make_pio_pin(pin); 33 let out_pin = pio.make_pio_pin(pin);
36 cfg.set_out_pins(&[&out_pin]); 34 cfg.set_out_pins(&[&out_pin]);
37 cfg.set_set_pins(&[&out_pin]); 35 cfg.set_set_pins(&[&out_pin]);
@@ -65,9 +63,8 @@ fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a,
65 ".wrap", 63 ".wrap",
66 ); 64 );
67 65
68 let relocated = RelocatedProgram::new(&prg.program);
69 let mut cfg = Config::default(); 66 let mut cfg = Config::default();
70 cfg.use_program(&pio.load_program(&relocated), &[]); 67 cfg.use_program(&pio.load_program(&prg.program), &[]);
71 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); 68 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
72 cfg.shift_in.auto_fill = true; 69 cfg.shift_in.auto_fill = true;
73 cfg.shift_in.direction = ShiftDirection::Right; 70 cfg.shift_in.direction = ShiftDirection::Right;
@@ -96,9 +93,8 @@ fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a,
96 "irq 3 [15]", 93 "irq 3 [15]",
97 ".wrap", 94 ".wrap",
98 ); 95 );
99 let relocated = RelocatedProgram::new(&prg.program);
100 let mut cfg = Config::default(); 96 let mut cfg = Config::default();
101 cfg.use_program(&pio.load_program(&relocated), &[]); 97 cfg.use_program(&pio.load_program(&prg.program), &[]);
102 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); 98 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
103 sm.set_config(&cfg); 99 sm.set_config(&cfg);
104} 100}
diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs
index 9ab72e1f3..86e5017ac 100644
--- a/examples/rp/src/bin/pio_dma.rs
+++ b/examples/rp/src/bin/pio_dma.rs
@@ -8,7 +8,6 @@ use embassy_executor::Spawner;
8use embassy_futures::join::join; 8use embassy_futures::join::join;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; 10use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
11use embassy_rp::relocate::RelocatedProgram;
12use embassy_rp::{bind_interrupts, Peripheral}; 11use embassy_rp::{bind_interrupts, Peripheral};
13use fixed::traits::ToFixed; 12use fixed::traits::ToFixed;
14use fixed_macro::types::U56F8; 13use fixed_macro::types::U56F8;
@@ -46,9 +45,8 @@ async fn main(_spawner: Spawner) {
46 ".wrap", 45 ".wrap",
47 ); 46 );
48 47
49 let relocated = RelocatedProgram::new(&prg.program);
50 let mut cfg = Config::default(); 48 let mut cfg = Config::default();
51 cfg.use_program(&common.load_program(&relocated), &[]); 49 cfg.use_program(&common.load_program(&prg.program), &[]);
52 cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed(); 50 cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();
53 cfg.shift_in = ShiftConfig { 51 cfg.shift_in = ShiftConfig {
54 auto_fill: true, 52 auto_fill: true,
diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs
index 8aedd24b6..d80c5c24b 100644
--- a/examples/rp/src/bin/pio_hd44780.rs
+++ b/examples/rp/src/bin/pio_hd44780.rs
@@ -14,7 +14,6 @@ use embassy_rp::pio::{
14 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 14 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
15}; 15};
16use embassy_rp::pwm::{self, Pwm}; 16use embassy_rp::pwm::{self, Pwm};
17use embassy_rp::relocate::RelocatedProgram;
18use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; 17use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
19use embassy_time::{Duration, Instant, Timer}; 18use embassy_time::{Duration, Instant, Timer};
20use {defmt_rtt as _, panic_probe as _}; 19use {defmt_rtt as _, panic_probe as _};
@@ -127,9 +126,8 @@ impl<'l> HD44780<'l> {
127 126
128 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); 127 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
129 128
130 let relocated = RelocatedProgram::new(&prg.program);
131 let mut cfg = Config::default(); 129 let mut cfg = Config::default();
132 cfg.use_program(&common.load_program(&relocated), &[&e]); 130 cfg.use_program(&common.load_program(&prg.program), &[&e]);
133 cfg.clock_divider = 125u8.into(); 131 cfg.clock_divider = 125u8.into();
134 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); 132 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
135 cfg.shift_out = ShiftConfig { 133 cfg.shift_out = ShiftConfig {
@@ -201,9 +199,8 @@ impl<'l> HD44780<'l> {
201 "# 199 "#
202 ); 200 );
203 201
204 let relocated = RelocatedProgram::new(&prg.program);
205 let mut cfg = Config::default(); 202 let mut cfg = Config::default();
206 cfg.use_program(&common.load_program(&relocated), &[&e]); 203 cfg.use_program(&common.load_program(&prg.program), &[&e]);
207 cfg.clock_divider = 8u8.into(); // ~64ns/insn 204 cfg.clock_divider = 8u8.into(); // ~64ns/insn
208 cfg.set_jmp_pin(&db7); 205 cfg.set_jmp_pin(&db7);
209 cfg.set_set_pins(&[&rs, &rw]); 206 cfg.set_set_pins(&[&rs, &rw]);
diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs
new file mode 100644
index 000000000..4c382c2ee
--- /dev/null
+++ b/examples/rp/src/bin/pio_uart.rs
@@ -0,0 +1,394 @@
1//! This example shows how to use the PIO module in the RP2040 chip to implement a duplex UART.
2//! The PIO module is a very powerful peripheral that can be used to implement many different
3//! protocols. It is a very flexible state machine that can be programmed to do almost anything.
4//!
5//! This example opens up a USB device that implements a CDC ACM serial port. It then uses the
6//! PIO module to implement a UART that is connected to the USB serial port. This allows you to
7//! communicate with a device connected to the RP2040 over USB serial.
8
9#![no_std]
10#![no_main]
11#![feature(type_alias_impl_trait)]
12#![feature(async_fn_in_trait)]
13
14use defmt::{info, panic, trace};
15use embassy_executor::Spawner;
16use embassy_futures::join::{join, join3};
17use embassy_rp::bind_interrupts;
18use embassy_rp::peripherals::{PIO0, USB};
19use embassy_rp::pio::InterruptHandler as PioInterruptHandler;
20use embassy_rp::usb::{Driver, Instance, InterruptHandler};
21use embassy_sync::blocking_mutex::raw::NoopRawMutex;
22use embassy_sync::pipe::Pipe;
23use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
24use embassy_usb::driver::EndpointError;
25use embassy_usb::{Builder, Config};
26use embedded_io::asynch::{Read, Write};
27use {defmt_rtt as _, panic_probe as _};
28
29use crate::uart::PioUart;
30use crate::uart_rx::PioUartRx;
31use crate::uart_tx::PioUartTx;
32
33bind_interrupts!(struct Irqs {
34 USBCTRL_IRQ => InterruptHandler<USB>;
35 PIO0_IRQ_0 => PioInterruptHandler<PIO0>;
36});
37
38#[embassy_executor::main]
39async fn main(_spawner: Spawner) {
40 info!("Hello there!");
41
42 let p = embassy_rp::init(Default::default());
43
44 // Create the driver, from the HAL.
45 let driver = Driver::new(p.USB, Irqs);
46
47 // Create embassy-usb Config
48 let mut config = Config::new(0xc0de, 0xcafe);
49 config.manufacturer = Some("Embassy");
50 config.product = Some("PIO UART example");
51 config.serial_number = Some("12345678");
52 config.max_power = 100;
53 config.max_packet_size_0 = 64;
54
55 // Required for windows compatibility.
56 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
57 config.device_class = 0xEF;
58 config.device_sub_class = 0x02;
59 config.device_protocol = 0x01;
60 config.composite_with_iads = true;
61
62 // Create embassy-usb DeviceBuilder using the driver and config.
63 // It needs some buffers for building the descriptors.
64 let mut device_descriptor = [0; 256];
65 let mut config_descriptor = [0; 256];
66 let mut bos_descriptor = [0; 256];
67 let mut control_buf = [0; 64];
68
69 let mut state = State::new();
70
71 let mut builder = Builder::new(
72 driver,
73 config,
74 &mut device_descriptor,
75 &mut config_descriptor,
76 &mut bos_descriptor,
77 &mut control_buf,
78 );
79
80 // Create classes on the builder.
81 let class = CdcAcmClass::new(&mut builder, &mut state, 64);
82
83 // Build the builder.
84 let mut usb = builder.build();
85
86 // Run the USB device.
87 let usb_fut = usb.run();
88
89 // PIO UART setup
90 let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5);
91 let (mut uart_tx, mut uart_rx) = uart.split();
92
93 // Pipe setup
94 let usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();
95 let mut usb_pipe_writer = usb_pipe.writer();
96 let mut usb_pipe_reader = usb_pipe.reader();
97
98 let uart_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();
99 let mut uart_pipe_writer = uart_pipe.writer();
100 let mut uart_pipe_reader = uart_pipe.reader();
101
102 let (mut usb_tx, mut usb_rx) = class.split();
103
104 // Read + write from USB
105 let usb_future = async {
106 loop {
107 info!("Wait for USB connection");
108 usb_rx.wait_connection().await;
109 info!("Connected");
110 let _ = join(
111 usb_read(&mut usb_rx, &mut uart_pipe_writer),
112 usb_write(&mut usb_tx, &mut usb_pipe_reader),
113 )
114 .await;
115 info!("Disconnected");
116 }
117 };
118
119 // Read + write from UART
120 let uart_future = join(
121 uart_read(&mut uart_rx, &mut usb_pipe_writer),
122 uart_write(&mut uart_tx, &mut uart_pipe_reader),
123 );
124
125 // Run everything concurrently.
126 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
127 join3(usb_fut, usb_future, uart_future).await;
128}
129
130struct Disconnected {}
131
132impl From<EndpointError> for Disconnected {
133 fn from(val: EndpointError) -> Self {
134 match val {
135 EndpointError::BufferOverflow => panic!("Buffer overflow"),
136 EndpointError::Disabled => Disconnected {},
137 }
138 }
139}
140
141/// Read from the USB and write it to the UART TX pipe
142async fn usb_read<'d, T: Instance + 'd>(
143 usb_rx: &mut Receiver<'d, Driver<'d, T>>,
144 uart_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,
145) -> Result<(), Disconnected> {
146 let mut buf = [0; 64];
147 loop {
148 let n = usb_rx.read_packet(&mut buf).await?;
149 let data = &buf[..n];
150 trace!("USB IN: {:x}", data);
151 uart_pipe_writer.write(data).await;
152 }
153}
154
155/// Read from the USB TX pipe and write it to the USB
156async fn usb_write<'d, T: Instance + 'd>(
157 usb_tx: &mut Sender<'d, Driver<'d, T>>,
158 usb_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,
159) -> Result<(), Disconnected> {
160 let mut buf = [0; 64];
161 loop {
162 let n = usb_pipe_reader.read(&mut buf).await;
163 let data = &buf[..n];
164 trace!("USB OUT: {:x}", data);
165 usb_tx.write_packet(&data).await?;
166 }
167}
168
169/// Read from the UART and write it to the USB TX pipe
170async fn uart_read(
171 uart_rx: &mut PioUartRx<'_>,
172 usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,
173) -> ! {
174 let mut buf = [0; 64];
175 loop {
176 let n = uart_rx.read(&mut buf).await.expect("UART read error");
177 if n == 0 {
178 continue;
179 }
180 let data = &buf[..n];
181 trace!("UART IN: {:x}", buf);
182 usb_pipe_writer.write(data).await;
183 }
184}
185
186/// Read from the UART TX pipe and write it to the UART
187async fn uart_write(
188 uart_tx: &mut PioUartTx<'_>,
189 uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,
190) -> ! {
191 let mut buf = [0; 64];
192 loop {
193 let n = uart_pipe_reader.read(&mut buf).await;
194 let data = &buf[..n];
195 trace!("UART OUT: {:x}", data);
196 let _ = uart_tx.write(&data).await;
197 }
198}
199
200mod uart {
201 use embassy_rp::peripherals::PIO0;
202 use embassy_rp::pio::{Pio, PioPin};
203 use embassy_rp::Peripheral;
204
205 use crate::uart_rx::PioUartRx;
206 use crate::uart_tx::PioUartTx;
207 use crate::Irqs;
208
209 pub struct PioUart<'a> {
210 tx: PioUartTx<'a>,
211 rx: PioUartRx<'a>,
212 }
213
214 impl<'a> PioUart<'a> {
215 pub fn new(
216 baud: u64,
217 pio: impl Peripheral<P = PIO0> + 'a,
218 tx_pin: impl PioPin,
219 rx_pin: impl PioPin,
220 ) -> PioUart<'a> {
221 let Pio {
222 mut common, sm0, sm1, ..
223 } = Pio::new(pio, Irqs);
224
225 let tx = PioUartTx::new(&mut common, sm0, tx_pin, baud);
226 let rx = PioUartRx::new(&mut common, sm1, rx_pin, baud);
227
228 PioUart { tx, rx }
229 }
230
231 pub fn split(self) -> (PioUartTx<'a>, PioUartRx<'a>) {
232 (self.tx, self.rx)
233 }
234 }
235}
236
237mod uart_tx {
238 use core::convert::Infallible;
239
240 use embassy_rp::gpio::Level;
241 use embassy_rp::peripherals::PIO0;
242 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
243 use embedded_io::asynch::Write;
244 use embedded_io::Io;
245 use fixed::traits::ToFixed;
246 use fixed_macro::types::U56F8;
247
248 pub struct PioUartTx<'a> {
249 sm_tx: StateMachine<'a, PIO0, 0>,
250 }
251
252 impl<'a> PioUartTx<'a> {
253 pub fn new(
254 common: &mut Common<'a, PIO0>,
255 mut sm_tx: StateMachine<'a, PIO0, 0>,
256 tx_pin: impl PioPin,
257 baud: u64,
258 ) -> Self {
259 let prg = pio_proc::pio_asm!(
260 r#"
261 .side_set 1 opt
262
263 ; An 8n1 UART transmit program.
264 ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
265
266 pull side 1 [7] ; Assert stop bit, or stall with line in idle state
267 set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks
268 bitloop: ; This loop will run 8 times (8n1 UART)
269 out pins, 1 ; Shift 1 bit from OSR to the first OUT pin
270 jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
271 "#
272 );
273 let tx_pin = common.make_pio_pin(tx_pin);
274 sm_tx.set_pins(Level::High, &[&tx_pin]);
275 sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]);
276
277 let mut cfg = Config::default();
278
279 cfg.set_out_pins(&[&tx_pin]);
280 cfg.use_program(&common.load_program(&prg.program), &[&tx_pin]);
281 cfg.shift_out.auto_fill = false;
282 cfg.shift_out.direction = ShiftDirection::Right;
283 cfg.fifo_join = FifoJoin::TxOnly;
284 cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed();
285 sm_tx.set_config(&cfg);
286 sm_tx.set_enable(true);
287
288 Self { sm_tx }
289 }
290
291 pub async fn write_u8(&mut self, data: u8) {
292 self.sm_tx.tx().wait_push(data as u32).await;
293 }
294 }
295
296 impl Io for PioUartTx<'_> {
297 type Error = Infallible;
298 }
299
300 impl Write for PioUartTx<'_> {
301 async fn write(&mut self, buf: &[u8]) -> Result<usize, Infallible> {
302 for byte in buf {
303 self.write_u8(*byte).await;
304 }
305 Ok(buf.len())
306 }
307 }
308}
309
310mod uart_rx {
311 use core::convert::Infallible;
312
313 use embassy_rp::gpio::Level;
314 use embassy_rp::peripherals::PIO0;
315 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
316 use embedded_io::asynch::Read;
317 use embedded_io::Io;
318 use fixed::traits::ToFixed;
319 use fixed_macro::types::U56F8;
320
321 pub struct PioUartRx<'a> {
322 sm_rx: StateMachine<'a, PIO0, 1>,
323 }
324
325 impl<'a> PioUartRx<'a> {
326 pub fn new(
327 common: &mut Common<'a, PIO0>,
328 mut sm_rx: StateMachine<'a, PIO0, 1>,
329 rx_pin: impl PioPin,
330 baud: u64,
331 ) -> Self {
332 let prg = pio_proc::pio_asm!(
333 r#"
334 ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
335 ; break conditions more gracefully.
336 ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
337
338 start:
339 wait 0 pin 0 ; Stall until start bit is asserted
340 set x, 7 [10] ; Preload bit counter, then delay until halfway through
341 rx_bitloop: ; the first data bit (12 cycles incl wait, set).
342 in pins, 1 ; Shift data bit into ISR
343 jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles
344 jmp pin good_rx_stop ; Check stop bit (should be high)
345
346 irq 4 rel ; Either a framing error or a break. Set a sticky flag,
347 wait 1 pin 0 ; and wait for line to return to idle state.
348 jmp start ; Don't push data if we didn't see good framing.
349
350 good_rx_stop: ; No delay before returning to start; a little slack is
351 in null 24
352 push ; important in case the TX clock is slightly too fast.
353 "#
354 );
355 let mut cfg = Config::default();
356 cfg.use_program(&common.load_program(&prg.program), &[]);
357
358 let rx_pin = common.make_pio_pin(rx_pin);
359 sm_rx.set_pins(Level::High, &[&rx_pin]);
360 cfg.set_in_pins(&[&rx_pin]);
361 cfg.set_jmp_pin(&rx_pin);
362 sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]);
363
364 cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed();
365 cfg.shift_in.auto_fill = false;
366 cfg.shift_in.direction = ShiftDirection::Right;
367 cfg.shift_in.threshold = 32;
368 cfg.fifo_join = FifoJoin::RxOnly;
369 sm_rx.set_config(&cfg);
370 sm_rx.set_enable(true);
371
372 Self { sm_rx }
373 }
374
375 pub async fn read_u8(&mut self) -> u8 {
376 self.sm_rx.rx().wait_pull().await as u8
377 }
378 }
379
380 impl Io for PioUartRx<'_> {
381 type Error = Infallible;
382 }
383
384 impl Read for PioUartRx<'_> {
385 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Infallible> {
386 let mut i = 0;
387 while i < buf.len() {
388 buf[i] = self.read_u8().await;
389 i += 1;
390 }
391 Ok(i)
392 }
393 }
394}
diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs
index 3de2bd48d..bc87016ec 100644
--- a/examples/rp/src/bin/pio_ws2812.rs
+++ b/examples/rp/src/bin/pio_ws2812.rs
@@ -12,7 +12,6 @@ use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{ 12use embassy_rp::pio::{
13 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 13 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
14}; 14};
15use embassy_rp::relocate::RelocatedProgram;
16use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; 15use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
17use embassy_time::{Duration, Timer}; 16use embassy_time::{Duration, Timer};
18use fixed::types::U24F8; 17use fixed::types::U24F8;
@@ -73,8 +72,7 @@ impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
73 cfg.set_out_pins(&[&out_pin]); 72 cfg.set_out_pins(&[&out_pin]);
74 cfg.set_set_pins(&[&out_pin]); 73 cfg.set_set_pins(&[&out_pin]);
75 74
76 let relocated = RelocatedProgram::new(&prg); 75 cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
77 cfg.use_program(&pio.load_program(&relocated), &[&out_pin]);
78 76
79 // Clock config, measured in kHz to avoid overflows 77 // Clock config, measured in kHz to avoid overflows
80 // TODO CLOCK_FREQ should come from embassy_rp 78 // TODO CLOCK_FREQ should come from embassy_rp