From a89a0c2f122759a7e743e75152f7af7f67b4387b Mon Sep 17 00:00:00 2001 From: ceekdee Date: Tue, 27 Sep 2022 21:55:41 -0500 Subject: [PATCH 01/46] Initial add for sx126x --- README.md | 2 +- embassy-lora/Cargo.toml | 2 + embassy-lora/src/lib.rs | 2 + embassy-lora/src/sx126x/mod.rs | 171 ++++ .../src/sx126x/sx126x_lora/board_specific.rs | 251 ++++++ embassy-lora/src/sx126x/sx126x_lora/mod.rs | 735 ++++++++++++++++++ .../src/sx126x/sx126x_lora/mod_params.rs | 469 +++++++++++ .../src/sx126x/sx126x_lora/subroutine.rs | 688 ++++++++++++++++ examples/nrf/Cargo.toml | 7 +- examples/nrf/src/bin/lora_p2p_report.rs | 84 ++ examples/nrf/src/bin/lora_p2p_sense.rs | 173 +++++ 11 files changed, 2582 insertions(+), 2 deletions(-) create mode 100644 embassy-lora/src/sx126x/mod.rs create mode 100644 embassy-lora/src/sx126x/sx126x_lora/board_specific.rs create mode 100644 embassy-lora/src/sx126x/sx126x_lora/mod.rs create mode 100644 embassy-lora/src/sx126x/sx126x_lora/mod_params.rs create mode 100644 embassy-lora/src/sx126x/sx126x_lora/subroutine.rs create mode 100644 examples/nrf/src/bin/lora_p2p_report.rs create mode 100644 examples/nrf/src/bin/lora_p2p_sense.rs diff --git a/README.md b/README.md index 9f08bf676..eaa91012c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ The embassy-net network stac The nrf-softdevice crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. - **LoRa** - -embassy-lora supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX127x transceivers. +embassy-lora supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers. - **USB** - embassy-usb implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 1757efa8b..0c2f983f6 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -8,6 +8,7 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" features = ["time", "defmt"] flavors = [ + { name = "rak4631", target = "thumbv7em-none-eabihf", features = ["rak4631"] }, { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, ] @@ -15,6 +16,7 @@ flavors = [ [lib] [features] +rak4631 = [] sx127x = [] stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] time = [] diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 90ba0d1d4..342f66b2a 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -7,6 +7,8 @@ pub(crate) mod fmt; #[cfg(feature = "stm32wl")] pub mod stm32wl; +#[cfg(feature = "rak4631")] +pub mod sx126x; #[cfg(feature = "sx127x")] pub mod sx127x; diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs new file mode 100644 index 000000000..d67c7b109 --- /dev/null +++ b/embassy-lora/src/sx126x/mod.rs @@ -0,0 +1,171 @@ +use core::future::Future; + +use defmt::Format; +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::*; +use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig}; +use lorawan_device::async_device::Timings; + +mod sx126x_lora; +use sx126x_lora::LoRa; + +use self::sx126x_lora::mod_params::RadioError; + +/// Semtech Sx126x LoRa peripheral +pub struct Sx126xRadio +where + SPI: SpiBus + 'static, + CS: OutputPin + 'static, + RESET: OutputPin + 'static, + ANTRX: OutputPin + 'static, + ANTTX: OutputPin + 'static, + WAIT: Wait + 'static, + BUS: Error + Format + 'static, +{ + pub lora: LoRa, +} + +impl Sx126xRadio +where + SPI: SpiBus + 'static, + CS: OutputPin + 'static, + RESET: OutputPin + 'static, + ANTRX: OutputPin + 'static, + ANTTX: OutputPin + 'static, + WAIT: Wait + 'static, + BUS: Error + Format + 'static, +{ + pub async fn new( + spi: SPI, + cs: CS, + reset: RESET, + antenna_rx: ANTRX, + antenna_tx: ANTTX, + dio1: WAIT, + busy: WAIT, + enable_public_network: bool, + ) -> Result> { + let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy); + lora.init().await?; + lora.set_lora_modem(enable_public_network).await?; + Ok(Self { lora }) + } +} + +impl Timings for Sx126xRadio +where + SPI: SpiBus + 'static, + CS: OutputPin + 'static, + RESET: OutputPin + 'static, + ANTRX: OutputPin + 'static, + ANTTX: OutputPin + 'static, + WAIT: Wait + 'static, + BUS: Error + Format + 'static, +{ + fn get_rx_window_offset_ms(&self) -> i32 { + -500 + } + fn get_rx_window_duration_ms(&self) -> u32 { + 800 + } +} + +impl PhyRxTx for Sx126xRadio +where + SPI: SpiBus + 'static, + CS: OutputPin + 'static, + RESET: OutputPin + 'static, + ANTRX: OutputPin + 'static, + ANTTX: OutputPin + 'static, + WAIT: Wait + 'static, + BUS: Error + Format + 'static, +{ + type PhyError = RadioError; + + type TxFuture<'m> = impl Future> + 'm + where + SPI: 'm, + CS: 'm, + RESET: 'm, + ANTRX: 'm, + ANTTX: 'm, + WAIT: 'm, + BUS: 'm; + + fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> { + trace!("TX START"); + async move { + self.lora + .set_tx_config( + config.pw, + config.rf.spreading_factor.into(), + config.rf.bandwidth.into(), + config.rf.coding_rate.into(), + 4, + false, + true, + false, + 0, + false, + ) + .await?; + self.lora.set_max_payload_length(buffer.len() as u8).await?; + self.lora.set_channel(config.rf.frequency).await?; + self.lora.send(buffer, 0xffffff).await?; + self.lora.process_irq(None, None, None).await?; + trace!("TX DONE"); + return Ok(0); + } + } + + type RxFuture<'m> = impl Future> + 'm + where + SPI: 'm, + CS: 'm, + RESET: 'm, + ANTRX: 'm, + ANTTX: 'm, + WAIT: 'm, + BUS: 'm; + + fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> { + trace!("RX START"); + async move { + self.lora + .set_rx_config( + config.spreading_factor.into(), + config.bandwidth.into(), + config.coding_rate.into(), + 4, + 4, + false, + 0u8, + true, + false, + 0, + false, + true, + ) + .await?; + self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?; + self.lora.set_channel(config.frequency).await?; + self.lora.rx(90 * 1000).await?; + let mut received_len = 0u8; + self.lora + .process_irq(Some(receiving_buffer), Some(&mut received_len), None) + .await?; + trace!("RX DONE"); + + let packet_status = self.lora.get_latest_packet_status(); + let mut rssi = 0i16; + let mut snr = 0i8; + if packet_status.is_some() { + rssi = packet_status.unwrap().rssi as i16; + snr = packet_status.unwrap().snr; + } + + Ok((received_len as usize, RxQuality::new(rssi, snr))) + } + } +} diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs new file mode 100644 index 000000000..5b4891c04 --- /dev/null +++ b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs @@ -0,0 +1,251 @@ +use embassy_time::{Duration, Timer}; +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiBus; + +use super::mod_params::RadioError::*; +use super::mod_params::*; +use super::LoRa; + +// Defines the time required for the TCXO to wakeup [ms]. +const BRD_TCXO_WAKEUP_TIME: u32 = 10; + +// Provides board-specific functionality for Semtech SX126x-based boards. Use #[cfg(feature = "board_type")] to specify unique board functionality. +// The base implementation supports the RAK4631 board. + +impl LoRa +where + SPI: SpiBus, + CS: OutputPin, + RESET: OutputPin, + ANTRX: OutputPin, + ANTTX: OutputPin, + WAIT: Wait, +{ + // De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes. + pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // Initialize the TCXO power pin + pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError> { + let timeout = self.brd_get_board_tcxo_wakeup_time() << 6; + self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout) + .await?; + Ok(()) + } + + // Initialize RF switch control pins + pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError> { + self.sub_set_dio2_as_rf_switch_ctrl(true).await?; + Ok(()) + } + + // Initialize the radio debug pins + pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // Hardware reset of the radio + pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError> { + Timer::after(Duration::from_millis(10)).await; + self.reset.set_low().map_err(|_| Reset)?; + Timer::after(Duration::from_millis(20)).await; + self.reset.set_high().map_err(|_| Reset)?; + Timer::after(Duration::from_millis(10)).await; + Ok(()) + } + + // Wait while the busy pin is high + pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError> { + self.busy.wait_for_low().await.map_err(|_| Busy)?; + Ok(()) + } + + // Wake up the radio + pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError> { + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?; + self.spi.write(&[0x00]).await.map_err(SPI)?; + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + self.brd_set_operating_mode(RadioMode::StandbyRC); + Ok(()) + } + + // Send a command that writes data to the radio + pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError> { + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[op_code.value()]).await.map_err(SPI)?; + self.spi.write(buffer).await.map_err(SPI)?; + self.cs.set_high().map_err(|_| CS)?; + + if op_code != OpCode::SetSleep { + self.brd_wait_on_busy().await?; + } + Ok(()) + } + + // Send a command that reads data from the radio, filling the provided buffer and returning a status + pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result> { + let mut status = [0u8]; + let mut input = [0u8]; + + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[op_code.value()]).await.map_err(SPI)?; + self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?; + for i in 0..buffer.len() { + self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; + buffer[i] = input[0]; + } + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + + Ok(status[0]) + } + + // Write one or more bytes of data to the radio memory + pub(super) async fn brd_write_registers( + &mut self, + start_register: Register, + buffer: &[u8], + ) -> Result<(), RadioError> { + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?; + self.spi + .write(&[ + ((start_register.addr() & 0xFF00) >> 8) as u8, + (start_register.addr() & 0x00FF) as u8, + ]) + .await + .map_err(SPI)?; + self.spi.write(buffer).await.map_err(SPI)?; + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Read one or more bytes of data from the radio memory + pub(super) async fn brd_read_registers( + &mut self, + start_register: Register, + buffer: &mut [u8], + ) -> Result<(), RadioError> { + let mut input = [0u8]; + + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?; + self.spi + .write(&[ + ((start_register.addr() & 0xFF00) >> 8) as u8, + (start_register.addr() & 0x00FF) as u8, + 0x00u8, + ]) + .await + .map_err(SPI)?; + for i in 0..buffer.len() { + self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; + buffer[i] = input[0]; + } + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Write data to the buffer holding the payload in the radio + pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError> { + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?; + self.spi.write(&[offset]).await.map_err(SPI)?; + self.spi.write(buffer).await.map_err(SPI)?; + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Read data from the buffer holding the payload in the radio + pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError> { + let mut input = [0u8]; + + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?; + self.spi.write(&[offset]).await.map_err(SPI)?; + self.spi.write(&[0x00]).await.map_err(SPI)?; + for i in 0..buffer.len() { + self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; + buffer[i] = input[0]; + } + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Set the radio output power + pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError> { + self.sub_set_tx_params(power, RampTime::Ramp40Us).await?; + Ok(()) + } + + // Get the radio type + pub(super) fn brd_get_radio_type(&mut self) -> RadioType { + #[cfg(feature = "rak4631")] + RadioType::SX1262 + } + + // Initialize the RF switch I/O pins interface + pub(super) async fn brd_ant_sw_on(&mut self) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // De-initialize the RF switch I/O pins interface for MCU low power modes + pub(super) async fn brd_ant_sw_off(&mut self) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // Check if the given RF frequency is supported by the hardware + pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result> { + #[cfg(feature = "rak4631")] + Ok(true) // all frequencies currently supported for the SX1262 within a rak4631 + } + + // Get the duration required for the TCXO to wakeup [ms]. + pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 { + BRD_TCXO_WAKEUP_TIME + } + + /* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process + pub(super) async fn brd_get_dio1_pin_state( + &mut self, + ) -> Result> { + Ok(0) + } + */ + + // Get the current radio operatiing mode + pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode { + self.operating_mode + } + + // Set/Update the current radio operating mode This function is only required to reflect the current radio operating mode when processing interrupts. + pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) { + self.operating_mode = mode; + } +} diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs new file mode 100644 index 000000000..02f5f83a2 --- /dev/null +++ b/embassy-lora/src/sx126x/sx126x_lora/mod.rs @@ -0,0 +1,735 @@ +#![allow(dead_code)] + +use embassy_time::{Duration, Timer}; +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiBus; + +mod board_specific; +pub mod mod_params; +mod subroutine; + +use mod_params::RadioError::*; +use mod_params::*; + +// Syncwords for public and private networks +const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444; +const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424; + +// Maximum number of registers that can be added to the retention list +const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4; + +// Possible LoRa bandwidths +const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz]; + +// Radio complete wakeup time with margin for temperature compensation [ms] +const RADIO_WAKEUP_TIME: u32 = 3; + +/// Provides high-level access to Semtech SX126x-based boards +pub struct LoRa { + spi: SPI, + cs: CS, + reset: RESET, + antenna_rx: ANTRX, + antenna_tx: ANTTX, + dio1: WAIT, + busy: WAIT, + operating_mode: RadioMode, + rx_continuous: bool, + max_payload_length: u8, + modulation_params: Option, + packet_type: PacketType, + packet_params: Option, + packet_status: Option, + image_calibrated: bool, + frequency_error: u32, +} + +impl LoRa +where + SPI: SpiBus, + CS: OutputPin, + RESET: OutputPin, + ANTRX: OutputPin, + ANTTX: OutputPin, + WAIT: Wait, +{ + /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time () + pub fn new(spi: SPI, cs: CS, reset: RESET, antenna_rx: ANTRX, antenna_tx: ANTTX, dio1: WAIT, busy: WAIT) -> Self { + Self { + spi, + cs, + reset, + antenna_rx, + antenna_tx, + dio1, + busy, + operating_mode: RadioMode::Sleep, + rx_continuous: false, + max_payload_length: 0xFFu8, + modulation_params: None, + packet_type: PacketType::LoRa, + packet_params: None, + packet_status: None, + image_calibrated: false, + frequency_error: 0u32, // where is volatile FrequencyError modified ??? + } + } + + /// Initialize the radio + pub async fn init(&mut self) -> Result<(), RadioError> { + self.sub_init().await?; + self.sub_set_standby(StandbyMode::RC).await?; + self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?; + self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?; + self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?; + self.sub_set_dio_irq_params( + IrqMask::All.value(), + IrqMask::All.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + self.add_register_to_retention_list(Register::RxGain.addr()).await?; + self.add_register_to_retention_list(Register::TxModulation.addr()) + .await?; + Ok(()) + } + + /// Return current radio state + pub fn get_status(&mut self) -> RadioState { + match self.brd_get_operating_mode() { + RadioMode::Transmit => RadioState::TxRunning, + RadioMode::Receive => RadioState::RxRunning, + RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting, + _ => RadioState::Idle, + } + } + + /// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired) + pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError> { + self.sub_set_packet_type(PacketType::LoRa).await?; + if enable_public_network { + self.brd_write_registers( + Register::LoRaSyncword, + &[ + ((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8, + (LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8, + ], + ) + .await?; + } else { + self.brd_write_registers( + Register::LoRaSyncword, + &[ + ((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8, + (LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8, + ], + ) + .await?; + } + + Ok(()) + } + + /// Sets the channel frequency + pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError> { + self.sub_set_rf_frequency(frequency).await?; + Ok(()) + } + + /* Checks if the channel is free for the given time. This is currently not implemented until a substitute + for switching to the FSK modem is found. + + pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool; + */ + + /// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts. Ensure set_lora_modem() is called befrorehand. + /// After calling this function either set_rx_config() or set_tx_config() must be called. + pub async fn get_random_value(&mut self) -> Result> { + self.sub_set_dio_irq_params( + IrqMask::None.value(), + IrqMask::None.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + + let result = self.sub_get_random().await?; + Ok(result) + } + + /// Set the reception parameters for the LoRa modem (only). Ensure set_lora_modem() is called befrorehand. + /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] + /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] + /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + /// preamble_length length in symbols (the hardware adds 4 more symbols) + /// symb_timeout RxSingle timeout value in symbols + /// fixed_len fixed length packets [0: variable, 1: fixed] + /// payload_len payload length when fixed length is used + /// crc_on [0: OFF, 1: ON] + /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON] + /// hop_period number of symbols between each hop + /// iq_inverted invert IQ signals [0: not inverted, 1: inverted] + /// rx_continuous reception mode [false: single mode, true: continuous mode] + pub async fn set_rx_config( + &mut self, + spreading_factor: SpreadingFactor, + bandwidth: Bandwidth, + coding_rate: CodingRate, + preamble_length: u16, + symb_timeout: u16, + fixed_len: bool, + payload_len: u8, + crc_on: bool, + _freq_hop_on: bool, + _hop_period: u8, + iq_inverted: bool, + rx_continuous: bool, + ) -> Result<(), RadioError> { + let mut symb_timeout_final = symb_timeout; + + self.rx_continuous = rx_continuous; + if self.rx_continuous { + symb_timeout_final = 0; + } + if fixed_len { + self.max_payload_length = payload_len; + } else { + self.max_payload_length = 0xFFu8; + } + + self.sub_set_stop_rx_timer_on_preamble_detect(false).await?; + + let mut low_data_rate_optimize = 0x00u8; + if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) + && (bandwidth == Bandwidth::_125KHz)) + || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) + { + low_data_rate_optimize = 0x01u8; + } + + let modulation_params = ModulationParams { + spreading_factor: spreading_factor, + bandwidth: bandwidth, + coding_rate: coding_rate, + low_data_rate_optimize: low_data_rate_optimize, + }; + + let mut preamble_length_final = preamble_length; + if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) + && (preamble_length < 12) + { + preamble_length_final = 12; + } + + let packet_params = PacketParams { + preamble_length: preamble_length_final, + implicit_header: fixed_len, + payload_length: self.max_payload_length, + crc_on: crc_on, + iq_inverted: iq_inverted, + }; + + self.modulation_params = Some(modulation_params); + self.packet_params = Some(packet_params); + + self.standby().await?; + self.sub_set_modulation_params().await?; + self.sub_set_packet_params().await?; + self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?; + + // Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) + let mut iq_polarity = [0x00u8]; + self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?; + if iq_inverted { + self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))]) + .await?; + } else { + self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)]) + .await?; + } + Ok(()) + } + + /// Set the transmission parameters for the LoRa modem (only). + /// power output power [dBm] + /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] + /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] + /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + /// preamble_length length in symbols (the hardware adds 4 more symbols) + /// fixed_len fixed length packets [0: variable, 1: fixed] + /// crc_on [0: OFF, 1: ON] + /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON] + /// hop_period number of symbols between each hop + /// iq_inverted invert IQ signals [0: not inverted, 1: inverted] + pub async fn set_tx_config( + &mut self, + power: i8, + spreading_factor: SpreadingFactor, + bandwidth: Bandwidth, + coding_rate: CodingRate, + preamble_length: u16, + fixed_len: bool, + crc_on: bool, + _freq_hop_on: bool, + _hop_period: u8, + iq_inverted: bool, + ) -> Result<(), RadioError> { + let mut low_data_rate_optimize = 0x00u8; + if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) + && (bandwidth == Bandwidth::_125KHz)) + || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) + { + low_data_rate_optimize = 0x01u8; + } + + let modulation_params = ModulationParams { + spreading_factor: spreading_factor, + bandwidth: bandwidth, + coding_rate: coding_rate, + low_data_rate_optimize: low_data_rate_optimize, + }; + + let mut preamble_length_final = preamble_length; + if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) + && (preamble_length < 12) + { + preamble_length_final = 12; + } + + let packet_params = PacketParams { + preamble_length: preamble_length_final, + implicit_header: fixed_len, + payload_length: self.max_payload_length, + crc_on: crc_on, + iq_inverted: iq_inverted, + }; + + self.modulation_params = Some(modulation_params); + self.packet_params = Some(packet_params); + + self.standby().await?; + self.sub_set_modulation_params().await?; + self.sub_set_packet_params().await?; + + // Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) + + let mut tx_modulation = [0x00u8]; + self.brd_read_registers(Register::TxModulation, &mut tx_modulation) + .await?; + if bandwidth == Bandwidth::_500KHz { + self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))]) + .await?; + } else { + self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)]) + .await?; + } + + self.brd_set_rf_tx_power(power).await?; + Ok(()) + } + + /// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported] + pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result> { + Ok(self.brd_check_rf_frequency(frequency).await?) + } + + /// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called) + /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] + /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] + /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + /// preamble_length length in symbols (the hardware adds 4 more symbols) + /// fixed_len fixed length packets [0: variable, 1: fixed] + /// payload_len sets payload length when fixed length is used + /// crc_on [0: OFF, 1: ON] + pub fn get_time_on_air( + &mut self, + spreading_factor: SpreadingFactor, + bandwidth: Bandwidth, + coding_rate: CodingRate, + preamble_length: u16, + fixed_len: bool, + payload_len: u8, + crc_on: bool, + ) -> Result> { + let numerator = 1000 + * Self::get_lora_time_on_air_numerator( + spreading_factor, + bandwidth, + coding_rate, + preamble_length, + fixed_len, + payload_len, + crc_on, + ); + let denominator = bandwidth.value_in_hz(); + if denominator == 0 { + Err(RadioError::InvalidBandwidth) + } else { + Ok((numerator + denominator - 1) / denominator) + } + } + + /// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms] + pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError> { + if self.packet_params.is_some() { + self.sub_set_dio_irq_params( + IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), + IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + + let mut packet_params = self.packet_params.as_mut().unwrap(); + packet_params.payload_length = buffer.len() as u8; + self.sub_set_packet_params().await?; + self.sub_send_payload(buffer, timeout).await?; + Ok(()) + } else { + Err(RadioError::PacketParamsMissing) + } + } + + /// Set the radio in sleep mode + pub async fn sleep(&mut self) -> Result<(), RadioError> { + self.sub_set_sleep(SleepParams { + wakeup_rtc: false, + reset: false, + warm_start: true, + }) + .await?; + Timer::after(Duration::from_millis(2)).await; + Ok(()) + } + + /// Set the radio in standby mode + pub async fn standby(&mut self) -> Result<(), RadioError> { + self.sub_set_standby(StandbyMode::RC).await?; + Ok(()) + } + + /// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)] + pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError> { + self.sub_set_dio_irq_params( + IrqMask::All.value(), + IrqMask::All.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + + if self.rx_continuous { + self.sub_set_rx(0xFFFFFF).await?; + } else { + self.sub_set_rx(timeout << 6).await?; + } + + Ok(()) + } + + /// Start a Channel Activity Detection + pub async fn start_cad(&mut self) -> Result<(), RadioError> { + self.sub_set_dio_irq_params( + IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), + IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + self.sub_set_cad().await?; + Ok(()) + } + + /// Sets the radio in continuous wave transmission mode + /// frequency channel RF frequency + /// power output power [dBm] + /// timeout transmission mode timeout [s] + pub async fn set_tx_continuous_wave( + &mut self, + frequency: u32, + power: i8, + _timeout: u16, + ) -> Result<(), RadioError> { + self.sub_set_rf_frequency(frequency).await?; + self.brd_set_rf_tx_power(power).await?; + self.sub_set_tx_continuous_wave().await?; + + Ok(()) + } + + /// Read the current RSSI value for the LoRa modem (only) [dBm] + pub async fn get_rssi(&mut self) -> Result> { + let value = self.sub_get_rssi_inst().await?; + Ok(value as i16) + } + + /// Write one or more radio registers with a buffer of a given size, starting at the first register address + pub async fn write_registers_from_buffer( + &mut self, + start_register: Register, + buffer: &[u8], + ) -> Result<(), RadioError> { + self.brd_write_registers(start_register, buffer).await?; + Ok(()) + } + + /// Read one or more radio registers into a buffer of a given size, starting at the first register address + pub async fn read_registers_into_buffer( + &mut self, + start_register: Register, + buffer: &mut [u8], + ) -> Result<(), RadioError> { + self.brd_read_registers(start_register, buffer).await?; + Ok(()) + } + + /// Set the maximum payload length (in bytes) for a LoRa modem (only). + pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError> { + if self.packet_params.is_some() { + let packet_params = self.packet_params.as_mut().unwrap(); + self.max_payload_length = max; + packet_params.payload_length = max; + self.sub_set_packet_params().await?; + Ok(()) + } else { + Err(RadioError::PacketParamsMissing) + } + } + + /// Get the time required for the board plus radio to get out of sleep [ms] + pub fn get_wakeup_time(&mut self) -> u32 { + self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME + } + + /// Process the radio irq + pub async fn process_irq( + &mut self, + receiving_buffer: Option<&mut [u8]>, + received_len: Option<&mut u8>, + cad_activity_detected: Option<&mut bool>, + ) -> Result<(), RadioError> { + loop { + info!("process_irq loop entered"); // debug ??? + + let de = self.sub_get_device_errors().await?; + info!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", + de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp); + let st = self.sub_get_status().await?; + info!( + "radio status: cmd_status: {:x}, chip_mode: {:x}", + st.cmd_status, st.chip_mode + ); + + self.dio1.wait_for_high().await.map_err(|_| DIO1)?; + let operating_mode = self.brd_get_operating_mode(); + let irq_flags = self.sub_get_irq_status().await?; + self.sub_clear_irq_status(irq_flags).await?; + info!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); // debug ??? + + // check for errors and unexpected interrupt masks (based on operation mode) + if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() { + if !self.rx_continuous { + self.brd_set_operating_mode(RadioMode::StandbyRC); + } + return Err(RadioError::HeaderError); + } else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() { + if operating_mode == RadioMode::Receive { + if !self.rx_continuous { + self.brd_set_operating_mode(RadioMode::StandbyRC); + } + return Err(RadioError::CRCErrorOnReceive); + } else { + return Err(RadioError::CRCErrorUnexpected); + } + } else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() { + if operating_mode == RadioMode::Transmit { + self.brd_set_operating_mode(RadioMode::StandbyRC); + return Err(RadioError::TransmitTimeout); + } else if operating_mode == RadioMode::Receive { + self.brd_set_operating_mode(RadioMode::StandbyRC); + return Err(RadioError::ReceiveTimeout); + } else { + return Err(RadioError::TimeoutUnexpected); + } + } else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value()) + && (operating_mode != RadioMode::Transmit) + { + return Err(RadioError::TransmitDoneUnexpected); + } else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value()) + && (operating_mode != RadioMode::Receive) + { + return Err(RadioError::ReceiveDoneUnexpected); + } else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value()) + || ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value())) + && (operating_mode != RadioMode::ChannelActivityDetection) + { + return Err(RadioError::CADUnexpected); + } + + // debug ??? + if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() { + info!("HeaderValid"); + } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() { + info!("PreambleDetected"); + } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() { + info!("SyncwordValid"); + } + + // handle completions + if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() { + self.brd_set_operating_mode(RadioMode::StandbyRC); + return Ok(()); + } else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() { + if !self.rx_continuous { + self.brd_set_operating_mode(RadioMode::StandbyRC); + + // implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3) + self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?; + let mut evt_clr = [0x00u8]; + self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?; + evt_clr[0] |= 1 << 1; + self.brd_write_registers(Register::EvtClr, &evt_clr).await?; + } + + if receiving_buffer.is_some() && received_len.is_some() { + *(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?; + } + self.packet_status = self.sub_get_packet_status().await?.into(); + return Ok(()); + } else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() { + if cad_activity_detected.is_some() { + *(cad_activity_detected.unwrap()) = + (irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value(); + } + self.brd_set_operating_mode(RadioMode::StandbyRC); + return Ok(()); + } + + // if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid + // are in that category), loop to wait again + } + } + + // SX126x-specific functions + + /// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms] + pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError> { + self.sub_set_dio_irq_params( + IrqMask::All.value(), + IrqMask::All.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + + if self.rx_continuous { + self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous + } else { + self.sub_set_rx_boosted(timeout << 6).await?; + } + + Ok(()) + } + + /// Set the Rx duty cycle management parameters (SX126x radios only) + /// rx_time structure describing reception timeout value + /// sleep_time structure describing sleep timeout value + pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError> { + self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?; + Ok(()) + } + + pub fn get_latest_packet_status(&mut self) -> Option { + self.packet_status + } + + // Utilities + + async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError> { + let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize]; + + // Read the address and registers already added to the list + self.brd_read_registers(Register::RetentionList, &mut buffer).await?; + + let number_of_registers = buffer[0]; + for i in 0..number_of_registers { + if register_address + == ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16) + { + return Ok(()); // register already in list + } + } + + if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION { + buffer[0] += 1; // increment number of registers + + buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8; + buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8; + self.brd_write_registers(Register::RetentionList, &buffer).await?; + + Ok(()) + } else { + Err(RadioError::RetentionListExceeded) + } + } + + fn get_lora_time_on_air_numerator( + spreading_factor: SpreadingFactor, + bandwidth: Bandwidth, + coding_rate: CodingRate, + preamble_length: u16, + fixed_len: bool, + payload_len: u8, + crc_on: bool, + ) -> u32 { + let cell_denominator; + let cr_denominator = (coding_rate.value() as i32) + 4; + + // Ensure that the preamble length is at least 12 symbols when using SF5 or SF6 + let mut preamble_length_final = preamble_length; + if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) + && (preamble_length < 12) + { + preamble_length_final = 12; + } + + let mut low_data_rate_optimize = false; + if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) + && (bandwidth == Bandwidth::_125KHz)) + || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) + { + low_data_rate_optimize = true; + } + + let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 }) + - (4 * spreading_factor.value() as i32) + + (if fixed_len { 0 } else { 20 }); + + if spreading_factor.value() <= 6 { + cell_denominator = 4 * (spreading_factor.value() as i32); + } else { + cell_numerator += 8; + if low_data_rate_optimize { + cell_denominator = 4 * ((spreading_factor.value() as i32) - 2); + } else { + cell_denominator = 4 * (spreading_factor.value() as i32); + } + } + + if cell_numerator < 0 { + cell_numerator = 0; + } + + let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator) + + (preamble_length_final as i32) + + 12; + + if spreading_factor.value() <= 6 { + intermediate = intermediate + 2; + } + + (((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32 + } +} diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs new file mode 100644 index 000000000..e270b2a09 --- /dev/null +++ b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs @@ -0,0 +1,469 @@ +use core::fmt::Debug; + +use lorawan_device::async_device::radio as device; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RadioError { + SPI(BUS), + CS, + Reset, + AntRx, + AntTx, + Busy, + DIO1, + PayloadSizeMismatch(usize, usize), + RetentionListExceeded, + InvalidBandwidth, + ModulationParamsMissing, + PacketParamsMissing, + HeaderError, + CRCErrorUnexpected, + CRCErrorOnReceive, + TransmitTimeout, + ReceiveTimeout, + TimeoutUnexpected, + TransmitDoneUnexpected, + ReceiveDoneUnexpected, + CADUnexpected, +} + +pub struct RadioSystemError { + pub rc_64khz_calibration: bool, + pub rc_13mhz_calibration: bool, + pub pll_calibration: bool, + pub adc_calibration: bool, + pub image_calibration: bool, + pub xosc_start: bool, + pub pll_lock: bool, + pub pa_ramp: bool, +} + +#[derive(Clone, Copy, PartialEq)] +pub enum PacketType { + GFSK = 0x00, + LoRa = 0x01, + None = 0x0F, +} + +impl PacketType { + pub const fn value(self) -> u8 { + self as u8 + } + pub fn to_enum(value: u8) -> Self { + if value == 0x00 { + PacketType::GFSK + } else if value == 0x01 { + PacketType::LoRa + } else { + PacketType::None + } + } +} + +#[derive(Clone, Copy)] +pub struct PacketStatus { + pub rssi: i8, + pub snr: i8, + pub signal_rssi: i8, + pub freq_error: u32, +} + +#[derive(Clone, Copy, PartialEq)] +pub enum RadioType { + SX1261, + SX1262, +} + +#[derive(Clone, Copy, PartialEq)] +pub enum RadioMode { + Sleep = 0x00, // sleep mode + StandbyRC = 0x01, // standby mode with RC oscillator + StandbyXOSC = 0x02, // standby mode with XOSC oscillator + FrequencySynthesis = 0x03, // frequency synthesis mode + Transmit = 0x04, // transmit mode + Receive = 0x05, // receive mode + ReceiveDutyCycle = 0x06, // receive duty cycle mode + ChannelActivityDetection = 0x07, // channel activity detection mode +} + +impl RadioMode { + /// Returns the value of the mode. + pub const fn value(self) -> u8 { + self as u8 + } + pub fn to_enum(value: u8) -> Self { + if value == 0x00 { + RadioMode::Sleep + } else if value == 0x01 { + RadioMode::StandbyRC + } else if value == 0x02 { + RadioMode::StandbyXOSC + } else if value == 0x03 { + RadioMode::FrequencySynthesis + } else if value == 0x04 { + RadioMode::Transmit + } else if value == 0x05 { + RadioMode::Receive + } else if value == 0x06 { + RadioMode::ReceiveDutyCycle + } else if value == 0x07 { + RadioMode::ChannelActivityDetection + } else { + RadioMode::Sleep + } + } +} + +pub enum RadioState { + Idle = 0x00, + RxRunning = 0x01, + TxRunning = 0x02, + ChannelActivityDetecting = 0x03, +} + +impl RadioState { + /// Returns the value of the state. + pub fn value(self) -> u8 { + self as u8 + } +} + +pub struct RadioStatus { + pub cmd_status: u8, + pub chip_mode: u8, +} + +impl RadioStatus { + pub fn value(self) -> u8 { + (self.chip_mode << 4) | (self.cmd_status << 1) + } +} + +#[derive(Clone, Copy)] +pub enum IrqMask { + None = 0x0000, + TxDone = 0x0001, + RxDone = 0x0002, + PreambleDetected = 0x0004, + SyncwordValid = 0x0008, + HeaderValid = 0x0010, + HeaderError = 0x0020, + CRCError = 0x0040, + CADDone = 0x0080, + CADActivityDetected = 0x0100, + RxTxTimeout = 0x0200, + All = 0xFFFF, +} + +impl IrqMask { + pub fn value(self) -> u16 { + self as u16 + } +} + +#[derive(Clone, Copy)] +pub enum Register { + PacketParams = 0x0704, // packet configuration + PayloadLength = 0x0702, // payload size + SynchTimeout = 0x0706, // recalculated number of symbols + Syncword = 0x06C0, // Syncword values + LoRaSyncword = 0x0740, // LoRa Syncword value + GeneratedRandomNumber = 0x0819, //32-bit generated random number + AnaLNA = 0x08E2, // disable the LNA + AnaMixer = 0x08E5, // disable the mixer + RxGain = 0x08AC, // RX gain (0x94: power saving, 0x96: rx boosted) + XTATrim = 0x0911, // device internal trimming capacitor + OCP = 0x08E7, // over current protection max value + RetentionList = 0x029F, // retention list + IQPolarity = 0x0736, // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) + TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) + TxClampCfg = 0x08D8, // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) + RTCCtrl = 0x0902, // RTC control + EvtClr = 0x0944, // event clear +} + +impl Register { + pub fn addr(self) -> u16 { + self as u16 + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum OpCode { + GetStatus = 0xC0, + WriteRegister = 0x0D, + ReadRegister = 0x1D, + WriteBuffer = 0x0E, + ReadBuffer = 0x1E, + SetSleep = 0x84, + SetStandby = 0x80, + SetFS = 0xC1, + SetTx = 0x83, + SetRx = 0x82, + SetRxDutyCycle = 0x94, + SetCAD = 0xC5, + SetTxContinuousWave = 0xD1, + SetTxContinuousPremable = 0xD2, + SetPacketType = 0x8A, + GetPacketType = 0x11, + SetRFFrequency = 0x86, + SetTxParams = 0x8E, + SetPAConfig = 0x95, + SetCADParams = 0x88, + SetBufferBaseAddress = 0x8F, + SetModulationParams = 0x8B, + SetPacketParams = 0x8C, + GetRxBufferStatus = 0x13, + GetPacketStatus = 0x14, + GetRSSIInst = 0x15, + GetStats = 0x10, + ResetStats = 0x00, + CfgDIOIrq = 0x08, + GetIrqStatus = 0x12, + ClrIrqStatus = 0x02, + Calibrate = 0x89, + CalibrateImage = 0x98, + SetRegulatorMode = 0x96, + GetErrors = 0x17, + ClrErrors = 0x07, + SetTCXOMode = 0x97, + SetTxFallbackMode = 0x93, + SetRFSwitchMode = 0x9D, + SetStopRxTimerOnPreamble = 0x9F, + SetLoRaSymbTimeout = 0xA0, +} + +impl OpCode { + pub fn value(self) -> u8 { + self as u8 + } +} + +pub struct SleepParams { + pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC + pub reset: bool, + pub warm_start: bool, +} + +impl SleepParams { + pub fn value(self) -> u8 { + ((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8) + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum StandbyMode { + RC = 0x00, + XOSC = 0x01, +} + +impl StandbyMode { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy)] +pub enum RegulatorMode { + UseLDO = 0x00, + UseDCDC = 0x01, +} + +impl RegulatorMode { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy)] +pub struct CalibrationParams { + pub rc64k_enable: bool, // calibrate RC64K clock + pub rc13m_enable: bool, // calibrate RC13M clock + pub pll_enable: bool, // calibrate PLL + pub adc_pulse_enable: bool, // calibrate ADC Pulse + pub adc_bulkn_enable: bool, // calibrate ADC bulkN + pub adc_bulkp_enable: bool, // calibrate ADC bulkP + pub img_enable: bool, +} + +impl CalibrationParams { + pub fn value(self) -> u8 { + ((self.img_enable as u8) << 6) + | ((self.adc_bulkp_enable as u8) << 5) + | ((self.adc_bulkn_enable as u8) << 4) + | ((self.adc_pulse_enable as u8) << 3) + | ((self.pll_enable as u8) << 2) + | ((self.rc13m_enable as u8) << 1) + | ((self.rc64k_enable as u8) << 0) + } +} + +#[derive(Clone, Copy)] +pub enum TcxoCtrlVoltage { + Ctrl1V6 = 0x00, + Ctrl1V7 = 0x01, + Ctrl1V8 = 0x02, + Ctrl2V2 = 0x03, + Ctrl2V4 = 0x04, + Ctrl2V7 = 0x05, + Ctrl3V0 = 0x06, + Ctrl3V3 = 0x07, +} + +impl TcxoCtrlVoltage { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy)] +pub enum RampTime { + Ramp10Us = 0x00, + Ramp20Us = 0x01, + Ramp40Us = 0x02, + Ramp80Us = 0x03, + Ramp200Us = 0x04, + Ramp800Us = 0x05, + Ramp1700Us = 0x06, + Ramp3400Us = 0x07, +} + +impl RampTime { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum SpreadingFactor { + _5 = 0x05, + _6 = 0x06, + _7 = 0x07, + _8 = 0x08, + _9 = 0x09, + _10 = 0x0A, + _11 = 0x0B, + _12 = 0x0C, +} + +impl SpreadingFactor { + pub fn value(self) -> u8 { + self as u8 + } +} + +impl From for SpreadingFactor { + fn from(sf: device::SpreadingFactor) -> Self { + match sf { + device::SpreadingFactor::_7 => SpreadingFactor::_7, + device::SpreadingFactor::_8 => SpreadingFactor::_8, + device::SpreadingFactor::_9 => SpreadingFactor::_9, + device::SpreadingFactor::_10 => SpreadingFactor::_10, + device::SpreadingFactor::_11 => SpreadingFactor::_11, + device::SpreadingFactor::_12 => SpreadingFactor::_12, + } + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum Bandwidth { + _500KHz = 0x06, + _250KHz = 0x05, + _125KHz = 0x04, +} + +impl Bandwidth { + pub fn value(self) -> u8 { + self as u8 + } + + pub fn value_in_hz(self) -> u32 { + match self { + Bandwidth::_125KHz => 125000u32, + Bandwidth::_250KHz => 250000u32, + Bandwidth::_500KHz => 500000u32, + } + } +} + +impl From for Bandwidth { + fn from(bw: device::Bandwidth) -> Self { + match bw { + device::Bandwidth::_500KHz => Bandwidth::_500KHz, + device::Bandwidth::_250KHz => Bandwidth::_250KHz, + device::Bandwidth::_125KHz => Bandwidth::_125KHz, + } + } +} + +#[derive(Clone, Copy)] +pub enum CodingRate { + _4_5 = 0x01, + _4_6 = 0x02, + _4_7 = 0x03, + _4_8 = 0x04, +} + +impl CodingRate { + pub fn value(self) -> u8 { + self as u8 + } +} + +impl From for CodingRate { + fn from(cr: device::CodingRate) -> Self { + match cr { + device::CodingRate::_4_5 => CodingRate::_4_5, + device::CodingRate::_4_6 => CodingRate::_4_6, + device::CodingRate::_4_7 => CodingRate::_4_7, + device::CodingRate::_4_8 => CodingRate::_4_8, + } + } +} + +#[derive(Clone, Copy)] +pub struct ModulationParams { + pub spreading_factor: SpreadingFactor, + pub bandwidth: Bandwidth, + pub coding_rate: CodingRate, + pub low_data_rate_optimize: u8, +} + +#[derive(Clone, Copy)] +pub struct PacketParams { + pub preamble_length: u16, // number of LoRa symbols in the preamble + pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length) + pub payload_length: u8, + pub crc_on: bool, + pub iq_inverted: bool, +} + +#[derive(Clone, Copy)] +pub enum CADSymbols { + _1 = 0x00, + _2 = 0x01, + _4 = 0x02, + _8 = 0x03, + _16 = 0x04, +} + +impl CADSymbols { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy)] +pub enum CADExitMode { + CADOnly = 0x00, + CADRx = 0x01, + CADLBT = 0x10, +} + +impl CADExitMode { + pub fn value(self) -> u8 { + self as u8 + } +} diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs new file mode 100644 index 000000000..02a0a72ec --- /dev/null +++ b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs @@ -0,0 +1,688 @@ +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiBus; + +use super::mod_params::*; +use super::LoRa; + +// Internal frequency of the radio +const SX126X_XTAL_FREQ: u32 = 32000000; + +// Scaling factor used to perform fixed-point operations +const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14; + +// PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT +const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT); + +// Maximum value for parameter symbNum +const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248; + +// Provides board-specific functionality for Semtech SX126x-based boards + +impl LoRa +where + SPI: SpiBus, + CS: OutputPin, + RESET: OutputPin, + ANTRX: OutputPin, + ANTTX: OutputPin, + WAIT: Wait, +{ + // Initialize the radio driver + pub(super) async fn sub_init(&mut self) -> Result<(), RadioError> { + self.brd_reset().await?; + self.brd_wakeup().await?; + self.sub_set_standby(StandbyMode::RC).await?; + self.brd_io_tcxo_init().await?; + self.brd_io_rf_switch_init().await?; + self.image_calibrated = false; + Ok(()) + } + + // Wakeup the radio if it is in Sleep mode and check that Busy is low + pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError> { + let operating_mode = self.brd_get_operating_mode(); + if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle { + self.brd_wakeup().await?; + self.brd_ant_sw_on().await?; + } + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Save the payload to be sent in the radio buffer + pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError> { + self.brd_write_buffer(0x00, payload).await?; + Ok(()) + } + + // Read the payload received. + pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result> { + let (size, offset) = self.sub_get_rx_buffer_status().await?; + if (size as usize) > buffer.len() { + Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len())) + } else { + self.brd_read_buffer(offset, buffer).await?; + Ok(size) + } + } + + // Send a payload + pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError> { + self.sub_set_payload(payload).await?; + self.sub_set_tx(timeout).await?; + Ok(()) + } + + // Get a 32-bit random value generated by the radio. A valid packet type must have been configured before using this command. + // + // The radio must be in reception mode before executing this function. This code can potentially result in interrupt generation. It is the responsibility of + // the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts + // generated during this process will not cause undesired side-effects in the software. + // + // The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing. + pub(super) async fn sub_get_random(&mut self) -> Result> { + let mut reg_ana_lna_buffer_original = [0x00u8]; + let mut reg_ana_mixer_buffer_original = [0x00u8]; + let mut reg_ana_lna_buffer = [0x00u8]; + let mut reg_ana_mixer_buffer = [0x00u8]; + let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8]; + + self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original) + .await?; + reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0)); + self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer).await?; + + self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original) + .await?; + reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7)); + self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer) + .await?; + + // Set radio in continuous reception + self.sub_set_rx(0xFFFFFFu32).await?; + + self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer) + .await?; + + self.sub_set_standby(StandbyMode::RC).await?; + + self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer_original) + .await?; + self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer_original) + .await?; + + Ok(Self::convert_u8_buffer_to_u32(&number_buffer)) + } + + // Set the radio in sleep mode + pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError> { + self.brd_ant_sw_off().await?; + + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_low(); + + if !sleep_config.warm_start { + self.image_calibrated = false; + } + + self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()]) + .await?; + self.brd_set_operating_mode(RadioMode::Sleep); + Ok(()) + } + + // Set the radio in configuration mode + pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?; + if mode == StandbyMode::RC { + self.brd_set_operating_mode(RadioMode::StandbyRC); + } else { + self.brd_set_operating_mode(RadioMode::StandbyXOSC); + } + + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_low(); + Ok(()) + } + + // Set the radio in FS mode + pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError> { + // antenna settings ??? + self.brd_write_command(OpCode::SetFS, &[]).await?; + self.brd_set_operating_mode(RadioMode::FrequencySynthesis); + Ok(()) + } + + // Set the radio in transmission mode with timeout specified + pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError> { + let buffer = [ + Self::timeout_1(timeout), + Self::timeout_2(timeout), + Self::timeout_3(timeout), + ]; + + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_high(); + + self.brd_set_operating_mode(RadioMode::Transmit); + self.brd_write_command(OpCode::SetTx, &buffer).await?; + Ok(()) + } + + // Set the radio in reception mode with timeout specified + pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError> { + let buffer = [ + Self::timeout_1(timeout), + Self::timeout_2(timeout), + Self::timeout_3(timeout), + ]; + + let _fix_rx = self.antenna_rx.set_high(); + let _fix_tx = self.antenna_tx.set_low(); + + self.brd_set_operating_mode(RadioMode::Receive); + self.brd_write_registers(Register::RxGain, &[0x94u8]).await?; + self.brd_write_command(OpCode::SetRx, &buffer).await?; + Ok(()) + } + + // Set the radio in reception mode with Boosted LNA gain and timeout specified + pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError> { + let buffer = [ + Self::timeout_1(timeout), + Self::timeout_2(timeout), + Self::timeout_3(timeout), + ]; + + let _fix_rx = self.antenna_rx.set_high(); + let _fix_tx = self.antenna_tx.set_low(); + + self.brd_set_operating_mode(RadioMode::Receive); + // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity + self.brd_write_registers(Register::RxGain, &[0x96u8]).await?; + self.brd_write_command(OpCode::SetRx, &buffer).await?; + Ok(()) + } + + // Set the Rx duty cycle management parameters + pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError> { + let buffer = [ + ((rx_time >> 16) & 0xFF) as u8, + ((rx_time >> 8) & 0xFF) as u8, + (rx_time & 0xFF) as u8, + ((sleep_time >> 16) & 0xFF) as u8, + ((sleep_time >> 8) & 0xFF) as u8, + (sleep_time & 0xFF) as u8, + ]; + + // antenna settings ??? + + self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?; + self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle); + Ok(()) + } + + // Set the radio in CAD mode + pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError> { + let _fix_rx = self.antenna_rx.set_high(); + let _fix_tx = self.antenna_tx.set_low(); + + self.brd_write_command(OpCode::SetCAD, &[]).await?; + self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); + Ok(()) + } + + // Set the radio in continuous wave transmission mode + pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError> { + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_high(); + + self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?; + self.brd_set_operating_mode(RadioMode::Transmit); + Ok(()) + } + + // Set the radio in continuous preamble transmission mode + pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError> { + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_high(); + + self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?; + self.brd_set_operating_mode(RadioMode::Transmit); + Ok(()) + } + + // Decide which interrupt will stop the internal radio rx timer. + // false timer stop after header/syncword detection + // true timer stop after preamble detection + pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect( + &mut self, + enable: bool, + ) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8]) + .await?; + Ok(()) + } + + // Set the number of symbols the radio will wait to validate a reception + pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError> { + let mut exp = 0u8; + let mut reg; + let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1; + while mant > 31 { + mant = (mant + 3) >> 2; + exp += 1; + } + reg = mant << ((2 * exp) + 1); + + self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?; + + if symb_num != 0 { + reg = exp + (mant << 3); + self.brd_write_registers(Register::SynchTimeout, &[reg]).await?; + } + + Ok(()) + } + + // Set the power regulators operating mode (LDO or DC_DC). Using only LDO implies that the Rx or Tx current is doubled + pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()]) + .await?; + Ok(()) + } + + // Calibrate the given radio block + pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError> { + self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()]) + .await?; + Ok(()) + } + + // Calibrate the image rejection based on the given frequency + pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError> { + let mut cal_freq = [0x00u8, 0x00u8]; + + if freq > 900000000 { + cal_freq[0] = 0xE1; + cal_freq[1] = 0xE9; + } else if freq > 850000000 { + cal_freq[0] = 0xD7; + cal_freq[1] = 0xDB; + } else if freq > 770000000 { + cal_freq[0] = 0xC1; + cal_freq[1] = 0xC5; + } else if freq > 460000000 { + cal_freq[0] = 0x75; + cal_freq[1] = 0x81; + } else if freq > 425000000 { + cal_freq[0] = 0x6B; + cal_freq[1] = 0x6F; + } + self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?; + Ok(()) + } + + // Activate the extention of the timeout when a long preamble is used + pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // Set the transmission parameters + // hp_max 0 for sx1261, 7 for sx1262 + // device_sel 1 for sx1261, 0 for sx1262 + // pa_lut 0 for 14dBm LUT, 1 for 22dBm LUT + pub(super) async fn sub_set_pa_config( + &mut self, + pa_duty_cycle: u8, + hp_max: u8, + device_sel: u8, + pa_lut: u8, + ) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut]) + .await?; + Ok(()) + } + + // Define into which mode the chip goes after a TX / RX done + pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode]) + .await?; + Ok(()) + } + + // Set the IRQ mask and DIO masks + pub(super) async fn sub_set_dio_irq_params( + &mut self, + irq_mask: u16, + dio1_mask: u16, + dio2_mask: u16, + dio3_mask: u16, + ) -> Result<(), RadioError> { + let mut buffer = [0x00u8; 8]; + + buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8; + buffer[1] = (irq_mask & 0x00FF) as u8; + buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8; + buffer[3] = (dio1_mask & 0x00FF) as u8; + buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8; + buffer[5] = (dio2_mask & 0x00FF) as u8; + buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8; + buffer[7] = (dio3_mask & 0x00FF) as u8; + self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?; + Ok(()) + } + + // Return the current IRQ status + pub(super) async fn sub_get_irq_status(&mut self) -> Result> { + let mut irq_status = [0x00u8, 0x00u8]; + self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?; + Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16)) + } + + // Indicate if DIO2 is used to control an RF Switch + pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?; + Ok(()) + } + + // Indicate if the radio main clock is supplied from a TCXO + // tcxo_voltage voltage used to control the TCXO on/off from DIO3 + // timeout duration given to the TCXO to go to 32MHz + pub(super) async fn sub_set_dio3_as_tcxo_ctrl( + &mut self, + tcxo_voltage: TcxoCtrlVoltage, + timeout: u32, + ) -> Result<(), RadioError> { + let buffer = [ + tcxo_voltage.value() & 0x07, + Self::timeout_1(timeout), + Self::timeout_2(timeout), + Self::timeout_3(timeout), + ]; + self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?; + + Ok(()) + } + + // Set the RF frequency (Hz) + pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError> { + let mut buffer = [0x00u8; 4]; + + if !self.image_calibrated { + self.sub_calibrate_image(frequency).await?; + self.image_calibrated = true; + } + + let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency); + + buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8; + buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8; + buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8; + buffer[3] = (freq_in_pll_steps & 0xFF) as u8; + self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?; + Ok(()) + } + + // Set the radio for the given protocol (LoRa or GFSK). This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters. + pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError> { + self.packet_type = packet_type; + self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()]) + .await?; + Ok(()) + } + + // Get the current radio protocol (LoRa or GFSK) + pub(super) fn sub_get_packet_type(&mut self) -> PacketType { + self.packet_type + } + + // Set the transmission parameters + // power RF output power [-18..13] dBm + // ramp_time transmission ramp up time + pub(super) async fn sub_set_tx_params( + &mut self, + mut power: i8, + ramp_time: RampTime, + ) -> Result<(), RadioError> { + if self.brd_get_radio_type() == RadioType::SX1261 { + if power == 15 { + self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?; + } else { + self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?; + } + + if power >= 14 { + power = 14; + } else if power < -17 { + power = -17; + } + } else { + // Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) + let mut tx_clamp_cfg = [0x00u8]; + self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?; + tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1); + self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?; + + self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?; + + if power > 22 { + power = 22; + } else if power < -9 { + power = -9; + } + } + + // power conversion of negative number from i8 to u8 ??? + self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()]) + .await?; + Ok(()) + } + + // Set the modulation parameters + pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError> { + if self.modulation_params.is_some() { + let mut buffer = [0x00u8; 4]; + + // Since this driver only supports LoRa, ensure the packet type is set accordingly + self.sub_set_packet_type(PacketType::LoRa).await?; + + let modulation_params = self.modulation_params.unwrap(); + buffer[0] = modulation_params.spreading_factor.value(); + buffer[1] = modulation_params.bandwidth.value(); + buffer[2] = modulation_params.coding_rate.value(); + buffer[3] = modulation_params.low_data_rate_optimize; + + self.brd_write_command(OpCode::SetModulationParams, &buffer).await?; + Ok(()) + } else { + Err(RadioError::ModulationParamsMissing) + } + } + + // Set the packet parameters + pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError> { + if self.packet_params.is_some() { + let mut buffer = [0x00u8; 6]; + + // Since this driver only supports LoRa, ensure the packet type is set accordingly + self.sub_set_packet_type(PacketType::LoRa).await?; + + let packet_params = self.packet_params.unwrap(); + buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8; + buffer[1] = (packet_params.preamble_length & 0xFF) as u8; + buffer[2] = packet_params.implicit_header as u8; + buffer[3] = packet_params.payload_length; + buffer[4] = packet_params.crc_on as u8; + buffer[5] = packet_params.iq_inverted as u8; + + self.brd_write_command(OpCode::SetPacketParams, &buffer).await?; + Ok(()) + } else { + Err(RadioError::PacketParamsMissing) + } + } + + // Set the channel activity detection (CAD) parameters + // symbols number of symbols to use for CAD operations + // det_peak limit for detection of SNR peak used in the CAD + // det_min minimum symbol recognition for CAD + // exit_mode operation to be done at the end of CAD action + // timeout timeout value to abort the CAD activity + + pub(super) async fn sub_set_cad_params( + &mut self, + symbols: CADSymbols, + det_peak: u8, + det_min: u8, + exit_mode: CADExitMode, + timeout: u32, + ) -> Result<(), RadioError> { + let mut buffer = [0x00u8; 7]; + + buffer[0] = symbols.value(); + buffer[1] = det_peak; + buffer[2] = det_min; + buffer[3] = exit_mode.value(); + buffer[4] = Self::timeout_1(timeout); + buffer[5] = Self::timeout_2(timeout); + buffer[6] = Self::timeout_3(timeout); + + self.brd_write_command(OpCode::SetCADParams, &buffer).await?; + self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); + Ok(()) + } + + // Set the data buffer base address for transmission and reception + pub(super) async fn sub_set_buffer_base_address( + &mut self, + tx_base_address: u8, + rx_base_address: u8, + ) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address]) + .await?; + Ok(()) + } + + // Get the current radio status + pub(super) async fn sub_get_status(&mut self) -> Result> { + let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?; + Ok(RadioStatus { + cmd_status: (status & (0x07 << 1)) >> 1, + chip_mode: (status & (0x07 << 4)) >> 4, + }) + } + + // Get the instantaneous RSSI value for the last packet received + pub(super) async fn sub_get_rssi_inst(&mut self) -> Result> { + let mut buffer = [0x00u8]; + self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?; + let rssi: i8 = (-(buffer[0] as i8)) >> 1; // check this ??? + Ok(rssi) + } + + // Get the last received packet buffer status + pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError> { + if self.packet_params.is_some() { + let mut status = [0x00u8; 2]; + let mut payload_length_buffer = [0x00u8]; + + self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?; + if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header { + self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer) + .await?; + } else { + payload_length_buffer[0] = status[0]; + } + + let payload_length = payload_length_buffer[0]; + let offset = status[1]; + + Ok((payload_length, offset)) + } else { + Err(RadioError::PacketParamsMissing) + } + } + + // Get the last received packet payload status + pub(super) async fn sub_get_packet_status(&mut self) -> Result> { + let mut status = [0x00u8; 3]; + self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?; + + // check this ??? + let rssi = (-(status[0] as i8)) >> 1; + let snr = ((status[1] as i8) + 2) >> 2; + let signal_rssi = (-(status[2] as i8)) >> 1; + let freq_error = self.frequency_error; + + Ok(PacketStatus { + rssi, + snr, + signal_rssi, + freq_error, + }) + } + + // Get the possible system errors + pub(super) async fn sub_get_device_errors(&mut self) -> Result> { + let mut errors = [0x00u8; 2]; + self.brd_read_command(OpCode::GetErrors, &mut errors).await?; + + Ok(RadioSystemError { + rc_64khz_calibration: (errors[1] & (1 << 0)) != 0, + rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0, + pll_calibration: (errors[1] & (1 << 2)) != 0, + adc_calibration: (errors[1] & (1 << 3)) != 0, + image_calibration: (errors[1] & (1 << 4)) != 0, + xosc_start: (errors[1] & (1 << 5)) != 0, + pll_lock: (errors[1] & (1 << 6)) != 0, + pa_ramp: (errors[0] & (1 << 0)) != 0, + }) + } + + // Clear all the errors in the device + pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError> { + self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?; + Ok(()) + } + + // Clear the IRQs + pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError> { + let mut buffer = [0x00u8, 0x00u8]; + buffer[0] = ((irq >> 8) & 0xFF) as u8; + buffer[1] = (irq & 0xFF) as u8; + self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?; + Ok(()) + } + + // Utility functions + + fn timeout_1(timeout: u32) -> u8 { + ((timeout >> 16) & 0xFF) as u8 + } + fn timeout_2(timeout: u32) -> u8 { + ((timeout >> 8) & 0xFF) as u8 + } + fn timeout_3(timeout: u32) -> u8 { + (timeout & 0xFF) as u8 + } + + // check this ??? + fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 { + let b0 = buffer[0] as u32; + let b1 = buffer[1] as u32; + let b2 = buffer[2] as u32; + let b3 = buffer[3] as u32; + (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 + } + + fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 { + // Get integer and fractional parts of the frequency computed with a PLL step scaled value + let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED; + let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED); + + (steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT) + + (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED) + } +} diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a5d340c69..54df16ba0 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -5,7 +5,8 @@ version = "0.1.0" [features] default = ["nightly"] -nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net"] +nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", + "embassy-lora", "lorawan-device", "lorawan"] [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } @@ -16,6 +17,10 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } embedded-io = "0.3.0" +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["rak4631", "time", "defmt"], optional = true } + +lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } +lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf/src/bin/lora_p2p_report.rs new file mode 100644 index 000000000..46cb848b1 --- /dev/null +++ b/examples/nrf/src/bin/lora_p2p_report.rs @@ -0,0 +1,84 @@ +//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +#![no_std] +#![no_main] +#![macro_use] +#![allow(dead_code)] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::sx126x::*; +use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; +use embassy_nrf::{interrupt, spim}; +use embassy_time::{Duration, Timer}; +use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut spi_config = spim::Config::default(); + spi_config.frequency = spim::Frequency::M1; // M16 ??? + + let mut radio = { + let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); + + let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard); + let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); + let busy = Input::new(p.P1_14.degrade(), Pull::Down); + let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); + let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard); + + match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { + Ok(r) => r, + Err(err) => { + info!("Sx126xRadio error = {}", err); + return; + } + } + }; + + let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); + + start_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + start_indicator.set_low(); + + match radio.lora.sleep().await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + + let rf_config = RfConfig { + frequency: 903900000, // channel in Hz + bandwidth: Bandwidth::_250KHz, + spreading_factor: SpreadingFactor::_10, + coding_rate: CodingRate::_4_8, + }; + + let mut buffer = [00u8; 100]; + + // P2P receive + match radio.rx(rf_config, &mut buffer).await { + Ok((buffer_len, rx_quality)) => info!( + "RX received = {:?} with length = {} rssi = {} snr = {}", + &buffer[0..buffer_len], + buffer_len, + rx_quality.rssi(), + rx_quality.snr() + ), + Err(err) => info!("RX error = {}", err), + } + + match radio.lora.sleep().await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + + debug_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + debug_indicator.set_low(); +} diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs new file mode 100644 index 000000000..57aaea665 --- /dev/null +++ b/examples/nrf/src/bin/lora_p2p_sense.rs @@ -0,0 +1,173 @@ +//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] +#![feature(alloc_error_handler)] +#![allow(incomplete_features)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::sx126x::*; +use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin as _, Pull}; +use embassy_nrf::temp::Temp; +use embassy_nrf::{interrupt, spim}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::pubsub::{PubSubChannel, Publisher}; +use embassy_time::{Duration, Timer}; +use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; +use {defmt_rtt as _, panic_probe as _, panic_probe as _}; + +// Sensor packet constants +const TEMPERATURE_UID: u8 = 0x01; +const MOTION_UID: u8 = 0x02; + +// Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) +static MESSAGE_BUS: PubSubChannel = PubSubChannel::new(); + +#[derive(Clone, defmt::Format)] +enum Message { + Temperature(i32), + MotionDetected, +} + +#[embassy_executor::task] +async fn temperature_task( + mut temperature: Temp<'static>, + publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>, +) { + Timer::after(Duration::from_secs(45)).await; // stabilize for 45 seconds + + let mut temperature_reporting_threshhold = 10; + + loop { + let value = temperature.read().await; + let mut temperature_val = value.to_num::(); + + info!("Temperature: {}", temperature_val); // debug ??? + + // only report every 2 degree Celsius drops, from 9 through 5, but starting at 3 always report + + if temperature_val == 8 || temperature_val == 6 || temperature_val == 4 { + temperature_val += 1; + } + + if temperature_reporting_threshhold > temperature_val + && (temperature_val == 9 || temperature_val == 7 || temperature_val == 5) + { + temperature_reporting_threshhold = temperature_val; + publisher.publish(Message::Temperature(temperature_val)).await; + } else if temperature_val <= 3 { + publisher.publish(Message::Temperature(temperature_val)).await; + } + + Timer::after(Duration::from_secs(20 * 60)).await; + } +} + +#[embassy_executor::task] +async fn motion_detection_task( + mut pir_pin: Input<'static, AnyPin>, + publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>, +) { + Timer::after(Duration::from_secs(30)).await; // stabilize for 30 seconds + + loop { + // wait for motion detection + pir_pin.wait_for_low().await; + publisher.publish(Message::MotionDetected).await; + + // wait a minute before setting up for more motion detection + Timer::after(Duration::from_secs(60)).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + // set up to funnel temperature and motion detection events to the Lora Tx task + let mut lora_tx_subscriber = unwrap!(MESSAGE_BUS.subscriber()); + let temperature_publisher = unwrap!(MESSAGE_BUS.publisher()); + let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher()); + + let mut spi_config = spim::Config::default(); + spi_config.frequency = spim::Frequency::M1; // M16 ??? + + let mut radio = { + let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); + + let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard); + let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); + let busy = Input::new(p.P1_14.degrade(), Pull::Down); + let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); + let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard); + + match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { + Ok(r) => r, + Err(err) => { + info!("Sx126xRadio error = {}", err); + return; + } + } + }; + + // set up for the temperature task + let temperature_irq = interrupt::take!(TEMP); + let temperature = Temp::new(p.TEMP, temperature_irq); + + // set the motion detection pin + let pir_pin = Input::new(p.P0_10.degrade(), Pull::Up); + + let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); + let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + + start_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + start_indicator.set_low(); + + match radio.lora.sleep().await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + + unwrap!(spawner.spawn(temperature_task(temperature, temperature_publisher))); + unwrap!(spawner.spawn(motion_detection_task(pir_pin, motion_detection_publisher))); + + loop { + let message = lora_tx_subscriber.next_message_pure().await; + + let tx_config = TxConfig { + // 11 byte maximum payload for Bandwidth 125 and SF 10 + pw: 20, // up to 20 // 5 ??? + rf: RfConfig { + frequency: 903900000, // channel in Hz, not MHz + bandwidth: Bandwidth::_250KHz, + spreading_factor: SpreadingFactor::_10, + coding_rate: CodingRate::_4_8, + }, + }; + + let mut buffer = [TEMPERATURE_UID, 0xffu8, MOTION_UID, 0x00u8]; + match message { + Message::Temperature(temperature) => buffer[1] = temperature as u8, + Message::MotionDetected => buffer[3] = 0x01u8, + }; + + // crypto for text ??? + match radio.tx(tx_config, &buffer).await { + Ok(ret_val) => info!("TX ret_val = {}", ret_val), + Err(err) => info!("TX error = {}", err), + } + + match radio.lora.sleep().await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + + debug_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + debug_indicator.set_low(); + } +} From 823bd714fb6da94cf3b31c2066c398207228b4c6 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 28 Sep 2022 05:19:43 +0200 Subject: [PATCH 02/46] Add E-H1 uart blocking & nb implementation --- embassy-stm32/src/usart/mod.rs | 73 ++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 6c2668748..5ee099348 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -359,6 +359,79 @@ mod eh1 { impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::ErrorType for UartRx<'d, T, RxDma> { type Error = Error; } + + + impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::nb::Read for UartRx<'d, T, RxDma> { + fn read(&mut self) -> nb::Result { + let r = T::regs(); + unsafe { + let sr = sr(r).read(); + if sr.pe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Parity)) + } else if sr.fe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Framing)) + } else if sr.ne() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Noise)) + } else if sr.ore() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Overrun)) + } else if sr.rxne() { + Ok(rdr(r).read_volatile()) + } else { + Err(nb::Error::WouldBlock) + } + } + } + } + + impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, TxDma> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::nb::Write for UartTx<'d, T, TxDma> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::nb::Read for Uart<'d, T, TxDma, RxDma> { + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::blocking::Write for Uart<'d, T, TxDma, RxDma> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::nb::Write for Uart<'d, T, TxDma, RxDma> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } } #[cfg(all( From 526e90d3f3b986fedc55ec421178b7b2ec954abf Mon Sep 17 00:00:00 2001 From: ceekdee Date: Wed, 28 Sep 2022 14:27:34 -0500 Subject: [PATCH 03/46] Update some outstanding questions --- embassy-lora/src/sx126x/sx126x_lora/mod.rs | 18 +++++++++--------- examples/nrf/src/bin/lora_p2p_sense.rs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs index 02f5f83a2..53fbde749 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/mod.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/mod.rs @@ -511,22 +511,23 @@ where cad_activity_detected: Option<&mut bool>, ) -> Result<(), RadioError> { loop { - info!("process_irq loop entered"); // debug ??? + trace!("process_irq loop entered"); let de = self.sub_get_device_errors().await?; - info!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", + trace!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp); let st = self.sub_get_status().await?; - info!( + trace!( "radio status: cmd_status: {:x}, chip_mode: {:x}", - st.cmd_status, st.chip_mode + st.cmd_status, + st.chip_mode ); self.dio1.wait_for_high().await.map_err(|_| DIO1)?; let operating_mode = self.brd_get_operating_mode(); let irq_flags = self.sub_get_irq_status().await?; self.sub_clear_irq_status(irq_flags).await?; - info!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); // debug ??? + trace!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); // check for errors and unexpected interrupt masks (based on operation mode) if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() { @@ -568,13 +569,12 @@ where return Err(RadioError::CADUnexpected); } - // debug ??? if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() { - info!("HeaderValid"); + trace!("HeaderValid"); } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() { - info!("PreambleDetected"); + trace!("PreambleDetected"); } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() { - info!("SyncwordValid"); + trace!("SyncwordValid"); } // handle completions diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs index 57aaea665..3c6bb8767 100644 --- a/examples/nrf/src/bin/lora_p2p_sense.rs +++ b/examples/nrf/src/bin/lora_p2p_sense.rs @@ -44,7 +44,7 @@ async fn temperature_task( let value = temperature.read().await; let mut temperature_val = value.to_num::(); - info!("Temperature: {}", temperature_val); // debug ??? + info!("Temperature: {}", temperature_val); // only report every 2 degree Celsius drops, from 9 through 5, but starting at 3 always report From dc90006982899a5b1be43123905dd65f7b161789 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 29 Sep 2022 07:58:11 +0200 Subject: [PATCH 04/46] Remove code duplication on nb_read --- embassy-stm32/src/usart/mod.rs | 77 ++++++++++++++-------------------- 1 file changed, 32 insertions(+), 45 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 5ee099348..4bf157292 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -160,6 +160,30 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { Ok(()) } + pub fn nb_read(&mut self) -> Result> { + let r = T::regs(); + unsafe { + let sr = sr(r).read(); + if sr.pe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Parity)) + } else if sr.fe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Framing)) + } else if sr.ne() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Noise)) + } else if sr.ore() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Overrun)) + } else if sr.rxne() { + Ok(rdr(r).read_volatile()) + } else { + Err(nb::Error::WouldBlock) + } + } + } + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { unsafe { let r = T::regs(); @@ -263,6 +287,10 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { self.rx.read(buffer).await } + pub fn nb_read(&mut self) -> Result> { + self.rx.nb_read() + } + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.rx.blocking_read(buffer) } @@ -281,27 +309,7 @@ mod eh02 { impl<'d, T: BasicInstance, RxDma> embedded_hal_02::serial::Read for UartRx<'d, T, RxDma> { type Error = Error; fn read(&mut self) -> Result> { - let r = T::regs(); - unsafe { - let sr = sr(r).read(); - if sr.pe() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Parity)) - } else if sr.fe() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Framing)) - } else if sr.ne() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Noise)) - } else if sr.ore() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Overrun)) - } else if sr.rxne() { - Ok(rdr(r).read_volatile()) - } else { - Err(nb::Error::WouldBlock) - } - } + self.nb_read() } } @@ -318,7 +326,7 @@ mod eh02 { impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_02::serial::Read for Uart<'d, T, TxDma, RxDma> { type Error = Error; fn read(&mut self) -> Result> { - embedded_hal_02::serial::Read::read(&mut self.rx) + self.nb_read() } } @@ -360,30 +368,9 @@ mod eh1 { type Error = Error; } - impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::nb::Read for UartRx<'d, T, RxDma> { fn read(&mut self) -> nb::Result { - let r = T::regs(); - unsafe { - let sr = sr(r).read(); - if sr.pe() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Parity)) - } else if sr.fe() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Framing)) - } else if sr.ne() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Noise)) - } else if sr.ore() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Overrun)) - } else if sr.rxne() { - Ok(rdr(r).read_volatile()) - } else { - Err(nb::Error::WouldBlock) - } - } + self.nb_read() } } @@ -409,7 +396,7 @@ mod eh1 { impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::nb::Read for Uart<'d, T, TxDma, RxDma> { fn read(&mut self) -> Result> { - embedded_hal_02::serial::Read::read(&mut self.rx) + self.nb_read() } } From 72c2e985bb481fbc2e138a8e98b9dbb27878f370 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 29 Sep 2022 11:02:43 +0200 Subject: [PATCH 05/46] Update embedded-hal versions and explicitly pin --- embassy-embedded-hal/Cargo.toml | 4 ++-- .../src/shared_bus/asynch/spi.rs | 6 ++--- .../src/shared_bus/blocking/i2c.rs | 3 +-- .../src/shared_bus/blocking/spi.rs | 8 +++---- embassy-lora/Cargo.toml | 4 ++-- embassy-nrf/Cargo.toml | 4 ++-- embassy-nrf/src/gpio.rs | 12 +++++----- embassy-nrf/src/gpiote.rs | 2 +- embassy-nrf/src/spim.rs | 8 +++---- embassy-nrf/src/twim.rs | 6 ++--- embassy-nrf/src/uarte.rs | 4 ++-- embassy-rp/Cargo.toml | 7 +++--- embassy-rp/src/gpio.rs | 22 +++++++++---------- embassy-rp/src/i2c.rs | 18 ++++++--------- embassy-rp/src/spi.rs | 8 +++---- embassy-rp/src/uart/mod.rs | 12 +++++----- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/exti.rs | 2 +- embassy-stm32/src/gpio.rs | 3 +-- embassy-stm32/src/i2c/v1.rs | 6 ++--- embassy-stm32/src/i2c/v2.rs | 6 ++--- embassy-stm32/src/spi/mod.rs | 8 +++---- embassy-time/Cargo.toml | 6 ++--- embassy-time/src/delay.rs | 2 +- examples/rp/src/bin/spi_display.rs | 12 +++++----- examples/stm32h7/Cargo.toml | 4 ++-- examples/stm32l4/Cargo.toml | 4 ++-- tests/rp/Cargo.toml | 4 ++-- tests/stm32/Cargo.toml | 4 ++-- 29 files changed, 94 insertions(+), 99 deletions(-) diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 462680720..fe8fac7c8 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -18,8 +18,8 @@ nightly = ["embedded-hal-async", "embedded-storage-async"] [dependencies] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } nb = "1.0.0" diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index c95b59ef0..a3814d6d0 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -29,7 +29,7 @@ use core::future::Future; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::mutex::Mutex; -use embedded_hal_1::digital::blocking::OutputPin; +use embedded_hal_1::digital::OutputPin; use embedded_hal_1::spi::ErrorType; use embedded_hal_async::spi; @@ -57,7 +57,7 @@ where type Error = SpiDeviceError; } -impl spi::SpiDevice for SpiDevice<'_, M, BUS, CS> +unsafe impl spi::SpiDevice for SpiDevice<'_, M, BUS, CS> where M: RawMutex + 'static, BUS: spi::SpiBusFlush + 'static, @@ -122,7 +122,7 @@ where type Error = SpiDeviceError; } -impl spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +unsafe impl spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> where M: RawMutex + 'static, BUS: spi::SpiBusFlush + SetConfig + 'static, diff --git a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs index a611e2d27..892000b26 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs @@ -20,8 +20,7 @@ use core::cell::RefCell; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; -use embedded_hal_1::i2c::blocking::{I2c, Operation}; -use embedded_hal_1::i2c::ErrorType; +use embedded_hal_1::i2c::{ErrorType, I2c, Operation}; use crate::shared_bus::I2cDeviceError; use crate::SetConfig; diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs index 23845d887..4a08dc36e 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs @@ -22,9 +22,9 @@ use core::cell::RefCell; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; -use embedded_hal_1::digital::blocking::OutputPin; +use embedded_hal_1::digital::OutputPin; use embedded_hal_1::spi; -use embedded_hal_1::spi::blocking::SpiBusFlush; +use embedded_hal_1::spi::SpiBusFlush; use crate::shared_bus::SpiDeviceError; use crate::SetConfig; @@ -50,7 +50,7 @@ where type Error = SpiDeviceError; } -impl embedded_hal_1::spi::blocking::SpiDevice for SpiDevice<'_, M, BUS, CS> +impl embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> where M: RawMutex, BUS: SpiBusFlush, @@ -146,7 +146,7 @@ where type Error = SpiDeviceError; } -impl embedded_hal_1::spi::blocking::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +impl embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> where M: RawMutex, BUS: SpiBusFlush + SetConfig, diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 1757efa8b..dcb0d8245 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -28,8 +28,8 @@ log = { version = "0.4.14", optional = true } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } -embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.1.0-alpha.2" } embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } embedded-hal = { version = "0.2", features = ["unproven"] } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index d80281fa3..58b820242 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -73,8 +73,8 @@ embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} embedded-io = { version = "0.3.0", features = ["async"], optional = true } defmt = { version = "0.3", optional = true } diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 924629908..bb64e41e9 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -574,7 +574,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Input<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> { fn is_high(&self) -> Result { Ok(self.is_high()) } @@ -588,7 +588,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Output<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> { fn set_high(&mut self) -> Result<(), Self::Error> { Ok(self.set_high()) } @@ -598,7 +598,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Output<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } @@ -615,7 +615,7 @@ mod eh1 { /// Implement [`InputPin`] for [`Flex`]; /// /// If the pin is not in input mode the result is unspecified. - impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> { fn is_high(&self) -> Result { Ok(self.is_high()) } @@ -625,7 +625,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> { fn set_high(&mut self) -> Result<(), Self::Error> { Ok(self.set_high()) } @@ -635,7 +635,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index b418be9d5..25ad90496 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -457,7 +457,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::blocking::InputPin for InputChannel<'d, C, T> { + impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> { fn is_high(&self) -> Result { Ok(self.pin.is_high()) } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 51cd73a47..d821d2353 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -446,25 +446,25 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusFlush for Spim<'d, T> { + impl<'d, T: Instance> embedded_hal_1::spi::SpiBusFlush for Spim<'d, T> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusRead for Spim<'d, T> { + impl<'d, T: Instance> embedded_hal_1::spi::SpiBusRead for Spim<'d, T> { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.blocking_transfer(words, &[]) } } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusWrite for Spim<'d, T> { + impl<'d, T: Instance> embedded_hal_1::spi::SpiBusWrite for Spim<'d, T> { fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { self.blocking_write(words) } } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBus for Spim<'d, T> { + impl<'d, T: Instance> embedded_hal_1::spi::SpiBus for Spim<'d, T> { fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 7c6ca1d30..8d6171fac 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -793,7 +793,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::i2c::blocking::I2c for Twim<'d, T> { + impl<'d, T: Instance> embedded_hal_1::i2c::I2c for Twim<'d, T> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } @@ -823,14 +823,14 @@ mod eh1 { fn transaction<'a>( &mut self, _address: u8, - _operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], + _operations: &mut [embedded_hal_1::i2c::Operation<'a>], ) -> Result<(), Self::Error> { todo!(); } fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> where - O: IntoIterator>, + O: IntoIterator>, { todo!(); } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 5f9c4f17d..d99599112 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -1040,7 +1040,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::serial::blocking::Write for Uarte<'d, T> { + impl<'d, T: Instance> embedded_hal_1::serial::Write for Uarte<'d, T> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } @@ -1054,7 +1054,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::serial::blocking::Write for UarteTx<'d, T> { + impl<'d, T: Instance> embedded_hal_1::serial::Write for UarteTx<'d, T> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 337a84f4a..3aca5dbb4 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -31,7 +31,7 @@ nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", " # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. -unstable-traits = ["embedded-hal-1"] +unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] [dependencies] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } @@ -58,5 +58,6 @@ rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c90 #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 9b9a08110..a28bae96b 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -867,7 +867,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Input<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> { fn is_high(&self) -> Result { Ok(self.is_high()) } @@ -881,7 +881,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Output<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> { fn set_high(&mut self) -> Result<(), Self::Error> { Ok(self.set_high()) } @@ -891,7 +891,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Output<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } @@ -901,7 +901,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Output<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for Output<'d, T> { fn toggle(&mut self) -> Result<(), Self::Error> { Ok(self.toggle()) } @@ -911,7 +911,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for OutputOpenDrain<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for OutputOpenDrain<'d, T> { fn set_high(&mut self) -> Result<(), Self::Error> { Ok(self.set_high()) } @@ -921,7 +921,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for OutputOpenDrain<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d, T> { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } @@ -931,7 +931,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for OutputOpenDrain<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for OutputOpenDrain<'d, T> { fn toggle(&mut self) -> Result<(), Self::Error> { Ok(self.toggle()) } @@ -941,7 +941,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> { fn is_high(&self) -> Result { Ok(self.is_high()) } @@ -951,7 +951,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> { fn set_high(&mut self) -> Result<(), Self::Error> { Ok(self.set_high()) } @@ -961,7 +961,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } @@ -971,7 +971,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for Flex<'d, T> { fn toggle(&mut self) -> Result<(), Self::Error> { Ok(self.toggle()) } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 9596d661d..52f910cef 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -379,7 +379,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } @@ -421,16 +421,14 @@ mod eh1 { fn transaction<'a>( &mut self, address: u8, - operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], + operations: &mut [embedded_hal_1::i2c::Operation<'a>], ) -> Result<(), Self::Error> { Self::setup(address.into())?; for i in 0..operations.len() { let last = i == operations.len() - 1; match &mut operations[i] { - embedded_hal_1::i2c::blocking::Operation::Read(buf) => { - self.read_blocking_internal(buf, false, last)? - } - embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?, + embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, } } Ok(()) @@ -438,17 +436,15 @@ mod eh1 { fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> where - O: IntoIterator>, + O: IntoIterator>, { Self::setup(address.into())?; let mut peekable = operations.into_iter().peekable(); while let Some(operation) = peekable.next() { let last = peekable.peek().is_none(); match operation { - embedded_hal_1::i2c::blocking::Operation::Read(buf) => { - self.read_blocking_internal(buf, false, last)? - } - embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?, + embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, } } Ok(()) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 03293e064..754e2dd30 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -523,25 +523,25 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, M> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusRead for Spi<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusRead for Spi<'d, T, M> { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.blocking_transfer(words, &[]) } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusWrite for Spi<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusWrite for Spi<'d, T, M> { fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { self.blocking_write(words) } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBus for Spi<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus for Spi<'d, T, M> { fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 567c79db3..56c25e189 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -486,7 +486,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for UartRx<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> { fn read(&mut self) -> nb::Result { let r = T::regs(); unsafe { @@ -509,7 +509,7 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for UartTx<'d, T, M> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } @@ -519,7 +519,7 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for UartTx<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } @@ -529,13 +529,13 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for Uart<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, T, M> { fn read(&mut self) -> Result> { embedded_hal_02::serial::Read::read(&mut self.rx) } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for Uart<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for Uart<'d, T, M> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } @@ -545,7 +545,7 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for Uart<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index a4a232f51..9566dbcaf 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -42,8 +42,8 @@ embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 07c96ead0..dca991859 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -155,7 +155,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: GpioPin> embedded_hal_1::digital::blocking::InputPin for ExtiInput<'d, T> { + impl<'d, T: GpioPin> embedded_hal_1::digital::InputPin for ExtiInput<'d, T> { fn is_high(&self) -> Result { Ok(self.is_high()) } diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index d794e3989..5e3346754 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -848,8 +848,7 @@ mod eh02 { #[cfg(feature = "unstable-traits")] mod eh1 { - use embedded_hal_1::digital::blocking::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; - use embedded_hal_1::digital::ErrorType; + use embedded_hal_1::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; use super::*; diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 9dc75789a..f39a37df6 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -334,7 +334,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T> { + impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } @@ -364,14 +364,14 @@ mod eh1 { fn transaction<'a>( &mut self, _address: u8, - _operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], + _operations: &mut [embedded_hal_1::i2c::Operation<'a>], ) -> Result<(), Self::Error> { todo!(); } fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> where - O: IntoIterator>, + O: IntoIterator>, { todo!(); } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index b7c89931c..89b52da98 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -883,7 +883,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T, NoDma, NoDma> { + impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } @@ -913,14 +913,14 @@ mod eh1 { fn transaction<'a>( &mut self, _address: u8, - _operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], + _operations: &mut [embedded_hal_1::i2c::Operation<'a>], ) -> Result<(), Self::Error> { todo!(); } fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> where - O: IntoIterator>, + O: IntoIterator>, { todo!(); } diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 556d12305..396427782 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -843,25 +843,25 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<'d, T, Tx, Rx> { + impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } } - impl<'d, T: Instance, W: Word> embedded_hal_1::spi::blocking::SpiBusRead for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBusRead for Spi<'d, T, NoDma, NoDma> { fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> { self.blocking_read(words) } } - impl<'d, T: Instance, W: Word> embedded_hal_1::spi::blocking::SpiBusWrite for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBusWrite for Spi<'d, T, NoDma, NoDma> { fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { self.blocking_write(words) } } - impl<'d, T: Instance, W: Word> embedded_hal_1::spi::blocking::SpiBus for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBus for Spi<'d, T, NoDma, NoDma> { fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 6d71e100f..c3b361b8a 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -105,8 +105,8 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} futures-util = { version = "0.3.17", default-features = false } embassy-macros = { version = "0.1.0", path = "../embassy-macros"} @@ -117,4 +117,4 @@ cfg-if = "1.0.0" # WASM dependencies wasm-bindgen = { version = "0.2.81", optional = true } js-sys = { version = "0.3", optional = true } -wasm-timer = { version = "0.2.5", optional = true } \ No newline at end of file +wasm-timer = { version = "0.2.5", optional = true } diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs index 0a7982963..ff6b6869a 100644 --- a/embassy-time/src/delay.rs +++ b/embassy-time/src/delay.rs @@ -18,7 +18,7 @@ pub struct Delay; mod eh1 { use super::*; - impl embedded_hal_1::delay::blocking::DelayUs for Delay { + impl embedded_hal_1::delay::DelayUs for Delay { type Error = core::convert::Infallible; fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index 23cd4355e..778cad3fa 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -108,9 +108,9 @@ mod shared_spi { use core::cell::RefCell; use core::fmt::Debug; - use embedded_hal_1::digital::blocking::OutputPin; + use embedded_hal_1::digital::OutputPin; use embedded_hal_1::spi; - use embedded_hal_1::spi::blocking::SpiDevice; + use embedded_hal_1::spi::SpiDevice; #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum SpiDeviceWithCsError { @@ -153,7 +153,7 @@ mod shared_spi { impl<'a, BUS, CS> SpiDevice for SpiDeviceWithCs<'a, BUS, CS> where - BUS: spi::blocking::SpiBusFlush, + BUS: spi::SpiBusFlush, CS: OutputPin, { type Bus = BUS; @@ -182,7 +182,7 @@ mod shared_spi { /// Driver for the XPT2046 resistive touchscreen sensor mod touch { - use embedded_hal_1::spi::blocking::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; + use embedded_hal_1::spi::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; struct Calibration { x1: i32, @@ -246,8 +246,8 @@ mod touch { mod my_display_interface { use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; - use embedded_hal_1::digital::blocking::OutputPin; - use embedded_hal_1::spi::blocking::{SpiBusWrite, SpiDevice}; + use embedded_hal_1::digital::OutputPin; + use embedded_hal_1::spi::{SpiBusWrite, SpiDevice}; /// SPI display interface. /// diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index fc60d7a88..e725e03cb 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -17,8 +17,8 @@ defmt-rtt = "0.3" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } -embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.1.0-alpha.2" } embedded-nal-async = "0.2.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 35a9c20f0..2e2d07dc3 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -18,8 +18,8 @@ defmt-rtt = "0.3" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } -embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.1.0-alpha.2" } 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/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 503373759..2745aef06 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -16,8 +16,8 @@ defmt-rtt = "0.3.0" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } -embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.1.0-alpha.2" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } embedded-io = { version = "0.3.0", features = ["async"] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index d9cd3f120..daae3d464 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -24,8 +24,8 @@ defmt-rtt = "0.3.0" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } -embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.1.0-alpha.2" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } [profile.dev] From f4ebc36b638a081b4a8b68ae72c4cca5199c4c4c Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 29 Sep 2022 14:24:42 +0200 Subject: [PATCH 06/46] Futures in pub & sub are now awaited instead of returned for better user compiler diagnostics. Added functions for reading how many messages are available --- embassy-sync/src/pubsub/mod.rs | 73 +++++++++++++++++++++++++++ embassy-sync/src/pubsub/publisher.rs | 13 ++++- embassy-sync/src/pubsub/subscriber.rs | 11 ++-- 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index 62a9e4763..335d7e33e 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -192,6 +192,10 @@ impl u64 { + self.inner.lock(|s| s.borrow().next_message_id - next_message_id) + } + fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T> { self.inner.lock(|s| { let mut s = s.borrow_mut(); @@ -217,6 +221,13 @@ impl usize { + self.inner.lock(|s| { + let s = s.borrow(); + s.queue.capacity() - s.queue.len() + }) + } + fn unregister_subscriber(&self, subscriber_next_message_id: u64) { self.inner.lock(|s| { let mut s = s.borrow_mut(); @@ -388,6 +399,10 @@ pub trait PubSubBehavior { /// If the message is not yet present and a context is given, then its waker is registered in the subsriber wakers. fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll>; + /// Get the amount of messages that are between the given the next_message_id and the most recent message. + /// This is not necessarily the amount of messages a subscriber can still received as it may have lagged. + fn available(&self, next_message_id: u64) -> u64; + /// Try to publish a message to the queue. /// /// If the queue is full and a context is given, then its waker is registered in the publisher wakers. @@ -396,6 +411,9 @@ pub trait PubSubBehavior { /// Publish a message immediately fn publish_immediate(&self, message: T); + /// The amount of messages that can still be published without having to wait or without having to lag the subscribers + fn space(&self) -> usize; + /// Let the channel know that a subscriber has dropped fn unregister_subscriber(&self, subscriber_next_message_id: u64); @@ -539,4 +557,59 @@ mod tests { drop(sub0); } + + #[futures_test::test] + async fn correct_available() { + let channel = PubSubChannel::::new(); + + let mut sub0 = channel.subscriber().unwrap(); + let mut sub1 = channel.subscriber().unwrap(); + let pub0 = channel.publisher().unwrap(); + + assert_eq!(sub0.available(), 0); + assert_eq!(sub1.available(), 0); + + pub0.publish(42).await; + + assert_eq!(sub0.available(), 1); + assert_eq!(sub1.available(), 1); + + sub1.next_message().await; + + assert_eq!(sub1.available(), 0); + + pub0.publish(42).await; + + assert_eq!(sub0.available(), 2); + assert_eq!(sub1.available(), 1); + } + + #[futures_test::test] + async fn correct_space() { + let channel = PubSubChannel::::new(); + + let mut sub0 = channel.subscriber().unwrap(); + let mut sub1 = channel.subscriber().unwrap(); + let pub0 = channel.publisher().unwrap(); + + assert_eq!(pub0.space(), 4); + + pub0.publish(42).await; + + assert_eq!(pub0.space(), 3); + + pub0.publish(42).await; + + assert_eq!(pub0.space(), 2); + + sub0.next_message().await; + sub0.next_message().await; + + assert_eq!(pub0.space(), 2); + + sub1.next_message().await; + assert_eq!(pub0.space(), 3); + sub1.next_message().await; + assert_eq!(pub0.space(), 4); + } } diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs index 705797f60..484f1dbfd 100644 --- a/embassy-sync/src/pubsub/publisher.rs +++ b/embassy-sync/src/pubsub/publisher.rs @@ -31,17 +31,26 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Pub<'a, PSB, T> { } /// Publish a message. But if the message queue is full, wait for all subscribers to have read the last message - pub fn publish<'s>(&'s self, message: T) -> PublisherWaitFuture<'s, 'a, PSB, T> { + pub async fn publish<'s>(&'s self, message: T) { PublisherWaitFuture { message: Some(message), publisher: self, } + .await } /// Publish a message if there is space in the message queue pub fn try_publish(&self, message: T) -> Result<(), T> { self.channel.publish_with_context(message, None) } + + /// The amount of messages that can still be published without having to wait or without having to lag the subscribers + /// + /// *Note: In the time between checking this and a publish action, other publishers may have had time to publish something. + /// So checking doesn't give any guarantees.* + pub fn space(&self) -> usize { + self.channel.space() + } } impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Drop for Pub<'a, PSB, T> { @@ -158,7 +167,7 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: } /// Future for the publisher wait action -pub struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { +struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { /// The message we need to publish message: Option, publisher: &'s Pub<'a, PSB, T>, diff --git a/embassy-sync/src/pubsub/subscriber.rs b/embassy-sync/src/pubsub/subscriber.rs index b9a2cbe18..8a8e9144b 100644 --- a/embassy-sync/src/pubsub/subscriber.rs +++ b/embassy-sync/src/pubsub/subscriber.rs @@ -28,8 +28,8 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Sub<'a, PSB, T> { } /// Wait for a published message - pub fn next_message<'s>(&'s mut self) -> SubscriberWaitFuture<'s, 'a, PSB, T> { - SubscriberWaitFuture { subscriber: self } + pub async fn next_message(&mut self) -> WaitResult { + SubscriberWaitFuture { subscriber: self }.await } /// Wait for a published message (ignoring lag results) @@ -64,6 +64,11 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Sub<'a, PSB, T> { } } } + + /// The amount of messages this subscriber hasn't received yet + pub fn available(&self) -> u64 { + self.channel.available(self.next_message_id) + } } impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Drop for Sub<'a, PSB, T> { @@ -135,7 +140,7 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: } /// Future for the subscriber wait action -pub struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { +struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { subscriber: &'s mut Sub<'a, PSB, T>, } From 874384826d4a6f9c9a9c8d3abf41f99a662f58fb Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 29 Sep 2022 15:15:10 +0200 Subject: [PATCH 07/46] Went back to named futures but now with must_use --- embassy-sync/src/pubsub/mod.rs | 2 +- embassy-sync/src/pubsub/publisher.rs | 6 +++--- embassy-sync/src/pubsub/subscriber.rs | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index 335d7e33e..faaf99dc6 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -562,7 +562,7 @@ mod tests { async fn correct_available() { let channel = PubSubChannel::::new(); - let mut sub0 = channel.subscriber().unwrap(); + let sub0 = channel.subscriber().unwrap(); let mut sub1 = channel.subscriber().unwrap(); let pub0 = channel.publisher().unwrap(); diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs index 484f1dbfd..faa67d947 100644 --- a/embassy-sync/src/pubsub/publisher.rs +++ b/embassy-sync/src/pubsub/publisher.rs @@ -31,12 +31,11 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Pub<'a, PSB, T> { } /// Publish a message. But if the message queue is full, wait for all subscribers to have read the last message - pub async fn publish<'s>(&'s self, message: T) { + pub fn publish<'s>(&'s self, message: T) -> PublisherWaitFuture<'s, 'a, PSB, T> { PublisherWaitFuture { message: Some(message), publisher: self, } - .await } /// Publish a message if there is space in the message queue @@ -167,7 +166,8 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: } /// Future for the publisher wait action -struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { /// The message we need to publish message: Option, publisher: &'s Pub<'a, PSB, T>, diff --git a/embassy-sync/src/pubsub/subscriber.rs b/embassy-sync/src/pubsub/subscriber.rs index 8a8e9144b..f420a75f0 100644 --- a/embassy-sync/src/pubsub/subscriber.rs +++ b/embassy-sync/src/pubsub/subscriber.rs @@ -28,8 +28,8 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Sub<'a, PSB, T> { } /// Wait for a published message - pub async fn next_message(&mut self) -> WaitResult { - SubscriberWaitFuture { subscriber: self }.await + pub fn next_message<'s>(&'s mut self) -> SubscriberWaitFuture<'s, 'a, PSB, T> { + SubscriberWaitFuture { subscriber: self } } /// Wait for a published message (ignoring lag results) @@ -140,7 +140,8 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: } /// Future for the subscriber wait action -struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { subscriber: &'s mut Sub<'a, PSB, T>, } From dab17627093faa709f309c81f067ed2b578f2a8e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 29 Sep 2022 15:52:23 +0200 Subject: [PATCH 08/46] usb: remove all "Direction as u8" casts. --- embassy-usb-driver/src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index fc29786fc..931e9c318 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -54,12 +54,16 @@ impl From for u8 { } impl EndpointAddress { - const INBITS: u8 = Direction::In as u8; + const INBITS: u8 = 0x80; /// Constructs a new EndpointAddress with the given index and direction. #[inline] pub fn from_parts(index: usize, dir: Direction) -> Self { - EndpointAddress(index as u8 | dir as u8) + let dir_u8 = match dir { + Direction::Out => 0x00, + Direction::In => Self::INBITS, + }; + EndpointAddress(index as u8 | dir_u8) } /// Gets the direction part of the address. From a7fdeac560b5e277afa80cd60f788a48df6069c9 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 30 Sep 2022 06:00:46 +0200 Subject: [PATCH 09/46] Remove flash lock/unlock public API from stm32 flash, and perform the unlocking and locking automatically on erase and write operations --- embassy-stm32/src/flash/mod.rs | 27 +++++++++---------- .../boot/application/stm32f3/src/bin/a.rs | 2 +- .../boot/application/stm32f7/src/bin/a.rs | 2 +- .../boot/application/stm32h7/src/bin/a.rs | 2 +- .../boot/application/stm32l0/src/bin/a.rs | 2 +- .../boot/application/stm32l1/src/bin/a.rs | 2 +- .../boot/application/stm32l4/src/bin/a.rs | 2 +- .../boot/application/stm32wl/src/bin/a.rs | 2 +- examples/boot/bootloader/stm32/src/main.rs | 2 +- examples/stm32f3/src/bin/flash.rs | 2 +- examples/stm32f4/src/bin/flash.rs | 2 +- examples/stm32f7/src/bin/flash.rs | 2 +- examples/stm32h7/src/bin/flash.rs | 2 +- examples/stm32l0/src/bin/flash.rs | 2 +- examples/stm32l1/src/bin/flash.rs | 2 +- examples/stm32wl/src/bin/flash.rs | 2 +- 16 files changed, 28 insertions(+), 29 deletions(-) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 5258c9b04..988cf9fae 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -23,17 +23,6 @@ impl<'d> Flash<'d> { Self { _inner: p } } - pub fn unlock(p: impl Peripheral

