aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDion Dokter <[email protected]>2021-10-11 10:59:21 +0200
committerDion Dokter <[email protected]>2021-10-11 10:59:34 +0200
commita8407c8abad163d2b795d7ab068f8b9861c6df2a (patch)
tree6d8474c069d3f3c5bfaa5ad16635d3e65fcce847
parent20674f7126c52d5c9a7584f266463b3da8a1a38e (diff)
parent9fec833015579cca5f1b398745cac7ad08245f0c (diff)
Merge remote-tracking branch 'upstream/master'
-rw-r--r--README.md2
-rw-r--r--embassy-lora/Cargo.toml33
-rw-r--r--embassy-lora/src/fmt.rs225
-rw-r--r--embassy-lora/src/lib.rs23
-rw-r--r--embassy-lora/src/stm32wl/mod.rs368
-rw-r--r--embassy-lora/src/sx127x/mod.rs201
-rw-r--r--embassy-lora/src/sx127x/sx127x_lora/mod.rs593
-rw-r--r--embassy-lora/src/sx127x/sx127x_lora/register.rs107
-rw-r--r--embassy-nrf/src/chips/nrf52805.rs26
-rw-r--r--embassy-nrf/src/chips/nrf52810.rs42
-rw-r--r--embassy-nrf/src/chips/nrf52811.rs42
-rw-r--r--embassy-nrf/src/chips/nrf52820.rs36
-rw-r--r--embassy-nrf/src/chips/nrf52832.rs42
-rw-r--r--embassy-nrf/src/chips/nrf52833.rs42
-rw-r--r--embassy-nrf/src/chips/nrf52840.rs42
-rw-r--r--embassy-nrf/src/chips/nrf9160.rs26
-rw-r--r--embassy-nrf/src/gpiote.rs8
-rw-r--r--embassy-nrf/src/ppi.rs90
-rw-r--r--embassy-nrf/src/saadc.rs173
-rw-r--r--embassy-nrf/src/util.rs2
-rw-r--r--examples/nrf/src/bin/saadc.rs10
-rw-r--r--examples/stm32l0/Cargo.toml4
-rw-r--r--examples/stm32l0/src/bin/lorawan.rs104
-rw-r--r--examples/stm32wl55/Cargo.toml6
-rw-r--r--examples/stm32wl55/src/bin/lorawan.rs79
25 files changed, 2160 insertions, 166 deletions
diff --git a/README.md b/README.md
index 13bcf6e5b..f8898dcd6 100644
--- a/README.md
+++ b/README.md
@@ -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]
2name = "embassy-lora"
3version = "0.1.0"
4authors = ["Ulf Lilleengen <[email protected]>"]
5edition = "2018"
6
7[lib]
8
9[features]
10sx127x = []
11stm32wl = ["embassy-stm32", "embassy-stm32/subghz"]
12time = []
13
14defmt-trace = []
15defmt-debug = []
16defmt-info = []
17defmt-warn = []
18defmt-error = []
19
20[dependencies]
21
22defmt = { version = "0.2.3", optional = true }
23log = { version = "0.4.14", optional = true }
24
25embassy = { version = "0.1.0", path = "../embassy", default-features = false }
26embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
27embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false }
28futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
29embedded-hal = { version = "0.2", features = ["unproven"] }
30bit_field = { version = "0.10" }
31
32lorawan-device = { git = "https://github.com/lulf/rust-lorawan.git", rev = "a373d06fa8858d251bc70d5789cebcd9a638ec42", default-features = false, features = ["async"] }
33lorawan-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"))]
5compile_error!("You may not enable both `defmt` and `log` features.");
6
7macro_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
18macro_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
29macro_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
40macro_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
51macro_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
62macro_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
73macro_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
84macro_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
95macro_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
106macro_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
119macro_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
132macro_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
145macro_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
158macro_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")]
172macro_rules! unwrap {
173 ($($x:tt)*) => {
174 ::defmt::unwrap!($($x)*)
175 };
176}
177
178#[cfg(not(feature = "defmt"))]
179macro_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)]
199pub struct NoneError;
200
201pub trait Try {
202 type Ok;
203 type Error;
204 fn into_result(self) -> Result<Self::Ok, Self::Error>;
205}
206
207impl<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
217impl<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
7pub(crate) mod fmt;
8
9#[cfg(feature = "stm32wl")]
10pub mod stm32wl;
11#[cfg(feature = "sx127x")]
12pub mod sx127x;
13
14/// A convenience timer to use with the LoRaWAN crate
15pub struct LoraTimer;
16
17#[cfg(feature = "time")]
18impl 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.
2use core::future::Future;
3use core::mem::MaybeUninit;
4use embassy::channel::signal::Signal;
5use embassy::interrupt::InterruptExt;
6use embassy::util::Unborrow;
7use embassy_hal_common::unborrow;
8use 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};
19use embedded_hal::digital::v2::OutputPin;
20use 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))]
27pub enum State {
28 Idle,
29 Txing,
30 Rxing,
31}
32
33#[derive(Debug, Copy, Clone)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35pub struct RadioError;
36
37static IRQ: Signal<(Status, u16)> = Signal::new();
38
39struct StateInner<'a> {
40 radio: SubGhz<'a, NoDma, NoDma>,
41 switch: RadioSwitch<'a>,
42}
43
44/// External state storage for the radio state
45pub struct SubGhzState<'a>(MaybeUninit<StateInner<'a>>);
46impl<'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.
53pub struct SubGhzRadio<'a> {
54 state: *mut StateInner<'a>,
55 _irq: SUBGHZ_RADIO,
56}
57
58impl<'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
95impl<'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
276impl 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
296impl<'a> From<embassy_stm32::spi::Error> for RadioError {
297 fn from(_: embassy_stm32::spi::Error) -> Self {
298 RadioError
299 }
300}
301
302impl<'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.
312pub struct RadioSwitch<'a> {
313 ctrl1: Output<'a, AnyPin>,
314 ctrl2: Output<'a, AnyPin>,
315 ctrl3: Output<'a, AnyPin>,
316}
317
318impl<'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
351fn 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
362fn 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 @@
1use core::future::Future;
2use embassy::traits::gpio::WaitForRisingEdge;
3use embedded_hal::blocking::delay::DelayMs;
4use embedded_hal::blocking::spi::{Transfer, Write};
5use embedded_hal::digital::v2::OutputPin;
6use lorawan_device::async_device::{
7 radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig},
8 Timings,
9};
10
11mod sx127x_lora;
12use 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.
16pub trait RadioSwitch {
17 fn set_tx(&mut self);
18 fn set_rx(&mut self);
19}
20
21/// Semtech Sx127x radio peripheral
22pub struct Sx127xRadio<SPI, CS, RESET, E, I, RFS>
23where
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))]
38pub enum State {
39 Idle,
40 Txing,
41 Rxing,
42}
43
44impl<SPI, CS, RESET, E, I, RFS> Sx127xRadio<SPI, CS, RESET, E, I, RFS>
45where
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
66impl<SPI, CS, RESET, E, I, RFS> Timings for Sx127xRadio<SPI, CS, RESET, E, I, RFS>
67where
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
82impl<SPI, CS, RESET, E, I, RFS> PhyRxTx for Sx127xRadio<SPI, CS, RESET, E, I, RFS>
83where
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))]
176pub struct Sx127xError;
177
178impl<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
184fn 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
195fn 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
8use bit_field::BitField;
9use embedded_hal::blocking::{
10 delay::DelayMs,
11 spi::{Transfer, Write},
12};
13use embedded_hal::digital::v2::OutputPin;
14use embedded_hal::spi::{Mode, Phase, Polarity};
15
16mod register;
17use self::register::PaConfig;
18use self::register::Register;
19pub use self::register::IRQ;
20
21/// Provides the necessary SPI mode configuration for the radio
22pub 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
28pub 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))]
39pub enum Error<SPI, CS, RESET> {
40 Uninformative,
41 VersionMismatch(u8),
42 CS(CS),
43 Reset(RESET),
44 SPI(SPI),
45 Transmitting,
46}
47
48use super::sx127x_lora::register::{FskDataModulationShaping, FskRampUpRamDown};
49use Error::*;
50
51#[cfg(not(feature = "version_0x09"))]
52const VERSION_CHECK: u8 = 0x12;
53
54#[cfg(feature = "version_0x09")]
55const VERSION_CHECK: u8 = 0x09;
56
57impl<SPI, CS, RESET, E> LoRa<SPI, CS, RESET>
58where
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)]
579pub enum RadioMode {
580 LongRangeMode = 0x80,
581 Sleep = 0x00,
582 Stdby = 0x01,
583 Tx = 0x03,
584 RxContinuous = 0x05,
585 RxSingle = 0x06,
586}
587
588impl 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)]
8pub 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)]
51pub enum PaConfig {
52 PaBoost = 0x80,
53 PaOutputRfoPin = 0,
54}
55
56#[derive(Clone, Copy)]
57pub enum IRQ {
58 IrqTxDoneMask = 0x08,
59 IrqPayloadCrcErrorMask = 0x20,
60 IrqRxDoneMask = 0x40,
61}
62
63impl Register {
64 pub fn addr(self) -> u8 {
65 self as u8
66 }
67}
68
69impl PaConfig {
70 pub fn addr(self) -> u8 {
71 self as u8
72 }
73}
74
75impl IRQ {
76 pub fn addr(self) -> u8 {
77 self as u8
78 }
79}
80
81#[derive(Clone, Copy)]
82pub enum FskDataModulationShaping {
83 None = 1,
84 GaussianBt1d0 = 2,
85 GaussianBt0d5 = 10,
86 GaussianBt0d3 = 11,
87}
88
89#[derive(Clone, Copy)]
90pub 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);
159impl_pin!(P0_30, 0, 30); 159impl_pin!(P0_30, 0, 30);
160impl_pin!(P0_31, 0, 31); 160impl_pin!(P0_31, 0, 31);
161 161
162impl_ppi_channel!(PPI_CH0, 0, configurable);
163impl_ppi_channel!(PPI_CH1, 1, configurable);
164impl_ppi_channel!(PPI_CH2, 2, configurable);
165impl_ppi_channel!(PPI_CH3, 3, configurable);
166impl_ppi_channel!(PPI_CH4, 4, configurable);
167impl_ppi_channel!(PPI_CH5, 5, configurable);
168impl_ppi_channel!(PPI_CH6, 6, configurable);
169impl_ppi_channel!(PPI_CH7, 7, configurable);
170impl_ppi_channel!(PPI_CH8, 8, configurable);
171impl_ppi_channel!(PPI_CH9, 9, configurable);
172impl_ppi_channel!(PPI_CH20, 20);
173impl_ppi_channel!(PPI_CH21, 21);
174impl_ppi_channel!(PPI_CH22, 22);
175impl_ppi_channel!(PPI_CH23, 23);
176impl_ppi_channel!(PPI_CH24, 24);
177impl_ppi_channel!(PPI_CH25, 25);
178impl_ppi_channel!(PPI_CH26, 26);
179impl_ppi_channel!(PPI_CH27, 27);
180impl_ppi_channel!(PPI_CH28, 28);
181impl_ppi_channel!(PPI_CH29, 29);
182impl_ppi_channel!(PPI_CH30, 30);
183impl_ppi_channel!(PPI_CH31, 31);
184
185impl_saadc_input!(P0_04, ANALOGINPUT2);
186impl_saadc_input!(P0_05, ANALOGINPUT3);
187
162pub mod irqs { 188pub 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);
164impl_pin!(P0_30, 0, 30); 164impl_pin!(P0_30, 0, 30);
165impl_pin!(P0_31, 0, 31); 165impl_pin!(P0_31, 0, 31);
166 166
167impl_ppi_channel!(PPI_CH0, 0, configurable);
168impl_ppi_channel!(PPI_CH1, 1, configurable);
169impl_ppi_channel!(PPI_CH2, 2, configurable);
170impl_ppi_channel!(PPI_CH3, 3, configurable);
171impl_ppi_channel!(PPI_CH4, 4, configurable);
172impl_ppi_channel!(PPI_CH5, 5, configurable);
173impl_ppi_channel!(PPI_CH6, 6, configurable);
174impl_ppi_channel!(PPI_CH7, 7, configurable);
175impl_ppi_channel!(PPI_CH8, 8, configurable);
176impl_ppi_channel!(PPI_CH9, 9, configurable);
177impl_ppi_channel!(PPI_CH10, 10, configurable);
178impl_ppi_channel!(PPI_CH11, 11, configurable);
179impl_ppi_channel!(PPI_CH12, 12, configurable);
180impl_ppi_channel!(PPI_CH13, 13, configurable);
181impl_ppi_channel!(PPI_CH14, 14, configurable);
182impl_ppi_channel!(PPI_CH15, 15, configurable);
183impl_ppi_channel!(PPI_CH16, 16, configurable);
184impl_ppi_channel!(PPI_CH17, 17, configurable);
185impl_ppi_channel!(PPI_CH18, 18, configurable);
186impl_ppi_channel!(PPI_CH19, 19, configurable);
187impl_ppi_channel!(PPI_CH20, 20);
188impl_ppi_channel!(PPI_CH21, 21);
189impl_ppi_channel!(PPI_CH22, 22);
190impl_ppi_channel!(PPI_CH23, 23);
191impl_ppi_channel!(PPI_CH24, 24);
192impl_ppi_channel!(PPI_CH25, 25);
193impl_ppi_channel!(PPI_CH26, 26);
194impl_ppi_channel!(PPI_CH27, 27);
195impl_ppi_channel!(PPI_CH28, 28);
196impl_ppi_channel!(PPI_CH29, 29);
197impl_ppi_channel!(PPI_CH30, 30);
198impl_ppi_channel!(PPI_CH31, 31);
199
200impl_saadc_input!(P0_02, ANALOGINPUT0);
201impl_saadc_input!(P0_03, ANALOGINPUT1);
202impl_saadc_input!(P0_04, ANALOGINPUT2);
203impl_saadc_input!(P0_05, ANALOGINPUT3);
204impl_saadc_input!(P0_28, ANALOGINPUT4);
205impl_saadc_input!(P0_29, ANALOGINPUT5);
206impl_saadc_input!(P0_30, ANALOGINPUT6);
207impl_saadc_input!(P0_31, ANALOGINPUT7);
208
167pub mod irqs { 209pub 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);
165impl_pin!(P0_30, 0, 30); 165impl_pin!(P0_30, 0, 30);
166impl_pin!(P0_31, 0, 31); 166impl_pin!(P0_31, 0, 31);
167 167
168impl_ppi_channel!(PPI_CH0, 0, configurable);
169impl_ppi_channel!(PPI_CH1, 1, configurable);
170impl_ppi_channel!(PPI_CH2, 2, configurable);
171impl_ppi_channel!(PPI_CH3, 3, configurable);
172impl_ppi_channel!(PPI_CH4, 4, configurable);
173impl_ppi_channel!(PPI_CH5, 5, configurable);
174impl_ppi_channel!(PPI_CH6, 6, configurable);
175impl_ppi_channel!(PPI_CH7, 7, configurable);
176impl_ppi_channel!(PPI_CH8, 8, configurable);
177impl_ppi_channel!(PPI_CH9, 9, configurable);
178impl_ppi_channel!(PPI_CH10, 10, configurable);
179impl_ppi_channel!(PPI_CH11, 11, configurable);
180impl_ppi_channel!(PPI_CH12, 12, configurable);
181impl_ppi_channel!(PPI_CH13, 13, configurable);
182impl_ppi_channel!(PPI_CH14, 14, configurable);
183impl_ppi_channel!(PPI_CH15, 15, configurable);
184impl_ppi_channel!(PPI_CH16, 16, configurable);
185impl_ppi_channel!(PPI_CH17, 17, configurable);
186impl_ppi_channel!(PPI_CH18, 18, configurable);
187impl_ppi_channel!(PPI_CH19, 19, configurable);
188impl_ppi_channel!(PPI_CH20, 20);
189impl_ppi_channel!(PPI_CH21, 21);
190impl_ppi_channel!(PPI_CH22, 22);
191impl_ppi_channel!(PPI_CH23, 23);
192impl_ppi_channel!(PPI_CH24, 24);
193impl_ppi_channel!(PPI_CH25, 25);
194impl_ppi_channel!(PPI_CH26, 26);
195impl_ppi_channel!(PPI_CH27, 27);
196impl_ppi_channel!(PPI_CH28, 28);
197impl_ppi_channel!(PPI_CH29, 29);
198impl_ppi_channel!(PPI_CH30, 30);
199impl_ppi_channel!(PPI_CH31, 31);
200
201impl_saadc_input!(P0_02, ANALOGINPUT0);
202impl_saadc_input!(P0_03, ANALOGINPUT1);
203impl_saadc_input!(P0_04, ANALOGINPUT2);
204impl_saadc_input!(P0_05, ANALOGINPUT3);
205impl_saadc_input!(P0_28, ANALOGINPUT4);
206impl_saadc_input!(P0_29, ANALOGINPUT5);
207impl_saadc_input!(P0_30, ANALOGINPUT6);
208impl_saadc_input!(P0_31, ANALOGINPUT7);
209
168pub mod irqs { 210pub 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);
163impl_pin!(P0_30, 0, 30); 160impl_pin!(P0_30, 0, 30);
164impl_pin!(P0_31, 0, 31); 161impl_pin!(P0_31, 0, 31);
165 162
163impl_ppi_channel!(PPI_CH0, 0, configurable);
164impl_ppi_channel!(PPI_CH1, 1, configurable);
165impl_ppi_channel!(PPI_CH2, 2, configurable);
166impl_ppi_channel!(PPI_CH3, 3, configurable);
167impl_ppi_channel!(PPI_CH4, 4, configurable);
168impl_ppi_channel!(PPI_CH5, 5, configurable);
169impl_ppi_channel!(PPI_CH6, 6, configurable);
170impl_ppi_channel!(PPI_CH7, 7, configurable);
171impl_ppi_channel!(PPI_CH8, 8, configurable);
172impl_ppi_channel!(PPI_CH9, 9, configurable);
173impl_ppi_channel!(PPI_CH10, 10, configurable);
174impl_ppi_channel!(PPI_CH11, 11, configurable);
175impl_ppi_channel!(PPI_CH12, 12, configurable);
176impl_ppi_channel!(PPI_CH13, 13, configurable);
177impl_ppi_channel!(PPI_CH14, 14, configurable);
178impl_ppi_channel!(PPI_CH15, 15, configurable);
179impl_ppi_channel!(PPI_CH16, 16, configurable);
180impl_ppi_channel!(PPI_CH17, 17, configurable);
181impl_ppi_channel!(PPI_CH18, 18, configurable);
182impl_ppi_channel!(PPI_CH19, 19, configurable);
183impl_ppi_channel!(PPI_CH20, 20);
184impl_ppi_channel!(PPI_CH21, 21);
185impl_ppi_channel!(PPI_CH22, 22);
186impl_ppi_channel!(PPI_CH23, 23);
187impl_ppi_channel!(PPI_CH24, 24);
188impl_ppi_channel!(PPI_CH25, 25);
189impl_ppi_channel!(PPI_CH26, 26);
190impl_ppi_channel!(PPI_CH27, 27);
191impl_ppi_channel!(PPI_CH28, 28);
192impl_ppi_channel!(PPI_CH29, 29);
193impl_ppi_channel!(PPI_CH30, 30);
194impl_ppi_channel!(PPI_CH31, 31);
195
166pub mod irqs { 196pub 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);
178impl_pin!(P0_30, 0, 30); 178impl_pin!(P0_30, 0, 30);
179impl_pin!(P0_31, 0, 31); 179impl_pin!(P0_31, 0, 31);
180 180
181impl_ppi_channel!(PPI_CH0, 0, configurable);
182impl_ppi_channel!(PPI_CH1, 1, configurable);
183impl_ppi_channel!(PPI_CH2, 2, configurable);
184impl_ppi_channel!(PPI_CH3, 3, configurable);
185impl_ppi_channel!(PPI_CH4, 4, configurable);
186impl_ppi_channel!(PPI_CH5, 5, configurable);
187impl_ppi_channel!(PPI_CH6, 6, configurable);
188impl_ppi_channel!(PPI_CH7, 7, configurable);
189impl_ppi_channel!(PPI_CH8, 8, configurable);
190impl_ppi_channel!(PPI_CH9, 9, configurable);
191impl_ppi_channel!(PPI_CH10, 10, configurable);
192impl_ppi_channel!(PPI_CH11, 11, configurable);
193impl_ppi_channel!(PPI_CH12, 12, configurable);
194impl_ppi_channel!(PPI_CH13, 13, configurable);
195impl_ppi_channel!(PPI_CH14, 14, configurable);
196impl_ppi_channel!(PPI_CH15, 15, configurable);
197impl_ppi_channel!(PPI_CH16, 16, configurable);
198impl_ppi_channel!(PPI_CH17, 17, configurable);
199impl_ppi_channel!(PPI_CH18, 18, configurable);
200impl_ppi_channel!(PPI_CH19, 19, configurable);
201impl_ppi_channel!(PPI_CH20, 20);
202impl_ppi_channel!(PPI_CH21, 21);
203impl_ppi_channel!(PPI_CH22, 22);
204impl_ppi_channel!(PPI_CH23, 23);
205impl_ppi_channel!(PPI_CH24, 24);
206impl_ppi_channel!(PPI_CH25, 25);
207impl_ppi_channel!(PPI_CH26, 26);
208impl_ppi_channel!(PPI_CH27, 27);
209impl_ppi_channel!(PPI_CH28, 28);
210impl_ppi_channel!(PPI_CH29, 29);
211impl_ppi_channel!(PPI_CH30, 30);
212impl_ppi_channel!(PPI_CH31, 31);
213
214impl_saadc_input!(P0_02, ANALOGINPUT0);
215impl_saadc_input!(P0_03, ANALOGINPUT1);
216impl_saadc_input!(P0_04, ANALOGINPUT2);
217impl_saadc_input!(P0_05, ANALOGINPUT3);
218impl_saadc_input!(P0_28, ANALOGINPUT4);
219impl_saadc_input!(P0_29, ANALOGINPUT5);
220impl_saadc_input!(P0_30, ANALOGINPUT6);
221impl_saadc_input!(P0_31, ANALOGINPUT7);
222
181pub mod irqs { 223pub 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);
218impl_pin!(P1_14, 1, 14); 218impl_pin!(P1_14, 1, 14);
219impl_pin!(P1_15, 1, 15); 219impl_pin!(P1_15, 1, 15);
220 220
221impl_ppi_channel!(PPI_CH0, 0, configurable);
222impl_ppi_channel!(PPI_CH1, 1, configurable);
223impl_ppi_channel!(PPI_CH2, 2, configurable);
224impl_ppi_channel!(PPI_CH3, 3, configurable);
225impl_ppi_channel!(PPI_CH4, 4, configurable);
226impl_ppi_channel!(PPI_CH5, 5, configurable);
227impl_ppi_channel!(PPI_CH6, 6, configurable);
228impl_ppi_channel!(PPI_CH7, 7, configurable);
229impl_ppi_channel!(PPI_CH8, 8, configurable);
230impl_ppi_channel!(PPI_CH9, 9, configurable);
231impl_ppi_channel!(PPI_CH10, 10, configurable);
232impl_ppi_channel!(PPI_CH11, 11, configurable);
233impl_ppi_channel!(PPI_CH12, 12, configurable);
234impl_ppi_channel!(PPI_CH13, 13, configurable);
235impl_ppi_channel!(PPI_CH14, 14, configurable);
236impl_ppi_channel!(PPI_CH15, 15, configurable);
237impl_ppi_channel!(PPI_CH16, 16, configurable);
238impl_ppi_channel!(PPI_CH17, 17, configurable);
239impl_ppi_channel!(PPI_CH18, 18, configurable);
240impl_ppi_channel!(PPI_CH19, 19, configurable);
241impl_ppi_channel!(PPI_CH20, 20);
242impl_ppi_channel!(PPI_CH21, 21);
243impl_ppi_channel!(PPI_CH22, 22);
244impl_ppi_channel!(PPI_CH23, 23);
245impl_ppi_channel!(PPI_CH24, 24);
246impl_ppi_channel!(PPI_CH25, 25);
247impl_ppi_channel!(PPI_CH26, 26);
248impl_ppi_channel!(PPI_CH27, 27);
249impl_ppi_channel!(PPI_CH28, 28);
250impl_ppi_channel!(PPI_CH29, 29);
251impl_ppi_channel!(PPI_CH30, 30);
252impl_ppi_channel!(PPI_CH31, 31);
253
254impl_saadc_input!(P0_02, ANALOGINPUT0);
255impl_saadc_input!(P0_03, ANALOGINPUT1);
256impl_saadc_input!(P0_04, ANALOGINPUT2);
257impl_saadc_input!(P0_05, ANALOGINPUT3);
258impl_saadc_input!(P0_28, ANALOGINPUT4);
259impl_saadc_input!(P0_29, ANALOGINPUT5);
260impl_saadc_input!(P0_30, ANALOGINPUT6);
261impl_saadc_input!(P0_31, ANALOGINPUT7);
262
221pub mod irqs { 263pub 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);
223impl_pin!(P1_14, 1, 14); 223impl_pin!(P1_14, 1, 14);
224impl_pin!(P1_15, 1, 15); 224impl_pin!(P1_15, 1, 15);
225 225
226impl_ppi_channel!(PPI_CH0, 0, configurable);
227impl_ppi_channel!(PPI_CH1, 1, configurable);
228impl_ppi_channel!(PPI_CH2, 2, configurable);
229impl_ppi_channel!(PPI_CH3, 3, configurable);
230impl_ppi_channel!(PPI_CH4, 4, configurable);
231impl_ppi_channel!(PPI_CH5, 5, configurable);
232impl_ppi_channel!(PPI_CH6, 6, configurable);
233impl_ppi_channel!(PPI_CH7, 7, configurable);
234impl_ppi_channel!(PPI_CH8, 8, configurable);
235impl_ppi_channel!(PPI_CH9, 9, configurable);
236impl_ppi_channel!(PPI_CH10, 10, configurable);
237impl_ppi_channel!(PPI_CH11, 11, configurable);
238impl_ppi_channel!(PPI_CH12, 12, configurable);
239impl_ppi_channel!(PPI_CH13, 13, configurable);
240impl_ppi_channel!(PPI_CH14, 14, configurable);
241impl_ppi_channel!(PPI_CH15, 15, configurable);
242impl_ppi_channel!(PPI_CH16, 16, configurable);
243impl_ppi_channel!(PPI_CH17, 17, configurable);
244impl_ppi_channel!(PPI_CH18, 18, configurable);
245impl_ppi_channel!(PPI_CH19, 19, configurable);
246impl_ppi_channel!(PPI_CH20, 20);
247impl_ppi_channel!(PPI_CH21, 21);
248impl_ppi_channel!(PPI_CH22, 22);
249impl_ppi_channel!(PPI_CH23, 23);
250impl_ppi_channel!(PPI_CH24, 24);
251impl_ppi_channel!(PPI_CH25, 25);
252impl_ppi_channel!(PPI_CH26, 26);
253impl_ppi_channel!(PPI_CH27, 27);
254impl_ppi_channel!(PPI_CH28, 28);
255impl_ppi_channel!(PPI_CH29, 29);
256impl_ppi_channel!(PPI_CH30, 30);
257impl_ppi_channel!(PPI_CH31, 31);
258
259impl_saadc_input!(P0_02, ANALOGINPUT0);
260impl_saadc_input!(P0_03, ANALOGINPUT1);
261impl_saadc_input!(P0_04, ANALOGINPUT2);
262impl_saadc_input!(P0_05, ANALOGINPUT3);
263impl_saadc_input!(P0_28, ANALOGINPUT4);
264impl_saadc_input!(P0_29, ANALOGINPUT5);
265impl_saadc_input!(P0_30, ANALOGINPUT6);
266impl_saadc_input!(P0_31, ANALOGINPUT7);
267
226pub mod irqs { 268pub 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);
171impl_pin!(P0_30, 0, 30); 171impl_pin!(P0_30, 0, 30);
172impl_pin!(P0_31, 0, 31); 172impl_pin!(P0_31, 0, 31);
173 173
174impl_ppi_channel!(PPI_CH0, 0, configurable);
175impl_ppi_channel!(PPI_CH1, 1, configurable);
176impl_ppi_channel!(PPI_CH2, 2, configurable);
177impl_ppi_channel!(PPI_CH3, 3, configurable);
178impl_ppi_channel!(PPI_CH4, 4, configurable);
179impl_ppi_channel!(PPI_CH5, 5, configurable);
180impl_ppi_channel!(PPI_CH6, 6, configurable);
181impl_ppi_channel!(PPI_CH7, 7, configurable);
182impl_ppi_channel!(PPI_CH8, 8, configurable);
183impl_ppi_channel!(PPI_CH9, 9, configurable);
184impl_ppi_channel!(PPI_CH10, 10, configurable);
185impl_ppi_channel!(PPI_CH11, 11, configurable);
186impl_ppi_channel!(PPI_CH12, 12, configurable);
187impl_ppi_channel!(PPI_CH13, 13, configurable);
188impl_ppi_channel!(PPI_CH14, 14, configurable);
189impl_ppi_channel!(PPI_CH15, 15, configurable);
190
191impl_saadc_input!(P0_13, ANALOGINPUT0);
192impl_saadc_input!(P0_14, ANALOGINPUT1);
193impl_saadc_input!(P0_15, ANALOGINPUT2);
194impl_saadc_input!(P0_16, ANALOGINPUT3);
195impl_saadc_input!(P0_17, ANALOGINPUT4);
196impl_saadc_input!(P0_18, ANALOGINPUT5);
197impl_saadc_input!(P0_19, ANALOGINPUT6);
198impl_saadc_input!(P0_20, ANALOGINPUT7);
199
174pub mod irqs { 200pub 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
146mod sealed { 148pub(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
204macro_rules! impl_channel { 206macro_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
220pub use channel_impl::*;
221#[cfg(not(feature = "nrf9160"))]
222mod 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
263mod 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);
310impl_group!(PPI_GROUP1, 1); 248impl_group!(PPI_GROUP1, 1);
311impl_group!(PPI_GROUP2, 2); 249impl_group!(PPI_GROUP2, 2);
312impl_group!(PPI_GROUP3, 3); 250impl_group!(PPI_GROUP3, 3);
313#[cfg(not(feature = "51"))] 251#[cfg(not(feature = "nrf51"))]
314impl_group!(PPI_GROUP4, 4); 252impl_group!(PPI_GROUP4, 4);
315#[cfg(not(feature = "51"))] 253#[cfg(not(feature = "nrf51"))]
316impl_group!(PPI_GROUP5, 5); 254impl_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
1use core::marker::PhantomData; 3use core::marker::PhantomData;
2use core::sync::atomic::{compiler_fence, Ordering}; 4use core::sync::atomic::{compiler_fence, Ordering};
3use core::task::Poll; 5use core::task::Poll;
@@ -18,7 +20,7 @@ use pac::{saadc_ns as saadc, SAADC_NS as SAADC};
18pub use saadc::{ 20pub 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::{
30pub enum Error {} 32pub enum Error {}
31 33
32/// One-shot saadc. Continuous sample mode TODO. 34/// One-shot saadc. Continuous sample mode TODO.
33pub struct OneShot<'d> { 35pub 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]
42pub struct Config { 45pub 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
52impl 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]
66pub 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
57impl Default for Config { 83impl<'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
70impl<'d> OneShot<'d> { 115impl<'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
178impl<'d> Drop for OneShot<'d> { 227impl<'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/// 235pub trait Input {
187/// Currently negative is always shorted to ground (0V). 236 fn channel(&self) -> InputChannel;
188pub trait PositivePin {
189 fn channel(&self) -> PositiveChannel;
190} 237}
191 238
192macro_rules! positive_pin_mappings { 239macro_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")]
207positive_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"))]
219positive_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"))]
12pub(crate) fn slice_in_ram_or<T>(slice: &[u8], err: T) -> Result<(), T> { 12pub(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;
7use defmt::panic; 7use defmt::panic;
8use embassy::executor::Spawner; 8use embassy::executor::Spawner;
9use embassy::time::{Duration, Timer}; 9use embassy::time::{Duration, Timer};
10use embassy_nrf::saadc::{Config, OneShot}; 10use embassy_nrf::saadc::{ChannelConfig, Config, OneShot};
11use embassy_nrf::{interrupt, Peripherals}; 11use embassy_nrf::{interrupt, Peripherals};
12use example_common::*; 12use example_common::*;
13 13
14#[embassy::main] 14#[embassy::main]
15async fn main(_spawner: Spawner, mut p: Peripherals) { 15async 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 = ["
23embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } 23embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" }
24embassy-macros = { path = "../../embassy-macros" } 24embassy-macros = { path = "../../embassy-macros" }
25 25
26embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time"] }
27lorawan-device = { git = "https://github.com/lulf/rust-lorawan.git", rev = "a373d06fa8858d251bc70d5789cebcd9a638ec42", default-features = false, features = ["async"] }
28lorawan-encoding = { git = "https://github.com/lulf/rust-lorawan.git", rev = "a373d06fa8858d251bc70d5789cebcd9a638ec42", default-features = false, features = ["default-crypto"] }
29
26defmt = "0.2.3" 30defmt = "0.2.3"
27defmt-rtt = "0.2.0" 31defmt-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"]
10mod example_common;
11
12use embassy_lora::{sx127x::*, LoraTimer};
13use 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};
24use lorawan_device::async_device::{region, Device, JoinMode};
25use lorawan_encoding::default_crypto::DefaultFactory as Crypto;
26
27fn 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()")]
34async 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
100pub struct DummySwitch;
101impl 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]
20embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-trace"] } 20embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-trace"] }
21embassy-traits = { version = "0.1.0", path = "../../embassy-traits", features = ["defmt"] } 21embassy-traits = { version = "0.1.0", path = "../../embassy-traits", features = ["defmt"] }
22embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "defmt-trace", "stm32wl55jc_cm4", "time-driver-tim2", "memory-x", "subghz"] } 22embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "defmt-trace", "stm32wl55jc_cm4", "time-driver-tim2", "memory-x", "subghz", "unstable-pac"] }
23embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } 23embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" }
24embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time"] }
25
26lorawan-device = { git = "https://github.com/lulf/rust-lorawan.git", rev = "a373d06fa8858d251bc70d5789cebcd9a638ec42", default-features = false, features = ["async"] }
27lorawan-encoding = { git = "https://github.com/lulf/rust-lorawan.git", rev = "a373d06fa8858d251bc70d5789cebcd9a638ec42", default-features = false, features = ["default-crypto"] }
24 28
25defmt = "0.2.3" 29defmt = "0.2.3"
26defmt-rtt = "0.2.0" 30defmt-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"]
9mod example_common;
10
11use embassy_lora::{stm32wl::*, LoraTimer};
12use 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};
21use lorawan_device::async_device::{region, Device, JoinMode};
22use lorawan_encoding::default_crypto::DefaultFactory as Crypto;
23
24fn 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()")]
31async 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}