Add pio transport to pico w example

This commit is contained in:
kbleeke 2023-02-19 16:31:35 +01:00
parent d57fe0de86
commit 0ff606dfc1
6 changed files with 263 additions and 34 deletions

View file

@ -5,11 +5,31 @@ edition = "2021"
[dependencies] [dependencies]
cyw43 = { path = "../../", features = ["defmt", "firmware-logs"]} cyw43 = { path = "../../", features = ["defmt", "firmware-logs"] }
embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers"] } embassy-executor = { version = "0.1.0", features = [
embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } "defmt",
embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } "integrated-timers",
embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } ] }
embassy-time = { version = "0.1.0", features = [
"defmt",
"defmt-timestamp-uptime",
] }
embassy-rp = { version = "0.1.0", features = [
"defmt",
"unstable-traits",
"nightly",
"unstable-pac",
"pio",
"time-driver",
] }
embassy-net = { version = "0.1.0", features = [
"defmt",
"tcp",
"dhcpv4",
"medium-ethernet",
"unstable-traits",
"nightly",
] }
atomic-polyfill = "0.1.5" atomic-polyfill = "0.1.5"
static_cell = "1.0" static_cell = "1.0"
@ -17,9 +37,15 @@ defmt = "0.3"
defmt-rtt = "0.3" defmt-rtt = "0.3"
panic-probe = { version = "0.3", features = ["print-defmt"] } panic-probe = { version = "0.3", features = ["print-defmt"] }
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"]} cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7.0" cortex-m-rt = "0.7.0"
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } futures = { version = "0.3.17", default-features = false, features = [
"async-await",
"cfg-target-has-atomic",
"unstable",
] }
pio-proc = "0.2"
pio = "0.2.1"
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" }
embedded-hal-async = { version = "0.2.0-alpha.0" } embedded-hal-async = { version = "0.2.0-alpha.0" }

View file

@ -14,23 +14,23 @@ use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
fn main() { fn main() {
// // Put `memory.x` in our output directory and ensure it's // Put `memory.x` in our output directory and ensure it's
// // on the linker search path. // on the linker search path.
// let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
// File::create(out.join("memory.x")) File::create(out.join("memory.x"))
// .unwrap() .unwrap()
// .write_all(include_bytes!("memory.x")) .write_all(include_bytes!("memory.x"))
// .unwrap(); .unwrap();
// println!("cargo:rustc-link-search={}", out.display()); println!("cargo:rustc-link-search={}", out.display());
// // By default, Cargo will re-run a build script whenever // By default, Cargo will re-run a build script whenever
// // any file in the project changes. By specifying `memory.x` // any file in the project changes. By specifying `memory.x`
// // here, we ensure the build script is only re-run when // here, we ensure the build script is only re-run when
// // `memory.x` is changed. // `memory.x` is changed.
// println!("cargo:rerun-if-changed=memory.x"); println!("cargo:rerun-if-changed=memory.x");
// println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=--nmagic");
// println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rustc-link-arg-bins=-Tlink.x");
// println!("cargo:rustc-link-arg-bins=-Tlink-rp.x"); println!("cargo:rustc-link-arg-bins=-Tlink-rp.x");
// println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
} }

View file