+ 'd) -> Self { - let flash = Self::new(p); - - unsafe { family::unlock() }; - flash - } - - pub fn lock(&mut self) { - unsafe { family::lock() }; - } - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { let offset = FLASH_BASE as u32 + offset; if offset as usize >= FLASH_END || offset as usize + bytes.len() > FLASH_END { @@ -57,7 +46,12 @@ impl<'d> Flash<'d> { self.clear_all_err(); - unsafe { family::blocking_write(offset, buf) } + unsafe { + family::unlock(); + let res = family::blocking_write(offset, buf); + family::lock(); + res + } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { @@ -72,7 +66,12 @@ impl<'d> Flash<'d> { self.clear_all_err(); - unsafe { family::blocking_erase(from, to) } + unsafe { + family::unlock(); + let res = family::blocking_erase(from, to); + family::lock(); + res + } } fn clear_all_err(&mut self) { @@ -82,7 +81,7 @@ impl<'d> Flash<'d> { impl Drop for Flash<'_> { fn drop(&mut self) { - self.lock(); + unsafe { family::lock() }; } } diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index fdbd5ab99..d92d59b29 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); + let flash = Flash::new(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PC13, Pull::Up); diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 77b897b0f..79ab80e09 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::unlock(p.FLASH); + let mut flash = Flash::new(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 0fe598a5d..8b452be34 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::unlock(p.FLASH); + let mut flash = Flash::new(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index f0b0b80e3..59ca34386 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); + let flash = Flash::new(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PB2, Pull::Up); diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index f0b0b80e3..59ca34386 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); + let flash = Flash::new(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PB2, Pull::Up); diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 5119bad2e..6cddc6cc8 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); + let flash = Flash::new(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PC13, Pull::Up); diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index faa650778..1ff47eddd 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); + let flash = Flash::new(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PA0, Pull::Up); diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 294464d1c..4b17cd799 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -20,7 +20,7 @@ fn main() -> ! { */ let mut bl: BootLoader = BootLoader::default(); - let flash = Flash::unlock(p.FLASH); + let flash = Flash::new(p.FLASH); let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index 2cf24dbd3..baa7484d0 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index 393d61e86..7ea068a42 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -13,7 +13,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); // Sector 5 test_flash(&mut f, 128 * 1024, 128 * 1024); diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index c10781d0c..4a7bca1fa 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs @@ -19,7 +19,7 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index 6682c64d5..ee86bdbf6 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -19,7 +19,7 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index 867cb4d3e..ffe4fb10b 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index a76b9879f..476ed51a4 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index eb7489760..2a8880624 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x36000; - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); info!("Reading..."); let mut buf = [0u8; 8]; From a283c47557ee6a0c0e54bcb7f27b6c85813ae0e3 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 30 Sep 2022 05:35:46 +0200 Subject: [PATCH 10/46] Implement embedded-hal-nb for uart --- embassy-stm32/Cargo.toml | 3 ++- embassy-stm32/src/usart/mod.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9566dbcaf..fbe37fe3d 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -44,6 +44,7 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } @@ -102,7 +103,7 @@ unstable-pac = [] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. -unstable-traits = ["embedded-hal-1"] +unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] # BEGIN GENERATED FEATURES # Generated by stm32-gen-features. DO NOT EDIT. diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 4bf157292..a152a0c15 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -368,13 +368,13 @@ mod eh1 { type Error = Error; } - impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::nb::Read for UartRx<'d, T, RxDma> { + impl<'d, T: BasicInstance, RxDma> embedded_hal_nb::serial::Read for UartRx<'d, T, RxDma> { fn read(&mut self) -> nb::Result { self.nb_read() } } - impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, TxDma> { + impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::Write for UartTx<'d, T, TxDma> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } @@ -384,7 +384,7 @@ mod eh1 { } } - impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::nb::Write for UartTx<'d, T, TxDma> { + impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, T, TxDma> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } @@ -394,13 +394,13 @@ mod eh1 { } } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::nb::Read for Uart<'d, T, TxDma, RxDma> { + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Read for Uart<'d, T, TxDma, RxDma> { fn read(&mut self) -> Result> { self.nb_read() } } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::blocking::Write for Uart<'d, T, TxDma, RxDma> { + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::Write for Uart<'d, T, TxDma, RxDma> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } @@ -410,7 +410,7 @@ mod eh1 { } } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::nb::Write for Uart<'d, T, TxDma, RxDma> { + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Write for Uart<'d, T, TxDma, RxDma> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } From 9f77dbf5ae442c1cac0c652b4ef25bf1c82ed9d4 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Thu, 29 Sep 2022 02:01:58 -0700 Subject: [PATCH 11/46] rp i2c: blocking example i2c example talking to mcp23017 i2c gpio expander. --- examples/rp/src/bin/i2c.rs | 70 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 examples/rp/src/bin/i2c.rs diff --git a/examples/rp/src/bin/i2c.rs b/examples/rp/src/bin/i2c.rs new file mode 100644 index 000000000..a5991d0da --- /dev/null +++ b/examples/rp/src/bin/i2c.rs @@ -0,0 +1,70 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::i2c::{self, Config}; +use embassy_time::{Duration, Timer}; +use embedded_hal_1::i2c::blocking::I2c; +use {defmt_rtt as _, panic_probe as _}; + +#[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(Duration::from_secs(1)).await; + } +} From d5abd32da21998178216693a4d549a0f2683a4bb Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sat, 1 Oct 2022 01:09:40 -0700 Subject: [PATCH 12/46] rename to i2c_blocking --- examples/rp/src/bin/{i2c.rs => i2c_blocking.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/rp/src/bin/{i2c.rs => i2c_blocking.rs} (100%) diff --git a/examples/rp/src/bin/i2c.rs b/examples/rp/src/bin/i2c_blocking.rs similarity index 100% rename from examples/rp/src/bin/i2c.rs rename to examples/rp/src/bin/i2c_blocking.rs From c96581879cf054d9e4fec9272e5cecb165844cbb Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sat, 1 Oct 2022 01:33:36 -0700 Subject: [PATCH 13/46] update embedded-hal api Also pin to alpha.9 since its a breaking change --- examples/rp/Cargo.toml | 2 +- examples/rp/src/bin/i2c_blocking.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 3c8f923e7..1bb6432e6 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -26,7 +26,7 @@ st7789 = "0.6.1" display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } embedded-hal-async = { version = "0.1.0-alpha.1" } embedded-io = { version = "0.3.0", features = ["async", "defmt"] } static_cell = "1.0.0" diff --git a/examples/rp/src/bin/i2c_blocking.rs b/examples/rp/src/bin/i2c_blocking.rs index a5991d0da..7623e33c8 100644 --- a/examples/rp/src/bin/i2c_blocking.rs +++ b/examples/rp/src/bin/i2c_blocking.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::i2c::{self, Config}; use embassy_time::{Duration, Timer}; -use embedded_hal_1::i2c::blocking::I2c; +use embedded_hal_1::i2c::I2c; use {defmt_rtt as _, panic_probe as _}; #[allow(dead_code)] From 90d392205fe7c07cbc59b09679cde6cfc1e244b6 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 28 Sep 2022 10:00:30 -0700 Subject: [PATCH 14/46] embassy-rp: inline I2c::regs It just returns a literal constant, so there's no reason not to always inline it. --- embassy-rp/src/i2c.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 52f910cef..ab56c9359 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -499,6 +499,7 @@ macro_rules! impl_instance { type Interrupt = crate::interrupt::$irq; + #[inline] fn regs() -> pac::i2c::I2c { pac::$type } From 8d38eacae426ef1b1e1f7255eed50a9588793fb4 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Tue, 27 Sep 2022 23:24:22 -0700 Subject: [PATCH 15/46] rp i2c: remove vestiges of DMA --- embassy-rp/src/i2c.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index ab56c9359..a6f278827 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -3,7 +3,6 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; use pac::i2c; -use crate::dma::AnyChannel; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; @@ -52,9 +51,6 @@ impl Default for Config { const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { - _tx_dma: Option>, - _rx_dma: Option>, - _dma_buf: [u16; 256], phantom: PhantomData<(&'d mut T, M)>, } @@ -66,7 +62,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { config: Config, ) -> Self { into_ref!(scl, sda); - Self::new_inner(_peri, scl.map_into(), sda.map_into(), None, None, config) + Self::new_inner(_peri, scl.map_into(), sda.map_into(), config) } } @@ -75,8 +71,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { _peri: impl Peripheral

