diff options
| author | Quentin Smith <[email protected]> | 2023-07-17 21:31:43 -0400 |
|---|---|---|
| committer | Quentin Smith <[email protected]> | 2023-07-17 21:31:43 -0400 |
| commit | 6f02403184eb7fb7990fb88fc9df9c4328a690a3 (patch) | |
| tree | 748f510e190bb2724750507a6e69ed1a8e08cb20 /examples/rp | |
| parent | d896f80405aa8963877049ed999e4aba25d6e2bb (diff) | |
| parent | 6b5df4523aa1c4902f02e803450ae4b418e0e3ca (diff) | |
Merge remote-tracking branch 'origin/main' into nrf-pdm
Diffstat (limited to 'examples/rp')
41 files changed, 3451 insertions, 137 deletions
diff --git a/examples/rp/.cargo/config.toml b/examples/rp/.cargo/config.toml index 3d6051389..3d7d61740 100644 --- a/examples/rp/.cargo/config.toml +++ b/examples/rp/.cargo/config.toml | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | runner = "probe-run --chip RP2040" | 2 | runner = "probe-rs run --chip RP2040" |
| 3 | 3 | ||
| 4 | [build] | 4 | [build] |
| 5 | target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ | 5 | target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ |
| 6 | 6 | ||
| 7 | [env] | 7 | [env] |
| 8 | DEFMT_LOG = "trace" | 8 | DEFMT_LOG = "debug" |
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index d804a660b..c812cb3ee 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml | |||
| @@ -2,18 +2,34 @@ | |||
| 2 | edition = "2021" | 2 | edition = "2021" |
| 3 | name = "embassy-rp-examples" | 3 | name = "embassy-rp-examples" |
| 4 | version = "0.1.0" | 4 | version = "0.1.0" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 5 | 6 | ||
| 6 | 7 | ||
| 7 | [dependencies] | 8 | [dependencies] |
| 8 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } |
| 9 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 10 | embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } |
| 11 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } | 12 | embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } | ||
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 15 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } | ||
| 16 | embassy-net-w5500 = { version = "0.1.0", path = "../../embassy-net-w5500", features = ["defmt"] } | ||
| 17 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 18 | embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } | ||
| 19 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] } | ||
| 20 | lora-phy = { version = "1" } | ||
| 21 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } | ||
| 22 | lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] } | ||
| 23 | cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } | ||
| 24 | cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } | ||
| 12 | 25 | ||
| 13 | defmt = "0.3" | 26 | defmt = "0.3" |
| 14 | defmt-rtt = "0.3" | 27 | defmt-rtt = "0.4" |
| 28 | fixed = "1.23.1" | ||
| 29 | fixed-macro = "1.2" | ||
| 15 | 30 | ||
| 16 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 31 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 32 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } | ||
| 17 | cortex-m-rt = "0.7.0" | 33 | cortex-m-rt = "0.7.0" |
| 18 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 34 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 19 | futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } | 35 | futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } |
| @@ -22,6 +38,22 @@ embedded-graphics = "0.7.1" | |||
| 22 | st7789 = "0.6.1" | 38 | st7789 = "0.6.1" |
| 23 | display-interface = "0.4.1" | 39 | display-interface = "0.4.1" |
| 24 | byte-slice-cast = { version = "1.2.0", default-features = false } | 40 | byte-slice-cast = { version = "1.2.0", default-features = false } |
| 41 | smart-leds = "0.3.0" | ||
| 42 | heapless = "0.7.15" | ||
| 43 | usbd-hid = "0.6.1" | ||
| 25 | 44 | ||
| 26 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } | 45 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } |
| 27 | embedded-hal-async = { version = "0.1.0-alpha.1" } | 46 | embedded-hal-async = "0.2.0-alpha.2" |
| 47 | embedded-io = { version = "0.4.0", features = ["async", "defmt"] } | ||
| 48 | embedded-storage = { version = "0.3" } | ||
| 49 | static_cell = { version = "1.1", features = ["nightly"]} | ||
| 50 | log = "0.4" | ||
| 51 | pio-proc = "0.2" | ||
| 52 | pio = "0.2.1" | ||
| 53 | rand = { version = "0.8.5", default-features = false } | ||
| 54 | |||
| 55 | [profile.release] | ||
| 56 | debug = true | ||
| 57 | |||
| 58 | [patch.crates-io] | ||
| 59 | lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } | ||
diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs new file mode 100644 index 000000000..81a8b8340 --- /dev/null +++ b/examples/rp/src/bin/adc.rs | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | //! This example test the ADC (Analog to Digital Conversion) of the RS2040 pin 26, 27 and 28. | ||
| 2 | //! It also reads the temperature sensor in the chip. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin}; | ||
| 11 | use embassy_rp::bind_interrupts; | ||
| 12 | use embassy_rp::gpio::Pull; | ||
| 13 | use embassy_time::{Duration, Timer}; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | ADC_IRQ_FIFO => InterruptHandler; | ||
| 18 | }); | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(_spawner: Spawner) { | ||
| 22 | let p = embassy_rp::init(Default::default()); | ||
| 23 | let mut adc = Adc::new(p.ADC, Irqs, Config::default()); | ||
| 24 | |||
| 25 | let mut p26 = Pin::new(p.PIN_26, Pull::None); | ||
| 26 | let mut p27 = Pin::new(p.PIN_27, Pull::None); | ||
| 27 | let mut p28 = Pin::new(p.PIN_28, Pull::None); | ||
| 28 | |||
| 29 | loop { | ||
| 30 | let level = adc.read(&mut p26).await.unwrap(); | ||
| 31 | info!("Pin 26 ADC: {}", level); | ||
| 32 | let level = adc.read(&mut p27).await.unwrap(); | ||
| 33 | info!("Pin 27 ADC: {}", level); | ||
| 34 | let level = adc.read(&mut p28).await.unwrap(); | ||
| 35 | info!("Pin 28 ADC: {}", level); | ||
| 36 | let temp = adc.read_temperature().await.unwrap(); | ||
| 37 | info!("Temp: {} degrees", convert_to_celsius(temp)); | ||
| 38 | Timer::after(Duration::from_secs(1)).await; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | fn convert_to_celsius(raw_temp: u16) -> f32 { | ||
| 43 | // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet | ||
| 44 | let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721; | ||
| 45 | let sign = if temp < 0.0 { -1.0 } else { 1.0 }; | ||
| 46 | let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16; | ||
| 47 | (rounded_temp_x10 as f32) / 10.0 | ||
| 48 | } | ||
diff --git a/examples/rp/src/bin/blinky.rs b/examples/rp/src/bin/blinky.rs index 7aa36a19f..295b000f3 100644 --- a/examples/rp/src/bin/blinky.rs +++ b/examples/rp/src/bin/blinky.rs | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | //! This example test the RP Pico on board LED. | ||
| 2 | //! | ||
| 3 | //! It does not work with the RP Pico W board. See wifi_blinky.rs. | ||
| 4 | |||
| 1 | #![no_std] | 5 | #![no_std] |
| 2 | #![no_main] | 6 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 7 | #![feature(type_alias_impl_trait)] |
diff --git a/examples/rp/src/bin/button.rs b/examples/rp/src/bin/button.rs index c5422c616..d7aa89410 100644 --- a/examples/rp/src/bin/button.rs +++ b/examples/rp/src/bin/button.rs | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | //! This example uses the RP Pico on board LED to test input pin 28. This is not the button on the board. | ||
| 2 | //! | ||
| 3 | //! It does not work with the RP Pico W board. Use wifi_blinky.rs and add input pin. | ||
| 4 | |||
| 1 | #![no_std] | 5 | #![no_std] |
| 2 | #![no_main] | 6 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 7 | #![feature(type_alias_impl_trait)] |
| @@ -9,9 +13,12 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 9 | #[embassy_executor::main] | 13 | #[embassy_executor::main] |
| 10 | async fn main(_spawner: Spawner) { | 14 | async fn main(_spawner: Spawner) { |
| 11 | let p = embassy_rp::init(Default::default()); | 15 | let p = embassy_rp::init(Default::default()); |
| 12 | let button = Input::new(p.PIN_28, Pull::Up); | ||
| 13 | let mut led = Output::new(p.PIN_25, Level::Low); | 16 | let mut led = Output::new(p.PIN_25, Level::Low); |
| 14 | 17 | ||
| 18 | // Use PIN_28, Pin34 on J0 for RP Pico, as a input. | ||
| 19 | // You need to add your own button. | ||
| 20 | let button = Input::new(p.PIN_28, Pull::Up); | ||
| 21 | |||
| 15 | loop { | 22 | loop { |
| 16 | if button.is_high() { | 23 | if button.is_high() { |
| 17 | led.set_high(); | 24 | led.set_high(); |
diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs new file mode 100644 index 000000000..e81da177b --- /dev/null +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | //! This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port. | ||
| 2 | //! | ||
| 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_futures::yield_now; | ||
| 12 | use embassy_net::{Stack, StackResources}; | ||
| 13 | use embassy_net_w5500::*; | ||
| 14 | use embassy_rp::clocks::RoscRng; | ||
| 15 | use embassy_rp::gpio::{Input, Level, Output, Pull}; | ||
| 16 | use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; | ||
| 17 | use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | ||
| 18 | use embassy_time::{Delay, Duration}; | ||
| 19 | use embedded_hal_async::spi::ExclusiveDevice; | ||
| 20 | use embedded_io::asynch::Write; | ||
| 21 | use rand::RngCore; | ||
| 22 | use static_cell::make_static; | ||
| 23 | use {defmt_rtt as _, panic_probe as _}; | ||
| 24 | |||
| 25 | #[embassy_executor::task] | ||
| 26 | async fn ethernet_task( | ||
| 27 | runner: Runner< | ||
| 28 | 'static, | ||
| 29 | ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>, Delay>, | ||
| 30 | Input<'static, PIN_21>, | ||
| 31 | Output<'static, PIN_20>, | ||
| 32 | >, | ||
| 33 | ) -> ! { | ||
| 34 | runner.run().await | ||
| 35 | } | ||
| 36 | |||
| 37 | #[embassy_executor::task] | ||
| 38 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | ||
| 39 | stack.run().await | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(spawner: Spawner) { | ||
| 44 | let p = embassy_rp::init(Default::default()); | ||
| 45 | let mut rng = RoscRng; | ||
| 46 | |||
| 47 | let mut spi_cfg = SpiConfig::default(); | ||
| 48 | spi_cfg.frequency = 50_000_000; | ||
| 49 | let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); | ||
| 50 | let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); | ||
| 51 | let cs = Output::new(p.PIN_17, Level::High); | ||
| 52 | let w5500_int = Input::new(p.PIN_21, Pull::Up); | ||
| 53 | let w5500_reset = Output::new(p.PIN_20, Level::High); | ||
| 54 | |||
| 55 | let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; | ||
| 56 | let state = make_static!(State::<8, 8>::new()); | ||
| 57 | let (device, runner) = embassy_net_w5500::new( | ||
| 58 | mac_addr, | ||
| 59 | state, | ||
| 60 | ExclusiveDevice::new(spi, cs, Delay), | ||
| 61 | w5500_int, | ||
| 62 | w5500_reset, | ||
| 63 | ) | ||
| 64 | .await; | ||
| 65 | unwrap!(spawner.spawn(ethernet_task(runner))); | ||
| 66 | |||
| 67 | // Generate random seed | ||
| 68 | let seed = rng.next_u64(); | ||
| 69 | |||
| 70 | // Init network stack | ||
| 71 | let stack = &*make_static!(Stack::new( | ||
| 72 | device, | ||
| 73 | embassy_net::Config::dhcpv4(Default::default()), | ||
| 74 | make_static!(StackResources::<3>::new()), | ||
| 75 | seed | ||
| 76 | )); | ||
| 77 | |||
| 78 | // Launch network task | ||
| 79 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 80 | |||
| 81 | info!("Waiting for DHCP..."); | ||
| 82 | let cfg = wait_for_config(stack).await; | ||
| 83 | let local_addr = cfg.address.address(); | ||
| 84 | info!("IP address: {:?}", local_addr); | ||
| 85 | |||
| 86 | // Create two sockets listening to the same port, to handle simultaneous connections | ||
| 87 | unwrap!(spawner.spawn(listen_task(&stack, 0, 1234))); | ||
| 88 | unwrap!(spawner.spawn(listen_task(&stack, 1, 1234))); | ||
| 89 | } | ||
| 90 | |||
| 91 | #[embassy_executor::task(pool_size = 2)] | ||
| 92 | async fn listen_task(stack: &'static Stack<Device<'static>>, id: u8, port: u16) { | ||
| 93 | let mut rx_buffer = [0; 4096]; | ||
| 94 | let mut tx_buffer = [0; 4096]; | ||
| 95 | let mut buf = [0; 4096]; | ||
| 96 | loop { | ||
| 97 | let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 98 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 99 | |||
| 100 | info!("SOCKET {}: Listening on TCP:{}...", id, port); | ||
| 101 | if let Err(e) = socket.accept(port).await { | ||
| 102 | warn!("accept error: {:?}", e); | ||
| 103 | continue; | ||
| 104 | } | ||
| 105 | info!("SOCKET {}: Received connection from {:?}", id, socket.remote_endpoint()); | ||
| 106 | |||
| 107 | loop { | ||
| 108 | let n = match socket.read(&mut buf).await { | ||
| 109 | Ok(0) => { | ||
| 110 | warn!("read EOF"); | ||
| 111 | break; | ||
| 112 | } | ||
| 113 | Ok(n) => n, | ||
| 114 | Err(e) => { | ||
| 115 | warn!("SOCKET {}: {:?}", id, e); | ||
| 116 | break; | ||
| 117 | } | ||
| 118 | }; | ||
| 119 | info!("SOCKET {}: rxd {}", id, core::str::from_utf8(&buf[..n]).unwrap()); | ||
| 120 | |||
| 121 | if let Err(e) = socket.write_all(&buf[..n]).await { | ||
| 122 | warn!("write error: {:?}", e); | ||
| 123 | break; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | ||
| 130 | loop { | ||
| 131 | if let Some(config) = stack.config_v4() { | ||
| 132 | return config.clone(); | ||
| 133 | } | ||
| 134 | yield_now().await; | ||
| 135 | } | ||
| 136 | } | ||
diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs new file mode 100644 index 000000000..9dd7ae973 --- /dev/null +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | //! This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. | ||
| 2 | //! | ||
| 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use core::str::FromStr; | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_futures::yield_now; | ||
| 14 | use embassy_net::{Stack, StackResources}; | ||
| 15 | use embassy_net_w5500::*; | ||
| 16 | use embassy_rp::clocks::RoscRng; | ||
| 17 | use embassy_rp::gpio::{Input, Level, Output, Pull}; | ||
| 18 | use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; | ||
| 19 | use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | ||
| 20 | use embassy_time::{Delay, Duration, Timer}; | ||
| 21 | use embedded_hal_async::spi::ExclusiveDevice; | ||
| 22 | use embedded_io::asynch::Write; | ||
| 23 | use rand::RngCore; | ||
| 24 | use static_cell::make_static; | ||
| 25 | use {defmt_rtt as _, panic_probe as _}; | ||
| 26 | |||
| 27 | #[embassy_executor::task] | ||
| 28 | async fn ethernet_task( | ||
| 29 | runner: Runner< | ||
| 30 | 'static, | ||
| 31 | ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>, Delay>, | ||
| 32 | Input<'static, PIN_21>, | ||
| 33 | Output<'static, PIN_20>, | ||
| 34 | >, | ||
| 35 | ) -> ! { | ||
| 36 | runner.run().await | ||
| 37 | } | ||
| 38 | |||
| 39 | #[embassy_executor::task] | ||
| 40 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | ||
| 41 | stack.run().await | ||
| 42 | } | ||
| 43 | |||
| 44 | #[embassy_executor::main] | ||
| 45 | async fn main(spawner: Spawner) { | ||
| 46 | let p = embassy_rp::init(Default::default()); | ||
| 47 | let mut rng = RoscRng; | ||
| 48 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 49 | |||
| 50 | let mut spi_cfg = SpiConfig::default(); | ||
| 51 | spi_cfg.frequency = 50_000_000; | ||
| 52 | let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); | ||
| 53 | let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); | ||
| 54 | let cs = Output::new(p.PIN_17, Level::High); | ||
| 55 | let w5500_int = Input::new(p.PIN_21, Pull::Up); | ||
| 56 | let w5500_reset = Output::new(p.PIN_20, Level::High); | ||
| 57 | |||
| 58 | let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; | ||
| 59 | let state = make_static!(State::<8, 8>::new()); | ||
| 60 | let (device, runner) = embassy_net_w5500::new( | ||
| 61 | mac_addr, | ||
| 62 | state, | ||
| 63 | ExclusiveDevice::new(spi, cs, Delay), | ||
| 64 | w5500_int, | ||
| 65 | w5500_reset, | ||
| 66 | ) | ||
| 67 | .await; | ||
| 68 | unwrap!(spawner.spawn(ethernet_task(runner))); | ||
| 69 | |||
| 70 | // Generate random seed | ||
| 71 | let seed = rng.next_u64(); | ||
| 72 | |||
| 73 | // Init network stack | ||
| 74 | let stack = &*make_static!(Stack::new( | ||
| 75 | device, | ||
| 76 | embassy_net::Config::dhcpv4(Default::default()), | ||
| 77 | make_static!(StackResources::<2>::new()), | ||
| 78 | seed | ||
| 79 | )); | ||
| 80 | |||
| 81 | // Launch network task | ||
| 82 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 83 | |||
| 84 | info!("Waiting for DHCP..."); | ||
| 85 | let cfg = wait_for_config(stack).await; | ||
| 86 | let local_addr = cfg.address.address(); | ||
| 87 | info!("IP address: {:?}", local_addr); | ||
| 88 | |||
| 89 | let mut rx_buffer = [0; 4096]; | ||
| 90 | let mut tx_buffer = [0; 4096]; | ||
| 91 | loop { | ||
| 92 | let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 93 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 94 | |||
| 95 | led.set_low(); | ||
| 96 | info!("Connecting..."); | ||
| 97 | let host_addr = embassy_net::Ipv4Address::from_str("192.168.1.110").unwrap(); | ||
| 98 | if let Err(e) = socket.connect((host_addr, 1234)).await { | ||
| 99 | warn!("connect error: {:?}", e); | ||
| 100 | continue; | ||
| 101 | } | ||
| 102 | info!("Connected to {:?}", socket.remote_endpoint()); | ||
| 103 | led.set_high(); | ||
| 104 | |||
| 105 | let msg = b"Hello world!\n"; | ||
| 106 | loop { | ||
| 107 | if let Err(e) = socket.write_all(msg).await { | ||
| 108 | warn!("write error: {:?}", e); | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | info!("txd: {}", core::str::from_utf8(msg).unwrap()); | ||
| 112 | Timer::after(Duration::from_secs(1)).await; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | ||
| 118 | loop { | ||
| 119 | if let Some(config) = stack.config_v4() { | ||
| 120 | return config.clone(); | ||
| 121 | } | ||
| 122 | yield_now().await; | ||
| 123 | } | ||
| 124 | } | ||
diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs new file mode 100644 index 000000000..db21c2b6f --- /dev/null +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | //! This example implements a TCP echo server on port 1234 and using DHCP. | ||
| 2 | //! Send it some data, you should see it echoed back and printed in the console. | ||
| 3 | //! | ||
| 4 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | #![feature(type_alias_impl_trait)] | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_futures::yield_now; | ||
| 13 | use embassy_net::{Stack, StackResources}; | ||
| 14 | use embassy_net_w5500::*; | ||
| 15 | use embassy_rp::clocks::RoscRng; | ||
| 16 | use embassy_rp::gpio::{Input, Level, Output, Pull}; | ||
| 17 | use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; | ||
| 18 | use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | ||
| 19 | use embassy_time::{Delay, Duration}; | ||
| 20 | use embedded_hal_async::spi::ExclusiveDevice; | ||
| 21 | use embedded_io::asynch::Write; | ||
| 22 | use rand::RngCore; | ||
| 23 | use static_cell::make_static; | ||
| 24 | use {defmt_rtt as _, panic_probe as _}; | ||
| 25 | #[embassy_executor::task] | ||
| 26 | async fn ethernet_task( | ||
| 27 | runner: Runner< | ||
| 28 | 'static, | ||
| 29 | ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>, Delay>, | ||
| 30 | Input<'static, PIN_21>, | ||
| 31 | Output<'static, PIN_20>, | ||
| 32 | >, | ||
| 33 | ) -> ! { | ||
| 34 | runner.run().await | ||
| 35 | } | ||
| 36 | |||
| 37 | #[embassy_executor::task] | ||
| 38 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | ||
| 39 | stack.run().await | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(spawner: Spawner) { | ||
| 44 | let p = embassy_rp::init(Default::default()); | ||
| 45 | let mut rng = RoscRng; | ||
| 46 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 47 | |||
| 48 | let mut spi_cfg = SpiConfig::default(); | ||
| 49 | spi_cfg.frequency = 50_000_000; | ||
| 50 | let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); | ||
| 51 | let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); | ||
| 52 | let cs = Output::new(p.PIN_17, Level::High); | ||
| 53 | let w5500_int = Input::new(p.PIN_21, Pull::Up); | ||
| 54 | let w5500_reset = Output::new(p.PIN_20, Level::High); | ||
| 55 | |||
| 56 | let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; | ||
| 57 | let state = make_static!(State::<8, 8>::new()); | ||
| 58 | let (device, runner) = embassy_net_w5500::new( | ||
| 59 | mac_addr, | ||
| 60 | state, | ||
| 61 | ExclusiveDevice::new(spi, cs, Delay), | ||
| 62 | w5500_int, | ||
| 63 | w5500_reset, | ||
| 64 | ) | ||
| 65 | .await; | ||
| 66 | unwrap!(spawner.spawn(ethernet_task(runner))); | ||
| 67 | |||
| 68 | // Generate random seed | ||
| 69 | let seed = rng.next_u64(); | ||
| 70 | |||
| 71 | // Init network stack | ||
| 72 | let stack = &*make_static!(Stack::new( | ||
| 73 | device, | ||
| 74 | embassy_net::Config::dhcpv4(Default::default()), | ||
| 75 | make_static!(StackResources::<2>::new()), | ||
| 76 | seed | ||
| 77 | )); | ||
| 78 | |||
| 79 | // Launch network task | ||
| 80 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 81 | |||
| 82 | info!("Waiting for DHCP..."); | ||
| 83 | let cfg = wait_for_config(stack).await; | ||
| 84 | let local_addr = cfg.address.address(); | ||
| 85 | info!("IP address: {:?}", local_addr); | ||
| 86 | |||
| 87 | let mut rx_buffer = [0; 4096]; | ||
| 88 | let mut tx_buffer = [0; 4096]; | ||
| 89 | let mut buf = [0; 4096]; | ||
| 90 | loop { | ||
| 91 | let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 92 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 93 | |||
| 94 | led.set_low(); | ||
| 95 | info!("Listening on TCP:1234..."); | ||
| 96 | if let Err(e) = socket.accept(1234).await { | ||
| 97 | warn!("accept error: {:?}", e); | ||
| 98 | continue; | ||
| 99 | } | ||
| 100 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 101 | led.set_high(); | ||
| 102 | |||
| 103 | loop { | ||
| 104 | let n = match socket.read(&mut buf).await { | ||
| 105 | Ok(0) => { | ||
| 106 | warn!("read EOF"); | ||
| 107 | break; | ||
| 108 | } | ||
| 109 | Ok(n) => n, | ||
| 110 | Err(e) => { | ||
| 111 | warn!("{:?}", e); | ||
| 112 | break; | ||
| 113 | } | ||
| 114 | }; | ||
| 115 | info!("rxd {}", core::str::from_utf8(&buf[..n]).unwrap()); | ||
| 116 | |||
| 117 | if let Err(e) = socket.write_all(&buf[..n]).await { | ||
| 118 | warn!("write error: {:?}", e); | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | ||
| 126 | loop { | ||
| 127 | if let Some(config) = stack.config_v4() { | ||
| 128 | return config.clone(); | ||
| 129 | } | ||
| 130 | yield_now().await; | ||
| 131 | } | ||
| 132 | } | ||
diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs new file mode 100644 index 000000000..038432b17 --- /dev/null +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | //! This example implements a UDP server listening on port 1234 and echoing back the data. | ||
| 2 | //! | ||
| 3 | //! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_futures::yield_now; | ||
| 12 | use embassy_net::udp::{PacketMetadata, UdpSocket}; | ||
| 13 | use embassy_net::{Stack, StackResources}; | ||
| 14 | use embassy_net_w5500::*; | ||
| 15 | use embassy_rp::clocks::RoscRng; | ||
| 16 | use embassy_rp::gpio::{Input, Level, Output, Pull}; | ||
| 17 | use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; | ||
| 18 | use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; | ||
| 19 | use embassy_time::Delay; | ||
| 20 | use embedded_hal_async::spi::ExclusiveDevice; | ||
| 21 | use rand::RngCore; | ||
| 22 | use static_cell::make_static; | ||
| 23 | use {defmt_rtt as _, panic_probe as _}; | ||
| 24 | #[embassy_executor::task] | ||
| 25 | async fn ethernet_task( | ||
| 26 | runner: Runner< | ||
| 27 | 'static, | ||
| 28 | ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>, Delay>, | ||
| 29 | Input<'static, PIN_21>, | ||
| 30 | Output<'static, PIN_20>, | ||
| 31 | >, | ||
| 32 | ) -> ! { | ||
| 33 | runner.run().await | ||
| 34 | } | ||
| 35 | |||
| 36 | #[embassy_executor::task] | ||
| 37 | async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { | ||
| 38 | stack.run().await | ||
| 39 | } | ||
| 40 | |||
| 41 | #[embassy_executor::main] | ||
| 42 | async fn main(spawner: Spawner) { | ||
| 43 | let p = embassy_rp::init(Default::default()); | ||
| 44 | let mut rng = RoscRng; | ||
| 45 | |||
| 46 | let mut spi_cfg = SpiConfig::default(); | ||
| 47 | spi_cfg.frequency = 50_000_000; | ||
| 48 | let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); | ||
| 49 | let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); | ||
| 50 | let cs = Output::new(p.PIN_17, Level::High); | ||
| 51 | let w5500_int = Input::new(p.PIN_21, Pull::Up); | ||
| 52 | let w5500_reset = Output::new(p.PIN_20, Level::High); | ||
| 53 | |||
| 54 | let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; | ||
| 55 | let state = make_static!(State::<8, 8>::new()); | ||
| 56 | let (device, runner) = embassy_net_w5500::new( | ||
| 57 | mac_addr, | ||
| 58 | state, | ||
| 59 | ExclusiveDevice::new(spi, cs, Delay), | ||
| 60 | w5500_int, | ||
| 61 | w5500_reset, | ||
| 62 | ) | ||
| 63 | .await; | ||
| 64 | unwrap!(spawner.spawn(ethernet_task(runner))); | ||
| 65 | |||
| 66 | // Generate random seed | ||
| 67 | let seed = rng.next_u64(); | ||
| 68 | |||
| 69 | // Init network stack | ||
| 70 | let stack = &*make_static!(Stack::new( | ||
| 71 | device, | ||
| 72 | embassy_net::Config::dhcpv4(Default::default()), | ||
| 73 | make_static!(StackResources::<2>::new()), | ||
| 74 | seed | ||
| 75 | )); | ||
| 76 | |||
| 77 | // Launch network task | ||
| 78 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 79 | |||
| 80 | info!("Waiting for DHCP..."); | ||
| 81 | let cfg = wait_for_config(stack).await; | ||
| 82 | let local_addr = cfg.address.address(); | ||
| 83 | info!("IP address: {:?}", local_addr); | ||
| 84 | |||
| 85 | // Then we can use it! | ||
| 86 | let mut rx_buffer = [0; 4096]; | ||
| 87 | let mut tx_buffer = [0; 4096]; | ||
| 88 | let mut rx_meta = [PacketMetadata::EMPTY; 16]; | ||
| 89 | let mut tx_meta = [PacketMetadata::EMPTY; 16]; | ||
| 90 | let mut buf = [0; 4096]; | ||
| 91 | loop { | ||
| 92 | let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer); | ||
| 93 | socket.bind(1234).unwrap(); | ||
| 94 | |||
| 95 | loop { | ||
| 96 | let (n, ep) = socket.recv_from(&mut buf).await.unwrap(); | ||
| 97 | if let Ok(s) = core::str::from_utf8(&buf[..n]) { | ||
| 98 | info!("rxd from {}: {}", ep, s); | ||
| 99 | } | ||
| 100 | socket.send_to(&buf[..n], ep).await.unwrap(); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { | ||
| 106 | loop { | ||
| 107 | if let Some(config) = stack.config_v4() { | ||
| 108 | return config.clone(); | ||
| 109 | } | ||
| 110 | yield_now().await; | ||
| 111 | } | ||
| 112 | } | ||
diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs new file mode 100644 index 000000000..4c4982acc --- /dev/null +++ b/examples/rp/src/bin/flash.rs | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | //! This example test the flash connected to the RP2040 chip. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; | ||
| 10 | use embassy_rp::peripherals::FLASH; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | const ADDR_OFFSET: u32 = 0x100000; | ||
| 15 | const FLASH_SIZE: usize = 2 * 1024 * 1024; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_rp::init(Default::default()); | ||
| 20 | info!("Hello World!"); | ||
| 21 | |||
| 22 | // add some delay to give an attached debug probe time to parse the | ||
| 23 | // defmt RTT header. Reading that header might touch flash memory, which | ||
| 24 | // interferes with flash write operations. | ||
| 25 | // https://github.com/knurling-rs/defmt/pull/683 | ||
| 26 | Timer::after(Duration::from_millis(10)).await; | ||
| 27 | |||
| 28 | let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); | ||
| 29 | |||
| 30 | // Get JEDEC id | ||
| 31 | let jedec = flash.jedec_id().unwrap(); | ||
| 32 | info!("jedec id: 0x{:x}", jedec); | ||
| 33 | |||
| 34 | // Get unique id | ||
| 35 | let mut uid = [0; 8]; | ||
| 36 | flash.unique_id(&mut uid).unwrap(); | ||
| 37 | info!("unique id: {:?}", uid); | ||
| 38 | |||
| 39 | erase_write_sector(&mut flash, 0x00); | ||
| 40 | |||
| 41 | multiwrite_bytes(&mut flash, ERASE_SIZE as u32); | ||
| 42 | |||
| 43 | loop {} | ||
| 44 | } | ||
| 45 | |||
| 46 | fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { | ||
| 47 | info!(">>>> [multiwrite_bytes]"); | ||
| 48 | let mut read_buf = [0u8; ERASE_SIZE]; | ||
| 49 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); | ||
| 50 | |||
| 51 | info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); | ||
| 52 | info!("Contents start with {=[u8]}", read_buf[0..4]); | ||
| 53 | |||
| 54 | defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); | ||
| 55 | |||
| 56 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); | ||
| 57 | info!("Contents after erase starts with {=[u8]}", read_buf[0..4]); | ||
| 58 | if read_buf.iter().any(|x| *x != 0xFF) { | ||
| 59 | defmt::panic!("unexpected"); | ||
| 60 | } | ||
| 61 | |||
| 62 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &[0x01])); | ||
| 63 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 1, &[0x02])); | ||
| 64 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 2, &[0x03])); | ||
| 65 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 3, &[0x04])); | ||
| 66 | |||
| 67 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); | ||
| 68 | info!("Contents after write starts with {=[u8]}", read_buf[0..4]); | ||
| 69 | if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] { | ||
| 70 | defmt::panic!("unexpected"); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { | ||
| 75 | info!(">>>> [erase_write_sector]"); | ||
| 76 | let mut buf = [0u8; ERASE_SIZE]; | ||
| 77 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); | ||
| 78 | |||
| 79 | info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); | ||
| 80 | info!("Contents start with {=[u8]}", buf[0..4]); | ||
| 81 | |||
| 82 | defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); | ||
| 83 | |||
| 84 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); | ||
| 85 | info!("Contents after erase starts with {=[u8]}", buf[0..4]); | ||
| 86 | if buf.iter().any(|x| *x != 0xFF) { | ||
| 87 | defmt::panic!("unexpected"); | ||
| 88 | } | ||
| 89 | |||
| 90 | for b in buf.iter_mut() { | ||
| 91 | *b = 0xDA; | ||
| 92 | } | ||
| 93 | |||
| 94 | defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &buf)); | ||
| 95 | |||
| 96 | defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); | ||
| 97 | info!("Contents after write starts with {=[u8]}", buf[0..4]); | ||
| 98 | if buf.iter().any(|x| *x != 0xDA) { | ||
| 99 | defmt::panic!("unexpected"); | ||
| 100 | } | ||
| 101 | } | ||
diff --git a/examples/rp/src/bin/gpio_async.rs b/examples/rp/src/bin/gpio_async.rs index 52d13a9d5..bf58044d5 100644 --- a/examples/rp/src/bin/gpio_async.rs +++ b/examples/rp/src/bin/gpio_async.rs | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | //! This example shows how async gpio can be used with a RP2040. | ||
| 2 | //! | ||
| 3 | //! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs. | ||
| 4 | |||
| 1 | #![no_std] | 5 | #![no_std] |
| 2 | #![no_main] | 6 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 7 | #![feature(type_alias_impl_trait)] |
| @@ -9,8 +13,6 @@ use embassy_time::{Duration, Timer}; | |||
| 9 | use gpio::{Input, Level, Output, Pull}; | 13 | use gpio::{Input, Level, Output, Pull}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 14 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 15 | ||
| 12 | /// This example shows how async gpio can be used with a RP2040. | ||
| 13 | /// | ||
| 14 | /// It requires an external signal to be manually triggered on PIN 16. For | 16 | /// It requires an external signal to be manually triggered on PIN 16. For |
| 15 | /// example, this could be accomplished using an external power source with a | 17 | /// example, this could be accomplished using an external power source with a |
| 16 | /// button so that it is possible to toggle the signal from low to high. | 18 | /// button so that it is possible to toggle the signal from low to high. |
diff --git a/examples/rp/src/bin/gpout.rs b/examples/rp/src/bin/gpout.rs new file mode 100644 index 000000000..0a3b5fa98 --- /dev/null +++ b/examples/rp/src/bin/gpout.rs | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | //! This example shows how GPOUT (General purpose clock outputs) can toggle a output pin. | ||
| 2 | //! | ||
| 3 | //! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_rp::clocks; | ||
| 12 | use embassy_time::{Duration, Timer}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = embassy_rp::init(Default::default()); | ||
| 18 | |||
| 19 | let gpout3 = clocks::Gpout::new(p.PIN_25); | ||
| 20 | gpout3.set_div(1000, 0); | ||
| 21 | gpout3.enable(); | ||
| 22 | |||
| 23 | loop { | ||
| 24 | gpout3.set_src(clocks::GpoutSrc::Sys); | ||
| 25 | info!( | ||
| 26 | "Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}", | ||
| 27 | gpout3.get_freq() | ||
| 28 | ); | ||
| 29 | Timer::after(Duration::from_secs(2)).await; | ||
| 30 | |||
| 31 | gpout3.set_src(clocks::GpoutSrc::Ref); | ||
| 32 | info!( | ||
| 33 | "Pin 25 is now outputing CLK_REF/1000, should be toggling at {}", | ||
| 34 | gpout3.get_freq() | ||
| 35 | ); | ||
| 36 | Timer::after(Duration::from_secs(2)).await; | ||
| 37 | } | ||
| 38 | } | ||
diff --git a/examples/rp/src/bin/i2c_async.rs b/examples/rp/src/bin/i2c_async.rs new file mode 100644 index 000000000..93224bc43 --- /dev/null +++ b/examples/rp/src/bin/i2c_async.rs | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | //! This example shows how to communicate asynchronous using i2c with external chips. | ||
| 2 | //! | ||
| 3 | //! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip. | ||
| 4 | //! (https://www.microchip.com/en-us/product/mcp23017) | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | #![feature(type_alias_impl_trait)] | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_rp::bind_interrupts; | ||
| 13 | use embassy_rp::i2c::{self, Config, InterruptHandler}; | ||
| 14 | use embassy_rp::peripherals::I2C1; | ||
| 15 | use embassy_time::{Duration, Timer}; | ||
| 16 | use embedded_hal_async::i2c::I2c; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | bind_interrupts!(struct Irqs { | ||
| 20 | I2C1_IRQ => InterruptHandler<I2C1>; | ||
| 21 | }); | ||
| 22 | |||
| 23 | #[allow(dead_code)] | ||
| 24 | mod mcp23017 { | ||
| 25 | pub const ADDR: u8 = 0x20; // default addr | ||
| 26 | |||
| 27 | macro_rules! mcpregs { | ||
| 28 | ($($name:ident : $val:expr),* $(,)?) => { | ||
| 29 | $( | ||
| 30 | pub const $name: u8 = $val; | ||
| 31 | )* | ||
| 32 | |||
| 33 | pub fn regname(reg: u8) -> &'static str { | ||
| 34 | match reg { | ||
| 35 | $( | ||
| 36 | $val => stringify!($name), | ||
| 37 | )* | ||
| 38 | _ => panic!("bad reg"), | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | // These are correct for IOCON.BANK=0 | ||
| 45 | mcpregs! { | ||
| 46 | IODIRA: 0x00, | ||
| 47 | IPOLA: 0x02, | ||
| 48 | GPINTENA: 0x04, | ||
| 49 | DEFVALA: 0x06, | ||
| 50 | INTCONA: 0x08, | ||
| 51 | IOCONA: 0x0A, | ||
| 52 | GPPUA: 0x0C, | ||
| 53 | INTFA: 0x0E, | ||
| 54 | INTCAPA: 0x10, | ||
| 55 | GPIOA: 0x12, | ||
| 56 | OLATA: 0x14, | ||
| 57 | IODIRB: 0x01, | ||
| 58 | IPOLB: 0x03, | ||
| 59 | GPINTENB: 0x05, | ||
| 60 | DEFVALB: 0x07, | ||
| 61 | INTCONB: 0x09, | ||
| 62 | IOCONB: 0x0B, | ||
| 63 | GPPUB: 0x0D, | ||
| 64 | INTFB: 0x0F, | ||
| 65 | INTCAPB: 0x11, | ||
| 66 | GPIOB: 0x13, | ||
| 67 | OLATB: 0x15, | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | #[embassy_executor::main] | ||
| 72 | async fn main(_spawner: Spawner) { | ||
| 73 | let p = embassy_rp::init(Default::default()); | ||
| 74 | |||
| 75 | let sda = p.PIN_14; | ||
| 76 | let scl = p.PIN_15; | ||
| 77 | |||
| 78 | info!("set up i2c "); | ||
| 79 | let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, Config::default()); | ||
| 80 | |||
| 81 | use mcp23017::*; | ||
| 82 | |||
| 83 | info!("init mcp23017 config for IxpandO"); | ||
| 84 | // init - a outputs, b inputs | ||
| 85 | i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap(); | ||
| 86 | i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap(); | ||
| 87 | i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups | ||
| 88 | |||
| 89 | let mut val = 1; | ||
| 90 | loop { | ||
| 91 | let mut portb = [0]; | ||
| 92 | |||
| 93 | i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap(); | ||
| 94 | info!("portb = {:02x}", portb[0]); | ||
| 95 | i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap(); | ||
| 96 | val = val.rotate_left(1); | ||
| 97 | |||
| 98 | // get a register dump | ||
| 99 | info!("getting register dump"); | ||
| 100 | let mut regs = [0; 22]; | ||
| 101 | i2c.write_read(ADDR, &[0], &mut regs).await.unwrap(); | ||
| 102 | // always get the regdump but only display it if portb'0 is set | ||
| 103 | if portb[0] & 1 != 0 { | ||
| 104 | for (idx, reg) in regs.into_iter().enumerate() { | ||
| 105 | info!("{} => {:02x}", regname(idx as u8), reg); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | Timer::after(Duration::from_millis(100)).await; | ||
| 110 | } | ||
| 111 | } | ||
diff --git a/examples/rp/src/bin/i2c_blocking.rs b/examples/rp/src/bin/i2c_blocking.rs new file mode 100644 index 000000000..1c8c2039d --- /dev/null +++ b/examples/rp/src/bin/i2c_blocking.rs | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | //! This example shows how to communicate using i2c with external chips. | ||
| 2 | //! | ||
| 3 | //! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip. | ||
| 4 | //! (https://www.microchip.com/en-us/product/mcp23017) | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | #![feature(type_alias_impl_trait)] | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_rp::i2c::{self, Config}; | ||
| 13 | use embassy_time::{Duration, Timer}; | ||
| 14 | use embedded_hal_1::i2c::I2c; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[allow(dead_code)] | ||
| 18 | mod mcp23017 { | ||
| 19 | pub const ADDR: u8 = 0x20; // default addr | ||
| 20 | |||
| 21 | pub const IODIRA: u8 = 0x00; | ||
| 22 | pub const IPOLA: u8 = 0x02; | ||
| 23 | pub const GPINTENA: u8 = 0x04; | ||
| 24 | pub const DEFVALA: u8 = 0x06; | ||
| 25 | pub const INTCONA: u8 = 0x08; | ||
| 26 | pub const IOCONA: u8 = 0x0A; | ||
| 27 | pub const GPPUA: u8 = 0x0C; | ||
| 28 | pub const INTFA: u8 = 0x0E; | ||
| 29 | pub const INTCAPA: u8 = 0x10; | ||
| 30 | pub const GPIOA: u8 = 0x12; | ||
| 31 | pub const OLATA: u8 = 0x14; | ||
| 32 | pub const IODIRB: u8 = 0x01; | ||
| 33 | pub const IPOLB: u8 = 0x03; | ||
| 34 | pub const GPINTENB: u8 = 0x05; | ||
| 35 | pub const DEFVALB: u8 = 0x07; | ||
| 36 | pub const INTCONB: u8 = 0x09; | ||
| 37 | pub const IOCONB: u8 = 0x0B; | ||
| 38 | pub const GPPUB: u8 = 0x0D; | ||
| 39 | pub const INTFB: u8 = 0x0F; | ||
| 40 | pub const INTCAPB: u8 = 0x11; | ||
| 41 | pub const GPIOB: u8 = 0x13; | ||
| 42 | pub const OLATB: u8 = 0x15; | ||
| 43 | } | ||
| 44 | |||
| 45 | #[embassy_executor::main] | ||
| 46 | async fn main(_spawner: Spawner) { | ||
| 47 | let p = embassy_rp::init(Default::default()); | ||
| 48 | |||
| 49 | let sda = p.PIN_14; | ||
| 50 | let scl = p.PIN_15; | ||
| 51 | |||
| 52 | info!("set up i2c "); | ||
| 53 | let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default()); | ||
| 54 | |||
| 55 | use mcp23017::*; | ||
| 56 | |||
| 57 | info!("init mcp23017 config for IxpandO"); | ||
| 58 | // init - a outputs, b inputs | ||
| 59 | i2c.write(ADDR, &[IODIRA, 0x00]).unwrap(); | ||
| 60 | i2c.write(ADDR, &[IODIRB, 0xff]).unwrap(); | ||
| 61 | i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups | ||
| 62 | |||
| 63 | let mut val = 0xaa; | ||
| 64 | loop { | ||
| 65 | let mut portb = [0]; | ||
| 66 | |||
| 67 | i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap(); | ||
| 68 | i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap(); | ||
| 69 | |||
| 70 | info!("portb = {:02x}", portb[0]); | ||
| 71 | val = !val; | ||
| 72 | |||
| 73 | Timer::after(Duration::from_secs(1)).await; | ||
| 74 | } | ||
| 75 | } | ||
diff --git a/examples/rp/src/bin/lora_lorawan.rs b/examples/rp/src/bin/lora_lorawan.rs new file mode 100644 index 000000000..d631fafa1 --- /dev/null +++ b/examples/rp/src/bin/lora_lorawan.rs | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. | ||
| 2 | //! It demonstrates LoRaWAN join functionality. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_lora::LoraTimer; | ||
| 13 | use embassy_rp::gpio::{Input, Level, Output, Pin, Pull}; | ||
| 14 | use embassy_rp::spi::{Config, Spi}; | ||
| 15 | use embassy_time::Delay; | ||
| 16 | use lora_phy::mod_params::*; | ||
| 17 | use lora_phy::sx1261_2::SX1261_2; | ||
| 18 | use lora_phy::LoRa; | ||
| 19 | use lorawan::default_crypto::DefaultFactory as Crypto; | ||
| 20 | use lorawan_device::async_device::lora_radio::LoRaRadio; | ||
| 21 | use lorawan_device::async_device::{region, Device, JoinMode}; | ||
| 22 | use {defmt_rtt as _, panic_probe as _}; | ||
| 23 | |||
| 24 | const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(_spawner: Spawner) { | ||
| 28 | let p = embassy_rp::init(Default::default()); | ||
| 29 | |||
| 30 | let miso = p.PIN_12; | ||
| 31 | let mosi = p.PIN_11; | ||
| 32 | let clk = p.PIN_10; | ||
| 33 | let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||
| 34 | |||
| 35 | let nss = Output::new(p.PIN_3.degrade(), Level::High); | ||
| 36 | let reset = Output::new(p.PIN_15.degrade(), Level::High); | ||
| 37 | let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); | ||
| 38 | let busy = Input::new(p.PIN_2.degrade(), Pull::None); | ||
| 39 | |||
| 40 | let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); | ||
| 41 | |||
| 42 | let mut delay = Delay; | ||
| 43 | |||
| 44 | let lora = { | ||
| 45 | match LoRa::new( | ||
| 46 | SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), | ||
| 47 | true, | ||
| 48 | &mut delay, | ||
| 49 | ) | ||
| 50 | .await | ||
| 51 | { | ||
| 52 | Ok(l) => l, | ||
| 53 | Err(err) => { | ||
| 54 | info!("Radio error = {}", err); | ||
| 55 | return; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | |||
| 60 | let radio = LoRaRadio::new(lora); | ||
| 61 | let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); | ||
| 62 | let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), embassy_rp::clocks::RoscRng); | ||
| 63 | |||
| 64 | defmt::info!("Joining LoRaWAN network"); | ||
| 65 | |||
| 66 | // TODO: Adjust the EUI and Keys according to your network credentials | ||
| 67 | match device | ||
| 68 | .join(&JoinMode::OTAA { | ||
| 69 | deveui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 70 | appeui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 71 | appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | ||
| 72 | }) | ||
| 73 | .await | ||
| 74 | { | ||
| 75 | Ok(()) => defmt::info!("LoRaWAN network joined"), | ||
| 76 | Err(err) => { | ||
| 77 | info!("Radio error = {}", err); | ||
| 78 | return; | ||
| 79 | } | ||
| 80 | }; | ||
| 81 | } | ||
diff --git a/examples/rp/src/bin/lora_p2p_receive.rs b/examples/rp/src/bin/lora_p2p_receive.rs new file mode 100644 index 000000000..396d669de --- /dev/null +++ b/examples/rp/src/bin/lora_p2p_receive.rs | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. | ||
| 2 | //! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_rp::gpio::{Input, Level, Output, Pin, Pull}; | ||
| 13 | use embassy_rp::spi::{Config, Spi}; | ||
| 14 | use embassy_time::{Delay, Duration, Timer}; | ||
| 15 | use lora_phy::mod_params::*; | ||
| 16 | use lora_phy::sx1261_2::SX1261_2; | ||
| 17 | use lora_phy::LoRa; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 24 | let p = embassy_rp::init(Default::default()); | ||
| 25 | |||
| 26 | let miso = p.PIN_12; | ||
| 27 | let mosi = p.PIN_11; | ||
| 28 | let clk = p.PIN_10; | ||
| 29 | let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||
| 30 | |||
| 31 | let nss = Output::new(p.PIN_3.degrade(), Level::High); | ||
| 32 | let reset = Output::new(p.PIN_15.degrade(), Level::High); | ||
| 33 | let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); | ||
| 34 | let busy = Input::new(p.PIN_2.degrade(), Pull::None); | ||
| 35 | |||
| 36 | let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); | ||
| 37 | |||
| 38 | let mut delay = Delay; | ||
| 39 | |||
| 40 | let mut lora = { | ||
| 41 | match LoRa::new( | ||
| 42 | SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), | ||
| 43 | false, | ||
| 44 | &mut delay, | ||
| 45 | ) | ||
| 46 | .await | ||
| 47 | { | ||
| 48 | Ok(l) => l, | ||
| 49 | Err(err) => { | ||
| 50 | info!("Radio error = {}", err); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | }; | ||
| 55 | |||
| 56 | let mut debug_indicator = Output::new(p.PIN_25, Level::Low); | ||
| 57 | |||
| 58 | let mut receiving_buffer = [00u8; 100]; | ||
| 59 | |||
| 60 | let mdltn_params = { | ||
| 61 | match lora.create_modulation_params( | ||
| 62 | SpreadingFactor::_10, | ||
| 63 | Bandwidth::_250KHz, | ||
| 64 | CodingRate::_4_8, | ||
| 65 | LORA_FREQUENCY_IN_HZ, | ||
| 66 | ) { | ||
| 67 | Ok(mp) => mp, | ||
| 68 | Err(err) => { | ||
| 69 | info!("Radio error = {}", err); | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | }; | ||
| 74 | |||
| 75 | let rx_pkt_params = { | ||
| 76 | match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { | ||
| 77 | Ok(pp) => pp, | ||
| 78 | Err(err) => { | ||
| 79 | info!("Radio error = {}", err); | ||
| 80 | return; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | }; | ||
| 84 | |||
| 85 | match lora | ||
| 86 | .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) | ||
| 87 | .await | ||
| 88 | { | ||
| 89 | Ok(()) => {} | ||
| 90 | Err(err) => { | ||
| 91 | info!("Radio error = {}", err); | ||
| 92 | return; | ||
| 93 | } | ||
| 94 | }; | ||
| 95 | |||
| 96 | loop { | ||
| 97 | receiving_buffer = [00u8; 100]; | ||
| 98 | match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { | ||
| 99 | Ok((received_len, _rx_pkt_status)) => { | ||
| 100 | if (received_len == 3) | ||
| 101 | && (receiving_buffer[0] == 0x01u8) | ||
| 102 | && (receiving_buffer[1] == 0x02u8) | ||
| 103 | && (receiving_buffer[2] == 0x03u8) | ||
| 104 | { | ||
| 105 | info!("rx successful"); | ||
| 106 | debug_indicator.set_high(); | ||
| 107 | Timer::after(Duration::from_secs(5)).await; | ||
| 108 | debug_indicator.set_low(); | ||
| 109 | } else { | ||
| 110 | info!("rx unknown packet"); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | Err(err) => info!("rx unsuccessful = {}", err), | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
diff --git a/examples/rp/src/bin/lora_p2p_send.rs b/examples/rp/src/bin/lora_p2p_send.rs new file mode 100644 index 000000000..a0f70fa5c --- /dev/null +++ b/examples/rp/src/bin/lora_p2p_send.rs | |||
| @@ -0,0 +1,104 @@ | |||
| 1 | //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. | ||
| 2 | //! It demonstrates LORA P2P send functionality. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_rp::gpio::{Input, Level, Output, Pin, Pull}; | ||
| 13 | use embassy_rp::spi::{Config, Spi}; | ||
| 14 | use embassy_time::Delay; | ||
| 15 | use lora_phy::mod_params::*; | ||
| 16 | use lora_phy::sx1261_2::SX1261_2; | ||
| 17 | use lora_phy::LoRa; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 24 | let p = embassy_rp::init(Default::default()); | ||
| 25 | |||
| 26 | let miso = p.PIN_12; | ||
| 27 | let mosi = p.PIN_11; | ||
| 28 | let clk = p.PIN_10; | ||
| 29 | let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||
| 30 | |||
| 31 | let nss = Output::new(p.PIN_3.degrade(), Level::High); | ||
| 32 | let reset = Output::new(p.PIN_15.degrade(), Level::High); | ||
| 33 | let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); | ||
| 34 | let busy = Input::new(p.PIN_2.degrade(), Pull::None); | ||
| 35 | |||
| 36 | let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); | ||
| 37 | |||
| 38 | let mut delay = Delay; | ||
| 39 | |||
| 40 | let mut lora = { | ||
| 41 | match LoRa::new( | ||
| 42 | SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), | ||
| 43 | false, | ||
| 44 | &mut delay, | ||
| 45 | ) | ||
| 46 | .await | ||
| 47 | { | ||
| 48 | Ok(l) => l, | ||
| 49 | Err(err) => { | ||
| 50 | info!("Radio error = {}", err); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | }; | ||
| 55 | |||
| 56 | let mdltn_params = { | ||
| 57 | match lora.create_modulation_params( | ||
| 58 | SpreadingFactor::_10, | ||
| 59 | Bandwidth::_250KHz, | ||
| 60 | CodingRate::_4_8, | ||
| 61 | LORA_FREQUENCY_IN_HZ, | ||
| 62 | ) { | ||
| 63 | Ok(mp) => mp, | ||
| 64 | Err(err) => { | ||
| 65 | info!("Radio error = {}", err); | ||
| 66 | return; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | }; | ||
| 70 | |||
| 71 | let mut tx_pkt_params = { | ||
| 72 | match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { | ||
| 73 | Ok(pp) => pp, | ||
| 74 | Err(err) => { | ||
| 75 | info!("Radio error = {}", err); | ||
| 76 | return; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | }; | ||
| 80 | |||
| 81 | match lora.prepare_for_tx(&mdltn_params, 20, false).await { | ||
| 82 | Ok(()) => {} | ||
| 83 | Err(err) => { | ||
| 84 | info!("Radio error = {}", err); | ||
| 85 | return; | ||
| 86 | } | ||
| 87 | }; | ||
| 88 | |||
| 89 | let buffer = [0x01u8, 0x02u8, 0x03u8]; | ||
| 90 | match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { | ||
| 91 | Ok(()) => { | ||
| 92 | info!("TX DONE"); | ||
| 93 | } | ||
| 94 | Err(err) => { | ||
| 95 | info!("Radio error = {}", err); | ||
| 96 | return; | ||
| 97 | } | ||
| 98 | }; | ||
| 99 | |||
| 100 | match lora.sleep(&mut delay).await { | ||
| 101 | Ok(()) => info!("Sleep successful"), | ||
| 102 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 103 | } | ||
| 104 | } | ||
diff --git a/examples/rp/src/bin/lora_p2p_send_multicore.rs b/examples/rp/src/bin/lora_p2p_send_multicore.rs new file mode 100644 index 000000000..89a62818d --- /dev/null +++ b/examples/rp/src/bin/lora_p2p_send_multicore.rs | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. | ||
| 2 | //! It demonstrates LORA P2P send functionality using the second core, with data provided by the first core. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Executor; | ||
| 11 | use embassy_lora::iv::GenericSx126xInterfaceVariant; | ||
| 12 | use embassy_rp::gpio::{AnyPin, Input, Level, Output, Pin, Pull}; | ||
| 13 | use embassy_rp::multicore::{spawn_core1, Stack}; | ||
| 14 | use embassy_rp::peripherals::SPI1; | ||
| 15 | use embassy_rp::spi::{Async, Config, Spi}; | ||
| 16 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 17 | use embassy_sync::channel::Channel; | ||
| 18 | use embassy_time::{Delay, Duration, Timer}; | ||
| 19 | use lora_phy::mod_params::*; | ||
| 20 | use lora_phy::sx1261_2::SX1261_2; | ||
| 21 | use lora_phy::LoRa; | ||
| 22 | use static_cell::StaticCell; | ||
| 23 | use {defmt_rtt as _, panic_probe as _}; | ||
| 24 | |||
| 25 | static mut CORE1_STACK: Stack<4096> = Stack::new(); | ||
| 26 | static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); | ||
| 27 | static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); | ||
| 28 | static CHANNEL: Channel<CriticalSectionRawMutex, [u8; 3], 1> = Channel::new(); | ||
| 29 | |||
| 30 | const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region | ||
| 31 | |||
| 32 | #[cortex_m_rt::entry] | ||
| 33 | fn main() -> ! { | ||
| 34 | let p = embassy_rp::init(Default::default()); | ||
| 35 | |||
| 36 | let miso = p.PIN_12; | ||
| 37 | let mosi = p.PIN_11; | ||
| 38 | let clk = p.PIN_10; | ||
| 39 | let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||
| 40 | |||
| 41 | let nss = Output::new(p.PIN_3.degrade(), Level::High); | ||
| 42 | let reset = Output::new(p.PIN_15.degrade(), Level::High); | ||
| 43 | let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); | ||
| 44 | let busy = Input::new(p.PIN_2.degrade(), Pull::None); | ||
| 45 | |||
| 46 | let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); | ||
| 47 | |||
| 48 | spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { | ||
| 49 | let executor1 = EXECUTOR1.init(Executor::new()); | ||
| 50 | executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(spi, iv)))); | ||
| 51 | }); | ||
| 52 | |||
| 53 | let executor0 = EXECUTOR0.init(Executor::new()); | ||
| 54 | executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); | ||
| 55 | } | ||
| 56 | |||
| 57 | #[embassy_executor::task] | ||
| 58 | async fn core0_task() { | ||
| 59 | info!("Hello from core 0"); | ||
| 60 | loop { | ||
| 61 | CHANNEL.send([0x01u8, 0x02u8, 0x03u8]).await; | ||
| 62 | Timer::after(Duration::from_millis(60 * 1000)).await; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | #[embassy_executor::task] | ||
| 67 | async fn core1_task( | ||
| 68 | spi: Spi<'static, SPI1, Async>, | ||
| 69 | iv: GenericSx126xInterfaceVariant<Output<'static, AnyPin>, Input<'static, AnyPin>>, | ||
| 70 | ) { | ||
| 71 | info!("Hello from core 1"); | ||
| 72 | let mut delay = Delay; | ||
| 73 | |||
| 74 | let mut lora = { | ||
| 75 | match LoRa::new( | ||
| 76 | SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), | ||
| 77 | false, | ||
| 78 | &mut delay, | ||
| 79 | ) | ||
| 80 | .await | ||
| 81 | { | ||
| 82 | Ok(l) => l, | ||
| 83 | Err(err) => { | ||
| 84 | info!("Radio error = {}", err); | ||
| 85 | return; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | let mdltn_params = { | ||
| 91 | match lora.create_modulation_params( | ||
| 92 | SpreadingFactor::_10, | ||
| 93 | Bandwidth::_250KHz, | ||
| 94 | CodingRate::_4_8, | ||
| 95 | LORA_FREQUENCY_IN_HZ, | ||
| 96 | ) { | ||
| 97 | Ok(mp) => mp, | ||
| 98 | Err(err) => { | ||
| 99 | info!("Radio error = {}", err); | ||
| 100 | return; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | |||
| 105 | let mut tx_pkt_params = { | ||
| 106 | match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { | ||
| 107 | Ok(pp) => pp, | ||
| 108 | Err(err) => { | ||
| 109 | info!("Radio error = {}", err); | ||
| 110 | return; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | }; | ||
| 114 | |||
| 115 | loop { | ||
| 116 | let buffer: [u8; 3] = CHANNEL.recv().await; | ||
| 117 | match lora.prepare_for_tx(&mdltn_params, 20, false).await { | ||
| 118 | Ok(()) => {} | ||
| 119 | Err(err) => { | ||
| 120 | info!("Radio error = {}", err); | ||
| 121 | return; | ||
| 122 | } | ||
| 123 | }; | ||
| 124 | |||
| 125 | match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { | ||
| 126 | Ok(()) => { | ||
| 127 | info!("TX DONE"); | ||
| 128 | } | ||
| 129 | Err(err) => { | ||
| 130 | info!("Radio error = {}", err); | ||
| 131 | return; | ||
| 132 | } | ||
| 133 | }; | ||
| 134 | |||
| 135 | match lora.sleep(&mut delay).await { | ||
| 136 | Ok(()) => info!("Sleep successful"), | ||
| 137 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } | ||
diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs new file mode 100644 index 000000000..893b724bf --- /dev/null +++ b/examples/rp/src/bin/multicore.rs | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | //! This example shows how to send messages between the two cores in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Executor; | ||
| 11 | use embassy_rp::gpio::{Level, Output}; | ||
| 12 | use embassy_rp::multicore::{spawn_core1, Stack}; | ||
| 13 | use embassy_rp::peripherals::PIN_25; | ||
| 14 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 15 | use embassy_sync::channel::Channel; | ||
| 16 | use embassy_time::{Duration, Timer}; | ||
| 17 | use static_cell::StaticCell; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | static mut CORE1_STACK: Stack<4096> = Stack::new(); | ||
| 21 | static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); | ||
| 22 | static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); | ||
| 23 | static CHANNEL: Channel<CriticalSectionRawMutex, LedState, 1> = Channel::new(); | ||
| 24 | |||
| 25 | enum LedState { | ||
| 26 | On, | ||
| 27 | Off, | ||
| 28 | } | ||
| 29 | |||
| 30 | #[cortex_m_rt::entry] | ||
| 31 | fn main() -> ! { | ||
| 32 | let p = embassy_rp::init(Default::default()); | ||
| 33 | let led = Output::new(p.PIN_25, Level::Low); | ||
| 34 | |||
| 35 | spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { | ||
| 36 | let executor1 = EXECUTOR1.init(Executor::new()); | ||
| 37 | executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); | ||
| 38 | }); | ||
| 39 | |||
| 40 | let executor0 = EXECUTOR0.init(Executor::new()); | ||
| 41 | executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); | ||
| 42 | } | ||
| 43 | |||
| 44 | #[embassy_executor::task] | ||
| 45 | async fn core0_task() { | ||
| 46 | info!("Hello from core 0"); | ||
| 47 | loop { | ||
| 48 | CHANNEL.send(LedState::On).await; | ||
| 49 | Timer::after(Duration::from_millis(100)).await; | ||
| 50 | CHANNEL.send(LedState::Off).await; | ||
| 51 | Timer::after(Duration::from_millis(400)).await; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | #[embassy_executor::task] | ||
| 56 | async fn core1_task(mut led: Output<'static, PIN_25>) { | ||
| 57 | info!("Hello from core 1"); | ||
| 58 | loop { | ||
| 59 | match CHANNEL.recv().await { | ||
| 60 | LedState::On => led.set_high(), | ||
| 61 | LedState::Off => led.set_low(), | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
diff --git a/examples/rp/src/bin/multiprio.rs b/examples/rp/src/bin/multiprio.rs new file mode 100644 index 000000000..9ace4cd68 --- /dev/null +++ b/examples/rp/src/bin/multiprio.rs | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | //! This example showcases how to create multiple Executor instances to run tasks at | ||
| 2 | //! different priority levels. | ||
| 3 | //! | ||
| 4 | //! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling | ||
| 5 | //! there's work in the queue, and `wfe` for waiting for work. | ||
| 6 | //! | ||
| 7 | //! Medium and high priority executors run in two interrupts with different priorities. | ||
| 8 | //! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since | ||
| 9 | //! when there's work the interrupt will trigger and run the executor. | ||
| 10 | //! | ||
| 11 | //! Sample output below. Note that high priority ticks can interrupt everything else, and | ||
| 12 | //! medium priority computations can interrupt low priority computations, making them to appear | ||
| 13 | //! to take significantly longer time. | ||
| 14 | //! | ||
| 15 | //! ```not_rust | ||
| 16 | //! [med] Starting long computation | ||
| 17 | //! [med] done in 992 ms | ||
| 18 | //! [high] tick! | ||
| 19 | //! [low] Starting long computation | ||
| 20 | //! [med] Starting long computation | ||
| 21 | //! [high] tick! | ||
| 22 | //! [high] tick! | ||
| 23 | //! [med] done in 993 ms | ||
| 24 | //! [med] Starting long computation | ||
| 25 | //! [high] tick! | ||
| 26 | //! [high] tick! | ||
| 27 | //! [med] done in 993 ms | ||
| 28 | //! [low] done in 3972 ms | ||
| 29 | //! [med] Starting long computation | ||
| 30 | //! [high] tick! | ||
| 31 | //! [high] tick! | ||
| 32 | //! [med] done in 993 ms | ||
| 33 | //! ``` | ||
| 34 | //! | ||
| 35 | //! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor. | ||
| 36 | //! You will get an output like the following. Note that no computation is ever interrupted. | ||
| 37 | //! | ||
| 38 | //! ```not_rust | ||
| 39 | //! [high] tick! | ||
| 40 | //! [med] Starting long computation | ||
| 41 | //! [med] done in 496 ms | ||
| 42 | //! [low] Starting long computation | ||
| 43 | //! [low] done in 992 ms | ||
| 44 | //! [med] Starting long computation | ||
| 45 | //! [med] done in 496 ms | ||
| 46 | //! [high] tick! | ||
| 47 | //! [low] Starting long computation | ||
| 48 | //! [low] done in 992 ms | ||
| 49 | //! [high] tick! | ||
| 50 | //! [med] Starting long computation | ||
| 51 | //! [med] done in 496 ms | ||
| 52 | //! [high] tick! | ||
| 53 | //! ``` | ||
| 54 | //! | ||
| 55 | |||
| 56 | #![no_std] | ||
| 57 | #![no_main] | ||
| 58 | #![feature(type_alias_impl_trait)] | ||
| 59 | |||
| 60 | use cortex_m_rt::entry; | ||
| 61 | use defmt::{info, unwrap}; | ||
| 62 | use embassy_executor::{Executor, InterruptExecutor}; | ||
| 63 | use embassy_rp::interrupt; | ||
| 64 | use embassy_rp::interrupt::{InterruptExt, Priority}; | ||
| 65 | use embassy_time::{Duration, Instant, Timer, TICK_HZ}; | ||
| 66 | use static_cell::StaticCell; | ||
| 67 | use {defmt_rtt as _, panic_probe as _}; | ||
| 68 | |||
| 69 | #[embassy_executor::task] | ||
| 70 | async fn run_high() { | ||
| 71 | loop { | ||
| 72 | info!(" [high] tick!"); | ||
| 73 | Timer::after(Duration::from_ticks(673740)).await; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | #[embassy_executor::task] | ||
| 78 | async fn run_med() { | ||
| 79 | loop { | ||
| 80 | let start = Instant::now(); | ||
| 81 | info!(" [med] Starting long computation"); | ||
| 82 | |||
| 83 | // Spin-wait to simulate a long CPU computation | ||
| 84 | cortex_m::asm::delay(125_000_000); // ~1 second | ||
| 85 | |||
| 86 | let end = Instant::now(); | ||
| 87 | let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ; | ||
| 88 | info!(" [med] done in {} ms", ms); | ||
| 89 | |||
| 90 | Timer::after(Duration::from_ticks(53421)).await; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | #[embassy_executor::task] | ||
| 95 | async fn run_low() { | ||
| 96 | loop { | ||
| 97 | let start = Instant::now(); | ||
| 98 | info!("[low] Starting long computation"); | ||
| 99 | |||
| 100 | // Spin-wait to simulate a long CPU computation | ||
| 101 | cortex_m::asm::delay(250_000_000); // ~2 seconds | ||
| 102 | |||
| 103 | let end = Instant::now(); | ||
| 104 | let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ; | ||
| 105 | info!("[low] done in {} ms", ms); | ||
| 106 | |||
| 107 | Timer::after(Duration::from_ticks(82983)).await; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); | ||
| 112 | static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); | ||
| 113 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); | ||
| 114 | |||
| 115 | #[interrupt] | ||
| 116 | unsafe fn SWI_IRQ_1() { | ||
| 117 | EXECUTOR_HIGH.on_interrupt() | ||
| 118 | } | ||
| 119 | |||
| 120 | #[interrupt] | ||
| 121 | unsafe fn SWI_IRQ_0() { | ||
| 122 | EXECUTOR_MED.on_interrupt() | ||
| 123 | } | ||
| 124 | |||
| 125 | #[entry] | ||
| 126 | fn main() -> ! { | ||
| 127 | info!("Hello World!"); | ||
| 128 | |||
| 129 | let _p = embassy_rp::init(Default::default()); | ||
| 130 | |||
| 131 | // High-priority executor: SWI_IRQ_1, priority level 2 | ||
| 132 | interrupt::SWI_IRQ_1.set_priority(Priority::P2); | ||
| 133 | let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1); | ||
| 134 | unwrap!(spawner.spawn(run_high())); | ||
| 135 | |||
| 136 | // Medium-priority executor: SWI_IRQ_0, priority level 3 | ||
| 137 | interrupt::SWI_IRQ_0.set_priority(Priority::P3); | ||
| 138 | let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0); | ||
| 139 | unwrap!(spawner.spawn(run_med())); | ||
| 140 | |||
| 141 | // Low priority executor: runs in thread mode, using WFE/SEV | ||
| 142 | let executor = EXECUTOR_LOW.init(Executor::new()); | ||
| 143 | executor.run(|spawner| { | ||
| 144 | unwrap!(spawner.spawn(run_low())); | ||
| 145 | }); | ||
| 146 | } | ||
diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs new file mode 100644 index 000000000..c001d6440 --- /dev/null +++ b/examples/rp/src/bin/pio_async.rs | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | //! This example shows powerful PIO module in the RP2040 chip. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | use defmt::info; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_rp::bind_interrupts; | ||
| 9 | use embassy_rp::peripherals::PIO0; | ||
| 10 | use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; | ||
| 11 | use embassy_rp::relocate::RelocatedProgram; | ||
| 12 | use fixed::traits::ToFixed; | ||
| 13 | use fixed_macro::types::U56F8; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 18 | }); | ||
| 19 | |||
| 20 | fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { | ||
| 21 | // Setup sm0 | ||
| 22 | |||
| 23 | // Send data serially to pin | ||
| 24 | let prg = pio_proc::pio_asm!( | ||
| 25 | ".origin 16", | ||
| 26 | "set pindirs, 1", | ||
| 27 | ".wrap_target", | ||
| 28 | "out pins,1 [19]", | ||
| 29 | ".wrap", | ||
| 30 | ); | ||
| 31 | |||
| 32 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 33 | let mut cfg = Config::default(); | ||
| 34 | cfg.use_program(&pio.load_program(&relocated), &[]); | ||
| 35 | let out_pin = pio.make_pio_pin(pin); | ||
| 36 | cfg.set_out_pins(&[&out_pin]); | ||
| 37 | cfg.set_set_pins(&[&out_pin]); | ||
| 38 | cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed(); | ||
| 39 | cfg.shift_out.auto_fill = true; | ||
| 40 | sm.set_config(&cfg); | ||
| 41 | } | ||
| 42 | |||
| 43 | #[embassy_executor::task] | ||
| 44 | async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) { | ||
| 45 | sm.set_enable(true); | ||
| 46 | |||
| 47 | let mut v = 0x0f0caffa; | ||
| 48 | loop { | ||
| 49 | sm.tx().wait_push(v).await; | ||
| 50 | v ^= 0xffff; | ||
| 51 | info!("Pushed {:032b} to FIFO", v); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) { | ||
| 56 | // Setupm sm1 | ||
| 57 | |||
| 58 | // Read 0b10101 repeatedly until ISR is full | ||
| 59 | let prg = pio_proc::pio_asm!( | ||
| 60 | // | ||
| 61 | ".origin 8", | ||
| 62 | "set x, 0x15", | ||
| 63 | ".wrap_target", | ||
| 64 | "in x, 5 [31]", | ||
| 65 | ".wrap", | ||
| 66 | ); | ||
| 67 | |||
| 68 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 69 | let mut cfg = Config::default(); | ||
| 70 | cfg.use_program(&pio.load_program(&relocated), &[]); | ||
| 71 | cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); | ||
| 72 | cfg.shift_in.auto_fill = true; | ||
| 73 | cfg.shift_in.direction = ShiftDirection::Right; | ||
| 74 | sm.set_config(&cfg); | ||
| 75 | } | ||
| 76 | |||
| 77 | #[embassy_executor::task] | ||
| 78 | async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) { | ||
| 79 | sm.set_enable(true); | ||
| 80 | loop { | ||
| 81 | let rx = sm.rx().wait_pull().await; | ||
| 82 | info!("Pulled {:032b} from FIFO", rx); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) { | ||
| 87 | // Setup sm2 | ||
| 88 | |||
| 89 | // Repeatedly trigger IRQ 3 | ||
| 90 | let prg = pio_proc::pio_asm!( | ||
| 91 | ".origin 0", | ||
| 92 | ".wrap_target", | ||
| 93 | "set x,10", | ||
| 94 | "delay:", | ||
| 95 | "jmp x-- delay [15]", | ||
| 96 | "irq 3 [15]", | ||
| 97 | ".wrap", | ||
| 98 | ); | ||
| 99 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 100 | let mut cfg = Config::default(); | ||
| 101 | cfg.use_program(&pio.load_program(&relocated), &[]); | ||
| 102 | cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); | ||
| 103 | sm.set_config(&cfg); | ||
| 104 | } | ||
| 105 | |||
| 106 | #[embassy_executor::task] | ||
| 107 | async fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) { | ||
| 108 | sm.set_enable(true); | ||
| 109 | loop { | ||
| 110 | irq.wait().await; | ||
| 111 | info!("IRQ trigged"); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | #[embassy_executor::main] | ||
| 116 | async fn main(spawner: Spawner) { | ||
| 117 | let p = embassy_rp::init(Default::default()); | ||
| 118 | let pio = p.PIO0; | ||
| 119 | |||
| 120 | let Pio { | ||
| 121 | mut common, | ||
| 122 | irq3, | ||
| 123 | mut sm0, | ||
| 124 | mut sm1, | ||
| 125 | mut sm2, | ||
| 126 | .. | ||
| 127 | } = Pio::new(pio, Irqs); | ||
| 128 | |||
| 129 | setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0); | ||
| 130 | setup_pio_task_sm1(&mut common, &mut sm1); | ||
| 131 | setup_pio_task_sm2(&mut common, &mut sm2); | ||
| 132 | spawner.spawn(pio_task_sm0(sm0)).unwrap(); | ||
| 133 | spawner.spawn(pio_task_sm1(sm1)).unwrap(); | ||
| 134 | spawner.spawn(pio_task_sm2(irq3, sm2)).unwrap(); | ||
| 135 | } | ||
diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs new file mode 100644 index 000000000..9ab72e1f3 --- /dev/null +++ b/examples/rp/src/bin/pio_dma.rs | |||
| @@ -0,0 +1,86 @@ | |||
| 1 | //! This example shows powerful PIO module in the RP2040 chip. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | use defmt::info; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_futures::join::join; | ||
| 9 | use embassy_rp::peripherals::PIO0; | ||
| 10 | use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; | ||
| 11 | use embassy_rp::relocate::RelocatedProgram; | ||
| 12 | use embassy_rp::{bind_interrupts, Peripheral}; | ||
| 13 | use fixed::traits::ToFixed; | ||
| 14 | use fixed_macro::types::U56F8; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 19 | }); | ||
| 20 | |||
| 21 | fn swap_nibbles(v: u32) -> u32 { | ||
| 22 | let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4; | ||
| 23 | let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8; | ||
| 24 | (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16 | ||
| 25 | } | ||
| 26 | |||
| 27 | #[embassy_executor::main] | ||
| 28 | async fn main(_spawner: Spawner) { | ||
| 29 | let p = embassy_rp::init(Default::default()); | ||
| 30 | let pio = p.PIO0; | ||
| 31 | let Pio { | ||
| 32 | mut common, | ||
| 33 | sm0: mut sm, | ||
| 34 | .. | ||
| 35 | } = Pio::new(pio, Irqs); | ||
| 36 | |||
| 37 | let prg = pio_proc::pio_asm!( | ||
| 38 | ".origin 0", | ||
| 39 | "set pindirs,1", | ||
| 40 | ".wrap_target", | ||
| 41 | "set y,7", | ||
| 42 | "loop:", | ||
| 43 | "out x,4", | ||
| 44 | "in x,4", | ||
| 45 | "jmp y--, loop", | ||
| 46 | ".wrap", | ||
| 47 | ); | ||
| 48 | |||
| 49 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 50 | let mut cfg = Config::default(); | ||
| 51 | cfg.use_program(&common.load_program(&relocated), &[]); | ||
| 52 | cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed(); | ||
| 53 | cfg.shift_in = ShiftConfig { | ||
| 54 | auto_fill: true, | ||
| 55 | threshold: 32, | ||
| 56 | direction: ShiftDirection::Left, | ||
| 57 | }; | ||
| 58 | cfg.shift_out = ShiftConfig { | ||
| 59 | auto_fill: true, | ||
| 60 | threshold: 32, | ||
| 61 | direction: ShiftDirection::Right, | ||
| 62 | }; | ||
| 63 | |||
| 64 | sm.set_config(&cfg); | ||
| 65 | sm.set_enable(true); | ||
| 66 | |||
| 67 | let mut dma_out_ref = p.DMA_CH0.into_ref(); | ||
| 68 | let mut dma_in_ref = p.DMA_CH1.into_ref(); | ||
| 69 | let mut dout = [0x12345678u32; 29]; | ||
| 70 | for i in 1..dout.len() { | ||
| 71 | dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7; | ||
| 72 | } | ||
| 73 | let mut din = [0u32; 29]; | ||
| 74 | loop { | ||
| 75 | let (rx, tx) = sm.rx_tx(); | ||
| 76 | join( | ||
| 77 | tx.dma_push(dma_out_ref.reborrow(), &dout), | ||
| 78 | rx.dma_pull(dma_in_ref.reborrow(), &mut din), | ||
| 79 | ) | ||
| 80 | .await; | ||
| 81 | for i in 0..din.len() { | ||
| 82 | assert_eq!(din[i], swap_nibbles(dout[i])); | ||
| 83 | } | ||
| 84 | info!("Swapped {} words", dout.len()); | ||
| 85 | } | ||
| 86 | } | ||
diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs new file mode 100644 index 000000000..8aedd24b6 --- /dev/null +++ b/examples/rp/src/bin/pio_hd44780.rs | |||
| @@ -0,0 +1,244 @@ | |||
| 1 | //! This example shows powerful PIO module in the RP2040 chip to communicate with a HD44780 display. | ||
| 2 | //! See (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use core::fmt::Write; | ||
| 9 | |||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_rp::dma::{AnyChannel, Channel}; | ||
| 12 | use embassy_rp::peripherals::PIO0; | ||
| 13 | use embassy_rp::pio::{ | ||
| 14 | Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, | ||
| 15 | }; | ||
| 16 | use embassy_rp::pwm::{self, Pwm}; | ||
| 17 | use embassy_rp::relocate::RelocatedProgram; | ||
| 18 | use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; | ||
| 19 | use embassy_time::{Duration, Instant, Timer}; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | bind_interrupts!(pub struct Irqs { | ||
| 23 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 24 | }); | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(_spawner: Spawner) { | ||
| 28 | // this test assumes a 2x16 HD44780 display attached as follow: | ||
| 29 | // rs = PIN0 | ||
| 30 | // rw = PIN1 | ||
| 31 | // e = PIN2 | ||
| 32 | // db4 = PIN3 | ||
| 33 | // db5 = PIN4 | ||
| 34 | // db6 = PIN5 | ||
| 35 | // db7 = PIN6 | ||
| 36 | // additionally a pwm signal for a bias voltage charge pump is provided on pin 15, | ||
| 37 | // allowing direct connection of the display to the RP2040 without level shifters. | ||
| 38 | let p = embassy_rp::init(Default::default()); | ||
| 39 | |||
| 40 | let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, { | ||
| 41 | let mut c = pwm::Config::default(); | ||
| 42 | c.divider = 125.into(); | ||
| 43 | c.top = 100; | ||
| 44 | c.compare_b = 50; | ||
| 45 | c | ||
| 46 | }); | ||
| 47 | |||
| 48 | let mut hd = HD44780::new( | ||
| 49 | p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, | ||
| 50 | ) | ||
| 51 | .await; | ||
| 52 | |||
| 53 | loop { | ||
| 54 | struct Buf<const N: usize>([u8; N], usize); | ||
| 55 | impl<const N: usize> Write for Buf<N> { | ||
| 56 | fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { | ||
| 57 | for b in s.as_bytes() { | ||
| 58 | if self.1 >= N { | ||
| 59 | return Err(core::fmt::Error); | ||
| 60 | } | ||
| 61 | self.0[self.1] = *b; | ||
| 62 | self.1 += 1; | ||
| 63 | } | ||
| 64 | Ok(()) | ||
| 65 | } | ||
| 66 | } | ||
| 67 | let mut buf = Buf([0; 16], 0); | ||
| 68 | write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap(); | ||
| 69 | hd.add_line(&buf.0[0..buf.1]).await; | ||
| 70 | Timer::after(Duration::from_secs(1)).await; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | pub struct HD44780<'l> { | ||
| 75 | dma: PeripheralRef<'l, AnyChannel>, | ||
| 76 | sm: StateMachine<'l, PIO0, 0>, | ||
| 77 | |||
| 78 | buf: [u8; 40], | ||
| 79 | } | ||
| 80 | |||
| 81 | impl<'l> HD44780<'l> { | ||
| 82 | pub async fn new( | ||
| 83 | pio: impl Peripheral<P = PIO0> + 'l, | ||
| 84 | irq: Irqs, | ||
| 85 | dma: impl Peripheral<P = impl Channel> + 'l, | ||
| 86 | rs: impl PioPin, | ||
| 87 | rw: impl PioPin, | ||
| 88 | e: impl PioPin, | ||
| 89 | db4: impl PioPin, | ||
| 90 | db5: impl PioPin, | ||
| 91 | db6: impl PioPin, | ||
| 92 | db7: impl PioPin, | ||
| 93 | ) -> HD44780<'l> { | ||
| 94 | into_ref!(dma); | ||
| 95 | |||
| 96 | let Pio { | ||
| 97 | mut common, | ||
| 98 | mut irq0, | ||
| 99 | mut sm0, | ||
| 100 | .. | ||
| 101 | } = Pio::new(pio, irq); | ||
| 102 | |||
| 103 | // takes command words (<wait:24> <command:4> <0:4>) | ||
| 104 | let prg = pio_proc::pio_asm!( | ||
| 105 | r#" | ||
| 106 | .side_set 1 opt | ||
| 107 | .origin 20 | ||
| 108 | |||
| 109 | loop: | ||
| 110 | out x, 24 | ||
| 111 | delay: | ||
| 112 | jmp x--, delay | ||
| 113 | out pins, 4 side 1 | ||
| 114 | out null, 4 side 0 | ||
| 115 | jmp !osre, loop | ||
| 116 | irq 0 | ||
| 117 | "#, | ||
| 118 | ); | ||
| 119 | |||
| 120 | let rs = common.make_pio_pin(rs); | ||
| 121 | let rw = common.make_pio_pin(rw); | ||
| 122 | let e = common.make_pio_pin(e); | ||
| 123 | let db4 = common.make_pio_pin(db4); | ||
| 124 | let db5 = common.make_pio_pin(db5); | ||
| 125 | let db6 = common.make_pio_pin(db6); | ||
| 126 | let db7 = common.make_pio_pin(db7); | ||
| 127 | |||
| 128 | sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); | ||
| 129 | |||
| 130 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 131 | let mut cfg = Config::default(); | ||
| 132 | cfg.use_program(&common.load_program(&relocated), &[&e]); | ||
| 133 | cfg.clock_divider = 125u8.into(); | ||
| 134 | cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); | ||
| 135 | cfg.shift_out = ShiftConfig { | ||
| 136 | auto_fill: true, | ||
| 137 | direction: ShiftDirection::Left, | ||
| 138 | threshold: 32, | ||
| 139 | }; | ||
| 140 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 141 | sm0.set_config(&cfg); | ||
| 142 | |||
| 143 | sm0.set_enable(true); | ||
| 144 | // init to 8 bit thrice | ||
| 145 | sm0.tx().push((50000 << 8) | 0x30); | ||
| 146 | sm0.tx().push((5000 << 8) | 0x30); | ||
| 147 | sm0.tx().push((200 << 8) | 0x30); | ||
| 148 | // init 4 bit | ||
| 149 | sm0.tx().push((200 << 8) | 0x20); | ||
| 150 | // set font and lines | ||
| 151 | sm0.tx().push((50 << 8) | 0x20); | ||
| 152 | sm0.tx().push(0b1100_0000); | ||
| 153 | |||
| 154 | irq0.wait().await; | ||
| 155 | sm0.set_enable(false); | ||
| 156 | |||
| 157 | // takes command sequences (<rs:1> <count:7>, data...) | ||
| 158 | // many side sets are only there to free up a delay bit! | ||
| 159 | let prg = pio_proc::pio_asm!( | ||
| 160 | r#" | ||
| 161 | .origin 27 | ||
| 162 | .side_set 1 | ||
| 163 | |||
| 164 | .wrap_target | ||
| 165 | pull side 0 | ||
| 166 | out x 1 side 0 ; !rs | ||
| 167 | out y 7 side 0 ; #data - 1 | ||
| 168 | |||
| 169 | ; rs/rw to e: >= 60ns | ||
| 170 | ; e high time: >= 500ns | ||
| 171 | ; e low time: >= 500ns | ||
| 172 | ; read data valid after e falling: ~5ns | ||
| 173 | ; write data hold after e falling: ~10ns | ||
| 174 | |||
| 175 | loop: | ||
| 176 | pull side 0 | ||
| 177 | jmp !x data side 0 | ||
| 178 | command: | ||
| 179 | set pins 0b00 side 0 | ||
| 180 | jmp shift side 0 | ||
| 181 | data: | ||
| 182 | set pins 0b01 side 0 | ||
| 183 | shift: | ||
| 184 | out pins 4 side 1 [9] | ||
| 185 | nop side 0 [9] | ||
| 186 | out pins 4 side 1 [9] | ||
| 187 | mov osr null side 0 [7] | ||
| 188 | out pindirs 4 side 0 | ||
| 189 | set pins 0b10 side 0 | ||
| 190 | busy: | ||
| 191 | nop side 1 [9] | ||
| 192 | jmp pin more side 0 [9] | ||
| 193 | mov osr ~osr side 1 [9] | ||
| 194 | nop side 0 [4] | ||
| 195 | out pindirs 4 side 0 | ||
| 196 | jmp y-- loop side 0 | ||
| 197 | .wrap | ||
| 198 | more: | ||
| 199 | nop side 1 [9] | ||
| 200 | jmp busy side 0 [9] | ||
| 201 | "# | ||
| 202 | ); | ||
| 203 | |||
| 204 | let relocated = RelocatedProgram::new(&prg.program); | ||
| 205 | let mut cfg = Config::default(); | ||
| 206 | cfg.use_program(&common.load_program(&relocated), &[&e]); | ||
| 207 | cfg.clock_divider = 8u8.into(); // ~64ns/insn | ||
| 208 | cfg.set_jmp_pin(&db7); | ||
| 209 | cfg.set_set_pins(&[&rs, &rw]); | ||
| 210 | cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); | ||
| 211 | cfg.shift_out.direction = ShiftDirection::Left; | ||
| 212 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 213 | sm0.set_config(&cfg); | ||
| 214 | |||
| 215 | sm0.set_enable(true); | ||
| 216 | |||
| 217 | // display on and cursor on and blinking, reset display | ||
| 218 | sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await; | ||
| 219 | |||
| 220 | Self { | ||
| 221 | dma: dma.map_into(), | ||
| 222 | sm: sm0, | ||
| 223 | buf: [0x20; 40], | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | pub async fn add_line(&mut self, s: &[u8]) { | ||
| 228 | // move cursor to 0:0, prepare 16 characters | ||
| 229 | self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]); | ||
| 230 | // move line 2 up | ||
| 231 | self.buf.copy_within(22..38, 3); | ||
| 232 | // move cursor to 1:0, prepare 16 characters | ||
| 233 | self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]); | ||
| 234 | // file line 2 with spaces | ||
| 235 | self.buf[22..38].fill(0x20); | ||
| 236 | // copy input line | ||
| 237 | let len = s.len().min(16); | ||
| 238 | self.buf[22..22 + len].copy_from_slice(&s[0..len]); | ||
| 239 | // set cursor to 1:15 | ||
| 240 | self.buf[38..].copy_from_slice(&[0x80, 0xcf]); | ||
| 241 | |||
| 242 | self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await; | ||
| 243 | } | ||
| 244 | } | ||
diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs new file mode 100644 index 000000000..3de2bd48d --- /dev/null +++ b/examples/rp/src/bin/pio_ws2812.rs | |||
| @@ -0,0 +1,160 @@ | |||
| 1 | //! This example shows powerful PIO module in the RP2040 chip to communicate with WS2812 LED modules. | ||
| 2 | //! See (https://www.sparkfun.com/categories/tags/ws2812) | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::dma::{AnyChannel, Channel}; | ||
| 11 | use embassy_rp::peripherals::PIO0; | ||
| 12 | use embassy_rp::pio::{ | ||
| 13 | Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, | ||
| 14 | }; | ||
| 15 | use embassy_rp::relocate::RelocatedProgram; | ||
| 16 | use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; | ||
| 17 | use embassy_time::{Duration, Timer}; | ||
| 18 | use fixed::types::U24F8; | ||
| 19 | use fixed_macro::fixed; | ||
| 20 | use smart_leds::RGB8; | ||
| 21 | use {defmt_rtt as _, panic_probe as _}; | ||
| 22 | |||
| 23 | bind_interrupts!(struct Irqs { | ||
| 24 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 25 | }); | ||
| 26 | |||
| 27 | pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> { | ||
| 28 | dma: PeripheralRef<'d, AnyChannel>, | ||
| 29 | sm: StateMachine<'d, P, S>, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> { | ||
| 33 | pub fn new( | ||
| 34 | pio: &mut Common<'d, P>, | ||
| 35 | mut sm: StateMachine<'d, P, S>, | ||
| 36 | dma: impl Peripheral<P = impl Channel> + 'd, | ||
| 37 | pin: impl PioPin, | ||
| 38 | ) -> Self { | ||
| 39 | into_ref!(dma); | ||
| 40 | |||
| 41 | // Setup sm0 | ||
| 42 | |||
| 43 | // prepare the PIO program | ||
| 44 | let side_set = pio::SideSet::new(false, 1, false); | ||
| 45 | let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set); | ||
| 46 | |||
| 47 | const T1: u8 = 2; // start bit | ||
| 48 | const T2: u8 = 5; // data bit | ||
| 49 | const T3: u8 = 3; // stop bit | ||
| 50 | const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; | ||
| 51 | |||
| 52 | let mut wrap_target = a.label(); | ||
| 53 | let mut wrap_source = a.label(); | ||
| 54 | let mut do_zero = a.label(); | ||
| 55 | a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0); | ||
| 56 | a.bind(&mut wrap_target); | ||
| 57 | // Do stop bit | ||
| 58 | a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0); | ||
| 59 | // Do start bit | ||
| 60 | a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1); | ||
| 61 | // Do data bit = 1 | ||
| 62 | a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1); | ||
| 63 | a.bind(&mut do_zero); | ||
| 64 | // Do data bit = 0 | ||
| 65 | a.nop_with_delay_and_side_set(T2 - 1, 0); | ||
| 66 | a.bind(&mut wrap_source); | ||
| 67 | |||
| 68 | let prg = a.assemble_with_wrap(wrap_source, wrap_target); | ||
| 69 | let mut cfg = Config::default(); | ||
| 70 | |||
| 71 | // Pin config | ||
| 72 | let out_pin = pio.make_pio_pin(pin); | ||
| 73 | cfg.set_out_pins(&[&out_pin]); | ||
| 74 | cfg.set_set_pins(&[&out_pin]); | ||
| 75 | |||
| 76 | let relocated = RelocatedProgram::new(&prg); | ||
| 77 | cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); | ||
| 78 | |||
| 79 | // Clock config, measured in kHz to avoid overflows | ||
| 80 | // TODO CLOCK_FREQ should come from embassy_rp | ||
| 81 | let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000); | ||
| 82 | let ws2812_freq = fixed!(800: U24F8); | ||
| 83 | let bit_freq = ws2812_freq * CYCLES_PER_BIT; | ||
| 84 | cfg.clock_divider = clock_freq / bit_freq; | ||
| 85 | |||
| 86 | // FIFO config | ||
| 87 | cfg.fifo_join = FifoJoin::TxOnly; | ||
| 88 | cfg.shift_out = ShiftConfig { | ||
| 89 | auto_fill: true, | ||
| 90 | threshold: 24, | ||
| 91 | direction: ShiftDirection::Left, | ||
| 92 | }; | ||
| 93 | |||
| 94 | sm.set_config(&cfg); | ||
| 95 | sm.set_enable(true); | ||
| 96 | |||
| 97 | Self { | ||
| 98 | dma: dma.map_into(), | ||
| 99 | sm, | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | pub async fn write(&mut self, colors: &[RGB8; N]) { | ||
| 104 | // Precompute the word bytes from the colors | ||
| 105 | let mut words = [0u32; N]; | ||
| 106 | for i in 0..N { | ||
| 107 | let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8); | ||
| 108 | words[i] = word; | ||
| 109 | } | ||
| 110 | |||
| 111 | // DMA transfer | ||
| 112 | self.sm.tx().dma_push(self.dma.reborrow(), &words).await; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | /// Input a value 0 to 255 to get a color value | ||
| 117 | /// The colours are a transition r - g - b - back to r. | ||
| 118 | fn wheel(mut wheel_pos: u8) -> RGB8 { | ||
| 119 | wheel_pos = 255 - wheel_pos; | ||
| 120 | if wheel_pos < 85 { | ||
| 121 | return (255 - wheel_pos * 3, 0, wheel_pos * 3).into(); | ||
| 122 | } | ||
| 123 | if wheel_pos < 170 { | ||
| 124 | wheel_pos -= 85; | ||
| 125 | return (0, wheel_pos * 3, 255 - wheel_pos * 3).into(); | ||
| 126 | } | ||
| 127 | wheel_pos -= 170; | ||
| 128 | (wheel_pos * 3, 255 - wheel_pos * 3, 0).into() | ||
| 129 | } | ||
| 130 | |||
| 131 | #[embassy_executor::main] | ||
| 132 | async fn main(_spawner: Spawner) { | ||
| 133 | info!("Start"); | ||
| 134 | let p = embassy_rp::init(Default::default()); | ||
| 135 | |||
| 136 | let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); | ||
| 137 | |||
| 138 | // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit | ||
| 139 | // feather boards for the 2040 both have one built in. | ||
| 140 | const NUM_LEDS: usize = 1; | ||
| 141 | let mut data = [RGB8::default(); NUM_LEDS]; | ||
| 142 | |||
| 143 | // For the thing plus, use pin 8 | ||
| 144 | // For the feather, use pin 16 | ||
| 145 | let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); | ||
| 146 | |||
| 147 | // Loop forever making RGB values and pushing them out to the WS2812. | ||
| 148 | loop { | ||
| 149 | for j in 0..(256 * 5) { | ||
| 150 | debug!("New Colors:"); | ||
| 151 | for i in 0..NUM_LEDS { | ||
| 152 | data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8); | ||
| 153 | debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b); | ||
| 154 | } | ||
| 155 | ws2812.write(&data).await; | ||
| 156 | |||
| 157 | Timer::after(Duration::from_micros(5)).await; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs new file mode 100644 index 000000000..9d919287c --- /dev/null +++ b/examples/rp/src/bin/pwm.rs | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | //! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_rp::pwm::{Config, Pwm}; | ||
| 12 | use embassy_time::{Duration, Timer}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = embassy_rp::init(Default::default()); | ||
| 18 | |||
| 19 | let mut c: Config = Default::default(); | ||
| 20 | c.top = 0x8000; | ||
| 21 | c.compare_b = 8; | ||
| 22 | let mut pwm = Pwm::new_output_b(p.PWM_CH4, p.PIN_25, c.clone()); | ||
| 23 | |||
| 24 | loop { | ||
| 25 | info!("current LED duty cycle: {}/32768", c.compare_b); | ||
| 26 | Timer::after(Duration::from_secs(1)).await; | ||
| 27 | c.compare_b = c.compare_b.rotate_left(4); | ||
| 28 | pwm.set_config(&c); | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs new file mode 100644 index 000000000..15aa8243f --- /dev/null +++ b/examples/rp/src/bin/rtc.rs | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | //! This example shows how to use RTC (Real Time Clock) in the RP2040 chip. | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc}; | ||
| 10 | use embassy_time::{Duration, Timer}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) { | ||
| 15 | let p = embassy_rp::init(Default::default()); | ||
| 16 | info!("Wait for 20s"); | ||
| 17 | |||
| 18 | let mut rtc = Rtc::new(p.RTC); | ||
| 19 | |||
| 20 | if !rtc.is_running() { | ||
| 21 | info!("Start RTC"); | ||
| 22 | let now = DateTime { | ||
| 23 | year: 2000, | ||
| 24 | month: 1, | ||
| 25 | day: 1, | ||
| 26 | day_of_week: DayOfWeek::Saturday, | ||
| 27 | hour: 0, | ||
| 28 | minute: 0, | ||
| 29 | second: 0, | ||
| 30 | }; | ||
| 31 | rtc.set_datetime(now).unwrap(); | ||
| 32 | } | ||
| 33 | |||
| 34 | Timer::after(Duration::from_millis(20000)).await; | ||
| 35 | |||
| 36 | if let Ok(dt) = rtc.now() { | ||
| 37 | info!( | ||
| 38 | "Now: {}-{:02}-{:02} {}:{:02}:{:02}", | ||
| 39 | dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, | ||
| 40 | ); | ||
| 41 | } | ||
| 42 | |||
| 43 | info!("Reboot."); | ||
| 44 | Timer::after(Duration::from_millis(200)).await; | ||
| 45 | cortex_m::peripheral::SCB::sys_reset(); | ||
| 46 | } | ||
diff --git a/examples/rp/src/bin/spi.rs b/examples/rp/src/bin/spi.rs index 88003ee17..602348f7a 100644 --- a/examples/rp/src/bin/spi.rs +++ b/examples/rp/src/bin/spi.rs | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | //! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! Example for resistive touch sensor in Waveshare Pico-ResTouch | ||
| 4 | |||
| 1 | #![no_std] | 5 | #![no_std] |
| 2 | #![no_main] | 6 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 7 | #![feature(type_alias_impl_trait)] |
| @@ -24,7 +28,7 @@ async fn main(_spawner: Spawner) { | |||
| 24 | // create SPI | 28 | // create SPI |
| 25 | let mut config = spi::Config::default(); | 29 | let mut config = spi::Config::default(); |
| 26 | config.frequency = 2_000_000; | 30 | config.frequency = 2_000_000; |
| 27 | let mut spi = Spi::new(p.SPI1, clk, mosi, miso, config); | 31 | let mut spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, config); |
| 28 | 32 | ||
| 29 | // Configure CS | 33 | // Configure CS |
| 30 | let mut cs = Output::new(touch_cs, Level::Low); | 34 | let mut cs = Output::new(touch_cs, Level::Low); |
diff --git a/examples/rp/src/bin/spi_async.rs b/examples/rp/src/bin/spi_async.rs new file mode 100644 index 000000000..328074e8b --- /dev/null +++ b/examples/rp/src/bin/spi_async.rs | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | //! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. | ||
| 2 | //! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::spi::{Config, Spi}; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | #[embassy_executor::main] | ||
| 15 | async fn main(_spawner: Spawner) { | ||
| 16 | let p = embassy_rp::init(Default::default()); | ||
| 17 | info!("Hello World!"); | ||
| 18 | |||
| 19 | let miso = p.PIN_12; | ||
| 20 | let mosi = p.PIN_11; | ||
| 21 | let clk = p.PIN_10; | ||
| 22 | |||
| 23 | let mut spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); | ||
| 24 | |||
| 25 | loop { | ||
| 26 | let tx_buf = [1_u8, 2, 3, 4, 5, 6]; | ||
| 27 | let mut rx_buf = [0_u8; 6]; | ||
| 28 | spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); | ||
| 29 | info!("{:?}", rx_buf); | ||
| 30 | Timer::after(Duration::from_secs(1)).await; | ||
| 31 | } | ||
| 32 | } | ||
diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index f0e54d87f..26c258e1c 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs | |||
| @@ -1,3 +1,8 @@ | |||
| 1 | //! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch | ||
| 4 | //! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8) | ||
| 5 | |||
| 1 | #![no_std] | 6 | #![no_std] |
| 2 | #![no_main] | 7 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 8 | #![feature(type_alias_impl_trait)] |
| @@ -5,10 +10,13 @@ | |||
| 5 | use core::cell::RefCell; | 10 | use core::cell::RefCell; |
| 6 | 11 | ||
| 7 | use defmt::*; | 12 | use defmt::*; |
| 13 | use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; | ||
| 8 | use embassy_executor::Spawner; | 14 | use embassy_executor::Spawner; |
| 9 | use embassy_rp::gpio::{Level, Output}; | 15 | use embassy_rp::gpio::{Level, Output}; |
| 10 | use embassy_rp::spi; | 16 | use embassy_rp::spi; |
| 11 | use embassy_rp::spi::Spi; | 17 | use embassy_rp::spi::{Blocking, Spi}; |
| 18 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 19 | use embassy_sync::blocking_mutex::Mutex; | ||
| 12 | use embassy_time::Delay; | 20 | use embassy_time::Delay; |
| 13 | use embedded_graphics::image::{Image, ImageRawLE}; | 21 | use embedded_graphics::image::{Image, ImageRawLE}; |
| 14 | use embedded_graphics::mono_font::ascii::FONT_10X20; | 22 | use embedded_graphics::mono_font::ascii::FONT_10X20; |
| @@ -21,10 +29,9 @@ use st7789::{Orientation, ST7789}; | |||
| 21 | use {defmt_rtt as _, panic_probe as _}; | 29 | use {defmt_rtt as _, panic_probe as _}; |
| 22 | 30 | ||
| 23 | use crate::my_display_interface::SPIDeviceInterface; | 31 | use crate::my_display_interface::SPIDeviceInterface; |
| 24 | use crate::shared_spi::SpiDeviceWithCs; | ||
| 25 | use crate::touch::Touch; | 32 | use crate::touch::Touch; |
| 26 | 33 | ||
| 27 | //const DISPLAY_FREQ: u32 = 64_000_000; | 34 | const DISPLAY_FREQ: u32 = 64_000_000; |
| 28 | const TOUCH_FREQ: u32 = 200_000; | 35 | const TOUCH_FREQ: u32 = 200_000; |
| 29 | 36 | ||
| 30 | #[embassy_executor::main] | 37 | #[embassy_executor::main] |
| @@ -43,15 +50,20 @@ async fn main(_spawner: Spawner) { | |||
| 43 | //let touch_irq = p.PIN_17; | 50 | //let touch_irq = p.PIN_17; |
| 44 | 51 | ||
| 45 | // create SPI | 52 | // create SPI |
| 46 | let mut config = spi::Config::default(); | 53 | let mut display_config = spi::Config::default(); |
| 47 | config.frequency = TOUCH_FREQ; // use the lowest freq | 54 | display_config.frequency = DISPLAY_FREQ; |
| 48 | config.phase = spi::Phase::CaptureOnSecondTransition; | 55 | display_config.phase = spi::Phase::CaptureOnSecondTransition; |
| 49 | config.polarity = spi::Polarity::IdleHigh; | 56 | display_config.polarity = spi::Polarity::IdleHigh; |
| 57 | let mut touch_config = spi::Config::default(); | ||
| 58 | touch_config.frequency = TOUCH_FREQ; | ||
| 59 | touch_config.phase = spi::Phase::CaptureOnSecondTransition; | ||
| 60 | touch_config.polarity = spi::Polarity::IdleHigh; | ||
| 50 | 61 | ||
| 51 | let spi_bus = RefCell::new(Spi::new(p.SPI1, clk, mosi, miso, config)); | 62 | let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); |
| 63 | let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi)); | ||
| 52 | 64 | ||
| 53 | let display_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(display_cs, Level::High)); | 65 | let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); |
| 54 | let touch_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(touch_cs, Level::High)); | 66 | let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config); |
| 55 | 67 | ||
| 56 | let mut touch = Touch::new(touch_spi); | 68 | let mut touch = Touch::new(touch_spi); |
| 57 | 69 | ||
| @@ -103,85 +115,9 @@ async fn main(_spawner: Spawner) { | |||
| 103 | } | 115 | } |
| 104 | } | 116 | } |
| 105 | 117 | ||
| 106 | mod shared_spi { | ||
| 107 | use core::cell::RefCell; | ||
| 108 | use core::fmt::Debug; | ||
| 109 | |||
| 110 | use embedded_hal_1::digital::blocking::OutputPin; | ||
| 111 | use embedded_hal_1::spi; | ||
| 112 | use embedded_hal_1::spi::blocking::SpiDevice; | ||
| 113 | |||
| 114 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 115 | pub enum SpiDeviceWithCsError<BUS, CS> { | ||
| 116 | #[allow(unused)] // will probably use in the future when adding a flush() to SpiBus | ||
| 117 | Spi(BUS), | ||
| 118 | Cs(CS), | ||
| 119 | } | ||
| 120 | |||
| 121 | impl<BUS, CS> spi::Error for SpiDeviceWithCsError<BUS, CS> | ||
| 122 | where | ||
| 123 | BUS: spi::Error + Debug, | ||
| 124 | CS: Debug, | ||
| 125 | { | ||
| 126 | fn kind(&self) -> spi::ErrorKind { | ||
| 127 | match self { | ||
| 128 | Self::Spi(e) => e.kind(), | ||
| 129 | Self::Cs(_) => spi::ErrorKind::Other, | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | pub struct SpiDeviceWithCs<'a, BUS, CS> { | ||
| 135 | bus: &'a RefCell<BUS>, | ||
| 136 | cs: CS, | ||
| 137 | } | ||
| 138 | |||
| 139 | impl<'a, BUS, CS> SpiDeviceWithCs<'a, BUS, CS> { | ||
| 140 | pub fn new(bus: &'a RefCell<BUS>, cs: CS) -> Self { | ||
| 141 | Self { bus, cs } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | impl<'a, BUS, CS> spi::ErrorType for SpiDeviceWithCs<'a, BUS, CS> | ||
| 146 | where | ||
| 147 | BUS: spi::ErrorType, | ||
| 148 | CS: OutputPin, | ||
| 149 | { | ||
| 150 | type Error = SpiDeviceWithCsError<BUS::Error, CS::Error>; | ||
| 151 | } | ||
| 152 | |||
| 153 | impl<'a, BUS, CS> SpiDevice for SpiDeviceWithCs<'a, BUS, CS> | ||
| 154 | where | ||
| 155 | BUS: spi::blocking::SpiBusFlush, | ||
| 156 | CS: OutputPin, | ||
| 157 | { | ||
| 158 | type Bus = BUS; | ||
| 159 | |||
| 160 | fn transaction<R>( | ||
| 161 | &mut self, | ||
| 162 | f: impl FnOnce(&mut Self::Bus) -> Result<R, BUS::Error>, | ||
| 163 | ) -> Result<R, Self::Error> { | ||
| 164 | let mut bus = self.bus.borrow_mut(); | ||
| 165 | self.cs.set_low().map_err(SpiDeviceWithCsError::Cs)?; | ||
| 166 | |||
| 167 | let f_res = f(&mut bus); | ||
| 168 | |||
| 169 | // On failure, it's important to still flush and deassert CS. | ||
| 170 | let flush_res = bus.flush(); | ||
| 171 | let cs_res = self.cs.set_high(); | ||
| 172 | |||
| 173 | let f_res = f_res.map_err(SpiDeviceWithCsError::Spi)?; | ||
| 174 | flush_res.map_err(SpiDeviceWithCsError::Spi)?; | ||
| 175 | cs_res.map_err(SpiDeviceWithCsError::Cs)?; | ||
| 176 | |||
| 177 | Ok(f_res) | ||
| 178 | } | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | /// Driver for the XPT2046 resistive touchscreen sensor | 118 | /// Driver for the XPT2046 resistive touchscreen sensor |
| 183 | mod touch { | 119 | mod touch { |
| 184 | use embedded_hal_1::spi::blocking::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; | 120 | use embedded_hal_1::spi::{Operation, SpiDevice}; |
| 185 | 121 | ||
| 186 | struct Calibration { | 122 | struct Calibration { |
| 187 | x1: i32, | 123 | x1: i32, |
| @@ -208,7 +144,6 @@ mod touch { | |||
| 208 | impl<SPI> Touch<SPI> | 144 | impl<SPI> Touch<SPI> |
| 209 | where | 145 | where |
| 210 | SPI: SpiDevice, | 146 | SPI: SpiDevice, |
| 211 | SPI::Bus: SpiBus, | ||
| 212 | { | 147 | { |
| 213 | pub fn new(spi: SPI) -> Self { | 148 | pub fn new(spi: SPI) -> Self { |
| 214 | Self { spi } | 149 | Self { spi } |
| @@ -218,13 +153,12 @@ mod touch { | |||
| 218 | let mut x = [0; 2]; | 153 | let mut x = [0; 2]; |
| 219 | let mut y = [0; 2]; | 154 | let mut y = [0; 2]; |
| 220 | self.spi | 155 | self.spi |
| 221 | .transaction(|bus| { | 156 | .transaction(&mut [ |
| 222 | bus.write(&[0x90])?; | 157 | Operation::Write(&[0x90]), |
| 223 | bus.read(&mut x)?; | 158 | Operation::Read(&mut x), |
| 224 | bus.write(&[0xd0])?; | 159 | Operation::Write(&[0xd0]), |
| 225 | bus.read(&mut y)?; | 160 | Operation::Read(&mut y), |
| 226 | Ok(()) | 161 | ]) |
| 227 | }) | ||
| 228 | .unwrap(); | 162 | .unwrap(); |
| 229 | 163 | ||
| 230 | let x = (u16::from_be_bytes(x) >> 3) as i32; | 164 | let x = (u16::from_be_bytes(x) >> 3) as i32; |
| @@ -245,8 +179,8 @@ mod touch { | |||
| 245 | 179 | ||
| 246 | mod my_display_interface { | 180 | mod my_display_interface { |
| 247 | use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; | 181 | use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; |
| 248 | use embedded_hal_1::digital::blocking::OutputPin; | 182 | use embedded_hal_1::digital::OutputPin; |
| 249 | use embedded_hal_1::spi::blocking::{SpiBusWrite, SpiDevice}; | 183 | use embedded_hal_1::spi::SpiDevice; |
| 250 | 184 | ||
| 251 | /// SPI display interface. | 185 | /// SPI display interface. |
| 252 | /// | 186 | /// |
| @@ -259,7 +193,6 @@ mod my_display_interface { | |||
| 259 | impl<SPI, DC> SPIDeviceInterface<SPI, DC> | 193 | impl<SPI, DC> SPIDeviceInterface<SPI, DC> |
| 260 | where | 194 | where |
| 261 | SPI: SpiDevice, | 195 | SPI: SpiDevice, |
| 262 | SPI::Bus: SpiBusWrite, | ||
| 263 | DC: OutputPin, | 196 | DC: OutputPin, |
| 264 | { | 197 | { |
| 265 | /// Create new SPI interface for communciation with a display driver | 198 | /// Create new SPI interface for communciation with a display driver |
| @@ -271,41 +204,26 @@ mod my_display_interface { | |||
| 271 | impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC> | 204 | impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC> |
| 272 | where | 205 | where |
| 273 | SPI: SpiDevice, | 206 | SPI: SpiDevice, |
| 274 | SPI::Bus: SpiBusWrite, | ||
| 275 | DC: OutputPin, | 207 | DC: OutputPin, |
| 276 | { | 208 | { |
| 277 | fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { | 209 | fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { |
| 278 | let r = self.spi.transaction(|bus| { | 210 | // 1 = data, 0 = command |
| 279 | // 1 = data, 0 = command | 211 | self.dc.set_low().map_err(|_| DisplayError::DCError)?; |
| 280 | if let Err(_) = self.dc.set_low() { | ||
| 281 | return Ok(Err(DisplayError::DCError)); | ||
| 282 | } | ||
| 283 | |||
| 284 | // Send words over SPI | ||
| 285 | send_u8(bus, cmds)?; | ||
| 286 | 212 | ||
| 287 | Ok(Ok(())) | 213 | send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?; |
| 288 | }); | 214 | Ok(()) |
| 289 | r.map_err(|_| DisplayError::BusWriteError)? | ||
| 290 | } | 215 | } |
| 291 | 216 | ||
| 292 | fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { | 217 | fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { |
| 293 | let r = self.spi.transaction(|bus| { | 218 | // 1 = data, 0 = command |
| 294 | // 1 = data, 0 = command | 219 | self.dc.set_high().map_err(|_| DisplayError::DCError)?; |
| 295 | if let Err(_) = self.dc.set_high() { | ||
| 296 | return Ok(Err(DisplayError::DCError)); | ||
| 297 | } | ||
| 298 | |||
| 299 | // Send words over SPI | ||
| 300 | send_u8(bus, buf)?; | ||
| 301 | 220 | ||
| 302 | Ok(Ok(())) | 221 | send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?; |
| 303 | }); | 222 | Ok(()) |
| 304 | r.map_err(|_| DisplayError::BusWriteError)? | ||
| 305 | } | 223 | } |
| 306 | } | 224 | } |
| 307 | 225 | ||
| 308 | fn send_u8<T: SpiBusWrite>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { | 226 | fn send_u8<T: SpiDevice>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { |
| 309 | match words { | 227 | match words { |
| 310 | DataFormat::U8(slice) => spi.write(slice), | 228 | DataFormat::U8(slice) => spi.write(slice), |
| 311 | DataFormat::U16(slice) => { | 229 | DataFormat::U16(slice) => { |
diff --git a/examples/rp/src/bin/uart.rs b/examples/rp/src/bin/uart.rs index c63b31cae..451c3c396 100644 --- a/examples/rp/src/bin/uart.rs +++ b/examples/rp/src/bin/uart.rs | |||
| @@ -1,3 +1,9 @@ | |||
| 1 | //! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! No specific hardware is specified in this example. Only output on pin 0 is tested. | ||
| 4 | //! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used | ||
| 5 | //! with its UART port. | ||
| 6 | |||
| 1 | #![no_std] | 7 | #![no_std] |
| 2 | #![no_main] | 8 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 9 | #![feature(type_alias_impl_trait)] |
| @@ -10,7 +16,7 @@ use {defmt_rtt as _, panic_probe as _}; | |||
| 10 | async fn main(_spawner: Spawner) { | 16 | async fn main(_spawner: Spawner) { |
| 11 | let p = embassy_rp::init(Default::default()); | 17 | let p = embassy_rp::init(Default::default()); |
| 12 | let config = uart::Config::default(); | 18 | let config = uart::Config::default(); |
| 13 | let mut uart = uart::Uart::new_with_rtscts(p.UART0, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, config); | 19 | let mut uart = uart::Uart::new_with_rtscts_blocking(p.UART0, p.PIN_0, p.PIN_1, p.PIN_3, p.PIN_2, config); |
| 14 | uart.blocking_write("Hello World!\r\n".as_bytes()).unwrap(); | 20 | uart.blocking_write("Hello World!\r\n".as_bytes()).unwrap(); |
| 15 | 21 | ||
| 16 | loop { | 22 | loop { |
diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs new file mode 100644 index 000000000..735201718 --- /dev/null +++ b/examples/rp/src/bin/uart_buffered_split.rs | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | //! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! No specific hardware is specified in this example. If you connect pin 0 and 1 you should get the same data back. | ||
| 4 | //! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used | ||
| 5 | //! with its UART port. | ||
| 6 | |||
| 7 | #![no_std] | ||
| 8 | #![no_main] | ||
| 9 | #![feature(type_alias_impl_trait)] | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_rp::bind_interrupts; | ||
| 14 | use embassy_rp::peripherals::UART0; | ||
| 15 | use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config}; | ||
| 16 | use embassy_time::{Duration, Timer}; | ||
| 17 | use embedded_io::asynch::{Read, Write}; | ||
| 18 | use static_cell::make_static; | ||
| 19 | use {defmt_rtt as _, panic_probe as _}; | ||
| 20 | |||
| 21 | bind_interrupts!(struct Irqs { | ||
| 22 | UART0_IRQ => BufferedInterruptHandler<UART0>; | ||
| 23 | }); | ||
| 24 | |||
| 25 | #[embassy_executor::main] | ||
| 26 | async fn main(spawner: Spawner) { | ||
| 27 | let p = embassy_rp::init(Default::default()); | ||
| 28 | let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0); | ||
| 29 | |||
| 30 | let tx_buf = &mut make_static!([0u8; 16])[..]; | ||
| 31 | let rx_buf = &mut make_static!([0u8; 16])[..]; | ||
| 32 | let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); | ||
| 33 | let (rx, mut tx) = uart.split(); | ||
| 34 | |||
| 35 | unwrap!(spawner.spawn(reader(rx))); | ||
| 36 | |||
| 37 | info!("Writing..."); | ||
| 38 | loop { | ||
| 39 | let data = [ | ||
| 40 | 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, | ||
| 41 | 29, 30, 31, | ||
| 42 | ]; | ||
| 43 | info!("TX {:?}", data); | ||
| 44 | tx.write_all(&data).await.unwrap(); | ||
| 45 | Timer::after(Duration::from_secs(1)).await; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | #[embassy_executor::task] | ||
| 50 | async fn reader(mut rx: BufferedUartRx<'static, UART0>) { | ||
| 51 | info!("Reading..."); | ||
| 52 | loop { | ||
| 53 | let mut buf = [0; 31]; | ||
| 54 | rx.read_exact(&mut buf).await.unwrap(); | ||
| 55 | info!("RX {:?}", buf); | ||
| 56 | } | ||
| 57 | } | ||
diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs new file mode 100644 index 000000000..c1515a911 --- /dev/null +++ b/examples/rp/src/bin/uart_unidir.rs | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | //! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for | ||
| 4 | //! this to work | ||
| 5 | //! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used | ||
| 6 | //! with its UART port. | ||
| 7 | |||
| 8 | #![no_std] | ||
| 9 | #![no_main] | ||
| 10 | #![feature(type_alias_impl_trait)] | ||
| 11 | |||
| 12 | use defmt::*; | ||
| 13 | use embassy_executor::Spawner; | ||
| 14 | use embassy_rp::bind_interrupts; | ||
| 15 | use embassy_rp::peripherals::UART1; | ||
| 16 | use embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx}; | ||
| 17 | use embassy_time::{Duration, Timer}; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | UART1_IRQ => InterruptHandler<UART1>; | ||
| 22 | }); | ||
| 23 | |||
| 24 | #[embassy_executor::main] | ||
| 25 | async fn main(spawner: Spawner) { | ||
| 26 | let p = embassy_rp::init(Default::default()); | ||
| 27 | |||
| 28 | let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default()); | ||
| 29 | let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default()); | ||
| 30 | |||
| 31 | unwrap!(spawner.spawn(reader(uart_rx))); | ||
| 32 | |||
| 33 | info!("Writing..."); | ||
| 34 | loop { | ||
| 35 | let data = [1u8, 2, 3, 4, 5, 6, 7, 8]; | ||
| 36 | info!("TX {:?}", data); | ||
| 37 | uart_tx.write(&data).await.unwrap(); | ||
| 38 | Timer::after(Duration::from_secs(1)).await; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::task] | ||
| 43 | async fn reader(mut rx: UartRx<'static, UART1, Async>) { | ||
| 44 | info!("Reading..."); | ||
| 45 | loop { | ||
| 46 | // read a total of 4 transmissions (32 / 8) and then print the result | ||
| 47 | let mut buf = [0; 32]; | ||
| 48 | rx.read(&mut buf).await.unwrap(); | ||
| 49 | info!("RX {:?}", buf); | ||
| 50 | } | ||
| 51 | } | ||
diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs new file mode 100644 index 000000000..0a08f667e --- /dev/null +++ b/examples/rp/src/bin/usb_ethernet.rs | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | //! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! This is a CDC-NCM class implementation, aka Ethernet over USB. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_net::tcp::TcpSocket; | ||
| 12 | use embassy_net::{Stack, StackResources}; | ||
| 13 | use embassy_rp::peripherals::USB; | ||
| 14 | use embassy_rp::usb::{Driver, InterruptHandler}; | ||
| 15 | use embassy_rp::{bind_interrupts, peripherals}; | ||
| 16 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; | ||
| 17 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; | ||
| 18 | use embassy_usb::{Builder, Config, UsbDevice}; | ||
| 19 | use embedded_io::asynch::Write; | ||
| 20 | use static_cell::make_static; | ||
| 21 | use {defmt_rtt as _, panic_probe as _}; | ||
| 22 | |||
| 23 | bind_interrupts!(struct Irqs { | ||
| 24 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 25 | }); | ||
| 26 | |||
| 27 | type MyDriver = Driver<'static, peripherals::USB>; | ||
| 28 | |||
| 29 | const MTU: usize = 1514; | ||
| 30 | |||
| 31 | #[embassy_executor::task] | ||
| 32 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | ||
| 33 | device.run().await | ||
| 34 | } | ||
| 35 | |||
| 36 | #[embassy_executor::task] | ||
| 37 | async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { | ||
| 38 | class.run().await | ||
| 39 | } | ||
| 40 | |||
| 41 | #[embassy_executor::task] | ||
| 42 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { | ||
| 43 | stack.run().await | ||
| 44 | } | ||
| 45 | |||
| 46 | #[embassy_executor::main] | ||
| 47 | async fn main(spawner: Spawner) { | ||
| 48 | let p = embassy_rp::init(Default::default()); | ||
| 49 | |||
| 50 | // Create the driver, from the HAL. | ||
| 51 | let driver = Driver::new(p.USB, Irqs); | ||
| 52 | |||
| 53 | // Create embassy-usb Config | ||
| 54 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 55 | config.manufacturer = Some("Embassy"); | ||
| 56 | config.product = Some("USB-Ethernet example"); | ||
| 57 | config.serial_number = Some("12345678"); | ||
| 58 | config.max_power = 100; | ||
| 59 | config.max_packet_size_0 = 64; | ||
| 60 | |||
| 61 | // Required for Windows support. | ||
| 62 | config.composite_with_iads = true; | ||
| 63 | config.device_class = 0xEF; | ||
| 64 | config.device_sub_class = 0x02; | ||
| 65 | config.device_protocol = 0x01; | ||
| 66 | |||
| 67 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 68 | let mut builder = Builder::new( | ||
| 69 | driver, | ||
| 70 | config, | ||
| 71 | &mut make_static!([0; 256])[..], | ||
| 72 | &mut make_static!([0; 256])[..], | ||
| 73 | &mut make_static!([0; 256])[..], | ||
| 74 | &mut make_static!([0; 128])[..], | ||
| 75 | ); | ||
| 76 | |||
| 77 | // Our MAC addr. | ||
| 78 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | ||
| 79 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | ||
| 80 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | ||
| 81 | |||
| 82 | // Create classes on the builder. | ||
| 83 | let class = CdcNcmClass::new(&mut builder, make_static!(State::new()), host_mac_addr, 64); | ||
| 84 | |||
| 85 | // Build the builder. | ||
| 86 | let usb = builder.build(); | ||
| 87 | |||
| 88 | unwrap!(spawner.spawn(usb_task(usb))); | ||
| 89 | |||
| 90 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(make_static!(NetState::new()), our_mac_addr); | ||
| 91 | unwrap!(spawner.spawn(usb_ncm_task(runner))); | ||
| 92 | |||
| 93 | let config = embassy_net::Config::dhcpv4(Default::default()); | ||
| 94 | //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 95 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 96 | // dns_servers: Vec::new(), | ||
| 97 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 98 | //}); | ||
| 99 | |||
| 100 | // Generate random seed | ||
| 101 | let seed = 1234; // guaranteed random, chosen by a fair dice roll | ||
| 102 | |||
| 103 | // Init network stack | ||
| 104 | let stack = &*make_static!(Stack::new( | ||
| 105 | device, | ||
| 106 | config, | ||
| 107 | make_static!(StackResources::<2>::new()), | ||
| 108 | seed | ||
| 109 | )); | ||
| 110 | |||
| 111 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 112 | |||
| 113 | // And now we can use it! | ||
| 114 | |||
| 115 | let mut rx_buffer = [0; 4096]; | ||
| 116 | let mut tx_buffer = [0; 4096]; | ||
| 117 | let mut buf = [0; 4096]; | ||
| 118 | |||
| 119 | loop { | ||
| 120 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 121 | socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); | ||
| 122 | |||
| 123 | info!("Listening on TCP:1234..."); | ||
| 124 | if let Err(e) = socket.accept(1234).await { | ||
| 125 | warn!("accept error: {:?}", e); | ||
| 126 | continue; | ||
| 127 | } | ||
| 128 | |||
| 129 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 130 | |||
| 131 | loop { | ||
| 132 | let n = match socket.read(&mut buf).await { | ||
| 133 | Ok(0) => { | ||
| 134 | warn!("read EOF"); | ||
| 135 | break; | ||
| 136 | } | ||
| 137 | Ok(n) => n, | ||
| 138 | Err(e) => { | ||
| 139 | warn!("read error: {:?}", e); | ||
| 140 | break; | ||
| 141 | } | ||
| 142 | }; | ||
| 143 | |||
| 144 | info!("rxd {:02x}", &buf[..n]); | ||
| 145 | |||
| 146 | match socket.write_all(&buf[..n]).await { | ||
| 147 | Ok(()) => {} | ||
| 148 | Err(e) => { | ||
| 149 | warn!("write error: {:?}", e); | ||
| 150 | break; | ||
| 151 | } | ||
| 152 | }; | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs new file mode 100644 index 000000000..99af1f02f --- /dev/null +++ b/examples/rp/src/bin/usb_hid_keyboard.rs | |||
| @@ -0,0 +1,188 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_futures::join::join; | ||
| 10 | use embassy_rp::bind_interrupts; | ||
| 11 | use embassy_rp::gpio::{Input, Pull}; | ||
| 12 | use embassy_rp::peripherals::USB; | ||
| 13 | use embassy_rp::usb::{Driver, InterruptHandler}; | ||
| 14 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | ||
| 15 | use embassy_usb::control::OutResponse; | ||
| 16 | use embassy_usb::{Builder, Config, Handler}; | ||
| 17 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 22 | }); | ||
| 23 | |||
| 24 | #[embassy_executor::main] | ||
| 25 | async fn main(_spawner: Spawner) { | ||
| 26 | let p = embassy_rp::init(Default::default()); | ||
| 27 | // Create the driver, from the HAL. | ||
| 28 | let driver = Driver::new(p.USB, Irqs); | ||
| 29 | |||
| 30 | // Create embassy-usb Config | ||
| 31 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 32 | config.manufacturer = Some("Embassy"); | ||
| 33 | config.product = Some("HID keyboard example"); | ||
| 34 | config.serial_number = Some("12345678"); | ||
| 35 | config.max_power = 100; | ||
| 36 | config.max_packet_size_0 = 64; | ||
| 37 | |||
| 38 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 39 | // It needs some buffers for building the descriptors. | ||
| 40 | let mut device_descriptor = [0; 256]; | ||
| 41 | let mut config_descriptor = [0; 256]; | ||
| 42 | let mut bos_descriptor = [0; 256]; | ||
| 43 | // You can also add a Microsoft OS descriptor. | ||
| 44 | // let mut msos_descriptor = [0; 256]; | ||
| 45 | let mut control_buf = [0; 64]; | ||
| 46 | let request_handler = MyRequestHandler {}; | ||
| 47 | let mut device_handler = MyDeviceHandler::new(); | ||
| 48 | |||
| 49 | let mut state = State::new(); | ||
| 50 | |||
| 51 | let mut builder = Builder::new( | ||
| 52 | driver, | ||
| 53 | config, | ||
| 54 | &mut device_descriptor, | ||
| 55 | &mut config_descriptor, | ||
| 56 | &mut bos_descriptor, | ||
| 57 | // &mut msos_descriptor, | ||
| 58 | &mut control_buf, | ||
| 59 | ); | ||
| 60 | |||
| 61 | builder.handler(&mut device_handler); | ||
| 62 | |||
| 63 | // Create classes on the builder. | ||
| 64 | let config = embassy_usb::class::hid::Config { | ||
| 65 | report_descriptor: KeyboardReport::desc(), | ||
| 66 | request_handler: Some(&request_handler), | ||
| 67 | poll_ms: 60, | ||
| 68 | max_packet_size: 64, | ||
| 69 | }; | ||
| 70 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | ||
| 71 | |||
| 72 | // Build the builder. | ||
| 73 | let mut usb = builder.build(); | ||
| 74 | |||
| 75 | // Run the USB device. | ||
| 76 | let usb_fut = usb.run(); | ||
| 77 | |||
| 78 | // Set up the signal pin that will be used to trigger the keyboard. | ||
| 79 | let mut signal_pin = Input::new(p.PIN_16, Pull::None); | ||
| 80 | |||
| 81 | let (reader, mut writer) = hid.split(); | ||
| 82 | |||
| 83 | // Do stuff with the class! | ||
| 84 | let in_fut = async { | ||
| 85 | loop { | ||
| 86 | info!("Waiting for HIGH on pin 16"); | ||
| 87 | signal_pin.wait_for_high().await; | ||
| 88 | info!("HIGH DETECTED"); | ||
| 89 | // Create a report with the A key pressed. (no shift modifier) | ||
| 90 | let report = KeyboardReport { | ||
| 91 | keycodes: [4, 0, 0, 0, 0, 0], | ||
| 92 | leds: 0, | ||
| 93 | modifier: 0, | ||
| 94 | reserved: 0, | ||
| 95 | }; | ||
| 96 | // Send the report. | ||
| 97 | match writer.write_serialize(&report).await { | ||
| 98 | Ok(()) => {} | ||
| 99 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 100 | }; | ||
| 101 | signal_pin.wait_for_low().await; | ||
| 102 | info!("LOW DETECTED"); | ||
| 103 | let report = KeyboardReport { | ||
| 104 | keycodes: [0, 0, 0, 0, 0, 0], | ||
| 105 | leds: 0, | ||
| 106 | modifier: 0, | ||
| 107 | reserved: 0, | ||
| 108 | }; | ||
| 109 | match writer.write_serialize(&report).await { | ||
| 110 | Ok(()) => {} | ||
| 111 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 112 | }; | ||
| 113 | } | ||
| 114 | }; | ||
| 115 | |||
| 116 | let out_fut = async { | ||
| 117 | reader.run(false, &request_handler).await; | ||
| 118 | }; | ||
| 119 | |||
| 120 | // Run everything concurrently. | ||
| 121 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 122 | join(usb_fut, join(in_fut, out_fut)).await; | ||
| 123 | } | ||
| 124 | |||
| 125 | struct MyRequestHandler {} | ||
| 126 | |||
| 127 | impl RequestHandler for MyRequestHandler { | ||
| 128 | fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> { | ||
| 129 | info!("Get report for {:?}", id); | ||
| 130 | None | ||
| 131 | } | ||
| 132 | |||
| 133 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { | ||
| 134 | info!("Set report for {:?}: {=[u8]}", id, data); | ||
| 135 | OutResponse::Accepted | ||
| 136 | } | ||
| 137 | |||
| 138 | fn set_idle_ms(&self, id: Option<ReportId>, dur: u32) { | ||
| 139 | info!("Set idle rate for {:?} to {:?}", id, dur); | ||
| 140 | } | ||
| 141 | |||
| 142 | fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> { | ||
| 143 | info!("Get idle rate for {:?}", id); | ||
| 144 | None | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | struct MyDeviceHandler { | ||
| 149 | configured: AtomicBool, | ||
| 150 | } | ||
| 151 | |||
| 152 | impl MyDeviceHandler { | ||
| 153 | fn new() -> Self { | ||
| 154 | MyDeviceHandler { | ||
| 155 | configured: AtomicBool::new(false), | ||
| 156 | } | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | impl Handler for MyDeviceHandler { | ||
| 161 | fn enabled(&mut self, enabled: bool) { | ||
| 162 | self.configured.store(false, Ordering::Relaxed); | ||
| 163 | if enabled { | ||
| 164 | info!("Device enabled"); | ||
| 165 | } else { | ||
| 166 | info!("Device disabled"); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | fn reset(&mut self) { | ||
| 171 | self.configured.store(false, Ordering::Relaxed); | ||
| 172 | info!("Bus reset, the Vbus current limit is 100mA"); | ||
| 173 | } | ||
| 174 | |||
| 175 | fn addressed(&mut self, addr: u8) { | ||
| 176 | self.configured.store(false, Ordering::Relaxed); | ||
| 177 | info!("USB address set to: {}", addr); | ||
| 178 | } | ||
| 179 | |||
| 180 | fn configured(&mut self, configured: bool) { | ||
| 181 | self.configured.store(configured, Ordering::Relaxed); | ||
| 182 | if configured { | ||
| 183 | info!("Device configured, it may now draw up to the configured current limit from Vbus.") | ||
| 184 | } else { | ||
| 185 | info!("Device is no longer configured, the Vbus current limit is 100mA."); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | } | ||
diff --git a/examples/rp/src/bin/usb_logger.rs b/examples/rp/src/bin/usb_logger.rs new file mode 100644 index 000000000..9c5e6897d --- /dev/null +++ b/examples/rp/src/bin/usb_logger.rs | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | //! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! This creates the possibility to send log::info/warn/error/debug! to USB serial port. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::bind_interrupts; | ||
| 11 | use embassy_rp::peripherals::USB; | ||
| 12 | use embassy_rp::usb::{Driver, InterruptHandler}; | ||
| 13 | use embassy_time::{Duration, Timer}; | ||
| 14 | use {defmt_rtt as _, panic_probe as _}; | ||
| 15 | |||
| 16 | bind_interrupts!(struct Irqs { | ||
| 17 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 18 | }); | ||
| 19 | |||
| 20 | #[embassy_executor::task] | ||
| 21 | async fn logger_task(driver: Driver<'static, USB>) { | ||
| 22 | embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); | ||
| 23 | } | ||
| 24 | |||
| 25 | #[embassy_executor::main] | ||
| 26 | async fn main(spawner: Spawner) { | ||
| 27 | let p = embassy_rp::init(Default::default()); | ||
| 28 | let driver = Driver::new(p.USB, Irqs); | ||
| 29 | spawner.spawn(logger_task(driver)).unwrap(); | ||
| 30 | |||
| 31 | let mut counter = 0; | ||
| 32 | loop { | ||
| 33 | counter += 1; | ||
| 34 | log::info!("Tick {}", counter); | ||
| 35 | Timer::after(Duration::from_secs(1)).await; | ||
| 36 | } | ||
| 37 | } | ||
diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs new file mode 100644 index 000000000..164e2052d --- /dev/null +++ b/examples/rp/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,109 @@ | |||
| 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::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 {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | USBCTRL_IRQ => InterruptHandler<USB>; | ||
| 22 | }); | ||
| 23 | |||
| 24 | #[embassy_executor::main] | ||
| 25 | async fn main(_spawner: Spawner) { | ||
| 26 | info!("Hello there!"); | ||
| 27 | |||
| 28 | let p = embassy_rp::init(Default::default()); | ||
| 29 | |||
| 30 | // Create the driver, from the HAL. | ||
| 31 | let driver = Driver::new(p.USB, Irqs); | ||
| 32 | |||
| 33 | // Create embassy-usb Config | ||
| 34 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 35 | config.manufacturer = Some("Embassy"); | ||
| 36 | config.product = Some("USB-serial example"); | ||
| 37 | config.serial_number = Some("12345678"); | ||
| 38 | config.max_power = 100; | ||
| 39 | config.max_packet_size_0 = 64; | ||
| 40 | |||
| 41 | // Required for windows compatibility. | ||
| 42 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 43 | config.device_class = 0xEF; | ||
| 44 | config.device_sub_class = 0x02; | ||
| 45 | config.device_protocol = 0x01; | ||
| 46 | config.composite_with_iads = true; | ||
| 47 | |||
| 48 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 49 | // It needs some buffers for building the descriptors. | ||
| 50 | let mut device_descriptor = [0; 256]; | ||
| 51 | let mut config_descriptor = [0; 256]; | ||
| 52 | let mut bos_descriptor = [0; 256]; | ||
| 53 | let mut control_buf = [0; 64]; | ||
| 54 | |||
| 55 | let mut state = State::new(); | ||
| 56 | |||
| 57 | let mut builder = Builder::new( | ||
| 58 | driver, | ||
| 59 | config, | ||
| 60 | &mut device_descriptor, | ||
| 61 | &mut config_descriptor, | ||
| 62 | &mut bos_descriptor, | ||
| 63 | &mut control_buf, | ||
| 64 | ); | ||
| 65 | |||
| 66 | // Create classes on the builder. | ||
| 67 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 68 | |||
| 69 | // Build the builder. | ||
| 70 | let mut usb = builder.build(); | ||
| 71 | |||
| 72 | // Run the USB device. | ||
| 73 | let usb_fut = usb.run(); | ||
| 74 | |||
| 75 | // Do stuff with the class! | ||
| 76 | let echo_fut = async { | ||
| 77 | loop { | ||
| 78 | class.wait_connection().await; | ||
| 79 | info!("Connected"); | ||
| 80 | let _ = echo(&mut class).await; | ||
| 81 | info!("Disconnected"); | ||
| 82 | } | ||
| 83 | }; | ||
| 84 | |||
| 85 | // Run everything concurrently. | ||
| 86 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 87 | join(usb_fut, echo_fut).await; | ||
| 88 | } | ||
| 89 | |||
| 90 | struct Disconnected {} | ||
| 91 | |||
| 92 | impl From<EndpointError> for Disconnected { | ||
| 93 | fn from(val: EndpointError) -> Self { | ||
| 94 | match val { | ||
| 95 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 96 | EndpointError::Disabled => Disconnected {}, | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 102 | let mut buf = [0; 64]; | ||
| 103 | loop { | ||
| 104 | let n = class.read_packet(&mut buf).await?; | ||
| 105 | let data = &buf[..n]; | ||
| 106 | info!("data: {:x}", data); | ||
| 107 | class.write_packet(data).await?; | ||
| 108 | } | ||
| 109 | } | ||
diff --git a/examples/rp/src/bin/watchdog.rs b/examples/rp/src/bin/watchdog.rs new file mode 100644 index 000000000..fe5eaf926 --- /dev/null +++ b/examples/rp/src/bin/watchdog.rs | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | //! This example shows how to use Watchdog in the RP2040 chip. | ||
| 2 | //! | ||
| 3 | //! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::info; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_rp::gpio; | ||
| 12 | use embassy_rp::watchdog::*; | ||
| 13 | use embassy_time::{Duration, Timer}; | ||
| 14 | use gpio::{Level, Output}; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_rp::init(Default::default()); | ||
| 20 | info!("Hello world!"); | ||
| 21 | |||
| 22 | let mut watchdog = Watchdog::new(p.WATCHDOG); | ||
| 23 | let mut led = Output::new(p.PIN_25, Level::Low); | ||
| 24 | |||
| 25 | // Set the LED high for 2 seconds so we know when we're about to start the watchdog | ||
| 26 | led.set_high(); | ||
| 27 | Timer::after(Duration::from_secs(2)).await; | ||
| 28 | |||
| 29 | // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it | ||
| 30 | watchdog.start(Duration::from_millis(1_050)); | ||
| 31 | info!("Started the watchdog timer"); | ||
| 32 | |||
| 33 | // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset | ||
| 34 | for _ in 1..=5 { | ||
| 35 | led.set_low(); | ||
| 36 | Timer::after(Duration::from_millis(500)).await; | ||
| 37 | led.set_high(); | ||
| 38 | Timer::after(Duration::from_millis(500)).await; | ||
| 39 | info!("Feeding watchdog"); | ||
| 40 | watchdog.feed(); | ||
| 41 | } | ||
| 42 | |||
| 43 | info!("Stopped feeding, device will reset in 1.05 seconds"); | ||
| 44 | // Blink 10 times per second, not feeding the watchdog. | ||
| 45 | // The processor should reset in 1.05 seconds. | ||
| 46 | loop { | ||
| 47 | led.set_low(); | ||
| 48 | Timer::after(Duration::from_millis(100)).await; | ||
| 49 | led.set_high(); | ||
| 50 | Timer::after(Duration::from_millis(100)).await; | ||
| 51 | } | ||
| 52 | } | ||
diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs new file mode 100644 index 000000000..e3e393445 --- /dev/null +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | //! This example uses the RP Pico W board Wifi chip (cyw43). | ||
| 2 | //! Creates an Access point Wifi network and creates a TCP endpoint on port 1234. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | #![feature(async_fn_in_trait)] | ||
| 8 | #![allow(incomplete_features)] | ||
| 9 | |||
| 10 | use core::str::from_utf8; | ||
| 11 | |||
| 12 | use cyw43_pio::PioSpi; | ||
| 13 | use defmt::*; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_net::tcp::TcpSocket; | ||
| 16 | use embassy_net::{Config, Stack, StackResources}; | ||
| 17 | use embassy_rp::bind_interrupts; | ||
| 18 | use embassy_rp::gpio::{Level, Output}; | ||
| 19 | use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; | ||
| 20 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 21 | use embassy_time::Duration; | ||
| 22 | use embedded_io::asynch::Write; | ||
| 23 | use static_cell::make_static; | ||
| 24 | use {defmt_rtt as _, panic_probe as _}; | ||
| 25 | |||
| 26 | bind_interrupts!(struct Irqs { | ||
| 27 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 28 | }); | ||
| 29 | |||
| 30 | #[embassy_executor::task] | ||
| 31 | async fn wifi_task( | ||
| 32 | runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, | ||
| 33 | ) -> ! { | ||
| 34 | runner.run().await | ||
| 35 | } | ||
| 36 | |||
| 37 | #[embassy_executor::task] | ||
| 38 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | ||
| 39 | stack.run().await | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(spawner: Spawner) { | ||
| 44 | info!("Hello World!"); | ||
| 45 | |||
| 46 | let p = embassy_rp::init(Default::default()); | ||
| 47 | |||
| 48 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 49 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 50 | |||
| 51 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 52 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 53 | // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 54 | // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 | ||
| 55 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 56 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 57 | |||
| 58 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 59 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 60 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 61 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | ||
| 62 | |||
| 63 | let state = make_static!(cyw43::State::new()); | ||
| 64 | let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 65 | unwrap!(spawner.spawn(wifi_task(runner))); | ||
| 66 | |||
| 67 | control.init(clm).await; | ||
| 68 | control | ||
| 69 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 70 | .await; | ||
| 71 | |||
| 72 | // Use a link-local address for communication without DHCP server | ||
| 73 | let config = Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 74 | address: embassy_net::Ipv4Cidr::new(embassy_net::Ipv4Address::new(169, 254, 1, 1), 16), | ||
| 75 | dns_servers: heapless::Vec::new(), | ||
| 76 | gateway: None, | ||
| 77 | }); | ||
| 78 | |||
| 79 | // Generate random seed | ||
| 80 | let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. | ||
| 81 | |||
| 82 | // Init network stack | ||
| 83 | let stack = &*make_static!(Stack::new( | ||
| 84 | net_device, | ||
| 85 | config, | ||
| 86 | make_static!(StackResources::<2>::new()), | ||
| 87 | seed | ||
| 88 | )); | ||
| 89 | |||
| 90 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 91 | |||
| 92 | //control.start_ap_open("cyw43", 5).await; | ||
| 93 | control.start_ap_wpa2("cyw43", "password", 5).await; | ||
| 94 | |||
| 95 | // And now we can use it! | ||
| 96 | |||
| 97 | let mut rx_buffer = [0; 4096]; | ||
| 98 | let mut tx_buffer = [0; 4096]; | ||
| 99 | let mut buf = [0; 4096]; | ||
| 100 | |||
| 101 | loop { | ||
| 102 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 103 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 104 | |||
| 105 | control.gpio_set(0, false).await; | ||
| 106 | info!("Listening on TCP:1234..."); | ||
| 107 | if let Err(e) = socket.accept(1234).await { | ||
| 108 | warn!("accept error: {:?}", e); | ||
| 109 | continue; | ||
| 110 | } | ||
| 111 | |||
| 112 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 113 | control.gpio_set(0, true).await; | ||
| 114 | |||
| 115 | loop { | ||
| 116 | let n = match socket.read(&mut buf).await { | ||
| 117 | Ok(0) => { | ||
| 118 | warn!("read EOF"); | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | Ok(n) => n, | ||
| 122 | Err(e) => { | ||
| 123 | warn!("read error: {:?}", e); | ||
| 124 | break; | ||
| 125 | } | ||
| 126 | }; | ||
| 127 | |||
| 128 | info!("rxd {}", from_utf8(&buf[..n]).unwrap()); | ||
| 129 | |||
| 130 | match socket.write_all(&buf[..n]).await { | ||
| 131 | Ok(()) => {} | ||
| 132 | Err(e) => { | ||
| 133 | warn!("write error: {:?}", e); | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | }; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | } | ||
diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs new file mode 100644 index 000000000..33d43788c --- /dev/null +++ b/examples/rp/src/bin/wifi_blinky.rs | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | //! This example test the RP Pico W on board LED. | ||
| 2 | //! | ||
| 3 | //! It does not work with the RP Pico board. See blinky.rs. | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use cyw43_pio::PioSpi; | ||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_rp::bind_interrupts; | ||
| 13 | use embassy_rp::gpio::{Level, Output}; | ||
| 14 | use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; | ||
| 15 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 16 | use embassy_time::{Duration, Timer}; | ||
| 17 | use static_cell::make_static; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | bind_interrupts!(struct Irqs { | ||
| 21 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 22 | }); | ||
| 23 | |||
| 24 | #[embassy_executor::task] | ||
| 25 | async fn wifi_task( | ||
| 26 | runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, | ||
| 27 | ) -> ! { | ||
| 28 | runner.run().await | ||
| 29 | } | ||
| 30 | |||
| 31 | #[embassy_executor::main] | ||
| 32 | async fn main(spawner: Spawner) { | ||
| 33 | let p = embassy_rp::init(Default::default()); | ||
| 34 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 35 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 36 | |||
| 37 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 38 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 39 | // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 40 | // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 | ||
| 41 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 42 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 43 | |||
| 44 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 45 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 46 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 47 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | ||
| 48 | |||
| 49 | let state = make_static!(cyw43::State::new()); | ||
| 50 | let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 51 | unwrap!(spawner.spawn(wifi_task(runner))); | ||
| 52 | |||
| 53 | control.init(clm).await; | ||
| 54 | control | ||
| 55 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 56 | .await; | ||
| 57 | |||
| 58 | let delay = Duration::from_secs(1); | ||
| 59 | loop { | ||
| 60 | info!("led on!"); | ||
| 61 | control.gpio_set(0, true).await; | ||
| 62 | Timer::after(delay).await; | ||
| 63 | |||
| 64 | info!("led off!"); | ||
| 65 | control.gpio_set(0, false).await; | ||
| 66 | Timer::after(delay).await; | ||
| 67 | } | ||
| 68 | } | ||
diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs new file mode 100644 index 000000000..743fab617 --- /dev/null +++ b/examples/rp/src/bin/wifi_scan.rs | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | //! This example uses the RP Pico W board Wifi chip (cyw43). | ||
| 2 | //! Scans Wifi for ssid names. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | #![feature(async_fn_in_trait)] | ||
| 8 | #![allow(incomplete_features)] | ||
| 9 | |||
| 10 | use core::str; | ||
| 11 | |||
| 12 | use cyw43_pio::PioSpi; | ||
| 13 | use defmt::*; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_net::Stack; | ||
| 16 | use embassy_rp::bind_interrupts; | ||
| 17 | use embassy_rp::gpio::{Level, Output}; | ||
| 18 | use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; | ||
| 19 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 20 | use static_cell::make_static; | ||
| 21 | use {defmt_rtt as _, panic_probe as _}; | ||
| 22 | |||
| 23 | bind_interrupts!(struct Irqs { | ||
| 24 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 25 | }); | ||
| 26 | |||
| 27 | #[embassy_executor::task] | ||
| 28 | async fn wifi_task( | ||
| 29 | runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, | ||
| 30 | ) -> ! { | ||
| 31 | runner.run().await | ||
| 32 | } | ||
| 33 | |||
| 34 | #[embassy_executor::task] | ||
| 35 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | ||
| 36 | stack.run().await | ||
| 37 | } | ||
| 38 | |||
| 39 | #[embassy_executor::main] | ||
| 40 | async fn main(spawner: Spawner) { | ||
| 41 | info!("Hello World!"); | ||
| 42 | |||
| 43 | let p = embassy_rp::init(Default::default()); | ||
| 44 | |||
| 45 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 46 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 47 | |||
| 48 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 49 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 50 | // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 51 | // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 | ||
| 52 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 53 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 54 | |||
| 55 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 56 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 57 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 58 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | ||
| 59 | |||
| 60 | let state = make_static!(cyw43::State::new()); | ||
| 61 | let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 62 | unwrap!(spawner.spawn(wifi_task(runner))); | ||
| 63 | |||
| 64 | control.init(clm).await; | ||
| 65 | control | ||
| 66 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 67 | .await; | ||
| 68 | |||
| 69 | let mut scanner = control.scan().await; | ||
| 70 | while let Some(bss) = scanner.next().await { | ||
| 71 | if let Ok(ssid_str) = str::from_utf8(&bss.ssid) { | ||
| 72 | info!("scanned {} == {:x}", ssid_str, bss.bssid); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs new file mode 100644 index 000000000..0223a3636 --- /dev/null +++ b/examples/rp/src/bin/wifi_tcp_server.rs | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | //! This example uses the RP Pico W board Wifi chip (cyw43). | ||
| 2 | //! Connects to specified Wifi network and creates a TCP endpoint on port 1234. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | #![feature(async_fn_in_trait)] | ||
| 8 | #![allow(incomplete_features)] | ||
| 9 | |||
| 10 | use core::str::from_utf8; | ||
| 11 | |||
| 12 | use cyw43_pio::PioSpi; | ||
| 13 | use defmt::*; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_net::tcp::TcpSocket; | ||
| 16 | use embassy_net::{Config, Stack, StackResources}; | ||
| 17 | use embassy_rp::bind_interrupts; | ||
| 18 | use embassy_rp::gpio::{Level, Output}; | ||
| 19 | use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; | ||
| 20 | use embassy_rp::pio::{InterruptHandler, Pio}; | ||
| 21 | use embassy_time::Duration; | ||
| 22 | use embedded_io::asynch::Write; | ||
| 23 | use static_cell::make_static; | ||
| 24 | use {defmt_rtt as _, panic_probe as _}; | ||
| 25 | |||
| 26 | bind_interrupts!(struct Irqs { | ||
| 27 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 28 | }); | ||
| 29 | |||
| 30 | const WIFI_NETWORK: &str = "EmbassyTest"; | ||
| 31 | const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; | ||
| 32 | |||
| 33 | #[embassy_executor::task] | ||
| 34 | async fn wifi_task( | ||
| 35 | runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, | ||
| 36 | ) -> ! { | ||
| 37 | runner.run().await | ||
| 38 | } | ||
| 39 | |||
| 40 | #[embassy_executor::task] | ||
| 41 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | ||
| 42 | stack.run().await | ||
| 43 | } | ||
| 44 | |||
| 45 | #[embassy_executor::main] | ||
| 46 | async fn main(spawner: Spawner) { | ||
| 47 | info!("Hello World!"); | ||
| 48 | |||
| 49 | let p = embassy_rp::init(Default::default()); | ||
| 50 | |||
| 51 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 52 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 53 | |||
| 54 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 55 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 56 | // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 57 | // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 | ||
| 58 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 59 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 60 | |||
| 61 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 62 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 63 | let mut pio = Pio::new(p.PIO0, Irqs); | ||
| 64 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | ||
| 65 | |||
| 66 | let state = make_static!(cyw43::State::new()); | ||
| 67 | let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 68 | unwrap!(spawner.spawn(wifi_task(runner))); | ||
| 69 | |||
| 70 | control.init(clm).await; | ||
| 71 | control | ||
| 72 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 73 | .await; | ||
| 74 | |||
| 75 | let config = Config::dhcpv4(Default::default()); | ||
| 76 | //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | ||
| 77 | // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), | ||
| 78 | // dns_servers: Vec::new(), | ||
| 79 | // gateway: Some(Ipv4Address::new(192, 168, 69, 1)), | ||
| 80 | //}); | ||
| 81 | |||
| 82 | // Generate random seed | ||
| 83 | let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. | ||
| 84 | |||
| 85 | // Init network stack | ||
| 86 | let stack = &*make_static!(Stack::new( | ||
| 87 | net_device, | ||
| 88 | config, | ||
| 89 | make_static!(StackResources::<2>::new()), | ||
| 90 | seed | ||
| 91 | )); | ||
| 92 | |||
| 93 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 94 | |||
| 95 | loop { | ||
| 96 | //control.join_open(WIFI_NETWORK).await; | ||
| 97 | match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { | ||
| 98 | Ok(_) => break, | ||
| 99 | Err(err) => { | ||
| 100 | info!("join failed with status={}", err.status); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | // And now we can use it! | ||
| 106 | |||
| 107 | let mut rx_buffer = [0; 4096]; | ||
| 108 | let mut tx_buffer = [0; 4096]; | ||
| 109 | let mut buf = [0; 4096]; | ||
| 110 | |||
| 111 | loop { | ||
| 112 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 113 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 114 | |||
| 115 | control.gpio_set(0, false).await; | ||
| 116 | info!("Listening on TCP:1234..."); | ||
| 117 | if let Err(e) = socket.accept(1234).await { | ||
| 118 | warn!("accept error: {:?}", e); | ||
| 119 | continue; | ||
| 120 | } | ||
| 121 | |||
| 122 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 123 | control.gpio_set(0, true).await; | ||
| 124 | |||
| 125 | loop { | ||
| 126 | let n = match socket.read(&mut buf).await { | ||
| 127 | Ok(0) => { | ||
| 128 | warn!("read EOF"); | ||
| 129 | break; | ||
| 130 | } | ||
| 131 | Ok(n) => n, | ||
| 132 | Err(e) => { | ||
| 133 | warn!("read error: {:?}", e); | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | }; | ||
| 137 | |||
| 138 | info!("rxd {}", from_utf8(&buf[..n]).unwrap()); | ||
| 139 | |||
| 140 | match socket.write_all(&buf[..n]).await { | ||
| 141 | Ok(()) => {} | ||
| 142 | Err(e) => { | ||
| 143 | warn!("write error: {:?}", e); | ||
| 144 | break; | ||
| 145 | } | ||
| 146 | }; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
