From 9bad9365dcf31dd558aca05f60d244beb9e5e697 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 16 Feb 2022 03:54:39 +0100 Subject: Update rust nightly, embedded-hal 1.0, embedded-hal-async. --- examples/nrf/Cargo.toml | 2 +- examples/rp/Cargo.toml | 7 +- examples/rp/src/bin/spi_display.rs | 375 +++++++++++++++++++------ examples/stm32f4/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l4/src/bin/spi_blocking_async.rs | 2 +- 7 files changed, 292 insertions(+), 100 deletions(-) (limited to 'examples') diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 2d9c99530..7fdc27ffa 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [features] default = ["nightly"] -nightly = ["embassy-nrf/nightly"] +nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits"] [dependencies] embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index c067fbbcf..830e54174 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -1,6 +1,6 @@ [package] authors = ["Dario Nieuwenhuis "] -edition = "2018" +edition = "2021" name = "embassy-rp-examples" version = "0.1.0" @@ -15,9 +15,12 @@ defmt-rtt = "0.3" cortex-m = "0.7.3" cortex-m-rt = "0.7.0" -embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } display-interface-spi = "0.4.1" embedded-graphics = "0.7.1" st7789 = "0.6.1" + +embedded-hal = { version = "1.0.0-alpha.7", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy2" } +display-interface = "0.4.1" +byte-slice-cast = { version = "1.2.0", default-features = false } diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index 01149c250..b2854afcb 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -6,16 +6,14 @@ mod example_common; use core::cell::RefCell; -use core::fmt::Debug; use defmt::*; -use display_interface_spi::SPIInterfaceNoCS; use embassy::executor::Spawner; use embassy::time::Delay; -use embassy_rp::peripherals; +use embassy_rp::gpio::{Level, Output}; use embassy_rp::spi; use embassy_rp::spi::Spi; -use embassy_rp::{gpio, Peripherals}; +use embassy_rp::Peripherals; use embedded_graphics::image::{Image, ImageRawLE}; use embedded_graphics::mono_font::ascii::FONT_10X20; use embedded_graphics::mono_font::MonoTextStyle; @@ -23,9 +21,15 @@ use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::*; use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; use embedded_graphics::text::Text; -use gpio::{Level, Output}; use st7789::{Orientation, ST7789}; +use crate::my_display_interface::SPIDeviceInterface; +use crate::shared_spi::SpiDeviceWithCs; +use crate::touch::Touch; + +//const DISPLAY_FREQ: u32 = 64_000_000; +const TOUCH_FREQ: u32 = 200_000; + #[embassy::main] async fn main(_spawner: Spawner, p: Peripherals) { info!("Hello World!"); @@ -42,17 +46,16 @@ async fn main(_spawner: Spawner, p: Peripherals) { // create SPI let mut config = spi::Config::default(); - config.frequency = DISPLAY_FREQ; + config.frequency = TOUCH_FREQ; // use the lowest freq config.phase = spi::Phase::CaptureOnSecondTransition; config.polarity = spi::Polarity::IdleHigh; - let spi = RefCell::new(SpiState { - last_mode: SpiMode::Display, - spi: Spi::new(p.SPI1, clk, mosi, miso, config), - display_cs: Output::new(display_cs, Level::Low), - }); + let spi_bus = RefCell::new(Spi::new(p.SPI1, clk, mosi, miso, config)); - let mut touch = Touch::new(TouchSpi(&spi), Output::new(touch_cs, Level::High)); + let display_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(display_cs, Level::High)); + let touch_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(touch_cs, Level::High)); + + let mut touch = Touch::new(touch_spi); let dcx = Output::new(dcx, Level::Low); let rst = Output::new(rst, Level::Low); @@ -62,7 +65,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { let _bl = Output::new(bl, Level::High); // display interface abstraction from SPI and DC - let di = SPIInterfaceNoCS::new(DisplaySpi(&spi), dcx); + let di = SPIDeviceInterface::new(display_spi, dcx); // create driver let mut display = ST7789::new(di, rst, 240, 320); @@ -104,107 +107,293 @@ async fn main(_spawner: Spawner, p: Peripherals) { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum SpiMode { - Display, - Touch, -} +mod shared_spi { + use core::cell::RefCell; + use core::fmt::Debug; -struct SpiState { - spi: Spi<'static, peripherals::SPI1>, - display_cs: Output<'static, peripherals::PIN_9>, + use embedded_hal::digital::blocking::OutputPin; + use embedded_hal::spi; + use embedded_hal::spi::blocking::SpiDevice; - last_mode: SpiMode, -} + #[derive(Copy, Clone, Eq, PartialEq, Debug)] + pub enum SpiDeviceWithCsError { + #[allow(unused)] // will probably use in the future when adding a flush() to SpiBus + Spi(BUS), + Cs(CS), + } -const DISPLAY_FREQ: u32 = 64_000_000; -const TOUCH_FREQ: u32 = 200_000; + impl spi::Error for SpiDeviceWithCsError + where + BUS: spi::Error + Debug, + CS: Debug, + { + fn kind(&self) -> spi::ErrorKind { + match self { + Self::Spi(e) => e.kind(), + Self::Cs(_) => spi::ErrorKind::Other, + } + } + } -struct DisplaySpi<'a>(&'a RefCell); -impl<'a> embedded_hal::blocking::spi::Write for DisplaySpi<'a> { - type Error = core::convert::Infallible; + pub struct SpiDeviceWithCs<'a, BUS, CS> { + bus: &'a RefCell, + cs: CS, + } - fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - let this = &mut *self.0.borrow_mut(); - if this.last_mode != SpiMode::Display { - this.spi.set_frequency(DISPLAY_FREQ); - this.display_cs.set_low(); - this.last_mode = SpiMode::Display; + impl<'a, BUS, CS> SpiDeviceWithCs<'a, BUS, CS> { + pub fn new(bus: &'a RefCell, cs: CS) -> Self { + Self { bus, cs } } - this.spi.write(words).unwrap(); - Ok(()) } -} -struct TouchSpi<'a>(&'a RefCell); -impl<'a> embedded_hal::blocking::spi::Transfer for TouchSpi<'a> { - type Error = core::convert::Infallible; + impl<'a, BUS, CS> spi::ErrorType for SpiDeviceWithCs<'a, BUS, CS> + where + BUS: spi::ErrorType, + CS: OutputPin, + { + type Error = SpiDeviceWithCsError; + } + + impl<'a, BUS, CS> SpiDevice for SpiDeviceWithCs<'a, BUS, CS> + where + BUS: spi::blocking::SpiBusFlush, + CS: OutputPin, + { + type Bus = BUS; + + fn transaction( + &mut self, + f: impl FnOnce(&mut Self::Bus) -> Result, + ) -> Result { + let mut bus = self.bus.borrow_mut(); + self.cs.set_low().map_err(SpiDeviceWithCsError::Cs)?; - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { - let this = &mut *self.0.borrow_mut(); - if this.last_mode != SpiMode::Touch { - this.spi.set_frequency(TOUCH_FREQ); - this.display_cs.set_high(); - this.last_mode = SpiMode::Touch; + let f_res = f(&mut bus); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + let f_res = f_res.map_err(SpiDeviceWithCsError::Spi)?; + flush_res.map_err(SpiDeviceWithCsError::Spi)?; + cs_res.map_err(SpiDeviceWithCsError::Cs)?; + + Ok(f_res) } - this.spi.transfer(words).unwrap(); - Ok(words) } } -struct Calibration { - x1: i32, - x2: i32, - y1: i32, - y2: i32, - sx: i32, - sy: i32, -} +/// Driver for the XPT2046 resistive touchscreen sensor +mod touch { + use embedded_hal::spi::blocking::{SpiBus, SpiBusRead, SpiBusWrite, 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: SPI, + } + + impl Touch + where + SPI: SpiDevice, + SPI::Bus: SpiBus, + { + 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(|bus| { + bus.write(&[0x90])?; + bus.read(&mut x)?; + bus.write(&[0xd0])?; + bus.read(&mut y)?; + Ok(()) + }) + .unwrap(); + + let x = (u16::from_be_bytes(x) >> 3) as i32; + let y = (u16::from_be_bytes(y) >> 3) as i32; + + let cal = &CALIBRATION; -const CALIBRATION: Calibration = Calibration { - x1: 3880, - x2: 340, - y1: 262, - y2: 3850, - sx: 320, - sy: 240, -}; - -struct Touch< - SPI: embedded_hal::blocking::spi::Transfer, - CS: embedded_hal::digital::v2::OutputPin, -> { - spi: SPI, - cs: CS, + 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)) + } + } + } } -impl, CS: embedded_hal::digital::v2::OutputPin> - Touch -where - SPI::Error: Debug, - CS::Error: Debug, -{ - pub fn new(spi: SPI, cs: CS) -> Self { - Self { spi, cs } +mod my_display_interface { + use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; + use embedded_hal::digital::blocking::OutputPin; + use embedded_hal::spi::blocking::{SpiBusWrite, SpiDevice}; + + /// SPI display interface. + /// + /// This combines the SPI peripheral and a data/command pin + pub struct SPIDeviceInterface { + spi: SPI, + dc: DC, } - pub fn read(&mut self) -> Option<(i32, i32)> { - self.cs.set_low().unwrap(); - let mut buf = [0x90, 0x00, 0x00, 0xd0, 0x00, 0x00]; - self.spi.transfer(&mut buf).unwrap(); - self.cs.set_high().unwrap(); + impl SPIDeviceInterface + where + SPI: SpiDevice, + SPI::Bus: SpiBusWrite, + DC: OutputPin, + { + /// Create new SPI interface for communciation with a display driver + pub fn new(spi: SPI, dc: DC) -> Self { + Self { spi, dc } + } + } - let x = ((buf[1] as u32) << 5 | (buf[2] as u32) >> 3) as i32; - let y = ((buf[4] as u32) << 5 | (buf[5] as u32) >> 3) as i32; + impl WriteOnlyDataCommand for SPIDeviceInterface + where + SPI: SpiDevice, + SPI::Bus: SpiBusWrite, + DC: OutputPin, + { + fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { + let r = self.spi.transaction(|bus| { + // 1 = data, 0 = command + if let Err(_) = self.dc.set_low() { + return Ok(Err(DisplayError::DCError)); + } + + // Send words over SPI + send_u8(bus, cmds)?; + + Ok(Ok(())) + }); + r.map_err(|_| DisplayError::BusWriteError)? + } - let cal = &CALIBRATION; + fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { + let r = self.spi.transaction(|bus| { + // 1 = data, 0 = command + if let Err(_) = self.dc.set_high() { + return Ok(Err(DisplayError::DCError)); + } + + // Send words over SPI + send_u8(bus, buf)?; + + Ok(Ok(())) + }); + r.map_err(|_| DisplayError::BusWriteError)? + } + } - 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)) + fn send_u8(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!(), } } } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 8b6441655..c53f04e1c 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -8,7 +8,7 @@ resolver = "2" [dependencies] embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "unstable-traits"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "usb-otg"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "usb-otg"] } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 2929f539c..2e761df8f 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -19,7 +19,7 @@ defmt-rtt = "0.3" cortex-m = "0.7.3" cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-async = { version = "0.0.1", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy"} +embedded-hal-async = { version = "0.0.1", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy2"} panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 1d5a83fa1..4b1f9a912 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -18,7 +18,7 @@ defmt-rtt = "0.3" cortex-m = "0.7.3" cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-async = { version = "0.0.1", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy"} +embedded-hal-async = { version = "0.0.1", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy2"} panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } diff --git a/examples/stm32l4/src/bin/spi_blocking_async.rs b/examples/stm32l4/src/bin/spi_blocking_async.rs index bcd2e32d5..89925d309 100644 --- a/examples/stm32l4/src/bin/spi_blocking_async.rs +++ b/examples/stm32l4/src/bin/spi_blocking_async.rs @@ -12,7 +12,7 @@ use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; use embassy_stm32::Peripherals; use embassy_traits::adapter::BlockingAsync; -use embedded_hal_async::spi::ReadWrite; +use embedded_hal_async::spi::SpiBus; use example_common::*; #[embassy::main] -- cgit