aboutsummaryrefslogtreecommitdiff
path: root/examples/rp/src/bin
diff options
context:
space:
mode:
authorPeter Krull <[email protected]>2024-09-23 19:02:59 +0200
committerGitHub <[email protected]>2024-09-23 19:02:59 +0200
commita2c473306f4a7c8e99add2546450ab3a7a97436e (patch)
tree5522a708e492db7d4632dc0a56fe5057244f03f0 /examples/rp/src/bin
parente02a987bafd4f0fcf9d80e7c4f6e1504b8b02cec (diff)
parent2935290a6222536d6341103f91bfd732165d3862 (diff)
Merge branch 'embassy-rs:main' into multi-signal
Diffstat (limited to 'examples/rp/src/bin')
-rw-r--r--examples/rp/src/bin/adc_dma.rs54
-rw-r--r--examples/rp/src/bin/assign_resources.rs79
-rw-r--r--examples/rp/src/bin/bluetooth.rs150
-rw-r--r--examples/rp/src/bin/ethernet_w5500_multisocket.rs24
-rw-r--r--examples/rp/src/bin/ethernet_w5500_tcp_client.rs20
-rw-r--r--examples/rp/src/bin/ethernet_w5500_tcp_server.rs20
-rw-r--r--examples/rp/src/bin/ethernet_w5500_udp.rs20
-rw-r--r--examples/rp/src/bin/i2c_async_embassy.rs85
-rw-r--r--examples/rp/src/bin/i2c_slave.rs2
-rw-r--r--examples/rp/src/bin/interrupt.rs94
-rw-r--r--examples/rp/src/bin/multicore.rs12
-rw-r--r--examples/rp/src/bin/multiprio.rs4
-rw-r--r--examples/rp/src/bin/orchestrate_tasks.rs318
-rw-r--r--examples/rp/src/bin/pio_hd44780.rs2
-rw-r--r--examples/rp/src/bin/pio_onewire.rs155
-rw-r--r--examples/rp/src/bin/pio_pwm.rs118
-rw-r--r--examples/rp/src/bin/pio_servo.rs208
-rw-r--r--examples/rp/src/bin/pio_stepper.rs2
-rw-r--r--examples/rp/src/bin/pio_uart.rs2
-rw-r--r--examples/rp/src/bin/pio_ws2812.rs7
-rw-r--r--examples/rp/src/bin/pwm.rs2
-rw-r--r--examples/rp/src/bin/pwm_input.rs3
-rw-r--r--examples/rp/src/bin/shared_bus.rs115
-rw-r--r--examples/rp/src/bin/sharing.rs150
-rw-r--r--examples/rp/src/bin/spi_sdmmc.rs83
-rw-r--r--examples/rp/src/bin/uart_buffered_split.rs2
-rw-r--r--examples/rp/src/bin/uart_r503.rs158
-rw-r--r--examples/rp/src/bin/usb_ethernet.rs25
-rw-r--r--examples/rp/src/bin/usb_hid_keyboard.rs16
-rw-r--r--examples/rp/src/bin/usb_hid_mouse.rs173
-rw-r--r--examples/rp/src/bin/usb_midi.rs2
-rw-r--r--examples/rp/src/bin/usb_raw.rs2
-rw-r--r--examples/rp/src/bin/usb_raw_bulk.rs2
-rw-r--r--examples/rp/src/bin/usb_serial.rs102
-rw-r--r--examples/rp/src/bin/usb_serial_with_logger.rs2
-rw-r--r--examples/rp/src/bin/usb_webusb.rs155
-rw-r--r--examples/rp/src/bin/wifi_ap_tcp_server.rs33
-rw-r--r--examples/rp/src/bin/wifi_blinky.rs8
-rw-r--r--examples/rp/src/bin/wifi_scan.rs13
-rw-r--r--examples/rp/src/bin/wifi_tcp_server.rs42
-rw-r--r--examples/rp/src/bin/wifi_webrequest.rs190
-rw-r--r--examples/rp/src/bin/zerocopy.rs94
42 files changed, 2561 insertions, 187 deletions
diff --git a/examples/rp/src/bin/adc_dma.rs b/examples/rp/src/bin/adc_dma.rs
new file mode 100644
index 000000000..f755cf5bf
--- /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, &mut dma).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, &mut dma)
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..ff6eff4a2
--- /dev/null
+++ b/examples/rp/src/bin/assign_resources.rs
@@ -0,0 +1,79 @@
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::gpio::{Level, Output};
18use embassy_rp::peripherals::{self, PIN_20, PIN_21};
19use embassy_time::Timer;
20use {defmt_rtt as _, panic_probe as _};
21
22#[embassy_executor::main]
23async fn main(spawner: Spawner) {
24 // initialize the peripherals
25 let p = embassy_rp::init(Default::default());
26
27 // 1) Assigning a resource to a task by passing parts of the peripherals.
28 spawner
29 .spawn(double_blinky_manually_assigned(spawner, p.PIN_20, p.PIN_21))
30 .unwrap();
31
32 // 2) Using the assign-resources macro to assign resources to a task.
33 // we perform the split, see further below for the definition of the resources struct
34 let r = split_resources!(p);
35 // and then we can use them
36 spawner.spawn(double_blinky_macro_assigned(spawner, r.leds)).unwrap();
37}
38
39// 1) Assigning a resource to a task by passing parts of the peripherals.
40#[embassy_executor::task]
41async fn double_blinky_manually_assigned(_spawner: Spawner, pin_20: PIN_20, pin_21: PIN_21) {
42 let mut led_20 = Output::new(pin_20, Level::Low);
43 let mut led_21 = Output::new(pin_21, Level::High);
44
45 loop {
46 info!("toggling leds");
47 led_20.toggle();
48 led_21.toggle();
49 Timer::after_secs(1).await;
50 }
51}
52
53// 2) Using the assign-resources macro to assign resources to a task.
54// first we define the resources we want to assign to the task using the assign_resources! macro
55// basically this will split up the peripherals struct into smaller structs, that we define here
56// naming is up to you, make sure your future self understands what you did here
57assign_resources! {
58 leds: Leds{
59 led_10: PIN_10,
60 led_11: PIN_11,
61 }
62 // add more resources to more structs if needed, for example defining one struct for each task
63}
64// this could be done in another file and imported here, but for the sake of simplicity we do it here
65// see https://github.com/adamgreig/assign-resources for more information
66
67// 2) Using the split resources in a task
68#[embassy_executor::task]
69async fn double_blinky_macro_assigned(_spawner: Spawner, r: Leds) {
70 let mut led_10 = Output::new(r.led_10, Level::Low);
71 let mut led_11 = Output::new(r.led_11, Level::High);
72
73 loop {
74 info!("toggling leds");
75 led_10.toggle();
76 led_11.toggle();
77 Timer::after_secs(1).await;
78 }
79}
diff --git a/examples/rp/src/bin/bluetooth.rs b/examples/rp/src/bin/bluetooth.rs
new file mode 100644
index 000000000..7524e7929
--- /dev/null
+++ b/examples/rp/src/bin/bluetooth.rs
@@ -0,0 +1,150 @@
1//! This example test the RP Pico W on board LED.
2//!
3//! It does not work with the RP Pico board. See blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use bt_hci::controller::ExternalController;
9use cyw43_pio::PioSpi;
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_futures::join::join3;
13use embassy_rp::bind_interrupts;
14use embassy_rp::gpio::{Level, Output};
15use embassy_rp::peripherals::{DMA_CH0, PIO0};
16use embassy_rp::pio::{InterruptHandler, Pio};
17use embassy_sync::blocking_mutex::raw::NoopRawMutex;
18use embassy_time::{Duration, Timer};
19use static_cell::StaticCell;
20use trouble_host::advertise::{AdStructure, Advertisement, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE};
21use trouble_host::attribute::{AttributeTable, CharacteristicProp, Service, Uuid};
22use trouble_host::gatt::GattEvent;
23use trouble_host::{Address, BleHost, BleHostResources, PacketQos};
24use {defmt_rtt as _, embassy_time as _, panic_probe as _};
25
26bind_interrupts!(struct Irqs {
27 PIO0_IRQ_0 => InterruptHandler<PIO0>;
28});
29
30#[embassy_executor::task]
31async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
32 runner.run().await
33}
34
35#[embassy_executor::main]
36async fn main(spawner: Spawner) {
37 let p = embassy_rp::init(Default::default());
38 let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin");
39 let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin");
40 let btfw = include_bytes!("../../../../cyw43-firmware/43439A0_btfw.bin");
41
42 // To make flashing faster for development, you may want to flash the firmwares independently
43 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
44 // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
45 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
46 // probe-rs download 43439A0_btfw.bin --format bin --chip RP2040 --base-address 0x10141400
47 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
48 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
49 //let btfw = unsafe { core::slice::from_raw_parts(0x10141400 as *const u8, 6164) };
50
51 let pwr = Output::new(p.PIN_23, Level::Low);
52 let cs = Output::new(p.PIN_25, Level::High);
53 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);
55
56 static STATE: StaticCell<cyw43::State> = StaticCell::new();
57 let state = STATE.init(cyw43::State::new());
58 let (_net_device, bt_device, mut control, runner) = cyw43::new_with_bluetooth(state, pwr, spi, fw, btfw).await;
59 unwrap!(spawner.spawn(cyw43_task(runner)));
60 control.init(clm).await;
61
62 let controller: ExternalController<_, 10> = ExternalController::new(bt_device);
63 static HOST_RESOURCES: StaticCell<BleHostResources<4, 32, 27>> = StaticCell::new();
64 let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None));
65
66 let mut ble: BleHost<'_, _> = BleHost::new(controller, host_resources);
67
68 ble.set_random_address(Address::random([0xff, 0x9f, 0x1a, 0x05, 0xe4, 0xff]));
69 let mut table: AttributeTable<'_, NoopRawMutex, 10> = AttributeTable::new();
70
71 // Generic Access Service (mandatory)
72 let id = b"Pico W Bluetooth";
73 let appearance = [0x80, 0x07];
74 let mut bat_level = [0; 1];
75 let handle = {
76 let mut svc = table.add_service(Service::new(0x1800));
77 let _ = svc.add_characteristic_ro(0x2a00, id);
78 let _ = svc.add_characteristic_ro(0x2a01, &appearance[..]);
79 svc.build();
80
81 // Generic attribute service (mandatory)
82 table.add_service(Service::new(0x1801));
83
84 // Battery service
85 let mut svc = table.add_service(Service::new(0x180f));
86
87 svc.add_characteristic(
88 0x2a19,
89 &[CharacteristicProp::Read, CharacteristicProp::Notify],
90 &mut bat_level,
91 )
92 .build()
93 };
94
95 let mut adv_data = [0; 31];
96 AdStructure::encode_slice(
97 &[
98 AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
99 AdStructure::ServiceUuids16(&[Uuid::Uuid16([0x0f, 0x18])]),
100 AdStructure::CompleteLocalName(b"Pico W Bluetooth"),
101 ],
102 &mut adv_data[..],
103 )
104 .unwrap();
105
106 let server = ble.gatt_server(&table);
107
108 info!("Starting advertising and GATT service");
109 let _ = join3(
110 ble.run(),
111 async {
112 loop {
113 match server.next().await {
114 Ok(GattEvent::Write { handle, connection: _ }) => {
115 let _ = table.get(handle, |value| {
116 info!("Write event. Value written: {:?}", value);
117 });
118 }
119 Ok(GattEvent::Read { .. }) => {
120 info!("Read event");
121 }
122 Err(e) => {
123 error!("Error processing GATT events: {:?}", e);
124 }
125 }
126 }
127 },
128 async {
129 let mut advertiser = ble
130 .advertise(
131 &Default::default(),
132 Advertisement::ConnectableScannableUndirected {
133 adv_data: &adv_data[..],
134 scan_data: &[],
135 },
136 )
137 .await
138 .unwrap();
139 let conn = advertiser.accept().await.unwrap();
140 // Keep connection alive
141 let mut tick: u8 = 0;
142 loop {
143 Timer::after(Duration::from_secs(10)).await;
144 tick += 1;
145 server.notify(handle, &conn, &[tick]).await.unwrap();
146 }
147 },
148 )
149 .await;
150}
diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs
index bd52cadca..12003adbe 100644
--- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs
+++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs
@@ -36,8 +36,8 @@ async fn ethernet_task(
36} 36}
37 37
38#[embassy_executor::task] 38#[embassy_executor::task]
39async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { 39async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
40 stack.run().await 40 runner.run().await
41} 41}
42 42
43#[embassy_executor::main] 43#[embassy_executor::main]
@@ -63,24 +63,24 @@ async fn main(spawner: Spawner) {
63 w5500_int, 63 w5500_int,
64 w5500_reset, 64 w5500_reset,
65 ) 65 )
66 .await; 66 .await
67 .unwrap();
67 unwrap!(spawner.spawn(ethernet_task(runner))); 68 unwrap!(spawner.spawn(ethernet_task(runner)));
68 69
69 // Generate random seed 70 // Generate random seed
70 let seed = rng.next_u64(); 71 let seed = rng.next_u64();
71 72
72 // Init network stack 73 // Init network stack
73 static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new();
74 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new(); 74 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
75 let stack = &*STACK.init(Stack::new( 75 let (stack, runner) = embassy_net::new(
76 device, 76 device,
77 embassy_net::Config::dhcpv4(Default::default()), 77 embassy_net::Config::dhcpv4(Default::default()),
78 RESOURCES.init(StackResources::<3>::new()), 78 RESOURCES.init(StackResources::new()),
79 seed, 79 seed,
80 )); 80 );
81 81
82 // Launch network task 82 // Launch network task
83 unwrap!(spawner.spawn(net_task(&stack))); 83 unwrap!(spawner.spawn(net_task(runner)));
84 84
85 info!("Waiting for DHCP..."); 85 info!("Waiting for DHCP...");
86 let cfg = wait_for_config(stack).await; 86 let cfg = wait_for_config(stack).await;
@@ -88,12 +88,12 @@ async fn main(spawner: Spawner) {
88 info!("IP address: {:?}", local_addr); 88 info!("IP address: {:?}", local_addr);
89 89
90 // Create two sockets listening to the same port, to handle simultaneous connections 90 // Create two sockets listening to the same port, to handle simultaneous connections
91 unwrap!(spawner.spawn(listen_task(&stack, 0, 1234))); 91 unwrap!(spawner.spawn(listen_task(stack, 0, 1234)));
92 unwrap!(spawner.spawn(listen_task(&stack, 1, 1234))); 92 unwrap!(spawner.spawn(listen_task(stack, 1, 1234)));
93} 93}
94 94
95#[embassy_executor::task(pool_size = 2)] 95#[embassy_executor::task(pool_size = 2)]
96async fn listen_task(stack: &'static Stack<Device<'static>>, id: u8, port: u16) { 96async fn listen_task(stack: Stack<'static>, id: u8, port: u16) {
97 let mut rx_buffer = [0; 4096]; 97 let mut rx_buffer = [0; 4096];
98 let mut tx_buffer = [0; 4096]; 98 let mut tx_buffer = [0; 4096];
99 let mut buf = [0; 4096]; 99 let mut buf = [0; 4096];
@@ -130,7 +130,7 @@ async fn listen_task(stack: &'static Stack<Device<'static>>, id: u8, port: u16)
130 } 130 }
131} 131}
132 132
133async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { 133async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
134 loop { 134 loop {
135 if let Some(config) = stack.config_v4() { 135 if let Some(config) = stack.config_v4() {
136 return config.clone(); 136 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..d66a43a88 100644
--- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs
+++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs
@@ -38,8 +38,8 @@ async fn ethernet_task(
38} 38}
39 39
40#[embassy_executor::task] 40#[embassy_executor::task]
41async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { 41async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
42 stack.run().await 42 runner.run().await
43} 43}
44 44
45#[embassy_executor::main] 45#[embassy_executor::main]
@@ -66,24 +66,24 @@ async fn main(spawner: Spawner) {
66 w5500_int, 66 w5500_int,
67 w5500_reset, 67 w5500_reset,
68 ) 68 )
69 .await; 69 .await
70 .unwrap();
70 unwrap!(spawner.spawn(ethernet_task(runner))); 71 unwrap!(spawner.spawn(ethernet_task(runner)));
71 72
72 // Generate random seed 73 // Generate random seed
73 let seed = rng.next_u64(); 74 let seed = rng.next_u64();
74 75
75 // Init network stack 76 // Init network stack
76 static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new(); 77 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
77 static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); 78 let (stack, runner) = embassy_net::new(
78 let stack = &*STACK.init(Stack::new(
79 device, 79 device,
80 embassy_net::Config::dhcpv4(Default::default()), 80 embassy_net::Config::dhcpv4(Default::default()),
81 RESOURCES.init(StackResources::<2>::new()), 81 RESOURCES.init(StackResources::new()),
82 seed, 82 seed,
83 )); 83 );
84 84
85 // Launch network task 85 // Launch network task
86 unwrap!(spawner.spawn(net_task(&stack))); 86 unwrap!(spawner.spawn(net_task(runner)));
87 87
88 info!("Waiting for DHCP..."); 88 info!("Waiting for DHCP...");
89 let cfg = wait_for_config(stack).await; 89 let cfg = wait_for_config(stack).await;
@@ -118,7 +118,7 @@ async fn main(spawner: Spawner) {
118 } 118 }
119} 119}
120 120
121async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { 121async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
122 loop { 122 loop {
123 if let Some(config) = stack.config_v4() { 123 if let Some(config) = stack.config_v4() {
124 return config.clone(); 124 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..97d9bd4c9 100644
--- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs
+++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs
@@ -37,8 +37,8 @@ async fn ethernet_task(
37} 37}
38 38
39#[embassy_executor::task] 39#[embassy_executor::task]
40async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { 40async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
41 stack.run().await 41 runner.run().await
42} 42}
43 43
44#[embassy_executor::main] 44#[embassy_executor::main]
@@ -65,24 +65,24 @@ async fn main(spawner: Spawner) {
65 w5500_int, 65 w5500_int,
66 w5500_reset, 66 w5500_reset,
67 ) 67 )
68 .await; 68 .await
69 .unwrap();
69 unwrap!(spawner.spawn(ethernet_task(runner))); 70 unwrap!(spawner.spawn(ethernet_task(runner)));
70 71
71 // Generate random seed 72 // Generate random seed
72 let seed = rng.next_u64(); 73 let seed = rng.next_u64();
73 74
74 // Init network stack 75 // Init network stack
75 static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new(); 76 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
76 static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); 77 let (stack, runner) = embassy_net::new(
77 let stack = &*STACK.init(Stack::new(
78 device, 78 device,
79 embassy_net::Config::dhcpv4(Default::default()), 79 embassy_net::Config::dhcpv4(Default::default()),
80 RESOURCES.init(StackResources::<2>::new()), 80 RESOURCES.init(StackResources::new()),
81 seed, 81 seed,
82 )); 82 );
83 83
84 // Launch network task 84 // Launch network task
85 unwrap!(spawner.spawn(net_task(&stack))); 85 unwrap!(spawner.spawn(net_task(runner)));
86 86
87 info!("Waiting for DHCP..."); 87 info!("Waiting for DHCP...");
88 let cfg = wait_for_config(stack).await; 88 let cfg = wait_for_config(stack).await;
@@ -127,7 +127,7 @@ async fn main(spawner: Spawner) {
127 } 127 }
128} 128}
129 129
130async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { 130async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
131 loop { 131 loop {
132 if let Some(config) = stack.config_v4() { 132 if let Some(config) = stack.config_v4() {
133 return config.clone(); 133 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..b1b5f9758 100644
--- a/examples/rp/src/bin/ethernet_w5500_udp.rs
+++ b/examples/rp/src/bin/ethernet_w5500_udp.rs
@@ -36,8 +36,8 @@ async fn ethernet_task(
36} 36}
37 37
38#[embassy_executor::task] 38#[embassy_executor::task]
39async fn net_task(stack: &'static Stack<Device<'static>>) -> ! { 39async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static>>) -> ! {
40 stack.run().await 40 runner.run().await
41} 41}
42 42
43#[embassy_executor::main] 43#[embassy_executor::main]
@@ -63,24 +63,24 @@ async fn main(spawner: Spawner) {
63 w5500_int, 63 w5500_int,
64 w5500_reset, 64 w5500_reset,
65 ) 65 )
66 .await; 66 .await
67 .unwrap();
67 unwrap!(spawner.spawn(ethernet_task(runner))); 68 unwrap!(spawner.spawn(ethernet_task(runner)));
68 69
69 // Generate random seed 70 // Generate random seed
70 let seed = rng.next_u64(); 71 let seed = rng.next_u64();
71 72
72 // Init network stack 73 // Init network stack
73 static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new(); 74 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
74 static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); 75 let (stack, runner) = embassy_net::new(
75 let stack = &*STACK.init(Stack::new(
76 device, 76 device,
77 embassy_net::Config::dhcpv4(Default::default()), 77 embassy_net::Config::dhcpv4(Default::default()),
78 RESOURCES.init(StackResources::<2>::new()), 78 RESOURCES.init(StackResources::new()),
79 seed, 79 seed,
80 )); 80 );
81 81
82 // Launch network task 82 // Launch network task
83 unwrap!(spawner.spawn(net_task(&stack))); 83 unwrap!(spawner.spawn(net_task(runner)));
84 84
85 info!("Waiting for DHCP..."); 85 info!("Waiting for DHCP...");
86 let cfg = wait_for_config(stack).await; 86 let cfg = wait_for_config(stack).await;
@@ -107,7 +107,7 @@ async fn main(spawner: Spawner) {
107 } 107 }
108} 108}
109 109
110async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfigV4 { 110async fn wait_for_config(stack: Stack<'static>) -> embassy_net::StaticConfigV4 {
111 loop { 111 loop {
112 if let Some(config) = stack.config_v4() { 112 if let Some(config) = stack.config_v4() {
113 return config.clone(); 113 return config.clone();
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_slave.rs b/examples/rp/src/bin/i2c_slave.rs
index ac470d2be..9fffb4646 100644
--- a/examples/rp/src/bin/i2c_slave.rs
+++ b/examples/rp/src/bin/i2c_slave.rs
@@ -110,7 +110,7 @@ async fn main(spawner: Spawner) {
110 let c_sda = p.PIN_1; 110 let c_sda = p.PIN_1;
111 let c_scl = p.PIN_0; 111 let c_scl = p.PIN_0;
112 let mut config = i2c::Config::default(); 112 let mut config = i2c::Config::default();
113 config.frequency = 5_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_sda, c_scl, Irqs, config);
115 115
116 unwrap!(spawner.spawn(controller_task(controller))); 116 unwrap!(spawner.spawn(controller_task(controller)));
diff --git a/examples/rp/src/bin/interrupt.rs b/examples/rp/src/bin/interrupt.rs
new file mode 100644
index 000000000..5b9d7027e
--- /dev/null
+++ b/examples/rp/src/bin/interrupt.rs
@@ -0,0 +1,94 @@
1//! This example shows how you can use raw interrupt handlers alongside embassy.
2//! The example also showcases some of the options available for sharing resources/data.
3//!
4//! In the example, an ADC reading is triggered every time the PWM wraps around.
5//! The sample data is sent down a channel, to be processed inside a low priority task.
6//! The processed data is then used to adjust the PWM duty cycle, once every second.
7
8#![no_std]
9#![no_main]
10
11use core::cell::{Cell, RefCell};
12
13use defmt::*;
14use embassy_executor::Spawner;
15use embassy_rp::adc::{self, Adc, Blocking};
16use embassy_rp::gpio::Pull;
17use embassy_rp::interrupt;
18use embassy_rp::pwm::{Config, Pwm};
19use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
20use embassy_sync::blocking_mutex::Mutex;
21use embassy_sync::channel::Channel;
22use embassy_time::{Duration, Ticker};
23use portable_atomic::{AtomicU32, Ordering};
24use static_cell::StaticCell;
25use {defmt_rtt as _, panic_probe as _};
26
27static COUNTER: AtomicU32 = AtomicU32::new(0);
28static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None));
29static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> =
30 Mutex::new(RefCell::new(None));
31static ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new();
32
33#[embassy_executor::main]
34async fn main(spawner: Spawner) {
35 embassy_rp::pac::SIO.spinlock(31).write_value(1);
36 let p = embassy_rp::init(Default::default());
37
38 let adc = Adc::new_blocking(p.ADC, Default::default());
39 let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None);
40 ADC.lock(|a| a.borrow_mut().replace((adc, p26)));
41
42 let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default());
43 PWM.lock(|p| p.borrow_mut().replace(pwm));
44
45 // Enable the interrupt for pwm slice 4
46 embassy_rp::pac::PWM.inte().modify(|w| w.set_ch4(true));
47 unsafe {
48 cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP);
49 }
50
51 // Tasks require their resources to have 'static lifetime
52 // No Mutex needed when sharing within the same executor/prio level
53 static AVG: StaticCell<Cell<u32>> = StaticCell::new();
54 let avg = AVG.init(Default::default());
55 spawner.must_spawn(processing(avg));
56
57 let mut ticker = Ticker::every(Duration::from_secs(1));
58 loop {
59 ticker.next().await;
60 let freq = COUNTER.swap(0, Ordering::Relaxed);
61 info!("pwm freq: {:?} Hz", freq);
62 info!("adc average: {:?}", avg.get());
63
64 // Update the pwm duty cycle, based on the averaged adc reading
65 let mut config = Config::default();
66 config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _;
67 PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config));
68 }
69}
70
71#[embassy_executor::task]
72async fn processing(avg: &'static Cell<u32>) {
73 let mut buffer: heapless::HistoryBuffer<u16, 100> = Default::default();
74 loop {
75 let val = ADC_VALUES.receive().await;
76 buffer.write(val);
77 let sum: u32 = buffer.iter().map(|x| *x as u32).sum();
78 avg.set(sum / buffer.len() as u32);
79 }
80}
81
82#[interrupt]
83fn PWM_IRQ_WRAP() {
84 critical_section::with(|cs| {
85 let mut adc = ADC.borrow(cs).borrow_mut();
86 let (adc, p26) = adc.as_mut().unwrap();
87 let val = adc.blocking_read(p26).unwrap();
88 ADC_VALUES.try_send(val).ok();
89
90 // Clear the interrupt, so we don't immediately re-enter this irq handler
91 PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped();
92 });
93 COUNTER.fetch_add(1, Ordering::Relaxed);
94}
diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs
index c7b087476..7cb546c91 100644
--- a/examples/rp/src/bin/multicore.rs
+++ b/examples/rp/src/bin/multicore.rs
@@ -30,10 +30,14 @@ fn main() -> ! {
30 let p = embassy_rp::init(Default::default()); 30 let p = embassy_rp::init(Default::default());
31 let led = Output::new(p.PIN_25, Level::Low); 31 let led = Output::new(p.PIN_25, Level::Low);
32 32
33 spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { 33 spawn_core1(
34 let executor1 = EXECUTOR1.init(Executor::new()); 34 p.CORE1,
35 executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); 35 unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },
36 }); 36 move || {
37 let executor1 = EXECUTOR1.init(Executor::new());
38 executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led))));
39 },
40 );
37 41
38 let executor0 = EXECUTOR0.init(Executor::new()); 42 let executor0 = EXECUTOR0.init(Executor::new());
39 executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); 43 executor0.run(|spawner| unwrap!(spawner.spawn(core0_task())));
diff --git a/examples/rp/src/bin/multiprio.rs b/examples/rp/src/bin/multiprio.rs
index 26b80c11d..2b397f97d 100644
--- a/examples/rp/src/bin/multiprio.rs
+++ b/examples/rp/src/bin/multiprio.rs
@@ -80,7 +80,7 @@ async fn run_med() {
80 info!(" [med] Starting long computation"); 80 info!(" [med] Starting long computation");
81 81
82 // Spin-wait to simulate a long CPU computation 82 // Spin-wait to simulate a long CPU computation
83 cortex_m::asm::delay(125_000_000); // ~1 second 83 embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second
84 84
85 let end = Instant::now(); 85 let end = Instant::now();
86 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ; 86 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
@@ -97,7 +97,7 @@ async fn run_low() {
97 info!("[low] Starting long computation"); 97 info!("[low] Starting long computation");
98 98
99 // Spin-wait to simulate a long CPU computation 99 // Spin-wait to simulate a long CPU computation
100 cortex_m::asm::delay(250_000_000); // ~2 seconds 100 embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds
101 101
102 let end = Instant::now(); 102 let end = Instant::now();
103 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ; 103 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
diff --git a/examples/rp/src/bin/orchestrate_tasks.rs b/examples/rp/src/bin/orchestrate_tasks.rs
new file mode 100644
index 000000000..0e21d5833
--- /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//! We demonstrate how to:
4//! - use a channel to send messages between tasks, in this case here in order to have one task control the state of the system.
5//! - use a signal to terminate a task.
6//! - use command channels to send commands to another task.
7//! - use different ways to receive messages, from a straightforwar awaiting on one channel to a more complex awaiting on multiple futures.
8//!
9//! There are more patterns to orchestrate tasks, this is just one example.
10//!
11//! We will use these tasks to generate example "state information":
12//! - a task that generates random numbers in intervals of 60s
13//! - a task that generates random numbers in intervals of 30s
14//! - a task that generates random numbers in intervals of 90s
15//! - a task that notifies about being attached/disattached from usb power
16//! - a task that measures vsys voltage in intervals of 30s
17//! - a task that consumes the state information and reacts to it
18
19#![no_std]
20#![no_main]
21
22use assign_resources::assign_resources;
23use defmt::*;
24use embassy_executor::Spawner;
25use embassy_futures::select::{select, Either};
26use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
27use embassy_rp::clocks::RoscRng;
28use embassy_rp::gpio::{Input, Pull};
29use embassy_rp::{bind_interrupts, peripherals};
30use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
31use embassy_sync::{channel, signal};
32use embassy_time::{Duration, Timer};
33use rand::RngCore;
34use {defmt_rtt as _, panic_probe as _};
35
36// This is just some preparation, see example `assign_resources.rs` for more information on this. We prep the rresources that we will be using in different tasks.
37// **Note**: This will not work with a board that has a wifi chip, because the wifi chip uses pins 24 and 29 for its own purposes. A way around this in software
38// is not trivial, at least if you intend to use wifi, too. Workaround is to wire from vsys and vbus pins to appropriate pins on the board through a voltage divider. Then use those pins.
39// For this example it will not matter much, the concept of what we are showing remains valid.
40assign_resources! {
41 vsys: Vsys {
42 adc: ADC,
43 pin_29: PIN_29,
44 },
45 vbus: Vbus {
46 pin_24: PIN_24,
47 },
48}
49
50bind_interrupts!(struct Irqs {
51 ADC_IRQ_FIFO => InterruptHandler;
52});
53
54/// This is the type of Events that we will send from the worker tasks to the orchestrating task.
55enum Events {
56 UsbPowered(bool),
57 VsysVoltage(f32),
58 FirstRandomSeed(u32),
59 SecondRandomSeed(u32),
60 ThirdRandomSeed(u32),
61 ResetFirstRandomSeed,
62}
63
64/// This is the type of Commands that we will send from the orchestrating task to the worker tasks.
65/// Note that we are lazy here and only have one command, you might want to have more.
66enum Commands {
67 /// This command will stop the appropriate worker task
68 Stop,
69}
70
71/// This is the state of the system, we will use this to orchestrate the system. This is a simple example, in a real world application this would be more complex.
72#[derive(Default, Debug, Clone, Format)]
73struct State {
74 usb_powered: bool,
75 vsys_voltage: f32,
76 first_random_seed: u32,
77 second_random_seed: u32,
78 third_random_seed: u32,
79 times_we_got_first_random_seed: u8,
80 maximum_times_we_want_first_random_seed: u8,
81}
82
83impl State {
84 fn new() -> Self {
85 Self {
86 usb_powered: false,
87 vsys_voltage: 0.0,
88 first_random_seed: 0,
89 second_random_seed: 0,
90 third_random_seed: 0,
91 times_we_got_first_random_seed: 0,
92 maximum_times_we_want_first_random_seed: 3,
93 }
94 }
95}
96
97/// Channel for the events that we want the orchestrator to react to, all state events are of the type Enum Events.
98/// We use a channel with an arbitrary size of 10, the precise size of the queue depends on your use case. This depends on how many events we
99/// expect to be generated in a given time frame and how fast the orchestrator can react to them. And then if we rather want the senders to wait for
100/// new slots in the queue or if we want the orchestrator to have a backlog of events to process. In this case here we expect to always be enough slots
101/// in the queue, so the worker tasks can in all nominal cases send their events and continue with their work without waiting.
102/// For the events we - in this case here - do not want to loose any events, so a channel is a good choice. See embassy_sync docs for other options.
103static EVENT_CHANNEL: channel::Channel<CriticalSectionRawMutex, Events, 10> = channel::Channel::new();
104
105/// Signal for stopping the first random signal task. We use a signal here, because we need no queue. It is suffiient to have one signal active.
106static STOP_FIRST_RANDOM_SIGNAL: signal::Signal<CriticalSectionRawMutex, Commands> = signal::Signal::new();
107
108/// Channel for the state that we want the consumer task to react to. We use a channel here, because we want to have a queue of state changes, although
109/// we want the queue to be of size 1, because we want to finish rwacting to the state change before the next one comes in. This is just a design choice
110/// and depends on your use case.
111static CONSUMER_CHANNEL: channel::Channel<CriticalSectionRawMutex, State, 1> = channel::Channel::new();
112
113// And now we can put all this into use
114
115/// This is the main task, that will not do very much besides spawning the other tasks. This is a design choice, you could do the
116/// orchestrating here. This is to show that we do not need a main loop here, the system will run indefinitely as long as at least one task is running.
117#[embassy_executor::main]
118async fn main(spawner: Spawner) {
119 // initialize the peripherals
120 let p = embassy_rp::init(Default::default());
121 // split the resources, for convenience - see above
122 let r = split_resources! {p};
123
124 // spawn the tasks
125 spawner.spawn(orchestrate(spawner)).unwrap();
126 spawner.spawn(random_60s(spawner)).unwrap();
127 spawner.spawn(random_90s(spawner)).unwrap();
128 spawner.spawn(usb_power(spawner, r.vbus)).unwrap();
129 spawner.spawn(vsys_voltage(spawner, r.vsys)).unwrap();
130 spawner.spawn(consumer(spawner)).unwrap();
131}
132
133/// This is the task handling the system state and orchestrating the other tasks. WEe can regard this as the "main loop" of the system.
134#[embassy_executor::task]
135async fn orchestrate(_spawner: Spawner) {
136 let mut state = State::new();
137
138 // we need to have a receiver for the events
139 let receiver = EVENT_CHANNEL.receiver();
140
141 // and we need a sender for the consumer task
142 let state_sender = CONSUMER_CHANNEL.sender();
143
144 loop {
145 // we await on the receiver, this will block until a new event is available
146 // as an alternative to this, we could also await on multiple channels, this would block until at least one of the channels has an event
147 // see the embassy_futures docs: https://docs.embassy.dev/embassy-futures/git/default/select/index.html
148 // The task random_30s does a select, if you want to have a look at that.
149 // Another reason to use select may also be that we want to have a timeout, so we can react to the absence of events within a time frame.
150 // We keep it simple here.
151 let event = receiver.receive().await;
152
153 // react to the events
154 match event {
155 Events::UsbPowered(usb_powered) => {
156 // update the state and/or react to the event here
157 state.usb_powered = usb_powered;
158 info!("Usb powered: {}", usb_powered);
159 }
160 Events::VsysVoltage(voltage) => {
161 // update the state and/or react to the event here
162 state.vsys_voltage = voltage;
163 info!("Vsys voltage: {}", voltage);
164 }
165 Events::FirstRandomSeed(seed) => {
166 // update the state and/or react to the event here
167 state.first_random_seed = seed;
168 // here we change some meta state, we count how many times we got the first random seed
169 state.times_we_got_first_random_seed += 1;
170 info!(
171 "First random seed: {}, and that was iteration {} of receiving this.",
172 seed, &state.times_we_got_first_random_seed
173 );
174 }
175 Events::SecondRandomSeed(seed) => {
176 // update the state and/or react to the event here
177 state.second_random_seed = seed;
178 info!("Second random seed: {}", seed);
179 }
180 Events::ThirdRandomSeed(seed) => {
181 // update the state and/or react to the event here
182 state.third_random_seed = seed;
183 info!("Third random seed: {}", seed);
184 }
185 Events::ResetFirstRandomSeed => {
186 // update the state and/or react to the event here
187 state.times_we_got_first_random_seed = 0;
188 state.first_random_seed = 0;
189 info!("Resetting the first random seed counter");
190 }
191 }
192 // we now have an altered state
193 // there is a crate for detecting field changes on crates.io (https://crates.io/crates/fieldset) that might be useful here
194 // for now we just keep it simple
195
196 // we send the state to the consumer task
197 // since the channel has a size of 1, this will block until the consumer task has received the state, which is what we want here in this example
198 // **Note:** It is bad design to send too much data between tasks, with no clear definition of what "too much" is. In this example we send the
199 // whole state, in a real world application you might want to send only the data, that is relevant to the consumer task AND only when it has changed.
200 // We keep it simple here.
201 state_sender.send(state.clone()).await;
202 }
203}
204
205/// This task will consume the state information and react to it. This is a simple example, in a real world application this would be more complex
206/// and we could have multiple consumer tasks, each reacting to different parts of the state.
207#[embassy_executor::task]
208async fn consumer(spawner: Spawner) {
209 // we need to have a receiver for the state
210 let receiver = CONSUMER_CHANNEL.receiver();
211 let sender = EVENT_CHANNEL.sender();
212 loop {
213 // we await on the receiver, this will block until a new state is available
214 let state = receiver.receive().await;
215 // react to the state, in this case here we just log it
216 info!("The consumer has reveived this state: {:?}", &state);
217
218 // here we react to the state, in this case here we want to start or stop the first random signal task depending on the state of the system
219 match state.times_we_got_first_random_seed {
220 max if max == state.maximum_times_we_want_first_random_seed => {
221 info!("Stopping the first random signal task");
222 // we send a command to the task
223 STOP_FIRST_RANDOM_SIGNAL.signal(Commands::Stop);
224 // we notify the orchestrator that we have sent the command
225 sender.send(Events::ResetFirstRandomSeed).await;
226 }
227 0 => {
228 // we start the task, which presents us with an interesting problem, because we may return here before the task has started
229 // here we just try and log if the task has started, in a real world application you might want to handle this more gracefully
230 info!("Starting the first random signal task");
231 match spawner.spawn(random_30s(spawner)) {
232 Ok(_) => info!("Successfully spawned random_30s task"),
233 Err(e) => info!("Failed to spawn random_30s task: {:?}", e),
234 }
235 }
236 _ => {}
237 }
238 }
239}
240
241/// This task will generate random numbers in intervals of 30s
242/// The task will terminate after it has received a command signal to stop, see the orchestrate task for that.
243/// Note that we are not spawning this task from main, as we will show how such a task can be spawned and closed dynamically.
244#[embassy_executor::task]
245async fn random_30s(_spawner: Spawner) {
246 let mut rng = RoscRng;
247 let sender = EVENT_CHANNEL.sender();
248 loop {
249 // we either await on the timer or the signal, whichever comes first.
250 let futures = select(Timer::after(Duration::from_secs(30)), STOP_FIRST_RANDOM_SIGNAL.wait()).await;
251 match futures {
252 Either::First(_) => {
253 // we received are operating on the timer
254 info!("30s are up, generating random number");
255 let random_number = rng.next_u32();
256 sender.send(Events::FirstRandomSeed(random_number)).await;
257 }
258 Either::Second(_) => {
259 // we received the signal to stop
260 info!("Received signal to stop, goodbye!");
261 break;
262 }
263 }
264 }
265}
266
267/// This task will generate random numbers in intervals of 60s
268#[embassy_executor::task]
269async fn random_60s(_spawner: Spawner) {
270 let mut rng = RoscRng;
271 let sender = EVENT_CHANNEL.sender();
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/// This task will generate random numbers in intervals of 90s
280#[embassy_executor::task]
281async fn random_90s(_spawner: Spawner) {
282 let mut rng = RoscRng;
283 let sender = EVENT_CHANNEL.sender();
284 loop {
285 Timer::after(Duration::from_secs(90)).await;
286 let random_number = rng.next_u32();
287 sender.send(Events::ThirdRandomSeed(random_number)).await;
288 }
289}
290
291/// This task will notify if we are connected to usb power
292#[embassy_executor::task]
293pub async fn usb_power(_spawner: Spawner, r: Vbus) {
294 let mut vbus_in = Input::new(r.pin_24, Pull::None);
295 let sender = EVENT_CHANNEL.sender();
296 loop {
297 sender.send(Events::UsbPowered(vbus_in.is_high())).await;
298 vbus_in.wait_for_any_edge().await;
299 }
300}
301
302/// This task will measure the vsys voltage in intervals of 30s
303#[embassy_executor::task]
304pub async fn vsys_voltage(_spawner: Spawner, r: Vsys) {
305 let mut adc = Adc::new(r.adc, Irqs, Config::default());
306 let vsys_in = r.pin_29;
307 let mut channel = Channel::new_pin(vsys_in, Pull::None);
308 let sender = EVENT_CHANNEL.sender();
309 loop {
310 // read the adc value
311 let adc_value = adc.read(&mut channel).await.unwrap();
312 // convert the adc value to voltage.
313 // 3.3 is the reference voltage, 3.0 is the factor for the inbuilt voltage divider and 4096 is the resolution of the adc
314 let voltage = (adc_value as f32) * 3.3 * 3.0 / 4096.0;
315 sender.send(Events::VsysVoltage(voltage)).await;
316 Timer::after(Duration::from_secs(30)).await;
317 }
318}
diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs
index 3fab7b5f2..6c02630e0 100644
--- a/examples/rp/src/bin/pio_hd44780.rs
+++ b/examples/rp/src/bin/pio_hd44780.rs
@@ -35,7 +35,7 @@ async fn main(_spawner: Spawner) {
35 // allowing direct connection of the display to the RP2040 without level shifters. 35 // allowing direct connection of the display to the RP2040 without level shifters.
36 let p = embassy_rp::init(Default::default()); 36 let p = embassy_rp::init(Default::default());
37 37
38 let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, { 38 let _pwm = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, {
39 let mut c = pwm::Config::default(); 39 let mut c = pwm::Config::default();
40 c.divider = 125.into(); 40 c.divider = 125.into();
41 c.top = 100; 41 c.top = 100;
diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs
new file mode 100644
index 000000000..5076101ec
--- /dev/null
+++ b/examples/rp/src/bin/pio_onewire.rs
@@ -0,0 +1,155 @@
1//! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor.
2
3#![no_std]
4#![no_main]
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::bind_interrupts;
8use embassy_rp::peripherals::PIO0;
9use embassy_rp::pio::{self, Common, Config, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine};
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12
13bind_interrupts!(struct Irqs {
14 PIO0_IRQ_0 => InterruptHandler<PIO0>;
15});
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 let p = embassy_rp::init(Default::default());
20 let mut pio = Pio::new(p.PIO0, Irqs);
21 let mut sensor = Ds18b20::new(&mut pio.common, pio.sm0, p.PIN_2);
22
23 loop {
24 sensor.start().await; // Start a new measurement
25 Timer::after_secs(1).await; // Allow 1s for the measurement to finish
26 match sensor.temperature().await {
27 Ok(temp) => info!("temp = {:?} deg C", temp),
28 _ => error!("sensor error"),
29 }
30 Timer::after_secs(1).await;
31 }
32}
33
34/// DS18B20 temperature sensor driver
35pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> {
36 sm: StateMachine<'d, PIO, SM>,
37}
38
39impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> {
40 /// Create a new instance the driver
41 pub fn new(common: &mut Common<'d, PIO>, mut sm: StateMachine<'d, PIO, SM>, pin: impl PioPin) -> Self {
42 let prg = pio_proc::pio_asm!(
43 r#"
44 .wrap_target
45 again:
46 pull block
47 mov x, osr
48 jmp !x, read
49 write:
50 set pindirs, 1
51 set pins, 0
52 loop1:
53 jmp x--,loop1
54 set pindirs, 0 [31]
55 wait 1 pin 0 [31]
56 pull block
57 mov x, osr
58 bytes1:
59 pull block
60 set y, 7
61 set pindirs, 1
62 bit1:
63 set pins, 0 [1]
64 out pins,1 [31]
65 set pins, 1 [20]
66 jmp y--,bit1
67 jmp x--,bytes1
68 set pindirs, 0 [31]
69 jmp again
70 read:
71 pull block
72 mov x, osr
73 bytes2:
74 set y, 7
75 bit2:
76 set pindirs, 1
77 set pins, 0 [1]
78 set pindirs, 0 [5]
79 in pins,1 [10]
80 jmp y--,bit2
81 jmp x--,bytes2
82 .wrap
83 "#,
84 );
85
86 let pin = common.make_pio_pin(pin);
87 let mut cfg = Config::default();
88 cfg.use_program(&common.load_program(&prg.program), &[]);
89 cfg.set_out_pins(&[&pin]);
90 cfg.set_in_pins(&[&pin]);
91 cfg.set_set_pins(&[&pin]);
92 cfg.shift_in = ShiftConfig {
93 auto_fill: true,
94 direction: ShiftDirection::Right,
95 threshold: 8,
96 };
97 cfg.clock_divider = 255_u8.into();
98 sm.set_config(&cfg);
99 sm.set_enable(true);
100 Self { sm }
101 }
102
103 /// Write bytes over the wire
104 async fn write_bytes(&mut self, bytes: &[u8]) {
105 self.sm.tx().wait_push(250).await;
106 self.sm.tx().wait_push(bytes.len() as u32 - 1).await;
107 for b in bytes {
108 self.sm.tx().wait_push(*b as u32).await;
109 }
110 }
111
112 /// Read bytes from the wire
113 async fn read_bytes(&mut self, bytes: &mut [u8]) {
114 self.sm.tx().wait_push(0).await;
115 self.sm.tx().wait_push(bytes.len() as u32 - 1).await;
116 for b in bytes.iter_mut() {
117 *b = (self.sm.rx().wait_pull().await >> 24) as u8;
118 }
119 }
120
121 /// Calculate CRC8 of the data
122 fn crc8(data: &[u8]) -> u8 {
123 let mut temp;
124 let mut data_byte;
125 let mut crc = 0;
126 for b in data {
127 data_byte = *b;
128 for _ in 0..8 {
129 temp = (crc ^ data_byte) & 0x01;
130 crc >>= 1;
131 if temp != 0 {
132 crc ^= 0x8C;
133 }
134 data_byte >>= 1;
135 }
136 }
137 crc
138 }
139
140 /// Start a new measurement. Allow at least 1000ms before getting `temperature`.
141 pub async fn start(&mut self) {
142 self.write_bytes(&[0xCC, 0x44]).await;
143 }
144
145 /// Read the temperature. Ensure >1000ms has passed since `start` before calling this.
146 pub async fn temperature(&mut self) -> Result<f32, ()> {
147 self.write_bytes(&[0xCC, 0xBE]).await;
148 let mut data = [0; 9];
149 self.read_bytes(&mut data).await;
150 match Self::crc8(&data) == 0 {
151 true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.),
152 false => Err(()),
153 }
154 }
155}
diff --git a/examples/rp/src/bin/pio_pwm.rs b/examples/rp/src/bin/pio_pwm.rs
new file mode 100644
index 000000000..23d63d435
--- /dev/null
+++ b/examples/rp/src/bin/pio_pwm.rs
@@ -0,0 +1,118 @@
1//! This example shows how to create a pwm using the PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use core::time::Duration;
6
7use embassy_executor::Spawner;
8use embassy_rp::gpio::Level;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine};
11use embassy_rp::{bind_interrupts, clocks};
12use embassy_time::Timer;
13use pio::InstructionOperands;
14use {defmt_rtt as _, panic_probe as _};
15
16const REFRESH_INTERVAL: u64 = 20000;
17
18bind_interrupts!(struct Irqs {
19 PIO0_IRQ_0 => InterruptHandler<PIO0>;
20});
21
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]
103async fn main(_spawner: Spawner) {
104 let p = embassy_rp::init(Default::default());
105 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
106
107 // Note that PIN_25 is the led pin on the Pico
108 let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25);
109 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));
110 pwm_pio.start();
111
112 let mut duration = 0;
113 loop {
114 duration = (duration + 1) % 1000;
115 pwm_pio.write(Duration::from_micros(duration));
116 Timer::after_millis(1).await;
117 }
118}
diff --git a/examples/rp/src/bin/pio_servo.rs b/examples/rp/src/bin/pio_servo.rs
new file mode 100644
index 000000000..a79540479
--- /dev/null
+++ b/examples/rp/src/bin/pio_servo.rs
@@ -0,0 +1,208 @@
1//! This example shows how to create a pwm using the PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use core::time::Duration;
6
7use embassy_executor::Spawner;
8use embassy_rp::gpio::Level;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine};
11use embassy_rp::{bind_interrupts, clocks};
12use embassy_time::Timer;
13use pio::InstructionOperands;
14use {defmt_rtt as _, panic_probe as _};
15
16const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo
17const DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo
18const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical
19const REFRESH_INTERVAL: u64 = 20000; // The period of each cycle
20
21bind_interrupts!(struct Irqs {
22 PIO0_IRQ_0 => InterruptHandler<PIO0>;
23});
24
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> {
106 pwm: PwmPio<'d, T, SM>,
107 period: Duration,
108 min_pulse_width: Duration,
109 max_pulse_width: Duration,
110 max_degree_rotation: u64,
111}
112
113impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
114 pub fn new(pwm: PwmPio<'d, T, SM>) -> Self {
115 Self {
116 pwm,
117 period: Duration::from_micros(REFRESH_INTERVAL),
118 min_pulse_width: Duration::from_micros(DEFAULT_MIN_PULSE_WIDTH),
119 max_pulse_width: Duration::from_micros(DEFAULT_MAX_PULSE_WIDTH),
120 max_degree_rotation: DEFAULT_MAX_DEGREE_ROTATION,
121 }
122 }
123
124 pub fn set_period(mut self, duration: Duration) -> Self {
125 self.period = duration;
126 self
127 }
128
129 pub fn set_min_pulse_width(mut self, duration: Duration) -> Self {
130 self.min_pulse_width = duration;
131 self
132 }
133
134 pub fn set_max_pulse_width(mut self, duration: Duration) -> Self {
135 self.max_pulse_width = duration;
136 self
137 }
138
139 pub fn set_max_degree_rotation(mut self, degree: u64) -> Self {
140 self.max_degree_rotation = degree;
141 self
142 }
143
144 pub fn build(mut self) -> Servo<'d, T, SM> {
145 self.pwm.set_period(self.period);
146 Servo {
147 pwm: self.pwm,
148 min_pulse_width: self.min_pulse_width,
149 max_pulse_width: self.max_pulse_width,
150 max_degree_rotation: self.max_degree_rotation,
151 }
152 }
153}
154
155pub struct Servo<'d, T: Instance, const SM: usize> {
156 pwm: PwmPio<'d, T, SM>,
157 min_pulse_width: Duration,
158 max_pulse_width: Duration,
159 max_degree_rotation: u64,
160}
161
162impl<'d, T: Instance, const SM: usize> Servo<'d, T, SM> {
163 pub fn start(&mut self) {
164 self.pwm.start();
165 }
166
167 pub fn stop(&mut self) {
168 self.pwm.stop();
169 }
170
171 pub fn write_time(&mut self, duration: Duration) {
172 self.pwm.write(duration);
173 }
174
175 pub fn rotate(&mut self, degree: u64) {
176 let degree_per_nano_second = (self.max_pulse_width.as_nanos() as u64 - self.min_pulse_width.as_nanos() as u64)
177 / self.max_degree_rotation;
178 let mut duration =
179 Duration::from_nanos(degree * degree_per_nano_second + self.min_pulse_width.as_nanos() as u64);
180 if self.max_pulse_width < duration {
181 duration = self.max_pulse_width;
182 }
183
184 self.pwm.write(duration);
185 }
186}
187
188#[embassy_executor::main]
189async fn main(_spawner: Spawner) {
190 let p = embassy_rp::init(Default::default());
191 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
192
193 let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1);
194 let mut servo = ServoBuilder::new(pwm_pio)
195 .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.
197 .set_max_pulse_width(Duration::from_micros(2600)) // Along with this value.
198 .build();
199
200 servo.start();
201
202 let mut degree = 0;
203 loop {
204 degree = (degree + 1) % 120;
205 servo.rotate(degree);
206 Timer::after_millis(50).await;
207 }
208}
diff --git a/examples/rp/src/bin/pio_stepper.rs b/examples/rp/src/bin/pio_stepper.rs
index ab9ecf623..4952f4fbd 100644
--- a/examples/rp/src/bin/pio_stepper.rs
+++ b/examples/rp/src/bin/pio_stepper.rs
@@ -69,7 +69,7 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
69 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed(); 69 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
70 assert!(clock_divider <= 65536, "clkdiv must be <= 65536"); 70 assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
71 assert!(clock_divider >= 1, "clkdiv must be >= 1"); 71 assert!(clock_divider >= 1, "clkdiv must be >= 1");
72 T::PIO.sm(SM).clkdiv().write(|w| w.0 = clock_divider.to_bits() << 8); 72 self.sm.set_clock_divider(clock_divider);
73 self.sm.clkdiv_restart(); 73 self.sm.clkdiv_restart();
74 } 74 }
75 75
diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs
index a07f1c180..53b696309 100644
--- a/examples/rp/src/bin/pio_uart.rs
+++ b/examples/rp/src/bin/pio_uart.rs
@@ -60,7 +60,6 @@ async fn main(_spawner: Spawner) {
60 60
61 // Create embassy-usb DeviceBuilder using the driver and config. 61 // Create embassy-usb DeviceBuilder using the driver and config.
62 // It needs some buffers for building the descriptors. 62 // It needs some buffers for building the descriptors.
63 let mut device_descriptor = [0; 256];
64 let mut config_descriptor = [0; 256]; 63 let mut config_descriptor = [0; 256];
65 let mut bos_descriptor = [0; 256]; 64 let mut bos_descriptor = [0; 256];
66 let mut control_buf = [0; 64]; 65 let mut control_buf = [0; 64];
@@ -70,7 +69,6 @@ async fn main(_spawner: Spawner) {
70 let mut builder = Builder::new( 69 let mut builder = Builder::new(
71 driver, 70 driver,
72 config, 71 config,
73 &mut device_descriptor,
74 &mut config_descriptor, 72 &mut config_descriptor,
75 &mut bos_descriptor, 73 &mut bos_descriptor,
76 &mut [], // no msos descriptors 74 &mut [], // no msos descriptors
diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs
index 9a97cb8a7..ac145933c 100644
--- a/examples/rp/src/bin/pio_ws2812.rs
+++ b/examples/rp/src/bin/pio_ws2812.rs
@@ -12,7 +12,7 @@ use embassy_rp::pio::{
12 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 12 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
13}; 13};
14use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; 14use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
15use embassy_time::Timer; 15use embassy_time::{Duration, Ticker, Timer};
16use fixed::types::U24F8; 16use fixed::types::U24F8;
17use fixed_macro::fixed; 17use fixed_macro::fixed;
18use smart_leds::RGB8; 18use smart_leds::RGB8;
@@ -107,6 +107,8 @@ impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
107 107
108 // DMA transfer 108 // DMA transfer
109 self.sm.tx().dma_push(self.dma.reborrow(), &words).await; 109 self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
110
111 Timer::after_micros(55).await;
110 } 112 }
111} 113}
112 114
@@ -143,6 +145,7 @@ async fn main(_spawner: Spawner) {
143 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); 145 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16);
144 146
145 // Loop forever making RGB values and pushing them out to the WS2812. 147 // Loop forever making RGB values and pushing them out to the WS2812.
148 let mut ticker = Ticker::every(Duration::from_millis(10));
146 loop { 149 loop {
147 for j in 0..(256 * 5) { 150 for j in 0..(256 * 5) {
148 debug!("New Colors:"); 151 debug!("New Colors:");
@@ -152,7 +155,7 @@ async fn main(_spawner: Spawner) {
152 } 155 }
153 ws2812.write(&data).await; 156 ws2812.write(&data).await;
154 157
155 Timer::after_millis(10).await; 158 ticker.next().await;
156 } 159 }
157 } 160 }
158} 161}
diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs
index 4fb62546d..26e233260 100644
--- a/examples/rp/src/bin/pwm.rs
+++ b/examples/rp/src/bin/pwm.rs
@@ -18,7 +18,7 @@ async fn main(_spawner: Spawner) {
18 let mut c: Config = Default::default(); 18 let mut c: Config = Default::default();
19 c.top = 0x8000; 19 c.top = 0x8000;
20 c.compare_b = 8; 20 c.compare_b = 8;
21 let mut pwm = Pwm::new_output_b(p.PWM_CH4, p.PIN_25, c.clone()); 21 let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone());
22 22
23 loop { 23 loop {
24 info!("current LED duty cycle: {}/32768", c.compare_b); 24 info!("current LED duty cycle: {}/32768", c.compare_b);
diff --git a/examples/rp/src/bin/pwm_input.rs b/examples/rp/src/bin/pwm_input.rs
index e7bcbfbd4..bf454a936 100644
--- a/examples/rp/src/bin/pwm_input.rs
+++ b/examples/rp/src/bin/pwm_input.rs
@@ -5,6 +5,7 @@
5 5
6use defmt::*; 6use defmt::*;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::gpio::Pull;
8use embassy_rp::pwm::{Config, InputMode, Pwm}; 9use embassy_rp::pwm::{Config, InputMode, Pwm};
9use embassy_time::{Duration, Ticker}; 10use embassy_time::{Duration, Ticker};
10use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
@@ -14,7 +15,7 @@ async fn main(_spawner: Spawner) {
14 let p = embassy_rp::init(Default::default()); 15 let p = embassy_rp::init(Default::default());
15 16
16 let cfg: Config = Default::default(); 17 let cfg: Config = Default::default();
17 let pwm = Pwm::new_input(p.PWM_CH2, p.PIN_5, InputMode::RisingEdge, cfg); 18 let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, Pull::None, InputMode::RisingEdge, cfg);
18 19
19 let mut ticker = Ticker::every(Duration::from_secs(1)); 20 let mut ticker = Ticker::every(Duration::from_secs(1));
20 loop { 21 loop {
diff --git a/examples/rp/src/bin/shared_bus.rs b/examples/rp/src/bin/shared_bus.rs
new file mode 100644
index 000000000..c6cb5d64c
--- /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::{AnyPin, 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.must_spawn(i2c_task_a(i2c_bus));
39 spawner.must_spawn(i2c_task_b(i2c_bus));
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(AnyPin::from(p.PIN_0), Level::High);
49 let cs_b = Output::new(AnyPin::from(p.PIN_1), Level::High);
50
51 spawner.must_spawn(spi_task_a(spi_bus, cs_a));
52 spawner.must_spawn(spi_task_b(spi_bus, cs_b));
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..5416e20ce
--- /dev/null
+++ b/examples/rp/src/bin/sharing.rs
@@ -0,0 +1,150 @@
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 rand::RngCore;
31use static_cell::{ConstStaticCell, StaticCell};
32use {defmt_rtt as _, panic_probe as _};
33
34type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>;
35
36struct MyType {
37 inner: u32,
38}
39
40static EXECUTOR_HI: InterruptExecutor = InterruptExecutor::new();
41static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
42
43// Use Atomics for simple values
44static ATOMIC: AtomicU32 = AtomicU32::new(0);
45
46// Use blocking Mutex with Cell/RefCell for sharing non-async things
47static MUTEX_BLOCKING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<MyType>> =
48 blocking_mutex::Mutex::new(RefCell::new(MyType { inner: 0 }));
49
50bind_interrupts!(struct Irqs {
51 UART0_IRQ => InterruptHandler<UART0>;
52});
53
54#[interrupt]
55unsafe fn SWI_IRQ_0() {
56 EXECUTOR_HI.on_interrupt()
57}
58
59#[entry]
60fn main() -> ! {
61 let p = embassy_rp::init(Default::default());
62 info!("Here we go!");
63
64 let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, uart::Config::default());
65 // Use the async Mutex for sharing async things (built-in interior mutability)
66 static UART: StaticCell<UartAsyncMutex> = StaticCell::new();
67 let uart = UART.init(mutex::Mutex::new(uart));
68
69 // High-priority executor: runs in interrupt mode
70 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
71 let spawner = EXECUTOR_HI.start(interrupt::SWI_IRQ_0);
72 spawner.must_spawn(task_a(uart));
73
74 // Low priority executor: runs in thread mode
75 let executor = EXECUTOR_LOW.init(Executor::new());
76 executor.run(|spawner| {
77 // No Mutex needed when sharing between tasks running on the same executor
78
79 // Use Cell for Copy-types
80 static CELL: ConstStaticCell<Cell<[u8; 4]>> = ConstStaticCell::new(Cell::new([0; 4]));
81 let cell = CELL.take();
82
83 // Use RefCell for &mut access
84 static REF_CELL: ConstStaticCell<RefCell<MyType>> = ConstStaticCell::new(RefCell::new(MyType { inner: 0 }));
85 let ref_cell = REF_CELL.take();
86
87 spawner.must_spawn(task_b(uart, cell, ref_cell));
88 spawner.must_spawn(task_c(cell, ref_cell));
89 });
90}
91
92#[embassy_executor::task]
93async fn task_a(uart: &'static UartAsyncMutex) {
94 let mut ticker = Ticker::every(Duration::from_secs(1));
95 loop {
96 let random = RoscRng.next_u32();
97
98 {
99 let mut uart = uart.lock().await;
100 uart.write(b"task a").await.unwrap();
101 // The uart lock is released when it goes out of scope
102 }
103
104 ATOMIC.store(random, Ordering::Relaxed);
105
106 MUTEX_BLOCKING.lock(|x| x.borrow_mut().inner = random);
107
108 ticker.next().await;
109 }
110}
111
112#[embassy_executor::task]
113async fn task_b(uart: &'static UartAsyncMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
114 let mut ticker = Ticker::every(Duration::from_secs(1));
115 loop {
116 let random = RoscRng.next_u32();
117
118 uart.lock().await.write(b"task b").await.unwrap();
119
120 cell.set(random.to_be_bytes());
121
122 ref_cell.borrow_mut().inner = random;
123
124 ticker.next().await;
125 }
126}
127
128#[embassy_executor::task]
129async fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
130 let mut ticker = Ticker::every(Duration::from_secs(1));
131 loop {
132 info!("=======================");
133
134 let atomic_val = ATOMIC.load(Ordering::Relaxed);
135 info!("atomic: {}", atomic_val);
136
137 MUTEX_BLOCKING.lock(|x| {
138 let val = x.borrow().inner;
139 info!("blocking mutex: {}", val);
140 });
141
142 let cell_val = cell.get();
143 info!("cell: {:?}", cell_val);
144
145 let ref_cell_val = ref_cell.borrow().inner;
146 info!("ref_cell: {:?}", ref_cell_val);
147
148 ticker.next().await;
149 }
150}
diff --git a/examples/rp/src/bin/spi_sdmmc.rs b/examples/rp/src/bin/spi_sdmmc.rs
new file mode 100644
index 000000000..4cbc82f7b
--- /dev/null
+++ b/examples/rp/src/bin/spi_sdmmc.rs
@@ -0,0 +1,83 @@
1//! This example shows how to use `embedded-sdmmc` with the RP2040 chip, over SPI.
2//!
3//! The example will attempt to read a file `MY_FILE.TXT` from the root directory
4//! of the SD card and print its contents.
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_embedded_hal::SetConfig;
11use embassy_executor::Spawner;
12use embassy_rp::spi::Spi;
13use embassy_rp::{gpio, spi};
14use embedded_hal_bus::spi::ExclusiveDevice;
15use embedded_sdmmc::sdcard::{DummyCsPin, SdCard};
16use gpio::{Level, Output};
17use {defmt_rtt as _, panic_probe as _};
18
19struct DummyTimesource();
20
21impl embedded_sdmmc::TimeSource for DummyTimesource {
22 fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
23 embedded_sdmmc::Timestamp {
24 year_since_1970: 0,
25 zero_indexed_month: 0,
26 zero_indexed_day: 0,
27 hours: 0,
28 minutes: 0,
29 seconds: 0,
30 }
31 }
32}
33
34#[embassy_executor::main]
35async fn main(_spawner: Spawner) {
36 embassy_rp::pac::SIO.spinlock(31).write_value(1);
37 let p = embassy_rp::init(Default::default());
38
39 // SPI clock needs to be running at <= 400kHz during initialization
40 let mut config = spi::Config::default();
41 config.frequency = 400_000;
42 let spi = Spi::new_blocking(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, config);
43 // Use a dummy cs pin here, for embedded-hal SpiDevice compatibility reasons
44 let spi_dev = ExclusiveDevice::new_no_delay(spi, DummyCsPin);
45 // Real cs pin
46 let cs = Output::new(p.PIN_16, Level::High);
47
48 let sdcard = SdCard::new(spi_dev, cs, embassy_time::Delay);
49 info!("Card size is {} bytes", sdcard.num_bytes().unwrap());
50
51 // Now that the card is initialized, the SPI clock can go faster
52 let mut config = spi::Config::default();
53 config.frequency = 16_000_000;
54 sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok();
55
56 // 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.
58 let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, DummyTimesource());
59
60 // Try and access Volume 0 (i.e. the first partition).
61 // The volume object holds information about the filesystem on that volume.
62 let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0)).unwrap();
63 info!("Volume 0: {:?}", defmt::Debug2Format(&volume0));
64
65 // Open the root directory (mutably borrows from the volume).
66 let mut root_dir = volume0.open_root_dir().unwrap();
67
68 // Open a file called "MY_FILE.TXT" in the root directory
69 // This mutably borrows the directory.
70 let mut my_file = root_dir
71 .open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::Mode::ReadOnly)
72 .unwrap();
73
74 // Print the contents of the file
75 while !my_file.is_eof() {
76 let mut buf = [0u8; 32];
77 if let Ok(n) = my_file.read(&mut buf) {
78 info!("{:a}", buf[..n]);
79 }
80 }
81
82 loop {}
83}
diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs
index fac61aa04..468d2b61a 100644
--- a/examples/rp/src/bin/uart_buffered_split.rs
+++ b/examples/rp/src/bin/uart_buffered_split.rs
@@ -31,7 +31,7 @@ async fn main(spawner: Spawner) {
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, Irqs, tx_pin, rx_pin, 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 unwrap!(spawner.spawn(reader(rx)));
37 37
diff --git a/examples/rp/src/bin/uart_r503.rs b/examples/rp/src/bin/uart_r503.rs
new file mode 100644
index 000000000..085be280b
--- /dev/null
+++ b/examples/rp/src/bin/uart_r503.rs
@@ -0,0 +1,158 @@
1#![no_std]
2#![no_main]
3
4use defmt::{debug, error, info};
5use embassy_executor::Spawner;
6use embassy_rp::bind_interrupts;
7use embassy_rp::peripherals::UART0;
8use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart};
9use embassy_time::{with_timeout, Duration, Timer};
10use heapless::Vec;
11use {defmt_rtt as _, panic_probe as _};
12
13bind_interrupts!(pub struct Irqs {
14 UART0_IRQ => UARTInterruptHandler<UART0>;
15});
16
17const START: u16 = 0xEF01;
18const ADDRESS: u32 = 0xFFFFFFFF;
19
20// ================================================================================
21
22// Data package format
23// Name Length Description
24// ==========================================================================================================
25// Start 2 bytes Fixed value of 0xEF01; High byte transferred first.
26// Address 4 bytes Default value is 0xFFFFFFFF, which can be modified by command.
27// High byte transferred first and at wrong adder value, module
28// will reject to transfer.
29// PID 1 byte 01H Command packet;
30// 02H Data packet; Data packet shall not appear alone in executing
31// processs, must follow command packet or acknowledge packet.
32// 07H Acknowledge packet;
33// 08H End of Data packet.
34// LENGTH 2 bytes Refers to the length of package content (command packets and data packets)
35// plus the length of Checksum (2 bytes). Unit is byte. Max length is 256 bytes.
36// And high byte is transferred first.
37// DATA - It can be commands, data, command’s parameters, acknowledge result, etc.
38// (fingerprint character value, template are all deemed as data);
39// SUM 2 bytes The arithmetic sum of package identifier, package length and all package
40// contens. Overflowing bits are omitted. high byte is transferred first.
41
42// ================================================================================
43
44// Checksum is calculated on 'length (2 bytes) + data (??)'.
45fn compute_checksum(buf: Vec<u8, 32>) -> u16 {
46 let mut checksum = 0u16;
47
48 let check_end = buf.len();
49 let checked_bytes = &buf[6..check_end];
50 for byte in checked_bytes {
51 checksum += (*byte) as u16;
52 }
53 return checksum;
54}
55
56#[embassy_executor::main]
57async fn main(_spawner: Spawner) {
58 info!("Start");
59
60 let p = embassy_rp::init(Default::default());
61
62 // Initialize the fingerprint scanner.
63 let mut config = Config::default();
64 config.baudrate = 57600;
65 config.stop_bits = StopBits::STOP1;
66 config.data_bits = DataBits::DataBits8;
67 config.parity = Parity::ParityNone;
68
69 let (uart, tx_pin, tx_dma, rx_pin, rx_dma) = (p.UART0, p.PIN_16, p.DMA_CH0, p.PIN_17, p.DMA_CH1);
70 let uart = Uart::new(uart, tx_pin, rx_pin, Irqs, tx_dma, rx_dma, config);
71 let (mut tx, mut rx) = uart.split();
72
73 let mut vec_buf: Vec<u8, 32> = heapless::Vec::new();
74 let mut data: Vec<u8, 32> = heapless::Vec::new();
75
76 let mut speeds: Vec<u8, 3> = heapless::Vec::new();
77 let _ = speeds.push(0xC8); // Slow
78 let _ = speeds.push(0x20); // Medium
79 let _ = speeds.push(0x02); // Fast
80
81 // Cycle through the three colours Red, Blue and Purple forever.
82 loop {
83 for colour in 1..=3 {
84 for speed in &speeds {
85 // Set the data first, because the length is dependent on that.
86 // However, we write the length bits before we do the data.
87 data.clear();
88 let _ = data.push(0x01); // ctrl=Breathing light
89 let _ = data.push(*speed);
90 let _ = data.push(colour as u8); // colour=Red, Blue, Purple
91 let _ = data.push(0x00); // times=Infinite
92
93 // Clear buffers
94 vec_buf.clear();
95
96 // START
97 let _ = vec_buf.extend_from_slice(&START.to_be_bytes()[..]);
98
99 // ADDRESS
100 let _ = vec_buf.extend_from_slice(&ADDRESS.to_be_bytes()[..]);
101
102 // PID
103 let _ = vec_buf.extend_from_slice(&[0x01]);
104
105 // LENGTH
106 let len: u16 = (1 + data.len() + 2).try_into().unwrap();
107 let _ = vec_buf.extend_from_slice(&len.to_be_bytes()[..]);
108
109 // COMMAND
110 let _ = vec_buf.push(0x35); // Command: AuraLedConfig
111
112 // DATA
113 let _ = vec_buf.extend_from_slice(&data);
114
115 // SUM
116 let chk = compute_checksum(vec_buf.clone());
117 let _ = vec_buf.extend_from_slice(&chk.to_be_bytes()[..]);
118
119 // =====
120
121 // Send command buffer.
122 let data_write: [u8; 16] = vec_buf.clone().into_array().unwrap();
123 debug!(" write='{:?}'", data_write[..]);
124 match tx.write(&data_write).await {
125 Ok(..) => info!("Write successful."),
126 Err(e) => error!("Write error: {:?}", e),
127 }
128
129 // =====
130
131 // Read command buffer.
132 let mut read_buf: [u8; 1] = [0; 1]; // Can only read one byte at a time!
133 let mut data_read: Vec<u8, 32> = heapless::Vec::new(); // Save buffer.
134
135 info!("Attempting read.");
136 loop {
137 // Some commands, like `Img2Tz()` needs longer, but we hard-code this to 200ms
138 // for this command.
139 match with_timeout(Duration::from_millis(200), rx.read(&mut read_buf)).await {
140 Ok(..) => {
141 // Extract and save read byte.
142 debug!(" r='{=u8:#04x}H' ({:03}D)", read_buf[0], read_buf[0]);
143 let _ = data_read.push(read_buf[0]).unwrap();
144 }
145 Err(..) => break, // TimeoutError -> Ignore.
146 }
147 }
148 info!("Read successful");
149 debug!(" read='{:?}'", data_read[..]);
150
151 Timer::after_secs(3).await;
152 info!("Changing speed.");
153 }
154
155 info!("Changing colour.");
156 }
157 }
158}
diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs
index 01f0d5967..9a15125d4 100644
--- a/examples/rp/src/bin/usb_ethernet.rs
+++ b/examples/rp/src/bin/usb_ethernet.rs
@@ -8,7 +8,8 @@
8use defmt::*; 8use defmt::*;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_net::tcp::TcpSocket; 10use embassy_net::tcp::TcpSocket;
11use embassy_net::{Stack, StackResources}; 11use embassy_net::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};
@@ -16,6 +17,7 @@ use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState
16use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; 17use embassy_usb::class::cdc_ncm::{CdcNcmClass, State};
17use embassy_usb::{Builder, Config, UsbDevice}; 18use embassy_usb::{Builder, Config, UsbDevice};
18use embedded_io_async::Write; 19use embedded_io_async::Write;
20use rand::RngCore;
19use static_cell::StaticCell; 21use static_cell::StaticCell;
20use {defmt_rtt as _, panic_probe as _}; 22use {defmt_rtt as _, panic_probe as _};
21 23
@@ -38,13 +40,14 @@ async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! {
38} 40}
39 41
40#[embassy_executor::task] 42#[embassy_executor::task]
41async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { 43async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>>) -> ! {
42 stack.run().await 44 runner.run().await
43} 45}
44 46
45#[embassy_executor::main] 47#[embassy_executor::main]
46async fn main(spawner: Spawner) { 48async fn main(spawner: Spawner) {
47 let p = embassy_rp::init(Default::default()); 49 let p = embassy_rp::init(Default::default());
50 let mut rng = RoscRng;
48 51
49 // Create the driver, from the HAL. 52 // Create the driver, from the HAL.
50 let driver = Driver::new(p.USB, Irqs); 53 let driver = Driver::new(p.USB, Irqs);
@@ -64,14 +67,12 @@ async fn main(spawner: Spawner) {
64 config.device_protocol = 0x01; 67 config.device_protocol = 0x01;
65 68
66 // Create embassy-usb DeviceBuilder using the driver and config. 69 // Create embassy-usb DeviceBuilder using the driver and config.
67 static DEVICE_DESC: StaticCell<[u8; 256]> = StaticCell::new();
68 static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); 70 static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new();
69 static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); 71 static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new();
70 static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new(); 72 static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new();
71 let mut builder = Builder::new( 73 let mut builder = Builder::new(
72 driver, 74 driver,
73 config, 75 config,
74 &mut DEVICE_DESC.init([0; 256])[..],
75 &mut CONFIG_DESC.init([0; 256])[..], 76 &mut CONFIG_DESC.init([0; 256])[..],
76 &mut BOS_DESC.init([0; 256])[..], 77 &mut BOS_DESC.init([0; 256])[..],
77 &mut [], // no msos descriptors 78 &mut [], // no msos descriptors
@@ -104,19 +105,13 @@ async fn main(spawner: Spawner) {
104 //}); 105 //});
105 106
106 // Generate random seed 107 // Generate random seed
107 let seed = 1234; // guaranteed random, chosen by a fair dice roll 108 let seed = rng.next_u64();
108 109
109 // Init network stack 110 // Init network stack
110 static STACK: StaticCell<Stack<Device<'static, MTU>>> = StaticCell::new(); 111 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
111 static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); 112 let (stack, runner) = embassy_net::new(device, config, RESOURCES.init(StackResources::new()), seed);
112 let stack = &*STACK.init(Stack::new(
113 device,
114 config,
115 RESOURCES.init(StackResources::<2>::new()),
116 seed,
117 ));
118 113
119 unwrap!(spawner.spawn(net_task(stack))); 114 unwrap!(spawner.spawn(net_task(runner)));
120 115
121 // And now we can use it! 116 // And now we can use it!
122 117
diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs
index b5ac16245..a7cb322d8 100644
--- a/examples/rp/src/bin/usb_hid_keyboard.rs
+++ b/examples/rp/src/bin/usb_hid_keyboard.rs
@@ -36,13 +36,12 @@ async fn main(_spawner: Spawner) {
36 36
37 // Create embassy-usb DeviceBuilder using the driver and config. 37 // Create embassy-usb DeviceBuilder using the driver and config.
38 // It needs some buffers for building the descriptors. 38 // It needs some buffers for building the descriptors.
39 let mut device_descriptor = [0; 256];
40 let mut config_descriptor = [0; 256]; 39 let mut config_descriptor = [0; 256];
41 let mut bos_descriptor = [0; 256]; 40 let mut bos_descriptor = [0; 256];
42 // You can also add a Microsoft OS descriptor. 41 // You can also add a Microsoft OS descriptor.
43 let mut msos_descriptor = [0; 256]; 42 let mut msos_descriptor = [0; 256];
44 let mut control_buf = [0; 64]; 43 let mut control_buf = [0; 64];
45 let request_handler = MyRequestHandler {}; 44 let mut request_handler = MyRequestHandler {};
46 let mut device_handler = MyDeviceHandler::new(); 45 let mut device_handler = MyDeviceHandler::new();
47 46
48 let mut state = State::new(); 47 let mut state = State::new();
@@ -50,7 +49,6 @@ async fn main(_spawner: Spawner) {
50 let mut builder = Builder::new( 49 let mut builder = Builder::new(
51 driver, 50 driver,
52 config, 51 config,
53 &mut device_descriptor,
54 &mut config_descriptor, 52 &mut config_descriptor,
55 &mut bos_descriptor, 53 &mut bos_descriptor,
56 &mut msos_descriptor, 54 &mut msos_descriptor,
@@ -62,7 +60,7 @@ async fn main(_spawner: Spawner) {
62 // Create classes on the builder. 60 // Create classes on the builder.
63 let config = embassy_usb::class::hid::Config { 61 let config = embassy_usb::class::hid::Config {
64 report_descriptor: KeyboardReport::desc(), 62 report_descriptor: KeyboardReport::desc(),
65 request_handler: Some(&request_handler), 63 request_handler: None,
66 poll_ms: 60, 64 poll_ms: 60,
67 max_packet_size: 64, 65 max_packet_size: 64,
68 }; 66 };
@@ -116,7 +114,7 @@ async fn main(_spawner: Spawner) {
116 }; 114 };
117 115
118 let out_fut = async { 116 let out_fut = async {
119 reader.run(false, &request_handler).await; 117 reader.run(false, &mut request_handler).await;
120 }; 118 };
121 119
122 // Run everything concurrently. 120 // Run everything concurrently.
@@ -127,21 +125,21 @@ async fn main(_spawner: Spawner) {
127struct MyRequestHandler {} 125struct MyRequestHandler {}
128 126
129impl RequestHandler for MyRequestHandler { 127impl RequestHandler for MyRequestHandler {
130 fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> { 128 fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
131 info!("Get report for {:?}", id); 129 info!("Get report for {:?}", id);
132 None 130 None
133 } 131 }
134 132
135 fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { 133 fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {
136 info!("Set report for {:?}: {=[u8]}", id, data); 134 info!("Set report for {:?}: {=[u8]}", id, data);
137 OutResponse::Accepted 135 OutResponse::Accepted
138 } 136 }
139 137
140 fn set_idle_ms(&self, id: Option<ReportId>, dur: u32) { 138 fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {
141 info!("Set idle rate for {:?} to {:?}", id, dur); 139 info!("Set idle rate for {:?} to {:?}", id, dur);
142 } 140 }
143 141
144 fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> { 142 fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {
145 info!("Get idle rate for {:?}", id); 143 info!("Get idle rate for {:?}", id);
146 None 144 None
147 } 145 }
diff --git a/examples/rp/src/bin/usb_hid_mouse.rs b/examples/rp/src/bin/usb_hid_mouse.rs
new file mode 100644
index 000000000..5ee650910
--- /dev/null
+++ b/examples/rp/src/bin/usb_hid_mouse.rs
@@ -0,0 +1,173 @@
1#![no_std]
2#![no_main]
3
4use core::sync::atomic::{AtomicBool, Ordering};
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_futures::join::join;
9use embassy_rp::bind_interrupts;
10use embassy_rp::clocks::RoscRng;
11use embassy_rp::peripherals::USB;
12use embassy_rp::usb::{Driver, InterruptHandler};
13use embassy_time::Timer;
14use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State};
15use embassy_usb::control::OutResponse;
16use embassy_usb::{Builder, Config, Handler};
17use rand::Rng;
18use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
19use {defmt_rtt as _, panic_probe as _};
20
21bind_interrupts!(struct Irqs {
22 USBCTRL_IRQ => InterruptHandler<USB>;
23});
24
25#[embassy_executor::main]
26async fn main(_spawner: Spawner) {
27 let p = embassy_rp::init(Default::default());
28 // Create the driver, from the HAL.
29 let driver = Driver::new(p.USB, Irqs);
30
31 // Create embassy-usb Config
32 let mut config = Config::new(0xc0de, 0xcafe);
33 config.manufacturer = Some("Embassy");
34 config.product = Some("HID keyboard example");
35 config.serial_number = Some("12345678");
36 config.max_power = 100;
37 config.max_packet_size_0 = 64;
38
39 // Create embassy-usb DeviceBuilder using the driver and config.
40 // It needs some buffers for building the descriptors.
41 let mut config_descriptor = [0; 256];
42 let mut bos_descriptor = [0; 256];
43 // You can also add a Microsoft OS descriptor.
44 let mut msos_descriptor = [0; 256];
45 let mut control_buf = [0; 64];
46 let mut request_handler = MyRequestHandler {};
47 let mut device_handler = MyDeviceHandler::new();
48
49 let mut state = State::new();
50
51 let mut builder = Builder::new(
52 driver,
53 config,
54 &mut config_descriptor,
55 &mut bos_descriptor,
56 &mut msos_descriptor,
57 &mut control_buf,
58 );
59
60 builder.handler(&mut device_handler);
61
62 // Create classes on the builder.
63 let config = embassy_usb::class::hid::Config {
64 report_descriptor: MouseReport::desc(),
65 request_handler: None,
66 poll_ms: 60,
67 max_packet_size: 64,
68 };
69 let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);
70
71 // Build the builder.
72 let mut usb = builder.build();
73
74 // Run the USB device.
75 let usb_fut = usb.run();
76
77 let (reader, mut writer) = hid.split();
78
79 // Do stuff with the class!
80 let in_fut = async {
81 let mut rng = RoscRng;
82
83 loop {
84 // every 1 second
85 _ = Timer::after_secs(1).await;
86 let report = MouseReport {
87 buttons: 0,
88 x: rng.gen_range(-100..100), // random small x movement
89 y: rng.gen_range(-100..100), // random small y movement
90 wheel: 0,
91 pan: 0,
92 };
93 // Send the report.
94 match writer.write_serialize(&report).await {
95 Ok(()) => {}
96 Err(e) => warn!("Failed to send report: {:?}", e),
97 }
98 }
99 };
100
101 let out_fut = async {
102 reader.run(false, &mut request_handler).await;
103 };
104
105 // Run everything concurrently.
106 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
107 join(usb_fut, join(in_fut, out_fut)).await;
108}
109
110struct MyRequestHandler {}
111
112impl RequestHandler for MyRequestHandler {
113 fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
114 info!("Get report for {:?}", id);
115 None
116 }
117
118 fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {
119 info!("Set report for {:?}: {=[u8]}", id, data);
120 OutResponse::Accepted
121 }
122
123 fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {
124 info!("Set idle rate for {:?} to {:?}", id, dur);
125 }
126
127 fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {
128 info!("Get idle rate for {:?}", id);
129 None
130 }
131}
132
133struct MyDeviceHandler {
134 configured: AtomicBool,
135}
136
137impl MyDeviceHandler {
138 fn new() -> Self {
139 MyDeviceHandler {
140 configured: AtomicBool::new(false),
141 }
142 }
143}
144
145impl Handler for MyDeviceHandler {
146 fn enabled(&mut self, enabled: bool) {
147 self.configured.store(false, Ordering::Relaxed);
148 if enabled {
149 info!("Device enabled");
150 } else {
151 info!("Device disabled");
152 }
153 }
154
155 fn reset(&mut self) {
156 self.configured.store(false, Ordering::Relaxed);
157 info!("Bus reset, the Vbus current limit is 100mA");
158 }
159
160 fn addressed(&mut self, addr: u8) {
161 self.configured.store(false, Ordering::Relaxed);
162 info!("USB address set to: {}", addr);
163 }
164
165 fn configured(&mut self, configured: bool) {
166 self.configured.store(configured, Ordering::Relaxed);
167 if configured {
168 info!("Device configured, it may now draw up to the configured current limit from Vbus.")
169 } else {
170 info!("Device is no longer configured, the Vbus current limit is 100mA.");
171 }
172 }
173}
diff --git a/examples/rp/src/bin/usb_midi.rs b/examples/rp/src/bin/usb_midi.rs
index 95306a35c..11db1b2e1 100644
--- a/examples/rp/src/bin/usb_midi.rs
+++ b/examples/rp/src/bin/usb_midi.rs
@@ -46,7 +46,6 @@ async fn main(_spawner: Spawner) {
46 46
47 // Create embassy-usb DeviceBuilder using the driver and config. 47 // Create embassy-usb DeviceBuilder using the driver and config.
48 // It needs some buffers for building the descriptors. 48 // It needs some buffers for building the descriptors.
49 let mut device_descriptor = [0; 256];
50 let mut config_descriptor = [0; 256]; 49 let mut config_descriptor = [0; 256];
51 let mut bos_descriptor = [0; 256]; 50 let mut bos_descriptor = [0; 256];
52 let mut control_buf = [0; 64]; 51 let mut control_buf = [0; 64];
@@ -54,7 +53,6 @@ async fn main(_spawner: Spawner) {
54 let mut builder = Builder::new( 53 let mut builder = Builder::new(
55 driver, 54 driver,
56 config, 55 config,
57 &mut device_descriptor,
58 &mut config_descriptor, 56 &mut config_descriptor,
59 &mut bos_descriptor, 57 &mut bos_descriptor,
60 &mut [], // no msos descriptors 58 &mut [], // no msos descriptors
diff --git a/examples/rp/src/bin/usb_raw.rs b/examples/rp/src/bin/usb_raw.rs
index a6c8a5b2e..97e7e0244 100644
--- a/examples/rp/src/bin/usb_raw.rs
+++ b/examples/rp/src/bin/usb_raw.rs
@@ -93,7 +93,6 @@ async fn main(_spawner: Spawner) {
93 93
94 // Create embassy-usb DeviceBuilder using the driver and config. 94 // Create embassy-usb DeviceBuilder using the driver and config.
95 // It needs some buffers for building the descriptors. 95 // It needs some buffers for building the descriptors.
96 let mut device_descriptor = [0; 256];
97 let mut config_descriptor = [0; 256]; 96 let mut config_descriptor = [0; 256];
98 let mut bos_descriptor = [0; 256]; 97 let mut bos_descriptor = [0; 256];
99 let mut msos_descriptor = [0; 256]; 98 let mut msos_descriptor = [0; 256];
@@ -106,7 +105,6 @@ async fn main(_spawner: Spawner) {
106 let mut builder = Builder::new( 105 let mut builder = Builder::new(
107 driver, 106 driver,
108 config, 107 config,
109 &mut device_descriptor,
110 &mut config_descriptor, 108 &mut config_descriptor,
111 &mut bos_descriptor, 109 &mut bos_descriptor,
112 &mut msos_descriptor, 110 &mut msos_descriptor,
diff --git a/examples/rp/src/bin/usb_raw_bulk.rs b/examples/rp/src/bin/usb_raw_bulk.rs
index 0dc8e9f72..331c3da4c 100644
--- a/examples/rp/src/bin/usb_raw_bulk.rs
+++ b/examples/rp/src/bin/usb_raw_bulk.rs
@@ -71,7 +71,6 @@ async fn main(_spawner: Spawner) {
71 71
72 // Create embassy-usb DeviceBuilder using the driver and config. 72 // Create embassy-usb DeviceBuilder using the driver and config.
73 // It needs some buffers for building the descriptors. 73 // It needs some buffers for building the descriptors.
74 let mut device_descriptor = [0; 256];
75 let mut config_descriptor = [0; 256]; 74 let mut config_descriptor = [0; 256];
76 let mut bos_descriptor = [0; 256]; 75 let mut bos_descriptor = [0; 256];
77 let mut msos_descriptor = [0; 256]; 76 let mut msos_descriptor = [0; 256];
@@ -80,7 +79,6 @@ async fn main(_spawner: Spawner) {
80 let mut builder = Builder::new( 79 let mut builder = Builder::new(
81 driver, 80 driver,
82 config, 81 config,
83 &mut device_descriptor,
84 &mut config_descriptor, 82 &mut config_descriptor,
85 &mut bos_descriptor, 83 &mut bos_descriptor,
86 &mut msos_descriptor, 84 &mut msos_descriptor,
diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs
index ab24a994c..4a802994a 100644
--- a/examples/rp/src/bin/usb_serial.rs
+++ b/examples/rp/src/bin/usb_serial.rs
@@ -5,15 +5,15 @@
5#![no_std] 5#![no_std]
6#![no_main] 6#![no_main]
7 7
8use defmt::{info, panic}; 8use defmt::{info, panic, unwrap};
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_futures::join::join;
11use embassy_rp::bind_interrupts; 10use embassy_rp::bind_interrupts;
12use embassy_rp::peripherals::USB; 11use embassy_rp::peripherals::USB;
13use embassy_rp::usb::{Driver, Instance, InterruptHandler}; 12use embassy_rp::usb::{Driver, Instance, InterruptHandler};
14use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; 13use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
15use embassy_usb::driver::EndpointError; 14use embassy_usb::driver::EndpointError;
16use embassy_usb::{Builder, Config}; 15use embassy_usb::UsbDevice;
16use static_cell::StaticCell;
17use {defmt_rtt as _, panic_probe as _}; 17use {defmt_rtt as _, panic_probe as _};
18 18
19bind_interrupts!(struct Irqs { 19bind_interrupts!(struct Irqs {
@@ -21,7 +21,7 @@ bind_interrupts!(struct Irqs {
21}); 21});
22 22
23#[embassy_executor::main] 23#[embassy_executor::main]
24async fn main(_spawner: Spawner) { 24async fn main(spawner: Spawner) {
25 info!("Hello there!"); 25 info!("Hello there!");
26 26
27 let p = embassy_rp::init(Default::default()); 27 let p = embassy_rp::init(Default::default());
@@ -30,61 +30,69 @@ async fn main(_spawner: Spawner) {
30 let driver = Driver::new(p.USB, Irqs); 30 let driver = Driver::new(p.USB, Irqs);
31 31
32 // Create embassy-usb Config 32 // Create embassy-usb Config
33 let mut config = Config::new(0xc0de, 0xcafe); 33 let config = {
34 config.manufacturer = Some("Embassy"); 34 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
35 config.product = Some("USB-serial example"); 35 config.manufacturer = Some("Embassy");
36 config.serial_number = Some("12345678"); 36 config.product = Some("USB-serial example");
37 config.max_power = 100; 37 config.serial_number = Some("12345678");
38 config.max_packet_size_0 = 64; 38 config.max_power = 100;
39 39 config.max_packet_size_0 = 64;
40 // Required for windows compatibility. 40
41 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help 41 // Required for windows compatibility.
42 config.device_class = 0xEF; 42 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
43 config.device_sub_class = 0x02; 43 config.device_class = 0xEF;
44 config.device_protocol = 0x01; 44 config.device_sub_class = 0x02;
45 config.composite_with_iads = true; 45 config.device_protocol = 0x01;
46 config.composite_with_iads = true;
47 config
48 };
46 49
47 // Create embassy-usb DeviceBuilder using the driver and config. 50 // Create embassy-usb DeviceBuilder using the driver and config.
48 // It needs some buffers for building the descriptors. 51 // It needs some buffers for building the descriptors.
49 let mut device_descriptor = [0; 256]; 52 let mut builder = {
50 let mut config_descriptor = [0; 256]; 53 static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();
51 let mut bos_descriptor = [0; 256]; 54 static BOS_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new();
52 let mut control_buf = [0; 64]; 55 static CONTROL_BUF: StaticCell<[u8; 64]> = StaticCell::new();
53 56
54 let mut state = State::new(); 57 let builder = embassy_usb::Builder::new(
55 58 driver,
56 let mut builder = Builder::new( 59 config,
57 driver, 60 CONFIG_DESCRIPTOR.init([0; 256]),
58 config, 61 BOS_DESCRIPTOR.init([0; 256]),
59 &mut device_descriptor, 62 &mut [], // no msos descriptors
60 &mut config_descriptor, 63 CONTROL_BUF.init([0; 64]),
61 &mut bos_descriptor, 64 );
62 &mut [], // no msos descriptors 65 builder
63 &mut control_buf, 66 };
64 );
65 67
66 // Create classes on the builder. 68 // Create classes on the builder.
67 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); 69 let mut class = {
70 static STATE: StaticCell<State> = StaticCell::new();
71 let state = STATE.init(State::new());
72 CdcAcmClass::new(&mut builder, state, 64)
73 };
68 74
69 // Build the builder. 75 // Build the builder.
70 let mut usb = builder.build(); 76 let usb = builder.build();
71 77
72 // Run the USB device. 78 // Run the USB device.
73 let usb_fut = usb.run(); 79 unwrap!(spawner.spawn(usb_task(usb)));
74 80
75 // Do stuff with the class! 81 // Do stuff with the class!
76 let echo_fut = async { 82 loop {
77 loop { 83 class.wait_connection().await;
78 class.wait_connection().await; 84 info!("Connected");
79 info!("Connected"); 85 let _ = echo(&mut class).await;
80 let _ = echo(&mut class).await; 86 info!("Disconnected");
81 info!("Disconnected"); 87 }
82 } 88}
83 }; 89
90type MyUsbDriver = Driver<'static, USB>;
91type MyUsbDevice = UsbDevice<'static, MyUsbDriver>;
84 92
85 // Run everything concurrently. 93#[embassy_executor::task]
86 // If we had made everything `'static` above instead, we could do this using separate tasks instead. 94async fn usb_task(mut usb: MyUsbDevice) -> ! {
87 join(usb_fut, echo_fut).await; 95 usb.run().await
88} 96}
89 97
90struct Disconnected {} 98struct Disconnected {}
diff --git a/examples/rp/src/bin/usb_serial_with_logger.rs b/examples/rp/src/bin/usb_serial_with_logger.rs
index 4ba4fc25c..f9cfdef94 100644
--- a/examples/rp/src/bin/usb_serial_with_logger.rs
+++ b/examples/rp/src/bin/usb_serial_with_logger.rs
@@ -46,7 +46,6 @@ async fn main(_spawner: Spawner) {
46 46
47 // Create embassy-usb DeviceBuilder using the driver and config. 47 // Create embassy-usb DeviceBuilder using the driver and config.
48 // It needs some buffers for building the descriptors. 48 // It needs some buffers for building the descriptors.
49 let mut device_descriptor = [0; 256];
50 let mut config_descriptor = [0; 256]; 49 let mut config_descriptor = [0; 256];
51 let mut bos_descriptor = [0; 256]; 50 let mut bos_descriptor = [0; 256];
52 let mut control_buf = [0; 64]; 51 let mut control_buf = [0; 64];
@@ -57,7 +56,6 @@ async fn main(_spawner: Spawner) {
57 let mut builder = Builder::new( 56 let mut builder = Builder::new(
58 driver, 57 driver,
59 config, 58 config,
60 &mut device_descriptor,
61 &mut config_descriptor, 59 &mut config_descriptor,
62 &mut bos_descriptor, 60 &mut bos_descriptor,
63 &mut [], // no msos descriptors 61 &mut [], // no msos descriptors
diff --git a/examples/rp/src/bin/usb_webusb.rs b/examples/rp/src/bin/usb_webusb.rs
new file mode 100644
index 000000000..e73938ac9
--- /dev/null
+++ b/examples/rp/src/bin/usb_webusb.rs
@@ -0,0 +1,155 @@
1//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.
2//!
3//! This creates a WebUSB capable device that echoes data back to the host.
4//!
5//! To test this in the browser (ideally host this on localhost:8080, to test the landing page
6//! feature):
7//! ```js
8//! (async () => {
9//! const device = await navigator.usb.requestDevice({ filters: [{ vendorId: 0xf569 }] });
10//! await device.open();
11//! await device.claimInterface(1);
12//! device.transferIn(1, 64).then(data => console.log(data));
13//! await device.transferOut(1, new Uint8Array([1,2,3]));
14//! })();
15//! ```
16
17#![no_std]
18#![no_main]
19
20use defmt::info;
21use embassy_executor::Spawner;
22use embassy_futures::join::join;
23use embassy_rp::bind_interrupts;
24use embassy_rp::peripherals::USB;
25use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler};
26use embassy_usb::class::web_usb::{Config as WebUsbConfig, State, Url, WebUsb};
27use embassy_usb::driver::{Driver, Endpoint, EndpointIn, EndpointOut};
28use embassy_usb::msos::{self, windows_version};
29use embassy_usb::{Builder, Config};
30use {defmt_rtt as _, panic_probe as _};
31
32bind_interrupts!(struct Irqs {
33 USBCTRL_IRQ => InterruptHandler<USB>;
34});
35
36// This is a randomly generated GUID to allow clients on Windows to find our device
37const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
38
39#[embassy_executor::main]
40async fn main(_spawner: Spawner) {
41 let p = embassy_rp::init(Default::default());
42
43 // Create the driver, from the HAL.
44 let driver = UsbDriver::new(p.USB, Irqs);
45
46 // Create embassy-usb Config
47 let mut config = Config::new(0xf569, 0x0001);
48 config.manufacturer = Some("Embassy");
49 config.product = Some("WebUSB example");
50 config.serial_number = Some("12345678");
51 config.max_power = 100;
52 config.max_packet_size_0 = 64;
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.
61 // It needs some buffers for building the descriptors.
62 let mut config_descriptor = [0; 256];
63 let mut bos_descriptor = [0; 256];
64 let mut control_buf = [0; 64];
65 let mut msos_descriptor = [0; 256];
66
67 let webusb_config = WebUsbConfig {
68 max_packet_size: 64,
69 vendor_code: 1,
70 // If defined, shows a landing page which the device manufacturer would like the user to visit in order to control their device. Suggest the user to navigate to this URL when the device is connected.
71 landing_url: Some(Url::new("http://localhost:8080")),
72 };
73
74 let mut state = State::new();
75
76 let mut builder = Builder::new(
77 driver,
78 config,
79 &mut config_descriptor,
80 &mut bos_descriptor,
81 &mut msos_descriptor,
82 &mut control_buf,
83 );
84
85 // Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
86 // We tell Windows that this entire device is compatible with the "WINUSB" feature,
87 // which causes it to use the built-in WinUSB driver automatically, which in turn
88 // can be used by libusb/rusb software without needing a custom driver or INF file.
89 // In principle you might want to call msos_feature() just on a specific function,
90 // if your device also has other functions that still use standard class drivers.
91 builder.msos_descriptor(windows_version::WIN8_1, 0);
92 builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
93 builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
94 "DeviceInterfaceGUIDs",
95 msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
96 ));
97
98 // Create classes on the builder (WebUSB just needs some setup, but doesn't return anything)
99 WebUsb::configure(&mut builder, &mut state, &webusb_config);
100 // Create some USB bulk endpoints for testing.
101 let mut endpoints = WebEndpoints::new(&mut builder, &webusb_config);
102
103 // Build the builder.
104 let mut usb = builder.build();
105
106 // Run the USB device.
107 let usb_fut = usb.run();
108
109 // Do some WebUSB transfers.
110 let webusb_fut = async {
111 loop {
112 endpoints.wait_connected().await;
113 info!("Connected");
114 endpoints.echo().await;
115 }
116 };
117
118 // Run everything concurrently.
119 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
120 join(usb_fut, webusb_fut).await;
121}
122
123struct WebEndpoints<'d, D: Driver<'d>> {
124 write_ep: D::EndpointIn,
125 read_ep: D::EndpointOut,
126}
127
128impl<'d, D: Driver<'d>> WebEndpoints<'d, D> {
129 fn new(builder: &mut Builder<'d, D>, config: &'d WebUsbConfig<'d>) -> Self {
130 let mut func = builder.function(0xff, 0x00, 0x00);
131 let mut iface = func.interface();
132 let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None);
133
134 let write_ep = alt.endpoint_bulk_in(config.max_packet_size);
135 let read_ep = alt.endpoint_bulk_out(config.max_packet_size);
136
137 WebEndpoints { write_ep, read_ep }
138 }
139
140 // Wait until the device's endpoints are enabled.
141 async fn wait_connected(&mut self) {
142 self.read_ep.wait_enabled().await
143 }
144
145 // Echo data back to the host.
146 async fn echo(&mut self) {
147 let mut buf = [0; 64];
148 loop {
149 let n = self.read_ep.read(&mut buf).await.unwrap();
150 let data = &buf[..n];
151 info!("Data read: {:x}", data);
152 self.write_ep.write(data).await.unwrap();
153 }
154 }
155}
diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs
index b60852359..4c9651433 100644
--- a/examples/rp/src/bin/wifi_ap_tcp_server.rs
+++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs
@@ -11,13 +11,15 @@ use cyw43_pio::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};
19use embassy_time::Duration; 20use embassy_time::Duration;
20use embedded_io_async::Write; 21use embedded_io_async::Write;
22use rand::RngCore;
21use static_cell::StaticCell; 23use static_cell::StaticCell;
22use {defmt_rtt as _, panic_probe as _}; 24use {defmt_rtt as _, panic_probe as _};
23 25
@@ -26,13 +28,13 @@ bind_interrupts!(struct Irqs {
26}); 28});
27 29
28#[embassy_executor::task] 30#[embassy_executor::task]
29async fn wifi_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { 31async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
30 runner.run().await 32 runner.run().await
31} 33}
32 34
33#[embassy_executor::task] 35#[embassy_executor::task]
34async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { 36async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {
35 stack.run().await 37 runner.run().await
36} 38}
37 39
38#[embassy_executor::main] 40#[embassy_executor::main]
@@ -40,14 +42,15 @@ async fn main(spawner: Spawner) {
40 info!("Hello World!"); 42 info!("Hello World!");
41 43
42 let p = embassy_rp::init(Default::default()); 44 let p = embassy_rp::init(Default::default());
45 let mut rng = RoscRng;
43 46
44 let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); 47 let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin");
45 let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); 48 let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin");
46 49
47 // To make flashing faster for development, you may want to flash the firmwares independently 50 // 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!`: 51 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
49 // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 52 // probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000
50 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 53 // 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) }; 54 //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) }; 55 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
53 56
@@ -59,7 +62,7 @@ async fn main(spawner: Spawner) {
59 static STATE: StaticCell<cyw43::State> = StaticCell::new(); 62 static STATE: StaticCell<cyw43::State> = StaticCell::new();
60 let state = STATE.init(cyw43::State::new()); 63 let state = STATE.init(cyw43::State::new());
61 let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; 64 let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await;
62 unwrap!(spawner.spawn(wifi_task(runner))); 65 unwrap!(spawner.spawn(cyw43_task(runner)));
63 66
64 control.init(clm).await; 67 control.init(clm).await;
65 control 68 control
@@ -74,19 +77,13 @@ async fn main(spawner: Spawner) {
74 }); 77 });
75 78
76 // Generate random seed 79 // Generate random seed
77 let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. 80 let seed = rng.next_u64();
78 81
79 // Init network stack 82 // Init network stack
80 static STACK: StaticCell<Stack<cyw43::NetDriver<'static>>> = StaticCell::new(); 83 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
81 static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); 84 let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed);
82 let stack = &*STACK.init(Stack::new( 85
83 net_device, 86 unwrap!(spawner.spawn(net_task(runner)));
84 config,
85 RESOURCES.init(StackResources::<2>::new()),
86 seed,
87 ));
88
89 unwrap!(spawner.spawn(net_task(stack)));
90 87
91 //control.start_ap_open("cyw43", 5).await; 88 //control.start_ap_open("cyw43", 5).await;
92 control.start_ap_wpa2("cyw43", "password", 5).await; 89 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 18eefe41f..04a61bbd5 100644
--- a/examples/rp/src/bin/wifi_blinky.rs
+++ b/examples/rp/src/bin/wifi_blinky.rs
@@ -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,8 +33,8 @@ 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 --format bin --chip RP2040 --base-address 0x10100000 36 // probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000
37 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 37 // probe-rs download 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
@@ -46,7 +46,7 @@ async fn main(spawner: Spawner) {
46 static STATE: StaticCell<cyw43::State> = StaticCell::new(); 46 static STATE: StaticCell<cyw43::State> = StaticCell::new();
47 let state = STATE.init(cyw43::State::new()); 47 let state = STATE.init(cyw43::State::new());
48 let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; 48 let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await;
49 unwrap!(spawner.spawn(wifi_task(runner))); 49 unwrap!(spawner.spawn(cyw43_task(runner)));
50 50
51 control.init(clm).await; 51 control.init(clm).await;
52 control 52 control
diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs
index e0f85a6b0..434f0074c 100644
--- a/examples/rp/src/bin/wifi_scan.rs
+++ b/examples/rp/src/bin/wifi_scan.rs
@@ -10,7 +10,6 @@ use core::str;
10use cyw43_pio::PioSpi; 10use cyw43_pio::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,13 +22,13 @@ 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] 29#[embassy_executor::task]
31async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { 30async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {
32 stack.run().await 31 runner.run().await
33} 32}
34 33
35#[embassy_executor::main] 34#[embassy_executor::main]
@@ -43,8 +42,8 @@ async fn main(spawner: Spawner) {
43 42
44 // To make flashing faster for development, you may want to flash the firmwares independently 43 // 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!`: 44 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
46 // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 45 // probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000
47 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 46 // 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) }; 47 //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) }; 48 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
50 49
@@ -56,7 +55,7 @@ async fn main(spawner: Spawner) {
56 static STATE: StaticCell<cyw43::State> = StaticCell::new(); 55 static STATE: StaticCell<cyw43::State> = StaticCell::new();
57 let state = STATE.init(cyw43::State::new()); 56 let state = STATE.init(cyw43::State::new());
58 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;
59 unwrap!(spawner.spawn(wifi_task(runner))); 58 unwrap!(spawner.spawn(cyw43_task(runner)));
60 59
61 control.init(clm).await; 60 control.init(clm).await;
62 control 61 control
diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs
index f1afc4a00..7bf546e01 100644
--- a/examples/rp/src/bin/wifi_tcp_server.rs
+++ b/examples/rp/src/bin/wifi_tcp_server.rs
@@ -7,17 +7,20 @@
7 7
8use core::str::from_utf8; 8use core::str::from_utf8;
9 9
10use cyw43::JoinOptions;
10use cyw43_pio::PioSpi; 11use cyw43_pio::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, Timer};
20use embedded_io_async::Write; 22use embedded_io_async::Write;
23use rand::RngCore;
21use static_cell::StaticCell; 24use static_cell::StaticCell;
22use {defmt_rtt as _, panic_probe as _}; 25use {defmt_rtt as _, panic_probe as _};
23 26
@@ -25,17 +28,17 @@ bind_interrupts!(struct Irqs {
25 PIO0_IRQ_0 => InterruptHandler<PIO0>; 28 PIO0_IRQ_0 => InterruptHandler<PIO0>;
26}); 29});
27 30
28const WIFI_NETWORK: &str = "EmbassyTest"; 31const WIFI_NETWORK: &str = "LadronDeWifi";
29const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; 32const WIFI_PASSWORD: &str = "MBfcaedHmyRFE4kaQ1O5SsY8";
30 33
31#[embassy_executor::task] 34#[embassy_executor::task]
32async fn wifi_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { 35async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
33 runner.run().await 36 runner.run().await
34} 37}
35 38
36#[embassy_executor::task] 39#[embassy_executor::task]
37async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { 40async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {
38 stack.run().await 41 runner.run().await
39} 42}
40 43
41#[embassy_executor::main] 44#[embassy_executor::main]
@@ -43,14 +46,15 @@ async fn main(spawner: Spawner) {
43 info!("Hello World!"); 46 info!("Hello World!");
44 47
45 let p = embassy_rp::init(Default::default()); 48 let p = embassy_rp::init(Default::default());
49 let mut rng = RoscRng;
46 50
47 let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); 51 let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin");
48 let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); 52 let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin");
49 53
50 // To make flashing faster for development, you may want to flash the firmwares independently 54 // 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!`: 55 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
52 // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 56 // probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000
53 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 57 // 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) }; 58 //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) }; 59 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
56 60
@@ -62,7 +66,7 @@ async fn main(spawner: Spawner) {
62 static STATE: StaticCell<cyw43::State> = StaticCell::new(); 66 static STATE: StaticCell<cyw43::State> = StaticCell::new();
63 let state = STATE.init(cyw43::State::new()); 67 let state = STATE.init(cyw43::State::new());
64 let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; 68 let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await;
65 unwrap!(spawner.spawn(wifi_task(runner))); 69 unwrap!(spawner.spawn(cyw43_task(runner)));
66 70
67 control.init(clm).await; 71 control.init(clm).await;
68 control 72 control
@@ -77,23 +81,19 @@ async fn main(spawner: Spawner) {
77 //}); 81 //});
78 82
79 // Generate random seed 83 // Generate random seed
80 let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. 84 let seed = rng.next_u64();
81 85
82 // Init network stack 86 // Init network stack
83 static STACK: StaticCell<Stack<cyw43::NetDriver<'static>>> = StaticCell::new(); 87 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
84 static RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); 88 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 89
92 unwrap!(spawner.spawn(net_task(stack))); 90 unwrap!(spawner.spawn(net_task(runner)));
93 91
94 loop { 92 loop {
95 //control.join_open(WIFI_NETWORK).await; 93 match control
96 match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { 94 .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes()))
95 .await
96 {
97 Ok(_) => break, 97 Ok(_) => break,
98 Err(err) => { 98 Err(err) => {
99 info!("join failed with status={}", err.status); 99 info!("join failed with status={}", err.status);
diff --git a/examples/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs
new file mode 100644
index 000000000..1ae909917
--- /dev/null
+++ b/examples/rp/src/bin/wifi_webrequest.rs
@@ -0,0 +1,190 @@
1//! This example uses the RP Pico W board Wifi chip (cyw43).
2//! Connects to Wifi network and makes a web request to get the current time.
3
4#![no_std]
5#![no_main]
6#![allow(async_fn_in_trait)]
7
8use core::str::from_utf8;
9
10use cyw43::JoinOptions;
11use cyw43_pio::PioSpi;
12use defmt::*;
13use embassy_executor::Spawner;
14use embassy_net::dns::DnsSocket;
15use embassy_net::tcp::client::{TcpClient, TcpClientState};
16use embassy_net::{Config, StackResources};
17use embassy_rp::bind_interrupts;
18use embassy_rp::clocks::RoscRng;
19use embassy_rp::gpio::{Level, Output};
20use embassy_rp::peripherals::{DMA_CH0, PIO0};
21use embassy_rp::pio::{InterruptHandler, Pio};
22use embassy_time::{Duration, Timer};
23use rand::RngCore;
24use reqwless::client::{HttpClient, TlsConfig, TlsVerify};
25use reqwless::request::Method;
26use serde::Deserialize;
27use static_cell::StaticCell;
28use {defmt_rtt as _, panic_probe as _, serde_json_core};
29
30bind_interrupts!(struct Irqs {
31 PIO0_IRQ_0 => InterruptHandler<PIO0>;
32});
33
34const WIFI_NETWORK: &str = "ssid"; // change to your network SSID
35const WIFI_PASSWORD: &str = "pwd"; // change to your network password
36
37#[embassy_executor::task]
38async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
39 runner.run().await
40}
41
42#[embassy_executor::task]
43async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! {
44 runner.run().await
45}
46
47#[embassy_executor::main]
48async fn main(spawner: Spawner) {
49 info!("Hello World!");
50
51 let p = embassy_rp::init(Default::default());
52 let mut rng = RoscRng;
53
54 let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin");
55 let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin");
56 // To make flashing faster for development, you may want to flash the firmwares independently
57 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
58 // probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000
59 // probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000
60 // let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) };
61 // let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
62
63 let pwr = Output::new(p.PIN_23, Level::Low);
64 let cs = Output::new(p.PIN_25, Level::High);
65 let mut pio = Pio::new(p.PIO0, Irqs);
66 let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0);
67
68 static STATE: StaticCell<cyw43::State> = StaticCell::new();
69 let state = STATE.init(cyw43::State::new());
70 let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await;
71 unwrap!(spawner.spawn(cyw43_task(runner)));
72
73 control.init(clm).await;
74 control
75 .set_power_management(cyw43::PowerManagementMode::PowerSave)
76 .await;
77
78 let config = Config::dhcpv4(Default::default());
79 // Use static IP configuration instead of DHCP
80 //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
81 // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24),
82 // dns_servers: Vec::new(),
83 // gateway: Some(Ipv4Address::new(192, 168, 69, 1)),
84 //});
85
86 // Generate random seed
87 let seed = rng.next_u64();
88
89 // Init network stack
90 static RESOURCES: StaticCell<StackResources<5>> = StaticCell::new();
91 let (stack, runner) = embassy_net::new(net_device, config, RESOURCES.init(StackResources::new()), seed);
92
93 unwrap!(spawner.spawn(net_task(runner)));
94
95 loop {
96 match control
97 .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes()))
98 .await
99 {
100 Ok(_) => break,
101 Err(err) => {
102 info!("join failed with status={}", err.status);
103 }
104 }
105 }
106
107 // Wait for DHCP, not necessary when using static IP
108 info!("waiting for DHCP...");
109 while !stack.is_config_up() {
110 Timer::after_millis(100).await;
111 }
112 info!("DHCP is now up!");
113
114 info!("waiting for link up...");
115 while !stack.is_link_up() {
116 Timer::after_millis(500).await;
117 }
118 info!("Link is up!");
119
120 info!("waiting for stack to be up...");
121 stack.wait_config_up().await;
122 info!("Stack is up!");
123
124 // And now we can use it!
125
126 loop {
127 let mut rx_buffer = [0; 8192];
128 let mut tls_read_buffer = [0; 16640];
129 let mut tls_write_buffer = [0; 16640];
130
131 let client_state = TcpClientState::<1, 1024, 1024>::new();
132 let tcp_client = TcpClient::new(stack, &client_state);
133 let dns_client = DnsSocket::new(stack);
134 let tls_config = TlsConfig::new(seed, &mut tls_read_buffer, &mut tls_write_buffer, TlsVerify::None);
135
136 let mut http_client = HttpClient::new_with_tls(&tcp_client, &dns_client, tls_config);
137 let url = "https://worldtimeapi.org/api/timezone/Europe/Berlin";
138 // for non-TLS requests, use this instead:
139 // let mut http_client = HttpClient::new(&tcp_client, &dns_client);
140 // let url = "http://worldtimeapi.org/api/timezone/Europe/Berlin";
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 return; // handle the error
149 }
150 };
151
152 let response = match request.send(&mut rx_buffer).await {
153 Ok(resp) => resp,
154 Err(_e) => {
155 error!("Failed to send HTTP request");
156 return; // handle the error;
157 }
158 };
159
160 let body = match from_utf8(response.body().read_to_end().await.unwrap()) {
161 Ok(b) => b,
162 Err(_e) => {
163 error!("Failed to read response body");
164 return; // handle the error
165 }
166 };
167 info!("Response body: {:?}", &body);
168
169 // parse the response body and update the RTC
170
171 #[derive(Deserialize)]
172 struct ApiResponse<'a> {
173 datetime: &'a str,
174 // other fields as needed
175 }
176
177 let bytes = body.as_bytes();
178 match serde_json_core::de::from_slice::<ApiResponse>(bytes) {
179 Ok((output, _used)) => {
180 info!("Datetime: {:?}", output.datetime);
181 }
182 Err(_e) => {
183 error!("Failed to parse response body");
184 return; // handle the error
185 }
186 }
187
188 Timer::after(Duration::from_secs(5)).await;
189 }
190}
diff --git a/examples/rp/src/bin/zerocopy.rs b/examples/rp/src/bin/zerocopy.rs
new file mode 100644
index 000000000..39f03c8e4
--- /dev/null
+++ b/examples/rp/src/bin/zerocopy.rs
@@ -0,0 +1,94 @@
1//! This example shows how to use `zerocopy_channel` from `embassy_sync` for
2//! sending large values between two tasks without copying.
3//! The example also shows how to use the RP2040 ADC with DMA.
4#![no_std]
5#![no_main]
6
7use core::sync::atomic::{AtomicU16, Ordering};
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler};
12use embassy_rp::bind_interrupts;
13use embassy_rp::gpio::Pull;
14use embassy_rp::peripherals::DMA_CH0;
15use embassy_sync::blocking_mutex::raw::NoopRawMutex;
16use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender};
17use embassy_time::{Duration, Ticker, Timer};
18use static_cell::StaticCell;
19use {defmt_rtt as _, panic_probe as _};
20
21type SampleBuffer = [u16; 512];
22
23bind_interrupts!(struct Irqs {
24 ADC_IRQ_FIFO => InterruptHandler;
25});
26
27const BLOCK_SIZE: usize = 512;
28const NUM_BLOCKS: usize = 2;
29static MAX: AtomicU16 = AtomicU16::new(0);
30
31struct AdcParts {
32 adc: Adc<'static, Async>,
33 pin: adc::Channel<'static>,
34 dma: DMA_CH0,
35}
36
37#[embassy_executor::main]
38async fn main(spawner: Spawner) {
39 let p = embassy_rp::init(Default::default());
40 info!("Here we go!");
41
42 let adc_parts = AdcParts {
43 adc: Adc::new(p.ADC, Irqs, Config::default()),
44 pin: adc::Channel::new_pin(p.PIN_29, Pull::None),
45 dma: p.DMA_CH0,
46 };
47
48 static BUF: StaticCell<[SampleBuffer; NUM_BLOCKS]> = StaticCell::new();
49 let buf = BUF.init([[0; BLOCK_SIZE]; NUM_BLOCKS]);
50
51 static CHANNEL: StaticCell<Channel<'_, NoopRawMutex, SampleBuffer>> = StaticCell::new();
52 let channel = CHANNEL.init(Channel::new(buf));
53 let (sender, receiver) = channel.split();
54
55 spawner.must_spawn(consumer(receiver));
56 spawner.must_spawn(producer(sender, adc_parts));
57
58 let mut ticker = Ticker::every(Duration::from_secs(1));
59 loop {
60 ticker.next().await;
61 let max = MAX.load(Ordering::Relaxed);
62 info!("latest block's max value: {:?}", max);
63 }
64}
65
66#[embassy_executor::task]
67async fn producer(mut sender: Sender<'static, NoopRawMutex, SampleBuffer>, mut adc: AdcParts) {
68 loop {
69 // Obtain a free buffer from the channel
70 let buf = sender.send().await;
71
72 // Fill it with data
73 adc.adc.read_many(&mut adc.pin, buf, 1, &mut adc.dma).await.unwrap();
74
75 // Notify the channel that the buffer is now ready to be received
76 sender.send_done();
77 }
78}
79
80#[embassy_executor::task]
81async fn consumer(mut receiver: Receiver<'static, NoopRawMutex, SampleBuffer>) {
82 loop {
83 // Receive a buffer from the channel
84 let buf = receiver.receive().await;
85
86 // Simulate using the data, while the producer is filling up the next buffer
87 Timer::after_micros(1000).await;
88 let max = buf.iter().max().unwrap();
89 MAX.store(*max, Ordering::Relaxed);
90
91 // Notify the channel that the buffer is now ready to be reused
92 receiver.receive_done();
93 }
94}