diff options
| author | Michael van Niekerk <[email protected]> | 2023-07-24 22:20:00 +0200 |
|---|---|---|
| committer | Michael van Niekerk <[email protected]> | 2023-07-24 22:20:00 +0200 |
| commit | a60d92cfbbacc909ba781802cad04fe00e849026 (patch) | |
| tree | 90763431421f9fedde70727a9260453b50e9739b /examples | |
| parent | 18b9b6c780b46165ead1de59ce258bd78786bdcb (diff) | |
Tx and Rx setup
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/rp/.idea/.gitignore | 8 | ||||
| -rw-r--r-- | examples/rp/.idea/modules.xml | 8 | ||||
| -rw-r--r-- | examples/rp/.idea/rp.iml | 12 | ||||
| -rw-r--r-- | examples/rp/.idea/vcs.xml | 6 | ||||
| -rw-r--r-- | examples/rp/src/bin/pio_uart.rs | 262 |
5 files changed, 296 insertions, 0 deletions
diff --git a/examples/rp/.idea/.gitignore b/examples/rp/.idea/.gitignore new file mode 100644 index 000000000..13566b81b --- /dev/null +++ b/examples/rp/.idea/.gitignore | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # Default ignored files | ||
| 2 | /shelf/ | ||
| 3 | /workspace.xml | ||
| 4 | # Editor-based HTTP Client requests | ||
| 5 | /httpRequests/ | ||
| 6 | # Datasource local storage ignored files | ||
| 7 | /dataSources/ | ||
| 8 | /dataSources.local.xml | ||
diff --git a/examples/rp/.idea/modules.xml b/examples/rp/.idea/modules.xml new file mode 100644 index 000000000..06ff4b23d --- /dev/null +++ b/examples/rp/.idea/modules.xml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <project version="4"> | ||
| 3 | <component name="ProjectModuleManager"> | ||
| 4 | <modules> | ||
| 5 | <module fileurl="file://$PROJECT_DIR$/.idea/rp.iml" filepath="$PROJECT_DIR$/.idea/rp.iml" /> | ||
| 6 | </modules> | ||
| 7 | </component> | ||
| 8 | </project> \ No newline at end of file | ||
diff --git a/examples/rp/.idea/rp.iml b/examples/rp/.idea/rp.iml new file mode 100644 index 000000000..9b4cf845b --- /dev/null +++ b/examples/rp/.idea/rp.iml | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <module type="JAVA_MODULE" version="4"> | ||
| 3 | <component name="NewModuleRootManager" inherit-compiler-output="true"> | ||
| 4 | <exclude-output /> | ||
| 5 | <content url="file://$MODULE_DIR$"> | ||
| 6 | <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> | ||
| 7 | <excludeFolder url="file://$MODULE_DIR$/target" /> | ||
| 8 | </content> | ||
| 9 | <orderEntry type="inheritedJdk" /> | ||
| 10 | <orderEntry type="sourceFolder" forTests="false" /> | ||
| 11 | </component> | ||
| 12 | </module> \ No newline at end of file | ||
diff --git a/examples/rp/.idea/vcs.xml b/examples/rp/.idea/vcs.xml new file mode 100644 index 000000000..b2bdec2d7 --- /dev/null +++ b/examples/rp/.idea/vcs.xml | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <project version="4"> | ||
| 3 | <component name="VcsDirectoryMappings"> | ||
| 4 | <mapping directory="$PROJECT_DIR$/../.." vcs="Git" /> | ||
| 5 | </component> | ||
| 6 | </project> \ No newline at end of file | ||
diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs new file mode 100644 index 000000000..14d05f4db --- /dev/null +++ b/examples/rp/src/bin/pio_uart.rs | |||
| @@ -0,0 +1,262 @@ | |||
| 1 | //! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! This creates a USB serial port that echos. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::{info, panic}; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_futures::join::join; | ||
| 12 | use embassy_rp::bind_interrupts; | ||
| 13 | use embassy_rp::peripherals::{PIO0, USB}; | ||
| 14 | use embassy_rp::usb::{Driver, Instance, InterruptHandler}; | ||
| 15 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 16 | use embassy_usb::driver::EndpointError; | ||
| 17 | use embassy_usb::{Builder, Config}; | ||
| 18 | use embassy_rp::pio::{InterruptHandler as PioInterruptHandler}; | ||
| 19 | use {defmt_rtt as _, panic_probe as _}; | ||
| 20 | |||
| 21 | bind_interrupts!(struct UsbIrqs { | ||
| 22 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 23 | }); | ||
| 24 | |||
| 25 | bind_interrupts!(struct PioIrqs { | ||
| 26 | PIO0_IRQ_0 => PioInterruptHandler<PIO0>; | ||
| 27 | }); | ||
| 28 | |||
| 29 | #[embassy_executor::main] | ||
| 30 | async fn main(_spawner: Spawner) { | ||
| 31 | info!("Hello there!"); | ||
| 32 | |||
| 33 | let p = embassy_rp::init(Default::default()); | ||
| 34 | |||
| 35 | // Create the driver, from the HAL. | ||
| 36 | let driver = Driver::new(p.USB, UsbIrqs); | ||
| 37 | |||
| 38 | // Create embassy-usb Config | ||
| 39 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 40 | config.manufacturer = Some("Embassy"); | ||
| 41 | config.product = Some("PIO UART example"); | ||
| 42 | config.serial_number = Some("12345678"); | ||
| 43 | config.max_power = 100; | ||
| 44 | config.max_packet_size_0 = 64; | ||
| 45 | |||
| 46 | // Required for windows compatibility. | ||
| 47 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 48 | config.device_class = 0xEF; | ||
| 49 | config.device_sub_class = 0x02; | ||
| 50 | config.device_protocol = 0x01; | ||
| 51 | config.composite_with_iads = true; | ||
| 52 | |||
| 53 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 54 | // It needs some buffers for building the descriptors. | ||
| 55 | let mut device_descriptor = [0; 256]; | ||
| 56 | let mut config_descriptor = [0; 256]; | ||
| 57 | let mut bos_descriptor = [0; 256]; | ||
| 58 | let mut control_buf = [0; 64]; | ||
| 59 | |||
| 60 | let mut state = State::new(); | ||
| 61 | |||
| 62 | let mut builder = Builder::new( | ||
| 63 | driver, | ||
| 64 | config, | ||
| 65 | &mut device_descriptor, | ||
| 66 | &mut config_descriptor, | ||
| 67 | &mut bos_descriptor, | ||
| 68 | &mut control_buf, | ||
| 69 | ); | ||
| 70 | |||
| 71 | // Create classes on the builder. | ||
| 72 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 73 | |||
| 74 | // Build the builder. | ||
| 75 | let mut usb = builder.build(); | ||
| 76 | |||
| 77 | // Run the USB device. | ||
| 78 | let usb_fut = usb.run(); | ||
| 79 | |||
| 80 | // Do stuff with the class! | ||
| 81 | let echo_fut = async { | ||
| 82 | loop { | ||
| 83 | class.wait_connection().await; | ||
| 84 | info!("Connected"); | ||
| 85 | let _ = echo(&mut class).await; | ||
| 86 | info!("Disconnected"); | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | // Run everything concurrently. | ||
| 91 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 92 | join(usb_fut, echo_fut).await; | ||
| 93 | } | ||
| 94 | |||
| 95 | struct Disconnected {} | ||
| 96 | |||
| 97 | impl From<EndpointError> for Disconnected { | ||
| 98 | fn from(val: EndpointError) -> Self { | ||
| 99 | match val { | ||
| 100 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 101 | EndpointError::Disabled => Disconnected {}, | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 107 | let mut buf = [0; 64]; | ||
| 108 | loop { | ||
| 109 | let n = class.read_packet(&mut buf).await?; | ||
| 110 | let data = &buf[..n]; | ||
| 111 | info!("data: {:x}", data); | ||
| 112 | class.write_packet(data).await?; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | mod uart { | ||
| 117 | use embassy_rp::peripherals::PIO0; | ||
| 118 | use embassy_rp::pio::{Common, Pio, PioPin, StateMachine}; | ||
| 119 | use embassy_rp::Peripheral; | ||
| 120 | |||
| 121 | use crate::PioIrqs; | ||
| 122 | |||
| 123 | pub struct PioUart<'a> { | ||
| 124 | baud: u64, | ||
| 125 | pio: Common<'a, PIO0>, | ||
| 126 | sm0: StateMachine<'a, PIO0, 0>, | ||
| 127 | sm1: StateMachine<'a, PIO0, 1>, | ||
| 128 | } | ||
| 129 | |||
| 130 | impl<'a> PioUart<'a> { | ||
| 131 | pub async fn new( | ||
| 132 | baud: u64, | ||
| 133 | pio: impl Peripheral<P = PIO0> + 'a, | ||
| 134 | tx_pin: impl PioPin, | ||
| 135 | rx_pin: impl PioPin, | ||
| 136 | ) -> PioUart<'a> { | ||
| 137 | let Pio { | ||
| 138 | mut common, | ||
| 139 | mut sm0, | ||
| 140 | mut sm1, | ||
| 141 | .. | ||
| 142 | } = Pio::new(pio, PioIrqs); | ||
| 143 | |||
| 144 | crate::uart_tx::setup_uart_tx_on_sm0(&mut common, &mut sm0, tx_pin, baud); | ||
| 145 | crate::uart_rx::setup_uart_rx_on_sm1(&mut common, &mut sm1, rx_pin, baud); | ||
| 146 | |||
| 147 | PioUart { | ||
| 148 | baud, | ||
| 149 | pio: common, | ||
| 150 | sm0, | ||
| 151 | sm1, | ||
| 152 | } | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | mod uart_tx { | ||
| 158 | use embassy_rp::gpio::Level; | ||
| 159 | use embassy_rp::peripherals::PIO0; | ||
| 160 | use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; | ||
| 161 | use embassy_rp::relocate::RelocatedProgram; | ||
| 162 | use fixed::traits::ToFixed; | ||
| 163 | use fixed_macro::types::U56F8; | ||
| 164 | |||
| 165 | pub fn setup_uart_tx_on_sm0<'a>( | ||
| 166 | common: &mut Common<'a, PIO0>, | ||
| 167 | sm_tx: &mut StateMachine<'a, PIO0, 0>, | ||
| 168 | tx_pin: impl PioPin, | ||
| 169 | baud: u64, | ||
| 170 | ) { | ||
| 171 | let prg = pio_proc::pio_asm!( | ||
| 172 | r#" | ||
| 173 | ;.program uart_tx | ||
| 174 | .side_set 1 opt | ||
| 175 | |||
| 176 | ; An 8n1 UART transmit program. | ||
| 177 | ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin. | ||
| 178 | |||
| 179 | pull side 1 [7] ; Assert stop bit, or stall with line in idle state | ||
| 180 | set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks | ||
| 181 | bitloop: ; This loop will run 8 times (8n1 UART) | ||
| 182 | out pins, 1 ; Shift 1 bit from OSR to the first OUT pin | ||
| 183 | jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. | ||
| 184 | "# | ||
| 185 | ); | ||
| 186 | let tx_pin = common.make_pio_pin(tx_pin); | ||
| 187 | sm_tx.set_pins(Level::High, &[&tx_pin]); | ||
| 188 | sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); | ||
| 189 | |||
| 190 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 191 | let mut cfg = Config::default(); | ||
| 192 | |||
| 193 | cfg.use_program(&common.load_program(&relocated), &[&tx_pin]); | ||
| 194 | cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); | ||
| 195 | cfg.shift_out.auto_fill = false; | ||
| 196 | cfg.shift_out.direction = ShiftDirection::Right; | ||
| 197 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 198 | cfg.set_out_pins(&[&tx_pin]); | ||
| 199 | cfg.set_set_pins(&[&tx_pin]); | ||
| 200 | sm_tx.set_config(&cfg); | ||
| 201 | sm_tx.set_enable(true) | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | mod uart_rx { | ||
| 206 | use embassy_rp::gpio::Level; | ||
| 207 | use embassy_rp::peripherals::PIO0; | ||
| 208 | use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; | ||
| 209 | use embassy_rp::relocate::RelocatedProgram; | ||
| 210 | use fixed::traits::ToFixed; | ||
| 211 | use fixed_macro::types::U56F8; | ||
| 212 | |||
| 213 | pub fn setup_uart_rx_on_sm1<'a>( | ||
| 214 | common: &mut Common<'a, PIO0>, | ||
| 215 | sm_rx: &mut StateMachine<'a, PIO0, 1>, | ||
| 216 | rx_pin: impl PioPin, | ||
| 217 | baud: u64, | ||
| 218 | ) { | ||
| 219 | let prg = pio_proc::pio_asm!( | ||
| 220 | r#" | ||
| 221 | ;.program uart_rx | ||
| 222 | |||
| 223 | ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and | ||
| 224 | ; break conditions more gracefully. | ||
| 225 | ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX. | ||
| 226 | |||
| 227 | start: | ||
| 228 | wait 0 pin 0 ; Stall until start bit is asserted | ||
| 229 | set x, 7 [10] ; Preload bit counter, then delay until halfway through | ||
| 230 | bitloop: ; the first data bit (12 cycles incl wait, set). | ||
| 231 | in pins, 1 ; Shift data bit into ISR | ||
| 232 | jmp x-- bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles | ||
| 233 | jmp pin good_stop ; Check stop bit (should be high) | ||
| 234 | |||
| 235 | irq 4 rel ; Either a framing error or a break. Set a sticky flag, | ||
| 236 | wait 1 pin 0 ; and wait for line to return to idle state. | ||
| 237 | jmp start ; Don't push data if we didn't see good framing. | ||
| 238 | |||
| 239 | good_stop: ; No delay before returning to start; a little slack is | ||
| 240 | push ; important in case the TX clock is slightly too fast. | ||
| 241 | "# | ||
| 242 | ); | ||
| 243 | |||
| 244 | let rx_pin = common.make_pio_pin(rx_pin); | ||
| 245 | sm_rx.set_pins(Level::High, &[&rx_pin]); | ||
| 246 | sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]); | ||
| 247 | |||
| 248 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 249 | let mut cfg = Config::default(); | ||
| 250 | |||
| 251 | cfg.use_program(&common.load_program(&relocated), &[&rx_pin]); | ||
| 252 | cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); | ||
| 253 | cfg.shift_out.auto_fill = false; | ||
| 254 | cfg.shift_out.direction = ShiftDirection::Right; | ||
| 255 | cfg.fifo_join = FifoJoin::RxOnly; | ||
| 256 | cfg.set_in_pins(&[&rx_pin]); | ||
| 257 | cfg.set_jmp_pin(&rx_pin); | ||
| 258 | // cfg.set_set_pins(&[&rx_pin]); | ||
| 259 | sm_rx.set_config(&cfg); | ||
| 260 | sm_rx.set_enable(true) | ||
| 261 | } | ||
| 262 | } | ||