+ 'd, scl: PeripheralRef<'d, AnyPin>, sda: PeripheralRef<'d, AnyPin>, - _tx_dma: Option>, - _rx_dma: Option>, config: Config, ) -> Self { into_ref!(_peri); @@ -173,9 +167,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } Self { - _tx_dma, - _rx_dma, - _dma_buf: [0; 256], phantom: PhantomData, } } From 72b645b0c96c7b8312d0a64f851e807faacd78af Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Tue, 27 Sep 2022 23:44:14 -0700 Subject: [PATCH 16/46] rp i2c: make blocking only for Mode=Blocking --- embassy-rp/src/i2c.rs | 212 +++++++++++++++++++++--------------------- 1 file changed, 107 insertions(+), 105 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index a6f278827..c609b02ea 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -56,14 +56,115 @@ pub struct I2c<'d, T: Instance, M: Mode> { impl<'d, T: Instance> I2c<'d, T, Blocking> { pub fn new_blocking( - _peri: impl Peripheral

+ 'd, + peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, config: Config, ) -> Self { into_ref!(scl, sda); - Self::new_inner(_peri, scl.map_into(), sda.map_into(), config) + Self::new_inner(peri, scl.map_into(), sda.map_into(), config) } + + fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if buffer.is_empty() { + return Err(Error::InvalidReadBufferLength); + } + + let p = T::regs(); + let lastindex = buffer.len() - 1; + for (i, byte) in buffer.iter_mut().enumerate() { + let first = i == 0; + let last = i == lastindex; + + // NOTE(unsafe) We have &mut self + unsafe { + // wait until there is space in the FIFO to write the next byte + while p.ic_txflr().read().txflr() == FIFO_SIZE {} + + p.ic_data_cmd().write(|w| { + w.set_restart(restart && first); + w.set_stop(send_stop && last); + + w.set_cmd(true); + }); + + while p.ic_rxflr().read().rxflr() == 0 { + self.read_and_clear_abort_reason()?; + } + + *byte = p.ic_data_cmd().read().dat(); + } + } + + Ok(()) + } + + fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + if bytes.is_empty() { + return Err(Error::InvalidWriteBufferLength); + } + + let p = T::regs(); + + for (i, byte) in bytes.iter().enumerate() { + let last = i == bytes.len() - 1; + + // NOTE(unsafe) We have &mut self + unsafe { + p.ic_data_cmd().write(|w| { + w.set_stop(send_stop && last); + w.set_dat(*byte); + }); + + // Wait until the transmission of the address/data from the + // internal shift register has completed. For this to function + // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The + // TX_EMPTY_CTRL flag was set in i2c_init. + while !p.ic_raw_intr_stat().read().tx_empty() {} + + let abort_reason = self.read_and_clear_abort_reason(); + + if abort_reason.is_err() || (send_stop && last) { + // If the transaction was aborted or if it completed + // successfully wait until the STOP condition has occured. + + while !p.ic_raw_intr_stat().read().stop_det() {} + + p.ic_clr_stop_det().read().clr_stop_det(); + } + + // Note the hardware issues a STOP automatically on an abort + // condition. Note also the hardware clears RX FIFO as well as + // TX on abort, ecause we set hwparam + // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. + abort_reason?; + } + } + Ok(()) + } + + // ========================= + // Blocking public API + // ========================= + + pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.read_blocking_internal(buffer, true, true) + // Automatic Stop + } + + pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, true) + } + + pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, false)?; + self.read_blocking_internal(buffer, true, true) + // Automatic Stop + } +} } impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { @@ -217,111 +318,12 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } } - fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { - if buffer.is_empty() { - return Err(Error::InvalidReadBufferLength); - } - - let p = T::regs(); - let lastindex = buffer.len() - 1; - for (i, byte) in buffer.iter_mut().enumerate() { - let first = i == 0; - let last = i == lastindex; - - // NOTE(unsafe) We have &mut self - unsafe { - // wait until there is space in the FIFO to write the next byte - while p.ic_txflr().read().txflr() == FIFO_SIZE {} - - p.ic_data_cmd().write(|w| { - w.set_restart(restart && first); - w.set_stop(send_stop && last); - - w.set_cmd(true); - }); - - while p.ic_rxflr().read().rxflr() == 0 { - self.read_and_clear_abort_reason()?; - } - - *byte = p.ic_data_cmd().read().dat(); - } - } - - Ok(()) - } - - fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { - if bytes.is_empty() { - return Err(Error::InvalidWriteBufferLength); - } - - let p = T::regs(); - - for (i, byte) in bytes.iter().enumerate() { - let last = i == bytes.len() - 1; - - // NOTE(unsafe) We have &mut self - unsafe { - p.ic_data_cmd().write(|w| { - w.set_stop(send_stop && last); - w.set_dat(*byte); - }); - - // Wait until the transmission of the address/data from the - // internal shift register has completed. For this to function - // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The - // TX_EMPTY_CTRL flag was set in i2c_init. - while !p.ic_raw_intr_stat().read().tx_empty() {} - - let abort_reason = self.read_and_clear_abort_reason(); - - if abort_reason.is_err() || (send_stop && last) { - // If the transaction was aborted or if it completed - // successfully wait until the STOP condition has occured. - - while !p.ic_raw_intr_stat().read().stop_det() {} - - p.ic_clr_stop_det().read().clr_stop_det(); - } - - // Note the hardware issues a STOP automatically on an abort - // condition. Note also the hardware clears RX FIFO as well as - // TX on abort, ecause we set hwparam - // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. - abort_reason?; - } - } - Ok(()) - } - - // ========================= - // Blocking public API - // ========================= - - pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - self.read_blocking_internal(buffer, true, true) - // Automatic Stop - } - - pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { - Self::setup(address.into())?; - self.write_blocking_internal(bytes, true) - } - - pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - self.write_blocking_internal(bytes, false)?; - self.read_blocking_internal(buffer, true, true) - // Automatic Stop - } } mod eh02 { use super::*; - impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { + impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, Blocking> { type Error = Error; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -329,7 +331,7 @@ mod eh02 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { + impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, Blocking> { type Error = Error; fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { @@ -337,7 +339,7 @@ mod eh02 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { + impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, Blocking> { type Error = Error; fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -370,7 +372,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { + impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, Blocking> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } From 5e2c52ee5b6fcc5b50589fd2590657e3f1083bff Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 28 Sep 2022 01:20:04 -0700 Subject: [PATCH 17/46] embassy-rp: async i2c implementation This is an interrupt-driven async i2c master implementation. It makes as best use of the RP2040's i2c block's fifos as possible to minimize interrupts. It implements embedded_hal_async::i2c for easy interop. WIP async impl --- embassy-rp/src/i2c.rs | 376 +++++++++++++++++++++++++++++++++++++++-- examples/rp/Cargo.toml | 3 + 2 files changed, 369 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index c609b02ea..6fc64d849 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -1,6 +1,10 @@ +use core::future; use core::marker::PhantomData; +use core::task::Poll; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; use pac::i2c; use crate::gpio::sealed::Pin; @@ -79,7 +83,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { // NOTE(unsafe) We have &mut self unsafe { // wait until there is space in the FIFO to write the next byte - while p.ic_txflr().read().txflr() == FIFO_SIZE {} + while Self::tx_fifo_full() {} p.ic_data_cmd().write(|w| { w.set_restart(restart && first); @@ -88,7 +92,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { w.set_cmd(true); }); - while p.ic_rxflr().read().rxflr() == 0 { + while Self::rx_fifo_len() == 0 { self.read_and_clear_abort_reason()?; } @@ -165,9 +169,250 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { // Automatic Stop } } + +static I2C_WAKER: AtomicWaker = AtomicWaker::new(); + +impl<'d, T: Instance> I2c<'d, T, Async> { + pub fn new_async( + peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(scl, sda, irq); + + let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config); + + irq.set_handler(Self::on_interrupt); + unsafe { + let i2c = T::regs(); + + // mask everything initially + i2c.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); + } + irq.unpend(); + debug_assert!(!irq.is_pending()); + irq.enable(); + + i2c + } + + /// Calls `f` to check if we are ready or not. + /// If not, `g` is called once the waker is set (to eg enable the required interrupts). + async fn wait_on(&mut self, mut f: F, mut g: G) -> U + where + F: FnMut(&mut Self) -> Poll, + G: FnMut(&mut Self), + { + future::poll_fn(|cx| { + let r = f(self); + + if r.is_pending() { + I2C_WAKER.register(cx.waker()); + g(self); + } + r + }) + .await + } + + // Mask interrupts and wake any task waiting for this interrupt + unsafe fn on_interrupt(_: *mut ()) { + let i2c = T::regs(); + i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); + + I2C_WAKER.wake(); + } + + async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if buffer.is_empty() { + return Err(Error::InvalidReadBufferLength); + } + + let p = T::regs(); + + let mut remaining = buffer.len(); + let mut remaining_queue = buffer.len(); + + let mut abort_reason = None; + + while remaining > 0 { + // Waggle SCK - basically the same as write + let tx_fifo_space = Self::tx_fifo_capacity(); + let mut batch = 0; + + debug_assert!(remaining_queue > 0); + + for _ in 0..remaining_queue.min(tx_fifo_space as usize) { + remaining_queue -= 1; + let last = remaining_queue == 0; + batch += 1; + + unsafe { + p.ic_data_cmd().write(|w| { + w.set_restart(restart && remaining_queue == buffer.len() - 1); + w.set_stop(last && send_stop); + w.set_cmd(true); + }); + } + } + + // We've either run out of txfifo or just plain finished setting up + // the clocks for the message - either way we need to wait for rx + // data. + + debug_assert!(batch > 0); + let res = self + .wait_on( + |me| { + let rxfifo = Self::rx_fifo_len(); + if let Err(abort_reason) = me.read_and_clear_abort_reason() { + Poll::Ready(Err(abort_reason)) + } else if rxfifo >= batch { + Poll::Ready(Ok(rxfifo)) + } else { + Poll::Pending + } + }, + |_me| unsafe { + // Set the read threshold to the number of bytes we're + // expecting so we don't get spurious interrupts. + p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1)); + + p.ic_intr_mask().modify(|w| { + w.set_m_rx_full(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + + match res { + Err(reason) => { + abort_reason = Some(reason); + // XXX keep going anyway? + break; + } + Ok(rxfifo) => { + // Fetch things from rx fifo. We're assuming we're the only + // rxfifo reader, so nothing else can take things from it. + let rxbytes = (rxfifo as usize).min(remaining); + let received = buffer.len() - remaining; + for b in &mut buffer[received..received + rxbytes] { + *b = unsafe { p.ic_data_cmd().read().dat() }; + } + remaining -= rxbytes; + } + }; + } + + // wait for stop condition to be emitted. + self.wait_on( + |_me| unsafe { + if !p.ic_raw_intr_stat().read().stop_det() && send_stop { + Poll::Pending + } else { + Poll::Ready(()) + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_stop_det(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + unsafe { p.ic_clr_stop_det().read() }; + + if let Some(abort_reason) = abort_reason { + return Err(abort_reason); + } + Ok(()) + } + + async fn write_async_internal( + &mut self, + bytes: impl IntoIterator, + send_stop: bool, + ) -> Result<(), Error> { + let p = T::regs(); + + let mut bytes = bytes.into_iter().peekable(); + + 'xmit: loop { + let tx_fifo_space = Self::tx_fifo_capacity(); + + for _ in 0..tx_fifo_space { + if let Some(byte) = bytes.next() { + let last = bytes.peek().is_none(); + + unsafe { + p.ic_data_cmd().write(|w| { + w.set_stop(last && send_stop); + w.set_cmd(false); + w.set_dat(byte); + }); + } + } else { + break 'xmit; + } + } + + self.wait_on( + |me| { + if let Err(abort_reason) = me.read_and_clear_abort_reason() { + Poll::Ready(Err(abort_reason)) + } else if !Self::tx_fifo_full() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_tx_empty(true); + w.set_m_tx_abrt(true); + }) + }, + ) + .await?; + } + + // wait for fifo to drain + self.wait_on( + |_me| unsafe { + if p.ic_raw_intr_stat().read().tx_empty() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_tx_empty(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + + Ok(()) + } + + pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(addr)?; + self.read_async_internal(buffer, false, true).await + } + + pub async fn write_async(&mut self, addr: u16, buffer: &[u8]) -> Result<(), Error> { + Self::setup(addr)?; + self.write_async_internal(buffer.iter().copied(), true).await + } } -impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { +impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { fn new_inner( _peri: impl Peripheral

+ 'd, scl: PeripheralRef<'d, AnyPin>, @@ -182,6 +427,10 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { let p = T::regs(); unsafe { + let reset = T::reset(); + crate::reset::reset(reset); + crate::reset::unreset_wait(reset); + p.ic_enable().write(|w| w.set_enable(false)); // Select controller mode & speed @@ -267,9 +516,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { p.ic_enable().write(|w| w.set_enable(true)); } - Self { - phantom: PhantomData, - } + Self { phantom: PhantomData } } fn setup(addr: u16) -> Result<(), Error> { @@ -290,6 +537,23 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { Ok(()) } + #[inline] + fn tx_fifo_full() -> bool { + Self::tx_fifo_capacity() == 0 + } + + #[inline] + fn tx_fifo_capacity() -> u8 { + let p = T::regs(); + unsafe { FIFO_SIZE - p.ic_txflr().read().txflr() } + } + + #[inline] + fn rx_fifo_len() -> u8 { + let p = T::regs(); + unsafe { p.ic_rxflr().read().rxflr() } + } + fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { let p = T::regs(); unsafe { @@ -317,7 +581,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } } } - } mod eh02 { @@ -444,6 +707,91 @@ mod eh1 { } } } +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod nightly { + use core::future::Future; + + use embedded_hal_1::i2c::Operation; + use embedded_hal_async::i2c::AddressMode; + + use super::*; + + impl<'d, A, T> embedded_hal_async::i2c::I2c for I2c<'d, T, Async> + where + A: AddressMode + Into + 'static, + T: Instance + 'd, + { + type ReadFuture<'a> = impl Future> + 'a + where Self: 'a; + type WriteFuture<'a> = impl Future> + 'a + where Self: 'a; + type WriteReadFuture<'a> = impl Future> + 'a + where Self: 'a; + type TransactionFuture<'a, 'b> = impl Future> + 'a + where Self: 'a, 'b: 'a; + + fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + let addr: u16 = address.into(); + + async move { + Self::setup(addr)?; + self.read_async_internal(buffer, false, true).await + } + } + + fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a> { + let addr: u16 = address.into(); + + async move { + Self::setup(addr)?; + self.write_async_internal(write.iter().copied(), true).await + } + } + + fn write_read<'a>( + &'a mut self, + address: A, + bytes: &'a [u8], + buffer: &'a mut [u8], + ) -> Self::WriteReadFuture<'a> { + let addr: u16 = address.into(); + + async move { + Self::setup(addr)?; + self.write_async_internal(bytes.iter().cloned(), false).await?; + self.read_async_internal(buffer, false, true).await + } + } + + fn transaction<'a, 'b>( + &'a mut self, + address: A, + operations: &'a mut [Operation<'b>], + ) -> Self::TransactionFuture<'a, 'b> { + let addr: u16 = address.into(); + + async move { + let mut iterator = operations.iter_mut(); + + while let Some(op) = iterator.next() { + let last = iterator.len() == 0; + + match op { + Operation::Read(buffer) => { + Self::setup(addr)?; + self.read_async_internal(buffer, false, last).await?; + } + Operation::Write(buffer) => { + Self::setup(addr)?; + self.write_async_internal(buffer.into_iter().cloned(), last).await?; + } + } + } + Ok(()) + } + } + } +} fn i2c_reserved_addr(addr: u16) -> bool { (addr & 0x78) == 0 || (addr & 0x78) == 0x78 @@ -459,6 +807,7 @@ mod sealed { type Interrupt: Interrupt; fn regs() -> crate::pac::i2c::I2c; + fn reset() -> crate::pac::resets::regs::Peripherals; } pub trait Mode {} @@ -485,7 +834,7 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { + ($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl sealed::Instance for peripherals::$type { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; @@ -496,13 +845,20 @@ macro_rules! impl_instance { fn regs() -> pac::i2c::I2c { pac::$type } + + #[inline] + fn reset() -> pac::resets::regs::Peripherals { + let mut ret = pac::resets::regs::Peripherals::default(); + ret.$reset(true); + ret + } } impl Instance for peripherals::$type {} }; } -impl_instance!(I2C0, I2C0_IRQ, 32, 33); -impl_instance!(I2C1, I2C1_IRQ, 34, 35); +impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); +impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 3c8f923e7..e689df6bd 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -30,3 +30,6 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } embedded-io = { version = "0.3.0", features = ["async", "defmt"] } static_cell = "1.0.0" + +[profile.release] +debug = true From 1ee4bb22deb19e93a7c68e04875889e3e4e31c29 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 28 Sep 2022 09:35:19 -0700 Subject: [PATCH 18/46] embassy-rp i2c: async (non-blocking) example Simple example exercising an mcp23017 GPIO expander, configured on RP2040 GPIOs 14+15 (i2c1) with 8 inputs and 8 outputs. Input bit 0 controls whether to display a mcp23017 register dump. --- examples/rp/src/bin/i2c_async.rs | 102 +++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 examples/rp/src/bin/i2c_async.rs diff --git a/examples/rp/src/bin/i2c_async.rs b/examples/rp/src/bin/i2c_async.rs new file mode 100644 index 000000000..d1a2e3cd7 --- /dev/null +++ b/examples/rp/src/bin/i2c_async.rs @@ -0,0 +1,102 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::i2c::{self, Config}; +use embassy_rp::interrupt; +use embassy_time::{Duration, Timer}; +use embedded_hal_async::i2c::I2c; +use {defmt_rtt as _, panic_probe as _}; + +#[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; + let irq = interrupt::take!(I2C1_IRQ); + + info!("set up i2c "); + let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, irq, 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(Duration::from_millis(100)).await; + } +} From 09afece93d0dccb750a0dbc9c63282d3dca55e48 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sat, 1 Oct 2022 19:28:27 -0700 Subject: [PATCH 19/46] make I2c::write_async take an iterator There's no other iterator async API right now. --- embassy-rp/src/i2c.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 6fc64d849..f62cf0b86 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -406,9 +406,9 @@ impl<'d, T: Instance> I2c<'d, T, Async> { self.read_async_internal(buffer, false, true).await } - pub async fn write_async(&mut self, addr: u16, buffer: &[u8]) -> Result<(), Error> { + pub async fn write_async(&mut self, addr: u16, bytes : impl IntoIterator) -> Result<(), Error> { Self::setup(addr)?; - self.write_async_internal(buffer.iter().copied(), true).await + self.write_async_internal(bytes, true).await } } From 753781a2639c3505ab046cb48acb6473b84b214b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 2 Oct 2022 22:24:59 +0200 Subject: [PATCH 20/46] Build docs in CI --- .github/workflows/doc.yml | 85 +++++++++++++++++++++++++++++++++ .github/workflows/rust.yml | 2 +- embassy-embedded-hal/Cargo.toml | 2 +- embassy-executor/Cargo.toml | 2 +- embassy-usb/Cargo.toml | 2 +- 5 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/doc.yml diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml new file mode 100644 index 000000000..349ca4040 --- /dev/null +++ b/.github/workflows/doc.yml @@ -0,0 +1,85 @@ +name: Docs + +on: + push: + branches: [master] + +env: + BUILDER_THREADS: '2' + +jobs: + doc: + runs-on: ubuntu-latest + + # Since stm32 crates take SO LONG to build, we split them + # into a separate job. This way it doesn't slow down updating + # the rest. + strategy: + matrix: + crates: + - stm32 + - rest + + # This will ensure at most one doc build job is running at a time + # (for stm32 and non-stm32 independently). + # If another job is already running, the new job will wait. + # If another job is already waiting, it'll be canceled. + # This means some commits will be skipped, but that's fine because + # we only care that the latest gets built. + concurrency: doc-${{ matrix.crates }} + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Install Rust targets + run: | + rustup target add x86_64-unknown-linux-gnu + rustup target add wasm32-unknown-unknown + rustup target add thumbv6m-none-eabi + rustup target add thumbv7m-none-eabi + rustup target add thumbv7em-none-eabi + rustup target add thumbv7em-none-eabihf + rustup target add thumbv8m.base-none-eabi + rustup target add thumbv8m.main-none-eabi + rustup target add thumbv8m.main-none-eabihf + + - name: Install docserver + run: | + wget -q -O /usr/local/bin/builder "https://github.com/embassy-rs/docserver/releases/download/v0.3/builder" + chmod +x /usr/local/bin/builder + + - name: build-stm32 + if: ${{ matrix.crates=='stm32' }} + run: | + mkdir crates + builder ./embassy-stm32 crates/embassy-stm32/git.zup + builder ./stm32-metapac crates/stm32-metapac/git.zup + + - name: build-rest + if: ${{ matrix.crates=='rest' }} + run: | + mkdir crates + builder ./embassy-boot/boot crates/embassy-boot/git.zup + builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup + builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup + builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup + builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup + builder ./embassy-executor crates/embassy-executor/git.zup + builder ./embassy-futures crates/embassy-futures/git.zup + builder ./embassy-lora crates/embassy-lora/git.zup + builder ./embassy-net crates/embassy-net/git.zup + builder ./embassy-nrf crates/embassy-nrf/git.zup + builder ./embassy-rp crates/embassy-rp/git.zup + builder ./embassy-sync crates/embassy-sync/git.zup + builder ./embassy-usb crates/embassy-usb/git.zup + builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup + + - name: upload + run: | + mkdir -p ~/.kube + echo "${{secrets.KUBECONFIG}}" > ~/.kube/config + POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) + kubectl cp crates $POD:/data + + \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d2e8e316b..b93c8783d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,7 +11,7 @@ env: jobs: all: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest needs: [build-nightly, build-stable, test] steps: - name: Done diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index fe8fac7c8..845f742e4 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-embedded-hal-v$VERSION/embassy-embedded-hal/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-embedded-hal/src/" features = ["nightly", "std"] -target = "thumbv7em-none-eabi" +target = "x86_64-unknown-linux-gnu" [features] std = [] diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index fa3d0b2b6..3b1c4ab46 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" -features = ["nightly", "defmt", "unstable-traits"] +features = ["nightly", "defmt"] flavors = [ { name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] }, { name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] }, diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index aad54dbaf..1f705e9ff 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-v$VERSION/embassy-usb/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb/src/" -features = ["defmt"] +features = ["defmt", "usbd-hid"] target = "thumbv7em-none-eabi" [features] From e8bb8faa23c1e8b78285646ca7e711bafe990e20 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sun, 2 Oct 2022 15:08:58 -0700 Subject: [PATCH 21/46] rp i2c: allow blocking ops on async contexts --- embassy-rp/src/i2c.rs | 210 +++++++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index f62cf0b86..e01692dc1 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -68,106 +68,6 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { into_ref!(scl, sda); Self::new_inner(peri, scl.map_into(), sda.map_into(), config) } - - fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { - if buffer.is_empty() { - return Err(Error::InvalidReadBufferLength); - } - - let p = T::regs(); - let lastindex = buffer.len() - 1; - for (i, byte) in buffer.iter_mut().enumerate() { - let first = i == 0; - let last = i == lastindex; - - // NOTE(unsafe) We have &mut self - unsafe { - // wait until there is space in the FIFO to write the next byte - while Self::tx_fifo_full() {} - - p.ic_data_cmd().write(|w| { - w.set_restart(restart && first); - w.set_stop(send_stop && last); - - w.set_cmd(true); - }); - - while Self::rx_fifo_len() == 0 { - self.read_and_clear_abort_reason()?; - } - - *byte = p.ic_data_cmd().read().dat(); - } - } - - Ok(()) - } - - fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { - if bytes.is_empty() { - return Err(Error::InvalidWriteBufferLength); - } - - let p = T::regs(); - - for (i, byte) in bytes.iter().enumerate() { - let last = i == bytes.len() - 1; - - // NOTE(unsafe) We have &mut self - unsafe { - p.ic_data_cmd().write(|w| { - w.set_stop(send_stop && last); - w.set_dat(*byte); - }); - - // Wait until the transmission of the address/data from the - // internal shift register has completed. For this to function - // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The - // TX_EMPTY_CTRL flag was set in i2c_init. - while !p.ic_raw_intr_stat().read().tx_empty() {} - - let abort_reason = self.read_and_clear_abort_reason(); - - if abort_reason.is_err() || (send_stop && last) { - // If the transaction was aborted or if it completed - // successfully wait until the STOP condition has occured. - - while !p.ic_raw_intr_stat().read().stop_det() {} - - p.ic_clr_stop_det().read().clr_stop_det(); - } - - // Note the hardware issues a STOP automatically on an abort - // condition. Note also the hardware clears RX FIFO as well as - // TX on abort, ecause we set hwparam - // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. - abort_reason?; - } - } - Ok(()) - } - - // ========================= - // Blocking public API - // ========================= - - pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - self.read_blocking_internal(buffer, true, true) - // Automatic Stop - } - - pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { - Self::setup(address.into())?; - self.write_blocking_internal(bytes, true) - } - - pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - self.write_blocking_internal(bytes, false)?; - self.read_blocking_internal(buffer, true, true) - // Automatic Stop - } } static I2C_WAKER: AtomicWaker = AtomicWaker::new(); @@ -406,7 +306,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { self.read_async_internal(buffer, false, true).await } - pub async fn write_async(&mut self, addr: u16, bytes : impl IntoIterator) -> Result<(), Error> { + pub async fn write_async(&mut self, addr: u16, bytes: impl IntoIterator) -> Result<(), Error> { Self::setup(addr)?; self.write_async_internal(bytes, true).await } @@ -581,12 +481,112 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { } } } + + fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if buffer.is_empty() { + return Err(Error::InvalidReadBufferLength); + } + + let p = T::regs(); + let lastindex = buffer.len() - 1; + for (i, byte) in buffer.iter_mut().enumerate() { + let first = i == 0; + let last = i == lastindex; + + // NOTE(unsafe) We have &mut self + unsafe { + // wait until there is space in the FIFO to write the next byte + while Self::tx_fifo_full() {} + + p.ic_data_cmd().write(|w| { + w.set_restart(restart && first); + w.set_stop(send_stop && last); + + w.set_cmd(true); + }); + + while Self::rx_fifo_len() == 0 { + self.read_and_clear_abort_reason()?; + } + + *byte = p.ic_data_cmd().read().dat(); + } + } + + Ok(()) + } + + fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + if bytes.is_empty() { + return Err(Error::InvalidWriteBufferLength); + } + + let p = T::regs(); + + for (i, byte) in bytes.iter().enumerate() { + let last = i == bytes.len() - 1; + + // NOTE(unsafe) We have &mut self + unsafe { + p.ic_data_cmd().write(|w| { + w.set_stop(send_stop && last); + w.set_dat(*byte); + }); + + // Wait until the transmission of the address/data from the + // internal shift register has completed. For this to function + // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The + // TX_EMPTY_CTRL flag was set in i2c_init. + while !p.ic_raw_intr_stat().read().tx_empty() {} + + let abort_reason = self.read_and_clear_abort_reason(); + + if abort_reason.is_err() || (send_stop && last) { + // If the transaction was aborted or if it completed + // successfully wait until the STOP condition has occured. + + while !p.ic_raw_intr_stat().read().stop_det() {} + + p.ic_clr_stop_det().read().clr_stop_det(); + } + + // Note the hardware issues a STOP automatically on an abort + // condition. Note also the hardware clears RX FIFO as well as + // TX on abort, ecause we set hwparam + // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. + abort_reason?; + } + } + Ok(()) + } + + // ========================= + // Blocking public API + // ========================= + + pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.read_blocking_internal(buffer, true, true) + // Automatic Stop + } + + pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, true) + } + + pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, false)?; + self.read_blocking_internal(buffer, true, true) + // Automatic Stop + } } mod eh02 { use super::*; - impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, Blocking> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { type Error = Error; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -594,7 +594,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, Blocking> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { type Error = Error; fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { @@ -602,7 +602,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, Blocking> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { type Error = Error; fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -635,7 +635,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, Blocking> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } From f075e624440af121da7a27a145e2acee0730c542 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 3 Oct 2022 01:59:44 +0200 Subject: [PATCH 22/46] Use 1 thread in ci doc building. --- .github/workflows/doc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 349ca4040..49e1cf71c 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -5,7 +5,7 @@ on: branches: [master] env: - BUILDER_THREADS: '2' + BUILDER_THREADS: '1' jobs: doc: From cae84991790976387aa4d4b7afae90094a876b25 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 3 Oct 2022 01:00:03 -0700 Subject: [PATCH 23/46] rp i2c: clean up tx_abrt handling Make sure we always wait for the stop bit if there's a reason to - either because we sent one, or because there was a hardware tx abort. --- embassy-rp/src/i2c.rs | 136 ++++++++++++++++++++++-------------------- 1 file changed, 70 insertions(+), 66 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index e01692dc1..f004c522c 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -135,7 +135,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let mut remaining = buffer.len(); let mut remaining_queue = buffer.len(); - let mut abort_reason = None; + let mut abort_reason = Ok(()); while remaining > 0 { // Waggle SCK - basically the same as write @@ -190,8 +190,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { match res { Err(reason) => { - abort_reason = Some(reason); - // XXX keep going anyway? + abort_reason = Err(reason); break; } Ok(rxfifo) => { @@ -207,29 +206,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { }; } - // wait for stop condition to be emitted. - self.wait_on( - |_me| unsafe { - if !p.ic_raw_intr_stat().read().stop_det() && send_stop { - Poll::Pending - } else { - Poll::Ready(()) - } - }, - |_me| unsafe { - p.ic_intr_mask().modify(|w| { - w.set_m_stop_det(true); - w.set_m_tx_abrt(true); - }); - }, - ) - .await; - unsafe { p.ic_clr_stop_det().read() }; - - if let Some(abort_reason) = abort_reason { - return Err(abort_reason); - } - Ok(()) + self.wait_stop_det(abort_reason, send_stop).await } async fn write_async_internal( @@ -241,7 +218,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let mut bytes = bytes.into_iter().peekable(); - 'xmit: loop { + let res = 'xmit: loop { let tx_fifo_space = Self::tx_fifo_capacity(); for _ in 0..tx_fifo_space { @@ -256,49 +233,76 @@ impl<'d, T: Instance> I2c<'d, T, Async> { }); } } else { - break 'xmit; + break 'xmit Ok(()); } } - self.wait_on( - |me| { - if let Err(abort_reason) = me.read_and_clear_abort_reason() { - Poll::Ready(Err(abort_reason)) - } else if !Self::tx_fifo_full() { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - }, - |_me| unsafe { - p.ic_intr_mask().modify(|w| { - w.set_m_tx_empty(true); - w.set_m_tx_abrt(true); - }) - }, - ) - .await?; + let res = self + .wait_on( + |me| { + if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() { + Poll::Ready(abort_reason) + } else if !Self::tx_fifo_full() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_tx_empty(true); + w.set_m_tx_abrt(true); + }) + }, + ) + .await; + if res.is_err() { + break res; + } + }; + + self.wait_stop_det(res, send_stop).await + } + + /// Helper to wait for a stop bit, for both tx and rx. If we had an abort, + /// then we'll get a hardware-generated stop, otherwise wait for a stop if + /// we're expecting it. + /// + /// Also handles an abort which arises while processing the tx fifo. + async fn wait_stop_det(&mut self, had_abort: Result<(), Error>, do_stop: bool) -> Result<(), Error> { + if had_abort.is_err() || do_stop { + let p = T::regs(); + + let had_abort2 = self + .wait_on( + |me| unsafe { + // We could see an abort while processing fifo backlog, + // so handle it here. + let abort = me.read_and_clear_abort_reason(); + if had_abort.is_ok() && abort.is_err() { + Poll::Ready(abort) + } else if p.ic_raw_intr_stat().read().stop_det() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_stop_det(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + unsafe { + p.ic_clr_stop_det().read(); + } + + had_abort.and(had_abort2) + } else { + had_abort } - - // wait for fifo to drain - self.wait_on( - |_me| unsafe { - if p.ic_raw_intr_stat().read().tx_empty() { - Poll::Ready(()) - } else { - Poll::Pending - } - }, - |_me| unsafe { - p.ic_intr_mask().modify(|w| { - w.set_m_tx_empty(true); - w.set_m_tx_abrt(true); - }); - }, - ) - .await; - - Ok(()) } pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> { From 4fd831e4a88fdba14751c8a71ae45fc0eac92c66 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 3 Oct 2022 18:50:03 -0700 Subject: [PATCH 24/46] rp async i2c: raise the tx_empty threshold Assert "tx_empty" interrupt a little early so there's time to wake up and start refilling the fifo before it drains. This avoids stalling the i2c bus if the tx fifo completely drains. --- embassy-rp/src/i2c.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index f004c522c..68cfc653e 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -243,12 +243,18 @@ impl<'d, T: Instance> I2c<'d, T, Async> { if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() { Poll::Ready(abort_reason) } else if !Self::tx_fifo_full() { + // resume if there's any space free in the tx fifo Poll::Ready(Ok(())) } else { Poll::Pending } }, |_me| unsafe { + // Set tx "free" threshold a little high so that we get + // woken before the fifo completely drains to minimize + // transfer stalls. + p.ic_tx_tl().write(|w| w.set_tx_tl(1)); + p.ic_intr_mask().modify(|w| { w.set_m_tx_empty(true); w.set_m_tx_abrt(true); From 59765590e0339e8f4294719b6e99a6ab110266d8 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 4 Oct 2022 16:38:11 +0200 Subject: [PATCH 25/46] Add required info to embassy-sync package Updates the README.md based on embassy-futures structure. --- embassy-sync/Cargo.toml | 10 ++++++++++ embassy-sync/README.md | 24 ++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 14ab1d003..584d5ba9f 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -2,6 +2,16 @@ name = "embassy-sync" version = "0.1.0" edition = "2021" +description = "no-std, no-alloc synchronization primitives with async support" +repository = "https://github.com/embassy-rs/embassy" +readme = "README.md" +license = "MIT OR Apache-2.0" +categories = [ + "embedded", + "no-std", + "concurrency", + "asynchronous", +] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-sync-v$VERSION/embassy-sync/src/" diff --git a/embassy-sync/README.md b/embassy-sync/README.md index 106295c0d..cc65cf6ef 100644 --- a/embassy-sync/README.md +++ b/embassy-sync/README.md @@ -1,12 +1,32 @@ # embassy-sync -Synchronization primitives and data structures with an async API: +An [Embassy](https://embassy.dev) project. + +Synchronization primitives and data structures with async support: - [`Channel`](channel::Channel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer. - [`PubSubChannel`](pubsub::PubSubChannel) - A broadcast channel (publish-subscribe) channel. Each message is received by all consumers. - [`Signal`](signal::Signal) - Signalling latest value to a single consumer. -- [`Mutex`](mutex::Mutex) - A Mutex for synchronizing state between asynchronous tasks. +- [`Mutex`](mutex::Mutex) - Mutex for synchronizing state between asynchronous tasks. - [`Pipe`](pipe::Pipe) - Byte stream implementing `embedded_io` traits. - [`WakerRegistration`](waitqueue::WakerRegistration) - Utility to register and wake a `Waker`. - [`AtomicWaker`](waitqueue::AtomicWaker) - A variant of `WakerRegistration` accessible using a non-mut API. - [`MultiWakerRegistration`](waitqueue::MultiWakerRegistration) - Utility registering and waking multiple `Waker`'s. + +## Interoperability + +Futures from this crate can run on any executor. + +## Minimum supported Rust version (MSRV) + +Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. From 530182d6683531f7c259448e6c54c866f35837c7 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Wed, 5 Oct 2022 15:15:03 +0200 Subject: [PATCH 26/46] Forgot to add space function to immediate publisher --- embassy-sync/src/pubsub/publisher.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs index faa67d947..e1edc9eb9 100644 --- a/embassy-sync/src/pubsub/publisher.rs +++ b/embassy-sync/src/pubsub/publisher.rs @@ -123,6 +123,14 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> ImmediatePub<'a, PSB, T> { pub fn try_publish(&self, message: T) -> Result<(), T> { self.channel.publish_with_context(message, None) } + + /// The amount of messages that can still be published without having to wait or without having to lag the subscribers + /// + /// *Note: In the time between checking this and a publish action, other publishers may have had time to publish something. + /// So checking doesn't give any guarantees.* + pub fn space(&self) -> usize { + self.channel.space() + } } /// An immediate publisher that holds a dynamic reference to the channel From d49d1b6b1cf6de9577816397db3c41f6e93aa4e6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 5 Oct 2022 17:08:02 +0200 Subject: [PATCH 27/46] ci/doc: build embassy-time too. --- .github/workflows/doc.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 49e1cf71c..eb460e738 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -72,6 +72,7 @@ jobs: builder ./embassy-nrf crates/embassy-nrf/git.zup builder ./embassy-rp crates/embassy-rp/git.zup builder ./embassy-sync crates/embassy-sync/git.zup + builder ./embassy-time crates/embassy-time/git.zup builder ./embassy-usb crates/embassy-usb/git.zup builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup From 9dca368c3dd1a8f00295b21c87d4fbb94afb60a5 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Fri, 7 Oct 2022 13:29:56 +0300 Subject: [PATCH 28/46] Use RccPeripheral for adc_v2 --- embassy-stm32/src/adc/mod.rs | 4 ++-- embassy-stm32/src/adc/v2.rs | 26 +++++++++----------------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 8da13073e..fba016a77 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -30,9 +30,9 @@ pub(crate) mod sealed { } } -#[cfg(not(adc_f1))] +#[cfg(not(any(adc_f1, adc_v2)))] pub trait Instance: sealed::Instance + 'static {} -#[cfg(adc_f1)] +#[cfg(any(adc_f1, adc_v2))] pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} #[cfg(all(not(adc_f1), not(adc_v1)))] pub trait Common: sealed::Common + 'static {} diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 25b7ba967..70e3b73b3 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -12,21 +12,6 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; -#[cfg(not(any(rcc_f4, rcc_f7)))] -fn enable() { - todo!() -} - -#[cfg(any(rcc_f4, rcc_f7))] -fn enable() { - critical_section::with(|_| unsafe { - // TODO do not enable all adc clocks if not needed - crate::pac::RCC.apb2enr().modify(|w| w.set_adc1en(true)); - crate::pac::RCC.apb2enr().modify(|w| w.set_adc2en(true)); - crate::pac::RCC.apb2enr().modify(|w| w.set_adc3en(true)); - }); -} - pub enum Resolution { TwelveBit, TenBit, @@ -164,9 +149,10 @@ where { pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { into_ref!(_peri); - enable(); + T::enable(); + T::reset(); - let presc = unsafe { Prescaler::from_pclk2(crate::rcc::get_freqs().apb2) }; + let presc = unsafe { Prescaler::from_pclk2(T::frequency()) }; unsafe { T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); } @@ -288,3 +274,9 @@ where } } } + +impl<'d, T: Instance> Drop for Adc<'d, T> { + fn drop(&mut self) { + T::disable(); + } +} From 6718ca3a9412c9fbd5f1161b1123664024ffbe37 Mon Sep 17 00:00:00 2001 From: chrysn Date: Fri, 7 Oct 2022 12:41:56 +0200 Subject: [PATCH 29/46] all Cargo.toml: Add license to all crate Cargo.toml files Closes: https://github.com/embassy-rs/embassy/issues/1002 --- docs/modules/ROOT/examples/basic/Cargo.toml | 1 + .../ROOT/examples/layer-by-layer/blinky-async/Cargo.toml | 1 + .../modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml | 1 + .../modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml | 1 + .../modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml | 1 + embassy-boot/boot/Cargo.toml | 1 + embassy-boot/nrf/Cargo.toml | 1 + embassy-boot/stm32/Cargo.toml | 1 + embassy-cortex-m/Cargo.toml | 1 + embassy-embedded-hal/Cargo.toml | 1 + embassy-executor/Cargo.toml | 1 + embassy-hal-common/Cargo.toml | 1 + embassy-lora/Cargo.toml | 1 + embassy-macros/Cargo.toml | 1 + embassy-net/Cargo.toml | 1 + embassy-nrf/Cargo.toml | 1 + embassy-rp/Cargo.toml | 1 + embassy-stm32/Cargo.toml | 1 + embassy-time/Cargo.toml | 1 + embassy-usb-driver/Cargo.toml | 3 ++- embassy-usb/Cargo.toml | 1 + examples/boot/application/nrf/Cargo.toml | 1 + examples/boot/application/stm32f3/Cargo.toml | 1 + examples/boot/application/stm32f7/Cargo.toml | 1 + examples/boot/application/stm32h7/Cargo.toml | 1 + examples/boot/application/stm32l0/Cargo.toml | 1 + examples/boot/application/stm32l1/Cargo.toml | 1 + examples/boot/application/stm32l4/Cargo.toml | 1 + examples/boot/application/stm32wl/Cargo.toml | 1 + examples/boot/bootloader/nrf/Cargo.toml | 1 + examples/boot/bootloader/stm32/Cargo.toml | 1 + examples/nrf-rtos-trace/Cargo.toml | 1 + examples/nrf/Cargo.toml | 1 + examples/rp/Cargo.toml | 1 + examples/std/Cargo.toml | 1 + examples/stm32f0/Cargo.toml | 1 + examples/stm32f1/Cargo.toml | 1 + examples/stm32f2/Cargo.toml | 1 + examples/stm32f3/Cargo.toml | 1 + examples/stm32f4/Cargo.toml | 1 + examples/stm32f7/Cargo.toml | 1 + examples/stm32g0/Cargo.toml | 1 + examples/stm32g4/Cargo.toml | 1 + examples/stm32h7/Cargo.toml | 1 + examples/stm32l0/Cargo.toml | 1 + examples/stm32l1/Cargo.toml | 1 + examples/stm32l4/Cargo.toml | 1 + examples/stm32l5/Cargo.toml | 1 + examples/stm32u5/Cargo.toml | 1 + examples/stm32wb/Cargo.toml | 1 + examples/stm32wl/Cargo.toml | 1 + examples/wasm/Cargo.toml | 1 + stm32-gen-features/Cargo.toml | 1 + stm32-metapac-gen/Cargo.toml | 1 + tests/rp/Cargo.toml | 1 + tests/stm32/Cargo.toml | 1 + xtask/Cargo.toml | 1 + 57 files changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index ae124a871..c13f546e2 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml @@ -3,6 +3,7 @@ authors = ["Dario Nieuwenhuis "] edition = "2018" name = "embassy-basic-example" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly"] } diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml index e2933076f..c9a963d4d 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml @@ -2,6 +2,7 @@ name = "blinky-async" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7" diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml index dbd3aba8b..f86361dd5 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml @@ -2,6 +2,7 @@ name = "blinky-hal" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7" diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml index 0dd326015..9733658b6 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml @@ -2,6 +2,7 @@ name = "blinky-irq" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7" diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml index e7f4f5d1f..a077f1828 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml @@ -2,6 +2,7 @@ name = "blinky-pac" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7" diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index a42f88688..54c67a375 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "embassy-boot" version = "0.1.0" description = "Bootloader using Embassy" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/boot/src/" diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml index 234393e7c..c6af70144 100644 --- a/embassy-boot/nrf/Cargo.toml +++ b/embassy-boot/nrf/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "embassy-boot-nrf" version = "0.1.0" description = "Bootloader lib for nRF chips" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/nrf/src/" diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml index ad4657e0d..9d12c6cfd 100644 --- a/embassy-boot/stm32/Cargo.toml +++ b/embassy-boot/stm32/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "embassy-boot-stm32" version = "0.1.0" description = "Bootloader lib for STM32 chips" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/stm32/src/" diff --git a/embassy-cortex-m/Cargo.toml b/embassy-cortex-m/Cargo.toml index 7efced669..5c5718d50 100644 --- a/embassy-cortex-m/Cargo.toml +++ b/embassy-cortex-m/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-cortex-m" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-cortex-m-v$VERSION/embassy-cortex-m/src/" diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 845f742e4..d0be6d195 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-embedded-hal" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 3b1c4ab46..d0f51646d 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-executor" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] diff --git a/embassy-hal-common/Cargo.toml b/embassy-hal-common/Cargo.toml index 58f0af6ab..e8617c02f 100644 --- a/embassy-hal-common/Cargo.toml +++ b/embassy-hal-common/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-hal-common" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [features] diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index dcb0d8245..0e7a982a1 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-lora" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/" diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 03fa79dd8..91d5ec8a3 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-macros" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [dependencies] syn = { version = "1.0.76", features = ["full", "extra-traits"] } diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index d5b13204e..967ef26a7 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-net" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 58b820242..5459bc90c 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-nrf" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/" diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 3aca5dbb4..c56858415 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-rp" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/" diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index fbe37fe3d..2610e5687 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-stm32" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index c3b361b8a..c51a71d01 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-time" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml index b525df337..d22bf7d72 100644 --- a/embassy-usb-driver/Cargo.toml +++ b/embassy-usb-driver/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-usb-driver" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -13,4 +14,4 @@ target = "thumbv7em-none-eabi" [dependencies] defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } \ No newline at end of file +log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 1f705e9ff..b59ba8a22 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-usb" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-v$VERSION/embassy-usb/src/" diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index b9ff92578..a5d82b601 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-nrf-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index ce1e6fe4a..3a1843562 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32f3-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 2fc7ae834..8d9c4490e 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32f7-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index fd809714a..b4314aa72 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32h7-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 470eca52a..a17d336a6 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32l0-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 2b4b29357..683f2c860 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32l1-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 40bddd194..b879c0d76 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32l4-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 5b4a61e8f..e3bc0e49c 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32wl-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index aa2a13ecb..b417a40d1 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "nrf-bootloader-example" version = "0.1.0" description = "Bootloader for nRF chips" +license = "MIT OR Apache-2.0" [dependencies] defmt = { version = "0.3", optional = true } diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index 491777103..4ddd1c99c 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "stm32-bootloader-example" version = "0.1.0" description = "Example bootloader for STM32 chips" +license = "MIT OR Apache-2.0" [dependencies] defmt = { version = "0.3", optional = true } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 87c9f33f5..d8c24dfad 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-nrf-rtos-trace-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [features] default = ["log", "nightly"] diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a5d340c69..9ebd04845 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-nrf-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [features] default = ["nightly"] diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 1bb6432e6..a5af8b2f0 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-rp-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index dbfd9d625..b9bd1e718 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-std-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index c82b79c86..a56c546ee 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-stm32f0-examples" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index e6553789a..6be131f30 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32f1-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 60cd54bd9..f6adda2a3 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32f2-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index f5b0b880c..27188dd19 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32f3-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index ea5c47a43..6d4f09fba 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32f4-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 6c2f846fe..dad92c0fc 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32f7-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 6baf17f36..f5673718d 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32g0-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index d8c05a979..ecda28805 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32g4-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index e725e03cb..1a05b9ecb 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32h7-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 7e61f0c19..7e1120f48 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32l0-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [features] default = ["nightly"] diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index a943c73d2..9460febf5 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32l1-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 2e2d07dc3..657605ebe 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32l4-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [features] diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 9ebab6476..63eac3ed2 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32l5-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [features] diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 164940586..3d704011b 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32u5-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 923833e46..5b96fa191 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32wb-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 94e0fb830..c827d2b71 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32wl-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index ea61fb921..e0e799a34 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-wasm-example" version = "0.1.0" +license = "MIT OR Apache-2.0" [lib] crate-type = ["cdylib"] diff --git a/stm32-gen-features/Cargo.toml b/stm32-gen-features/Cargo.toml index f92d127ea..f4aa08ebe 100644 --- a/stm32-gen-features/Cargo.toml +++ b/stm32-gen-features/Cargo.toml @@ -2,6 +2,7 @@ name = "gen_features" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/stm32-metapac-gen/Cargo.toml b/stm32-metapac-gen/Cargo.toml index 0ec2075f3..3c1dab57a 100644 --- a/stm32-metapac-gen/Cargo.toml +++ b/stm32-metapac-gen/Cargo.toml @@ -2,6 +2,7 @@ name = "stm32-metapac-gen" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [dependencies] diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 2745aef06..d6770d6e9 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-rp-tests" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index daae3d464..bebbf557e 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32-tests" version = "0.1.0" +license = "MIT OR Apache-2.0" [features] stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index d9d6c9b26..696cfdafe 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "xtask" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] anyhow = "1.0.43" From df7174ecb03f466d4b97f2ce1a11e687317bd93a Mon Sep 17 00:00:00 2001 From: chemicstry Date: Fri, 7 Oct 2022 14:31:55 +0300 Subject: [PATCH 30/46] Fix internal channel reading on adc_v2 --- embassy-stm32/src/adc/mod.rs | 5 ++ embassy-stm32/src/adc/v2.rs | 149 ++++++++++++++++++++++---------- embassy-stm32/src/adc/v4.rs | 8 -- examples/stm32f4/src/bin/adc.rs | 25 +++++- 4 files changed, 130 insertions(+), 57 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index fba016a77..0eb4eba73 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -28,6 +28,10 @@ pub(crate) mod sealed { pub trait AdcPin { fn channel(&self) -> u8; } + + pub trait InternalChannel { + fn channel(&self) -> u8; + } } #[cfg(not(any(adc_f1, adc_v2)))] @@ -37,6 +41,7 @@ pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} #[cfg(all(not(adc_f1), not(adc_v1)))] pub trait Common: sealed::Common + 'static {} pub trait AdcPin: sealed::AdcPin {} +pub trait InternalChannel: sealed::InternalChannel {} #[cfg(not(stm32h7))] foreach_peripheral!( diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 70e3b73b3..4fe4ad1f0 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -3,7 +3,9 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; +use super::InternalChannel; use crate::adc::{AdcPin, Instance}; +use crate::peripherals::ADC1; use crate::time::Hertz; use crate::Peripheral; @@ -12,6 +14,9 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; +/// ADC turn-on time +pub const ADC_POWERUP_TIME_US: u32 = 3; + pub enum Resolution { TwelveBit, TenBit, @@ -46,24 +51,53 @@ impl Resolution { } pub struct VrefInt; -impl AdcPin for VrefInt {} -impl super::sealed::AdcPin for VrefInt { +impl InternalChannel for VrefInt {} +impl super::sealed::InternalChannel for VrefInt { fn channel(&self) -> u8 { 17 } } +impl VrefInt { + /// Time needed for internal voltage reference to stabilize + pub fn start_time_us() -> u32 { + 10 + } +} + pub struct Temperature; -impl AdcPin for Temperature {} -impl super::sealed::AdcPin for Temperature { +impl InternalChannel for Temperature {} +impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { - 16 + cfg_if::cfg_if! { + if #[cfg(any(stm32f40, stm32f41))] { + 16 + } else { + 18 + } + } + } +} + +impl Temperature { + /// Converts temperature sensor reading in millivolts to degrees celcius + pub fn to_celcius(sample_mv: u16) -> f32 { + // From 6.3.22 Temperature sensor characteristics + const V25: i32 = 760; // mV + const AVG_SLOPE: f32 = 2.5; // mV/C + + (sample_mv as i32 - V25) as f32 / AVG_SLOPE + 25.0 + } + + /// Time needed for temperature sensor readings to stabilize + pub fn start_time_us() -> u32 { + 10 } } pub struct Vbat; -impl AdcPin for Vbat {} -impl super::sealed::AdcPin for Vbat { +impl InternalChannel for Vbat {} +impl super::sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { 18 } @@ -152,19 +186,16 @@ where T::enable(); T::reset(); - let presc = unsafe { Prescaler::from_pclk2(T::frequency()) }; + let presc = Prescaler::from_pclk2(T::frequency()); unsafe { T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); - } - unsafe { - // disable before config is set T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); + reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); }); } - delay.delay_us(20); // TODO? + delay.delay_us(ADC_POWERUP_TIME_US); Self { sample_time: Default::default(), @@ -194,6 +225,45 @@ where ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 } + /// Enables internal voltage reference and returns [VrefInt], which can be used in + /// [Adc::read_internal()] to perform conversion. + pub fn enable_vrefint(&self) -> VrefInt { + unsafe { + T::common_regs().ccr().modify(|reg| { + reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED); + }); + } + + VrefInt {} + } + + /// Enables internal temperature sensor and returns [Temperature], which can be used in + /// [Adc::read_internal()] to perform conversion. + /// + /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, + /// temperature sensor will return vbat value. + pub fn enable_temperature(&self) -> Temperature { + unsafe { + T::common_regs().ccr().modify(|reg| { + reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED); + }); + } + + Temperature {} + } + + /// Enables vbat input and returns [Vbat], which can be used in + /// [Adc::read_internal()] to perform conversion. + pub fn enable_vbat(&self) -> Vbat { + unsafe { + T::common_regs().ccr().modify(|reg| { + reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED); + }); + } + + Vbat {} + } + /// Perform a single conversion. fn convert(&mut self) -> u16 { unsafe { @@ -224,44 +294,31 @@ where P: crate::gpio::sealed::Pin, { unsafe { - // dissable ADC - T::regs().cr2().modify(|reg| { - reg.set_swstart(false); - }); - T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); - }); - pin.set_as_analog(); - // Configure ADC - T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res())); - - // Select channel - T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel())); - - // Configure channel - Self::set_channel_sample_time(pin.channel(), self.sample_time); - - // enable adc - T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); - }); - - let val = self.convert(); - - // dissable ADC - T::regs().cr2().modify(|reg| { - reg.set_swstart(false); - }); - T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); - }); - - val + self.read_channel(pin.channel()) } } + pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { + unsafe { self.read_channel(channel.channel()) } + } + + unsafe fn read_channel(&mut self, channel: u8) -> u16 { + // Configure ADC + T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res())); + + // Select channel + T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); + + // Configure channel + Self::set_channel_sample_time(channel, self.sample_time); + + let val = self.convert(); + + val + } + unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { if ch <= 9 { T::regs() diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index d356d7b66..7e6a219e1 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -50,14 +50,6 @@ impl Resolution { } } -pub trait InternalChannel: sealed::InternalChannel {} - -mod sealed { - pub trait InternalChannel { - fn channel(&self) -> u8; - } -} - // NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs pub struct VrefInt; impl InternalChannel for VrefInt {} diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 871185074..6f80c1ef1 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -2,9 +2,10 @@ #![no_main] #![feature(type_alias_impl_trait)] +use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::Adc; +use embassy_stm32::adc::{Adc, SampleTime, Temperature, VrefInt}; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -13,12 +14,30 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut adc = Adc::new(p.ADC1, &mut Delay); + let mut delay = Delay; + let mut adc = Adc::new(p.ADC1, &mut delay); let mut pin = p.PC1; + let mut vrefint = adc.enable_vrefint(); + let mut temp = adc.enable_temperature(); + + // Startup delay can be combined to the maximum of either + delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); + loop { + // Read pin let v = adc.read(&mut pin); - info!("--> {} - {} mV", v, adc.to_millivolts(v)); + info!("PC1: {} ({} mV)", v, adc.to_millivolts(v)); + + // Read internal temperature + let v = adc.read_internal(&mut temp); + let celcius = Temperature::to_celcius(adc.to_millivolts(v)); + info!("Internal temp: {} ({} C)", v, celcius); + + // Read internal voltage reference + let v = adc.read_internal(&mut vrefint); + info!("VrefInt: {} ({} mV)", v, adc.to_millivolts(v)); + Timer::after(Duration::from_millis(100)).await; } } From 322cfafed353ddfbe62121238ef97c56dd7a6eed Mon Sep 17 00:00:00 2001 From: chemicstry Date: Fri, 7 Oct 2022 14:53:03 +0300 Subject: [PATCH 31/46] Fix adc_v4 compilation --- embassy-stm32/src/adc/v4.rs | 8 ++++---- examples/stm32f4/src/bin/adc.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 7e6a219e1..eda2b2a72 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{AdcPin, Instance}; +use super::{AdcPin, Instance, InternalChannel}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -53,7 +53,7 @@ impl Resolution { // NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs pub struct VrefInt; impl InternalChannel for VrefInt {} -impl sealed::InternalChannel for VrefInt { +impl super::sealed::InternalChannel for VrefInt { fn channel(&self) -> u8 { 19 } @@ -61,7 +61,7 @@ impl sealed::InternalChannel for VrefInt { pub struct Temperature; impl InternalChannel for Temperature {} -impl sealed::InternalChannel for Temperature { +impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { 18 } @@ -69,7 +69,7 @@ impl sealed::InternalChannel for Temperature { pub struct Vbat; impl InternalChannel for Vbat {} -impl sealed::InternalChannel for Vbat { +impl super::sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { // TODO this should be 14 for H7a/b/35 17 diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 6f80c1ef1..1d030f7dc 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -5,7 +5,7 @@ use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, SampleTime, Temperature, VrefInt}; +use embassy_stm32::adc::{Adc, Temperature, VrefInt}; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; From aa8ba2115c4606b53dedce1af2da5de2fd59f563 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 8 Oct 2022 11:35:11 +0800 Subject: [PATCH 32/46] Expose Pin::pin() and Pin::bank() as public --- embassy-rp/src/gpio.rs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index a28bae96b..f79f592b4 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -599,12 +599,12 @@ pub(crate) mod sealed { fn pin_bank(&self) -> u8; #[inline] - fn pin(&self) -> u8 { + fn _pin(&self) -> u8 { self.pin_bank() & 0x1f } #[inline] - fn bank(&self) -> Bank { + fn _bank(&self) -> Bank { if self.pin_bank() & 0x20 == 0 { Bank::Bank0 } else { @@ -613,35 +613,35 @@ pub(crate) mod sealed { } fn io(&self) -> pac::io::Gpio { - let block = match self.bank() { + let block = match self._bank() { Bank::Bank0 => crate::pac::IO_BANK0, Bank::Qspi => crate::pac::IO_QSPI, }; - block.gpio(self.pin() as _) + block.gpio(self._pin() as _) } fn pad_ctrl(&self) -> Reg { - let block = match self.bank() { + let block = match self._bank() { Bank::Bank0 => crate::pac::PADS_BANK0, Bank::Qspi => crate::pac::PADS_QSPI, }; - block.gpio(self.pin() as _) + block.gpio(self._pin() as _) } fn sio_out(&self) -> pac::sio::Gpio { - SIO.gpio_out(self.bank() as _) + SIO.gpio_out(self._bank() as _) } fn sio_oe(&self) -> pac::sio::Gpio { - SIO.gpio_oe(self.bank() as _) + SIO.gpio_oe(self._bank() as _) } fn sio_in(&self) -> Reg { - SIO.gpio_in(self.bank() as _) + SIO.gpio_in(self._bank() as _) } fn int_proc(&self) -> pac::io::Int { - let io_block = match self.bank() { + let io_block = match self._bank() { Bank::Bank0 => crate::pac::IO_BANK0, Bank::Qspi => crate::pac::IO_QSPI, }; @@ -658,6 +658,18 @@ pub trait Pin: Peripheral

+ Into + sealed::Pin + Sized + 'stat pin_bank: self.pin_bank(), } } + + /// Returns the pin number within a bank + #[inline] + fn pin(&self) -> u8 { + self._pin() + } + + /// Returns the bank of this pin + #[inline] + fn bank(&self) -> Bank { + self._bank() + } } pub struct AnyPin { From f554962f54b9f9e8f85a0dd099635619bd1acd1d Mon Sep 17 00:00:00 2001 From: ceekdee Date: Sat, 8 Oct 2022 14:32:22 -0500 Subject: [PATCH 33/46] Improve generics and consolidate antenna handling --- embassy-lora/src/sx126x/mod.rs | 48 ++++++------------- .../src/sx126x/sx126x_lora/board_specific.rs | 39 ++++++++++----- embassy-lora/src/sx126x/sx126x_lora/mod.rs | 19 ++++---- .../src/sx126x/sx126x_lora/subroutine.rs | 34 ++++--------- examples/nrf/src/bin/lora_p2p_report.rs | 8 ++-- examples/nrf/src/bin/lora_p2p_sense.rs | 8 ++-- 6 files changed, 69 insertions(+), 87 deletions(-) diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs index d67c7b109..ed8cb4059 100644 --- a/embassy-lora/src/sx126x/mod.rs +++ b/embassy-lora/src/sx126x/mod.rs @@ -13,35 +13,29 @@ use sx126x_lora::LoRa; use self::sx126x_lora::mod_params::RadioError; /// Semtech Sx126x LoRa peripheral -pub struct Sx126xRadio +pub struct Sx126xRadio where SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - ANTRX: OutputPin + 'static, - ANTTX: OutputPin + 'static, + CTRL: OutputPin + 'static, WAIT: Wait + 'static, BUS: Error + Format + 'static, { - pub lora: LoRa, + pub lora: LoRa, } -impl Sx126xRadio +impl Sx126xRadio where SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - ANTRX: OutputPin + 'static, - ANTTX: OutputPin + 'static, + CTRL: OutputPin + 'static, WAIT: Wait + 'static, BUS: Error + Format + 'static, { pub async fn new( spi: SPI, - cs: CS, - reset: RESET, - antenna_rx: ANTRX, - antenna_tx: ANTTX, + cs: CTRL, + reset: CTRL, + antenna_rx: CTRL, + antenna_tx: CTRL, dio1: WAIT, busy: WAIT, enable_public_network: bool, @@ -53,13 +47,10 @@ where } } -impl Timings for Sx126xRadio +impl Timings for Sx126xRadio where SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - ANTRX: OutputPin + 'static, - ANTTX: OutputPin + 'static, + CTRL: OutputPin + 'static, WAIT: Wait + 'static, BUS: Error + Format + 'static, { @@ -71,13 +62,10 @@ where } } -impl PhyRxTx for Sx126xRadio +impl PhyRxTx for Sx126xRadio where SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - ANTRX: OutputPin + 'static, - ANTTX: OutputPin + 'static, + CTRL: OutputPin + 'static, WAIT: Wait + 'static, BUS: Error + Format + 'static, { @@ -86,10 +74,7 @@ where type TxFuture<'m> = impl Future> + 'm where SPI: 'm, - CS: 'm, - RESET: 'm, - ANTRX: 'm, - ANTTX: 'm, + CTRL: 'm, WAIT: 'm, BUS: 'm; @@ -122,10 +107,7 @@ where type RxFuture<'m> = impl Future> + 'm where SPI: 'm, - CS: 'm, - RESET: 'm, - ANTRX: 'm, - ANTTX: 'm, + CTRL: 'm, WAIT: 'm, BUS: 'm; diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs index 5b4891c04..1fb085887 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs @@ -13,13 +13,10 @@ const BRD_TCXO_WAKEUP_TIME: u32 = 10; // Provides board-specific functionality for Semtech SX126x-based boards. Use #[cfg(feature = "board_type")] to specify unique board functionality. // The base implementation supports the RAK4631 board. -impl LoRa +impl LoRa where SPI: SpiBus, - CS: OutputPin, - RESET: OutputPin, - ANTRX: OutputPin, - ANTTX: OutputPin, + CTRL: OutputPin, WAIT: Wait, { // De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes. @@ -210,14 +207,34 @@ where RadioType::SX1262 } - // Initialize the RF switch I/O pins interface - pub(super) async fn brd_ant_sw_on(&mut self) -> Result<(), RadioError> { - Ok(()) // no operation currently + // Quiesce the antenna(s). + pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError> { + #[cfg(feature = "rak4631")] + { + self.antenna_tx.set_low().map_err(|_| AntTx)?; + self.antenna_rx.set_low().map_err(|_| AntRx)?; + } + Ok(()) } - // De-initialize the RF switch I/O pins interface for MCU low power modes - pub(super) async fn brd_ant_sw_off(&mut self) -> Result<(), RadioError> { - Ok(()) // no operation currently + // Prepare the antenna(s) for a receive operation + pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError> { + #[cfg(feature = "rak4631")] + { + self.antenna_tx.set_low().map_err(|_| AntTx)?; + self.antenna_rx.set_high().map_err(|_| AntRx)?; + } + Ok(()) + } + + // Prepare the antenna(s) for a send operation + pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError> { + #[cfg(feature = "rak4631")] + { + self.antenna_rx.set_low().map_err(|_| AntRx)?; + self.antenna_tx.set_high().map_err(|_| AntTx)?; + } + Ok(()) } // Check if the given RF frequency is supported by the hardware diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs index 53fbde749..280f26d51 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/mod.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/mod.rs @@ -26,12 +26,12 @@ const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, const RADIO_WAKEUP_TIME: u32 = 3; /// Provides high-level access to Semtech SX126x-based boards -pub struct LoRa { +pub struct LoRa { spi: SPI, - cs: CS, - reset: RESET, - antenna_rx: ANTRX, - antenna_tx: ANTTX, + cs: CTRL, + reset: CTRL, + antenna_rx: CTRL, + antenna_tx: CTRL, dio1: WAIT, busy: WAIT, operating_mode: RadioMode, @@ -45,17 +45,14 @@ pub struct LoRa { frequency_error: u32, } -impl LoRa +impl LoRa where SPI: SpiBus, - CS: OutputPin, - RESET: OutputPin, - ANTRX: OutputPin, - ANTTX: OutputPin, + CTRL: OutputPin, WAIT: Wait, { /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time () - pub fn new(spi: SPI, cs: CS, reset: RESET, antenna_rx: ANTRX, antenna_tx: ANTTX, dio1: WAIT, busy: WAIT) -> Self { + pub fn new(spi: SPI, cs: CTRL, reset: CTRL, antenna_rx: CTRL, antenna_tx: CTRL, dio1: WAIT, busy: WAIT) -> Self { Self { spi, cs, diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs index 02a0a72ec..283e60993 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs @@ -19,13 +19,10 @@ const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248; // Provides board-specific functionality for Semtech SX126x-based boards -impl LoRa +impl LoRa where SPI: SpiBus, - CS: OutputPin, - RESET: OutputPin, - ANTRX: OutputPin, - ANTTX: OutputPin, + CTRL: OutputPin, WAIT: Wait, { // Initialize the radio driver @@ -44,7 +41,6 @@ where let operating_mode = self.brd_get_operating_mode(); if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle { self.brd_wakeup().await?; - self.brd_ant_sw_on().await?; } self.brd_wait_on_busy().await?; Ok(()) @@ -117,10 +113,7 @@ where // Set the radio in sleep mode pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError> { - self.brd_ant_sw_off().await?; - - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_sleep()?; if !sleep_config.warm_start { self.image_calibrated = false; @@ -141,8 +134,7 @@ where self.brd_set_operating_mode(RadioMode::StandbyXOSC); } - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_sleep()?; Ok(()) } @@ -162,8 +154,7 @@ where Self::timeout_3(timeout), ]; - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_high(); + self.brd_ant_set_tx()?; self.brd_set_operating_mode(RadioMode::Transmit); self.brd_write_command(OpCode::SetTx, &buffer).await?; @@ -178,8 +169,7 @@ where Self::timeout_3(timeout), ]; - let _fix_rx = self.antenna_rx.set_high(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_set_rx()?; self.brd_set_operating_mode(RadioMode::Receive); self.brd_write_registers(Register::RxGain, &[0x94u8]).await?; @@ -195,8 +185,7 @@ where Self::timeout_3(timeout), ]; - let _fix_rx = self.antenna_rx.set_high(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_set_rx()?; self.brd_set_operating_mode(RadioMode::Receive); // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity @@ -225,8 +214,7 @@ where // Set the radio in CAD mode pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError> { - let _fix_rx = self.antenna_rx.set_high(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_set_rx()?; self.brd_write_command(OpCode::SetCAD, &[]).await?; self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); @@ -235,8 +223,7 @@ where // Set the radio in continuous wave transmission mode pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError> { - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_high(); + self.brd_ant_set_tx()?; self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?; self.brd_set_operating_mode(RadioMode::Transmit); @@ -245,8 +232,7 @@ where // Set the radio in continuous preamble transmission mode pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError> { - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_high(); + self.brd_ant_set_tx()?; self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?; self.brd_set_operating_mode(RadioMode::Transmit); diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf/src/bin/lora_p2p_report.rs index 46cb848b1..4ba3d30ce 100644 --- a/examples/nrf/src/bin/lora_p2p_report.rs +++ b/examples/nrf/src/bin/lora_p2p_report.rs @@ -24,12 +24,12 @@ async fn main(_spawner: Spawner) { let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); - let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard); - let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard); + let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); let busy = Input::new(p.P1_14.degrade(), Pull::Down); - let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); - let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard); + let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); + let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { Ok(r) => r, diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs index 3c6bb8767..405a8403f 100644 --- a/examples/nrf/src/bin/lora_p2p_sense.rs +++ b/examples/nrf/src/bin/lora_p2p_sense.rs @@ -97,12 +97,12 @@ async fn main(spawner: Spawner) { let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); - let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard); - let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard); + let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); let busy = Input::new(p.P1_14.degrade(), Pull::Down); - let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); - let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard); + let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); + let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { Ok(r) => r, From e1faf8860776f6ad2bac2f3b06e7160fe00da7df Mon Sep 17 00:00:00 2001 From: huntc Date: Sun, 9 Oct 2022 13:07:25 +1100 Subject: [PATCH 34/46] Removes some of the code duplication for UarteWithIdle This commit removes some of the code duplication for UarteWithIdle at the expense of requiring a split. As the example illustrates though, this expense seems worth the benefit in terms of maintenance, and the avoidance of copying over methods. My main motivation for this commit was actually due to the `event_endtx` method not having been copied across. --- embassy-nrf/src/uarte.rs | 468 +++++++++++------------------- examples/nrf/src/bin/uart_idle.rs | 7 +- 2 files changed, 170 insertions(+), 305 deletions(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index d99599112..636d6c7a3 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -173,6 +173,61 @@ impl<'d, T: Instance> Uarte<'d, T> { (self.tx, self.rx) } + /// Split the Uarte into a transmitter and receiver that will + /// return on idle, which is determined as the time it takes + /// for two bytes to be received. + pub fn split_with_idle( + self, + timer: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) { + let mut timer = Timer::new(timer); + + into_ref!(ppi_ch1, ppi_ch2); + + let r = T::regs(); + + // BAUDRATE register values are `baudrate * 2^32 / 16000000` + // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values + // + // We want to stop RX if line is idle for 2 bytes worth of time + // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) + // This gives us the amount of 16M ticks for 20 bits. + let baudrate = r.baudrate.read().baudrate().variant().unwrap(); + let timeout = 0x8000_0000 / (baudrate as u32 / 40); + + timer.set_frequency(Frequency::F16MHz); + timer.cc(0).write(timeout); + timer.cc(0).short_compare_clear(); + timer.cc(0).short_compare_stop(); + + let mut ppi_ch1 = Ppi::new_one_to_two( + ppi_ch1.map_into(), + Event::from_reg(&r.events_rxdrdy), + timer.task_clear(), + timer.task_start(), + ); + ppi_ch1.enable(); + + let mut ppi_ch2 = Ppi::new_one_to_one( + ppi_ch2.map_into(), + timer.cc(0).event_compare(), + Task::from_reg(&r.tasks_stoprx), + ); + ppi_ch2.enable(); + + ( + self.tx, + UarteRxWithIdle { + rx: self.rx, + timer, + ppi_ch1: ppi_ch1, + _ppi_ch2: ppi_ch2, + }, + ) + } + /// Return the endtx event for use with PPI pub fn event_endtx(&self) -> Event { let r = T::regs(); @@ -597,6 +652,117 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> { } } +pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> { + rx: UarteRx<'d, T>, + timer: Timer<'d, U>, + ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>, + _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>, +} + +impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.ppi_ch1.disable(); + self.rx.read(buffer).await + } + + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.ppi_ch1.disable(); + self.rx.blocking_read(buffer) + } + + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { + if buffer.len() == 0 { + return Err(Error::BufferZeroLength); + } + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + + let ptr = buffer.as_ptr(); + let len = buffer.len(); + + let r = T::regs(); + let s = T::state(); + + self.ppi_ch1.enable(); + + let drop = OnDrop::new(|| { + self.timer.stop(); + + r.intenclr.write(|w| w.endrx().clear()); + r.events_rxto.reset(); + r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + + while r.events_endrx.read().bits() == 0 {} + }); + + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + r.events_endrx.reset(); + r.intenset.write(|w| w.endrx().set()); + + compiler_fence(Ordering::SeqCst); + + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + + poll_fn(|cx| { + s.endrx_waker.register(cx.waker()); + if r.events_endrx.read().bits() != 0 { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + let n = r.rxd.amount.read().amount().bits() as usize; + + self.timer.stop(); + r.events_rxstarted.reset(); + + drop.defuse(); + + Ok(n) + } + + pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { + if buffer.len() == 0 { + return Err(Error::BufferZeroLength); + } + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + + let ptr = buffer.as_ptr(); + let len = buffer.len(); + + let r = T::regs(); + + self.ppi_ch1.enable(); + + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + r.events_endrx.reset(); + r.intenclr.write(|w| w.endrx().clear()); + + compiler_fence(Ordering::SeqCst); + + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + + while r.events_endrx.read().bits() == 0 {} + + compiler_fence(Ordering::SeqCst); + let n = r.rxd.amount.read().amount().bits() as usize; + + self.timer.stop(); + r.events_rxstarted.reset(); + + Ok(n) + } +} + #[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))] pub(crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) { // Do nothing @@ -665,270 +831,6 @@ pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) { } } -/// Interface to an UARTE peripheral that uses an additional timer and two PPI channels, -/// allowing it to implement the ReadUntilIdle trait. -pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> { - tx: UarteTx<'d, U>, - rx: UarteRxWithIdle<'d, U, T>, -} - -impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> { - /// Create a new UARTE without hardware flow control - pub fn new( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: impl Peripheral

+ 'd, - txd: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(rxd, txd); - Self::new_inner( - uarte, - timer, - ppi_ch1, - ppi_ch2, - irq, - rxd.map_into(), - txd.map_into(), - None, - None, - config, - ) - } - - /// Create a new UARTE with hardware flow control (RTS/CTS) - pub fn new_with_rtscts( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: impl Peripheral

+ 'd, - txd: impl Peripheral

+ 'd, - cts: impl Peripheral

+ 'd, - rts: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(rxd, txd, cts, rts); - Self::new_inner( - uarte, - timer, - ppi_ch1, - ppi_ch2, - irq, - rxd.map_into(), - txd.map_into(), - Some(cts.map_into()), - Some(rts.map_into()), - config, - ) - } - - fn new_inner( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: PeripheralRef<'d, AnyPin>, - txd: PeripheralRef<'d, AnyPin>, - cts: Option>, - rts: Option>, - config: Config, - ) -> Self { - let baudrate = config.baudrate; - let (tx, rx) = Uarte::new_inner(uarte, irq, rxd, txd, cts, rts, config).split(); - - let mut timer = Timer::new(timer); - - into_ref!(ppi_ch1, ppi_ch2); - - let r = U::regs(); - - // BAUDRATE register values are `baudrate * 2^32 / 16000000` - // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values - // - // We want to stop RX if line is idle for 2 bytes worth of time - // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) - // This gives us the amount of 16M ticks for 20 bits. - let timeout = 0x8000_0000 / (baudrate as u32 / 40); - - timer.set_frequency(Frequency::F16MHz); - timer.cc(0).write(timeout); - timer.cc(0).short_compare_clear(); - timer.cc(0).short_compare_stop(); - - let mut ppi_ch1 = Ppi::new_one_to_two( - ppi_ch1.map_into(), - Event::from_reg(&r.events_rxdrdy), - timer.task_clear(), - timer.task_start(), - ); - ppi_ch1.enable(); - - let mut ppi_ch2 = Ppi::new_one_to_one( - ppi_ch2.map_into(), - timer.cc(0).event_compare(), - Task::from_reg(&r.tasks_stoprx), - ); - ppi_ch2.enable(); - - Self { - tx, - rx: UarteRxWithIdle { - rx, - timer, - ppi_ch1: ppi_ch1, - _ppi_ch2: ppi_ch2, - }, - } - } - - /// Split the Uarte into a transmitter and receiver, which is - /// particuarly useful when having two tasks correlating to - /// transmitting and receiving. - pub fn split(self) -> (UarteTx<'d, U>, UarteRxWithIdle<'d, U, T>) { - (self.tx, self.rx) - } - - pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.rx.read(buffer).await - } - - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - self.tx.write(buffer).await - } - - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.rx.blocking_read(buffer) - } - - pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { - self.tx.blocking_write(buffer) - } - - pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - self.rx.read_until_idle(buffer).await - } - - pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - self.rx.blocking_read_until_idle(buffer) - } -} - -pub struct UarteRxWithIdle<'d, U: Instance, T: TimerInstance> { - rx: UarteRx<'d, U>, - timer: Timer<'d, T>, - ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>, - _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>, -} - -impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> { - pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.ppi_ch1.disable(); - self.rx.read(buffer).await - } - - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.ppi_ch1.disable(); - self.rx.blocking_read(buffer) - } - - pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - if buffer.len() == 0 { - return Err(Error::BufferZeroLength); - } - if buffer.len() > EASY_DMA_SIZE { - return Err(Error::BufferTooLong); - } - - let ptr = buffer.as_ptr(); - let len = buffer.len(); - - let r = U::regs(); - let s = U::state(); - - self.ppi_ch1.enable(); - - let drop = OnDrop::new(|| { - self.timer.stop(); - - r.intenclr.write(|w| w.endrx().clear()); - r.events_rxto.reset(); - r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - - while r.events_endrx.read().bits() == 0 {} - }); - - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); - - r.events_endrx.reset(); - r.intenset.write(|w| w.endrx().set()); - - compiler_fence(Ordering::SeqCst); - - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); - - poll_fn(|cx| { - s.endrx_waker.register(cx.waker()); - if r.events_endrx.read().bits() != 0 { - return Poll::Ready(()); - } - Poll::Pending - }) - .await; - - compiler_fence(Ordering::SeqCst); - let n = r.rxd.amount.read().amount().bits() as usize; - - self.timer.stop(); - r.events_rxstarted.reset(); - - drop.defuse(); - - Ok(n) - } - - pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - if buffer.len() == 0 { - return Err(Error::BufferZeroLength); - } - if buffer.len() > EASY_DMA_SIZE { - return Err(Error::BufferTooLong); - } - - let ptr = buffer.as_ptr(); - let len = buffer.len(); - - let r = U::regs(); - - self.ppi_ch1.enable(); - - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); - - r.events_endrx.reset(); - r.intenclr.write(|w| w.endrx().clear()); - - compiler_fence(Ordering::SeqCst); - - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); - - while r.events_endrx.read().bits() == 0 {} - - compiler_fence(Ordering::SeqCst); - let n = r.rxd.amount.read().amount().bits() as usize; - - self.timer.stop(); - r.events_rxstarted.reset(); - - Ok(n) - } -} pub(crate) mod sealed { use core::sync::atomic::AtomicU8; @@ -1006,18 +908,6 @@ mod eh02 { Ok(()) } } - - impl<'d, U: Instance, T: TimerInstance> embedded_hal_02::blocking::serial::Write for UarteWithIdle<'d, U, T> { - type Error = Error; - - fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn bflush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - } } #[cfg(feature = "unstable-traits")] @@ -1067,10 +957,6 @@ mod eh1 { impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> { type Error = Error; } - - impl<'d, U: Instance, T: TimerInstance> embedded_hal_1::serial::ErrorType for UarteWithIdle<'d, U, T> { - type Error = Error; - } } #[cfg(all( @@ -1126,26 +1012,4 @@ mod eha { self.read(buffer) } } - - impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read for UarteWithIdle<'d, U, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buffer) - } - } - - impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write for UarteWithIdle<'d, U, T> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buffer) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } - } } diff --git a/examples/nrf/src/bin/uart_idle.rs b/examples/nrf/src/bin/uart_idle.rs index 09ec624c0..6af4f7097 100644 --- a/examples/nrf/src/bin/uart_idle.rs +++ b/examples/nrf/src/bin/uart_idle.rs @@ -15,7 +15,8 @@ async fn main(_spawner: Spawner) { config.baudrate = uarte::Baudrate::BAUD115200; let irq = interrupt::take!(UARTE0_UART0); - let mut uart = uarte::UarteWithIdle::new(p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, irq, p.P0_08, p.P0_06, config); + let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); + let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); info!("uarte initialized!"); @@ -23,12 +24,12 @@ async fn main(_spawner: Spawner) { let mut buf = [0; 8]; buf.copy_from_slice(b"Hello!\r\n"); - unwrap!(uart.write(&buf).await); + unwrap!(tx.write(&buf).await); info!("wrote hello in uart!"); loop { info!("reading..."); - let n = unwrap!(uart.read_until_idle(&mut buf).await); + let n = unwrap!(rx.read_until_idle(&mut buf).await); info!("got {} bytes", n); } } From 327d3cf0df7a1b116ea7ec44d36a569e6ba6ca16 Mon Sep 17 00:00:00 2001 From: ceekdee Date: Mon, 10 Oct 2022 12:35:42 -0500 Subject: [PATCH 35/46] Change rak4631 feature to sx126x, removing use in board-specific processing; simplify the P2P examples; correct RSSI computation. --- embassy-lora/Cargo.toml | 4 +- embassy-lora/src/lib.rs | 2 +- .../src/sx126x/sx126x_lora/board_specific.rs | 28 ++----- .../src/sx126x/sx126x_lora/subroutine.rs | 6 +- examples/nrf/Cargo.toml | 2 +- examples/nrf/src/bin/lora_p2p_report.rs | 62 +++++++------- examples/nrf/src/bin/lora_p2p_sense.rs | 84 ++++--------------- 7 files changed, 61 insertions(+), 127 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index bcb837d9d..ea2c3fe67 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -9,7 +9,7 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" features = ["time", "defmt"] flavors = [ - { name = "rak4631", target = "thumbv7em-none-eabihf", features = ["rak4631"] }, + { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] }, { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, ] @@ -17,7 +17,7 @@ flavors = [ [lib] [features] -rak4631 = [] +sx126x = [] sx127x = [] stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] time = [] diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 342f66b2a..3e4748430 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -7,7 +7,7 @@ pub(crate) mod fmt; #[cfg(feature = "stm32wl")] pub mod stm32wl; -#[cfg(feature = "rak4631")] +#[cfg(feature = "sx126x")] pub mod sx126x; #[cfg(feature = "sx127x")] pub mod sx127x; diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs index 1fb085887..a7b9e1486 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs @@ -10,8 +10,7 @@ use super::LoRa; // Defines the time required for the TCXO to wakeup [ms]. const BRD_TCXO_WAKEUP_TIME: u32 = 10; -// Provides board-specific functionality for Semtech SX126x-based boards. Use #[cfg(feature = "board_type")] to specify unique board functionality. -// The base implementation supports the RAK4631 board. +// Provides board-specific functionality for Semtech SX126x-based boards. impl LoRa where @@ -203,44 +202,33 @@ where // Get the radio type pub(super) fn brd_get_radio_type(&mut self) -> RadioType { - #[cfg(feature = "rak4631")] RadioType::SX1262 } // Quiesce the antenna(s). pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError> { - #[cfg(feature = "rak4631")] - { - self.antenna_tx.set_low().map_err(|_| AntTx)?; - self.antenna_rx.set_low().map_err(|_| AntRx)?; - } + self.antenna_tx.set_low().map_err(|_| AntTx)?; + self.antenna_rx.set_low().map_err(|_| AntRx)?; Ok(()) } // Prepare the antenna(s) for a receive operation pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError> { - #[cfg(feature = "rak4631")] - { - self.antenna_tx.set_low().map_err(|_| AntTx)?; - self.antenna_rx.set_high().map_err(|_| AntRx)?; - } + self.antenna_tx.set_low().map_err(|_| AntTx)?; + self.antenna_rx.set_high().map_err(|_| AntRx)?; Ok(()) } // Prepare the antenna(s) for a send operation pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError> { - #[cfg(feature = "rak4631")] - { - self.antenna_rx.set_low().map_err(|_| AntRx)?; - self.antenna_tx.set_high().map_err(|_| AntTx)?; - } + self.antenna_rx.set_low().map_err(|_| AntRx)?; + self.antenna_tx.set_high().map_err(|_| AntTx)?; Ok(()) } // Check if the given RF frequency is supported by the hardware pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result> { - #[cfg(feature = "rak4631")] - Ok(true) // all frequencies currently supported for the SX1262 within a rak4631 + Ok(true) } // Get the duration required for the TCXO to wakeup [ms]. diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs index 283e60993..2e78b919b 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs @@ -564,7 +564,7 @@ where pub(super) async fn sub_get_rssi_inst(&mut self) -> Result> { let mut buffer = [0x00u8]; self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?; - let rssi: i8 = (-(buffer[0] as i8)) >> 1; // check this ??? + let rssi: i8 = ((-(buffer[0] as i32)) >> 1) as i8; // check this ??? Ok(rssi) } @@ -597,9 +597,9 @@ where self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?; // check this ??? - let rssi = (-(status[0] as i8)) >> 1; + let rssi = ((-(status[0] as i32)) >> 1) as i8; let snr = ((status[1] as i8) + 2) >> 2; - let signal_rssi = (-(status[2] as i8)) >> 1; + let signal_rssi = ((-(status[2] as i32)) >> 1) as i8; let freq_error = self.frequency_error; Ok(PacketStatus { diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 91c418213..6949042e2 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -18,7 +18,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } embedded-io = "0.3.0" -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["rak4631", "time", "defmt"], optional = true } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf/src/bin/lora_p2p_report.rs index 4ba3d30ce..d512b83f6 100644 --- a/examples/nrf/src/bin/lora_p2p_report.rs +++ b/examples/nrf/src/bin/lora_p2p_report.rs @@ -1,4 +1,6 @@ //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +//! Other nrf/sx126x combinations may work with appropriate pin modifications. +//! It demonstates LORA P2P functionality in conjunction with example lora_p2p_sense.rs. #![no_std] #![no_main] #![macro_use] @@ -18,7 +20,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); let mut spi_config = spim::Config::default(); - spi_config.frequency = spim::Frequency::M1; // M16 ??? + spi_config.frequency = spim::Frequency::M16; let mut radio = { let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); @@ -47,38 +49,30 @@ async fn main(_spawner: Spawner) { Timer::after(Duration::from_secs(5)).await; start_indicator.set_low(); - match radio.lora.sleep().await { - Ok(()) => info!("Sleep successful"), - Err(err) => info!("Sleep unsuccessful = {}", err), + loop { + let rf_config = RfConfig { + frequency: 903900000, // channel in Hz + bandwidth: Bandwidth::_250KHz, + spreading_factor: SpreadingFactor::_10, + coding_rate: CodingRate::_4_8, + }; + + let mut buffer = [00u8; 100]; + + // P2P receive + match radio.rx(rf_config, &mut buffer).await { + Ok((buffer_len, rx_quality)) => info!( + "RX received = {:?} with length = {} rssi = {} snr = {}", + &buffer[0..buffer_len], + buffer_len, + rx_quality.rssi(), + rx_quality.snr() + ), + Err(err) => info!("RX error = {}", err), + } + + debug_indicator.set_high(); + Timer::after(Duration::from_secs(2)).await; + debug_indicator.set_low(); } - - let rf_config = RfConfig { - frequency: 903900000, // channel in Hz - bandwidth: Bandwidth::_250KHz, - spreading_factor: SpreadingFactor::_10, - coding_rate: CodingRate::_4_8, - }; - - let mut buffer = [00u8; 100]; - - // P2P receive - match radio.rx(rf_config, &mut buffer).await { - Ok((buffer_len, rx_quality)) => info!( - "RX received = {:?} with length = {} rssi = {} snr = {}", - &buffer[0..buffer_len], - buffer_len, - rx_quality.rssi(), - rx_quality.snr() - ), - Err(err) => info!("RX error = {}", err), - } - - match radio.lora.sleep().await { - Ok(()) => info!("Sleep successful"), - Err(err) => info!("Sleep unsuccessful = {}", err), - } - - debug_indicator.set_high(); - Timer::after(Duration::from_secs(5)).await; - debug_indicator.set_low(); } diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs index 405a8403f..b9768874b 100644 --- a/examples/nrf/src/bin/lora_p2p_sense.rs +++ b/examples/nrf/src/bin/lora_p2p_sense.rs @@ -1,4 +1,6 @@ //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +//! Other nrf/sx126x combinations may work with appropriate pin modifications. +//! It demonstates LORA P2P functionality in conjunction with example lora_p2p_report.rs. #![no_std] #![no_main] #![macro_use] @@ -9,8 +11,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_lora::sx126x::*; -use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin as _, Pull}; -use embassy_nrf::temp::Temp; +use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; use embassy_nrf::{interrupt, spim}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::pubsub::{PubSubChannel, Publisher}; @@ -18,10 +19,6 @@ use embassy_time::{Duration, Timer}; use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; use {defmt_rtt as _, panic_probe as _, panic_probe as _}; -// Sensor packet constants -const TEMPERATURE_UID: u8 = 0x01; -const MOTION_UID: u8 = 0x02; - // Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) static MESSAGE_BUS: PubSubChannel = PubSubChannel::new(); @@ -32,53 +29,20 @@ enum Message { } #[embassy_executor::task] -async fn temperature_task( - mut temperature: Temp<'static>, - publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>, -) { - Timer::after(Duration::from_secs(45)).await; // stabilize for 45 seconds - - let mut temperature_reporting_threshhold = 10; - +async fn temperature_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { + // Publish a fake temperature every 43 seconds, minimizing LORA traffic. loop { - let value = temperature.read().await; - let mut temperature_val = value.to_num::(); - - info!("Temperature: {}", temperature_val); - - // only report every 2 degree Celsius drops, from 9 through 5, but starting at 3 always report - - if temperature_val == 8 || temperature_val == 6 || temperature_val == 4 { - temperature_val += 1; - } - - if temperature_reporting_threshhold > temperature_val - && (temperature_val == 9 || temperature_val == 7 || temperature_val == 5) - { - temperature_reporting_threshhold = temperature_val; - publisher.publish(Message::Temperature(temperature_val)).await; - } else if temperature_val <= 3 { - publisher.publish(Message::Temperature(temperature_val)).await; - } - - Timer::after(Duration::from_secs(20 * 60)).await; + Timer::after(Duration::from_secs(43)).await; + publisher.publish(Message::Temperature(9)).await; } } #[embassy_executor::task] -async fn motion_detection_task( - mut pir_pin: Input<'static, AnyPin>, - publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>, -) { - Timer::after(Duration::from_secs(30)).await; // stabilize for 30 seconds - +async fn motion_detection_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { + // Publish a fake motion detection every 79 seconds, minimizing LORA traffic. loop { - // wait for motion detection - pir_pin.wait_for_low().await; + Timer::after(Duration::from_secs(79)).await; publisher.publish(Message::MotionDetected).await; - - // wait a minute before setting up for more motion detection - Timer::after(Duration::from_secs(60)).await; } } @@ -91,7 +55,7 @@ async fn main(spawner: Spawner) { let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher()); let mut spi_config = spim::Config::default(); - spi_config.frequency = spim::Frequency::M1; // M16 ??? + spi_config.frequency = spim::Frequency::M16; let mut radio = { let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); @@ -113,15 +77,7 @@ async fn main(spawner: Spawner) { } }; - // set up for the temperature task - let temperature_irq = interrupt::take!(TEMP); - let temperature = Temp::new(p.TEMP, temperature_irq); - - // set the motion detection pin - let pir_pin = Input::new(p.P0_10.degrade(), Pull::Up); - let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); - let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); start_indicator.set_high(); Timer::after(Duration::from_secs(5)).await; @@ -132,15 +88,15 @@ async fn main(spawner: Spawner) { Err(err) => info!("Sleep unsuccessful = {}", err), } - unwrap!(spawner.spawn(temperature_task(temperature, temperature_publisher))); - unwrap!(spawner.spawn(motion_detection_task(pir_pin, motion_detection_publisher))); + unwrap!(spawner.spawn(temperature_task(temperature_publisher))); + unwrap!(spawner.spawn(motion_detection_task(motion_detection_publisher))); loop { let message = lora_tx_subscriber.next_message_pure().await; let tx_config = TxConfig { // 11 byte maximum payload for Bandwidth 125 and SF 10 - pw: 20, // up to 20 // 5 ??? + pw: 10, // up to 20 rf: RfConfig { frequency: 903900000, // channel in Hz, not MHz bandwidth: Bandwidth::_250KHz, @@ -149,13 +105,13 @@ async fn main(spawner: Spawner) { }, }; - let mut buffer = [TEMPERATURE_UID, 0xffu8, MOTION_UID, 0x00u8]; + let mut buffer = [0x00u8]; match message { - Message::Temperature(temperature) => buffer[1] = temperature as u8, - Message::MotionDetected => buffer[3] = 0x01u8, + Message::Temperature(temperature) => buffer[0] = temperature as u8, + Message::MotionDetected => buffer[0] = 0x01u8, }; - // crypto for text ??? + // unencrypted match radio.tx(tx_config, &buffer).await { Ok(ret_val) => info!("TX ret_val = {}", ret_val), Err(err) => info!("TX error = {}", err), @@ -165,9 +121,5 @@ async fn main(spawner: Spawner) { Ok(()) => info!("Sleep successful"), Err(err) => info!("Sleep unsuccessful = {}", err), } - - debug_indicator.set_high(); - Timer::after(Duration::from_secs(5)).await; - debug_indicator.set_low(); } } From 4da6320e63ad1c93944774c15a9fcc4641fe32ef Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 11 Oct 2022 10:20:31 +0200 Subject: [PATCH 36/46] Add note on partition sizes to bootloader docs See #1007 --- docs/modules/ROOT/pages/bootloader.adoc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc index ae92e9d5d..7dbfeb3eb 100644 --- a/docs/modules/ROOT/pages/bootloader.adoc +++ b/docs/modules/ROOT/pages/bootloader.adoc @@ -25,10 +25,19 @@ image::bootloader_flash.png[Bootloader flash layout] The bootloader divides the storage into 4 main partitions, configurable when creating the bootloader instance or via linker scripts: -* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash. -* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader. -* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. -* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped. +* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash, but if you need to debug it and have space available, increasing this to 24kB will allow you to run the bootloader with probe-rs. +* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader. The size required for this partition depends on the size of your application. +* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. This partition must be at least 1 page bigger than the ACTIVE partition, since the swap algorithm uses the extra space to ensure power safe copy of data: ++ +Partition Size~dfu~= Partition Size~active~+ Page Size~active~ ++ +All values are specified in bytes. + +* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size given by: ++ +Partition Size~state~ = Write Size~state~ + (2 × Partition Size~active~ / Page Size~active~) ++ +All values are specified in bytes. The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes. The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined. From 5846b4ff7d6a3ab177abe1f1070b0b21d5a87f1d Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 12 Oct 2022 10:54:47 +0200 Subject: [PATCH 37/46] Correctly enable RTC_IRQ when scheduling an RTC alarm --- embassy-rp/src/rtc/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index 7f3bbbe73..e4b6f0b1d 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -145,6 +145,8 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { filter.write_setup_1(w); }); + self.inner.regs().inte().modify(|w| w.set_rtc(true)); + // Set the enable bit and check if it is set self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true)); while !self.inner.regs().irq_setup_0().read().match_active() { From a4afab46403aacd7bb555cadaefad7f216fb9931 Mon Sep 17 00:00:00 2001 From: pbert Date: Sun, 10 Jul 2022 20:12:25 +0200 Subject: [PATCH 38/46] add support for pdm microphones in nrf driver --- embassy-nrf/src/chips/nrf52810.rs | 3 + embassy-nrf/src/chips/nrf52811.rs | 3 + embassy-nrf/src/chips/nrf52833.rs | 3 + embassy-nrf/src/chips/nrf52840.rs | 3 + embassy-nrf/src/chips/nrf9160.rs | 3 + embassy-nrf/src/lib.rs | 8 + embassy-nrf/src/pdm.rs | 242 ++++++++++++++++++++++++++++++ examples/nrf/src/bin/pdm.rs | 33 ++++ 8 files changed, 298 insertions(+) create mode 100644 embassy-nrf/src/pdm.rs create mode 100644 examples/nrf/src/bin/pdm.rs diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index faa52d8fb..3e500098c 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -128,6 +128,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index bbdf1cbe5..25c7c0d91 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -128,6 +128,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 39a0f93f9..3b33907d2 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -158,6 +158,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } #[cfg(feature = "nightly")] diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index e3d8f34a1..ae59f8b25 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -161,6 +161,9 @@ embassy_hal_common::peripherals! { // TEMP TEMP, + + // PDM + PDM, } #[cfg(feature = "nightly")] diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index a4be8564e..f8ed11e03 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -260,6 +260,9 @@ embassy_hal_common::peripherals! { P0_29, P0_30, P0_31, + + // PDM + PDM, } impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index d7bd21702..bc70fc2f6 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -76,6 +76,14 @@ pub mod gpio; pub mod gpiote; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; +#[cfg(any( + feature = "nrf52810", + feature = "nrf52811", + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf9160" +))] +pub mod pdm; pub mod ppi; #[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod pwm; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs new file mode 100644 index 000000000..b7c7022cf --- /dev/null +++ b/embassy-nrf/src/pdm.rs @@ -0,0 +1,242 @@ +//! PDM mirophone interface + +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_common::drop::OnDrop; +use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +use futures::future::poll_fn; + +use crate::chip::EASY_DMA_SIZE; +use crate::gpio::sealed::Pin; +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::interrupt::{self, InterruptExt}; +use crate::peripherals::PDM; +use crate::{pac, Peripheral}; + +/// PDM microphone interface +pub struct Pdm<'d> { + irq: PeripheralRef<'d, interrupt::PDM>, + phantom: PhantomData<&'d PDM>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + BufferTooLong, + BufferZeroLength, + NotRunning, +} + +static WAKER: AtomicWaker = AtomicWaker::new(); +static DUMMY_BUFFER: [i16; 1] = [0; 1]; + +impl<'d> Pdm<'d> { + /// Create PDM driver + pub fn new( + pdm: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + clk: impl Peripheral

