diff options
| author | Dion Dokter <[email protected]> | 2021-10-11 10:59:21 +0200 |
|---|---|---|
| committer | Dion Dokter <[email protected]> | 2021-10-11 10:59:34 +0200 |
| commit | a8407c8abad163d2b795d7ab068f8b9861c6df2a (patch) | |
| tree | 6d8474c069d3f3c5bfaa5ad16635d3e65fcce847 | |
| parent | 20674f7126c52d5c9a7584f266463b3da8a1a38e (diff) | |
| parent | 9fec833015579cca5f1b398745cac7ad08245f0c (diff) | |
Merge remote-tracking branch 'upstream/master'
25 files changed, 2160 insertions, 166 deletions
| @@ -35,6 +35,8 @@ The `embassy-nrf` crate contains implementations for nRF 52 series SoCs. | |||
| 35 | - `uarte`: UARTE driver implementing `AsyncBufRead` and `AsyncWrite`. | 35 | - `uarte`: UARTE driver implementing `AsyncBufRead` and `AsyncWrite`. |
| 36 | - `qspi`: QSPI driver implementing `Flash`. | 36 | - `qspi`: QSPI driver implementing `Flash`. |
| 37 | - `gpiote`: GPIOTE driver. Allows `await`ing GPIO pin changes. Great for reading buttons or receiving interrupts from external chips. | 37 | - `gpiote`: GPIOTE driver. Allows `await`ing GPIO pin changes. Great for reading buttons or receiving interrupts from external chips. |
| 38 | - `saadc`: SAADC driver. Provides a full implementation of the one-shot sampling for analog channels. | ||
| 39 | |||
| 38 | - `rtc`: RTC driver implementing `Clock` and `Alarm`, for use with `embassy::executor`. | 40 | - `rtc`: RTC driver implementing `Clock` and `Alarm`, for use with `embassy::executor`. |
| 39 | 41 | ||
| 40 | ## Examples | 42 | ## Examples |
diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml new file mode 100644 index 000000000..af0f25d5a --- /dev/null +++ b/embassy-lora/Cargo.toml | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | [package] | ||
| 2 | name = "embassy-lora" | ||
| 3 | version = "0.1.0" | ||
| 4 | authors = ["Ulf Lilleengen <[email protected]>"] | ||
| 5 | edition = "2018" | ||
| 6 | |||
| 7 | [lib] | ||
| 8 | |||
| 9 | [features] | ||
| 10 | sx127x = [] | ||
| 11 | stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] | ||
| 12 | time = [] | ||
| 13 | |||
| 14 | defmt-trace = [] | ||
| 15 | defmt-debug = [] | ||
| 16 | defmt-info = [] | ||
| 17 | defmt-warn = [] | ||
| 18 | defmt-error = [] | ||
| 19 | |||
| 20 | [dependencies] | ||
| 21 | |||
| 22 | defmt = { version = "0.2.3", optional = true } | ||
| 23 | log = { version = "0.4.14", optional = true } | ||
| 24 | |||
| 25 | embassy = { version = "0.1.0", path = "../embassy", default-features = false } | ||
| 26 | embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } | ||
| 27 | embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } | ||
| 28 | futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } | ||
| 29 | embedded-hal = { version = "0.2", features = ["unproven"] } | ||
| 30 | bit_field = { version = "0.10" } | ||
| 31 | |||
| 32 | lorawan-device = { git = "https://github.com/lulf/rust-lorawan.git", rev = "a373d06fa8858d251bc70d5789cebcd9a638ec42", default-features = false, features = ["async"] } | ||
| 33 | lorawan-encoding = { git = "https://github.com/lulf/rust-lorawan.git", rev = "a373d06fa8858d251bc70d5789cebcd9a638ec42", default-features = false } \ No newline at end of file | ||
diff --git a/embassy-lora/src/fmt.rs b/embassy-lora/src/fmt.rs new file mode 100644 index 000000000..066970813 --- /dev/null +++ b/embassy-lora/src/fmt.rs | |||
| @@ -0,0 +1,225 @@ | |||
| 1 | #![macro_use] | ||
| 2 | #![allow(unused_macros)] | ||
| 3 | |||
| 4 | #[cfg(all(feature = "defmt", feature = "log"))] | ||
| 5 | compile_error!("You may not enable both `defmt` and `log` features."); | ||
| 6 | |||
| 7 | macro_rules! assert { | ||
| 8 | ($($x:tt)*) => { | ||
| 9 | { | ||
| 10 | #[cfg(not(feature = "defmt"))] | ||
| 11 | ::core::assert!($($x)*); | ||
| 12 | #[cfg(feature = "defmt")] | ||
| 13 | ::defmt::assert!($($x)*); | ||
| 14 | } | ||
| 15 | }; | ||
| 16 | } | ||
| 17 | |||
| 18 | macro_rules! assert_eq { | ||
| 19 | ($($x:tt)*) => { | ||
| 20 | { | ||
| 21 | #[cfg(not(feature = "defmt"))] | ||
| 22 | ::core::assert_eq!($($x)*); | ||
| 23 | #[cfg(feature = "defmt")] | ||
| 24 | ::defmt::assert_eq!($($x)*); | ||
| 25 | } | ||
| 26 | }; | ||
| 27 | } | ||
| 28 | |||
| 29 | macro_rules! assert_ne { | ||
| 30 | ($($x:tt)*) => { | ||
| 31 | { | ||
| 32 | #[cfg(not(feature = "defmt"))] | ||
| 33 | ::core::assert_ne!($($x)*); | ||
| 34 | #[cfg(feature = "defmt")] | ||
| 35 | ::defmt::assert_ne!($($x)*); | ||
| 36 | } | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | |||
| 40 | macro_rules! debug_assert { | ||
| 41 | ($($x:tt)*) => { | ||
| 42 | { | ||
| 43 | #[cfg(not(feature = "defmt"))] | ||
| 44 | ::core::debug_assert!($($x)*); | ||
| 45 | #[cfg(feature = "defmt")] | ||
| 46 | ::defmt::debug_assert!($($x)*); | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | } | ||
| 50 | |||
| 51 | macro_rules! debug_assert_eq { | ||
| 52 | ($($x:tt)*) => { | ||
| 53 | { | ||
| 54 | #[cfg(not(feature = "defmt"))] | ||
| 55 | ::core::debug_assert_eq!($($x)*); | ||
| 56 | #[cfg(feature = "defmt")] | ||
| 57 | ::defmt::debug_assert_eq!($($x)*); | ||
| 58 | } | ||
| 59 | }; | ||
| 60 | } | ||
| 61 | |||
| 62 | macro_rules! debug_assert_ne { | ||
| 63 | ($($x:tt)*) => { | ||
| 64 | { | ||
| 65 | #[cfg(not(feature = "defmt"))] | ||
| 66 | ::core::debug_assert_ne!($($x)*); | ||
| 67 | #[cfg(feature = "defmt")] | ||
| 68 | ::defmt::debug_assert_ne!($($x)*); | ||
| 69 | } | ||
| 70 | }; | ||
| 71 | } | ||
| 72 | |||
| 73 | macro_rules! todo { | ||
| 74 | ($($x:tt)*) => { | ||
| 75 | { | ||
| 76 | #[cfg(not(feature = "defmt"))] | ||
| 77 | ::core::todo!($($x)*); | ||
| 78 | #[cfg(feature = "defmt")] | ||
| 79 | ::defmt::todo!($($x)*); | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | } | ||
| 83 | |||
| 84 | macro_rules! unreachable { | ||
| 85 | ($($x:tt)*) => { | ||
| 86 | { | ||
| 87 | #[cfg(not(feature = "defmt"))] | ||
| 88 | ::core::unreachable!($($x)*); | ||
| 89 | #[cfg(feature = "defmt")] | ||
| 90 | ::defmt::unreachable!($($x)*); | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | } | ||
| 94 | |||
| 95 | macro_rules! panic { | ||
| 96 | ($($x:tt)*) => { | ||
| 97 | { | ||
| 98 | #[cfg(not(feature = "defmt"))] | ||
| 99 | ::core::panic!($($x)*); | ||
| 100 | #[cfg(feature = "defmt")] | ||
| 101 | ::defmt::panic!($($x)*); | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | } | ||
| 105 | |||
| 106 | macro_rules! trace { | ||
| 107 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 108 | { | ||
| 109 | #[cfg(feature = "log")] | ||
| 110 | ::log::trace!($s $(, $x)*); | ||
| 111 | #[cfg(feature = "defmt")] | ||
| 112 | ::defmt::trace!($s $(, $x)*); | ||
| 113 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 114 | let _ = ($( & $x ),*); | ||
| 115 | } | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | |||
| 119 | macro_rules! debug { | ||
| 120 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 121 | { | ||
| 122 | #[cfg(feature = "log")] | ||
| 123 | ::log::debug!($s $(, $x)*); | ||
| 124 | #[cfg(feature = "defmt")] | ||
| 125 | ::defmt::debug!($s $(, $x)*); | ||
| 126 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 127 | let _ = ($( & $x ),*); | ||
| 128 | } | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | |||
| 132 | macro_rules! info { | ||
| 133 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 134 | { | ||
| 135 | #[cfg(feature = "log")] | ||
| 136 | ::log::info!($s $(, $x)*); | ||
| 137 | #[cfg(feature = "defmt")] | ||
| 138 | ::defmt::info!($s $(, $x)*); | ||
| 139 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 140 | let _ = ($( & $x ),*); | ||
| 141 | } | ||
| 142 | }; | ||
| 143 | } | ||
| 144 | |||
| 145 | macro_rules! warn { | ||
| 146 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 147 | { | ||
| 148 | #[cfg(feature = "log")] | ||
| 149 | ::log::warn!($s $(, $x)*); | ||
| 150 | #[cfg(feature = "defmt")] | ||
| 151 | ::defmt::warn!($s $(, $x)*); | ||
| 152 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 153 | let _ = ($( & $x ),*); | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | } | ||
| 157 | |||
| 158 | macro_rules! error { | ||
| 159 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 160 | { | ||
| 161 | #[cfg(feature = "log")] | ||
| 162 | ::log::error!($s $(, $x)*); | ||
| 163 | #[cfg(feature = "defmt")] | ||
| 164 | ::defmt::error!($s $(, $x)*); | ||
| 165 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 166 | let _ = ($( & $x ),*); | ||
| 167 | } | ||
| 168 | }; | ||
| 169 | } | ||
| 170 | |||
| 171 | #[cfg(feature = "defmt")] | ||
| 172 | macro_rules! unwrap { | ||
| 173 | ($($x:tt)*) => { | ||
| 174 | ::defmt::unwrap!($($x)*) | ||
| 175 | }; | ||
| 176 | } | ||
| 177 | |||
| 178 | #[cfg(not(feature = "defmt"))] | ||
| 179 | macro_rules! unwrap { | ||
| 180 | ($arg:expr) => { | ||
| 181 | match $crate::fmt::Try::into_result($arg) { | ||
| 182 | ::core::result::Result::Ok(t) => t, | ||
| 183 | ::core::result::Result::Err(e) => { | ||
| 184 | ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | }; | ||
| 188 | ($arg:expr, $($msg:expr),+ $(,)? ) => { | ||
| 189 | match $crate::fmt::Try::into_result($arg) { | ||
| 190 | ::core::result::Result::Ok(t) => t, | ||
| 191 | ::core::result::Result::Err(e) => { | ||
| 192 | ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 199 | pub struct NoneError; | ||
| 200 | |||
| 201 | pub trait Try { | ||
| 202 | type Ok; | ||
| 203 | type Error; | ||
| 204 | fn into_result(self) -> Result<Self::Ok, Self::Error>; | ||
| 205 | } | ||
| 206 | |||
| 207 | impl<T> Try for Option<T> { | ||
| 208 | type Ok = T; | ||
| 209 | type Error = NoneError; | ||
| 210 | |||
| 211 | #[inline] | ||
| 212 | fn into_result(self) -> Result<T, NoneError> { | ||
| 213 | self.ok_or(NoneError) | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | impl<T, E> Try for Result<T, E> { | ||
| 218 | type Ok = T; | ||
| 219 | type Error = E; | ||
| 220 | |||
| 221 | #[inline] | ||
| 222 | fn into_result(self) -> Self { | ||
| 223 | self | ||
| 224 | } | ||
| 225 | } | ||
diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs new file mode 100644 index 000000000..b2da22090 --- /dev/null +++ b/embassy-lora/src/lib.rs | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![feature(type_alias_impl_trait)] | ||
| 3 | #![feature(generic_associated_types)] | ||
| 4 | //! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device | ||
| 5 | //! crate's async LoRaWAN MAC implementation. | ||
| 6 | |||
| 7 | pub(crate) mod fmt; | ||
| 8 | |||
| 9 | #[cfg(feature = "stm32wl")] | ||
| 10 | pub mod stm32wl; | ||
| 11 | #[cfg(feature = "sx127x")] | ||
| 12 | pub mod sx127x; | ||
| 13 | |||
| 14 | /// A convenience timer to use with the LoRaWAN crate | ||
| 15 | pub struct LoraTimer; | ||
| 16 | |||
| 17 | #[cfg(feature = "time")] | ||
| 18 | impl lorawan_device::async_device::radio::Timer for LoraTimer { | ||
| 19 | type DelayFuture<'m> = impl core::future::Future<Output = ()> + 'm; | ||
| 20 | fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> { | ||
| 21 | embassy::time::Timer::after(embassy::time::Duration::from_millis(millis)) | ||
| 22 | } | ||
| 23 | } | ||
diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs new file mode 100644 index 000000000..8cac46f7a --- /dev/null +++ b/embassy-lora/src/stm32wl/mod.rs | |||
| @@ -0,0 +1,368 @@ | |||
| 1 | //! A radio driver integration for the radio found on STM32WL family devices. | ||
| 2 | use core::future::Future; | ||
| 3 | use core::mem::MaybeUninit; | ||
| 4 | use embassy::channel::signal::Signal; | ||
| 5 | use embassy::interrupt::InterruptExt; | ||
| 6 | use embassy::util::Unborrow; | ||
| 7 | use embassy_hal_common::unborrow; | ||
| 8 | use embassy_stm32::{ | ||
| 9 | dma::NoDma, | ||
| 10 | gpio::{AnyPin, Output}, | ||
| 11 | interrupt::SUBGHZ_RADIO, | ||
| 12 | subghz::{ | ||
| 13 | CalibrateImage, CfgIrq, CodingRate, HeaderType, Irq, LoRaBandwidth, LoRaModParams, | ||
| 14 | LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PaSel, PacketType, RampTime, RegMode, | ||
| 15 | RfFreq, SpreadingFactor as SF, StandbyClk, Status, SubGhz, TcxoMode, TcxoTrim, Timeout, | ||
| 16 | TxParams, | ||
| 17 | }, | ||
| 18 | }; | ||
| 19 | use embedded_hal::digital::v2::OutputPin; | ||
| 20 | use lorawan_device::async_device::{ | ||
| 21 | radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}, | ||
| 22 | Timings, | ||
| 23 | }; | ||
| 24 | |||
| 25 | #[derive(Debug, Copy, Clone)] | ||
| 26 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 27 | pub enum State { | ||
| 28 | Idle, | ||
| 29 | Txing, | ||
| 30 | Rxing, | ||
| 31 | } | ||
| 32 | |||
| 33 | #[derive(Debug, Copy, Clone)] | ||
| 34 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 35 | pub struct RadioError; | ||
| 36 | |||
| 37 | static IRQ: Signal<(Status, u16)> = Signal::new(); | ||
| 38 | |||
| 39 | struct StateInner<'a> { | ||
| 40 | radio: SubGhz<'a, NoDma, NoDma>, | ||
| 41 | switch: RadioSwitch<'a>, | ||
| 42 | } | ||
| 43 | |||
| 44 | /// External state storage for the radio state | ||
| 45 | pub struct SubGhzState<'a>(MaybeUninit<StateInner<'a>>); | ||
| 46 | impl<'a> SubGhzState<'a> { | ||
| 47 | pub const fn new() -> Self { | ||
| 48 | Self(MaybeUninit::uninit()) | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | /// The radio peripheral keeping the radio state and owning the radio IRQ. | ||
| 53 | pub struct SubGhzRadio<'a> { | ||
| 54 | state: *mut StateInner<'a>, | ||
| 55 | _irq: SUBGHZ_RADIO, | ||
| 56 | } | ||
| 57 | |||
| 58 | impl<'a> SubGhzRadio<'a> { | ||
| 59 | /// Create a new instance of a SubGhz radio for LoRaWAN. | ||
| 60 | /// | ||
| 61 | /// # Safety | ||
| 62 | /// Do not leak self or futures | ||
| 63 | pub unsafe fn new( | ||
| 64 | state: &'a mut SubGhzState<'a>, | ||
| 65 | radio: SubGhz<'a, NoDma, NoDma>, | ||
| 66 | switch: RadioSwitch<'a>, | ||
| 67 | irq: impl Unborrow<Target = SUBGHZ_RADIO>, | ||
| 68 | ) -> Self { | ||
| 69 | unborrow!(irq); | ||
| 70 | |||
| 71 | let mut inner = StateInner { radio, switch }; | ||
| 72 | inner.radio.reset(); | ||
| 73 | |||
| 74 | let state_ptr = state.0.as_mut_ptr(); | ||
| 75 | state_ptr.write(inner); | ||
| 76 | |||
| 77 | irq.disable(); | ||
| 78 | irq.set_handler(|p| { | ||
| 79 | // This is safe because we only get interrupts when configured for, so | ||
| 80 | // the radio will be awaiting on the signal at this point. If not, the ISR will | ||
| 81 | // anyway only adjust the state in the IRQ signal state. | ||
| 82 | let state = unsafe { &mut *(p as *mut StateInner<'a>) }; | ||
| 83 | state.on_interrupt(); | ||
| 84 | }); | ||
| 85 | irq.set_handler_context(state_ptr as *mut ()); | ||
| 86 | irq.enable(); | ||
| 87 | |||
| 88 | Self { | ||
| 89 | state: state_ptr, | ||
| 90 | _irq: irq, | ||
| 91 | } | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | impl<'a> StateInner<'a> { | ||
| 96 | /// Configure radio settings in preparation for TX or RX | ||
| 97 | pub(crate) fn configure(&mut self) -> Result<(), RadioError> { | ||
| 98 | trace!("Configuring STM32WL SUBGHZ radio"); | ||
| 99 | self.radio.set_standby(StandbyClk::Rc)?; | ||
| 100 | let tcxo_mode = TcxoMode::new() | ||
| 101 | .set_txco_trim(TcxoTrim::Volts1pt7) | ||
| 102 | .set_timeout(Timeout::from_duration_sat( | ||
| 103 | core::time::Duration::from_millis(40), | ||
| 104 | )); | ||
| 105 | |||
| 106 | self.radio.set_tcxo_mode(&tcxo_mode)?; | ||
| 107 | self.radio.set_regulator_mode(RegMode::Ldo)?; | ||
| 108 | |||
| 109 | self.radio.calibrate_image(CalibrateImage::ISM_863_870)?; | ||
| 110 | |||
| 111 | self.radio.set_buffer_base_address(0, 0)?; | ||
| 112 | |||
| 113 | self.radio.set_pa_config( | ||
| 114 | &PaConfig::new() | ||
| 115 | .set_pa_duty_cycle(0x1) | ||
| 116 | .set_hp_max(0x0) | ||
| 117 | .set_pa(PaSel::Lp), | ||
| 118 | )?; | ||
| 119 | |||
| 120 | self.radio.set_pa_ocp(Ocp::Max140m)?; | ||
| 121 | |||
| 122 | // let tx_params = TxParams::LP_14.set_ramp_time(RampTime::Micros40); | ||
| 123 | self.radio.set_tx_params( | ||
| 124 | &TxParams::new() | ||
| 125 | .set_ramp_time(RampTime::Micros40) | ||
| 126 | .set_power(0x0A), | ||
| 127 | )?; | ||
| 128 | |||
| 129 | self.radio.set_packet_type(PacketType::LoRa)?; | ||
| 130 | self.radio.set_lora_sync_word(LoRaSyncWord::Public)?; | ||
| 131 | trace!("Done initializing STM32WL SUBGHZ radio"); | ||
| 132 | Ok(()) | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form | ||
| 136 | /// the upcoming RX window start. | ||
| 137 | async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, RadioError> { | ||
| 138 | //trace!("TX Request: {}", config); | ||
| 139 | trace!("TX START"); | ||
| 140 | self.switch.set_tx_lp(); | ||
| 141 | self.configure()?; | ||
| 142 | |||
| 143 | self.radio | ||
| 144 | .set_rf_frequency(&RfFreq::from_frequency(config.rf.frequency))?; | ||
| 145 | |||
| 146 | let mod_params = LoRaModParams::new() | ||
| 147 | .set_sf(convert_spreading_factor(config.rf.spreading_factor)) | ||
| 148 | .set_bw(convert_bandwidth(config.rf.bandwidth)) | ||
| 149 | .set_cr(CodingRate::Cr45) | ||
| 150 | .set_ldro_en(true); | ||
| 151 | self.radio.set_lora_mod_params(&mod_params)?; | ||
| 152 | |||
| 153 | let packet_params = LoRaPacketParams::new() | ||
| 154 | .set_preamble_len(8) | ||
| 155 | .set_header_type(HeaderType::Variable) | ||
| 156 | .set_payload_len(buf.len() as u8) | ||
| 157 | .set_crc_en(true) | ||
| 158 | .set_invert_iq(false); | ||
| 159 | |||
| 160 | self.radio.set_lora_packet_params(&packet_params)?; | ||
| 161 | |||
| 162 | let irq_cfg = CfgIrq::new() | ||
| 163 | .irq_enable_all(Irq::TxDone) | ||
| 164 | .irq_enable_all(Irq::RxDone) | ||
| 165 | .irq_enable_all(Irq::Timeout); | ||
| 166 | self.radio.set_irq_cfg(&irq_cfg)?; | ||
| 167 | |||
| 168 | self.radio.set_buffer_base_address(0, 0)?; | ||
| 169 | self.radio.write_buffer(0, buf)?; | ||
| 170 | |||
| 171 | self.radio.set_tx(Timeout::DISABLED)?; | ||
| 172 | |||
| 173 | loop { | ||
| 174 | let (_status, irq_status) = IRQ.wait().await; | ||
| 175 | IRQ.reset(); | ||
| 176 | |||
| 177 | if irq_status & Irq::TxDone.mask() != 0 { | ||
| 178 | let stats = self.radio.lora_stats()?; | ||
| 179 | let (status, error_mask) = self.radio.op_error()?; | ||
| 180 | trace!( | ||
| 181 | "TX done. Stats: {:?}. OP error: {:?}, mask {:?}", | ||
| 182 | stats, | ||
| 183 | status, | ||
| 184 | error_mask | ||
| 185 | ); | ||
| 186 | |||
| 187 | return Ok(0); | ||
| 188 | } else if irq_status & Irq::Timeout.mask() != 0 { | ||
| 189 | trace!("TX timeout"); | ||
| 190 | return Err(RadioError); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | /// Perform a radio receive operation with the radio config and receive buffer. The receive buffer must | ||
| 196 | /// be able to hold a single LoRaWAN packet. | ||
| 197 | async fn do_rx( | ||
| 198 | &mut self, | ||
| 199 | config: RfConfig, | ||
| 200 | buf: &mut [u8], | ||
| 201 | ) -> Result<(usize, RxQuality), RadioError> { | ||
| 202 | assert!(buf.len() >= 255); | ||
| 203 | trace!("RX START"); | ||
| 204 | // trace!("Starting RX: {}", config); | ||
| 205 | self.switch.set_rx(); | ||
| 206 | self.configure()?; | ||
| 207 | |||
| 208 | self.radio | ||
| 209 | .set_rf_frequency(&RfFreq::from_frequency(config.frequency))?; | ||
| 210 | |||
| 211 | let mod_params = LoRaModParams::new() | ||
| 212 | .set_sf(convert_spreading_factor(config.spreading_factor)) | ||
| 213 | .set_bw(convert_bandwidth(config.bandwidth)) | ||
| 214 | .set_cr(CodingRate::Cr45) | ||
| 215 | .set_ldro_en(true); | ||
| 216 | self.radio.set_lora_mod_params(&mod_params)?; | ||
| 217 | |||
| 218 | let packet_params = LoRaPacketParams::new() | ||
| 219 | .set_preamble_len(8) | ||
| 220 | .set_header_type(HeaderType::Variable) | ||
| 221 | .set_payload_len(0xFF) | ||
| 222 | .set_crc_en(true) | ||
| 223 | .set_invert_iq(true); | ||
| 224 | self.radio.set_lora_packet_params(&packet_params)?; | ||
| 225 | |||
| 226 | let irq_cfg = CfgIrq::new() | ||
| 227 | .irq_enable_all(Irq::RxDone) | ||
| 228 | .irq_enable_all(Irq::PreambleDetected) | ||
| 229 | .irq_enable_all(Irq::HeaderErr) | ||
| 230 | .irq_enable_all(Irq::Timeout) | ||
| 231 | .irq_enable_all(Irq::Err); | ||
| 232 | self.radio.set_irq_cfg(&irq_cfg)?; | ||
| 233 | |||
| 234 | self.radio.set_rx(Timeout::DISABLED)?; | ||
| 235 | trace!("RX started"); | ||
| 236 | |||
| 237 | loop { | ||
| 238 | let (status, irq_status) = IRQ.wait().await; | ||
| 239 | IRQ.reset(); | ||
| 240 | trace!("RX IRQ {:?}, {:?}", status, irq_status); | ||
| 241 | if irq_status & Irq::RxDone.mask() != 0 { | ||
| 242 | let (status, len, ptr) = self.radio.rx_buffer_status()?; | ||
| 243 | |||
| 244 | let packet_status = self.radio.lora_packet_status()?; | ||
| 245 | let rssi = packet_status.rssi_pkt().to_integer(); | ||
| 246 | let snr = packet_status.snr_pkt().to_integer(); | ||
| 247 | trace!( | ||
| 248 | "RX done. Received {} bytes. RX status: {:?}. Pkt status: {:?}", | ||
| 249 | len, | ||
| 250 | status.cmd(), | ||
| 251 | packet_status, | ||
| 252 | ); | ||
| 253 | self.radio.read_buffer(ptr, &mut buf[..len as usize])?; | ||
| 254 | self.radio.set_standby(StandbyClk::Rc)?; | ||
| 255 | return Ok((len as usize, RxQuality::new(rssi, snr as i8))); | ||
| 256 | } else if irq_status & (Irq::Timeout.mask() | Irq::TxDone.mask()) != 0 { | ||
| 257 | return Err(RadioError); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | /// Read interrupt status and store in global signal | ||
| 263 | fn on_interrupt(&mut self) { | ||
| 264 | let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); | ||
| 265 | self.radio | ||
| 266 | .clear_irq_status(irq_status) | ||
| 267 | .expect("error clearing irq status"); | ||
| 268 | if irq_status & Irq::PreambleDetected.mask() != 0 { | ||
| 269 | trace!("Preamble detected, ignoring"); | ||
| 270 | } else { | ||
| 271 | IRQ.signal((status, irq_status)); | ||
| 272 | } | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | impl PhyRxTx for SubGhzRadio<'static> { | ||
| 277 | type PhyError = RadioError; | ||
| 278 | |||
| 279 | type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm; | ||
| 280 | fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { | ||
| 281 | async move { | ||
| 282 | let inner = unsafe { &mut *self.state }; | ||
| 283 | inner.do_tx(config, buf).await | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm; | ||
| 288 | fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { | ||
| 289 | async move { | ||
| 290 | let inner = unsafe { &mut *self.state }; | ||
| 291 | inner.do_rx(config, buf).await | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | impl<'a> From<embassy_stm32::spi::Error> for RadioError { | ||
| 297 | fn from(_: embassy_stm32::spi::Error) -> Self { | ||
| 298 | RadioError | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | impl<'a> Timings for SubGhzRadio<'a> { | ||
| 303 | fn get_rx_window_offset_ms(&self) -> i32 { | ||
| 304 | -200 | ||
| 305 | } | ||
| 306 | fn get_rx_window_duration_ms(&self) -> u32 { | ||
| 307 | 800 | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | /// Represents the radio switch found on STM32WL based boards, used to control the radio for transmission or reception. | ||
| 312 | pub struct RadioSwitch<'a> { | ||
| 313 | ctrl1: Output<'a, AnyPin>, | ||
| 314 | ctrl2: Output<'a, AnyPin>, | ||
| 315 | ctrl3: Output<'a, AnyPin>, | ||
| 316 | } | ||
| 317 | |||
| 318 | impl<'a> RadioSwitch<'a> { | ||
| 319 | pub fn new( | ||
| 320 | ctrl1: Output<'a, AnyPin>, | ||
| 321 | ctrl2: Output<'a, AnyPin>, | ||
| 322 | ctrl3: Output<'a, AnyPin>, | ||
| 323 | ) -> Self { | ||
| 324 | Self { | ||
| 325 | ctrl1, | ||
| 326 | ctrl2, | ||
| 327 | ctrl3, | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | pub(crate) fn set_rx(&mut self) { | ||
| 332 | self.ctrl1.set_high().unwrap(); | ||
| 333 | self.ctrl2.set_low().unwrap(); | ||
| 334 | self.ctrl3.set_high().unwrap(); | ||
| 335 | } | ||
| 336 | |||
| 337 | pub(crate) fn set_tx_lp(&mut self) { | ||
| 338 | self.ctrl1.set_high().unwrap(); | ||
| 339 | self.ctrl2.set_high().unwrap(); | ||
| 340 | self.ctrl3.set_high().unwrap(); | ||
| 341 | } | ||
| 342 | |||
| 343 | #[allow(dead_code)] | ||
| 344 | pub(crate) fn set_tx_hp(&mut self) { | ||
| 345 | self.ctrl2.set_high().unwrap(); | ||
| 346 | self.ctrl1.set_low().unwrap(); | ||
| 347 | self.ctrl3.set_high().unwrap(); | ||
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 351 | fn convert_spreading_factor(sf: SpreadingFactor) -> SF { | ||
| 352 | match sf { | ||
| 353 | SpreadingFactor::_7 => SF::Sf7, | ||
| 354 | SpreadingFactor::_8 => SF::Sf8, | ||
| 355 | SpreadingFactor::_9 => SF::Sf9, | ||
| 356 | SpreadingFactor::_10 => SF::Sf10, | ||
| 357 | SpreadingFactor::_11 => SF::Sf11, | ||
| 358 | SpreadingFactor::_12 => SF::Sf12, | ||
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 362 | fn convert_bandwidth(bw: Bandwidth) -> LoRaBandwidth { | ||
| 363 | match bw { | ||
| 364 | Bandwidth::_125KHz => LoRaBandwidth::Bw125, | ||
| 365 | Bandwidth::_250KHz => LoRaBandwidth::Bw250, | ||
| 366 | Bandwidth::_500KHz => LoRaBandwidth::Bw500, | ||
| 367 | } | ||
| 368 | } | ||
diff --git a/embassy-lora/src/sx127x/mod.rs b/embassy-lora/src/sx127x/mod.rs new file mode 100644 index 000000000..a9736b85f --- /dev/null +++ b/embassy-lora/src/sx127x/mod.rs | |||
| @@ -0,0 +1,201 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use embassy::traits::gpio::WaitForRisingEdge; | ||
| 3 | use embedded_hal::blocking::delay::DelayMs; | ||
| 4 | use embedded_hal::blocking::spi::{Transfer, Write}; | ||
| 5 | use embedded_hal::digital::v2::OutputPin; | ||
| 6 | use lorawan_device::async_device::{ | ||
| 7 | radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}, | ||
| 8 | Timings, | ||
| 9 | }; | ||
| 10 | |||
| 11 | mod sx127x_lora; | ||
| 12 | use sx127x_lora::{Error as RadioError, LoRa, RadioMode, IRQ}; | ||
| 13 | |||
| 14 | /// Trait representing a radio switch for boards using the Sx127x radio. One some | ||
| 15 | /// boards, this will be a dummy implementation that does nothing. | ||
| 16 | pub trait RadioSwitch { | ||
| 17 | fn set_tx(&mut self); | ||
| 18 | fn set_rx(&mut self); | ||
| 19 | } | ||
| 20 | |||
| 21 | /// Semtech Sx127x radio peripheral | ||
| 22 | pub struct Sx127xRadio<SPI, CS, RESET, E, I, RFS> | ||
| 23 | where | ||
| 24 | SPI: Transfer<u8, Error = E> + Write<u8, Error = E> + 'static, | ||
| 25 | E: 'static, | ||
| 26 | CS: OutputPin + 'static, | ||
| 27 | RESET: OutputPin + 'static, | ||
| 28 | I: WaitForRisingEdge + 'static, | ||
| 29 | RFS: RadioSwitch + 'static, | ||
| 30 | { | ||
| 31 | radio: LoRa<SPI, CS, RESET>, | ||
| 32 | rfs: RFS, | ||
| 33 | irq: I, | ||
| 34 | } | ||
| 35 | |||
| 36 | #[derive(Debug, Copy, Clone)] | ||
| 37 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 38 | pub enum State { | ||
| 39 | Idle, | ||
| 40 | Txing, | ||
| 41 | Rxing, | ||
| 42 | } | ||
| 43 | |||
| 44 | impl<SPI, CS, RESET, E, I, RFS> Sx127xRadio<SPI, CS, RESET, E, I, RFS> | ||
| 45 | where | ||
| 46 | SPI: Transfer<u8, Error = E> + Write<u8, Error = E> + 'static, | ||
| 47 | CS: OutputPin + 'static, | ||
| 48 | RESET: OutputPin + 'static, | ||
| 49 | I: WaitForRisingEdge + 'static, | ||
| 50 | RFS: RadioSwitch + 'static, | ||
| 51 | { | ||
| 52 | pub fn new<D: DelayMs<u32>>( | ||
| 53 | spi: SPI, | ||
| 54 | cs: CS, | ||
| 55 | reset: RESET, | ||
| 56 | irq: I, | ||
| 57 | rfs: RFS, | ||
| 58 | d: &mut D, | ||
| 59 | ) -> Result<Self, RadioError<E, CS::Error, RESET::Error>> { | ||
| 60 | let mut radio = LoRa::new(spi, cs, reset); | ||
| 61 | radio.reset(d)?; | ||
| 62 | Ok(Self { radio, irq, rfs }) | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | impl<SPI, CS, RESET, E, I, RFS> Timings for Sx127xRadio<SPI, CS, RESET, E, I, RFS> | ||
| 67 | where | ||
| 68 | SPI: Transfer<u8, Error = E> + Write<u8, Error = E> + 'static, | ||
| 69 | CS: OutputPin + 'static, | ||
| 70 | RESET: OutputPin + 'static, | ||
| 71 | I: WaitForRisingEdge + 'static, | ||
| 72 | RFS: RadioSwitch + 'static, | ||
| 73 | { | ||
| 74 | fn get_rx_window_offset_ms(&self) -> i32 { | ||
| 75 | -500 | ||
| 76 | } | ||
| 77 | fn get_rx_window_duration_ms(&self) -> u32 { | ||
| 78 | 800 | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | impl<SPI, CS, RESET, E, I, RFS> PhyRxTx for Sx127xRadio<SPI, CS, RESET, E, I, RFS> | ||
| 83 | where | ||
| 84 | SPI: Transfer<u8, Error = E> + Write<u8, Error = E> + 'static, | ||
| 85 | CS: OutputPin + 'static, | ||
| 86 | E: 'static, | ||
| 87 | RESET: OutputPin + 'static, | ||
| 88 | I: WaitForRisingEdge + 'static, | ||
| 89 | RFS: RadioSwitch + 'static, | ||
| 90 | { | ||
| 91 | type PhyError = Sx127xError; | ||
| 92 | |||
| 93 | #[rustfmt::skip] | ||
| 94 | type TxFuture<'m> where SPI: 'm, CS: 'm, RESET: 'm, E: 'm, I: 'm, RFS: 'm = impl Future<Output = Result<u32, Self::PhyError>> + 'm; | ||
| 95 | |||
| 96 | fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { | ||
| 97 | trace!("TX START"); | ||
| 98 | async move { | ||
| 99 | self.rfs.set_tx(); | ||
| 100 | self.radio.set_tx_power(14, 0)?; | ||
| 101 | self.radio.set_frequency(config.rf.frequency)?; | ||
| 102 | // TODO: Modify radio to support other coding rates | ||
| 103 | self.radio.set_coding_rate_4(5)?; | ||
| 104 | self.radio | ||
| 105 | .set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth))?; | ||
| 106 | self.radio | ||
| 107 | .set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor))?; | ||
| 108 | |||
| 109 | self.radio.set_preamble_length(8)?; | ||
| 110 | self.radio.set_lora_pa_ramp()?; | ||
| 111 | self.radio.set_lora_sync_word()?; | ||
| 112 | self.radio.set_invert_iq(false)?; | ||
| 113 | self.radio.set_crc(true)?; | ||
| 114 | |||
| 115 | self.radio.set_dio0_tx_done()?; | ||
| 116 | self.radio.transmit_payload(buf)?; | ||
| 117 | |||
| 118 | loop { | ||
| 119 | self.irq.wait_for_rising_edge().await; | ||
| 120 | self.radio.set_mode(RadioMode::Stdby).ok().unwrap(); | ||
| 121 | let irq = self.radio.clear_irq().ok().unwrap(); | ||
| 122 | if (irq & IRQ::IrqTxDoneMask.addr()) != 0 { | ||
| 123 | trace!("TX DONE"); | ||
| 124 | return Ok(0); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | #[rustfmt::skip] | ||
| 131 | type RxFuture<'m> where SPI: 'm, CS: 'm, RESET: 'm, E: 'm, I: 'm, RFS: 'm = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm; | ||
| 132 | |||
| 133 | fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { | ||
| 134 | trace!("RX START"); | ||
| 135 | async move { | ||
| 136 | self.rfs.set_rx(); | ||
| 137 | self.radio.reset_payload_length()?; | ||
| 138 | self.radio.set_frequency(config.frequency)?; | ||
| 139 | // TODO: Modify radio to support other coding rates | ||
| 140 | self.radio.set_coding_rate_4(5)?; | ||
| 141 | self.radio | ||
| 142 | .set_signal_bandwidth(bandwidth_to_i64(config.bandwidth))?; | ||
| 143 | self.radio | ||
| 144 | .set_spreading_factor(spreading_factor_to_u8(config.spreading_factor))?; | ||
| 145 | |||
| 146 | self.radio.set_preamble_length(8)?; | ||
| 147 | self.radio.set_lora_sync_word()?; | ||
| 148 | self.radio.set_invert_iq(true)?; | ||
| 149 | self.radio.set_crc(true)?; | ||
| 150 | |||
| 151 | self.radio.set_dio0_rx_done()?; | ||
| 152 | self.radio.set_mode(RadioMode::RxContinuous)?; | ||
| 153 | |||
| 154 | loop { | ||
| 155 | self.irq.wait_for_rising_edge().await; | ||
| 156 | self.radio.set_mode(RadioMode::Stdby).ok().unwrap(); | ||
| 157 | let irq = self.radio.clear_irq().ok().unwrap(); | ||
| 158 | if (irq & IRQ::IrqRxDoneMask.addr()) != 0 { | ||
| 159 | let rssi = self.radio.get_packet_rssi().unwrap_or(0) as i16; | ||
| 160 | let snr = self.radio.get_packet_snr().unwrap_or(0.0) as i8; | ||
| 161 | let response = if let Ok(size) = self.radio.read_packet_size() { | ||
| 162 | self.radio.read_packet(buf)?; | ||
| 163 | Ok((size, RxQuality::new(rssi, snr))) | ||
| 164 | } else { | ||
| 165 | Ok((0, RxQuality::new(rssi, snr))) | ||
| 166 | }; | ||
| 167 | trace!("RX DONE"); | ||
| 168 | return response; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 176 | pub struct Sx127xError; | ||
| 177 | |||
| 178 | impl<A, B, C> From<sx127x_lora::Error<A, B, C>> for Sx127xError { | ||
| 179 | fn from(_: sx127x_lora::Error<A, B, C>) -> Self { | ||
| 180 | Sx127xError | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | fn spreading_factor_to_u8(sf: SpreadingFactor) -> u8 { | ||
| 185 | match sf { | ||
| 186 | SpreadingFactor::_7 => 7, | ||
| 187 | SpreadingFactor::_8 => 8, | ||
| 188 | SpreadingFactor::_9 => 9, | ||
| 189 | SpreadingFactor::_10 => 10, | ||
| 190 | SpreadingFactor::_11 => 11, | ||
| 191 | SpreadingFactor::_12 => 12, | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | fn bandwidth_to_i64(bw: Bandwidth) -> i64 { | ||
| 196 | match bw { | ||
| 197 | Bandwidth::_125KHz => 125_000, | ||
| 198 | Bandwidth::_250KHz => 250_000, | ||
| 199 | Bandwidth::_500KHz => 500_000, | ||
| 200 | } | ||
| 201 | } | ||
diff --git a/embassy-lora/src/sx127x/sx127x_lora/mod.rs b/embassy-lora/src/sx127x/sx127x_lora/mod.rs new file mode 100644 index 000000000..c541815fe --- /dev/null +++ b/embassy-lora/src/sx127x/sx127x_lora/mod.rs | |||
| @@ -0,0 +1,593 @@ | |||
| 1 | // Copyright Charles Wade (https://github.com/mr-glt/sx127x_lora). Licensed under the Apache 2.0 | ||
| 2 | // license | ||
| 3 | // | ||
| 4 | // Modifications made to make the driver work with the rust-lorawan link layer. | ||
| 5 | |||
| 6 | #![allow(dead_code)] | ||
| 7 | |||
| 8 | use bit_field::BitField; | ||
| 9 | use embedded_hal::blocking::{ | ||
| 10 | delay::DelayMs, | ||
| 11 | spi::{Transfer, Write}, | ||
| 12 | }; | ||
| 13 | use embedded_hal::digital::v2::OutputPin; | ||
| 14 | use embedded_hal::spi::{Mode, Phase, Polarity}; | ||
| 15 | |||
| 16 | mod register; | ||
| 17 | use self::register::PaConfig; | ||
| 18 | use self::register::Register; | ||
| 19 | pub use self::register::IRQ; | ||
| 20 | |||
| 21 | /// Provides the necessary SPI mode configuration for the radio | ||
| 22 | pub const MODE: Mode = Mode { | ||
| 23 | phase: Phase::CaptureOnSecondTransition, | ||
| 24 | polarity: Polarity::IdleHigh, | ||
| 25 | }; | ||
| 26 | |||
| 27 | /// Provides high-level access to Semtech SX1276/77/78/79 based boards connected to a Raspberry Pi | ||
| 28 | pub struct LoRa<SPI, CS, RESET> { | ||
| 29 | spi: SPI, | ||
| 30 | cs: CS, | ||
| 31 | reset: RESET, | ||
| 32 | pub explicit_header: bool, | ||
| 33 | pub mode: RadioMode, | ||
| 34 | } | ||
| 35 | |||
| 36 | #[allow(clippy::upper_case_acronyms)] | ||
| 37 | #[derive(Debug)] | ||
| 38 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 39 | pub enum Error<SPI, CS, RESET> { | ||
| 40 | Uninformative, | ||
| 41 | VersionMismatch(u8), | ||
| 42 | CS(CS), | ||
| 43 | Reset(RESET), | ||
| 44 | SPI(SPI), | ||
| 45 | Transmitting, | ||
| 46 | } | ||
| 47 | |||
| 48 | use super::sx127x_lora::register::{FskDataModulationShaping, FskRampUpRamDown}; | ||
| 49 | use Error::*; | ||
| 50 | |||
| 51 | #[cfg(not(feature = "version_0x09"))] | ||
| 52 | const VERSION_CHECK: u8 = 0x12; | ||
| 53 | |||
| 54 | #[cfg(feature = "version_0x09")] | ||
| 55 | const VERSION_CHECK: u8 = 0x09; | ||
| 56 | |||
| 57 | impl<SPI, CS, RESET, E> LoRa<SPI, CS, RESET> | ||
| 58 | where | ||
| 59 | SPI: Transfer<u8, Error = E> + Write<u8, Error = E>, | ||
| 60 | CS: OutputPin, | ||
| 61 | RESET: OutputPin, | ||
| 62 | { | ||
| 63 | /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time. | ||
| 64 | /// This also preforms a hardware reset of the module and then puts it in standby. | ||
| 65 | pub fn new(spi: SPI, cs: CS, reset: RESET) -> Self { | ||
| 66 | Self { | ||
| 67 | spi, | ||
| 68 | cs, | ||
| 69 | reset, | ||
| 70 | explicit_header: true, | ||
| 71 | mode: RadioMode::Sleep, | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | pub fn reset<D: DelayMs<u32>>( | ||
| 76 | &mut self, | ||
| 77 | d: &mut D, | ||
| 78 | ) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 79 | self.reset.set_low().map_err(Reset)?; | ||
| 80 | d.delay_ms(10_u32); | ||
| 81 | self.reset.set_high().map_err(Reset)?; | ||
| 82 | d.delay_ms(10_u32); | ||
| 83 | let version = self.read_register(Register::RegVersion.addr())?; | ||
| 84 | if version == VERSION_CHECK { | ||
| 85 | self.set_mode(RadioMode::Sleep)?; | ||
| 86 | self.write_register(Register::RegFifoTxBaseAddr.addr(), 0)?; | ||
| 87 | self.write_register(Register::RegFifoRxBaseAddr.addr(), 0)?; | ||
| 88 | let lna = self.read_register(Register::RegLna.addr())?; | ||
| 89 | self.write_register(Register::RegLna.addr(), lna | 0x03)?; | ||
| 90 | self.write_register(Register::RegModemConfig3.addr(), 0x04)?; | ||
| 91 | self.set_tcxo(true)?; | ||
| 92 | self.set_mode(RadioMode::Stdby)?; | ||
| 93 | self.cs.set_high().map_err(CS)?; | ||
| 94 | Ok(()) | ||
| 95 | } else { | ||
| 96 | Err(Error::VersionMismatch(version)) | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | /// Transmits up to 255 bytes of data. To avoid the use of an allocator, this takes a fixed 255 u8 | ||
| 101 | /// array and a payload size and returns the number of bytes sent if successful. | ||
| 102 | pub fn transmit_payload_busy( | ||
| 103 | &mut self, | ||
| 104 | buffer: [u8; 255], | ||
| 105 | payload_size: usize, | ||
| 106 | ) -> Result<usize, Error<E, CS::Error, RESET::Error>> { | ||
| 107 | if self.transmitting()? { | ||
| 108 | Err(Transmitting) | ||
| 109 | } else { | ||
| 110 | self.set_mode(RadioMode::Stdby)?; | ||
| 111 | if self.explicit_header { | ||
| 112 | self.set_explicit_header_mode()?; | ||
| 113 | } else { | ||
| 114 | self.set_implicit_header_mode()?; | ||
| 115 | } | ||
| 116 | |||
| 117 | self.write_register(Register::RegIrqFlags.addr(), 0)?; | ||
| 118 | self.write_register(Register::RegFifoAddrPtr.addr(), 0)?; | ||
| 119 | self.write_register(Register::RegPayloadLength.addr(), 0)?; | ||
| 120 | for byte in buffer.iter().take(payload_size) { | ||
| 121 | self.write_register(Register::RegFifo.addr(), *byte)?; | ||
| 122 | } | ||
| 123 | self.write_register(Register::RegPayloadLength.addr(), payload_size as u8)?; | ||
| 124 | self.set_mode(RadioMode::Tx)?; | ||
| 125 | while self.transmitting()? {} | ||
| 126 | Ok(payload_size) | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | pub fn set_dio0_tx_done(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 131 | self.write_register(Register::RegIrqFlagsMask.addr(), 0b1111_0111)?; | ||
| 132 | let mapping = self.read_register(Register::RegDioMapping1.addr())?; | ||
| 133 | self.write_register(Register::RegDioMapping1.addr(), (mapping & 0x3F) | 0x40) | ||
| 134 | } | ||
| 135 | |||
| 136 | pub fn set_dio0_rx_done(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 137 | self.write_register(Register::RegIrqFlagsMask.addr(), 0b0001_1111)?; | ||
| 138 | let mapping = self.read_register(Register::RegDioMapping1.addr())?; | ||
| 139 | self.write_register(Register::RegDioMapping1.addr(), mapping & 0x3F) | ||
| 140 | } | ||
| 141 | |||
| 142 | pub fn transmit_payload( | ||
| 143 | &mut self, | ||
| 144 | buffer: &[u8], | ||
| 145 | ) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 146 | assert!(buffer.len() < 255); | ||
| 147 | if self.transmitting()? { | ||
| 148 | Err(Transmitting) | ||
| 149 | } else { | ||
| 150 | self.set_mode(RadioMode::Stdby)?; | ||
| 151 | if self.explicit_header { | ||
| 152 | self.set_explicit_header_mode()?; | ||
| 153 | } else { | ||
| 154 | self.set_implicit_header_mode()?; | ||
| 155 | } | ||
| 156 | |||
| 157 | self.write_register(Register::RegIrqFlags.addr(), 0)?; | ||
| 158 | self.write_register(Register::RegFifoAddrPtr.addr(), 0)?; | ||
| 159 | self.write_register(Register::RegPayloadLength.addr(), 0)?; | ||
| 160 | for byte in buffer.iter() { | ||
| 161 | self.write_register(Register::RegFifo.addr(), *byte)?; | ||
| 162 | } | ||
| 163 | self.write_register(Register::RegPayloadLength.addr(), buffer.len() as u8)?; | ||
| 164 | self.set_mode(RadioMode::Tx)?; | ||
| 165 | Ok(()) | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | pub fn packet_ready(&mut self) -> Result<bool, Error<E, CS::Error, RESET::Error>> { | ||
| 170 | Ok(self.read_register(Register::RegIrqFlags.addr())?.get_bit(6)) | ||
| 171 | } | ||
| 172 | |||
| 173 | pub fn irq_flags_mask(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> { | ||
| 174 | Ok(self.read_register(Register::RegIrqFlagsMask.addr())? as u8) | ||
| 175 | } | ||
| 176 | |||
| 177 | pub fn irq_flags(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> { | ||
| 178 | Ok(self.read_register(Register::RegIrqFlags.addr())? as u8) | ||
| 179 | } | ||
| 180 | |||
| 181 | pub fn read_packet_size(&mut self) -> Result<usize, Error<E, CS::Error, RESET::Error>> { | ||
| 182 | let size = self.read_register(Register::RegRxNbBytes.addr())?; | ||
| 183 | Ok(size as usize) | ||
| 184 | } | ||
| 185 | |||
| 186 | /// Returns the contents of the fifo as a fixed 255 u8 array. This should only be called is there is a | ||
| 187 | /// new packet ready to be read. | ||
| 188 | pub fn read_packet( | ||
| 189 | &mut self, | ||
| 190 | buffer: &mut [u8], | ||
| 191 | ) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 192 | self.clear_irq()?; | ||
| 193 | let size = self.read_register(Register::RegRxNbBytes.addr())?; | ||
| 194 | assert!(size as usize <= buffer.len()); | ||
| 195 | let fifo_addr = self.read_register(Register::RegFifoRxCurrentAddr.addr())?; | ||
| 196 | self.write_register(Register::RegFifoAddrPtr.addr(), fifo_addr)?; | ||
| 197 | for i in 0..size { | ||
| 198 | let byte = self.read_register(Register::RegFifo.addr())?; | ||
| 199 | buffer[i as usize] = byte; | ||
| 200 | } | ||
| 201 | self.write_register(Register::RegFifoAddrPtr.addr(), 0)?; | ||
| 202 | Ok(()) | ||
| 203 | } | ||
| 204 | |||
| 205 | /// Returns true if the radio is currently transmitting a packet. | ||
| 206 | pub fn transmitting(&mut self) -> Result<bool, Error<E, CS::Error, RESET::Error>> { | ||
| 207 | if (self.read_register(Register::RegOpMode.addr())? & RadioMode::Tx.addr()) | ||
| 208 | == RadioMode::Tx.addr() | ||
| 209 | { | ||
| 210 | Ok(true) | ||
| 211 | } else { | ||
| 212 | if (self.read_register(Register::RegIrqFlags.addr())? & IRQ::IrqTxDoneMask.addr()) == 1 | ||
| 213 | { | ||
| 214 | self.write_register(Register::RegIrqFlags.addr(), IRQ::IrqTxDoneMask.addr())?; | ||
| 215 | } | ||
| 216 | Ok(false) | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | /// Clears the radio's IRQ registers. | ||
| 221 | pub fn clear_irq(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> { | ||
| 222 | let irq_flags = self.read_register(Register::RegIrqFlags.addr())?; | ||
| 223 | self.write_register(Register::RegIrqFlags.addr(), 0xFF)?; | ||
| 224 | Ok(irq_flags) | ||
| 225 | } | ||
| 226 | |||
| 227 | /// Sets the transmit power and pin. Levels can range from 0-14 when the output | ||
| 228 | /// pin = 0(RFO), and form 0-20 when output pin = 1(PaBoost). Power is in dB. | ||
| 229 | /// Default value is `17`. | ||
| 230 | pub fn set_tx_power( | ||
| 231 | &mut self, | ||
| 232 | mut level: i32, | ||
| 233 | output_pin: u8, | ||
| 234 | ) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 235 | if PaConfig::PaOutputRfoPin.addr() == output_pin { | ||
| 236 | // RFO | ||
| 237 | if level < 0 { | ||
| 238 | level = 0; | ||
| 239 | } else if level > 14 { | ||
| 240 | level = 14; | ||
| 241 | } | ||
| 242 | self.write_register(Register::RegPaConfig.addr(), (0x70 | level) as u8) | ||
| 243 | } else { | ||
| 244 | // PA BOOST | ||
| 245 | if level > 17 { | ||
| 246 | if level > 20 { | ||
| 247 | level = 20; | ||
| 248 | } | ||
| 249 | // subtract 3 from level, so 18 - 20 maps to 15 - 17 | ||
| 250 | level -= 3; | ||
| 251 | |||
| 252 | // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.) | ||
| 253 | self.write_register(Register::RegPaDac.addr(), 0x87)?; | ||
| 254 | self.set_ocp(140)?; | ||
| 255 | } else { | ||
| 256 | if level < 2 { | ||
| 257 | level = 2; | ||
| 258 | } | ||
| 259 | //Default value PA_HF/LF or +17dBm | ||
| 260 | self.write_register(Register::RegPaDac.addr(), 0x84)?; | ||
| 261 | self.set_ocp(100)?; | ||
| 262 | } | ||
| 263 | level -= 2; | ||
| 264 | self.write_register( | ||
| 265 | Register::RegPaConfig.addr(), | ||
| 266 | PaConfig::PaBoost.addr() | level as u8, | ||
| 267 | ) | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | pub fn get_modem_stat(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> { | ||
| 272 | Ok(self.read_register(Register::RegModemStat.addr())? as u8) | ||
| 273 | } | ||
| 274 | |||
| 275 | /// Sets the over current protection on the radio(mA). | ||
| 276 | pub fn set_ocp(&mut self, ma: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 277 | let mut ocp_trim: u8 = 27; | ||
| 278 | |||
| 279 | if ma <= 120 { | ||
| 280 | ocp_trim = (ma - 45) / 5; | ||
| 281 | } else if ma <= 240 { | ||
| 282 | ocp_trim = (ma + 30) / 10; | ||
| 283 | } | ||
| 284 | self.write_register(Register::RegOcp.addr(), 0x20 | (0x1F & ocp_trim)) | ||
| 285 | } | ||
| 286 | |||
| 287 | /// Sets the state of the radio. Default mode after initiation is `Standby`. | ||
| 288 | pub fn set_mode(&mut self, mode: RadioMode) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 289 | if self.explicit_header { | ||
| 290 | self.set_explicit_header_mode()?; | ||
| 291 | } else { | ||
| 292 | self.set_implicit_header_mode()?; | ||
| 293 | } | ||
| 294 | self.write_register( | ||
| 295 | Register::RegOpMode.addr(), | ||
| 296 | RadioMode::LongRangeMode.addr() | mode.addr(), | ||
| 297 | )?; | ||
| 298 | |||
| 299 | self.mode = mode; | ||
| 300 | Ok(()) | ||
| 301 | } | ||
| 302 | |||
| 303 | pub fn reset_payload_length(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 304 | self.write_register(Register::RegPayloadLength.addr(), 0xFF) | ||
| 305 | } | ||
| 306 | |||
| 307 | /// Sets the frequency of the radio. Values are in megahertz. | ||
| 308 | /// I.E. 915 MHz must be used for North America. Check regulation for your area. | ||
| 309 | pub fn set_frequency(&mut self, freq: u32) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 310 | const FREQ_STEP: f64 = 61.03515625; | ||
| 311 | // calculate register values | ||
| 312 | let frf = (freq as f64 / FREQ_STEP) as u32; | ||
| 313 | // write registers | ||
| 314 | self.write_register( | ||
| 315 | Register::RegFrfMsb.addr(), | ||
| 316 | ((frf & 0x00FF_0000) >> 16) as u8, | ||
| 317 | )?; | ||
| 318 | self.write_register(Register::RegFrfMid.addr(), ((frf & 0x0000_FF00) >> 8) as u8)?; | ||
| 319 | self.write_register(Register::RegFrfLsb.addr(), (frf & 0x0000_00FF) as u8) | ||
| 320 | } | ||
| 321 | |||
| 322 | /// Sets the radio to use an explicit header. Default state is `ON`. | ||
| 323 | fn set_explicit_header_mode(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 324 | let reg_modem_config_1 = self.read_register(Register::RegModemConfig1.addr())?; | ||
| 325 | self.write_register(Register::RegModemConfig1.addr(), reg_modem_config_1 & 0xfe)?; | ||
| 326 | self.explicit_header = true; | ||
| 327 | Ok(()) | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Sets the radio to use an implicit header. Default state is `OFF`. | ||
| 331 | fn set_implicit_header_mode(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 332 | let reg_modem_config_1 = self.read_register(Register::RegModemConfig1.addr())?; | ||
| 333 | self.write_register(Register::RegModemConfig1.addr(), reg_modem_config_1 & 0x01)?; | ||
| 334 | self.explicit_header = false; | ||
| 335 | Ok(()) | ||
| 336 | } | ||
| 337 | |||
| 338 | /// Sets the spreading factor of the radio. Supported values are between 6 and 12. | ||
| 339 | /// If a spreading factor of 6 is set, implicit header mode must be used to transmit | ||
| 340 | /// and receive packets. Default value is `7`. | ||
| 341 | pub fn set_spreading_factor( | ||
| 342 | &mut self, | ||
| 343 | mut sf: u8, | ||
| 344 | ) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 345 | if sf < 6 { | ||
| 346 | sf = 6; | ||
| 347 | } else if sf > 12 { | ||
| 348 | sf = 12; | ||
| 349 | } | ||
| 350 | |||
| 351 | if sf == 6 { | ||
| 352 | self.write_register(Register::RegDetectionOptimize.addr(), 0xc5)?; | ||
| 353 | self.write_register(Register::RegDetectionThreshold.addr(), 0x0c)?; | ||
| 354 | } else { | ||
| 355 | self.write_register(Register::RegDetectionOptimize.addr(), 0xc3)?; | ||
| 356 | self.write_register(Register::RegDetectionThreshold.addr(), 0x0a)?; | ||
| 357 | } | ||
| 358 | let modem_config_2 = self.read_register(Register::RegModemConfig2.addr())?; | ||
| 359 | self.write_register( | ||
| 360 | Register::RegModemConfig2.addr(), | ||
| 361 | (modem_config_2 & 0x0f) | ((sf << 4) & 0xf0), | ||
| 362 | )?; | ||
| 363 | self.set_ldo_flag()?; | ||
| 364 | |||
| 365 | self.write_register(Register::RegSymbTimeoutLsb.addr(), 0x05)?; | ||
| 366 | |||
| 367 | Ok(()) | ||
| 368 | } | ||
| 369 | |||
| 370 | pub fn set_tcxo(&mut self, external: bool) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 371 | if external { | ||
| 372 | self.write_register(Register::RegTcxo.addr(), 0x10) | ||
| 373 | } else { | ||
| 374 | self.write_register(Register::RegTcxo.addr(), 0x00) | ||
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | /// Sets the signal bandwidth of the radio. Supported values are: `7800 Hz`, `10400 Hz`, | ||
| 379 | /// `15600 Hz`, `20800 Hz`, `31250 Hz`,`41700 Hz` ,`62500 Hz`,`125000 Hz` and `250000 Hz` | ||
| 380 | /// Default value is `125000 Hz` | ||
| 381 | pub fn set_signal_bandwidth( | ||
| 382 | &mut self, | ||
| 383 | sbw: i64, | ||
| 384 | ) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 385 | let bw: i64 = match sbw { | ||
| 386 | 7_800 => 0, | ||
| 387 | 10_400 => 1, | ||
| 388 | 15_600 => 2, | ||
| 389 | 20_800 => 3, | ||
| 390 | 31_250 => 4, | ||
| 391 | 41_700 => 5, | ||
| 392 | 62_500 => 6, | ||
| 393 | 125_000 => 7, | ||
| 394 | 250_000 => 8, | ||
| 395 | _ => 9, | ||
| 396 | }; | ||
| 397 | let modem_config_1 = self.read_register(Register::RegModemConfig1.addr())?; | ||
| 398 | self.write_register( | ||
| 399 | Register::RegModemConfig1.addr(), | ||
| 400 | (modem_config_1 & 0x0f) | ((bw << 4) as u8), | ||
| 401 | )?; | ||
| 402 | self.set_ldo_flag()?; | ||
| 403 | Ok(()) | ||
| 404 | } | ||
| 405 | |||
| 406 | /// Sets the coding rate of the radio with the numerator fixed at 4. Supported values | ||
| 407 | /// are between `5` and `8`, these correspond to coding rates of `4/5` and `4/8`. | ||
| 408 | /// Default value is `5`. | ||
| 409 | pub fn set_coding_rate_4( | ||
| 410 | &mut self, | ||
| 411 | mut denominator: u8, | ||
| 412 | ) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 413 | if denominator < 5 { | ||
| 414 | denominator = 5; | ||
| 415 | } else if denominator > 8 { | ||
| 416 | denominator = 8; | ||
| 417 | } | ||
| 418 | let cr = denominator - 4; | ||
| 419 | let modem_config_1 = self.read_register(Register::RegModemConfig1.addr())?; | ||
| 420 | self.write_register( | ||
| 421 | Register::RegModemConfig1.addr(), | ||
| 422 | (modem_config_1 & 0xf1) | (cr << 1), | ||
| 423 | ) | ||
| 424 | } | ||
| 425 | |||
| 426 | /// Sets the preamble length of the radio. Values are between 6 and 65535. | ||
| 427 | /// Default value is `8`. | ||
| 428 | pub fn set_preamble_length( | ||
| 429 | &mut self, | ||
| 430 | length: i64, | ||
| 431 | ) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 432 | self.write_register(Register::RegPreambleMsb.addr(), (length >> 8) as u8)?; | ||
| 433 | self.write_register(Register::RegPreambleLsb.addr(), length as u8) | ||
| 434 | } | ||
| 435 | |||
| 436 | /// Enables are disables the radio's CRC check. Default value is `false`. | ||
| 437 | pub fn set_crc(&mut self, value: bool) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 438 | let modem_config_2 = self.read_register(Register::RegModemConfig2.addr())?; | ||
| 439 | if value { | ||
| 440 | self.write_register(Register::RegModemConfig2.addr(), modem_config_2 | 0x04) | ||
| 441 | } else { | ||
| 442 | self.write_register(Register::RegModemConfig2.addr(), modem_config_2 & 0xfb) | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | /// Inverts the radio's IQ signals. Default value is `false`. | ||
| 447 | pub fn set_invert_iq(&mut self, value: bool) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 448 | if value { | ||
| 449 | self.write_register(Register::RegInvertiq.addr(), 0x66)?; | ||
| 450 | self.write_register(Register::RegInvertiq2.addr(), 0x19) | ||
| 451 | } else { | ||
| 452 | self.write_register(Register::RegInvertiq.addr(), 0x27)?; | ||
| 453 | self.write_register(Register::RegInvertiq2.addr(), 0x1d) | ||
| 454 | } | ||
| 455 | } | ||
| 456 | |||
| 457 | /// Returns the spreading factor of the radio. | ||
| 458 | pub fn get_spreading_factor(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> { | ||
| 459 | Ok(self.read_register(Register::RegModemConfig2.addr())? >> 4) | ||
| 460 | } | ||
| 461 | |||
| 462 | /// Returns the signal bandwidth of the radio. | ||
| 463 | pub fn get_signal_bandwidth(&mut self) -> Result<i64, Error<E, CS::Error, RESET::Error>> { | ||
| 464 | let bw = self.read_register(Register::RegModemConfig1.addr())? >> 4; | ||
| 465 | let bw = match bw { | ||
| 466 | 0 => 7_800, | ||
| 467 | 1 => 10_400, | ||
| 468 | 2 => 15_600, | ||
| 469 | 3 => 20_800, | ||
| 470 | 4 => 31_250, | ||
| 471 | 5 => 41_700, | ||
| 472 | 6 => 62_500, | ||
| 473 | 7 => 125_000, | ||
| 474 | 8 => 250_000, | ||
| 475 | 9 => 500_000, | ||
| 476 | _ => -1, | ||
| 477 | }; | ||
| 478 | Ok(bw) | ||
| 479 | } | ||
| 480 | |||
| 481 | /// Returns the RSSI of the last received packet. | ||
| 482 | pub fn get_packet_rssi(&mut self) -> Result<i32, Error<E, CS::Error, RESET::Error>> { | ||
| 483 | Ok(i32::from(self.read_register(Register::RegPktRssiValue.addr())?) - 157) | ||
| 484 | } | ||
| 485 | |||
| 486 | /// Returns the signal to noise radio of the the last received packet. | ||
| 487 | pub fn get_packet_snr(&mut self) -> Result<f64, Error<E, CS::Error, RESET::Error>> { | ||
| 488 | Ok(f64::from( | ||
| 489 | self.read_register(Register::RegPktSnrValue.addr())?, | ||
| 490 | )) | ||
| 491 | } | ||
| 492 | |||
| 493 | /// Returns the frequency error of the last received packet in Hz. | ||
| 494 | pub fn get_packet_frequency_error(&mut self) -> Result<i64, Error<E, CS::Error, RESET::Error>> { | ||
| 495 | let mut freq_error: i32; | ||
| 496 | freq_error = i32::from(self.read_register(Register::RegFreqErrorMsb.addr())? & 0x7); | ||
| 497 | freq_error <<= 8i64; | ||
| 498 | freq_error += i32::from(self.read_register(Register::RegFreqErrorMid.addr())?); | ||
| 499 | freq_error <<= 8i64; | ||
| 500 | freq_error += i32::from(self.read_register(Register::RegFreqErrorLsb.addr())?); | ||
| 501 | |||
| 502 | let f_xtal = 32_000_000; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14) | ||
| 503 | let f_error = ((f64::from(freq_error) * (1i64 << 24) as f64) / f64::from(f_xtal)) | ||
| 504 | * (self.get_signal_bandwidth()? as f64 / 500_000.0f64); // p. 37 | ||
| 505 | Ok(f_error as i64) | ||
| 506 | } | ||
| 507 | |||
| 508 | fn set_ldo_flag(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 509 | let sw = self.get_signal_bandwidth()?; | ||
| 510 | // Section 4.1.1.5 | ||
| 511 | let symbol_duration = 1000 / (sw / ((1_i64) << self.get_spreading_factor()?)); | ||
| 512 | |||
| 513 | // Section 4.1.1.6 | ||
| 514 | let ldo_on = symbol_duration > 16; | ||
| 515 | |||
| 516 | let mut config_3 = self.read_register(Register::RegModemConfig3.addr())?; | ||
| 517 | config_3.set_bit(3, ldo_on); | ||
| 518 | //config_3.set_bit(2, true); | ||
| 519 | self.write_register(Register::RegModemConfig3.addr(), config_3) | ||
| 520 | } | ||
| 521 | |||
| 522 | fn read_register(&mut self, reg: u8) -> Result<u8, Error<E, CS::Error, RESET::Error>> { | ||
| 523 | self.cs.set_low().map_err(CS)?; | ||
| 524 | |||
| 525 | let mut buffer = [reg & 0x7f, 0]; | ||
| 526 | let transfer = self.spi.transfer(&mut buffer).map_err(SPI)?; | ||
| 527 | self.cs.set_high().map_err(CS)?; | ||
| 528 | Ok(transfer[1]) | ||
| 529 | } | ||
| 530 | |||
| 531 | fn write_register( | ||
| 532 | &mut self, | ||
| 533 | reg: u8, | ||
| 534 | byte: u8, | ||
| 535 | ) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 536 | self.cs.set_low().map_err(CS)?; | ||
| 537 | |||
| 538 | let buffer = [reg | 0x80, byte]; | ||
| 539 | self.spi.write(&buffer).map_err(SPI)?; | ||
| 540 | self.cs.set_high().map_err(CS)?; | ||
| 541 | Ok(()) | ||
| 542 | } | ||
| 543 | |||
| 544 | pub fn put_in_fsk_mode(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 545 | // Put in FSK mode | ||
| 546 | let mut op_mode = 0; | ||
| 547 | op_mode | ||
| 548 | .set_bit(7, false) // FSK mode | ||
| 549 | .set_bits(5..6, 0x00) // FSK modulation | ||
| 550 | .set_bit(3, false) //Low freq registers | ||
| 551 | .set_bits(0..2, 0b011); // Mode | ||
| 552 | |||
| 553 | self.write_register(Register::RegOpMode as u8, op_mode) | ||
| 554 | } | ||
| 555 | |||
| 556 | pub fn set_fsk_pa_ramp( | ||
| 557 | &mut self, | ||
| 558 | modulation_shaping: FskDataModulationShaping, | ||
| 559 | ramp: FskRampUpRamDown, | ||
| 560 | ) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 561 | let mut pa_ramp = 0; | ||
| 562 | pa_ramp | ||
| 563 | .set_bits(5..6, modulation_shaping as u8) | ||
| 564 | .set_bits(0..3, ramp as u8); | ||
| 565 | |||
| 566 | self.write_register(Register::RegPaRamp as u8, pa_ramp) | ||
| 567 | } | ||
| 568 | |||
| 569 | pub fn set_lora_pa_ramp(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 570 | self.write_register(Register::RegPaRamp as u8, 0b1000) | ||
| 571 | } | ||
| 572 | |||
| 573 | pub fn set_lora_sync_word(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> { | ||
| 574 | self.write_register(Register::RegSyncWord as u8, 0x34) | ||
| 575 | } | ||
| 576 | } | ||
| 577 | /// Modes of the radio and their corresponding register values. | ||
| 578 | #[derive(Clone, Copy)] | ||
| 579 | pub enum RadioMode { | ||
| 580 | LongRangeMode = 0x80, | ||
| 581 | Sleep = 0x00, | ||
| 582 | Stdby = 0x01, | ||
| 583 | Tx = 0x03, | ||
| 584 | RxContinuous = 0x05, | ||
| 585 | RxSingle = 0x06, | ||
| 586 | } | ||
| 587 | |||
| 588 | impl RadioMode { | ||
| 589 | /// Returns the address of the mode. | ||
| 590 | pub fn addr(self) -> u8 { | ||
| 591 | self as u8 | ||
| 592 | } | ||
| 593 | } | ||
diff --git a/embassy-lora/src/sx127x/sx127x_lora/register.rs b/embassy-lora/src/sx127x/sx127x_lora/register.rs new file mode 100644 index 000000000..2445e21b1 --- /dev/null +++ b/embassy-lora/src/sx127x/sx127x_lora/register.rs | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | // Copyright Charles Wade (https://github.com/mr-glt/sx127x_lora). Licensed under the Apache 2.0 | ||
| 2 | // license | ||
| 3 | // | ||
| 4 | // Modifications made to make the driver work with the rust-lorawan link layer. | ||
| 5 | #![allow(dead_code, clippy::enum_variant_names)] | ||
| 6 | |||
| 7 | #[derive(Clone, Copy)] | ||
| 8 | pub enum Register { | ||
| 9 | RegFifo = 0x00, | ||
| 10 | RegOpMode = 0x01, | ||
| 11 | RegFrfMsb = 0x06, | ||
| 12 | RegFrfMid = 0x07, | ||
| 13 | RegFrfLsb = 0x08, | ||
| 14 | RegPaConfig = 0x09, | ||
| 15 | RegPaRamp = 0x0a, | ||
| 16 | RegOcp = 0x0b, | ||
| 17 | RegLna = 0x0c, | ||
| 18 | RegFifoAddrPtr = 0x0d, | ||
| 19 | RegFifoTxBaseAddr = 0x0e, | ||
| 20 | RegFifoRxBaseAddr = 0x0f, | ||
| 21 | RegFifoRxCurrentAddr = 0x10, | ||
| 22 | RegIrqFlagsMask = 0x11, | ||
| 23 | RegIrqFlags = 0x12, | ||
| 24 | RegRxNbBytes = 0x13, | ||
| 25 | RegPktSnrValue = 0x19, | ||
| 26 | RegModemStat = 0x18, | ||
| 27 | RegPktRssiValue = 0x1a, | ||
| 28 | RegModemConfig1 = 0x1d, | ||
| 29 | RegModemConfig2 = 0x1e, | ||
| 30 | RegSymbTimeoutLsb = 0x1f, | ||
| 31 | RegPreambleMsb = 0x20, | ||
| 32 | RegPreambleLsb = 0x21, | ||
| 33 | RegPayloadLength = 0x22, | ||
| 34 | RegMaxPayloadLength = 0x23, | ||
| 35 | RegModemConfig3 = 0x26, | ||
| 36 | RegFreqErrorMsb = 0x28, | ||
| 37 | RegFreqErrorMid = 0x29, | ||
| 38 | RegFreqErrorLsb = 0x2a, | ||
| 39 | RegRssiWideband = 0x2c, | ||
| 40 | RegDetectionOptimize = 0x31, | ||
| 41 | RegInvertiq = 0x33, | ||
| 42 | RegDetectionThreshold = 0x37, | ||
| 43 | RegSyncWord = 0x39, | ||
| 44 | RegInvertiq2 = 0x3b, | ||
| 45 | RegDioMapping1 = 0x40, | ||
| 46 | RegVersion = 0x42, | ||
| 47 | RegTcxo = 0x4b, | ||
| 48 | RegPaDac = 0x4d, | ||
| 49 | } | ||
| 50 | #[derive(Clone, Copy)] | ||
| 51 | pub enum PaConfig { | ||
| 52 | PaBoost = 0x80, | ||
| 53 | PaOutputRfoPin = 0, | ||
| 54 | } | ||
| 55 | |||
| 56 | #[derive(Clone, Copy)] | ||
| 57 | pub enum IRQ { | ||
| 58 | IrqTxDoneMask = 0x08, | ||
| 59 | IrqPayloadCrcErrorMask = 0x20, | ||
| 60 | IrqRxDoneMask = 0x40, | ||
| 61 | } | ||
| 62 | |||
| 63 | impl Register { | ||
| 64 | pub fn addr(self) -> u8 { | ||
| 65 | self as u8 | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | impl PaConfig { | ||
| 70 | pub fn addr(self) -> u8 { | ||
| 71 | self as u8 | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | impl IRQ { | ||
| 76 | pub fn addr(self) -> u8 { | ||
| 77 | self as u8 | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | #[derive(Clone, Copy)] | ||
| 82 | pub enum FskDataModulationShaping { | ||
| 83 | None = 1, | ||
| 84 | GaussianBt1d0 = 2, | ||
| 85 | GaussianBt0d5 = 10, | ||
| 86 | GaussianBt0d3 = 11, | ||
| 87 | } | ||
| 88 | |||
| 89 | #[derive(Clone, Copy)] | ||
| 90 | pub enum FskRampUpRamDown { | ||
| 91 | _3d4ms = 0b000, | ||
| 92 | _2ms = 0b0001, | ||
| 93 | _1ms = 0b0010, | ||
| 94 | _500us = 0b0011, | ||
| 95 | _250us = 0b0100, | ||
| 96 | _125us = 0b0101, | ||
| 97 | _100us = 0b0110, | ||
| 98 | _62us = 0b0111, | ||
| 99 | _50us = 0b1000, | ||
| 100 | _40us = 0b1001, | ||
| 101 | _31us = 0b1010, | ||
| 102 | _25us = 0b1011, | ||
| 103 | _20us = 0b1100, | ||
| 104 | _15us = 0b1101, | ||
| 105 | _12us = 0b1110, | ||
| 106 | _10us = 0b1111, | ||
| 107 | } | ||
diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index f214d927b..db1fac2ff 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs | |||
| @@ -159,6 +159,32 @@ impl_pin!(P0_29, 0, 29); | |||
| 159 | impl_pin!(P0_30, 0, 30); | 159 | impl_pin!(P0_30, 0, 30); |
| 160 | impl_pin!(P0_31, 0, 31); | 160 | impl_pin!(P0_31, 0, 31); |
| 161 | 161 | ||
| 162 | impl_ppi_channel!(PPI_CH0, 0, configurable); | ||
| 163 | impl_ppi_channel!(PPI_CH1, 1, configurable); | ||
| 164 | impl_ppi_channel!(PPI_CH2, 2, configurable); | ||
| 165 | impl_ppi_channel!(PPI_CH3, 3, configurable); | ||
| 166 | impl_ppi_channel!(PPI_CH4, 4, configurable); | ||
| 167 | impl_ppi_channel!(PPI_CH5, 5, configurable); | ||
| 168 | impl_ppi_channel!(PPI_CH6, 6, configurable); | ||
| 169 | impl_ppi_channel!(PPI_CH7, 7, configurable); | ||
| 170 | impl_ppi_channel!(PPI_CH8, 8, configurable); | ||
| 171 | impl_ppi_channel!(PPI_CH9, 9, configurable); | ||
| 172 | impl_ppi_channel!(PPI_CH20, 20); | ||
| 173 | impl_ppi_channel!(PPI_CH21, 21); | ||
| 174 | impl_ppi_channel!(PPI_CH22, 22); | ||
| 175 | impl_ppi_channel!(PPI_CH23, 23); | ||
| 176 | impl_ppi_channel!(PPI_CH24, 24); | ||
| 177 | impl_ppi_channel!(PPI_CH25, 25); | ||
| 178 | impl_ppi_channel!(PPI_CH26, 26); | ||
| 179 | impl_ppi_channel!(PPI_CH27, 27); | ||
| 180 | impl_ppi_channel!(PPI_CH28, 28); | ||
| 181 | impl_ppi_channel!(PPI_CH29, 29); | ||
| 182 | impl_ppi_channel!(PPI_CH30, 30); | ||
| 183 | impl_ppi_channel!(PPI_CH31, 31); | ||
| 184 | |||
| 185 | impl_saadc_input!(P0_04, ANALOGINPUT2); | ||
| 186 | impl_saadc_input!(P0_05, ANALOGINPUT3); | ||
| 187 | |||
| 162 | pub mod irqs { | 188 | pub mod irqs { |
| 163 | use crate::pac::Interrupt as InterruptEnum; | 189 | use crate::pac::Interrupt as InterruptEnum; |
| 164 | use embassy_macros::interrupt_declare as declare; | 190 | use embassy_macros::interrupt_declare as declare; |
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index caef3e7e7..06b9bfb38 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs | |||
| @@ -164,6 +164,48 @@ impl_pin!(P0_29, 0, 29); | |||
| 164 | impl_pin!(P0_30, 0, 30); | 164 | impl_pin!(P0_30, 0, 30); |
| 165 | impl_pin!(P0_31, 0, 31); | 165 | impl_pin!(P0_31, 0, 31); |
| 166 | 166 | ||
| 167 | impl_ppi_channel!(PPI_CH0, 0, configurable); | ||
| 168 | impl_ppi_channel!(PPI_CH1, 1, configurable); | ||
| 169 | impl_ppi_channel!(PPI_CH2, 2, configurable); | ||
| 170 | impl_ppi_channel!(PPI_CH3, 3, configurable); | ||
| 171 | impl_ppi_channel!(PPI_CH4, 4, configurable); | ||
| 172 | impl_ppi_channel!(PPI_CH5, 5, configurable); | ||
| 173 | impl_ppi_channel!(PPI_CH6, 6, configurable); | ||
| 174 | impl_ppi_channel!(PPI_CH7, 7, configurable); | ||
| 175 | impl_ppi_channel!(PPI_CH8, 8, configurable); | ||
| 176 | impl_ppi_channel!(PPI_CH9, 9, configurable); | ||
| 177 | impl_ppi_channel!(PPI_CH10, 10, configurable); | ||
| 178 | impl_ppi_channel!(PPI_CH11, 11, configurable); | ||
| 179 | impl_ppi_channel!(PPI_CH12, 12, configurable); | ||
| 180 | impl_ppi_channel!(PPI_CH13, 13, configurable); | ||
| 181 | impl_ppi_channel!(PPI_CH14, 14, configurable); | ||
| 182 | impl_ppi_channel!(PPI_CH15, 15, configurable); | ||
| 183 | impl_ppi_channel!(PPI_CH16, 16, configurable); | ||
| 184 | impl_ppi_channel!(PPI_CH17, 17, configurable); | ||
| 185 | impl_ppi_channel!(PPI_CH18, 18, configurable); | ||
| 186 | impl_ppi_channel!(PPI_CH19, 19, configurable); | ||
| 187 | impl_ppi_channel!(PPI_CH20, 20); | ||
| 188 | impl_ppi_channel!(PPI_CH21, 21); | ||
| 189 | impl_ppi_channel!(PPI_CH22, 22); | ||
| 190 | impl_ppi_channel!(PPI_CH23, 23); | ||
| 191 | impl_ppi_channel!(PPI_CH24, 24); | ||
| 192 | impl_ppi_channel!(PPI_CH25, 25); | ||
| 193 | impl_ppi_channel!(PPI_CH26, 26); | ||
| 194 | impl_ppi_channel!(PPI_CH27, 27); | ||
| 195 | impl_ppi_channel!(PPI_CH28, 28); | ||
| 196 | impl_ppi_channel!(PPI_CH29, 29); | ||
| 197 | impl_ppi_channel!(PPI_CH30, 30); | ||
| 198 | impl_ppi_channel!(PPI_CH31, 31); | ||
| 199 | |||
| 200 | impl_saadc_input!(P0_02, ANALOGINPUT0); | ||
| 201 | impl_saadc_input!(P0_03, ANALOGINPUT1); | ||
| 202 | impl_saadc_input!(P0_04, ANALOGINPUT2); | ||
| 203 | impl_saadc_input!(P0_05, ANALOGINPUT3); | ||
| 204 | impl_saadc_input!(P0_28, ANALOGINPUT4); | ||
| 205 | impl_saadc_input!(P0_29, ANALOGINPUT5); | ||
| 206 | impl_saadc_input!(P0_30, ANALOGINPUT6); | ||
| 207 | impl_saadc_input!(P0_31, ANALOGINPUT7); | ||
| 208 | |||
| 167 | pub mod irqs { | 209 | pub mod irqs { |
| 168 | use crate::pac::Interrupt as InterruptEnum; | 210 | use crate::pac::Interrupt as InterruptEnum; |
| 169 | use embassy_macros::interrupt_declare as declare; | 211 | use embassy_macros::interrupt_declare as declare; |
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index cbf14df2a..2f8d98b31 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs | |||
| @@ -165,6 +165,48 @@ impl_pin!(P0_29, 0, 29); | |||
| 165 | impl_pin!(P0_30, 0, 30); | 165 | impl_pin!(P0_30, 0, 30); |
| 166 | impl_pin!(P0_31, 0, 31); | 166 | impl_pin!(P0_31, 0, 31); |
| 167 | 167 | ||
| 168 | impl_ppi_channel!(PPI_CH0, 0, configurable); | ||
| 169 | impl_ppi_channel!(PPI_CH1, 1, configurable); | ||
| 170 | impl_ppi_channel!(PPI_CH2, 2, configurable); | ||
| 171 | impl_ppi_channel!(PPI_CH3, 3, configurable); | ||
| 172 | impl_ppi_channel!(PPI_CH4, 4, configurable); | ||
| 173 | impl_ppi_channel!(PPI_CH5, 5, configurable); | ||
| 174 | impl_ppi_channel!(PPI_CH6, 6, configurable); | ||
| 175 | impl_ppi_channel!(PPI_CH7, 7, configurable); | ||
| 176 | impl_ppi_channel!(PPI_CH8, 8, configurable); | ||
| 177 | impl_ppi_channel!(PPI_CH9, 9, configurable); | ||
| 178 | impl_ppi_channel!(PPI_CH10, 10, configurable); | ||
| 179 | impl_ppi_channel!(PPI_CH11, 11, configurable); | ||
| 180 | impl_ppi_channel!(PPI_CH12, 12, configurable); | ||
| 181 | impl_ppi_channel!(PPI_CH13, 13, configurable); | ||
| 182 | impl_ppi_channel!(PPI_CH14, 14, configurable); | ||
| 183 | impl_ppi_channel!(PPI_CH15, 15, configurable); | ||
| 184 | impl_ppi_channel!(PPI_CH16, 16, configurable); | ||
| 185 | impl_ppi_channel!(PPI_CH17, 17, configurable); | ||
| 186 | impl_ppi_channel!(PPI_CH18, 18, configurable); | ||
| 187 | impl_ppi_channel!(PPI_CH19, 19, configurable); | ||
| 188 | impl_ppi_channel!(PPI_CH20, 20); | ||
| 189 | impl_ppi_channel!(PPI_CH21, 21); | ||
| 190 | impl_ppi_channel!(PPI_CH22, 22); | ||
| 191 | impl_ppi_channel!(PPI_CH23, 23); | ||
| 192 | impl_ppi_channel!(PPI_CH24, 24); | ||
| 193 | impl_ppi_channel!(PPI_CH25, 25); | ||
| 194 | impl_ppi_channel!(PPI_CH26, 26); | ||
| 195 | impl_ppi_channel!(PPI_CH27, 27); | ||
| 196 | impl_ppi_channel!(PPI_CH28, 28); | ||
| 197 | impl_ppi_channel!(PPI_CH29, 29); | ||
| 198 | impl_ppi_channel!(PPI_CH30, 30); | ||
| 199 | impl_ppi_channel!(PPI_CH31, 31); | ||
| 200 | |||
| 201 | impl_saadc_input!(P0_02, ANALOGINPUT0); | ||
| 202 | impl_saadc_input!(P0_03, ANALOGINPUT1); | ||
| 203 | impl_saadc_input!(P0_04, ANALOGINPUT2); | ||
| 204 | impl_saadc_input!(P0_05, ANALOGINPUT3); | ||
| 205 | impl_saadc_input!(P0_28, ANALOGINPUT4); | ||
| 206 | impl_saadc_input!(P0_29, ANALOGINPUT5); | ||
| 207 | impl_saadc_input!(P0_30, ANALOGINPUT6); | ||
| 208 | impl_saadc_input!(P0_31, ANALOGINPUT7); | ||
| 209 | |||
| 168 | pub mod irqs { | 210 | pub mod irqs { |
| 169 | use crate::pac::Interrupt as InterruptEnum; | 211 | use crate::pac::Interrupt as InterruptEnum; |
| 170 | use embassy_macros::interrupt_declare as declare; | 212 | use embassy_macros::interrupt_declare as declare; |
diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 4c0d3b0b8..fc7a367ef 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs | |||
| @@ -22,9 +22,6 @@ embassy_hal_common::peripherals! { | |||
| 22 | TWISPI0, | 22 | TWISPI0, |
| 23 | TWISPI1, | 23 | TWISPI1, |
| 24 | 24 | ||
| 25 | // SAADC | ||
| 26 | SAADC, | ||
| 27 | |||
| 28 | // TIMER | 25 | // TIMER |
| 29 | TIMER0, | 26 | TIMER0, |
| 30 | TIMER1, | 27 | TIMER1, |
| @@ -163,6 +160,39 @@ impl_pin!(P0_29, 0, 29); | |||
| 163 | impl_pin!(P0_30, 0, 30); | 160 | impl_pin!(P0_30, 0, 30); |
| 164 | impl_pin!(P0_31, 0, 31); | 161 | impl_pin!(P0_31, 0, 31); |
| 165 | 162 | ||
| 163 | impl_ppi_channel!(PPI_CH0, 0, configurable); | ||
| 164 | impl_ppi_channel!(PPI_CH1, 1, configurable); | ||
| 165 | impl_ppi_channel!(PPI_CH2, 2, configurable); | ||
| 166 | impl_ppi_channel!(PPI_CH3, 3, configurable); | ||
| 167 | impl_ppi_channel!(PPI_CH4, 4, configurable); | ||
| 168 | impl_ppi_channel!(PPI_CH5, 5, configurable); | ||
| 169 | impl_ppi_channel!(PPI_CH6, 6, configurable); | ||
| 170 | impl_ppi_channel!(PPI_CH7, 7, configurable); | ||
| 171 | impl_ppi_channel!(PPI_CH8, 8, configurable); | ||
| 172 | impl_ppi_channel!(PPI_CH9, 9, configurable); | ||
| 173 | impl_ppi_channel!(PPI_CH10, 10, configurable); | ||
| 174 | impl_ppi_channel!(PPI_CH11, 11, configurable); | ||
| 175 | impl_ppi_channel!(PPI_CH12, 12, configurable); | ||
| 176 | impl_ppi_channel!(PPI_CH13, 13, configurable); | ||
| 177 | impl_ppi_channel!(PPI_CH14, 14, configurable); | ||
| 178 | impl_ppi_channel!(PPI_CH15, 15, configurable); | ||
| 179 | impl_ppi_channel!(PPI_CH16, 16, configurable); | ||
| 180 | impl_ppi_channel!(PPI_CH17, 17, configurable); | ||
| 181 | impl_ppi_channel!(PPI_CH18, 18, configurable); | ||
| 182 | impl_ppi_channel!(PPI_CH19, 19, configurable); | ||
| 183 | impl_ppi_channel!(PPI_CH20, 20); | ||
| 184 | impl_ppi_channel!(PPI_CH21, 21); | ||
| 185 | impl_ppi_channel!(PPI_CH22, 22); | ||
| 186 | impl_ppi_channel!(PPI_CH23, 23); | ||
| 187 | impl_ppi_channel!(PPI_CH24, 24); | ||
| 188 | impl_ppi_channel!(PPI_CH25, 25); | ||
| 189 | impl_ppi_channel!(PPI_CH26, 26); | ||
| 190 | impl_ppi_channel!(PPI_CH27, 27); | ||
| 191 | impl_ppi_channel!(PPI_CH28, 28); | ||
| 192 | impl_ppi_channel!(PPI_CH29, 29); | ||
| 193 | impl_ppi_channel!(PPI_CH30, 30); | ||
| 194 | impl_ppi_channel!(PPI_CH31, 31); | ||
| 195 | |||
| 166 | pub mod irqs { | 196 | pub mod irqs { |
| 167 | use crate::pac::Interrupt as InterruptEnum; | 197 | use crate::pac::Interrupt as InterruptEnum; |
| 168 | use embassy_macros::interrupt_declare as declare; | 198 | use embassy_macros::interrupt_declare as declare; |
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index b95aa604b..47cf27de4 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs | |||
| @@ -178,6 +178,48 @@ impl_pin!(P0_29, 0, 29); | |||
| 178 | impl_pin!(P0_30, 0, 30); | 178 | impl_pin!(P0_30, 0, 30); |
| 179 | impl_pin!(P0_31, 0, 31); | 179 | impl_pin!(P0_31, 0, 31); |
| 180 | 180 | ||
| 181 | impl_ppi_channel!(PPI_CH0, 0, configurable); | ||
| 182 | impl_ppi_channel!(PPI_CH1, 1, configurable); | ||
| 183 | impl_ppi_channel!(PPI_CH2, 2, configurable); | ||
| 184 | impl_ppi_channel!(PPI_CH3, 3, configurable); | ||
| 185 | impl_ppi_channel!(PPI_CH4, 4, configurable); | ||
| 186 | impl_ppi_channel!(PPI_CH5, 5, configurable); | ||
| 187 | impl_ppi_channel!(PPI_CH6, 6, configurable); | ||
| 188 | impl_ppi_channel!(PPI_CH7, 7, configurable); | ||
| 189 | impl_ppi_channel!(PPI_CH8, 8, configurable); | ||
| 190 | impl_ppi_channel!(PPI_CH9, 9, configurable); | ||
| 191 | impl_ppi_channel!(PPI_CH10, 10, configurable); | ||
| 192 | impl_ppi_channel!(PPI_CH11, 11, configurable); | ||
| 193 | impl_ppi_channel!(PPI_CH12, 12, configurable); | ||
| 194 | impl_ppi_channel!(PPI_CH13, 13, configurable); | ||
| 195 | impl_ppi_channel!(PPI_CH14, 14, configurable); | ||
| 196 | impl_ppi_channel!(PPI_CH15, 15, configurable); | ||
| 197 | impl_ppi_channel!(PPI_CH16, 16, configurable); | ||
| 198 | impl_ppi_channel!(PPI_CH17, 17, configurable); | ||
| 199 | impl_ppi_channel!(PPI_CH18, 18, configurable); | ||
| 200 | impl_ppi_channel!(PPI_CH19, 19, configurable); | ||
| 201 | impl_ppi_channel!(PPI_CH20, 20); | ||
| 202 | impl_ppi_channel!(PPI_CH21, 21); | ||
| 203 | impl_ppi_channel!(PPI_CH22, 22); | ||
| 204 | impl_ppi_channel!(PPI_CH23, 23); | ||
| 205 | impl_ppi_channel!(PPI_CH24, 24); | ||
| 206 | impl_ppi_channel!(PPI_CH25, 25); | ||
| 207 | impl_ppi_channel!(PPI_CH26, 26); | ||
| 208 | impl_ppi_channel!(PPI_CH27, 27); | ||
| 209 | impl_ppi_channel!(PPI_CH28, 28); | ||
| 210 | impl_ppi_channel!(PPI_CH29, 29); | ||
| 211 | impl_ppi_channel!(PPI_CH30, 30); | ||
| 212 | impl_ppi_channel!(PPI_CH31, 31); | ||
| 213 | |||
| 214 | impl_saadc_input!(P0_02, ANALOGINPUT0); | ||
| 215 | impl_saadc_input!(P0_03, ANALOGINPUT1); | ||
| 216 | impl_saadc_input!(P0_04, ANALOGINPUT2); | ||
| 217 | impl_saadc_input!(P0_05, ANALOGINPUT3); | ||
| 218 | impl_saadc_input!(P0_28, ANALOGINPUT4); | ||
| 219 | impl_saadc_input!(P0_29, ANALOGINPUT5); | ||
| 220 | impl_saadc_input!(P0_30, ANALOGINPUT6); | ||
| 221 | impl_saadc_input!(P0_31, ANALOGINPUT7); | ||
| 222 | |||
| 181 | pub mod irqs { | 223 | pub mod irqs { |
| 182 | use crate::pac::Interrupt as InterruptEnum; | 224 | use crate::pac::Interrupt as InterruptEnum; |
| 183 | use embassy_macros::interrupt_declare as declare; | 225 | use embassy_macros::interrupt_declare as declare; |
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 570e4b103..b3d813e24 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs | |||
| @@ -218,6 +218,48 @@ impl_pin!(P1_13, 1, 13); | |||
| 218 | impl_pin!(P1_14, 1, 14); | 218 | impl_pin!(P1_14, 1, 14); |
| 219 | impl_pin!(P1_15, 1, 15); | 219 | impl_pin!(P1_15, 1, 15); |
| 220 | 220 | ||
| 221 | impl_ppi_channel!(PPI_CH0, 0, configurable); | ||
| 222 | impl_ppi_channel!(PPI_CH1, 1, configurable); | ||
| 223 | impl_ppi_channel!(PPI_CH2, 2, configurable); | ||
| 224 | impl_ppi_channel!(PPI_CH3, 3, configurable); | ||
| 225 | impl_ppi_channel!(PPI_CH4, 4, configurable); | ||
| 226 | impl_ppi_channel!(PPI_CH5, 5, configurable); | ||
| 227 | impl_ppi_channel!(PPI_CH6, 6, configurable); | ||
| 228 | impl_ppi_channel!(PPI_CH7, 7, configurable); | ||
| 229 | impl_ppi_channel!(PPI_CH8, 8, configurable); | ||
| 230 | impl_ppi_channel!(PPI_CH9, 9, configurable); | ||
| 231 | impl_ppi_channel!(PPI_CH10, 10, configurable); | ||
| 232 | impl_ppi_channel!(PPI_CH11, 11, configurable); | ||
| 233 | impl_ppi_channel!(PPI_CH12, 12, configurable); | ||
| 234 | impl_ppi_channel!(PPI_CH13, 13, configurable); | ||
| 235 | impl_ppi_channel!(PPI_CH14, 14, configurable); | ||
| 236 | impl_ppi_channel!(PPI_CH15, 15, configurable); | ||
| 237 | impl_ppi_channel!(PPI_CH16, 16, configurable); | ||
| 238 | impl_ppi_channel!(PPI_CH17, 17, configurable); | ||
| 239 | impl_ppi_channel!(PPI_CH18, 18, configurable); | ||
| 240 | impl_ppi_channel!(PPI_CH19, 19, configurable); | ||
| 241 | impl_ppi_channel!(PPI_CH20, 20); | ||
| 242 | impl_ppi_channel!(PPI_CH21, 21); | ||
| 243 | impl_ppi_channel!(PPI_CH22, 22); | ||
| 244 | impl_ppi_channel!(PPI_CH23, 23); | ||
| 245 | impl_ppi_channel!(PPI_CH24, 24); | ||
| 246 | impl_ppi_channel!(PPI_CH25, 25); | ||
| 247 | impl_ppi_channel!(PPI_CH26, 26); | ||
| 248 | impl_ppi_channel!(PPI_CH27, 27); | ||
| 249 | impl_ppi_channel!(PPI_CH28, 28); | ||
| 250 | impl_ppi_channel!(PPI_CH29, 29); | ||
| 251 | impl_ppi_channel!(PPI_CH30, 30); | ||
| 252 | impl_ppi_channel!(PPI_CH31, 31); | ||
| 253 | |||
| 254 | impl_saadc_input!(P0_02, ANALOGINPUT0); | ||
| 255 | impl_saadc_input!(P0_03, ANALOGINPUT1); | ||
| 256 | impl_saadc_input!(P0_04, ANALOGINPUT2); | ||
| 257 | impl_saadc_input!(P0_05, ANALOGINPUT3); | ||
| 258 | impl_saadc_input!(P0_28, ANALOGINPUT4); | ||
| 259 | impl_saadc_input!(P0_29, ANALOGINPUT5); | ||
| 260 | impl_saadc_input!(P0_30, ANALOGINPUT6); | ||
| 261 | impl_saadc_input!(P0_31, ANALOGINPUT7); | ||
| 262 | |||
| 221 | pub mod irqs { | 263 | pub mod irqs { |
| 222 | use crate::pac::Interrupt as InterruptEnum; | 264 | use crate::pac::Interrupt as InterruptEnum; |
| 223 | use embassy_macros::interrupt_declare as declare; | 265 | use embassy_macros::interrupt_declare as declare; |
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 1221f23b3..473036f61 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs | |||
| @@ -223,6 +223,48 @@ impl_pin!(P1_13, 1, 13); | |||
| 223 | impl_pin!(P1_14, 1, 14); | 223 | impl_pin!(P1_14, 1, 14); |
| 224 | impl_pin!(P1_15, 1, 15); | 224 | impl_pin!(P1_15, 1, 15); |
| 225 | 225 | ||
| 226 | impl_ppi_channel!(PPI_CH0, 0, configurable); | ||
| 227 | impl_ppi_channel!(PPI_CH1, 1, configurable); | ||
| 228 | impl_ppi_channel!(PPI_CH2, 2, configurable); | ||
| 229 | impl_ppi_channel!(PPI_CH3, 3, configurable); | ||
| 230 | impl_ppi_channel!(PPI_CH4, 4, configurable); | ||
| 231 | impl_ppi_channel!(PPI_CH5, 5, configurable); | ||
| 232 | impl_ppi_channel!(PPI_CH6, 6, configurable); | ||
| 233 | impl_ppi_channel!(PPI_CH7, 7, configurable); | ||
| 234 | impl_ppi_channel!(PPI_CH8, 8, configurable); | ||
| 235 | impl_ppi_channel!(PPI_CH9, 9, configurable); | ||
| 236 | impl_ppi_channel!(PPI_CH10, 10, configurable); | ||
| 237 | impl_ppi_channel!(PPI_CH11, 11, configurable); | ||
| 238 | impl_ppi_channel!(PPI_CH12, 12, configurable); | ||
| 239 | impl_ppi_channel!(PPI_CH13, 13, configurable); | ||
| 240 | impl_ppi_channel!(PPI_CH14, 14, configurable); | ||
| 241 | impl_ppi_channel!(PPI_CH15, 15, configurable); | ||
| 242 | impl_ppi_channel!(PPI_CH16, 16, configurable); | ||
| 243 | impl_ppi_channel!(PPI_CH17, 17, configurable); | ||
| 244 | impl_ppi_channel!(PPI_CH18, 18, configurable); | ||
| 245 | impl_ppi_channel!(PPI_CH19, 19, configurable); | ||
| 246 | impl_ppi_channel!(PPI_CH20, 20); | ||
| 247 | impl_ppi_channel!(PPI_CH21, 21); | ||
| 248 | impl_ppi_channel!(PPI_CH22, 22); | ||
| 249 | impl_ppi_channel!(PPI_CH23, 23); | ||
| 250 | impl_ppi_channel!(PPI_CH24, 24); | ||
| 251 | impl_ppi_channel!(PPI_CH25, 25); | ||
| 252 | impl_ppi_channel!(PPI_CH26, 26); | ||
| 253 | impl_ppi_channel!(PPI_CH27, 27); | ||
| 254 | impl_ppi_channel!(PPI_CH28, 28); | ||
| 255 | impl_ppi_channel!(PPI_CH29, 29); | ||
| 256 | impl_ppi_channel!(PPI_CH30, 30); | ||
| 257 | impl_ppi_channel!(PPI_CH31, 31); | ||
| 258 | |||
| 259 | impl_saadc_input!(P0_02, ANALOGINPUT0); | ||
| 260 | impl_saadc_input!(P0_03, ANALOGINPUT1); | ||
| 261 | impl_saadc_input!(P0_04, ANALOGINPUT2); | ||
| 262 | impl_saadc_input!(P0_05, ANALOGINPUT3); | ||
| 263 | impl_saadc_input!(P0_28, ANALOGINPUT4); | ||
| 264 | impl_saadc_input!(P0_29, ANALOGINPUT5); | ||
| 265 | impl_saadc_input!(P0_30, ANALOGINPUT6); | ||
| 266 | impl_saadc_input!(P0_31, ANALOGINPUT7); | ||
| 267 | |||
| 226 | pub mod irqs { | 268 | pub mod irqs { |
| 227 | use crate::pac::Interrupt as InterruptEnum; | 269 | use crate::pac::Interrupt as InterruptEnum; |
| 228 | use embassy_macros::interrupt_declare as declare; | 270 | use embassy_macros::interrupt_declare as declare; |
diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 0730c1258..75b9e71d2 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs | |||
| @@ -171,6 +171,32 @@ impl_pin!(P0_29, 0, 29); | |||
| 171 | impl_pin!(P0_30, 0, 30); | 171 | impl_pin!(P0_30, 0, 30); |
| 172 | impl_pin!(P0_31, 0, 31); | 172 | impl_pin!(P0_31, 0, 31); |
| 173 | 173 | ||
| 174 | impl_ppi_channel!(PPI_CH0, 0, configurable); | ||
| 175 | impl_ppi_channel!(PPI_CH1, 1, configurable); | ||
| 176 | impl_ppi_channel!(PPI_CH2, 2, configurable); | ||
| 177 | impl_ppi_channel!(PPI_CH3, 3, configurable); | ||
| 178 | impl_ppi_channel!(PPI_CH4, 4, configurable); | ||
| 179 | impl_ppi_channel!(PPI_CH5, 5, configurable); | ||
| 180 | impl_ppi_channel!(PPI_CH6, 6, configurable); | ||
| 181 | impl_ppi_channel!(PPI_CH7, 7, configurable); | ||
| 182 | impl_ppi_channel!(PPI_CH8, 8, configurable); | ||
| 183 | impl_ppi_channel!(PPI_CH9, 9, configurable); | ||
| 184 | impl_ppi_channel!(PPI_CH10, 10, configurable); | ||
| 185 | impl_ppi_channel!(PPI_CH11, 11, configurable); | ||
| 186 | impl_ppi_channel!(PPI_CH12, 12, configurable); | ||
| 187 | impl_ppi_channel!(PPI_CH13, 13, configurable); | ||
| 188 | impl_ppi_channel!(PPI_CH14, 14, configurable); | ||
| 189 | impl_ppi_channel!(PPI_CH15, 15, configurable); | ||
| 190 | |||
| 191 | impl_saadc_input!(P0_13, ANALOGINPUT0); | ||
| 192 | impl_saadc_input!(P0_14, ANALOGINPUT1); | ||
| 193 | impl_saadc_input!(P0_15, ANALOGINPUT2); | ||
| 194 | impl_saadc_input!(P0_16, ANALOGINPUT3); | ||
| 195 | impl_saadc_input!(P0_17, ANALOGINPUT4); | ||
| 196 | impl_saadc_input!(P0_18, ANALOGINPUT5); | ||
| 197 | impl_saadc_input!(P0_19, ANALOGINPUT6); | ||
| 198 | impl_saadc_input!(P0_20, ANALOGINPUT7); | ||
| 199 | |||
| 174 | pub mod irqs { | 200 | pub mod irqs { |
| 175 | use crate::pac::Interrupt as InterruptEnum; | 201 | use crate::pac::Interrupt as InterruptEnum; |
| 176 | use embassy_macros::interrupt_declare as declare; | 202 | use embassy_macros::interrupt_declare as declare; |
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index fe181a3c6..6cf9c9a39 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs | |||
| @@ -266,14 +266,14 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { | |||
| 266 | } | 266 | } |
| 267 | 267 | ||
| 268 | /// Triggers `task set` (set associated pin high). | 268 | /// Triggers `task set` (set associated pin high). |
| 269 | #[cfg(not(feature = "51"))] | 269 | #[cfg(not(feature = "nrf51"))] |
| 270 | pub fn set(&self) { | 270 | pub fn set(&self) { |
| 271 | let g = unsafe { &*GPIOTE::ptr() }; | 271 | let g = unsafe { &*GPIOTE::ptr() }; |
| 272 | g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) }); | 272 | g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) }); |
| 273 | } | 273 | } |
| 274 | 274 | ||
| 275 | /// Triggers `task clear` (set associated pin low). | 275 | /// Triggers `task clear` (set associated pin low). |
| 276 | #[cfg(not(feature = "51"))] | 276 | #[cfg(not(feature = "nrf51"))] |
| 277 | pub fn clear(&self) { | 277 | pub fn clear(&self) { |
| 278 | let g = unsafe { &*GPIOTE::ptr() }; | 278 | let g = unsafe { &*GPIOTE::ptr() }; |
| 279 | g.tasks_clr[self.ch.number()].write(|w| unsafe { w.bits(1) }); | 279 | g.tasks_clr[self.ch.number()].write(|w| unsafe { w.bits(1) }); |
| @@ -286,14 +286,14 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { | |||
| 286 | } | 286 | } |
| 287 | 287 | ||
| 288 | /// Returns the CLR task, for use with PPI. | 288 | /// Returns the CLR task, for use with PPI. |
| 289 | #[cfg(not(feature = "51"))] | 289 | #[cfg(not(feature = "nrf51"))] |
| 290 | pub fn task_clr(&self) -> Task { | 290 | pub fn task_clr(&self) -> Task { |
| 291 | let g = unsafe { &*GPIOTE::ptr() }; | 291 | let g = unsafe { &*GPIOTE::ptr() }; |
| 292 | Task::from_reg(&g.tasks_clr[self.ch.number()]) | 292 | Task::from_reg(&g.tasks_clr[self.ch.number()]) |
| 293 | } | 293 | } |
| 294 | 294 | ||
| 295 | /// Returns the SET task, for use with PPI. | 295 | /// Returns the SET task, for use with PPI. |
| 296 | #[cfg(not(feature = "51"))] | 296 | #[cfg(not(feature = "nrf51"))] |
| 297 | pub fn task_set(&self) -> Task { | 297 | pub fn task_set(&self) -> Task { |
| 298 | let g = unsafe { &*GPIOTE::ptr() }; | 298 | let g = unsafe { &*GPIOTE::ptr() }; |
| 299 | Task::from_reg(&g.tasks_set[self.ch.number()]) | 299 | Task::from_reg(&g.tasks_set[self.ch.number()]) |
diff --git a/embassy-nrf/src/ppi.rs b/embassy-nrf/src/ppi.rs index 1d1f88915..727f6913c 100644 --- a/embassy-nrf/src/ppi.rs +++ b/embassy-nrf/src/ppi.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 1 | //! HAL interface for the PPI peripheral. | 3 | //! HAL interface for the PPI peripheral. |
| 2 | //! | 4 | //! |
| 3 | //! The Programmable Peripheral Interconnect interface allows for an autonomous interoperability | 5 | //! The Programmable Peripheral Interconnect interface allows for an autonomous interoperability |
| @@ -38,7 +40,7 @@ impl<'d, C: Channel> Ppi<'d, C> { | |||
| 38 | ch, | 40 | ch, |
| 39 | phantom: PhantomData, | 41 | phantom: PhantomData, |
| 40 | }; | 42 | }; |
| 41 | #[cfg(not(any(feature = "51", feature = "nrf9160")))] | 43 | #[cfg(not(any(feature = "nrf51", feature = "nrf9160")))] |
| 42 | this.clear_fork_task(); | 44 | this.clear_fork_task(); |
| 43 | this | 45 | this |
| 44 | } | 46 | } |
| @@ -57,7 +59,7 @@ impl<'d, C: Channel> Ppi<'d, C> { | |||
| 57 | .write(|w| unsafe { w.bits(1 << self.ch.number()) }); | 59 | .write(|w| unsafe { w.bits(1 << self.ch.number()) }); |
| 58 | } | 60 | } |
| 59 | 61 | ||
| 60 | #[cfg(not(any(feature = "51", feature = "nrf9160")))] | 62 | #[cfg(not(any(feature = "nrf51", feature = "nrf9160")))] |
| 61 | /// Sets the fork task that must be triggered when the configured event occurs. The user must | 63 | /// Sets the fork task that must be triggered when the configured event occurs. The user must |
| 62 | /// provide a reference to the task. | 64 | /// provide a reference to the task. |
| 63 | pub fn set_fork_task(&mut self, task: Task) { | 65 | pub fn set_fork_task(&mut self, task: Task) { |
| @@ -67,7 +69,7 @@ impl<'d, C: Channel> Ppi<'d, C> { | |||
| 67 | .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) }) | 69 | .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) }) |
| 68 | } | 70 | } |
| 69 | 71 | ||
| 70 | #[cfg(not(any(feature = "51", feature = "nrf9160")))] | 72 | #[cfg(not(any(feature = "nrf51", feature = "nrf9160")))] |
| 71 | /// Clear the fork task endpoint. Previously set task will no longer be triggered. | 73 | /// Clear the fork task endpoint. Previously set task will no longer be triggered. |
| 72 | pub fn clear_fork_task(&mut self) { | 74 | pub fn clear_fork_task(&mut self) { |
| 73 | let r = unsafe { &*PPI::ptr() }; | 75 | let r = unsafe { &*PPI::ptr() }; |
| @@ -143,7 +145,7 @@ impl Event { | |||
| 143 | } | 145 | } |
| 144 | } | 146 | } |
| 145 | 147 | ||
| 146 | mod sealed { | 148 | pub(crate) mod sealed { |
| 147 | pub trait ConfigurableChannel {} | 149 | pub trait ConfigurableChannel {} |
| 148 | pub trait Channel {} | 150 | pub trait Channel {} |
| 149 | pub trait Group {} | 151 | pub trait Group {} |
| @@ -201,15 +203,15 @@ impl Channel for AnyConfigurableChannel { | |||
| 201 | } | 203 | } |
| 202 | } | 204 | } |
| 203 | 205 | ||
| 204 | macro_rules! impl_channel { | 206 | macro_rules! impl_ppi_channel { |
| 205 | ($type:ident, $number:expr, configurable) => { | 207 | ($type:ident, $number:expr, configurable) => { |
| 206 | impl_channel!($type, $number); | 208 | impl_ppi_channel!($type, $number); |
| 207 | impl sealed::ConfigurableChannel for peripherals::$type {} | 209 | impl crate::ppi::sealed::ConfigurableChannel for peripherals::$type {} |
| 208 | impl ConfigurableChannel for peripherals::$type {} | 210 | impl crate::ppi::ConfigurableChannel for peripherals::$type {} |
| 209 | }; | 211 | }; |
| 210 | ($type:ident, $number:expr) => { | 212 | ($type:ident, $number:expr) => { |
| 211 | impl sealed::Channel for peripherals::$type {} | 213 | impl crate::ppi::sealed::Channel for peripherals::$type {} |
| 212 | impl Channel for peripherals::$type { | 214 | impl crate::ppi::Channel for peripherals::$type { |
| 213 | fn number(&self) -> usize { | 215 | fn number(&self) -> usize { |
| 214 | $number | 216 | $number |
| 215 | } | 217 | } |
| @@ -217,70 +219,6 @@ macro_rules! impl_channel { | |||
| 217 | }; | 219 | }; |
| 218 | } | 220 | } |
| 219 | 221 | ||
| 220 | pub use channel_impl::*; | ||
| 221 | #[cfg(not(feature = "nrf9160"))] | ||
| 222 | mod channel_impl { | ||
| 223 | use super::*; | ||
| 224 | |||
| 225 | impl_channel!(PPI_CH0, 0, configurable); | ||
| 226 | impl_channel!(PPI_CH1, 1, configurable); | ||
| 227 | impl_channel!(PPI_CH2, 2, configurable); | ||
| 228 | impl_channel!(PPI_CH3, 3, configurable); | ||
| 229 | impl_channel!(PPI_CH4, 4, configurable); | ||
| 230 | impl_channel!(PPI_CH5, 5, configurable); | ||
| 231 | impl_channel!(PPI_CH6, 6, configurable); | ||
| 232 | impl_channel!(PPI_CH7, 7, configurable); | ||
| 233 | impl_channel!(PPI_CH8, 8, configurable); | ||
| 234 | impl_channel!(PPI_CH9, 9, configurable); | ||
| 235 | impl_channel!(PPI_CH10, 10, configurable); | ||
| 236 | impl_channel!(PPI_CH11, 11, configurable); | ||
| 237 | impl_channel!(PPI_CH12, 12, configurable); | ||
| 238 | impl_channel!(PPI_CH13, 13, configurable); | ||
| 239 | impl_channel!(PPI_CH14, 14, configurable); | ||
| 240 | impl_channel!(PPI_CH15, 15, configurable); | ||
| 241 | #[cfg(not(feature = "51",))] | ||
| 242 | impl_channel!(PPI_CH16, 16, configurable); | ||
| 243 | #[cfg(not(feature = "51"))] | ||
| 244 | impl_channel!(PPI_CH17, 17, configurable); | ||
| 245 | #[cfg(not(feature = "51"))] | ||
| 246 | impl_channel!(PPI_CH18, 18, configurable); | ||
| 247 | #[cfg(not(feature = "51"))] | ||
| 248 | impl_channel!(PPI_CH19, 19, configurable); | ||
| 249 | impl_channel!(PPI_CH20, 20); | ||
| 250 | impl_channel!(PPI_CH21, 21); | ||
| 251 | impl_channel!(PPI_CH22, 22); | ||
| 252 | impl_channel!(PPI_CH23, 23); | ||
| 253 | impl_channel!(PPI_CH24, 24); | ||
| 254 | impl_channel!(PPI_CH25, 25); | ||
| 255 | impl_channel!(PPI_CH26, 26); | ||
| 256 | impl_channel!(PPI_CH27, 27); | ||
| 257 | impl_channel!(PPI_CH28, 28); | ||
| 258 | impl_channel!(PPI_CH29, 29); | ||
| 259 | impl_channel!(PPI_CH30, 30); | ||
| 260 | impl_channel!(PPI_CH31, 31); | ||
| 261 | } | ||
| 262 | #[cfg(feature = "nrf9160")] // TODO: Implement configurability for nrf9160 and then remove these channel_impl modules | ||
| 263 | mod channel_impl { | ||
| 264 | use super::*; | ||
| 265 | |||
| 266 | impl_channel!(PPI_CH0, 0, configurable); | ||
| 267 | impl_channel!(PPI_CH1, 1, configurable); | ||
| 268 | impl_channel!(PPI_CH2, 2, configurable); | ||
| 269 | impl_channel!(PPI_CH3, 3, configurable); | ||
| 270 | impl_channel!(PPI_CH4, 4, configurable); | ||
| 271 | impl_channel!(PPI_CH5, 5, configurable); | ||
| 272 | impl_channel!(PPI_CH6, 6, configurable); | ||
| 273 | impl_channel!(PPI_CH7, 7, configurable); | ||
| 274 | impl_channel!(PPI_CH8, 8, configurable); | ||
| 275 | impl_channel!(PPI_CH9, 9, configurable); | ||
| 276 | impl_channel!(PPI_CH10, 10, configurable); | ||
| 277 | impl_channel!(PPI_CH11, 11, configurable); | ||
| 278 | impl_channel!(PPI_CH12, 12, configurable); | ||
| 279 | impl_channel!(PPI_CH13, 13, configurable); | ||
| 280 | impl_channel!(PPI_CH14, 14, configurable); | ||
| 281 | impl_channel!(PPI_CH15, 15, configurable); | ||
| 282 | } | ||
| 283 | |||
| 284 | // ====================== | 222 | // ====================== |
| 285 | // groups | 223 | // groups |
| 286 | 224 | ||
| @@ -310,7 +248,7 @@ impl_group!(PPI_GROUP0, 0); | |||
| 310 | impl_group!(PPI_GROUP1, 1); | 248 | impl_group!(PPI_GROUP1, 1); |
| 311 | impl_group!(PPI_GROUP2, 2); | 249 | impl_group!(PPI_GROUP2, 2); |
| 312 | impl_group!(PPI_GROUP3, 3); | 250 | impl_group!(PPI_GROUP3, 3); |
| 313 | #[cfg(not(feature = "51"))] | 251 | #[cfg(not(feature = "nrf51"))] |
| 314 | impl_group!(PPI_GROUP4, 4); | 252 | impl_group!(PPI_GROUP4, 4); |
| 315 | #[cfg(not(feature = "51"))] | 253 | #[cfg(not(feature = "nrf51"))] |
| 316 | impl_group!(PPI_GROUP5, 5); | 254 | impl_group!(PPI_GROUP5, 5); |
diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 49a71e497..b1d8faac8 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 1 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 2 | use core::sync::atomic::{compiler_fence, Ordering}; | 4 | use core::sync::atomic::{compiler_fence, Ordering}; |
| 3 | use core::task::Poll; | 5 | use core::task::Poll; |
| @@ -18,7 +20,7 @@ use pac::{saadc_ns as saadc, SAADC_NS as SAADC}; | |||
| 18 | pub use saadc::{ | 20 | pub use saadc::{ |
| 19 | ch::{ | 21 | ch::{ |
| 20 | config::{GAIN_A as Gain, REFSEL_A as Reference, RESP_A as Resistor, TACQ_A as Time}, | 22 | config::{GAIN_A as Gain, REFSEL_A as Reference, RESP_A as Resistor, TACQ_A as Time}, |
| 21 | pselp::PSELP_A as PositiveChannel, | 23 | pselp::PSELP_A as InputChannel, // We treat the positive and negative channels with the same enum values to keep our type tidy and given they are the same |
| 22 | }, | 24 | }, |
| 23 | oversample::OVERSAMPLE_A as Oversample, | 25 | oversample::OVERSAMPLE_A as Oversample, |
| 24 | resolution::VAL_A as Resolution, | 26 | resolution::VAL_A as Resolution, |
| @@ -30,7 +32,7 @@ pub use saadc::{ | |||
| 30 | pub enum Error {} | 32 | pub enum Error {} |
| 31 | 33 | ||
| 32 | /// One-shot saadc. Continuous sample mode TODO. | 34 | /// One-shot saadc. Continuous sample mode TODO. |
| 33 | pub struct OneShot<'d> { | 35 | pub struct OneShot<'d, const N: usize> { |
| 34 | phantom: PhantomData<&'d mut peripherals::SAADC>, | 36 | phantom: PhantomData<&'d mut peripherals::SAADC>, |
| 35 | } | 37 | } |
| 36 | 38 | ||
| @@ -39,11 +41,29 @@ static WAKER: AtomicWaker = AtomicWaker::new(); | |||
| 39 | /// Used to configure the SAADC peripheral. | 41 | /// Used to configure the SAADC peripheral. |
| 40 | /// | 42 | /// |
| 41 | /// See the `Default` impl for suitable default values. | 43 | /// See the `Default` impl for suitable default values. |
| 44 | #[non_exhaustive] | ||
| 42 | pub struct Config { | 45 | pub struct Config { |
| 43 | /// Output resolution in bits. | 46 | /// Output resolution in bits. |
| 44 | pub resolution: Resolution, | 47 | pub resolution: Resolution, |
| 45 | /// Average 2^`oversample` input samples before transferring the result into memory. | 48 | /// Average 2^`oversample` input samples before transferring the result into memory. |
| 46 | pub oversample: Oversample, | 49 | pub oversample: Oversample, |
| 50 | } | ||
| 51 | |||
| 52 | impl Default for Config { | ||
| 53 | /// Default configuration for single channel sampling. | ||
| 54 | fn default() -> Self { | ||
| 55 | Self { | ||
| 56 | resolution: Resolution::_14BIT, | ||
| 57 | oversample: Oversample::BYPASS, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | /// Used to configure an individual SAADC peripheral channel. | ||
| 63 | /// | ||
| 64 | /// See the `Default` impl for suitable default values. | ||
| 65 | #[non_exhaustive] | ||
| 66 | pub struct ChannelConfig<'d> { | ||
| 47 | /// Reference voltage of the SAADC input. | 67 | /// Reference voltage of the SAADC input. |
| 48 | pub reference: Reference, | 68 | pub reference: Reference, |
| 49 | /// Gain used to control the effective input range of the SAADC. | 69 | /// Gain used to control the effective input range of the SAADC. |
| @@ -52,26 +72,52 @@ pub struct Config { | |||
| 52 | pub resistor: Resistor, | 72 | pub resistor: Resistor, |
| 53 | /// Acquisition time in microseconds. | 73 | /// Acquisition time in microseconds. |
| 54 | pub time: Time, | 74 | pub time: Time, |
| 75 | /// Positive channel to sample | ||
| 76 | p_channel: InputChannel, | ||
| 77 | /// An optional negative channel to sample | ||
| 78 | n_channel: Option<InputChannel>, | ||
| 79 | |||
| 80 | phantom: PhantomData<&'d ()>, | ||
| 55 | } | 81 | } |
| 56 | 82 | ||
| 57 | impl Default for Config { | 83 | impl<'d> ChannelConfig<'d> { |
| 58 | fn default() -> Self { | 84 | /// Default configuration for single ended channel sampling. |
| 85 | pub fn single_ended(input: impl Unborrow<Target = impl Input> + 'd) -> Self { | ||
| 86 | unborrow!(input); | ||
| 87 | Self { | ||
| 88 | reference: Reference::INTERNAL, | ||
| 89 | gain: Gain::GAIN1_6, | ||
| 90 | resistor: Resistor::BYPASS, | ||
| 91 | time: Time::_10US, | ||
| 92 | p_channel: input.channel(), | ||
| 93 | n_channel: None, | ||
| 94 | phantom: PhantomData, | ||
| 95 | } | ||
| 96 | } | ||
| 97 | /// Default configuration for differential channel sampling. | ||
| 98 | pub fn differential( | ||
| 99 | p_input: impl Unborrow<Target = impl Input> + 'd, | ||
| 100 | n_input: impl Unborrow<Target = impl Input> + 'd, | ||
| 101 | ) -> Self { | ||
| 102 | unborrow!(p_input, n_input); | ||
| 59 | Self { | 103 | Self { |
| 60 | resolution: Resolution::_14BIT, | ||
| 61 | oversample: Oversample::OVER8X, | ||
| 62 | reference: Reference::VDD1_4, | 104 | reference: Reference::VDD1_4, |
| 63 | gain: Gain::GAIN1_4, | 105 | gain: Gain::GAIN1_6, |
| 64 | resistor: Resistor::BYPASS, | 106 | resistor: Resistor::BYPASS, |
| 65 | time: Time::_20US, | 107 | time: Time::_10US, |
| 108 | p_channel: p_input.channel(), | ||
| 109 | n_channel: Some(n_input.channel()), | ||
| 110 | phantom: PhantomData, | ||
| 66 | } | 111 | } |
| 67 | } | 112 | } |
| 68 | } | 113 | } |
| 69 | 114 | ||
| 70 | impl<'d> OneShot<'d> { | 115 | impl<'d, const N: usize> OneShot<'d, N> { |
| 71 | pub fn new( | 116 | pub fn new( |
| 72 | _saadc: impl Unborrow<Target = peripherals::SAADC> + 'd, | 117 | _saadc: impl Unborrow<Target = peripherals::SAADC> + 'd, |
| 73 | irq: impl Unborrow<Target = interrupt::SAADC> + 'd, | 118 | irq: impl Unborrow<Target = interrupt::SAADC> + 'd, |
| 74 | config: Config, | 119 | config: Config, |
| 120 | channel_configs: [ChannelConfig; N], | ||
| 75 | ) -> Self { | 121 | ) -> Self { |
| 76 | unborrow!(irq); | 122 | unborrow!(irq); |
| 77 | 123 | ||
| @@ -80,31 +126,39 @@ impl<'d> OneShot<'d> { | |||
| 80 | let Config { | 126 | let Config { |
| 81 | resolution, | 127 | resolution, |
| 82 | oversample, | 128 | oversample, |
| 83 | reference, | ||
| 84 | gain, | ||
| 85 | resistor, | ||
| 86 | time, | ||
| 87 | } = config; | 129 | } = config; |
| 88 | 130 | ||
| 89 | // Configure pins | 131 | // Configure channels |
| 90 | r.enable.write(|w| w.enable().enabled()); | 132 | r.enable.write(|w| w.enable().enabled()); |
| 91 | r.resolution.write(|w| w.val().variant(resolution)); | 133 | r.resolution.write(|w| w.val().variant(resolution)); |
| 92 | r.oversample.write(|w| w.oversample().variant(oversample)); | 134 | r.oversample.write(|w| w.oversample().variant(oversample)); |
| 93 | 135 | ||
| 94 | r.ch[0].config.write(|w| { | 136 | for (i, cc) in channel_configs.iter().enumerate() { |
| 95 | w.refsel().variant(reference); | 137 | r.ch[i].pselp.write(|w| w.pselp().variant(cc.p_channel)); |
| 96 | w.gain().variant(gain); | 138 | if let Some(n_channel) = cc.n_channel { |
| 97 | w.tacq().variant(time); | 139 | r.ch[i] |
| 98 | w.mode().se(); | 140 | .pseln |
| 99 | w.resp().variant(resistor); | 141 | .write(|w| unsafe { w.pseln().bits(n_channel as u8) }); |
| 100 | w.resn().bypass(); | ||
| 101 | if !matches!(oversample, Oversample::BYPASS) { | ||
| 102 | w.burst().enabled(); | ||
| 103 | } else { | ||
| 104 | w.burst().disabled(); | ||
| 105 | } | 142 | } |
| 106 | w | 143 | r.ch[i].config.write(|w| { |
| 107 | }); | 144 | w.refsel().variant(cc.reference); |
| 145 | w.gain().variant(cc.gain); | ||
| 146 | w.tacq().variant(cc.time); | ||
| 147 | if cc.n_channel.is_none() { | ||
| 148 | w.mode().se(); | ||
| 149 | } else { | ||
| 150 | w.mode().diff(); | ||
| 151 | } | ||
| 152 | w.resp().variant(cc.resistor); | ||
| 153 | w.resn().bypass(); | ||
| 154 | if !matches!(oversample, Oversample::BYPASS) { | ||
| 155 | w.burst().enabled(); | ||
| 156 | } else { | ||
| 157 | w.burst().disabled(); | ||
| 158 | } | ||
| 159 | w | ||
| 160 | }); | ||
| 161 | } | ||
| 108 | 162 | ||
| 109 | // Disable all events interrupts | 163 | // Disable all events interrupts |
| 110 | r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); | 164 | r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); |
| @@ -131,18 +185,16 @@ impl<'d> OneShot<'d> { | |||
| 131 | unsafe { &*SAADC::ptr() } | 185 | unsafe { &*SAADC::ptr() } |
| 132 | } | 186 | } |
| 133 | 187 | ||
| 134 | pub async fn sample(&mut self, pin: &mut impl PositivePin) -> i16 { | 188 | pub async fn sample(&mut self, buf: &mut [i16; N]) { |
| 135 | let r = Self::regs(); | 189 | let r = Self::regs(); |
| 136 | 190 | ||
| 137 | // Set positive channel | ||
| 138 | r.ch[0].pselp.write(|w| w.pselp().variant(pin.channel())); | ||
| 139 | |||
| 140 | // Set up the DMA | 191 | // Set up the DMA |
| 141 | let mut val: i16 = 0; | ||
| 142 | r.result | 192 | r.result |
| 143 | .ptr | 193 | .ptr |
| 144 | .write(|w| unsafe { w.ptr().bits(((&mut val) as *mut _) as u32) }); | 194 | .write(|w| unsafe { w.ptr().bits(buf.as_mut_ptr() as u32) }); |
| 145 | r.result.maxcnt.write(|w| unsafe { w.maxcnt().bits(1) }); | 195 | r.result |
| 196 | .maxcnt | ||
| 197 | .write(|w| unsafe { w.maxcnt().bits(N as _) }); | ||
| 146 | 198 | ||
| 147 | // Reset and enable the end event | 199 | // Reset and enable the end event |
| 148 | r.events_end.reset(); | 200 | r.events_end.reset(); |
| @@ -169,60 +221,27 @@ impl<'d> OneShot<'d> { | |||
| 169 | Poll::Pending | 221 | Poll::Pending |
| 170 | }) | 222 | }) |
| 171 | .await; | 223 | .await; |
| 172 | |||
| 173 | // The DMA wrote the sampled value to `val`. | ||
| 174 | val | ||
| 175 | } | 224 | } |
| 176 | } | 225 | } |
| 177 | 226 | ||
| 178 | impl<'d> Drop for OneShot<'d> { | 227 | impl<'d, const N: usize> Drop for OneShot<'d, N> { |
| 179 | fn drop(&mut self) { | 228 | fn drop(&mut self) { |
| 180 | let r = Self::regs(); | 229 | let r = Self::regs(); |
| 181 | r.enable.write(|w| w.enable().disabled()); | 230 | r.enable.write(|w| w.enable().disabled()); |
| 182 | } | 231 | } |
| 183 | } | 232 | } |
| 184 | 233 | ||
| 185 | /// A pin that can be used as the positive end of a ADC differential in the SAADC periperhal. | 234 | /// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal. |
| 186 | /// | 235 | pub trait Input { |
| 187 | /// Currently negative is always shorted to ground (0V). | 236 | fn channel(&self) -> InputChannel; |
| 188 | pub trait PositivePin { | ||
| 189 | fn channel(&self) -> PositiveChannel; | ||
| 190 | } | 237 | } |
| 191 | 238 | ||
| 192 | macro_rules! positive_pin_mappings { | 239 | macro_rules! impl_saadc_input { |
| 193 | ( $($ch:ident => $pin:ident,)*) => { | 240 | ($pin:ident, $ch:ident) => { |
| 194 | $( | 241 | impl crate::saadc::Input for crate::peripherals::$pin { |
| 195 | impl PositivePin for crate::peripherals::$pin { | 242 | fn channel(&self) -> crate::saadc::InputChannel { |
| 196 | fn channel(&self) -> PositiveChannel { | 243 | crate::saadc::InputChannel::$ch |
| 197 | PositiveChannel::$ch | ||
| 198 | } | ||
| 199 | } | 244 | } |
| 200 | )* | 245 | } |
| 201 | }; | 246 | }; |
| 202 | } | 247 | } |
| 203 | |||
| 204 | // TODO the variant names are unchecked | ||
| 205 | // the pins are copied from nrf hal | ||
| 206 | #[cfg(feature = "nrf9160")] | ||
| 207 | positive_pin_mappings! { | ||
| 208 | ANALOGINPUT0 => P0_13, | ||
| 209 | ANALOGINPUT1 => P0_14, | ||
| 210 | ANALOGINPUT2 => P0_15, | ||
| 211 | ANALOGINPUT3 => P0_16, | ||
| 212 | ANALOGINPUT4 => P0_17, | ||
| 213 | ANALOGINPUT5 => P0_18, | ||
| 214 | ANALOGINPUT6 => P0_19, | ||
| 215 | ANALOGINPUT7 => P0_20, | ||
| 216 | } | ||
| 217 | |||
| 218 | #[cfg(not(feature = "nrf9160"))] | ||
| 219 | positive_pin_mappings! { | ||
| 220 | ANALOGINPUT0 => P0_02, | ||
| 221 | ANALOGINPUT1 => P0_03, | ||
| 222 | ANALOGINPUT2 => P0_04, | ||
| 223 | ANALOGINPUT3 => P0_05, | ||
| 224 | ANALOGINPUT4 => P0_28, | ||
| 225 | ANALOGINPUT5 => P0_29, | ||
| 226 | ANALOGINPUT6 => P0_30, | ||
| 227 | ANALOGINPUT7 => P0_31, | ||
| 228 | } | ||
diff --git a/embassy-nrf/src/util.rs b/embassy-nrf/src/util.rs index 7c3974eeb..344fb01f9 100644 --- a/embassy-nrf/src/util.rs +++ b/embassy-nrf/src/util.rs | |||
| @@ -8,7 +8,7 @@ pub(crate) fn slice_in_ram(slice: &[u8]) -> bool { | |||
| 8 | } | 8 | } |
| 9 | 9 | ||
| 10 | /// Return an error if slice is not in RAM. | 10 | /// Return an error if slice is not in RAM. |
| 11 | #[cfg(not(feature = "51"))] | 11 | #[cfg(not(feature = "nrf51"))] |
| 12 | pub(crate) fn slice_in_ram_or<T>(slice: &[u8], err: T) -> Result<(), T> { | 12 | pub(crate) fn slice_in_ram_or<T>(slice: &[u8], err: T) -> Result<(), T> { |
| 13 | if slice.len() == 0 || slice_in_ram(slice) { | 13 | if slice.len() == 0 || slice_in_ram(slice) { |
| 14 | Ok(()) | 14 | Ok(()) |
diff --git a/examples/nrf/src/bin/saadc.rs b/examples/nrf/src/bin/saadc.rs index c4d23360e..d12717c04 100644 --- a/examples/nrf/src/bin/saadc.rs +++ b/examples/nrf/src/bin/saadc.rs | |||
| @@ -7,18 +7,20 @@ mod example_common; | |||
| 7 | use defmt::panic; | 7 | use defmt::panic; |
| 8 | use embassy::executor::Spawner; | 8 | use embassy::executor::Spawner; |
| 9 | use embassy::time::{Duration, Timer}; | 9 | use embassy::time::{Duration, Timer}; |
| 10 | use embassy_nrf::saadc::{Config, OneShot}; | 10 | use embassy_nrf::saadc::{ChannelConfig, Config, OneShot}; |
| 11 | use embassy_nrf::{interrupt, Peripherals}; | 11 | use embassy_nrf::{interrupt, Peripherals}; |
| 12 | use example_common::*; | 12 | use example_common::*; |
| 13 | 13 | ||
| 14 | #[embassy::main] | 14 | #[embassy::main] |
| 15 | async fn main(_spawner: Spawner, mut p: Peripherals) { | 15 | async fn main(_spawner: Spawner, mut p: Peripherals) { |
| 16 | let config = Config::default(); | 16 | let config = Config::default(); |
| 17 | let mut saadc = OneShot::new(p.SAADC, interrupt::take!(SAADC), config); | 17 | let channel_config = ChannelConfig::single_ended(&mut p.P0_02); |
| 18 | let mut saadc = OneShot::new(p.SAADC, interrupt::take!(SAADC), config, [channel_config]); | ||
| 18 | 19 | ||
| 19 | loop { | 20 | loop { |
| 20 | let sample = saadc.sample(&mut p.P0_02).await; | 21 | let mut buf = [0; 1]; |
| 21 | info!("sample: {=i16}", sample); | 22 | saadc.sample(&mut buf).await; |
| 23 | info!("sample: {=i16}", &buf[0]); | ||
| 22 | Timer::after(Duration::from_millis(100)).await; | 24 | Timer::after(Duration::from_millis(100)).await; |
| 23 | } | 25 | } |
| 24 | } | 26 | } |
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 7dfcdb0fe..371ac68cc 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml | |||
| @@ -23,6 +23,10 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" | |||
| 23 | embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } | 23 | embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } |
| 24 | embassy-macros = { path = "../../embassy-macros" } | 24 | embassy-macros = { path = "../../embassy-macros" } |
| 25 | 25 | ||
| 26 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time"] } | ||
| 27 | lorawan-device = { git = "https://github.com/lulf/rust-lorawan.git", rev = "a373d06fa8858d251bc70d5789cebcd9a638ec42", default-features = false, features = ["async"] } | ||
| 28 | lorawan-encoding = { git = "https://github.com/lulf/rust-lorawan.git", rev = "a373d06fa8858d251bc70d5789cebcd9a638ec42", default-features = false, features = ["default-crypto"] } | ||
| 29 | |||
| 26 | defmt = "0.2.3" | 30 | defmt = "0.2.3" |
| 27 | defmt-rtt = "0.2.0" | 31 | defmt-rtt = "0.2.0" |
| 28 | 32 | ||
diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs new file mode 100644 index 000000000..5ca69f9a7 --- /dev/null +++ b/examples/stm32l0/src/bin/lorawan.rs | |||
| @@ -0,0 +1,104 @@ | |||
| 1 | //! This example runs on the STM32 LoRa Discovery board which has a builtin Semtech Sx127x radio | ||
| 2 | #![no_std] | ||
| 3 | #![no_main] | ||
| 4 | #![macro_use] | ||
| 5 | #![allow(dead_code)] | ||
| 6 | #![feature(generic_associated_types)] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | #[path = "../example_common.rs"] | ||
| 10 | mod example_common; | ||
| 11 | |||
| 12 | use embassy_lora::{sx127x::*, LoraTimer}; | ||
| 13 | use embassy_stm32::{ | ||
| 14 | dbgmcu::Dbgmcu, | ||
| 15 | dma::NoDma, | ||
| 16 | exti::ExtiInput, | ||
| 17 | gpio::{Input, Level, Output, Pull, Speed}, | ||
| 18 | rcc, | ||
| 19 | rng::Rng, | ||
| 20 | spi, | ||
| 21 | time::U32Ext, | ||
| 22 | Peripherals, | ||
| 23 | }; | ||
| 24 | use lorawan_device::async_device::{region, Device, JoinMode}; | ||
| 25 | use lorawan_encoding::default_crypto::DefaultFactory as Crypto; | ||
| 26 | |||
| 27 | fn config() -> embassy_stm32::Config { | ||
| 28 | let mut config = embassy_stm32::Config::default(); | ||
| 29 | config.rcc = config.rcc.clock_src(embassy_stm32::rcc::ClockSrc::HSI16); | ||
| 30 | config | ||
| 31 | } | ||
| 32 | |||
| 33 | #[embassy::main(config = "config()")] | ||
| 34 | async fn main(_spawner: embassy::executor::Spawner, mut p: Peripherals) { | ||
| 35 | unsafe { | ||
| 36 | Dbgmcu::enable_all(); | ||
| 37 | } | ||
| 38 | |||
| 39 | let mut rcc = rcc::Rcc::new(p.RCC); | ||
| 40 | let _ = rcc.enable_hsi48(&mut p.SYSCFG, p.CRS); | ||
| 41 | |||
| 42 | // SPI for sx127x | ||
| 43 | let spi = spi::Spi::new( | ||
| 44 | p.SPI1, | ||
| 45 | p.PB3, | ||
| 46 | p.PA7, | ||
| 47 | p.PA6, | ||
| 48 | NoDma, | ||
| 49 | NoDma, | ||
| 50 | 200_000.hz(), | ||
| 51 | spi::Config::default(), | ||
| 52 | ); | ||
| 53 | |||
| 54 | let cs = Output::new(p.PA15, Level::High, Speed::Low); | ||
| 55 | let reset = Output::new(p.PC0, Level::High, Speed::Low); | ||
| 56 | let _ = Input::new(p.PB1, Pull::None); | ||
| 57 | |||
| 58 | let ready = Input::new(p.PB4, Pull::Up); | ||
| 59 | let ready_pin = ExtiInput::new(ready, p.EXTI4); | ||
| 60 | |||
| 61 | let radio = Sx127xRadio::new( | ||
| 62 | spi, | ||
| 63 | cs, | ||
| 64 | reset, | ||
| 65 | ready_pin, | ||
| 66 | DummySwitch, | ||
| 67 | &mut embassy::time::Delay, | ||
| 68 | ) | ||
| 69 | .unwrap(); | ||
| 70 | |||
| 71 | let region = region::EU868::default().into(); | ||
| 72 | let mut radio_buffer = [0; 256]; | ||
| 73 | let mut device: Device<'_, _, Crypto, _, _> = Device::new( | ||
| 74 | region, | ||
| 75 | radio, | ||
| 76 | LoraTimer, | ||
| 77 | Rng::new(p.RNG), | ||
| 78 | &mut radio_buffer[..], | ||
| 79 | ); | ||
| 80 | |||
| 81 | defmt::info!("Joining LoRaWAN network"); | ||
| 82 | |||
| 83 | // TODO: Adjust the EUI and Keys according to your network credentials | ||
| 84 | device | ||
| 85 | .join(&JoinMode::OTAA { | ||
| 86 | deveui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 87 | appeui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 88 | appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | ||
| 89 | }) | ||
| 90 | .await | ||
| 91 | .ok() | ||
| 92 | .unwrap(); | ||
| 93 | defmt::info!("LoRaWAN network joined"); | ||
| 94 | |||
| 95 | defmt::info!("Sending 'PING'"); | ||
| 96 | device.send(b"PING", 1, false).await.ok().unwrap(); | ||
| 97 | defmt::info!("Message sent!"); | ||
| 98 | } | ||
| 99 | |||
| 100 | pub struct DummySwitch; | ||
| 101 | impl RadioSwitch for DummySwitch { | ||
| 102 | fn set_rx(&mut self) {} | ||
| 103 | fn set_tx(&mut self) {} | ||
| 104 | } | ||
diff --git a/examples/stm32wl55/Cargo.toml b/examples/stm32wl55/Cargo.toml index d0c727862..4688bdad1 100644 --- a/examples/stm32wl55/Cargo.toml +++ b/examples/stm32wl55/Cargo.toml | |||
| @@ -19,8 +19,12 @@ defmt-error = [] | |||
| 19 | [dependencies] | 19 | [dependencies] |
| 20 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-trace"] } | 20 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-trace"] } |
| 21 | embassy-traits = { version = "0.1.0", path = "../../embassy-traits", features = ["defmt"] } | 21 | embassy-traits = { version = "0.1.0", path = "../../embassy-traits", features = ["defmt"] } |
| 22 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "defmt-trace", "stm32wl55jc_cm4", "time-driver-tim2", "memory-x", "subghz"] } | 22 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "defmt-trace", "stm32wl55jc_cm4", "time-driver-tim2", "memory-x", "subghz", "unstable-pac"] } |
| 23 | embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } | 23 | embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } |
| 24 | embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time"] } | ||
| 25 | |||
| 26 | lorawan-device = { git = "https://github.com/lulf/rust-lorawan.git", rev = "a373d06fa8858d251bc70d5789cebcd9a638ec42", default-features = false, features = ["async"] } | ||
| 27 | lorawan-encoding = { git = "https://github.com/lulf/rust-lorawan.git", rev = "a373d06fa8858d251bc70d5789cebcd9a638ec42", default-features = false, features = ["default-crypto"] } | ||
| 24 | 28 | ||
| 25 | defmt = "0.2.3" | 29 | defmt = "0.2.3" |
| 26 | defmt-rtt = "0.2.0" | 30 | defmt-rtt = "0.2.0" |
diff --git a/examples/stm32wl55/src/bin/lorawan.rs b/examples/stm32wl55/src/bin/lorawan.rs new file mode 100644 index 000000000..155905ae7 --- /dev/null +++ b/examples/stm32wl55/src/bin/lorawan.rs | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![macro_use] | ||
| 4 | #![allow(dead_code)] | ||
| 5 | #![feature(generic_associated_types)] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | #[path = "../example_common.rs"] | ||
| 9 | mod example_common; | ||
| 10 | |||
| 11 | use embassy_lora::{stm32wl::*, LoraTimer}; | ||
| 12 | use embassy_stm32::{ | ||
| 13 | dbgmcu::Dbgmcu, | ||
| 14 | dma::NoDma, | ||
| 15 | gpio::{Level, Output, Pin, Speed}, | ||
| 16 | interrupt, pac, rcc, | ||
| 17 | rng::Rng, | ||
| 18 | subghz::*, | ||
| 19 | Peripherals, | ||
| 20 | }; | ||
| 21 | use lorawan_device::async_device::{region, Device, JoinMode}; | ||
| 22 | use lorawan_encoding::default_crypto::DefaultFactory as Crypto; | ||
| 23 | |||
| 24 | fn config() -> embassy_stm32::Config { | ||
| 25 | let mut config = embassy_stm32::Config::default(); | ||
| 26 | config.rcc = config.rcc.clock_src(embassy_stm32::rcc::ClockSrc::HSI16); | ||
| 27 | config | ||
| 28 | } | ||
| 29 | |||
| 30 | #[embassy::main(config = "config()")] | ||
| 31 | async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) { | ||
| 32 | unsafe { | ||
| 33 | Dbgmcu::enable_all(); | ||
| 34 | let mut rcc = rcc::Rcc::new(p.RCC); | ||
| 35 | rcc.enable_lsi(); | ||
| 36 | pac::RCC.ccipr().modify(|w| { | ||
| 37 | w.set_rngsel(0b01); | ||
| 38 | }); | ||
| 39 | } | ||
| 40 | |||
| 41 | let ctrl1 = Output::new(p.PC3.degrade(), Level::High, Speed::High); | ||
| 42 | let ctrl2 = Output::new(p.PC4.degrade(), Level::High, Speed::High); | ||
| 43 | let ctrl3 = Output::new(p.PC5.degrade(), Level::High, Speed::High); | ||
| 44 | let rfs = RadioSwitch::new(ctrl1, ctrl2, ctrl3); | ||
| 45 | |||
| 46 | let radio = SubGhz::new(p.SUBGHZSPI, p.PA5, p.PA7, p.PA6, NoDma, NoDma); | ||
| 47 | |||
| 48 | let irq = interrupt::take!(SUBGHZ_RADIO); | ||
| 49 | static mut RADIO_STATE: SubGhzState<'static> = SubGhzState::new(); | ||
| 50 | let radio = unsafe { SubGhzRadio::new(&mut RADIO_STATE, radio, rfs, irq) }; | ||
| 51 | |||
| 52 | let region = region::EU868::default().into(); | ||
| 53 | let mut radio_buffer = [0; 256]; | ||
| 54 | let mut device: Device<'_, _, Crypto, _, _> = Device::new( | ||
| 55 | region, | ||
| 56 | radio, | ||
| 57 | LoraTimer, | ||
| 58 | Rng::new(p.RNG), | ||
| 59 | &mut radio_buffer[..], | ||
| 60 | ); | ||
| 61 | |||
| 62 | defmt::info!("Joining LoRaWAN network"); | ||
| 63 | |||
| 64 | // TODO: Adjust the EUI and Keys according to your network credentials | ||
| 65 | device | ||
| 66 | .join(&JoinMode::OTAA { | ||
| 67 | deveui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 68 | appeui: [0, 0, 0, 0, 0, 0, 0, 0], | ||
| 69 | appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], | ||
| 70 | }) | ||
| 71 | .await | ||
| 72 | .ok() | ||
| 73 | .unwrap(); | ||
| 74 | defmt::info!("LoRaWAN network joined"); | ||
| 75 | |||
| 76 | defmt::info!("Sending 'PING'"); | ||
| 77 | device.send(b"PING", 1, false).await.ok().unwrap(); | ||
| 78 | defmt::info!("Message sent!"); | ||
| 79 | } | ||
