aboutsummaryrefslogtreecommitdiff
path: root/examples/rp/src
diff options
context:
space:
mode:
authorDion Dokter <[email protected]>2025-11-20 13:22:38 +0100
committerDion Dokter <[email protected]>2025-11-20 13:22:38 +0100
commit4f2c36e447455e8d33607d586859d3d075cabf1d (patch)
tree003cd822d688acd7c074dd229663b4648d100f71 /examples/rp/src
parent663732d85abbae400f2dbab2c411802a5b60e9b1 (diff)
parent661874d11de7d93ed52e08e020a9d4c7ee11122d (diff)
Merge branch 'main' into u0-lcd
Diffstat (limited to 'examples/rp/src')
-rw-r--r--examples/rp/src/bin/adc.rs9
-rw-r--r--examples/rp/src/bin/adc_dma.rs54
-rw-r--r--examples/rp/src/bin/assign_resources.rs82
-rw-r--r--examples/rp/src/bin/blinky_two_channels.rs8
-rw-r--r--examples/rp/src/bin/blinky_two_tasks.rs8
-rw-r--r--examples/rp/src/bin/debounce.rs2
-rw-r--r--examples/rp/src/bin/ethernet_w5500_icmp.rs143
-rw-r--r--examples/rp/src/bin/ethernet_w5500_icmp_ping.rs134
-rw-r--r--examples/rp/src/bin/ethernet_w5500_multisocket.rs29
-rw-r--r--examples/rp/src/bin/ethernet_w5500_tcp_client.rs25
-rw-r--r--examples/rp/src/bin/ethernet_w5500_tcp_server.rs25
-rw-r--r--examples/rp/src/bin/ethernet_w5500_udp.rs25
-rw-r--r--examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs155
-rw-r--r--examples/rp/src/bin/i2c_async_embassy.rs85
-rw-r--r--examples/rp/src/bin/i2c_blocking.rs4
-rw-r--r--examples/rp/src/bin/i2c_slave.rs16
-rw-r--r--examples/rp/src/bin/interrupt.rs5
-rw-r--r--examples/rp/src/bin/multicore.rs6
-rw-r--r--examples/rp/src/bin/multiprio.rs12
-rw-r--r--examples/rp/src/bin/orchestrate_tasks.rs318
-rw-r--r--examples/rp/src/bin/overclock.rs64
-rw-r--r--examples/rp/src/bin/overclock_manual.rs81
-rw-r--r--examples/rp/src/bin/pio_async.rs21
-rw-r--r--examples/rp/src/bin/pio_dma.rs13
-rw-r--r--examples/rp/src/bin/pio_hd44780.rs201
-rw-r--r--examples/rp/src/bin/pio_i2s.rs76
-rw-r--r--examples/rp/src/bin/pio_onewire.rs88
-rw-r--r--examples/rp/src/bin/pio_onewire_parasite.rs89
-rw-r--r--examples/rp/src/bin/pio_pwm.rs90
-rw-r--r--examples/rp/src/bin/pio_rotary_encoder.rs83
-rw-r--r--examples/rp/src/bin/pio_servo.rs96
-rw-r--r--examples/rp/src/bin/pio_spi.rs48
-rw-r--r--examples/rp/src/bin/pio_spi_async.rs57
-rw-r--r--examples/rp/src/bin/pio_stepper.rs137
-rw-r--r--examples/rp/src/bin/pio_uart.rs230
-rw-r--r--examples/rp/src/bin/pio_ws2812.rs105
-rw-r--r--examples/rp/src/bin/pwm.rs63
-rw-r--r--examples/rp/src/bin/rtc.rs8
-rw-r--r--examples/rp/src/bin/rtc_alarm.rs66
-rw-r--r--examples/rp/src/bin/shared_bus.rs115
-rw-r--r--examples/rp/src/bin/sharing.rs149
-rw-r--r--examples/rp/src/bin/spi_display.rs175
-rw-r--r--examples/rp/src/bin/spi_gc9a01.rs125
-rw-r--r--examples/rp/src/bin/spi_sdmmc.rs4
-rw-r--r--examples/rp/src/bin/uart_buffered_split.rs8
-rw-r--r--examples/rp/src/bin/uart_r503.rs2
-rw-r--r--examples/rp/src/bin/uart_unidir.rs4
-rw-r--r--examples/rp/src/bin/usb_ethernet.rs32
-rw-r--r--examples/rp/src/bin/usb_hid_keyboard.rs86
-rwxr-xr-x[-rw-r--r--]examples/rp/src/bin/usb_hid_mouse.rs67
-rw-r--r--examples/rp/src/bin/usb_logger.rs2
-rw-r--r--examples/rp/src/bin/usb_midi.rs7
-rw-r--r--examples/rp/src/bin/usb_raw.rs7
-rw-r--r--examples/rp/src/bin/usb_raw_bulk.rs11
-rw-r--r--examples/rp/src/bin/usb_serial.rs11
-rw-r--r--examples/rp/src/bin/usb_serial_with_handler.rs64
-rw-r--r--examples/rp/src/bin/usb_serial_with_logger.rs7
-rw-r--r--examples/rp/src/bin/usb_webusb.rs10
-rw-r--r--examples/rp/src/bin/wifi_ap_tcp_server.rs45
-rw-r--r--examples/rp/src/bin/wifi_blinky.rs21
-rw-r--r--examples/rp/src/bin/wifi_scan.rs27
-rw-r--r--examples/rp/src/bin/wifi_tcp_server.rs73
-rw-r--r--examples/rp/src/bin/wifi_webrequest.rs213
-rw-r--r--examples/rp/src/bin/zerocopy.rs13
64 files changed, 2674 insertions, 1365 deletions
diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs
index 1bb7c2249..015915586 100644
--- a/examples/rp/src/bin/adc.rs
+++ b/examples/rp/src/bin/adc.rs
@@ -12,9 +12,12 @@ use embassy_rp::gpio::Pull;
12use embassy_time::Timer; 12use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
14 14
15bind_interrupts!(struct Irqs { 15bind_interrupts!(
16 ADC_IRQ_FIFO => InterruptHandler; 16 /// Binds the ADC interrupts.
17}); 17 struct Irqs {
18 ADC_IRQ_FIFO => InterruptHandler;
19 }
20);
18 21
19#[embassy_executor::main] 22#[embassy_executor::main]
20async fn main(_spawner: Spawner) { 23async fn main(_spawner: Spawner) {
diff --git a/examples/rp/src/bin/adc_dma.rs b/examples/rp/src/bin/adc_dma.rs
new file mode 100644
index 000000000..b42c13fde
--- /dev/null
+++ b/examples/rp/src/bin/adc_dma.rs
@@ -0,0 +1,54 @@
1//! This example shows how to use the RP2040 ADC with DMA, both single- and multichannel reads.
2//! For multichannel, the samples are interleaved in the buffer:
3//! `[ch1, ch2, ch3, ch4, ch1, ch2, ch3, ch4, ...]`
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
10use embassy_rp::bind_interrupts;
11use embassy_rp::gpio::Pull;
12use embassy_time::{Duration, Ticker};
13use {defmt_rtt as _, panic_probe as _};
14
15bind_interrupts!(struct Irqs {
16 ADC_IRQ_FIFO => InterruptHandler;
17});
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 let p = embassy_rp::init(Default::default());
22 info!("Here we go!");
23
24 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
25 let mut dma = p.DMA_CH0;
26 let mut pin = Channel::new_pin(p.PIN_26, Pull::Up);
27 let mut pins = [
28 Channel::new_pin(p.PIN_27, Pull::Down),
29 Channel::new_pin(p.PIN_28, Pull::None),
30 Channel::new_pin(p.PIN_29, Pull::Up),
31 Channel::new_temp_sensor(p.ADC_TEMP_SENSOR),
32 ];
33
34 const BLOCK_SIZE: usize = 100;
35 const NUM_CHANNELS: usize = 4;
36 let mut ticker = Ticker::every(Duration::from_secs(1));
37 loop {
38 // Read 100 samples from a single channel
39 let mut buf = [0_u16; BLOCK_SIZE];
40 let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1)
41 adc.read_many(&mut pin, &mut buf, div, dma.reborrow()).await.unwrap();
42 info!("single: {:?} ...etc", buf[..8]);
43
44 // Read 100 samples from 4 channels interleaved
45 let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }];
46 let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1)
47 adc.read_many_multichannel(&mut pins, &mut buf, div, dma.reborrow())
48 .await
49 .unwrap();
50 info!("multi: {:?} ...etc", buf[..NUM_CHANNELS * 2]);
51
52 ticker.next().await;
53 }
54}
diff --git a/examples/rp/src/bin/assign_resources.rs b/examples/rp/src/bin/assign_resources.rs
new file mode 100644
index 000000000..aaa134768
--- /dev/null
+++ b/examples/rp/src/bin/assign_resources.rs
@@ -0,0 +1,82 @@
1//! This example demonstrates how to assign resources to multiple tasks by splitting up the peripherals.
2//! It is not about sharing the same resources between tasks, see sharing.rs for that or head to https://embassy.dev/book/#_sharing_peripherals_between_tasks)
3//! Of course splitting up resources and sharing resources can be combined, yet this example is only about splitting up resources.
4//!
5//! There are basically two ways we demonstrate here:
6//! 1) Assigning resources to a task by passing parts of the peripherals
7//! 2) Assigning resources to a task by passing a struct with the split up peripherals, using the assign-resources macro
8//!
9//! using four LEDs on Pins 10, 11, 20 and 21
10
11#![no_std]
12#![no_main]
13
14use assign_resources::assign_resources;
15use defmt::*;
16use embassy_executor::Spawner;
17use embassy_rp::Peri;
18use embassy_rp::gpio::{Level, Output};
19use embassy_rp::peripherals::{self, PIN_20, PIN_21};
20use embassy_time::Timer;
21use {defmt_rtt as _, panic_probe as _};
22
23#[embassy_executor::main]
24async fn main(spawner: Spawner) {
25 // initialize the peripherals
26 let p = embassy_rp::init(Default::default());
27
28 // 1) Assigning a resource to a task by passing parts of the peripherals.
29 spawner.spawn(double_blinky_manually_assigned(spawner, p.PIN_20, p.PIN_21).unwrap());
30
31 // 2) Using the assign-resources macro to assign resources to a task.
32 // we perform the split, see further below for the definition of the resources struct
33 let r = split_resources!(p);
34 // and then we can use them
35 spawner.spawn(double_blinky_macro_assigned(spawner, r.leds).unwrap());
36}
37
38// 1) Assigning a resource to a task by passing parts of the peripherals.
39#[embassy_executor::task]
40async fn double_blinky_manually_assigned(
41 _spawner: Spawner,
42 pin_20: Peri<'static, PIN_20>,
43 pin_21: Peri<'static, PIN_21>,
44) {
45 let mut led_20 = Output::new(pin_20, Level::Low);
46 let mut led_21 = Output::new(pin_21, Level::High);
47
48 loop {
49 info!("toggling leds");
50 led_20.toggle();
51 led_21.toggle();
52 Timer::after_secs(1).await;
53 }
54}
55
56// 2) Using the assign-resources macro to assign resources to a task.
57// first we define the resources we want to assign to the task using the assign_resources! macro
58// basically this will split up the peripherals struct into smaller structs, that we define here
59// naming is up to you, make sure your future self understands what you did here
60assign_resources! {
61 leds: Leds{
62 led_10: PIN_10,
63 led_11: PIN_11,
64 }
65 // add more resources to more structs if needed, for example defining one struct for each task
66}
67// this could be done in another file and imported here, but for the sake of simplicity we do it here
68// see https://github.com/adamgreig/assign-resources for more information
69
70// 2) Using the split resources in a task
71#[embassy_executor::task]
72async fn double_blinky_macro_assigned(_spawner: Spawner, r: Leds) {
73 let mut led_10 = Output::new(r.led_10, Level::Low);
74 let mut led_11 = Output::new(r.led_11, Level::High);
75
76 loop {
77 info!("toggling leds");
78 led_10.toggle();
79 led_11.toggle();
80 Timer::after_secs(1).await;
81 }
82}
diff --git a/examples/rp/src/bin/blinky_two_channels.rs b/examples/rp/src/bin/blinky_two_channels.rs
index b2eec2a21..87f3a3545 100644
--- a/examples/rp/src/bin/blinky_two_channels.rs
+++ b/examples/rp/src/bin/blinky_two_channels.rs
@@ -11,7 +11,7 @@ use embassy_rp::gpio;
11use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; 11use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
12use embassy_sync::channel::{Channel, Sender}; 12use embassy_sync::channel::{Channel, Sender};
13use embassy_time::{Duration, Ticker}; 13use embassy_time::{Duration, Ticker};
14use gpio::{AnyPin, Level, Output}; 14use gpio::{Level, Output};
15use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
16 16
17enum LedState { 17enum LedState {
@@ -22,13 +22,13 @@ static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();
22#[embassy_executor::main] 22#[embassy_executor::main]
23async fn main(spawner: Spawner) { 23async fn main(spawner: Spawner) {
24 let p = embassy_rp::init(Default::default()); 24 let p = embassy_rp::init(Default::default());
25 let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High); 25 let mut led = Output::new(p.PIN_25, Level::High);
26 26
27 let dt = 100 * 1_000_000; 27 let dt = 100 * 1_000_000;
28 let k = 1.003; 28 let k = 1.003;
29 29
30 unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt)))); 30 spawner.spawn(unwrap!(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));
31 unwrap!(spawner.spawn(toggle_led( 31 spawner.spawn(unwrap!(toggle_led(
32 CHANNEL.sender(), 32 CHANNEL.sender(),
33 Duration::from_nanos((dt as f64 * k) as u64) 33 Duration::from_nanos((dt as f64 * k) as u64)
34 ))); 34 )));
diff --git a/examples/rp/src/bin/blinky_two_tasks.rs b/examples/rp/src/bin/blinky_two_tasks.rs
index a57b513d6..aac7d928b 100644
--- a/examples/rp/src/bin/blinky_two_tasks.rs
+++ b/examples/rp/src/bin/blinky_two_tasks.rs
@@ -11,7 +11,7 @@ use embassy_rp::gpio;
11use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; 11use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
12use embassy_sync::mutex::Mutex; 12use embassy_sync::mutex::Mutex;
13use embassy_time::{Duration, Ticker}; 13use embassy_time::{Duration, Ticker};
14use gpio::{AnyPin, Level, Output}; 14use gpio::{Level, Output};
15use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
16 16
17type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>; 17type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>;
@@ -21,7 +21,7 @@ static LED: LedType = Mutex::new(None);
21async fn main(spawner: Spawner) { 21async fn main(spawner: Spawner) {
22 let p = embassy_rp::init(Default::default()); 22 let p = embassy_rp::init(Default::default());
23 // set the content of the global LED reference to the real LED pin 23 // set the content of the global LED reference to the real LED pin
24 let led = Output::new(AnyPin::from(p.PIN_25), Level::High); 24 let led = Output::new(p.PIN_25, Level::High);
25 // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the 25 // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the
26 // Mutex is released 26 // Mutex is released
27 { 27 {
@@ -30,8 +30,8 @@ async fn main(spawner: Spawner) {
30 let dt = 100 * 1_000_000; 30 let dt = 100 * 1_000_000;
31 let k = 1.003; 31 let k = 1.003;
32 32
33 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt)))); 33 spawner.spawn(unwrap!(toggle_led(&LED, Duration::from_nanos(dt))));
34 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64)))); 34 spawner.spawn(unwrap!(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));
35} 35}
36 36
37#[embassy_executor::task(pool_size = 2)] 37#[embassy_executor::task(pool_size = 2)]
diff --git a/examples/rp/src/bin/debounce.rs b/examples/rp/src/bin/debounce.rs
index 0077f19fc..6eeb01d0a 100644
--- a/examples/rp/src/bin/debounce.rs
+++ b/examples/rp/src/bin/debounce.rs
@@ -7,7 +7,7 @@
7use defmt::info; 7use defmt::info;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_rp::gpio::{Input, Level, Pull}; 9use embassy_rp::gpio::{Input, Level, Pull};
10use embassy_time::{with_deadline, Duration, Instant, Timer}; 10use embassy_time::{Duration, Instant, Timer, with_deadline};
11use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
12 12
13pub struct Debouncer<'a> { 13pub struct Debouncer<'a> {
diff --git a/examples/rp/src/bin/ethernet_w5500_icmp.rs b/examples/rp/src/bin/ethernet_w5500_icmp.rs
new file mode 100644
index 000000000..8c684a791
--- /dev/null
+++ b/examples/rp/src/bin/ethernet_w5500_icmp.rs
@@ -0,0 +1,143 @@
1//! This example implements an echo (ping) with an ICMP Socket and using defmt to report the results.
2//!
3//! Although there is a better way to execute pings using the child module ping of the icmp module,
4//! this example allows for other icmp messages like `Destination unreachable` to be sent aswell.
5//!
6//! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board.
7
8#![no_std]
9#![no_main]
10
11use defmt::*;
12use embassy_executor::Spawner;
13use embassy_futures::yield_now;
14use embassy_net::icmp::{ChecksumCapabilities, IcmpEndpoint, IcmpSocket, Icmpv4Packet, Icmpv4Repr, PacketMetadata};
15use embassy_net::{Stack, StackResources};
16use embassy_net_wiznet::chip::W5500;
17use embassy_net_wiznet::*;
18use embassy_rp::clocks::RoscRng;
19use embassy_rp::gpio::{Input, Level, Output, Pull};
20use embassy_rp::peripherals::SPI0;
21use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
22use embassy_time::{Delay, Instant, Timer};
23use embedded_hal_bus::spi::ExclusiveDevice;
24use static_cell::StaticCell;
25use {defmt_rtt as _, panic_probe as _};
26
27type ExclusiveSpiDevice = ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>;
28
29#[embassy_executor::task]
30async fn ethernet_task(runner: Runner<'static, W5500, ExclusiveSpiDevice, Input<'static>, Output<'static>>) -> ! {
31 runner.run().await
32}
33
34#[embassy_executor::task]
35async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
36 runner.run().await
37}
38
39#[embassy_executor::main]
40async fn main(spawner: Spawner) {
41 let p = embassy_rp::init(Default::default());
42 let mut rng = RoscRng;
43
44 let mut spi_cfg = SpiConfig::default();
45 spi_cfg.frequency = 50_000_000;
46 let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);
47 let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg);
48 let cs = Output::new(p.PIN_17, Level::High);
49 let w5500_int = Input::new(p.PIN_21, Pull::Up);
50 let w5500_reset = Output::new(p.PIN_20, Level::High);
51
52 let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];
53 static STATE: StaticCell<State<8, 8>> = StaticCell::new();
54 let state = STATE.init(State::<8, 8>::new());
55 let (device, runner) = embassy_net_wiznet::new(
56 mac_addr,
57 state,
58 ExclusiveDevice::new(spi, cs, Delay),
59 w5500_int,
60 w5500_reset,
61 )
62 .await
63 .unwrap();
64 spawner.spawn(unwrap!(ethernet_task(runner)));
65
66 // Generate random seed
67 let seed = rng.next_u64();
68
69 // Init network stack
70 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
71 let (stack, runner) = embassy_net::new(
72 device,
73 embassy_net::Config::dhcpv4(Default::default()),
74 RESOURCES.init(StackResources::new()),
75 seed,
76 );
77
78 // Launch network task
79 spawner.spawn(unwrap!(net_task(runner)));
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 // Then we can use it!
87 let mut rx_buffer = [0; 256];
88 let mut tx_buffer = [0; 256];
89 let mut rx_meta = [PacketMetadata::EMPTY];
90 let mut tx_meta = [PacketMetadata::EMPTY];
91
92 // Identifier used for the ICMP socket
93 let ident = 42;
94
95 // Create and bind the socket
96 let mut socket = IcmpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);
97 socket.bind(IcmpEndpoint::Ident(ident)).unwrap();
98
99 // Create the repr for the packet
100 let icmp_repr = Icmpv4Repr::EchoRequest {
101 ident,
102 seq_no: 0,
103 data: b"Hello, icmp!",
104 };
105
106 // Send the packet and store the starting instant to mesure latency later
107 let start = socket
108 .send_to_with(icmp_repr.buffer_len(), cfg.gateway.unwrap(), |buf| {
109 // Create and populate the packet buffer allocated by `send_to_with`
110 let mut icmp_packet = Icmpv4Packet::new_unchecked(buf);
111 icmp_repr.emit(&mut icmp_packet, &ChecksumCapabilities::default());
112 Instant::now() // Return the instant where the packet was sent
113 })
114 .await
115 .unwrap();
116
117 // Recieve and log the data of the reply
118 socket
119 .recv_from_with(|(buf, addr)| {
120 let packet = Icmpv4Packet::new_checked(buf).unwrap();
121 info!(
122 "Recieved {:?} from {} in {}ms",
123 packet.data(),
124 addr,
125 start.elapsed().as_millis()
126 );
127 })
128 .await
129 .unwrap();
130
131 loop {
132 Timer::after_secs(10).await;
133 }
134}
135
136async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
137 loop {
138 if let Some(config) = stack.config_v4() {
139 return config.clone();
140 }
141 yield_now().await;
142 }
143}
diff --git a/examples/rp/src/bin/ethernet_w5500_icmp_ping.rs b/examples/rp/src/bin/ethernet_w5500_icmp_ping.rs
new file mode 100644
index 000000000..cb667f24f
--- /dev/null
+++ b/examples/rp/src/bin/ethernet_w5500_icmp_ping.rs
@@ -0,0 +1,134 @@
1//! This example implements a LAN ping scan with the ping utilities in the icmp module of embassy-net.
2//!
3//! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board.
4
5#![no_std]
6#![no_main]
7
8use core::net::Ipv4Addr;
9use core::ops::Not;
10use core::str::FromStr;
11
12use defmt::*;
13use embassy_executor::Spawner;
14use embassy_futures::yield_now;
15use embassy_net::icmp::PacketMetadata;
16use embassy_net::icmp::ping::{PingManager, PingParams};
17use embassy_net::{Ipv4Cidr, Stack, StackResources};
18use embassy_net_wiznet::chip::W5500;
19use embassy_net_wiznet::*;
20use embassy_rp::clocks::RoscRng;
21use embassy_rp::gpio::{Input, Level, Output, Pull};
22use embassy_rp::peripherals::SPI0;
23use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
24use embassy_time::{Delay, Duration};
25use embedded_hal_bus::spi::ExclusiveDevice;
26use static_cell::StaticCell;
27use {defmt_rtt as _, panic_probe as _};
28
29type ExclusiveSpiDevice = ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, Delay>;
30
31#[embassy_executor::task]
32async fn ethernet_task(runner: Runner<'static, W5500, ExclusiveSpiDevice, Input<'static>, Output<'static>>) -> ! {
33 runner.run().await
34}
35
36#[embassy_executor::task]
37async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
38 runner.run().await
39}
40
41#[embassy_executor::main]
42async 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 static STATE: StaticCell<State<8, 8>> = StaticCell::new();
56 let state = STATE.init(State::<8, 8>::new());
57 let (device, runner) = embassy_net_wiznet::new(
58 mac_addr,
59 state,
60 ExclusiveDevice::new(spi, cs, Delay),
61 w5500_int,
62 w5500_reset,
63 )
64 .await
65 .unwrap();
66 spawner.spawn(unwrap!(ethernet_task(runner)));
67
68 // Generate random seed
69 let seed = rng.next_u64();
70
71 // Init network stack
72 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
73 let (stack, runner) = embassy_net::new(
74 device,
75 embassy_net::Config::dhcpv4(Default::default()),
76 RESOURCES.init(StackResources::new()),
77 seed,
78 );
79
80 // Launch network task
81 spawner.spawn(unwrap!(net_task(runner)));
82
83 info!("Waiting for DHCP...");
84 let cfg = wait_for_config(stack).await;
85 let local_addr = cfg.address.address();
86 info!("IP address: {:?}", local_addr);
87 let gateway = cfg.gateway.unwrap();
88 let mask = cfg.address.netmask();
89 let lower_bound = (gateway.to_bits() & mask.to_bits()) + 1;
90 let upper_bound = gateway.to_bits() | mask.to_bits().not();
91 let addr_range = lower_bound..=upper_bound;
92
93 // Then we can use it!
94 let mut rx_buffer = [0; 256];
95 let mut tx_buffer = [0; 256];
96 let mut rx_meta = [PacketMetadata::EMPTY];
97 let mut tx_meta = [PacketMetadata::EMPTY];
98
99 // Create the ping manager instance
100 let mut ping_manager = PingManager::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);
101 let addr = "192.168.8.1"; // Address to ping to
102 // Create the PingParams with the target address
103 let mut ping_params = PingParams::new(Ipv4Addr::from_str(addr).unwrap());
104 // (optional) Set custom properties of the ping
105 ping_params.set_payload(b"Hello, Ping!"); // custom payload
106 ping_params.set_count(1); // ping 1 times per ping call
107 ping_params.set_timeout(Duration::from_millis(500)); // wait .5 seconds instead of 4
108
109 info!("Online hosts in {}:", Ipv4Cidr::from_netmask(gateway, mask).unwrap());
110 let mut total_online_hosts = 0u32;
111 for addr in addr_range {
112 let ip_addr = Ipv4Addr::from_bits(addr);
113 // Set the target address in the ping params
114 ping_params.set_target(ip_addr);
115 // Execute the ping with the given parameters and wait for the reply
116 match ping_manager.ping(&ping_params).await {
117 Ok(time) => {
118 info!("{} is online\n- latency: {}ms\n", ip_addr, time.as_millis());
119 total_online_hosts += 1;
120 }
121 _ => continue,
122 }
123 }
124 info!("Ping scan complete, total online hosts: {}", total_online_hosts);
125}
126
127async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
128 loop {
129 if let Some(config) = stack.config_v4() {
130 return config.clone();
131 }
132 yield_now().await;
133 }
134}
diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs
index bd52cadca..5c049ddca 100644
--- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs
+++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs
@@ -1,6 +1,6 @@
1//! This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port. 1//! This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port.
2//! 2//!
3//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. 3//! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board.
4 4
5#![no_std] 5#![no_std]
6#![no_main] 6#![no_main]
@@ -18,7 +18,6 @@ use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
18use embassy_time::{Delay, Duration}; 18use embassy_time::{Delay, Duration};
19use embedded_hal_bus::spi::ExclusiveDevice; 19use embedded_hal_bus::spi::ExclusiveDevice;
20use embedded_io_async::Write; 20use embedded_io_async::Write;
21use rand::RngCore;
22use static_cell::StaticCell; 21use static_cell::StaticCell;
23use {defmt_rtt as _, panic_probe as _}; 22use {defmt_rtt as _, panic_probe as _};
24 23
@@ -36,8 +35,8 @@ async fn ethernet_task(
36} 35}
37 36
38#[embassy_executor::task] 37#[embassy_executor::task]
39async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { 38async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
40 stack.run().await 39 runner.run().await
41} 40}
42 41
43#[embassy_executor::main] 42#[embassy_executor::main]
@@ -63,24 +62,24 @@ async fn main(spawner: Spawner) {
63 w5500_int, 62 w5500_int,
64 w5500_reset, 63 w5500_reset,
65 ) 64 )
66 .await; 65 .await
67 unwrap!(spawner.spawn(ethernet_task(runner))); 66 .unwrap();
67 spawner.spawn(unwrap!(ethernet_task(runner)));
68 68
69 // Generate random seed 69 // Generate random seed
70 let seed = rng.next_u64(); 70 let seed = rng.next_u64();
71 71
72 // Init network stack 72 // Init network stack
73 static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new();
74 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); 73 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
75 let stack = &*STACK.init(Stack::new( 74 let (stack, runner) = embassy_net::new(
76 device, 75 device,
77 embassy_net::Config::dhcpv4(Default::default()), 76 embassy_net::Config::dhcpv4(Default::default()),
78 RESOURCES.init(StackResources::<3>::new()), 77 RESOURCES.init(StackResources::new()),
79 seed, 78 seed,
80 )); 79 );
81 80
82 // Launch network task 81 // Launch network task
83 unwrap!(spawner.spawn(net_task(&stack))); 82 spawner.spawn(unwrap!(net_task(runner)));
84 83
85 info!("Waiting for DHCP..."); 84 info!("Waiting for DHCP...");
86 let cfg = wait_for_config(stack).await; 85 let cfg = wait_for_config(stack).await;
@@ -88,12 +87,12 @@ async fn main(spawner: Spawner) {
88 info!("IP address: {:?}", local_addr); 87 info!("IP address: {:?}", local_addr);
89 88
90 // Create two sockets listening to the same port, to handle simultaneous connections 89 // Create two sockets listening to the same port, to handle simultaneous connections
91 unwrap!(spawner.spawn(listen_task(&stack, 0, 1234))); 90 spawner.spawn(unwrap!(listen_task(stack, 0, 1234)));
92 unwrap!(spawner.spawn(listen_task(&stack, 1, 1234))); 91 spawner.spawn(unwrap!(listen_task(stack, 1, 1234)));
93} 92}
94 93
95#[embassy_executor::task(pool_size = 2)] 94#[embassy_executor::task(pool_size = 2)]
96async fn listen_task(stack: &'static Stack<Device<'static>>, id: u8, port: u16) { 95async fn listen_task(stack: Stack<'static>, id: u8, port: u16) {
97 let mut rx_buffer = [0; 4096]; 96 let mut rx_buffer = [0; 4096];
98 let mut tx_buffer = [0; 4096]; 97 let mut tx_buffer = [0; 4096];
99 let mut buf = [0; 4096]; 98 let mut buf = [0; 4096];
@@ -130,7 +129,7 @@ async fn listen_task(stack: &'static Stack<Device<'static>>, id: u8, port: u16)
130 } 129 }
131} 130}
132 131
133async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { 132async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
134 loop { 133 loop {
135 if let Some(config) = stack.config_v4() { 134 if let Some(config) = stack.config_v4() {
136 return config.clone(); 135 return config.clone();
diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs
index 3e4fbd2e6..7552e4f9b 100644
--- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs
+++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs
@@ -1,6 +1,6 @@
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. 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//! 2//!
3//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. 3//! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board.
4 4
5#![no_std] 5#![no_std]
6#![no_main] 6#![no_main]
@@ -20,7 +20,6 @@ use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
20use embassy_time::{Delay, Duration, Timer}; 20use embassy_time::{Delay, Duration, Timer};
21use embedded_hal_bus::spi::ExclusiveDevice; 21use embedded_hal_bus::spi::ExclusiveDevice;
22use embedded_io_async::Write; 22use embedded_io_async::Write;
23use rand::RngCore;
24use static_cell::StaticCell; 23use static_cell::StaticCell;
25use {defmt_rtt as _, panic_probe as _}; 24use {defmt_rtt as _, panic_probe as _};
26 25
@@ -38,8 +37,8 @@ async fn ethernet_task(
38} 37}
39 38
40#[embassy_executor::task] 39#[embassy_executor::task]
41async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { 40async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
42 stack.run().await 41 runner.run().await
43} 42}
44 43
45#[embassy_executor::main] 44#[embassy_executor::main]
@@ -66,24 +65,24 @@ async fn main(spawner: Spawner) {
66 w5500_int, 65 w5500_int,
67 w5500_reset, 66 w5500_reset,
68 ) 67 )
69 .await; 68 .await
70 unwrap!(spawner.spawn(ethernet_task(runner))); 69 .unwrap();
70 spawner.spawn(unwrap!(ethernet_task(runner)));
71 71
72 // Generate random seed 72 // Generate random seed
73 let seed = rng.next_u64(); 73 let seed = rng.next_u64();
74 74
75 // Init network stack 75 // Init network stack
76 static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new(); 76 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
77 static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); 77 let (stack, runner) = embassy_net::new(
78 let stack = &*STACK.init(Stack::new(
79 device, 78 device,
80 embassy_net::Config::dhcpv4(Default::default()), 79 embassy_net::Config::dhcpv4(Default::default()),
81 RESOURCES.init(StackResources::<2>::new()), 80 RESOURCES.init(StackResources::new()),
82 seed, 81 seed,
83 )); 82 );
84 83
85 // Launch network task 84 // Launch network task
86 unwrap!(spawner.spawn(net_task(&stack))); 85 spawner.spawn(unwrap!(net_task(runner)));
87 86
88 info!("Waiting for DHCP..."); 87 info!("Waiting for DHCP...");
89 let cfg = wait_for_config(stack).await; 88 let cfg = wait_for_config(stack).await;
@@ -118,7 +117,7 @@ async fn main(spawner: Spawner) {
118 } 117 }
119} 118}
120 119
121async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { 120async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
122 loop { 121 loop {
123 if let Some(config) = stack.config_v4() { 122 if let Some(config) = stack.config_v4() {
124 return config.clone(); 123 return config.clone();
diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs
index 5532851f3..7b6fecad4 100644
--- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs
+++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs
@@ -1,7 +1,7 @@
1//! This example implements a TCP echo server on port 1234 and using DHCP. 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. 2//! Send it some data, you should see it echoed back and printed in the console.
3//! 3//!
4//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. 4//! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board.
5 5
6#![no_std] 6#![no_std]
7#![no_main] 7#![no_main]
@@ -19,7 +19,6 @@ use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
19use embassy_time::{Delay, Duration}; 19use embassy_time::{Delay, Duration};
20use embedded_hal_bus::spi::ExclusiveDevice; 20use embedded_hal_bus::spi::ExclusiveDevice;
21use embedded_io_async::Write; 21use embedded_io_async::Write;
22use rand::RngCore;
23use static_cell::StaticCell; 22use static_cell::StaticCell;
24use {defmt_rtt as _, panic_probe as _}; 23use {defmt_rtt as _, panic_probe as _};
25 24
@@ -37,8 +36,8 @@ async fn ethernet_task(
37} 36}
38 37
39#[embassy_executor::task] 38#[embassy_executor::task]
40async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { 39async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
41 stack.run().await 40 runner.run().await
42} 41}
43 42
44#[embassy_executor::main] 43#[embassy_executor::main]
@@ -65,24 +64,24 @@ async fn main(spawner: Spawner) {
65 w5500_int, 64 w5500_int,
66 w5500_reset, 65 w5500_reset,
67 ) 66 )
68 .await; 67 .await
69 unwrap!(spawner.spawn(ethernet_task(runner))); 68 .unwrap();
69 spawner.spawn(unwrap!(ethernet_task(runner)));
70 70
71 // Generate random seed 71 // Generate random seed
72 let seed = rng.next_u64(); 72 let seed = rng.next_u64();
73 73
74 // Init network stack 74 // Init network stack
75 static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new(); 75 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
76 static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); 76 let (stack, runner) = embassy_net::new(
77 let stack = &*STACK.init(Stack::new(
78 device, 77 device,
79 embassy_net::Config::dhcpv4(Default::default()), 78 embassy_net::Config::dhcpv4(Default::default()),
80 RESOURCES.init(StackResources::<2>::new()), 79 RESOURCES.init(StackResources::new()),
81 seed, 80 seed,
82 )); 81 );
83 82
84 // Launch network task 83 // Launch network task
85 unwrap!(spawner.spawn(net_task(&stack))); 84 spawner.spawn(unwrap!(net_task(runner)));
86 85
87 info!("Waiting for DHCP..."); 86 info!("Waiting for DHCP...");
88 let cfg = wait_for_config(stack).await; 87 let cfg = wait_for_config(stack).await;
@@ -127,7 +126,7 @@ async fn main(spawner: Spawner) {
127 } 126 }
128} 127}
129 128
130async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { 129async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
131 loop { 130 loop {
132 if let Some(config) = stack.config_v4() { 131 if let Some(config) = stack.config_v4() {
133 return config.clone(); 132 return config.clone();
diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs
index adb1d8941..f099490f5 100644
--- a/examples/rp/src/bin/ethernet_w5500_udp.rs
+++ b/examples/rp/src/bin/ethernet_w5500_udp.rs
@@ -1,6 +1,6 @@
1//! This example implements a UDP server listening on port 1234 and echoing back the data. 1//! This example implements a UDP server listening on port 1234 and echoing back the data.
2//! 2//!
3//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. 3//! Example written for the [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico) board.
4 4
5#![no_std] 5#![no_std]
6#![no_main] 6#![no_main]
@@ -18,7 +18,6 @@ use embassy_rp::peripherals::SPI0;
18use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; 18use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
19use embassy_time::Delay; 19use embassy_time::Delay;
20use embedded_hal_bus::spi::ExclusiveDevice; 20use embedded_hal_bus::spi::ExclusiveDevice;
21use rand::RngCore;
22use static_cell::StaticCell; 21use static_cell::StaticCell;
23use {defmt_rtt as _, panic_probe as _}; 22use {defmt_rtt as _, panic_probe as _};
24 23
@@ -36,8 +35,8 @@ async fn ethernet_task(
36} 35}
37 36
38#[embassy_executor::task] 37#[embassy_executor::task]
39async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { 38async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
40 stack.run().await 39 runner.run().await
41} 40}
42 41
43#[embassy_executor::main] 42#[embassy_executor::main]
@@ -63,24 +62,24 @@ async fn main(spawner: Spawner) {
63 w5500_int, 62 w5500_int,
64 w5500_reset, 63 w5500_reset,
65 ) 64 )
66 .await; 65 .await
67 unwrap!(spawner.spawn(ethernet_task(runner))); 66 .unwrap();
67 spawner.spawn(unwrap!(ethernet_task(runner)));
68 68
69 // Generate random seed 69 // Generate random seed
70 let seed = rng.next_u64(); 70 let seed = rng.next_u64();
71 71
72 // Init network stack 72 // Init network stack
73 static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new(); 73 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
74 static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); 74 let (stack, runner) = embassy_net::new(
75 let stack = &*STACK.init(Stack::new(
76 device, 75 device,
77 embassy_net::Config::dhcpv4(Default::default()), 76 embassy_net::Config::dhcpv4(Default::default()),
78 RESOURCES.init(StackResources::<2>::new()), 77 RESOURCES.init(StackResources::new()),
79 seed, 78 seed,
80 )); 79 );
81 80
82 // Launch network task 81 // Launch network task
83 unwrap!(spawner.spawn(net_task(&stack))); 82 spawner.spawn(unwrap!(net_task(runner)));
84 83
85 info!("Waiting for DHCP..."); 84 info!("Waiting for DHCP...");
86 let cfg = wait_for_config(stack).await; 85 let cfg = wait_for_config(stack).await;
@@ -107,7 +106,7 @@ async fn main(spawner: Spawner) {
107 } 106 }
108} 107}
109 108
110async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { 109async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
111 loop { 110 loop {
112 if let Some(config) = stack.config_v4() { 111 if let Some(config) = stack.config_v4() {
113 return config.clone(); 112 return config.clone();
diff --git a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs
new file mode 100644
index 000000000..b402029b5
--- /dev/null
+++ b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs
@@ -0,0 +1,155 @@
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 W55RP20-EVB-Pico`](https://docs.wiznet.io/Product/ioNIC/W55RP20/w55rp20-evb-pico) board.
5//! Note: the W55RP20 is a single package that contains both a RP2040 and the Wiznet W5500 ethernet
6//! controller
7
8#![no_std]
9#![no_main]
10
11use defmt::*;
12use embassy_executor::Spawner;
13use embassy_futures::yield_now;
14use embassy_net::{Stack, StackResources};
15use embassy_net_wiznet::chip::W5500;
16use embassy_net_wiznet::*;
17use embassy_rp::clocks::RoscRng;
18use embassy_rp::gpio::{Input, Level, Output, Pull};
19use embassy_rp::peripherals::PIO0;
20use embassy_rp::pio_programs::spi::Spi;
21use embassy_rp::spi::{Async, Config as SpiConfig};
22use embassy_rp::{bind_interrupts, pio};
23use embassy_time::{Delay, Duration};
24use embedded_hal_bus::spi::ExclusiveDevice;
25use embedded_io_async::Write;
26use static_cell::StaticCell;
27use {defmt_rtt as _, panic_probe as _};
28
29bind_interrupts!(struct Irqs {
30 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
31});
32
33#[embassy_executor::task]
34async fn ethernet_task(
35 runner: Runner<
36 'static,
37 W5500,
38 ExclusiveDevice<Spi<'static, PIO0, 0, Async>, Output<'static>, Delay>,
39 Input<'static>,
40 Output<'static>,
41 >,
42) -> ! {
43 runner.run().await
44}
45
46#[embassy_executor::task]
47async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
48 runner.run().await
49}
50
51#[embassy_executor::main]
52async fn main(spawner: Spawner) {
53 let p = embassy_rp::init(Default::default());
54 let mut rng = RoscRng;
55 let mut led = Output::new(p.PIN_19, Level::Low);
56
57 // The W55RP20 uses a PIO unit for SPI communication, once the SPI bus has been formed using a
58 // PIO statemachine everything else is generally unchanged from the other examples that use the W5500
59 let mosi = p.PIN_23;
60 let miso = p.PIN_22;
61 let clk = p.PIN_21;
62
63 let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs);
64
65 // Construct an SPI driver backed by a PIO state machine
66 let mut spi_cfg = SpiConfig::default();
67 spi_cfg.frequency = 12_500_000; // The PIO SPI program is much less stable than the actual SPI
68 // peripheral, use higher speeds at your peril
69 let spi = Spi::new(&mut common, sm0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg);
70
71 // Further control pins
72 let cs = Output::new(p.PIN_20, Level::High);
73 let w5500_int = Input::new(p.PIN_24, Pull::Up);
74 let w5500_reset = Output::new(p.PIN_25, Level::High);
75
76 let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];
77 static STATE: StaticCell<State<8, 8>> = StaticCell::new();
78 let state = STATE.init(State::<8, 8>::new());
79 let (device, runner) = embassy_net_wiznet::new(
80 mac_addr,
81 state,
82 ExclusiveDevice::new(spi, cs, Delay),
83 w5500_int,
84 w5500_reset,
85 )
86 .await
87 .unwrap();
88 spawner.spawn(unwrap!(ethernet_task(runner)));
89
90 // Generate random seed
91 let seed = rng.next_u64();
92
93 // Init network stack
94 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
95 let (stack, runner) = embassy_net::new(
96 device,
97 embassy_net::Config::dhcpv4(Default::default()),
98 RESOURCES.init(StackResources::new()),
99 seed,
100 );
101
102 // Launch network task
103 spawner.spawn(unwrap!(net_task(runner)));
104
105 info!("Waiting for DHCP...");
106 let cfg = wait_for_config(stack).await;
107 let local_addr = cfg.address.address();
108 info!("IP address: {:?}", local_addr);
109
110 let mut rx_buffer = [0; 4096];
111 let mut tx_buffer = [0; 4096];
112 let mut buf = [0; 4096];
113 loop {
114 let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
115 socket.set_timeout(Some(Duration::from_secs(10)));
116
117 led.set_low();
118 info!("Listening on TCP:1234...");
119 if let Err(e) = socket.accept(1234).await {
120 warn!("accept error: {:?}", e);
121 continue;
122 }
123 info!("Received connection from {:?}", socket.remote_endpoint());
124 led.set_high();
125
126 loop {
127 let n = match socket.read(&mut buf).await {
128 Ok(0) => {
129 warn!("read EOF");
130 break;
131 }
132 Ok(n) => n,
133 Err(e) => {
134 warn!("{:?}", e);
135 break;
136 }
137 };
138 info!("rxd {}", core::str::from_utf8(&buf[..n]).unwrap());
139
140 if let Err(e) = socket.write_all(&buf[..n]).await {
141 warn!("write error: {:?}", e);
142 break;
143 }
144 }
145 }
146}
147
148async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
149 loop {
150 if let Some(config) = stack.config_v4() {
151 return config.clone();
152 }
153 yield_now().await;
154 }
155}
diff --git a/examples/rp/src/bin/i2c_async_embassy.rs b/examples/rp/src/bin/i2c_async_embassy.rs
new file mode 100644
index 000000000..a65b71b9f
--- /dev/null
+++ b/examples/rp/src/bin/i2c_async_embassy.rs
@@ -0,0 +1,85 @@
1//! This example shows how to communicate asynchronous using i2c with external chip.
2//!
3//! It's using embassy's functions directly instead of traits from embedded_hal_async::i2c::I2c.
4//! While most of i2c devices are addressed using 7 bits, an extension allows 10 bits too.
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_rp::i2c::InterruptHandler;
11use {defmt_rtt as _, panic_probe as _};
12
13// Our anonymous hypotetical temperature sensor could be:
14// a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C
15// It requires no configuration or calibration, works with all i2c bus speeds,
16// never stretches clock or does anything complicated. Replies with one u16.
17// It requires only one write to take it out of suspend mode, and stays on.
18// Often result would be just on 12 bits, but here we'll simplify it to 16.
19
20enum UncomplicatedSensorId {
21 A(UncomplicatedSensorU8),
22 B(UncomplicatedSensorU16),
23}
24enum UncomplicatedSensorU8 {
25 First = 0x48,
26}
27enum UncomplicatedSensorU16 {
28 Other = 0x0049,
29}
30
31impl Into<u16> for UncomplicatedSensorU16 {
32 fn into(self) -> u16 {
33 self as u16
34 }
35}
36impl Into<u16> for UncomplicatedSensorU8 {
37 fn into(self) -> u16 {
38 0x48
39 }
40}
41impl From<UncomplicatedSensorId> for u16 {
42 fn from(t: UncomplicatedSensorId) -> Self {
43 match t {
44 UncomplicatedSensorId::A(x) => x.into(),
45 UncomplicatedSensorId::B(x) => x.into(),
46 }
47 }
48}
49
50embassy_rp::bind_interrupts!(struct Irqs {
51 I2C1_IRQ => InterruptHandler<embassy_rp::peripherals::I2C1>;
52});
53
54#[embassy_executor::main]
55async fn main(_task_spawner: embassy_executor::Spawner) {
56 let p = embassy_rp::init(Default::default());
57 let sda = p.PIN_14;
58 let scl = p.PIN_15;
59 let config = embassy_rp::i2c::Config::default();
60 let mut bus = embassy_rp::i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, config);
61
62 const WAKEYWAKEY: u16 = 0xBABE;
63 let mut result: [u8; 2] = [0, 0];
64 // wait for sensors to initialize
65 embassy_time::Timer::after(embassy_time::Duration::from_millis(100)).await;
66
67 let _res_1 = bus
68 .write_async(UncomplicatedSensorU8::First, WAKEYWAKEY.to_be_bytes())
69 .await;
70 let _res_2 = bus
71 .write_async(UncomplicatedSensorU16::Other, WAKEYWAKEY.to_be_bytes())
72 .await;
73
74 loop {
75 let s1 = UncomplicatedSensorId::A(UncomplicatedSensorU8::First);
76 let s2 = UncomplicatedSensorId::B(UncomplicatedSensorU16::Other);
77 let sensors = [s1, s2];
78 for sensor in sensors {
79 if bus.read_async(sensor, &mut result).await.is_ok() {
80 info!("Result {}", u16::from_be_bytes(result.into()));
81 }
82 }
83 embassy_time::Timer::after(embassy_time::Duration::from_millis(200)).await;
84 }
85}
diff --git a/examples/rp/src/bin/i2c_blocking.rs b/examples/rp/src/bin/i2c_blocking.rs
index c9c8a2760..317921374 100644
--- a/examples/rp/src/bin/i2c_blocking.rs
+++ b/examples/rp/src/bin/i2c_blocking.rs
@@ -49,7 +49,9 @@ async fn main(_spawner: Spawner) {
49 let scl = p.PIN_15; 49 let scl = p.PIN_15;
50 50
51 info!("set up i2c "); 51 info!("set up i2c ");
52 let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default()); 52 // Default I2C config enables internal pull-up resistors.
53 let config = Config::default();
54 let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, config);
53 55
54 use mcp23017::*; 56 use mcp23017::*;
55 57
diff --git a/examples/rp/src/bin/i2c_slave.rs b/examples/rp/src/bin/i2c_slave.rs
index 9fffb4646..e2b8b0d06 100644
--- a/examples/rp/src/bin/i2c_slave.rs
+++ b/examples/rp/src/bin/i2c_slave.rs
@@ -99,19 +99,19 @@ async fn main(spawner: Spawner) {
99 let p = embassy_rp::init(Default::default()); 99 let p = embassy_rp::init(Default::default());
100 info!("Hello World!"); 100 info!("Hello World!");
101 101
102 let d_sda = p.PIN_3; 102 let d_sda = p.PIN_2;
103 let d_scl = p.PIN_2; 103 let d_scl = p.PIN_3;
104 let mut config = i2c_slave::Config::default(); 104 let mut config = i2c_slave::Config::default();
105 config.addr = DEV_ADDR as u16; 105 config.addr = DEV_ADDR as u16;
106 let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config); 106 let device = i2c_slave::I2cSlave::new(p.I2C1, d_scl, d_sda, Irqs, config);
107 107
108 unwrap!(spawner.spawn(device_task(device))); 108 spawner.spawn(unwrap!(device_task(device)));
109 109
110 let c_sda = p.PIN_1; 110 let c_sda = p.PIN_0;
111 let c_scl = p.PIN_0; 111 let c_scl = p.PIN_1;
112 let mut config = i2c::Config::default(); 112 let mut config = i2c::Config::default();
113 config.frequency = 1_000_000; 113 config.frequency = 1_000_000;
114 let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config); 114 let controller = i2c::I2c::new_async(p.I2C0, c_scl, c_sda, Irqs, config);
115 115
116 unwrap!(spawner.spawn(controller_task(controller))); 116 spawner.spawn(unwrap!(controller_task(controller)));
117} 117}
diff --git a/examples/rp/src/bin/interrupt.rs b/examples/rp/src/bin/interrupt.rs
index 5b9d7027e..2605622ab 100644
--- a/examples/rp/src/bin/interrupt.rs
+++ b/examples/rp/src/bin/interrupt.rs
@@ -16,8 +16,8 @@ use embassy_rp::adc::{self, Adc, Blocking};
16use embassy_rp::gpio::Pull; 16use embassy_rp::gpio::Pull;
17use embassy_rp::interrupt; 17use embassy_rp::interrupt;
18use embassy_rp::pwm::{Config, Pwm}; 18use embassy_rp::pwm::{Config, Pwm};
19use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
20use embassy_sync::blocking_mutex::Mutex; 19use embassy_sync::blocking_mutex::Mutex;
20use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
21use embassy_sync::channel::Channel; 21use embassy_sync::channel::Channel;
22use embassy_time::{Duration, Ticker}; 22use embassy_time::{Duration, Ticker};
23use portable_atomic::{AtomicU32, Ordering}; 23use portable_atomic::{AtomicU32, Ordering};
@@ -32,7 +32,6 @@ static ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new();
32 32
33#[embassy_executor::main] 33#[embassy_executor::main]
34async fn main(spawner: Spawner) { 34async fn main(spawner: Spawner) {
35 embassy_rp::pac::SIO.spinlock(31).write_value(1);
36 let p = embassy_rp::init(Default::default()); 35 let p = embassy_rp::init(Default::default());
37 36
38 let adc = Adc::new_blocking(p.ADC, Default::default()); 37 let adc = Adc::new_blocking(p.ADC, Default::default());
@@ -52,7 +51,7 @@ async fn main(spawner: Spawner) {
52 // No Mutex needed when sharing within the same executor/prio level 51 // No Mutex needed when sharing within the same executor/prio level
53 static AVG: StaticCell<Cell<u32>> = StaticCell::new(); 52 static AVG: StaticCell<Cell<u32>> = StaticCell::new();
54 let avg = AVG.init(Default::default()); 53 let avg = AVG.init(Default::default());
55 spawner.must_spawn(processing(avg)); 54 spawner.spawn(processing(avg).unwrap());
56 55
57 let mut ticker = Ticker::every(Duration::from_secs(1)); 56 let mut ticker = Ticker::every(Duration::from_secs(1));
58 loop { 57 loop {
diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs
index 7cb546c91..d289f8020 100644
--- a/examples/rp/src/bin/multicore.rs
+++ b/examples/rp/src/bin/multicore.rs
@@ -8,7 +8,7 @@
8use defmt::*; 8use defmt::*;
9use embassy_executor::Executor; 9use embassy_executor::Executor;
10use embassy_rp::gpio::{Level, Output}; 10use embassy_rp::gpio::{Level, Output};
11use embassy_rp::multicore::{spawn_core1, Stack}; 11use embassy_rp::multicore::{Stack, spawn_core1};
12use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 12use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
13use embassy_sync::channel::Channel; 13use embassy_sync::channel::Channel;
14use embassy_time::Timer; 14use embassy_time::Timer;
@@ -35,12 +35,12 @@ fn main() -> ! {
35 unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) }, 35 unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },
36 move || { 36 move || {
37 let executor1 = EXECUTOR1.init(Executor::new()); 37 let executor1 = EXECUTOR1.init(Executor::new());
38 executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); 38 executor1.run(|spawner| spawner.spawn(unwrap!(core1_task(led))));
39 }, 39 },
40 ); 40 );
41 41
42 let executor0 = EXECUTOR0.init(Executor::new()); 42 let executor0 = EXECUTOR0.init(Executor::new());
43 executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); 43 executor0.run(|spawner| spawner.spawn(unwrap!(core0_task())));
44} 44}
45 45
46#[embassy_executor::task] 46#[embassy_executor::task]
diff --git a/examples/rp/src/bin/multiprio.rs b/examples/rp/src/bin/multiprio.rs
index 2b397f97d..310047505 100644
--- a/examples/rp/src/bin/multiprio.rs
+++ b/examples/rp/src/bin/multiprio.rs
@@ -61,7 +61,7 @@ use defmt::{info, unwrap};
61use embassy_executor::{Executor, InterruptExecutor}; 61use embassy_executor::{Executor, InterruptExecutor};
62use embassy_rp::interrupt; 62use embassy_rp::interrupt;
63use embassy_rp::interrupt::{InterruptExt, Priority}; 63use embassy_rp::interrupt::{InterruptExt, Priority};
64use embassy_time::{Instant, Timer, TICK_HZ}; 64use embassy_time::{Instant, TICK_HZ, Timer};
65use static_cell::StaticCell; 65use static_cell::StaticCell;
66use {defmt_rtt as _, panic_probe as _}; 66use {defmt_rtt as _, panic_probe as _};
67 67
@@ -113,12 +113,12 @@ static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
113 113
114#[interrupt] 114#[interrupt]
115unsafe fn SWI_IRQ_1() { 115unsafe fn SWI_IRQ_1() {
116 EXECUTOR_HIGH.on_interrupt() 116 unsafe { EXECUTOR_HIGH.on_interrupt() }
117} 117}
118 118
119#[interrupt] 119#[interrupt]
120unsafe fn SWI_IRQ_0() { 120unsafe fn SWI_IRQ_0() {
121 EXECUTOR_MED.on_interrupt() 121 unsafe { EXECUTOR_MED.on_interrupt() }
122} 122}
123 123
124#[entry] 124#[entry]
@@ -130,16 +130,16 @@ fn main() -> ! {
130 // High-priority executor: SWI_IRQ_1, priority level 2 130 // High-priority executor: SWI_IRQ_1, priority level 2
131 interrupt::SWI_IRQ_1.set_priority(Priority::P2); 131 interrupt::SWI_IRQ_1.set_priority(Priority::P2);
132 let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1); 132 let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1);
133 unwrap!(spawner.spawn(run_high())); 133 spawner.spawn(unwrap!(run_high()));
134 134
135 // Medium-priority executor: SWI_IRQ_0, priority level 3 135 // Medium-priority executor: SWI_IRQ_0, priority level 3
136 interrupt::SWI_IRQ_0.set_priority(Priority::P3); 136 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
137 let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0); 137 let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0);
138 unwrap!(spawner.spawn(run_med())); 138 spawner.spawn(unwrap!(run_med()));
139 139
140 // Low priority executor: runs in thread mode, using WFE/SEV 140 // Low priority executor: runs in thread mode, using WFE/SEV
141 let executor = EXECUTOR_LOW.init(Executor::new()); 141 let executor = EXECUTOR_LOW.init(Executor::new());
142 executor.run(|spawner| { 142 executor.run(|spawner| {
143 unwrap!(spawner.spawn(run_low())); 143 spawner.spawn(unwrap!(run_low()));
144 }); 144 });
145} 145}
diff --git a/examples/rp/src/bin/orchestrate_tasks.rs b/examples/rp/src/bin/orchestrate_tasks.rs
new file mode 100644
index 000000000..cd26a5371
--- /dev/null
+++ b/examples/rp/src/bin/orchestrate_tasks.rs
@@ -0,0 +1,318 @@
1//! This example demonstrates some approaches to communicate between tasks in order to orchestrate the state of the system.
2//!
3//! The system consists of several tasks:
4//! - Three tasks that generate random numbers at different intervals (simulating i.e. sensor readings)
5//! - A task that monitors USB power connection (hardware event handling)
6//! - A task that reads system voltage (ADC sampling)
7//! - A consumer task that processes all this information
8//!
9//! The system maintains state in a single place, wrapped in a Mutex.
10//!
11//! We demonstrate how to:
12//! - use a mutex to maintain shared state between tasks
13//! - use a channel to send events between tasks
14//! - use an orchestrator task to coordinate tasks and handle state transitions
15//! - use signals to notify about state changes and terminate tasks
16
17#![no_std]
18#![no_main]
19
20use assign_resources::assign_resources;
21use defmt::*;
22use embassy_executor::Spawner;
23use embassy_futures::select::{Either, select};
24use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
25use embassy_rp::clocks::RoscRng;
26use embassy_rp::gpio::{Input, Pull};
27use embassy_rp::{Peri, bind_interrupts, peripherals};
28use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
29use embassy_sync::mutex::Mutex;
30use embassy_sync::{channel, signal};
31use embassy_time::{Duration, Timer};
32use {defmt_rtt as _, panic_probe as _};
33
34// Hardware resource assignment. See other examples for different ways of doing this.
35assign_resources! {
36 vsys: Vsys {
37 adc: ADC,
38 pin_29: PIN_29,
39 },
40 vbus: Vbus {
41 pin_24: PIN_24,
42 },
43}
44
45// Interrupt binding - required for hardware peripherals like ADC
46bind_interrupts!(struct Irqs {
47 ADC_IRQ_FIFO => InterruptHandler;
48});
49
50/// Events that worker tasks send to the orchestrator
51enum Events {
52 UsbPowered(bool), // USB connection state changed
53 VsysVoltage(f32), // New voltage reading
54 FirstRandomSeed(u32), // Random number from 30s timer
55 SecondRandomSeed(u32), // Random number from 60s timer
56 ThirdRandomSeed(u32), // Random number from 90s timer
57 ResetFirstRandomSeed, // Signal to reset the first counter
58}
59
60/// Commands that can control task behavior.
61/// Currently only used to stop tasks, but could be extended for other controls.
62enum Commands {
63 /// Signals a task to stop execution
64 Stop,
65}
66
67/// The central state of our system, shared between tasks.
68#[derive(Clone, Format)]
69struct State {
70 usb_powered: bool,
71 vsys_voltage: f32,
72 first_random_seed: u32,
73 second_random_seed: u32,
74 third_random_seed: u32,
75 first_random_seed_task_running: bool,
76 times_we_got_first_random_seed: u8,
77 maximum_times_we_want_first_random_seed: u8,
78}
79
80/// A formatted view of the system status, used for logging. Used for the below `get_system_summary` fn.
81#[derive(Format)]
82struct SystemStatus {
83 power_source: &'static str,
84 voltage: f32,
85}
86
87impl State {
88 const fn new() -> Self {
89 Self {
90 usb_powered: false,
91 vsys_voltage: 0.0,
92 first_random_seed: 0,
93 second_random_seed: 0,
94 third_random_seed: 0,
95 first_random_seed_task_running: false,
96 times_we_got_first_random_seed: 0,
97 maximum_times_we_want_first_random_seed: 3,
98 }
99 }
100
101 /// Returns a formatted summary of power state and voltage.
102 /// Shows how to create methods that work with shared state.
103 fn get_system_summary(&self) -> SystemStatus {
104 SystemStatus {
105 power_source: if self.usb_powered {
106 "USB powered"
107 } else {
108 "Battery powered"
109 },
110 voltage: self.vsys_voltage,
111 }
112 }
113}
114
115/// The shared state protected by a mutex
116static SYSTEM_STATE: Mutex<CriticalSectionRawMutex, State> = Mutex::new(State::new());
117
118/// Channel for events from worker tasks to the orchestrator
119static EVENT_CHANNEL: channel::Channel<CriticalSectionRawMutex, Events, 10> = channel::Channel::new();
120
121/// Signal used to stop the first random number task
122static STOP_FIRST_RANDOM_SIGNAL: signal::Signal<CriticalSectionRawMutex, Commands> = signal::Signal::new();
123
124/// Signal for notifying about state changes
125static STATE_CHANGED: signal::Signal<CriticalSectionRawMutex, ()> = signal::Signal::new();
126
127#[embassy_executor::main]
128async fn main(spawner: Spawner) {
129 let p = embassy_rp::init(Default::default());
130 let r = split_resources! {p};
131
132 spawner.spawn(orchestrate(spawner).unwrap());
133 spawner.spawn(random_60s(spawner).unwrap());
134 spawner.spawn(random_90s(spawner).unwrap());
135 // `random_30s` is not spawned here, butin the orchestrate task depending on state
136 spawner.spawn(usb_power(spawner, r.vbus).unwrap());
137 spawner.spawn(vsys_voltage(spawner, r.vsys).unwrap());
138 spawner.spawn(consumer(spawner).unwrap());
139}
140
141/// Main task that processes all events and updates system state.
142#[embassy_executor::task]
143async fn orchestrate(spawner: Spawner) {
144 let receiver = EVENT_CHANNEL.receiver();
145
146 loop {
147 // Do nothing until we receive any event
148 let event = receiver.receive().await;
149
150 // Scope in which we want to lock the system state. As an alternative we could also call `drop` on the state
151 {
152 let mut state = SYSTEM_STATE.lock().await;
153
154 match event {
155 Events::UsbPowered(usb_powered) => {
156 state.usb_powered = usb_powered;
157 info!("Usb powered: {}", usb_powered);
158 info!("System summary: {}", state.get_system_summary());
159 }
160 Events::VsysVoltage(voltage) => {
161 state.vsys_voltage = voltage;
162 info!("Vsys voltage: {}", voltage);
163 }
164 Events::FirstRandomSeed(seed) => {
165 state.first_random_seed = seed;
166 state.times_we_got_first_random_seed += 1;
167 info!(
168 "First random seed: {}, and that was iteration {} of receiving this.",
169 seed, &state.times_we_got_first_random_seed
170 );
171 }
172 Events::SecondRandomSeed(seed) => {
173 state.second_random_seed = seed;
174 info!("Second random seed: {}", seed);
175 }
176 Events::ThirdRandomSeed(seed) => {
177 state.third_random_seed = seed;
178 info!("Third random seed: {}", seed);
179 }
180 Events::ResetFirstRandomSeed => {
181 state.times_we_got_first_random_seed = 0;
182 state.first_random_seed = 0;
183 info!("Resetting the first random seed counter");
184 }
185 }
186
187 // Handle task orchestration based on state
188 // Just placed as an example here, could be hooked into the event system, puton a timer, ...
189 match state.times_we_got_first_random_seed {
190 max if max == state.maximum_times_we_want_first_random_seed => {
191 info!("Stopping the first random signal task");
192 STOP_FIRST_RANDOM_SIGNAL.signal(Commands::Stop);
193 EVENT_CHANNEL.sender().send(Events::ResetFirstRandomSeed).await;
194 }
195 0 => {
196 let respawn_first_random_seed_task = !state.first_random_seed_task_running;
197 // Deliberately dropping the Mutex lock here to release it before a lengthy operation
198 drop(state);
199 if respawn_first_random_seed_task {
200 info!("(Re)-Starting the first random signal task");
201 spawner.spawn(random_30s(spawner).unwrap());
202 }
203 }
204 _ => {}
205 }
206 }
207
208 STATE_CHANGED.signal(());
209 }
210}
211
212/// Task that monitors state changes and logs system status.
213#[embassy_executor::task]
214async fn consumer(_spawner: Spawner) {
215 loop {
216 // Wait for state change notification
217 STATE_CHANGED.wait().await;
218
219 let state = SYSTEM_STATE.lock().await;
220 info!(
221 "State update - {} | Seeds - First: {} (count: {}/{}, running: {}), Second: {}, Third: {}",
222 state.get_system_summary(),
223 state.first_random_seed,
224 state.times_we_got_first_random_seed,
225 state.maximum_times_we_want_first_random_seed,
226 state.first_random_seed_task_running,
227 state.second_random_seed,
228 state.third_random_seed
229 );
230 }
231}
232
233/// Task that generates random numbers every 30 seconds until stopped.
234/// Shows how to handle both timer events and stop signals.
235/// As an example of some routine we want to be on or off depending on other needs.
236#[embassy_executor::task]
237async fn random_30s(_spawner: Spawner) {
238 {
239 let mut state = SYSTEM_STATE.lock().await;
240 state.first_random_seed_task_running = true;
241 }
242
243 let mut rng = RoscRng;
244 let sender = EVENT_CHANNEL.sender();
245
246 loop {
247 // Wait for either 30s timer or stop signal (like select() in Go)
248 match select(Timer::after(Duration::from_secs(30)), STOP_FIRST_RANDOM_SIGNAL.wait()).await {
249 Either::First(_) => {
250 info!("30s are up, generating random number");
251 let random_number = rng.next_u32();
252 sender.send(Events::FirstRandomSeed(random_number)).await;
253 }
254 Either::Second(_) => {
255 info!("Received signal to stop, goodbye!");
256
257 let mut state = SYSTEM_STATE.lock().await;
258 state.first_random_seed_task_running = false;
259
260 break;
261 }
262 }
263 }
264}
265
266/// Task that generates random numbers every 60 seconds. As an example of some routine.
267#[embassy_executor::task]
268async fn random_60s(_spawner: Spawner) {
269 let mut rng = RoscRng;
270 let sender = EVENT_CHANNEL.sender();
271
272 loop {
273 Timer::after(Duration::from_secs(60)).await;
274 let random_number = rng.next_u32();
275 sender.send(Events::SecondRandomSeed(random_number)).await;
276 }
277}
278
279/// Task that generates random numbers every 90 seconds. . As an example of some routine.
280#[embassy_executor::task]
281async fn random_90s(_spawner: Spawner) {
282 let mut rng = RoscRng;
283 let sender = EVENT_CHANNEL.sender();
284
285 loop {
286 Timer::after(Duration::from_secs(90)).await;
287 let random_number = rng.next_u32();
288 sender.send(Events::ThirdRandomSeed(random_number)).await;
289 }
290}
291
292/// Task that monitors USB power connection. As an example of some Interrupt somewhere.
293#[embassy_executor::task]
294pub async fn usb_power(_spawner: Spawner, r: Vbus) {
295 let mut vbus_in = Input::new(r.pin_24, Pull::None);
296 let sender = EVENT_CHANNEL.sender();
297
298 loop {
299 sender.send(Events::UsbPowered(vbus_in.is_high())).await;
300 vbus_in.wait_for_any_edge().await;
301 }
302}
303
304/// Task that reads system voltage through ADC. As an example of some continuous sensor reading.
305#[embassy_executor::task]
306pub async fn vsys_voltage(_spawner: Spawner, r: Vsys) {
307 let mut adc = Adc::new(r.adc, Irqs, Config::default());
308 let vsys_in = r.pin_29;
309 let mut channel = Channel::new_pin(vsys_in, Pull::None);
310 let sender = EVENT_CHANNEL.sender();
311
312 loop {
313 Timer::after(Duration::from_secs(30)).await;
314 let adc_value = adc.read(&mut channel).await.unwrap();
315 let voltage = (adc_value as f32) * 3.3 * 3.0 / 4096.0;
316 sender.send(Events::VsysVoltage(voltage)).await;
317 }
318}
diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs
new file mode 100644
index 000000000..a98185a8e
--- /dev/null
+++ b/examples/rp/src/bin/overclock.rs
@@ -0,0 +1,64 @@
1//! # Overclocking the RP2040 to 200 MHz
2//!
3//! This example demonstrates how to configure the RP2040 to run at 200 MHz.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::clocks::{ClockConfig, clk_sys_freq, core_voltage};
11use embassy_rp::config::Config;
12use embassy_rp::gpio::{Level, Output};
13use embassy_time::{Duration, Instant, Timer};
14use {defmt_rtt as _, panic_probe as _};
15
16const COUNT_TO: i64 = 10_000_000;
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) -> ! {
20 // Set up for clock frequency of 200 MHz, setting all necessary defaults.
21 let config = Config::new(ClockConfig::system_freq(200_000_000).unwrap());
22
23 // Initialize the peripherals
24 let p = embassy_rp::init(config);
25
26 // Show CPU frequency for verification
27 let sys_freq = clk_sys_freq();
28 info!("System clock frequency: {} MHz", sys_freq / 1_000_000);
29 // Show core voltage for verification
30 let core_voltage = core_voltage().unwrap();
31 info!("Core voltage: {}", core_voltage);
32
33 // LED to indicate the system is running
34 let mut led = Output::new(p.PIN_25, Level::Low);
35
36 loop {
37 // Reset the counter at the start of measurement period
38 let mut counter = 0;
39
40 // Turn LED on while counting
41 led.set_high();
42
43 let start = Instant::now();
44
45 // This is a busy loop that will take some time to complete
46 while counter < COUNT_TO {
47 counter += 1;
48 }
49
50 let elapsed = Instant::now() - start;
51
52 // Report the elapsed time
53 led.set_low();
54 info!(
55 "At {}Mhz: Elapsed time to count to {}: {}ms",
56 sys_freq / 1_000_000,
57 counter,
58 elapsed.as_millis()
59 );
60
61 // Wait 2 seconds before starting the next measurement
62 Timer::after(Duration::from_secs(2)).await;
63 }
64}
diff --git a/examples/rp/src/bin/overclock_manual.rs b/examples/rp/src/bin/overclock_manual.rs
new file mode 100644
index 000000000..18397f9a8
--- /dev/null
+++ b/examples/rp/src/bin/overclock_manual.rs
@@ -0,0 +1,81 @@
1//! # Overclocking the RP2040 to 200 MHz manually
2//!
3//! This example demonstrates how to manually configure the RP2040 to run at 200 MHz.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::clocks::{ClockConfig, CoreVoltage, PllConfig, clk_sys_freq, core_voltage};
11use embassy_rp::config::Config;
12use embassy_rp::gpio::{Level, Output};
13use embassy_time::{Duration, Instant, Timer};
14use {defmt_rtt as _, panic_probe as _};
15
16const COUNT_TO: i64 = 10_000_000;
17
18/// Configure the RP2040 for 200 MHz operation by manually specifying the PLL settings.
19fn configure_manual_overclock() -> Config {
20 // Set the PLL configuration manually, starting from default values
21 let mut config = Config::default();
22
23 // Set the system clock to 200 MHz
24 config.clocks = ClockConfig::manual_pll(
25 12_000_000, // Crystal frequency, 12 MHz is common. If using custom, set to your value.
26 PllConfig {
27 refdiv: 1, // Reference divider
28 fbdiv: 100, // Feedback divider
29 post_div1: 3, // Post divider 1
30 post_div2: 2, // Post divider 2
31 },
32 CoreVoltage::V1_15, // Core voltage, should be set to V1_15 for 200 MHz
33 );
34
35 config
36}
37
38#[embassy_executor::main]
39async fn main(_spawner: Spawner) -> ! {
40 // Initialize with our manual overclock configuration
41 let p = embassy_rp::init(configure_manual_overclock());
42
43 // Show CPU frequency for verification
44 let sys_freq = clk_sys_freq();
45 info!("System clock frequency: {} MHz", sys_freq / 1_000_000);
46 // Show core voltage for verification
47 let core_voltage = core_voltage().unwrap();
48 info!("Core voltage: {}", core_voltage);
49
50 // LED to indicate the system is running
51 let mut led = Output::new(p.PIN_25, Level::Low);
52
53 loop {
54 // Reset the counter at the start of measurement period
55 let mut counter = 0;
56
57 // Turn LED on while counting
58 led.set_high();
59
60 let start = Instant::now();
61
62 // This is a busy loop that will take some time to complete
63 while counter < COUNT_TO {
64 counter += 1;
65 }
66
67 let elapsed = Instant::now() - start;
68
69 // Report the elapsed time
70 led.set_low();
71 info!(
72 "At {}Mhz: Elapsed time to count to {}: {}ms",
73 sys_freq / 1_000_000,
74 counter,
75 elapsed.as_millis()
76 );
77
78 // Wait 2 seconds before starting the next measurement
79 Timer::after(Duration::from_secs(2)).await;
80 }
81}
diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs
index ee248591b..55e983c36 100644
--- a/examples/rp/src/bin/pio_async.rs
+++ b/examples/rp/src/bin/pio_async.rs
@@ -4,9 +4,10 @@
4#![no_main] 4#![no_main]
5use defmt::info; 5use defmt::info;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_rp::bind_interrupts;
8use embassy_rp::peripherals::PIO0; 7use embassy_rp::peripherals::PIO0;
8use embassy_rp::pio::program::pio_asm;
9use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; 9use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};
10use embassy_rp::{Peri, bind_interrupts};
10use fixed::traits::ToFixed; 11use fixed::traits::ToFixed;
11use fixed_macro::types::U56F8; 12use fixed_macro::types::U56F8;
12use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
@@ -15,11 +16,11 @@ bind_interrupts!(struct Irqs {
15 PIO0_IRQ_0 => InterruptHandler<PIO0>; 16 PIO0_IRQ_0 => InterruptHandler<PIO0>;
16}); 17});
17 18
18fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { 19fn setup_pio_task_sm0<'d>(pio: &mut Common<'d, PIO0>, sm: &mut StateMachine<'d, PIO0, 0>, pin: Peri<'d, impl PioPin>) {
19 // Setup sm0 20 // Setup sm0
20 21
21 // Send data serially to pin 22 // Send data serially to pin
22 let prg = pio_proc::pio_asm!( 23 let prg = pio_asm!(
23 ".origin 16", 24 ".origin 16",
24 "set pindirs, 1", 25 "set pindirs, 1",
25 ".wrap_target", 26 ".wrap_target",
@@ -49,11 +50,11 @@ async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) {
49 } 50 }
50} 51}
51 52
52fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) { 53fn setup_pio_task_sm1<'d>(pio: &mut Common<'d, PIO0>, sm: &mut StateMachine<'d, PIO0, 1>) {
53 // Setupm sm1 54 // Setupm sm1
54 55
55 // Read 0b10101 repeatedly until ISR is full 56 // Read 0b10101 repeatedly until ISR is full
56 let prg = pio_proc::pio_asm!( 57 let prg = pio_asm!(
57 // 58 //
58 ".origin 8", 59 ".origin 8",
59 "set x, 0x15", 60 "set x, 0x15",
@@ -79,11 +80,11 @@ async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) {
79 } 80 }
80} 81}
81 82
82fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) { 83fn setup_pio_task_sm2<'d>(pio: &mut Common<'d, PIO0>, sm: &mut StateMachine<'d, PIO0, 2>) {
83 // Setup sm2 84 // Setup sm2
84 85
85 // Repeatedly trigger IRQ 3 86 // Repeatedly trigger IRQ 3
86 let prg = pio_proc::pio_asm!( 87 let prg = pio_asm!(
87 ".origin 0", 88 ".origin 0",
88 ".wrap_target", 89 ".wrap_target",
89 "set x,10", 90 "set x,10",
@@ -124,7 +125,7 @@ async fn main(spawner: Spawner) {
124 setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0); 125 setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0);
125 setup_pio_task_sm1(&mut common, &mut sm1); 126 setup_pio_task_sm1(&mut common, &mut sm1);
126 setup_pio_task_sm2(&mut common, &mut sm2); 127 setup_pio_task_sm2(&mut common, &mut sm2);
127 spawner.spawn(pio_task_sm0(sm0)).unwrap(); 128 spawner.spawn(pio_task_sm0(sm0).unwrap());
128 spawner.spawn(pio_task_sm1(sm1)).unwrap(); 129 spawner.spawn(pio_task_sm1(sm1).unwrap());
129 spawner.spawn(pio_task_sm2(irq3, sm2)).unwrap(); 130 spawner.spawn(pio_task_sm2(irq3, sm2).unwrap());
130} 131}
diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs
index 02700269c..64d603ba4 100644
--- a/examples/rp/src/bin/pio_dma.rs
+++ b/examples/rp/src/bin/pio_dma.rs
@@ -5,9 +5,10 @@
5use defmt::info; 5use defmt::info;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_futures::join::join; 7use embassy_futures::join::join;
8use embassy_rp::bind_interrupts;
8use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::program::pio_asm;
9use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; 11use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
10use embassy_rp::{bind_interrupts, Peripheral};
11use fixed::traits::ToFixed; 12use fixed::traits::ToFixed;
12use fixed_macro::types::U56F8; 13use fixed_macro::types::U56F8;
13use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
@@ -32,7 +33,7 @@ async fn main(_spawner: Spawner) {
32 .. 33 ..
33 } = Pio::new(pio, Irqs); 34 } = Pio::new(pio, Irqs);
34 35
35 let prg = pio_proc::pio_asm!( 36 let prg = pio_asm!(
36 ".origin 0", 37 ".origin 0",
37 "set pindirs,1", 38 "set pindirs,1",
38 ".wrap_target", 39 ".wrap_target",
@@ -61,8 +62,8 @@ async fn main(_spawner: Spawner) {
61 sm.set_config(&cfg); 62 sm.set_config(&cfg);
62 sm.set_enable(true); 63 sm.set_enable(true);
63 64
64 let mut dma_out_ref = p.DMA_CH0.into_ref(); 65 let mut dma_out_ref = p.DMA_CH0;
65 let mut dma_in_ref = p.DMA_CH1.into_ref(); 66 let mut dma_in_ref = p.DMA_CH1;
66 let mut dout = [0x12345678u32; 29]; 67 let mut dout = [0x12345678u32; 29];
67 for i in 1..dout.len() { 68 for i in 1..dout.len() {
68 dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7; 69 dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7;
@@ -71,8 +72,8 @@ async fn main(_spawner: Spawner) {
71 loop { 72 loop {
72 let (rx, tx) = sm.rx_tx(); 73 let (rx, tx) = sm.rx_tx();
73 join( 74 join(
74 tx.dma_push(dma_out_ref.reborrow(), &dout), 75 tx.dma_push(dma_out_ref.reborrow(), &dout, false),
75 rx.dma_pull(dma_in_ref.reborrow(), &mut din), 76 rx.dma_pull(dma_in_ref.reborrow(), &mut din, false),
76 ) 77 )
77 .await; 78 .await;
78 for i in 0..din.len() { 79 for i in 0..din.len() {
diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs
index 6c02630e0..164e6f8d3 100644
--- a/examples/rp/src/bin/pio_hd44780.rs
+++ b/examples/rp/src/bin/pio_hd44780.rs
@@ -7,13 +7,11 @@
7use core::fmt::Write; 7use core::fmt::Write;
8 8
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_rp::dma::{AnyChannel, Channel}; 10use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::PIO0; 11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{ 12use embassy_rp::pio::{InterruptHandler, Pio};
13 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 13use embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram};
14};
15use embassy_rp::pwm::{self, Pwm}; 14use embassy_rp::pwm::{self, Pwm};
16use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
17use embassy_time::{Instant, Timer}; 15use embassy_time::{Instant, Timer};
18use {defmt_rtt as _, panic_probe as _}; 16use {defmt_rtt as _, panic_probe as _};
19 17
@@ -43,8 +41,27 @@ async fn main(_spawner: Spawner) {
43 c 41 c
44 }); 42 });
45 43
46 let mut hd = HD44780::new( 44 let Pio {
47 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, 45 mut common, sm0, irq0, ..
46 } = Pio::new(p.PIO0, Irqs);
47
48 let word_prg = PioHD44780CommandWordProgram::new(&mut common);
49 let seq_prg = PioHD44780CommandSequenceProgram::new(&mut common);
50
51 let mut hd = PioHD44780::new(
52 &mut common,
53 sm0,
54 irq0,
55 p.DMA_CH3,
56 p.PIN_0,
57 p.PIN_1,
58 p.PIN_2,
59 p.PIN_3,
60 p.PIN_4,
61 p.PIN_5,
62 p.PIN_6,
63 &word_prg,
64 &seq_prg,
48 ) 65 )
49 .await; 66 .await;
50 67
@@ -68,173 +85,3 @@ async fn main(_spawner: Spawner) {
68 Timer::after_secs(1).await; 85 Timer::after_secs(1).await;
69 } 86 }
70} 87}
71
72pub struct HD44780<'l> {
73 dma: PeripheralRef<'l, AnyChannel>,
74 sm: StateMachine<'l, PIO0, 0>,
75
76 buf: [u8; 40],
77}
78
79impl<'l> HD44780<'l> {
80 pub async fn new(
81 pio: impl Peripheral<P = PIO0> + 'l,
82 irq: Irqs,
83 dma: impl Peripheral<P = impl Channel> + 'l,
84 rs: impl PioPin,
85 rw: impl PioPin,
86 e: impl PioPin,
87 db4: impl PioPin,
88 db5: impl PioPin,
89 db6: impl PioPin,
90 db7: impl PioPin,
91 ) -> HD44780<'l> {
92 into_ref!(dma);
93
94 let Pio {
95 mut common,
96 mut irq0,
97 mut sm0,
98 ..
99 } = Pio::new(pio, irq);
100
101 // takes command words (<wait:24> <command:4> <0:4>)
102 let prg = pio_proc::pio_asm!(
103 r#"
104 .side_set 1 opt
105 .origin 20
106
107 loop:
108 out x, 24
109 delay:
110 jmp x--, delay
111 out pins, 4 side 1
112 out null, 4 side 0
113 jmp !osre, loop
114 irq 0
115 "#,
116 );
117
118 let rs = common.make_pio_pin(rs);
119 let rw = common.make_pio_pin(rw);
120 let e = common.make_pio_pin(e);
121 let db4 = common.make_pio_pin(db4);
122 let db5 = common.make_pio_pin(db5);
123 let db6 = common.make_pio_pin(db6);
124 let db7 = common.make_pio_pin(db7);
125
126 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
127
128 let mut cfg = Config::default();
129 cfg.use_program(&common.load_program(&prg.program), &[&e]);
130 cfg.clock_divider = 125u8.into();
131 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
132 cfg.shift_out = ShiftConfig {
133 auto_fill: true,
134 direction: ShiftDirection::Left,
135 threshold: 32,
136 };
137 cfg.fifo_join = FifoJoin::TxOnly;
138 sm0.set_config(&cfg);
139
140 sm0.set_enable(true);
141 // init to 8 bit thrice
142 sm0.tx().push((50000 << 8) | 0x30);
143 sm0.tx().push((5000 << 8) | 0x30);
144 sm0.tx().push((200 << 8) | 0x30);
145 // init 4 bit
146 sm0.tx().push((200 << 8) | 0x20);
147 // set font and lines
148 sm0.tx().push((50 << 8) | 0x20);
149 sm0.tx().push(0b1100_0000);
150
151 irq0.wait().await;
152 sm0.set_enable(false);
153
154 // takes command sequences (<rs:1> <count:7>, data...)
155 // many side sets are only there to free up a delay bit!
156 let prg = pio_proc::pio_asm!(
157 r#"
158 .origin 27
159 .side_set 1
160
161 .wrap_target
162 pull side 0
163 out x 1 side 0 ; !rs
164 out y 7 side 0 ; #data - 1
165
166 ; rs/rw to e: >= 60ns
167 ; e high time: >= 500ns
168 ; e low time: >= 500ns
169 ; read data valid after e falling: ~5ns
170 ; write data hold after e falling: ~10ns
171
172 loop:
173 pull side 0
174 jmp !x data side 0
175 command:
176 set pins 0b00 side 0
177 jmp shift side 0
178 data:
179 set pins 0b01 side 0
180 shift:
181 out pins 4 side 1 [9]
182 nop side 0 [9]
183 out pins 4 side 1 [9]
184 mov osr null side 0 [7]
185 out pindirs 4 side 0
186 set pins 0b10 side 0
187 busy:
188 nop side 1 [9]
189 jmp pin more side 0 [9]
190 mov osr ~osr side 1 [9]
191 nop side 0 [4]
192 out pindirs 4 side 0
193 jmp y-- loop side 0
194 .wrap
195 more:
196 nop side 1 [9]
197 jmp busy side 0 [9]
198 "#
199 );
200
201 let mut cfg = Config::default();
202 cfg.use_program(&common.load_program(&prg.program), &[&e]);
203 cfg.clock_divider = 8u8.into(); // ~64ns/insn
204 cfg.set_jmp_pin(&db7);
205 cfg.set_set_pins(&[&rs, &rw]);
206 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
207 cfg.shift_out.direction = ShiftDirection::Left;
208 cfg.fifo_join = FifoJoin::TxOnly;
209 sm0.set_config(&cfg);
210
211 sm0.set_enable(true);
212
213 // display on and cursor on and blinking, reset display
214 sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
215
216 Self {
217 dma: dma.map_into(),
218 sm: sm0,
219 buf: [0x20; 40],
220 }
221 }
222
223 pub async fn add_line(&mut self, s: &[u8]) {
224 // move cursor to 0:0, prepare 16 characters
225 self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);
226 // move line 2 up
227 self.buf.copy_within(22..38, 3);
228 // move cursor to 1:0, prepare 16 characters
229 self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);
230 // file line 2 with spaces
231 self.buf[22..38].fill(0x20);
232 // copy input line
233 let len = s.len().min(16);
234 self.buf[22..22 + len].copy_from_slice(&s[0..len]);
235 // set cursor to 1:15
236 self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
237
238 self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await;
239 }
240}
diff --git a/examples/rp/src/bin/pio_i2s.rs b/examples/rp/src/bin/pio_i2s.rs
index cf60e5b30..695a74cc3 100644
--- a/examples/rp/src/bin/pio_i2s.rs
+++ b/examples/rp/src/bin/pio_i2s.rs
@@ -13,10 +13,11 @@
13use core::mem; 13use core::mem;
14 14
15use embassy_executor::Spawner; 15use embassy_executor::Spawner;
16use embassy_rp::bind_interrupts;
17use embassy_rp::bootsel::is_bootsel_pressed;
16use embassy_rp::peripherals::PIO0; 18use embassy_rp::peripherals::PIO0;
17use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; 19use embassy_rp::pio::{InterruptHandler, Pio};
18use embassy_rp::{bind_interrupts, Peripheral}; 20use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram};
19use fixed::traits::ToFixed;
20use static_cell::StaticCell; 21use static_cell::StaticCell;
21use {defmt_rtt as _, panic_probe as _}; 22use {defmt_rtt as _, panic_probe as _};
22 23
@@ -25,61 +26,30 @@ bind_interrupts!(struct Irqs {
25}); 26});
26 27
27const SAMPLE_RATE: u32 = 48_000; 28const SAMPLE_RATE: u32 = 48_000;
29const BIT_DEPTH: u32 = 16;
28 30
29#[embassy_executor::main] 31#[embassy_executor::main]
30async fn main(_spawner: Spawner) { 32async fn main(_spawner: Spawner) {
31 let mut p = embassy_rp::init(Default::default()); 33 let mut p = embassy_rp::init(Default::default());
32 34
33 // Setup pio state machine for i2s output 35 // Setup pio state machine for i2s output
34 let mut pio = Pio::new(p.PIO0, Irqs); 36 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
35
36 #[rustfmt::skip]
37 let pio_program = pio_proc::pio_asm!(
38 ".side_set 2",
39 " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock
40 "left_data:",
41 " out pins, 1 side 0b00",
42 " jmp x-- left_data side 0b01",
43 " out pins 1 side 0b10",
44 " set x, 14 side 0b11",
45 "right_data:",
46 " out pins 1 side 0b10",
47 " jmp x-- right_data side 0b11",
48 " out pins 1 side 0b00",
49 );
50 37
51 let bit_clock_pin = p.PIN_18; 38 let bit_clock_pin = p.PIN_18;
52 let left_right_clock_pin = p.PIN_19; 39 let left_right_clock_pin = p.PIN_19;
53 let data_pin = p.PIN_20; 40 let data_pin = p.PIN_20;
54 41
55 let data_pin = pio.common.make_pio_pin(data_pin); 42 let program = PioI2sOutProgram::new(&mut common);
56 let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin); 43 let mut i2s = PioI2sOut::new(
57 let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin); 44 &mut common,
58 45 sm0,
59 let cfg = { 46 p.DMA_CH0,
60 let mut cfg = Config::default(); 47 data_pin,
61 cfg.use_program( 48 bit_clock_pin,
62 &pio.common.load_program(&pio_program.program), 49 left_right_clock_pin,
63 &[&bit_clock_pin, &left_right_clock_pin], 50 SAMPLE_RATE,
64 ); 51 BIT_DEPTH,
65 cfg.set_out_pins(&[&data_pin]); 52 &program,
66 const BIT_DEPTH: u32 = 16;
67 const CHANNELS: u32 = 2;
68 let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS;
69 cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed();
70 cfg.shift_out = ShiftConfig {
71 threshold: 32,
72 direction: ShiftDirection::Left,
73 auto_fill: true,
74 };
75 // join fifos to have twice the time to start the next dma transfer
76 cfg.fifo_join = FifoJoin::TxOnly;
77 cfg
78 };
79 pio.sm0.set_config(&cfg);
80 pio.sm0.set_pin_dirs(
81 embassy_rp::pio::Direction::Out,
82 &[&data_pin, &left_right_clock_pin, &bit_clock_pin],
83 ); 53 );
84 54
85 // create two audio buffers (back and front) which will take turns being 55 // create two audio buffers (back and front) which will take turns being
@@ -90,20 +60,20 @@ async fn main(_spawner: Spawner) {
90 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); 60 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);
91 61
92 // start pio state machine 62 // start pio state machine
93 pio.sm0.set_enable(true);
94 let tx = pio.sm0.tx();
95 let mut dma_ref = p.DMA_CH0.into_ref();
96
97 let mut fade_value: i32 = 0; 63 let mut fade_value: i32 = 0;
98 let mut phase: i32 = 0; 64 let mut phase: i32 = 0;
99 65
100 loop { 66 loop {
101 // trigger transfer of front buffer data to the pio fifo 67 // trigger transfer of front buffer data to the pio fifo
102 // but don't await the returned future, yet 68 // but don't await the returned future, yet
103 let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer); 69 let dma_future = i2s.write(front_buffer);
104 70
105 // fade in audio when bootsel is pressed 71 // fade in audio when bootsel is pressed
106 let fade_target = if p.BOOTSEL.is_pressed() { i32::MAX } else { 0 }; 72 let fade_target = if is_bootsel_pressed(p.BOOTSEL.reborrow()) {
73 i32::MAX
74 } else {
75 0
76 };
107 77
108 // fill back buffer with fresh audio samples before awaiting the dma future 78 // fill back buffer with fresh audio samples before awaiting the dma future
109 for s in back_buffer.iter_mut() { 79 for s in back_buffer.iter_mut() {
diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs
new file mode 100644
index 000000000..6432edb8a
--- /dev/null
+++ b/examples/rp/src/bin/pio_onewire.rs
@@ -0,0 +1,88 @@
1//! This example shows how you can use PIO to read one or more `DS18B20` one-wire temperature sensors.
2//! This uses externally powered sensors. For parasite power, see the pio_onewire_parasite.rs example.
3
4#![no_std]
5#![no_main]
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{InterruptHandler, Pio};
11use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch};
12use embassy_time::Timer;
13use heapless::Vec;
14use {defmt_rtt as _, panic_probe as _};
15
16bind_interrupts!(struct Irqs {
17 PIO0_IRQ_0 => InterruptHandler<PIO0>;
18});
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) {
22 let p = embassy_rp::init(Default::default());
23 let mut pio = Pio::new(p.PIO0, Irqs);
24
25 let prg = PioOneWireProgram::new(&mut pio.common);
26 let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);
27
28 info!("Starting onewire search");
29
30 let mut devices = Vec::<u64, 10>::new();
31 let mut search = PioOneWireSearch::new();
32 for _ in 0..10 {
33 if !search.is_finished() {
34 if let Some(address) = search.next(&mut onewire).await {
35 if crc8(&address.to_le_bytes()) == 0 {
36 info!("Found addres: {:x}", address);
37 let _ = devices.push(address);
38 } else {
39 warn!("Found invalid address: {:x}", address);
40 }
41 }
42 }
43 }
44
45 info!("Search done, found {} devices", devices.len());
46
47 loop {
48 onewire.reset().await;
49 // Skip rom and trigger conversion, we can trigger all devices on the bus immediately
50 onewire.write_bytes(&[0xCC, 0x44]).await;
51
52 Timer::after_secs(1).await; // Allow 1s for the measurement to finish
53
54 // Read all devices one by one
55 for device in &devices {
56 onewire.reset().await;
57 onewire.write_bytes(&[0x55]).await; // Match rom
58 onewire.write_bytes(&device.to_le_bytes()).await;
59 onewire.write_bytes(&[0xBE]).await; // Read scratchpad
60
61 let mut data = [0; 9];
62 onewire.read_bytes(&mut data).await;
63 if crc8(&data) == 0 {
64 let temp = ((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.;
65 info!("Read device {:x}: {} deg C", device, temp);
66 } else {
67 warn!("Reading device {:x} failed", device);
68 }
69 }
70 Timer::after_secs(1).await;
71 }
72}
73
74fn crc8(data: &[u8]) -> u8 {
75 let mut crc = 0;
76 for b in data {
77 let mut data_byte = *b;
78 for _ in 0..8 {
79 let temp = (crc ^ data_byte) & 0x01;
80 crc >>= 1;
81 if temp != 0 {
82 crc ^= 0x8C;
83 }
84 data_byte >>= 1;
85 }
86 }
87 crc
88}
diff --git a/examples/rp/src/bin/pio_onewire_parasite.rs b/examples/rp/src/bin/pio_onewire_parasite.rs
new file mode 100644
index 000000000..78fb94b18
--- /dev/null
+++ b/examples/rp/src/bin/pio_onewire_parasite.rs
@@ -0,0 +1,89 @@
1//! This example shows how you can use PIO to read one or more `DS18B20`
2//! one-wire temperature sensors using parasite power.
3//! It applies a strong pullup during conversion, see "Powering the DS18B20" in the datasheet.
4//! For externally powered sensors, use the pio_onewire.rs example.
5
6#![no_std]
7#![no_main]
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{InterruptHandler, Pio};
13use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram, PioOneWireSearch};
14use embassy_time::Duration;
15use heapless::Vec;
16use {defmt_rtt as _, panic_probe as _};
17
18bind_interrupts!(struct Irqs {
19 PIO0_IRQ_0 => InterruptHandler<PIO0>;
20});
21
22#[embassy_executor::main]
23async fn main(_spawner: Spawner) {
24 let p = embassy_rp::init(Default::default());
25 let mut pio = Pio::new(p.PIO0, Irqs);
26
27 let prg = PioOneWireProgram::new(&mut pio.common);
28 let mut onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);
29
30 info!("Starting onewire search");
31
32 let mut devices = Vec::<u64, 10>::new();
33 let mut search = PioOneWireSearch::new();
34 for _ in 0..10 {
35 if !search.is_finished() {
36 if let Some(address) = search.next(&mut onewire).await {
37 if crc8(&address.to_le_bytes()) == 0 {
38 info!("Found address: {:x}", address);
39 let _ = devices.push(address);
40 } else {
41 warn!("Found invalid address: {:x}", address);
42 }
43 }
44 }
45 }
46
47 info!("Search done, found {} devices", devices.len());
48
49 loop {
50 // Read all devices one by one
51 for device in &devices {
52 onewire.reset().await;
53 onewire.write_bytes(&[0x55]).await; // Match rom
54 onewire.write_bytes(&device.to_le_bytes()).await;
55 // 750 ms delay required for default 12-bit resolution.
56 onewire.write_bytes_pullup(&[0x44], Duration::from_millis(750)).await;
57
58 onewire.reset().await;
59 onewire.write_bytes(&[0x55]).await; // Match rom
60 onewire.write_bytes(&device.to_le_bytes()).await;
61 onewire.write_bytes(&[0xBE]).await; // Read scratchpad
62
63 let mut data = [0; 9];
64 onewire.read_bytes(&mut data).await;
65 if crc8(&data) == 0 {
66 let temp = ((data[1] as i16) << 8 | data[0] as i16) as f32 / 16.;
67 info!("Read device {:x}: {} deg C", device, temp);
68 } else {
69 warn!("Reading device {:x} failed. {:02x}", device, data);
70 }
71 }
72 }
73}
74
75fn crc8(data: &[u8]) -> u8 {
76 let mut crc = 0;
77 for b in data {
78 let mut data_byte = *b;
79 for _ in 0..8 {
80 let temp = (crc ^ data_byte) & 0x01;
81 crc >>= 1;
82 if temp != 0 {
83 crc ^= 0x8C;
84 }
85 data_byte >>= 1;
86 }
87 }
88 crc
89}
diff --git a/examples/rp/src/bin/pio_pwm.rs b/examples/rp/src/bin/pio_pwm.rs
index 23d63d435..7eabb2289 100644
--- a/examples/rp/src/bin/pio_pwm.rs
+++ b/examples/rp/src/bin/pio_pwm.rs
@@ -5,12 +5,11 @@
5use core::time::Duration; 5use core::time::Duration;
6 6
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::gpio::Level; 8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; 10use embassy_rp::pio::{InterruptHandler, Pio};
11use embassy_rp::{bind_interrupts, clocks}; 11use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};
12use embassy_time::Timer; 12use embassy_time::Timer;
13use pio::InstructionOperands;
14use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
15 14
16const REFRESH_INTERVAL: u64 = 20000; 15const REFRESH_INTERVAL: u64 = 20000;
@@ -19,93 +18,14 @@ bind_interrupts!(struct Irqs {
19 PIO0_IRQ_0 => InterruptHandler<PIO0>; 18 PIO0_IRQ_0 => InterruptHandler<PIO0>;
20}); 19});
21 20
22pub fn to_pio_cycles(duration: Duration) -> u32 {
23 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
24}
25
26pub struct PwmPio<'d, T: Instance, const SM: usize> {
27 sm: StateMachine<'d, T, SM>,
28}
29
30impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
31 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
32 let prg = pio_proc::pio_asm!(
33 ".side_set 1 opt"
34 "pull noblock side 0"
35 "mov x, osr"
36 "mov y, isr"
37 "countloop:"
38 "jmp x!=y noset"
39 "jmp skip side 1"
40 "noset:"
41 "nop"
42 "skip:"
43 "jmp y-- countloop"
44 );
45
46 pio.load_program(&prg.program);
47 let pin = pio.make_pio_pin(pin);
48 sm.set_pins(Level::High, &[&pin]);
49 sm.set_pin_dirs(Direction::Out, &[&pin]);
50
51 let mut cfg = Config::default();
52 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
53
54 sm.set_config(&cfg);
55
56 Self { sm }
57 }
58
59 pub fn start(&mut self) {
60 self.sm.set_enable(true);
61 }
62
63 pub fn stop(&mut self) {
64 self.sm.set_enable(false);
65 }
66
67 pub fn set_period(&mut self, duration: Duration) {
68 let is_enabled = self.sm.is_enabled();
69 while !self.sm.tx().empty() {} // Make sure that the queue is empty
70 self.sm.set_enable(false);
71 self.sm.tx().push(to_pio_cycles(duration));
72 unsafe {
73 self.sm.exec_instr(
74 InstructionOperands::PULL {
75 if_empty: false,
76 block: false,
77 }
78 .encode(),
79 );
80 self.sm.exec_instr(
81 InstructionOperands::OUT {
82 destination: ::pio::OutDestination::ISR,
83 bit_count: 32,
84 }
85 .encode(),
86 );
87 };
88 if is_enabled {
89 self.sm.set_enable(true) // Enable if previously enabled
90 }
91 }
92
93 pub fn set_level(&mut self, level: u32) {
94 self.sm.tx().push(level);
95 }
96
97 pub fn write(&mut self, duration: Duration) {
98 self.set_level(to_pio_cycles(duration));
99 }
100}
101
102#[embassy_executor::main] 21#[embassy_executor::main]
103async fn main(_spawner: Spawner) { 22async fn main(_spawner: Spawner) {
104 let p = embassy_rp::init(Default::default()); 23 let p = embassy_rp::init(Default::default());
105 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); 24 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
106 25
107 // Note that PIN_25 is the led pin on the Pico 26 // Note that PIN_25 is the led pin on the Pico
108 let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25); 27 let prg = PioPwmProgram::new(&mut common);
28 let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg);
109 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); 29 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));
110 pwm_pio.start(); 30 pwm_pio.start();
111 31
diff --git a/examples/rp/src/bin/pio_rotary_encoder.rs b/examples/rp/src/bin/pio_rotary_encoder.rs
index 58bdadbc0..2fc19970b 100644
--- a/examples/rp/src/bin/pio_rotary_encoder.rs
+++ b/examples/rp/src/bin/pio_rotary_encoder.rs
@@ -5,70 +5,30 @@
5 5
6use defmt::info; 6use defmt::info;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::gpio::Pull; 8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::{bind_interrupts, pio}; 10use embassy_rp::pio::{InterruptHandler, Pio};
11use fixed::traits::ToFixed; 11use embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram};
12use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};
13use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
14 13
15bind_interrupts!(struct Irqs { 14bind_interrupts!(struct Irqs {
16 PIO0_IRQ_0 => InterruptHandler<PIO0>; 15 PIO0_IRQ_0 => InterruptHandler<PIO0>;
17}); 16});
18 17
19pub struct PioEncoder<'d, T: Instance, const SM: usize> { 18#[embassy_executor::task]
20 sm: StateMachine<'d, T, SM>, 19async fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) {
21} 20 let mut count = 0;
22 21 loop {
23impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { 22 info!("Count: {}", count);
24 pub fn new( 23 count += match encoder.read().await {
25 pio: &mut Common<'d, T>, 24 Direction::Clockwise => 1,
26 mut sm: StateMachine<'d, T, SM>, 25 Direction::CounterClockwise => -1,
27 pin_a: impl PioPin, 26 };
28 pin_b: impl PioPin,
29 ) -> Self {
30 let mut pin_a = pio.make_pio_pin(pin_a);
31 let mut pin_b = pio.make_pio_pin(pin_b);
32 pin_a.set_pull(Pull::Up);
33 pin_b.set_pull(Pull::Up);
34 sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
35
36 let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",);
37
38 let mut cfg = Config::default();
39 cfg.set_in_pins(&[&pin_a, &pin_b]);
40 cfg.fifo_join = FifoJoin::RxOnly;
41 cfg.shift_in.direction = ShiftDirection::Left;
42 cfg.clock_divider = 10_000.to_fixed();
43 cfg.use_program(&pio.load_program(&prg.program), &[]);
44 sm.set_config(&cfg);
45 sm.set_enable(true);
46 Self { sm }
47 }
48
49 pub async fn read(&mut self) -> Direction {
50 loop {
51 match self.sm.rx().wait_pull().await {
52 0 => return Direction::CounterClockwise,
53 1 => return Direction::Clockwise,
54 _ => {}
55 }
56 }
57 } 27 }
58} 28}
59 29
60pub enum Direction { 30#[embassy_executor::task]
61 Clockwise, 31async fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) {
62 CounterClockwise,
63}
64
65#[embassy_executor::main]
66async fn main(_spawner: Spawner) {
67 let p = embassy_rp::init(Default::default());
68 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
69
70 let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);
71
72 let mut count = 0; 32 let mut count = 0;
73 loop { 33 loop {
74 info!("Count: {}", count); 34 info!("Count: {}", count);
@@ -78,3 +38,18 @@ async fn main(_spawner: Spawner) {
78 }; 38 };
79 } 39 }
80} 40}
41
42#[embassy_executor::main]
43async fn main(spawner: Spawner) {
44 let p = embassy_rp::init(Default::default());
45 let Pio {
46 mut common, sm0, sm1, ..
47 } = Pio::new(p.PIO0, Irqs);
48
49 let prg = PioEncoderProgram::new(&mut common);
50 let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg);
51 let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg);
52
53 spawner.spawn(encoder_0(encoder0).unwrap());
54 spawner.spawn(encoder_1(encoder1).unwrap());
55}
diff --git a/examples/rp/src/bin/pio_servo.rs b/examples/rp/src/bin/pio_servo.rs
index a79540479..c52ee7492 100644
--- a/examples/rp/src/bin/pio_servo.rs
+++ b/examples/rp/src/bin/pio_servo.rs
@@ -5,12 +5,11 @@
5use core::time::Duration; 5use core::time::Duration;
6 6
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::gpio::Level; 8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; 10use embassy_rp::pio::{Instance, InterruptHandler, Pio};
11use embassy_rp::{bind_interrupts, clocks}; 11use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};
12use embassy_time::Timer; 12use embassy_time::Timer;
13use pio::InstructionOperands;
14use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
15 14
16const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo 15const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo
@@ -22,88 +21,8 @@ bind_interrupts!(struct Irqs {
22 PIO0_IRQ_0 => InterruptHandler<PIO0>; 21 PIO0_IRQ_0 => InterruptHandler<PIO0>;
23}); 22});
24 23
25pub fn to_pio_cycles(duration: Duration) -> u32 {
26 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
27}
28
29pub struct PwmPio<'d, T: Instance, const SM: usize> {
30 sm: StateMachine<'d, T, SM>,
31}
32
33impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
34 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
35 let prg = pio_proc::pio_asm!(
36 ".side_set 1 opt"
37 "pull noblock side 0"
38 "mov x, osr"
39 "mov y, isr"
40 "countloop:"
41 "jmp x!=y noset"
42 "jmp skip side 1"
43 "noset:"
44 "nop"
45 "skip:"
46 "jmp y-- countloop"
47 );
48
49 pio.load_program(&prg.program);
50 let pin = pio.make_pio_pin(pin);
51 sm.set_pins(Level::High, &[&pin]);
52 sm.set_pin_dirs(Direction::Out, &[&pin]);
53
54 let mut cfg = Config::default();
55 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
56
57 sm.set_config(&cfg);
58
59 Self { sm }
60 }
61
62 pub fn start(&mut self) {
63 self.sm.set_enable(true);
64 }
65
66 pub fn stop(&mut self) {
67 self.sm.set_enable(false);
68 }
69
70 pub fn set_period(&mut self, duration: Duration) {
71 let is_enabled = self.sm.is_enabled();
72 while !self.sm.tx().empty() {} // Make sure that the queue is empty
73 self.sm.set_enable(false);
74 self.sm.tx().push(to_pio_cycles(duration));
75 unsafe {
76 self.sm.exec_instr(
77 InstructionOperands::PULL {
78 if_empty: false,
79 block: false,
80 }
81 .encode(),
82 );
83 self.sm.exec_instr(
84 InstructionOperands::OUT {
85 destination: ::pio::OutDestination::ISR,
86 bit_count: 32,
87 }
88 .encode(),
89 );
90 };
91 if is_enabled {
92 self.sm.set_enable(true) // Enable if previously enabled
93 }
94 }
95
96 pub fn set_level(&mut self, level: u32) {
97 self.sm.tx().push(level);
98 }
99
100 pub fn write(&mut self, duration: Duration) {
101 self.set_level(to_pio_cycles(duration));
102 }
103}
104
105pub struct ServoBuilder<'d, T: Instance, const SM: usize> { 24pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
106 pwm: PwmPio<'d, T, SM>, 25 pwm: PioPwm<'d, T, SM>,
107 period: Duration, 26 period: Duration,
108 min_pulse_width: Duration, 27 min_pulse_width: Duration,
109 max_pulse_width: Duration, 28 max_pulse_width: Duration,
@@ -111,7 +30,7 @@ pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
111} 30}
112 31
113impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { 32impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
114 pub fn new(pwm: PwmPio<'d, T, SM>) -> Self { 33 pub fn new(pwm: PioPwm<'d, T, SM>) -> Self {
115 Self { 34 Self {
116 pwm, 35 pwm,
117 period: Duration::from_micros(REFRESH_INTERVAL), 36 period: Duration::from_micros(REFRESH_INTERVAL),
@@ -153,7 +72,7 @@ impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
153} 72}
154 73
155pub struct Servo<'d, T: Instance, const SM: usize> { 74pub struct Servo<'d, T: Instance, const SM: usize> {
156 pwm: PwmPio<'d, T, SM>, 75 pwm: PioPwm<'d, T, SM>,
157 min_pulse_width: Duration, 76 min_pulse_width: Duration,
158 max_pulse_width: Duration, 77 max_pulse_width: Duration,
159 max_degree_rotation: u64, 78 max_degree_rotation: u64,
@@ -190,7 +109,8 @@ async fn main(_spawner: Spawner) {
190 let p = embassy_rp::init(Default::default()); 109 let p = embassy_rp::init(Default::default());
191 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); 110 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
192 111
193 let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1); 112 let prg = PioPwmProgram::new(&mut common);
113 let pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_1, &prg);
194 let mut servo = ServoBuilder::new(pwm_pio) 114 let mut servo = ServoBuilder::new(pwm_pio)
195 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo 115 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo
196 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment. 116 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.
diff --git a/examples/rp/src/bin/pio_spi.rs b/examples/rp/src/bin/pio_spi.rs
new file mode 100644
index 000000000..4218327ec
--- /dev/null
+++ b/examples/rp/src/bin/pio_spi.rs
@@ -0,0 +1,48 @@
1//! This example shows how to use a PIO state machine as an additional SPI
2//! (Serial Peripheral Interface) on the RP2040 chip. No specific hardware is
3//! specified in this example.
4//!
5//! If you connect pin 6 and 7 you should get the same data back.
6
7#![no_std]
8#![no_main]
9
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio_programs::spi::Spi;
14use embassy_rp::spi::Config;
15use embassy_rp::{bind_interrupts, pio};
16use embassy_time::Timer;
17use {defmt_rtt as _, panic_probe as _};
18
19bind_interrupts!(struct Irqs {
20 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
21});
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let p = embassy_rp::init(Default::default());
26 info!("Hello World!");
27
28 // These pins are routed to different hardware SPI peripherals, but we can
29 // use them together regardless
30 let mosi = p.PIN_6; // SPI0 SCLK
31 let miso = p.PIN_7; // SPI0 MOSI
32 let clk = p.PIN_8; // SPI1 MISO
33
34 let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs);
35
36 // Construct an SPI driver backed by a PIO state machine
37 let mut spi = Spi::new_blocking(&mut common, sm0, clk, mosi, miso, Config::default());
38
39 loop {
40 let tx_buf = [1_u8, 2, 3, 4, 5, 6];
41 let mut rx_buf = [0_u8; 6];
42
43 spi.blocking_transfer(&mut rx_buf, &tx_buf).unwrap();
44 info!("{:?}", rx_buf);
45
46 Timer::after_secs(1).await;
47 }
48}
diff --git a/examples/rp/src/bin/pio_spi_async.rs b/examples/rp/src/bin/pio_spi_async.rs
new file mode 100644
index 000000000..18b57d26e
--- /dev/null
+++ b/examples/rp/src/bin/pio_spi_async.rs
@@ -0,0 +1,57 @@
1//! This example shows how to use a PIO state machine as an additional SPI
2//! (Serial Peripheral Interface) on the RP2040 chip. No specific hardware is
3//! specified in this example.
4//!
5//! If you connect pin 6 and 7 you should get the same data back.
6
7#![no_std]
8#![no_main]
9
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio_programs::spi::Spi;
14use embassy_rp::spi::Config;
15use embassy_rp::{bind_interrupts, pio};
16use embassy_time::Timer;
17use {defmt_rtt as _, panic_probe as _};
18
19bind_interrupts!(struct Irqs {
20 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
21});
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let p = embassy_rp::init(Default::default());
26 info!("Hello World!");
27
28 // These pins are routed to different hardware SPI peripherals, but we can
29 // use them together regardless
30 let mosi = p.PIN_6; // SPI0 SCLK
31 let miso = p.PIN_7; // SPI0 MOSI
32 let clk = p.PIN_8; // SPI1 MISO
33
34 let pio::Pio { mut common, sm0, .. } = pio::Pio::new(p.PIO0, Irqs);
35
36 // Construct an SPI driver backed by a PIO state machine
37 let mut spi = Spi::new(
38 &mut common,
39 sm0,
40 clk,
41 mosi,
42 miso,
43 p.DMA_CH0,
44 p.DMA_CH1,
45 Config::default(),
46 );
47
48 loop {
49 let tx_buf = [1_u8, 2, 3, 4, 5, 6];
50 let mut rx_buf = [0_u8; 6];
51
52 spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
53 info!("{:?}", rx_buf);
54
55 Timer::after_secs(1).await;
56 }
57}
diff --git a/examples/rp/src/bin/pio_stepper.rs b/examples/rp/src/bin/pio_stepper.rs
index 4952f4fbd..e8f203990 100644
--- a/examples/rp/src/bin/pio_stepper.rs
+++ b/examples/rp/src/bin/pio_stepper.rs
@@ -3,143 +3,20 @@
3 3
4#![no_std] 4#![no_std]
5#![no_main] 5#![no_main]
6use core::mem::{self, MaybeUninit};
7 6
8use defmt::info; 7use defmt::info;
9use embassy_executor::Spawner; 8use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts; 9use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine}; 11use embassy_rp::pio::{InterruptHandler, Pio};
13use embassy_time::{with_timeout, Duration, Timer}; 12use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram};
14use fixed::traits::ToFixed; 13use embassy_time::{Duration, Timer, with_timeout};
15use fixed::types::extra::U8;
16use fixed::FixedU32;
17use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
18 15
19bind_interrupts!(struct Irqs { 16bind_interrupts!(struct Irqs {
20 PIO0_IRQ_0 => InterruptHandler<PIO0>; 17 PIO0_IRQ_0 => InterruptHandler<PIO0>;
21}); 18});
22 19
23pub struct PioStepper<'d, T: Instance, const SM: usize> {
24 irq: Irq<'d, T, SM>,
25 sm: StateMachine<'d, T, SM>,
26}
27
28impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
29 pub fn new(
30 pio: &mut Common<'d, T>,
31 mut sm: StateMachine<'d, T, SM>,
32 irq: Irq<'d, T, SM>,
33 pin0: impl PioPin,
34 pin1: impl PioPin,
35 pin2: impl PioPin,
36 pin3: impl PioPin,
37 ) -> Self {
38 let prg = pio_proc::pio_asm!(
39 "pull block",
40 "mov x, osr",
41 "pull block",
42 "mov y, osr",
43 "jmp !x end",
44 "loop:",
45 "jmp !osre step",
46 "mov osr, y",
47 "step:",
48 "out pins, 4 [31]"
49 "jmp x-- loop",
50 "end:",
51 "irq 0 rel"
52 );
53 let pin0 = pio.make_pio_pin(pin0);
54 let pin1 = pio.make_pio_pin(pin1);
55 let pin2 = pio.make_pio_pin(pin2);
56 let pin3 = pio.make_pio_pin(pin3);
57 sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
58 let mut cfg = Config::default();
59 cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
60 cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
61 cfg.use_program(&pio.load_program(&prg.program), &[]);
62 sm.set_config(&cfg);
63 sm.set_enable(true);
64 Self { irq, sm }
65 }
66
67 // Set pulse frequency
68 pub fn set_frequency(&mut self, freq: u32) {
69 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
70 assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
71 assert!(clock_divider >= 1, "clkdiv must be >= 1");
72 self.sm.set_clock_divider(clock_divider);
73 self.sm.clkdiv_restart();
74 }
75
76 // Full step, one phase
77 pub async fn step(&mut self, steps: i32) {
78 if steps > 0 {
79 self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await
80 } else {
81 self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await
82 }
83 }
84
85 // Full step, two phase
86 pub async fn step2(&mut self, steps: i32) {
87 if steps > 0 {
88 self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await
89 } else {
90 self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await
91 }
92 }
93
94 // Half step
95 pub async fn step_half(&mut self, steps: i32) {
96 if steps > 0 {
97 self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await
98 } else {
99 self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await
100 }
101 }
102
103 async fn run(&mut self, steps: i32, pattern: u32) {
104 self.sm.tx().wait_push(steps as u32).await;
105 self.sm.tx().wait_push(pattern).await;
106 let drop = OnDrop::new(|| {
107 self.sm.clear_fifos();
108 unsafe {
109 self.sm.exec_instr(
110 pio::InstructionOperands::JMP {
111 address: 0,
112 condition: pio::JmpCondition::Always,
113 }
114 .encode(),
115 );
116 }
117 });
118 self.irq.wait().await;
119 drop.defuse();
120 }
121}
122
123struct OnDrop<F: FnOnce()> {
124 f: MaybeUninit<F>,
125}
126
127impl<F: FnOnce()> OnDrop<F> {
128 pub fn new(f: F) -> Self {
129 Self { f: MaybeUninit::new(f) }
130 }
131
132 pub fn defuse(self) {
133 mem::forget(self)
134 }
135}
136
137impl<F: FnOnce()> Drop for OnDrop<F> {
138 fn drop(&mut self) {
139 unsafe { self.f.as_ptr().read()() }
140 }
141}
142
143#[embassy_executor::main] 20#[embassy_executor::main]
144async fn main(_spawner: Spawner) { 21async fn main(_spawner: Spawner) {
145 let p = embassy_rp::init(Default::default()); 22 let p = embassy_rp::init(Default::default());
@@ -147,14 +24,18 @@ async fn main(_spawner: Spawner) {
147 mut common, irq0, sm0, .. 24 mut common, irq0, sm0, ..
148 } = Pio::new(p.PIO0, Irqs); 25 } = Pio::new(p.PIO0, Irqs);
149 26
150 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7); 27 let prg = PioStepperProgram::new(&mut common);
28 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7, &prg);
151 stepper.set_frequency(120); 29 stepper.set_frequency(120);
152 loop { 30 loop {
153 info!("CW full steps"); 31 info!("CW full steps");
154 stepper.step(1000).await; 32 stepper.step(1000).await;
155 33
156 info!("CCW full steps, drop after 1 sec"); 34 info!("CCW full steps, drop after 1 sec");
157 if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await { 35 if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX))
36 .await
37 .is_err()
38 {
158 info!("Time's up!"); 39 info!("Time's up!");
159 Timer::after(Duration::from_secs(1)).await; 40 Timer::after(Duration::from_secs(1)).await;
160 } 41 }
diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs
index 53b696309..485c65204 100644
--- a/examples/rp/src/bin/pio_uart.rs
+++ b/examples/rp/src/bin/pio_uart.rs
@@ -13,10 +13,10 @@
13use defmt::{info, panic, trace}; 13use defmt::{info, panic, trace};
14use embassy_executor::Spawner; 14use embassy_executor::Spawner;
15use embassy_futures::join::{join, join3}; 15use embassy_futures::join::{join, join3};
16use embassy_rp::bind_interrupts;
17use embassy_rp::peripherals::{PIO0, USB}; 16use embassy_rp::peripherals::{PIO0, USB};
18use embassy_rp::pio::InterruptHandler as PioInterruptHandler; 17use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram};
19use embassy_rp::usb::{Driver, Instance, InterruptHandler}; 18use embassy_rp::usb::{Driver, Instance, InterruptHandler};
19use embassy_rp::{bind_interrupts, pio};
20use embassy_sync::blocking_mutex::raw::NoopRawMutex; 20use embassy_sync::blocking_mutex::raw::NoopRawMutex;
21use embassy_sync::pipe::Pipe; 21use embassy_sync::pipe::Pipe;
22use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; 22use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
@@ -25,13 +25,11 @@ use embassy_usb::{Builder, Config};
25use embedded_io_async::{Read, Write}; 25use embedded_io_async::{Read, Write};
26use {defmt_rtt as _, panic_probe as _}; 26use {defmt_rtt as _, panic_probe as _};
27 27
28use crate::uart::PioUart; 28//use crate::uart::PioUart;
29use crate::uart_rx::PioUartRx;
30use crate::uart_tx::PioUartTx;
31 29
32bind_interrupts!(struct Irqs { 30bind_interrupts!(struct Irqs {
33 USBCTRL_IRQ => InterruptHandler<USB>; 31 USBCTRL_IRQ => InterruptHandler<USB>;
34 PIO0_IRQ_0 => PioInterruptHandler<PIO0>; 32 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
35}); 33});
36 34
37#[embassy_executor::main] 35#[embassy_executor::main]
@@ -51,13 +49,6 @@ async fn main(_spawner: Spawner) {
51 config.max_power = 100; 49 config.max_power = 100;
52 config.max_packet_size_0 = 64; 50 config.max_packet_size_0 = 64;
53 51
54 // Required for windows compatibility.
55 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
56 config.device_class = 0xEF;
57 config.device_sub_class = 0x02;
58 config.device_protocol = 0x01;
59 config.composite_with_iads = true;
60
61 // Create embassy-usb DeviceBuilder using the driver and config. 52 // Create embassy-usb DeviceBuilder using the driver and config.
62 // It needs some buffers for building the descriptors. 53 // It needs some buffers for building the descriptors.
63 let mut config_descriptor = [0; 256]; 54 let mut config_descriptor = [0; 256];
@@ -85,8 +76,15 @@ async fn main(_spawner: Spawner) {
85 let usb_fut = usb.run(); 76 let usb_fut = usb.run();
86 77
87 // PIO UART setup 78 // PIO UART setup
88 let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5); 79 let pio::Pio {
89 let (mut uart_tx, mut uart_rx) = uart.split(); 80 mut common, sm0, sm1, ..
81 } = pio::Pio::new(p.PIO0, Irqs);
82
83 let tx_program = PioUartTxProgram::new(&mut common);
84 let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program);
85
86 let rx_program = PioUartRxProgram::new(&mut common);
87 let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program);
90 88
91 // Pipe setup 89 // Pipe setup
92 let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new(); 90 let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();
@@ -163,8 +161,8 @@ async fn usb_write<'d, T: Instance + 'd>(
163} 161}
164 162
165/// Read from the UART and write it to the USB TX pipe 163/// Read from the UART and write it to the USB TX pipe
166async fn uart_read( 164async fn uart_read<PIO: pio::Instance, const SM: usize>(
167 uart_rx: &mut PioUartRx<'_>, 165 uart_rx: &mut PioUartRx<'_, PIO, SM>,
168 usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, 166 usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,
169) -> ! { 167) -> ! {
170 let mut buf = [0; 64]; 168 let mut buf = [0; 64];
@@ -180,8 +178,8 @@ async fn uart_read(
180} 178}
181 179
182/// Read from the UART TX pipe and write it to the UART 180/// Read from the UART TX pipe and write it to the UART
183async fn uart_write( 181async fn uart_write<PIO: pio::Instance, const SM: usize>(
184 uart_tx: &mut PioUartTx<'_>, 182 uart_tx: &mut PioUartTx<'_, PIO, SM>,
185 uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, 183 uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,
186) -> ! { 184) -> ! {
187 let mut buf = [0; 64]; 185 let mut buf = [0; 64];
@@ -192,197 +190,3 @@ async fn uart_write(
192 let _ = uart_tx.write(&data).await; 190 let _ = uart_tx.write(&data).await;
193 } 191 }
194} 192}
195
196mod uart {
197 use embassy_rp::peripherals::PIO0;
198 use embassy_rp::pio::{Pio, PioPin};
199 use embassy_rp::Peripheral;
200
201 use crate::uart_rx::PioUartRx;
202 use crate::uart_tx::PioUartTx;
203 use crate::Irqs;
204
205 pub struct PioUart<'a> {
206 tx: PioUartTx<'a>,
207 rx: PioUartRx<'a>,
208 }
209
210 impl<'a> PioUart<'a> {
211 pub fn new(
212 baud: u64,
213 pio: impl Peripheral<P = PIO0> + 'a,
214 tx_pin: impl PioPin,
215 rx_pin: impl PioPin,
216 ) -> PioUart<'a> {
217 let Pio {
218 mut common, sm0, sm1, ..
219 } = Pio::new(pio, Irqs);
220
221 let tx = PioUartTx::new(&mut common, sm0, tx_pin, baud);
222 let rx = PioUartRx::new(&mut common, sm1, rx_pin, baud);
223
224 PioUart { tx, rx }
225 }
226
227 pub fn split(self) -> (PioUartTx<'a>, PioUartRx<'a>) {
228 (self.tx, self.rx)
229 }
230 }
231}
232
233mod uart_tx {
234 use core::convert::Infallible;
235
236 use embassy_rp::gpio::Level;
237 use embassy_rp::peripherals::PIO0;
238 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
239 use embedded_io_async::{ErrorType, Write};
240 use fixed::traits::ToFixed;
241 use fixed_macro::types::U56F8;
242
243 pub struct PioUartTx<'a> {
244 sm_tx: StateMachine<'a, PIO0, 0>,
245 }
246
247 impl<'a> PioUartTx<'a> {
248 pub fn new(
249 common: &mut Common<'a, PIO0>,
250 mut sm_tx: StateMachine<'a, PIO0, 0>,
251 tx_pin: impl PioPin,
252 baud: u64,
253 ) -> Self {
254 let prg = pio_proc::pio_asm!(
255 r#"
256 .side_set 1 opt
257
258 ; An 8n1 UART transmit program.
259 ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
260
261 pull side 1 [7] ; Assert stop bit, or stall with line in idle state
262 set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks
263 bitloop: ; This loop will run 8 times (8n1 UART)
264 out pins, 1 ; Shift 1 bit from OSR to the first OUT pin
265 jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
266 "#
267 );
268 let tx_pin = common.make_pio_pin(tx_pin);
269 sm_tx.set_pins(Level::High, &[&tx_pin]);
270 sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]);
271
272 let mut cfg = Config::default();
273
274 cfg.set_out_pins(&[&tx_pin]);
275 cfg.use_program(&common.load_program(&prg.program), &[&tx_pin]);
276 cfg.shift_out.auto_fill = false;
277 cfg.shift_out.direction = ShiftDirection::Right;
278 cfg.fifo_join = FifoJoin::TxOnly;
279 cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed();
280 sm_tx.set_config(&cfg);
281 sm_tx.set_enable(true);
282
283 Self { sm_tx }
284 }
285
286 pub async fn write_u8(&mut self, data: u8) {
287 self.sm_tx.tx().wait_push(data as u32).await;
288 }
289 }
290
291 impl ErrorType for PioUartTx<'_> {
292 type Error = Infallible;
293 }
294
295 impl Write for PioUartTx<'_> {
296 async fn write(&mut self, buf: &[u8]) -> Result<usize, Infallible> {
297 for byte in buf {
298 self.write_u8(*byte).await;
299 }
300 Ok(buf.len())
301 }
302 }
303}
304
305mod uart_rx {
306 use core::convert::Infallible;
307
308 use embassy_rp::gpio::Level;
309 use embassy_rp::peripherals::PIO0;
310 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
311 use embedded_io_async::{ErrorType, Read};
312 use fixed::traits::ToFixed;
313 use fixed_macro::types::U56F8;
314
315 pub struct PioUartRx<'a> {
316 sm_rx: StateMachine<'a, PIO0, 1>,
317 }
318
319 impl<'a> PioUartRx<'a> {
320 pub fn new(
321 common: &mut Common<'a, PIO0>,
322 mut sm_rx: StateMachine<'a, PIO0, 1>,
323 rx_pin: impl PioPin,
324 baud: u64,
325 ) -> Self {
326 let prg = pio_proc::pio_asm!(
327 r#"
328 ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
329 ; break conditions more gracefully.
330 ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
331
332 start:
333 wait 0 pin 0 ; Stall until start bit is asserted
334 set x, 7 [10] ; Preload bit counter, then delay until halfway through
335 rx_bitloop: ; the first data bit (12 cycles incl wait, set).
336 in pins, 1 ; Shift data bit into ISR
337 jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles
338 jmp pin good_rx_stop ; Check stop bit (should be high)
339
340 irq 4 rel ; Either a framing error or a break. Set a sticky flag,
341 wait 1 pin 0 ; and wait for line to return to idle state.
342 jmp start ; Don't push data if we didn't see good framing.
343
344 good_rx_stop: ; No delay before returning to start; a little slack is
345 in null 24
346 push ; important in case the TX clock is slightly too fast.
347 "#
348 );
349 let mut cfg = Config::default();
350 cfg.use_program(&common.load_program(&prg.program), &[]);
351
352 let rx_pin = common.make_pio_pin(rx_pin);
353 sm_rx.set_pins(Level::High, &[&rx_pin]);
354 cfg.set_in_pins(&[&rx_pin]);
355 cfg.set_jmp_pin(&rx_pin);
356 sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]);
357
358 cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed();
359 cfg.shift_in.auto_fill = false;
360 cfg.shift_in.direction = ShiftDirection::Right;
361 cfg.shift_in.threshold = 32;
362 cfg.fifo_join = FifoJoin::RxOnly;
363 sm_rx.set_config(&cfg);
364 sm_rx.set_enable(true);
365
366 Self { sm_rx }
367 }
368
369 pub async fn read_u8(&mut self) -> u8 {
370 self.sm_rx.rx().wait_pull().await as u8
371 }
372 }
373
374 impl ErrorType for PioUartRx<'_> {
375 type Error = Infallible;
376 }
377
378 impl Read for PioUartRx<'_> {
379 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Infallible> {
380 let mut i = 0;
381 while i < buf.len() {
382 buf[i] = self.read_u8().await;
383 i += 1;
384 }
385 Ok(i)
386 }
387 }
388}
diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs
index ac145933c..d1fcfc471 100644
--- a/examples/rp/src/bin/pio_ws2812.rs
+++ b/examples/rp/src/bin/pio_ws2812.rs
@@ -6,15 +6,11 @@
6 6
7use defmt::*; 7use defmt::*;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_rp::dma::{AnyChannel, Channel}; 9use embassy_rp::bind_interrupts;
10use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{ 11use embassy_rp::pio::{InterruptHandler, Pio};
12 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 12use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program};
13}; 13use embassy_time::{Duration, Ticker};
14use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
15use embassy_time::{Duration, Ticker, Timer};
16use fixed::types::U24F8;
17use fixed_macro::fixed;
18use smart_leds::RGB8; 14use smart_leds::RGB8;
19use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
20 16
@@ -22,96 +18,6 @@ bind_interrupts!(struct Irqs {
22 PIO0_IRQ_0 => InterruptHandler<PIO0>; 18 PIO0_IRQ_0 => InterruptHandler<PIO0>;
23}); 19});
24 20
25pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> {
26 dma: PeripheralRef<'d, AnyChannel>,
27 sm: StateMachine<'d, P, S>,
28}
29
30impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
31 pub fn new(
32 pio: &mut Common<'d, P>,
33 mut sm: StateMachine<'d, P, S>,
34 dma: impl Peripheral<P = impl Channel> + 'd,
35 pin: impl PioPin,
36 ) -> Self {
37 into_ref!(dma);
38
39 // Setup sm0
40
41 // prepare the PIO program
42 let side_set = pio::SideSet::new(false, 1, false);
43 let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
44
45 const T1: u8 = 2; // start bit
46 const T2: u8 = 5; // data bit
47 const T3: u8 = 3; // stop bit
48 const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
49
50 let mut wrap_target = a.label();
51 let mut wrap_source = a.label();
52 let mut do_zero = a.label();
53 a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
54 a.bind(&mut wrap_target);
55 // Do stop bit
56 a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
57 // Do start bit
58 a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
59 // Do data bit = 1
60 a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
61 a.bind(&mut do_zero);
62 // Do data bit = 0
63 a.nop_with_delay_and_side_set(T2 - 1, 0);
64 a.bind(&mut wrap_source);
65
66 let prg = a.assemble_with_wrap(wrap_source, wrap_target);
67 let mut cfg = Config::default();
68
69 // Pin config
70 let out_pin = pio.make_pio_pin(pin);
71 cfg.set_out_pins(&[&out_pin]);
72 cfg.set_set_pins(&[&out_pin]);
73
74 cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
75
76 // Clock config, measured in kHz to avoid overflows
77 // TODO CLOCK_FREQ should come from embassy_rp
78 let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000);
79 let ws2812_freq = fixed!(800: U24F8);
80 let bit_freq = ws2812_freq * CYCLES_PER_BIT;
81 cfg.clock_divider = clock_freq / bit_freq;
82
83 // FIFO config
84 cfg.fifo_join = FifoJoin::TxOnly;
85 cfg.shift_out = ShiftConfig {
86 auto_fill: true,
87 threshold: 24,
88 direction: ShiftDirection::Left,
89 };
90
91 sm.set_config(&cfg);
92 sm.set_enable(true);
93
94 Self {
95 dma: dma.map_into(),
96 sm,
97 }
98 }
99
100 pub async fn write(&mut self, colors: &[RGB8; N]) {
101 // Precompute the word bytes from the colors
102 let mut words = [0u32; N];
103 for i in 0..N {
104 let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8);
105 words[i] = word;
106 }
107
108 // DMA transfer
109 self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
110
111 Timer::after_micros(55).await;
112 }
113}
114
115/// Input a value 0 to 255 to get a color value 21/// Input a value 0 to 255 to get a color value
116/// The colours are a transition r - g - b - back to r. 22/// The colours are a transition r - g - b - back to r.
117fn wheel(mut wheel_pos: u8) -> RGB8 { 23fn wheel(mut wheel_pos: u8) -> RGB8 {
@@ -142,7 +48,8 @@ async fn main(_spawner: Spawner) {
142 // Common neopixel pins: 48 // Common neopixel pins:
143 // Thing plus: 8 49 // Thing plus: 8
144 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 50 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4
145 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); 51 let program = PioWs2812Program::new(&mut common);
52 let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16, &program);
146 53
147 // Loop forever making RGB values and pushing them out to the WS2812. 54 // Loop forever making RGB values and pushing them out to the WS2812.
148 let mut ticker = Ticker::every(Duration::from_millis(10)); 55 let mut ticker = Ticker::every(Duration::from_millis(10));
diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs
index 26e233260..f985bf7cf 100644
--- a/examples/rp/src/bin/pwm.rs
+++ b/examples/rp/src/bin/pwm.rs
@@ -1,24 +1,37 @@
1//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip. 1//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip.
2//! 2//!
3//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. 3//! We demonstrate two ways of using PWM:
4//! 1. Via config
5//! 2. Via setting a duty cycle
4 6
5#![no_std] 7#![no_std]
6#![no_main] 8#![no_main]
7 9
8use defmt::*; 10use defmt::*;
9use embassy_executor::Spawner; 11use embassy_executor::Spawner;
10use embassy_rp::pwm::{Config, Pwm}; 12use embassy_rp::Peri;
13use embassy_rp::peripherals::{PIN_4, PIN_25, PWM_SLICE2, PWM_SLICE4};
14use embassy_rp::pwm::{Config, Pwm, SetDutyCycle};
11use embassy_time::Timer; 15use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _}; 16use {defmt_rtt as _, panic_probe as _};
13 17
14#[embassy_executor::main] 18#[embassy_executor::main]
15async fn main(_spawner: Spawner) { 19async fn main(spawner: Spawner) {
16 let p = embassy_rp::init(Default::default()); 20 let p = embassy_rp::init(Default::default());
21 spawner.spawn(pwm_set_config(p.PWM_SLICE4, p.PIN_25).unwrap());
22 spawner.spawn(pwm_set_dutycycle(p.PWM_SLICE2, p.PIN_4).unwrap());
23}
17 24
18 let mut c: Config = Default::default(); 25/// Demonstrate PWM by modifying & applying the config
19 c.top = 0x8000; 26///
27/// Using the onboard led, if You are using a different Board than plain Pico2 (i.e. W variant)
28/// you must use another slice & pin and an appropriate resistor.
29#[embassy_executor::task]
30async fn pwm_set_config(slice4: Peri<'static, PWM_SLICE4>, pin25: Peri<'static, PIN_25>) {
31 let mut c = Config::default();
32 c.top = 32_768;
20 c.compare_b = 8; 33 c.compare_b = 8;
21 let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone()); 34 let mut pwm = Pwm::new_output_b(slice4, pin25, c.clone());
22 35
23 loop { 36 loop {
24 info!("current LED duty cycle: {}/32768", c.compare_b); 37 info!("current LED duty cycle: {}/32768", c.compare_b);
@@ -27,3 +40,41 @@ async fn main(_spawner: Spawner) {
27 pwm.set_config(&c); 40 pwm.set_config(&c);
28 } 41 }
29} 42}
43
44/// Demonstrate PWM by setting duty cycle
45///
46/// Using GP4 in Slice2, make sure to use an appropriate resistor.
47#[embassy_executor::task]
48async fn pwm_set_dutycycle(slice2: Peri<'static, PWM_SLICE2>, pin4: Peri<'static, PIN_4>) {
49 // If we aim for a specific frequency, here is how we can calculate the top value.
50 // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0.
51 // Every such wraparound is one PWM cycle. So here is how we get 25KHz:
52 let desired_freq_hz = 25_000;
53 let clock_freq_hz = embassy_rp::clocks::clk_sys_freq();
54 let divider = 16u8;
55 let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1;
56
57 let mut c = Config::default();
58 c.top = period;
59 c.divider = divider.into();
60
61 let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone());
62
63 loop {
64 // 100% duty cycle, fully on
65 pwm.set_duty_cycle_fully_on().unwrap();
66 Timer::after_secs(1).await;
67
68 // 66% duty cycle. Expressed as simple percentage.
69 pwm.set_duty_cycle_percent(66).unwrap();
70 Timer::after_secs(1).await;
71
72 // 25% duty cycle. Expressed as 32768/4 = 8192.
73 pwm.set_duty_cycle(c.top / 4).unwrap();
74 Timer::after_secs(1).await;
75
76 // 0% duty cycle, fully off.
77 pwm.set_duty_cycle_fully_off().unwrap();
78 Timer::after_secs(1).await;
79 }
80}
diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs
index e9a5e43a8..1692bdf36 100644
--- a/examples/rp/src/bin/rtc.rs
+++ b/examples/rp/src/bin/rtc.rs
@@ -5,16 +5,22 @@
5 5
6use defmt::*; 6use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts;
8use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc}; 9use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc};
9use embassy_time::Timer; 10use embassy_time::Timer;
10use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
11 12
13// Bind the RTC interrupt to the handler
14bind_interrupts!(struct Irqs {
15 RTC_IRQ => embassy_rp::rtc::InterruptHandler;
16});
17
12#[embassy_executor::main] 18#[embassy_executor::main]
13async fn main(_spawner: Spawner) { 19async fn main(_spawner: Spawner) {
14 let p = embassy_rp::init(Default::default()); 20 let p = embassy_rp::init(Default::default());
15 info!("Wait for 20s"); 21 info!("Wait for 20s");
16 22
17 let mut rtc = Rtc::new(p.RTC); 23 let mut rtc = Rtc::new(p.RTC, Irqs);
18 24
19 if !rtc.is_running() { 25 if !rtc.is_running() {
20 info!("Start RTC"); 26 info!("Start RTC");
diff --git a/examples/rp/src/bin/rtc_alarm.rs b/examples/rp/src/bin/rtc_alarm.rs
new file mode 100644
index 000000000..bde49ccd5
--- /dev/null
+++ b/examples/rp/src/bin/rtc_alarm.rs
@@ -0,0 +1,66 @@
1//! This example shows how to use RTC (Real Time Clock) for scheduling alarms and reacting to them.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_futures::select::{Either, select};
9use embassy_rp::bind_interrupts;
10use embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc};
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13
14// Bind the RTC interrupt to the handler
15bind_interrupts!(struct Irqs {
16 RTC_IRQ => embassy_rp::rtc::InterruptHandler;
17});
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 let p = embassy_rp::init(Default::default());
22 let mut rtc = Rtc::new(p.RTC, Irqs);
23
24 if !rtc.is_running() {
25 info!("Start RTC");
26 let now = DateTime {
27 year: 2000,
28 month: 1,
29 day: 1,
30 day_of_week: DayOfWeek::Saturday,
31 hour: 0,
32 minute: 0,
33 second: 0,
34 };
35 rtc.set_datetime(now).unwrap();
36 }
37
38 loop {
39 // Wait for 5 seconds or until the alarm is triggered
40 match select(Timer::after_secs(5), rtc.wait_for_alarm()).await {
41 // Timer expired
42 Either::First(_) => {
43 let dt = rtc.now().unwrap();
44 info!(
45 "Now: {}-{:02}-{:02} {}:{:02}:{:02}",
46 dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
47 );
48
49 // See if the alarm is already scheduled, if not, schedule it
50 if rtc.alarm_scheduled().is_none() {
51 info!("Scheduling alarm for 30 seconds from now");
52 rtc.schedule_alarm(DateTimeFilter::default().second((dt.second + 30) % 60));
53 info!("Alarm scheduled: {}", rtc.alarm_scheduled().unwrap());
54 }
55 }
56 // Alarm triggered
57 Either::Second(_) => {
58 let dt = rtc.now().unwrap();
59 info!(
60 "ALARM TRIGGERED! Now: {}-{:02}-{:02} {}:{:02}:{:02}",
61 dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
62 );
63 }
64 }
65 }
66}
diff --git a/examples/rp/src/bin/shared_bus.rs b/examples/rp/src/bin/shared_bus.rs
new file mode 100644
index 000000000..db7566b1a
--- /dev/null
+++ b/examples/rp/src/bin/shared_bus.rs
@@ -0,0 +1,115 @@
1//! This example shows how to share (async) I2C and SPI buses between multiple devices.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;
8use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
11use embassy_rp::gpio::{Level, Output};
12use embassy_rp::i2c::{self, I2c, InterruptHandler};
13use embassy_rp::peripherals::{I2C1, SPI1};
14use embassy_rp::spi::{self, Spi};
15use embassy_sync::blocking_mutex::raw::NoopRawMutex;
16use embassy_sync::mutex::Mutex;
17use embassy_time::Timer;
18use static_cell::StaticCell;
19use {defmt_rtt as _, panic_probe as _};
20
21type Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>;
22type I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>;
23
24bind_interrupts!(struct Irqs {
25 I2C1_IRQ => InterruptHandler<I2C1>;
26});
27
28#[embassy_executor::main]
29async fn main(spawner: Spawner) {
30 let p = embassy_rp::init(Default::default());
31 info!("Here we go!");
32
33 // Shared I2C bus
34 let i2c = I2c::new_async(p.I2C1, p.PIN_15, p.PIN_14, Irqs, i2c::Config::default());
35 static I2C_BUS: StaticCell<I2c1Bus> = StaticCell::new();
36 let i2c_bus = I2C_BUS.init(Mutex::new(i2c));
37
38 spawner.spawn(i2c_task_a(i2c_bus).unwrap());
39 spawner.spawn(i2c_task_b(i2c_bus).unwrap());
40
41 // Shared SPI bus
42 let spi_cfg = spi::Config::default();
43 let spi = Spi::new(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, p.DMA_CH0, p.DMA_CH1, spi_cfg);
44 static SPI_BUS: StaticCell<Spi1Bus> = StaticCell::new();
45 let spi_bus = SPI_BUS.init(Mutex::new(spi));
46
47 // Chip select pins for the SPI devices
48 let cs_a = Output::new(p.PIN_0, Level::High);
49 let cs_b = Output::new(p.PIN_1, Level::High);
50
51 spawner.spawn(spi_task_a(spi_bus, cs_a).unwrap());
52 spawner.spawn(spi_task_b(spi_bus, cs_b).unwrap());
53}
54
55#[embassy_executor::task]
56async fn i2c_task_a(i2c_bus: &'static I2c1Bus) {
57 let i2c_dev = I2cDevice::new(i2c_bus);
58 let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xC0);
59 loop {
60 info!("i2c task A");
61 Timer::after_secs(1).await;
62 }
63}
64
65#[embassy_executor::task]
66async fn i2c_task_b(i2c_bus: &'static I2c1Bus) {
67 let i2c_dev = I2cDevice::new(i2c_bus);
68 let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xDE);
69 loop {
70 info!("i2c task B");
71 Timer::after_secs(1).await;
72 }
73}
74
75#[embassy_executor::task]
76async fn spi_task_a(spi_bus: &'static Spi1Bus, cs: Output<'static>) {
77 let spi_dev = SpiDevice::new(spi_bus, cs);
78 let _sensor = DummySpiDeviceDriver::new(spi_dev);
79 loop {
80 info!("spi task A");
81 Timer::after_secs(1).await;
82 }
83}
84
85#[embassy_executor::task]
86async fn spi_task_b(spi_bus: &'static Spi1Bus, cs: Output<'static>) {
87 let spi_dev = SpiDevice::new(spi_bus, cs);
88 let _sensor = DummySpiDeviceDriver::new(spi_dev);
89 loop {
90 info!("spi task B");
91 Timer::after_secs(1).await;
92 }
93}
94
95// Dummy I2C device driver, using `embedded-hal-async`
96struct DummyI2cDeviceDriver<I2C: embedded_hal_async::i2c::I2c> {
97 _i2c: I2C,
98}
99
100impl<I2C: embedded_hal_async::i2c::I2c> DummyI2cDeviceDriver<I2C> {
101 fn new(i2c_dev: I2C, _address: u8) -> Self {
102 Self { _i2c: i2c_dev }
103 }
104}
105
106// Dummy SPI device driver, using `embedded-hal-async`
107struct DummySpiDeviceDriver<SPI: embedded_hal_async::spi::SpiDevice> {
108 _spi: SPI,
109}
110
111impl<SPI: embedded_hal_async::spi::SpiDevice> DummySpiDeviceDriver<SPI> {
112 fn new(spi_dev: SPI) -> Self {
113 Self { _spi: spi_dev }
114 }
115}
diff --git a/examples/rp/src/bin/sharing.rs b/examples/rp/src/bin/sharing.rs
new file mode 100644
index 000000000..618ab9117
--- /dev/null
+++ b/examples/rp/src/bin/sharing.rs
@@ -0,0 +1,149 @@
1//! This example shows some common strategies for sharing resources between tasks.
2//!
3//! We demonstrate five different ways of sharing, covering different use cases:
4//! - Atomics: This method is used for simple values, such as bool and u8..u32
5//! - Blocking Mutex: This is used for sharing non-async things, using Cell/RefCell for interior mutability.
6//! - Async Mutex: This is used for sharing async resources, where you need to hold the lock across await points.
7//! The async Mutex has interior mutability built-in, so no RefCell is needed.
8//! - Cell: For sharing Copy types between tasks running on the same executor.
9//! - RefCell: When you want &mut access to a value shared between tasks running on the same executor.
10//!
11//! More information: https://embassy.dev/book/#_sharing_peripherals_between_tasks
12
13#![no_std]
14#![no_main]
15
16use core::cell::{Cell, RefCell};
17use core::sync::atomic::{AtomicU32, Ordering};
18
19use cortex_m_rt::entry;
20use defmt::info;
21use embassy_executor::{Executor, InterruptExecutor};
22use embassy_rp::clocks::RoscRng;
23use embassy_rp::interrupt::{InterruptExt, Priority};
24use embassy_rp::peripherals::UART0;
25use embassy_rp::uart::{self, InterruptHandler, UartTx};
26use embassy_rp::{bind_interrupts, interrupt};
27use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
28use embassy_sync::{blocking_mutex, mutex};
29use embassy_time::{Duration, Ticker};
30use static_cell::{ConstStaticCell, StaticCell};
31use {defmt_rtt as _, panic_probe as _};
32
33type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, uart::Async>>;
34
35struct MyType {
36 inner: u32,
37}
38
39static EXECUTOR_HI: InterruptExecutor = InterruptExecutor::new();
40static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
41
42// Use Atomics for simple values
43static ATOMIC: AtomicU32 = AtomicU32::new(0);
44
45// Use blocking Mutex with Cell/RefCell for sharing non-async things
46static MUTEX_BLOCKING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<MyType>> =
47 blocking_mutex::Mutex::new(RefCell::new(MyType { inner: 0 }));
48
49bind_interrupts!(struct Irqs {
50 UART0_IRQ => InterruptHandler<UART0>;
51});
52
53#[interrupt]
54unsafe fn SWI_IRQ_0() {
55 unsafe { EXECUTOR_HI.on_interrupt() }
56}
57
58#[entry]
59fn main() -> ! {
60 let p = embassy_rp::init(Default::default());
61 info!("Here we go!");
62
63 let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, uart::Config::default());
64 // Use the async Mutex for sharing async things (built-in interior mutability)
65 static UART: StaticCell<UartAsyncMutex> = StaticCell::new();
66 let uart = UART.init(mutex::Mutex::new(uart));
67
68 // High-priority executor: runs in interrupt mode
69 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
70 let spawner = EXECUTOR_HI.start(interrupt::SWI_IRQ_0);
71 spawner.spawn(task_a(uart).unwrap());
72
73 // Low priority executor: runs in thread mode
74 let executor = EXECUTOR_LOW.init(Executor::new());
75 executor.run(|spawner| {
76 // No Mutex needed when sharing between tasks running on the same executor
77
78 // Use Cell for Copy-types
79 static CELL: ConstStaticCell<Cell<[u8; 4]>> = ConstStaticCell::new(Cell::new([0; 4]));
80 let cell = CELL.take();
81
82 // Use RefCell for &mut access
83 static REF_CELL: ConstStaticCell<RefCell<MyType>> = ConstStaticCell::new(RefCell::new(MyType { inner: 0 }));
84 let ref_cell = REF_CELL.take();
85
86 spawner.spawn(task_b(uart, cell, ref_cell).unwrap());
87 spawner.spawn(task_c(cell, ref_cell).unwrap());
88 });
89}
90
91#[embassy_executor::task]
92async fn task_a(uart: &'static UartAsyncMutex) {
93 let mut ticker = Ticker::every(Duration::from_secs(1));
94 loop {
95 let random = RoscRng.next_u32();
96
97 {
98 let mut uart = uart.lock().await;
99 uart.write(b"task a").await.unwrap();
100 // The uart lock is released when it goes out of scope
101 }
102
103 ATOMIC.store(random, Ordering::Relaxed);
104
105 MUTEX_BLOCKING.lock(|x| x.borrow_mut().inner = random);
106
107 ticker.next().await;
108 }
109}
110
111#[embassy_executor::task]
112async fn task_b(uart: &'static UartAsyncMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
113 let mut ticker = Ticker::every(Duration::from_secs(1));
114 loop {
115 let random = RoscRng.next_u32();
116
117 uart.lock().await.write(b"task b").await.unwrap();
118
119 cell.set(random.to_be_bytes());
120
121 ref_cell.borrow_mut().inner = random;
122
123 ticker.next().await;
124 }
125}
126
127#[embassy_executor::task]
128async fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
129 let mut ticker = Ticker::every(Duration::from_secs(1));
130 loop {
131 info!("=======================");
132
133 let atomic_val = ATOMIC.load(Ordering::Relaxed);
134 info!("atomic: {}", atomic_val);
135
136 MUTEX_BLOCKING.lock(|x| {
137 let val = x.borrow().inner;
138 info!("blocking mutex: {}", val);
139 });
140
141 let cell_val = cell.get();
142 info!("cell: {:?}", cell_val);
143
144 let ref_cell_val = ref_cell.borrow().inner;
145 info!("ref_cell: {:?}", ref_cell_val);
146
147 ticker.next().await;
148 }
149}
diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs
index e937b9d0a..4bf924e56 100644
--- a/examples/rp/src/bin/spi_display.rs
+++ b/examples/rp/src/bin/spi_display.rs
@@ -9,25 +9,27 @@
9use core::cell::RefCell; 9use core::cell::RefCell;
10 10
11use defmt::*; 11use defmt::*;
12use display_interface_spi::SPIInterface;
12use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; 13use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
13use embassy_executor::Spawner; 14use embassy_executor::Spawner;
14use embassy_rp::gpio::{Level, Output}; 15use embassy_rp::gpio::{Level, Output};
15use embassy_rp::spi; 16use embassy_rp::spi;
16use embassy_rp::spi::{Blocking, Spi}; 17use embassy_rp::spi::Spi;
17use embassy_sync::blocking_mutex::raw::NoopRawMutex;
18use embassy_sync::blocking_mutex::Mutex; 18use embassy_sync::blocking_mutex::Mutex;
19use embassy_sync::blocking_mutex::raw::NoopRawMutex;
19use embassy_time::Delay; 20use embassy_time::Delay;
20use embedded_graphics::image::{Image, ImageRawLE}; 21use embedded_graphics::image::{Image, ImageRawLE};
21use embedded_graphics::mono_font::ascii::FONT_10X20;
22use embedded_graphics::mono_font::MonoTextStyle; 22use embedded_graphics::mono_font::MonoTextStyle;
23use embedded_graphics::mono_font::ascii::FONT_10X20;
23use embedded_graphics::pixelcolor::Rgb565; 24use embedded_graphics::pixelcolor::Rgb565;
24use embedded_graphics::prelude::*; 25use embedded_graphics::prelude::*;
25use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; 26use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
26use embedded_graphics::text::Text; 27use embedded_graphics::text::Text;
27use st7789::{Orientation, ST7789}; 28use mipidsi::Builder;
29use mipidsi::models::ST7789;
30use mipidsi::options::{Orientation, Rotation};
28use {defmt_rtt as _, panic_probe as _}; 31use {defmt_rtt as _, panic_probe as _};
29 32
30use crate::my_display_interface::SPIDeviceInterface;
31use crate::touch::Touch; 33use crate::touch::Touch;
32 34
33const DISPLAY_FREQ: u32 = 64_000_000; 35const DISPLAY_FREQ: u32 = 64_000_000;
@@ -58,7 +60,7 @@ async fn main(_spawner: Spawner) {
58 touch_config.phase = spi::Phase::CaptureOnSecondTransition; 60 touch_config.phase = spi::Phase::CaptureOnSecondTransition;
59 touch_config.polarity = spi::Polarity::IdleHigh; 61 touch_config.polarity = spi::Polarity::IdleHigh;
60 62
61 let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); 63 let spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone());
62 let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi)); 64 let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));
63 65
64 let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); 66 let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);
@@ -74,17 +76,15 @@ async fn main(_spawner: Spawner) {
74 let _bl = Output::new(bl, Level::High); 76 let _bl = Output::new(bl, Level::High);
75 77
76 // display interface abstraction from SPI and DC 78 // display interface abstraction from SPI and DC
77 let di = SPIDeviceInterface::new(display_spi, dcx); 79 let di = SPIInterface::new(display_spi, dcx);
78 80
79 // create driver 81 // Define the display from the display interface and initialize it
80 let mut display = ST7789::new(di, rst, 240, 320); 82 let mut display = Builder::new(ST7789, di)
81 83 .display_size(240, 320)
82 // initialize 84 .reset_pin(rst)
83 display.init(&mut Delay).unwrap(); 85 .orientation(Orientation::new().rotate(Rotation::Deg90))
84 86 .init(&mut Delay)
85 // set default orientation 87 .unwrap();
86 display.set_orientation(Orientation::Landscape).unwrap();
87
88 display.clear(Rgb565::BLACK).unwrap(); 88 display.clear(Rgb565::BLACK).unwrap();
89 89
90 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); 90 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86);
@@ -167,146 +167,7 @@ mod touch {
167 167
168 let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx); 168 let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx);
169 let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy); 169 let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy);
170 if x == 0 && y == 0 { 170 if x == 0 && y == 0 { None } else { Some((x, y)) }
171 None
172 } else {
173 Some((x, y))
174 }
175 }
176 }
177}
178
179mod my_display_interface {
180 use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand};
181 use embedded_hal_1::digital::OutputPin;
182 use embedded_hal_1::spi::SpiDevice;
183
184 /// SPI display interface.
185 ///
186 /// This combines the SPI peripheral and a data/command pin
187 pub struct SPIDeviceInterface<SPI, DC> {
188 spi: SPI,
189 dc: DC,
190 }
191
192 impl<SPI, DC> SPIDeviceInterface<SPI, DC>
193 where
194 SPI: SpiDevice,
195 DC: OutputPin,
196 {
197 /// Create new SPI interface for communciation with a display driver
198 pub fn new(spi: SPI, dc: DC) -> Self {
199 Self { spi, dc }
200 }
201 }
202
203 impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC>
204 where
205 SPI: SpiDevice,
206 DC: OutputPin,
207 {
208 fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> {
209 // 1 = data, 0 = command
210 self.dc.set_low().map_err(|_| DisplayError::DCError)?;
211
212 send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?;
213 Ok(())
214 }
215
216 fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> {
217 // 1 = data, 0 = command
218 self.dc.set_high().map_err(|_| DisplayError::DCError)?;
219
220 send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?;
221 Ok(())
222 }
223 }
224
225 fn send_u8<T: SpiDevice>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> {
226 match words {
227 DataFormat::U8(slice) => spi.write(slice),
228 DataFormat::U16(slice) => {
229 use byte_slice_cast::*;
230 spi.write(slice.as_byte_slice())
231 }
232 DataFormat::U16LE(slice) => {
233 use byte_slice_cast::*;
234 for v in slice.as_mut() {
235 *v = v.to_le();
236 }
237 spi.write(slice.as_byte_slice())
238 }
239 DataFormat::U16BE(slice) => {
240 use byte_slice_cast::*;
241 for v in slice.as_mut() {
242 *v = v.to_be();
243 }
244 spi.write(slice.as_byte_slice())
245 }
246 DataFormat::U8Iter(iter) => {
247 let mut buf = [0; 32];
248 let mut i = 0;
249
250 for v in iter.into_iter() {
251 buf[i] = v;
252 i += 1;
253
254 if i == buf.len() {
255 spi.write(&buf)?;
256 i = 0;
257 }
258 }
259
260 if i > 0 {
261 spi.write(&buf[..i])?;
262 }
263
264 Ok(())
265 }
266 DataFormat::U16LEIter(iter) => {
267 use byte_slice_cast::*;
268 let mut buf = [0; 32];
269 let mut i = 0;
270
271 for v in iter.map(u16::to_le) {
272 buf[i] = v;
273 i += 1;
274
275 if i == buf.len() {
276 spi.write(&buf.as_byte_slice())?;
277 i = 0;
278 }
279 }
280
281 if i > 0 {
282 spi.write(&buf[..i].as_byte_slice())?;
283 }
284
285 Ok(())
286 }
287 DataFormat::U16BEIter(iter) => {
288 use byte_slice_cast::*;
289 let mut buf = [0; 64];
290 let mut i = 0;
291 let len = buf.len();
292
293 for v in iter.map(u16::to_be) {
294 buf[i] = v;
295 i += 1;
296
297 if i == len {
298 spi.write(&buf.as_byte_slice())?;
299 i = 0;
300 }
301 }
302
303 if i > 0 {
304 spi.write(&buf[..i].as_byte_slice())?;
305 }
306
307 Ok(())
308 }
309 _ => unimplemented!(),
310 } 171 }
311 } 172 }
312} 173}
diff --git a/examples/rp/src/bin/spi_gc9a01.rs b/examples/rp/src/bin/spi_gc9a01.rs
new file mode 100644
index 000000000..fd007b9bd
--- /dev/null
+++ b/examples/rp/src/bin/spi_gc9a01.rs
@@ -0,0 +1,125 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//!
3//! Example written for a display using the GC9A01 chip. Possibly the Waveshare RP2040-LCD-1.28
4//! (https://www.waveshare.com/wiki/RP2040-LCD-1.28)
5
6#![no_std]
7#![no_main]
8
9use core::cell::RefCell;
10
11use defmt::*;
12use display_interface_spi::SPIInterface;
13use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
14use embassy_executor::Spawner;
15use embassy_rp::clocks::RoscRng;
16use embassy_rp::gpio::{Level, Output};
17use embassy_rp::spi;
18use embassy_rp::spi::{Blocking, Spi};
19use embassy_sync::blocking_mutex::Mutex;
20use embassy_sync::blocking_mutex::raw::NoopRawMutex;
21use embassy_time::{Delay, Duration, Timer};
22use embedded_graphics::image::{Image, ImageRawLE};
23use embedded_graphics::pixelcolor::Rgb565;
24use embedded_graphics::prelude::*;
25use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
26use mipidsi::Builder;
27use mipidsi::models::GC9A01;
28use mipidsi::options::{ColorInversion, ColorOrder};
29use {defmt_rtt as _, panic_probe as _};
30
31const DISPLAY_FREQ: u32 = 64_000_000;
32const LCD_X_RES: i32 = 240;
33const LCD_Y_RES: i32 = 240;
34const FERRIS_WIDTH: u32 = 86;
35const FERRIS_HEIGHT: u32 = 64;
36
37#[embassy_executor::main]
38async fn main(_spawner: Spawner) {
39 let p = embassy_rp::init(Default::default());
40 let mut rng = RoscRng;
41
42 info!("Hello World!");
43
44 let bl = p.PIN_25;
45 let rst = p.PIN_12;
46 let display_cs = p.PIN_9;
47 let dcx = p.PIN_8;
48 let mosi = p.PIN_11;
49 let clk = p.PIN_10;
50
51 // create SPI
52 let mut display_config = spi::Config::default();
53 display_config.frequency = DISPLAY_FREQ;
54 display_config.phase = spi::Phase::CaptureOnSecondTransition;
55 display_config.polarity = spi::Polarity::IdleHigh;
56
57 let spi: Spi<'_, _, Blocking> = Spi::new_blocking_txonly(p.SPI1, clk, mosi, display_config.clone());
58 let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));
59
60 let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);
61 let dcx = Output::new(dcx, Level::Low);
62 let rst = Output::new(rst, Level::Low);
63 // dcx: 0 = command, 1 = data
64
65 // Enable LCD backlight
66 let _bl = Output::new(bl, Level::High);
67
68 // display interface abstraction from SPI and DC
69 let di = SPIInterface::new(display_spi, dcx);
70
71 // Define the display from the display interface and initialize it
72 let mut display = Builder::new(GC9A01, di)
73 .display_size(240, 240)
74 .reset_pin(rst)
75 .color_order(ColorOrder::Bgr)
76 .invert_colors(ColorInversion::Inverted)
77 .init(&mut Delay)
78 .unwrap();
79 display.clear(Rgb565::BLACK).unwrap();
80
81 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), FERRIS_WIDTH);
82 let mut ferris = Image::new(&raw_image_data, Point::zero());
83
84 let r = rng.next_u32();
85 let mut delta = Point {
86 x: ((r % 10) + 5) as i32,
87 y: (((r >> 8) % 10) + 5) as i32,
88 };
89 loop {
90 // Move Ferris
91 let bb = ferris.bounding_box();
92 let tl = bb.top_left;
93 let br = bb.bottom_right().unwrap();
94 if tl.x < 0 || br.x > LCD_X_RES {
95 delta.x = -delta.x;
96 }
97 if tl.y < 0 || br.y > LCD_Y_RES {
98 delta.y = -delta.y;
99 }
100
101 // Erase ghosting
102 let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLACK).build();
103 let mut off = Point { x: 0, y: 0 };
104 if delta.x < 0 {
105 off.x = FERRIS_WIDTH as i32;
106 }
107 Rectangle::new(tl + off, Size::new(delta.x as u32, FERRIS_HEIGHT))
108 .into_styled(style)
109 .draw(&mut display)
110 .unwrap();
111 off = Point { x: 0, y: 0 };
112 if delta.y < 0 {
113 off.y = FERRIS_HEIGHT as i32;
114 }
115 Rectangle::new(tl + off, Size::new(FERRIS_WIDTH, delta.y as u32))
116 .into_styled(style)
117 .draw(&mut display)
118 .unwrap();
119 // Translate Ferris
120 ferris.translate_mut(delta);
121 // Display the image
122 ferris.draw(&mut display).unwrap();
123 Timer::after(Duration::from_millis(50)).await;
124 }
125}
diff --git a/examples/rp/src/bin/spi_sdmmc.rs b/examples/rp/src/bin/spi_sdmmc.rs
index 4cbc82f7b..4e3c2f199 100644
--- a/examples/rp/src/bin/spi_sdmmc.rs
+++ b/examples/rp/src/bin/spi_sdmmc.rs
@@ -7,7 +7,6 @@
7#![no_main] 7#![no_main]
8 8
9use defmt::*; 9use defmt::*;
10use embassy_embedded_hal::SetConfig;
11use embassy_executor::Spawner; 10use embassy_executor::Spawner;
12use embassy_rp::spi::Spi; 11use embassy_rp::spi::Spi;
13use embassy_rp::{gpio, spi}; 12use embassy_rp::{gpio, spi};
@@ -33,7 +32,6 @@ impl embedded_sdmmc::TimeSource for DummyTimesource {
33 32
34#[embassy_executor::main] 33#[embassy_executor::main]
35async fn main(_spawner: Spawner) { 34async fn main(_spawner: Spawner) {
36 embassy_rp::pac::SIO.spinlock(31).write_value(1);
37 let p = embassy_rp::init(Default::default()); 35 let p = embassy_rp::init(Default::default());
38 36
39 // SPI clock needs to be running at <= 400kHz during initialization 37 // SPI clock needs to be running at <= 400kHz during initialization
@@ -51,7 +49,7 @@ async fn main(_spawner: Spawner) {
51 // Now that the card is initialized, the SPI clock can go faster 49 // Now that the card is initialized, the SPI clock can go faster
52 let mut config = spi::Config::default(); 50 let mut config = spi::Config::default();
53 config.frequency = 16_000_000; 51 config.frequency = 16_000_000;
54 sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok(); 52 sdcard.spi(|dev| dev.bus_mut().set_config(&config));
55 53
56 // Now let's look for volumes (also known as partitions) on our block device. 54 // Now let's look for volumes (also known as partitions) on our block device.
57 // To do this we need a Volume Manager. It will take ownership of the block device. 55 // To do this we need a Volume Manager. It will take ownership of the block device.
diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs
index fac61aa04..820daed96 100644
--- a/examples/rp/src/bin/uart_buffered_split.rs
+++ b/examples/rp/src/bin/uart_buffered_split.rs
@@ -30,10 +30,10 @@ async fn main(spawner: Spawner) {
30 let tx_buf = &mut TX_BUF.init([0; 16])[..]; 30 let tx_buf = &mut TX_BUF.init([0; 16])[..];
31 static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new(); 31 static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
32 let rx_buf = &mut RX_BUF.init([0; 16])[..]; 32 let rx_buf = &mut RX_BUF.init([0; 16])[..];
33 let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); 33 let uart = BufferedUart::new(uart, tx_pin, rx_pin, Irqs, tx_buf, rx_buf, Config::default());
34 let (rx, mut tx) = uart.split(); 34 let (mut tx, rx) = uart.split();
35 35
36 unwrap!(spawner.spawn(reader(rx))); 36 spawner.spawn(unwrap!(reader(rx)));
37 37
38 info!("Writing..."); 38 info!("Writing...");
39 loop { 39 loop {
@@ -48,7 +48,7 @@ async fn main(spawner: Spawner) {
48} 48}
49 49
50#[embassy_executor::task] 50#[embassy_executor::task]
51async fn reader(mut rx: BufferedUartRx<'static, UART0>) { 51async fn reader(mut rx: BufferedUartRx) {
52 info!("Reading..."); 52 info!("Reading...");
53 loop { 53 loop {
54 let mut buf = [0; 31]; 54 let mut buf = [0; 31];
diff --git a/examples/rp/src/bin/uart_r503.rs b/examples/rp/src/bin/uart_r503.rs
index 085be280b..a25d45b18 100644
--- a/examples/rp/src/bin/uart_r503.rs
+++ b/examples/rp/src/bin/uart_r503.rs
@@ -6,7 +6,7 @@ use embassy_executor::Spawner;
6use embassy_rp::bind_interrupts; 6use embassy_rp::bind_interrupts;
7use embassy_rp::peripherals::UART0; 7use embassy_rp::peripherals::UART0;
8use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart}; 8use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart};
9use embassy_time::{with_timeout, Duration, Timer}; 9use embassy_time::{Duration, Timer, with_timeout};
10use heapless::Vec; 10use heapless::Vec;
11use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
12 12
diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs
index a45f40756..573b45b51 100644
--- a/examples/rp/src/bin/uart_unidir.rs
+++ b/examples/rp/src/bin/uart_unidir.rs
@@ -27,7 +27,7 @@ async fn main(spawner: Spawner) {
27 let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default()); 27 let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default());
28 let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default()); 28 let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default());
29 29
30 unwrap!(spawner.spawn(reader(uart_rx))); 30 spawner.spawn(unwrap!(reader(uart_rx)));
31 31
32 info!("Writing..."); 32 info!("Writing...");
33 loop { 33 loop {
@@ -39,7 +39,7 @@ async fn main(spawner: Spawner) {
39} 39}
40 40
41#[embassy_executor::task] 41#[embassy_executor::task]
42async fn reader(mut rx: UartRx<'static, UART1, Async>) { 42async fn reader(mut rx: UartRx<'static, Async>) {
43 info!("Reading..."); 43 info!("Reading...");
44 loop { 44 loop {
45 // read a total of 4 transmissions (32 / 8) and then print the result 45 // read a total of 4 transmissions (32 / 8) and then print the result
diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs
index f1b124efa..b62a602b1 100644
--- a/examples/rp/src/bin/usb_ethernet.rs
+++ b/examples/rp/src/bin/usb_ethernet.rs
@@ -7,8 +7,9 @@
7 7
8use defmt::*; 8use defmt::*;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_net::StackResources;
10use embassy_net::tcp::TcpSocket; 11use embassy_net::tcp::TcpSocket;
11use embassy_net::{Stack, StackResources}; 12use embassy_rp::clocks::RoscRng;
12use embassy_rp::peripherals::USB; 13use embassy_rp::peripherals::USB;
13use embassy_rp::usb::{Driver, InterruptHandler}; 14use embassy_rp::usb::{Driver, InterruptHandler};
14use embassy_rp::{bind_interrupts, peripherals}; 15use embassy_rp::{bind_interrupts, peripherals};
@@ -38,13 +39,14 @@ async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! {
38} 39}
39 40
40#[embassy_executor::task] 41#[embassy_executor::task]
41async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { 42async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! {
42 stack.run().await 43 runner.run().await
43} 44}
44 45
45#[embassy_executor::main] 46#[embassy_executor::main]
46async fn main(spawner: Spawner) { 47async fn main(spawner: Spawner) {
47 let p = embassy_rp::init(Default::default()); 48 let p = embassy_rp::init(Default::default());
49 let mut rng = RoscRng;
48 50
49 // Create the driver, from the HAL. 51 // Create the driver, from the HAL.
50 let driver = Driver::new(p.USB, Irqs); 52 let driver = Driver::new(p.USB, Irqs);
@@ -57,12 +59,6 @@ async fn main(spawner: Spawner) {
57 config.max_power = 100; 59 config.max_power = 100;
58 config.max_packet_size_0 = 64; 60 config.max_packet_size_0 = 64;
59 61
60 // Required for Windows support.
61 config.composite_with_iads = true;
62 config.device_class = 0xEF;
63 config.device_sub_class = 0x02;
64 config.device_protocol = 0x01;
65
66 // Create embassy-usb DeviceBuilder using the driver and config. 62 // Create embassy-usb DeviceBuilder using the driver and config.
67 static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); 63 static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new();
68 static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); 64 static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new();
@@ -88,11 +84,11 @@ async fn main(spawner: Spawner) {
88 // Build the builder. 84 // Build the builder.
89 let usb = builder.build(); 85 let usb = builder.build();
90 86
91 unwrap!(spawner.spawn(usb_task(usb))); 87 spawner.spawn(unwrap!(usb_task(usb)));
92 88
93 static NET_STATE: StaticCell<NetState<MTU, 4, 4>> = StaticCell::new(); 89 static NET_STATE: StaticCell<NetState<MTU, 4, 4>> = StaticCell::new();
94 let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(NET_STATE.init(NetState::new()), our_mac_addr); 90 let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(NET_STATE.init(NetState::new()), our_mac_addr);
95 unwrap!(spawner.spawn(usb_ncm_task(runner))); 91 spawner.spawn(unwrap!(usb_ncm_task(runner)));
96 92
97 let config = embassy_net::Config::dhcpv4(Default::default()); 93 let config = embassy_net::Config::dhcpv4(Default::default());
98 //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { 94 //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
@@ -102,19 +98,13 @@ async fn main(spawner: Spawner) {
102 //}); 98 //});
103 99
104 // Generate random seed 100 // Generate random seed
105 let seed = 1234; // guaranteed random, chosen by a fair dice roll 101 let seed = rng.next_u64();
106 102
107 // Init network stack 103 // Init network stack
108 static STACK: StaticCell<Stack<Device<'static, MTU>>> = StaticCell::new(); 104 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
109 static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); 105 let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);
110 let stack = &*STACK.init(Stack::new(
111 device,
112 config,
113 RESOURCES.init(StackResources::<2>::new()),
114 seed,
115 ));
116 106
117 unwrap!(spawner.spawn(net_task(stack))); 107 spawner.spawn(unwrap!(net_task(runner)));
118 108
119 // And now we can use it! 109 // And now we can use it!
120 110
diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs
index a7cb322d8..2f6d169bf 100644
--- a/examples/rp/src/bin/usb_hid_keyboard.rs
+++ b/examples/rp/src/bin/usb_hid_keyboard.rs
@@ -1,7 +1,7 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::sync::atomic::{AtomicBool, Ordering}; 4use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
5 5
6use defmt::*; 6use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
@@ -10,7 +10,9 @@ use embassy_rp::bind_interrupts;
10use embassy_rp::gpio::{Input, Pull}; 10use embassy_rp::gpio::{Input, Pull};
11use embassy_rp::peripherals::USB; 11use embassy_rp::peripherals::USB;
12use embassy_rp::usb::{Driver, InterruptHandler}; 12use embassy_rp::usb::{Driver, InterruptHandler};
13use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; 13use embassy_usb::class::hid::{
14 HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State,
15};
14use embassy_usb::control::OutResponse; 16use embassy_usb::control::OutResponse;
15use embassy_usb::{Builder, Config, Handler}; 17use embassy_usb::{Builder, Config, Handler};
16use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; 18use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
@@ -20,6 +22,8 @@ bind_interrupts!(struct Irqs {
20 USBCTRL_IRQ => InterruptHandler<USB>; 22 USBCTRL_IRQ => InterruptHandler<USB>;
21}); 23});
22 24
25static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);
26
23#[embassy_executor::main] 27#[embassy_executor::main]
24async fn main(_spawner: Spawner) { 28async fn main(_spawner: Spawner) {
25 let p = embassy_rp::init(Default::default()); 29 let p = embassy_rp::init(Default::default());
@@ -33,6 +37,10 @@ async fn main(_spawner: Spawner) {
33 config.serial_number = Some("12345678"); 37 config.serial_number = Some("12345678");
34 config.max_power = 100; 38 config.max_power = 100;
35 config.max_packet_size_0 = 64; 39 config.max_packet_size_0 = 64;
40 config.composite_with_iads = false;
41 config.device_class = 0;
42 config.device_sub_class = 0;
43 config.device_protocol = 0;
36 44
37 // Create embassy-usb DeviceBuilder using the driver and config. 45 // Create embassy-usb DeviceBuilder using the driver and config.
38 // It needs some buffers for building the descriptors. 46 // It needs some buffers for building the descriptors.
@@ -63,6 +71,8 @@ async fn main(_spawner: Spawner) {
63 request_handler: None, 71 request_handler: None,
64 poll_ms: 60, 72 poll_ms: 60,
65 max_packet_size: 64, 73 max_packet_size: 64,
74 hid_subclass: HidSubclass::Boot,
75 hid_boot_protocol: HidBootProtocol::Keyboard,
66 }; 76 };
67 let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); 77 let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);
68 78
@@ -86,30 +96,46 @@ async fn main(_spawner: Spawner) {
86 info!("Waiting for HIGH on pin 16"); 96 info!("Waiting for HIGH on pin 16");
87 signal_pin.wait_for_high().await; 97 signal_pin.wait_for_high().await;
88 info!("HIGH DETECTED"); 98 info!("HIGH DETECTED");
89 // Create a report with the A key pressed. (no shift modifier) 99
90 let report = KeyboardReport { 100 if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {
91 keycodes: [4, 0, 0, 0, 0, 0], 101 match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await {
92 leds: 0, 102 Ok(()) => {}
93 modifier: 0, 103 Err(e) => warn!("Failed to send boot report: {:?}", e),
94 reserved: 0, 104 };
95 }; 105 } else {
96 // Send the report. 106 // Create a report with the A key pressed. (no shift modifier)
97 match writer.write_serialize(&report).await { 107 let report = KeyboardReport {
98 Ok(()) => {} 108 keycodes: [4, 0, 0, 0, 0, 0],
99 Err(e) => warn!("Failed to send report: {:?}", e), 109 leds: 0,
100 }; 110 modifier: 0,
111 reserved: 0,
112 };
113 // Send the report.
114 match writer.write_serialize(&report).await {
115 Ok(()) => {}
116 Err(e) => warn!("Failed to send report: {:?}", e),
117 };
118 }
119
101 signal_pin.wait_for_low().await; 120 signal_pin.wait_for_low().await;
102 info!("LOW DETECTED"); 121 info!("LOW DETECTED");
103 let report = KeyboardReport { 122 if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {
104 keycodes: [0, 0, 0, 0, 0, 0], 123 match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await {
105 leds: 0, 124 Ok(()) => {}
106 modifier: 0, 125 Err(e) => warn!("Failed to send boot report: {:?}", e),
107 reserved: 0, 126 };
108 }; 127 } else {
109 match writer.write_serialize(&report).await { 128 let report = KeyboardReport {
110 Ok(()) => {} 129 keycodes: [0, 0, 0, 0, 0, 0],
111 Err(e) => warn!("Failed to send report: {:?}", e), 130 leds: 0,
112 }; 131 modifier: 0,
132 reserved: 0,
133 };
134 match writer.write_serialize(&report).await {
135 Ok(()) => {}
136 Err(e) => warn!("Failed to send report: {:?}", e),
137 };
138 }
113 } 139 }
114 }; 140 };
115 141
@@ -135,6 +161,18 @@ impl RequestHandler for MyRequestHandler {
135 OutResponse::Accepted 161 OutResponse::Accepted
136 } 162 }
137 163
164 fn get_protocol(&self) -> HidProtocolMode {
165 let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed));
166 info!("The current HID protocol mode is: {}", protocol);
167 protocol
168 }
169
170 fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {
171 info!("Switching to HID protocol mode: {}", protocol);
172 HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed);
173 OutResponse::Accepted
174 }
175
138 fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { 176 fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {
139 info!("Set idle rate for {:?} to {:?}", id, dur); 177 info!("Set idle rate for {:?} to {:?}", id, dur);
140 } 178 }
diff --git a/examples/rp/src/bin/usb_hid_mouse.rs b/examples/rp/src/bin/usb_hid_mouse.rs
index cce344fb0..dc331cbdd 100644..100755
--- a/examples/rp/src/bin/usb_hid_mouse.rs
+++ b/examples/rp/src/bin/usb_hid_mouse.rs
@@ -1,18 +1,19 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::sync::atomic::{AtomicBool, Ordering}; 4use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
5 5
6use defmt::*; 6use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_futures::join::join; 8use embassy_futures::join::join;
9use embassy_rp::bind_interrupts; 9use embassy_rp::bind_interrupts;
10use embassy_rp::clocks::RoscRng; 10use embassy_rp::clocks::RoscRng;
11use embassy_rp::gpio::{Input, Pull};
12use embassy_rp::peripherals::USB; 11use embassy_rp::peripherals::USB;
13use embassy_rp::usb::{Driver, InterruptHandler}; 12use embassy_rp::usb::{Driver, InterruptHandler};
14use embassy_time::Timer; 13use embassy_time::Timer;
15use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; 14use embassy_usb::class::hid::{
15 HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State,
16};
16use embassy_usb::control::OutResponse; 17use embassy_usb::control::OutResponse;
17use embassy_usb::{Builder, Config, Handler}; 18use embassy_usb::{Builder, Config, Handler};
18use rand::Rng; 19use rand::Rng;
@@ -23,6 +24,8 @@ bind_interrupts!(struct Irqs {
23 USBCTRL_IRQ => InterruptHandler<USB>; 24 USBCTRL_IRQ => InterruptHandler<USB>;
24}); 25});
25 26
27static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8);
28
26#[embassy_executor::main] 29#[embassy_executor::main]
27async fn main(_spawner: Spawner) { 30async fn main(_spawner: Spawner) {
28 let p = embassy_rp::init(Default::default()); 31 let p = embassy_rp::init(Default::default());
@@ -36,6 +39,10 @@ async fn main(_spawner: Spawner) {
36 config.serial_number = Some("12345678"); 39 config.serial_number = Some("12345678");
37 config.max_power = 100; 40 config.max_power = 100;
38 config.max_packet_size_0 = 64; 41 config.max_packet_size_0 = 64;
42 config.composite_with_iads = false;
43 config.device_class = 0;
44 config.device_sub_class = 0;
45 config.device_protocol = 0;
39 46
40 // Create embassy-usb DeviceBuilder using the driver and config. 47 // Create embassy-usb DeviceBuilder using the driver and config.
41 // It needs some buffers for building the descriptors. 48 // It needs some buffers for building the descriptors.
@@ -66,6 +73,8 @@ async fn main(_spawner: Spawner) {
66 request_handler: None, 73 request_handler: None,
67 poll_ms: 60, 74 poll_ms: 60,
68 max_packet_size: 64, 75 max_packet_size: 64,
76 hid_subclass: HidSubclass::Boot,
77 hid_boot_protocol: HidBootProtocol::Mouse,
69 }; 78 };
70 let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); 79 let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);
71 80
@@ -75,12 +84,6 @@ async fn main(_spawner: Spawner) {
75 // Run the USB device. 84 // Run the USB device.
76 let usb_fut = usb.run(); 85 let usb_fut = usb.run();
77 86
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 // Enable the schmitt trigger to slightly debounce.
82 signal_pin.set_schmitt(true);
83
84 let (reader, mut writer) = hid.split(); 87 let (reader, mut writer) = hid.split();
85 88
86 // Do stuff with the class! 89 // Do stuff with the class!
@@ -90,17 +93,29 @@ async fn main(_spawner: Spawner) {
90 loop { 93 loop {
91 // every 1 second 94 // every 1 second
92 _ = Timer::after_secs(1).await; 95 _ = Timer::after_secs(1).await;
93 let report = MouseReport { 96
94 buttons: 0, 97 let x = rng.random_range(-100..100); // random small x movement
95 x: rng.gen_range(-100..100), // random small x movement 98 let y = rng.random_range(-100..100); // random small y movement
96 y: rng.gen_range(-100..100), // random small y movement 99
97 wheel: 0, 100 if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 {
98 pan: 0, 101 let buttons = 0u8;
99 }; 102 match writer.write(&[buttons, x as u8, y as u8]).await {
100 // Send the report. 103 Ok(()) => {}
101 match writer.write_serialize(&report).await { 104 Err(e) => warn!("Failed to send boot report: {:?}", e),
102 Ok(()) => {} 105 }
103 Err(e) => warn!("Failed to send report: {:?}", e), 106 } else {
107 let report = MouseReport {
108 buttons: 0,
109 x,
110 y,
111 wheel: 0,
112 pan: 0,
113 };
114 // Send the report.
115 match writer.write_serialize(&report).await {
116 Ok(()) => {}
117 Err(e) => warn!("Failed to send report: {:?}", e),
118 }
104 } 119 }
105 } 120 }
106 }; 121 };
@@ -127,6 +142,18 @@ impl RequestHandler for MyRequestHandler {
127 OutResponse::Accepted 142 OutResponse::Accepted
128 } 143 }
129 144
145 fn get_protocol(&self) -> HidProtocolMode {
146 let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed));
147 info!("The current HID protocol mode is: {}", protocol);
148 protocol
149 }
150
151 fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {
152 info!("Switching to HID protocol mode: {}", protocol);
153 HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed);
154 OutResponse::Accepted
155 }
156
130 fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) { 157 fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {
131 info!("Set idle rate for {:?} to {:?}", id, dur); 158 info!("Set idle rate for {:?} to {:?}", id, dur);
132 } 159 }
diff --git a/examples/rp/src/bin/usb_logger.rs b/examples/rp/src/bin/usb_logger.rs
index af401ed63..ed2333efc 100644
--- a/examples/rp/src/bin/usb_logger.rs
+++ b/examples/rp/src/bin/usb_logger.rs
@@ -25,7 +25,7 @@ async fn logger_task(driver: Driver<'static, USB>) {
25async fn main(spawner: Spawner) { 25async fn main(spawner: Spawner) {
26 let p = embassy_rp::init(Default::default()); 26 let p = embassy_rp::init(Default::default());
27 let driver = Driver::new(p.USB, Irqs); 27 let driver = Driver::new(p.USB, Irqs);
28 spawner.spawn(logger_task(driver)).unwrap(); 28 spawner.spawn(logger_task(driver).unwrap());
29 29
30 let mut counter = 0; 30 let mut counter = 0;
31 loop { 31 loop {
diff --git a/examples/rp/src/bin/usb_midi.rs b/examples/rp/src/bin/usb_midi.rs
index 11db1b2e1..3b7910f8b 100644
--- a/examples/rp/src/bin/usb_midi.rs
+++ b/examples/rp/src/bin/usb_midi.rs
@@ -37,13 +37,6 @@ async fn main(_spawner: Spawner) {
37 config.max_power = 100; 37 config.max_power = 100;
38 config.max_packet_size_0 = 64; 38 config.max_packet_size_0 = 64;
39 39
40 // Required for windows compatibility.
41 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
42 config.device_class = 0xEF;
43 config.device_sub_class = 0x02;
44 config.device_protocol = 0x01;
45 config.composite_with_iads = true;
46
47 // Create embassy-usb DeviceBuilder using the driver and config. 40 // Create embassy-usb DeviceBuilder using the driver and config.
48 // It needs some buffers for building the descriptors. 41 // It needs some buffers for building the descriptors.
49 let mut config_descriptor = [0; 256]; 42 let mut config_descriptor = [0; 256];
diff --git a/examples/rp/src/bin/usb_raw.rs b/examples/rp/src/bin/usb_raw.rs
index 97e7e0244..5974c04c0 100644
--- a/examples/rp/src/bin/usb_raw.rs
+++ b/examples/rp/src/bin/usb_raw.rs
@@ -84,13 +84,6 @@ async fn main(_spawner: Spawner) {
84 config.max_power = 100; 84 config.max_power = 100;
85 config.max_packet_size_0 = 64; 85 config.max_packet_size_0 = 64;
86 86
87 // // Required for windows compatibility.
88 // // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
89 config.device_class = 0xEF;
90 config.device_sub_class = 0x02;
91 config.device_protocol = 0x01;
92 config.composite_with_iads = true;
93
94 // Create embassy-usb DeviceBuilder using the driver and config. 87 // Create embassy-usb DeviceBuilder using the driver and config.
95 // It needs some buffers for building the descriptors. 88 // It needs some buffers for building the descriptors.
96 let mut config_descriptor = [0; 256]; 89 let mut config_descriptor = [0; 256];
diff --git a/examples/rp/src/bin/usb_raw_bulk.rs b/examples/rp/src/bin/usb_raw_bulk.rs
index 331c3da4c..0747901d1 100644
--- a/examples/rp/src/bin/usb_raw_bulk.rs
+++ b/examples/rp/src/bin/usb_raw_bulk.rs
@@ -62,13 +62,6 @@ async fn main(_spawner: Spawner) {
62 config.max_power = 100; 62 config.max_power = 100;
63 config.max_packet_size_0 = 64; 63 config.max_packet_size_0 = 64;
64 64
65 // // Required for windows compatibility.
66 // // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
67 config.device_class = 0xEF;
68 config.device_sub_class = 0x02;
69 config.device_protocol = 0x01;
70 config.composite_with_iads = true;
71
72 // Create embassy-usb DeviceBuilder using the driver and config. 65 // Create embassy-usb DeviceBuilder using the driver and config.
73 // It needs some buffers for building the descriptors. 66 // It needs some buffers for building the descriptors.
74 let mut config_descriptor = [0; 256]; 67 let mut config_descriptor = [0; 256];
@@ -103,8 +96,8 @@ async fn main(_spawner: Spawner) {
103 let mut function = builder.function(0xFF, 0, 0); 96 let mut function = builder.function(0xFF, 0, 0);
104 let mut interface = function.interface(); 97 let mut interface = function.interface();
105 let mut alt = interface.alt_setting(0xFF, 0, 0, None); 98 let mut alt = interface.alt_setting(0xFF, 0, 0, None);
106 let mut read_ep = alt.endpoint_bulk_out(64); 99 let mut read_ep = alt.endpoint_bulk_out(None, 64);
107 let mut write_ep = alt.endpoint_bulk_in(64); 100 let mut write_ep = alt.endpoint_bulk_in(None, 64);
108 drop(function); 101 drop(function);
109 102
110 // Build the builder. 103 // Build the builder.
diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs
index 4a802994a..23d0c9e8b 100644
--- a/examples/rp/src/bin/usb_serial.rs
+++ b/examples/rp/src/bin/usb_serial.rs
@@ -10,9 +10,9 @@ use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts; 10use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::USB; 11use embassy_rp::peripherals::USB;
12use embassy_rp::usb::{Driver, Instance, InterruptHandler}; 12use embassy_rp::usb::{Driver, Instance, InterruptHandler};
13use embassy_usb::UsbDevice;
13use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; 14use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
14use embassy_usb::driver::EndpointError; 15use embassy_usb::driver::EndpointError;
15use embassy_usb::UsbDevice;
16use static_cell::StaticCell; 16use static_cell::StaticCell;
17use {defmt_rtt as _, panic_probe as _}; 17use {defmt_rtt as _, panic_probe as _};
18 18
@@ -37,13 +37,6 @@ async fn main(spawner: Spawner) {
37 config.serial_number = Some("12345678"); 37 config.serial_number = Some("12345678");
38 config.max_power = 100; 38 config.max_power = 100;
39 config.max_packet_size_0 = 64; 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 config 40 config
48 }; 41 };
49 42
@@ -76,7 +69,7 @@ async fn main(spawner: Spawner) {
76 let usb = builder.build(); 69 let usb = builder.build();
77 70
78 // Run the USB device. 71 // Run the USB device.
79 unwrap!(spawner.spawn(usb_task(usb))); 72 spawner.spawn(unwrap!(usb_task(usb)));
80 73
81 // Do stuff with the class! 74 // Do stuff with the class!
82 loop { 75 loop {
diff --git a/examples/rp/src/bin/usb_serial_with_handler.rs b/examples/rp/src/bin/usb_serial_with_handler.rs
new file mode 100644
index 000000000..b85c9029b
--- /dev/null
+++ b/examples/rp/src/bin/usb_serial_with_handler.rs
@@ -0,0 +1,64 @@
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
8use core::str;
9
10use embassy_executor::Spawner;
11use embassy_rp::bind_interrupts;
12use embassy_rp::peripherals::USB;
13use embassy_rp::rom_data::reset_to_usb_boot;
14use embassy_rp::usb::{Driver, InterruptHandler};
15use embassy_time::Timer;
16use embassy_usb_logger::ReceiverHandler;
17use {defmt_rtt as _, panic_probe as _};
18
19bind_interrupts!(struct Irqs {
20 USBCTRL_IRQ => InterruptHandler<USB>;
21});
22
23struct Handler;
24
25impl ReceiverHandler for Handler {
26 async fn handle_data(&self, data: &[u8]) {
27 if let Ok(data) = str::from_utf8(data) {
28 let data = data.trim();
29
30 // If you are using elf2uf2-term with the '-t' flag, then when closing the serial monitor,
31 // this will automatically put the pico into boot mode
32 if data == "q" || data == "elf2uf2-term" {
33 reset_to_usb_boot(0, 0); // Restart the chip
34 } else if data.eq_ignore_ascii_case("hello") {
35 log::info!("World!");
36 } else {
37 log::info!("Recieved: {:?}", data);
38 }
39 }
40 }
41
42 fn new() -> Self {
43 Self
44 }
45}
46
47#[embassy_executor::task]
48async fn logger_task(driver: Driver<'static, USB>) {
49 embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver, Handler);
50}
51
52#[embassy_executor::main]
53async fn main(spawner: Spawner) {
54 let p = embassy_rp::init(Default::default());
55 let driver = Driver::new(p.USB, Irqs);
56 spawner.spawn(logger_task(driver).unwrap());
57
58 let mut counter = 0;
59 loop {
60 counter += 1;
61 log::info!("Tick {}", counter);
62 Timer::after_secs(1).await;
63 }
64}
diff --git a/examples/rp/src/bin/usb_serial_with_logger.rs b/examples/rp/src/bin/usb_serial_with_logger.rs
index f9cfdef94..ea13a1e27 100644
--- a/examples/rp/src/bin/usb_serial_with_logger.rs
+++ b/examples/rp/src/bin/usb_serial_with_logger.rs
@@ -37,13 +37,6 @@ async fn main(_spawner: Spawner) {
37 config.max_power = 100; 37 config.max_power = 100;
38 config.max_packet_size_0 = 64; 38 config.max_packet_size_0 = 64;
39 39
40 // Required for windows compatibility.
41 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
42 config.device_class = 0xEF;
43 config.device_sub_class = 0x02;
44 config.device_protocol = 0x01;
45 config.composite_with_iads = true;
46
47 // Create embassy-usb DeviceBuilder using the driver and config. 40 // Create embassy-usb DeviceBuilder using the driver and config.
48 // It needs some buffers for building the descriptors. 41 // It needs some buffers for building the descriptors.
49 let mut config_descriptor = [0; 256]; 42 let mut config_descriptor = [0; 256];
diff --git a/examples/rp/src/bin/usb_webusb.rs b/examples/rp/src/bin/usb_webusb.rs
index e73938ac9..5cecb92f0 100644
--- a/examples/rp/src/bin/usb_webusb.rs
+++ b/examples/rp/src/bin/usb_webusb.rs
@@ -51,12 +51,6 @@ async fn main(_spawner: Spawner) {
51 config.max_power = 100; 51 config.max_power = 100;
52 config.max_packet_size_0 = 64; 52 config.max_packet_size_0 = 64;
53 53
54 // Required for windows compatibility.
55 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
56 config.device_class = 0xff;
57 config.device_sub_class = 0x00;
58 config.device_protocol = 0x00;
59
60 // Create embassy-usb DeviceBuilder using the driver and config. 54 // Create embassy-usb DeviceBuilder using the driver and config.
61 // It needs some buffers for building the descriptors. 55 // It needs some buffers for building the descriptors.
62 let mut config_descriptor = [0; 256]; 56 let mut config_descriptor = [0; 256];
@@ -131,8 +125,8 @@ impl<'d, D: Driver<'d>> WebEndpoints<'d, D> {
131 let mut iface = func.interface(); 125 let mut iface = func.interface();
132 let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None); 126 let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None);
133 127
134 let write_ep = alt.endpoint_bulk_in(config.max_packet_size); 128 let write_ep = alt.endpoint_bulk_in(None, config.max_packet_size);
135 let read_ep = alt.endpoint_bulk_out(config.max_packet_size); 129 let read_ep = alt.endpoint_bulk_out(None, config.max_packet_size);
136 130
137 WebEndpoints { write_ep, read_ep } 131 WebEndpoints { write_ep, read_ep }
138 } 132 }
diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs
index dd0ca61a4..0828dbbb9 100644
--- a/examples/rp/src/bin/wifi_ap_tcp_server.rs
+++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs
@@ -7,12 +7,13 @@
7 7
8use core::str::from_utf8; 8use core::str::from_utf8;
9 9
10use cyw43_pio::PioSpi; 10use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};
11use defmt::*; 11use defmt::*;
12use embassy_executor::Spawner; 12use embassy_executor::Spawner;
13use embassy_net::tcp::TcpSocket; 13use embassy_net::tcp::TcpSocket;
14use embassy_net::{Config, Stack, StackResources}; 14use embassy_net::{Config, StackResources};
15use embassy_rp::bind_interrupts; 15use embassy_rp::bind_interrupts;
16use embassy_rp::clocks::RoscRng;
16use embassy_rp::gpio::{Level, Output}; 17use embassy_rp::gpio::{Level, Output};
17use embassy_rp::peripherals::{DMA_CH0, PIO0}; 18use embassy_rp::peripherals::{DMA_CH0, PIO0};
18use embassy_rp::pio::{InterruptHandler, Pio}; 19use embassy_rp::pio::{InterruptHandler, Pio};
@@ -26,13 +27,13 @@ bind_interrupts!(struct Irqs {
26}); 27});
27 28
28#[embassy_executor::task] 29#[embassy_executor::task]
29async fn wifi_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { 30async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
30 runner.run().await 31 runner.run().await
31} 32}
32 33
33#[embassy_executor::task] 34#[embassy_executor::task]
34async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { 35async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {
35 stack.run().await 36 runner.run().await
36} 37}
37 38
38#[embassy_executor::main] 39#[embassy_executor::main]
@@ -40,26 +41,36 @@ async fn main(spawner: Spawner) {
40 info!("Hello World!"); 41 info!("Hello World!");
41 42
42 let p = embassy_rp::init(Default::default()); 43 let p = embassy_rp::init(Default::default());
44 let mut rng = RoscRng;
43 45
44 let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); 46 let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin");
45 let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); 47 let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin");
46 48
47 // To make flashing faster for development, you may want to flash the firmwares independently 49 // To make flashing faster for development, you may want to flash the firmwares independently
48 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: 50 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
49 // probe-rs download 43439A0.bin ---binary-format --chip RP2040 --base-address 0x10100000 51 // probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000
50 // probe-rs download 43439A0_clm.bin ---binary-format --chip RP2040 --base-address 0x10140000 52 // probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000
51 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; 53 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };
52 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; 54 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
53 55
54 let pwr = Output::new(p.PIN_23, Level::Low); 56 let pwr = Output::new(p.PIN_23, Level::Low);
55 let cs = Output::new(p.PIN_25, Level::High); 57 let cs = Output::new(p.PIN_25, Level::High);
56 let mut pio = Pio::new(p.PIO0, Irqs); 58 let mut pio = Pio::new(p.PIO0, Irqs);
57 let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); 59 let spi = PioSpi::new(
60 &mut pio.common,
61 pio.sm0,
62 DEFAULT_CLOCK_DIVIDER,
63 pio.irq0,
64 cs,
65 p.PIN_24,
66 p.PIN_29,
67 p.DMA_CH0,
68 );
58 69
59 static STATE: StaticCell<cyw43::State> = StaticCell::new(); 70 static STATE: StaticCell<cyw43::State> = StaticCell::new();
60 let state = STATE.init(cyw43::State::new()); 71 let state = STATE.init(cyw43::State::new());
61 let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; 72 let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await;
62 unwrap!(spawner.spawn(wifi_task(runner))); 73 spawner.spawn(unwrap!(cyw43_task(runner)));
63 74
64 control.init(clm).await; 75 control.init(clm).await;
65 control 76 control
@@ -74,19 +85,13 @@ async fn main(spawner: Spawner) {
74 }); 85 });
75 86
76 // Generate random seed 87 // Generate random seed
77 let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. 88 let seed = rng.next_u64();
78 89
79 // Init network stack 90 // Init network stack
80 static STACK: StaticCell<Stack<cyw43::NetDriver<'static>>> = StaticCell::new(); 91 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
81 static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); 92 let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed);
82 let stack = &*STACK.init(Stack::new( 93
83 net_device, 94 spawner.spawn(unwrap!(net_task(runner)));
84 config,
85 RESOURCES.init(StackResources::<2>::new()),
86 seed,
87 ));
88
89 unwrap!(spawner.spawn(net_task(stack)));
90 95
91 //control.start_ap_open("cyw43", 5).await; 96 //control.start_ap_open("cyw43", 5).await;
92 control.start_ap_wpa2("cyw43", "password", 5).await; 97 control.start_ap_wpa2("cyw43", "password", 5).await;
diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs
index 7af6c8c5f..aa6ee4df0 100644
--- a/examples/rp/src/bin/wifi_blinky.rs
+++ b/examples/rp/src/bin/wifi_blinky.rs
@@ -5,7 +5,7 @@
5#![no_std] 5#![no_std]
6#![no_main] 6#![no_main]
7 7
8use cyw43_pio::PioSpi; 8use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};
9use defmt::*; 9use defmt::*;
10use embassy_executor::Spawner; 10use embassy_executor::Spawner;
11use embassy_rp::bind_interrupts; 11use embassy_rp::bind_interrupts;
@@ -21,7 +21,7 @@ bind_interrupts!(struct Irqs {
21}); 21});
22 22
23#[embassy_executor::task] 23#[embassy_executor::task]
24async fn wifi_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { 24async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
25 runner.run().await 25 runner.run().await
26} 26}
27 27
@@ -33,20 +33,29 @@ async fn main(spawner: Spawner) {
33 33
34 // To make flashing faster for development, you may want to flash the firmwares independently 34 // To make flashing faster for development, you may want to flash the firmwares independently
35 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: 35 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
36 // probe-rs download 43439A0.bin ---binary-format --chip RP2040 --base-address 0x10100000 36 // probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000
37 // probe-rs download 43439A0_clm.bin ---binary-format --chip RP2040 --base-address 0x10140000 37 // probe-rs download ../../cyw43-firmware/43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000
38 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; 38 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };
39 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; 39 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
40 40
41 let pwr = Output::new(p.PIN_23, Level::Low); 41 let pwr = Output::new(p.PIN_23, Level::Low);
42 let cs = Output::new(p.PIN_25, Level::High); 42 let cs = Output::new(p.PIN_25, Level::High);
43 let mut pio = Pio::new(p.PIO0, Irqs); 43 let mut pio = Pio::new(p.PIO0, Irqs);
44 let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); 44 let spi = PioSpi::new(
45 &mut pio.common,
46 pio.sm0,
47 DEFAULT_CLOCK_DIVIDER,
48 pio.irq0,
49 cs,
50 p.PIN_24,
51 p.PIN_29,
52 p.DMA_CH0,
53 );
45 54
46 static STATE: StaticCell<cyw43::State> = StaticCell::new(); 55 static STATE: StaticCell<cyw43::State> = StaticCell::new();
47 let state = STATE.init(cyw43::State::new()); 56 let state = STATE.init(cyw43::State::new());
48 let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; 57 let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await;
49 unwrap!(spawner.spawn(wifi_task(runner))); 58 spawner.spawn(unwrap!(cyw43_task(runner)));
50 59
51 control.init(clm).await; 60 control.init(clm).await;
52 control 61 control
diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs
index 41ad7492f..7e3de1db9 100644
--- a/examples/rp/src/bin/wifi_scan.rs
+++ b/examples/rp/src/bin/wifi_scan.rs
@@ -7,10 +7,9 @@
7 7
8use core::str; 8use core::str;
9 9
10use cyw43_pio::PioSpi; 10use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};
11use defmt::*; 11use defmt::*;
12use embassy_executor::Spawner; 12use embassy_executor::Spawner;
13use embassy_net::Stack;
14use embassy_rp::bind_interrupts; 13use embassy_rp::bind_interrupts;
15use embassy_rp::gpio::{Level, Output}; 14use embassy_rp::gpio::{Level, Output};
16use embassy_rp::peripherals::{DMA_CH0, PIO0}; 15use embassy_rp::peripherals::{DMA_CH0, PIO0};
@@ -23,15 +22,10 @@ bind_interrupts!(struct Irqs {
23}); 22});
24 23
25#[embassy_executor::task] 24#[embassy_executor::task]
26async fn wifi_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { 25async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
27 runner.run().await 26 runner.run().await
28} 27}
29 28
30#[embassy_executor::task]
31async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! {
32 stack.run().await
33}
34
35#[embassy_executor::main] 29#[embassy_executor::main]
36async fn main(spawner: Spawner) { 30async fn main(spawner: Spawner) {
37 info!("Hello World!"); 31 info!("Hello World!");
@@ -43,20 +37,29 @@ async fn main(spawner: Spawner) {
43 37
44 // To make flashing faster for development, you may want to flash the firmwares independently 38 // To make flashing faster for development, you may want to flash the firmwares independently
45 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: 39 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
46 // probe-rs download 43439A0.bin ---binary-format --chip RP2040 --base-address 0x10100000 40 // probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000
47 // probe-rs download 43439A0_clm.bin ---binary-format --chip RP2040 --base-address 0x10140000 41 // probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000
48 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; 42 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };
49 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; 43 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
50 44
51 let pwr = Output::new(p.PIN_23, Level::Low); 45 let pwr = Output::new(p.PIN_23, Level::Low);
52 let cs = Output::new(p.PIN_25, Level::High); 46 let cs = Output::new(p.PIN_25, Level::High);
53 let mut pio = Pio::new(p.PIO0, Irqs); 47 let mut pio = Pio::new(p.PIO0, Irqs);
54 let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); 48 let spi = PioSpi::new(
49 &mut pio.common,
50 pio.sm0,
51 DEFAULT_CLOCK_DIVIDER,
52 pio.irq0,
53 cs,
54 p.PIN_24,
55 p.PIN_29,
56 p.DMA_CH0,
57 );
55 58
56 static STATE: StaticCell<cyw43::State> = StaticCell::new(); 59 static STATE: StaticCell<cyw43::State> = StaticCell::new();
57 let state = STATE.init(cyw43::State::new()); 60 let state = STATE.init(cyw43::State::new());
58 let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; 61 let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await;
59 unwrap!(spawner.spawn(wifi_task(runner))); 62 spawner.spawn(unwrap!(cyw43_task(runner)));
60 63
61 control.init(clm).await; 64 control.init(clm).await;
62 control 65 control
diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs
index 272978efc..e39de4902 100644
--- a/examples/rp/src/bin/wifi_tcp_server.rs
+++ b/examples/rp/src/bin/wifi_tcp_server.rs
@@ -7,16 +7,18 @@
7 7
8use core::str::from_utf8; 8use core::str::from_utf8;
9 9
10use cyw43_pio::PioSpi; 10use cyw43::JoinOptions;
11use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};
11use defmt::*; 12use defmt::*;
12use embassy_executor::Spawner; 13use embassy_executor::Spawner;
13use embassy_net::tcp::TcpSocket; 14use embassy_net::tcp::TcpSocket;
14use embassy_net::{Config, Stack, StackResources}; 15use embassy_net::{Config, StackResources};
15use embassy_rp::bind_interrupts; 16use embassy_rp::bind_interrupts;
17use embassy_rp::clocks::RoscRng;
16use embassy_rp::gpio::{Level, Output}; 18use embassy_rp::gpio::{Level, Output};
17use embassy_rp::peripherals::{DMA_CH0, PIO0}; 19use embassy_rp::peripherals::{DMA_CH0, PIO0};
18use embassy_rp::pio::{InterruptHandler, Pio}; 20use embassy_rp::pio::{InterruptHandler, Pio};
19use embassy_time::{Duration, Timer}; 21use embassy_time::Duration;
20use embedded_io_async::Write; 22use embedded_io_async::Write;
21use static_cell::StaticCell; 23use static_cell::StaticCell;
22use {defmt_rtt as _, panic_probe as _}; 24use {defmt_rtt as _, panic_probe as _};
@@ -25,17 +27,17 @@ bind_interrupts!(struct Irqs {
25 PIO0_IRQ_0 => InterruptHandler<PIO0>; 27 PIO0_IRQ_0 => InterruptHandler<PIO0>;
26}); 28});
27 29
28const WIFI_NETWORK: &str = "EmbassyTest"; 30const WIFI_NETWORK: &str = "ssid"; // change to your network SSID
29const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; 31const WIFI_PASSWORD: &str = "pwd"; // change to your network password
30 32
31#[embassy_executor::task] 33#[embassy_executor::task]
32async fn wifi_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { 34async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
33 runner.run().await 35 runner.run().await
34} 36}
35 37
36#[embassy_executor::task] 38#[embassy_executor::task]
37async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { 39async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {
38 stack.run().await 40 runner.run().await
39} 41}
40 42
41#[embassy_executor::main] 43#[embassy_executor::main]
@@ -43,26 +45,36 @@ async fn main(spawner: Spawner) {
43 info!("Hello World!"); 45 info!("Hello World!");
44 46
45 let p = embassy_rp::init(Default::default()); 47 let p = embassy_rp::init(Default::default());
48 let mut rng = RoscRng;
46 49
47 let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); 50 let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin");
48 let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); 51 let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin");
49 52
50 // To make flashing faster for development, you may want to flash the firmwares independently 53 // To make flashing faster for development, you may want to flash the firmwares independently
51 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: 54 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
52 // probe-rs download 43439A0.bin ---binary-format --chip RP2040 --base-address 0x10100000 55 // probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000
53 // probe-rs download 43439A0_clm.bin ---binary-format --chip RP2040 --base-address 0x10140000 56 // probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000
54 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; 57 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };
55 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; 58 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
56 59
57 let pwr = Output::new(p.PIN_23, Level::Low); 60 let pwr = Output::new(p.PIN_23, Level::Low);
58 let cs = Output::new(p.PIN_25, Level::High); 61 let cs = Output::new(p.PIN_25, Level::High);
59 let mut pio = Pio::new(p.PIO0, Irqs); 62 let mut pio = Pio::new(p.PIO0, Irqs);
60 let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); 63 let spi = PioSpi::new(
64 &mut pio.common,
65 pio.sm0,
66 DEFAULT_CLOCK_DIVIDER,
67 pio.irq0,
68 cs,
69 p.PIN_24,
70 p.PIN_29,
71 p.DMA_CH0,
72 );
61 73
62 static STATE: StaticCell<cyw43::State> = StaticCell::new(); 74 static STATE: StaticCell<cyw43::State> = StaticCell::new();
63 let state = STATE.init(cyw43::State::new()); 75 let state = STATE.init(cyw43::State::new());
64 let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; 76 let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await;
65 unwrap!(spawner.spawn(wifi_task(runner))); 77 spawner.spawn(unwrap!(cyw43_task(runner)));
66 78
67 control.init(clm).await; 79 control.init(clm).await;
68 control 80 control
@@ -77,38 +89,29 @@ async fn main(spawner: Spawner) {
77 //}); 89 //});
78 90
79 // Generate random seed 91 // Generate random seed
80 let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. 92 let seed = rng.next_u64();
81 93
82 // Init network stack 94 // Init network stack
83 static STACK: StaticCell<Stack<cyw43::NetDriver<'static>>> = StaticCell::new(); 95 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
84 static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); 96 let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed);
85 let stack = &*STACK.init(Stack::new(
86 net_device,
87 config,
88 RESOURCES.init(StackResources::<2>::new()),
89 seed,
90 ));
91 97
92 unwrap!(spawner.spawn(net_task(stack))); 98 spawner.spawn(unwrap!(net_task(runner)));
93 99
94 loop { 100 while let Err(err) = control
95 //control.join_open(WIFI_NETWORK).await; 101 .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes()))
96 match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { 102 .await
97 Ok(_) => break, 103 {
98 Err(err) => { 104 info!("join failed with status={}", err.status);
99 info!("join failed with status={}", err.status);
100 }
101 }
102 } 105 }
103 106
104 // Wait for DHCP, not necessary when using static IP 107 info!("waiting for link...");
108 stack.wait_link_up().await;
109
105 info!("waiting for DHCP..."); 110 info!("waiting for DHCP...");
106 while !stack.is_config_up() { 111 stack.wait_config_up().await;
107 Timer::after_millis(100).await;
108 }
109 info!("DHCP is now up!");
110 112
111 // And now we can use it! 113 // And now we can use it!
114 info!("Stack is up!");
112 115
113 let mut rx_buffer = [0; 4096]; 116 let mut rx_buffer = [0; 4096];
114 let mut tx_buffer = [0; 4096]; 117 let mut tx_buffer = [0; 4096];
diff --git a/examples/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs
new file mode 100644
index 000000000..ce85f4b9a
--- /dev/null
+++ b/examples/rp/src/bin/wifi_webrequest.rs
@@ -0,0 +1,213 @@
1//! This example uses the RP Pico W board Wifi chip (cyw43).
2//! Connects to Wifi network and makes a web request to httpbin.org.
3
4#![no_std]
5#![no_main]
6
7use core::str::from_utf8;
8
9use cyw43::JoinOptions;
10use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};
11use defmt::*;
12use embassy_executor::Spawner;
13use embassy_net::dns::DnsSocket;
14use embassy_net::tcp::client::{TcpClient, TcpClientState};
15use embassy_net::{Config, StackResources};
16use embassy_rp::bind_interrupts;
17use embassy_rp::clocks::RoscRng;
18use embassy_rp::gpio::{Level, Output};
19use embassy_rp::peripherals::{DMA_CH0, PIO0};
20use embassy_rp::pio::{InterruptHandler, Pio};
21use embassy_time::{Duration, Timer};
22use reqwless::client::HttpClient;
23// Uncomment these for TLS requests:
24// use reqwless::client::{HttpClient, TlsConfig, TlsVerify};
25use reqwless::request::Method;
26use serde::Deserialize;
27use serde_json_core::from_slice;
28use static_cell::StaticCell;
29use {defmt_rtt as _, panic_probe as _};
30
31bind_interrupts!(struct Irqs {
32 PIO0_IRQ_0 => InterruptHandler<PIO0>;
33});
34
35const WIFI_NETWORK: &str = "ssid"; // change to your network SSID
36const WIFI_PASSWORD: &str = "pwd"; // change to your network password
37
38#[embassy_executor::task]
39async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
40 runner.run().await
41}
42
43#[embassy_executor::task]
44async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {
45 runner.run().await
46}
47
48#[embassy_executor::main]
49async fn main(spawner: Spawner) {
50 info!("Hello World!");
51
52 let p = embassy_rp::init(Default::default());
53 let mut rng = RoscRng;
54
55 let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin");
56 let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin");
57 // To make flashing faster for development, you may want to flash the firmwares independently
58 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
59 // probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000
60 // probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000
61 // let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };
62 // let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
63
64 let pwr = Output::new(p.PIN_23, Level::Low);
65 let cs = Output::new(p.PIN_25, Level::High);
66 let mut pio = Pio::new(p.PIO0, Irqs);
67 let spi = PioSpi::new(
68 &mut pio.common,
69 pio.sm0,
70 DEFAULT_CLOCK_DIVIDER,
71 pio.irq0,
72 cs,
73 p.PIN_24,
74 p.PIN_29,
75 p.DMA_CH0,
76 );
77
78 static STATE: StaticCell<cyw43::State> = StaticCell::new();
79 let state = STATE.init(cyw43::State::new());
80 let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await;
81 spawner.spawn(unwrap!(cyw43_task(runner)));
82
83 control.init(clm).await;
84 control
85 .set_power_management(cyw43::PowerManagementMode::PowerSave)
86 .await;
87
88 let config = Config::dhcpv4(Default::default());
89 // Use static IP configuration instead of DHCP
90 //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
91 // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24),
92 // dns_servers: Vec::new(),
93 // gateway: Some(Ipv4Address::new(192, 168, 69, 1)),
94 //});
95
96 // Generate random seed
97 let seed = rng.next_u64();
98
99 // Init network stack
100 static RESOURCES: StaticCell<StackResources<5>> = StaticCell::new();
101 let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed);
102
103 spawner.spawn(unwrap!(net_task(runner)));
104
105 while let Err(err) = control
106 .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes()))
107 .await
108 {
109 info!("join failed with status={}", err.status);
110 }
111
112 info!("waiting for link...");
113 stack.wait_link_up().await;
114
115 info!("waiting for DHCP...");
116 stack.wait_config_up().await;
117
118 // And now we can use it!
119 info!("Stack is up!");
120
121 // And now we can use it!
122
123 loop {
124 let mut rx_buffer = [0; 4096];
125 // Uncomment these for TLS requests:
126 // let mut tls_read_buffer = [0; 16640];
127 // let mut tls_write_buffer = [0; 16640];
128
129 let client_state = TcpClientState::<1, 4096, 4096>::new();
130 let tcp_client = TcpClient::new(stack, &client_state);
131 let dns_client = DnsSocket::new(stack);
132 // Uncomment these for TLS requests:
133 // let tls_config = TlsConfig::new(seed, &mut tls_read_buffer, &mut tls_write_buffer, TlsVerify::None);
134
135 // Using non-TLS HTTP for this example
136 let mut http_client = HttpClient::new(&tcp_client, &dns_client);
137 let url = "http://httpbin.org/json";
138 // For TLS requests, use this instead:
139 // let mut http_client = HttpClient::new_with_tls(&tcp_client, &dns_client, tls_config);
140 // let url = "https://httpbin.org/json";
141
142 info!("connecting to {}", &url);
143
144 let mut request = match http_client.request(Method::GET, url).await {
145 Ok(req) => req,
146 Err(e) => {
147 error!("Failed to make HTTP request: {:?}", e);
148 Timer::after(Duration::from_secs(5)).await;
149 continue;
150 }
151 };
152
153 let response = match request.send(&mut rx_buffer).await {
154 Ok(resp) => resp,
155 Err(e) => {
156 error!("Failed to send HTTP request: {:?}", e);
157 Timer::after(Duration::from_secs(5)).await;
158 continue;
159 }
160 };
161
162 info!("Response status: {}", response.status.0);
163
164 let body_bytes = match response.body().read_to_end().await {
165 Ok(b) => b,
166 Err(_e) => {
167 error!("Failed to read response body");
168 Timer::after(Duration::from_secs(5)).await;
169 continue;
170 }
171 };
172
173 let body = match from_utf8(body_bytes) {
174 Ok(b) => b,
175 Err(_e) => {
176 error!("Failed to parse response body as UTF-8");
177 Timer::after(Duration::from_secs(5)).await;
178 continue;
179 }
180 };
181 info!("Response body length: {} bytes", body.len());
182
183 // Parse the JSON response from httpbin.org/json
184 #[derive(Deserialize)]
185 struct SlideShow<'a> {
186 author: &'a str,
187 title: &'a str,
188 }
189
190 #[derive(Deserialize)]
191 struct HttpBinResponse<'a> {
192 #[serde(borrow)]
193 slideshow: SlideShow<'a>,
194 }
195
196 let bytes = body.as_bytes();
197 match from_slice::<HttpBinResponse>(bytes) {
198 Ok((output, _used)) => {
199 info!("Successfully parsed JSON response!");
200 info!("Slideshow title: {:?}", output.slideshow.title);
201 info!("Slideshow author: {:?}", output.slideshow.author);
202 }
203 Err(e) => {
204 error!("Failed to parse JSON response: {}", Debug2Format(&e));
205 // Log preview of response for debugging
206 let preview = if body.len() > 200 { &body[..200] } else { body };
207 info!("Response preview: {:?}", preview);
208 }
209 }
210
211 Timer::after(Duration::from_secs(5)).await;
212 }
213}
diff --git a/examples/rp/src/bin/zerocopy.rs b/examples/rp/src/bin/zerocopy.rs
index 39f03c8e4..fc5f95e6e 100644
--- a/examples/rp/src/bin/zerocopy.rs
+++ b/examples/rp/src/bin/zerocopy.rs
@@ -9,9 +9,9 @@ use core::sync::atomic::{AtomicU16, Ordering};
9use defmt::*; 9use defmt::*;
10use embassy_executor::Spawner; 10use embassy_executor::Spawner;
11use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler}; 11use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler};
12use embassy_rp::bind_interrupts;
13use embassy_rp::gpio::Pull; 12use embassy_rp::gpio::Pull;
14use embassy_rp::peripherals::DMA_CH0; 13use embassy_rp::peripherals::DMA_CH0;
14use embassy_rp::{Peri, bind_interrupts};
15use embassy_sync::blocking_mutex::raw::NoopRawMutex; 15use embassy_sync::blocking_mutex::raw::NoopRawMutex;
16use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender}; 16use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender};
17use embassy_time::{Duration, Ticker, Timer}; 17use embassy_time::{Duration, Ticker, Timer};
@@ -31,7 +31,7 @@ static MAX: AtomicU16 = AtomicU16::new(0);
31struct AdcParts { 31struct AdcParts {
32 adc: Adc<'static, Async>, 32 adc: Adc<'static, Async>,
33 pin: adc::Channel<'static>, 33 pin: adc::Channel<'static>,
34 dma: DMA_CH0, 34 dma: Peri<'static, DMA_CH0>,
35} 35}
36 36
37#[embassy_executor::main] 37#[embassy_executor::main]
@@ -52,8 +52,8 @@ async fn main(spawner: Spawner) {
52 let channel = CHANNEL.init(Channel::new(buf)); 52 let channel = CHANNEL.init(Channel::new(buf));
53 let (sender, receiver) = channel.split(); 53 let (sender, receiver) = channel.split();
54 54
55 spawner.must_spawn(consumer(receiver)); 55 spawner.spawn(consumer(receiver).unwrap());
56 spawner.must_spawn(producer(sender, adc_parts)); 56 spawner.spawn(producer(sender, adc_parts).unwrap());
57 57
58 let mut ticker = Ticker::every(Duration::from_secs(1)); 58 let mut ticker = Ticker::every(Duration::from_secs(1));
59 loop { 59 loop {
@@ -70,7 +70,10 @@ async fn producer(mut sender: Sender<'static, NoopRawMutex, SampleBuffer>, mut a
70 let buf = sender.send().await; 70 let buf = sender.send().await;
71 71
72 // Fill it with data 72 // Fill it with data
73 adc.adc.read_many(&mut adc.pin, buf, 1, &mut adc.dma).await.unwrap(); 73 adc.adc
74 .read_many(&mut adc.pin, buf, 1, adc.dma.reborrow())
75 .await
76 .unwrap();
74 77
75 // Notify the channel that the buffer is now ready to be received 78 // Notify the channel that the buffer is now ready to be received
76 sender.send_done(); 79 sender.send_done();