+ 'd, + din: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(clk, din); + Self::new_inner(pdm, irq, clk.map_into(), din.map_into(), config) + } + + fn new_inner( + _pdm: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + clk: PeripheralRef<'d, AnyPin>, + din: PeripheralRef<'d, AnyPin>, + config: Config, + ) -> Self { + into_ref!(irq); + + let r = Self::regs(); + + // setup gpio pins + din.conf().write(|w| w.input().set_bit()); + r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) }); + clk.set_low(); + clk.conf().write(|w| w.dir().output()); + r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) }); + + // configure + // use default for + // - gain right + // - gain left + // - clk + // - ratio + r.mode.write(|w| { + w.edge().bit(config.edge == Edge::LeftRising); + w.operation().bit(config.operation_mode == OperationMode::Mono); + w + }); + r.gainl.write(|w| w.gainl().default_gain()); + r.gainr.write(|w| w.gainr().default_gain()); + + // IRQ + irq.disable(); + irq.set_handler(|_| { + let r = Self::regs(); + r.intenclr.write(|w| w.end().clear()); + WAKER.wake(); + }); + irq.enable(); + + r.enable.write(|w| w.enable().set_bit()); + + Self { + phantom: PhantomData, + irq, + } + } + + /// Start sampling microphon data into a dummy buffer + /// Usefull to start the microphon and keep it active between recording samples + pub async fn start(&mut self) { + let r = Self::regs(); + + // start dummy sampling because microphon needs some setup time + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + r.tasks_start.write(|w| w.tasks_start().set_bit()); + } + + /// Stop sampling microphon data inta a dummy buffer + pub async fn stop(&mut self) { + let r = Self::regs(); + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + r.events_started.reset(); + } + + pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { + if buffer.len() == 0 { + return Err(Error::BufferZeroLength); + } + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + + let r = Self::regs(); + + if r.events_started.read().events_started().bit_is_clear() { + return Err(Error::NotRunning); + } + + let drop = OnDrop::new(move || { + r.intenclr.write(|w| w.end().clear()); + r.events_stopped.reset(); + + // reset to dummy buffer + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + while r.events_stopped.read().bits() == 0 {} + }); + + // setup user buffer + let ptr = buffer.as_ptr(); + let len = buffer.len(); + r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) }); + r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) }); + + // wait till the current sample is finished and the user buffer sample is started + Self::wait_for_sample().await; + + // reset the buffer back to the dummy buffer + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + // wait till the user buffer is sampled + Self::wait_for_sample().await; + + drop.defuse(); + + Ok(()) + } + + async fn wait_for_sample() { + let r = Self::regs(); + + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + compiler_fence(Ordering::SeqCst); + + poll_fn(|cx| { + WAKER.register(cx.waker()); + if r.events_end.read().events_end().bit_is_set() { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + } + + fn regs() -> &'static pac::pdm::RegisterBlock { + unsafe { &*pac::PDM::ptr() } + } +} + +/// PDM microphone driver Config +pub struct Config { + /// Use stero or mono operation + pub operation_mode: OperationMode, + /// On which edge the left channel should be samples + pub edge: Edge, +} + +impl Default for Config { + fn default() -> Self { + Self { + operation_mode: OperationMode::Mono, + edge: Edge::LeftFalling, + } + } +} + +#[derive(PartialEq)] +pub enum OperationMode { + Mono, + Stereo, +} +#[derive(PartialEq)] +pub enum Edge { + LeftRising, + LeftFalling, +} + +impl<'d> Drop for Pdm<'d> { + fn drop(&mut self) { + let r = Self::regs(); + + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + + self.irq.disable(); + + r.enable.write(|w| w.enable().disabled()); + + r.psel.din.reset(); + r.psel.clk.reset(); + } +} diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf/src/bin/pdm.rs new file mode 100644 index 000000000..7388580fb --- /dev/null +++ b/examples/nrf/src/bin/pdm.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::pdm::{Config, Pdm}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_p: Spawner) { + let p = embassy_nrf::init(Default::default()); + let config = Config::default(); + let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), p.P0_01, p.P0_00, config); + + loop { + pdm.start().await; + + // wait some time till the microphon settled + Timer::after(Duration::from_millis(1000)).await; + + const SAMPLES: usize = 2048; + let mut buf = [0i16; SAMPLES]; + pdm.sample(&mut buf).await.unwrap(); + + info!("samples: {:?}", &buf); + + pdm.stop().await; + Timer::after(Duration::from_millis(100)).await; + } +} From e4c2b2aa9a61e5135d2e129705c9bdc3b4cb73ae Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sun, 16 Oct 2022 18:00:23 -0700 Subject: [PATCH 39/46] rp i2c: have separate wakers for each i2c unit If they both share one waker, there's the possibility that some wakeups could get lost. --- embassy-rp/src/i2c.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 68cfc653e..b9e4382c7 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -70,7 +70,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { } } -static I2C_WAKER: AtomicWaker = AtomicWaker::new(); +static I2C_WAKER: [AtomicWaker; 2] = [AtomicWaker::new(), AtomicWaker::new()]; impl<'d, T: Instance> I2c<'d, T, Async> { pub fn new_async( @@ -109,7 +109,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let r = f(self); if r.is_pending() { - I2C_WAKER.register(cx.waker()); + I2C_WAKER[T::IDX].register(cx.waker()); g(self); } r @@ -122,7 +122,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let i2c = T::regs(); i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); - I2C_WAKER.wake(); + I2C_WAKER[T::IDX].wake(); } async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { @@ -813,6 +813,7 @@ mod sealed { pub trait Instance { const TX_DREQ: u8; const RX_DREQ: u8; + const IDX: usize; type Interrupt: Interrupt; @@ -844,10 +845,11 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { + ($type:ident, $idx:expr, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl sealed::Instance for peripherals::$type { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; + const IDX: usize = $idx; type Interrupt = crate::interrupt::$irq; @@ -867,8 +869,8 @@ macro_rules! impl_instance { }; } -impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); -impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); +impl_instance!(I2C0, 0, I2C0_IRQ, set_i2c0, 32, 33); +impl_instance!(I2C1, 1, I2C1_IRQ, set_i2c1, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} From 02a3cdb507d535908bc0668a31526c05b5d01005 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 17 Oct 2022 21:50:40 -0700 Subject: [PATCH 40/46] Associate state with the instance rather than having a separate array --- embassy-rp/src/i2c.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index b9e4382c7..d6742f6a6 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -70,8 +70,6 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { } } -static I2C_WAKER: [AtomicWaker; 2] = [AtomicWaker::new(), AtomicWaker::new()]; - impl<'d, T: Instance> I2c<'d, T, Async> { pub fn new_async( peri: impl Peripheral

