mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-23 23:32:30 +00:00
Initial rp235x support
Examples have been run, but there is not yet a test suite.
This commit is contained in:
parent
891c5ee105
commit
b185e02a42
@ -15,6 +15,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/sr
|
||||
features = ["defmt", "unstable-pac", "time-driver"]
|
||||
flavors = [
|
||||
{ name = "rp2040", target = "thumbv6m-none-eabi" },
|
||||
{ name = "rp235x", target = "thumbv8m.main-none-eabi" },
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
@ -23,7 +24,9 @@ features = ["defmt", "unstable-pac", "time-driver"]
|
||||
[features]
|
||||
default = [ "rt" ]
|
||||
## Enable the rt feature of [`rp-pac`](https://docs.rs/rp-pac). This brings in the [`cortex-m-rt`](https://docs.rs/cortex-m-rt) crate, which adds startup code and minimal runtime initialization.
|
||||
rt = [ "rp-pac/rt" ]
|
||||
rt = []
|
||||
rt-2040 = [ "rt", "rp-pac/rt" ]
|
||||
rt-235x = [ "rt", "rp23-pac/rt" ]
|
||||
|
||||
## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.
|
||||
defmt = ["dep:defmt", "embassy-usb-driver/defmt", "embassy-hal-internal/defmt"]
|
||||
@ -88,6 +91,17 @@ boot2-w25x10cl = []
|
||||
## ```
|
||||
boot2-none = []
|
||||
|
||||
rp2040 = ["dep:rp-pac"]
|
||||
rp235x = ["dep:rp23-pac"]
|
||||
rp235xa = ["rp235x"]
|
||||
rp235xb = ["rp235x"]
|
||||
|
||||
# Add a binary-info header block containing picotool-compatible metadata.
|
||||
#
|
||||
# Takes up a little flash space, but picotool can then report the name of your
|
||||
# program and other details.
|
||||
binary-info = [ "rt-235x" ]
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.0", path = "../embassy-sync" }
|
||||
embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
|
||||
@ -112,7 +126,8 @@ embedded-storage-async = { version = "0.4.1" }
|
||||
rand_core = "0.6.4"
|
||||
fixed = "1.23.1"
|
||||
|
||||
rp-pac = { version = "6" }
|
||||
rp-pac = { path = "/home/cbjamo/235x/rp-pac", feature = ["rt"], optional = true }
|
||||
rp23-pac = { path = "/home/cbjamo/235x/rp23-pac/", feature = ["rt"], optional = true }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
||||
@ -123,6 +138,7 @@ pio-proc = {version= "0.2" }
|
||||
pio = {version= "0.2.1" }
|
||||
rp2040-boot2 = "0.3"
|
||||
document-features = "0.2.7"
|
||||
sha2-const-stable = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
embassy-executor = { version = "0.6.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] }
|
||||
|
@ -4,14 +4,16 @@ use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// Put the linker script somewhere the linker can find it
|
||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
let link_x = include_bytes!("link-rp.x.in");
|
||||
let mut f = File::create(out.join("link-rp.x")).unwrap();
|
||||
f.write_all(link_x).unwrap();
|
||||
if env::var("CARGO_FEATURE_RP2040").is_ok() {
|
||||
// Put the linker script somewhere the linker can find it
|
||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
let link_x = include_bytes!("link-rp.x.in");
|
||||
let mut f = File::create(out.join("link-rp.x")).unwrap();
|
||||
f.write_all(link_x).unwrap();
|
||||
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=link-rp.x.in");
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=link-rp.x.in");
|
||||
}
|
||||
}
|
||||
|
@ -5,4 +5,4 @@ SECTIONS {
|
||||
{
|
||||
KEEP(*(.boot2));
|
||||
} > BOOT2
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ use embassy_sync::waitqueue::AtomicWaker;
|
||||
use crate::gpio::{self, AnyPin, Pull, SealedPin as GpioPin};
|
||||
use crate::interrupt::typelevel::Binding;
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::pac::dma::vals::TreqSel;
|
||||
use crate::peripherals::{ADC, ADC_TEMP_SENSOR};
|
||||
use crate::{dma, interrupt, pac, peripherals, Peripheral, RegExt};
|
||||
|
||||
@ -229,7 +230,10 @@ impl<'d> Adc<'d, Async> {
|
||||
div: u16,
|
||||
dma: impl Peripheral<P = impl dma::Channel>,
|
||||
) -> Result<(), Error> {
|
||||
#[cfg(feature = "rp2040")]
|
||||
let mut rrobin = 0_u8;
|
||||
#[cfg(feature = "rp235x")]
|
||||
let mut rrobin = 0_u16;
|
||||
for c in channels {
|
||||
rrobin |= 1 << c;
|
||||
}
|
||||
@ -278,7 +282,7 @@ impl<'d> Adc<'d, Async> {
|
||||
}
|
||||
let auto_reset = ResetDmaConfig;
|
||||
|
||||
let dma = unsafe { dma::read(dma, r.fifo().as_ptr() as *const W, buf as *mut [W], 36) };
|
||||
let dma = unsafe { dma::read(dma, r.fifo().as_ptr() as *const W, buf as *mut [W], TreqSel::ADC) };
|
||||
// start conversions and wait for dma to finish. we can't report errors early
|
||||
// because there's no interrupt to signal them, and inspecting every element
|
||||
// of the fifo is too costly to do here.
|
||||
@ -423,10 +427,31 @@ macro_rules! impl_pin {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "rp235xa", feature = "rp2040"))]
|
||||
impl_pin!(PIN_26, 0);
|
||||
#[cfg(any(feature = "rp235xa", feature = "rp2040"))]
|
||||
impl_pin!(PIN_27, 1);
|
||||
#[cfg(any(feature = "rp235xa", feature = "rp2040"))]
|
||||
impl_pin!(PIN_28, 2);
|
||||
#[cfg(any(feature = "rp235xa", feature = "rp2040"))]
|
||||
impl_pin!(PIN_29, 3);
|
||||
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_40, 0);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_41, 1);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_42, 2);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_43, 3);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_44, 4);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_45, 5);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_46, 6);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_47, 7);
|
||||
|
||||
impl SealedAdcChannel for peripherals::ADC_TEMP_SENSOR {}
|
||||
impl AdcChannel for peripherals::ADC_TEMP_SENSOR {}
|
||||
|
31
embassy-rp/src/binary_info/consts.rs
Normal file
31
embassy-rp/src/binary_info/consts.rs
Normal file
@ -0,0 +1,31 @@
|
||||
//! Constants for binary info
|
||||
|
||||
/// All Raspberry Pi specified IDs have this tag.
|
||||
///
|
||||
/// You can create your own for custom fields.
|
||||
pub const TAG_RASPBERRY_PI: u16 = super::make_tag(b"RP");
|
||||
|
||||
/// Used to note the program name - use with StringEntry
|
||||
pub const ID_RP_PROGRAM_NAME: u32 = 0x02031c86;
|
||||
/// Used to note the program version - use with StringEntry
|
||||
pub const ID_RP_PROGRAM_VERSION_STRING: u32 = 0x11a9bc3a;
|
||||
/// Used to note the program build date - use with StringEntry
|
||||
pub const ID_RP_PROGRAM_BUILD_DATE_STRING: u32 = 0x9da22254;
|
||||
/// Used to note the size of the binary - use with IntegerEntry
|
||||
pub const ID_RP_BINARY_END: u32 = 0x68f465de;
|
||||
/// Used to note a URL for the program - use with StringEntry
|
||||
pub const ID_RP_PROGRAM_URL: u32 = 0x1856239a;
|
||||
/// Used to note a description of the program - use with StringEntry
|
||||
pub const ID_RP_PROGRAM_DESCRIPTION: u32 = 0xb6a07c19;
|
||||
/// Used to note some feature of the program - use with StringEntry
|
||||
pub const ID_RP_PROGRAM_FEATURE: u32 = 0xa1f4b453;
|
||||
/// Used to note some whether this was a Debug or Release build - use with StringEntry
|
||||
pub const ID_RP_PROGRAM_BUILD_ATTRIBUTE: u32 = 0x4275f0d3;
|
||||
/// Used to note the Pico SDK version used - use with StringEntry
|
||||
pub const ID_RP_SDK_VERSION: u32 = 0x5360b3ab;
|
||||
/// Used to note which board this program targets - use with StringEntry
|
||||
pub const ID_RP_PICO_BOARD: u32 = 0xb63cffbb;
|
||||
/// Used to note which `boot2` image this program uses - use with StringEntry
|
||||
pub const ID_RP_BOOT2_NAME: u32 = 0x7f8882e1;
|
||||
|
||||
// End of file
|
171
embassy-rp/src/binary_info/macros.rs
Normal file
171
embassy-rp/src/binary_info/macros.rs
Normal file
@ -0,0 +1,171 @@
|
||||
//! Handy macros for making Binary Info entries
|
||||
|
||||
/// Generate a static item containing the given environment variable,
|
||||
/// and return its [`EntryAddr`](super::EntryAddr).
|
||||
#[macro_export]
|
||||
macro_rules! binary_info_env {
|
||||
($tag:expr, $id:expr, $env_var_name:expr) => {
|
||||
$crate::binary_info_str!($tag, $id, {
|
||||
let value = concat!(env!($env_var_name), "\0");
|
||||
// # Safety
|
||||
//
|
||||
// We used `concat!` to null-terminate on the line above.
|
||||
let value_cstr =
|
||||
unsafe { core::ffi::CStr::from_bytes_with_nul_unchecked(value.as_bytes()) };
|
||||
value_cstr
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate a static item containing the given string, and return its
|
||||
/// [`EntryAddr`](super::EntryAddr).
|
||||
///
|
||||
/// You must pass a numeric tag, a numeric ID, and `&CStr` (which is always
|
||||
/// null-terminated).
|
||||
#[macro_export]
|
||||
macro_rules! binary_info_str {
|
||||
($tag:expr, $id:expr, $str:expr) => {{
|
||||
static ENTRY: $crate::binary_info::StringEntry =
|
||||
$crate::binary_info::StringEntry::new($tag, $id, $str);
|
||||
ENTRY.addr()
|
||||
}};
|
||||
}
|
||||
|
||||
/// Generate a static item containing the given string, and return its
|
||||
/// [`EntryAddr`](super::EntryAddr).
|
||||
///
|
||||
/// You must pass a numeric tag, a numeric ID, and `&CStr` (which is always
|
||||
/// null-terminated).
|
||||
#[macro_export]
|
||||
macro_rules! binary_info_int {
|
||||
($tag:expr, $id:expr, $int:expr) => {{
|
||||
static ENTRY: $crate::binary_info::IntegerEntry =
|
||||
$crate::binary_info::IntegerEntry::new($tag, $id, $int);
|
||||
ENTRY.addr()
|
||||
}};
|
||||
}
|
||||
|
||||
/// Generate a static item containing the program name, and return its
|
||||
/// [`EntryAddr`](super::EntryAddr).
|
||||
#[macro_export]
|
||||
macro_rules! binary_info_rp_program_name {
|
||||
($name:expr) => {
|
||||
$crate::binary_info_str!(
|
||||
$crate::binary_info::consts::TAG_RASPBERRY_PI,
|
||||
$crate::binary_info::consts::ID_RP_PROGRAM_NAME,
|
||||
$name
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate a static item containing the `CARGO_BIN_NAME` as the program name,
|
||||
/// and return its [`EntryAddr`](super::EntryAddr).
|
||||
#[macro_export]
|
||||
macro_rules! binary_info_rp_cargo_bin_name {
|
||||
() => {
|
||||
$crate::binary_info_env!(
|
||||
$crate::binary_info::consts::TAG_RASPBERRY_PI,
|
||||
$crate::binary_info::consts::ID_RP_PROGRAM_NAME,
|
||||
"CARGO_BIN_NAME"
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate a static item containing the program version, and return its
|
||||
/// [`EntryAddr`](super::EntryAddr).
|
||||
#[macro_export]
|
||||
macro_rules! binary_info_rp_program_version {
|
||||
($version:expr) => {{
|
||||
$crate::binary_info_str!(
|
||||
$crate::binary_info::consts::TAG_RASPBERRY_PI,
|
||||
$crate::binary_info::consts::ID_RP_PROGRAM_VERSION,
|
||||
$version
|
||||
)
|
||||
}};
|
||||
}
|
||||
|
||||
/// Generate a static item containing the `CARGO_PKG_VERSION` as the program
|
||||
/// version, and return its [`EntryAddr`](super::EntryAddr).
|
||||
#[macro_export]
|
||||
macro_rules! binary_info_rp_cargo_version {
|
||||
() => {
|
||||
$crate::binary_info_env!(
|
||||
$crate::binary_info::consts::TAG_RASPBERRY_PI,
|
||||
$crate::binary_info::consts::ID_RP_PROGRAM_VERSION_STRING,
|
||||
"CARGO_PKG_VERSION"
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate a static item containing the program URL, and return its
|
||||
/// [`EntryAddr`](super::EntryAddr).
|
||||
#[macro_export]
|
||||
macro_rules! binary_info_rp_program_url {
|
||||
($url:expr) => {
|
||||
$crate::binary_info_str!(
|
||||
$crate::binary_info::consts::TAG_RASPBERRY_PI,
|
||||
$crate::binary_info::consts::ID_RP_PROGRAM_URL,
|
||||
$url
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate a static item containing the `CARGO_PKG_HOMEPAGE` as the program URL,
|
||||
/// and return its [`EntryAddr`](super::EntryAddr).
|
||||
#[macro_export]
|
||||
macro_rules! binary_info_rp_cargo_homepage_url {
|
||||
() => {
|
||||
$crate::binary_info_env!(
|
||||
$crate::binary_info::consts::TAG_RASPBERRY_PI,
|
||||
$crate::binary_info::consts::ID_RP_PROGRAM_URL,
|
||||
"CARGO_PKG_HOMEPAGE"
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate a static item containing the program description, and return its
|
||||
/// [`EntryAddr`](super::EntryAddr).
|
||||
#[macro_export]
|
||||
macro_rules! binary_info_rp_program_description {
|
||||
($description:expr) => {
|
||||
$crate::binary_info_str!(
|
||||
$crate::binary_info::consts::TAG_RASPBERRY_PI,
|
||||
$crate::binary_info::consts::ID_RP_PROGRAM_DESCRIPTION,
|
||||
$description
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate a static item containing whether this is a debug or a release
|
||||
/// build, and return its [`EntryAddr`](super::EntryAddr).
|
||||
#[macro_export]
|
||||
macro_rules! binary_info_rp_program_build_attribute {
|
||||
() => {
|
||||
$crate::binary_info_str!(
|
||||
$crate::binary_info::consts::TAG_RASPBERRY_PI,
|
||||
$crate::binary_info::consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE,
|
||||
{
|
||||
if cfg!(debug_assertions) {
|
||||
c"debug"
|
||||
} else {
|
||||
c"release"
|
||||
}
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate a static item containing the specific board this program runs on,
|
||||
/// and return its [`EntryAddr`](super::EntryAddr).
|
||||
#[macro_export]
|
||||
macro_rules! binary_info_rp_pico_board {
|
||||
($board:expr) => {
|
||||
$crate::binary_info_str!(
|
||||
$crate::binary_info::consts::TAG_RASPBERRY_PI,
|
||||
$crate::binary_info::consts::ID_RP_PICO_BOARD,
|
||||
$board
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// End of file
|
174
embassy-rp/src/binary_info/mod.rs
Normal file
174
embassy-rp/src/binary_info/mod.rs
Normal file
@ -0,0 +1,174 @@
|
||||
//! Code and types for creating Picotool compatible "Binary Info" metadata
|
||||
//!
|
||||
//! Add something like this to your program, and compile with the "binary-info"
|
||||
//! and "rt" features:
|
||||
//!
|
||||
//! ```
|
||||
//! # use rp235x_hal as hal;
|
||||
//! #[link_section = ".bi_entries"]
|
||||
//! #[used]
|
||||
//! pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 3] = [
|
||||
//! hal::binary_info_rp_program_name!(c"Program Name Here"),
|
||||
//! hal::binary_info_rp_cargo_version!(),
|
||||
//! hal::binary_info_int!(hal::binary_info::make_tag(b"JP"), 0x0000_0001, 0x12345678),
|
||||
//! ];
|
||||
//! ```
|
||||
|
||||
pub mod consts;
|
||||
|
||||
mod types;
|
||||
pub use types::*;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
extern "C" {
|
||||
/// The linker script sets this symbol to have the address of the first
|
||||
/// entry in the `.bi_entries` section.
|
||||
static __bi_entries_start: EntryAddr;
|
||||
/// The linker script sets this symbol to have the address just past the
|
||||
/// last entry in the `.bi_entries` section.
|
||||
static __bi_entries_end: EntryAddr;
|
||||
/// The linker script sets this symbol to have the address of the first
|
||||
/// entry in the `.data` section.
|
||||
static __sdata: u32;
|
||||
/// The linker script sets this symbol to have the address just past the
|
||||
/// first entry in the `.data` section.
|
||||
static __edata: u32;
|
||||
/// The linker script sets this symbol to have the address of the
|
||||
/// initialisation data for the first entry in the `.data` section (i.e. a
|
||||
/// flash address, not a RAM address).
|
||||
static __sidata: u32;
|
||||
}
|
||||
|
||||
/// Picotool can find this block in our ELF file and report interesting
|
||||
/// metadata.
|
||||
///
|
||||
/// The data here tells picotool the start and end flash addresses of our
|
||||
/// metadata.
|
||||
#[cfg(feature = "binary-info")]
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static PICOTOOL_HEADER: Header = unsafe {
|
||||
Header::new(
|
||||
core::ptr::addr_of!(__bi_entries_start),
|
||||
core::ptr::addr_of!(__bi_entries_end),
|
||||
&MAPPING_TABLE,
|
||||
)
|
||||
};
|
||||
|
||||
/// This tells picotool how to convert RAM addresses back into Flash addresses
|
||||
#[cfg(feature = "binary-info")]
|
||||
pub static MAPPING_TABLE: [MappingTableEntry; 2] = [
|
||||
// This is the entry for .data
|
||||
MappingTableEntry {
|
||||
source_addr_start: unsafe { core::ptr::addr_of!(__sidata) },
|
||||
dest_addr_start: unsafe { core::ptr::addr_of!(__sdata) },
|
||||
dest_addr_end: unsafe { core::ptr::addr_of!(__edata) },
|
||||
},
|
||||
// This is the terminating marker
|
||||
MappingTableEntry::null(),
|
||||
];
|
||||
|
||||
/// Create a 'Binary Info' entry containing the program name
|
||||
///
|
||||
/// This is well-known to picotool, and will be displayed if you run `picotool info`.
|
||||
///
|
||||
/// * Tag: [`consts::TAG_RASPBERRY_PI`]
|
||||
/// * ID: [`consts::ID_RP_PROGRAM_NAME`]
|
||||
pub const fn rp_program_name(name: &'static core::ffi::CStr) -> StringEntry {
|
||||
StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_NAME, name)
|
||||
}
|
||||
|
||||
/// Create a 'Binary Info' entry containing the program version.
|
||||
///
|
||||
/// * Tag: [`consts::TAG_RASPBERRY_PI`]
|
||||
/// * Id: [`consts::ID_RP_PROGRAM_VERSION_STRING`]
|
||||
pub const fn rp_program_version(name: &'static core::ffi::CStr) -> StringEntry {
|
||||
StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_VERSION_STRING, name)
|
||||
}
|
||||
|
||||
/// Create a 'Binary Info' entry with a URL
|
||||
///
|
||||
/// * Tag: [`consts::TAG_RASPBERRY_PI`]
|
||||
/// * Id: [`consts::ID_RP_PROGRAM_URL`]
|
||||
pub const fn rp_program_url(url: &'static core::ffi::CStr) -> StringEntry {
|
||||
StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_URL, url)
|
||||
}
|
||||
|
||||
/// Create a 'Binary Info' with the program build date
|
||||
///
|
||||
/// * Tag: [`consts::TAG_RASPBERRY_PI`]
|
||||
/// * Id: [`consts::ID_RP_PROGRAM_BUILD_DATE_STRING`]
|
||||
pub const fn rp_program_build_date_string(value: &'static core::ffi::CStr) -> StringEntry {
|
||||
StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_BUILD_DATE_STRING, value)
|
||||
}
|
||||
|
||||
/// Create a 'Binary Info' with the size of the binary
|
||||
///
|
||||
/// * Tag: [`consts::TAG_RASPBERRY_PI`]
|
||||
/// * Id: [`consts::ID_RP_BINARY_END`]
|
||||
pub const fn rp_binary_end(value: u32) -> IntegerEntry {
|
||||
IntegerEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BINARY_END, value)
|
||||
}
|
||||
|
||||
/// Create a 'Binary Info' with a description of the program
|
||||
///
|
||||
/// * Tag: [`consts::TAG_RASPBERRY_PI`]
|
||||
/// * Id: [`consts::ID_RP_PROGRAM_DESCRIPTION`]
|
||||
pub const fn rp_program_description(value: &'static core::ffi::CStr) -> StringEntry {
|
||||
StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_DESCRIPTION, value)
|
||||
}
|
||||
|
||||
/// Create a 'Binary Info' with some feature of the program
|
||||
///
|
||||
/// * Tag: [`consts::TAG_RASPBERRY_PI`]
|
||||
/// * Id: [`consts::ID_RP_PROGRAM_FEATURE`]
|
||||
pub const fn rp_program_feature(value: &'static core::ffi::CStr) -> StringEntry {
|
||||
StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_FEATURE, value)
|
||||
}
|
||||
|
||||
/// Create a 'Binary Info' with some whether this was a Debug or Release build
|
||||
///
|
||||
/// * Tag: [`consts::TAG_RASPBERRY_PI`]
|
||||
/// * Id: [`consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE`]
|
||||
pub const fn rp_program_build_attribute(value: &'static core::ffi::CStr) -> StringEntry {
|
||||
StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PROGRAM_BUILD_ATTRIBUTE, value)
|
||||
}
|
||||
|
||||
/// Create a 'Binary Info' with the Pico SDK version used
|
||||
///
|
||||
/// * Tag: [`consts::TAG_RASPBERRY_PI`]
|
||||
/// * Id: [`consts::ID_RP_SDK_VERSION`]
|
||||
pub const fn rp_sdk_version(value: &'static core::ffi::CStr) -> StringEntry {
|
||||
StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_SDK_VERSION, value)
|
||||
}
|
||||
|
||||
/// Create a 'Binary Info' with which board this program targets
|
||||
///
|
||||
/// * Tag: [`consts::TAG_RASPBERRY_PI`]
|
||||
/// * Id: [`consts::ID_RP_PICO_BOARD`]
|
||||
pub const fn rp_pico_board(value: &'static core::ffi::CStr) -> StringEntry {
|
||||
StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_PICO_BOARD, value)
|
||||
}
|
||||
|
||||
/// Create a 'Binary Info' with which `boot2` image this program uses
|
||||
///
|
||||
/// * Tag: [`consts::TAG_RASPBERRY_PI`]
|
||||
/// * Id: [`consts::ID_RP_BOOT2_NAME`]
|
||||
pub const fn rp_boot2_name(value: &'static core::ffi::CStr) -> StringEntry {
|
||||
StringEntry::new(consts::TAG_RASPBERRY_PI, consts::ID_RP_BOOT2_NAME, value)
|
||||
}
|
||||
|
||||
/// Create a tag from two ASCII letters.
|
||||
///
|
||||
/// ```
|
||||
/// # use rp235x_hal as hal;
|
||||
/// let tag = hal::binary_info::make_tag(b"RP");
|
||||
/// assert_eq!(tag, 0x5052);
|
||||
/// ```
|
||||
pub const fn make_tag(c: &[u8; 2]) -> u16 {
|
||||
u16::from_le_bytes(*c)
|
||||
}
|
||||
|
||||
// End of file
|
192
embassy-rp/src/binary_info/types.rs
Normal file
192
embassy-rp/src/binary_info/types.rs
Normal file
@ -0,0 +1,192 @@
|
||||
//! Types for the Binary Info system
|
||||
|
||||
/// This is the 'Binary Info' header block that `picotool` looks for in your UF2
|
||||
/// file/ELF file/Pico in Bootloader Mode to give you useful metadata about your
|
||||
/// program.
|
||||
///
|
||||
/// It should be placed in the first 4096 bytes of flash, so use your `memory.x`
|
||||
/// to insert a section between `.text` and `.vector_table` and put a static
|
||||
/// value of this type in that section.
|
||||
#[repr(C)]
|
||||
pub struct Header {
|
||||
/// Must be equal to Picotool::MARKER_START
|
||||
marker_start: u32,
|
||||
/// The first in our table of pointers to Entries
|
||||
entries_start: *const EntryAddr,
|
||||
/// The last in our table of pointers to Entries
|
||||
entries_end: *const EntryAddr,
|
||||
/// The first entry in a null-terminated RAM/Flash mapping table
|
||||
mapping_table: *const MappingTableEntry,
|
||||
/// Must be equal to Picotool::MARKER_END
|
||||
marker_end: u32,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
/// This is the `BINARY_INFO_MARKER_START` magic value from `picotool`
|
||||
const MARKER_START: u32 = 0x7188ebf2;
|
||||
/// This is the `BINARY_INFO_MARKER_END` magic value from `picotool`
|
||||
const MARKER_END: u32 = 0xe71aa390;
|
||||
|
||||
/// Create a new `picotool` compatible header.
|
||||
///
|
||||
/// * `entries_start` - the first [`EntryAddr`] in the table
|
||||
/// * `entries_end` - the last [`EntryAddr`] in the table
|
||||
/// * `mapping_table` - the RAM/Flash address mapping table
|
||||
pub const fn new(
|
||||
entries_start: *const EntryAddr,
|
||||
entries_end: *const EntryAddr,
|
||||
mapping_table: &'static [MappingTableEntry],
|
||||
) -> Self {
|
||||
let mapping_table = mapping_table.as_ptr();
|
||||
Self {
|
||||
marker_start: Self::MARKER_START,
|
||||
entries_start,
|
||||
entries_end,
|
||||
mapping_table,
|
||||
marker_end: Self::MARKER_END,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need this as rustc complains that is is unsafe to share `*const u32`
|
||||
// pointers between threads. We only allow these to be created with static
|
||||
// data, so this is OK.
|
||||
unsafe impl Sync for Header {}
|
||||
|
||||
/// This is a reference to an entry. It's like a `&dyn` ref to some type `T:
|
||||
/// Entry`, except that the run-time type information is encoded into the
|
||||
/// Entry itself in very specific way.
|
||||
#[repr(transparent)]
|
||||
pub struct EntryAddr(*const u32);
|
||||
|
||||
// We need this as rustc complains that is is unsafe to share `*const u32`
|
||||
// pointers between threads. We only allow these to be created with static
|
||||
// data, so this is OK.
|
||||
unsafe impl Sync for EntryAddr {}
|
||||
|
||||
/// Allows us to tell picotool where values are in the UF2 given their run-time
|
||||
/// address.
|
||||
///
|
||||
/// The most obvious example is RAM variables, which must be found in the
|
||||
/// `.data` section of the UF2.
|
||||
#[repr(C)]
|
||||
pub struct MappingTableEntry {
|
||||
/// The start address in RAM (or wherever the address picotool finds will
|
||||
/// point)
|
||||
pub source_addr_start: *const u32,
|
||||
/// The start address in flash (or whever the data actually lives in the
|
||||
/// ELF)
|
||||
pub dest_addr_start: *const u32,
|
||||
/// The end address in flash
|
||||
pub dest_addr_end: *const u32,
|
||||
}
|
||||
|
||||
impl MappingTableEntry {
|
||||
/// Generate a null entry to mark the end of the list
|
||||
pub const fn null() -> MappingTableEntry {
|
||||
MappingTableEntry {
|
||||
source_addr_start: core::ptr::null(),
|
||||
dest_addr_start: core::ptr::null(),
|
||||
dest_addr_end: core::ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need this as rustc complains that is is unsafe to share `*const u32`
|
||||
// pointers between threads. We only allow these to be created with static
|
||||
// data, so this is OK.
|
||||
unsafe impl Sync for MappingTableEntry {}
|
||||
|
||||
/// This is the set of data types that `picotool` supports.
|
||||
#[repr(u16)]
|
||||
pub enum DataType {
|
||||
/// Raw data
|
||||
Raw = 1,
|
||||
/// Data with a size
|
||||
SizedData = 2,
|
||||
/// A list of binary data
|
||||
BinaryInfoListZeroTerminated = 3,
|
||||
/// A BSON encoded blob
|
||||
Bson = 4,
|
||||
/// An Integer with an ID
|
||||
IdAndInt = 5,
|
||||
/// A string with an Id
|
||||
IdAndString = 6,
|
||||
/// A block device
|
||||
BlockDevice = 7,
|
||||
/// GPIO pins, with their function
|
||||
PinsWithFunction = 8,
|
||||
/// GPIO pins, with their name
|
||||
PinsWithName = 9,
|
||||
/// GPIO pins, with multiple names?
|
||||
PinsWithNames = 10,
|
||||
}
|
||||
|
||||
/// All Entries start with this common header
|
||||
#[repr(C)]
|
||||
struct EntryCommon {
|
||||
data_type: DataType,
|
||||
tag: u16,
|
||||
}
|
||||
|
||||
/// An entry which contains both an ID (e.g. `ID_RP_PROGRAM_NAME`) and a pointer
|
||||
/// to a null-terminated string.
|
||||
#[repr(C)]
|
||||
pub struct StringEntry {
|
||||
header: EntryCommon,
|
||||
id: u32,
|
||||
value: *const core::ffi::c_char,
|
||||
}
|
||||
|
||||
impl StringEntry {
|
||||
/// Create a new `StringEntry`
|
||||
pub const fn new(tag: u16, id: u32, value: &'static core::ffi::CStr) -> StringEntry {
|
||||
StringEntry {
|
||||
header: EntryCommon {
|
||||
data_type: DataType::IdAndString,
|
||||
tag,
|
||||
},
|
||||
id,
|
||||
value: value.as_ptr(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get this entry's address
|
||||
pub const fn addr(&self) -> EntryAddr {
|
||||
EntryAddr(self as *const Self as *const u32)
|
||||
}
|
||||
}
|
||||
|
||||
// We need this as rustc complains that is is unsafe to share `*const
|
||||
// core::ffi::c_char` pointers between threads. We only allow these to be
|
||||
// created with static string slices, so it's OK.
|
||||
unsafe impl Sync for StringEntry {}
|
||||
|
||||
/// An entry which contains both an ID (e.g. `ID_RP_BINARY_END`) and an integer.
|
||||
#[repr(C)]
|
||||
pub struct IntegerEntry {
|
||||
header: EntryCommon,
|
||||
id: u32,
|
||||
value: u32,
|
||||
}
|
||||
|
||||
impl IntegerEntry {
|
||||
/// Create a new `StringEntry`
|
||||
pub const fn new(tag: u16, id: u32, value: u32) -> IntegerEntry {
|
||||
IntegerEntry {
|
||||
header: EntryCommon {
|
||||
data_type: DataType::IdAndInt,
|
||||
tag,
|
||||
},
|
||||
id,
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get this entry's address
|
||||
pub const fn addr(&self) -> EntryAddr {
|
||||
EntryAddr(self as *const Self as *const u32)
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
1076
embassy-rp/src/block.rs
Normal file
1076
embassy-rp/src/block.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,17 @@
|
||||
//! Clock configuration for the RP2040
|
||||
|
||||
#[cfg(feature = "rp2040")]
|
||||
use core::arch::asm;
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{AtomicU16, AtomicU32, Ordering};
|
||||
#[cfg(feature = "rp2040")]
|
||||
use core::sync::atomic::AtomicU16;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use pac::clocks::vals::*;
|
||||
|
||||
use crate::gpio::{AnyPin, SealedPin};
|
||||
#[cfg(feature = "rp2040")]
|
||||
use crate::pac::common::{Reg, RW};
|
||||
use crate::{pac, reset, Peripheral};
|
||||
|
||||
@ -26,6 +31,7 @@ struct Clocks {
|
||||
// gpin1: AtomicU32,
|
||||
rosc: AtomicU32,
|
||||
peri: AtomicU32,
|
||||
#[cfg(feature = "rp2040")]
|
||||
rtc: AtomicU16,
|
||||
}
|
||||
|
||||
@ -41,6 +47,7 @@ static CLOCKS: Clocks = Clocks {
|
||||
// gpin1: AtomicU32::new(0),
|
||||
rosc: AtomicU32::new(0),
|
||||
peri: AtomicU32::new(0),
|
||||
#[cfg(feature = "rp2040")]
|
||||
rtc: AtomicU16::new(0),
|
||||
};
|
||||
|
||||
@ -81,6 +88,7 @@ pub struct ClockConfig {
|
||||
/// ADC clock configuration.
|
||||
pub adc_clk: Option<AdcClkConfig>,
|
||||
/// RTC clock configuration.
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub rtc_clk: Option<RtcClkConfig>,
|
||||
// gpin0: Option<(u32, Gpin<'static, AnyPin>)>,
|
||||
// gpin1: Option<(u32, Gpin<'static, AnyPin>)>,
|
||||
@ -135,6 +143,7 @@ impl ClockConfig {
|
||||
phase: 0,
|
||||
}),
|
||||
// CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
|
||||
#[cfg(feature = "rp2040")]
|
||||
rtc_clk: Some(RtcClkConfig {
|
||||
src: RtcClkSrc::PllUsb,
|
||||
div_int: 1024,
|
||||
@ -174,6 +183,7 @@ impl ClockConfig {
|
||||
phase: 0,
|
||||
}),
|
||||
// CLK RTC = ROSC (140MHz) / 2986.667969 ≅ 46875Hz
|
||||
#[cfg(feature = "rp2040")]
|
||||
rtc_clk: Some(RtcClkConfig {
|
||||
src: RtcClkSrc::Rosc,
|
||||
div_int: 2986,
|
||||
@ -295,9 +305,17 @@ pub struct SysClkConfig {
|
||||
/// SYS clock source.
|
||||
pub src: SysClkSrc,
|
||||
/// SYS clock divider.
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub div_int: u32,
|
||||
/// SYS clock fraction.
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub div_frac: u8,
|
||||
/// SYS clock divider.
|
||||
#[cfg(feature = "rp235x")]
|
||||
pub div_int: u16,
|
||||
/// SYS clock fraction.
|
||||
#[cfg(feature = "rp235x")]
|
||||
pub div_frac: u16,
|
||||
}
|
||||
|
||||
/// USB clock source.
|
||||
@ -358,6 +376,7 @@ pub struct AdcClkConfig {
|
||||
#[repr(u8)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub enum RtcClkSrc {
|
||||
/// PLL USB.
|
||||
PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB as _,
|
||||
@ -372,6 +391,7 @@ pub enum RtcClkSrc {
|
||||
}
|
||||
|
||||
/// RTC clock config.
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub struct RtcClkConfig {
|
||||
/// RTC clock source.
|
||||
pub src: RtcClkSrc,
|
||||
@ -396,10 +416,9 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
||||
peris.set_pads_qspi(false);
|
||||
peris.set_pll_sys(false);
|
||||
peris.set_pll_usb(false);
|
||||
// TODO investigate if usb should be unreset here
|
||||
peris.set_usbctrl(false);
|
||||
peris.set_syscfg(false);
|
||||
peris.set_rtc(false);
|
||||
//peris.set_rtc(false);
|
||||
reset::reset(peris);
|
||||
|
||||
// Disable resus that may be enabled from previous software
|
||||
@ -409,9 +428,15 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
||||
|
||||
// Before we touch PLLs, switch sys and ref cleanly away from their aux sources.
|
||||
c.clk_sys_ctrl().modify(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
|
||||
#[cfg(feature = "rp2040")]
|
||||
while c.clk_sys_selected().read() != 1 {}
|
||||
#[cfg(feature = "rp235x")]
|
||||
while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1) {}
|
||||
c.clk_ref_ctrl().modify(|w| w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH));
|
||||
#[cfg(feature = "rp2040")]
|
||||
while c.clk_ref_selected().read() != 1 {}
|
||||
#[cfg(feature = "rp235x")]
|
||||
while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {}
|
||||
|
||||
// Reset the PLLs
|
||||
let mut peris = reset::Peripherals(0);
|
||||
@ -479,11 +504,16 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
||||
w.set_src(ref_src);
|
||||
w.set_auxsrc(ref_aux);
|
||||
});
|
||||
while c.clk_ref_selected().read() != 1 << ref_src as u32 {}
|
||||
#[cfg(feature = "rp2040")]
|
||||
while c.clk_ref_selected().read() != (1 << ref_src as u32) {}
|
||||
#[cfg(feature = "rp235x")]
|
||||
while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ref_src as u32) {}
|
||||
c.clk_ref_div().write(|w| {
|
||||
w.set_int(config.ref_clk.div);
|
||||
});
|
||||
|
||||
// Configure tick generation on the 2040. On the 2350 the timers are driven from the sysclk.
|
||||
#[cfg(feature = "rp2040")]
|
||||
pac::WATCHDOG.tick().write(|w| {
|
||||
w.set_cycles((clk_ref_freq / 1_000_000) as u16);
|
||||
w.set_enable(true);
|
||||
@ -500,7 +530,6 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
||||
// SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq),
|
||||
// SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1, gpin1_freq),
|
||||
};
|
||||
assert!(config.sys_clk.div_int <= 0x1000000);
|
||||
let div = config.sys_clk.div_int as u64 * 256 + config.sys_clk.div_frac as u64;
|
||||
(src, aux, ((freq as u64 * 256) / div) as u32)
|
||||
};
|
||||
@ -508,13 +537,21 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
||||
CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed);
|
||||
if sys_src != ClkSysCtrlSrc::CLK_REF {
|
||||
c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
|
||||
while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF as u32 {}
|
||||
#[cfg(feature = "rp2040")]
|
||||
while c.clk_sys_selected().read() != (1 << ClkSysCtrlSrc::CLK_REF as u32) {}
|
||||
#[cfg(feature = "rp235x")]
|
||||
while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << ClkSysCtrlSrc::CLK_REF as u32) {}
|
||||
}
|
||||
c.clk_sys_ctrl().write(|w| {
|
||||
w.set_auxsrc(sys_aux);
|
||||
w.set_src(sys_src);
|
||||
});
|
||||
while c.clk_sys_selected().read() != 1 << sys_src as u32 {}
|
||||
|
||||
#[cfg(feature = "rp2040")]
|
||||
while c.clk_sys_selected().read() != (1 << sys_src as u32) {}
|
||||
#[cfg(feature = "rp235x")]
|
||||
while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << sys_src as u32) {}
|
||||
|
||||
c.clk_sys_div().write(|w| {
|
||||
w.set_int(config.sys_clk.div_int);
|
||||
w.set_frac(config.sys_clk.div_frac);
|
||||
@ -592,6 +629,8 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
||||
CLOCKS.adc.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// rp2040 specific clocks
|
||||
#[cfg(feature = "rp2040")]
|
||||
if let Some(conf) = config.rtc_clk {
|
||||
c.clk_rtc_div().write(|w| {
|
||||
w.set_int(conf.div_int);
|
||||
@ -621,6 +660,13 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
||||
CLOCKS.rtc.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// rp235x specific clocks
|
||||
#[cfg(feature = "rp235x")]
|
||||
{
|
||||
// TODO hstx clock
|
||||
peris.set_hstx(false);
|
||||
}
|
||||
|
||||
// Peripheral clocks should now all be running
|
||||
reset::unreset_wait(peris);
|
||||
}
|
||||
@ -709,6 +755,7 @@ pub fn clk_adc_freq() -> u32 {
|
||||
}
|
||||
|
||||
/// RTC clock frequency.
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub fn clk_rtc_freq() -> u16 {
|
||||
CLOCKS.rtc.load(Ordering::Relaxed)
|
||||
}
|
||||
@ -855,8 +902,8 @@ pub enum GpoutSrc {
|
||||
Usb = ClkGpoutCtrlAuxsrc::CLK_USB as _,
|
||||
/// ADC.
|
||||
Adc = ClkGpoutCtrlAuxsrc::CLK_ADC as _,
|
||||
/// RTC.
|
||||
Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _,
|
||||
// RTC.
|
||||
//Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _,
|
||||
/// REF.
|
||||
Ref = ClkGpoutCtrlAuxsrc::CLK_REF as _,
|
||||
}
|
||||
@ -877,6 +924,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
|
||||
}
|
||||
|
||||
/// Set clock divider.
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub fn set_div(&self, int: u32, frac: u8) {
|
||||
let c = pac::CLOCKS;
|
||||
c.clk_gpout_div(self.gpout.number()).write(|w| {
|
||||
@ -885,6 +933,16 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Set clock divider.
|
||||
#[cfg(feature = "rp235x")]
|
||||
pub fn set_div(&self, int: u16, frac: u16) {
|
||||
let c = pac::CLOCKS;
|
||||
c.clk_gpout_div(self.gpout.number()).write(|w| {
|
||||
w.set_int(int);
|
||||
w.set_frac(frac);
|
||||
});
|
||||
}
|
||||
|
||||
/// Set clock source.
|
||||
pub fn set_src(&self, src: GpoutSrc) {
|
||||
let c = pac::CLOCKS;
|
||||
@ -924,13 +982,13 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
|
||||
ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(),
|
||||
ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(),
|
||||
ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(),
|
||||
ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq() as _,
|
||||
//ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq() as _,
|
||||
ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let div = c.clk_gpout_div(self.gpout.number()).read();
|
||||
let int = if div.int() == 0 { 65536 } else { div.int() } as u64;
|
||||
let int = if div.int() == 0 { 0xFFFF } else { div.int() } as u64;
|
||||
let frac = div.frac() as u64;
|
||||
|
||||
((base as u64 * 256) / (int * 256 + frac)) as u32
|
||||
@ -987,7 +1045,7 @@ impl rand_core::RngCore for RoscRng {
|
||||
/// and can only be exited through resets, dormant-wake GPIO interrupts,
|
||||
/// and RTC interrupts. If RTC is clocked from an internal clock source
|
||||
/// it will be stopped and not function as a wakeup source.
|
||||
#[cfg(target_arch = "arm")]
|
||||
#[cfg(all(target_arch = "arm", feature = "rp2040"))]
|
||||
pub fn dormant_sleep() {
|
||||
struct Set<T: Copy, F: Fn()>(Reg<T, RW>, T, F);
|
||||
|
||||
@ -1107,7 +1165,7 @@ pub fn dormant_sleep() {
|
||||
coma = in (reg) 0x636f6d61,
|
||||
);
|
||||
} else {
|
||||
pac::ROSC.dormant().write_value(0x636f6d61);
|
||||
pac::ROSC.dormant().write_value(rp_pac::rosc::regs::Dormant(0x636f6d61));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ pub unsafe fn read<'a, C: Channel, W: Word>(
|
||||
ch: impl Peripheral<P = C> + 'a,
|
||||
from: *const W,
|
||||
to: *mut [W],
|
||||
dreq: u8,
|
||||
dreq: vals::TreqSel,
|
||||
) -> Transfer<'a, C> {
|
||||
copy_inner(
|
||||
ch,
|
||||
@ -66,7 +66,7 @@ pub unsafe fn write<'a, C: Channel, W: Word>(
|
||||
ch: impl Peripheral<P = C> + 'a,
|
||||
from: *const [W],
|
||||
to: *mut W,
|
||||
dreq: u8,
|
||||
dreq: vals::TreqSel,
|
||||
) -> Transfer<'a, C> {
|
||||
copy_inner(
|
||||
ch,
|
||||
@ -90,7 +90,7 @@ pub unsafe fn write_repeated<'a, C: Channel, W: Word>(
|
||||
ch: impl Peripheral<P = C> + 'a,
|
||||
to: *mut W,
|
||||
len: usize,
|
||||
dreq: u8,
|
||||
dreq: vals::TreqSel,
|
||||
) -> Transfer<'a, C> {
|
||||
copy_inner(
|
||||
ch,
|
||||
@ -123,7 +123,7 @@ pub unsafe fn copy<'a, C: Channel, W: Word>(
|
||||
W::size(),
|
||||
true,
|
||||
true,
|
||||
vals::TreqSel::PERMANENT.0,
|
||||
vals::TreqSel::PERMANENT,
|
||||
)
|
||||
}
|
||||
|
||||
@ -135,7 +135,7 @@ fn copy_inner<'a, C: Channel>(
|
||||
data_size: DataSize,
|
||||
incr_read: bool,
|
||||
incr_write: bool,
|
||||
dreq: u8,
|
||||
dreq: vals::TreqSel,
|
||||
) -> Transfer<'a, C> {
|
||||
into_ref!(ch);
|
||||
|
||||
@ -143,14 +143,20 @@ fn copy_inner<'a, C: Channel>(
|
||||
|
||||
p.read_addr().write_value(from as u32);
|
||||
p.write_addr().write_value(to as u32);
|
||||
p.trans_count().write_value(len as u32);
|
||||
#[cfg(feature = "rp2040")]
|
||||
p.trans_count().write(|w| {
|
||||
*w = len as u32;
|
||||
});
|
||||
#[cfg(feature = "rp235x")]
|
||||
p.trans_count().write(|w| {
|
||||
w.set_mode(0.into());
|
||||
w.set_count(len as u32);
|
||||
});
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
p.ctrl_trig().write(|w| {
|
||||
// TODO: Add all DREQ options to pac vals::TreqSel, and use
|
||||
// `set_treq:sel`
|
||||
w.0 = ((dreq as u32) & 0x3f) << 15usize;
|
||||
w.set_treq_sel(dreq);
|
||||
w.set_data_size(data_size);
|
||||
w.set_incr_read(incr_read);
|
||||
w.set_incr_write(incr_write);
|
||||
@ -297,3 +303,11 @@ channel!(DMA_CH8, 8);
|
||||
channel!(DMA_CH9, 9);
|
||||
channel!(DMA_CH10, 10);
|
||||
channel!(DMA_CH11, 11);
|
||||
#[cfg(feature = "rp235x")]
|
||||
channel!(DMA_CH12, 12);
|
||||
#[cfg(feature = "rp235x")]
|
||||
channel!(DMA_CH13, 13);
|
||||
#[cfg(feature = "rp235x")]
|
||||
channel!(DMA_CH14, 14);
|
||||
#[cfg(feature = "rp235x")]
|
||||
channel!(DMA_CH15, 15);
|
||||
|
@ -302,7 +302,14 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> {
|
||||
// pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on
|
||||
// general XIP access.
|
||||
const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _;
|
||||
let transfer = unsafe { crate::dma::read(self.dma.as_mut().unwrap(), XIP_AUX_BASE, data, 37) };
|
||||
let transfer = unsafe {
|
||||
crate::dma::read(
|
||||
self.dma.as_mut().unwrap(),
|
||||
XIP_AUX_BASE,
|
||||
data,
|
||||
pac::dma::vals::TreqSel::XIP_STREAM,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(BackgroundRead {
|
||||
flash: PhantomData,
|
||||
@ -597,6 +604,7 @@ mod ram_helpers {
|
||||
/// addr must be aligned to 4096
|
||||
#[inline(never)]
|
||||
#[link_section = ".data.ram_func"]
|
||||
#[cfg(feature = "rp2040")]
|
||||
unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {
|
||||
/*
|
||||
Should be equivalent to:
|
||||
@ -659,6 +667,11 @@ mod ram_helpers {
|
||||
);
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[link_section = ".data.ram_func"]
|
||||
#[cfg(feature = "rp235x")]
|
||||
unsafe fn write_flash_inner(_addr: u32, _len: u32, _data: Option<&[u8]>, _ptrs: *const FlashFunctionPointers) {}
|
||||
|
||||
#[repr(C)]
|
||||
struct FlashCommand {
|
||||
cmd_addr: *const u8,
|
||||
@ -758,6 +771,7 @@ mod ram_helpers {
|
||||
/// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)
|
||||
#[inline(never)]
|
||||
#[link_section = ".data.ram_func"]
|
||||
#[cfg(feature = "rp2040")]
|
||||
unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) {
|
||||
#[cfg(target_arch = "arm")]
|
||||
core::arch::asm!(
|
||||
@ -874,6 +888,11 @@ mod ram_helpers {
|
||||
clobber_abi("C"),
|
||||
);
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[link_section = ".data.ram_func"]
|
||||
#[cfg(feature = "rp235x")]
|
||||
unsafe fn read_flash_inner(_cmd: FlashCommand, _ptrs: *const FlashFunctionPointers) {}
|
||||
}
|
||||
|
||||
/// Make sure to uphold the contract points with rp2040-flash.
|
||||
@ -887,7 +906,7 @@ pub(crate) unsafe fn in_ram(operation: impl FnOnce()) -> Result<(), Error> {
|
||||
}
|
||||
|
||||
// Make sure CORE1 is paused during the entire duration of the RAM function
|
||||
crate::multicore::pause_core1();
|
||||
//crate::multicore::pause_core1();
|
||||
|
||||
critical_section::with(|_| {
|
||||
// Wait for all DMA channels in flash to finish before ram operation
|
||||
@ -904,7 +923,7 @@ pub(crate) unsafe fn in_ram(operation: impl FnOnce()) -> Result<(), Error> {
|
||||
});
|
||||
|
||||
// Resume CORE1 execution
|
||||
crate::multicore::resume_core1();
|
||||
//crate::multicore::resume_core1();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![cfg(feature = "rp2040")]
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mod.rs
|
||||
|
||||
|
@ -178,6 +178,13 @@ impl<'d> Input<'d> {
|
||||
pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake<'_> {
|
||||
self.pin.dormant_wake(cfg)
|
||||
}
|
||||
|
||||
/// Set the pin's pad isolation
|
||||
#[cfg(feature = "rp235x")]
|
||||
#[inline]
|
||||
pub fn set_pad_isolation(&mut self, isolate: bool) {
|
||||
self.pin.set_pad_isolation(isolate)
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt trigger levels.
|
||||
@ -413,6 +420,13 @@ impl<'d> Output<'d> {
|
||||
pub fn toggle(&mut self) {
|
||||
self.pin.toggle()
|
||||
}
|
||||
|
||||
/// Set the pin's pad isolation
|
||||
#[cfg(feature = "rp235x")]
|
||||
#[inline]
|
||||
pub fn set_pad_isolation(&mut self, isolate: bool) {
|
||||
self.pin.set_pad_isolation(isolate)
|
||||
}
|
||||
}
|
||||
|
||||
/// GPIO output open-drain.
|
||||
@ -539,6 +553,13 @@ impl<'d> OutputOpenDrain<'d> {
|
||||
pub async fn wait_for_any_edge(&mut self) {
|
||||
self.pin.wait_for_any_edge().await;
|
||||
}
|
||||
|
||||
/// Set the pin's pad isolation
|
||||
#[cfg(feature = "rp235x")]
|
||||
#[inline]
|
||||
pub fn set_pad_isolation(&mut self, isolate: bool) {
|
||||
self.pin.set_pad_isolation(isolate)
|
||||
}
|
||||
}
|
||||
|
||||
/// GPIO flexible pin.
|
||||
@ -564,7 +585,15 @@ impl<'d> Flex<'d> {
|
||||
});
|
||||
|
||||
pin.gpio().ctrl().write(|w| {
|
||||
#[cfg(feature = "rp2040")]
|
||||
w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _);
|
||||
#[cfg(feature = "rp235x")]
|
||||
w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIOB_PROC_0 as _);
|
||||
});
|
||||
|
||||
#[cfg(feature = "rp235x")]
|
||||
pin.pad_ctrl().modify(|w| {
|
||||
w.set_iso(false);
|
||||
});
|
||||
|
||||
Self { pin: pin.map_into() }
|
||||
@ -760,6 +789,15 @@ impl<'d> Flex<'d> {
|
||||
cfg,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the pin's pad isolation
|
||||
#[cfg(feature = "rp235x")]
|
||||
#[inline]
|
||||
pub fn set_pad_isolation(&mut self, isolate: bool) {
|
||||
self.pin.pad_ctrl().modify(|w| {
|
||||
w.set_iso(isolate);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Drop for Flex<'d> {
|
||||
@ -956,6 +994,44 @@ impl_pin!(PIN_27, Bank::Bank0, 27);
|
||||
impl_pin!(PIN_28, Bank::Bank0, 28);
|
||||
impl_pin!(PIN_29, Bank::Bank0, 29);
|
||||
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_30, Bank::Bank0, 30);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_31, Bank::Bank0, 31);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_32, Bank::Bank0, 32);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_33, Bank::Bank0, 33);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_34, Bank::Bank0, 34);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_35, Bank::Bank0, 35);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_36, Bank::Bank0, 36);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_37, Bank::Bank0, 37);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_38, Bank::Bank0, 38);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_39, Bank::Bank0, 39);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_40, Bank::Bank0, 40);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_41, Bank::Bank0, 41);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_42, Bank::Bank0, 42);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_43, Bank::Bank0, 43);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_44, Bank::Bank0, 44);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_45, Bank::Bank0, 45);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_46, Bank::Bank0, 46);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_47, Bank::Bank0, 47);
|
||||
|
||||
// TODO rp235x bank1 as gpio support
|
||||
#[cfg(feature = "qspi-as-gpio")]
|
||||
impl_pin!(PIN_QSPI_SCLK, Bank::Qspi, 0);
|
||||
#[cfg(feature = "qspi-as-gpio")]
|
||||
|
@ -884,3 +884,39 @@ impl_pin!(PIN_26, I2C1, SdaPin);
|
||||
impl_pin!(PIN_27, I2C1, SclPin);
|
||||
impl_pin!(PIN_28, I2C0, SdaPin);
|
||||
impl_pin!(PIN_29, I2C0, SclPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_30, I2C1, SdaPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_31, I2C1, SclPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_32, I2C0, SdaPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_33, I2C0, SclPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_34, I2C1, SdaPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_35, I2C1, SclPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_36, I2C0, SdaPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_37, I2C0, SclPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_38, I2C1, SdaPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_39, I2C1, SclPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_40, I2C0, SdaPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_41, I2C0, SclPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_42, I2C1, SdaPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_43, I2C1, SclPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_44, I2C0, SdaPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_45, I2C0, SclPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_46, I2C1, SdaPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_47, I2C1, SclPin);
|
||||
|
@ -15,6 +15,11 @@ mod critical_section_impl;
|
||||
mod intrinsics;
|
||||
|
||||
pub mod adc;
|
||||
#[cfg(feature = "rp235x")]
|
||||
pub mod binary_info;
|
||||
#[cfg(feature = "rp235x")]
|
||||
pub mod block;
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub mod bootsel;
|
||||
pub mod clocks;
|
||||
pub mod dma;
|
||||
@ -41,14 +46,20 @@ pub(crate) mod relocate;
|
||||
|
||||
// Reexports
|
||||
pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
|
||||
#[cfg(feature = "unstable-pac")]
|
||||
#[cfg(all(feature = "unstable-pac", feature = "rp235x"))]
|
||||
pub use rp23_pac as pac;
|
||||
#[cfg(all(feature = "unstable-pac", feature = "rp2040"))]
|
||||
pub use rp_pac as pac;
|
||||
#[cfg(not(feature = "unstable-pac"))]
|
||||
|
||||
#[cfg(all(not(feature = "unstable-pac"), feature = "rp235x"))]
|
||||
pub(crate) use rp23_pac as pac;
|
||||
#[cfg(all(not(feature = "unstable-pac"), feature = "rp2040"))]
|
||||
pub(crate) use rp_pac as pac;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
pub use crate::pac::NVIC_PRIO_BITS;
|
||||
|
||||
#[cfg(feature = "rp2040")]
|
||||
embassy_hal_internal::interrupt_mod!(
|
||||
TIMER_IRQ_0,
|
||||
TIMER_IRQ_1,
|
||||
@ -84,6 +95,54 @@ embassy_hal_internal::interrupt_mod!(
|
||||
SWI_IRQ_5,
|
||||
);
|
||||
|
||||
#[cfg(feature = "rp235x")]
|
||||
embassy_hal_internal::interrupt_mod!(
|
||||
TIMER0_IRQ_0,
|
||||
TIMER0_IRQ_1,
|
||||
TIMER0_IRQ_2,
|
||||
TIMER0_IRQ_3,
|
||||
TIMER1_IRQ_0,
|
||||
TIMER1_IRQ_1,
|
||||
TIMER1_IRQ_2,
|
||||
TIMER1_IRQ_3,
|
||||
PWM_IRQ_WRAP_0,
|
||||
PWM_IRQ_WRAP_1,
|
||||
DMA_IRQ_0,
|
||||
DMA_IRQ_1,
|
||||
USBCTRL_IRQ,
|
||||
PIO0_IRQ_0,
|
||||
PIO0_IRQ_1,
|
||||
PIO1_IRQ_0,
|
||||
PIO1_IRQ_1,
|
||||
PIO2_IRQ_0,
|
||||
PIO2_IRQ_1,
|
||||
IO_IRQ_BANK0,
|
||||
IO_IRQ_BANK0_NS,
|
||||
IO_IRQ_QSPI,
|
||||
IO_IRQ_QSPI_NS,
|
||||
SIO_IRQ_FIFO,
|
||||
SIO_IRQ_BELL,
|
||||
SIO_IRQ_FIFO_NS,
|
||||
SIO_IRQ_BELL_NS,
|
||||
CLOCKS_IRQ,
|
||||
SPI0_IRQ,
|
||||
SPI1_IRQ,
|
||||
UART0_IRQ,
|
||||
UART1_IRQ,
|
||||
ADC_IRQ_FIFO,
|
||||
I2C0_IRQ,
|
||||
I2C1_IRQ,
|
||||
TRNG_IRQ,
|
||||
PLL_SYS_IRQ,
|
||||
PLL_USB_IRQ,
|
||||
SWI_IRQ_0,
|
||||
SWI_IRQ_1,
|
||||
SWI_IRQ_2,
|
||||
SWI_IRQ_3,
|
||||
SWI_IRQ_4,
|
||||
SWI_IRQ_5,
|
||||
);
|
||||
|
||||
/// Macro to bind interrupts to handlers.
|
||||
///
|
||||
/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
|
||||
@ -123,6 +182,7 @@ macro_rules! bind_interrupts {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "rp2040")]
|
||||
embassy_hal_internal::peripherals! {
|
||||
PIN_0,
|
||||
PIN_1,
|
||||
@ -210,6 +270,139 @@ embassy_hal_internal::peripherals! {
|
||||
BOOTSEL,
|
||||
}
|
||||
|
||||
#[cfg(feature = "rp235x")]
|
||||
embassy_hal_internal::peripherals! {
|
||||
PIN_0,
|
||||
PIN_1,
|
||||
PIN_2,
|
||||
PIN_3,
|
||||
PIN_4,
|
||||
PIN_5,
|
||||
PIN_6,
|
||||
PIN_7,
|
||||
PIN_8,
|
||||
PIN_9,
|
||||
PIN_10,
|
||||
PIN_11,
|
||||
PIN_12,
|
||||
PIN_13,
|
||||
PIN_14,
|
||||
PIN_15,
|
||||
PIN_16,
|
||||
PIN_17,
|
||||
PIN_18,
|
||||
PIN_19,
|
||||
PIN_20,
|
||||
PIN_21,
|
||||
PIN_22,
|
||||
PIN_23,
|
||||
PIN_24,
|
||||
PIN_25,
|
||||
PIN_26,
|
||||
PIN_27,
|
||||
PIN_28,
|
||||
PIN_29,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_30,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_31,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_32,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_33,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_34,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_35,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_36,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_37,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_38,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_39,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_40,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_41,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_42,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_43,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_44,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_45,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_46,
|
||||
#[cfg(feature = "rp235xb")]
|
||||
PIN_47,
|
||||
PIN_QSPI_SCLK,
|
||||
PIN_QSPI_SS,
|
||||
PIN_QSPI_SD0,
|
||||
PIN_QSPI_SD1,
|
||||
PIN_QSPI_SD2,
|
||||
PIN_QSPI_SD3,
|
||||
|
||||
UART0,
|
||||
UART1,
|
||||
|
||||
SPI0,
|
||||
SPI1,
|
||||
|
||||
I2C0,
|
||||
I2C1,
|
||||
|
||||
DMA_CH0,
|
||||
DMA_CH1,
|
||||
DMA_CH2,
|
||||
DMA_CH3,
|
||||
DMA_CH4,
|
||||
DMA_CH5,
|
||||
DMA_CH6,
|
||||
DMA_CH7,
|
||||
DMA_CH8,
|
||||
DMA_CH9,
|
||||
DMA_CH10,
|
||||
DMA_CH11,
|
||||
DMA_CH12,
|
||||
DMA_CH13,
|
||||
DMA_CH14,
|
||||
DMA_CH15,
|
||||
|
||||
PWM_SLICE0,
|
||||
PWM_SLICE1,
|
||||
PWM_SLICE2,
|
||||
PWM_SLICE3,
|
||||
PWM_SLICE4,
|
||||
PWM_SLICE5,
|
||||
PWM_SLICE6,
|
||||
PWM_SLICE7,
|
||||
PWM_SLICE8,
|
||||
PWM_SLICE9,
|
||||
PWM_SLICE10,
|
||||
PWM_SLICE11,
|
||||
|
||||
USB,
|
||||
|
||||
RTC,
|
||||
|
||||
FLASH,
|
||||
|
||||
ADC,
|
||||
ADC_TEMP_SENSOR,
|
||||
|
||||
CORE1,
|
||||
|
||||
PIO0,
|
||||
PIO1,
|
||||
PIO2,
|
||||
|
||||
WATCHDOG,
|
||||
BOOTSEL,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "boot2-none"))]
|
||||
macro_rules! select_bootloader {
|
||||
( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => {
|
||||
@ -279,6 +472,7 @@ pub fn install_core0_stack_guard() -> Result<(), ()> {
|
||||
unsafe { install_stack_guard(core::ptr::addr_of_mut!(_stack_end)) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "rp2040")]
|
||||
#[inline(always)]
|
||||
fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
|
||||
let core = unsafe { cortex_m::Peripherals::steal() };
|
||||
@ -306,6 +500,24 @@ fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rp235x")]
|
||||
#[inline(always)]
|
||||
fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
|
||||
let core = unsafe { cortex_m::Peripherals::steal() };
|
||||
|
||||
// Fail if MPU is already configured
|
||||
if core.MPU.ctrl.read() != 0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
core.MPU.ctrl.write(5); // enable mpu with background default map
|
||||
core.MPU.rbar.write(stack_bottom as u32 & !0xff); // set address
|
||||
core.MPU.rlar.write(1); // enable region
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// HAL configuration for RP.
|
||||
pub mod config {
|
||||
use crate::clocks::ClockConfig;
|
||||
@ -354,7 +566,7 @@ pub fn init(config: config::Config) -> Peripherals {
|
||||
peripherals
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[cfg(all(feature = "rt", feature = "rp2040"))]
|
||||
#[cortex_m_rt::pre_init]
|
||||
unsafe fn pre_init() {
|
||||
// SIO does not get reset when core0 is reset with either `scb::sys_reset()` or with SWD.
|
||||
|
@ -84,7 +84,7 @@ impl<const SIZE: usize> Stack<SIZE> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[cfg(all(feature = "rt", feature = "rp2040"))]
|
||||
#[interrupt]
|
||||
#[link_section = ".data.ram_func"]
|
||||
unsafe fn SIO_IRQ_PROC1() {
|
||||
@ -109,6 +109,31 @@ unsafe fn SIO_IRQ_PROC1() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "rp235x"))]
|
||||
#[interrupt]
|
||||
#[link_section = ".data.ram_func"]
|
||||
unsafe fn SIO_IRQ_FIFO() {
|
||||
let sio = pac::SIO;
|
||||
// Clear IRQ
|
||||
sio.fifo().st().write(|w| w.set_wof(false));
|
||||
|
||||
while sio.fifo().st().read().vld() {
|
||||
// Pause CORE1 execution and disable interrupts
|
||||
if fifo_read_wfe() == PAUSE_TOKEN {
|
||||
cortex_m::interrupt::disable();
|
||||
// Signal to CORE0 that execution is paused
|
||||
fifo_write(PAUSE_TOKEN);
|
||||
// Wait for `resume` signal from CORE0
|
||||
while fifo_read_wfe() != RESUME_TOKEN {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
cortex_m::interrupt::enable();
|
||||
// Signal to CORE0 that execution is resumed
|
||||
fifo_write(RESUME_TOKEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a function on this core
|
||||
pub fn spawn_core1<F, const SIZE: usize>(_core1: CORE1, stack: &'static mut Stack<SIZE>, entry: F)
|
||||
where
|
||||
@ -135,7 +160,14 @@ where
|
||||
|
||||
IS_CORE1_INIT.store(true, Ordering::Release);
|
||||
// Enable fifo interrupt on CORE1 for `pause` functionality.
|
||||
unsafe { interrupt::SIO_IRQ_PROC1.enable() };
|
||||
#[cfg(feature = "rp2040")]
|
||||
unsafe {
|
||||
interrupt::SIO_IRQ_PROC1.enable()
|
||||
};
|
||||
#[cfg(feature = "rp235x")]
|
||||
unsafe {
|
||||
interrupt::SIO_IRQ_FIFO.enable()
|
||||
};
|
||||
|
||||
entry()
|
||||
}
|
||||
|
@ -10,14 +10,11 @@ use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use fixed::types::extra::U8;
|
||||
use fixed::FixedU32;
|
||||
use pac::io::vals::Gpio0ctrlFuncsel;
|
||||
use pac::pio::vals::SmExecctrlStatusSel;
|
||||
use pio::{Program, SideSet, Wrap};
|
||||
|
||||
use crate::dma::{Channel, Transfer, Word};
|
||||
use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate};
|
||||
use crate::interrupt::typelevel::{Binding, Handler, Interrupt};
|
||||
use crate::pac::dma::vals::TreqSel;
|
||||
use crate::relocate::RelocatedProgram;
|
||||
use crate::{pac, peripherals, RegExt};
|
||||
|
||||
@ -355,11 +352,14 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
|
||||
let p = ch.regs();
|
||||
p.write_addr().write_value(data.as_ptr() as u32);
|
||||
p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32);
|
||||
p.trans_count().write_value(data.len() as u32);
|
||||
#[cfg(feature = "rp2040")]
|
||||
p.trans_count().write(|w| *w = data.len() as u32);
|
||||
#[cfg(feature = "rp235x")]
|
||||
p.trans_count().write(|w| w.set_count(data.len() as u32));
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
p.ctrl_trig().write(|w| {
|
||||
// Set RX DREQ for this statemachine
|
||||
w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8 + 4));
|
||||
w.set_treq_sel(crate::pac::dma::vals::TreqSel::from(pio_no * 8 + SM as u8 + 4));
|
||||
w.set_data_size(W::size());
|
||||
w.set_chain_to(ch.number());
|
||||
w.set_incr_read(false);
|
||||
@ -437,11 +437,14 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
|
||||
let p = ch.regs();
|
||||
p.read_addr().write_value(data.as_ptr() as u32);
|
||||
p.write_addr().write_value(PIO::PIO.txf(SM).as_ptr() as u32);
|
||||
p.trans_count().write_value(data.len() as u32);
|
||||
#[cfg(feature = "rp2040")]
|
||||
p.trans_count().write(|w| *w = data.len() as u32);
|
||||
#[cfg(feature = "rp235x")]
|
||||
p.trans_count().write(|w| w.set_count(data.len() as u32));
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
p.ctrl_trig().write(|w| {
|
||||
// Set TX DREQ for this statemachine
|
||||
w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8));
|
||||
w.set_treq_sel(crate::pac::dma::vals::TreqSel::from(pio_no * 8 + SM as u8));
|
||||
w.set_data_size(W::size());
|
||||
w.set_chain_to(ch.number());
|
||||
w.set_incr_read(true);
|
||||
@ -523,6 +526,39 @@ pub struct PinConfig {
|
||||
pub out_base: u8,
|
||||
}
|
||||
|
||||
/// Comparison level or IRQ index for the MOV x, STATUS instruction.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[cfg(feature = "rp235x")]
|
||||
pub enum StatusN {
|
||||
/// IRQ flag in this PIO block
|
||||
This(u8),
|
||||
/// IRQ flag in the next lower PIO block
|
||||
Lower(u8),
|
||||
/// IRQ flag in the next higher PIO block
|
||||
Higher(u8),
|
||||
}
|
||||
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl Default for StatusN {
|
||||
fn default() -> Self {
|
||||
Self::This(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl Into<crate::pac::pio::vals::ExecctrlStatusN> for StatusN {
|
||||
fn into(self) -> crate::pac::pio::vals::ExecctrlStatusN {
|
||||
let x = match self {
|
||||
StatusN::This(n) => n,
|
||||
StatusN::Lower(n) => n + 0x08,
|
||||
StatusN::Higher(n) => n + 0x10,
|
||||
};
|
||||
|
||||
crate::pac::pio::vals::ExecctrlStatusN(x)
|
||||
}
|
||||
}
|
||||
|
||||
/// PIO config.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Config<'d, PIO: Instance> {
|
||||
@ -537,7 +573,12 @@ pub struct Config<'d, PIO: Instance> {
|
||||
/// Which source to use for checking status.
|
||||
pub status_sel: StatusSource,
|
||||
/// Status comparison level.
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub status_n: u8,
|
||||
// This cfg probably shouldn't be required, but the SVD for the 2040 doesn't have the enum
|
||||
#[cfg(feature = "rp235x")]
|
||||
/// Status comparison level.
|
||||
pub status_n: StatusN,
|
||||
exec: ExecConfig,
|
||||
origin: Option<u8>,
|
||||
/// Configure FIFO allocation.
|
||||
@ -653,7 +694,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
|
||||
assert!(config.clock_divider <= 65536, "clkdiv must be <= 65536");
|
||||
assert!(config.clock_divider >= 1, "clkdiv must be >= 1");
|
||||
assert!(config.out_en_sel < 32, "out_en_sel must be < 32");
|
||||
assert!(config.status_n < 32, "status_n must be < 32");
|
||||
//assert!(config.status_n < 32, "status_n must be < 32");
|
||||
// sm expects 0 for 32, truncation makes that happen
|
||||
assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32");
|
||||
assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32");
|
||||
@ -668,11 +709,17 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
|
||||
w.set_out_sticky(config.out_sticky);
|
||||
w.set_wrap_top(config.exec.wrap_top);
|
||||
w.set_wrap_bottom(config.exec.wrap_bottom);
|
||||
#[cfg(feature = "rp235x")]
|
||||
w.set_status_sel(match config.status_sel {
|
||||
StatusSource::TxFifoLevel => SmExecctrlStatusSel::TXLEVEL,
|
||||
StatusSource::RxFifoLevel => SmExecctrlStatusSel::RXLEVEL,
|
||||
StatusSource::TxFifoLevel => pac::pio::vals::ExecctrlStatusSel::TXLEVEL,
|
||||
StatusSource::RxFifoLevel => pac::pio::vals::ExecctrlStatusSel::RXLEVEL,
|
||||
});
|
||||
w.set_status_n(config.status_n);
|
||||
#[cfg(feature = "rp2040")]
|
||||
w.set_status_sel(match config.status_sel {
|
||||
StatusSource::TxFifoLevel => pac::pio::vals::SmExecctrlStatusSel::TXLEVEL,
|
||||
StatusSource::RxFifoLevel => pac::pio::vals::SmExecctrlStatusSel::RXLEVEL,
|
||||
});
|
||||
w.set_status_n(config.status_n.into());
|
||||
});
|
||||
sm.shiftctrl().write(|w| {
|
||||
w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly);
|
||||
@ -1147,7 +1194,7 @@ fn on_pio_drop<PIO: Instance>() {
|
||||
let state = PIO::state();
|
||||
if state.users.fetch_sub(1, Ordering::AcqRel) == 1 {
|
||||
let used_pins = state.used_pins.load(Ordering::Relaxed);
|
||||
let null = Gpio0ctrlFuncsel::NULL as _;
|
||||
let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _;
|
||||
// we only have 30 pins. don't test the other two since gpio() asserts.
|
||||
for i in 0..30 {
|
||||
if used_pins & (1 << i) != 0 {
|
||||
@ -1203,6 +1250,8 @@ macro_rules! impl_pio {
|
||||
|
||||
impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0);
|
||||
impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pio!(PIO2, 2, PIO2, PIO2_0, PIO2_IRQ_0);
|
||||
|
||||
/// PIO pin.
|
||||
pub trait PioPin: gpio::Pin {}
|
||||
@ -1247,3 +1296,25 @@ impl_pio_pin! {
|
||||
PIN_28,
|
||||
PIN_29,
|
||||
}
|
||||
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pio_pin! {
|
||||
PIN_30,
|
||||
PIN_31,
|
||||
PIN_32,
|
||||
PIN_33,
|
||||
PIN_34,
|
||||
PIN_35,
|
||||
PIN_36,
|
||||
PIN_37,
|
||||
PIN_38,
|
||||
PIN_39,
|
||||
PIN_40,
|
||||
PIN_41,
|
||||
PIN_42,
|
||||
PIN_43,
|
||||
PIN_44,
|
||||
PIN_45,
|
||||
PIN_46,
|
||||
PIN_47,
|
||||
}
|
||||
|
@ -363,6 +363,15 @@ slice!(PWM_SLICE5, 5);
|
||||
slice!(PWM_SLICE6, 6);
|
||||
slice!(PWM_SLICE7, 7);
|
||||
|
||||
#[cfg(feature = "rp235x")]
|
||||
slice!(PWM_SLICE8, 8);
|
||||
#[cfg(feature = "rp235x")]
|
||||
slice!(PWM_SLICE9, 9);
|
||||
#[cfg(feature = "rp235x")]
|
||||
slice!(PWM_SLICE10, 10);
|
||||
#[cfg(feature = "rp235x")]
|
||||
slice!(PWM_SLICE11, 11);
|
||||
|
||||
/// PWM Channel A.
|
||||
pub trait ChannelAPin<T: Slice>: GpioPin {}
|
||||
/// PWM Channel B.
|
||||
@ -404,3 +413,39 @@ impl_pin!(PIN_26, PWM_SLICE5, ChannelAPin);
|
||||
impl_pin!(PIN_27, PWM_SLICE5, ChannelBPin);
|
||||
impl_pin!(PIN_28, PWM_SLICE6, ChannelAPin);
|
||||
impl_pin!(PIN_29, PWM_SLICE6, ChannelBPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_30, PWM_SLICE7, ChannelAPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_31, PWM_SLICE7, ChannelBPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_32, PWM_SLICE8, ChannelAPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_33, PWM_SLICE8, ChannelBPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_34, PWM_SLICE9, ChannelAPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_35, PWM_SLICE9, ChannelBPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_36, PWM_SLICE10, ChannelAPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_37, PWM_SLICE10, ChannelBPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_38, PWM_SLICE11, ChannelAPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_39, PWM_SLICE11, ChannelBPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_40, PWM_SLICE8, ChannelAPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_41, PWM_SLICE8, ChannelBPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_42, PWM_SLICE9, ChannelAPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_43, PWM_SLICE9, ChannelBPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_44, PWM_SLICE10, ChannelAPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_45, PWM_SLICE10, ChannelBPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_46, PWM_SLICE11, ChannelAPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_47, PWM_SLICE11, ChannelBPin);
|
||||
|
@ -2,7 +2,7 @@ pub use pac::resets::regs::Peripherals;
|
||||
|
||||
use crate::pac;
|
||||
|
||||
pub const ALL_PERIPHERALS: Peripherals = Peripherals(0x01ffffff);
|
||||
pub const ALL_PERIPHERALS: Peripherals = Peripherals(0x01ff_ffff);
|
||||
|
||||
pub(crate) fn reset(peris: Peripherals) {
|
||||
pac::RESETS.reset().write_value(peris);
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![cfg(feature = "rp2040")]
|
||||
//! RTC driver.
|
||||
mod filter;
|
||||
|
||||
|
@ -106,15 +106,55 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> {
|
||||
|
||||
if let Some(pin) = &clk {
|
||||
pin.gpio().ctrl().write(|w| w.set_funcsel(1));
|
||||
pin.pad_ctrl().write(|w| {
|
||||
#[cfg(feature = "rp235x")]
|
||||
w.set_iso(false);
|
||||
w.set_schmitt(true);
|
||||
w.set_slewfast(false);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(false);
|
||||
w.set_pde(false);
|
||||
});
|
||||
}
|
||||
if let Some(pin) = &mosi {
|
||||
pin.gpio().ctrl().write(|w| w.set_funcsel(1));
|
||||
pin.pad_ctrl().write(|w| {
|
||||
#[cfg(feature = "rp235x")]
|
||||
w.set_iso(false);
|
||||
w.set_schmitt(true);
|
||||
w.set_slewfast(false);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(false);
|
||||
w.set_pde(false);
|
||||
});
|
||||
}
|
||||
if let Some(pin) = &miso {
|
||||
pin.gpio().ctrl().write(|w| w.set_funcsel(1));
|
||||
pin.pad_ctrl().write(|w| {
|
||||
#[cfg(feature = "rp235x")]
|
||||
w.set_iso(false);
|
||||
w.set_schmitt(true);
|
||||
w.set_slewfast(false);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(false);
|
||||
w.set_pde(false);
|
||||
});
|
||||
}
|
||||
if let Some(pin) = &cs {
|
||||
pin.gpio().ctrl().write(|w| w.set_funcsel(1));
|
||||
pin.pad_ctrl().write(|w| {
|
||||
#[cfg(feature = "rp235x")]
|
||||
w.set_iso(false);
|
||||
w.set_schmitt(true);
|
||||
w.set_slewfast(false);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(false);
|
||||
w.set_pde(false);
|
||||
});
|
||||
}
|
||||
Self {
|
||||
inner,
|
||||
@ -442,8 +482,8 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
|
||||
trait SealedMode {}
|
||||
|
||||
trait SealedInstance {
|
||||
const TX_DREQ: u8;
|
||||
const RX_DREQ: u8;
|
||||
const TX_DREQ: pac::dma::vals::TreqSel;
|
||||
const RX_DREQ: pac::dma::vals::TreqSel;
|
||||
|
||||
fn regs(&self) -> pac::spi::Spi;
|
||||
}
|
||||
@ -459,8 +499,8 @@ pub trait Instance: SealedInstance {}
|
||||
macro_rules! impl_instance {
|
||||
($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => {
|
||||
impl SealedInstance for peripherals::$type {
|
||||
const TX_DREQ: u8 = $tx_dreq;
|
||||
const RX_DREQ: u8 = $rx_dreq;
|
||||
const TX_DREQ: pac::dma::vals::TreqSel = $tx_dreq;
|
||||
const RX_DREQ: pac::dma::vals::TreqSel = $rx_dreq;
|
||||
|
||||
fn regs(&self) -> pac::spi::Spi {
|
||||
pac::$type
|
||||
@ -470,8 +510,18 @@ macro_rules! impl_instance {
|
||||
};
|
||||
}
|
||||
|
||||
impl_instance!(SPI0, Spi0, 16, 17);
|
||||
impl_instance!(SPI1, Spi1, 18, 19);
|
||||
impl_instance!(
|
||||
SPI0,
|
||||
Spi0,
|
||||
pac::dma::vals::TreqSel::SPI0_TX,
|
||||
pac::dma::vals::TreqSel::SPI0_RX
|
||||
);
|
||||
impl_instance!(
|
||||
SPI1,
|
||||
Spi1,
|
||||
pac::dma::vals::TreqSel::SPI1_TX,
|
||||
pac::dma::vals::TreqSel::SPI1_RX
|
||||
);
|
||||
|
||||
/// CLK pin.
|
||||
pub trait ClkPin<T: Instance>: GpioPin {}
|
||||
@ -518,6 +568,42 @@ impl_pin!(PIN_26, SPI1, ClkPin);
|
||||
impl_pin!(PIN_27, SPI1, MosiPin);
|
||||
impl_pin!(PIN_28, SPI1, MisoPin);
|
||||
impl_pin!(PIN_29, SPI1, CsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_30, SPI1, ClkPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_31, SPI1, MosiPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_32, SPI0, MisoPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_33, SPI0, CsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_34, SPI0, ClkPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_35, SPI0, MosiPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_36, SPI0, MisoPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_37, SPI0, CsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_38, SPI0, ClkPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_39, SPI0, MosiPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_40, SPI1, MisoPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_41, SPI1, CsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_42, SPI1, ClkPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_43, SPI1, MosiPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_44, SPI1, MisoPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_45, SPI1, CsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_46, SPI1, ClkPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_47, SPI1, MosiPin);
|
||||
|
||||
macro_rules! impl_mode {
|
||||
($name:ident) => {
|
||||
|
@ -10,6 +10,11 @@ use embassy_time_driver::{AlarmHandle, Driver};
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::{interrupt, pac};
|
||||
|
||||
#[cfg(feature = "rp2040")]
|
||||
use pac::TIMER;
|
||||
#[cfg(feature = "rp235x")]
|
||||
use pac::TIMER0 as TIMER;
|
||||
|
||||
struct AlarmState {
|
||||
timestamp: Cell<u64>,
|
||||
callback: Cell<Option<(fn(*mut ()), *mut ())>>,
|
||||
@ -35,9 +40,9 @@ embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{
|
||||
impl Driver for TimerDriver {
|
||||
fn now(&self) -> u64 {
|
||||
loop {
|
||||
let hi = pac::TIMER.timerawh().read();
|
||||
let lo = pac::TIMER.timerawl().read();
|
||||
let hi2 = pac::TIMER.timerawh().read();
|
||||
let hi = TIMER.timerawh().read();
|
||||
let lo = TIMER.timerawl().read();
|
||||
let hi2 = TIMER.timerawh().read();
|
||||
if hi == hi2 {
|
||||
return (hi as u64) << 32 | (lo as u64);
|
||||
}
|
||||
@ -77,13 +82,13 @@ impl Driver for TimerDriver {
|
||||
// Note that we're not checking the high bits at all. This means the irq may fire early
|
||||
// if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire
|
||||
// it is checked if the alarm time has passed.
|
||||
pac::TIMER.alarm(n).write_value(timestamp as u32);
|
||||
TIMER.alarm(n).write_value(timestamp as u32);
|
||||
|
||||
let now = self.now();
|
||||
if timestamp <= now {
|
||||
// If alarm timestamp has passed the alarm will not fire.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
pac::TIMER.armed().write(|w| w.set_armed(1 << n));
|
||||
TIMER.armed().write(|w| w.set_armed(1 << n));
|
||||
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
|
||||
@ -105,17 +110,17 @@ impl TimerDriver {
|
||||
} else {
|
||||
// Not elapsed, arm it again.
|
||||
// This can happen if it was set more than 2^32 us in the future.
|
||||
pac::TIMER.alarm(n).write_value(timestamp as u32);
|
||||
TIMER.alarm(n).write_value(timestamp as u32);
|
||||
}
|
||||
});
|
||||
|
||||
// clear the irq
|
||||
pac::TIMER.intr().write(|w| w.set_alarm(n, true));
|
||||
TIMER.intr().write(|w| w.set_alarm(n, true));
|
||||
}
|
||||
|
||||
fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
|
||||
// disarm
|
||||
pac::TIMER.armed().write(|w| w.set_armed(1 << n));
|
||||
TIMER.armed().write(|w| w.set_armed(1 << n));
|
||||
|
||||
let alarm = &self.alarms.borrow(cs)[n];
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
@ -138,38 +143,72 @@ pub unsafe fn init() {
|
||||
});
|
||||
|
||||
// enable all irqs
|
||||
pac::TIMER.inte().write(|w| {
|
||||
TIMER.inte().write(|w| {
|
||||
w.set_alarm(0, true);
|
||||
w.set_alarm(1, true);
|
||||
w.set_alarm(2, true);
|
||||
w.set_alarm(3, true);
|
||||
});
|
||||
interrupt::TIMER_IRQ_0.enable();
|
||||
interrupt::TIMER_IRQ_1.enable();
|
||||
interrupt::TIMER_IRQ_2.enable();
|
||||
interrupt::TIMER_IRQ_3.enable();
|
||||
#[cfg(feature = "rp2040")]
|
||||
{
|
||||
interrupt::TIMER_IRQ_0.enable();
|
||||
interrupt::TIMER_IRQ_1.enable();
|
||||
interrupt::TIMER_IRQ_2.enable();
|
||||
interrupt::TIMER_IRQ_3.enable();
|
||||
}
|
||||
#[cfg(feature = "rp235x")]
|
||||
{
|
||||
interrupt::TIMER0_IRQ_0.enable();
|
||||
interrupt::TIMER0_IRQ_1.enable();
|
||||
interrupt::TIMER0_IRQ_2.enable();
|
||||
interrupt::TIMER0_IRQ_3.enable();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[cfg(all(feature = "rt", feature = "rp2040"))]
|
||||
#[interrupt]
|
||||
fn TIMER_IRQ_0() {
|
||||
DRIVER.check_alarm(0)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[cfg(all(feature = "rt", feature = "rp2040"))]
|
||||
#[interrupt]
|
||||
fn TIMER_IRQ_1() {
|
||||
DRIVER.check_alarm(1)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[cfg(all(feature = "rt", feature = "rp2040"))]
|
||||
#[interrupt]
|
||||
fn TIMER_IRQ_2() {
|
||||
DRIVER.check_alarm(2)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[cfg(all(feature = "rt", feature = "rp2040"))]
|
||||
#[interrupt]
|
||||
fn TIMER_IRQ_3() {
|
||||
DRIVER.check_alarm(3)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "rp235x"))]
|
||||
#[interrupt]
|
||||
fn TIMER0_IRQ_0() {
|
||||
DRIVER.check_alarm(0)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "rp235x"))]
|
||||
#[interrupt]
|
||||
fn TIMER0_IRQ_1() {
|
||||
DRIVER.check_alarm(1)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "rp235x"))]
|
||||
#[interrupt]
|
||||
fn TIMER0_IRQ_2() {
|
||||
DRIVER.check_alarm(2)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "rp235x"))]
|
||||
#[interrupt]
|
||||
fn TIMER0_IRQ_3() {
|
||||
DRIVER.check_alarm(3)
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> {
|
||||
});
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// is held across an await and makes the future non-Send.
|
||||
crate::dma::write(ch, buffer, T::regs().uartdr().as_ptr() as *mut _, T::TX_DREQ)
|
||||
crate::dma::write(ch, buffer, T::regs().uartdr().as_ptr() as *mut _, T::TX_DREQ.into())
|
||||
};
|
||||
transfer.await;
|
||||
Ok(())
|
||||
@ -422,7 +422,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
||||
let transfer = unsafe {
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// is held across an await and makes the future non-Send.
|
||||
crate::dma::read(ch, T::regs().uartdr().as_ptr() as *const _, buffer, T::RX_DREQ)
|
||||
crate::dma::read(ch, T::regs().uartdr().as_ptr() as *const _, buffer, T::RX_DREQ.into())
|
||||
};
|
||||
|
||||
// wait for either the transfer to complete or an error to happen.
|
||||
@ -571,7 +571,12 @@ impl<'d, T: Instance> UartRx<'d, T, Async> {
|
||||
let transfer = unsafe {
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// is held across an await and makes the future non-Send.
|
||||
crate::dma::read(&mut ch, T::regs().uartdr().as_ptr() as *const _, sbuffer, T::RX_DREQ)
|
||||
crate::dma::read(
|
||||
&mut ch,
|
||||
T::regs().uartdr().as_ptr() as *const _,
|
||||
sbuffer,
|
||||
T::RX_DREQ.into(),
|
||||
)
|
||||
};
|
||||
|
||||
// wait for either the transfer to complete or an error to happen.
|
||||
@ -830,8 +835,16 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
|
||||
) {
|
||||
let r = T::regs();
|
||||
if let Some(pin) = &tx {
|
||||
let funcsel = {
|
||||
let pin_number = ((pin.gpio().as_ptr() as u32) & 0x1FF) / 8;
|
||||
if (pin_number % 4) == 0 {
|
||||
2
|
||||
} else {
|
||||
11
|
||||
}
|
||||
};
|
||||
pin.gpio().ctrl().write(|w| {
|
||||
w.set_funcsel(2);
|
||||
w.set_funcsel(funcsel);
|
||||
w.set_outover(if config.invert_tx {
|
||||
Outover::INVERT
|
||||
} else {
|
||||
@ -841,8 +854,16 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
|
||||
pin.pad_ctrl().write(|w| w.set_ie(true));
|
||||
}
|
||||
if let Some(pin) = &rx {
|
||||
let funcsel = {
|
||||
let pin_number = ((pin.gpio().as_ptr() as u32) & 0x1FF) / 8;
|
||||
if ((pin_number - 1) % 4) == 0 {
|
||||
2
|
||||
} else {
|
||||
11
|
||||
}
|
||||
};
|
||||
pin.gpio().ctrl().write(|w| {
|
||||
w.set_funcsel(2);
|
||||
w.set_funcsel(funcsel);
|
||||
w.set_inover(if config.invert_rx {
|
||||
Inover::INVERT
|
||||
} else {
|
||||
@ -904,7 +925,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> {
|
||||
});
|
||||
}
|
||||
|
||||
fn lcr_modify<R>(f: impl FnOnce(&mut rp_pac::uart::regs::UartlcrH) -> R) -> R {
|
||||
fn lcr_modify<R>(f: impl FnOnce(&mut crate::pac::uart::regs::UartlcrH) -> R) -> R {
|
||||
let r = T::regs();
|
||||
|
||||
// Notes from PL011 reference manual:
|
||||
@ -1332,3 +1353,92 @@ impl_pin!(PIN_26, UART1, CtsPin);
|
||||
impl_pin!(PIN_27, UART1, RtsPin);
|
||||
impl_pin!(PIN_28, UART0, TxPin);
|
||||
impl_pin!(PIN_29, UART0, RxPin);
|
||||
|
||||
// Additional functions added by all 2350s
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_2, UART0, TxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_3, UART0, RxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_6, UART1, TxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_7, UART1, RxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_10, UART1, TxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_11, UART1, RxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_14, UART0, TxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_15, UART0, RxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_18, UART0, TxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_19, UART0, RxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_22, UART1, TxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_23, UART1, RxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_26, UART1, TxPin);
|
||||
#[cfg(feature = "rp235x")]
|
||||
impl_pin!(PIN_27, UART1, RxPin);
|
||||
|
||||
// Additional pins added by larger 2350 packages.
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_30, UART0, CtsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_31, UART0, RtsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_32, UART0, TxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_33, UART0, RxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_34, UART0, CtsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_35, UART0, RtsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_36, UART1, TxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_37, UART1, RxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_38, UART1, CtsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_39, UART1, RtsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_40, UART1, TxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_41, UART1, RxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_42, UART1, CtsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_43, UART1, RtsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_44, UART0, TxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_45, UART0, RxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_46, UART0, CtsPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_47, UART0, RtsPin);
|
||||
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_30, UART0, TxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_31, UART0, RxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_34, UART0, TxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_35, UART0, RxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_38, UART1, TxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_39, UART1, RxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_42, UART1, TxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_43, UART1, RxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_46, UART0, TxPin);
|
||||
#[cfg(feature = "rp235xb")]
|
||||
impl_pin!(PIN_47, UART0, RxPin);
|
||||
|
@ -28,10 +28,10 @@ pub trait Instance: SealedInstance + 'static {
|
||||
|
||||
impl crate::usb::SealedInstance for peripherals::USB {
|
||||
fn regs() -> pac::usb::Usb {
|
||||
pac::USBCTRL_REGS
|
||||
pac::USB
|
||||
}
|
||||
fn dpram() -> crate::pac::usb_dpram::UsbDpram {
|
||||
pac::USBCTRL_DPRAM
|
||||
pac::USB_DPRAM
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ impl crate::usb::Instance for peripherals::USB {
|
||||
|
||||
const EP_COUNT: usize = 16;
|
||||
const EP_MEMORY_SIZE: usize = 4096;
|
||||
const EP_MEMORY: *mut u8 = pac::USBCTRL_DPRAM.as_ptr() as *mut u8;
|
||||
const EP_MEMORY: *mut u8 = pac::USB_DPRAM.as_ptr() as *mut u8;
|
||||
|
||||
const NEW_AW: AtomicWaker = AtomicWaker::new();
|
||||
static BUS_WAKER: AtomicWaker = NEW_AW;
|
||||
|
@ -34,6 +34,7 @@ impl Watchdog {
|
||||
///
|
||||
/// * `cycles` - Total number of tick cycles before the next tick is generated.
|
||||
/// It is expected to be the frequency in MHz of clk_ref.
|
||||
#[cfg(feature = "rp2040")]
|
||||
pub fn enable_tick_generation(&mut self, cycles: u8) {
|
||||
let watchdog = pac::WATCHDOG;
|
||||
watchdog.tick().write(|w| {
|
||||
|
@ -10,7 +10,7 @@ embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal",
|
||||
embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl"] }
|
||||
embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] }
|
||||
embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
|
||||
@ -51,7 +51,7 @@ embedded-hal-async = "1.0"
|
||||
embedded-hal-bus = { version = "0.1", features = ["async"] }
|
||||
embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
|
||||
embedded-storage = { version = "0.3" }
|
||||
static_cell = "2"
|
||||
static_cell = "2.1"
|
||||
portable-atomic = { version = "1.5", features = ["critical-section"] }
|
||||
log = "0.4"
|
||||
pio-proc = "0.2"
|
||||
|
9
examples/rp23/.cargo/config.toml
Normal file
9
examples/rp23/.cargo/config.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
#runner = "probe-rs run --chip RP2040"
|
||||
runner = "elf2uf2-rs -d"
|
||||
|
||||
[build]
|
||||
target = "thumbv8m.main-none-eabihf"
|
||||
|
||||
[env]
|
||||
DEFMT_LOG = "debug"
|
80
examples/rp23/Cargo.toml
Normal file
80
examples/rp23/Cargo.toml
Normal file
@ -0,0 +1,80 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "embassy-rp2350-examples"
|
||||
version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
|
||||
[dependencies]
|
||||
embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] }
|
||||
embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info", "rt-235x", "boot2-none"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] }
|
||||
embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" }
|
||||
cyw43 = { version = "0.2.0", path = "../../cyw43", features = ["defmt", "firmware-logs", "bluetooth"] }
|
||||
cyw43-pio = { version = "0.2.0", path = "../../cyw43-pio", features = ["defmt"] }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
fixed = "1.23.1"
|
||||
fixed-macro = "1.2"
|
||||
|
||||
# for web request example
|
||||
reqwless = { version = "0.12.0", features = ["defmt",]}
|
||||
serde = { version = "1.0.203", default-features = false, features = ["derive"] }
|
||||
serde-json-core = "0.5.1"
|
||||
|
||||
# for assign resources example
|
||||
assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" }
|
||||
|
||||
#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
||||
cortex-m = { version = "0.7.6", features = ["inline-asm"] }
|
||||
cortex-m-rt = "0.7.0"
|
||||
critical-section = "1.1"
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
display-interface-spi = "0.4.1"
|
||||
embedded-graphics = "0.7.1"
|
||||
st7789 = "0.6.1"
|
||||
display-interface = "0.4.1"
|
||||
byte-slice-cast = { version = "1.2.0", default-features = false }
|
||||
smart-leds = "0.3.0"
|
||||
heapless = "0.8"
|
||||
usbd-hid = "0.8.1"
|
||||
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
||||
embedded-hal-async = "1.0"
|
||||
embedded-hal-bus = { version = "0.1", features = ["async"] }
|
||||
embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
|
||||
embedded-storage = { version = "0.3" }
|
||||
static_cell = "2.1"
|
||||
portable-atomic = { version = "1.5", features = ["critical-section"] }
|
||||
log = "0.4"
|
||||
pio-proc = "0.2"
|
||||
pio = "0.2.1"
|
||||
rand = { version = "0.8.5", default-features = false }
|
||||
embedded-sdmmc = "0.7.0"
|
||||
|
||||
bt-hci = { version = "0.1.0", default-features = false, features = ["defmt"] }
|
||||
trouble-host = { version = "0.1.0", features = ["defmt", "gatt"] }
|
||||
|
||||
[profile.release]
|
||||
debug = 2
|
||||
|
||||
[profile.dev]
|
||||
lto = true
|
||||
opt-level = "z"
|
||||
|
||||
[patch.crates-io]
|
||||
trouble-host = { git = "https://github.com/embassy-rs/trouble.git", rev = "4b8c0f499b34e46ca23a56e2d1640ede371722cf" }
|
||||
bt-hci = { git = "https://github.com/alexmoon/bt-hci.git", rev = "b9cd5954f6bd89b535cad9c418e9fdf12812d7c3" }
|
||||
embassy-executor = { path = "../../embassy-executor" }
|
||||
embassy-sync = { path = "../../embassy-sync" }
|
||||
embassy-futures = { path = "../../embassy-futures" }
|
||||
embassy-time = { path = "../../embassy-time" }
|
||||
embassy-time-driver = { path = "../../embassy-time-driver" }
|
||||
embassy-embedded-hal = { path = "../../embassy-embedded-hal" }
|
BIN
examples/rp23/assets/ferris.raw
Normal file
BIN
examples/rp23/assets/ferris.raw
Normal file
Binary file not shown.
35
examples/rp23/build.rs
Normal file
35
examples/rp23/build.rs
Normal file
@ -0,0 +1,35 @@
|
||||
//! This build script copies the `memory.x` file from the crate root into
|
||||
//! a directory where the linker can always find it at build time.
|
||||
//! For many projects this is optional, as the linker always searches the
|
||||
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||
//! are using a workspace or have a more complicated build setup, this
|
||||
//! build script becomes required. Additionally, by requesting that
|
||||
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||
//! updating `memory.x` ensures a rebuild of the application with the
|
||||
//! new memory settings.
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// Put `memory.x` in our output directory and ensure it's
|
||||
// on the linker search path.
|
||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
File::create(out.join("memory.x"))
|
||||
.unwrap()
|
||||
.write_all(include_bytes!("memory.x"))
|
||||
.unwrap();
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
// By default, Cargo will re-run a build script whenever
|
||||
// any file in the project changes. By specifying `memory.x`
|
||||
// here, we ensure the build script is only re-run when
|
||||
// `memory.x` is changed.
|
||||
println!("cargo:rerun-if-changed=memory.x");
|
||||
|
||||
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
||||
}
|
74
examples/rp23/memory.x
Normal file
74
examples/rp23/memory.x
Normal file
@ -0,0 +1,74 @@
|
||||
MEMORY {
|
||||
/*
|
||||
* The RP2350 has either external or internal flash.
|
||||
*
|
||||
* 2 MiB is a safe default here, although a Pico 2 has 4 MiB.
|
||||
*/
|
||||
FLASH : ORIGIN = 0x10000000, LENGTH = 2048K
|
||||
/*
|
||||
* RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping.
|
||||
* This is usually good for performance, as it distributes load on
|
||||
* those banks evenly.
|
||||
*/
|
||||
RAM : ORIGIN = 0x20000000, LENGTH = 512K
|
||||
/*
|
||||
* RAM banks 8 and 9 use a direct mapping. They can be used to have
|
||||
* memory areas dedicated for some specific job, improving predictability
|
||||
* of access times.
|
||||
* Example: Separate stacks for core0 and core1.
|
||||
*/
|
||||
SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K
|
||||
SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
/* ### Boot ROM info
|
||||
*
|
||||
* Goes after .vector_table, to keep it in the first 4K of flash
|
||||
* where the Boot ROM (and picotool) can find it
|
||||
*/
|
||||
.start_block : ALIGN(4)
|
||||
{
|
||||
__start_block_addr = .;
|
||||
KEEP(*(.start_block));
|
||||
} > FLASH
|
||||
|
||||
} INSERT AFTER .vector_table;
|
||||
|
||||
/* move .text to start /after/ the boot info */
|
||||
_stext = ADDR(.start_block) + SIZEOF(.start_block);
|
||||
|
||||
SECTIONS {
|
||||
/* ### Picotool 'Binary Info' Entries
|
||||
*
|
||||
* Picotool looks through this block (as we have pointers to it in our
|
||||
* header) to find interesting information.
|
||||
*/
|
||||
.bi_entries : ALIGN(4)
|
||||
{
|
||||
/* We put this in the header */
|
||||
__bi_entries_start = .;
|
||||
/* Here are the entries */
|
||||
KEEP(*(.bi_entries));
|
||||
/* Keep this block a nice round size */
|
||||
. = ALIGN(4);
|
||||
/* We put this in the header */
|
||||
__bi_entries_end = .;
|
||||
} > FLASH
|
||||
} INSERT AFTER .text;
|
||||
|
||||
SECTIONS {
|
||||
/* ### Boot ROM extra info
|
||||
*
|
||||
* Goes after everything in our program, so it can contain a signature.
|
||||
*/
|
||||
.end_block : ALIGN(4)
|
||||
{
|
||||
__end_block_addr = .;
|
||||
KEEP(*(.end_block));
|
||||
} > FLASH
|
||||
|
||||
} INSERT AFTER .uninit;
|
||||
|
||||
PROVIDE(start_to_end = __end_block_addr - __start_block_addr);
|
||||
PROVIDE(end_to_start = __start_block_addr - __end_block_addr);
|
64
examples/rp23/src/bin/adc.rs
Normal file
64
examples/rp23/src/bin/adc.rs
Normal file
@ -0,0 +1,64 @@
|
||||
//! This example test the ADC (Analog to Digital Conversion) of the RS2040 pin 26, 27 and 28.
|
||||
//! It also reads the temperature sensor in the chip.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::gpio::Pull;
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
ADC_IRQ_FIFO => InterruptHandler;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let mut adc = Adc::new(p.ADC, Irqs, Config::default());
|
||||
|
||||
let mut p26 = Channel::new_pin(p.PIN_26, Pull::None);
|
||||
let mut p27 = Channel::new_pin(p.PIN_27, Pull::None);
|
||||
let mut p28 = Channel::new_pin(p.PIN_28, Pull::None);
|
||||
let mut ts = Channel::new_temp_sensor(p.ADC_TEMP_SENSOR);
|
||||
|
||||
loop {
|
||||
let level = adc.read(&mut p26).await.unwrap();
|
||||
info!("Pin 26 ADC: {}", level);
|
||||
let level = adc.read(&mut p27).await.unwrap();
|
||||
info!("Pin 27 ADC: {}", level);
|
||||
let level = adc.read(&mut p28).await.unwrap();
|
||||
info!("Pin 28 ADC: {}", level);
|
||||
let temp = adc.read(&mut ts).await.unwrap();
|
||||
info!("Temp: {} degrees", convert_to_celsius(temp));
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_celsius(raw_temp: u16) -> f32 {
|
||||
// According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet
|
||||
let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721;
|
||||
let sign = if temp < 0.0 { -1.0 } else { 1.0 };
|
||||
let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16;
|
||||
(rounded_temp_x10 as f32) / 10.0
|
||||
}
|
70
examples/rp23/src/bin/adc_dma.rs
Normal file
70
examples/rp23/src/bin/adc_dma.rs
Normal file
@ -0,0 +1,70 @@
|
||||
//! This example shows how to use the RP2040 ADC with DMA, both single- and multichannel reads.
|
||||
//! For multichannel, the samples are interleaved in the buffer:
|
||||
//! `[ch1, ch2, ch3, ch4, ch1, ch2, ch3, ch4, ...]`
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler};
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::gpio::Pull;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
ADC_IRQ_FIFO => InterruptHandler;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Here we go!");
|
||||
|
||||
let mut adc = Adc::new(p.ADC, Irqs, Config::default());
|
||||
let mut dma = p.DMA_CH0;
|
||||
let mut pin = Channel::new_pin(p.PIN_26, Pull::Up);
|
||||
let mut pins = [
|
||||
Channel::new_pin(p.PIN_27, Pull::Down),
|
||||
Channel::new_pin(p.PIN_28, Pull::None),
|
||||
Channel::new_pin(p.PIN_29, Pull::Up),
|
||||
Channel::new_temp_sensor(p.ADC_TEMP_SENSOR),
|
||||
];
|
||||
|
||||
const BLOCK_SIZE: usize = 100;
|
||||
const NUM_CHANNELS: usize = 4;
|
||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||
loop {
|
||||
// Read 100 samples from a single channel
|
||||
let mut buf = [0_u16; BLOCK_SIZE];
|
||||
let div = 479; // 100kHz sample rate (48Mhz / 100kHz - 1)
|
||||
adc.read_many(&mut pin, &mut buf, div, &mut dma).await.unwrap();
|
||||
info!("single: {:?} ...etc", buf[..8]);
|
||||
|
||||
// Read 100 samples from 4 channels interleaved
|
||||
let mut buf = [0_u16; { BLOCK_SIZE * NUM_CHANNELS }];
|
||||
let div = 119; // 100kHz sample rate (48Mhz / 100kHz * 4ch - 1)
|
||||
adc.read_many_multichannel(&mut pins, &mut buf, div, &mut dma)
|
||||
.await
|
||||
.unwrap();
|
||||
info!("multi: {:?} ...etc", buf[..NUM_CHANNELS * 2]);
|
||||
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
95
examples/rp23/src/bin/assign_resources.rs
Normal file
95
examples/rp23/src/bin/assign_resources.rs
Normal file
@ -0,0 +1,95 @@
|
||||
//! This example demonstrates how to assign resources to multiple tasks by splitting up the peripherals.
|
||||
//! 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)
|
||||
//! Of course splitting up resources and sharing resources can be combined, yet this example is only about splitting up resources.
|
||||
//!
|
||||
//! There are basically two ways we demonstrate here:
|
||||
//! 1) Assigning resources to a task by passing parts of the peripherals
|
||||
//! 2) Assigning resources to a task by passing a struct with the split up peripherals, using the assign-resources macro
|
||||
//!
|
||||
//! using four LEDs on Pins 10, 11, 20 and 21
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use assign_resources::assign_resources;
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio::{Level, Output};
|
||||
use embassy_rp::peripherals::{self, PIN_20, PIN_21};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
// initialize the peripherals
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
// 1) Assigning a resource to a task by passing parts of the peripherals.
|
||||
spawner
|
||||
.spawn(double_blinky_manually_assigned(spawner, p.PIN_20, p.PIN_21))
|
||||
.unwrap();
|
||||
|
||||
// 2) Using the assign-resources macro to assign resources to a task.
|
||||
// we perform the split, see further below for the definition of the resources struct
|
||||
let r = split_resources!(p);
|
||||
// and then we can use them
|
||||
spawner.spawn(double_blinky_macro_assigned(spawner, r.leds)).unwrap();
|
||||
}
|
||||
|
||||
// 1) Assigning a resource to a task by passing parts of the peripherals.
|
||||
#[embassy_executor::task]
|
||||
async fn double_blinky_manually_assigned(_spawner: Spawner, pin_20: PIN_20, pin_21: PIN_21) {
|
||||
let mut led_20 = Output::new(pin_20, Level::Low);
|
||||
let mut led_21 = Output::new(pin_21, Level::High);
|
||||
|
||||
loop {
|
||||
info!("toggling leds");
|
||||
led_20.toggle();
|
||||
led_21.toggle();
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Using the assign-resources macro to assign resources to a task.
|
||||
// first we define the resources we want to assign to the task using the assign_resources! macro
|
||||
// basically this will split up the peripherals struct into smaller structs, that we define here
|
||||
// naming is up to you, make sure your future self understands what you did here
|
||||
assign_resources! {
|
||||
leds: Leds{
|
||||
led_10: PIN_10,
|
||||
led_11: PIN_11,
|
||||
}
|
||||
// add more resources to more structs if needed, for example defining one struct for each task
|
||||
}
|
||||
// this could be done in another file and imported here, but for the sake of simplicity we do it here
|
||||
// see https://github.com/adamgreig/assign-resources for more information
|
||||
|
||||
// 2) Using the split resources in a task
|
||||
#[embassy_executor::task]
|
||||
async fn double_blinky_macro_assigned(_spawner: Spawner, r: Leds) {
|
||||
let mut led_10 = Output::new(r.led_10, Level::Low);
|
||||
let mut led_11 = Output::new(r.led_11, Level::High);
|
||||
|
||||
loop {
|
||||
info!("toggling leds");
|
||||
led_10.toggle();
|
||||
led_11.toggle();
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
44
examples/rp23/src/bin/blinky.rs
Normal file
44
examples/rp23/src/bin/blinky.rs
Normal file
@ -0,0 +1,44 @@
|
||||
//! This example test the RP Pico on board LED.
|
||||
//!
|
||||
//! It does not work with the RP Pico W board. See wifi_blinky.rs.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio;
|
||||
use embassy_time::Timer;
|
||||
use gpio::{Level, Output};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let mut led = Output::new(p.PIN_2, Level::Low);
|
||||
|
||||
loop {
|
||||
info!("led on!");
|
||||
led.set_high();
|
||||
Timer::after_millis(250).await;
|
||||
|
||||
info!("led off!");
|
||||
led.set_low();
|
||||
Timer::after_millis(250).await;
|
||||
}
|
||||
}
|
66
examples/rp23/src/bin/blinky_two_channels.rs
Normal file
66
examples/rp23/src/bin/blinky_two_channels.rs
Normal file
@ -0,0 +1,66 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
/// This example demonstrates how to access a given pin from more than one embassy task
|
||||
/// The on-board LED is toggled by two tasks with slightly different periods, leading to the
|
||||
/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar
|
||||
/// to interference and the 'beats' you can hear if you play two frequencies close to one another
|
||||
/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio;
|
||||
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||
use embassy_sync::channel::{Channel, Sender};
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use gpio::{AnyPin, Level, Output};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
enum LedState {
|
||||
Toggle,
|
||||
}
|
||||
static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High);
|
||||
|
||||
let dt = 100 * 1_000_000;
|
||||
let k = 1.003;
|
||||
|
||||
unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));
|
||||
unwrap!(spawner.spawn(toggle_led(
|
||||
CHANNEL.sender(),
|
||||
Duration::from_nanos((dt as f64 * k) as u64)
|
||||
)));
|
||||
|
||||
loop {
|
||||
match CHANNEL.receive().await {
|
||||
LedState::Toggle => led.toggle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task(pool_size = 2)]
|
||||
async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {
|
||||
let mut ticker = Ticker::every(delay);
|
||||
loop {
|
||||
control.send(LedState::Toggle).await;
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
65
examples/rp23/src/bin/blinky_two_tasks.rs
Normal file
65
examples/rp23/src/bin/blinky_two_tasks.rs
Normal file
@ -0,0 +1,65 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
/// This example demonstrates how to access a given pin from more than one embassy task
|
||||
/// The on-board LED is toggled by two tasks with slightly different periods, leading to the
|
||||
/// apparent duty cycle of the LED increasing, then decreasing, linearly. The phenomenon is similar
|
||||
/// to interference and the 'beats' you can hear if you play two frequencies close to one another
|
||||
/// [Link explaining it](https://www.physicsclassroom.com/class/sound/Lesson-3/Interference-and-Beats)
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio;
|
||||
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use gpio::{AnyPin, Level, Output};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static>>>;
|
||||
static LED: LedType = Mutex::new(None);
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
// set the content of the global LED reference to the real LED pin
|
||||
let led = Output::new(AnyPin::from(p.PIN_25), Level::High);
|
||||
// inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the
|
||||
// Mutex is released
|
||||
{
|
||||
*(LED.lock().await) = Some(led);
|
||||
}
|
||||
let dt = 100 * 1_000_000;
|
||||
let k = 1.003;
|
||||
|
||||
unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt))));
|
||||
unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));
|
||||
}
|
||||
|
||||
#[embassy_executor::task(pool_size = 2)]
|
||||
async fn toggle_led(led: &'static LedType, delay: Duration) {
|
||||
let mut ticker = Ticker::every(delay);
|
||||
loop {
|
||||
{
|
||||
let mut led_unlocked = led.lock().await;
|
||||
if let Some(pin_ref) = led_unlocked.as_mut() {
|
||||
pin_ref.toggle();
|
||||
}
|
||||
}
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
44
examples/rp23/src/bin/button.rs
Normal file
44
examples/rp23/src/bin/button.rs
Normal file
@ -0,0 +1,44 @@
|
||||
//! This example uses the RP Pico on board LED to test input pin 28. This is not the button on the board.
|
||||
//!
|
||||
//! It does not work with the RP Pico W board. Use wifi_blinky.rs and add input pin.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio::{Input, Level, Output, Pull};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let mut led = Output::new(p.PIN_25, Level::Low);
|
||||
|
||||
// Use PIN_28, Pin34 on J0 for RP Pico, as a input.
|
||||
// You need to add your own button.
|
||||
let button = Input::new(p.PIN_28, Pull::Up);
|
||||
|
||||
loop {
|
||||
if button.is_high() {
|
||||
led.set_high();
|
||||
} else {
|
||||
led.set_low();
|
||||
}
|
||||
}
|
||||
}
|
96
examples/rp23/src/bin/debounce.rs
Normal file
96
examples/rp23/src/bin/debounce.rs
Normal file
@ -0,0 +1,96 @@
|
||||
//! This example shows the ease of debouncing a button with async rust.
|
||||
//! Hook up a button or switch between pin 9 and ground.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio::{Input, Level, Pull};
|
||||
use embassy_time::{with_deadline, Duration, Instant, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
pub struct Debouncer<'a> {
|
||||
input: Input<'a>,
|
||||
debounce: Duration,
|
||||
}
|
||||
|
||||
impl<'a> Debouncer<'a> {
|
||||
pub fn new(input: Input<'a>, debounce: Duration) -> Self {
|
||||
Self { input, debounce }
|
||||
}
|
||||
|
||||
pub async fn debounce(&mut self) -> Level {
|
||||
loop {
|
||||
let l1 = self.input.get_level();
|
||||
|
||||
self.input.wait_for_any_edge().await;
|
||||
|
||||
Timer::after(self.debounce).await;
|
||||
|
||||
let l2 = self.input.get_level();
|
||||
if l1 != l2 {
|
||||
break l2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let mut btn = Debouncer::new(Input::new(p.PIN_9, Pull::Up), Duration::from_millis(20));
|
||||
|
||||
info!("Debounce Demo");
|
||||
|
||||
loop {
|
||||
// button pressed
|
||||
btn.debounce().await;
|
||||
let start = Instant::now();
|
||||
info!("Button Press");
|
||||
|
||||
match with_deadline(start + Duration::from_secs(1), btn.debounce()).await {
|
||||
// Button Released < 1s
|
||||
Ok(_) => {
|
||||
info!("Button pressed for: {}ms", start.elapsed().as_millis());
|
||||
continue;
|
||||
}
|
||||
// button held for > 1s
|
||||
Err(_) => {
|
||||
info!("Button Held");
|
||||
}
|
||||
}
|
||||
|
||||
match with_deadline(start + Duration::from_secs(5), btn.debounce()).await {
|
||||
// Button released <5s
|
||||
Ok(_) => {
|
||||
info!("Button pressed for: {}ms", start.elapsed().as_millis());
|
||||
continue;
|
||||
}
|
||||
// button held for > >5s
|
||||
Err(_) => {
|
||||
info!("Button Long Held");
|
||||
}
|
||||
}
|
||||
|
||||
// wait for button release before handling another press
|
||||
btn.debounce().await;
|
||||
info!("Button pressed for: {}ms", start.elapsed().as_millis());
|
||||
}
|
||||
}
|
150
examples/rp23/src/bin/flash.rs
Normal file
150
examples/rp23/src/bin/flash.rs
Normal file
@ -0,0 +1,150 @@
|
||||
//! This example test the flash connected to the RP2040 chip.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE};
|
||||
use embassy_rp::peripherals::FLASH;
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
const ADDR_OFFSET: u32 = 0x100000;
|
||||
const FLASH_SIZE: usize = 2 * 1024 * 1024;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Hello World!");
|
||||
|
||||
// add some delay to give an attached debug probe time to parse the
|
||||
// defmt RTT header. Reading that header might touch flash memory, which
|
||||
// interferes with flash write operations.
|
||||
// https://github.com/knurling-rs/defmt/pull/683
|
||||
Timer::after_millis(10).await;
|
||||
|
||||
let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0);
|
||||
|
||||
// Get JEDEC id
|
||||
let jedec = flash.blocking_jedec_id().unwrap();
|
||||
info!("jedec id: 0x{:x}", jedec);
|
||||
|
||||
// Get unique id
|
||||
let mut uid = [0; 8];
|
||||
flash.blocking_unique_id(&mut uid).unwrap();
|
||||
info!("unique id: {:?}", uid);
|
||||
|
||||
erase_write_sector(&mut flash, 0x00);
|
||||
|
||||
multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
|
||||
|
||||
background_read(&mut flash, (ERASE_SIZE * 2) as u32).await;
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
|
||||
info!(">>>> [multiwrite_bytes]");
|
||||
let mut read_buf = [0u8; ERASE_SIZE];
|
||||
defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
|
||||
|
||||
info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
|
||||
info!("Contents start with {=[u8]}", read_buf[0..4]);
|
||||
|
||||
defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
|
||||
|
||||
defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
|
||||
info!("Contents after erase starts with {=[u8]}", read_buf[0..4]);
|
||||
if read_buf.iter().any(|x| *x != 0xFF) {
|
||||
defmt::panic!("unexpected");
|
||||
}
|
||||
|
||||
defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &[0x01]));
|
||||
defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 1, &[0x02]));
|
||||
defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 2, &[0x03]));
|
||||
defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 3, &[0x04]));
|
||||
|
||||
defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
|
||||
info!("Contents after write starts with {=[u8]}", read_buf[0..4]);
|
||||
if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] {
|
||||
defmt::panic!("unexpected");
|
||||
}
|
||||
}
|
||||
|
||||
fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
|
||||
info!(">>>> [erase_write_sector]");
|
||||
let mut buf = [0u8; ERASE_SIZE];
|
||||
defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
|
||||
|
||||
info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
|
||||
info!("Contents start with {=[u8]}", buf[0..4]);
|
||||
|
||||
defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
|
||||
|
||||
defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
|
||||
info!("Contents after erase starts with {=[u8]}", buf[0..4]);
|
||||
if buf.iter().any(|x| *x != 0xFF) {
|
||||
defmt::panic!("unexpected");
|
||||
}
|
||||
|
||||
for b in buf.iter_mut() {
|
||||
*b = 0xDA;
|
||||
}
|
||||
|
||||
defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &buf));
|
||||
|
||||
defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf));
|
||||
info!("Contents after write starts with {=[u8]}", buf[0..4]);
|
||||
if buf.iter().any(|x| *x != 0xDA) {
|
||||
defmt::panic!("unexpected");
|
||||
}
|
||||
}
|
||||
|
||||
async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
|
||||
info!(">>>> [background_read]");
|
||||
|
||||
let mut buf = [0u32; 8];
|
||||
defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
|
||||
|
||||
info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32);
|
||||
info!("Contents start with {=u32:x}", buf[0]);
|
||||
|
||||
defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32));
|
||||
|
||||
defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
|
||||
info!("Contents after erase starts with {=u32:x}", buf[0]);
|
||||
if buf.iter().any(|x| *x != 0xFFFFFFFF) {
|
||||
defmt::panic!("unexpected");
|
||||
}
|
||||
|
||||
for b in buf.iter_mut() {
|
||||
*b = 0xDABA1234;
|
||||
}
|
||||
|
||||
defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, unsafe {
|
||||
core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4)
|
||||
}));
|
||||
|
||||
defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await;
|
||||
info!("Contents after write starts with {=u32:x}", buf[0]);
|
||||
if buf.iter().any(|x| *x != 0xDABA1234) {
|
||||
defmt::panic!("unexpected");
|
||||
}
|
||||
}
|
56
examples/rp23/src/bin/gpio_async.rs
Normal file
56
examples/rp23/src/bin/gpio_async.rs
Normal file
@ -0,0 +1,56 @@
|
||||
//! This example shows how async gpio can be used with a RP2040.
|
||||
//!
|
||||
//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio;
|
||||
use embassy_time::Timer;
|
||||
use gpio::{Input, Level, Output, Pull};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
/// It requires an external signal to be manually triggered on PIN 16. For
|
||||
/// example, this could be accomplished using an external power source with a
|
||||
/// button so that it is possible to toggle the signal from low to high.
|
||||
///
|
||||
/// This example will begin with turning on the LED on the board and wait for a
|
||||
/// high signal on PIN 16. Once the high event/signal occurs the program will
|
||||
/// continue and turn off the LED, and then wait for 2 seconds before completing
|
||||
/// the loop and starting over again.
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let mut led = Output::new(p.PIN_25, Level::Low);
|
||||
let mut async_input = Input::new(p.PIN_16, Pull::None);
|
||||
|
||||
loop {
|
||||
info!("wait_for_high. Turn on LED");
|
||||
led.set_high();
|
||||
|
||||
async_input.wait_for_high().await;
|
||||
|
||||
info!("done wait_for_high. Turn off LED");
|
||||
led.set_low();
|
||||
|
||||
Timer::after_secs(2).await;
|
||||
}
|
||||
}
|
53
examples/rp23/src/bin/gpout.rs
Normal file
53
examples/rp23/src/bin/gpout.rs
Normal file
@ -0,0 +1,53 @@
|
||||
//! This example shows how GPOUT (General purpose clock outputs) can toggle a output pin.
|
||||
//!
|
||||
//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::clocks;
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let gpout3 = clocks::Gpout::new(p.PIN_25);
|
||||
gpout3.set_div(1000, 0);
|
||||
gpout3.enable();
|
||||
|
||||
loop {
|
||||
gpout3.set_src(clocks::GpoutSrc::Sys);
|
||||
info!(
|
||||
"Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}",
|
||||
gpout3.get_freq()
|
||||
);
|
||||
Timer::after_secs(2).await;
|
||||
|
||||
gpout3.set_src(clocks::GpoutSrc::Ref);
|
||||
info!(
|
||||
"Pin 25 is now outputing CLK_REF/1000, should be toggling at {}",
|
||||
gpout3.get_freq()
|
||||
);
|
||||
Timer::after_secs(2).await;
|
||||
}
|
||||
}
|
126
examples/rp23/src/bin/i2c_async.rs
Normal file
126
examples/rp23/src/bin/i2c_async.rs
Normal file
@ -0,0 +1,126 @@
|
||||
//! This example shows how to communicate asynchronous using i2c with external chips.
|
||||
//!
|
||||
//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.
|
||||
//! (https://www.microchip.com/en-us/product/mcp23017)
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::i2c::{self, Config, InterruptHandler};
|
||||
use embassy_rp::peripherals::I2C1;
|
||||
use embassy_time::Timer;
|
||||
use embedded_hal_async::i2c::I2c;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C1_IRQ => InterruptHandler<I2C1>;
|
||||
});
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod mcp23017 {
|
||||
pub const ADDR: u8 = 0x20; // default addr
|
||||
|
||||
macro_rules! mcpregs {
|
||||
($($name:ident : $val:expr),* $(,)?) => {
|
||||
$(
|
||||
pub const $name: u8 = $val;
|
||||
)*
|
||||
|
||||
pub fn regname(reg: u8) -> &'static str {
|
||||
match reg {
|
||||
$(
|
||||
$val => stringify!($name),
|
||||
)*
|
||||
_ => panic!("bad reg"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These are correct for IOCON.BANK=0
|
||||
mcpregs! {
|
||||
IODIRA: 0x00,
|
||||
IPOLA: 0x02,
|
||||
GPINTENA: 0x04,
|
||||
DEFVALA: 0x06,
|
||||
INTCONA: 0x08,
|
||||
IOCONA: 0x0A,
|
||||
GPPUA: 0x0C,
|
||||
INTFA: 0x0E,
|
||||
INTCAPA: 0x10,
|
||||
GPIOA: 0x12,
|
||||
OLATA: 0x14,
|
||||
IODIRB: 0x01,
|
||||
IPOLB: 0x03,
|
||||
GPINTENB: 0x05,
|
||||
DEFVALB: 0x07,
|
||||
INTCONB: 0x09,
|
||||
IOCONB: 0x0B,
|
||||
GPPUB: 0x0D,
|
||||
INTFB: 0x0F,
|
||||
INTCAPB: 0x11,
|
||||
GPIOB: 0x13,
|
||||
OLATB: 0x15,
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let sda = p.PIN_14;
|
||||
let scl = p.PIN_15;
|
||||
|
||||
info!("set up i2c ");
|
||||
let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, Config::default());
|
||||
|
||||
use mcp23017::*;
|
||||
|
||||
info!("init mcp23017 config for IxpandO");
|
||||
// init - a outputs, b inputs
|
||||
i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap();
|
||||
i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap();
|
||||
i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups
|
||||
|
||||
let mut val = 1;
|
||||
loop {
|
||||
let mut portb = [0];
|
||||
|
||||
i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap();
|
||||
info!("portb = {:02x}", portb[0]);
|
||||
i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap();
|
||||
val = val.rotate_left(1);
|
||||
|
||||
// get a register dump
|
||||
info!("getting register dump");
|
||||
let mut regs = [0; 22];
|
||||
i2c.write_read(ADDR, &[0], &mut regs).await.unwrap();
|
||||
// always get the regdump but only display it if portb'0 is set
|
||||
if portb[0] & 1 != 0 {
|
||||
for (idx, reg) in regs.into_iter().enumerate() {
|
||||
info!("{} => {:02x}", regname(idx as u8), reg);
|
||||
}
|
||||
}
|
||||
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
}
|
101
examples/rp23/src/bin/i2c_async_embassy.rs
Normal file
101
examples/rp23/src/bin/i2c_async_embassy.rs
Normal file
@ -0,0 +1,101 @@
|
||||
//! This example shows how to communicate asynchronous using i2c with external chip.
|
||||
//!
|
||||
//! It's using embassy's functions directly instead of traits from embedded_hal_async::i2c::I2c.
|
||||
//! While most of i2c devices are addressed using 7 bits, an extension allows 10 bits too.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_rp::i2c::InterruptHandler;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
// Our anonymous hypotetical temperature sensor could be:
|
||||
// a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C
|
||||
// It requires no configuration or calibration, works with all i2c bus speeds,
|
||||
// never stretches clock or does anything complicated. Replies with one u16.
|
||||
// It requires only one write to take it out of suspend mode, and stays on.
|
||||
// Often result would be just on 12 bits, but here we'll simplify it to 16.
|
||||
|
||||
enum UncomplicatedSensorId {
|
||||
A(UncomplicatedSensorU8),
|
||||
B(UncomplicatedSensorU16),
|
||||
}
|
||||
enum UncomplicatedSensorU8 {
|
||||
First = 0x48,
|
||||
}
|
||||
enum UncomplicatedSensorU16 {
|
||||
Other = 0x0049,
|
||||
}
|
||||
|
||||
impl Into<u16> for UncomplicatedSensorU16 {
|
||||
fn into(self) -> u16 {
|
||||
self as u16
|
||||
}
|
||||
}
|
||||
impl Into<u16> for UncomplicatedSensorU8 {
|
||||
fn into(self) -> u16 {
|
||||
0x48
|
||||
}
|
||||
}
|
||||
impl From<UncomplicatedSensorId> for u16 {
|
||||
fn from(t: UncomplicatedSensorId) -> Self {
|
||||
match t {
|
||||
UncomplicatedSensorId::A(x) => x.into(),
|
||||
UncomplicatedSensorId::B(x) => x.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
embassy_rp::bind_interrupts!(struct Irqs {
|
||||
I2C1_IRQ => InterruptHandler<embassy_rp::peripherals::I2C1>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_task_spawner: embassy_executor::Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let sda = p.PIN_14;
|
||||
let scl = p.PIN_15;
|
||||
let config = embassy_rp::i2c::Config::default();
|
||||
let mut bus = embassy_rp::i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, config);
|
||||
|
||||
const WAKEYWAKEY: u16 = 0xBABE;
|
||||
let mut result: [u8; 2] = [0, 0];
|
||||
// wait for sensors to initialize
|
||||
embassy_time::Timer::after(embassy_time::Duration::from_millis(100)).await;
|
||||
|
||||
let _res_1 = bus
|
||||
.write_async(UncomplicatedSensorU8::First, WAKEYWAKEY.to_be_bytes())
|
||||
.await;
|
||||
let _res_2 = bus
|
||||
.write_async(UncomplicatedSensorU16::Other, WAKEYWAKEY.to_be_bytes())
|
||||
.await;
|
||||
|
||||
loop {
|
||||
let s1 = UncomplicatedSensorId::A(UncomplicatedSensorU8::First);
|
||||
let s2 = UncomplicatedSensorId::B(UncomplicatedSensorU16::Other);
|
||||
let sensors = [s1, s2];
|
||||
for sensor in sensors {
|
||||
if bus.read_async(sensor, &mut result).await.is_ok() {
|
||||
info!("Result {}", u16::from_be_bytes(result.into()));
|
||||
}
|
||||
}
|
||||
embassy_time::Timer::after(embassy_time::Duration::from_millis(200)).await;
|
||||
}
|
||||
}
|
90
examples/rp23/src/bin/i2c_blocking.rs
Normal file
90
examples/rp23/src/bin/i2c_blocking.rs
Normal file
@ -0,0 +1,90 @@
|
||||
//! This example shows how to communicate using i2c with external chips.
|
||||
//!
|
||||
//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip.
|
||||
//! (https://www.microchip.com/en-us/product/mcp23017)
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::i2c::{self, Config};
|
||||
use embassy_time::Timer;
|
||||
use embedded_hal_1::i2c::I2c;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod mcp23017 {
|
||||
pub const ADDR: u8 = 0x20; // default addr
|
||||
|
||||
pub const IODIRA: u8 = 0x00;
|
||||
pub const IPOLA: u8 = 0x02;
|
||||
pub const GPINTENA: u8 = 0x04;
|
||||
pub const DEFVALA: u8 = 0x06;
|
||||
pub const INTCONA: u8 = 0x08;
|
||||
pub const IOCONA: u8 = 0x0A;
|
||||
pub const GPPUA: u8 = 0x0C;
|
||||
pub const INTFA: u8 = 0x0E;
|
||||
pub const INTCAPA: u8 = 0x10;
|
||||
pub const GPIOA: u8 = 0x12;
|
||||
pub const OLATA: u8 = 0x14;
|
||||
pub const IODIRB: u8 = 0x01;
|
||||
pub const IPOLB: u8 = 0x03;
|
||||
pub const GPINTENB: u8 = 0x05;
|
||||
pub const DEFVALB: u8 = 0x07;
|
||||
pub const INTCONB: u8 = 0x09;
|
||||
pub const IOCONB: u8 = 0x0B;
|
||||
pub const GPPUB: u8 = 0x0D;
|
||||
pub const INTFB: u8 = 0x0F;
|
||||
pub const INTCAPB: u8 = 0x11;
|
||||
pub const GPIOB: u8 = 0x13;
|
||||
pub const OLATB: u8 = 0x15;
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let sda = p.PIN_14;
|
||||
let scl = p.PIN_15;
|
||||
|
||||
info!("set up i2c ");
|
||||
let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());
|
||||
|
||||
use mcp23017::*;
|
||||
|
||||
info!("init mcp23017 config for IxpandO");
|
||||
// init - a outputs, b inputs
|
||||
i2c.write(ADDR, &[IODIRA, 0x00]).unwrap();
|
||||
i2c.write(ADDR, &[IODIRB, 0xff]).unwrap();
|
||||
i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups
|
||||
|
||||
let mut val = 0xaa;
|
||||
loop {
|
||||
let mut portb = [0];
|
||||
|
||||
i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap();
|
||||
i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap();
|
||||
|
||||
info!("portb = {:02x}", portb[0]);
|
||||
val = !val;
|
||||
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
132
examples/rp23/src/bin/i2c_slave.rs
Normal file
132
examples/rp23/src/bin/i2c_slave.rs
Normal file
@ -0,0 +1,132 @@
|
||||
//! This example shows how to use the 2040 as an i2c slave.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::peripherals::{I2C0, I2C1};
|
||||
use embassy_rp::{bind_interrupts, i2c, i2c_slave};
|
||||
use embassy_time::Timer;
|
||||
use embedded_hal_async::i2c::I2c;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::{block::ImageDef};
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C0_IRQ => i2c::InterruptHandler<I2C0>;
|
||||
I2C1_IRQ => i2c::InterruptHandler<I2C1>;
|
||||
});
|
||||
|
||||
const DEV_ADDR: u8 = 0x42;
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! {
|
||||
info!("Device start");
|
||||
|
||||
let mut state = 0;
|
||||
|
||||
loop {
|
||||
let mut buf = [0u8; 128];
|
||||
match dev.listen(&mut buf).await {
|
||||
Ok(i2c_slave::Command::GeneralCall(len)) => info!("Device received general call write: {}", buf[..len]),
|
||||
Ok(i2c_slave::Command::Read) => loop {
|
||||
match dev.respond_to_read(&[state]).await {
|
||||
Ok(x) => match x {
|
||||
i2c_slave::ReadStatus::Done => break,
|
||||
i2c_slave::ReadStatus::NeedMoreBytes => (),
|
||||
i2c_slave::ReadStatus::LeftoverBytes(x) => {
|
||||
info!("tried to write {} extra bytes", x);
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(e) => error!("error while responding {}", e),
|
||||
}
|
||||
},
|
||||
Ok(i2c_slave::Command::Write(len)) => info!("Device received write: {}", buf[..len]),
|
||||
Ok(i2c_slave::Command::WriteRead(len)) => {
|
||||
info!("device received write read: {:x}", buf[..len]);
|
||||
match buf[0] {
|
||||
// Set the state
|
||||
0xC2 => {
|
||||
state = buf[1];
|
||||
match dev.respond_and_fill(&[state], 0x00).await {
|
||||
Ok(read_status) => info!("response read status {}", read_status),
|
||||
Err(e) => error!("error while responding {}", e),
|
||||
}
|
||||
}
|
||||
// Reset State
|
||||
0xC8 => {
|
||||
state = 0;
|
||||
match dev.respond_and_fill(&[state], 0x00).await {
|
||||
Ok(read_status) => info!("response read status {}", read_status),
|
||||
Err(e) => error!("error while responding {}", e),
|
||||
}
|
||||
}
|
||||
x => error!("Invalid Write Read {:x}", x),
|
||||
}
|
||||
}
|
||||
Err(e) => error!("{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) {
|
||||
info!("Controller start");
|
||||
|
||||
loop {
|
||||
let mut resp_buff = [0u8; 2];
|
||||
for i in 0..10 {
|
||||
match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await {
|
||||
Ok(_) => info!("write_read response: {}", resp_buff),
|
||||
Err(e) => error!("Error writing {}", e),
|
||||
}
|
||||
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
match con.read(DEV_ADDR, &mut resp_buff).await {
|
||||
Ok(_) => info!("read response: {}", resp_buff),
|
||||
Err(e) => error!("Error writing {}", e),
|
||||
}
|
||||
match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await {
|
||||
Ok(_) => info!("write_read response: {}", resp_buff),
|
||||
Err(e) => error!("Error writing {}", e),
|
||||
}
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Hello World!");
|
||||
|
||||
let d_sda = p.PIN_3;
|
||||
let d_scl = p.PIN_2;
|
||||
let mut config = i2c_slave::Config::default();
|
||||
config.addr = DEV_ADDR as u16;
|
||||
let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config);
|
||||
|
||||
unwrap!(spawner.spawn(device_task(device)));
|
||||
|
||||
let c_sda = p.PIN_1;
|
||||
let c_scl = p.PIN_0;
|
||||
let mut config = i2c::Config::default();
|
||||
config.frequency = 1_000_000;
|
||||
let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config);
|
||||
|
||||
unwrap!(spawner.spawn(controller_task(controller)));
|
||||
}
|
110
examples/rp23/src/bin/interrupt.rs
Normal file
110
examples/rp23/src/bin/interrupt.rs
Normal file
@ -0,0 +1,110 @@
|
||||
//! This example shows how you can use raw interrupt handlers alongside embassy.
|
||||
//! The example also showcases some of the options available for sharing resources/data.
|
||||
//!
|
||||
//! In the example, an ADC reading is triggered every time the PWM wraps around.
|
||||
//! The sample data is sent down a channel, to be processed inside a low priority task.
|
||||
//! The processed data is then used to adjust the PWM duty cycle, once every second.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::cell::{Cell, RefCell};
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::adc::{self, Adc, Blocking};
|
||||
use embassy_rp::gpio::Pull;
|
||||
use embassy_rp::interrupt;
|
||||
use embassy_rp::pwm::{Config, Pwm};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use portable_atomic::{AtomicU32, Ordering};
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
static COUNTER: AtomicU32 = AtomicU32::new(0);
|
||||
static PWM: Mutex<CriticalSectionRawMutex, RefCell<Option<Pwm>>> = Mutex::new(RefCell::new(None));
|
||||
static ADC: Mutex<CriticalSectionRawMutex, RefCell<Option<(Adc<Blocking>, adc::Channel)>>> =
|
||||
Mutex::new(RefCell::new(None));
|
||||
static ADC_VALUES: Channel<CriticalSectionRawMutex, u16, 2048> = Channel::new();
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
embassy_rp::pac::SIO.spinlock(31).write_value(1);
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let adc = Adc::new_blocking(p.ADC, Default::default());
|
||||
let p26 = adc::Channel::new_pin(p.PIN_26, Pull::None);
|
||||
ADC.lock(|a| a.borrow_mut().replace((adc, p26)));
|
||||
|
||||
let pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, Default::default());
|
||||
PWM.lock(|p| p.borrow_mut().replace(pwm));
|
||||
|
||||
// Enable the interrupt for pwm slice 4
|
||||
embassy_rp::pac::PWM.irq0_inte().modify(|w| w.set_ch4(true));
|
||||
unsafe {
|
||||
cortex_m::peripheral::NVIC::unmask(interrupt::PWM_IRQ_WRAP_0);
|
||||
}
|
||||
|
||||
// Tasks require their resources to have 'static lifetime
|
||||
// No Mutex needed when sharing within the same executor/prio level
|
||||
static AVG: StaticCell<Cell<u32>> = StaticCell::new();
|
||||
let avg = AVG.init(Default::default());
|
||||
spawner.must_spawn(processing(avg));
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||
loop {
|
||||
ticker.next().await;
|
||||
let freq = COUNTER.swap(0, Ordering::Relaxed);
|
||||
info!("pwm freq: {:?} Hz", freq);
|
||||
info!("adc average: {:?}", avg.get());
|
||||
|
||||
// Update the pwm duty cycle, based on the averaged adc reading
|
||||
let mut config = Config::default();
|
||||
config.compare_b = ((avg.get() as f32 / 4095.0) * config.top as f32) as _;
|
||||
PWM.lock(|p| p.borrow_mut().as_mut().unwrap().set_config(&config));
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn processing(avg: &'static Cell<u32>) {
|
||||
let mut buffer: heapless::HistoryBuffer<u16, 100> = Default::default();
|
||||
loop {
|
||||
let val = ADC_VALUES.receive().await;
|
||||
buffer.write(val);
|
||||
let sum: u32 = buffer.iter().map(|x| *x as u32).sum();
|
||||
avg.set(sum / buffer.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn PWM_IRQ_WRAP_0() {
|
||||
critical_section::with(|cs| {
|
||||
let mut adc = ADC.borrow(cs).borrow_mut();
|
||||
let (adc, p26) = adc.as_mut().unwrap();
|
||||
let val = adc.blocking_read(p26).unwrap();
|
||||
ADC_VALUES.try_send(val).ok();
|
||||
|
||||
// Clear the interrupt, so we don't immediately re-enter this irq handler
|
||||
PWM.borrow(cs).borrow_mut().as_mut().unwrap().clear_wrapped();
|
||||
});
|
||||
COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
82
examples/rp23/src/bin/multicore.rs
Normal file
82
examples/rp23/src/bin/multicore.rs
Normal file
@ -0,0 +1,82 @@
|
||||
//! This example shows how to send messages between the two cores in the RP2040 chip.
|
||||
//!
|
||||
//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Executor;
|
||||
use embassy_rp::gpio::{Level, Output};
|
||||
use embassy_rp::multicore::{spawn_core1, Stack};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_time::Timer;
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
static mut CORE1_STACK: Stack<4096> = Stack::new();
|
||||
static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
|
||||
static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
|
||||
static CHANNEL: Channel<CriticalSectionRawMutex, LedState, 1> = Channel::new();
|
||||
|
||||
enum LedState {
|
||||
On,
|
||||
Off,
|
||||
}
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let led = Output::new(p.PIN_25, Level::Low);
|
||||
|
||||
spawn_core1(
|
||||
p.CORE1,
|
||||
unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) },
|
||||
move || {
|
||||
let executor1 = EXECUTOR1.init(Executor::new());
|
||||
executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led))));
|
||||
},
|
||||
);
|
||||
|
||||
let executor0 = EXECUTOR0.init(Executor::new());
|
||||
executor0.run(|spawner| unwrap!(spawner.spawn(core0_task())));
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn core0_task() {
|
||||
info!("Hello from core 0");
|
||||
loop {
|
||||
CHANNEL.send(LedState::On).await;
|
||||
Timer::after_millis(100).await;
|
||||
CHANNEL.send(LedState::Off).await;
|
||||
Timer::after_millis(400).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn core1_task(mut led: Output<'static>) {
|
||||
info!("Hello from core 1");
|
||||
loop {
|
||||
match CHANNEL.receive().await {
|
||||
LedState::On => led.set_high(),
|
||||
LedState::Off => led.set_low(),
|
||||
}
|
||||
}
|
||||
}
|
161
examples/rp23/src/bin/multiprio.rs
Normal file
161
examples/rp23/src/bin/multiprio.rs
Normal file
@ -0,0 +1,161 @@
|
||||
//! This example showcases how to create multiple Executor instances to run tasks at
|
||||
//! different priority levels.
|
||||
//!
|
||||
//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling
|
||||
//! there's work in the queue, and `wfe` for waiting for work.
|
||||
//!
|
||||
//! Medium and high priority executors run in two interrupts with different priorities.
|
||||
//! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since
|
||||
//! when there's work the interrupt will trigger and run the executor.
|
||||
//!
|
||||
//! Sample output below. Note that high priority ticks can interrupt everything else, and
|
||||
//! medium priority computations can interrupt low priority computations, making them to appear
|
||||
//! to take significantly longer time.
|
||||
//!
|
||||
//! ```not_rust
|
||||
//! [med] Starting long computation
|
||||
//! [med] done in 992 ms
|
||||
//! [high] tick!
|
||||
//! [low] Starting long computation
|
||||
//! [med] Starting long computation
|
||||
//! [high] tick!
|
||||
//! [high] tick!
|
||||
//! [med] done in 993 ms
|
||||
//! [med] Starting long computation
|
||||
//! [high] tick!
|
||||
//! [high] tick!
|
||||
//! [med] done in 993 ms
|
||||
//! [low] done in 3972 ms
|
||||
//! [med] Starting long computation
|
||||
//! [high] tick!
|
||||
//! [high] tick!
|
||||
//! [med] done in 993 ms
|
||||
//! ```
|
||||
//!
|
||||
//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor.
|
||||
//! You will get an output like the following. Note that no computation is ever interrupted.
|
||||
//!
|
||||
//! ```not_rust
|
||||
//! [high] tick!
|
||||
//! [med] Starting long computation
|
||||
//! [med] done in 496 ms
|
||||
//! [low] Starting long computation
|
||||
//! [low] done in 992 ms
|
||||
//! [med] Starting long computation
|
||||
//! [med] done in 496 ms
|
||||
//! [high] tick!
|
||||
//! [low] Starting long computation
|
||||
//! [low] done in 992 ms
|
||||
//! [high] tick!
|
||||
//! [med] Starting long computation
|
||||
//! [med] done in 496 ms
|
||||
//! [high] tick!
|
||||
//! ```
|
||||
//!
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use defmt::{info, unwrap};
|
||||
use embassy_executor::{Executor, InterruptExecutor};
|
||||
use embassy_rp::interrupt;
|
||||
use embassy_rp::interrupt::{InterruptExt, Priority};
|
||||
use embassy_time::{Instant, Timer, TICK_HZ};
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn run_high() {
|
||||
loop {
|
||||
info!(" [high] tick!");
|
||||
Timer::after_ticks(673740).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn run_med() {
|
||||
loop {
|
||||
let start = Instant::now();
|
||||
info!(" [med] Starting long computation");
|
||||
|
||||
// Spin-wait to simulate a long CPU computation
|
||||
embassy_time::block_for(embassy_time::Duration::from_secs(1)); // ~1 second
|
||||
|
||||
let end = Instant::now();
|
||||
let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
|
||||
info!(" [med] done in {} ms", ms);
|
||||
|
||||
Timer::after_ticks(53421).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn run_low() {
|
||||
loop {
|
||||
let start = Instant::now();
|
||||
info!("[low] Starting long computation");
|
||||
|
||||
// Spin-wait to simulate a long CPU computation
|
||||
embassy_time::block_for(embassy_time::Duration::from_secs(2)); // ~2 seconds
|
||||
|
||||
let end = Instant::now();
|
||||
let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ;
|
||||
info!("[low] done in {} ms", ms);
|
||||
|
||||
Timer::after_ticks(82983).await;
|
||||
}
|
||||
}
|
||||
|
||||
static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new();
|
||||
static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new();
|
||||
static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
|
||||
|
||||
#[interrupt]
|
||||
unsafe fn SWI_IRQ_1() {
|
||||
EXECUTOR_HIGH.on_interrupt()
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
unsafe fn SWI_IRQ_0() {
|
||||
EXECUTOR_MED.on_interrupt()
|
||||
}
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
info!("Hello World!");
|
||||
|
||||
let _p = embassy_rp::init(Default::default());
|
||||
|
||||
// High-priority executor: SWI_IRQ_1, priority level 2
|
||||
interrupt::SWI_IRQ_1.set_priority(Priority::P2);
|
||||
let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1);
|
||||
unwrap!(spawner.spawn(run_high()));
|
||||
|
||||
// Medium-priority executor: SWI_IRQ_0, priority level 3
|
||||
interrupt::SWI_IRQ_0.set_priority(Priority::P3);
|
||||
let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0);
|
||||
unwrap!(spawner.spawn(run_med()));
|
||||
|
||||
// Low priority executor: runs in thread mode, using WFE/SEV
|
||||
let executor = EXECUTOR_LOW.init(Executor::new());
|
||||
executor.run(|spawner| {
|
||||
unwrap!(spawner.spawn(run_low()));
|
||||
});
|
||||
}
|
146
examples/rp23/src/bin/pio_async.rs
Normal file
146
examples/rp23/src/bin/pio_async.rs
Normal file
@ -0,0 +1,146 @@
|
||||
//! This example shows powerful PIO module in the RP2040 chip.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::peripherals::PIO0;
|
||||
use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};
|
||||
use fixed::traits::ToFixed;
|
||||
use fixed_macro::types::U56F8;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||
});
|
||||
|
||||
fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) {
|
||||
// Setup sm0
|
||||
|
||||
// Send data serially to pin
|
||||
let prg = pio_proc::pio_asm!(
|
||||
".origin 16",
|
||||
"set pindirs, 1",
|
||||
".wrap_target",
|
||||
"out pins,1 [19]",
|
||||
".wrap",
|
||||
);
|
||||
|
||||
let mut cfg = Config::default();
|
||||
cfg.use_program(&pio.load_program(&prg.program), &[]);
|
||||
let out_pin = pio.make_pio_pin(pin);
|
||||
cfg.set_out_pins(&[&out_pin]);
|
||||
cfg.set_set_pins(&[&out_pin]);
|
||||
cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed();
|
||||
cfg.shift_out.auto_fill = true;
|
||||
sm.set_config(&cfg);
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) {
|
||||
sm.set_enable(true);
|
||||
|
||||
let mut v = 0x0f0caffa;
|
||||
loop {
|
||||
sm.tx().wait_push(v).await;
|
||||
v ^= 0xffff;
|
||||
info!("Pushed {:032b} to FIFO", v);
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) {
|
||||
// Setupm sm1
|
||||
|
||||
// Read 0b10101 repeatedly until ISR is full
|
||||
let prg = pio_proc::pio_asm!(
|
||||
//
|
||||
".origin 8",
|
||||
"set x, 0x15",
|
||||
".wrap_target",
|
||||
"in x, 5 [31]",
|
||||
".wrap",
|
||||
);
|
||||
|
||||
let mut cfg = Config::default();
|
||||
cfg.use_program(&pio.load_program(&prg.program), &[]);
|
||||
cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
|
||||
cfg.shift_in.auto_fill = true;
|
||||
cfg.shift_in.direction = ShiftDirection::Right;
|
||||
sm.set_config(&cfg);
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) {
|
||||
sm.set_enable(true);
|
||||
loop {
|
||||
let rx = sm.rx().wait_pull().await;
|
||||
info!("Pulled {:032b} from FIFO", rx);
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) {
|
||||
// Setup sm2
|
||||
|
||||
// Repeatedly trigger IRQ 3
|
||||
let prg = pio_proc::pio_asm!(
|
||||
".origin 0",
|
||||
".wrap_target",
|
||||
"set x,10",
|
||||
"delay:",
|
||||
"jmp x-- delay [15]",
|
||||
"irq 3 [15]",
|
||||
".wrap",
|
||||
);
|
||||
let mut cfg = Config::default();
|
||||
cfg.use_program(&pio.load_program(&prg.program), &[]);
|
||||
cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
|
||||
sm.set_config(&cfg);
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) {
|
||||
sm.set_enable(true);
|
||||
loop {
|
||||
irq.wait().await;
|
||||
info!("IRQ trigged");
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let pio = p.PIO0;
|
||||
|
||||
let Pio {
|
||||
mut common,
|
||||
irq3,
|
||||
mut sm0,
|
||||
mut sm1,
|
||||
mut sm2,
|
||||
..
|
||||
} = Pio::new(pio, Irqs);
|
||||
|
||||
setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0);
|
||||
setup_pio_task_sm1(&mut common, &mut sm1);
|
||||
setup_pio_task_sm2(&mut common, &mut sm2);
|
||||
spawner.spawn(pio_task_sm0(sm0)).unwrap();
|
||||
spawner.spawn(pio_task_sm1(sm1)).unwrap();
|
||||
spawner.spawn(pio_task_sm2(irq3, sm2)).unwrap();
|
||||
}
|
99
examples/rp23/src/bin/pio_dma.rs
Normal file
99
examples/rp23/src/bin/pio_dma.rs
Normal file
@ -0,0 +1,99 @@
|
||||
//! This example shows powerful PIO module in the RP2040 chip.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_rp::peripherals::PIO0;
|
||||
use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
|
||||
use embassy_rp::{bind_interrupts, Peripheral};
|
||||
use fixed::traits::ToFixed;
|
||||
use fixed_macro::types::U56F8;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||
});
|
||||
|
||||
fn swap_nibbles(v: u32) -> u32 {
|
||||
let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4;
|
||||
let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8;
|
||||
(v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let pio = p.PIO0;
|
||||
let Pio {
|
||||
mut common,
|
||||
sm0: mut sm,
|
||||
..
|
||||
} = Pio::new(pio, Irqs);
|
||||
|
||||
let prg = pio_proc::pio_asm!(
|
||||
".origin 0",
|
||||
"set pindirs,1",
|
||||
".wrap_target",
|
||||
"set y,7",
|
||||
"loop:",
|
||||
"out x,4",
|
||||
"in x,4",
|
||||
"jmp y--, loop",
|
||||
".wrap",
|
||||
);
|
||||
|
||||
let mut cfg = Config::default();
|
||||
cfg.use_program(&common.load_program(&prg.program), &[]);
|
||||
cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();
|
||||
cfg.shift_in = ShiftConfig {
|
||||
auto_fill: true,
|
||||
threshold: 32,
|
||||
direction: ShiftDirection::Left,
|
||||
};
|
||||
cfg.shift_out = ShiftConfig {
|
||||
auto_fill: true,
|
||||
threshold: 32,
|
||||
direction: ShiftDirection::Right,
|
||||
};
|
||||
|
||||
sm.set_config(&cfg);
|
||||
sm.set_enable(true);
|
||||
|
||||
let mut dma_out_ref = p.DMA_CH0.into_ref();
|
||||
let mut dma_in_ref = p.DMA_CH1.into_ref();
|
||||
let mut dout = [0x12345678u32; 29];
|
||||
for i in 1..dout.len() {
|
||||
dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7;
|
||||
}
|
||||
let mut din = [0u32; 29];
|
||||
loop {
|
||||
let (rx, tx) = sm.rx_tx();
|
||||
join(
|
||||
tx.dma_push(dma_out_ref.reborrow(), &dout),
|
||||
rx.dma_pull(dma_in_ref.reborrow(), &mut din),
|
||||
)
|
||||
.await;
|
||||
for i in 0..din.len() {
|
||||
assert_eq!(din[i], swap_nibbles(dout[i]));
|
||||
}
|
||||
info!("Swapped {} words", dout.len());
|
||||
}
|
||||
}
|
256
examples/rp23/src/bin/pio_hd44780.rs
Normal file
256
examples/rp23/src/bin/pio_hd44780.rs
Normal file
@ -0,0 +1,256 @@
|
||||
//! This example shows powerful PIO module in the RP2040 chip to communicate with a HD44780 display.
|
||||
//! See (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf)
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::fmt::Write;
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::dma::{AnyChannel, Channel};
|
||||
use embassy_rp::peripherals::PIO0;
|
||||
use embassy_rp::pio::{
|
||||
Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
|
||||
};
|
||||
use embassy_rp::pwm::{self, Pwm};
|
||||
use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
|
||||
use embassy_time::{Instant, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(pub struct Irqs {
|
||||
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
// this test assumes a 2x16 HD44780 display attached as follow:
|
||||
// rs = PIN0
|
||||
// rw = PIN1
|
||||
// e = PIN2
|
||||
// db4 = PIN3
|
||||
// db5 = PIN4
|
||||
// db6 = PIN5
|
||||
// db7 = PIN6
|
||||
// additionally a pwm signal for a bias voltage charge pump is provided on pin 15,
|
||||
// allowing direct connection of the display to the RP2040 without level shifters.
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let _pwm = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, {
|
||||
let mut c = pwm::Config::default();
|
||||
c.divider = 125.into();
|
||||
c.top = 100;
|
||||
c.compare_b = 50;
|
||||
c
|
||||
});
|
||||
|
||||
let mut hd = HD44780::new(
|
||||
p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6,
|
||||
)
|
||||
.await;
|
||||
|
||||
loop {
|
||||
struct Buf<const N: usize>([u8; N], usize);
|
||||
impl<const N: usize> Write for Buf<N> {
|
||||
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
|
||||
for b in s.as_bytes() {
|
||||
if self.1 >= N {
|
||||
return Err(core::fmt::Error);
|
||||
}
|
||||
self.0[self.1] = *b;
|
||||
self.1 += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
let mut buf = Buf([0; 16], 0);
|
||||
write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap();
|
||||
hd.add_line(&buf.0[0..buf.1]).await;
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HD44780<'l> {
|
||||
dma: PeripheralRef<'l, AnyChannel>,
|
||||
sm: StateMachine<'l, PIO0, 0>,
|
||||
|
||||
buf: [u8; 40],
|
||||
}
|
||||
|
||||
impl<'l> HD44780<'l> {
|
||||
pub async fn new(
|
||||
pio: impl Peripheral<P = PIO0> + 'l,
|
||||
irq: Irqs,
|
||||
dma: impl Peripheral<P = impl Channel> + 'l,
|
||||
rs: impl PioPin,
|
||||
rw: impl PioPin,
|
||||
e: impl PioPin,
|
||||
db4: impl PioPin,
|
||||
db5: impl PioPin,
|
||||
db6: impl PioPin,
|
||||
db7: impl PioPin,
|
||||
) -> HD44780<'l> {
|
||||
into_ref!(dma);
|
||||
|
||||
let Pio {
|
||||
mut common,
|
||||
mut irq0,
|
||||
mut sm0,
|
||||
..
|
||||
} = Pio::new(pio, irq);
|
||||
|
||||
// takes command words (<wait:24> <command:4> <0:4>)
|
||||
let prg = pio_proc::pio_asm!(
|
||||
r#"
|
||||
.side_set 1 opt
|
||||
.origin 20
|
||||
|
||||
loop:
|
||||
out x, 24
|
||||
delay:
|
||||
jmp x--, delay
|
||||
out pins, 4 side 1
|
||||
out null, 4 side 0
|
||||
jmp !osre, loop
|
||||
irq 0
|
||||
"#,
|
||||
);
|
||||
|
||||
let rs = common.make_pio_pin(rs);
|
||||
let rw = common.make_pio_pin(rw);
|
||||
let e = common.make_pio_pin(e);
|
||||
let db4 = common.make_pio_pin(db4);
|
||||
let db5 = common.make_pio_pin(db5);
|
||||
let db6 = common.make_pio_pin(db6);
|
||||
let db7 = common.make_pio_pin(db7);
|
||||
|
||||
sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
|
||||
|
||||
let mut cfg = Config::default();
|
||||
cfg.use_program(&common.load_program(&prg.program), &[&e]);
|
||||
cfg.clock_divider = 125u8.into();
|
||||
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
||||
cfg.shift_out = ShiftConfig {
|
||||
auto_fill: true,
|
||||
direction: ShiftDirection::Left,
|
||||
threshold: 32,
|
||||
};
|
||||
cfg.fifo_join = FifoJoin::TxOnly;
|
||||
sm0.set_config(&cfg);
|
||||
|
||||
sm0.set_enable(true);
|
||||
// init to 8 bit thrice
|
||||
sm0.tx().push((50000 << 8) | 0x30);
|
||||
sm0.tx().push((5000 << 8) | 0x30);
|
||||
sm0.tx().push((200 << 8) | 0x30);
|
||||
// init 4 bit
|
||||
sm0.tx().push((200 << 8) | 0x20);
|
||||
// set font and lines
|
||||
sm0.tx().push((50 << 8) | 0x20);
|
||||
sm0.tx().push(0b1100_0000);
|
||||
|
||||
irq0.wait().await;
|
||||
sm0.set_enable(false);
|
||||
|
||||
// takes command sequences (<rs:1> <count:7>, data...)
|
||||
// many side sets are only there to free up a delay bit!
|
||||
let prg = pio_proc::pio_asm!(
|
||||
r#"
|
||||
.origin 27
|
||||
.side_set 1
|
||||
|
||||
.wrap_target
|
||||
pull side 0
|
||||
out x 1 side 0 ; !rs
|
||||
out y 7 side 0 ; #data - 1
|
||||
|
||||
; rs/rw to e: >= 60ns
|
||||
; e high time: >= 500ns
|
||||
; e low time: >= 500ns
|
||||
; read data valid after e falling: ~5ns
|
||||
; write data hold after e falling: ~10ns
|
||||
|
||||
loop:
|
||||
pull side 0
|
||||
jmp !x data side 0
|
||||
command:
|
||||
set pins 0b00 side 0
|
||||
jmp shift side 0
|
||||
data:
|
||||
set pins 0b01 side 0
|
||||
shift:
|
||||
out pins 4 side 1 [9]
|
||||
nop side 0 [9]
|
||||
out pins 4 side 1 [9]
|
||||
mov osr null side 0 [7]
|
||||
out pindirs 4 side 0
|
||||
set pins 0b10 side 0
|
||||
busy:
|
||||
nop side 1 [9]
|
||||
jmp pin more side 0 [9]
|
||||
mov osr ~osr side 1 [9]
|
||||
nop side 0 [4]
|
||||
out pindirs 4 side 0
|
||||
jmp y-- loop side 0
|
||||
.wrap
|
||||
more:
|
||||
nop side 1 [9]
|
||||
jmp busy side 0 [9]
|
||||
"#
|
||||
);
|
||||
|
||||
let mut cfg = Config::default();
|
||||
cfg.use_program(&common.load_program(&prg.program), &[&e]);
|
||||
cfg.clock_divider = 8u8.into(); // ~64ns/insn
|
||||
cfg.set_jmp_pin(&db7);
|
||||
cfg.set_set_pins(&[&rs, &rw]);
|
||||
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
||||
cfg.shift_out.direction = ShiftDirection::Left;
|
||||
cfg.fifo_join = FifoJoin::TxOnly;
|
||||
sm0.set_config(&cfg);
|
||||
|
||||
sm0.set_enable(true);
|
||||
|
||||
// display on and cursor on and blinking, reset display
|
||||
sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
|
||||
|
||||
Self {
|
||||
dma: dma.map_into(),
|
||||
sm: sm0,
|
||||
buf: [0x20; 40],
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn add_line(&mut self, s: &[u8]) {
|
||||
// move cursor to 0:0, prepare 16 characters
|
||||
self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);
|
||||
// move line 2 up
|
||||
self.buf.copy_within(22..38, 3);
|
||||
// move cursor to 1:0, prepare 16 characters
|
||||
self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);
|
||||
// file line 2 with spaces
|
||||
self.buf[22..38].fill(0x20);
|
||||
// copy input line
|
||||
let len = s.len().min(16);
|
||||
self.buf[22..22 + len].copy_from_slice(&s[0..len]);
|
||||
// set cursor to 1:15
|
||||
self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
|
||||
|
||||
self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await;
|
||||
}
|
||||
}
|
141
examples/rp23/src/bin/pio_i2s.rs
Normal file
141
examples/rp23/src/bin/pio_i2s.rs
Normal file
@ -0,0 +1,141 @@
|
||||
//! This example shows generating audio and sending it to a connected i2s DAC using the PIO
|
||||
//! module of the RP2040.
|
||||
//!
|
||||
//! Connect the i2s DAC as follows:
|
||||
//! bclk : GPIO 18
|
||||
//! lrc : GPIO 19
|
||||
//! din : GPIO 20
|
||||
//! Then hold down the boot select button to trigger a rising triangle waveform.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::mem;
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::peripherals::PIO0;
|
||||
use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
|
||||
use embassy_rp::{bind_interrupts, Peripheral};
|
||||
use fixed::traits::ToFixed;
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||
});
|
||||
|
||||
const SAMPLE_RATE: u32 = 48_000;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
// Setup pio state machine for i2s output
|
||||
let mut pio = Pio::new(p.PIO0, Irqs);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let pio_program = pio_proc::pio_asm!(
|
||||
".side_set 2",
|
||||
" set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock
|
||||
"left_data:",
|
||||
" out pins, 1 side 0b00",
|
||||
" jmp x-- left_data side 0b01",
|
||||
" out pins 1 side 0b10",
|
||||
" set x, 14 side 0b11",
|
||||
"right_data:",
|
||||
" out pins 1 side 0b10",
|
||||
" jmp x-- right_data side 0b11",
|
||||
" out pins 1 side 0b00",
|
||||
);
|
||||
|
||||
let bit_clock_pin = p.PIN_18;
|
||||
let left_right_clock_pin = p.PIN_19;
|
||||
let data_pin = p.PIN_20;
|
||||
|
||||
let data_pin = pio.common.make_pio_pin(data_pin);
|
||||
let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin);
|
||||
let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin);
|
||||
|
||||
let cfg = {
|
||||
let mut cfg = Config::default();
|
||||
cfg.use_program(
|
||||
&pio.common.load_program(&pio_program.program),
|
||||
&[&bit_clock_pin, &left_right_clock_pin],
|
||||
);
|
||||
cfg.set_out_pins(&[&data_pin]);
|
||||
const BIT_DEPTH: u32 = 16;
|
||||
const CHANNELS: u32 = 2;
|
||||
let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS;
|
||||
cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed();
|
||||
cfg.shift_out = ShiftConfig {
|
||||
threshold: 32,
|
||||
direction: ShiftDirection::Left,
|
||||
auto_fill: true,
|
||||
};
|
||||
// join fifos to have twice the time to start the next dma transfer
|
||||
cfg.fifo_join = FifoJoin::TxOnly;
|
||||
cfg
|
||||
};
|
||||
pio.sm0.set_config(&cfg);
|
||||
pio.sm0.set_pin_dirs(
|
||||
embassy_rp::pio::Direction::Out,
|
||||
&[&data_pin, &left_right_clock_pin, &bit_clock_pin],
|
||||
);
|
||||
|
||||
// create two audio buffers (back and front) which will take turns being
|
||||
// filled with new audio data and being sent to the pio fifo using dma
|
||||
const BUFFER_SIZE: usize = 960;
|
||||
static DMA_BUFFER: StaticCell<[u32; BUFFER_SIZE * 2]> = StaticCell::new();
|
||||
let dma_buffer = DMA_BUFFER.init_with(|| [0u32; BUFFER_SIZE * 2]);
|
||||
let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);
|
||||
|
||||
// start pio state machine
|
||||
pio.sm0.set_enable(true);
|
||||
let tx = pio.sm0.tx();
|
||||
let mut dma_ref = p.DMA_CH0.into_ref();
|
||||
|
||||
let mut fade_value: i32 = 0;
|
||||
let mut phase: i32 = 0;
|
||||
|
||||
loop {
|
||||
// trigger transfer of front buffer data to the pio fifo
|
||||
// but don't await the returned future, yet
|
||||
let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer);
|
||||
|
||||
// fade in audio
|
||||
let fade_target = i32::MAX;
|
||||
|
||||
// fill back buffer with fresh audio samples before awaiting the dma future
|
||||
for s in back_buffer.iter_mut() {
|
||||
// exponential approach of fade_value => fade_target
|
||||
fade_value += (fade_target - fade_value) >> 14;
|
||||
// generate triangle wave with amplitude and frequency based on fade value
|
||||
phase = (phase + (fade_value >> 22)) & 0xffff;
|
||||
let triangle_sample = (phase as i16 as i32).abs() - 16384;
|
||||
let sample = (triangle_sample * (fade_value >> 15)) >> 16;
|
||||
// duplicate mono sample into lower and upper half of dma word
|
||||
*s = (sample as u16 as u32) * 0x10001;
|
||||
}
|
||||
|
||||
// now await the dma future. once the dma finishes, the next buffer needs to be queued
|
||||
// within DMA_DEPTH / SAMPLE_RATE = 8 / 48000 seconds = 166us
|
||||
dma_future.await;
|
||||
mem::swap(&mut back_buffer, &mut front_buffer);
|
||||
}
|
||||
}
|
134
examples/rp23/src/bin/pio_pwm.rs
Normal file
134
examples/rp23/src/bin/pio_pwm.rs
Normal file
@ -0,0 +1,134 @@
|
||||
//! This example shows how to create a pwm using the PIO module in the RP2040 chip.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
use core::time::Duration;
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio::Level;
|
||||
use embassy_rp::peripherals::PIO0;
|
||||
use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine};
|
||||
use embassy_rp::{bind_interrupts, clocks};
|
||||
use embassy_time::Timer;
|
||||
use pio::InstructionOperands;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
const REFRESH_INTERVAL: u64 = 20000;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||
});
|
||||
|
||||
pub fn to_pio_cycles(duration: Duration) -> u32 {
|
||||
(clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
|
||||
}
|
||||
|
||||
pub struct PwmPio<'d, T: Instance, const SM: usize> {
|
||||
sm: StateMachine<'d, T, SM>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
|
||||
pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
|
||||
let prg = pio_proc::pio_asm!(
|
||||
".side_set 1 opt"
|
||||
"pull noblock side 0"
|
||||
"mov x, osr"
|
||||
"mov y, isr"
|
||||
"countloop:"
|
||||
"jmp x!=y noset"
|
||||
"jmp skip side 1"
|
||||
"noset:"
|
||||
"nop"
|
||||
"skip:"
|
||||
"jmp y-- countloop"
|
||||
);
|
||||
|
||||
pio.load_program(&prg.program);
|
||||
let pin = pio.make_pio_pin(pin);
|
||||
sm.set_pins(Level::High, &[&pin]);
|
||||
sm.set_pin_dirs(Direction::Out, &[&pin]);
|
||||
|
||||
let mut cfg = Config::default();
|
||||
cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
|
||||
|
||||
sm.set_config(&cfg);
|
||||
|
||||
Self { sm }
|
||||
}
|
||||
|
||||
pub fn start(&mut self) {
|
||||
self.sm.set_enable(true);
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.sm.set_enable(false);
|
||||
}
|
||||
|
||||
pub fn set_period(&mut self, duration: Duration) {
|
||||
let is_enabled = self.sm.is_enabled();
|
||||
while !self.sm.tx().empty() {} // Make sure that the queue is empty
|
||||
self.sm.set_enable(false);
|
||||
self.sm.tx().push(to_pio_cycles(duration));
|
||||
unsafe {
|
||||
self.sm.exec_instr(
|
||||
InstructionOperands::PULL {
|
||||
if_empty: false,
|
||||
block: false,
|
||||
}
|
||||
.encode(),
|
||||
);
|
||||
self.sm.exec_instr(
|
||||
InstructionOperands::OUT {
|
||||
destination: ::pio::OutDestination::ISR,
|
||||
bit_count: 32,
|
||||
}
|
||||
.encode(),
|
||||
);
|
||||
};
|
||||
if is_enabled {
|
||||
self.sm.set_enable(true) // Enable if previously enabled
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_level(&mut self, level: u32) {
|
||||
self.sm.tx().push(level);
|
||||
}
|
||||
|
||||
pub fn write(&mut self, duration: Duration) {
|
||||
self.set_level(to_pio_cycles(duration));
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
|
||||
|
||||
// Note that PIN_25 is the led pin on the Pico
|
||||
let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25);
|
||||
pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));
|
||||
pwm_pio.start();
|
||||
|
||||
let mut duration = 0;
|
||||
loop {
|
||||
duration = (duration + 1) % 1000;
|
||||
pwm_pio.write(Duration::from_micros(duration));
|
||||
Timer::after_millis(1).await;
|
||||
}
|
||||
}
|
96
examples/rp23/src/bin/pio_rotary_encoder.rs
Normal file
96
examples/rp23/src/bin/pio_rotary_encoder.rs
Normal file
@ -0,0 +1,96 @@
|
||||
//! This example shows how to use the PIO module in the RP2040 to read a quadrature rotary encoder.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio::Pull;
|
||||
use embassy_rp::peripherals::PIO0;
|
||||
use embassy_rp::{bind_interrupts, pio};
|
||||
use fixed::traits::ToFixed;
|
||||
use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||
});
|
||||
|
||||
pub struct PioEncoder<'d, T: Instance, const SM: usize> {
|
||||
sm: StateMachine<'d, T, SM>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {
|
||||
pub fn new(
|
||||
pio: &mut Common<'d, T>,
|
||||
mut sm: StateMachine<'d, T, SM>,
|
||||
pin_a: impl PioPin,
|
||||
pin_b: impl PioPin,
|
||||
) -> Self {
|
||||
let mut pin_a = pio.make_pio_pin(pin_a);
|
||||
let mut pin_b = pio.make_pio_pin(pin_b);
|
||||
pin_a.set_pull(Pull::Up);
|
||||
pin_b.set_pull(Pull::Up);
|
||||
sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
|
||||
|
||||
let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",);
|
||||
|
||||
let mut cfg = Config::default();
|
||||
cfg.set_in_pins(&[&pin_a, &pin_b]);
|
||||
cfg.fifo_join = FifoJoin::RxOnly;
|
||||
cfg.shift_in.direction = ShiftDirection::Left;
|
||||
cfg.clock_divider = 10_000.to_fixed();
|
||||
cfg.use_program(&pio.load_program(&prg.program), &[]);
|
||||
sm.set_config(&cfg);
|
||||
sm.set_enable(true);
|
||||
Self { sm }
|
||||
}
|
||||
|
||||
pub async fn read(&mut self) -> Direction {
|
||||
loop {
|
||||
match self.sm.rx().wait_pull().await {
|
||||
0 => return Direction::CounterClockwise,
|
||||
1 => return Direction::Clockwise,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Direction {
|
||||
Clockwise,
|
||||
CounterClockwise,
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
|
||||
|
||||
let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);
|
||||
|
||||
let mut count = 0;
|
||||
loop {
|
||||
info!("Count: {}", count);
|
||||
count += match encoder.read().await {
|
||||
Direction::Clockwise => 1,
|
||||
Direction::CounterClockwise => -1,
|
||||
};
|
||||
}
|
||||
}
|
224
examples/rp23/src/bin/pio_servo.rs
Normal file
224
examples/rp23/src/bin/pio_servo.rs
Normal file
@ -0,0 +1,224 @@
|
||||
//! This example shows how to create a pwm using the PIO module in the RP2040 chip.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
use core::time::Duration;
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio::Level;
|
||||
use embassy_rp::peripherals::PIO0;
|
||||
use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine};
|
||||
use embassy_rp::{bind_interrupts, clocks};
|
||||
use embassy_time::Timer;
|
||||
use pio::InstructionOperands;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo
|
||||
const DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo
|
||||
const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical
|
||||
const REFRESH_INTERVAL: u64 = 20000; // The period of each cycle
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||
});
|
||||
|
||||
pub fn to_pio_cycles(duration: Duration) -> u32 {
|
||||
(clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
|
||||
}
|
||||
|
||||
pub struct PwmPio<'d, T: Instance, const SM: usize> {
|
||||
sm: StateMachine<'d, T, SM>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
|
||||
pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
|
||||
let prg = pio_proc::pio_asm!(
|
||||
".side_set 1 opt"
|
||||
"pull noblock side 0"
|
||||
"mov x, osr"
|
||||
"mov y, isr"
|
||||
"countloop:"
|
||||
"jmp x!=y noset"
|
||||
"jmp skip side 1"
|
||||
"noset:"
|
||||
"nop"
|
||||
"skip:"
|
||||
"jmp y-- countloop"
|
||||
);
|
||||
|
||||
pio.load_program(&prg.program);
|
||||
let pin = pio.make_pio_pin(pin);
|
||||
sm.set_pins(Level::High, &[&pin]);
|
||||
sm.set_pin_dirs(Direction::Out, &[&pin]);
|
||||
|
||||
let mut cfg = Config::default();
|
||||
cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
|
||||
|
||||
sm.set_config(&cfg);
|
||||
|
||||
Self { sm }
|
||||
}
|
||||
|
||||
pub fn start(&mut self) {
|
||||
self.sm.set_enable(true);
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.sm.set_enable(false);
|
||||
}
|
||||
|
||||
pub fn set_period(&mut self, duration: Duration) {
|
||||
let is_enabled = self.sm.is_enabled();
|
||||
while !self.sm.tx().empty() {} // Make sure that the queue is empty
|
||||
self.sm.set_enable(false);
|
||||
self.sm.tx().push(to_pio_cycles(duration));
|
||||
unsafe {
|
||||
self.sm.exec_instr(
|
||||
InstructionOperands::PULL {
|
||||
if_empty: false,
|
||||
block: false,
|
||||
}
|
||||
.encode(),
|
||||
);
|
||||
self.sm.exec_instr(
|
||||
InstructionOperands::OUT {
|
||||
destination: ::pio::OutDestination::ISR,
|
||||
bit_count: 32,
|
||||
}
|
||||
.encode(),
|
||||
);
|
||||
};
|
||||
if is_enabled {
|
||||
self.sm.set_enable(true) // Enable if previously enabled
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_level(&mut self, level: u32) {
|
||||
self.sm.tx().push(level);
|
||||
}
|
||||
|
||||
pub fn write(&mut self, duration: Duration) {
|
||||
self.set_level(to_pio_cycles(duration));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
|
||||
pwm: PwmPio<'d, T, SM>,
|
||||
period: Duration,
|
||||
min_pulse_width: Duration,
|
||||
max_pulse_width: Duration,
|
||||
max_degree_rotation: u64,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
|
||||
pub fn new(pwm: PwmPio<'d, T, SM>) -> Self {
|
||||
Self {
|
||||
pwm,
|
||||
period: Duration::from_micros(REFRESH_INTERVAL),
|
||||
min_pulse_width: Duration::from_micros(DEFAULT_MIN_PULSE_WIDTH),
|
||||
max_pulse_width: Duration::from_micros(DEFAULT_MAX_PULSE_WIDTH),
|
||||
max_degree_rotation: DEFAULT_MAX_DEGREE_ROTATION,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_period(mut self, duration: Duration) -> Self {
|
||||
self.period = duration;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_min_pulse_width(mut self, duration: Duration) -> Self {
|
||||
self.min_pulse_width = duration;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_max_pulse_width(mut self, duration: Duration) -> Self {
|
||||
self.max_pulse_width = duration;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_max_degree_rotation(mut self, degree: u64) -> Self {
|
||||
self.max_degree_rotation = degree;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> Servo<'d, T, SM> {
|
||||
self.pwm.set_period(self.period);
|
||||
Servo {
|
||||
pwm: self.pwm,
|
||||
min_pulse_width: self.min_pulse_width,
|
||||
max_pulse_width: self.max_pulse_width,
|
||||
max_degree_rotation: self.max_degree_rotation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Servo<'d, T: Instance, const SM: usize> {
|
||||
pwm: PwmPio<'d, T, SM>,
|
||||
min_pulse_width: Duration,
|
||||
max_pulse_width: Duration,
|
||||
max_degree_rotation: u64,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const SM: usize> Servo<'d, T, SM> {
|
||||
pub fn start(&mut self) {
|
||||
self.pwm.start();
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.pwm.stop();
|
||||
}
|
||||
|
||||
pub fn write_time(&mut self, duration: Duration) {
|
||||
self.pwm.write(duration);
|
||||
}
|
||||
|
||||
pub fn rotate(&mut self, degree: u64) {
|
||||
let degree_per_nano_second = (self.max_pulse_width.as_nanos() as u64 - self.min_pulse_width.as_nanos() as u64)
|
||||
/ self.max_degree_rotation;
|
||||
let mut duration =
|
||||
Duration::from_nanos(degree * degree_per_nano_second + self.min_pulse_width.as_nanos() as u64);
|
||||
if self.max_pulse_width < duration {
|
||||
duration = self.max_pulse_width;
|
||||
}
|
||||
|
||||
self.pwm.write(duration);
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
|
||||
|
||||
let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1);
|
||||
let mut servo = ServoBuilder::new(pwm_pio)
|
||||
.set_max_degree_rotation(120) // Example of adjusting values for MG996R servo
|
||||
.set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.
|
||||
.set_max_pulse_width(Duration::from_micros(2600)) // Along with this value.
|
||||
.build();
|
||||
|
||||
servo.start();
|
||||
|
||||
let mut degree = 0;
|
||||
loop {
|
||||
degree = (degree + 1) % 120;
|
||||
servo.rotate(degree);
|
||||
Timer::after_millis(50).await;
|
||||
}
|
||||
}
|
184
examples/rp23/src/bin/pio_stepper.rs
Normal file
184
examples/rp23/src/bin/pio_stepper.rs
Normal file
@ -0,0 +1,184 @@
|
||||
//! This example shows how to use the PIO module in the RP2040 to implement a stepper motor driver
|
||||
//! for a 5-wire stepper such as the 28BYJ-48. You can halt an ongoing rotation by dropping the future.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
use core::mem::{self, MaybeUninit};
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::peripherals::PIO0;
|
||||
use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine};
|
||||
use embassy_time::{with_timeout, Duration, Timer};
|
||||
use fixed::traits::ToFixed;
|
||||
use fixed::types::extra::U8;
|
||||
use fixed::FixedU32;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||
});
|
||||
|
||||
pub struct PioStepper<'d, T: Instance, const SM: usize> {
|
||||
irq: Irq<'d, T, SM>,
|
||||
sm: StateMachine<'d, T, SM>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
|
||||
pub fn new(
|
||||
pio: &mut Common<'d, T>,
|
||||
mut sm: StateMachine<'d, T, SM>,
|
||||
irq: Irq<'d, T, SM>,
|
||||
pin0: impl PioPin,
|
||||
pin1: impl PioPin,
|
||||
pin2: impl PioPin,
|
||||
pin3: impl PioPin,
|
||||
) -> Self {
|
||||
let prg = pio_proc::pio_asm!(
|
||||
"pull block",
|
||||
"mov x, osr",
|
||||
"pull block",
|
||||
"mov y, osr",
|
||||
"jmp !x end",
|
||||
"loop:",
|
||||
"jmp !osre step",
|
||||
"mov osr, y",
|
||||
"step:",
|
||||
"out pins, 4 [31]"
|
||||
"jmp x-- loop",
|
||||
"end:",
|
||||
"irq 0 rel"
|
||||
);
|
||||
let pin0 = pio.make_pio_pin(pin0);
|
||||
let pin1 = pio.make_pio_pin(pin1);
|
||||
let pin2 = pio.make_pio_pin(pin2);
|
||||
let pin3 = pio.make_pio_pin(pin3);
|
||||
sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
|
||||
let mut cfg = Config::default();
|
||||
cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
|
||||
cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
|
||||
cfg.use_program(&pio.load_program(&prg.program), &[]);
|
||||
sm.set_config(&cfg);
|
||||
sm.set_enable(true);
|
||||
Self { irq, sm }
|
||||
}
|
||||
|
||||
// Set pulse frequency
|
||||
pub fn set_frequency(&mut self, freq: u32) {
|
||||
let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
|
||||
assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
|
||||
assert!(clock_divider >= 1, "clkdiv must be >= 1");
|
||||
self.sm.set_clock_divider(clock_divider);
|
||||
self.sm.clkdiv_restart();
|
||||
}
|
||||
|
||||
// Full step, one phase
|
||||
pub async fn step(&mut self, steps: i32) {
|
||||
if steps > 0 {
|
||||
self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await
|
||||
} else {
|
||||
self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await
|
||||
}
|
||||
}
|
||||
|
||||
// Full step, two phase
|
||||
pub async fn step2(&mut self, steps: i32) {
|
||||
if steps > 0 {
|
||||
self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await
|
||||
} else {
|
||||
self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await
|
||||
}
|
||||
}
|
||||
|
||||
// Half step
|
||||
pub async fn step_half(&mut self, steps: i32) {
|
||||
if steps > 0 {
|
||||
self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await
|
||||
} else {
|
||||
self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(&mut self, steps: i32, pattern: u32) {
|
||||
self.sm.tx().wait_push(steps as u32).await;
|
||||
self.sm.tx().wait_push(pattern).await;
|
||||
let drop = OnDrop::new(|| {
|
||||
self.sm.clear_fifos();
|
||||
unsafe {
|
||||
self.sm.exec_instr(
|
||||
pio::InstructionOperands::JMP {
|
||||
address: 0,
|
||||
condition: pio::JmpCondition::Always,
|
||||
}
|
||||
.encode(),
|
||||
);
|
||||
}
|
||||
});
|
||||
self.irq.wait().await;
|
||||
drop.defuse();
|
||||
}
|
||||
}
|
||||
|
||||
struct OnDrop<F: FnOnce()> {
|
||||
f: MaybeUninit<F>,
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> OnDrop<F> {
|
||||
pub fn new(f: F) -> Self {
|
||||
Self { f: MaybeUninit::new(f) }
|
||||
}
|
||||
|
||||
pub fn defuse(self) {
|
||||
mem::forget(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.f.as_ptr().read()() }
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let Pio {
|
||||
mut common, irq0, sm0, ..
|
||||
} = Pio::new(p.PIO0, Irqs);
|
||||
|
||||
let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7);
|
||||
stepper.set_frequency(120);
|
||||
loop {
|
||||
info!("CW full steps");
|
||||
stepper.step(1000).await;
|
||||
|
||||
info!("CCW full steps, drop after 1 sec");
|
||||
if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await {
|
||||
info!("Time's up!");
|
||||
Timer::after(Duration::from_secs(1)).await;
|
||||
}
|
||||
|
||||
info!("CW half steps");
|
||||
stepper.step_half(1000).await;
|
||||
|
||||
info!("CCW half steps");
|
||||
stepper.step_half(-1000).await;
|
||||
}
|
||||
}
|
177
examples/rp23/src/bin/pio_ws2812.rs
Normal file
177
examples/rp23/src/bin/pio_ws2812.rs
Normal file
@ -0,0 +1,177 @@
|
||||
//! This example shows powerful PIO module in the RP2040 chip to communicate with WS2812 LED modules.
|
||||
//! See (https://www.sparkfun.com/categories/tags/ws2812)
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::dma::{AnyChannel, Channel};
|
||||
use embassy_rp::peripherals::PIO0;
|
||||
use embassy_rp::pio::{
|
||||
Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
|
||||
};
|
||||
use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
|
||||
use embassy_time::{Duration, Ticker, Timer};
|
||||
use fixed::types::U24F8;
|
||||
use fixed_macro::fixed;
|
||||
use smart_leds::RGB8;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||
});
|
||||
|
||||
pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> {
|
||||
dma: PeripheralRef<'d, AnyChannel>,
|
||||
sm: StateMachine<'d, P, S>,
|
||||
}
|
||||
|
||||
impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
|
||||
pub fn new(
|
||||
pio: &mut Common<'d, P>,
|
||||
mut sm: StateMachine<'d, P, S>,
|
||||
dma: impl Peripheral<P = impl Channel> + 'd,
|
||||
pin: impl PioPin,
|
||||
) -> Self {
|
||||
into_ref!(dma);
|
||||
|
||||
// Setup sm0
|
||||
|
||||
// prepare the PIO program
|
||||
let side_set = pio::SideSet::new(false, 1, false);
|
||||
let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
|
||||
|
||||
const T1: u8 = 2; // start bit
|
||||
const T2: u8 = 5; // data bit
|
||||
const T3: u8 = 3; // stop bit
|
||||
const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
|
||||
|
||||
let mut wrap_target = a.label();
|
||||
let mut wrap_source = a.label();
|
||||
let mut do_zero = a.label();
|
||||
a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
|
||||
a.bind(&mut wrap_target);
|
||||
// Do stop bit
|
||||
a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
|
||||
// Do start bit
|
||||
a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
|
||||
// Do data bit = 1
|
||||
a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
|
||||
a.bind(&mut do_zero);
|
||||
// Do data bit = 0
|
||||
a.nop_with_delay_and_side_set(T2 - 1, 0);
|
||||
a.bind(&mut wrap_source);
|
||||
|
||||
let prg = a.assemble_with_wrap(wrap_source, wrap_target);
|
||||
let mut cfg = Config::default();
|
||||
|
||||
// Pin config
|
||||
let out_pin = pio.make_pio_pin(pin);
|
||||
cfg.set_out_pins(&[&out_pin]);
|
||||
cfg.set_set_pins(&[&out_pin]);
|
||||
|
||||
cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
|
||||
|
||||
// Clock config, measured in kHz to avoid overflows
|
||||
// TODO CLOCK_FREQ should come from embassy_rp
|
||||
let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000);
|
||||
let ws2812_freq = fixed!(800: U24F8);
|
||||
let bit_freq = ws2812_freq * CYCLES_PER_BIT;
|
||||
cfg.clock_divider = clock_freq / bit_freq;
|
||||
|
||||
// FIFO config
|
||||
cfg.fifo_join = FifoJoin::TxOnly;
|
||||
cfg.shift_out = ShiftConfig {
|
||||
auto_fill: true,
|
||||
threshold: 24,
|
||||
direction: ShiftDirection::Left,
|
||||
};
|
||||
|
||||
sm.set_config(&cfg);
|
||||
sm.set_enable(true);
|
||||
|
||||
Self {
|
||||
dma: dma.map_into(),
|
||||
sm,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn write(&mut self, colors: &[RGB8; N]) {
|
||||
// Precompute the word bytes from the colors
|
||||
let mut words = [0u32; N];
|
||||
for i in 0..N {
|
||||
let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8);
|
||||
words[i] = word;
|
||||
}
|
||||
|
||||
// DMA transfer
|
||||
self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
|
||||
|
||||
Timer::after_micros(55).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Input a value 0 to 255 to get a color value
|
||||
/// The colours are a transition r - g - b - back to r.
|
||||
fn wheel(mut wheel_pos: u8) -> RGB8 {
|
||||
wheel_pos = 255 - wheel_pos;
|
||||
if wheel_pos < 85 {
|
||||
return (255 - wheel_pos * 3, 0, wheel_pos * 3).into();
|
||||
}
|
||||
if wheel_pos < 170 {
|
||||
wheel_pos -= 85;
|
||||
return (0, wheel_pos * 3, 255 - wheel_pos * 3).into();
|
||||
}
|
||||
wheel_pos -= 170;
|
||||
(wheel_pos * 3, 255 - wheel_pos * 3, 0).into()
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
info!("Start");
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
|
||||
|
||||
// This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit
|
||||
// feather boards for the 2040 both have one built in.
|
||||
const NUM_LEDS: usize = 1;
|
||||
let mut data = [RGB8::default(); NUM_LEDS];
|
||||
|
||||
// Common neopixel pins:
|
||||
// Thing plus: 8
|
||||
// Adafruit Feather: 16; Adafruit Feather+RFM95: 4
|
||||
let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16);
|
||||
|
||||
// Loop forever making RGB values and pushing them out to the WS2812.
|
||||
let mut ticker = Ticker::every(Duration::from_millis(10));
|
||||
loop {
|
||||
for j in 0..(256 * 5) {
|
||||
debug!("New Colors:");
|
||||
for i in 0..NUM_LEDS {
|
||||
data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8);
|
||||
debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b);
|
||||
}
|
||||
ws2812.write(&data).await;
|
||||
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
}
|
45
examples/rp23/src/bin/pwm.rs
Normal file
45
examples/rp23/src/bin/pwm.rs
Normal file
@ -0,0 +1,45 @@
|
||||
//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip.
|
||||
//!
|
||||
//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::pwm::{Config, Pwm};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let mut c: Config = Default::default();
|
||||
c.top = 0x8000;
|
||||
c.compare_b = 8;
|
||||
let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone());
|
||||
|
||||
loop {
|
||||
info!("current LED duty cycle: {}/32768", c.compare_b);
|
||||
Timer::after_secs(1).await;
|
||||
c.compare_b = c.compare_b.rotate_left(4);
|
||||
pwm.set_config(&c);
|
||||
}
|
||||
}
|
42
examples/rp23/src/bin/pwm_input.rs
Normal file
42
examples/rp23/src/bin/pwm_input.rs
Normal file
@ -0,0 +1,42 @@
|
||||
//! This example shows how to use the PWM module to measure the frequency of an input signal.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio::Pull;
|
||||
use embassy_rp::pwm::{Config, InputMode, Pwm};
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let cfg: Config = Default::default();
|
||||
let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, Pull::None, InputMode::RisingEdge, cfg);
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||
loop {
|
||||
info!("Input frequency: {} Hz", pwm.counter());
|
||||
pwm.set_counter(0);
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
47
examples/rp23/src/bin/rosc.rs
Normal file
47
examples/rp23/src/bin/rosc.rs
Normal file
@ -0,0 +1,47 @@
|
||||
//! This example test the RP Pico on board LED.
|
||||
//!
|
||||
//! It does not work with the RP Pico W board. See wifi_blinky.rs.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::{clocks, gpio};
|
||||
use embassy_time::Timer;
|
||||
use gpio::{Level, Output};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = embassy_rp::config::Config::default();
|
||||
config.clocks = clocks::ClockConfig::rosc();
|
||||
let p = embassy_rp::init(config);
|
||||
let mut led = Output::new(p.PIN_25, Level::Low);
|
||||
|
||||
loop {
|
||||
info!("led on!");
|
||||
led.set_high();
|
||||
Timer::after_secs(1).await;
|
||||
|
||||
info!("led off!");
|
||||
led.set_low();
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
131
examples/rp23/src/bin/shared_bus.rs
Normal file
131
examples/rp23/src/bin/shared_bus.rs
Normal file
@ -0,0 +1,131 @@
|
||||
//! This example shows how to share (async) I2C and SPI buses between multiple devices.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice;
|
||||
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::gpio::{AnyPin, Level, Output};
|
||||
use embassy_rp::i2c::{self, I2c, InterruptHandler};
|
||||
use embassy_rp::peripherals::{I2C1, SPI1};
|
||||
use embassy_rp::spi::{self, Spi};
|
||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use embassy_time::Timer;
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
type Spi1Bus = Mutex<NoopRawMutex, Spi<'static, SPI1, spi::Async>>;
|
||||
type I2c1Bus = Mutex<NoopRawMutex, I2c<'static, I2C1, i2c::Async>>;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C1_IRQ => InterruptHandler<I2C1>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Here we go!");
|
||||
|
||||
// Shared I2C bus
|
||||
let i2c = I2c::new_async(p.I2C1, p.PIN_15, p.PIN_14, Irqs, i2c::Config::default());
|
||||
static I2C_BUS: StaticCell<I2c1Bus> = StaticCell::new();
|
||||
let i2c_bus = I2C_BUS.init(Mutex::new(i2c));
|
||||
|
||||
spawner.must_spawn(i2c_task_a(i2c_bus));
|
||||
spawner.must_spawn(i2c_task_b(i2c_bus));
|
||||
|
||||
// Shared SPI bus
|
||||
let spi_cfg = spi::Config::default();
|
||||
let spi = Spi::new(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, p.DMA_CH0, p.DMA_CH1, spi_cfg);
|
||||
static SPI_BUS: StaticCell<Spi1Bus> = StaticCell::new();
|
||||
let spi_bus = SPI_BUS.init(Mutex::new(spi));
|
||||
|
||||
// Chip select pins for the SPI devices
|
||||
let cs_a = Output::new(AnyPin::from(p.PIN_0), Level::High);
|
||||
let cs_b = Output::new(AnyPin::from(p.PIN_1), Level::High);
|
||||
|
||||
spawner.must_spawn(spi_task_a(spi_bus, cs_a));
|
||||
spawner.must_spawn(spi_task_b(spi_bus, cs_b));
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn i2c_task_a(i2c_bus: &'static I2c1Bus) {
|
||||
let i2c_dev = I2cDevice::new(i2c_bus);
|
||||
let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xC0);
|
||||
loop {
|
||||
info!("i2c task A");
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn i2c_task_b(i2c_bus: &'static I2c1Bus) {
|
||||
let i2c_dev = I2cDevice::new(i2c_bus);
|
||||
let _sensor = DummyI2cDeviceDriver::new(i2c_dev, 0xDE);
|
||||
loop {
|
||||
info!("i2c task B");
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn spi_task_a(spi_bus: &'static Spi1Bus, cs: Output<'static>) {
|
||||
let spi_dev = SpiDevice::new(spi_bus, cs);
|
||||
let _sensor = DummySpiDeviceDriver::new(spi_dev);
|
||||
loop {
|
||||
info!("spi task A");
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn spi_task_b(spi_bus: &'static Spi1Bus, cs: Output<'static>) {
|
||||
let spi_dev = SpiDevice::new(spi_bus, cs);
|
||||
let _sensor = DummySpiDeviceDriver::new(spi_dev);
|
||||
loop {
|
||||
info!("spi task B");
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
|
||||
// Dummy I2C device driver, using `embedded-hal-async`
|
||||
struct DummyI2cDeviceDriver<I2C: embedded_hal_async::i2c::I2c> {
|
||||
_i2c: I2C,
|
||||
}
|
||||
|
||||
impl<I2C: embedded_hal_async::i2c::I2c> DummyI2cDeviceDriver<I2C> {
|
||||
fn new(i2c_dev: I2C, _address: u8) -> Self {
|
||||
Self { _i2c: i2c_dev }
|
||||
}
|
||||
}
|
||||
|
||||
// Dummy SPI device driver, using `embedded-hal-async`
|
||||
struct DummySpiDeviceDriver<SPI: embedded_hal_async::spi::SpiDevice> {
|
||||
_spi: SPI,
|
||||
}
|
||||
|
||||
impl<SPI: embedded_hal_async::spi::SpiDevice> DummySpiDeviceDriver<SPI> {
|
||||
fn new(spi_dev: SPI) -> Self {
|
||||
Self { _spi: spi_dev }
|
||||
}
|
||||
}
|
166
examples/rp23/src/bin/sharing.rs
Normal file
166
examples/rp23/src/bin/sharing.rs
Normal file
@ -0,0 +1,166 @@
|
||||
//! This example shows some common strategies for sharing resources between tasks.
|
||||
//!
|
||||
//! We demonstrate five different ways of sharing, covering different use cases:
|
||||
//! - Atomics: This method is used for simple values, such as bool and u8..u32
|
||||
//! - Blocking Mutex: This is used for sharing non-async things, using Cell/RefCell for interior mutability.
|
||||
//! - Async Mutex: This is used for sharing async resources, where you need to hold the lock across await points.
|
||||
//! The async Mutex has interior mutability built-in, so no RefCell is needed.
|
||||
//! - Cell: For sharing Copy types between tasks running on the same executor.
|
||||
//! - RefCell: When you want &mut access to a value shared between tasks running on the same executor.
|
||||
//!
|
||||
//! More information: https://embassy.dev/book/#_sharing_peripherals_between_tasks
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::cell::{Cell, RefCell};
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use defmt::info;
|
||||
use embassy_executor::{Executor, InterruptExecutor};
|
||||
use embassy_rp::clocks::RoscRng;
|
||||
use embassy_rp::interrupt::{Priority, InterruptExt};
|
||||
use embassy_rp::peripherals::UART0;
|
||||
use embassy_rp::uart::{self, InterruptHandler, UartTx};
|
||||
use embassy_rp::{bind_interrupts, interrupt};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::{blocking_mutex, mutex};
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use rand::RngCore;
|
||||
use static_cell::{ConstStaticCell, StaticCell};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
type UartAsyncMutex = mutex::Mutex<CriticalSectionRawMutex, UartTx<'static, UART0, uart::Async>>;
|
||||
|
||||
struct MyType {
|
||||
inner: u32,
|
||||
}
|
||||
|
||||
static EXECUTOR_HI: InterruptExecutor = InterruptExecutor::new();
|
||||
static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new();
|
||||
|
||||
// Use Atomics for simple values
|
||||
static ATOMIC: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
// Use blocking Mutex with Cell/RefCell for sharing non-async things
|
||||
static MUTEX_BLOCKING: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<MyType>> =
|
||||
blocking_mutex::Mutex::new(RefCell::new(MyType { inner: 0 }));
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
UART0_IRQ => InterruptHandler<UART0>;
|
||||
});
|
||||
|
||||
#[interrupt]
|
||||
unsafe fn SWI_IRQ_0() {
|
||||
EXECUTOR_HI.on_interrupt()
|
||||
}
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Here we go!");
|
||||
|
||||
let uart = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, uart::Config::default());
|
||||
// Use the async Mutex for sharing async things (built-in interior mutability)
|
||||
static UART: StaticCell<UartAsyncMutex> = StaticCell::new();
|
||||
let uart = UART.init(mutex::Mutex::new(uart));
|
||||
|
||||
// High-priority executor: runs in interrupt mode
|
||||
interrupt::SWI_IRQ_0.set_priority(Priority::P3);
|
||||
let spawner = EXECUTOR_HI.start(interrupt::SWI_IRQ_0);
|
||||
spawner.must_spawn(task_a(uart));
|
||||
|
||||
// Low priority executor: runs in thread mode
|
||||
let executor = EXECUTOR_LOW.init(Executor::new());
|
||||
executor.run(|spawner| {
|
||||
// No Mutex needed when sharing between tasks running on the same executor
|
||||
|
||||
// Use Cell for Copy-types
|
||||
static CELL: ConstStaticCell<Cell<[u8; 4]>> = ConstStaticCell::new(Cell::new([0; 4]));
|
||||
let cell = CELL.take();
|
||||
|
||||
// Use RefCell for &mut access
|
||||
static REF_CELL: ConstStaticCell<RefCell<MyType>> = ConstStaticCell::new(RefCell::new(MyType { inner: 0 }));
|
||||
let ref_cell = REF_CELL.take();
|
||||
|
||||
spawner.must_spawn(task_b(uart, cell, ref_cell));
|
||||
spawner.must_spawn(task_c(cell, ref_cell));
|
||||
});
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn task_a(uart: &'static UartAsyncMutex) {
|
||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||
loop {
|
||||
let random = RoscRng.next_u32();
|
||||
|
||||
{
|
||||
let mut uart = uart.lock().await;
|
||||
uart.write(b"task a").await.unwrap();
|
||||
// The uart lock is released when it goes out of scope
|
||||
}
|
||||
|
||||
ATOMIC.store(random, Ordering::Relaxed);
|
||||
|
||||
MUTEX_BLOCKING.lock(|x| x.borrow_mut().inner = random);
|
||||
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn task_b(uart: &'static UartAsyncMutex, cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
|
||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||
loop {
|
||||
let random = RoscRng.next_u32();
|
||||
|
||||
uart.lock().await.write(b"task b").await.unwrap();
|
||||
|
||||
cell.set(random.to_be_bytes());
|
||||
|
||||
ref_cell.borrow_mut().inner = random;
|
||||
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn task_c(cell: &'static Cell<[u8; 4]>, ref_cell: &'static RefCell<MyType>) {
|
||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||
loop {
|
||||
info!("=======================");
|
||||
|
||||
let atomic_val = ATOMIC.load(Ordering::Relaxed);
|
||||
info!("atomic: {}", atomic_val);
|
||||
|
||||
MUTEX_BLOCKING.lock(|x| {
|
||||
let val = x.borrow().inner;
|
||||
info!("blocking mutex: {}", val);
|
||||
});
|
||||
|
||||
let cell_val = cell.get();
|
||||
info!("cell: {:?}", cell_val);
|
||||
|
||||
let ref_cell_val = ref_cell.borrow().inner;
|
||||
info!("ref_cell: {:?}", ref_cell_val);
|
||||
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
62
examples/rp23/src/bin/spi.rs
Normal file
62
examples/rp23/src/bin/spi.rs
Normal file
@ -0,0 +1,62 @@
|
||||
//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
|
||||
//!
|
||||
//! Example for resistive touch sensor in Waveshare Pico-ResTouch
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::spi::Spi;
|
||||
use embassy_rp::{gpio, spi};
|
||||
use gpio::{Level, Output};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Hello World!");
|
||||
|
||||
// Example for resistive touch sensor in Waveshare Pico-ResTouch
|
||||
|
||||
let miso = p.PIN_12;
|
||||
let mosi = p.PIN_11;
|
||||
let clk = p.PIN_10;
|
||||
let touch_cs = p.PIN_16;
|
||||
|
||||
// create SPI
|
||||
let mut config = spi::Config::default();
|
||||
config.frequency = 2_000_000;
|
||||
let mut spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, config);
|
||||
|
||||
// Configure CS
|
||||
let mut cs = Output::new(touch_cs, Level::Low);
|
||||
|
||||
loop {
|
||||
cs.set_low();
|
||||
let mut buf = [0x90, 0x00, 0x00, 0xd0, 0x00, 0x00];
|
||||
spi.blocking_transfer_in_place(&mut buf).unwrap();
|
||||
cs.set_high();
|
||||
|
||||
let x = (buf[1] as u32) << 5 | (buf[2] as u32) >> 3;
|
||||
let y = (buf[4] as u32) << 5 | (buf[5] as u32) >> 3;
|
||||
|
||||
info!("touch: {=u32} {=u32}", x, y);
|
||||
}
|
||||
}
|
47
examples/rp23/src/bin/spi_async.rs
Normal file
47
examples/rp23/src/bin/spi_async.rs
Normal file
@ -0,0 +1,47 @@
|
||||
//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
|
||||
//! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::spi::{Config, Spi};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Hello World!");
|
||||
|
||||
let miso = p.PIN_12;
|
||||
let mosi = p.PIN_11;
|
||||
let clk = p.PIN_10;
|
||||
|
||||
let mut spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
|
||||
|
||||
loop {
|
||||
let tx_buf = [1_u8, 2, 3, 4, 5, 6];
|
||||
let mut rx_buf = [0_u8; 6];
|
||||
spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
|
||||
info!("{:?}", rx_buf);
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
328
examples/rp23/src/bin/spi_display.rs
Normal file
328
examples/rp23/src/bin/spi_display.rs
Normal file
@ -0,0 +1,328 @@
|
||||
//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip.
|
||||
//!
|
||||
//! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch
|
||||
//! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8)
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::cell::RefCell;
|
||||
|
||||
use defmt::*;
|
||||
use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio::{Level, Output};
|
||||
use embassy_rp::spi;
|
||||
use embassy_rp::spi::{Blocking, Spi};
|
||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embassy_time::Delay;
|
||||
use embedded_graphics::image::{Image, ImageRawLE};
|
||||
use embedded_graphics::mono_font::ascii::FONT_10X20;
|
||||
use embedded_graphics::mono_font::MonoTextStyle;
|
||||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle};
|
||||
use embedded_graphics::text::Text;
|
||||
use st7789::{Orientation, ST7789};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
use crate::my_display_interface::SPIDeviceInterface;
|
||||
use crate::touch::Touch;
|
||||
|
||||
const DISPLAY_FREQ: u32 = 64_000_000;
|
||||
const TOUCH_FREQ: u32 = 200_000;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Hello World!");
|
||||
|
||||
let bl = p.PIN_13;
|
||||
let rst = p.PIN_15;
|
||||
let display_cs = p.PIN_9;
|
||||
let dcx = p.PIN_8;
|
||||
let miso = p.PIN_12;
|
||||
let mosi = p.PIN_11;
|
||||
let clk = p.PIN_10;
|
||||
let touch_cs = p.PIN_16;
|
||||
//let touch_irq = p.PIN_17;
|
||||
|
||||
// create SPI
|
||||
let mut display_config = spi::Config::default();
|
||||
display_config.frequency = DISPLAY_FREQ;
|
||||
display_config.phase = spi::Phase::CaptureOnSecondTransition;
|
||||
display_config.polarity = spi::Polarity::IdleHigh;
|
||||
let mut touch_config = spi::Config::default();
|
||||
touch_config.frequency = TOUCH_FREQ;
|
||||
touch_config.phase = spi::Phase::CaptureOnSecondTransition;
|
||||
touch_config.polarity = spi::Polarity::IdleHigh;
|
||||
|
||||
let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone());
|
||||
let spi_bus: Mutex<NoopRawMutex, _> = Mutex::new(RefCell::new(spi));
|
||||
|
||||
let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config);
|
||||
let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config);
|
||||
|
||||
let mut touch = Touch::new(touch_spi);
|
||||
|
||||
let dcx = Output::new(dcx, Level::Low);
|
||||
let rst = Output::new(rst, Level::Low);
|
||||
// dcx: 0 = command, 1 = data
|
||||
|
||||
// Enable LCD backlight
|
||||
let _bl = Output::new(bl, Level::High);
|
||||
|
||||
// display interface abstraction from SPI and DC
|
||||
let di = SPIDeviceInterface::new(display_spi, dcx);
|
||||
|
||||
// create driver
|
||||
let mut display = ST7789::new(di, rst, 240, 320);
|
||||
|
||||
// initialize
|
||||
display.init(&mut Delay).unwrap();
|
||||
|
||||
// set default orientation
|
||||
display.set_orientation(Orientation::Landscape).unwrap();
|
||||
|
||||
display.clear(Rgb565::BLACK).unwrap();
|
||||
|
||||
let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86);
|
||||
let ferris = Image::new(&raw_image_data, Point::new(34, 68));
|
||||
|
||||
// Display the image
|
||||
ferris.draw(&mut display).unwrap();
|
||||
|
||||
let style = MonoTextStyle::new(&FONT_10X20, Rgb565::GREEN);
|
||||
Text::new(
|
||||
"Hello embedded_graphics \n + embassy + RP2040!",
|
||||
Point::new(20, 200),
|
||||
style,
|
||||
)
|
||||
.draw(&mut display)
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
if let Some((x, y)) = touch.read() {
|
||||
let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLUE).build();
|
||||
|
||||
Rectangle::new(Point::new(x - 1, y - 1), Size::new(3, 3))
|
||||
.into_styled(style)
|
||||
.draw(&mut display)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Driver for the XPT2046 resistive touchscreen sensor
|
||||
mod touch {
|
||||
use embedded_hal_1::spi::{Operation, SpiDevice};
|
||||
|
||||
struct Calibration {
|
||||
x1: i32,
|
||||
x2: i32,
|
||||
y1: i32,
|
||||
y2: i32,
|
||||
sx: i32,
|
||||
sy: i32,
|
||||
}
|
||||
|
||||
const CALIBRATION: Calibration = Calibration {
|
||||
x1: 3880,
|
||||
x2: 340,
|
||||
y1: 262,
|
||||
y2: 3850,
|
||||
sx: 320,
|
||||
sy: 240,
|
||||
};
|
||||
|
||||
pub struct Touch<SPI: SpiDevice> {
|
||||
spi: SPI,
|
||||
}
|
||||
|
||||
impl<SPI> Touch<SPI>
|
||||
where
|
||||
SPI: SpiDevice,
|
||||
{
|
||||
pub fn new(spi: SPI) -> Self {
|
||||
Self { spi }
|
||||
}
|
||||
|
||||
pub fn read(&mut self) -> Option<(i32, i32)> {
|
||||
let mut x = [0; 2];
|
||||
let mut y = [0; 2];
|
||||
self.spi
|
||||
.transaction(&mut [
|
||||
Operation::Write(&[0x90]),
|
||||
Operation::Read(&mut x),
|
||||
Operation::Write(&[0xd0]),
|
||||
Operation::Read(&mut y),
|
||||
])
|
||||
.unwrap();
|
||||
|
||||
let x = (u16::from_be_bytes(x) >> 3) as i32;
|
||||
let y = (u16::from_be_bytes(y) >> 3) as i32;
|
||||
|
||||
let cal = &CALIBRATION;
|
||||
|
||||
let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx);
|
||||
let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy);
|
||||
if x == 0 && y == 0 {
|
||||
None
|
||||
} else {
|
||||
Some((x, y))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod my_display_interface {
|
||||
use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand};
|
||||
use embedded_hal_1::digital::OutputPin;
|
||||
use embedded_hal_1::spi::SpiDevice;
|
||||
|
||||
/// SPI display interface.
|
||||
///
|
||||
/// This combines the SPI peripheral and a data/command pin
|
||||
pub struct SPIDeviceInterface<SPI, DC> {
|
||||
spi: SPI,
|
||||
dc: DC,
|
||||
}
|
||||
|
||||
impl<SPI, DC> SPIDeviceInterface<SPI, DC>
|
||||
where
|
||||
SPI: SpiDevice,
|
||||
DC: OutputPin,
|
||||
{
|
||||
/// Create new SPI interface for communciation with a display driver
|
||||
pub fn new(spi: SPI, dc: DC) -> Self {
|
||||
Self { spi, dc }
|
||||
}
|
||||
}
|
||||
|
||||
impl<SPI, DC> WriteOnlyDataCommand for SPIDeviceInterface<SPI, DC>
|
||||
where
|
||||
SPI: SpiDevice,
|
||||
DC: OutputPin,
|
||||
{
|
||||
fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> {
|
||||
// 1 = data, 0 = command
|
||||
self.dc.set_low().map_err(|_| DisplayError::DCError)?;
|
||||
|
||||
send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> {
|
||||
// 1 = data, 0 = command
|
||||
self.dc.set_high().map_err(|_| DisplayError::DCError)?;
|
||||
|
||||
send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn send_u8<T: SpiDevice>(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> {
|
||||
match words {
|
||||
DataFormat::U8(slice) => spi.write(slice),
|
||||
DataFormat::U16(slice) => {
|
||||
use byte_slice_cast::*;
|
||||
spi.write(slice.as_byte_slice())
|
||||
}
|
||||
DataFormat::U16LE(slice) => {
|
||||
use byte_slice_cast::*;
|
||||
for v in slice.as_mut() {
|
||||
*v = v.to_le();
|
||||
}
|
||||
spi.write(slice.as_byte_slice())
|
||||
}
|
||||
DataFormat::U16BE(slice) => {
|
||||
use byte_slice_cast::*;
|
||||
for v in slice.as_mut() {
|
||||
*v = v.to_be();
|
||||
}
|
||||
spi.write(slice.as_byte_slice())
|
||||
}
|
||||
DataFormat::U8Iter(iter) => {
|
||||
let mut buf = [0; 32];
|
||||
let mut i = 0;
|
||||
|
||||
for v in iter.into_iter() {
|
||||
buf[i] = v;
|
||||
i += 1;
|
||||
|
||||
if i == buf.len() {
|
||||
spi.write(&buf)?;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
spi.write(&buf[..i])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
DataFormat::U16LEIter(iter) => {
|
||||
use byte_slice_cast::*;
|
||||
let mut buf = [0; 32];
|
||||
let mut i = 0;
|
||||
|
||||
for v in iter.map(u16::to_le) {
|
||||
buf[i] = v;
|
||||
i += 1;
|
||||
|
||||
if i == buf.len() {
|
||||
spi.write(&buf.as_byte_slice())?;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
spi.write(&buf[..i].as_byte_slice())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
DataFormat::U16BEIter(iter) => {
|
||||
use byte_slice_cast::*;
|
||||
let mut buf = [0; 64];
|
||||
let mut i = 0;
|
||||
let len = buf.len();
|
||||
|
||||
for v in iter.map(u16::to_be) {
|
||||
buf[i] = v;
|
||||
i += 1;
|
||||
|
||||
if i == len {
|
||||
spi.write(&buf.as_byte_slice())?;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
spi.write(&buf[..i].as_byte_slice())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
99
examples/rp23/src/bin/spi_sdmmc.rs
Normal file
99
examples/rp23/src/bin/spi_sdmmc.rs
Normal file
@ -0,0 +1,99 @@
|
||||
//! This example shows how to use `embedded-sdmmc` with the RP2040 chip, over SPI.
|
||||
//!
|
||||
//! The example will attempt to read a file `MY_FILE.TXT` from the root directory
|
||||
//! of the SD card and print its contents.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::spi::Spi;
|
||||
use embassy_rp::{gpio, spi};
|
||||
use embedded_hal_bus::spi::ExclusiveDevice;
|
||||
use embedded_sdmmc::sdcard::{DummyCsPin, SdCard};
|
||||
use gpio::{Level, Output};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
struct DummyTimesource();
|
||||
|
||||
impl embedded_sdmmc::TimeSource for DummyTimesource {
|
||||
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
|
||||
embedded_sdmmc::Timestamp {
|
||||
year_since_1970: 0,
|
||||
zero_indexed_month: 0,
|
||||
zero_indexed_day: 0,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
seconds: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
embassy_rp::pac::SIO.spinlock(31).write_value(1);
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
// SPI clock needs to be running at <= 400kHz during initialization
|
||||
let mut config = spi::Config::default();
|
||||
config.frequency = 400_000;
|
||||
let spi = Spi::new_blocking(p.SPI1, p.PIN_10, p.PIN_11, p.PIN_12, config);
|
||||
// Use a dummy cs pin here, for embedded-hal SpiDevice compatibility reasons
|
||||
let spi_dev = ExclusiveDevice::new_no_delay(spi, DummyCsPin);
|
||||
// Real cs pin
|
||||
let cs = Output::new(p.PIN_16, Level::High);
|
||||
|
||||
let sdcard = SdCard::new(spi_dev, cs, embassy_time::Delay);
|
||||
info!("Card size is {} bytes", sdcard.num_bytes().unwrap());
|
||||
|
||||
// Now that the card is initialized, the SPI clock can go faster
|
||||
let mut config = spi::Config::default();
|
||||
config.frequency = 16_000_000;
|
||||
sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok();
|
||||
|
||||
// Now let's look for volumes (also known as partitions) on our block device.
|
||||
// To do this we need a Volume Manager. It will take ownership of the block device.
|
||||
let mut volume_mgr = embedded_sdmmc::VolumeManager::new(sdcard, DummyTimesource());
|
||||
|
||||
// Try and access Volume 0 (i.e. the first partition).
|
||||
// The volume object holds information about the filesystem on that volume.
|
||||
let mut volume0 = volume_mgr.open_volume(embedded_sdmmc::VolumeIdx(0)).unwrap();
|
||||
info!("Volume 0: {:?}", defmt::Debug2Format(&volume0));
|
||||
|
||||
// Open the root directory (mutably borrows from the volume).
|
||||
let mut root_dir = volume0.open_root_dir().unwrap();
|
||||
|
||||
// Open a file called "MY_FILE.TXT" in the root directory
|
||||
// This mutably borrows the directory.
|
||||
let mut my_file = root_dir
|
||||
.open_file_in_dir("MY_FILE.TXT", embedded_sdmmc::Mode::ReadOnly)
|
||||
.unwrap();
|
||||
|
||||
// Print the contents of the file
|
||||
while !my_file.is_eof() {
|
||||
let mut buf = [0u8; 32];
|
||||
if let Ok(n) = my_file.read(&mut buf) {
|
||||
info!("{:a}", buf[..n]);
|
||||
}
|
||||
}
|
||||
|
||||
loop {}
|
||||
}
|
40
examples/rp23/src/bin/uart.rs
Normal file
40
examples/rp23/src/bin/uart.rs
Normal file
@ -0,0 +1,40 @@
|
||||
//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
|
||||
//!
|
||||
//! No specific hardware is specified in this example. Only output on pin 0 is tested.
|
||||
//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
|
||||
//! with its UART port.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::uart;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let config = uart::Config::default();
|
||||
let mut uart = uart::Uart::new_blocking(p.UART1, p.PIN_4, p.PIN_5, config);
|
||||
uart.blocking_write("Hello World!\r\n".as_bytes()).unwrap();
|
||||
|
||||
loop {
|
||||
uart.blocking_write("hello there!\r\n".as_bytes()).unwrap();
|
||||
cortex_m::asm::delay(1_000_000);
|
||||
}
|
||||
}
|
74
examples/rp23/src/bin/uart_buffered_split.rs
Normal file
74
examples/rp23/src/bin/uart_buffered_split.rs
Normal file
@ -0,0 +1,74 @@
|
||||
//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
|
||||
//!
|
||||
//! No specific hardware is specified in this example. If you connect pin 0 and 1 you should get the same data back.
|
||||
//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
|
||||
//! with its UART port.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::peripherals::UART0;
|
||||
use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config};
|
||||
use embassy_time::Timer;
|
||||
use embedded_io_async::{Read, Write};
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
UART0_IRQ => BufferedInterruptHandler<UART0>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0);
|
||||
|
||||
static TX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
|
||||
let tx_buf = &mut TX_BUF.init([0; 16])[..];
|
||||
static RX_BUF: StaticCell<[u8; 16]> = StaticCell::new();
|
||||
let rx_buf = &mut RX_BUF.init([0; 16])[..];
|
||||
let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default());
|
||||
let (mut tx, rx) = uart.split();
|
||||
|
||||
unwrap!(spawner.spawn(reader(rx)));
|
||||
|
||||
info!("Writing...");
|
||||
loop {
|
||||
let data = [
|
||||
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,
|
||||
29, 30, 31,
|
||||
];
|
||||
info!("TX {:?}", data);
|
||||
tx.write_all(&data).await.unwrap();
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn reader(mut rx: BufferedUartRx<'static, UART0>) {
|
||||
info!("Reading...");
|
||||
loop {
|
||||
let mut buf = [0; 31];
|
||||
rx.read_exact(&mut buf).await.unwrap();
|
||||
info!("RX {:?}", buf);
|
||||
}
|
||||
}
|
174
examples/rp23/src/bin/uart_r503.rs
Normal file
174
examples/rp23/src/bin/uart_r503.rs
Normal file
@ -0,0 +1,174 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::{debug, error, info};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::peripherals::UART0;
|
||||
use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart};
|
||||
use embassy_time::{with_timeout, Duration, Timer};
|
||||
use heapless::Vec;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(pub struct Irqs {
|
||||
UART0_IRQ => UARTInterruptHandler<UART0>;
|
||||
});
|
||||
|
||||
const START: u16 = 0xEF01;
|
||||
const ADDRESS: u32 = 0xFFFFFFFF;
|
||||
|
||||
// ================================================================================
|
||||
|
||||
// Data package format
|
||||
// Name Length Description
|
||||
// ==========================================================================================================
|
||||
// Start 2 bytes Fixed value of 0xEF01; High byte transferred first.
|
||||
// Address 4 bytes Default value is 0xFFFFFFFF, which can be modified by command.
|
||||
// High byte transferred first and at wrong adder value, module
|
||||
// will reject to transfer.
|
||||
// PID 1 byte 01H Command packet;
|
||||
// 02H Data packet; Data packet shall not appear alone in executing
|
||||
// processs, must follow command packet or acknowledge packet.
|
||||
// 07H Acknowledge packet;
|
||||
// 08H End of Data packet.
|
||||
// LENGTH 2 bytes Refers to the length of package content (command packets and data packets)
|
||||
// plus the length of Checksum (2 bytes). Unit is byte. Max length is 256 bytes.
|
||||
// And high byte is transferred first.
|
||||
// DATA - It can be commands, data, command’s parameters, acknowledge result, etc.
|
||||
// (fingerprint character value, template are all deemed as data);
|
||||
// SUM 2 bytes The arithmetic sum of package identifier, package length and all package
|
||||
// contens. Overflowing bits are omitted. high byte is transferred first.
|
||||
|
||||
// ================================================================================
|
||||
|
||||
// Checksum is calculated on 'length (2 bytes) + data (??)'.
|
||||
fn compute_checksum(buf: Vec<u8, 32>) -> u16 {
|
||||
let mut checksum = 0u16;
|
||||
|
||||
let check_end = buf.len();
|
||||
let checked_bytes = &buf[6..check_end];
|
||||
for byte in checked_bytes {
|
||||
checksum += (*byte) as u16;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
info!("Start");
|
||||
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
// Initialize the fingerprint scanner.
|
||||
let mut config = Config::default();
|
||||
config.baudrate = 57600;
|
||||
config.stop_bits = StopBits::STOP1;
|
||||
config.data_bits = DataBits::DataBits8;
|
||||
config.parity = Parity::ParityNone;
|
||||
|
||||
let (uart, tx_pin, tx_dma, rx_pin, rx_dma) = (p.UART0, p.PIN_16, p.DMA_CH0, p.PIN_17, p.DMA_CH1);
|
||||
let uart = Uart::new(uart, tx_pin, rx_pin, Irqs, tx_dma, rx_dma, config);
|
||||
let (mut tx, mut rx) = uart.split();
|
||||
|
||||
let mut vec_buf: Vec<u8, 32> = heapless::Vec::new();
|
||||
let mut data: Vec<u8, 32> = heapless::Vec::new();
|
||||
|
||||
let mut speeds: Vec<u8, 3> = heapless::Vec::new();
|
||||
let _ = speeds.push(0xC8); // Slow
|
||||
let _ = speeds.push(0x20); // Medium
|
||||
let _ = speeds.push(0x02); // Fast
|
||||
|
||||
// Cycle through the three colours Red, Blue and Purple forever.
|
||||
loop {
|
||||
for colour in 1..=3 {
|
||||
for speed in &speeds {
|
||||
// Set the data first, because the length is dependent on that.
|
||||
// However, we write the length bits before we do the data.
|
||||
data.clear();
|
||||
let _ = data.push(0x01); // ctrl=Breathing light
|
||||
let _ = data.push(*speed);
|
||||
let _ = data.push(colour as u8); // colour=Red, Blue, Purple
|
||||
let _ = data.push(0x00); // times=Infinite
|
||||
|
||||
// Clear buffers
|
||||
vec_buf.clear();
|
||||
|
||||
// START
|
||||
let _ = vec_buf.extend_from_slice(&START.to_be_bytes()[..]);
|
||||
|
||||
// ADDRESS
|
||||
let _ = vec_buf.extend_from_slice(&ADDRESS.to_be_bytes()[..]);
|
||||
|
||||
// PID
|
||||
let _ = vec_buf.extend_from_slice(&[0x01]);
|
||||
|
||||
// LENGTH
|
||||
let len: u16 = (1 + data.len() + 2).try_into().unwrap();
|
||||
let _ = vec_buf.extend_from_slice(&len.to_be_bytes()[..]);
|
||||
|
||||
// COMMAND
|
||||
let _ = vec_buf.push(0x35); // Command: AuraLedConfig
|
||||
|
||||
// DATA
|
||||
let _ = vec_buf.extend_from_slice(&data);
|
||||
|
||||
// SUM
|
||||
let chk = compute_checksum(vec_buf.clone());
|
||||
let _ = vec_buf.extend_from_slice(&chk.to_be_bytes()[..]);
|
||||
|
||||
// =====
|
||||
|
||||
// Send command buffer.
|
||||
let data_write: [u8; 16] = vec_buf.clone().into_array().unwrap();
|
||||
debug!(" write='{:?}'", data_write[..]);
|
||||
match tx.write(&data_write).await {
|
||||
Ok(..) => info!("Write successful."),
|
||||
Err(e) => error!("Write error: {:?}", e),
|
||||
}
|
||||
|
||||
// =====
|
||||
|
||||
// Read command buffer.
|
||||
let mut read_buf: [u8; 1] = [0; 1]; // Can only read one byte at a time!
|
||||
let mut data_read: Vec<u8, 32> = heapless::Vec::new(); // Save buffer.
|
||||
|
||||
info!("Attempting read.");
|
||||
loop {
|
||||
// Some commands, like `Img2Tz()` needs longer, but we hard-code this to 200ms
|
||||
// for this command.
|
||||
match with_timeout(Duration::from_millis(200), rx.read(&mut read_buf)).await {
|
||||
Ok(..) => {
|
||||
// Extract and save read byte.
|
||||
debug!(" r='{=u8:#04x}H' ({:03}D)", read_buf[0], read_buf[0]);
|
||||
let _ = data_read.push(read_buf[0]).unwrap();
|
||||
}
|
||||
Err(..) => break, // TimeoutError -> Ignore.
|
||||
}
|
||||
}
|
||||
info!("Read successful");
|
||||
debug!(" read='{:?}'", data_read[..]);
|
||||
|
||||
Timer::after_secs(3).await;
|
||||
info!("Changing speed.");
|
||||
}
|
||||
|
||||
info!("Changing colour.");
|
||||
}
|
||||
}
|
||||
}
|
66
examples/rp23/src/bin/uart_unidir.rs
Normal file
66
examples/rp23/src/bin/uart_unidir.rs
Normal file
@ -0,0 +1,66 @@
|
||||
//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip.
|
||||
//!
|
||||
//! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for
|
||||
//! this to work
|
||||
//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used
|
||||
//! with its UART port.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::peripherals::UART1;
|
||||
use embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
UART1_IRQ => InterruptHandler<UART1>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default());
|
||||
let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default());
|
||||
|
||||
unwrap!(spawner.spawn(reader(uart_rx)));
|
||||
|
||||
info!("Writing...");
|
||||
loop {
|
||||
let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
|
||||
info!("TX {:?}", data);
|
||||
uart_tx.write(&data).await.unwrap();
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn reader(mut rx: UartRx<'static, UART1, Async>) {
|
||||
info!("Reading...");
|
||||
loop {
|
||||
// read a total of 4 transmissions (32 / 8) and then print the result
|
||||
let mut buf = [0; 32];
|
||||
rx.read(&mut buf).await.unwrap();
|
||||
info!("RX {:?}", buf);
|
||||
}
|
||||
}
|
171
examples/rp23/src/bin/usb_webusb.rs
Normal file
171
examples/rp23/src/bin/usb_webusb.rs
Normal file
@ -0,0 +1,171 @@
|
||||
//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip.
|
||||
//!
|
||||
//! This creates a WebUSB capable device that echoes data back to the host.
|
||||
//!
|
||||
//! To test this in the browser (ideally host this on localhost:8080, to test the landing page
|
||||
//! feature):
|
||||
//! ```js
|
||||
//! (async () => {
|
||||
//! const device = await navigator.usb.requestDevice({ filters: [{ vendorId: 0xf569 }] });
|
||||
//! await device.open();
|
||||
//! await device.claimInterface(1);
|
||||
//! device.transferIn(1, 64).then(data => console.log(data));
|
||||
//! await device.transferOut(1, new Uint8Array([1,2,3]));
|
||||
//! })();
|
||||
//! ```
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::peripherals::USB;
|
||||
use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler};
|
||||
use embassy_usb::class::web_usb::{Config as WebUsbConfig, State, Url, WebUsb};
|
||||
use embassy_usb::driver::{Driver, Endpoint, EndpointIn, EndpointOut};
|
||||
use embassy_usb::msos::{self, windows_version};
|
||||
use embassy_usb::{Builder, Config};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
USBCTRL_IRQ => InterruptHandler<USB>;
|
||||
});
|
||||
|
||||
// This is a randomly generated GUID to allow clients on Windows to find our device
|
||||
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
// Create the driver, from the HAL.
|
||||
let driver = UsbDriver::new(p.USB, Irqs);
|
||||
|
||||
// Create embassy-usb Config
|
||||
let mut config = Config::new(0xf569, 0x0001);
|
||||
config.manufacturer = Some("Embassy");
|
||||
config.product = Some("WebUSB example");
|
||||
config.serial_number = Some("12345678");
|
||||
config.max_power = 100;
|
||||
config.max_packet_size_0 = 64;
|
||||
|
||||
// Required for windows compatibility.
|
||||
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
|
||||
config.device_class = 0xff;
|
||||
config.device_sub_class = 0x00;
|
||||
config.device_protocol = 0x00;
|
||||
|
||||
// Create embassy-usb DeviceBuilder using the driver and config.
|
||||
// It needs some buffers for building the descriptors.
|
||||
let mut config_descriptor = [0; 256];
|
||||
let mut bos_descriptor = [0; 256];
|
||||
let mut control_buf = [0; 64];
|
||||
let mut msos_descriptor = [0; 256];
|
||||
|
||||
let webusb_config = WebUsbConfig {
|
||||
max_packet_size: 64,
|
||||
vendor_code: 1,
|
||||
// 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.
|
||||
landing_url: Some(Url::new("http://localhost:8080")),
|
||||
};
|
||||
|
||||
let mut state = State::new();
|
||||
|
||||
let mut builder = Builder::new(
|
||||
driver,
|
||||
config,
|
||||
&mut config_descriptor,
|
||||
&mut bos_descriptor,
|
||||
&mut msos_descriptor,
|
||||
&mut control_buf,
|
||||
);
|
||||
|
||||
// Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
|
||||
// We tell Windows that this entire device is compatible with the "WINUSB" feature,
|
||||
// which causes it to use the built-in WinUSB driver automatically, which in turn
|
||||
// can be used by libusb/rusb software without needing a custom driver or INF file.
|
||||
// In principle you might want to call msos_feature() just on a specific function,
|
||||
// if your device also has other functions that still use standard class drivers.
|
||||
builder.msos_descriptor(windows_version::WIN8_1, 0);
|
||||
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
|
||||
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
|
||||
"DeviceInterfaceGUIDs",
|
||||
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
|
||||
));
|
||||
|
||||
// Create classes on the builder (WebUSB just needs some setup, but doesn't return anything)
|
||||
WebUsb::configure(&mut builder, &mut state, &webusb_config);
|
||||
// Create some USB bulk endpoints for testing.
|
||||
let mut endpoints = WebEndpoints::new(&mut builder, &webusb_config);
|
||||
|
||||
// Build the builder.
|
||||
let mut usb = builder.build();
|
||||
|
||||
// Run the USB device.
|
||||
let usb_fut = usb.run();
|
||||
|
||||
// Do some WebUSB transfers.
|
||||
let webusb_fut = async {
|
||||
loop {
|
||||
endpoints.wait_connected().await;
|
||||
info!("Connected");
|
||||
endpoints.echo().await;
|
||||
}
|
||||
};
|
||||
|
||||
// Run everything concurrently.
|
||||
// If we had made everything `'static` above instead, we could do this using separate tasks instead.
|
||||
join(usb_fut, webusb_fut).await;
|
||||
}
|
||||
|
||||
struct WebEndpoints<'d, D: Driver<'d>> {
|
||||
write_ep: D::EndpointIn,
|
||||
read_ep: D::EndpointOut,
|
||||
}
|
||||
|
||||
impl<'d, D: Driver<'d>> WebEndpoints<'d, D> {
|
||||
fn new(builder: &mut Builder<'d, D>, config: &'d WebUsbConfig<'d>) -> Self {
|
||||
let mut func = builder.function(0xff, 0x00, 0x00);
|
||||
let mut iface = func.interface();
|
||||
let mut alt = iface.alt_setting(0xff, 0x00, 0x00, None);
|
||||
|
||||
let write_ep = alt.endpoint_bulk_in(config.max_packet_size);
|
||||
let read_ep = alt.endpoint_bulk_out(config.max_packet_size);
|
||||
|
||||
WebEndpoints { write_ep, read_ep }
|
||||
}
|
||||
|
||||
// Wait until the device's endpoints are enabled.
|
||||
async fn wait_connected(&mut self) {
|
||||
self.read_ep.wait_enabled().await
|
||||
}
|
||||
|
||||
// Echo data back to the host.
|
||||
async fn echo(&mut self) {
|
||||
let mut buf = [0; 64];
|
||||
loop {
|
||||
let n = self.read_ep.read(&mut buf).await.unwrap();
|
||||
let data = &buf[..n];
|
||||
info!("Data read: {:x}", data);
|
||||
self.write_ep.write(data).await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
67
examples/rp23/src/bin/watchdog.rs
Normal file
67
examples/rp23/src/bin/watchdog.rs
Normal file
@ -0,0 +1,67 @@
|
||||
//! This example shows how to use Watchdog in the RP2040 chip.
|
||||
//!
|
||||
//! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::info;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio;
|
||||
use embassy_rp::watchdog::*;
|
||||
use embassy_time::{Duration, Timer};
|
||||
use gpio::{Level, Output};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Hello world!");
|
||||
|
||||
let mut watchdog = Watchdog::new(p.WATCHDOG);
|
||||
let mut led = Output::new(p.PIN_25, Level::Low);
|
||||
|
||||
// Set the LED high for 2 seconds so we know when we're about to start the watchdog
|
||||
led.set_high();
|
||||
Timer::after_secs(2).await;
|
||||
|
||||
// Set to watchdog to reset if it's not fed within 1.05 seconds, and start it
|
||||
watchdog.start(Duration::from_millis(1_050));
|
||||
info!("Started the watchdog timer");
|
||||
|
||||
// Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset
|
||||
for _ in 1..=5 {
|
||||
led.set_low();
|
||||
Timer::after_millis(500).await;
|
||||
led.set_high();
|
||||
Timer::after_millis(500).await;
|
||||
info!("Feeding watchdog");
|
||||
watchdog.feed();
|
||||
}
|
||||
|
||||
info!("Stopped feeding, device will reset in 1.05 seconds");
|
||||
// Blink 10 times per second, not feeding the watchdog.
|
||||
// The processor should reset in 1.05 seconds.
|
||||
loop {
|
||||
led.set_low();
|
||||
Timer::after_millis(100).await;
|
||||
led.set_high();
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
}
|
110
examples/rp23/src/bin/zerocopy.rs
Normal file
110
examples/rp23/src/bin/zerocopy.rs
Normal file
@ -0,0 +1,110 @@
|
||||
//! This example shows how to use `zerocopy_channel` from `embassy_sync` for
|
||||
//! sending large values between two tasks without copying.
|
||||
//! The example also shows how to use the RP2040 ADC with DMA.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::sync::atomic::{AtomicU16, Ordering};
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler};
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::gpio::Pull;
|
||||
use embassy_rp::peripherals::DMA_CH0;
|
||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||
use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender};
|
||||
use embassy_time::{Duration, Ticker, Timer};
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
// Program metadata for `picotool info`
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info_rp_cargo_bin_name!(),
|
||||
embassy_rp::binary_info_rp_cargo_version!(),
|
||||
embassy_rp::binary_info_rp_program_description!(c"Blinky"),
|
||||
embassy_rp::binary_info_rp_program_build_attribute!(),
|
||||
];
|
||||
|
||||
|
||||
type SampleBuffer = [u16; 512];
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
ADC_IRQ_FIFO => InterruptHandler;
|
||||
});
|
||||
|
||||
const BLOCK_SIZE: usize = 512;
|
||||
const NUM_BLOCKS: usize = 2;
|
||||
static MAX: AtomicU16 = AtomicU16::new(0);
|
||||
|
||||
struct AdcParts {
|
||||
adc: Adc<'static, Async>,
|
||||
pin: adc::Channel<'static>,
|
||||
dma: DMA_CH0,
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Here we go!");
|
||||
|
||||
let adc_parts = AdcParts {
|
||||
adc: Adc::new(p.ADC, Irqs, Config::default()),
|
||||
pin: adc::Channel::new_pin(p.PIN_29, Pull::None),
|
||||
dma: p.DMA_CH0,
|
||||
};
|
||||
|
||||
static BUF: StaticCell<[SampleBuffer; NUM_BLOCKS]> = StaticCell::new();
|
||||
let buf = BUF.init([[0; BLOCK_SIZE]; NUM_BLOCKS]);
|
||||
|
||||
static CHANNEL: StaticCell<Channel<'_, NoopRawMutex, SampleBuffer>> = StaticCell::new();
|
||||
let channel = CHANNEL.init(Channel::new(buf));
|
||||
let (sender, receiver) = channel.split();
|
||||
|
||||
spawner.must_spawn(consumer(receiver));
|
||||
spawner.must_spawn(producer(sender, adc_parts));
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||
loop {
|
||||
ticker.next().await;
|
||||
let max = MAX.load(Ordering::Relaxed);
|
||||
info!("latest block's max value: {:?}", max);
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn producer(mut sender: Sender<'static, NoopRawMutex, SampleBuffer>, mut adc: AdcParts) {
|
||||
loop {
|
||||
// Obtain a free buffer from the channel
|
||||
let buf = sender.send().await;
|
||||
|
||||
// Fill it with data
|
||||
adc.adc.read_many(&mut adc.pin, buf, 1, &mut adc.dma).await.unwrap();
|
||||
|
||||
// Notify the channel that the buffer is now ready to be received
|
||||
sender.send_done();
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn consumer(mut receiver: Receiver<'static, NoopRawMutex, SampleBuffer>) {
|
||||
loop {
|
||||
// Receive a buffer from the channel
|
||||
let buf = receiver.receive().await;
|
||||
|
||||
// Simulate using the data, while the producer is filling up the next buffer
|
||||
Timer::after_micros(1000).await;
|
||||
let max = buf.iter().max().unwrap();
|
||||
MAX.store(*max, Ordering::Relaxed);
|
||||
|
||||
// Notify the channel that the buffer is now ready to be reused
|
||||
receiver.receive_done();
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ teleprobe-meta = "1.1"
|
||||
embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", ] }
|
||||
embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] }
|
||||
embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram", "rp2040"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
|
||||
embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
|
||||
|
Loading…
Reference in New Issue
Block a user