aboutsummaryrefslogtreecommitdiff
path: root/examples/rp235x/src
diff options
context:
space:
mode:
authorCurly <[email protected]>2025-02-23 07:33:58 -0800
committerCurly <[email protected]>2025-02-23 07:33:58 -0800
commit3932835998802fc3abf7cce4f736e072858ebfd1 (patch)
tree5dd714b99bc74a03556c58809237c88691c293bb /examples/rp235x/src
parentc3c67db93e627a4fafe5e1a1123e5cbb4abafe47 (diff)
rename `rp23` (?) folder to `rp235x`; fix `ci.sh` to use `rp235x` folder
Diffstat (limited to 'examples/rp235x/src')
-rw-r--r--examples/rp235x/src/bin/adc.rs48
-rw-r--r--examples/rp235x/src/bin/adc_dma.rs54
-rw-r--r--examples/rp235x/src/bin/assign_resources.rs79
-rw-r--r--examples/rp235x/src/bin/blinky.rs42
-rw-r--r--examples/rp235x/src/bin/blinky_two_channels.rs50
-rw-r--r--examples/rp235x/src/bin/blinky_two_tasks.rs49
-rw-r--r--examples/rp235x/src/bin/button.rs28
-rw-r--r--examples/rp235x/src/bin/debounce.rs80
-rw-r--r--examples/rp235x/src/bin/flash.rs125
-rw-r--r--examples/rp235x/src/bin/gpio_async.rs40
-rw-r--r--examples/rp235x/src/bin/gpout.rs37
-rw-r--r--examples/rp235x/src/bin/i2c_async.rs110
-rw-r--r--examples/rp235x/src/bin/i2c_async_embassy.rs85
-rw-r--r--examples/rp235x/src/bin/i2c_blocking.rs74
-rw-r--r--examples/rp235x/src/bin/i2c_slave.rs117
-rw-r--r--examples/rp235x/src/bin/interrupt.rs93
-rw-r--r--examples/rp235x/src/bin/multicore.rs66
-rw-r--r--examples/rp235x/src/bin/multiprio.rs145
-rw-r--r--examples/rp235x/src/bin/otp.rs31
-rw-r--r--examples/rp235x/src/bin/pio_async.rs131
-rw-r--r--examples/rp235x/src/bin/pio_dma.rs84
-rw-r--r--examples/rp235x/src/bin/pio_hd44780.rs87
-rw-r--r--examples/rp235x/src/bin/pio_i2s.rs95
-rw-r--r--examples/rp235x/src/bin/pio_onewire.rs83
-rw-r--r--examples/rp235x/src/bin/pio_pwm.rs38
-rw-r--r--examples/rp235x/src/bin/pio_rotary_encoder.rs55
-rw-r--r--examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs112
-rw-r--r--examples/rp235x/src/bin/pio_servo.rs128
-rw-r--r--examples/rp235x/src/bin/pio_stepper.rs49
-rw-r--r--examples/rp235x/src/bin/pio_uart.rs190
-rw-r--r--examples/rp235x/src/bin/pio_ws2812.rs68
-rw-r--r--examples/rp235x/src/bin/pwm.rs79
-rw-r--r--examples/rp235x/src/bin/pwm_input.rs26
-rw-r--r--examples/rp235x/src/bin/pwm_tb6612fng_motor_driver.rs105
-rw-r--r--examples/rp235x/src/bin/rosc.rs31
-rw-r--r--examples/rp235x/src/bin/shared_bus.rs115
-rw-r--r--examples/rp235x/src/bin/sharing.rs150
-rw-r--r--examples/rp235x/src/bin/spi.rs46
-rw-r--r--examples/rp235x/src/bin/spi_async.rs31
-rw-r--r--examples/rp235x/src/bin/spi_display.rs177
-rw-r--r--examples/rp235x/src/bin/spi_sdmmc.rs82
-rw-r--r--examples/rp235x/src/bin/trng.rs49
-rw-r--r--examples/rp235x/src/bin/uart.rs25
-rw-r--r--examples/rp235x/src/bin/uart_buffered_split.rs58
-rw-r--r--examples/rp235x/src/bin/uart_r503.rs158
-rw-r--r--examples/rp235x/src/bin/uart_unidir.rs50
-rw-r--r--examples/rp235x/src/bin/usb_hid_keyboard.rs188
-rw-r--r--examples/rp235x/src/bin/usb_webusb.rs155
-rw-r--r--examples/rp235x/src/bin/watchdog.rs51
-rw-r--r--examples/rp235x/src/bin/wifi_blinky_pico_plus_2.rs88
-rw-r--r--examples/rp235x/src/bin/zerocopy.rs94
51 files changed, 4231 insertions, 0 deletions
diff --git a/examples/rp235x/src/bin/adc.rs b/examples/rp235x/src/bin/adc.rs
new file mode 100644
index 000000000..b7324f755
--- /dev/null
+++ b/examples/rp235x/src/bin/adc.rs
@@ -0,0 +1,48 @@
1//! This example test the ADC (Analog to Digital Conversion) of the RP2350A pins 26, 27 and 28.
2//! It also reads the temperature sensor in the chip.
3
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::Timer;
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 let mut adc = Adc::new(p.ADC, Irqs, Config::default());
23
24 let mut p26 = Channel::new_pin(p.PIN_26, Pull::None);
25 let mut p27 = Channel::new_pin(p.PIN_27, Pull::None);
26 let mut p28 = Channel::new_pin(p.PIN_28, Pull::None);
27 let mut ts = Channel::new_temp_sensor(p.ADC_TEMP_SENSOR);
28
29 loop {
30 let level = adc.read(&mut p26).await.unwrap();
31 info!("Pin 26 ADC: {}", level);
32 let level = adc.read(&mut p27).await.unwrap();
33 info!("Pin 27 ADC: {}", level);
34 let level = adc.read(&mut p28).await.unwrap();
35 info!("Pin 28 ADC: {}", level);
36 let temp = adc.read(&mut ts).await.unwrap();
37 info!("Temp: {} degrees", convert_to_celsius(temp));
38 Timer::after_secs(1).await;
39 }
40}
41
42fn convert_to_celsius(raw_temp: u16) -> f32 {
43 // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet
44 let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721;
45 let sign = if temp < 0.0 { -1.0 } else { 1.0 };
46 let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16;
47 (rounded_temp_x10 as f32) / 10.0
48}
diff --git a/examples/rp235x/src/bin/adc_dma.rs b/examples/rp235x/src/bin/adc_dma.rs
new file mode 100644
index 000000000..f755cf5bf
--- /dev/null
+++ b/examples/rp235x/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/rp235x/src/bin/assign_resources.rs b/examples/rp235x/src/bin/assign_resources.rs
new file mode 100644
index 000000000..ff6eff4a2
--- /dev/null
+++ b/examples/rp235x/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/rp235x/src/bin/blinky.rs b/examples/rp235x/src/bin/blinky.rs
new file mode 100644
index 000000000..2d962baca
--- /dev/null
+++ b/examples/rp235x/src/bin/blinky.rs
@@ -0,0 +1,42 @@
1//! This example test the RP Pico on board LED.
2//!
3//! It does not work with the RP Pico W board. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::gpio;
11use embassy_time::Timer;
12use gpio::{Level, Output};
13use {defmt_rtt as _, panic_probe as _};
14
15// Program metadata for `picotool info`.
16// This isn't needed, but it's recomended to have these minimal entries.
17#[link_section = ".bi_entries"]
18#[used]
19pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
20 embassy_rp::binary_info::rp_program_name!(c"Blinky Example"),
21 embassy_rp::binary_info::rp_program_description!(
22 c"This example tests the RP Pico on board LED, connected to gpio 25"
23 ),
24 embassy_rp::binary_info::rp_cargo_version!(),
25 embassy_rp::binary_info::rp_program_build_attribute!(),
26];
27
28#[embassy_executor::main]
29async fn main(_spawner: Spawner) {
30 let p = embassy_rp::init(Default::default());
31 let mut led = Output::new(p.PIN_25, Level::Low);
32
33 loop {
34 info!("led on!");
35 led.set_high();
36 Timer::after_millis(250).await;
37
38 info!("led off!");
39 led.set_low();
40 Timer::after_millis(250).await;
41 }
42}
diff --git a/examples/rp235x/src/bin/blinky_two_channels.rs b/examples/rp235x/src/bin/blinky_two_channels.rs
new file mode 100644
index 000000000..b2eec2a21
--- /dev/null
+++ b/examples/rp235x/src/bin/blinky_two_channels.rs
@@ -0,0 +1,50 @@
1#![no_std]
2#![no_main]
3/// This example demonstrates how to access a given pin from more than one embassy task
4/// The on-board LED is toggled by two tasks with slightly different periods, leading to the
5/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar
6/// to interference and the 'beats' you can hear if you play two frequencies close to one another
7/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::gpio;
11use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
12use embassy_sync::channel::{Channel, Sender};
13use embassy_time::{Duration, Ticker};
14use gpio::{AnyPin, Level, Output};
15use {defmt_rtt as _, panic_probe as _};
16
17enum LedState {
18 Toggle,
19}
20static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();
21
22#[embassy_executor::main]
23async fn main(spawner: Spawner) {
24 let p = embassy_rp::init(Default::default());
25 let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High);
26
27 let dt = 100 * 1_000_000;
28 let k = 1.003;
29
30 unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));
31 unwrap!(spawner.spawn(toggle_led(
32 CHANNEL.sender(),
33 Duration::from_nanos((dt as f64 * k) as u64)
34 )));
35
36 loop {
37 match CHANNEL.receive().await {
38 LedState::Toggle => led.toggle(),
39 }
40 }
41}
42
43#[embassy_executor::task(pool_size = 2)]
44async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {
45 let mut ticker = Ticker::every(delay);
46 loop {
47 control.send(LedState::Toggle).await;
48 ticker.next().await;
49 }
50}
diff --git a/examples/rp235x/src/bin/blinky_two_tasks.rs b/examples/rp235x/src/bin/blinky_two_tasks.rs
new file mode 100644
index 000000000..a57b513d6
--- /dev/null
+++ b/examples/rp235x/src/bin/blinky_two_tasks.rs
@@ -0,0 +1,49 @@
1#![no_std]
2#![no_main]
3/// This example demonstrates how to access a given pin from more than one embassy task
4/// The on-board LED is toggled by two tasks with slightly different periods, leading to the
5/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar
6/// to interference and the 'beats' you can hear if you play two frequencies close to one another
7/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::gpio;
11use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
12use embassy_sync::mutex::Mutex;
13use embassy_time::{Duration, Ticker};
14use gpio::{AnyPin, Level, Output};
15use {defmt_rtt as _, panic_probe as _};
16
17type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>;
18static LED: LedType = Mutex::new(None);
19
20#[embassy_executor::main]
21async fn main(spawner: Spawner) {
22 let p = embassy_rp::init(Default::default());
23 // set the content of the global LED reference to the real LED pin
24 let led = Output::new(AnyPin::from(p.PIN_25), Level::High);
25 // inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the
26 // Mutex is released
27 {
28 *(LED.lock().await) = Some(led);
29 }
30 let dt = 100 * 1_000_000;
31 let k = 1.003;
32
33 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt))));
34 unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));
35}
36
37#[embassy_executor::task(pool_size = 2)]
38async fn toggle_led(led: &'static LedType, delay: Duration) {
39 let mut ticker = Ticker::every(delay);
40 loop {
41 {
42 let mut led_unlocked = led.lock().await;
43 if let Some(pin_ref) = led_unlocked.as_mut() {
44 pin_ref.toggle();
45 }
46 }
47 ticker.next().await;
48 }
49}
diff --git a/examples/rp235x/src/bin/button.rs b/examples/rp235x/src/bin/button.rs
new file mode 100644
index 000000000..4ad2ca3b7
--- /dev/null
+++ b/examples/rp235x/src/bin/button.rs
@@ -0,0 +1,28 @@
1//! This example uses the RP Pico on board LED to test input pin 28. This is not the button on the board.
2//!
3//! It does not work with the RP Pico W board. Use wifi_blinky.rs and add input pin.
4
5#![no_std]
6#![no_main]
7
8use embassy_executor::Spawner;
9use embassy_rp::gpio::{Input, Level, Output, Pull};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let p = embassy_rp::init(Default::default());
15 let mut led = Output::new(p.PIN_25, Level::Low);
16
17 // Use PIN_28, Pin34 on J0 for RP Pico, as a input.
18 // You need to add your own button.
19 let button = Input::new(p.PIN_28, Pull::Up);
20
21 loop {
22 if button.is_high() {
23 led.set_high();
24 } else {
25 led.set_low();
26 }
27 }
28}
diff --git a/examples/rp235x/src/bin/debounce.rs b/examples/rp235x/src/bin/debounce.rs
new file mode 100644
index 000000000..0077f19fc
--- /dev/null
+++ b/examples/rp235x/src/bin/debounce.rs
@@ -0,0 +1,80 @@
1//! This example shows the ease of debouncing a button with async rust.
2//! Hook up a button or switch between pin 9 and ground.
3
4#![no_std]
5#![no_main]
6
7use defmt::info;
8use embassy_executor::Spawner;
9use embassy_rp::gpio::{Input, Level, Pull};
10use embassy_time::{with_deadline, Duration, Instant, Timer};
11use {defmt_rtt as _, panic_probe as _};
12
13pub struct Debouncer<'a> {
14 input: Input<'a>,
15 debounce: Duration,
16}
17
18impl<'a> Debouncer<'a> {
19 pub fn new(input: Input<'a>, debounce: Duration) -> Self {
20 Self { input, debounce }
21 }
22
23 pub async fn debounce(&mut self) -> Level {
24 loop {
25 let l1 = self.input.get_level();
26
27 self.input.wait_for_any_edge().await;
28
29 Timer::after(self.debounce).await;
30
31 let l2 = self.input.get_level();
32 if l1 != l2 {
33 break l2;
34 }
35 }
36 }
37}
38
39#[embassy_executor::main]
40async fn main(_spawner: Spawner) {
41 let p = embassy_rp::init(Default::default());
42 let mut btn = Debouncer::new(Input::new(p.PIN_9, Pull::Up), Duration::from_millis(20));
43
44 info!("Debounce Demo");
45
46 loop {
47 // button pressed
48 btn.debounce().await;
49 let start = Instant::now();
50 info!("Button Press");
51
52 match with_deadline(start + Duration::from_secs(1), btn.debounce()).await {
53 // Button Released < 1s
54 Ok(_) => {
55 info!("Button pressed for: {}ms", start.elapsed().as_millis());
56 continue;
57 }
58 // button held for > 1s
59 Err(_) => {
60 info!("Button Held");
61 }
62 }
63
64 match with_deadline(start + Duration::from_secs(5), btn.debounce()).await {
65 // Button released <5s
66 Ok(_) => {
67 info!("Button pressed for: {}ms", start.elapsed().as_millis());
68 continue;
69 }
70 // button held for > >5s
71 Err(_) => {
72 info!("Button Long Held");
73 }
74 }
75
76 // wait for button release before handling another press
77 btn.debounce().await;
78 info!("Button pressed for: {}ms", start.elapsed().as_millis());
79 }
80}
diff --git a/examples/rp235x/src/bin/flash.rs b/examples/rp235x/src/bin/flash.rs
new file mode 100644
index 000000000..31ad4aafc
--- /dev/null
+++ b/examples/rp235x/src/bin/flash.rs
@@ -0,0 +1,125 @@
1//! This example test the flash connected to the RP2350 chip.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE};
9use embassy_rp::peripherals::FLASH;
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12
13const ADDR_OFFSET: u32 = 0x100000;
14const FLASH_SIZE: usize = 2 * 1024 * 1024;
15
16#[embassy_executor::main]
17async fn main(_spawner: Spawner) {
18 let p = embassy_rp::init(Default::default());
19 info!("Hello World!");
20
21 // add some delay to give an attached debug probe time to parse the
22 // defmt RTT header. Reading that header might touch flash memory, which
23 // interferes with flash write operations.
24 // https://github.com/knurling-rs/defmt/pull/683
25 Timer::after_millis(10).await;
26
27 let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0);
28
29 erase_write_sector(&mut flash, 0x00);
30
31 multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
32
33 background_read(&mut flash, (ERASE_SIZE * 2) as u32).await;
34
35 info!("Flash Works!");
36}
37
38fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
39 info!(">>>> [multiwrite_bytes]");
40 let mut read_buf = [0u8; ERASE_SIZE];
41 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
42
43 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
44 info!("Contents start with {=[u8]}", read_buf[0..4]);
45
46 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
47
48 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
49 info!("Contents after erase starts with {=[u8]}", read_buf[0..4]);
50 if read_buf.iter().any(|x| *x != 0xFF) {
51 defmt::panic!("unexpected");
52 }
53
54 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &[0x01]));
55 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 1, &[0x02]));
56 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 2, &[0x03]));
57 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 3, &[0x04]));
58
59 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
60 info!("Contents after write starts with {=[u8]}", read_buf[0..4]);
61 if read_buf[0..4] != [0x01, 0x02, 0x03, 0x04] {
62 defmt::panic!("unexpected");
63 }
64}
65
66fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
67 info!(">>>> [erase_write_sector]");
68 let mut buf = [0u8; ERASE_SIZE];
69 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
70
71 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
72 info!("Contents start with {=[u8]}", buf[0..4]);
73
74 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
75
76 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
77 info!("Contents after erase starts with {=[u8]}", buf[0..4]);
78 if buf.iter().any(|x| *x != 0xFF) {
79 defmt::panic!("unexpected");
80 }
81
82 for b in buf.iter_mut() {
83 *b = 0xDA;
84 }
85
86 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &buf));
87
88 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
89 info!("Contents after write starts with {=[u8]}", buf[0..4]);
90 if buf.iter().any(|x| *x != 0xDA) {
91 defmt::panic!("unexpected");
92 }
93}
94
95async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
96 info!(">>>> [background_read]");
97
98 let mut buf = [0u32; 8];
99 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
100
101 info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
102 info!("Contents start with {=u32:x}", buf[0]);
103
104 defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
105
106 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
107 info!("Contents after erase starts with {=u32:x}", buf[0]);
108 if buf.iter().any(|x| *x != 0xFFFFFFFF) {
109 defmt::panic!("unexpected");
110 }
111
112 for b in buf.iter_mut() {
113 *b = 0xDABA1234;
114 }
115
116 defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, unsafe {
117 core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4)
118 }));
119
120 defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
121 info!("Contents after write starts with {=u32:x}", buf[0]);
122 if buf.iter().any(|x| *x != 0xDABA1234) {
123 defmt::panic!("unexpected");
124 }
125}
diff --git a/examples/rp235x/src/bin/gpio_async.rs b/examples/rp235x/src/bin/gpio_async.rs
new file mode 100644
index 000000000..b79fb2a15
--- /dev/null
+++ b/examples/rp235x/src/bin/gpio_async.rs
@@ -0,0 +1,40 @@
1//! This example shows how async gpio can be used with a RP2040.
2//!
3//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::gpio;
11use embassy_time::Timer;
12use gpio::{Input, Level, Output, Pull};
13use {defmt_rtt as _, panic_probe as _};
14
15/// It requires an external signal to be manually triggered on PIN 16. For
16/// example, this could be accomplished using an external power source with a
17/// button so that it is possible to toggle the signal from low to high.
18///
19/// This example will begin with turning on the LED on the board and wait for a
20/// high signal on PIN 16. Once the high event/signal occurs the program will
21/// continue and turn off the LED, and then wait for 2 seconds before completing
22/// the loop and starting over again.
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let p = embassy_rp::init(Default::default());
26 let mut led = Output::new(p.PIN_25, Level::Low);
27 let mut async_input = Input::new(p.PIN_16, Pull::None);
28
29 loop {
30 info!("wait_for_high. Turn on LED");
31 led.set_high();
32
33 async_input.wait_for_high().await;
34
35 info!("done wait_for_high. Turn off LED");
36 led.set_low();
37
38 Timer::after_secs(2).await;
39 }
40}
diff --git a/examples/rp235x/src/bin/gpout.rs b/examples/rp235x/src/bin/gpout.rs
new file mode 100644
index 000000000..011359253
--- /dev/null
+++ b/examples/rp235x/src/bin/gpout.rs
@@ -0,0 +1,37 @@
1//! This example shows how GPOUT (General purpose clock outputs) can toggle a output pin.
2//!
3//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::clocks;
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13
14#[embassy_executor::main]
15async fn main(_spawner: Spawner) {
16 let p = embassy_rp::init(Default::default());
17
18 let gpout3 = clocks::Gpout::new(p.PIN_25);
19 gpout3.set_div(1000, 0);
20 gpout3.enable();
21
22 loop {
23 gpout3.set_src(clocks::GpoutSrc::Sys);
24 info!(
25 "Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}",
26 gpout3.get_freq()
27 );
28 Timer::after_secs(2).await;
29
30 gpout3.set_src(clocks::GpoutSrc::Ref);
31 info!(
32 "Pin 25 is now outputing CLK_REF/1000, should be toggling at {}",
33 gpout3.get_freq()
34 );
35 Timer::after_secs(2).await;
36 }
37}
diff --git a/examples/rp235x/src/bin/i2c_async.rs b/examples/rp235x/src/bin/i2c_async.rs
new file mode 100644
index 000000000..e31cc894c
--- /dev/null
+++ b/examples/rp235x/src/bin/i2c_async.rs
@@ -0,0 +1,110 @@
1//! This example shows how to communicate asynchronous using i2c with external chips.
2//!
3//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.
4//! (https://www.microchip.com/en-us/product/mcp23017)
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::bind_interrupts;
12use embassy_rp::i2c::{self, Config, InterruptHandler};
13use embassy_rp::peripherals::I2C1;
14use embassy_time::Timer;
15use embedded_hal_async::i2c::I2c;
16use {defmt_rtt as _, panic_probe as _};
17
18bind_interrupts!(struct Irqs {
19 I2C1_IRQ => InterruptHandler<I2C1>;
20});
21
22#[allow(dead_code)]
23mod mcp23017 {
24 pub const ADDR: u8 = 0x20; // default addr
25
26 macro_rules! mcpregs {
27 ($($name:ident : $val:expr),* $(,)?) => {
28 $(
29 pub const $name: u8 = $val;
30 )*
31
32 pub fn regname(reg: u8) -> &'static str {
33 match reg {
34 $(
35 $val => stringify!($name),
36 )*
37 _ => panic!("bad reg"),
38 }
39 }
40 }
41 }
42
43 // These are correct for IOCON.BANK=0
44 mcpregs! {
45 IODIRA: 0x00,
46 IPOLA: 0x02,
47 GPINTENA: 0x04,
48 DEFVALA: 0x06,
49 INTCONA: 0x08,
50 IOCONA: 0x0A,
51 GPPUA: 0x0C,
52 INTFA: 0x0E,
53 INTCAPA: 0x10,
54 GPIOA: 0x12,
55 OLATA: 0x14,
56 IODIRB: 0x01,
57 IPOLB: 0x03,
58 GPINTENB: 0x05,
59 DEFVALB: 0x07,
60 INTCONB: 0x09,
61 IOCONB: 0x0B,
62 GPPUB: 0x0D,
63 INTFB: 0x0F,
64 INTCAPB: 0x11,
65 GPIOB: 0x13,
66 OLATB: 0x15,
67 }
68}
69
70#[embassy_executor::main]
71async fn main(_spawner: Spawner) {
72 let p = embassy_rp::init(Default::default());
73
74 let sda = p.PIN_14;
75 let scl = p.PIN_15;
76
77 info!("set up i2c ");
78 let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, Config::default());
79
80 use mcp23017::*;
81
82 info!("init mcp23017 config for IxpandO");
83 // init - a outputs, b inputs
84 i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap();
85 i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap();
86 i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups
87
88 let mut val = 1;
89 loop {
90 let mut portb = [0];
91
92 i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap();
93 info!("portb = {:02x}", portb[0]);
94 i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap();
95 val = val.rotate_left(1);
96
97 // get a register dump
98 info!("getting register dump");
99 let mut regs = [0; 22];
100 i2c.write_read(ADDR, &[0], &mut regs).await.unwrap();
101 // always get the regdump but only display it if portb'0 is set
102 if portb[0] & 1 != 0 {
103 for (idx, reg) in regs.into_iter().enumerate() {
104 info!("{} => {:02x}", regname(idx as u8), reg);
105 }
106 }
107
108 Timer::after_millis(100).await;
109 }
110}
diff --git a/examples/rp235x/src/bin/i2c_async_embassy.rs b/examples/rp235x/src/bin/i2c_async_embassy.rs
new file mode 100644
index 000000000..a65b71b9f
--- /dev/null
+++ b/examples/rp235x/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/rp235x/src/bin/i2c_blocking.rs b/examples/rp235x/src/bin/i2c_blocking.rs
new file mode 100644
index 000000000..c9c8a2760
--- /dev/null
+++ b/examples/rp235x/src/bin/i2c_blocking.rs
@@ -0,0 +1,74 @@
1//! This example shows how to communicate using i2c with external chips.
2//!
3//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.
4//! (https://www.microchip.com/en-us/product/mcp23017)
5
6#![no_std]
7#![no_main]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::i2c::{self, Config};
12use embassy_time::Timer;
13use embedded_hal_1::i2c::I2c;
14use {defmt_rtt as _, panic_probe as _};
15
16#[allow(dead_code)]
17mod mcp23017 {
18 pub const ADDR: u8 = 0x20; // default addr
19
20 pub const IODIRA: u8 = 0x00;
21 pub const IPOLA: u8 = 0x02;
22 pub const GPINTENA: u8 = 0x04;
23 pub const DEFVALA: u8 = 0x06;
24 pub const INTCONA: u8 = 0x08;
25 pub const IOCONA: u8 = 0x0A;
26 pub const GPPUA: u8 = 0x0C;
27 pub const INTFA: u8 = 0x0E;
28 pub const INTCAPA: u8 = 0x10;
29 pub const GPIOA: u8 = 0x12;
30 pub const OLATA: u8 = 0x14;
31 pub const IODIRB: u8 = 0x01;
32 pub const IPOLB: u8 = 0x03;
33 pub const GPINTENB: u8 = 0x05;
34 pub const DEFVALB: u8 = 0x07;
35 pub const INTCONB: u8 = 0x09;
36 pub const IOCONB: u8 = 0x0B;
37 pub const GPPUB: u8 = 0x0D;
38 pub const INTFB: u8 = 0x0F;
39 pub const INTCAPB: u8 = 0x11;
40 pub const GPIOB: u8 = 0x13;
41 pub const OLATB: u8 = 0x15;
42}
43
44#[embassy_executor::main]
45async fn main(_spawner: Spawner) {
46 let p = embassy_rp::init(Default::default());
47
48 let sda = p.PIN_14;
49 let scl = p.PIN_15;
50
51 info!("set up i2c ");
52 let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());
53
54 use mcp23017::*;
55
56 info!("init mcp23017 config for IxpandO");
57 // init - a outputs, b inputs
58 i2c.write(ADDR, &[IODIRA, 0x00]).unwrap();
59 i2c.write(ADDR, &[IODIRB, 0xff]).unwrap();
60 i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups
61
62 let mut val = 0xaa;
63 loop {
64 let mut portb = [0];
65
66 i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap();
67 i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap();
68
69 info!("portb = {:02x}", portb[0]);
70 val = !val;
71
72 Timer::after_secs(1).await;
73 }
74}
diff --git a/examples/rp235x/src/bin/i2c_slave.rs b/examples/rp235x/src/bin/i2c_slave.rs
new file mode 100644
index 000000000..9fffb4646
--- /dev/null
+++ b/examples/rp235x/src/bin/i2c_slave.rs
@@ -0,0 +1,117 @@
1//! This example shows how to use the 2040 as an i2c slave.
2#![no_std]
3#![no_main]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::peripherals::{I2C0, I2C1};
8use embassy_rp::{bind_interrupts, i2c, i2c_slave};
9use embassy_time::Timer;
10use embedded_hal_async::i2c::I2c;
11use {defmt_rtt as _, panic_probe as _};
12
13bind_interrupts!(struct Irqs {
14 I2C0_IRQ => i2c::InterruptHandler<I2C0>;
15 I2C1_IRQ => i2c::InterruptHandler<I2C1>;
16});
17
18const DEV_ADDR: u8 = 0x42;
19
20#[embassy_executor::task]
21async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! {
22 info!("Device start");
23
24 let mut state = 0;
25
26 loop {
27 let mut buf = [0u8; 128];
28 match dev.listen(&mut buf).await {
29 Ok(i2c_slave::Command::GeneralCall(len)) => info!("Device received general call write: {}", buf[..len]),
30 Ok(i2c_slave::Command::Read) => loop {
31 match dev.respond_to_read(&[state]).await {
32 Ok(x) => match x {
33 i2c_slave::ReadStatus::Done => break,
34 i2c_slave::ReadStatus::NeedMoreBytes => (),
35 i2c_slave::ReadStatus::LeftoverBytes(x) => {
36 info!("tried to write {} extra bytes", x);
37 break;
38 }
39 },
40 Err(e) => error!("error while responding {}", e),
41 }
42 },
43 Ok(i2c_slave::Command::Write(len)) => info!("Device received write: {}", buf[..len]),
44 Ok(i2c_slave::Command::WriteRead(len)) => {
45 info!("device received write read: {:x}", buf[..len]);
46 match buf[0] {
47 // Set the state
48 0xC2 => {
49 state = buf[1];
50 match dev.respond_and_fill(&[state], 0x00).await {
51 Ok(read_status) => info!("response read status {}", read_status),
52 Err(e) => error!("error while responding {}", e),
53 }
54 }
55 // Reset State
56 0xC8 => {
57 state = 0;
58 match dev.respond_and_fill(&[state], 0x00).await {
59 Ok(read_status) => info!("response read status {}", read_status),
60 Err(e) => error!("error while responding {}", e),
61 }
62 }
63 x => error!("Invalid Write Read {:x}", x),
64 }
65 }
66 Err(e) => error!("{}", e),
67 }
68 }
69}
70
71#[embassy_executor::task]
72async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) {
73 info!("Controller start");
74
75 loop {
76 let mut resp_buff = [0u8; 2];
77 for i in 0..10 {
78 match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await {
79 Ok(_) => info!("write_read response: {}", resp_buff),
80 Err(e) => error!("Error writing {}", e),
81 }
82
83 Timer::after_millis(100).await;
84 }
85 match con.read(DEV_ADDR, &mut resp_buff).await {
86 Ok(_) => info!("read response: {}", resp_buff),
87 Err(e) => error!("Error writing {}", e),
88 }
89 match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await {
90 Ok(_) => info!("write_read response: {}", resp_buff),
91 Err(e) => error!("Error writing {}", e),
92 }
93 Timer::after_millis(100).await;
94 }
95}
96
97#[embassy_executor::main]
98async fn main(spawner: Spawner) {
99 let p = embassy_rp::init(Default::default());
100 info!("Hello World!");
101
102 let d_sda = p.PIN_3;
103 let d_scl = p.PIN_2;
104 let mut config = i2c_slave::Config::default();
105 config.addr = DEV_ADDR as u16;
106 let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config);
107
108 unwrap!(spawner.spawn(device_task(device)));
109
110 let c_sda = p.PIN_1;
111 let c_scl = p.PIN_0;
112 let mut config = i2c::Config::default();
113 config.frequency = 1_000_000;
114 let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config);
115
116 unwrap!(spawner.spawn(controller_task(controller)));
117}
diff --git a/examples/rp235x/src/bin/interrupt.rs b/examples/rp235x/src/bin/interrupt.rs
new file mode 100644
index 000000000..e9ac76486
--- /dev/null
+++ b/examples/rp235x/src/bin/interrupt.rs
@@ -0,0 +1,93 @@
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 let p = embassy_rp::init(Default::default());
36
37 let adc = Adc::new_blocking(p.ADC, Default::default());
38 let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None);
39 ADC.lock(|a| a.borrow_mut().replace((adc, p26)));
40
41 let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default());
42 PWM.lock(|p| p.borrow_mut().replace(pwm));
43
44 // Enable the interrupt for pwm slice 4
45 embassy_rp::pac::PWM.irq0_inte().modify(|w| w.set_ch4(true));
46 unsafe {
47 cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP_0);
48 }
49
50 // Tasks require their resources to have 'static lifetime
51 // No Mutex needed when sharing within the same executor/prio level
52 static AVG: StaticCell<Cell<u32>> = StaticCell::new();
53 let avg = AVG.init(Default::default());
54 spawner.must_spawn(processing(avg));
55
56 let mut ticker = Ticker::every(Duration::from_secs(1));
57 loop {
58 ticker.next().await;
59 let freq = COUNTER.swap(0, Ordering::Relaxed);
60 info!("pwm freq: {:?} Hz", freq);
61 info!("adc average: {:?}", avg.get());
62
63 // Update the pwm duty cycle, based on the averaged adc reading
64 let mut config = Config::default();
65 config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _;
66 PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config));
67 }
68}
69
70#[embassy_executor::task]
71async fn processing(avg: &'static Cell<u32>) {
72 let mut buffer: heapless::HistoryBuffer<u16, 100> = Default::default();
73 loop {
74 let val = ADC_VALUES.receive().await;
75 buffer.write(val);
76 let sum: u32 = buffer.iter().map(|x| *x as u32).sum();
77 avg.set(sum / buffer.len() as u32);
78 }
79}
80
81#[interrupt]
82fn PWM_IRQ_WRAP_0() {
83 critical_section::with(|cs| {
84 let mut adc = ADC.borrow(cs).borrow_mut();
85 let (adc, p26) = adc.as_mut().unwrap();
86 let val = adc.blocking_read(p26).unwrap();
87 ADC_VALUES.try_send(val).ok();
88
89 // Clear the interrupt, so we don't immediately re-enter this irq handler
90 PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped();
91 });
92 COUNTER.fetch_add(1, Ordering::Relaxed);
93}
diff --git a/examples/rp235x/src/bin/multicore.rs b/examples/rp235x/src/bin/multicore.rs
new file mode 100644
index 000000000..7cb546c91
--- /dev/null
+++ b/examples/rp235x/src/bin/multicore.rs
@@ -0,0 +1,66 @@
1//! This example shows how to send messages between the two cores in the RP2040 chip.
2//!
3//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Executor;
10use embassy_rp::gpio::{Level, Output};
11use embassy_rp::multicore::{spawn_core1, Stack};
12use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
13use embassy_sync::channel::Channel;
14use embassy_time::Timer;
15use static_cell::StaticCell;
16use {defmt_rtt as _, panic_probe as _};
17
18static mut CORE1_STACK: Stack<4096> = Stack::new();
19static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
20static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
21static CHANNEL: Channel<CriticalSectionRawMutex, LedState, 1> = Channel::new();
22
23enum LedState {
24 On,
25 Off,
26}
27
28#[cortex_m_rt::entry]
29fn main() -> ! {
30 let p = embassy_rp::init(Default::default());
31 let led = Output::new(p.PIN_25, Level::Low);
32
33 spawn_core1(
34 p.CORE1,
35 unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },
36 move || {
37 let executor1 = EXECUTOR1.init(Executor::new());
38 executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led))));
39 },
40 );
41
42 let executor0 = EXECUTOR0.init(Executor::new());
43 executor0.run(|spawner| unwrap!(spawner.spawn(core0_task())));
44}
45
46#[embassy_executor::task]
47async fn core0_task() {
48 info!("Hello from core 0");
49 loop {
50 CHANNEL.send(LedState::On).await;
51 Timer::after_millis(100).await;
52 CHANNEL.send(LedState::Off).await;
53 Timer::after_millis(400).await;
54 }
55}
56
57#[embassy_executor::task]
58async fn core1_task(mut led: Output<'static>) {
59 info!("Hello from core 1");
60 loop {
61 match CHANNEL.receive().await {
62 LedState::On => led.set_high(),
63 LedState::Off => led.set_low(),
64 }
65 }
66}
diff --git a/examples/rp235x/src/bin/multiprio.rs b/examples/rp235x/src/bin/multiprio.rs
new file mode 100644
index 000000000..2b397f97d
--- /dev/null
+++ b/examples/rp235x/src/bin/multiprio.rs
@@ -0,0 +1,145 @@
1//! This example showcases how to create multiple Executor instances to run tasks at
2//! different priority levels.
3//!
4//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling
5//! there's work in the queue, and `wfe` for waiting for work.
6//!
7//! Medium and high priority executors run in two interrupts with different priorities.
8//! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since
9//! when there's work the interrupt will trigger and run the executor.
10//!
11//! Sample output below. Note that high priority ticks can interrupt everything else, and
12//! medium priority computations can interrupt low priority computations, making them to appear
13//! to take significantly longer time.
14//!
15//! ```not_rust
16//! [med] Starting long computation
17//! [med] done in 992 ms
18//! [high] tick!
19//! [low] Starting long computation
20//! [med] Starting long computation
21//! [high] tick!
22//! [high] tick!
23//! [med] done in 993 ms
24//! [med] Starting long computation
25//! [high] tick!
26//! [high] tick!
27//! [med] done in 993 ms
28//! [low] done in 3972 ms
29//! [med] Starting long computation
30//! [high] tick!
31//! [high] tick!
32//! [med] done in 993 ms
33//! ```
34//!
35//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor.
36//! You will get an output like the following. Note that no computation is ever interrupted.
37//!
38//! ```not_rust
39//! [high] tick!
40//! [med] Starting long computation
41//! [med] done in 496 ms
42//! [low] Starting long computation
43//! [low] done in 992 ms
44//! [med] Starting long computation
45//! [med] done in 496 ms
46//! [high] tick!
47//! [low] Starting long computation
48//! [low] done in 992 ms
49//! [high] tick!
50//! [med] Starting long computation
51//! [med] done in 496 ms
52//! [high] tick!
53//! ```
54//!
55
56#![no_std]
57#![no_main]
58
59use cortex_m_rt::entry;
60use defmt::{info, unwrap};
61use embassy_executor::{Executor, InterruptExecutor};
62use embassy_rp::interrupt;
63use embassy_rp::interrupt::{InterruptExt, Priority};
64use embassy_time::{Instant, Timer, TICK_HZ};
65use static_cell::StaticCell;
66use {defmt_rtt as _, panic_probe as _};
67
68#[embassy_executor::task]
69async fn run_high() {
70 loop {
71 info!(" [high] tick!");
72 Timer::after_ticks(673740).await;
73 }
74}
75
76#[embassy_executor::task]
77async fn run_med() {
78 loop {
79 let start = Instant::now();
80 info!(" [med] Starting long computation");
81
82 // Spin-wait to simulate a long CPU computation
83 embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second
84
85 let end = Instant::now();
86 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
87 info!(" [med] done in {} ms", ms);
88
89 Timer::after_ticks(53421).await;
90 }
91}
92
93#[embassy_executor::task]
94async fn run_low() {
95 loop {
96 let start = Instant::now();
97 info!("[low] Starting long computation");
98
99 // Spin-wait to simulate a long CPU computation
100 embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds
101
102 let end = Instant::now();
103 let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
104 info!("[low] done in {} ms", ms);
105
106 Timer::after_ticks(82983).await;
107 }
108}
109
110static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();
111static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();
112static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
113
114#[interrupt]
115unsafe fn SWI_IRQ_1() {
116 EXECUTOR_HIGH.on_interrupt()
117}
118
119#[interrupt]
120unsafe fn SWI_IRQ_0() {
121 EXECUTOR_MED.on_interrupt()
122}
123
124#[entry]
125fn main() -> ! {
126 info!("Hello World!");
127
128 let _p = embassy_rp::init(Default::default());
129
130 // High-priority executor: SWI_IRQ_1, priority level 2
131 interrupt::SWI_IRQ_1.set_priority(Priority::P2);
132 let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1);
133 unwrap!(spawner.spawn(run_high()));
134
135 // Medium-priority executor: SWI_IRQ_0, priority level 3
136 interrupt::SWI_IRQ_0.set_priority(Priority::P3);
137 let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0);
138 unwrap!(spawner.spawn(run_med()));
139
140 // Low priority executor: runs in thread mode, using WFE/SEV
141 let executor = EXECUTOR_LOW.init(Executor::new());
142 executor.run(|spawner| {
143 unwrap!(spawner.spawn(run_low()));
144 });
145}
diff --git a/examples/rp235x/src/bin/otp.rs b/examples/rp235x/src/bin/otp.rs
new file mode 100644
index 000000000..5ffbb7610
--- /dev/null
+++ b/examples/rp235x/src/bin/otp.rs
@@ -0,0 +1,31 @@
1//! This example shows reading the OTP constants on the RP235x.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::otp;
9use embassy_time::Timer;
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_spawner: Spawner) {
14 let _ = embassy_rp::init(Default::default());
15 //
16 // add some delay to give an attached debug probe time to parse the
17 // defmt RTT header. Reading that header might touch flash memory, which
18 // interferes with flash write operations.
19 // https://github.com/knurling-rs/defmt/pull/683
20 Timer::after_millis(10).await;
21
22 let chip_id = unwrap!(otp::get_chipid());
23 info!("Unique id:{:X}", chip_id);
24
25 let private_rand = unwrap!(otp::get_private_random_number());
26 info!("Private Rand:{:X}", private_rand);
27
28 loop {
29 Timer::after_secs(1).await;
30 }
31}
diff --git a/examples/rp235x/src/bin/pio_async.rs b/examples/rp235x/src/bin/pio_async.rs
new file mode 100644
index 000000000..08c702347
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_async.rs
@@ -0,0 +1,131 @@
1//! This example shows powerful PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use defmt::info;
6use embassy_executor::Spawner;
7use embassy_rp::bind_interrupts;
8use embassy_rp::peripherals::PIO0;
9use embassy_rp::pio::program::pio_asm;
10use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};
11use fixed::traits::ToFixed;
12use fixed_macro::types::U56F8;
13use {defmt_rtt as _, panic_probe as _};
14
15bind_interrupts!(struct Irqs {
16 PIO0_IRQ_0 => InterruptHandler<PIO0>;
17});
18
19fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) {
20 // Setup sm0
21
22 // Send data serially to pin
23 let prg = pio_asm!(
24 ".origin 16",
25 "set pindirs, 1",
26 ".wrap_target",
27 "out pins,1 [19]",
28 ".wrap",
29 );
30
31 let mut cfg = Config::default();
32 cfg.use_program(&pio.load_program(&prg.program), &[]);
33 let out_pin = pio.make_pio_pin(pin);
34 cfg.set_out_pins(&[&out_pin]);
35 cfg.set_set_pins(&[&out_pin]);
36 cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed();
37 cfg.shift_out.auto_fill = true;
38 sm.set_config(&cfg);
39}
40
41#[embassy_executor::task]
42async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) {
43 sm.set_enable(true);
44
45 let mut v = 0x0f0caffa;
46 loop {
47 sm.tx().wait_push(v).await;
48 v ^= 0xffff;
49 info!("Pushed {:032b} to FIFO", v);
50 }
51}
52
53fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) {
54 // Setupm sm1
55
56 // Read 0b10101 repeatedly until ISR is full
57 let prg = pio_asm!(
58 //
59 ".origin 8",
60 "set x, 0x15",
61 ".wrap_target",
62 "in x, 5 [31]",
63 ".wrap",
64 );
65
66 let mut cfg = Config::default();
67 cfg.use_program(&pio.load_program(&prg.program), &[]);
68 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
69 cfg.shift_in.auto_fill = true;
70 cfg.shift_in.direction = ShiftDirection::Right;
71 sm.set_config(&cfg);
72}
73
74#[embassy_executor::task]
75async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) {
76 sm.set_enable(true);
77 loop {
78 let rx = sm.rx().wait_pull().await;
79 info!("Pulled {:032b} from FIFO", rx);
80 }
81}
82
83fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) {
84 // Setup sm2
85
86 // Repeatedly trigger IRQ 3
87 let prg = pio_asm!(
88 ".origin 0",
89 ".wrap_target",
90 "set x,10",
91 "delay:",
92 "jmp x-- delay [15]",
93 "irq 3 [15]",
94 ".wrap",
95 );
96 let mut cfg = Config::default();
97 cfg.use_program(&pio.load_program(&prg.program), &[]);
98 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
99 sm.set_config(&cfg);
100}
101
102#[embassy_executor::task]
103async fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) {
104 sm.set_enable(true);
105 loop {
106 irq.wait().await;
107 info!("IRQ trigged");
108 }
109}
110
111#[embassy_executor::main]
112async fn main(spawner: Spawner) {
113 let p = embassy_rp::init(Default::default());
114 let pio = p.PIO0;
115
116 let Pio {
117 mut common,
118 irq3,
119 mut sm0,
120 mut sm1,
121 mut sm2,
122 ..
123 } = Pio::new(pio, Irqs);
124
125 setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0);
126 setup_pio_task_sm1(&mut common, &mut sm1);
127 setup_pio_task_sm2(&mut common, &mut sm2);
128 spawner.spawn(pio_task_sm0(sm0)).unwrap();
129 spawner.spawn(pio_task_sm1(sm1)).unwrap();
130 spawner.spawn(pio_task_sm2(irq3, sm2)).unwrap();
131}
diff --git a/examples/rp235x/src/bin/pio_dma.rs b/examples/rp235x/src/bin/pio_dma.rs
new file mode 100644
index 000000000..d00ed2142
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_dma.rs
@@ -0,0 +1,84 @@
1//! This example shows powerful PIO module in the RP2040 chip.
2
3#![no_std]
4#![no_main]
5use defmt::info;
6use embassy_executor::Spawner;
7use embassy_futures::join::join;
8use embassy_rp::peripherals::PIO0;
9use embassy_rp::pio::program::pio_asm;
10use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
11use embassy_rp::{bind_interrupts, Peripheral};
12use fixed::traits::ToFixed;
13use fixed_macro::types::U56F8;
14use {defmt_rtt as _, panic_probe as _};
15
16bind_interrupts!(struct Irqs {
17 PIO0_IRQ_0 => InterruptHandler<PIO0>;
18});
19
20fn swap_nibbles(v: u32) -> u32 {
21 let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4;
22 let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8;
23 (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16
24}
25
26#[embassy_executor::main]
27async fn main(_spawner: Spawner) {
28 let p = embassy_rp::init(Default::default());
29 let pio = p.PIO0;
30 let Pio {
31 mut common,
32 sm0: mut sm,
33 ..
34 } = Pio::new(pio, Irqs);
35
36 let prg = pio_asm!(
37 ".origin 0",
38 "set pindirs,1",
39 ".wrap_target",
40 "set y,7",
41 "loop:",
42 "out x,4",
43 "in x,4",
44 "jmp y--, loop",
45 ".wrap",
46 );
47
48 let mut cfg = Config::default();
49 cfg.use_program(&common.load_program(&prg.program), &[]);
50 cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();
51 cfg.shift_in = ShiftConfig {
52 auto_fill: true,
53 threshold: 32,
54 direction: ShiftDirection::Left,
55 };
56 cfg.shift_out = ShiftConfig {
57 auto_fill: true,
58 threshold: 32,
59 direction: ShiftDirection::Right,
60 };
61
62 sm.set_config(&cfg);
63 sm.set_enable(true);
64
65 let mut dma_out_ref = p.DMA_CH0.into_ref();
66 let mut dma_in_ref = p.DMA_CH1.into_ref();
67 let mut dout = [0x12345678u32; 29];
68 for i in 1..dout.len() {
69 dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7;
70 }
71 let mut din = [0u32; 29];
72 loop {
73 let (rx, tx) = sm.rx_tx();
74 join(
75 tx.dma_push(dma_out_ref.reborrow(), &dout),
76 rx.dma_pull(dma_in_ref.reborrow(), &mut din),
77 )
78 .await;
79 for i in 0..din.len() {
80 assert_eq!(din[i], swap_nibbles(dout[i]));
81 }
82 info!("Swapped {} words", dout.len());
83 }
84}
diff --git a/examples/rp235x/src/bin/pio_hd44780.rs b/examples/rp235x/src/bin/pio_hd44780.rs
new file mode 100644
index 000000000..164e6f8d3
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_hd44780.rs
@@ -0,0 +1,87 @@
1//! This example shows powerful PIO module in the RP2040 chip to communicate with a HD44780 display.
2//! See (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf)
3
4#![no_std]
5#![no_main]
6
7use core::fmt::Write;
8
9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{InterruptHandler, Pio};
13use embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram};
14use embassy_rp::pwm::{self, Pwm};
15use embassy_time::{Instant, Timer};
16use {defmt_rtt as _, panic_probe as _};
17
18bind_interrupts!(pub struct Irqs {
19 PIO0_IRQ_0 => InterruptHandler<PIO0>;
20});
21
22#[embassy_executor::main]
23async fn main(_spawner: Spawner) {
24 // this test assumes a 2x16 HD44780 display attached as follow:
25 // rs = PIN0
26 // rw = PIN1
27 // e = PIN2
28 // db4 = PIN3
29 // db5 = PIN4
30 // db6 = PIN5
31 // db7 = PIN6
32 // additionally a pwm signal for a bias voltage charge pump is provided on pin 15,
33 // allowing direct connection of the display to the RP2040 without level shifters.
34 let p = embassy_rp::init(Default::default());
35
36 let _pwm = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, {
37 let mut c = pwm::Config::default();
38 c.divider = 125.into();
39 c.top = 100;
40 c.compare_b = 50;
41 c
42 });
43
44 let Pio {
45 mut common, sm0, irq0, ..
46 } = Pio::new(p.PIO0, Irqs);
47
48 let word_prg = PioHD44780CommandWordProgram::new(&mut common);
49 let seq_prg = PioHD44780CommandSequenceProgram::new(&mut common);
50
51 let mut hd = PioHD44780::new(
52 &mut common,
53 sm0,
54 irq0,
55 p.DMA_CH3,
56 p.PIN_0,
57 p.PIN_1,
58 p.PIN_2,
59 p.PIN_3,
60 p.PIN_4,
61 p.PIN_5,
62 p.PIN_6,
63 &word_prg,
64 &seq_prg,
65 )
66 .await;
67
68 loop {
69 struct Buf<const N: usize>([u8; N], usize);
70 impl<const N: usize> Write for Buf<N> {
71 fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
72 for b in s.as_bytes() {
73 if self.1 >= N {
74 return Err(core::fmt::Error);
75 }
76 self.0[self.1] = *b;
77 self.1 += 1;
78 }
79 Ok(())
80 }
81 }
82 let mut buf = Buf([0; 16], 0);
83 write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap();
84 hd.add_line(&buf.0[0..buf.1]).await;
85 Timer::after_secs(1).await;
86 }
87}
diff --git a/examples/rp235x/src/bin/pio_i2s.rs b/examples/rp235x/src/bin/pio_i2s.rs
new file mode 100644
index 000000000..ae937a4ed
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_i2s.rs
@@ -0,0 +1,95 @@
1//! This example shows generating audio and sending it to a connected i2s DAC using the PIO
2//! module of the RP2040.
3//!
4//! Connect the i2s DAC as follows:
5//! bclk : GPIO 18
6//! lrc : GPIO 19
7//! din : GPIO 20
8//! Then hold down the boot select button to trigger a rising triangle waveform.
9
10#![no_std]
11#![no_main]
12
13use core::mem;
14
15use embassy_executor::Spawner;
16use embassy_rp::bind_interrupts;
17use embassy_rp::gpio::{Input, Pull};
18use embassy_rp::peripherals::PIO0;
19use embassy_rp::pio::{InterruptHandler, Pio};
20use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram};
21use static_cell::StaticCell;
22use {defmt_rtt as _, panic_probe as _};
23
24bind_interrupts!(struct Irqs {
25 PIO0_IRQ_0 => InterruptHandler<PIO0>;
26});
27
28const SAMPLE_RATE: u32 = 48_000;
29const BIT_DEPTH: u32 = 16;
30const CHANNELS: u32 = 2;
31
32#[embassy_executor::main]
33async fn main(_spawner: Spawner) {
34 let p = embassy_rp::init(Default::default());
35
36 // Setup pio state machine for i2s output
37 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
38
39 let bit_clock_pin = p.PIN_18;
40 let left_right_clock_pin = p.PIN_19;
41 let data_pin = p.PIN_20;
42
43 let program = PioI2sOutProgram::new(&mut common);
44 let mut i2s = PioI2sOut::new(
45 &mut common,
46 sm0,
47 p.DMA_CH0,
48 data_pin,
49 bit_clock_pin,
50 left_right_clock_pin,
51 SAMPLE_RATE,
52 BIT_DEPTH,
53 CHANNELS,
54 &program,
55 );
56
57 let fade_input = Input::new(p.PIN_0, Pull::Up);
58
59 // create two audio buffers (back and front) which will take turns being
60 // filled with new audio data and being sent to the pio fifo using dma
61 const BUFFER_SIZE: usize = 960;
62 static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new();
63 let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]);
64 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);
65
66 // start pio state machine
67 let mut fade_value: i32 = 0;
68 let mut phase: i32 = 0;
69
70 loop {
71 // trigger transfer of front buffer data to the pio fifo
72 // but don't await the returned future, yet
73 let dma_future = i2s.write(front_buffer);
74
75 // fade in audio when bootsel is pressed
76 let fade_target = if fade_input.is_low() { i32::MAX } else { 0 };
77
78 // fill back buffer with fresh audio samples before awaiting the dma future
79 for s in back_buffer.iter_mut() {
80 // exponential approach of fade_value => fade_target
81 fade_value += (fade_target - fade_value) >> 14;
82 // generate triangle wave with amplitude and frequency based on fade value
83 phase = (phase + (fade_value >> 22)) & 0xffff;
84 let triangle_sample = (phase as i16 as i32).abs() - 16384;
85 let sample = (triangle_sample * (fade_value >> 15)) >> 16;
86 // duplicate mono sample into lower and upper half of dma word
87 *s = (sample as u16 as u32) * 0x10001;
88 }
89
90 // now await the dma future. once the dma finishes, the next buffer needs to be queued
91 // within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us
92 dma_future.await;
93 mem::swap(&mut back_buffer, &mut front_buffer);
94 }
95}
diff --git a/examples/rp235x/src/bin/pio_onewire.rs b/examples/rp235x/src/bin/pio_onewire.rs
new file mode 100644
index 000000000..991510851
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_onewire.rs
@@ -0,0 +1,83 @@
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, InterruptHandler, Pio};
10use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram};
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13
14bind_interrupts!(struct Irqs {
15 PIO0_IRQ_0 => InterruptHandler<PIO0>;
16});
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 let p = embassy_rp::init(Default::default());
21 let mut pio = Pio::new(p.PIO0, Irqs);
22
23 let prg = PioOneWireProgram::new(&mut pio.common);
24 let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);
25
26 let mut sensor = Ds18b20::new(onewire);
27
28 loop {
29 sensor.start().await; // Start a new measurement
30 Timer::after_secs(1).await; // Allow 1s for the measurement to finish
31 match sensor.temperature().await {
32 Ok(temp) => info!("temp = {:?} deg C", temp),
33 _ => error!("sensor error"),
34 }
35 Timer::after_secs(1).await;
36 }
37}
38
39/// DS18B20 temperature sensor driver
40pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> {
41 wire: PioOneWire<'d, PIO, SM>,
42}
43
44impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> {
45 pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self {
46 Self { wire }
47 }
48
49 /// Calculate CRC8 of the data
50 fn crc8(data: &[u8]) -> u8 {
51 let mut temp;
52 let mut data_byte;
53 let mut crc = 0;
54 for b in data {
55 data_byte = *b;
56 for _ in 0..8 {
57 temp = (crc ^ data_byte) & 0x01;
58 crc >>= 1;
59 if temp != 0 {
60 crc ^= 0x8C;
61 }
62 data_byte >>= 1;
63 }
64 }
65 crc
66 }
67
68 /// Start a new measurement. Allow at least 1000ms before getting `temperature`.
69 pub async fn start(&mut self) {
70 self.wire.write_bytes(&[0xCC, 0x44]).await;
71 }
72
73 /// Read the temperature. Ensure >1000ms has passed since `start` before calling this.
74 pub async fn temperature(&mut self) -> Result<f32, ()> {
75 self.wire.write_bytes(&[0xCC, 0xBE]).await;
76 let mut data = [0; 9];
77 self.wire.read_bytes(&mut data).await;
78 match Self::crc8(&data) == 0 {
79 true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.),
80 false => Err(()),
81 }
82 }
83}
diff --git a/examples/rp235x/src/bin/pio_pwm.rs b/examples/rp235x/src/bin/pio_pwm.rs
new file mode 100644
index 000000000..7eabb2289
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_pwm.rs
@@ -0,0 +1,38 @@
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::bind_interrupts;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{InterruptHandler, Pio};
11use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};
12use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _};
14
15const REFRESH_INTERVAL: u64 = 20000;
16
17bind_interrupts!(struct Irqs {
18 PIO0_IRQ_0 => InterruptHandler<PIO0>;
19});
20
21#[embassy_executor::main]
22async fn main(_spawner: Spawner) {
23 let p = embassy_rp::init(Default::default());
24 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
25
26 // Note that PIN_25 is the led pin on the Pico
27 let prg = PioPwmProgram::new(&mut common);
28 let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg);
29 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));
30 pwm_pio.start();
31
32 let mut duration = 0;
33 loop {
34 duration = (duration + 1) % 1000;
35 pwm_pio.write(Duration::from_micros(duration));
36 Timer::after_millis(1).await;
37 }
38}
diff --git a/examples/rp235x/src/bin/pio_rotary_encoder.rs b/examples/rp235x/src/bin/pio_rotary_encoder.rs
new file mode 100644
index 000000000..2750f61ae
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_rotary_encoder.rs
@@ -0,0 +1,55 @@
1//! This example shows how to use the PIO module in the RP2040 to read a quadrature rotary encoder.
2
3#![no_std]
4#![no_main]
5
6use defmt::info;
7use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{InterruptHandler, Pio};
11use embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram};
12use {defmt_rtt as _, panic_probe as _};
13
14bind_interrupts!(struct Irqs {
15 PIO0_IRQ_0 => InterruptHandler<PIO0>;
16});
17
18#[embassy_executor::task]
19async fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) {
20 let mut count = 0;
21 loop {
22 info!("Count: {}", count);
23 count += match encoder.read().await {
24 Direction::Clockwise => 1,
25 Direction::CounterClockwise => -1,
26 };
27 }
28}
29
30#[embassy_executor::task]
31async fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) {
32 let mut count = 0;
33 loop {
34 info!("Count: {}", count);
35 count += match encoder.read().await {
36 Direction::Clockwise => 1,
37 Direction::CounterClockwise => -1,
38 };
39 }
40}
41
42#[embassy_executor::main]
43async fn main(spawner: Spawner) {
44 let p = embassy_rp::init(Default::default());
45 let Pio {
46 mut common, sm0, sm1, ..
47 } = Pio::new(p.PIO0, Irqs);
48
49 let prg = PioEncoderProgram::new(&mut common);
50 let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg);
51 let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg);
52
53 spawner.must_spawn(encoder_0(encoder0));
54 spawner.must_spawn(encoder_1(encoder1));
55}
diff --git a/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs b/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs
new file mode 100644
index 000000000..0216c131b
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs
@@ -0,0 +1,112 @@
1//! This example shows how to use the PIO module in the RP235x to read a quadrature rotary encoder.
2//! It differs from the other example in that it uses the RX FIFO as a status register
3
4#![no_std]
5#![no_main]
6
7use defmt::info;
8use embassy_executor::Spawner;
9use embassy_rp::gpio::Pull;
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::program::pio_asm;
12use embassy_rp::{bind_interrupts, pio};
13use embassy_time::Timer;
14use fixed::traits::ToFixed;
15use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};
16use {defmt_rtt as _, panic_probe as _};
17
18// Program metadata for `picotool info`
19#[link_section = ".bi_entries"]
20#[used]
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info::rp_program_name!(c"example_pio_rotary_encoder_rxf"),
23 embassy_rp::binary_info::rp_cargo_version!(),
24 embassy_rp::binary_info::rp_program_description!(c"Rotary encoder (RXF)"),
25 embassy_rp::binary_info::rp_program_build_attribute!(),
26];
27
28bind_interrupts!(struct Irqs {
29 PIO0_IRQ_0 => InterruptHandler<PIO0>;
30});
31
32pub struct PioEncoder<'d, T: Instance, const SM: usize> {
33 sm: StateMachine<'d, T, SM>,
34}
35
36impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {
37 pub fn new(
38 pio: &mut Common<'d, T>,
39 mut sm: StateMachine<'d, T, SM>,
40 pin_a: impl PioPin,
41 pin_b: impl PioPin,
42 ) -> Self {
43 let mut pin_a = pio.make_pio_pin(pin_a);
44 let mut pin_b = pio.make_pio_pin(pin_b);
45 pin_a.set_pull(Pull::Up);
46 pin_b.set_pull(Pull::Up);
47
48 sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
49
50 let prg = pio_asm!(
51 "start:"
52 // encoder count is stored in X
53 "mov isr, x"
54 // and then moved to the RX FIFO register
55 "mov rxfifo[0], isr"
56
57 // wait for encoder transition
58 "wait 1 pin 1"
59 "wait 0 pin 1"
60
61 "set y, 0"
62 "mov y, pins[1]"
63
64 // update X depending on pin 1
65 "jmp !y decr"
66
67 // this is just a clever way of doing x++
68 "mov x, ~x"
69 "jmp x--, incr"
70 "incr:"
71 "mov x, ~x"
72 "jmp start"
73
74 // and this is x--
75 "decr:"
76 "jmp x--, start"
77 );
78
79 let mut cfg = Config::default();
80 cfg.set_in_pins(&[&pin_a, &pin_b]);
81 cfg.fifo_join = FifoJoin::RxAsStatus;
82 cfg.shift_in.direction = ShiftDirection::Left;
83 cfg.clock_divider = 10_000.to_fixed();
84 cfg.use_program(&pio.load_program(&prg.program), &[]);
85 sm.set_config(&cfg);
86
87 sm.set_enable(true);
88 Self { sm }
89 }
90
91 pub async fn read(&mut self) -> u32 {
92 self.sm.get_rxf_entry(0)
93 }
94}
95
96pub enum Direction {
97 Clockwise,
98 CounterClockwise,
99}
100
101#[embassy_executor::main]
102async fn main(_spawner: Spawner) {
103 let p = embassy_rp::init(Default::default());
104 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
105
106 let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);
107
108 loop {
109 info!("Count: {}", encoder.read().await);
110 Timer::after_millis(1000).await;
111 }
112}
diff --git a/examples/rp235x/src/bin/pio_servo.rs b/examples/rp235x/src/bin/pio_servo.rs
new file mode 100644
index 000000000..c52ee7492
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_servo.rs
@@ -0,0 +1,128 @@
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::bind_interrupts;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Instance, InterruptHandler, Pio};
11use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};
12use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _};
14
15const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo
16const DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo
17const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical
18const REFRESH_INTERVAL: u64 = 20000; // The period of each cycle
19
20bind_interrupts!(struct Irqs {
21 PIO0_IRQ_0 => InterruptHandler<PIO0>;
22});
23
24pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
25 pwm: PioPwm<'d, T, SM>,
26 period: Duration,
27 min_pulse_width: Duration,
28 max_pulse_width: Duration,
29 max_degree_rotation: u64,
30}
31
32impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
33 pub fn new(pwm: PioPwm<'d, T, SM>) -> Self {
34 Self {
35 pwm,
36 period: Duration::from_micros(REFRESH_INTERVAL),
37 min_pulse_width: Duration::from_micros(DEFAULT_MIN_PULSE_WIDTH),
38 max_pulse_width: Duration::from_micros(DEFAULT_MAX_PULSE_WIDTH),
39 max_degree_rotation: DEFAULT_MAX_DEGREE_ROTATION,
40 }
41 }
42
43 pub fn set_period(mut self, duration: Duration) -> Self {
44 self.period = duration;
45 self
46 }
47
48 pub fn set_min_pulse_width(mut self, duration: Duration) -> Self {
49 self.min_pulse_width = duration;
50 self
51 }
52
53 pub fn set_max_pulse_width(mut self, duration: Duration) -> Self {
54 self.max_pulse_width = duration;
55 self
56 }
57
58 pub fn set_max_degree_rotation(mut self, degree: u64) -> Self {
59 self.max_degree_rotation = degree;
60 self
61 }
62
63 pub fn build(mut self) -> Servo<'d, T, SM> {
64 self.pwm.set_period(self.period);
65 Servo {
66 pwm: self.pwm,
67 min_pulse_width: self.min_pulse_width,
68 max_pulse_width: self.max_pulse_width,
69 max_degree_rotation: self.max_degree_rotation,
70 }
71 }
72}
73
74pub struct Servo<'d, T: Instance, const SM: usize> {
75 pwm: PioPwm<'d, T, SM>,
76 min_pulse_width: Duration,
77 max_pulse_width: Duration,
78 max_degree_rotation: u64,
79}
80
81impl<'d, T: Instance, const SM: usize> Servo<'d, T, SM> {
82 pub fn start(&mut self) {
83 self.pwm.start();
84 }
85
86 pub fn stop(&mut self) {
87 self.pwm.stop();
88 }
89
90 pub fn write_time(&mut self, duration: Duration) {
91 self.pwm.write(duration);
92 }
93
94 pub fn rotate(&mut self, degree: u64) {
95 let degree_per_nano_second = (self.max_pulse_width.as_nanos() as u64 - self.min_pulse_width.as_nanos() as u64)
96 / self.max_degree_rotation;
97 let mut duration =
98 Duration::from_nanos(degree * degree_per_nano_second + self.min_pulse_width.as_nanos() as u64);
99 if self.max_pulse_width < duration {
100 duration = self.max_pulse_width;
101 }
102
103 self.pwm.write(duration);
104 }
105}
106
107#[embassy_executor::main]
108async fn main(_spawner: Spawner) {
109 let p = embassy_rp::init(Default::default());
110 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
111
112 let prg = PioPwmProgram::new(&mut common);
113 let pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_1, &prg);
114 let mut servo = ServoBuilder::new(pwm_pio)
115 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo
116 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.
117 .set_max_pulse_width(Duration::from_micros(2600)) // Along with this value.
118 .build();
119
120 servo.start();
121
122 let mut degree = 0;
123 loop {
124 degree = (degree + 1) % 120;
125 servo.rotate(degree);
126 Timer::after_millis(50).await;
127 }
128}
diff --git a/examples/rp235x/src/bin/pio_stepper.rs b/examples/rp235x/src/bin/pio_stepper.rs
new file mode 100644
index 000000000..3862c248b
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_stepper.rs
@@ -0,0 +1,49 @@
1//! This example shows how to use the PIO module in the RP2040 to implement a stepper motor driver
2//! for a 5-wire stepper such as the 28BYJ-48. You can halt an ongoing rotation by dropping the future.
3
4#![no_std]
5#![no_main]
6
7use defmt::info;
8use embassy_executor::Spawner;
9use embassy_rp::bind_interrupts;
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{InterruptHandler, Pio};
12use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram};
13use embassy_time::{with_timeout, Duration, Timer};
14use {defmt_rtt as _, panic_probe as _};
15
16bind_interrupts!(struct Irqs {
17 PIO0_IRQ_0 => InterruptHandler<PIO0>;
18});
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) {
22 let p = embassy_rp::init(Default::default());
23 let Pio {
24 mut common, irq0, sm0, ..
25 } = Pio::new(p.PIO0, Irqs);
26
27 let prg = PioStepperProgram::new(&mut common);
28 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7, &prg);
29 stepper.set_frequency(120);
30 loop {
31 info!("CW full steps");
32 stepper.step(1000).await;
33
34 info!("CCW full steps, drop after 1 sec");
35 if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX))
36 .await
37 .is_err()
38 {
39 info!("Time's up!");
40 Timer::after(Duration::from_secs(1)).await;
41 }
42
43 info!("CW half steps");
44 stepper.step_half(1000).await;
45
46 info!("CCW half steps");
47 stepper.step_half(-1000).await;
48 }
49}
diff --git a/examples/rp235x/src/bin/pio_uart.rs b/examples/rp235x/src/bin/pio_uart.rs
new file mode 100644
index 000000000..9712984f9
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_uart.rs
@@ -0,0 +1,190 @@
1//! This example shows how to use the PIO module in the RP2040 chip to implement a duplex UART.
2//! The PIO module is a very powerful peripheral that can be used to implement many different
3//! protocols. It is a very flexible state machine that can be programmed to do almost anything.
4//!
5//! This example opens up a USB device that implements a CDC ACM serial port. It then uses the
6//! PIO module to implement a UART that is connected to the USB serial port. This allows you to
7//! communicate with a device connected to the RP2040 over USB serial.
8
9#![no_std]
10#![no_main]
11#![allow(async_fn_in_trait)]
12
13use defmt::{info, panic, trace};
14use embassy_executor::Spawner;
15use embassy_futures::join::{join, join3};
16use embassy_rp::peripherals::{PIO0, USB};
17use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram};
18use embassy_rp::usb::{Driver, Instance, InterruptHandler};
19use embassy_rp::{bind_interrupts, pio};
20use embassy_sync::blocking_mutex::raw::NoopRawMutex;
21use embassy_sync::pipe::Pipe;
22use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
23use embassy_usb::driver::EndpointError;
24use embassy_usb::{Builder, Config};
25use embedded_io_async::{Read, Write};
26use {defmt_rtt as _, panic_probe as _};
27
28bind_interrupts!(struct Irqs {
29 USBCTRL_IRQ => InterruptHandler<USB>;
30 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
31});
32
33#[embassy_executor::main]
34async fn main(_spawner: Spawner) {
35 info!("Hello there!");
36
37 let p = embassy_rp::init(Default::default());
38
39 // Create the driver, from the HAL.
40 let driver = Driver::new(p.USB, Irqs);
41
42 // Create embassy-usb Config
43 let mut config = Config::new(0xc0de, 0xcafe);
44 config.manufacturer = Some("Embassy");
45 config.product = Some("PIO UART example");
46 config.serial_number = Some("12345678");
47 config.max_power = 100;
48 config.max_packet_size_0 = 64;
49
50 // Create embassy-usb DeviceBuilder using the driver and config.
51 // It needs some buffers for building the descriptors.
52 let mut config_descriptor = [0; 256];
53 let mut bos_descriptor = [0; 256];
54 let mut control_buf = [0; 64];
55
56 let mut state = State::new();
57
58 let mut builder = Builder::new(
59 driver,
60 config,
61 &mut config_descriptor,
62 &mut bos_descriptor,
63 &mut [], // no msos descriptors
64 &mut control_buf,
65 );
66
67 // Create classes on the builder.
68 let class = CdcAcmClass::new(&mut builder, &mut state, 64);
69
70 // Build the builder.
71 let mut usb = builder.build();
72
73 // Run the USB device.
74 let usb_fut = usb.run();
75
76 // PIO UART setup
77 let pio::Pio {
78 mut common, sm0, sm1, ..
79 } = pio::Pio::new(p.PIO0, Irqs);
80
81 let tx_program = PioUartTxProgram::new(&mut common);
82 let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program);
83
84 let rx_program = PioUartRxProgram::new(&mut common);
85 let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program);
86
87 // Pipe setup
88 let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();
89 let (mut usb_pipe_reader, mut usb_pipe_writer) = usb_pipe.split();
90
91 let mut uart_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();
92 let (mut uart_pipe_reader, mut uart_pipe_writer) = uart_pipe.split();
93
94 let (mut usb_tx, mut usb_rx) = class.split();
95
96 // Read + write from USB
97 let usb_future = async {
98 loop {
99 info!("Wait for USB connection");
100 usb_rx.wait_connection().await;
101 info!("Connected");
102 let _ = join(
103 usb_read(&mut usb_rx, &mut uart_pipe_writer),
104 usb_write(&mut usb_tx, &mut usb_pipe_reader),
105 )
106 .await;
107 info!("Disconnected");
108 }
109 };
110
111 // Read + write from UART
112 let uart_future = join(
113 uart_read(&mut uart_rx, &mut usb_pipe_writer),
114 uart_write(&mut uart_tx, &mut uart_pipe_reader),
115 );
116
117 // Run everything concurrently.
118 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
119 join3(usb_fut, usb_future, uart_future).await;
120}
121
122struct Disconnected {}
123
124impl From<EndpointError> for Disconnected {
125 fn from(val: EndpointError) -> Self {
126 match val {
127 EndpointError::BufferOverflow => panic!("Buffer overflow"),
128 EndpointError::Disabled => Disconnected {},
129 }
130 }
131}
132
133/// Read from the USB and write it to the UART TX pipe
134async fn usb_read<'d, T: Instance + 'd>(
135 usb_rx: &mut Receiver<'d, Driver<'d, T>>,
136 uart_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,
137) -> Result<(), Disconnected> {
138 let mut buf = [0; 64];
139 loop {
140 let n = usb_rx.read_packet(&mut buf).await?;
141 let data = &buf[..n];
142 trace!("USB IN: {:x}", data);
143 (*uart_pipe_writer).write(data).await;
144 }
145}
146
147/// Read from the USB TX pipe and write it to the USB
148async fn usb_write<'d, T: Instance + 'd>(
149 usb_tx: &mut Sender<'d, Driver<'d, T>>,
150 usb_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,
151) -> Result<(), Disconnected> {
152 let mut buf = [0; 64];
153 loop {
154 let n = (*usb_pipe_reader).read(&mut buf).await;
155 let data = &buf[..n];
156 trace!("USB OUT: {:x}", data);
157 usb_tx.write_packet(&data).await?;
158 }
159}
160
161/// Read from the UART and write it to the USB TX pipe
162async fn uart_read<PIO: pio::Instance, const SM: usize>(
163 uart_rx: &mut PioUartRx<'_, PIO, SM>,
164 usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,
165) -> ! {
166 let mut buf = [0; 64];
167 loop {
168 let n = uart_rx.read(&mut buf).await.expect("UART read error");
169 if n == 0 {
170 continue;
171 }
172 let data = &buf[..n];
173 trace!("UART IN: {:x}", buf);
174 (*usb_pipe_writer).write(data).await;
175 }
176}
177
178/// Read from the UART TX pipe and write it to the UART
179async fn uart_write<PIO: pio::Instance, const SM: usize>(
180 uart_tx: &mut PioUartTx<'_, PIO, SM>,
181 uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,
182) -> ! {
183 let mut buf = [0; 64];
184 loop {
185 let n = (*uart_pipe_reader).read(&mut buf).await;
186 let data = &buf[..n];
187 trace!("UART OUT: {:x}", data);
188 let _ = uart_tx.write(&data).await;
189 }
190}
diff --git a/examples/rp235x/src/bin/pio_ws2812.rs b/examples/rp235x/src/bin/pio_ws2812.rs
new file mode 100644
index 000000000..d1fcfc471
--- /dev/null
+++ b/examples/rp235x/src/bin/pio_ws2812.rs
@@ -0,0 +1,68 @@
1//! This example shows powerful PIO module in the RP2040 chip to communicate with WS2812 LED modules.
2//! See (https://www.sparkfun.com/categories/tags/ws2812)
3
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::bind_interrupts;
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{InterruptHandler, Pio};
12use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program};
13use embassy_time::{Duration, Ticker};
14use smart_leds::RGB8;
15use {defmt_rtt as _, panic_probe as _};
16
17bind_interrupts!(struct Irqs {
18 PIO0_IRQ_0 => InterruptHandler<PIO0>;
19});
20
21/// Input a value 0 to 255 to get a color value
22/// The colours are a transition r - g - b - back to r.
23fn wheel(mut wheel_pos: u8) -> RGB8 {
24 wheel_pos = 255 - wheel_pos;
25 if wheel_pos < 85 {
26 return (255 - wheel_pos * 3, 0, wheel_pos * 3).into();
27 }
28 if wheel_pos < 170 {
29 wheel_pos -= 85;
30 return (0, wheel_pos * 3, 255 - wheel_pos * 3).into();
31 }
32 wheel_pos -= 170;
33 (wheel_pos * 3, 255 - wheel_pos * 3, 0).into()
34}
35
36#[embassy_executor::main]
37async fn main(_spawner: Spawner) {
38 info!("Start");
39 let p = embassy_rp::init(Default::default());
40
41 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
42
43 // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit
44 // feather boards for the 2040 both have one built in.
45 const NUM_LEDS: usize = 1;
46 let mut data = [RGB8::default(); NUM_LEDS];
47
48 // Common neopixel pins:
49 // Thing plus: 8
50 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4
51 let program = PioWs2812Program::new(&mut common);
52 let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16, &program);
53
54 // Loop forever making RGB values and pushing them out to the WS2812.
55 let mut ticker = Ticker::every(Duration::from_millis(10));
56 loop {
57 for j in 0..(256 * 5) {
58 debug!("New Colors:");
59 for i in 0..NUM_LEDS {
60 data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8);
61 debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b);
62 }
63 ws2812.write(&data).await;
64
65 ticker.next().await;
66 }
67 }
68}
diff --git a/examples/rp235x/src/bin/pwm.rs b/examples/rp235x/src/bin/pwm.rs
new file mode 100644
index 000000000..a3c0f7e49
--- /dev/null
+++ b/examples/rp235x/src/bin/pwm.rs
@@ -0,0 +1,79 @@
1//! This example shows how to use PWM (Pulse Width Modulation) in the RP235x chip.
2//!
3//! We demonstrate two ways of using PWM:
4//! 1. Via config
5//! 2. Via setting a duty cycle
6
7#![no_std]
8#![no_main]
9
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4};
13use embassy_rp::pwm::{Config, Pwm, SetDutyCycle};
14use embassy_time::Timer;
15use {defmt_rtt as _, panic_probe as _};
16
17#[embassy_executor::main]
18async fn main(spawner: Spawner) {
19 let p = embassy_rp::init(Default::default());
20 spawner.spawn(pwm_set_config(p.PWM_SLICE4, p.PIN_25)).unwrap();
21 spawner.spawn(pwm_set_dutycycle(p.PWM_SLICE2, p.PIN_4)).unwrap();
22}
23
24/// Demonstrate PWM by modifying & applying the config
25///
26/// Using the onboard led, if You are using a different Board than plain Pico2 (i.e. W variant)
27/// you must use another slice & pin and an appropriate resistor.
28#[embassy_executor::task]
29async fn pwm_set_config(slice4: PWM_SLICE4, pin25: PIN_25) {
30 let mut c = Config::default();
31 c.top = 32_768;
32 c.compare_b = 8;
33 let mut pwm = Pwm::new_output_b(slice4, pin25, c.clone());
34
35 loop {
36 info!("current LED duty cycle: {}/32768", c.compare_b);
37 Timer::after_secs(1).await;
38 c.compare_b = c.compare_b.rotate_left(4);
39 pwm.set_config(&c);
40 }
41}
42
43/// Demonstrate PWM by setting duty cycle
44///
45/// Using GP4 in Slice2, make sure to use an appropriate resistor.
46#[embassy_executor::task]
47async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) {
48 // If we aim for a specific frequency, here is how we can calculate the top value.
49 // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0.
50 // Every such wraparound is one PWM cycle. So here is how we get 25KHz:
51 let desired_freq_hz = 25_000;
52 let clock_freq_hz = embassy_rp::clocks::clk_sys_freq();
53 let divider = 16u8;
54 let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1;
55
56 let mut c = Config::default();
57 c.top = period;
58 c.divider = divider.into();
59
60 let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone());
61
62 loop {
63 // 100% duty cycle, fully on
64 pwm.set_duty_cycle_fully_on().unwrap();
65 Timer::after_secs(1).await;
66
67 // 66% duty cycle. Expressed as simple percentage.
68 pwm.set_duty_cycle_percent(66).unwrap();
69 Timer::after_secs(1).await;
70
71 // 25% duty cycle. Expressed as 32768/4 = 8192.
72 pwm.set_duty_cycle(c.top / 4).unwrap();
73 Timer::after_secs(1).await;
74
75 // 0% duty cycle, fully off.
76 pwm.set_duty_cycle_fully_off().unwrap();
77 Timer::after_secs(1).await;
78 }
79}
diff --git a/examples/rp235x/src/bin/pwm_input.rs b/examples/rp235x/src/bin/pwm_input.rs
new file mode 100644
index 000000000..bf454a936
--- /dev/null
+++ b/examples/rp235x/src/bin/pwm_input.rs
@@ -0,0 +1,26 @@
1//! This example shows how to use the PWM module to measure the frequency of an input signal.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::gpio::Pull;
9use embassy_rp::pwm::{Config, InputMode, Pwm};
10use embassy_time::{Duration, Ticker};
11use {defmt_rtt as _, panic_probe as _};
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let p = embassy_rp::init(Default::default());
16
17 let cfg: Config = Default::default();
18 let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, Pull::None, InputMode::RisingEdge, cfg);
19
20 let mut ticker = Ticker::every(Duration::from_secs(1));
21 loop {
22 info!("Input frequency: {} Hz", pwm.counter());
23 pwm.set_counter(0);
24 ticker.next().await;
25 }
26}
diff --git a/examples/rp235x/src/bin/pwm_tb6612fng_motor_driver.rs b/examples/rp235x/src/bin/pwm_tb6612fng_motor_driver.rs
new file mode 100644
index 000000000..3b700884c
--- /dev/null
+++ b/examples/rp235x/src/bin/pwm_tb6612fng_motor_driver.rs
@@ -0,0 +1,105 @@
1//! # PWM TB6612FNG motor driver
2//!
3//! This example shows the use of a TB6612FNG motor driver. The driver is built on top of embedded_hal and the example demonstrates how embassy_rp can be used to interact with ist.
4
5#![no_std]
6#![no_main]
7
8use assign_resources::assign_resources;
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::config::Config;
12use embassy_rp::gpio::Output;
13use embassy_rp::{gpio, peripherals, pwm};
14use embassy_time::{Duration, Timer};
15use tb6612fng::{DriveCommand, Motor, Tb6612fng};
16use {defmt_rtt as _, panic_probe as _};
17
18assign_resources! {
19 motor: MotorResources {
20 standby_pin: PIN_22,
21 left_slice: PWM_SLICE6,
22 left_pwm_pin: PIN_28,
23 left_forward_pin: PIN_21,
24 left_backward_pin: PIN_20,
25 right_slice: PWM_SLICE5,
26 right_pwm_pin: PIN_27,
27 right_forward_pin: PIN_19,
28 right_backward_pin: PIN_18,
29 },
30}
31
32#[embassy_executor::main]
33async fn main(_spawner: Spawner) {
34 let p = embassy_rp::init(Config::default());
35 let s = split_resources!(p);
36 let r = s.motor;
37
38 // we want a PWM frequency of 10KHz, especially cheaper motors do not respond well to higher frequencies
39 let desired_freq_hz = 10_000;
40 let clock_freq_hz = embassy_rp::clocks::clk_sys_freq();
41 let divider = 16u8;
42 let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1;
43
44 // we need a standby output and two motors to construct a full TB6612FNG
45
46 // standby pin
47 let stby = Output::new(r.standby_pin, gpio::Level::Low);
48
49 // motor A, here defined to be the left motor
50 let left_fwd = gpio::Output::new(r.left_forward_pin, gpio::Level::Low);
51 let left_bckw = gpio::Output::new(r.left_backward_pin, gpio::Level::Low);
52 let mut left_speed = pwm::Config::default();
53 left_speed.top = period;
54 left_speed.divider = divider.into();
55 let left_pwm = pwm::Pwm::new_output_a(r.left_slice, r.left_pwm_pin, left_speed);
56 let left_motor = Motor::new(left_fwd, left_bckw, left_pwm).unwrap();
57
58 // motor B, here defined to be the right motor
59 let right_fwd = gpio::Output::new(r.right_forward_pin, gpio::Level::Low);
60 let right_bckw = gpio::Output::new(r.right_backward_pin, gpio::Level::Low);
61 let mut right_speed = pwm::Config::default();
62 right_speed.top = period;
63 right_speed.divider = divider.into();
64 let right_pwm = pwm::Pwm::new_output_b(r.right_slice, r.right_pwm_pin, right_speed);
65 let right_motor = Motor::new(right_fwd, right_bckw, right_pwm).unwrap();
66
67 // construct the motor driver
68 let mut control = Tb6612fng::new(left_motor, right_motor, stby).unwrap();
69
70 loop {
71 // wake up the motor driver
72 info!("end standby");
73 control.disable_standby().unwrap();
74 Timer::after(Duration::from_millis(100)).await;
75
76 // drive a straight line forward at 20% speed for 5s
77 info!("drive straight");
78 control.motor_a.drive(DriveCommand::Forward(80)).unwrap();
79 control.motor_b.drive(DriveCommand::Forward(80)).unwrap();
80 Timer::after(Duration::from_secs(5)).await;
81
82 // coast for 2s
83 info!("coast");
84 control.motor_a.drive(DriveCommand::Stop).unwrap();
85 control.motor_b.drive(DriveCommand::Stop).unwrap();
86 Timer::after(Duration::from_secs(2)).await;
87
88 // actively brake
89 info!("brake");
90 control.motor_a.drive(DriveCommand::Brake).unwrap();
91 control.motor_b.drive(DriveCommand::Brake).unwrap();
92 Timer::after(Duration::from_secs(1)).await;
93
94 // slowly turn for 3s
95 info!("turn");
96 control.motor_a.drive(DriveCommand::Backward(50)).unwrap();
97 control.motor_b.drive(DriveCommand::Forward(50)).unwrap();
98 Timer::after(Duration::from_secs(3)).await;
99
100 // and put the driver in standby mode and wait for 5s
101 info!("standby");
102 control.enable_standby().unwrap();
103 Timer::after(Duration::from_secs(5)).await;
104 }
105}
diff --git a/examples/rp235x/src/bin/rosc.rs b/examples/rp235x/src/bin/rosc.rs
new file mode 100644
index 000000000..942b72319
--- /dev/null
+++ b/examples/rp235x/src/bin/rosc.rs
@@ -0,0 +1,31 @@
1//! This example test the RP Pico on board LED.
2//!
3//! It does not work with the RP Pico W board. See wifi_blinky.rs.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::{clocks, gpio};
11use embassy_time::Timer;
12use gpio::{Level, Output};
13use {defmt_rtt as _, panic_probe as _};
14
15#[embassy_executor::main]
16async fn main(_spawner: Spawner) {
17 let mut config = embassy_rp::config::Config::default();
18 config.clocks = clocks::ClockConfig::rosc();
19 let p = embassy_rp::init(config);
20 let mut led = Output::new(p.PIN_25, Level::Low);
21
22 loop {
23 info!("led on!");
24 led.set_high();
25 Timer::after_secs(1).await;
26
27 info!("led off!");
28 led.set_low();
29 Timer::after_secs(1).await;
30 }
31}
diff --git a/examples/rp235x/src/bin/shared_bus.rs b/examples/rp235x/src/bin/shared_bus.rs
new file mode 100644
index 000000000..c6cb5d64c
--- /dev/null
+++ b/examples/rp235x/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/rp235x/src/bin/sharing.rs b/examples/rp235x/src/bin/sharing.rs
new file mode 100644
index 000000000..5416e20ce
--- /dev/null
+++ b/examples/rp235x/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/rp235x/src/bin/spi.rs b/examples/rp235x/src/bin/spi.rs
new file mode 100644
index 000000000..4cc4f5210
--- /dev/null
+++ b/examples/rp235x/src/bin/spi.rs
@@ -0,0 +1,46 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//!
3//! Example for resistive touch sensor in Waveshare Pico-ResTouch
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::spi::Spi;
11use embassy_rp::{gpio, spi};
12use gpio::{Level, Output};
13use {defmt_rtt as _, panic_probe as _};
14
15#[embassy_executor::main]
16async fn main(_spawner: Spawner) {
17 let p = embassy_rp::init(Default::default());
18 info!("Hello World!");
19
20 // Example for resistive touch sensor in Waveshare Pico-ResTouch
21
22 let miso = p.PIN_12;
23 let mosi = p.PIN_11;
24 let clk = p.PIN_10;
25 let touch_cs = p.PIN_16;
26
27 // create SPI
28 let mut config = spi::Config::default();
29 config.frequency = 2_000_000;
30 let mut spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, config);
31
32 // Configure CS
33 let mut cs = Output::new(touch_cs, Level::Low);
34
35 loop {
36 cs.set_low();
37 let mut buf = [0x90, 0x00, 0x00, 0xd0, 0x00, 0x00];
38 spi.blocking_transfer_in_place(&mut buf).unwrap();
39 cs.set_high();
40
41 let x = (buf[1] as u32) << 5 | (buf[2] as u32) >> 3;
42 let y = (buf[4] as u32) << 5 | (buf[5] as u32) >> 3;
43
44 info!("touch: {=u32} {=u32}", x, y);
45 }
46}
diff --git a/examples/rp235x/src/bin/spi_async.rs b/examples/rp235x/src/bin/spi_async.rs
new file mode 100644
index 000000000..266584efc
--- /dev/null
+++ b/examples/rp235x/src/bin/spi_async.rs
@@ -0,0 +1,31 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
2//! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back.
3
4#![no_std]
5#![no_main]
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_rp::spi::{Config, Spi};
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12
13#[embassy_executor::main]
14async fn main(_spawner: Spawner) {
15 let p = embassy_rp::init(Default::default());
16 info!("Hello World!");
17
18 let miso = p.PIN_12;
19 let mosi = p.PIN_11;
20 let clk = p.PIN_10;
21
22 let mut spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
23
24 loop {
25 let tx_buf = [1_u8, 2, 3, 4, 5, 6];
26 let mut rx_buf = [0_u8; 6];
27 spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
28 info!("{:?}", rx_buf);
29 Timer::after_secs(1).await;
30 }
31}
diff --git a/examples/rp235x/src/bin/spi_display.rs b/examples/rp235x/src/bin/spi_display.rs
new file mode 100644
index 000000000..9c524ab25
--- /dev/null
+++ b/examples/rp235x/src/bin/spi_display.rs
@@ -0,0 +1,177 @@
1//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2350 chip.
2//!
3//! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch
4//! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8)
5
6#![no_std]
7#![no_main]
8
9use core::cell::RefCell;
10
11use defmt::*;
12use display_interface_spi::SPIInterface;
13use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
14use embassy_executor::Spawner;
15use embassy_rp::gpio::{Level, Output};
16use embassy_rp::spi;
17use embassy_rp::spi::{Blocking, Spi};
18use embassy_sync::blocking_mutex::raw::NoopRawMutex;
19use embassy_sync::blocking_mutex::Mutex;
20use embassy_time::Delay;
21use embedded_graphics::image::{Image, ImageRawLE};
22use embedded_graphics::mono_font::ascii::FONT_10X20;
23use embedded_graphics::mono_font::MonoTextStyle;
24use embedded_graphics::pixelcolor::Rgb565;
25use embedded_graphics::prelude::*;
26use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
27use embedded_graphics::text::Text;
28use mipidsi::models::ST7789;
29use mipidsi::options::{Orientation, Rotation};
30use mipidsi::Builder;
31use {defmt_rtt as _, panic_probe as _};
32
33use crate::touch::Touch;
34
35const DISPLAY_FREQ: u32 = 64_000_000;
36const TOUCH_FREQ: u32 = 200_000;
37
38#[embassy_executor::main]
39async fn main(_spawner: Spawner) {
40 let p = embassy_rp::init(Default::default());
41 info!("Hello World!");
42
43 let bl = p.PIN_13;
44 let rst = p.PIN_15;
45 let display_cs = p.PIN_9;
46 let dcx = p.PIN_8;
47 let miso = p.PIN_12;
48 let mosi = p.PIN_11;
49 let clk = p.PIN_10;
50 let touch_cs = p.PIN_16;
51 //let touch_irq = p.PIN_17;
52
53 // create SPI
54 let mut display_config = spi::Config::default();
55 display_config.frequency = DISPLAY_FREQ;
56 display_config.phase = spi::Phase::CaptureOnSecondTransition;
57 display_config.polarity = spi::Polarity::IdleHigh;
58 let mut touch_config = spi::Config::default();
59 touch_config.frequency = TOUCH_FREQ;
60 touch_config.phase = spi::Phase::CaptureOnSecondTransition;
61 touch_config.polarity = spi::Polarity::IdleHigh;
62
63 let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone());
64 let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));
65
66 let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);
67 let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config);
68
69 let mut touch = Touch::new(touch_spi);
70
71 let dcx = Output::new(dcx, Level::Low);
72 let rst = Output::new(rst, Level::Low);
73 // dcx: 0 = command, 1 = data
74
75 // Enable LCD backlight
76 let _bl = Output::new(bl, Level::High);
77
78 // display interface abstraction from SPI and DC
79 let di = SPIInterface::new(display_spi, dcx);
80
81 // Define the display from the display interface and initialize it
82 let mut display = Builder::new(ST7789, di)
83 .display_size(240, 320)
84 .reset_pin(rst)
85 .orientation(Orientation::new().rotate(Rotation::Deg90))
86 .init(&mut Delay)
87 .unwrap();
88 display.clear(Rgb565::BLACK).unwrap();
89
90 let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86);
91 let ferris = Image::new(&raw_image_data, Point::new(34, 68));
92
93 // Display the image
94 ferris.draw(&mut display).unwrap();
95
96 let style = MonoTextStyle::new(&FONT_10X20, Rgb565::GREEN);
97 Text::new(
98 "Hello embedded_graphics \n + embassy + RP2040!",
99 Point::new(20, 200),
100 style,
101 )
102 .draw(&mut display)
103 .unwrap();
104
105 loop {
106 if let Some((x, y)) = touch.read() {
107 let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLUE).build();
108
109 Rectangle::new(Point::new(x - 1, y - 1), Size::new(3, 3))
110 .into_styled(style)
111 .draw(&mut display)
112 .unwrap();
113 }
114 }
115}
116
117/// Driver for the XPT2046 resistive touchscreen sensor
118mod touch {
119 use embedded_hal_1::spi::{Operation, SpiDevice};
120
121 struct Calibration {
122 x1: i32,
123 x2: i32,
124 y1: i32,
125 y2: i32,
126 sx: i32,
127 sy: i32,
128 }
129
130 const CALIBRATION: Calibration = Calibration {
131 x1: 3880,
132 x2: 340,
133 y1: 262,
134 y2: 3850,
135 sx: 320,
136 sy: 240,
137 };
138
139 pub struct Touch<SPI: SpiDevice> {
140 spi: SPI,
141 }
142
143 impl<SPI> Touch<SPI>
144 where
145 SPI: SpiDevice,
146 {
147 pub fn new(spi: SPI) -> Self {
148 Self { spi }
149 }
150
151 pub fn read(&mut self) -> Option<(i32, i32)> {
152 let mut x = [0; 2];
153 let mut y = [0; 2];
154 self.spi
155 .transaction(&mut [
156 Operation::Write(&[0x90]),
157 Operation::Read(&mut x),
158 Operation::Write(&[0xd0]),
159 Operation::Read(&mut y),
160 ])
161 .unwrap();
162
163 let x = (u16::from_be_bytes(x) >> 3) as i32;
164 let y = (u16::from_be_bytes(y) >> 3) as i32;
165
166 let cal = &CALIBRATION;
167
168 let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx);
169 let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy);
170 if x == 0 && y == 0 {
171 None
172 } else {
173 Some((x, y))
174 }
175 }
176 }
177}
diff --git a/examples/rp235x/src/bin/spi_sdmmc.rs b/examples/rp235x/src/bin/spi_sdmmc.rs
new file mode 100644
index 000000000..9808b6a5d
--- /dev/null
+++ b/examples/rp235x/src/bin/spi_sdmmc.rs
@@ -0,0 +1,82 @@
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 let p = embassy_rp::init(Default::default());
37
38 // SPI clock needs to be running at <= 400kHz during initialization
39 let mut config = spi::Config::default();
40 config.frequency = 400_000;
41 let spi = Spi::new_blocking(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, config);
42 // Use a dummy cs pin here, for embedded-hal SpiDevice compatibility reasons
43 let spi_dev = ExclusiveDevice::new_no_delay(spi, DummyCsPin);
44 // Real cs pin
45 let cs = Output::new(p.PIN_16, Level::High);
46
47 let sdcard = SdCard::new(spi_dev, cs, embassy_time::Delay);
48 info!("Card size is {} bytes", sdcard.num_bytes().unwrap());
49
50 // Now that the card is initialized, the SPI clock can go faster
51 let mut config = spi::Config::default();
52 config.frequency = 16_000_000;
53 sdcard.spi(|dev| SetConfig::set_config(dev.bus_mut(), &config)).ok();
54
55 // Now let's look for volumes (also known as partitions) on our block device.
56 // To do this we need a Volume Manager. It will take ownership of the block device.
57 let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, DummyTimesource());
58
59 // Try and access Volume 0 (i.e. the first partition).
60 // The volume object holds information about the filesystem on that volume.
61 let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0)).unwrap();
62 info!("Volume 0: {:?}", defmt::Debug2Format(&volume0));
63
64 // Open the root directory (mutably borrows from the volume).
65 let mut root_dir = volume0.open_root_dir().unwrap();
66
67 // Open a file called "MY_FILE.TXT" in the root directory
68 // This mutably borrows the directory.
69 let mut my_file = root_dir
70 .open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::Mode::ReadOnly)
71 .unwrap();
72
73 // Print the contents of the file
74 while !my_file.is_eof() {
75 let mut buf = [0u8; 32];
76 if let Ok(n) = my_file.read(&mut buf) {
77 info!("{:a}", buf[..n]);
78 }
79 }
80
81 loop {}
82}
diff --git a/examples/rp235x/src/bin/trng.rs b/examples/rp235x/src/bin/trng.rs
new file mode 100644
index 000000000..ad19aef3e
--- /dev/null
+++ b/examples/rp235x/src/bin/trng.rs
@@ -0,0 +1,49 @@
1//! This example shows TRNG usage
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts;
9use embassy_rp::gpio::{Level, Output};
10use embassy_rp::peripherals::TRNG;
11use embassy_rp::trng::Trng;
12use embassy_time::Timer;
13use rand::RngCore;
14use {defmt_rtt as _, panic_probe as _};
15
16bind_interrupts!(struct Irqs {
17 TRNG_IRQ => embassy_rp::trng::InterruptHandler<TRNG>;
18});
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) {
22 let peripherals = embassy_rp::init(Default::default());
23
24 // Initialize the TRNG with default configuration
25 let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default());
26 // A buffer to collect random bytes in.
27 let mut randomness = [0u8; 58];
28
29 let mut led = Output::new(peripherals.PIN_25, Level::Low);
30
31 loop {
32 trng.fill_bytes(&mut randomness).await;
33 info!("Random bytes async {}", &randomness);
34 trng.blocking_fill_bytes(&mut randomness);
35 info!("Random bytes blocking {}", &randomness);
36 let random_u32 = trng.next_u32();
37 let random_u64 = trng.next_u64();
38 info!("Random u32 {} u64 {}", random_u32, random_u64);
39 // Random number of blinks between 0 and 31
40 let blinks = random_u32 % 32;
41 for _ in 0..blinks {
42 led.set_high();
43 Timer::after_millis(20).await;
44 led.set_low();
45 Timer::after_millis(20).await;
46 }
47 Timer::after_millis(1000).await;
48 }
49}
diff --git a/examples/rp235x/src/bin/uart.rs b/examples/rp235x/src/bin/uart.rs
new file mode 100644
index 000000000..a59f537bf
--- /dev/null
+++ b/examples/rp235x/src/bin/uart.rs
@@ -0,0 +1,25 @@
1//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
2//!
3//! No specific hardware is specified in this example. Only output on pin 0 is tested.
4//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
5//! with its UART port.
6
7#![no_std]
8#![no_main]
9
10use embassy_executor::Spawner;
11use embassy_rp::uart;
12use {defmt_rtt as _, panic_probe as _};
13
14#[embassy_executor::main]
15async fn main(_spawner: Spawner) {
16 let p = embassy_rp::init(Default::default());
17 let config = uart::Config::default();
18 let mut uart = uart::Uart::new_blocking(p.UART1, p.PIN_4, p.PIN_5, config);
19 uart.blocking_write("Hello World!\r\n".as_bytes()).unwrap();
20
21 loop {
22 uart.blocking_write("hello there!\r\n".as_bytes()).unwrap();
23 cortex_m::asm::delay(1_000_000);
24 }
25}
diff --git a/examples/rp235x/src/bin/uart_buffered_split.rs b/examples/rp235x/src/bin/uart_buffered_split.rs
new file mode 100644
index 000000000..468d2b61a
--- /dev/null
+++ b/examples/rp235x/src/bin/uart_buffered_split.rs
@@ -0,0 +1,58 @@
1//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
2//!
3//! No specific hardware is specified in this example. If you connect pin 0 and 1 you should get the same data back.
4//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
5//! with its UART port.
6
7#![no_std]
8#![no_main]
9
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_rp::bind_interrupts;
13use embassy_rp::peripherals::UART0;
14use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config};
15use embassy_time::Timer;
16use embedded_io_async::{Read, Write};
17use static_cell::StaticCell;
18use {defmt_rtt as _, panic_probe as _};
19
20bind_interrupts!(struct Irqs {
21 UART0_IRQ => BufferedInterruptHandler<UART0>;
22});
23
24#[embassy_executor::main]
25async fn main(spawner: Spawner) {
26 let p = embassy_rp::init(Default::default());
27 let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0);
28
29 static TX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
30 let tx_buf = &mut TX_BUF.init([0; 16])[..];
31 static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
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());
34 let (mut tx, rx) = uart.split();
35
36 unwrap!(spawner.spawn(reader(rx)));
37
38 info!("Writing...");
39 loop {
40 let data = [
41 1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
42 29, 30, 31,
43 ];
44 info!("TX {:?}", data);
45 tx.write_all(&data).await.unwrap();
46 Timer::after_secs(1).await;
47 }
48}
49
50#[embassy_executor::task]
51async fn reader(mut rx: BufferedUartRx<'static, UART0>) {
52 info!("Reading...");
53 loop {
54 let mut buf = [0; 31];
55 rx.read_exact(&mut buf).await.unwrap();
56 info!("RX {:?}", buf);
57 }
58}
diff --git a/examples/rp235x/src/bin/uart_r503.rs b/examples/rp235x/src/bin/uart_r503.rs
new file mode 100644
index 000000000..085be280b
--- /dev/null
+++ b/examples/rp235x/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/rp235x/src/bin/uart_unidir.rs b/examples/rp235x/src/bin/uart_unidir.rs
new file mode 100644
index 000000000..a45f40756
--- /dev/null
+++ b/examples/rp235x/src/bin/uart_unidir.rs
@@ -0,0 +1,50 @@
1//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
2//!
3//! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for
4//! this to work
5//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
6//! with its UART port.
7
8#![no_std]
9#![no_main]
10
11use defmt::*;
12use embassy_executor::Spawner;
13use embassy_rp::bind_interrupts;
14use embassy_rp::peripherals::UART1;
15use embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx};
16use embassy_time::Timer;
17use {defmt_rtt as _, panic_probe as _};
18
19bind_interrupts!(struct Irqs {
20 UART1_IRQ => InterruptHandler<UART1>;
21});
22
23#[embassy_executor::main]
24async fn main(spawner: Spawner) {
25 let p = embassy_rp::init(Default::default());
26
27 let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default());
28 let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default());
29
30 unwrap!(spawner.spawn(reader(uart_rx)));
31
32 info!("Writing...");
33 loop {
34 let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
35 info!("TX {:?}", data);
36 uart_tx.write(&data).await.unwrap();
37 Timer::after_secs(1).await;
38 }
39}
40
41#[embassy_executor::task]
42async fn reader(mut rx: UartRx<'static, UART1, Async>) {
43 info!("Reading...");
44 loop {
45 // read a total of 4 transmissions (32 / 8) and then print the result
46 let mut buf = [0; 32];
47 rx.read(&mut buf).await.unwrap();
48 info!("RX {:?}", buf);
49 }
50}
diff --git a/examples/rp235x/src/bin/usb_hid_keyboard.rs b/examples/rp235x/src/bin/usb_hid_keyboard.rs
new file mode 100644
index 000000000..6f496e23a
--- /dev/null
+++ b/examples/rp235x/src/bin/usb_hid_keyboard.rs
@@ -0,0 +1,188 @@
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::gpio::{Input, Pull};
11use embassy_rp::peripherals::USB;
12use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler};
13use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State as HidState};
14use embassy_usb::control::OutResponse;
15use embassy_usb::{Builder, Config, Handler};
16use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
17use {defmt_rtt as _, panic_probe as _};
18
19bind_interrupts!(struct Irqs {
20 USBCTRL_IRQ => InterruptHandler<USB>;
21});
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let p = embassy_rp::init(Default::default());
26 // Create the driver, from the HAL.
27 let driver = UsbDriver::new(p.USB, Irqs);
28
29 // Create embassy-usb Config
30 let mut config = Config::new(0xc0de, 0xcafe);
31 config.manufacturer = Some("Embassy");
32 config.product = Some("HID keyboard example");
33 config.serial_number = Some("12345678");
34 config.max_power = 100;
35 config.max_packet_size_0 = 64;
36
37 // Create embassy-usb DeviceBuilder using the driver and config.
38 // It needs some buffers for building the descriptors.
39 let mut config_descriptor = [0; 256];
40 let mut bos_descriptor = [0; 256];
41 // You can also add a Microsoft OS descriptor.
42 let mut msos_descriptor = [0; 256];
43 let mut control_buf = [0; 64];
44 let mut request_handler = MyRequestHandler {};
45 let mut device_handler = MyDeviceHandler::new();
46
47 let mut state = HidState::new();
48
49 let mut builder = Builder::new(
50 driver,
51 config,
52 &mut config_descriptor,
53 &mut bos_descriptor,
54 &mut msos_descriptor,
55 &mut control_buf,
56 );
57
58 builder.handler(&mut device_handler);
59
60 // Create classes on the builder.
61 let config = embassy_usb::class::hid::Config {
62 report_descriptor: KeyboardReport::desc(),
63 request_handler: None,
64 poll_ms: 60,
65 max_packet_size: 64,
66 };
67 let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);
68
69 // Build the builder.
70 let mut usb = builder.build();
71
72 // Run the USB device.
73 let usb_fut = usb.run();
74
75 // Set up the signal pin that will be used to trigger the keyboard.
76 let mut signal_pin = Input::new(p.PIN_16, Pull::None);
77
78 // Enable the schmitt trigger to slightly debounce.
79 signal_pin.set_schmitt(true);
80
81 let (reader, mut writer) = hid.split();
82
83 // Do stuff with the class!
84 let in_fut = async {
85 loop {
86 info!("Waiting for HIGH on pin 16");
87 signal_pin.wait_for_high().await;
88 info!("HIGH DETECTED");
89 // Create a report with the A key pressed. (no shift modifier)
90 let report = KeyboardReport {
91 keycodes: [4, 0, 0, 0, 0, 0],
92 leds: 0,
93 modifier: 0,
94 reserved: 0,
95 };
96 // Send the report.
97 match writer.write_serialize(&report).await {
98 Ok(()) => {}
99 Err(e) => warn!("Failed to send report: {:?}", e),
100 };
101 signal_pin.wait_for_low().await;
102 info!("LOW DETECTED");
103 let report = KeyboardReport {
104 keycodes: [0, 0, 0, 0, 0, 0],
105 leds: 0,
106 modifier: 0,
107 reserved: 0,
108 };
109 match writer.write_serialize(&report).await {
110 Ok(()) => {}
111 Err(e) => warn!("Failed to send report: {:?}", e),
112 };
113 }
114 };
115
116 let out_fut = async {
117 reader.run(false, &mut request_handler).await;
118 };
119
120 // Run everything concurrently.
121 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
122 join(usb_fut, join(in_fut, out_fut)).await;
123}
124
125struct MyRequestHandler {}
126
127impl RequestHandler for MyRequestHandler {
128 fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
129 info!("Get report for {:?}", id);
130 None
131 }
132
133 fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {
134 info!("Set report for {:?}: {=[u8]}", id, data);
135 OutResponse::Accepted
136 }
137
138 fn set_idle_ms(&mut self, id: Option<ReportId>, dur: u32) {
139 info!("Set idle rate for {:?} to {:?}", id, dur);
140 }
141
142 fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {
143 info!("Get idle rate for {:?}", id);
144 None
145 }
146}
147
148struct MyDeviceHandler {
149 configured: AtomicBool,
150}
151
152impl MyDeviceHandler {
153 fn new() -> Self {
154 MyDeviceHandler {
155 configured: AtomicBool::new(false),
156 }
157 }
158}
159
160impl Handler for MyDeviceHandler {
161 fn enabled(&mut self, enabled: bool) {
162 self.configured.store(false, Ordering::Relaxed);
163 if enabled {
164 info!("Device enabled");
165 } else {
166 info!("Device disabled");
167 }
168 }
169
170 fn reset(&mut self) {
171 self.configured.store(false, Ordering::Relaxed);
172 info!("Bus reset, the Vbus current limit is 100mA");
173 }
174
175 fn addressed(&mut self, addr: u8) {
176 self.configured.store(false, Ordering::Relaxed);
177 info!("USB address set to: {}", addr);
178 }
179
180 fn configured(&mut self, configured: bool) {
181 self.configured.store(configured, Ordering::Relaxed);
182 if configured {
183 info!("Device configured, it may now draw up to the configured current limit from Vbus.")
184 } else {
185 info!("Device is no longer configured, the Vbus current limit is 100mA.");
186 }
187 }
188}
diff --git a/examples/rp235x/src/bin/usb_webusb.rs b/examples/rp235x/src/bin/usb_webusb.rs
new file mode 100644
index 000000000..e73938ac9
--- /dev/null
+++ b/examples/rp235x/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/rp235x/src/bin/watchdog.rs b/examples/rp235x/src/bin/watchdog.rs
new file mode 100644
index 000000000..b9d4ef22f
--- /dev/null
+++ b/examples/rp235x/src/bin/watchdog.rs
@@ -0,0 +1,51 @@
1//! This example shows how to use Watchdog in the RP2040 chip.
2//!
3//! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor.
4
5#![no_std]
6#![no_main]
7
8use defmt::info;
9use embassy_executor::Spawner;
10use embassy_rp::gpio;
11use embassy_rp::watchdog::*;
12use embassy_time::{Duration, Timer};
13use gpio::{Level, Output};
14use {defmt_rtt as _, panic_probe as _};
15
16#[embassy_executor::main]
17async fn main(_spawner: Spawner) {
18 let p = embassy_rp::init(Default::default());
19 info!("Hello world!");
20
21 let mut watchdog = Watchdog::new(p.WATCHDOG);
22 let mut led = Output::new(p.PIN_25, Level::Low);
23
24 // Set the LED high for 2 seconds so we know when we're about to start the watchdog
25 led.set_high();
26 Timer::after_secs(2).await;
27
28 // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it
29 watchdog.start(Duration::from_millis(1_050));
30 info!("Started the watchdog timer");
31
32 // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset
33 for _ in 1..=5 {
34 led.set_low();
35 Timer::after_millis(500).await;
36 led.set_high();
37 Timer::after_millis(500).await;
38 info!("Feeding watchdog");
39 watchdog.feed();
40 }
41
42 info!("Stopped feeding, device will reset in 1.05 seconds");
43 // Blink 10 times per second, not feeding the watchdog.
44 // The processor should reset in 1.05 seconds.
45 loop {
46 led.set_low();
47 Timer::after_millis(100).await;
48 led.set_high();
49 Timer::after_millis(100).await;
50 }
51}
diff --git a/examples/rp235x/src/bin/wifi_blinky_pico_plus_2.rs b/examples/rp235x/src/bin/wifi_blinky_pico_plus_2.rs
new file mode 100644
index 000000000..ab7d6a93e
--- /dev/null
+++ b/examples/rp235x/src/bin/wifi_blinky_pico_plus_2.rs
@@ -0,0 +1,88 @@
1//! This example test the Pimoroni Pico Plus 2 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 cyw43_pio::{PioSpi, RM2_CLOCK_DIVIDER};
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_rp::peripherals::{DMA_CH0, PIO0};
12use embassy_rp::pio::{InterruptHandler, Pio};
13use embassy_rp::{bind_interrupts, gpio};
14use embassy_time::{Duration, Timer};
15use gpio::{Level, Output};
16use static_cell::StaticCell;
17use {defmt_rtt as _, panic_probe as _};
18
19// Program metadata for `picotool info`.
20// This isn't needed, but it's recomended to have these minimal entries.
21#[link_section = ".bi_entries"]
22#[used]
23pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
24 embassy_rp::binary_info::rp_program_name!(c"Blinky Example"),
25 embassy_rp::binary_info::rp_program_description!(
26 c"This example tests the RP Pico on board LED, connected to gpio 25"
27 ),
28 embassy_rp::binary_info::rp_cargo_version!(),
29 embassy_rp::binary_info::rp_program_build_attribute!(),
30];
31
32bind_interrupts!(struct Irqs {
33 PIO0_IRQ_0 => InterruptHandler<PIO0>;
34});
35
36#[embassy_executor::task]
37async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! {
38 runner.run().await
39}
40
41#[embassy_executor::main]
42async fn main(spawner: Spawner) {
43 let p = embassy_rp::init(Default::default());
44 let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin");
45 let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin");
46
47 // 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!`:
49 // probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000
50 // probe-rs download ../../cyw43-firmware/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) };
52 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
53
54 let pwr = Output::new(p.PIN_23, Level::Low);
55 let cs = Output::new(p.PIN_25, Level::High);
56 let mut pio = Pio::new(p.PIO0, Irqs);
57 let spi = PioSpi::new(
58 &mut pio.common,
59 pio.sm0,
60 RM2_CLOCK_DIVIDER,
61 pio.irq0,
62 cs,
63 p.PIN_24,
64 p.PIN_29,
65 p.DMA_CH0,
66 );
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 delay = Duration::from_secs(1);
79 loop {
80 info!("led on!");
81 control.gpio_set(0, true).await;
82 Timer::after(delay).await;
83
84 info!("led off!");
85 control.gpio_set(0, false).await;
86 Timer::after(delay).await;
87 }
88}
diff --git a/examples/rp235x/src/bin/zerocopy.rs b/examples/rp235x/src/bin/zerocopy.rs
new file mode 100644
index 000000000..39f03c8e4
--- /dev/null
+++ b/examples/rp235x/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}