+ 'd, @@ -109,7 +107,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let r = f(self); if r.is_pending() { - I2C_WAKER[T::IDX].register(cx.waker()); + T::waker().register(cx.waker()); g(self); } r @@ -122,7 +120,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let i2c = T::regs(); i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); - I2C_WAKER[T::IDX].wake(); + T::waker().wake(); } async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { @@ -809,16 +807,17 @@ fn i2c_reserved_addr(addr: u16) -> bool { mod sealed { use embassy_cortex_m::interrupt::Interrupt; + use embassy_sync::waitqueue::AtomicWaker; pub trait Instance { const TX_DREQ: u8; const RX_DREQ: u8; - const IDX: usize; type Interrupt: Interrupt; fn regs() -> crate::pac::i2c::I2c; fn reset() -> crate::pac::resets::regs::Peripherals; + fn waker() -> &'static AtomicWaker; } pub trait Mode {} @@ -845,11 +844,10 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $idx:expr, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { + ($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl sealed::Instance for peripherals::$type { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - const IDX: usize = $idx; type Interrupt = crate::interrupt::$irq; @@ -864,13 +862,20 @@ macro_rules! impl_instance { ret.$reset(true); ret } + + #[inline] + fn waker() -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + + &WAKER + } } impl Instance for peripherals::$type {} }; } -impl_instance!(I2C0, 0, I2C0_IRQ, set_i2c0, 32, 33); -impl_instance!(I2C1, 1, I2C1_IRQ, set_i2c1, 34, 35); +impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); +impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} From 6c5d81ada52bfac1592c7be025038a3eea3acc42 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Tue, 18 Oct 2022 22:42:02 +0200 Subject: [PATCH 41/46] Add memory barriers to H7 flash driver to mitigate PGSERR errors The stm32h7xx-hal uses only the ordering barrier, while the CubeMX uses the DSB and ISB instructions, to be on the safe side, both are used here. --- embassy-stm32/src/flash/h7.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 7ce0ac776..3f2129de8 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -39,6 +39,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error w.set_psize(2); // 32 bits at once }); + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst); + let ret = { let mut ret: Result<(), Error> = Ok(()); let mut offset = offset; @@ -64,6 +68,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error bank.cr().write(|w| w.set_pg(false)); + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst); + ret } From 9d2641f2f5f96e85f3c900a69388071f1adddfdc Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Mon, 17 Oct 2022 15:50:46 +0200 Subject: [PATCH 42/46] Enable defmt in embassy-hal-common --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 2610e5687..9194ae788 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -75,7 +75,7 @@ quote = "1.0.15" stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} [features] -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] sdmmc-rs = ["embedded-sdmmc"] net = ["embassy-net" ] memory-x = ["stm32-metapac/memory-x"] From fa495b8e889b6fec2b44f5c59c69ea4fe798f4ec Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 19 Oct 2022 03:36:18 -0500 Subject: [PATCH 43/46] Add missing examples to rust-analyzer linked projects --- .vscode/settings.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5e9e51799..62e5a362f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,10 +20,13 @@ //"embassy-executor/Cargo.toml", //"embassy-sync/Cargo.toml", "examples/nrf/Cargo.toml", + // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", // "examples/stm32f0/Cargo.toml", // "examples/stm32f1/Cargo.toml", + // "examples/stm32f2/Cargo.toml", + // "examples/stm32f3/Cargo.toml", // "examples/stm32f4/Cargo.toml", // "examples/stm32f7/Cargo.toml", // "examples/stm32g0/Cargo.toml", @@ -32,8 +35,11 @@ // "examples/stm32l0/Cargo.toml", // "examples/stm32l1/Cargo.toml", // "examples/stm32l4/Cargo.toml", + // "examples/stm32l5/Cargo.toml", // "examples/stm32u5/Cargo.toml", + // "examples/stm32wb/Cargo.toml", // "examples/stm32wb55/Cargo.toml", + // "examples/stm32wl/Cargo.toml", // "examples/stm32wl55/Cargo.toml", // "examples/wasm/Cargo.toml", ], From de103a5f4fba5e913988928e22301b4ee05e8515 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 20 Oct 2022 16:12:59 +0200 Subject: [PATCH 44/46] Add missing files and features for basic example --- docs/modules/ROOT/examples/basic/Cargo.toml | 5 ++- docs/modules/ROOT/examples/basic/build.rs | 35 +++++++++++++++++++++ docs/modules/ROOT/examples/basic/memory.x | 7 +++++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 docs/modules/ROOT/examples/basic/build.rs create mode 100644 docs/modules/ROOT/examples/basic/memory.x diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index c13f546e2..d9f8a285a 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml @@ -6,14 +6,13 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly"] } +embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } defmt = "0.3" defmt-rtt = "0.3" -cortex-m = "0.7.3" +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" -embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/docs/modules/ROOT/examples/basic/build.rs b/docs/modules/ROOT/examples/basic/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/docs/modules/ROOT/examples/basic/build.rs @@ -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"); +} diff --git a/docs/modules/ROOT/examples/basic/memory.x b/docs/modules/ROOT/examples/basic/memory.x new file mode 100644 index 000000000..9b04edec0 --- /dev/null +++ b/docs/modules/ROOT/examples/basic/memory.x @@ -0,0 +1,7 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} From f45d34ce7c5663da9e3c96ab698e8a84dbafb4ac Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 20 Oct 2022 23:46:02 +0800 Subject: [PATCH 45/46] Fix mistaken EP_IN_WAKERS --- embassy-rp/src/usb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 0a904aab3..8d6aa33b0 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -522,7 +522,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { trace!("wait_enabled IN WAITING"); let index = self.info.addr.index(); poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); + EP_IN_WAKERS[index].register(cx.waker()); let val = unsafe { T::dpram().ep_in_control(self.info.addr.index() - 1).read() }; if val.enable() { Poll::Ready(()) From 866a42f3aeb196e2b0f854a52683ee07cffc4ccd Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 21 Oct 2022 22:02:13 +0800 Subject: [PATCH 46/46] rp usb: wait for accept() completion This ensures that the current response has finished being sent before the subsequent set_address() happens. Otherwise connecting a device is intermittent, can fail depending on timing. --- embassy-rp/src/usb.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 0a904aab3..7a83dcb4a 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -811,8 +811,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { async move { trace!("control: accept"); + let bufcontrol = T::dpram().ep_in_buffer_control(0); unsafe { - let bufcontrol = T::dpram().ep_in_buffer_control(0); bufcontrol.write(|w| { w.set_length(0, 0); w.set_pid(0, true); @@ -826,6 +826,18 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { w.set_available(0, true); }); } + + // wait for completion before returning, needed so + // set_address() doesn't happen early. + poll_fn(|cx| { + EP_IN_WAKERS[0].register(cx.waker()); + if unsafe { bufcontrol.read().available(0) } { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; } }