@ -4,21 +4,25 @@
#![feature(async_fn_in_trait)] #![feature(async_fn_in_trait)]
#![allow(incomplete_features)] #![allow(incomplete_features)]
mod pio;
use core::convert::Infallible; use core::convert::Infallible;
use core::str::from_utf8;
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_net::tcp::TcpSocket; use embassy_net::tcp::TcpSocket;
use embassy_net::{Config, Stack, StackResources}; use embassy_net::{Config, Stack, StackResources};
use embassy_rp::gpio::{Flex, Level, Output}; use embassy_rp::gpio::{Flex, Level, Output};
use embassy_rp::peripherals::{PIN_23, PIN_24, PIN_25, PIN_29}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_24, PIN_25, PIN_29};
use embassy_rp::pio::{Pio0, PioPeripherial, PioStateMachineInstance, Sm0};
use embedded_hal_1::spi::ErrorType; use embedded_hal_1::spi::ErrorType;
use embedded_hal_async::spi::{ExclusiveDevice, SpiBusFlush, SpiBusRead, SpiBusWrite}; use embedded_hal_async::spi::{ExclusiveDevice, SpiBusFlush, SpiBusRead, SpiBusWrite};
use embedded_io::asynch::Write; use embedded_io::asynch::Write;
use static_cell::StaticCell; use static_cell::StaticCell;
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
use core::str::from_utf8; use crate::pio::PioSpi;
macro_rules! singleton { macro_rules! singleton {
($val:expr) => {{ ($val:expr) => {{
@ -30,7 +34,11 @@ macro_rules! singleton {
#[embassy_executor::task] #[embassy_executor::task]
async fn wifi_task( async fn wifi_task(
runner: cyw43::Runner<'static, Output<'static, PIN_23>, ExclusiveDevice<MySpi, Output<'static, PIN_25>>>, runner: cyw43::Runner<
'static,
Output<'static, PIN_23>,
ExclusiveDevice<PioSpi<PioStateMachineInstance<Pio0, Sm0>, DMA_CH0>, Output<'static, PIN_25>>,
>,
) -> ! { ) -> ! {
runner.run().await runner.run().await
} }
@ -59,12 +67,15 @@ async fn main(spawner: Spawner) {
let pwr = Output::new(p.PIN_23, Level::Low); let pwr = Output::new(p.PIN_23, Level::Low);
let cs = Output::new(p.PIN_25, Level::High); let cs = Output::new(p.PIN_25, Level::High);
let clk = Output::new(p.PIN_29, Level::Low); // let clk = Output::new(p.PIN_29, Level::Low);
let mut dio = Flex::new(p.PIN_24); // let mut dio = Flex::new(p.PIN_24);
dio.set_low(); // dio.set_low();
dio.set_as_output(); // dio.set_as_output();
// // let bus = MySpi { clk, dio };
let bus = MySpi { clk, dio }; let (_, sm, _, _, _) = p.PIO0.split();
let dma = p.DMA_CH0;
let bus = PioSpi::new(sm, p.PIN_24, p.PIN_29, dma);
let spi = ExclusiveDevice::new(bus, cs); let spi = ExclusiveDevice::new(bus, cs);
let state = singleton!(cyw43::State::new()); let state = singleton!(cyw43::State::new());
@ -110,6 +121,7 @@ async fn main(spawner: Spawner) {
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
control.gpio_set(0, false).await;
info!("Listening on TCP:1234..."); info!("Listening on TCP:1234...");
if let Err(e) = socket.accept(1234).await { if let Err(e) = socket.accept(1234).await {
warn!("accept error: {:?}", e); warn!("accept error: {:?}", e);
@ -117,6 +129,7 @@ async fn main(spawner: Spawner) {
} }
info!("Received connection from {:?}", socket.remote_endpoint()); info!("Received connection from {:?}", socket.remote_endpoint());
control.gpio_set(0, true).await;
loop { loop {
let n = match socket.read(&mut buf).await { let n = match socket.read(&mut buf).await {

View file

@ -0,0 +1,190 @@
use core::slice;
use cyw43::SpiBusCyw43;
use embassy_rp::dma::Channel;
use embassy_rp::gpio::{Pin, Pull};
use embassy_rp::pio::{PioStateMachine, ShiftDirection};
use embassy_rp::relocate::RelocatedProgram;
use embassy_rp::{pio_instr_util, Peripheral};
use embedded_hal_1::spi::ErrorType;
use embedded_hal_async::spi::SpiBusFlush;
use pio::Wrap;
use pio_proc::pio_asm;
pub struct PioSpi<SM, DMA> {
// cs: Output<'static, AnyPin>,
sm: SM,
dma: DMA,
wrap_target: u8,
}
impl<SM, DMA> PioSpi<SM, DMA>
where
SM: PioStateMachine,
DMA: Channel,
{
pub fn new<DIO, CLK>(
mut sm: SM,
// cs: AnyPin,
dio: DIO,
clk: CLK,
dma: DMA,
) -> Self
where
DIO: Pin,
CLK: Pin,
{
let program = pio_asm!(
".side_set 1"
// "set pindirs, 1 side 0"
// "set pins, 0 side 0"
".wrap_target"
"lp:",
"out pins, 1 side 0"
"jmp x-- lp side 1"
"set pindirs, 0 side 0"
// "nop side 1"
"lp2:"
"in pins, 1 side 0"
"jmp y-- lp2 side 1"
".wrap"
);
let relocated = RelocatedProgram::new(&program.program);
let mut pin_io = sm.make_pio_pin(dio);
pin_io.set_pull(Pull::Down);
pin_io.set_schmitt(true);
let pin_clk = sm.make_pio_pin(clk);
sm.write_instr(relocated.origin() as usize, relocated.code());
// 16 Mhz
sm.set_clkdiv(0x07d0);
// 8Mhz
sm.set_clkdiv(0x0a_00);
// 1Mhz
// sm.set_clkdiv(0x7d_00);
// slowest possible
// sm.set_clkdiv(0xffff_00);
sm.set_autopull(true);
// sm.set_pull_threshold(32);
sm.set_autopush(true);
// sm.set_push_threshold(32);
sm.set_out_pins(&[&pin_io]);
sm.set_in_base_pin(&pin_io);
sm.set_set_pins(&[&pin_clk]);
pio_instr_util::set_pindir(&mut sm, 0b1);
sm.set_set_pins(&[&pin_io]);
pio_instr_util::set_pindir(&mut sm, 0b1);
sm.set_sideset_base_pin(&pin_clk);
sm.set_sideset_count(1);
sm.set_out_shift_dir(ShiftDirection::Left);
sm.set_in_shift_dir(ShiftDirection::Left);
let Wrap { source, target } = relocated.wrap();
sm.set_wrap(source, target);
// pull low for startup
pio_instr_util::set_pin(&mut sm, 0);
Self {
// cs: Output::new(cs, Level::High),
sm,
dma,
wrap_target: target,
}
}
pub async fn write(&mut self, write: &[u32]) {
let write_bits = write.len() * 32 - 1;
let read_bits = 31;
defmt::trace!("write={} read={}", write_bits, read_bits);
let mut dma = Peripheral::into_ref(&mut self.dma);
pio_instr_util::set_x(&mut self.sm, write_bits as u32);
pio_instr_util::set_y(&mut self.sm, read_bits as u32);
pio_instr_util::set_pindir(&mut self.sm, 0b1);
pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target);
self.sm.set_enable(true);
self.sm.dma_push(dma.reborrow(), write).await;
let mut status = 0;
self.sm.dma_pull(dma, slice::from_mut(&mut status)).await;
defmt::trace!("{:#08x}", status);
self.sm.set_enable(false);
}
pub async fn cmd_read(&mut self, cmd: u32, read: &mut [u32]) {
let write_bits = 31;
let read_bits = read.len() * 32 - 1;
defmt::trace!("write={} read={}", write_bits, read_bits);
let mut dma = Peripheral::into_ref(&mut self.dma);
pio_instr_util::set_y(&mut self.sm, read_bits as u32);
pio_instr_util::set_x(&mut self.sm, write_bits as u32);
pio_instr_util::set_pindir(&mut self.sm, 0b1);
pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target);
// self.cs.set_low();
self.sm.set_enable(true);
self.sm.dma_push(dma.reborrow(), slice::from_ref(&cmd)).await;
self.sm.dma_pull(dma, read).await;
self.sm.set_enable(false);
}
}
#[derive(Debug)]
pub enum PioError {}
impl embedded_hal_async::spi::Error for PioError {
fn kind(&self) -> embedded_hal_1::spi::ErrorKind {
embedded_hal_1::spi::ErrorKind::Other
}
}
impl<SM, DMA> ErrorType for PioSpi<SM, DMA>
where
SM: PioStateMachine,
{
type Error = PioError;
}
impl<SM, DMA> SpiBusFlush for PioSpi<SM, DMA>
where
SM: PioStateMachine,
{
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<SM, DMA> SpiBusCyw43<u32> for PioSpi<SM, DMA>
where
SM: PioStateMachine,
DMA: Channel,
{
async fn cmd_write<'a>(&'a mut self, write: &'a [u32]) -> Result<(), Self::Error> {
self.write(write).await;
Ok(())
}
async fn cmd_read<'a>(&'a mut self, write: &'a [u32], read: &'a mut [u32]) -> Result<(), Self::Error> {
self.cmd_read(write[0], read).await;
Ok(())
}
}

View file

@ -3,7 +3,7 @@ use core::slice;
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use embedded_hal_1::digital::OutputPin; use embedded_hal_1::digital::OutputPin;
use embedded_hal_1::spi::ErrorType; use embedded_hal_1::spi::ErrorType;
use embedded_hal_async::spi::{transaction, SpiBusRead, SpiBusWrite, SpiDevice}; use embedded_hal_async::spi::{transaction, SpiDevice};
use crate::consts::*; use crate::consts::*;

View file

@ -22,7 +22,7 @@ use embassy_futures::yield_now;
use embassy_net_driver_channel as ch; use embassy_net_driver_channel as ch;
use embassy_time::{block_for, Duration, Timer}; use embassy_time::{block_for, Duration, Timer};
use embedded_hal_1::digital::OutputPin; use embedded_hal_1::digital::OutputPin;
use embedded_hal_async::spi::{SpiBusRead, SpiBusWrite, SpiDevice}; use embedded_hal_async::spi::SpiDevice;
use crate::bus::Bus; use crate::bus::Bus;
pub use crate::bus::SpiBusCyw43; pub use crate::bus::SpiBusCyw43;