aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--embassy-lora/Cargo.toml7
-rw-r--r--embassy-lora/src/lib.rs15
-rw-r--r--embassy-lora/src/stm32wl/mod.rs291
-rw-r--r--embassy-lora/src/sx126x/mod.rs137
-rw-r--r--embassy-lora/src/sx126x/sx126x_lora/board_specific.rs256
-rw-r--r--embassy-lora/src/sx126x/sx126x_lora/mod.rs732
-rw-r--r--embassy-lora/src/sx126x/sx126x_lora/mod_params.rs469
-rw-r--r--embassy-lora/src/sx126x/sx126x_lora/subroutine.rs674
-rw-r--r--embassy-lora/src/sx127x/mod.rs192
-rw-r--r--embassy-lora/src/sx127x/sx127x_lora/mod.rs539
-rw-r--r--embassy-lora/src/sx127x/sx127x_lora/register.rs107
-rw-r--r--embassy-stm32/src/lib.rs3
-rw-r--r--embassy-stm32/src/subghz/bit_sync.rs160
-rw-r--r--embassy-stm32/src/subghz/cad_params.rs230
-rw-r--r--embassy-stm32/src/subghz/calibrate.rs122
-rw-r--r--embassy-stm32/src/subghz/fallback_mode.rs37
-rw-r--r--embassy-stm32/src/subghz/hse_trim.rs107
-rw-r--r--embassy-stm32/src/subghz/irq.rs292
-rw-r--r--embassy-stm32/src/subghz/lora_sync_word.rs20
-rw-r--r--embassy-stm32/src/subghz/mod.rs1004
-rw-r--r--embassy-stm32/src/subghz/mod_params.rs1045
-rw-r--r--embassy-stm32/src/subghz/ocp.rs14
-rw-r--r--embassy-stm32/src/subghz/op_error.rs48
-rw-r--r--embassy-stm32/src/subghz/pa_config.rs196
-rw-r--r--embassy-stm32/src/subghz/packet_params.rs534
-rw-r--r--embassy-stm32/src/subghz/packet_status.rs282
-rw-r--r--embassy-stm32/src/subghz/packet_type.rs44
-rw-r--r--embassy-stm32/src/subghz/pkt_ctrl.rs247
-rw-r--r--embassy-stm32/src/subghz/pmode.rs27
-rw-r--r--embassy-stm32/src/subghz/pwr_ctrl.rs160
-rw-r--r--embassy-stm32/src/subghz/reg_mode.rs18
-rw-r--r--embassy-stm32/src/subghz/rf_frequency.rs135
-rw-r--r--embassy-stm32/src/subghz/rx_timeout_stop.rs21
-rw-r--r--embassy-stm32/src/subghz/sleep_cfg.rs107
-rw-r--r--embassy-stm32/src/subghz/smps.rs45
-rw-r--r--embassy-stm32/src/subghz/standby_clk.rs20
-rw-r--r--embassy-stm32/src/subghz/stats.rs184
-rw-r--r--embassy-stm32/src/subghz/status.rs197
-rw-r--r--embassy-stm32/src/subghz/tcxo_mode.rs170
-rw-r--r--embassy-stm32/src/subghz/timeout.rs492
-rw-r--r--embassy-stm32/src/subghz/tx_params.rs192
-rw-r--r--embassy-stm32/src/subghz/value_error.rs129
-rw-r--r--examples/nrf52840/Cargo.toml2
-rw-r--r--examples/stm32l0/Cargo.toml2
45 files changed, 6 insertions, 9701 deletions
diff --git a/README.md b/README.md
index 270f27a8f..b65dcbe15 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
35The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. 35The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
36 36
37- **LoRa** - 37- **LoRa** -
38<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers. 38<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking.
39 39
40- **USB** - 40- **USB** -
41<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. 41<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml
index 692a82040..aa0d2911d 100644
--- a/embassy-lora/Cargo.toml
+++ b/embassy-lora/Cargo.toml
@@ -9,19 +9,15 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em
9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" 9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/"
10features = ["time", "defmt"] 10features = ["time", "defmt"]
11flavors = [ 11flavors = [
12 { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] },
13 { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x"] },
14 { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/time-driver-any"] }, 12 { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/time-driver-any"] },
15] 13]
16 14
17[lib] 15[lib]
18 16
19[features] 17[features]
20sx126x = []
21sx127x = []
22stm32wl = ["dep:embassy-stm32"] 18stm32wl = ["dep:embassy-stm32"]
23time = [] 19time = []
24defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] 20defmt = ["dep:defmt", "lorawan-device/defmt"]
25external-lora-phy = ["dep:lora-phy"] 21external-lora-phy = ["dep:lora-phy"]
26 22
27[dependencies] 23[dependencies]
@@ -41,4 +37,3 @@ bit_field = { version = "0.10" }
41 37
42lora-phy = { version = "1", optional = true } 38lora-phy = { version = "1", optional = true }
43lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] } 39lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] }
44lorawan = { version = "0.7.3", default-features = false }
diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs
index c01d3f71a..c391a8029 100644
--- a/embassy-lora/src/lib.rs
+++ b/embassy-lora/src/lib.rs
@@ -1,24 +1,13 @@
1#![no_std] 1#![no_std]
2#![feature(async_fn_in_trait, impl_trait_projections)] 2#![feature(async_fn_in_trait, impl_trait_projections)]
3#![allow(incomplete_features)] 3#![allow(incomplete_features)]
4//! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device 4//! embassy-lora holds LoRa-specific functionality.
5//! crate's async LoRaWAN MAC implementation.
6 5
7pub(crate) mod fmt; 6pub(crate) mod fmt;
8#[cfg(feature = "external-lora-phy")] 7#[cfg(feature = "external-lora-phy")]
9/// interface variants required by the external lora crate 8/// interface variants required by the external lora physical layer crate (lora-phy)
10pub mod iv; 9pub mod iv;
11 10
12#[cfg(feature = "stm32wl")]
13#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
14pub mod stm32wl;
15#[cfg(feature = "sx126x")]
16#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
17pub mod sx126x;
18#[cfg(feature = "sx127x")]
19#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
20pub mod sx127x;
21
22#[cfg(feature = "time")] 11#[cfg(feature = "time")]
23use embassy_time::{Duration, Instant, Timer}; 12use embassy_time::{Duration, Instant, Timer};
24 13
diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs
deleted file mode 100644
index dae9a195c..000000000
--- a/embassy-lora/src/stm32wl/mod.rs
+++ /dev/null
@@ -1,291 +0,0 @@
1//! A radio driver integration for the radio found on STM32WL family devices.
2#![allow(deprecated)]
3use core::future::poll_fn;
4use core::task::Poll;
5
6use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
7use embassy_stm32::dma::NoDma;
8use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO};
9use embassy_stm32::subghz::{
10 CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, HseTrim, Irq, LoRaBandwidth, LoRaModParams,
11 LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk,
12 Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams,
13};
14use embassy_sync::waitqueue::AtomicWaker;
15use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig};
16use lorawan_device::async_device::Timings;
17
18#[derive(Debug, Copy, Clone)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20pub enum State {
21 Idle,
22 Txing,
23 Rxing,
24}
25
26#[derive(Debug, Copy, Clone)]
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28pub struct RadioError;
29
30static IRQ_WAKER: AtomicWaker = AtomicWaker::new();
31
32/// The radio peripheral keeping the radio state and owning the radio IRQ.
33pub struct SubGhzRadio<'d, RS> {
34 radio: SubGhz<'d, NoDma, NoDma>,
35 switch: RS,
36 irq: PeripheralRef<'d, SUBGHZ_RADIO>,
37}
38
39#[derive(Default)]
40#[non_exhaustive]
41pub struct SubGhzRadioConfig {
42 pub reg_mode: RegMode,
43 pub calibrate_image: CalibrateImage,
44 pub pa_config: PaConfig,
45 pub tx_params: TxParams,
46}
47
48impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> {
49 /// Create a new instance of a SubGhz radio for LoRaWAN.
50 pub fn new(
51 mut radio: SubGhz<'d, NoDma, NoDma>,
52 switch: RS,
53 irq: impl Peripheral<P = SUBGHZ_RADIO> + 'd,
54 config: SubGhzRadioConfig,
55 ) -> Result<Self, RadioError> {
56 into_ref!(irq);
57
58 radio.reset();
59
60 irq.disable();
61 irq.set_handler(|_| {
62 IRQ_WAKER.wake();
63 unsafe { SUBGHZ_RADIO::steal().disable() };
64 });
65
66 configure_radio(&mut radio, config)?;
67
68 Ok(Self { radio, switch, irq })
69 }
70
71 /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form
72 /// the upcoming RX window start.
73 async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, RadioError> {
74 trace!("TX request: {:?}", config);
75 self.switch.set_tx();
76
77 self.radio
78 .set_rf_frequency(&RfFreq::from_frequency(config.rf.frequency))?;
79
80 self.set_lora_mod_params(config.rf)?;
81
82 let packet_params = LoRaPacketParams::new()
83 .set_preamble_len(8)
84 .set_header_type(HeaderType::Variable)
85 .set_payload_len(buf.len() as u8)
86 .set_crc_en(true)
87 .set_invert_iq(false);
88
89 self.radio.set_lora_packet_params(&packet_params)?;
90
91 let irq_cfg = CfgIrq::new().irq_enable_all(Irq::TxDone).irq_enable_all(Irq::Timeout);
92 self.radio.set_irq_cfg(&irq_cfg)?;
93
94 self.radio.set_buffer_base_address(0, 0)?;
95 self.radio.write_buffer(0, buf)?;
96
97 // The maximum airtime for any LoRaWAN package is 2793.5ms.
98 // The value of 4000ms is copied from C driver and gives us a good safety margin.
99 self.radio.set_tx(Timeout::from_millis_sat(4000))?;
100 trace!("TX started");
101
102 loop {
103 let (_status, irq_status) = self.irq_wait().await;
104
105 if irq_status & Irq::TxDone.mask() != 0 {
106 trace!("TX done");
107 return Ok(0);
108 }
109
110 if irq_status & Irq::Timeout.mask() != 0 {
111 return Err(RadioError);
112 }
113 }
114 }
115
116 fn set_lora_mod_params(&mut self, config: RfConfig) -> Result<(), Error> {
117 let mod_params = LoRaModParams::new()
118 .set_sf(convert_spreading_factor(&config.spreading_factor))
119 .set_bw(convert_bandwidth(&config.bandwidth))
120 .set_cr(CodingRate::Cr45)
121 .set_ldro_en(matches!(
122 (config.spreading_factor, config.bandwidth),
123 (SpreadingFactor::_12, Bandwidth::_125KHz)
124 | (SpreadingFactor::_12, Bandwidth::_250KHz)
125 | (SpreadingFactor::_11, Bandwidth::_125KHz)
126 ));
127 self.radio.set_lora_mod_params(&mod_params)
128 }
129
130 /// Perform a radio receive operation with the radio config and receive buffer. The receive buffer must
131 /// be able to hold a single LoRaWAN packet.
132 async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> {
133 assert!(buf.len() >= 255);
134 trace!("RX request: {:?}", config);
135 self.switch.set_rx();
136
137 self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?;
138
139 self.set_lora_mod_params(config)?;
140
141 let packet_params = LoRaPacketParams::new()
142 .set_preamble_len(8)
143 .set_header_type(HeaderType::Variable)
144 .set_payload_len(0xFF)
145 .set_crc_en(false)
146 .set_invert_iq(true);
147 self.radio.set_lora_packet_params(&packet_params)?;
148
149 let irq_cfg = CfgIrq::new()
150 .irq_enable_all(Irq::RxDone)
151 .irq_enable_all(Irq::PreambleDetected)
152 .irq_enable_all(Irq::HeaderValid)
153 .irq_enable_all(Irq::HeaderErr)
154 .irq_enable_all(Irq::Err)
155 .irq_enable_all(Irq::Timeout);
156 self.radio.set_irq_cfg(&irq_cfg)?;
157
158 self.radio.set_buffer_base_address(0, 0)?;
159
160 // NOTE: Upper layer handles timeout by cancelling the future
161 self.radio.set_rx(Timeout::DISABLED)?;
162
163 trace!("RX started");
164
165 loop {
166 let (_status, irq_status) = self.irq_wait().await;
167
168 if irq_status & Irq::RxDone.mask() != 0 {
169 let (_status, len, ptr) = self.radio.rx_buffer_status()?;
170 let packet_status = self.radio.lora_packet_status()?;
171 let rssi = packet_status.rssi_pkt().to_integer();
172 let snr = packet_status.snr_pkt().to_integer();
173 self.radio.read_buffer(ptr, &mut buf[..len as usize])?;
174 self.radio.set_standby(StandbyClk::Rc)?;
175
176 #[cfg(feature = "defmt")]
177 trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]);
178
179 #[cfg(feature = "log")]
180 trace!("RX done: {:02x?}", &mut buf[..len as usize]);
181 return Ok((len as usize, RxQuality::new(rssi, snr as i8)));
182 }
183
184 if irq_status & Irq::Timeout.mask() != 0 {
185 return Err(RadioError);
186 }
187 }
188 }
189
190 async fn irq_wait(&mut self) -> (Status, u16) {
191 poll_fn(|cx| {
192 self.irq.unpend();
193 self.irq.enable();
194 IRQ_WAKER.register(cx.waker());
195
196 let (status, irq_status) = self.radio.irq_status().expect("error getting irq status");
197 self.radio
198 .clear_irq_status(irq_status)
199 .expect("error clearing irq status");
200
201 trace!("SUGHZ IRQ 0b{:016b}, {:?}", irq_status, status);
202
203 if irq_status == 0 {
204 Poll::Pending
205 } else {
206 Poll::Ready((status, irq_status))
207 }
208 })
209 .await
210 }
211}
212
213fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConfig) -> Result<(), RadioError> {
214 trace!("Configuring STM32WL SUBGHZ radio");
215
216 radio.set_regulator_mode(config.reg_mode)?;
217 radio.set_standby(StandbyClk::Rc)?;
218
219 let tcxo_mode = TcxoMode::new()
220 .set_txco_trim(TcxoTrim::Volts1pt7)
221 .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(100)));
222 radio.set_tcxo_mode(&tcxo_mode)?;
223 // Reduce input capacitance as shown in Reference Manual "Figure 23. HSE32 TCXO control".
224 // The STM32CUBE C driver also does this.
225 radio.set_hse_in_trim(HseTrim::MIN)?;
226
227 // Re-calibrate everything after setting the TXCO config.
228 radio.calibrate(0x7F)?;
229 radio.calibrate_image(config.calibrate_image)?;
230
231 radio.set_pa_config(&config.pa_config)?;
232 radio.set_tx_params(&config.tx_params)?;
233 radio.set_pa_ocp(Ocp::Max140m)?;
234
235 radio.set_packet_type(PacketType::LoRa)?;
236 radio.set_lora_sync_word(LoRaSyncWord::Public)?;
237
238 trace!("Done initializing STM32WL SUBGHZ radio");
239 Ok(())
240}
241
242impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> {
243 type PhyError = RadioError;
244
245 async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, Self::PhyError> {
246 self.do_tx(config, buf).await
247 }
248
249 async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> {
250 self.do_rx(config, buf).await
251 }
252}
253
254impl From<embassy_stm32::spi::Error> for RadioError {
255 fn from(_: embassy_stm32::spi::Error) -> Self {
256 RadioError
257 }
258}
259
260impl<'d, RS> Timings for SubGhzRadio<'d, RS> {
261 fn get_rx_window_offset_ms(&self) -> i32 {
262 -3
263 }
264 fn get_rx_window_duration_ms(&self) -> u32 {
265 1003
266 }
267}
268
269pub trait RadioSwitch {
270 fn set_rx(&mut self);
271 fn set_tx(&mut self);
272}
273
274fn convert_spreading_factor(sf: &SpreadingFactor) -> SF {
275 match sf {
276 SpreadingFactor::_7 => SF::Sf7,
277 SpreadingFactor::_8 => SF::Sf8,
278 SpreadingFactor::_9 => SF::Sf9,
279 SpreadingFactor::_10 => SF::Sf10,
280 SpreadingFactor::_11 => SF::Sf11,
281 SpreadingFactor::_12 => SF::Sf12,
282 }
283}
284
285fn convert_bandwidth(bw: &Bandwidth) -> LoRaBandwidth {
286 match bw {
287 Bandwidth::_125KHz => LoRaBandwidth::Bw125,
288 Bandwidth::_250KHz => LoRaBandwidth::Bw250,
289 Bandwidth::_500KHz => LoRaBandwidth::Bw500,
290 }
291}
diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs
deleted file mode 100644
index 2f0b8c8e3..000000000
--- a/embassy-lora/src/sx126x/mod.rs
+++ /dev/null
@@ -1,137 +0,0 @@
1use defmt::Format;
2use embedded_hal::digital::v2::OutputPin;
3use embedded_hal_async::digital::Wait;
4use embedded_hal_async::spi::*;
5use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig};
6use lorawan_device::async_device::Timings;
7
8mod sx126x_lora;
9use sx126x_lora::LoRa;
10
11use self::sx126x_lora::mod_params::RadioError;
12
13/// Semtech Sx126x LoRa peripheral
14pub struct Sx126xRadio<SPI, CTRL, WAIT, BUS>
15where
16 SPI: SpiBus<u8, Error = BUS> + 'static,
17 CTRL: OutputPin + 'static,
18 WAIT: Wait + 'static,
19 BUS: Error + Format + 'static,
20{
21 pub lora: LoRa<SPI, CTRL, WAIT>,
22}
23
24impl<SPI, CTRL, WAIT, BUS> Sx126xRadio<SPI, CTRL, WAIT, BUS>
25where
26 SPI: SpiBus<u8, Error = BUS> + 'static,
27 CTRL: OutputPin + 'static,
28 WAIT: Wait + 'static,
29 BUS: Error + Format + 'static,
30{
31 pub async fn new(
32 spi: SPI,
33 cs: CTRL,
34 reset: CTRL,
35 antenna_rx: CTRL,
36 antenna_tx: CTRL,
37 dio1: WAIT,
38 busy: WAIT,
39 enable_public_network: bool,
40 ) -> Result<Self, RadioError<BUS>> {
41 let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy);
42 lora.init().await?;
43 lora.set_lora_modem(enable_public_network).await?;
44 Ok(Self { lora })
45 }
46}
47
48impl<SPI, CTRL, WAIT, BUS> Timings for Sx126xRadio<SPI, CTRL, WAIT, BUS>
49where
50 SPI: SpiBus<u8, Error = BUS> + 'static,
51 CTRL: OutputPin + 'static,
52 WAIT: Wait + 'static,
53 BUS: Error + Format + 'static,
54{
55 fn get_rx_window_offset_ms(&self) -> i32 {
56 -50
57 }
58 fn get_rx_window_duration_ms(&self) -> u32 {
59 1050
60 }
61}
62
63impl<SPI, CTRL, WAIT, BUS> PhyRxTx for Sx126xRadio<SPI, CTRL, WAIT, BUS>
64where
65 SPI: SpiBus<u8, Error = BUS> + 'static,
66 CTRL: OutputPin + 'static,
67 WAIT: Wait + 'static,
68 BUS: Error + Format + 'static,
69{
70 type PhyError = RadioError<BUS>;
71
72 async fn tx(&mut self, config: TxConfig, buffer: &[u8]) -> Result<u32, Self::PhyError> {
73 trace!("TX START");
74 self.lora
75 .set_tx_config(
76 config.pw,
77 config.rf.spreading_factor.into(),
78 config.rf.bandwidth.into(),
79 config.rf.coding_rate.into(),
80 8,
81 false,
82 true,
83 false,
84 0,
85 false,
86 )
87 .await?;
88 self.lora.set_max_payload_length(buffer.len() as u8).await?;
89 self.lora.set_channel(config.rf.frequency).await?;
90 self.lora.send(buffer, 0xffffff).await?;
91 self.lora.process_irq(None, None, None).await?;
92 trace!("TX DONE");
93 return Ok(0);
94 }
95
96 async fn rx(
97 &mut self,
98 config: RfConfig,
99 receiving_buffer: &mut [u8],
100 ) -> Result<(usize, RxQuality), Self::PhyError> {
101 trace!("RX START");
102 self.lora
103 .set_rx_config(
104 config.spreading_factor.into(),
105 config.bandwidth.into(),
106 config.coding_rate.into(),
107 8,
108 4,
109 false,
110 0u8,
111 true,
112 false,
113 0,
114 true,
115 true,
116 )
117 .await?;
118 self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?;
119 self.lora.set_channel(config.frequency).await?;
120 self.lora.rx(90 * 1000).await?;
121 let mut received_len = 0u8;
122 self.lora
123 .process_irq(Some(receiving_buffer), Some(&mut received_len), None)
124 .await?;
125 trace!("RX DONE");
126
127 let packet_status = self.lora.get_latest_packet_status();
128 let mut rssi = 0i16;
129 let mut snr = 0i8;
130 if packet_status.is_some() {
131 rssi = packet_status.unwrap().rssi as i16;
132 snr = packet_status.unwrap().snr;
133 }
134
135 Ok((received_len as usize, RxQuality::new(rssi, snr)))
136 }
137}
diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs
deleted file mode 100644
index a7b9e1486..000000000
--- a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs
+++ /dev/null
@@ -1,256 +0,0 @@
1use embassy_time::{Duration, Timer};
2use embedded_hal::digital::v2::OutputPin;
3use embedded_hal_async::digital::Wait;
4use embedded_hal_async::spi::SpiBus;
5
6use super::mod_params::RadioError::*;
7use super::mod_params::*;
8use super::LoRa;
9
10// Defines the time required for the TCXO to wakeup [ms].
11const BRD_TCXO_WAKEUP_TIME: u32 = 10;
12
13// Provides board-specific functionality for Semtech SX126x-based boards.
14
15impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
16where
17 SPI: SpiBus<u8, Error = BUS>,
18 CTRL: OutputPin,
19 WAIT: Wait,
20{
21 // De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes.
22 pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError<BUS>> {
23 Ok(()) // no operation currently
24 }
25
26 // Initialize the TCXO power pin
27 pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError<BUS>> {
28 let timeout = self.brd_get_board_tcxo_wakeup_time() << 6;
29 self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout)
30 .await?;
31 Ok(())
32 }
33
34 // Initialize RF switch control pins
35 pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError<BUS>> {
36 self.sub_set_dio2_as_rf_switch_ctrl(true).await?;
37 Ok(())
38 }
39
40 // Initialize the radio debug pins
41 pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError<BUS>> {
42 Ok(()) // no operation currently
43 }
44
45 // Hardware reset of the radio
46 pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError<BUS>> {
47 Timer::after(Duration::from_millis(10)).await;
48 self.reset.set_low().map_err(|_| Reset)?;
49 Timer::after(Duration::from_millis(20)).await;
50 self.reset.set_high().map_err(|_| Reset)?;
51 Timer::after(Duration::from_millis(10)).await;
52 Ok(())
53 }
54
55 // Wait while the busy pin is high
56 pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError<BUS>> {
57 self.busy.wait_for_low().await.map_err(|_| Busy)?;
58 Ok(())
59 }
60
61 // Wake up the radio
62 pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError<BUS>> {
63 self.cs.set_low().map_err(|_| CS)?;
64 self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?;
65 self.spi.write(&[0x00]).await.map_err(SPI)?;
66 self.cs.set_high().map_err(|_| CS)?;
67
68 self.brd_wait_on_busy().await?;
69 self.brd_set_operating_mode(RadioMode::StandbyRC);
70 Ok(())
71 }
72
73 // Send a command that writes data to the radio
74 pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
75 self.sub_check_device_ready().await?;
76
77 self.cs.set_low().map_err(|_| CS)?;
78 self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
79 self.spi.write(buffer).await.map_err(SPI)?;
80 self.cs.set_high().map_err(|_| CS)?;
81
82 if op_code != OpCode::SetSleep {
83 self.brd_wait_on_busy().await?;
84 }
85 Ok(())
86 }
87
88 // Send a command that reads data from the radio, filling the provided buffer and returning a status
89 pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
90 let mut status = [0u8];
91 let mut input = [0u8];
92
93 self.sub_check_device_ready().await?;
94
95 self.cs.set_low().map_err(|_| CS)?;
96 self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
97 self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?;
98 for i in 0..buffer.len() {
99 self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
100 buffer[i] = input[0];
101 }
102 self.cs.set_high().map_err(|_| CS)?;
103
104 self.brd_wait_on_busy().await?;
105
106 Ok(status[0])
107 }
108
109 // Write one or more bytes of data to the radio memory
110 pub(super) async fn brd_write_registers(
111 &mut self,
112 start_register: Register,
113 buffer: &[u8],
114 ) -> Result<(), RadioError<BUS>> {
115 self.sub_check_device_ready().await?;
116
117 self.cs.set_low().map_err(|_| CS)?;
118 self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?;
119 self.spi
120 .write(&[
121 ((start_register.addr() & 0xFF00) >> 8) as u8,
122 (start_register.addr() & 0x00FF) as u8,
123 ])
124 .await
125 .map_err(SPI)?;
126 self.spi.write(buffer).await.map_err(SPI)?;
127 self.cs.set_high().map_err(|_| CS)?;
128
129 self.brd_wait_on_busy().await?;
130 Ok(())
131 }
132
133 // Read one or more bytes of data from the radio memory
134 pub(super) async fn brd_read_registers(
135 &mut self,
136 start_register: Register,
137 buffer: &mut [u8],
138 ) -> Result<(), RadioError<BUS>> {
139 let mut input = [0u8];
140
141 self.sub_check_device_ready().await?;
142
143 self.cs.set_low().map_err(|_| CS)?;
144 self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?;
145 self.spi
146 .write(&[
147 ((start_register.addr() & 0xFF00) >> 8) as u8,
148 (start_register.addr() & 0x00FF) as u8,
149 0x00u8,
150 ])
151 .await
152 .map_err(SPI)?;
153 for i in 0..buffer.len() {
154 self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
155 buffer[i] = input[0];
156 }
157 self.cs.set_high().map_err(|_| CS)?;
158
159 self.brd_wait_on_busy().await?;
160 Ok(())
161 }
162
163 // Write data to the buffer holding the payload in the radio
164 pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
165 self.sub_check_device_ready().await?;
166
167 self.cs.set_low().map_err(|_| CS)?;
168 self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?;
169 self.spi.write(&[offset]).await.map_err(SPI)?;
170 self.spi.write(buffer).await.map_err(SPI)?;
171 self.cs.set_high().map_err(|_| CS)?;
172
173 self.brd_wait_on_busy().await?;
174 Ok(())
175 }
176
177 // Read data from the buffer holding the payload in the radio
178 pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError<BUS>> {
179 let mut input = [0u8];
180
181 self.sub_check_device_ready().await?;
182
183 self.cs.set_low().map_err(|_| CS)?;
184 self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?;
185 self.spi.write(&[offset]).await.map_err(SPI)?;
186 self.spi.write(&[0x00]).await.map_err(SPI)?;
187 for i in 0..buffer.len() {
188 self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
189 buffer[i] = input[0];
190 }
191 self.cs.set_high().map_err(|_| CS)?;
192
193 self.brd_wait_on_busy().await?;
194 Ok(())
195 }
196
197 // Set the radio output power
198 pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError<BUS>> {
199 self.sub_set_tx_params(power, RampTime::Ramp40Us).await?;
200 Ok(())
201 }
202
203 // Get the radio type
204 pub(super) fn brd_get_radio_type(&mut self) -> RadioType {
205 RadioType::SX1262
206 }
207
208 // Quiesce the antenna(s).
209 pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError<BUS>> {
210 self.antenna_tx.set_low().map_err(|_| AntTx)?;
211 self.antenna_rx.set_low().map_err(|_| AntRx)?;
212 Ok(())
213 }
214
215 // Prepare the antenna(s) for a receive operation
216 pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError<BUS>> {
217 self.antenna_tx.set_low().map_err(|_| AntTx)?;
218 self.antenna_rx.set_high().map_err(|_| AntRx)?;
219 Ok(())
220 }
221
222 // Prepare the antenna(s) for a send operation
223 pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError<BUS>> {
224 self.antenna_rx.set_low().map_err(|_| AntRx)?;
225 self.antenna_tx.set_high().map_err(|_| AntTx)?;
226 Ok(())
227 }
228
229 // Check if the given RF frequency is supported by the hardware
230 pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result<bool, RadioError<BUS>> {
231 Ok(true)
232 }
233
234 // Get the duration required for the TCXO to wakeup [ms].
235 pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 {
236 BRD_TCXO_WAKEUP_TIME
237 }
238
239 /* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process
240 pub(super) async fn brd_get_dio1_pin_state(
241 &mut self,
242 ) -> Result<u32, RadioError<BUS>> {
243 Ok(0)
244 }
245 */
246
247 // Get the current radio operatiing mode
248 pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode {
249 self.operating_mode
250 }
251
252 // Set/Update the current radio operating mode This function is only required to reflect the current radio operating mode when processing interrupts.
253 pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) {
254 self.operating_mode = mode;
255 }
256}
diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs
deleted file mode 100644
index 280f26d51..000000000
--- a/embassy-lora/src/sx126x/sx126x_lora/mod.rs
+++ /dev/null
@@ -1,732 +0,0 @@
1#![allow(dead_code)]
2
3use embassy_time::{Duration, Timer};
4use embedded_hal::digital::v2::OutputPin;
5use embedded_hal_async::digital::Wait;
6use embedded_hal_async::spi::SpiBus;
7
8mod board_specific;
9pub mod mod_params;
10mod subroutine;
11
12use mod_params::RadioError::*;
13use mod_params::*;
14
15// Syncwords for public and private networks
16const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444;
17const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424;
18
19// Maximum number of registers that can be added to the retention list
20const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4;
21
22// Possible LoRa bandwidths
23const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz];
24
25// Radio complete wakeup time with margin for temperature compensation [ms]
26const RADIO_WAKEUP_TIME: u32 = 3;
27
28/// Provides high-level access to Semtech SX126x-based boards
29pub struct LoRa<SPI, CTRL, WAIT> {
30 spi: SPI,
31 cs: CTRL,
32 reset: CTRL,
33 antenna_rx: CTRL,
34 antenna_tx: CTRL,
35 dio1: WAIT,
36 busy: WAIT,
37 operating_mode: RadioMode,
38 rx_continuous: bool,
39 max_payload_length: u8,
40 modulation_params: Option<ModulationParams>,
41 packet_type: PacketType,
42 packet_params: Option<PacketParams>,
43 packet_status: Option<PacketStatus>,
44 image_calibrated: bool,
45 frequency_error: u32,
46}
47
48impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
49where
50 SPI: SpiBus<u8, Error = BUS>,
51 CTRL: OutputPin,
52 WAIT: Wait,
53{
54 /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time ()
55 pub fn new(spi: SPI, cs: CTRL, reset: CTRL, antenna_rx: CTRL, antenna_tx: CTRL, dio1: WAIT, busy: WAIT) -> Self {
56 Self {
57 spi,
58 cs,
59 reset,
60 antenna_rx,
61 antenna_tx,
62 dio1,
63 busy,
64 operating_mode: RadioMode::Sleep,
65 rx_continuous: false,
66 max_payload_length: 0xFFu8,
67 modulation_params: None,
68 packet_type: PacketType::LoRa,
69 packet_params: None,
70 packet_status: None,
71 image_calibrated: false,
72 frequency_error: 0u32, // where is volatile FrequencyError modified ???
73 }
74 }
75
76 /// Initialize the radio
77 pub async fn init(&mut self) -> Result<(), RadioError<BUS>> {
78 self.sub_init().await?;
79 self.sub_set_standby(StandbyMode::RC).await?;
80 self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?;
81 self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?;
82 self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?;
83 self.sub_set_dio_irq_params(
84 IrqMask::All.value(),
85 IrqMask::All.value(),
86 IrqMask::None.value(),
87 IrqMask::None.value(),
88 )
89 .await?;
90 self.add_register_to_retention_list(Register::RxGain.addr()).await?;
91 self.add_register_to_retention_list(Register::TxModulation.addr())
92 .await?;
93 Ok(())
94 }
95
96 /// Return current radio state
97 pub fn get_status(&mut self) -> RadioState {
98 match self.brd_get_operating_mode() {
99 RadioMode::Transmit => RadioState::TxRunning,
100 RadioMode::Receive => RadioState::RxRunning,
101 RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting,
102 _ => RadioState::Idle,
103 }
104 }
105
106 /// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired)
107 pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError<BUS>> {
108 self.sub_set_packet_type(PacketType::LoRa).await?;
109 if enable_public_network {
110 self.brd_write_registers(
111 Register::LoRaSyncword,
112 &[
113 ((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8,
114 (LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8,
115 ],
116 )
117 .await?;
118 } else {
119 self.brd_write_registers(
120 Register::LoRaSyncword,
121 &[
122 ((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8,
123 (LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8,
124 ],
125 )
126 .await?;
127 }
128
129 Ok(())
130 }
131
132 /// Sets the channel frequency
133 pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
134 self.sub_set_rf_frequency(frequency).await?;
135 Ok(())
136 }
137
138 /* Checks if the channel is free for the given time. This is currently not implemented until a substitute
139 for switching to the FSK modem is found.
140
141 pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool;
142 */
143
144 /// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts. Ensure set_lora_modem() is called befrorehand.
145 /// After calling this function either set_rx_config() or set_tx_config() must be called.
146 pub async fn get_random_value(&mut self) -> Result<u32, RadioError<BUS>> {
147 self.sub_set_dio_irq_params(
148 IrqMask::None.value(),
149 IrqMask::None.value(),
150 IrqMask::None.value(),
151 IrqMask::None.value(),
152 )
153 .await?;
154
155 let result = self.sub_get_random().await?;
156 Ok(result)
157 }
158
159 /// Set the reception parameters for the LoRa modem (only). Ensure set_lora_modem() is called befrorehand.
160 /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
161 /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
162 /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
163 /// preamble_length length in symbols (the hardware adds 4 more symbols)
164 /// symb_timeout RxSingle timeout value in symbols
165 /// fixed_len fixed length packets [0: variable, 1: fixed]
166 /// payload_len payload length when fixed length is used
167 /// crc_on [0: OFF, 1: ON]
168 /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON]
169 /// hop_period number of symbols between each hop
170 /// iq_inverted invert IQ signals [0: not inverted, 1: inverted]
171 /// rx_continuous reception mode [false: single mode, true: continuous mode]
172 pub async fn set_rx_config(
173 &mut self,
174 spreading_factor: SpreadingFactor,
175 bandwidth: Bandwidth,
176 coding_rate: CodingRate,
177 preamble_length: u16,
178 symb_timeout: u16,
179 fixed_len: bool,
180 payload_len: u8,
181 crc_on: bool,
182 _freq_hop_on: bool,
183 _hop_period: u8,
184 iq_inverted: bool,
185 rx_continuous: bool,
186 ) -> Result<(), RadioError<BUS>> {
187 let mut symb_timeout_final = symb_timeout;
188
189 self.rx_continuous = rx_continuous;
190 if self.rx_continuous {
191 symb_timeout_final = 0;
192 }
193 if fixed_len {
194 self.max_payload_length = payload_len;
195 } else {
196 self.max_payload_length = 0xFFu8;
197 }
198
199 self.sub_set_stop_rx_timer_on_preamble_detect(false).await?;
200
201 let mut low_data_rate_optimize = 0x00u8;
202 if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
203 && (bandwidth == Bandwidth::_125KHz))
204 || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
205 {
206 low_data_rate_optimize = 0x01u8;
207 }
208
209 let modulation_params = ModulationParams {
210 spreading_factor: spreading_factor,
211 bandwidth: bandwidth,
212 coding_rate: coding_rate,
213 low_data_rate_optimize: low_data_rate_optimize,
214 };
215
216 let mut preamble_length_final = preamble_length;
217 if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
218 && (preamble_length < 12)
219 {
220 preamble_length_final = 12;
221 }
222
223 let packet_params = PacketParams {
224 preamble_length: preamble_length_final,
225 implicit_header: fixed_len,
226 payload_length: self.max_payload_length,
227 crc_on: crc_on,
228 iq_inverted: iq_inverted,
229 };
230
231 self.modulation_params = Some(modulation_params);
232 self.packet_params = Some(packet_params);
233
234 self.standby().await?;
235 self.sub_set_modulation_params().await?;
236 self.sub_set_packet_params().await?;
237 self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?;
238
239 // Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
240 let mut iq_polarity = [0x00u8];
241 self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?;
242 if iq_inverted {
243 self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))])
244 .await?;
245 } else {
246 self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)])
247 .await?;
248 }
249 Ok(())
250 }
251
252 /// Set the transmission parameters for the LoRa modem (only).
253 /// power output power [dBm]
254 /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
255 /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
256 /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
257 /// preamble_length length in symbols (the hardware adds 4 more symbols)
258 /// fixed_len fixed length packets [0: variable, 1: fixed]
259 /// crc_on [0: OFF, 1: ON]
260 /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON]
261 /// hop_period number of symbols between each hop
262 /// iq_inverted invert IQ signals [0: not inverted, 1: inverted]
263 pub async fn set_tx_config(
264 &mut self,
265 power: i8,
266 spreading_factor: SpreadingFactor,
267 bandwidth: Bandwidth,
268 coding_rate: CodingRate,
269 preamble_length: u16,
270 fixed_len: bool,
271 crc_on: bool,
272 _freq_hop_on: bool,
273 _hop_period: u8,
274 iq_inverted: bool,
275 ) -> Result<(), RadioError<BUS>> {
276 let mut low_data_rate_optimize = 0x00u8;
277 if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
278 && (bandwidth == Bandwidth::_125KHz))
279 || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
280 {
281 low_data_rate_optimize = 0x01u8;
282 }
283
284 let modulation_params = ModulationParams {
285 spreading_factor: spreading_factor,
286 bandwidth: bandwidth,
287 coding_rate: coding_rate,
288 low_data_rate_optimize: low_data_rate_optimize,
289 };
290
291 let mut preamble_length_final = preamble_length;
292 if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
293 && (preamble_length < 12)
294 {
295 preamble_length_final = 12;
296 }
297
298 let packet_params = PacketParams {
299 preamble_length: preamble_length_final,
300 implicit_header: fixed_len,
301 payload_length: self.max_payload_length,
302 crc_on: crc_on,
303 iq_inverted: iq_inverted,
304 };
305
306 self.modulation_params = Some(modulation_params);
307 self.packet_params = Some(packet_params);
308
309 self.standby().await?;
310 self.sub_set_modulation_params().await?;
311 self.sub_set_packet_params().await?;
312
313 // Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
314
315 let mut tx_modulation = [0x00u8];
316 self.brd_read_registers(Register::TxModulation, &mut tx_modulation)
317 .await?;
318 if bandwidth == Bandwidth::_500KHz {
319 self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))])
320 .await?;
321 } else {
322 self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)])
323 .await?;
324 }
325
326 self.brd_set_rf_tx_power(power).await?;
327 Ok(())
328 }
329
330 /// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported]
331 pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result<bool, RadioError<BUS>> {
332 Ok(self.brd_check_rf_frequency(frequency).await?)
333 }
334
335 /// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called)
336 /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
337 /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
338 /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
339 /// preamble_length length in symbols (the hardware adds 4 more symbols)
340 /// fixed_len fixed length packets [0: variable, 1: fixed]
341 /// payload_len sets payload length when fixed length is used
342 /// crc_on [0: OFF, 1: ON]
343 pub fn get_time_on_air(
344 &mut self,
345 spreading_factor: SpreadingFactor,
346 bandwidth: Bandwidth,
347 coding_rate: CodingRate,
348 preamble_length: u16,
349 fixed_len: bool,
350 payload_len: u8,
351 crc_on: bool,
352 ) -> Result<u32, RadioError<BUS>> {
353 let numerator = 1000
354 * Self::get_lora_time_on_air_numerator(
355 spreading_factor,
356 bandwidth,
357 coding_rate,
358 preamble_length,
359 fixed_len,
360 payload_len,
361 crc_on,
362 );
363 let denominator = bandwidth.value_in_hz();
364 if denominator == 0 {
365 Err(RadioError::InvalidBandwidth)
366 } else {
367 Ok((numerator + denominator - 1) / denominator)
368 }
369 }
370
371 /// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms]
372 pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
373 if self.packet_params.is_some() {
374 self.sub_set_dio_irq_params(
375 IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
376 IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
377 IrqMask::None.value(),
378 IrqMask::None.value(),
379 )
380 .await?;
381
382 let mut packet_params = self.packet_params.as_mut().unwrap();
383 packet_params.payload_length = buffer.len() as u8;
384 self.sub_set_packet_params().await?;
385 self.sub_send_payload(buffer, timeout).await?;
386 Ok(())
387 } else {
388 Err(RadioError::PacketParamsMissing)
389 }
390 }
391
392 /// Set the radio in sleep mode
393 pub async fn sleep(&mut self) -> Result<(), RadioError<BUS>> {
394 self.sub_set_sleep(SleepParams {
395 wakeup_rtc: false,
396 reset: false,
397 warm_start: true,
398 })
399 .await?;
400 Timer::after(Duration::from_millis(2)).await;
401 Ok(())
402 }
403
404 /// Set the radio in standby mode
405 pub async fn standby(&mut self) -> Result<(), RadioError<BUS>> {
406 self.sub_set_standby(StandbyMode::RC).await?;
407 Ok(())
408 }
409
410 /// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)]
411 pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
412 self.sub_set_dio_irq_params(
413 IrqMask::All.value(),
414 IrqMask::All.value(),
415 IrqMask::None.value(),
416 IrqMask::None.value(),
417 )
418 .await?;
419
420 if self.rx_continuous {
421 self.sub_set_rx(0xFFFFFF).await?;
422 } else {
423 self.sub_set_rx(timeout << 6).await?;
424 }
425
426 Ok(())
427 }
428
429 /// Start a Channel Activity Detection
430 pub async fn start_cad(&mut self) -> Result<(), RadioError<BUS>> {
431 self.sub_set_dio_irq_params(
432 IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
433 IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
434 IrqMask::None.value(),
435 IrqMask::None.value(),
436 )
437 .await?;
438 self.sub_set_cad().await?;
439 Ok(())
440 }
441
442 /// Sets the radio in continuous wave transmission mode
443 /// frequency channel RF frequency
444 /// power output power [dBm]
445 /// timeout transmission mode timeout [s]
446 pub async fn set_tx_continuous_wave(
447 &mut self,
448 frequency: u32,
449 power: i8,
450 _timeout: u16,
451 ) -> Result<(), RadioError<BUS>> {
452 self.sub_set_rf_frequency(frequency).await?;
453 self.brd_set_rf_tx_power(power).await?;
454 self.sub_set_tx_continuous_wave().await?;
455
456 Ok(())
457 }
458
459 /// Read the current RSSI value for the LoRa modem (only) [dBm]
460 pub async fn get_rssi(&mut self) -> Result<i16, RadioError<BUS>> {
461 let value = self.sub_get_rssi_inst().await?;
462 Ok(value as i16)
463 }
464
465 /// Write one or more radio registers with a buffer of a given size, starting at the first register address
466 pub async fn write_registers_from_buffer(
467 &mut self,
468 start_register: Register,
469 buffer: &[u8],
470 ) -> Result<(), RadioError<BUS>> {
471 self.brd_write_registers(start_register, buffer).await?;
472 Ok(())
473 }
474
475 /// Read one or more radio registers into a buffer of a given size, starting at the first register address
476 pub async fn read_registers_into_buffer(
477 &mut self,
478 start_register: Register,
479 buffer: &mut [u8],
480 ) -> Result<(), RadioError<BUS>> {
481 self.brd_read_registers(start_register, buffer).await?;
482 Ok(())
483 }
484
485 /// Set the maximum payload length (in bytes) for a LoRa modem (only).
486 pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError<BUS>> {
487 if self.packet_params.is_some() {
488 let packet_params = self.packet_params.as_mut().unwrap();
489 self.max_payload_length = max;
490 packet_params.payload_length = max;
491 self.sub_set_packet_params().await?;
492 Ok(())
493 } else {
494 Err(RadioError::PacketParamsMissing)
495 }
496 }
497
498 /// Get the time required for the board plus radio to get out of sleep [ms]
499 pub fn get_wakeup_time(&mut self) -> u32 {
500 self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME
501 }
502
503 /// Process the radio irq
504 pub async fn process_irq(
505 &mut self,
506 receiving_buffer: Option<&mut [u8]>,
507 received_len: Option<&mut u8>,
508 cad_activity_detected: Option<&mut bool>,
509 ) -> Result<(), RadioError<BUS>> {
510 loop {
511 trace!("process_irq loop entered");
512
513 let de = self.sub_get_device_errors().await?;
514 trace!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}",
515 de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp);
516 let st = self.sub_get_status().await?;
517 trace!(
518 "radio status: cmd_status: {:x}, chip_mode: {:x}",
519 st.cmd_status,
520 st.chip_mode
521 );
522
523 self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
524 let operating_mode = self.brd_get_operating_mode();
525 let irq_flags = self.sub_get_irq_status().await?;
526 self.sub_clear_irq_status(irq_flags).await?;
527 trace!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags);
528
529 // check for errors and unexpected interrupt masks (based on operation mode)
530 if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() {
531 if !self.rx_continuous {
532 self.brd_set_operating_mode(RadioMode::StandbyRC);
533 }
534 return Err(RadioError::HeaderError);
535 } else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() {
536 if operating_mode == RadioMode::Receive {
537 if !self.rx_continuous {
538 self.brd_set_operating_mode(RadioMode::StandbyRC);
539 }
540 return Err(RadioError::CRCErrorOnReceive);
541 } else {
542 return Err(RadioError::CRCErrorUnexpected);
543 }
544 } else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() {
545 if operating_mode == RadioMode::Transmit {
546 self.brd_set_operating_mode(RadioMode::StandbyRC);
547 return Err(RadioError::TransmitTimeout);
548 } else if operating_mode == RadioMode::Receive {
549 self.brd_set_operating_mode(RadioMode::StandbyRC);
550 return Err(RadioError::ReceiveTimeout);
551 } else {
552 return Err(RadioError::TimeoutUnexpected);
553 }
554 } else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value())
555 && (operating_mode != RadioMode::Transmit)
556 {
557 return Err(RadioError::TransmitDoneUnexpected);
558 } else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value())
559 && (operating_mode != RadioMode::Receive)
560 {
561 return Err(RadioError::ReceiveDoneUnexpected);
562 } else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value())
563 || ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value()))
564 && (operating_mode != RadioMode::ChannelActivityDetection)
565 {
566 return Err(RadioError::CADUnexpected);
567 }
568
569 if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() {
570 trace!("HeaderValid");
571 } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() {
572 trace!("PreambleDetected");
573 } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() {
574 trace!("SyncwordValid");
575 }
576
577 // handle completions
578 if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() {
579 self.brd_set_operating_mode(RadioMode::StandbyRC);
580 return Ok(());
581 } else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() {
582 if !self.rx_continuous {
583 self.brd_set_operating_mode(RadioMode::StandbyRC);
584
585 // implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3)
586 self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?;
587 let mut evt_clr = [0x00u8];
588 self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?;
589 evt_clr[0] |= 1 << 1;
590 self.brd_write_registers(Register::EvtClr, &evt_clr).await?;
591 }
592
593 if receiving_buffer.is_some() && received_len.is_some() {
594 *(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?;
595 }
596 self.packet_status = self.sub_get_packet_status().await?.into();
597 return Ok(());
598 } else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() {
599 if cad_activity_detected.is_some() {
600 *(cad_activity_detected.unwrap()) =
601 (irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value();
602 }
603 self.brd_set_operating_mode(RadioMode::StandbyRC);
604 return Ok(());
605 }
606
607 // if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid
608 // are in that category), loop to wait again
609 }
610 }
611
612 // SX126x-specific functions
613
614 /// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms]
615 pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
616 self.sub_set_dio_irq_params(
617 IrqMask::All.value(),
618 IrqMask::All.value(),
619 IrqMask::None.value(),
620 IrqMask::None.value(),
621 )
622 .await?;
623
624 if self.rx_continuous {
625 self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous
626 } else {
627 self.sub_set_rx_boosted(timeout << 6).await?;
628 }
629
630 Ok(())
631 }
632
633 /// Set the Rx duty cycle management parameters (SX126x radios only)
634 /// rx_time structure describing reception timeout value
635 /// sleep_time structure describing sleep timeout value
636 pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
637 self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?;
638 Ok(())
639 }
640
641 pub fn get_latest_packet_status(&mut self) -> Option<PacketStatus> {
642 self.packet_status
643 }
644
645 // Utilities
646
647 async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError<BUS>> {
648 let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize];
649
650 // Read the address and registers already added to the list
651 self.brd_read_registers(Register::RetentionList, &mut buffer).await?;
652
653 let number_of_registers = buffer[0];
654 for i in 0..number_of_registers {
655 if register_address
656 == ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16)
657 {
658 return Ok(()); // register already in list
659 }
660 }
661
662 if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION {
663 buffer[0] += 1; // increment number of registers
664
665 buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8;
666 buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8;
667 self.brd_write_registers(Register::RetentionList, &buffer).await?;
668
669 Ok(())
670 } else {
671 Err(RadioError::RetentionListExceeded)
672 }
673 }
674
675 fn get_lora_time_on_air_numerator(
676 spreading_factor: SpreadingFactor,
677 bandwidth: Bandwidth,
678 coding_rate: CodingRate,
679 preamble_length: u16,
680 fixed_len: bool,
681 payload_len: u8,
682 crc_on: bool,
683 ) -> u32 {
684 let cell_denominator;
685 let cr_denominator = (coding_rate.value() as i32) + 4;
686
687 // Ensure that the preamble length is at least 12 symbols when using SF5 or SF6
688 let mut preamble_length_final = preamble_length;
689 if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
690 && (preamble_length < 12)
691 {
692 preamble_length_final = 12;
693 }
694
695 let mut low_data_rate_optimize = false;
696 if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
697 && (bandwidth == Bandwidth::_125KHz))
698 || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
699 {
700 low_data_rate_optimize = true;
701 }
702
703 let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 })
704 - (4 * spreading_factor.value() as i32)
705 + (if fixed_len { 0 } else { 20 });
706
707 if spreading_factor.value() <= 6 {
708 cell_denominator = 4 * (spreading_factor.value() as i32);
709 } else {
710 cell_numerator += 8;
711 if low_data_rate_optimize {
712 cell_denominator = 4 * ((spreading_factor.value() as i32) - 2);
713 } else {
714 cell_denominator = 4 * (spreading_factor.value() as i32);
715 }
716 }
717
718 if cell_numerator < 0 {
719 cell_numerator = 0;
720 }
721
722 let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator)
723 + (preamble_length_final as i32)
724 + 12;
725
726 if spreading_factor.value() <= 6 {
727 intermediate = intermediate + 2;
728 }
729
730 (((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32
731 }
732}
diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs
deleted file mode 100644
index e270b2a09..000000000
--- a/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs
+++ /dev/null
@@ -1,469 +0,0 @@
1use core::fmt::Debug;
2
3use lorawan_device::async_device::radio as device;
4
5#[allow(clippy::upper_case_acronyms)]
6#[derive(Debug)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub enum RadioError<BUS> {
9 SPI(BUS),
10 CS,
11 Reset,
12 AntRx,
13 AntTx,
14 Busy,
15 DIO1,
16 PayloadSizeMismatch(usize, usize),
17 RetentionListExceeded,
18 InvalidBandwidth,
19 ModulationParamsMissing,
20 PacketParamsMissing,
21 HeaderError,
22 CRCErrorUnexpected,
23 CRCErrorOnReceive,
24 TransmitTimeout,
25 ReceiveTimeout,
26 TimeoutUnexpected,
27 TransmitDoneUnexpected,
28 ReceiveDoneUnexpected,
29 CADUnexpected,
30}
31
32pub struct RadioSystemError {
33 pub rc_64khz_calibration: bool,
34 pub rc_13mhz_calibration: bool,
35 pub pll_calibration: bool,
36 pub adc_calibration: bool,
37 pub image_calibration: bool,
38 pub xosc_start: bool,
39 pub pll_lock: bool,
40 pub pa_ramp: bool,
41}
42
43#[derive(Clone, Copy, PartialEq)]
44pub enum PacketType {
45 GFSK = 0x00,
46 LoRa = 0x01,
47 None = 0x0F,
48}
49
50impl PacketType {
51 pub const fn value(self) -> u8 {
52 self as u8
53 }
54 pub fn to_enum(value: u8) -> Self {
55 if value == 0x00 {
56 PacketType::GFSK
57 } else if value == 0x01 {
58 PacketType::LoRa
59 } else {
60 PacketType::None
61 }
62 }
63}
64
65#[derive(Clone, Copy)]
66pub struct PacketStatus {
67 pub rssi: i8,
68 pub snr: i8,
69 pub signal_rssi: i8,
70 pub freq_error: u32,
71}
72
73#[derive(Clone, Copy, PartialEq)]
74pub enum RadioType {
75 SX1261,
76 SX1262,
77}
78
79#[derive(Clone, Copy, PartialEq)]
80pub enum RadioMode {
81 Sleep = 0x00, // sleep mode
82 StandbyRC = 0x01, // standby mode with RC oscillator
83 StandbyXOSC = 0x02, // standby mode with XOSC oscillator
84 FrequencySynthesis = 0x03, // frequency synthesis mode
85 Transmit = 0x04, // transmit mode
86 Receive = 0x05, // receive mode
87 ReceiveDutyCycle = 0x06, // receive duty cycle mode
88 ChannelActivityDetection = 0x07, // channel activity detection mode
89}
90
91impl RadioMode {
92 /// Returns the value of the mode.
93 pub const fn value(self) -> u8 {
94 self as u8
95 }
96 pub fn to_enum(value: u8) -> Self {
97 if value == 0x00 {
98 RadioMode::Sleep
99 } else if value == 0x01 {
100 RadioMode::StandbyRC
101 } else if value == 0x02 {
102 RadioMode::StandbyXOSC
103 } else if value == 0x03 {
104 RadioMode::FrequencySynthesis
105 } else if value == 0x04 {
106 RadioMode::Transmit
107 } else if value == 0x05 {
108 RadioMode::Receive
109 } else if value == 0x06 {
110 RadioMode::ReceiveDutyCycle
111 } else if value == 0x07 {
112 RadioMode::ChannelActivityDetection
113 } else {
114 RadioMode::Sleep
115 }
116 }
117}
118
119pub enum RadioState {
120 Idle = 0x00,
121 RxRunning = 0x01,
122 TxRunning = 0x02,
123 ChannelActivityDetecting = 0x03,
124}
125
126impl RadioState {
127 /// Returns the value of the state.
128 pub fn value(self) -> u8 {
129 self as u8
130 }
131}
132
133pub struct RadioStatus {
134 pub cmd_status: u8,
135 pub chip_mode: u8,
136}
137
138impl RadioStatus {
139 pub fn value(self) -> u8 {
140 (self.chip_mode << 4) | (self.cmd_status << 1)
141 }
142}
143
144#[derive(Clone, Copy)]
145pub enum IrqMask {
146 None = 0x0000,
147 TxDone = 0x0001,
148 RxDone = 0x0002,
149 PreambleDetected = 0x0004,
150 SyncwordValid = 0x0008,
151 HeaderValid = 0x0010,
152 HeaderError = 0x0020,
153 CRCError = 0x0040,
154 CADDone = 0x0080,
155 CADActivityDetected = 0x0100,
156 RxTxTimeout = 0x0200,
157 All = 0xFFFF,
158}
159
160impl IrqMask {
161 pub fn value(self) -> u16 {
162 self as u16
163 }
164}
165
166#[derive(Clone, Copy)]
167pub enum Register {
168 PacketParams = 0x0704, // packet configuration
169 PayloadLength = 0x0702, // payload size
170 SynchTimeout = 0x0706, // recalculated number of symbols
171 Syncword = 0x06C0, // Syncword values
172 LoRaSyncword = 0x0740, // LoRa Syncword value
173 GeneratedRandomNumber = 0x0819, //32-bit generated random number
174 AnaLNA = 0x08E2, // disable the LNA
175 AnaMixer = 0x08E5, // disable the mixer
176 RxGain = 0x08AC, // RX gain (0x94: power saving, 0x96: rx boosted)
177 XTATrim = 0x0911, // device internal trimming capacitor
178 OCP = 0x08E7, // over current protection max value
179 RetentionList = 0x029F, // retention list
180 IQPolarity = 0x0736, // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
181 TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
182 TxClampCfg = 0x08D8, // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
183 RTCCtrl = 0x0902, // RTC control
184 EvtClr = 0x0944, // event clear
185}
186
187impl Register {
188 pub fn addr(self) -> u16 {
189 self as u16
190 }
191}
192
193#[derive(Clone, Copy, PartialEq)]
194pub enum OpCode {
195 GetStatus = 0xC0,
196 WriteRegister = 0x0D,
197 ReadRegister = 0x1D,
198 WriteBuffer = 0x0E,
199 ReadBuffer = 0x1E,
200 SetSleep = 0x84,
201 SetStandby = 0x80,
202 SetFS = 0xC1,
203 SetTx = 0x83,
204 SetRx = 0x82,
205 SetRxDutyCycle = 0x94,
206 SetCAD = 0xC5,
207 SetTxContinuousWave = 0xD1,
208 SetTxContinuousPremable = 0xD2,
209 SetPacketType = 0x8A,
210 GetPacketType = 0x11,
211 SetRFFrequency = 0x86,
212 SetTxParams = 0x8E,
213 SetPAConfig = 0x95,
214 SetCADParams = 0x88,
215 SetBufferBaseAddress = 0x8F,
216 SetModulationParams = 0x8B,
217 SetPacketParams = 0x8C,
218 GetRxBufferStatus = 0x13,
219 GetPacketStatus = 0x14,
220 GetRSSIInst = 0x15,
221 GetStats = 0x10,
222 ResetStats = 0x00,
223 CfgDIOIrq = 0x08,
224 GetIrqStatus = 0x12,
225 ClrIrqStatus = 0x02,
226 Calibrate = 0x89,
227 CalibrateImage = 0x98,
228 SetRegulatorMode = 0x96,
229 GetErrors = 0x17,
230 ClrErrors = 0x07,
231 SetTCXOMode = 0x97,
232 SetTxFallbackMode = 0x93,
233 SetRFSwitchMode = 0x9D,
234 SetStopRxTimerOnPreamble = 0x9F,
235 SetLoRaSymbTimeout = 0xA0,
236}
237
238impl OpCode {
239 pub fn value(self) -> u8 {
240 self as u8
241 }
242}
243
244pub struct SleepParams {
245 pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC
246 pub reset: bool,
247 pub warm_start: bool,
248}
249
250impl SleepParams {
251 pub fn value(self) -> u8 {
252 ((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8)
253 }
254}
255
256#[derive(Clone, Copy, PartialEq)]
257pub enum StandbyMode {
258 RC = 0x00,
259 XOSC = 0x01,
260}
261
262impl StandbyMode {
263 pub fn value(self) -> u8 {
264 self as u8
265 }
266}
267
268#[derive(Clone, Copy)]
269pub enum RegulatorMode {
270 UseLDO = 0x00,
271 UseDCDC = 0x01,
272}
273
274impl RegulatorMode {
275 pub fn value(self) -> u8 {
276 self as u8
277 }
278}
279
280#[derive(Clone, Copy)]
281pub struct CalibrationParams {
282 pub rc64k_enable: bool, // calibrate RC64K clock
283 pub rc13m_enable: bool, // calibrate RC13M clock
284 pub pll_enable: bool, // calibrate PLL
285 pub adc_pulse_enable: bool, // calibrate ADC Pulse
286 pub adc_bulkn_enable: bool, // calibrate ADC bulkN
287 pub adc_bulkp_enable: bool, // calibrate ADC bulkP
288 pub img_enable: bool,
289}
290
291impl CalibrationParams {
292 pub fn value(self) -> u8 {
293 ((self.img_enable as u8) << 6)
294 | ((self.adc_bulkp_enable as u8) << 5)
295 | ((self.adc_bulkn_enable as u8) << 4)
296 | ((self.adc_pulse_enable as u8) << 3)
297 | ((self.pll_enable as u8) << 2)
298 | ((self.rc13m_enable as u8) << 1)
299 | ((self.rc64k_enable as u8) << 0)
300 }
301}
302
303#[derive(Clone, Copy)]
304pub enum TcxoCtrlVoltage {
305 Ctrl1V6 = 0x00,
306 Ctrl1V7 = 0x01,
307 Ctrl1V8 = 0x02,
308 Ctrl2V2 = 0x03,
309 Ctrl2V4 = 0x04,
310 Ctrl2V7 = 0x05,
311 Ctrl3V0 = 0x06,
312 Ctrl3V3 = 0x07,
313}
314
315impl TcxoCtrlVoltage {
316 pub fn value(self) -> u8 {
317 self as u8
318 }
319}
320
321#[derive(Clone, Copy)]
322pub enum RampTime {
323 Ramp10Us = 0x00,
324 Ramp20Us = 0x01,
325 Ramp40Us = 0x02,
326 Ramp80Us = 0x03,
327 Ramp200Us = 0x04,
328 Ramp800Us = 0x05,
329 Ramp1700Us = 0x06,
330 Ramp3400Us = 0x07,
331}
332
333impl RampTime {
334 pub fn value(self) -> u8 {
335 self as u8
336 }
337}
338
339#[derive(Clone, Copy, PartialEq)]
340pub enum SpreadingFactor {
341 _5 = 0x05,
342 _6 = 0x06,
343 _7 = 0x07,
344 _8 = 0x08,
345 _9 = 0x09,
346 _10 = 0x0A,
347 _11 = 0x0B,
348 _12 = 0x0C,
349}
350
351impl SpreadingFactor {
352 pub fn value(self) -> u8 {
353 self as u8
354 }
355}
356
357impl From<device::SpreadingFactor> for SpreadingFactor {
358 fn from(sf: device::SpreadingFactor) -> Self {
359 match sf {
360 device::SpreadingFactor::_7 => SpreadingFactor::_7,
361 device::SpreadingFactor::_8 => SpreadingFactor::_8,
362 device::SpreadingFactor::_9 => SpreadingFactor::_9,
363 device::SpreadingFactor::_10 => SpreadingFactor::_10,
364 device::SpreadingFactor::_11 => SpreadingFactor::_11,
365 device::SpreadingFactor::_12 => SpreadingFactor::_12,
366 }
367 }
368}
369
370#[derive(Clone, Copy, PartialEq)]
371pub enum Bandwidth {
372 _500KHz = 0x06,
373 _250KHz = 0x05,
374 _125KHz = 0x04,
375}
376
377impl Bandwidth {
378 pub fn value(self) -> u8 {
379 self as u8
380 }
381
382 pub fn value_in_hz(self) -> u32 {
383 match self {
384 Bandwidth::_125KHz => 125000u32,
385 Bandwidth::_250KHz => 250000u32,
386 Bandwidth::_500KHz => 500000u32,
387 }
388 }
389}
390
391impl From<device::Bandwidth> for Bandwidth {
392 fn from(bw: device::Bandwidth) -> Self {
393 match bw {
394 device::Bandwidth::_500KHz => Bandwidth::_500KHz,
395 device::Bandwidth::_250KHz => Bandwidth::_250KHz,
396 device::Bandwidth::_125KHz => Bandwidth::_125KHz,
397 }
398 }
399}
400
401#[derive(Clone, Copy)]
402pub enum CodingRate {
403 _4_5 = 0x01,
404 _4_6 = 0x02,
405 _4_7 = 0x03,
406 _4_8 = 0x04,
407}
408
409impl CodingRate {
410 pub fn value(self) -> u8 {
411 self as u8
412 }
413}
414
415impl From<device::CodingRate> for CodingRate {
416 fn from(cr: device::CodingRate) -> Self {
417 match cr {
418 device::CodingRate::_4_5 => CodingRate::_4_5,
419 device::CodingRate::_4_6 => CodingRate::_4_6,
420 device::CodingRate::_4_7 => CodingRate::_4_7,
421 device::CodingRate::_4_8 => CodingRate::_4_8,
422 }
423 }
424}
425
426#[derive(Clone, Copy)]
427pub struct ModulationParams {
428 pub spreading_factor: SpreadingFactor,
429 pub bandwidth: Bandwidth,
430 pub coding_rate: CodingRate,
431 pub low_data_rate_optimize: u8,
432}
433
434#[derive(Clone, Copy)]
435pub struct PacketParams {
436 pub preamble_length: u16, // number of LoRa symbols in the preamble
437 pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length)
438 pub payload_length: u8,
439 pub crc_on: bool,
440 pub iq_inverted: bool,
441}
442
443#[derive(Clone, Copy)]
444pub enum CADSymbols {
445 _1 = 0x00,
446 _2 = 0x01,
447 _4 = 0x02,
448 _8 = 0x03,
449 _16 = 0x04,
450}
451
452impl CADSymbols {
453 pub fn value(self) -> u8 {
454 self as u8
455 }
456}
457
458#[derive(Clone, Copy)]
459pub enum CADExitMode {
460 CADOnly = 0x00,
461 CADRx = 0x01,
462 CADLBT = 0x10,
463}
464
465impl CADExitMode {
466 pub fn value(self) -> u8 {
467 self as u8
468 }
469}
diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs
deleted file mode 100644
index 2e78b919b..000000000
--- a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs
+++ /dev/null
@@ -1,674 +0,0 @@
1use embedded_hal::digital::v2::OutputPin;
2use embedded_hal_async::digital::Wait;
3use embedded_hal_async::spi::SpiBus;
4
5use super::mod_params::*;
6use super::LoRa;
7
8// Internal frequency of the radio
9const SX126X_XTAL_FREQ: u32 = 32000000;
10
11// Scaling factor used to perform fixed-point operations
12const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14;
13
14// PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT
15const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT);
16
17// Maximum value for parameter symbNum
18const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248;
19
20// Provides board-specific functionality for Semtech SX126x-based boards
21
22impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
23where
24 SPI: SpiBus<u8, Error = BUS>,
25 CTRL: OutputPin,
26 WAIT: Wait,
27{
28 // Initialize the radio driver
29 pub(super) async fn sub_init(&mut self) -> Result<(), RadioError<BUS>> {
30 self.brd_reset().await?;
31 self.brd_wakeup().await?;
32 self.sub_set_standby(StandbyMode::RC).await?;
33 self.brd_io_tcxo_init().await?;
34 self.brd_io_rf_switch_init().await?;
35 self.image_calibrated = false;
36 Ok(())
37 }
38
39 // Wakeup the radio if it is in Sleep mode and check that Busy is low
40 pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError<BUS>> {
41 let operating_mode = self.brd_get_operating_mode();
42 if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle {
43 self.brd_wakeup().await?;
44 }
45 self.brd_wait_on_busy().await?;
46 Ok(())
47 }
48
49 // Save the payload to be sent in the radio buffer
50 pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError<BUS>> {
51 self.brd_write_buffer(0x00, payload).await?;
52 Ok(())
53 }
54
55 // Read the payload received.
56 pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
57 let (size, offset) = self.sub_get_rx_buffer_status().await?;
58 if (size as usize) > buffer.len() {
59 Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len()))
60 } else {
61 self.brd_read_buffer(offset, buffer).await?;
62 Ok(size)
63 }
64 }
65
66 // Send a payload
67 pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
68 self.sub_set_payload(payload).await?;
69 self.sub_set_tx(timeout).await?;
70 Ok(())
71 }
72
73 // Get a 32-bit random value generated by the radio. A valid packet type must have been configured before using this command.
74 //
75 // The radio must be in reception mode before executing this function. This code can potentially result in interrupt generation. It is the responsibility of
76 // the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts
77 // generated during this process will not cause undesired side-effects in the software.
78 //
79 // The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing.
80 pub(super) async fn sub_get_random(&mut self) -> Result<u32, RadioError<BUS>> {
81 let mut reg_ana_lna_buffer_original = [0x00u8];
82 let mut reg_ana_mixer_buffer_original = [0x00u8];
83 let mut reg_ana_lna_buffer = [0x00u8];
84 let mut reg_ana_mixer_buffer = [0x00u8];
85 let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8];
86
87 self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original)
88 .await?;
89 reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0));
90 self.brd_write_registers(Register::AnaLNA, &reg_ana_lna_buffer).await?;
91
92 self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original)
93 .await?;
94 reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7));
95 self.brd_write_registers(Register::AnaMixer, &reg_ana_mixer_buffer)
96 .await?;
97
98 // Set radio in continuous reception
99 self.sub_set_rx(0xFFFFFFu32).await?;
100
101 self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer)
102 .await?;
103
104 self.sub_set_standby(StandbyMode::RC).await?;
105
106 self.brd_write_registers(Register::AnaLNA, &reg_ana_lna_buffer_original)
107 .await?;
108 self.brd_write_registers(Register::AnaMixer, &reg_ana_mixer_buffer_original)
109 .await?;
110
111 Ok(Self::convert_u8_buffer_to_u32(&number_buffer))
112 }
113
114 // Set the radio in sleep mode
115 pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError<BUS>> {
116 self.brd_ant_sleep()?;
117
118 if !sleep_config.warm_start {
119 self.image_calibrated = false;
120 }
121
122 self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()])
123 .await?;
124 self.brd_set_operating_mode(RadioMode::Sleep);
125 Ok(())
126 }
127
128 // Set the radio in configuration mode
129 pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError<BUS>> {
130 self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?;
131 if mode == StandbyMode::RC {
132 self.brd_set_operating_mode(RadioMode::StandbyRC);
133 } else {
134 self.brd_set_operating_mode(RadioMode::StandbyXOSC);
135 }
136
137 self.brd_ant_sleep()?;
138 Ok(())
139 }
140
141 // Set the radio in FS mode
142 pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError<BUS>> {
143 // antenna settings ???
144 self.brd_write_command(OpCode::SetFS, &[]).await?;
145 self.brd_set_operating_mode(RadioMode::FrequencySynthesis);
146 Ok(())
147 }
148
149 // Set the radio in transmission mode with timeout specified
150 pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
151 let buffer = [
152 Self::timeout_1(timeout),
153 Self::timeout_2(timeout),
154 Self::timeout_3(timeout),
155 ];
156
157 self.brd_ant_set_tx()?;
158
159 self.brd_set_operating_mode(RadioMode::Transmit);
160 self.brd_write_command(OpCode::SetTx, &buffer).await?;
161 Ok(())
162 }
163
164 // Set the radio in reception mode with timeout specified
165 pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
166 let buffer = [
167 Self::timeout_1(timeout),
168 Self::timeout_2(timeout),
169 Self::timeout_3(timeout),
170 ];
171
172 self.brd_ant_set_rx()?;
173
174 self.brd_set_operating_mode(RadioMode::Receive);
175 self.brd_write_registers(Register::RxGain, &[0x94u8]).await?;
176 self.brd_write_command(OpCode::SetRx, &buffer).await?;
177 Ok(())
178 }
179
180 // Set the radio in reception mode with Boosted LNA gain and timeout specified
181 pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
182 let buffer = [
183 Self::timeout_1(timeout),
184 Self::timeout_2(timeout),
185 Self::timeout_3(timeout),
186 ];
187
188 self.brd_ant_set_rx()?;
189
190 self.brd_set_operating_mode(RadioMode::Receive);
191 // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity
192 self.brd_write_registers(Register::RxGain, &[0x96u8]).await?;
193 self.brd_write_command(OpCode::SetRx, &buffer).await?;
194 Ok(())
195 }
196
197 // Set the Rx duty cycle management parameters
198 pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
199 let buffer = [
200 ((rx_time >> 16) & 0xFF) as u8,
201 ((rx_time >> 8) & 0xFF) as u8,
202 (rx_time & 0xFF) as u8,
203 ((sleep_time >> 16) & 0xFF) as u8,
204 ((sleep_time >> 8) & 0xFF) as u8,
205 (sleep_time & 0xFF) as u8,
206 ];
207
208 // antenna settings ???
209
210 self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?;
211 self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle);
212 Ok(())
213 }
214
215 // Set the radio in CAD mode
216 pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError<BUS>> {
217 self.brd_ant_set_rx()?;
218
219 self.brd_write_command(OpCode::SetCAD, &[]).await?;
220 self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
221 Ok(())
222 }
223
224 // Set the radio in continuous wave transmission mode
225 pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError<BUS>> {
226 self.brd_ant_set_tx()?;
227
228 self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?;
229 self.brd_set_operating_mode(RadioMode::Transmit);
230 Ok(())
231 }
232
233 // Set the radio in continuous preamble transmission mode
234 pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError<BUS>> {
235 self.brd_ant_set_tx()?;
236
237 self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?;
238 self.brd_set_operating_mode(RadioMode::Transmit);
239 Ok(())
240 }
241
242 // Decide which interrupt will stop the internal radio rx timer.
243 // false timer stop after header/syncword detection
244 // true timer stop after preamble detection
245 pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect(
246 &mut self,
247 enable: bool,
248 ) -> Result<(), RadioError<BUS>> {
249 self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8])
250 .await?;
251 Ok(())
252 }
253
254 // Set the number of symbols the radio will wait to validate a reception
255 pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError<BUS>> {
256 let mut exp = 0u8;
257 let mut reg;
258 let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1;
259 while mant > 31 {
260 mant = (mant + 3) >> 2;
261 exp += 1;
262 }
263 reg = mant << ((2 * exp) + 1);
264
265 self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?;
266
267 if symb_num != 0 {
268 reg = exp + (mant << 3);
269 self.brd_write_registers(Register::SynchTimeout, &[reg]).await?;
270 }
271
272 Ok(())
273 }
274
275 // Set the power regulators operating mode (LDO or DC_DC). Using only LDO implies that the Rx or Tx current is doubled
276 pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError<BUS>> {
277 self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()])
278 .await?;
279 Ok(())
280 }
281
282 // Calibrate the given radio block
283 pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError<BUS>> {
284 self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()])
285 .await?;
286 Ok(())
287 }
288
289 // Calibrate the image rejection based on the given frequency
290 pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError<BUS>> {
291 let mut cal_freq = [0x00u8, 0x00u8];
292
293 if freq > 900000000 {
294 cal_freq[0] = 0xE1;
295 cal_freq[1] = 0xE9;
296 } else if freq > 850000000 {
297 cal_freq[0] = 0xD7;
298 cal_freq[1] = 0xDB;
299 } else if freq > 770000000 {
300 cal_freq[0] = 0xC1;
301 cal_freq[1] = 0xC5;
302 } else if freq > 460000000 {
303 cal_freq[0] = 0x75;
304 cal_freq[1] = 0x81;
305 } else if freq > 425000000 {
306 cal_freq[0] = 0x6B;
307 cal_freq[1] = 0x6F;
308 }
309 self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?;
310 Ok(())
311 }
312
313 // Activate the extention of the timeout when a long preamble is used
314 pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError<BUS>> {
315 Ok(()) // no operation currently
316 }
317
318 // Set the transmission parameters
319 // hp_max 0 for sx1261, 7 for sx1262
320 // device_sel 1 for sx1261, 0 for sx1262
321 // pa_lut 0 for 14dBm LUT, 1 for 22dBm LUT
322 pub(super) async fn sub_set_pa_config(
323 &mut self,
324 pa_duty_cycle: u8,
325 hp_max: u8,
326 device_sel: u8,
327 pa_lut: u8,
328 ) -> Result<(), RadioError<BUS>> {
329 self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut])
330 .await?;
331 Ok(())
332 }
333
334 // Define into which mode the chip goes after a TX / RX done
335 pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError<BUS>> {
336 self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode])
337 .await?;
338 Ok(())
339 }
340
341 // Set the IRQ mask and DIO masks
342 pub(super) async fn sub_set_dio_irq_params(
343 &mut self,
344 irq_mask: u16,
345 dio1_mask: u16,
346 dio2_mask: u16,
347 dio3_mask: u16,
348 ) -> Result<(), RadioError<BUS>> {
349 let mut buffer = [0x00u8; 8];
350
351 buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8;
352 buffer[1] = (irq_mask & 0x00FF) as u8;
353 buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8;
354 buffer[3] = (dio1_mask & 0x00FF) as u8;
355 buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8;
356 buffer[5] = (dio2_mask & 0x00FF) as u8;
357 buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8;
358 buffer[7] = (dio3_mask & 0x00FF) as u8;
359 self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?;
360 Ok(())
361 }
362
363 // Return the current IRQ status
364 pub(super) async fn sub_get_irq_status(&mut self) -> Result<u16, RadioError<BUS>> {
365 let mut irq_status = [0x00u8, 0x00u8];
366 self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?;
367 Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16))
368 }
369
370 // Indicate if DIO2 is used to control an RF Switch
371 pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError<BUS>> {
372 self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?;
373 Ok(())
374 }
375
376 // Indicate if the radio main clock is supplied from a TCXO
377 // tcxo_voltage voltage used to control the TCXO on/off from DIO3
378 // timeout duration given to the TCXO to go to 32MHz
379 pub(super) async fn sub_set_dio3_as_tcxo_ctrl(
380 &mut self,
381 tcxo_voltage: TcxoCtrlVoltage,
382 timeout: u32,
383 ) -> Result<(), RadioError<BUS>> {
384 let buffer = [
385 tcxo_voltage.value() & 0x07,
386 Self::timeout_1(timeout),
387 Self::timeout_2(timeout),
388 Self::timeout_3(timeout),
389 ];
390 self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?;
391
392 Ok(())
393 }
394
395 // Set the RF frequency (Hz)
396 pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
397 let mut buffer = [0x00u8; 4];
398
399 if !self.image_calibrated {
400 self.sub_calibrate_image(frequency).await?;
401 self.image_calibrated = true;
402 }
403
404 let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency);
405
406 buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8;
407 buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8;
408 buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8;
409 buffer[3] = (freq_in_pll_steps & 0xFF) as u8;
410 self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?;
411 Ok(())
412 }
413
414 // Set the radio for the given protocol (LoRa or GFSK). This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters.
415 pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError<BUS>> {
416 self.packet_type = packet_type;
417 self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()])
418 .await?;
419 Ok(())
420 }
421
422 // Get the current radio protocol (LoRa or GFSK)
423 pub(super) fn sub_get_packet_type(&mut self) -> PacketType {
424 self.packet_type
425 }
426
427 // Set the transmission parameters
428 // power RF output power [-18..13] dBm
429 // ramp_time transmission ramp up time
430 pub(super) async fn sub_set_tx_params(
431 &mut self,
432 mut power: i8,
433 ramp_time: RampTime,
434 ) -> Result<(), RadioError<BUS>> {
435 if self.brd_get_radio_type() == RadioType::SX1261 {
436 if power == 15 {
437 self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?;
438 } else {
439 self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?;
440 }
441
442 if power >= 14 {
443 power = 14;
444 } else if power < -17 {
445 power = -17;
446 }
447 } else {
448 // Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
449 let mut tx_clamp_cfg = [0x00u8];
450 self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?;
451 tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1);
452 self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?;
453
454 self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?;
455
456 if power > 22 {
457 power = 22;
458 } else if power < -9 {
459 power = -9;
460 }
461 }
462
463 // power conversion of negative number from i8 to u8 ???
464 self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()])
465 .await?;
466 Ok(())
467 }
468
469 // Set the modulation parameters
470 pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError<BUS>> {
471 if self.modulation_params.is_some() {
472 let mut buffer = [0x00u8; 4];
473
474 // Since this driver only supports LoRa, ensure the packet type is set accordingly
475 self.sub_set_packet_type(PacketType::LoRa).await?;
476
477 let modulation_params = self.modulation_params.unwrap();
478 buffer[0] = modulation_params.spreading_factor.value();
479 buffer[1] = modulation_params.bandwidth.value();
480 buffer[2] = modulation_params.coding_rate.value();
481 buffer[3] = modulation_params.low_data_rate_optimize;
482
483 self.brd_write_command(OpCode::SetModulationParams, &buffer).await?;
484 Ok(())
485 } else {
486 Err(RadioError::ModulationParamsMissing)
487 }
488 }
489
490 // Set the packet parameters
491 pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError<BUS>> {
492 if self.packet_params.is_some() {
493 let mut buffer = [0x00u8; 6];
494
495 // Since this driver only supports LoRa, ensure the packet type is set accordingly
496 self.sub_set_packet_type(PacketType::LoRa).await?;
497
498 let packet_params = self.packet_params.unwrap();
499 buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8;
500 buffer[1] = (packet_params.preamble_length & 0xFF) as u8;
501 buffer[2] = packet_params.implicit_header as u8;
502 buffer[3] = packet_params.payload_length;
503 buffer[4] = packet_params.crc_on as u8;
504 buffer[5] = packet_params.iq_inverted as u8;
505
506 self.brd_write_command(OpCode::SetPacketParams, &buffer).await?;
507 Ok(())
508 } else {
509 Err(RadioError::PacketParamsMissing)
510 }
511 }
512
513 // Set the channel activity detection (CAD) parameters
514 // symbols number of symbols to use for CAD operations
515 // det_peak limit for detection of SNR peak used in the CAD
516 // det_min minimum symbol recognition for CAD
517 // exit_mode operation to be done at the end of CAD action
518 // timeout timeout value to abort the CAD activity
519
520 pub(super) async fn sub_set_cad_params(
521 &mut self,
522 symbols: CADSymbols,
523 det_peak: u8,
524 det_min: u8,
525 exit_mode: CADExitMode,
526 timeout: u32,
527 ) -> Result<(), RadioError<BUS>> {
528 let mut buffer = [0x00u8; 7];
529
530 buffer[0] = symbols.value();
531 buffer[1] = det_peak;
532 buffer[2] = det_min;
533 buffer[3] = exit_mode.value();
534 buffer[4] = Self::timeout_1(timeout);
535 buffer[5] = Self::timeout_2(timeout);
536 buffer[6] = Self::timeout_3(timeout);
537
538 self.brd_write_command(OpCode::SetCADParams, &buffer).await?;
539 self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
540 Ok(())
541 }
542
543 // Set the data buffer base address for transmission and reception
544 pub(super) async fn sub_set_buffer_base_address(
545 &mut self,
546 tx_base_address: u8,
547 rx_base_address: u8,
548 ) -> Result<(), RadioError<BUS>> {
549 self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address])
550 .await?;
551 Ok(())
552 }
553
554 // Get the current radio status
555 pub(super) async fn sub_get_status(&mut self) -> Result<RadioStatus, RadioError<BUS>> {
556 let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?;
557 Ok(RadioStatus {
558 cmd_status: (status & (0x07 << 1)) >> 1,
559 chip_mode: (status & (0x07 << 4)) >> 4,
560 })
561 }
562
563 // Get the instantaneous RSSI value for the last packet received
564 pub(super) async fn sub_get_rssi_inst(&mut self) -> Result<i8, RadioError<BUS>> {
565 let mut buffer = [0x00u8];
566 self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?;
567 let rssi: i8 = ((-(buffer[0] as i32)) >> 1) as i8; // check this ???
568 Ok(rssi)
569 }
570
571 // Get the last received packet buffer status
572 pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError<BUS>> {
573 if self.packet_params.is_some() {
574 let mut status = [0x00u8; 2];
575 let mut payload_length_buffer = [0x00u8];
576
577 self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?;
578 if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header {
579 self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer)
580 .await?;
581 } else {
582 payload_length_buffer[0] = status[0];
583 }
584
585 let payload_length = payload_length_buffer[0];
586 let offset = status[1];
587
588 Ok((payload_length, offset))
589 } else {
590 Err(RadioError::PacketParamsMissing)
591 }
592 }
593
594 // Get the last received packet payload status
595 pub(super) async fn sub_get_packet_status(&mut self) -> Result<PacketStatus, RadioError<BUS>> {
596 let mut status = [0x00u8; 3];
597 self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?;
598
599 // check this ???
600 let rssi = ((-(status[0] as i32)) >> 1) as i8;
601 let snr = ((status[1] as i8) + 2) >> 2;
602 let signal_rssi = ((-(status[2] as i32)) >> 1) as i8;
603 let freq_error = self.frequency_error;
604
605 Ok(PacketStatus {
606 rssi,
607 snr,
608 signal_rssi,
609 freq_error,
610 })
611 }
612
613 // Get the possible system errors
614 pub(super) async fn sub_get_device_errors(&mut self) -> Result<RadioSystemError, RadioError<BUS>> {
615 let mut errors = [0x00u8; 2];
616 self.brd_read_command(OpCode::GetErrors, &mut errors).await?;
617
618 Ok(RadioSystemError {
619 rc_64khz_calibration: (errors[1] & (1 << 0)) != 0,
620 rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0,
621 pll_calibration: (errors[1] & (1 << 2)) != 0,
622 adc_calibration: (errors[1] & (1 << 3)) != 0,
623 image_calibration: (errors[1] & (1 << 4)) != 0,
624 xosc_start: (errors[1] & (1 << 5)) != 0,
625 pll_lock: (errors[1] & (1 << 6)) != 0,
626 pa_ramp: (errors[0] & (1 << 0)) != 0,
627 })
628 }
629
630 // Clear all the errors in the device
631 pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError<BUS>> {
632 self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?;
633 Ok(())
634 }
635
636 // Clear the IRQs
637 pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError<BUS>> {
638 let mut buffer = [0x00u8, 0x00u8];
639 buffer[0] = ((irq >> 8) & 0xFF) as u8;
640 buffer[1] = (irq & 0xFF) as u8;
641 self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?;
642 Ok(())
643 }
644
645 // Utility functions
646
647 fn timeout_1(timeout: u32) -> u8 {
648 ((timeout >> 16) & 0xFF) as u8
649 }
650 fn timeout_2(timeout: u32) -> u8 {
651 ((timeout >> 8) & 0xFF) as u8
652 }
653 fn timeout_3(timeout: u32) -> u8 {
654 (timeout & 0xFF) as u8
655 }
656
657 // check this ???
658 fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 {
659 let b0 = buffer[0] as u32;
660 let b1 = buffer[1] as u32;
661 let b2 = buffer[2] as u32;
662 let b3 = buffer[3] as u32;
663 (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
664 }
665
666 fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 {
667 // Get integer and fractional parts of the frequency computed with a PLL step scaled value
668 let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED;
669 let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED);
670
671 (steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT)
672 + (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED)
673 }
674}
diff --git a/embassy-lora/src/sx127x/mod.rs b/embassy-lora/src/sx127x/mod.rs
deleted file mode 100644
index 4e8dc2232..000000000
--- a/embassy-lora/src/sx127x/mod.rs
+++ /dev/null
@@ -1,192 +0,0 @@
1use embedded_hal::digital::v2::OutputPin;
2use embedded_hal_async::digital::Wait;
3use embedded_hal_async::spi::*;
4use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig};
5use lorawan_device::async_device::Timings;
6
7mod sx127x_lora;
8use sx127x_lora::{Error as RadioError, LoRa, RadioMode, IRQ};
9
10/// Trait representing a radio switch for boards using the Sx127x radio. One some
11/// boards, this will be a dummy implementation that does nothing.
12pub trait RadioSwitch {
13 fn set_tx(&mut self);
14 fn set_rx(&mut self);
15}
16
17/// Semtech Sx127x radio peripheral
18pub struct Sx127xRadio<SPI, CS, RESET, E, I, RFS>
19where
20 SPI: SpiBus<u8, Error = E> + 'static,
21 E: 'static,
22 CS: OutputPin + 'static,
23 RESET: OutputPin + 'static,
24 I: Wait + 'static,
25 RFS: RadioSwitch + 'static,
26{
27 radio: LoRa<SPI, CS, RESET>,
28 rfs: RFS,
29 irq: I,
30}
31
32#[derive(Debug, Copy, Clone)]
33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34pub enum State {
35 Idle,
36 Txing,
37 Rxing,
38}
39
40impl<SPI, CS, RESET, E, I, RFS> Sx127xRadio<SPI, CS, RESET, E, I, RFS>
41where
42 SPI: SpiBus<u8, Error = E> + 'static,
43 CS: OutputPin + 'static,
44 RESET: OutputPin + 'static,
45 I: Wait + 'static,
46 RFS: RadioSwitch + 'static,
47 E: 'static,
48{
49 pub async fn new(
50 spi: SPI,
51 cs: CS,
52 reset: RESET,
53 irq: I,
54 rfs: RFS,
55 ) -> Result<Self, RadioError<E, CS::Error, RESET::Error>> {
56 let mut radio = LoRa::new(spi, cs, reset);
57 radio.reset().await?;
58 Ok(Self { radio, irq, rfs })
59 }
60}
61
62impl<SPI, CS, RESET, E, I, RFS> Timings for Sx127xRadio<SPI, CS, RESET, E, I, RFS>
63where
64 SPI: SpiBus<u8, Error = E> + 'static,
65 CS: OutputPin + 'static,
66 RESET: OutputPin + 'static,
67 I: Wait + 'static,
68 RFS: RadioSwitch + 'static,
69{
70 fn get_rx_window_offset_ms(&self) -> i32 {
71 -3
72 }
73 fn get_rx_window_duration_ms(&self) -> u32 {
74 1003
75 }
76}
77
78impl<SPI, CS, RESET, E, I, RFS> PhyRxTx for Sx127xRadio<SPI, CS, RESET, E, I, RFS>
79where
80 SPI: SpiBus<u8, Error = E> + 'static,
81 CS: OutputPin + 'static,
82 E: 'static,
83 RESET: OutputPin + 'static,
84 I: Wait + 'static,
85 RFS: RadioSwitch + 'static,
86{
87 type PhyError = Sx127xError;
88
89 async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, Self::PhyError> {
90 trace!("TX START");
91 self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
92 self.rfs.set_tx();
93 self.radio.set_tx_power(14, 0).await?;
94 self.radio.set_frequency(config.rf.frequency).await?;
95 // TODO: Modify radio to support other coding rates
96 self.radio.set_coding_rate_4(5).await?;
97 self.radio
98 .set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth))
99 .await?;
100 self.radio
101 .set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor))
102 .await?;
103
104 self.radio.set_preamble_length(8).await?;
105 self.radio.set_lora_pa_ramp().await?;
106 self.radio.set_lora_sync_word().await?;
107 self.radio.set_invert_iq(false).await?;
108 self.radio.set_crc(true).await?;
109
110 self.radio.set_dio0_tx_done().await?;
111
112 self.radio.transmit_start(buf).await?;
113
114 loop {
115 self.irq.wait_for_rising_edge().await.unwrap();
116 self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
117 let irq = self.radio.clear_irq().await.ok().unwrap();
118 if (irq & IRQ::IrqTxDoneMask.addr()) != 0 {
119 trace!("TX DONE");
120 return Ok(0);
121 }
122 }
123 }
124
125 async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> {
126 self.rfs.set_rx();
127 self.radio.reset_payload_length().await?;
128 self.radio.set_frequency(config.frequency).await?;
129 // TODO: Modify radio to support other coding rates
130 self.radio.set_coding_rate_4(5).await?;
131 self.radio
132 .set_signal_bandwidth(bandwidth_to_i64(config.bandwidth))
133 .await?;
134 self.radio
135 .set_spreading_factor(spreading_factor_to_u8(config.spreading_factor))
136 .await?;
137
138 self.radio.set_preamble_length(8).await?;
139 self.radio.set_lora_sync_word().await?;
140 self.radio.set_invert_iq(true).await?;
141 self.radio.set_crc(true).await?;
142
143 self.radio.set_dio0_rx_done().await?;
144 self.radio.set_mode(RadioMode::RxContinuous).await?;
145
146 loop {
147 self.irq.wait_for_rising_edge().await.unwrap();
148 self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap();
149 let irq = self.radio.clear_irq().await.ok().unwrap();
150 if (irq & IRQ::IrqRxDoneMask.addr()) != 0 {
151 let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16;
152 let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8;
153 let response = if let Ok(size) = self.radio.read_packet_size().await {
154 self.radio.read_packet(buf).await?;
155 Ok((size, RxQuality::new(rssi, snr)))
156 } else {
157 Ok((0, RxQuality::new(rssi, snr)))
158 };
159 trace!("RX DONE");
160 return response;
161 }
162 }
163 }
164}
165
166#[cfg_attr(feature = "defmt", derive(defmt::Format))]
167pub struct Sx127xError;
168
169impl<A, B, C> From<sx127x_lora::Error<A, B, C>> for Sx127xError {
170 fn from(_: sx127x_lora::Error<A, B, C>) -> Self {
171 Sx127xError
172 }
173}
174
175fn spreading_factor_to_u8(sf: SpreadingFactor) -> u8 {
176 match sf {
177 SpreadingFactor::_7 => 7,
178 SpreadingFactor::_8 => 8,
179 SpreadingFactor::_9 => 9,
180 SpreadingFactor::_10 => 10,
181 SpreadingFactor::_11 => 11,
182 SpreadingFactor::_12 => 12,
183 }
184}
185
186fn bandwidth_to_i64(bw: Bandwidth) -> i64 {
187 match bw {
188 Bandwidth::_125KHz => 125_000,
189 Bandwidth::_250KHz => 250_000,
190 Bandwidth::_500KHz => 500_000,
191 }
192}
diff --git a/embassy-lora/src/sx127x/sx127x_lora/mod.rs b/embassy-lora/src/sx127x/sx127x_lora/mod.rs
deleted file mode 100644
index aacc9da22..000000000
--- a/embassy-lora/src/sx127x/sx127x_lora/mod.rs
+++ /dev/null
@@ -1,539 +0,0 @@
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 embassy_time::{Duration, Timer};
10use embedded_hal::digital::v2::OutputPin;
11use embedded_hal_async::spi::SpiBus;
12
13mod register;
14pub use self::register::IRQ;
15use self::register::{PaConfig, Register};
16
17/// Provides high-level access to Semtech SX1276/77/78/79 based boards connected to a Raspberry Pi
18pub struct LoRa<SPI, CS, RESET> {
19 spi: SPI,
20 cs: CS,
21 reset: RESET,
22 pub explicit_header: bool,
23 pub mode: RadioMode,
24}
25
26#[allow(clippy::upper_case_acronyms)]
27#[derive(Debug)]
28#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29pub enum Error<SPI, CS, RESET> {
30 Uninformative,
31 VersionMismatch(u8),
32 CS(CS),
33 Reset(RESET),
34 SPI(SPI),
35 Transmitting,
36}
37
38use Error::*;
39
40use super::sx127x_lora::register::{FskDataModulationShaping, FskRampUpRamDown};
41
42#[cfg(not(feature = "version_0x09"))]
43const VERSION_CHECK: u8 = 0x12;
44
45#[cfg(feature = "version_0x09")]
46const VERSION_CHECK: u8 = 0x09;
47
48impl<SPI, CS, RESET, E> LoRa<SPI, CS, RESET>
49where
50 SPI: SpiBus<u8, Error = E>,
51 CS: OutputPin,
52 RESET: OutputPin,
53{
54 /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time.
55 /// This also preforms a hardware reset of the module and then puts it in standby.
56 pub fn new(spi: SPI, cs: CS, reset: RESET) -> Self {
57 Self {
58 spi,
59 cs,
60 reset,
61 explicit_header: true,
62 mode: RadioMode::Sleep,
63 }
64 }
65
66 pub async fn reset(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
67 self.reset.set_low().map_err(Reset)?;
68 Timer::after(Duration::from_millis(10)).await;
69 self.reset.set_high().map_err(Reset)?;
70 Timer::after(Duration::from_millis(10)).await;
71 let version = self.read_register(Register::RegVersion.addr()).await?;
72 if version == VERSION_CHECK {
73 self.set_mode(RadioMode::Sleep).await?;
74 self.write_register(Register::RegFifoTxBaseAddr.addr(), 0).await?;
75 self.write_register(Register::RegFifoRxBaseAddr.addr(), 0).await?;
76 let lna = self.read_register(Register::RegLna.addr()).await?;
77 self.write_register(Register::RegLna.addr(), lna | 0x03).await?;
78 self.write_register(Register::RegModemConfig3.addr(), 0x04).await?;
79 self.set_tcxo(true).await?;
80 self.set_mode(RadioMode::Stdby).await?;
81 self.cs.set_high().map_err(CS)?;
82 Ok(())
83 } else {
84 Err(Error::VersionMismatch(version))
85 }
86 }
87
88 pub async fn set_dio0_tx_done(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
89 self.write_register(Register::RegIrqFlagsMask.addr(), 0b1111_0111)
90 .await?;
91 let mapping = self.read_register(Register::RegDioMapping1.addr()).await?;
92 self.write_register(Register::RegDioMapping1.addr(), (mapping & 0x3F) | 0x40)
93 .await
94 }
95
96 pub async fn set_dio0_rx_done(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
97 self.write_register(Register::RegIrqFlagsMask.addr(), 0b0001_1111)
98 .await?;
99 let mapping = self.read_register(Register::RegDioMapping1.addr()).await?;
100 self.write_register(Register::RegDioMapping1.addr(), mapping & 0x3F)
101 .await
102 }
103
104 pub async fn transmit_start(&mut self, buffer: &[u8]) -> Result<(), Error<E, CS::Error, RESET::Error>> {
105 assert!(buffer.len() < 255);
106 if self.transmitting().await? {
107 //trace!("ALREADY TRANSMNITTING");
108 Err(Transmitting)
109 } else {
110 self.set_mode(RadioMode::Stdby).await?;
111 if self.explicit_header {
112 self.set_explicit_header_mode().await?;
113 } else {
114 self.set_implicit_header_mode().await?;
115 }
116
117 self.write_register(Register::RegIrqFlags.addr(), 0).await?;
118 self.write_register(Register::RegFifoAddrPtr.addr(), 0).await?;
119 self.write_register(Register::RegPayloadLength.addr(), 0).await?;
120 for byte in buffer.iter() {
121 self.write_register(Register::RegFifo.addr(), *byte).await?;
122 }
123 self.write_register(Register::RegPayloadLength.addr(), buffer.len() as u8)
124 .await?;
125 self.set_mode(RadioMode::Tx).await?;
126 Ok(())
127 }
128 }
129
130 pub async fn packet_ready(&mut self) -> Result<bool, Error<E, CS::Error, RESET::Error>> {
131 Ok(self.read_register(Register::RegIrqFlags.addr()).await?.get_bit(6))
132 }
133
134 pub async fn irq_flags_mask(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> {
135 Ok(self.read_register(Register::RegIrqFlagsMask.addr()).await? as u8)
136 }
137
138 pub async fn irq_flags(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> {
139 Ok(self.read_register(Register::RegIrqFlags.addr()).await? as u8)
140 }
141
142 pub async fn read_packet_size(&mut self) -> Result<usize, Error<E, CS::Error, RESET::Error>> {
143 let size = self.read_register(Register::RegRxNbBytes.addr()).await?;
144 Ok(size as usize)
145 }
146
147 /// Returns the contents of the fifo as a fixed 255 u8 array. This should only be called is there is a
148 /// new packet ready to be read.
149 pub async fn read_packet(&mut self, buffer: &mut [u8]) -> Result<(), Error<E, CS::Error, RESET::Error>> {
150 self.clear_irq().await?;
151 let size = self.read_register(Register::RegRxNbBytes.addr()).await?;
152 assert!(size as usize <= buffer.len());
153 let fifo_addr = self.read_register(Register::RegFifoRxCurrentAddr.addr()).await?;
154 self.write_register(Register::RegFifoAddrPtr.addr(), fifo_addr).await?;
155 for i in 0..size {
156 let byte = self.read_register(Register::RegFifo.addr()).await?;
157 buffer[i as usize] = byte;
158 }
159 self.write_register(Register::RegFifoAddrPtr.addr(), 0).await?;
160 Ok(())
161 }
162
163 /// Returns true if the radio is currently transmitting a packet.
164 pub async fn transmitting(&mut self) -> Result<bool, Error<E, CS::Error, RESET::Error>> {
165 if (self.read_register(Register::RegOpMode.addr()).await?) & RadioMode::Tx.addr() == RadioMode::Tx.addr() {
166 Ok(true)
167 } else {
168 if (self.read_register(Register::RegIrqFlags.addr()).await? & IRQ::IrqTxDoneMask.addr()) == 1 {
169 self.write_register(Register::RegIrqFlags.addr(), IRQ::IrqTxDoneMask.addr())
170 .await?;
171 }
172 Ok(false)
173 }
174 }
175
176 /// Clears the radio's IRQ registers.
177 pub async fn clear_irq(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> {
178 let irq_flags = self.read_register(Register::RegIrqFlags.addr()).await?;
179 self.write_register(Register::RegIrqFlags.addr(), 0xFF).await?;
180 Ok(irq_flags)
181 }
182
183 /// Sets the transmit power and pin. Levels can range from 0-14 when the output
184 /// pin = 0(RFO), and form 0-20 when output pin = 1(PaBoost). Power is in dB.
185 /// Default value is `17`.
186 pub async fn set_tx_power(
187 &mut self,
188 mut level: i32,
189 output_pin: u8,
190 ) -> Result<(), Error<E, CS::Error, RESET::Error>> {
191 if PaConfig::PaOutputRfoPin.addr() == output_pin {
192 // RFO
193 if level < 0 {
194 level = 0;
195 } else if level > 14 {
196 level = 14;
197 }
198 self.write_register(Register::RegPaConfig.addr(), (0x70 | level) as u8)
199 .await
200 } else {
201 // PA BOOST
202 if level > 17 {
203 if level > 20 {
204 level = 20;
205 }
206 // subtract 3 from level, so 18 - 20 maps to 15 - 17
207 level -= 3;
208
209 // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.)
210 self.write_register(Register::RegPaDac.addr(), 0x87).await?;
211 self.set_ocp(140).await?;
212 } else {
213 if level < 2 {
214 level = 2;
215 }
216 //Default value PA_HF/LF or +17dBm
217 self.write_register(Register::RegPaDac.addr(), 0x84).await?;
218 self.set_ocp(100).await?;
219 }
220 level -= 2;
221 self.write_register(Register::RegPaConfig.addr(), PaConfig::PaBoost.addr() | level as u8)
222 .await
223 }
224 }
225
226 pub async fn get_modem_stat(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> {
227 Ok(self.read_register(Register::RegModemStat.addr()).await? as u8)
228 }
229
230 /// Sets the over current protection on the radio(mA).
231 pub async fn set_ocp(&mut self, ma: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> {
232 let mut ocp_trim: u8 = 27;
233
234 if ma <= 120 {
235 ocp_trim = (ma - 45) / 5;
236 } else if ma <= 240 {
237 ocp_trim = (ma + 30) / 10;
238 }
239 self.write_register(Register::RegOcp.addr(), 0x20 | (0x1F & ocp_trim))
240 .await
241 }
242
243 /// Sets the state of the radio. Default mode after initiation is `Standby`.
244 pub async fn set_mode(&mut self, mode: RadioMode) -> Result<(), Error<E, CS::Error, RESET::Error>> {
245 if self.explicit_header {
246 self.set_explicit_header_mode().await?;
247 } else {
248 self.set_implicit_header_mode().await?;
249 }
250 self.write_register(
251 Register::RegOpMode.addr(),
252 RadioMode::LongRangeMode.addr() | mode.addr(),
253 )
254 .await?;
255
256 self.mode = mode;
257 Ok(())
258 }
259
260 pub async fn reset_payload_length(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
261 self.write_register(Register::RegPayloadLength.addr(), 0xFF).await
262 }
263
264 /// Sets the frequency of the radio. Values are in megahertz.
265 /// I.E. 915 MHz must be used for North America. Check regulation for your area.
266 pub async fn set_frequency(&mut self, freq: u32) -> Result<(), Error<E, CS::Error, RESET::Error>> {
267 const FREQ_STEP: f64 = 61.03515625;
268 // calculate register values
269 let frf = (freq as f64 / FREQ_STEP) as u32;
270 // write registers
271 self.write_register(Register::RegFrfMsb.addr(), ((frf & 0x00FF_0000) >> 16) as u8)
272 .await?;
273 self.write_register(Register::RegFrfMid.addr(), ((frf & 0x0000_FF00) >> 8) as u8)
274 .await?;
275 self.write_register(Register::RegFrfLsb.addr(), (frf & 0x0000_00FF) as u8)
276 .await
277 }
278
279 /// Sets the radio to use an explicit header. Default state is `ON`.
280 async fn set_explicit_header_mode(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
281 let reg_modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?;
282 self.write_register(Register::RegModemConfig1.addr(), reg_modem_config_1 & 0xfe)
283 .await?;
284 self.explicit_header = true;
285 Ok(())
286 }
287
288 /// Sets the radio to use an implicit header. Default state is `OFF`.
289 async fn set_implicit_header_mode(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
290 let reg_modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?;
291 self.write_register(Register::RegModemConfig1.addr(), reg_modem_config_1 & 0x01)
292 .await?;
293 self.explicit_header = false;
294 Ok(())
295 }
296
297 /// Sets the spreading factor of the radio. Supported values are between 6 and 12.
298 /// If a spreading factor of 6 is set, implicit header mode must be used to transmit
299 /// and receive packets. Default value is `7`.
300 pub async fn set_spreading_factor(&mut self, mut sf: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> {
301 if sf < 6 {
302 sf = 6;
303 } else if sf > 12 {
304 sf = 12;
305 }
306
307 if sf == 6 {
308 self.write_register(Register::RegDetectionOptimize.addr(), 0xc5).await?;
309 self.write_register(Register::RegDetectionThreshold.addr(), 0x0c)
310 .await?;
311 } else {
312 self.write_register(Register::RegDetectionOptimize.addr(), 0xc3).await?;
313 self.write_register(Register::RegDetectionThreshold.addr(), 0x0a)
314 .await?;
315 }
316 let modem_config_2 = self.read_register(Register::RegModemConfig2.addr()).await?;
317 self.write_register(
318 Register::RegModemConfig2.addr(),
319 (modem_config_2 & 0x0f) | ((sf << 4) & 0xf0),
320 )
321 .await?;
322 self.set_ldo_flag().await?;
323
324 self.write_register(Register::RegSymbTimeoutLsb.addr(), 0x05).await?;
325
326 Ok(())
327 }
328
329 pub async fn set_tcxo(&mut self, external: bool) -> Result<(), Error<E, CS::Error, RESET::Error>> {
330 if external {
331 self.write_register(Register::RegTcxo.addr(), 0x10).await
332 } else {
333 self.write_register(Register::RegTcxo.addr(), 0x00).await
334 }
335 }
336
337 /// Sets the signal bandwidth of the radio. Supported values are: `7800 Hz`, `10400 Hz`,
338 /// `15600 Hz`, `20800 Hz`, `31250 Hz`,`41700 Hz` ,`62500 Hz`,`125000 Hz` and `250000 Hz`
339 /// Default value is `125000 Hz`
340 pub async fn set_signal_bandwidth(&mut self, sbw: i64) -> Result<(), Error<E, CS::Error, RESET::Error>> {
341 let bw: i64 = match sbw {
342 7_800 => 0,
343 10_400 => 1,
344 15_600 => 2,
345 20_800 => 3,
346 31_250 => 4,
347 41_700 => 5,
348 62_500 => 6,
349 125_000 => 7,
350 250_000 => 8,
351 _ => 9,
352 };
353 let modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?;
354 self.write_register(
355 Register::RegModemConfig1.addr(),
356 (modem_config_1 & 0x0f) | ((bw << 4) as u8),
357 )
358 .await?;
359 self.set_ldo_flag().await?;
360 Ok(())
361 }
362
363 /// Sets the coding rate of the radio with the numerator fixed at 4. Supported values
364 /// are between `5` and `8`, these correspond to coding rates of `4/5` and `4/8`.
365 /// Default value is `5`.
366 pub async fn set_coding_rate_4(&mut self, mut denominator: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> {
367 if denominator < 5 {
368 denominator = 5;
369 } else if denominator > 8 {
370 denominator = 8;
371 }
372 let cr = denominator - 4;
373 let modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?;
374 self.write_register(Register::RegModemConfig1.addr(), (modem_config_1 & 0xf1) | (cr << 1))
375 .await
376 }
377
378 /// Sets the preamble length of the radio. Values are between 6 and 65535.
379 /// Default value is `8`.
380 pub async fn set_preamble_length(&mut self, length: i64) -> Result<(), Error<E, CS::Error, RESET::Error>> {
381 self.write_register(Register::RegPreambleMsb.addr(), (length >> 8) as u8)
382 .await?;
383 self.write_register(Register::RegPreambleLsb.addr(), length as u8).await
384 }
385
386 /// Enables are disables the radio's CRC check. Default value is `false`.
387 pub async fn set_crc(&mut self, value: bool) -> Result<(), Error<E, CS::Error, RESET::Error>> {
388 let modem_config_2 = self.read_register(Register::RegModemConfig2.addr()).await?;
389 if value {
390 self.write_register(Register::RegModemConfig2.addr(), modem_config_2 | 0x04)
391 .await
392 } else {
393 self.write_register(Register::RegModemConfig2.addr(), modem_config_2 & 0xfb)
394 .await
395 }
396 }
397
398 /// Inverts the radio's IQ signals. Default value is `false`.
399 pub async fn set_invert_iq(&mut self, value: bool) -> Result<(), Error<E, CS::Error, RESET::Error>> {
400 if value {
401 self.write_register(Register::RegInvertiq.addr(), 0x66).await?;
402 self.write_register(Register::RegInvertiq2.addr(), 0x19).await
403 } else {
404 self.write_register(Register::RegInvertiq.addr(), 0x27).await?;
405 self.write_register(Register::RegInvertiq2.addr(), 0x1d).await
406 }
407 }
408
409 /// Returns the spreading factor of the radio.
410 pub async fn get_spreading_factor(&mut self) -> Result<u8, Error<E, CS::Error, RESET::Error>> {
411 Ok(self.read_register(Register::RegModemConfig2.addr()).await? >> 4)
412 }
413
414 /// Returns the signal bandwidth of the radio.
415 pub async fn get_signal_bandwidth(&mut self) -> Result<i64, Error<E, CS::Error, RESET::Error>> {
416 let bw = self.read_register(Register::RegModemConfig1.addr()).await? >> 4;
417 let bw = match bw {
418 0 => 7_800,
419 1 => 10_400,
420 2 => 15_600,
421 3 => 20_800,
422 4 => 31_250,
423 5 => 41_700,
424 6 => 62_500,
425 7 => 125_000,
426 8 => 250_000,
427 9 => 500_000,
428 _ => -1,
429 };
430 Ok(bw)
431 }
432
433 /// Returns the RSSI of the last received packet.
434 pub async fn get_packet_rssi(&mut self) -> Result<i32, Error<E, CS::Error, RESET::Error>> {
435 Ok(i32::from(self.read_register(Register::RegPktRssiValue.addr()).await?) - 157)
436 }
437
438 /// Returns the signal to noise radio of the the last received packet.
439 pub async fn get_packet_snr(&mut self) -> Result<f64, Error<E, CS::Error, RESET::Error>> {
440 Ok(f64::from(self.read_register(Register::RegPktSnrValue.addr()).await?))
441 }
442
443 /// Returns the frequency error of the last received packet in Hz.
444 pub async fn get_packet_frequency_error(&mut self) -> Result<i64, Error<E, CS::Error, RESET::Error>> {
445 let mut freq_error: i32;
446 freq_error = i32::from(self.read_register(Register::RegFreqErrorMsb.addr()).await? & 0x7);
447 freq_error <<= 8i64;
448 freq_error += i32::from(self.read_register(Register::RegFreqErrorMid.addr()).await?);
449 freq_error <<= 8i64;
450 freq_error += i32::from(self.read_register(Register::RegFreqErrorLsb.addr()).await?);
451
452 let f_xtal = 32_000_000; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14)
453 let f_error = ((f64::from(freq_error) * (1i64 << 24) as f64) / f64::from(f_xtal))
454 * (self.get_signal_bandwidth().await? as f64 / 500_000.0f64); // p. 37
455 Ok(f_error as i64)
456 }
457
458 async fn set_ldo_flag(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
459 let sw = self.get_signal_bandwidth().await?;
460 // Section 4.1.1.5
461 let symbol_duration = 1000 / (sw / ((1_i64) << self.get_spreading_factor().await?));
462
463 // Section 4.1.1.6
464 let ldo_on = symbol_duration > 16;
465
466 let mut config_3 = self.read_register(Register::RegModemConfig3.addr()).await?;
467 config_3.set_bit(3, ldo_on);
468 //config_3.set_bit(2, true);
469 self.write_register(Register::RegModemConfig3.addr(), config_3).await
470 }
471
472 async fn read_register(&mut self, reg: u8) -> Result<u8, Error<E, CS::Error, RESET::Error>> {
473 let mut buffer = [reg & 0x7f, 0];
474 self.cs.set_low().map_err(CS)?;
475
476 let _ = self.spi.transfer(&mut buffer, &[reg & 0x7f, 0]).await.map_err(SPI)?;
477
478 self.cs.set_high().map_err(CS)?;
479 Ok(buffer[1])
480 }
481
482 async fn write_register(&mut self, reg: u8, byte: u8) -> Result<(), Error<E, CS::Error, RESET::Error>> {
483 self.cs.set_low().map_err(CS)?;
484 let buffer = [reg | 0x80, byte];
485 self.spi.write(&buffer).await.map_err(SPI)?;
486 self.cs.set_high().map_err(CS)?;
487 Ok(())
488 }
489
490 pub async fn put_in_fsk_mode(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
491 // Put in FSK mode
492 let mut op_mode = 0;
493 op_mode
494 .set_bit(7, false) // FSK mode
495 .set_bits(5..6, 0x00) // FSK modulation
496 .set_bit(3, false) //Low freq registers
497 .set_bits(0..2, 0b011); // Mode
498
499 self.write_register(Register::RegOpMode as u8, op_mode).await
500 }
501
502 pub async fn set_fsk_pa_ramp(
503 &mut self,
504 modulation_shaping: FskDataModulationShaping,
505 ramp: FskRampUpRamDown,
506 ) -> Result<(), Error<E, CS::Error, RESET::Error>> {
507 let mut pa_ramp = 0;
508 pa_ramp
509 .set_bits(5..6, modulation_shaping as u8)
510 .set_bits(0..3, ramp as u8);
511
512 self.write_register(Register::RegPaRamp as u8, pa_ramp).await
513 }
514
515 pub async fn set_lora_pa_ramp(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
516 self.write_register(Register::RegPaRamp as u8, 0b1000).await
517 }
518
519 pub async fn set_lora_sync_word(&mut self) -> Result<(), Error<E, CS::Error, RESET::Error>> {
520 self.write_register(Register::RegSyncWord as u8, 0x34).await
521 }
522}
523/// Modes of the radio and their corresponding register values.
524#[derive(Clone, Copy)]
525pub enum RadioMode {
526 LongRangeMode = 0x80,
527 Sleep = 0x00,
528 Stdby = 0x01,
529 Tx = 0x03,
530 RxContinuous = 0x05,
531 RxSingle = 0x06,
532}
533
534impl RadioMode {
535 /// Returns the address of the mode.
536 pub fn addr(self) -> u8 {
537 self as u8
538 }
539}
diff --git a/embassy-lora/src/sx127x/sx127x_lora/register.rs b/embassy-lora/src/sx127x/sx127x_lora/register.rs
deleted file mode 100644
index 2445e21b1..000000000
--- a/embassy-lora/src/sx127x/sx127x_lora/register.rs
+++ /dev/null
@@ -1,107 +0,0 @@
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-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index f0fc152ce..24a26eddd 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -55,9 +55,6 @@ pub mod rtc;
55pub mod sdmmc; 55pub mod sdmmc;
56#[cfg(spi)] 56#[cfg(spi)]
57pub mod spi; 57pub mod spi;
58#[cfg(stm32wl)]
59#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")]
60pub mod subghz;
61#[cfg(usart)] 58#[cfg(usart)]
62pub mod usart; 59pub mod usart;
63#[cfg(all(usb, feature = "time"))] 60#[cfg(all(usb, feature = "time"))]
diff --git a/embassy-stm32/src/subghz/bit_sync.rs b/embassy-stm32/src/subghz/bit_sync.rs
deleted file mode 100644
index f3cba05f9..000000000
--- a/embassy-stm32/src/subghz/bit_sync.rs
+++ /dev/null
@@ -1,160 +0,0 @@
1/// Bit synchronization.
2///
3/// This must be cleared to `0x00` (the reset value) when using packet types
4/// other than LoRa.
5///
6/// Argument of [`set_bit_sync`](crate::subghz::SubGhz::set_bit_sync).
7#[derive(Debug, PartialEq, Eq, Clone, Copy)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9pub struct BitSync {
10 val: u8,
11}
12
13impl BitSync {
14 /// Bit synchronization register reset value.
15 pub const RESET: BitSync = BitSync { val: 0x00 };
16
17 /// Create a new [`BitSync`] structure from a raw value.
18 ///
19 /// Reserved bits will be masked.
20 pub const fn from_raw(raw: u8) -> Self {
21 Self { val: raw & 0x70 }
22 }
23
24 /// Get the raw value of the [`BitSync`] register.
25 pub const fn as_bits(&self) -> u8 {
26 self.val
27 }
28
29 /// LoRa simple bit synchronization enable.
30 ///
31 /// # Example
32 ///
33 /// Enable simple bit synchronization.
34 ///
35 /// ```
36 /// use stm32wlxx_hal::subghz::BitSync;
37 ///
38 /// const BIT_SYNC: BitSync = BitSync::RESET.set_simple_bit_sync_en(true);
39 /// # assert_eq!(u8::from(BIT_SYNC), 0x40u8);
40 /// ```
41 #[must_use = "set_simple_bit_sync_en returns a modified BitSync"]
42 pub const fn set_simple_bit_sync_en(mut self, en: bool) -> BitSync {
43 if en {
44 self.val |= 1 << 6;
45 } else {
46 self.val &= !(1 << 6);
47 }
48 self
49 }
50
51 /// Returns `true` if simple bit synchronization is enabled.
52 ///
53 /// # Example
54 ///
55 /// ```
56 /// use stm32wlxx_hal::subghz::BitSync;
57 ///
58 /// let bs: BitSync = BitSync::RESET;
59 /// assert_eq!(bs.simple_bit_sync_en(), false);
60 /// let bs: BitSync = bs.set_simple_bit_sync_en(true);
61 /// assert_eq!(bs.simple_bit_sync_en(), true);
62 /// let bs: BitSync = bs.set_simple_bit_sync_en(false);
63 /// assert_eq!(bs.simple_bit_sync_en(), false);
64 /// ```
65 pub const fn simple_bit_sync_en(&self) -> bool {
66 self.val & (1 << 6) != 0
67 }
68
69 /// LoRa RX data inversion.
70 ///
71 /// # Example
72 ///
73 /// Invert receive data.
74 ///
75 /// ```
76 /// use stm32wlxx_hal::subghz::BitSync;
77 ///
78 /// const BIT_SYNC: BitSync = BitSync::RESET.set_rx_data_inv(true);
79 /// # assert_eq!(u8::from(BIT_SYNC), 0x20u8);
80 /// ```
81 #[must_use = "set_rx_data_inv returns a modified BitSync"]
82 pub const fn set_rx_data_inv(mut self, inv: bool) -> BitSync {
83 if inv {
84 self.val |= 1 << 5;
85 } else {
86 self.val &= !(1 << 5);
87 }
88 self
89 }
90
91 /// Returns `true` if LoRa RX data is inverted.
92 ///
93 /// # Example
94 ///
95 /// ```
96 /// use stm32wlxx_hal::subghz::BitSync;
97 ///
98 /// let bs: BitSync = BitSync::RESET;
99 /// assert_eq!(bs.rx_data_inv(), false);
100 /// let bs: BitSync = bs.set_rx_data_inv(true);
101 /// assert_eq!(bs.rx_data_inv(), true);
102 /// let bs: BitSync = bs.set_rx_data_inv(false);
103 /// assert_eq!(bs.rx_data_inv(), false);
104 /// ```
105 pub const fn rx_data_inv(&self) -> bool {
106 self.val & (1 << 5) != 0
107 }
108
109 /// LoRa normal bit synchronization enable.
110 ///
111 /// # Example
112 ///
113 /// Enable normal bit synchronization.
114 ///
115 /// ```
116 /// use stm32wlxx_hal::subghz::BitSync;
117 ///
118 /// const BIT_SYNC: BitSync = BitSync::RESET.set_norm_bit_sync_en(true);
119 /// # assert_eq!(u8::from(BIT_SYNC), 0x10u8);
120 /// ```
121 #[must_use = "set_norm_bit_sync_en returns a modified BitSync"]
122 pub const fn set_norm_bit_sync_en(mut self, en: bool) -> BitSync {
123 if en {
124 self.val |= 1 << 4;
125 } else {
126 self.val &= !(1 << 4);
127 }
128 self
129 }
130
131 /// Returns `true` if normal bit synchronization is enabled.
132 ///
133 /// # Example
134 ///
135 /// ```
136 /// use stm32wlxx_hal::subghz::BitSync;
137 ///
138 /// let bs: BitSync = BitSync::RESET;
139 /// assert_eq!(bs.norm_bit_sync_en(), false);
140 /// let bs: BitSync = bs.set_norm_bit_sync_en(true);
141 /// assert_eq!(bs.norm_bit_sync_en(), true);
142 /// let bs: BitSync = bs.set_norm_bit_sync_en(false);
143 /// assert_eq!(bs.norm_bit_sync_en(), false);
144 /// ```
145 pub const fn norm_bit_sync_en(&self) -> bool {
146 self.val & (1 << 4) != 0
147 }
148}
149
150impl From<BitSync> for u8 {
151 fn from(bs: BitSync) -> Self {
152 bs.val
153 }
154}
155
156impl Default for BitSync {
157 fn default() -> Self {
158 Self::RESET
159 }
160}
diff --git a/embassy-stm32/src/subghz/cad_params.rs b/embassy-stm32/src/subghz/cad_params.rs
deleted file mode 100644
index 1d90ff706..000000000
--- a/embassy-stm32/src/subghz/cad_params.rs
+++ /dev/null
@@ -1,230 +0,0 @@
1use super::Timeout;
2
3/// Number of symbols used for channel activity detection scans.
4///
5/// Argument of [`CadParams::set_num_symbol`].
6#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8#[repr(u8)]
9pub enum NbCadSymbol {
10 /// 1 symbol.
11 S1 = 0x0,
12 /// 2 symbols.
13 S2 = 0x1,
14 /// 4 symbols.
15 S4 = 0x2,
16 /// 8 symbols.
17 S8 = 0x3,
18 /// 16 symbols.
19 S16 = 0x4,
20}
21
22/// Mode to enter after a channel activity detection scan is finished.
23///
24/// Argument of [`CadParams::set_exit_mode`].
25#[derive(Debug, PartialEq, Eq)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27#[repr(u8)]
28pub enum ExitMode {
29 /// Standby with RC 13 MHz mode entry after CAD.
30 Standby = 0,
31 /// Standby with RC 13 MHz mode after CAD if no LoRa symbol is detected
32 /// during the CAD scan.
33 /// If a LoRa symbol is detected, the sub-GHz radio stays in RX mode
34 /// until a packet is received or until the CAD timeout is reached.
35 StandbyLoRa = 1,
36}
37
38/// Channel activity detection (CAD) parameters.
39///
40/// Argument of [`set_cad_params`].
41///
42/// # Recommended CAD settings
43///
44/// This is taken directly from the datasheet.
45///
46/// "The correct values selected in the table below must be carefully tested to
47/// ensure a good detection at sensitivity level and to limit the number of
48/// false detections"
49///
50/// | SF (Spreading Factor) | [`set_det_peak`] | [`set_det_min`] |
51/// |-----------------------|------------------|-----------------|
52/// | 5 | 0x18 | 0x10 |
53/// | 6 | 0x19 | 0x10 |
54/// | 7 | 0x20 | 0x10 |
55/// | 8 | 0x21 | 0x10 |
56/// | 9 | 0x22 | 0x10 |
57/// | 10 | 0x23 | 0x10 |
58/// | 11 | 0x24 | 0x10 |
59/// | 12 | 0x25 | 0x10 |
60///
61/// [`set_cad_params`]: crate::subghz::SubGhz::set_cad_params
62/// [`set_det_peak`]: crate::subghz::CadParams::set_det_peak
63/// [`set_det_min`]: crate::subghz::CadParams::set_det_min
64#[derive(Debug, PartialEq, Eq, Clone, Copy)]
65#[cfg_attr(feature = "defmt", derive(defmt::Format))]
66pub struct CadParams {
67 buf: [u8; 8],
68}
69
70impl CadParams {
71 /// Create a new `CadParams`.
72 ///
73 /// This is the same as `default`, but in a `const` function.
74 ///
75 /// # Example
76 ///
77 /// ```
78 /// use stm32wlxx_hal::subghz::CadParams;
79 ///
80 /// const CAD_PARAMS: CadParams = CadParams::new();
81 /// assert_eq!(CAD_PARAMS, CadParams::default());
82 /// ```
83 pub const fn new() -> CadParams {
84 CadParams {
85 buf: [super::OpCode::SetCadParams as u8, 0, 0, 0, 0, 0, 0, 0],
86 }
87 .set_num_symbol(NbCadSymbol::S1)
88 .set_det_peak(0x18)
89 .set_det_min(0x10)
90 .set_exit_mode(ExitMode::Standby)
91 }
92
93 /// Number of symbols used for a CAD scan.
94 ///
95 /// # Example
96 ///
97 /// Set the number of symbols to 4.
98 ///
99 /// ```
100 /// use stm32wlxx_hal::subghz::{CadParams, NbCadSymbol};
101 ///
102 /// const CAD_PARAMS: CadParams = CadParams::new().set_num_symbol(NbCadSymbol::S4);
103 /// # assert_eq!(CAD_PARAMS.as_slice()[1], 0x2);
104 /// ```
105 #[must_use = "set_num_symbol returns a modified CadParams"]
106 pub const fn set_num_symbol(mut self, nb: NbCadSymbol) -> CadParams {
107 self.buf[1] = nb as u8;
108 self
109 }
110
111 /// Used with [`set_det_min`] to correlate the LoRa symbol.
112 ///
113 /// See the table in [`CadParams`] docs for recommended values.
114 ///
115 /// # Example
116 ///
117 /// Setting the recommended value for a spreading factor of 7.
118 ///
119 /// ```
120 /// use stm32wlxx_hal::subghz::CadParams;
121 ///
122 /// const CAD_PARAMS: CadParams = CadParams::new().set_det_peak(0x20).set_det_min(0x10);
123 /// # assert_eq!(CAD_PARAMS.as_slice()[2], 0x20);
124 /// # assert_eq!(CAD_PARAMS.as_slice()[3], 0x10);
125 /// ```
126 ///
127 /// [`set_det_min`]: crate::subghz::CadParams::set_det_min
128 #[must_use = "set_det_peak returns a modified CadParams"]
129 pub const fn set_det_peak(mut self, peak: u8) -> CadParams {
130 self.buf[2] = peak;
131 self
132 }
133
134 /// Used with [`set_det_peak`] to correlate the LoRa symbol.
135 ///
136 /// See the table in [`CadParams`] docs for recommended values.
137 ///
138 /// # Example
139 ///
140 /// Setting the recommended value for a spreading factor of 6.
141 ///
142 /// ```
143 /// use stm32wlxx_hal::subghz::CadParams;
144 ///
145 /// const CAD_PARAMS: CadParams = CadParams::new().set_det_peak(0x18).set_det_min(0x10);
146 /// # assert_eq!(CAD_PARAMS.as_slice()[2], 0x18);
147 /// # assert_eq!(CAD_PARAMS.as_slice()[3], 0x10);
148 /// ```
149 ///
150 /// [`set_det_peak`]: crate::subghz::CadParams::set_det_peak
151 #[must_use = "set_det_min returns a modified CadParams"]
152 pub const fn set_det_min(mut self, min: u8) -> CadParams {
153 self.buf[3] = min;
154 self
155 }
156
157 /// Mode to enter after a channel activity detection scan is finished.
158 ///
159 /// # Example
160 ///
161 /// ```
162 /// use stm32wlxx_hal::subghz::{CadParams, ExitMode};
163 ///
164 /// const CAD_PARAMS: CadParams = CadParams::new().set_exit_mode(ExitMode::Standby);
165 /// # assert_eq!(CAD_PARAMS.as_slice()[4], 0x00);
166 /// # assert_eq!(CAD_PARAMS.set_exit_mode(ExitMode::StandbyLoRa).as_slice()[4], 0x01);
167 /// ```
168 #[must_use = "set_exit_mode returns a modified CadParams"]
169 pub const fn set_exit_mode(mut self, mode: ExitMode) -> CadParams {
170 self.buf[4] = mode as u8;
171 self
172 }
173
174 /// Set the timeout.
175 ///
176 /// This is only used with [`ExitMode::StandbyLoRa`].
177 ///
178 /// # Example
179 ///
180 /// ```
181 /// use stm32wlxx_hal::subghz::{CadParams, ExitMode, Timeout};
182 ///
183 /// const TIMEOUT: Timeout = Timeout::from_raw(0x123456);
184 /// const CAD_PARAMS: CadParams = CadParams::new()
185 /// .set_exit_mode(ExitMode::StandbyLoRa)
186 /// .set_timeout(TIMEOUT);
187 /// # assert_eq!(CAD_PARAMS.as_slice()[4], 0x01);
188 /// # assert_eq!(CAD_PARAMS.as_slice()[5], 0x12);
189 /// # assert_eq!(CAD_PARAMS.as_slice()[6], 0x34);
190 /// # assert_eq!(CAD_PARAMS.as_slice()[7], 0x56);
191 /// ```
192 #[must_use = "set_timeout returns a modified CadParams"]
193 pub const fn set_timeout(mut self, to: Timeout) -> CadParams {
194 let to_bytes: [u8; 3] = to.as_bytes();
195 self.buf[5] = to_bytes[0];
196 self.buf[6] = to_bytes[1];
197 self.buf[7] = to_bytes[2];
198 self
199 }
200
201 /// Extracts a slice containing the packet.
202 ///
203 /// # Example
204 ///
205 /// ```
206 /// use stm32wlxx_hal::subghz::{CadParams, ExitMode, NbCadSymbol, Timeout};
207 ///
208 /// const TIMEOUT: Timeout = Timeout::from_raw(0x123456);
209 /// const CAD_PARAMS: CadParams = CadParams::new()
210 /// .set_num_symbol(NbCadSymbol::S4)
211 /// .set_det_peak(0x18)
212 /// .set_det_min(0x10)
213 /// .set_exit_mode(ExitMode::StandbyLoRa)
214 /// .set_timeout(TIMEOUT);
215 ///
216 /// assert_eq!(
217 /// CAD_PARAMS.as_slice(),
218 /// &[0x88, 0x02, 0x18, 0x10, 0x01, 0x12, 0x34, 0x56]
219 /// );
220 /// ```
221 pub const fn as_slice(&self) -> &[u8] {
222 &self.buf
223 }
224}
225
226impl Default for CadParams {
227 fn default() -> Self {
228 Self::new()
229 }
230}
diff --git a/embassy-stm32/src/subghz/calibrate.rs b/embassy-stm32/src/subghz/calibrate.rs
deleted file mode 100644
index f94538f86..000000000
--- a/embassy-stm32/src/subghz/calibrate.rs
+++ /dev/null
@@ -1,122 +0,0 @@
1/// Image calibration.
2///
3/// Argument of [`calibrate_image`].
4///
5/// [`calibrate_image`]: crate::subghz::SubGhz::calibrate_image
6#[derive(Debug, PartialEq, Eq, Clone, Copy)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub struct CalibrateImage(pub(crate) u8, pub(crate) u8);
9
10impl CalibrateImage {
11 /// Image calibration for the 430 - 440 MHz ISM band.
12 pub const ISM_430_440: CalibrateImage = CalibrateImage(0x6B, 0x6F);
13
14 /// Image calibration for the 470 - 510 MHz ISM band.
15 pub const ISM_470_510: CalibrateImage = CalibrateImage(0x75, 0x81);
16
17 /// Image calibration for the 779 - 787 MHz ISM band.
18 pub const ISM_779_787: CalibrateImage = CalibrateImage(0xC1, 0xC5);
19
20 /// Image calibration for the 863 - 870 MHz ISM band.
21 pub const ISM_863_870: CalibrateImage = CalibrateImage(0xD7, 0xDB);
22
23 /// Image calibration for the 902 - 928 MHz ISM band.
24 pub const ISM_902_928: CalibrateImage = CalibrateImage(0xE1, 0xE9);
25
26 /// Create a new `CalibrateImage` structure from raw values.
27 ///
28 /// # Example
29 ///
30 /// ```
31 /// use stm32wlxx_hal::subghz::CalibrateImage;
32 ///
33 /// const CAL: CalibrateImage = CalibrateImage::new(0xE1, 0xE9);
34 /// assert_eq!(CAL, CalibrateImage::ISM_902_928);
35 /// ```
36 pub const fn new(f1: u8, f2: u8) -> CalibrateImage {
37 CalibrateImage(f1, f2)
38 }
39
40 /// Create a new `CalibrateImage` structure from two frequencies.
41 ///
42 /// # Arguments
43 ///
44 /// The units for `freq1` and `freq2` are in MHz.
45 ///
46 /// # Panics
47 ///
48 /// * Panics if `freq1` is less than `freq2`.
49 /// * Panics if `freq1` or `freq2` is not a multiple of 4MHz.
50 /// * Panics if `freq1` or `freq2` is greater than `1020`.
51 ///
52 /// # Example
53 ///
54 /// Create an image calibration for the 430 - 440 MHz ISM band.
55 ///
56 /// ```
57 /// use stm32wlxx_hal::subghz::CalibrateImage;
58 ///
59 /// let cal: CalibrateImage = CalibrateImage::from_freq(428, 444);
60 /// assert_eq!(cal, CalibrateImage::ISM_430_440);
61 /// ```
62 pub fn from_freq(freq1: u16, freq2: u16) -> CalibrateImage {
63 assert!(freq2 >= freq1);
64 assert_eq!(freq1 % 4, 0);
65 assert_eq!(freq2 % 4, 0);
66 assert!(freq1 <= 1020);
67 assert!(freq2 <= 1020);
68 CalibrateImage((freq1 / 4) as u8, (freq2 / 4) as u8)
69 }
70}
71
72impl Default for CalibrateImage {
73 fn default() -> Self {
74 CalibrateImage::new(0xE1, 0xE9)
75 }
76}
77
78/// Block calibration.
79///
80/// Argument of [`calibrate`].
81///
82/// [`calibrate`]: crate::subghz::SubGhz::calibrate
83#[derive(PartialEq, Eq, Debug, Clone, Copy)]
84#[cfg_attr(feature = "defmt", derive(defmt::Format))]
85#[repr(u8)]
86pub enum Calibrate {
87 /// Image calibration
88 Image = 1 << 6,
89 /// RF-ADC bulk P calibration
90 AdcBulkP = 1 << 5,
91 /// RF-ADC bulk N calibration
92 AdcBulkN = 1 << 4,
93 /// RF-ADC pulse calibration
94 AdcPulse = 1 << 3,
95 /// RF-PLL calibration
96 Pll = 1 << 2,
97 /// Sub-GHz radio RC 13 MHz calibration
98 Rc13M = 1 << 1,
99 /// Sub-GHz radio RC 64 kHz calibration
100 Rc64K = 1,
101}
102
103impl Calibrate {
104 /// Get the bitmask for the block calibration.
105 ///
106 /// # Example
107 ///
108 /// ```
109 /// use stm32wlxx_hal::subghz::Calibrate;
110 ///
111 /// assert_eq!(Calibrate::Image.mask(), 0b0100_0000);
112 /// assert_eq!(Calibrate::AdcBulkP.mask(), 0b0010_0000);
113 /// assert_eq!(Calibrate::AdcBulkN.mask(), 0b0001_0000);
114 /// assert_eq!(Calibrate::AdcPulse.mask(), 0b0000_1000);
115 /// assert_eq!(Calibrate::Pll.mask(), 0b0000_0100);
116 /// assert_eq!(Calibrate::Rc13M.mask(), 0b0000_0010);
117 /// assert_eq!(Calibrate::Rc64K.mask(), 0b0000_0001);
118 /// ```
119 pub const fn mask(self) -> u8 {
120 self as u8
121 }
122}
diff --git a/embassy-stm32/src/subghz/fallback_mode.rs b/embassy-stm32/src/subghz/fallback_mode.rs
deleted file mode 100644
index 50ec592f5..000000000
--- a/embassy-stm32/src/subghz/fallback_mode.rs
+++ /dev/null
@@ -1,37 +0,0 @@
1/// Fallback mode after successful packet transmission or packet reception.
2///
3/// Argument of [`set_tx_rx_fallback_mode`].
4///
5/// [`set_tx_rx_fallback_mode`]: crate::subghz::SubGhz::set_tx_rx_fallback_mode.
6#[derive(Debug, PartialEq, Eq, Clone, Copy)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8#[repr(u8)]
9pub enum FallbackMode {
10 /// Standby mode entry.
11 Standby = 0x20,
12 /// Standby with HSE32 enabled.
13 StandbyHse = 0x30,
14 /// Frequency synthesizer entry.
15 Fs = 0x40,
16}
17
18impl From<FallbackMode> for u8 {
19 fn from(fm: FallbackMode) -> Self {
20 fm as u8
21 }
22}
23
24impl Default for FallbackMode {
25 /// Default fallback mode after power-on reset.
26 ///
27 /// # Example
28 ///
29 /// ```
30 /// use stm32wlxx_hal::subghz::FallbackMode;
31 ///
32 /// assert_eq!(FallbackMode::default(), FallbackMode::Standby);
33 /// ```
34 fn default() -> Self {
35 FallbackMode::Standby
36 }
37}
diff --git a/embassy-stm32/src/subghz/hse_trim.rs b/embassy-stm32/src/subghz/hse_trim.rs
deleted file mode 100644
index edfd52aca..000000000
--- a/embassy-stm32/src/subghz/hse_trim.rs
+++ /dev/null
@@ -1,107 +0,0 @@
1use super::ValueError;
2
3/// HSE32 load capacitor trimming.
4///
5/// Argument of [`set_hse_in_trim`] and [`set_hse_out_trim`].
6///
7/// [`set_hse_in_trim`]: crate::subghz::SubGhz::set_hse_in_trim
8/// [`set_hse_out_trim`]: crate::subghz::SubGhz::set_hse_out_trim
9#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
10#[cfg_attr(feature = "defmt", derive(defmt::Format))]
11pub struct HseTrim {
12 val: u8,
13}
14
15impl HseTrim {
16 /// Maximum capacitor value, ~33.4 pF
17 pub const MAX: HseTrim = HseTrim::from_raw(0x2F);
18
19 /// Minimum capacitor value, ~11.3 pF
20 pub const MIN: HseTrim = HseTrim::from_raw(0x00);
21
22 /// Power-on-reset capacitor value, ~20.3 pF
23 ///
24 /// This is the same as `default`.
25 ///
26 /// # Example
27 ///
28 /// ```
29 /// use stm32wlxx_hal::subghz::HseTrim;
30 ///
31 /// assert_eq!(HseTrim::POR, HseTrim::default());
32 /// ```
33 pub const POR: HseTrim = HseTrim::from_raw(0x12);
34
35 /// Create a new [`HseTrim`] structure from a raw value.
36 ///
37 /// Values greater than the maximum of `0x2F` will be set to the maximum.
38 ///
39 /// # Example
40 ///
41 /// ```
42 /// use stm32wlxx_hal::subghz::HseTrim;
43 ///
44 /// assert_eq!(HseTrim::from_raw(0xFF), HseTrim::MAX);
45 /// assert_eq!(HseTrim::from_raw(0x2F), HseTrim::MAX);
46 /// assert_eq!(HseTrim::from_raw(0x00), HseTrim::MIN);
47 /// ```
48 pub const fn from_raw(raw: u8) -> HseTrim {
49 if raw > 0x2F {
50 HseTrim { val: 0x2F }
51 } else {
52 HseTrim { val: raw }
53 }
54 }
55
56 /// Create a HSE trim value from farads.
57 ///
58 /// Values greater than the maximum of 33.4 pF will be set to the maximum.
59 /// Values less than the minimum of 11.3 pF will be set to the minimum.
60 ///
61 /// # Example
62 ///
63 /// ```
64 /// use stm32wlxx_hal::subghz::HseTrim;
65 ///
66 /// assert!(HseTrim::from_farads(1.0).is_err());
67 /// assert!(HseTrim::from_farads(1e-12).is_err());
68 /// assert_eq!(HseTrim::from_farads(20.2e-12), Ok(HseTrim::default()));
69 /// ```
70 pub fn from_farads(farads: f32) -> Result<HseTrim, ValueError<f32>> {
71 const MAX: f32 = 33.4E-12;
72 const MIN: f32 = 11.3E-12;
73 if farads > MAX {
74 Err(ValueError::too_high(farads, MAX))
75 } else if farads < MIN {
76 Err(ValueError::too_low(farads, MIN))
77 } else {
78 Ok(HseTrim::from_raw(((farads - 11.3e-12) / 0.47e-12) as u8))
79 }
80 }
81
82 /// Get the capacitance as farads.
83 ///
84 /// # Example
85 ///
86 /// ```
87 /// use stm32wlxx_hal::subghz::HseTrim;
88 ///
89 /// assert_eq!((HseTrim::MAX.as_farads() * 10e11) as u8, 33);
90 /// assert_eq!((HseTrim::MIN.as_farads() * 10e11) as u8, 11);
91 /// ```
92 pub fn as_farads(&self) -> f32 {
93 (self.val as f32) * 0.47E-12 + 11.3E-12
94 }
95}
96
97impl From<HseTrim> for u8 {
98 fn from(ht: HseTrim) -> Self {
99 ht.val
100 }
101}
102
103impl Default for HseTrim {
104 fn default() -> Self {
105 Self::POR
106 }
107}
diff --git a/embassy-stm32/src/subghz/irq.rs b/embassy-stm32/src/subghz/irq.rs
deleted file mode 100644
index b56b8ad94..000000000
--- a/embassy-stm32/src/subghz/irq.rs
+++ /dev/null
@@ -1,292 +0,0 @@
1/// Interrupt lines.
2///
3/// Argument of [`CfgIrq::irq_enable`] and [`CfgIrq::irq_disable`].
4#[derive(Debug, PartialEq, Eq, Clone, Copy)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6pub enum IrqLine {
7 /// Global interrupt.
8 Global,
9 /// Interrupt line 1.
10 ///
11 /// This will output to the [`RfIrq0`](crate::gpio::RfIrq0) pin.
12 Line1,
13 /// Interrupt line 2.
14 ///
15 /// This will output to the [`RfIrq1`](crate::gpio::RfIrq1) pin.
16 Line2,
17 /// Interrupt line 3.
18 ///
19 /// This will output to the [`RfIrq2`](crate::gpio::RfIrq2) pin.
20 Line3,
21}
22
23impl IrqLine {
24 pub(super) const fn offset(&self) -> usize {
25 match self {
26 IrqLine::Global => 1,
27 IrqLine::Line1 => 3,
28 IrqLine::Line2 => 5,
29 IrqLine::Line3 => 7,
30 }
31 }
32}
33
34/// IRQ bit mapping
35///
36/// See table 37 "IRQ bit mapping and definition" in the reference manual for
37/// more information.
38#[repr(u16)]
39#[derive(Debug, PartialEq, Eq, Clone, Copy)]
40#[cfg_attr(feature = "defmt", derive(defmt::Format))]
41pub enum Irq {
42 /// Packet transmission finished.
43 ///
44 /// * Packet type: LoRa and GFSK
45 /// * Operation: TX
46 TxDone = (1 << 0),
47 /// Packet reception finished.
48 ///
49 /// * Packet type: LoRa and GFSK
50 /// * Operation: RX
51 RxDone = (1 << 1),
52 /// Preamble detected.
53 ///
54 /// * Packet type: LoRa and GFSK
55 /// * Operation: RX
56 PreambleDetected = (1 << 2),
57 /// Synchronization word valid.
58 ///
59 /// * Packet type: GFSK
60 /// * Operation: RX
61 SyncDetected = (1 << 3),
62 /// Header valid.
63 ///
64 /// * Packet type: LoRa
65 /// * Operation: RX
66 HeaderValid = (1 << 4),
67 /// Header CRC error.
68 ///
69 /// * Packet type: LoRa
70 /// * Operation: RX
71 HeaderErr = (1 << 5),
72 /// Dual meaning error.
73 ///
74 /// For GFSK RX this indicates a preamble, syncword, address, CRC, or length
75 /// error.
76 ///
77 /// For LoRa RX this indicates a CRC error.
78 Err = (1 << 6),
79 /// Channel activity detection finished.
80 ///
81 /// * Packet type: LoRa
82 /// * Operation: CAD
83 CadDone = (1 << 7),
84 /// Channel activity detected.
85 ///
86 /// * Packet type: LoRa
87 /// * Operation: CAD
88 CadDetected = (1 << 8),
89 /// RX or TX timeout.
90 ///
91 /// * Packet type: LoRa and GFSK
92 /// * Operation: RX and TX
93 Timeout = (1 << 9),
94}
95
96impl Irq {
97 /// Get the bitmask for an IRQ.
98 ///
99 /// # Example
100 ///
101 /// ```
102 /// use stm32wlxx_hal::subghz::Irq;
103 ///
104 /// assert_eq!(Irq::TxDone.mask(), 0x0001);
105 /// assert_eq!(Irq::Timeout.mask(), 0x0200);
106 /// ```
107 pub const fn mask(self) -> u16 {
108 self as u16
109 }
110}
111
112/// Argument for [`set_irq_cfg`].
113///
114/// [`set_irq_cfg`]: crate::subghz::SubGhz::set_irq_cfg
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116#[cfg_attr(feature = "defmt", derive(defmt::Format))]
117pub struct CfgIrq {
118 buf: [u8; 9],
119}
120
121impl CfgIrq {
122 /// Create a new `CfgIrq`.
123 ///
124 /// This is the same as `default`, but in a `const` function.
125 ///
126 /// The default value has all interrupts disabled on all lines.
127 ///
128 /// # Example
129 ///
130 /// ```
131 /// use stm32wlxx_hal::subghz::CfgIrq;
132 ///
133 /// const IRQ_CFG: CfgIrq = CfgIrq::new();
134 /// ```
135 pub const fn new() -> CfgIrq {
136 CfgIrq {
137 buf: [
138 super::OpCode::CfgDioIrq as u8,
139 0x00,
140 0x00,
141 0x00,
142 0x00,
143 0x00,
144 0x00,
145 0x00,
146 0x00,
147 ],
148 }
149 }
150
151 /// Enable an interrupt.
152 ///
153 /// # Example
154 ///
155 /// ```
156 /// use stm32wlxx_hal::subghz::{CfgIrq, Irq, IrqLine};
157 ///
158 /// const IRQ_CFG: CfgIrq = CfgIrq::new()
159 /// .irq_enable(IrqLine::Global, Irq::TxDone)
160 /// .irq_enable(IrqLine::Global, Irq::Timeout);
161 /// # assert_eq!(IRQ_CFG.as_slice()[1], 0x02);
162 /// # assert_eq!(IRQ_CFG.as_slice()[2], 0x01);
163 /// # assert_eq!(IRQ_CFG.as_slice()[3], 0x00);
164 /// ```
165 #[must_use = "irq_enable returns a modified CfgIrq"]
166 pub const fn irq_enable(mut self, line: IrqLine, irq: Irq) -> CfgIrq {
167 let mask: u16 = irq as u16;
168 let offset: usize = line.offset();
169 self.buf[offset] |= ((mask >> 8) & 0xFF) as u8;
170 self.buf[offset + 1] |= (mask & 0xFF) as u8;
171 self
172 }
173
174 /// Enable an interrupt on all lines.
175 ///
176 /// As far as I can tell with empirical testing all IRQ lines need to be
177 /// enabled for the internal interrupt to be pending in the NVIC.
178 ///
179 /// # Example
180 ///
181 /// ```
182 /// use stm32wlxx_hal::subghz::{CfgIrq, Irq};
183 ///
184 /// const IRQ_CFG: CfgIrq = CfgIrq::new()
185 /// .irq_enable_all(Irq::TxDone)
186 /// .irq_enable_all(Irq::Timeout);
187 /// # assert_eq!(IRQ_CFG.as_slice()[1], 0x02);
188 /// # assert_eq!(IRQ_CFG.as_slice()[2], 0x01);
189 /// # assert_eq!(IRQ_CFG.as_slice()[3], 0x02);
190 /// # assert_eq!(IRQ_CFG.as_slice()[4], 0x01);
191 /// # assert_eq!(IRQ_CFG.as_slice()[5], 0x02);
192 /// # assert_eq!(IRQ_CFG.as_slice()[6], 0x01);
193 /// # assert_eq!(IRQ_CFG.as_slice()[7], 0x02);
194 /// # assert_eq!(IRQ_CFG.as_slice()[8], 0x01);
195 /// ```
196 #[must_use = "irq_enable_all returns a modified CfgIrq"]
197 pub const fn irq_enable_all(mut self, irq: Irq) -> CfgIrq {
198 let mask: [u8; 2] = irq.mask().to_be_bytes();
199
200 self.buf[1] |= mask[0];
201 self.buf[2] |= mask[1];
202 self.buf[3] |= mask[0];
203 self.buf[4] |= mask[1];
204 self.buf[5] |= mask[0];
205 self.buf[6] |= mask[1];
206 self.buf[7] |= mask[0];
207 self.buf[8] |= mask[1];
208
209 self
210 }
211
212 /// Disable an interrupt.
213 ///
214 /// # Example
215 ///
216 /// ```
217 /// use stm32wlxx_hal::subghz::{CfgIrq, Irq, IrqLine};
218 ///
219 /// const IRQ_CFG: CfgIrq = CfgIrq::new()
220 /// .irq_enable(IrqLine::Global, Irq::TxDone)
221 /// .irq_enable(IrqLine::Global, Irq::Timeout)
222 /// .irq_disable(IrqLine::Global, Irq::TxDone)
223 /// .irq_disable(IrqLine::Global, Irq::Timeout);
224 /// # assert_eq!(IRQ_CFG.as_slice()[1], 0x00);
225 /// # assert_eq!(IRQ_CFG.as_slice()[2], 0x00);
226 /// # assert_eq!(IRQ_CFG.as_slice()[3], 0x00);
227 /// ```
228 #[must_use = "irq_disable returns a modified CfgIrq"]
229 pub const fn irq_disable(mut self, line: IrqLine, irq: Irq) -> CfgIrq {
230 let mask: u16 = !(irq as u16);
231 let offset: usize = line.offset();
232 self.buf[offset] &= ((mask >> 8) & 0xFF) as u8;
233 self.buf[offset + 1] &= (mask & 0xFF) as u8;
234 self
235 }
236
237 /// Disable an interrupt on all lines.
238 ///
239 /// # Example
240 ///
241 /// ```
242 /// use stm32wlxx_hal::subghz::{CfgIrq, Irq};
243 ///
244 /// const IRQ_CFG: CfgIrq = CfgIrq::new()
245 /// .irq_enable_all(Irq::TxDone)
246 /// .irq_enable_all(Irq::Timeout)
247 /// .irq_disable_all(Irq::TxDone)
248 /// .irq_disable_all(Irq::Timeout);
249 /// # assert_eq!(IRQ_CFG, CfgIrq::new());
250 /// ```
251 #[must_use = "irq_disable_all returns a modified CfgIrq"]
252 pub const fn irq_disable_all(mut self, irq: Irq) -> CfgIrq {
253 let mask: [u8; 2] = (!irq.mask()).to_be_bytes();
254
255 self.buf[1] &= mask[0];
256 self.buf[2] &= mask[1];
257 self.buf[3] &= mask[0];
258 self.buf[4] &= mask[1];
259 self.buf[5] &= mask[0];
260 self.buf[6] &= mask[1];
261 self.buf[7] &= mask[0];
262 self.buf[8] &= mask[1];
263
264 self
265 }
266
267 /// Extracts a slice containing the packet.
268 ///
269 /// # Example
270 ///
271 /// ```
272 /// use stm32wlxx_hal::subghz::{CfgIrq, Irq};
273 ///
274 /// const IRQ_CFG: CfgIrq = CfgIrq::new()
275 /// .irq_enable_all(Irq::TxDone)
276 /// .irq_enable_all(Irq::Timeout);
277 ///
278 /// assert_eq!(
279 /// IRQ_CFG.as_slice(),
280 /// &[0x08, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01]
281 /// );
282 /// ```
283 pub const fn as_slice(&self) -> &[u8] {
284 &self.buf
285 }
286}
287
288impl Default for CfgIrq {
289 fn default() -> Self {
290 Self::new()
291 }
292}
diff --git a/embassy-stm32/src/subghz/lora_sync_word.rs b/embassy-stm32/src/subghz/lora_sync_word.rs
deleted file mode 100644
index 2c163104e..000000000
--- a/embassy-stm32/src/subghz/lora_sync_word.rs
+++ /dev/null
@@ -1,20 +0,0 @@
1/// LoRa synchronization word.
2///
3/// Argument of [`set_lora_sync_word`][crate::subghz::SubGhz::set_lora_sync_word].
4#[derive(Debug, PartialEq, Eq, Clone, Copy)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6pub enum LoRaSyncWord {
7 /// LoRa private network.
8 Private,
9 /// LoRa public network.
10 Public,
11}
12
13impl LoRaSyncWord {
14 pub(crate) const fn bytes(self) -> [u8; 2] {
15 match self {
16 LoRaSyncWord::Private => [0x14, 0x24],
17 LoRaSyncWord::Public => [0x34, 0x44],
18 }
19 }
20}
diff --git a/embassy-stm32/src/subghz/mod.rs b/embassy-stm32/src/subghz/mod.rs
deleted file mode 100644
index cd566ba24..000000000
--- a/embassy-stm32/src/subghz/mod.rs
+++ /dev/null
@@ -1,1004 +0,0 @@
1//! Sub-GHz radio operating in the 150 - 960 MHz ISM band
2//!
3//! The main radio type is [`SubGhz`].
4//!
5//! ## LoRa user notice
6//!
7//! The Sub-GHz radio may have an undocumented erratum, see this ST community
8//! post for more information: [link]
9//!
10//! [link]: https://community.st.com/s/question/0D53W00000hR8kpSAC/stm32wl55-erratum-clairification
11//!
12//! NOTE: This HAL is based on https://github.com/newAM/stm32wl-hal, but adopted for use with the stm32-metapac
13//! and SPI HALs.
14
15mod bit_sync;
16mod cad_params;
17mod calibrate;
18mod fallback_mode;
19mod hse_trim;
20mod irq;
21mod lora_sync_word;
22mod mod_params;
23mod ocp;
24mod op_error;
25mod pa_config;
26mod packet_params;
27mod packet_status;
28mod packet_type;
29mod pkt_ctrl;
30mod pmode;
31mod pwr_ctrl;
32mod reg_mode;
33mod rf_frequency;
34mod rx_timeout_stop;
35mod sleep_cfg;
36mod smps;
37mod standby_clk;
38mod stats;
39mod status;
40mod tcxo_mode;
41mod timeout;
42mod tx_params;
43mod value_error;
44
45pub use bit_sync::BitSync;
46pub use cad_params::{CadParams, ExitMode, NbCadSymbol};
47pub use calibrate::{Calibrate, CalibrateImage};
48use embassy_hal_common::ratio::Ratio;
49pub use fallback_mode::FallbackMode;
50pub use hse_trim::HseTrim;
51pub use irq::{CfgIrq, Irq, IrqLine};
52pub use lora_sync_word::LoRaSyncWord;
53pub use mod_params::{
54 BpskModParams, CodingRate, FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape, LoRaBandwidth,
55 LoRaModParams, SpreadingFactor,
56};
57pub use ocp::Ocp;
58pub use op_error::OpError;
59pub use pa_config::{PaConfig, PaSel};
60pub use packet_params::{
61 AddrComp, BpskPacketParams, CrcType, GenericPacketParams, HeaderType, LoRaPacketParams, PreambleDetection,
62};
63pub use packet_status::{FskPacketStatus, LoRaPacketStatus};
64pub use packet_type::PacketType;
65pub use pkt_ctrl::{InfSeqSel, PktCtrl};
66pub use pmode::PMode;
67pub use pwr_ctrl::{CurrentLim, PwrCtrl};
68pub use reg_mode::RegMode;
69pub use rf_frequency::RfFreq;
70pub use rx_timeout_stop::RxTimeoutStop;
71pub use sleep_cfg::{SleepCfg, Startup};
72pub use smps::SmpsDrv;
73pub use standby_clk::StandbyClk;
74pub use stats::{FskStats, LoRaStats, Stats};
75pub use status::{CmdStatus, Status, StatusMode};
76pub use tcxo_mode::{TcxoMode, TcxoTrim};
77pub use timeout::Timeout;
78pub use tx_params::{RampTime, TxParams};
79pub use value_error::ValueError;
80
81use crate::dma::NoDma;
82use crate::peripherals::SUBGHZSPI;
83use crate::rcc::sealed::RccPeripheral;
84use crate::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0};
85use crate::time::Hertz;
86use crate::{pac, Peripheral};
87
88/// Passthrough for SPI errors (for now)
89pub type Error = crate::spi::Error;
90
91struct Nss {
92 _priv: (),
93}
94
95impl Nss {
96 #[inline(always)]
97 pub fn new() -> Nss {
98 Self::clear();
99 Nss { _priv: () }
100 }
101
102 /// Clear NSS, enabling SPI transactions
103 #[inline(always)]
104 fn clear() {
105 let pwr = pac::PWR;
106 unsafe {
107 pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW));
108 }
109 }
110
111 /// Set NSS, disabling SPI transactions
112 #[inline(always)]
113 fn set() {
114 let pwr = pac::PWR;
115 unsafe {
116 pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH));
117 }
118 }
119}
120
121impl Drop for Nss {
122 fn drop(&mut self) {
123 Self::set()
124 }
125}
126
127/// Wakeup the radio from sleep mode.
128///
129/// # Safety
130///
131/// 1. This must not be called when the SubGHz radio is in use.
132/// 2. This must not be called when the SubGHz SPI bus is in use.
133///
134/// # Example
135///
136/// See [`SubGhz::set_sleep`]
137#[inline]
138unsafe fn wakeup() {
139 Nss::clear();
140 // RM0453 rev 2 page 171 section 5.7.2 "Sleep mode"
141 // on a firmware request via the sub-GHz radio SPI NSS signal
142 // (keeping sub-GHz radio SPI NSS low for at least 20 μs)
143 //
144 // I have found this to be a more reliable mechanism for ensuring NSS is
145 // pulled low for long enough to wake the radio.
146 while rfbusys() {}
147 Nss::set();
148}
149
150/// Returns `true` if the radio is busy.
151///
152/// This may not be set immediately after NSS going low.
153///
154/// See RM0461 Rev 4 section 5.3 page 181 "Radio busy management" for more
155/// details.
156#[inline]
157fn rfbusys() -> bool {
158 // safety: atmoic read with no side-effects
159 //unsafe { (*pac::PWR::ptr()).sr2.read().rfbusys().is_busy() }
160 let pwr = pac::PWR;
161 unsafe { pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY }
162}
163
164/*
165/// Returns `true` if the radio is busy or NSS is low.
166///
167/// See RM0461 Rev 4 section 5.3 page 181 "Radio busy management" for more
168/// details.
169#[inline]
170fn rfbusyms() -> bool {
171 let pwr = pac::PWR;
172 unsafe { pwr.sr2().read().rfbusyms() == pac::pwr::vals::Rfbusyms::BUSY }
173}
174*/
175
176/// Sub-GHz radio peripheral
177pub struct SubGhz<'d, Tx, Rx> {
178 spi: Spi<'d, SUBGHZSPI, Tx, Rx>,
179}
180
181impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> {
182 fn pulse_radio_reset() {
183 let rcc = pac::RCC;
184 unsafe {
185 rcc.csr().modify(|w| w.set_rfrst(true));
186 rcc.csr().modify(|w| w.set_rfrst(false));
187 }
188 }
189
190 // TODO: This should be replaced with async handling based on IRQ
191 fn poll_not_busy(&self) {
192 let mut count: u32 = 1_000_000;
193 while rfbusys() {
194 count -= 1;
195 if count == 0 {
196 let pwr = pac::PWR;
197 unsafe {
198 panic!(
199 "rfbusys timeout pwr.sr2=0x{:X} pwr.subghzspicr=0x{:X} pwr.cr1=0x{:X}",
200 pwr.sr2().read().0,
201 pwr.subghzspicr().read().0,
202 pwr.cr1().read().0
203 );
204 }
205 }
206 }
207 }
208
209 /// Create a new sub-GHz radio driver from a peripheral.
210 ///
211 /// This will reset the radio and the SPI bus, and enable the peripheral
212 /// clock.
213 pub fn new(
214 peri: impl Peripheral<P = SUBGHZSPI> + 'd,
215 txdma: impl Peripheral<P = Tx> + 'd,
216 rxdma: impl Peripheral<P = Rx> + 'd,
217 ) -> Self {
218 Self::pulse_radio_reset();
219
220 // see RM0453 rev 1 section 7.2.13 page 291
221 // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two.
222 // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz.
223 let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000));
224 let mut config = SpiConfig::default();
225 config.mode = MODE_0;
226 config.bit_order = BitOrder::MsbFirst;
227 let spi = Spi::new_subghz(peri, txdma, rxdma, clk, config);
228
229 unsafe { wakeup() };
230
231 SubGhz { spi }
232 }
233
234 pub fn is_busy(&mut self) -> bool {
235 rfbusys()
236 }
237
238 pub fn reset(&mut self) {
239 Self::pulse_radio_reset();
240 }
241}
242
243impl<'d> SubGhz<'d, NoDma, NoDma> {
244 fn read(&mut self, opcode: OpCode, data: &mut [u8]) -> Result<(), Error> {
245 self.poll_not_busy();
246 {
247 let _nss: Nss = Nss::new();
248 self.spi.blocking_write(&[opcode as u8])?;
249 self.spi.blocking_transfer_in_place(data)?;
250 }
251 self.poll_not_busy();
252 Ok(())
253 }
254
255 /// Read one byte from the sub-Ghz radio.
256 fn read_1(&mut self, opcode: OpCode) -> Result<u8, Error> {
257 let mut buf: [u8; 1] = [0; 1];
258 self.read(opcode, &mut buf)?;
259 Ok(buf[0])
260 }
261
262 /// Read a fixed number of bytes from the sub-Ghz radio.
263 fn read_n<const N: usize>(&mut self, opcode: OpCode) -> Result<[u8; N], Error> {
264 let mut buf: [u8; N] = [0; N];
265 self.read(opcode, &mut buf)?;
266 Ok(buf)
267 }
268
269 fn write(&mut self, data: &[u8]) -> Result<(), Error> {
270 self.poll_not_busy();
271 {
272 let _nss: Nss = Nss::new();
273 self.spi.blocking_write(data)?;
274 }
275 self.poll_not_busy();
276 Ok(())
277 }
278
279 pub fn write_buffer(&mut self, offset: u8, data: &[u8]) -> Result<(), Error> {
280 self.poll_not_busy();
281 {
282 let _nss: Nss = Nss::new();
283 self.spi.blocking_write(&[OpCode::WriteBuffer as u8, offset])?;
284 self.spi.blocking_write(data)?;
285 }
286 self.poll_not_busy();
287
288 Ok(())
289 }
290
291 /// Read the radio buffer at the given offset.
292 ///
293 /// The offset and length of a received packet is provided by
294 /// [`rx_buffer_status`](Self::rx_buffer_status).
295 pub fn read_buffer(&mut self, offset: u8, buf: &mut [u8]) -> Result<Status, Error> {
296 let mut status_buf: [u8; 1] = [0];
297
298 self.poll_not_busy();
299 {
300 let _nss: Nss = Nss::new();
301 self.spi.blocking_write(&[OpCode::ReadBuffer as u8, offset])?;
302 self.spi.blocking_transfer_in_place(&mut status_buf)?;
303 self.spi.blocking_transfer_in_place(buf)?;
304 }
305 self.poll_not_busy();
306
307 Ok(status_buf[0].into())
308 }
309}
310
311// helper to pack register writes into a single buffer to avoid multiple DMA
312// transfers
313macro_rules! wr_reg {
314 [$reg:ident, $($data:expr),+] => {
315 &[
316 OpCode::WriteRegister as u8,
317 Register::$reg.address().to_be_bytes()[0],
318 Register::$reg.address().to_be_bytes()[1],
319 $($data),+
320 ]
321 };
322}
323
324// 5.8.2
325/// Register access
326impl<'d> SubGhz<'d, NoDma, NoDma> {
327 // register write with variable length data
328 fn write_register(&mut self, register: Register, data: &[u8]) -> Result<(), Error> {
329 let addr: [u8; 2] = register.address().to_be_bytes();
330
331 self.poll_not_busy();
332 {
333 let _nss: Nss = Nss::new();
334 self.spi
335 .blocking_write(&[OpCode::WriteRegister as u8, addr[0], addr[1]])?;
336 self.spi.blocking_write(data)?;
337 }
338 self.poll_not_busy();
339
340 Ok(())
341 }
342
343 /// Set the LoRa bit synchronization.
344 pub fn set_bit_sync(&mut self, bs: BitSync) -> Result<(), Error> {
345 self.write(wr_reg![GBSYNC, bs.as_bits()])
346 }
347
348 /// Set the generic packet control register.
349 pub fn set_pkt_ctrl(&mut self, pkt_ctrl: PktCtrl) -> Result<(), Error> {
350 self.write(wr_reg![GPKTCTL1A, pkt_ctrl.as_bits()])
351 }
352
353 /// Set the initial value for generic packet whitening.
354 ///
355 /// This sets the first 8 bits, the 9th bit is set with
356 /// [`set_pkt_ctrl`](Self::set_pkt_ctrl).
357 pub fn set_init_whitening(&mut self, init: u8) -> Result<(), Error> {
358 self.write(wr_reg![GWHITEINIRL, init])
359 }
360
361 /// Set the initial value for generic packet CRC polynomial.
362 pub fn set_crc_polynomial(&mut self, polynomial: u16) -> Result<(), Error> {
363 let bytes: [u8; 2] = polynomial.to_be_bytes();
364 self.write(wr_reg![GCRCINIRH, bytes[0], bytes[1]])
365 }
366
367 /// Set the generic packet CRC polynomial.
368 pub fn set_initial_crc_polynomial(&mut self, polynomial: u16) -> Result<(), Error> {
369 let bytes: [u8; 2] = polynomial.to_be_bytes();
370 self.write(wr_reg![GCRCPOLRH, bytes[0], bytes[1]])
371 }
372
373 /// Set the synchronization word registers.
374 pub fn set_sync_word(&mut self, sync_word: &[u8; 8]) -> Result<(), Error> {
375 self.write_register(Register::GSYNC7, sync_word)
376 }
377
378 /// Set the LoRa synchronization word registers.
379 pub fn set_lora_sync_word(&mut self, sync_word: LoRaSyncWord) -> Result<(), Error> {
380 let bytes: [u8; 2] = sync_word.bytes();
381 self.write(wr_reg![LSYNCH, bytes[0], bytes[1]])
382 }
383
384 /// Set the RX gain control.
385 pub fn set_rx_gain(&mut self, pmode: PMode) -> Result<(), Error> {
386 self.write(wr_reg![RXGAINC, pmode as u8])
387 }
388
389 /// Set the power amplifier over current protection.
390 pub fn set_pa_ocp(&mut self, ocp: Ocp) -> Result<(), Error> {
391 self.write(wr_reg![PAOCP, ocp as u8])
392 }
393
394 /// Restart the radio RTC.
395 ///
396 /// This is used to workaround an erratum for [`set_rx_duty_cycle`].
397 ///
398 /// [`set_rx_duty_cycle`]: crate::subghz::SubGhz::set_rx_duty_cycle
399 pub fn restart_rtc(&mut self) -> Result<(), Error> {
400 self.write(wr_reg![RTCCTLR, 0b1])
401 }
402
403 /// Set the radio real-time-clock period.
404 ///
405 /// This is used to workaround an erratum for [`set_rx_duty_cycle`].
406 ///
407 /// [`set_rx_duty_cycle`]: crate::subghz::SubGhz::set_rx_duty_cycle
408 pub fn set_rtc_period(&mut self, period: Timeout) -> Result<(), Error> {
409 let tobits: u32 = period.into_bits();
410 self.write(wr_reg![
411 RTCPRDR2,
412 (tobits >> 16) as u8,
413 (tobits >> 8) as u8,
414 tobits as u8
415 ])
416 }
417
418 /// Set the HSE32 crystal OSC_IN load capacitor trimming.
419 pub fn set_hse_in_trim(&mut self, trim: HseTrim) -> Result<(), Error> {
420 self.write(wr_reg![HSEINTRIM, trim.into()])
421 }
422
423 /// Set the HSE32 crystal OSC_OUT load capacitor trimming.
424 pub fn set_hse_out_trim(&mut self, trim: HseTrim) -> Result<(), Error> {
425 self.write(wr_reg![HSEOUTTRIM, trim.into()])
426 }
427
428 /// Set the SMPS clock detection enabled.
429 ///
430 /// SMPS clock detection must be enabled fore enabling the SMPS.
431 pub fn set_smps_clock_det_en(&mut self, en: bool) -> Result<(), Error> {
432 self.write(wr_reg![SMPSC0, (en as u8) << 6])
433 }
434
435 /// Set the power current limiting.
436 pub fn set_pwr_ctrl(&mut self, pwr_ctrl: PwrCtrl) -> Result<(), Error> {
437 self.write(wr_reg![PC, pwr_ctrl.as_bits()])
438 }
439
440 /// Set the maximum SMPS drive capability.
441 pub fn set_smps_drv(&mut self, drv: SmpsDrv) -> Result<(), Error> {
442 self.write(wr_reg![SMPSC2, (drv as u8) << 1])
443 }
444
445 /// Set the node address.
446 ///
447 /// Used with [`GenericPacketParams::set_addr_comp`] to filter packets based
448 /// on node address.
449 pub fn set_node_addr(&mut self, addr: u8) -> Result<(), Error> {
450 self.write(wr_reg![NODE, addr])
451 }
452
453 /// Set the broadcast address.
454 ///
455 /// Used with [`GenericPacketParams::set_addr_comp`] to filter packets based
456 /// on broadcast address.
457 pub fn set_broadcast_addr(&mut self, addr: u8) -> Result<(), Error> {
458 self.write(wr_reg![BROADCAST, addr])
459 }
460
461 /// Set both the broadcast address and node address.
462 ///
463 /// This is a combination of [`set_node_addr`] and [`set_broadcast_addr`]
464 /// in a single SPI transfer.
465 ///
466 /// [`set_node_addr`]: Self::set_node_addr
467 /// [`set_broadcast_addr`]: Self::set_broadcast_addr
468 pub fn set_addrs(&mut self, node: u8, broadcast: u8) -> Result<(), Error> {
469 self.write(wr_reg![NODE, node, broadcast])
470 }
471}
472
473// 5.8.3
474/// Operating mode commands
475impl<'d> SubGhz<'d, NoDma, NoDma> {
476 /// Put the radio into sleep mode.
477 ///
478 /// This command is only accepted in standby mode.
479 /// The cfg argument allows some optional functions to be maintained
480 /// in sleep mode.
481 ///
482 /// # Safety
483 ///
484 /// 1. After the `set_sleep` command, the sub-GHz radio NSS must not go low
485 /// for 500 μs.
486 /// No reason is provided, the reference manual (RM0453 rev 2) simply
487 /// says "you must".
488 /// 2. The radio cannot be used while in sleep mode.
489 /// 3. The radio must be woken up with [`wakeup`] before resuming use.
490 ///
491 /// # Example
492 ///
493 /// Put the radio into sleep mode.
494 ///
495 /// ```no_run
496 /// # let dp = unsafe { embassy_stm32::pac::Peripherals::steal() };
497 /// # let mut sg = embassy_stm32::subghz::SubGhz::new(p.SUBGHZSPI, ...);
498 /// use embassy_stm32::{
499 /// subghz::{wakeup, SleepCfg, StandbyClk},
500 /// };
501 ///
502 /// sg.set_standby(StandbyClk::Rc)?;
503 /// unsafe { sg.set_sleep(SleepCfg::default())? };
504 /// embassy_time::Timer::after(embassy_time::Duration::from_micros(500)).await;
505 /// unsafe { wakeup() };
506 /// # Ok::<(), embassy_stm32::subghz::Error>(())
507 /// ```
508 pub unsafe fn set_sleep(&mut self, cfg: SleepCfg) -> Result<(), Error> {
509 // poll for busy before, but not after
510 // radio idles with busy high while in sleep mode
511 self.poll_not_busy();
512 {
513 let _nss: Nss = Nss::new();
514 self.spi.blocking_write(&[OpCode::SetSleep as u8, u8::from(cfg)])?;
515 }
516 Ok(())
517 }
518
519 /// Put the radio into standby mode.
520 pub fn set_standby(&mut self, standby_clk: StandbyClk) -> Result<(), Error> {
521 self.write(&[OpCode::SetStandby as u8, u8::from(standby_clk)])
522 }
523
524 /// Put the subghz radio into frequency synthesis mode.
525 ///
526 /// The RF-PLL frequency must be set with [`set_rf_frequency`] before using
527 /// this command.
528 ///
529 /// Check the datasheet for more information, this is a test command but
530 /// I honestly do not see any use for it. Please update this description
531 /// if you know more than I do.
532 ///
533 /// [`set_rf_frequency`]: crate::subghz::SubGhz::set_rf_frequency
534 pub fn set_fs(&mut self) -> Result<(), Error> {
535 self.write(&[OpCode::SetFs.into()])
536 }
537
538 /// Setup the sub-GHz radio for TX.
539 pub fn set_tx(&mut self, timeout: Timeout) -> Result<(), Error> {
540 let tobits: u32 = timeout.into_bits();
541 self.write(&[
542 OpCode::SetTx.into(),
543 (tobits >> 16) as u8,
544 (tobits >> 8) as u8,
545 tobits as u8,
546 ])
547 }
548
549 /// Setup the sub-GHz radio for RX.
550 pub fn set_rx(&mut self, timeout: Timeout) -> Result<(), Error> {
551 let tobits: u32 = timeout.into_bits();
552 self.write(&[
553 OpCode::SetRx.into(),
554 (tobits >> 16) as u8,
555 (tobits >> 8) as u8,
556 tobits as u8,
557 ])
558 }
559
560 /// Allows selection of the receiver event which stops the RX timeout timer.
561 pub fn set_rx_timeout_stop(&mut self, rx_timeout_stop: RxTimeoutStop) -> Result<(), Error> {
562 self.write(&[OpCode::SetStopRxTimerOnPreamble.into(), rx_timeout_stop.into()])
563 }
564
565 /// Put the radio in non-continuous RX mode.
566 ///
567 /// This command must be sent in Standby mode.
568 /// This command is only functional with FSK and LoRa packet type.
569 ///
570 /// The following steps are performed:
571 /// 1. Save sub-GHz radio configuration.
572 /// 2. Enter Receive mode and listen for a preamble for the specified `rx_period`.
573 /// 3. Upon the detection of a preamble, the `rx_period` timeout is stopped
574 /// and restarted with the value 2 x `rx_period` + `sleep_period`.
575 /// During this new period, the sub-GHz radio looks for the detection of
576 /// a synchronization word when in (G)FSK modulation mode,
577 /// or a header when in LoRa modulation mode.
578 /// 4. If no packet is received during the listen period defined by
579 /// 2 x `rx_period` + `sleep_period`, the sleep mode is entered for a
580 /// duration of `sleep_period`. At the end of the receive period,
581 /// the sub-GHz radio takes some time to save the context before starting
582 /// the sleep period.
583 /// 5. After the sleep period, a new listening period is automatically
584 /// started. The sub-GHz radio restores the sub-GHz radio configuration
585 /// and continuous with step 2.
586 ///
587 /// The listening mode is terminated in one of the following cases:
588 /// * if a packet is received during the listening period: the sub-GHz radio
589 /// issues a [`RxDone`] interrupt and enters standby mode.
590 /// * if [`set_standby`] is sent during the listening period or after the
591 /// sub-GHz has been requested to exit sleep mode by sub-GHz radio SPI NSS
592 ///
593 /// # Erratum
594 ///
595 /// When a preamble is detected the radio should restart the RX timeout
596 /// with a value of 2 × `rx_period` + `sleep_period`.
597 /// Instead the radio erroneously uses `sleep_period`.
598 ///
599 /// To workaround this use [`restart_rtc`] and [`set_rtc_period`] to
600 /// reprogram the radio timeout to 2 × `rx_period` + `sleep_period`.
601 ///
602 /// Use code similar to this in the [`PreambleDetected`] interrupt handler.
603 ///
604 /// ```no_run
605 /// # let rx_period: Timeout = Timeout::from_millis_sat(100);
606 /// # let sleep_period: Timeout = Timeout::from_millis_sat(100);
607 /// # let mut sg = unsafe { stm32wlxx_hal::subghz::SubGhz::steal() };
608 /// use stm32wlxx_hal::subghz::Timeout;
609 ///
610 /// let period: Timeout = rx_period
611 /// .saturating_add(rx_period)
612 /// .saturating_add(sleep_period);
613 ///
614 /// sg.set_rtc_period(period)?;
615 /// sg.restart_rtc()?;
616 /// # Ok::<(), stm32wlxx_hal::subghz::Error>(())
617 /// ```
618 ///
619 /// Please read the erratum for more details.
620 ///
621 /// [`PreambleDetected`]: crate::subghz::Irq::PreambleDetected
622 /// [`restart_rtc`]: crate::subghz::SubGhz::restart_rtc
623 /// [`RxDone`]: crate::subghz::Irq::RxDone
624 /// [`set_rf_frequency`]: crate::subghz::SubGhz::set_rf_frequency
625 /// [`set_rtc_period`]: crate::subghz::SubGhz::set_rtc_period
626 /// [`set_standby`]: crate::subghz::SubGhz::set_standby
627 pub fn set_rx_duty_cycle(&mut self, rx_period: Timeout, sleep_period: Timeout) -> Result<(), Error> {
628 let rx_period_bits: u32 = rx_period.into_bits();
629 let sleep_period_bits: u32 = sleep_period.into_bits();
630 self.write(&[
631 OpCode::SetRxDutyCycle.into(),
632 (rx_period_bits >> 16) as u8,
633 (rx_period_bits >> 8) as u8,
634 rx_period_bits as u8,
635 (sleep_period_bits >> 16) as u8,
636 (sleep_period_bits >> 8) as u8,
637 sleep_period_bits as u8,
638 ])
639 }
640
641 /// Channel Activity Detection (CAD) with LoRa packets.
642 ///
643 /// The channel activity detection (CAD) is a specific LoRa operation mode,
644 /// where the sub-GHz radio searches for a LoRa radio signal.
645 /// After the search is completed, the Standby mode is automatically
646 /// entered, CAD is done and IRQ is generated.
647 /// When a LoRa radio signal is detected, the CAD detected IRQ is also
648 /// generated.
649 ///
650 /// The length of the search must be configured with [`set_cad_params`]
651 /// prior to calling `set_cad`.
652 ///
653 /// [`set_cad_params`]: crate::subghz::SubGhz::set_cad_params
654 pub fn set_cad(&mut self) -> Result<(), Error> {
655 self.write(&[OpCode::SetCad.into()])
656 }
657
658 /// Generate a continuous transmit tone at the RF-PLL frequency.
659 ///
660 /// The sub-GHz radio remains in continuous transmit tone mode until a mode
661 /// configuration command is received.
662 pub fn set_tx_continuous_wave(&mut self) -> Result<(), Error> {
663 self.write(&[OpCode::SetTxContinuousWave as u8])
664 }
665
666 /// Generate an infinite preamble at the RF-PLL frequency.
667 ///
668 /// The preamble is an alternating 0s and 1s sequence in generic (G)FSK and
669 /// (G)MSK modulations.
670 /// The preamble is symbol 0 in LoRa modulation.
671 /// The sub-GHz radio remains in infinite preamble mode until a mode
672 /// configuration command is received.
673 pub fn set_tx_continuous_preamble(&mut self) -> Result<(), Error> {
674 self.write(&[OpCode::SetTxContinuousPreamble as u8])
675 }
676}
677
678// 5.8.4
679/// Radio configuration commands
680impl<'d> SubGhz<'d, NoDma, NoDma> {
681 /// Set the packet type (modulation scheme).
682 pub fn set_packet_type(&mut self, packet_type: PacketType) -> Result<(), Error> {
683 self.write(&[OpCode::SetPacketType as u8, packet_type as u8])
684 }
685
686 /// Get the packet type.
687 pub fn packet_type(&mut self) -> Result<Result<PacketType, u8>, Error> {
688 let pkt_type: [u8; 2] = self.read_n(OpCode::GetPacketType)?;
689 Ok(PacketType::from_raw(pkt_type[1]))
690 }
691
692 /// Set the radio carrier frequency.
693 pub fn set_rf_frequency(&mut self, freq: &RfFreq) -> Result<(), Error> {
694 self.write(freq.as_slice())
695 }
696
697 /// Set the transmit output power and the PA ramp-up time.
698 pub fn set_tx_params(&mut self, params: &TxParams) -> Result<(), Error> {
699 self.write(params.as_slice())
700 }
701
702 /// Power amplifier configuration.
703 ///
704 /// Used to customize the maximum output power and efficiency.
705 pub fn set_pa_config(&mut self, pa_config: &PaConfig) -> Result<(), Error> {
706 self.write(pa_config.as_slice())
707 }
708
709 /// Operating mode to enter after a successful packet transmission or
710 /// packet reception.
711 pub fn set_tx_rx_fallback_mode(&mut self, fm: FallbackMode) -> Result<(), Error> {
712 self.write(&[OpCode::SetTxRxFallbackMode as u8, fm as u8])
713 }
714
715 /// Set channel activity detection (CAD) parameters.
716 pub fn set_cad_params(&mut self, params: &CadParams) -> Result<(), Error> {
717 self.write(params.as_slice())
718 }
719
720 /// Set the data buffer base address for the packet handling in TX and RX.
721 ///
722 /// There is a single buffer for both TX and RX.
723 /// The buffer is not memory mapped, it is accessed via the
724 /// [`read_buffer`](SubGhz::read_buffer) and
725 /// [`write_buffer`](SubGhz::write_buffer) methods.
726 pub fn set_buffer_base_address(&mut self, tx: u8, rx: u8) -> Result<(), Error> {
727 self.write(&[OpCode::SetBufferBaseAddress as u8, tx, rx])
728 }
729
730 /// Set the (G)FSK modulation parameters.
731 pub fn set_fsk_mod_params(&mut self, params: &FskModParams) -> Result<(), Error> {
732 self.write(params.as_slice())
733 }
734
735 /// Set the LoRa modulation parameters.
736 pub fn set_lora_mod_params(&mut self, params: &LoRaModParams) -> Result<(), Error> {
737 self.write(params.as_slice())
738 }
739
740 /// Set the BPSK modulation parameters.
741 pub fn set_bpsk_mod_params(&mut self, params: &BpskModParams) -> Result<(), Error> {
742 self.write(params.as_slice())
743 }
744
745 /// Set the generic (FSK) packet parameters.
746 pub fn set_packet_params(&mut self, params: &GenericPacketParams) -> Result<(), Error> {
747 self.write(params.as_slice())
748 }
749
750 /// Set the BPSK packet parameters.
751 pub fn set_bpsk_packet_params(&mut self, params: &BpskPacketParams) -> Result<(), Error> {
752 self.write(params.as_slice())
753 }
754
755 /// Set the LoRa packet parameters.
756 pub fn set_lora_packet_params(&mut self, params: &LoRaPacketParams) -> Result<(), Error> {
757 self.write(params.as_slice())
758 }
759
760 /// Set the number of LoRa symbols to be received before starting the
761 /// reception of a LoRa packet.
762 ///
763 /// Packet reception is started after `n` + 1 symbols are detected.
764 pub fn set_lora_symb_timeout(&mut self, n: u8) -> Result<(), Error> {
765 self.write(&[OpCode::SetLoRaSymbTimeout.into(), n])
766 }
767}
768
769// 5.8.5
770/// Communication status and information commands
771impl<'d> SubGhz<'d, NoDma, NoDma> {
772 /// Get the radio status.
773 ///
774 /// The hardware (or documentation) appears to have many bugs where this
775 /// will return reserved values.
776 /// See this thread in the ST community for details: [link]
777 ///
778 /// [link]: https://community.st.com/s/question/0D53W00000hR9GQSA0/stm32wl55-getstatus-command-returns-reserved-cmdstatus
779 pub fn status(&mut self) -> Result<Status, Error> {
780 Ok(self.read_1(OpCode::GetStatus)?.into())
781 }
782
783 /// Get the RX buffer status.
784 ///
785 /// The return tuple is (status, payload_length, buffer_pointer).
786 pub fn rx_buffer_status(&mut self) -> Result<(Status, u8, u8), Error> {
787 let data: [u8; 3] = self.read_n(OpCode::GetRxBufferStatus)?;
788 Ok((data[0].into(), data[1], data[2]))
789 }
790
791 /// Returns information on the last received (G)FSK packet.
792 pub fn fsk_packet_status(&mut self) -> Result<FskPacketStatus, Error> {
793 Ok(FskPacketStatus::from(self.read_n(OpCode::GetPacketStatus)?))
794 }
795
796 /// Returns information on the last received LoRa packet.
797 pub fn lora_packet_status(&mut self) -> Result<LoRaPacketStatus, Error> {
798 Ok(LoRaPacketStatus::from(self.read_n(OpCode::GetPacketStatus)?))
799 }
800
801 /// Get the instantaneous signal strength during packet reception.
802 ///
803 /// The units are in dbm.
804 pub fn rssi_inst(&mut self) -> Result<(Status, Ratio<i16>), Error> {
805 let data: [u8; 2] = self.read_n(OpCode::GetRssiInst)?;
806 let status: Status = data[0].into();
807 let rssi: Ratio<i16> = Ratio::new_raw(i16::from(data[1]), -2);
808
809 Ok((status, rssi))
810 }
811
812 /// (G)FSK packet stats.
813 pub fn fsk_stats(&mut self) -> Result<Stats<FskStats>, Error> {
814 let data: [u8; 7] = self.read_n(OpCode::GetStats)?;
815 Ok(Stats::from_raw_fsk(data))
816 }
817
818 /// LoRa packet stats.
819 pub fn lora_stats(&mut self) -> Result<Stats<LoRaStats>, Error> {
820 let data: [u8; 7] = self.read_n(OpCode::GetStats)?;
821 Ok(Stats::from_raw_lora(data))
822 }
823
824 /// Reset the stats as reported in [`lora_stats`](SubGhz::lora_stats) and
825 /// [`fsk_stats`](SubGhz::fsk_stats).
826 pub fn reset_stats(&mut self) -> Result<(), Error> {
827 const RESET_STATS: [u8; 7] = [0x00; 7];
828 self.write(&RESET_STATS)
829 }
830}
831
832// 5.8.6
833/// IRQ commands
834impl<'d> SubGhz<'d, NoDma, NoDma> {
835 /// Set the interrupt configuration.
836 pub fn set_irq_cfg(&mut self, cfg: &CfgIrq) -> Result<(), Error> {
837 self.write(cfg.as_slice())
838 }
839
840 /// Get the IRQ status.
841 pub fn irq_status(&mut self) -> Result<(Status, u16), Error> {
842 let data: [u8; 3] = self.read_n(OpCode::GetIrqStatus)?;
843 let irq_status: u16 = u16::from_be_bytes([data[1], data[2]]);
844 Ok((data[0].into(), irq_status))
845 }
846
847 /// Clear the IRQ status.
848 pub fn clear_irq_status(&mut self, mask: u16) -> Result<(), Error> {
849 self.write(&[OpCode::ClrIrqStatus as u8, (mask >> 8) as u8, mask as u8])
850 }
851}
852
853// 5.8.7
854/// Miscellaneous commands
855impl<'d> SubGhz<'d, NoDma, NoDma> {
856 /// Calibrate one or several blocks at any time when in standby mode.
857 pub fn calibrate(&mut self, cal: u8) -> Result<(), Error> {
858 // bit 7 is reserved and must be kept at reset value.
859 self.write(&[OpCode::Calibrate as u8, cal & 0x7F])
860 }
861
862 /// Calibrate the image at the given frequencies.
863 ///
864 /// Requires the radio to be in standby mode.
865 pub fn calibrate_image(&mut self, cal: CalibrateImage) -> Result<(), Error> {
866 self.write(&[OpCode::CalibrateImage as u8, cal.0, cal.1])
867 }
868
869 /// Set the radio power supply.
870 pub fn set_regulator_mode(&mut self, reg_mode: RegMode) -> Result<(), Error> {
871 self.write(&[OpCode::SetRegulatorMode as u8, reg_mode as u8])
872 }
873
874 /// Get the radio operational errors.
875 pub fn op_error(&mut self) -> Result<(Status, u16), Error> {
876 let data: [u8; 3] = self.read_n(OpCode::GetError)?;
877 Ok((data[0].into(), u16::from_be_bytes([data[1], data[2]])))
878 }
879
880 /// Clear all errors as reported by [`op_error`](SubGhz::op_error).
881 pub fn clear_error(&mut self) -> Result<(), Error> {
882 self.write(&[OpCode::ClrError as u8, 0x00])
883 }
884}
885
886// 5.8.8
887/// Set TCXO mode command
888impl<'d> SubGhz<'d, NoDma, NoDma> {
889 /// Set the TCXO trim and HSE32 ready timeout.
890 pub fn set_tcxo_mode(&mut self, tcxo_mode: &TcxoMode) -> Result<(), Error> {
891 self.write(tcxo_mode.as_slice())
892 }
893}
894
895/// sub-GHz radio opcodes.
896///
897/// See Table 41 "Sub-GHz radio SPI commands overview"
898#[repr(u8)]
899#[derive(Debug, Clone, Copy)]
900#[allow(dead_code)]
901pub(crate) enum OpCode {
902 Calibrate = 0x89,
903 CalibrateImage = 0x98,
904 CfgDioIrq = 0x08,
905 ClrError = 0x07,
906 ClrIrqStatus = 0x02,
907 GetError = 0x17,
908 GetIrqStatus = 0x12,
909 GetPacketStatus = 0x14,
910 GetPacketType = 0x11,
911 GetRssiInst = 0x15,
912 GetRxBufferStatus = 0x13,
913 GetStats = 0x10,
914 GetStatus = 0xC0,
915 ReadBuffer = 0x1E,
916 RegRegister = 0x1D,
917 ResetStats = 0x00,
918 SetBufferBaseAddress = 0x8F,
919 SetCad = 0xC5,
920 SetCadParams = 0x88,
921 SetFs = 0xC1,
922 SetLoRaSymbTimeout = 0xA0,
923 SetModulationParams = 0x8B,
924 SetPacketParams = 0x8C,
925 SetPacketType = 0x8A,
926 SetPaConfig = 0x95,
927 SetRegulatorMode = 0x96,
928 SetRfFrequency = 0x86,
929 SetRx = 0x82,
930 SetRxDutyCycle = 0x94,
931 SetSleep = 0x84,
932 SetStandby = 0x80,
933 SetStopRxTimerOnPreamble = 0x9F,
934 SetTcxoMode = 0x97,
935 SetTx = 0x83,
936 SetTxContinuousPreamble = 0xD2,
937 SetTxContinuousWave = 0xD1,
938 SetTxParams = 0x8E,
939 SetTxRxFallbackMode = 0x93,
940 WriteBuffer = 0x0E,
941 WriteRegister = 0x0D,
942}
943
944impl From<OpCode> for u8 {
945 fn from(opcode: OpCode) -> Self {
946 opcode as u8
947 }
948}
949
950#[repr(u16)]
951#[allow(clippy::upper_case_acronyms)]
952pub(crate) enum Register {
953 /// Generic bit synchronization.
954 GBSYNC = 0x06AC,
955 /// Generic packet control.
956 GPKTCTL1A = 0x06B8,
957 /// Generic whitening.
958 GWHITEINIRL = 0x06B9,
959 /// Generic CRC initial.
960 GCRCINIRH = 0x06BC,
961 /// Generic CRC polynomial.
962 GCRCPOLRH = 0x06BE,
963 /// Generic synchronization word 7.
964 GSYNC7 = 0x06C0,
965 /// Node address.
966 NODE = 0x06CD,
967 /// Broadcast address.
968 BROADCAST = 0x06CE,
969 /// LoRa synchronization word MSB.
970 LSYNCH = 0x0740,
971 /// LoRa synchronization word LSB.
972 #[allow(dead_code)]
973 LSYNCL = 0x0741,
974 /// Receiver gain control.
975 RXGAINC = 0x08AC,
976 /// PA over current protection.
977 PAOCP = 0x08E7,
978 /// RTC control.
979 RTCCTLR = 0x0902,
980 /// RTC period MSB.
981 RTCPRDR2 = 0x0906,
982 /// RTC period mid-byte.
983 #[allow(dead_code)]
984 RTCPRDR1 = 0x0907,
985 /// RTC period LSB.
986 #[allow(dead_code)]
987 RTCPRDR0 = 0x0908,
988 /// HSE32 OSC_IN capacitor trim.
989 HSEINTRIM = 0x0911,
990 /// HSE32 OSC_OUT capacitor trim.
991 HSEOUTTRIM = 0x0912,
992 /// SMPS control 0.
993 SMPSC0 = 0x0916,
994 /// Power control.
995 PC = 0x091A,
996 /// SMPS control 2.
997 SMPSC2 = 0x0923,
998}
999
1000impl Register {
1001 pub const fn address(self) -> u16 {
1002 self as u16
1003 }
1004}
diff --git a/embassy-stm32/src/subghz/mod_params.rs b/embassy-stm32/src/subghz/mod_params.rs
deleted file mode 100644
index d997ae112..000000000
--- a/embassy-stm32/src/subghz/mod_params.rs
+++ /dev/null
@@ -1,1045 +0,0 @@
1/// Bandwidth options for [`FskModParams`].
2#[derive(Debug, PartialEq, Eq, Clone, Copy)]
3#[cfg_attr(feature = "defmt", derive(defmt::Format))]
4pub enum FskBandwidth {
5 /// 4.8 kHz double-sideband
6 Bw4 = 0x1F,
7 /// 5.8 kHz double-sideband
8 Bw5 = 0x17,
9 /// 7.3 kHz double-sideband
10 Bw7 = 0x0F,
11 /// 9.7 kHz double-sideband
12 Bw9 = 0x1E,
13 /// 11.7 kHz double-sideband
14 Bw11 = 0x16,
15 /// 14.6 kHz double-sideband
16 Bw14 = 0x0E,
17 /// 19.5 kHz double-sideband
18 Bw19 = 0x1D,
19 /// 23.4 kHz double-sideband
20 Bw23 = 0x15,
21 /// 29.3 kHz double-sideband
22 Bw29 = 0x0D,
23 /// 39.0 kHz double-sideband
24 Bw39 = 0x1C,
25 /// 46.9 kHz double-sideband
26 Bw46 = 0x14,
27 /// 58.6 kHz double-sideband
28 Bw58 = 0x0C,
29 /// 78.2 kHz double-sideband
30 Bw78 = 0x1B,
31 /// 93.8 kHz double-sideband
32 Bw93 = 0x13,
33 /// 117.3 kHz double-sideband
34 Bw117 = 0x0B,
35 /// 156.2 kHz double-sideband
36 Bw156 = 0x1A,
37 /// 187.2 kHz double-sideband
38 Bw187 = 0x12,
39 /// 234.3 kHz double-sideband
40 Bw234 = 0x0A,
41 /// 312.0 kHz double-sideband
42 Bw312 = 0x19,
43 /// 373.6 kHz double-sideband
44 Bw373 = 0x11,
45 /// 467.0 kHz double-sideband
46 Bw467 = 0x09,
47}
48
49impl FskBandwidth {
50 /// Get the bandwidth in hertz.
51 ///
52 /// # Example
53 ///
54 /// ```
55 /// use stm32wlxx_hal::subghz::FskBandwidth;
56 ///
57 /// assert_eq!(FskBandwidth::Bw4.hertz(), 4_800);
58 /// assert_eq!(FskBandwidth::Bw5.hertz(), 5_800);
59 /// assert_eq!(FskBandwidth::Bw7.hertz(), 7_300);
60 /// assert_eq!(FskBandwidth::Bw9.hertz(), 9_700);
61 /// assert_eq!(FskBandwidth::Bw11.hertz(), 11_700);
62 /// assert_eq!(FskBandwidth::Bw14.hertz(), 14_600);
63 /// assert_eq!(FskBandwidth::Bw19.hertz(), 19_500);
64 /// assert_eq!(FskBandwidth::Bw23.hertz(), 23_400);
65 /// assert_eq!(FskBandwidth::Bw29.hertz(), 29_300);
66 /// assert_eq!(FskBandwidth::Bw39.hertz(), 39_000);
67 /// assert_eq!(FskBandwidth::Bw46.hertz(), 46_900);
68 /// assert_eq!(FskBandwidth::Bw58.hertz(), 58_600);
69 /// assert_eq!(FskBandwidth::Bw78.hertz(), 78_200);
70 /// assert_eq!(FskBandwidth::Bw93.hertz(), 93_800);
71 /// assert_eq!(FskBandwidth::Bw117.hertz(), 117_300);
72 /// assert_eq!(FskBandwidth::Bw156.hertz(), 156_200);
73 /// assert_eq!(FskBandwidth::Bw187.hertz(), 187_200);
74 /// assert_eq!(FskBandwidth::Bw234.hertz(), 234_300);
75 /// assert_eq!(FskBandwidth::Bw312.hertz(), 312_000);
76 /// assert_eq!(FskBandwidth::Bw373.hertz(), 373_600);
77 /// assert_eq!(FskBandwidth::Bw467.hertz(), 467_000);
78 /// ```
79 pub const fn hertz(&self) -> u32 {
80 match self {
81 FskBandwidth::Bw4 => 4_800,
82 FskBandwidth::Bw5 => 5_800,
83 FskBandwidth::Bw7 => 7_300,
84 FskBandwidth::Bw9 => 9_700,
85 FskBandwidth::Bw11 => 11_700,
86 FskBandwidth::Bw14 => 14_600,
87 FskBandwidth::Bw19 => 19_500,
88 FskBandwidth::Bw23 => 23_400,
89 FskBandwidth::Bw29 => 29_300,
90 FskBandwidth::Bw39 => 39_000,
91 FskBandwidth::Bw46 => 46_900,
92 FskBandwidth::Bw58 => 58_600,
93 FskBandwidth::Bw78 => 78_200,
94 FskBandwidth::Bw93 => 93_800,
95 FskBandwidth::Bw117 => 117_300,
96 FskBandwidth::Bw156 => 156_200,
97 FskBandwidth::Bw187 => 187_200,
98 FskBandwidth::Bw234 => 234_300,
99 FskBandwidth::Bw312 => 312_000,
100 FskBandwidth::Bw373 => 373_600,
101 FskBandwidth::Bw467 => 467_000,
102 }
103 }
104
105 /// Convert from a raw bit value.
106 ///
107 /// Invalid values will be returned in the `Err` variant of the result.
108 ///
109 /// # Example
110 ///
111 /// ```
112 /// use stm32wlxx_hal::subghz::FskBandwidth;
113 ///
114 /// assert_eq!(FskBandwidth::from_bits(0x1F), Ok(FskBandwidth::Bw4));
115 /// assert_eq!(FskBandwidth::from_bits(0x17), Ok(FskBandwidth::Bw5));
116 /// assert_eq!(FskBandwidth::from_bits(0x0F), Ok(FskBandwidth::Bw7));
117 /// assert_eq!(FskBandwidth::from_bits(0x1E), Ok(FskBandwidth::Bw9));
118 /// assert_eq!(FskBandwidth::from_bits(0x16), Ok(FskBandwidth::Bw11));
119 /// assert_eq!(FskBandwidth::from_bits(0x0E), Ok(FskBandwidth::Bw14));
120 /// assert_eq!(FskBandwidth::from_bits(0x1D), Ok(FskBandwidth::Bw19));
121 /// assert_eq!(FskBandwidth::from_bits(0x15), Ok(FskBandwidth::Bw23));
122 /// assert_eq!(FskBandwidth::from_bits(0x0D), Ok(FskBandwidth::Bw29));
123 /// assert_eq!(FskBandwidth::from_bits(0x1C), Ok(FskBandwidth::Bw39));
124 /// assert_eq!(FskBandwidth::from_bits(0x14), Ok(FskBandwidth::Bw46));
125 /// assert_eq!(FskBandwidth::from_bits(0x0C), Ok(FskBandwidth::Bw58));
126 /// assert_eq!(FskBandwidth::from_bits(0x1B), Ok(FskBandwidth::Bw78));
127 /// assert_eq!(FskBandwidth::from_bits(0x13), Ok(FskBandwidth::Bw93));
128 /// assert_eq!(FskBandwidth::from_bits(0x0B), Ok(FskBandwidth::Bw117));
129 /// assert_eq!(FskBandwidth::from_bits(0x1A), Ok(FskBandwidth::Bw156));
130 /// assert_eq!(FskBandwidth::from_bits(0x12), Ok(FskBandwidth::Bw187));
131 /// assert_eq!(FskBandwidth::from_bits(0x0A), Ok(FskBandwidth::Bw234));
132 /// assert_eq!(FskBandwidth::from_bits(0x19), Ok(FskBandwidth::Bw312));
133 /// assert_eq!(FskBandwidth::from_bits(0x11), Ok(FskBandwidth::Bw373));
134 /// assert_eq!(FskBandwidth::from_bits(0x09), Ok(FskBandwidth::Bw467));
135 /// assert_eq!(FskBandwidth::from_bits(0x00), Err(0x00));
136 /// ```
137 pub const fn from_bits(bits: u8) -> Result<Self, u8> {
138 match bits {
139 0x1F => Ok(Self::Bw4),
140 0x17 => Ok(Self::Bw5),
141 0x0F => Ok(Self::Bw7),
142 0x1E => Ok(Self::Bw9),
143 0x16 => Ok(Self::Bw11),
144 0x0E => Ok(Self::Bw14),
145 0x1D => Ok(Self::Bw19),
146 0x15 => Ok(Self::Bw23),
147 0x0D => Ok(Self::Bw29),
148 0x1C => Ok(Self::Bw39),
149 0x14 => Ok(Self::Bw46),
150 0x0C => Ok(Self::Bw58),
151 0x1B => Ok(Self::Bw78),
152 0x13 => Ok(Self::Bw93),
153 0x0B => Ok(Self::Bw117),
154 0x1A => Ok(Self::Bw156),
155 0x12 => Ok(Self::Bw187),
156 0x0A => Ok(Self::Bw234),
157 0x19 => Ok(Self::Bw312),
158 0x11 => Ok(Self::Bw373),
159 0x09 => Ok(Self::Bw467),
160 x => Err(x),
161 }
162 }
163}
164
165impl Ord for FskBandwidth {
166 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
167 self.hertz().cmp(&other.hertz())
168 }
169}
170
171impl PartialOrd for FskBandwidth {
172 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
173 Some(self.hertz().cmp(&other.hertz()))
174 }
175}
176
177/// Pulse shaping options for [`FskModParams`].
178#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
179#[cfg_attr(feature = "defmt", derive(defmt::Format))]
180pub enum FskPulseShape {
181 /// No filtering applied.
182 None = 0b00,
183 /// Gaussian BT 0.3
184 Bt03 = 0x08,
185 /// Gaussian BT 0.5
186 Bt05 = 0x09,
187 /// Gaussian BT 0.7
188 Bt07 = 0x0A,
189 /// Gaussian BT 1.0
190 Bt10 = 0x0B,
191}
192
193/// Bitrate argument for [`FskModParams::set_bitrate`] and
194/// [`BpskModParams::set_bitrate`].
195#[derive(Debug, PartialEq, Eq, Clone, Copy)]
196pub struct FskBitrate {
197 bits: u32,
198}
199
200impl FskBitrate {
201 /// Create a new `FskBitrate` from a bitrate in bits per second.
202 ///
203 /// This the resulting value will be rounded down, and will saturate if
204 /// `bps` is outside of the theoretical limits.
205 ///
206 /// # Example
207 ///
208 /// ```
209 /// use stm32wlxx_hal::subghz::FskBitrate;
210 ///
211 /// const BITRATE: FskBitrate = FskBitrate::from_bps(9600);
212 /// assert_eq!(BITRATE.as_bps(), 9600);
213 /// ```
214 pub const fn from_bps(bps: u32) -> Self {
215 const MAX: u32 = 0x00FF_FFFF;
216 if bps == 0 {
217 Self { bits: MAX }
218 } else {
219 let bits: u32 = 32 * 32_000_000 / bps;
220 if bits > MAX {
221 Self { bits: MAX }
222 } else {
223 Self { bits }
224 }
225 }
226 }
227
228 /// Create a new `FskBitrate` from a raw bit value.
229 ///
230 /// bits = 32 × 32 MHz / bitrate
231 ///
232 /// **Note:** Only the first 24 bits of the `u32` are used, the `bits`
233 /// argument will be masked.
234 ///
235 /// # Example
236 ///
237 /// ```
238 /// use stm32wlxx_hal::subghz::FskBitrate;
239 ///
240 /// const BITRATE: FskBitrate = FskBitrate::from_raw(0x7D00);
241 /// assert_eq!(BITRATE.as_bps(), 32_000);
242 /// ```
243 pub const fn from_raw(bits: u32) -> Self {
244 Self {
245 bits: bits & 0x00FF_FFFF,
246 }
247 }
248
249 /// Return the bitrate in bits per second, rounded down.
250 ///
251 /// # Example
252 ///
253 /// ```
254 /// use stm32wlxx_hal::subghz::FskBitrate;
255 ///
256 /// const BITS_PER_SEC: u32 = 9600;
257 /// const BITRATE: FskBitrate = FskBitrate::from_bps(BITS_PER_SEC);
258 /// assert_eq!(BITRATE.as_bps(), BITS_PER_SEC);
259 /// ```
260 pub const fn as_bps(&self) -> u32 {
261 if self.bits == 0 {
262 0
263 } else {
264 32 * 32_000_000 / self.bits
265 }
266 }
267
268 pub(crate) const fn into_bits(self) -> u32 {
269 self.bits
270 }
271}
272
273impl Ord for FskBitrate {
274 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
275 self.as_bps().cmp(&other.as_bps())
276 }
277}
278
279impl PartialOrd for FskBitrate {
280 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
281 Some(self.as_bps().cmp(&other.as_bps()))
282 }
283}
284
285/// Frequency deviation argument for [`FskModParams::set_fdev`]
286#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
287#[cfg_attr(feature = "defmt", derive(defmt::Format))]
288pub struct FskFdev {
289 bits: u32,
290}
291
292impl FskFdev {
293 /// Create a new `FskFdev` from a frequency deviation in hertz, rounded
294 /// down.
295 ///
296 /// # Example
297 ///
298 /// ```
299 /// use stm32wlxx_hal::subghz::FskFdev;
300 ///
301 /// const FDEV: FskFdev = FskFdev::from_hertz(31_250);
302 /// assert_eq!(FDEV.as_hertz(), 31_250);
303 /// ```
304 pub const fn from_hertz(hz: u32) -> Self {
305 Self {
306 bits: ((hz as u64) * (1 << 25) / 32_000_000) as u32 & 0x00FF_FFFF,
307 }
308 }
309
310 /// Create a new `FskFdev` from a raw bit value.
311 ///
312 /// bits = fdev × 2<sup>25</sup> / 32 MHz
313 ///
314 /// **Note:** Only the first 24 bits of the `u32` are used, the `bits`
315 /// argument will be masked.
316 ///
317 /// # Example
318 ///
319 /// ```
320 /// use stm32wlxx_hal::subghz::FskFdev;
321 ///
322 /// const FDEV: FskFdev = FskFdev::from_raw(0x8000);
323 /// assert_eq!(FDEV.as_hertz(), 31_250);
324 /// ```
325 pub const fn from_raw(bits: u32) -> Self {
326 Self {
327 bits: bits & 0x00FF_FFFF,
328 }
329 }
330
331 /// Return the frequency deviation in hertz, rounded down.
332 ///
333 /// # Example
334 ///
335 /// ```
336 /// use stm32wlxx_hal::subghz::FskFdev;
337 ///
338 /// const HERTZ: u32 = 31_250;
339 /// const FDEV: FskFdev = FskFdev::from_hertz(HERTZ);
340 /// assert_eq!(FDEV.as_hertz(), HERTZ);
341 /// ```
342 pub const fn as_hertz(&self) -> u32 {
343 ((self.bits as u64) * 32_000_000 / (1 << 25)) as u32
344 }
345
346 pub(crate) const fn into_bits(self) -> u32 {
347 self.bits
348 }
349}
350
351/// (G)FSK modulation parameters.
352#[derive(Debug, PartialEq, Eq, Clone, Copy)]
353#[cfg_attr(feature = "defmt", derive(defmt::Format))]
354pub struct FskModParams {
355 buf: [u8; 9],
356}
357
358impl FskModParams {
359 /// Create a new `FskModParams` struct.
360 ///
361 /// This is the same as `default`, but in a `const` function.
362 ///
363 /// # Example
364 ///
365 /// ```
366 /// use stm32wlxx_hal::subghz::FskModParams;
367 ///
368 /// const MOD_PARAMS: FskModParams = FskModParams::new();
369 /// ```
370 pub const fn new() -> FskModParams {
371 FskModParams {
372 buf: [
373 super::OpCode::SetModulationParams as u8,
374 0x00,
375 0x00,
376 0x00,
377 0x00,
378 0x00,
379 0x00,
380 0x00,
381 0x00,
382 ],
383 }
384 .set_bitrate(FskBitrate::from_bps(50_000))
385 .set_pulse_shape(FskPulseShape::None)
386 .set_bandwidth(FskBandwidth::Bw58)
387 .set_fdev(FskFdev::from_hertz(25_000))
388 }
389
390 /// Get the bitrate.
391 ///
392 /// # Example
393 ///
394 /// Setting the bitrate to 32,000 bits per second.
395 ///
396 /// ```
397 /// use stm32wlxx_hal::subghz::{FskBitrate, FskModParams};
398 ///
399 /// const BITRATE: FskBitrate = FskBitrate::from_bps(32_000);
400 /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bitrate(BITRATE);
401 /// assert_eq!(MOD_PARAMS.bitrate(), BITRATE);
402 /// ```
403 pub const fn bitrate(&self) -> FskBitrate {
404 let raw: u32 = u32::from_be_bytes([0, self.buf[1], self.buf[2], self.buf[3]]);
405 FskBitrate::from_raw(raw)
406 }
407
408 /// Set the bitrate.
409 ///
410 /// # Example
411 ///
412 /// Setting the bitrate to 32,000 bits per second.
413 ///
414 /// ```
415 /// use stm32wlxx_hal::subghz::{FskBitrate, FskModParams};
416 ///
417 /// const BITRATE: FskBitrate = FskBitrate::from_bps(32_000);
418 /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bitrate(BITRATE);
419 /// # assert_eq!(MOD_PARAMS.as_slice()[1], 0x00);
420 /// # assert_eq!(MOD_PARAMS.as_slice()[2], 0x7D);
421 /// # assert_eq!(MOD_PARAMS.as_slice()[3], 0x00);
422 /// ```
423 #[must_use = "set_bitrate returns a modified FskModParams"]
424 pub const fn set_bitrate(mut self, bitrate: FskBitrate) -> FskModParams {
425 let bits: u32 = bitrate.into_bits();
426 self.buf[1] = ((bits >> 16) & 0xFF) as u8;
427 self.buf[2] = ((bits >> 8) & 0xFF) as u8;
428 self.buf[3] = (bits & 0xFF) as u8;
429 self
430 }
431
432 /// Set the pulse shaping.
433 ///
434 /// # Example
435 ///
436 /// ```
437 /// use stm32wlxx_hal::subghz::{FskModParams, FskPulseShape};
438 ///
439 /// const MOD_PARAMS: FskModParams = FskModParams::new().set_pulse_shape(FskPulseShape::Bt03);
440 /// # assert_eq!(MOD_PARAMS.as_slice()[4], 0x08);
441 /// ```
442 #[must_use = "set_pulse_shape returns a modified FskModParams"]
443 pub const fn set_pulse_shape(mut self, shape: FskPulseShape) -> FskModParams {
444 self.buf[4] = shape as u8;
445 self
446 }
447
448 /// Get the bandwidth.
449 ///
450 /// Values that do not correspond to a valid [`FskBandwidth`] will be
451 /// returned in the `Err` variant of the result.
452 ///
453 /// # Example
454 ///
455 /// ```
456 /// use stm32wlxx_hal::subghz::{FskBandwidth, FskModParams};
457 ///
458 /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bandwidth(FskBandwidth::Bw9);
459 /// assert_eq!(MOD_PARAMS.bandwidth(), Ok(FskBandwidth::Bw9));
460 /// ```
461 pub const fn bandwidth(&self) -> Result<FskBandwidth, u8> {
462 FskBandwidth::from_bits(self.buf[5])
463 }
464
465 /// Set the bandwidth.
466 ///
467 /// # Example
468 ///
469 /// ```
470 /// use stm32wlxx_hal::subghz::{FskBandwidth, FskModParams};
471 ///
472 /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bandwidth(FskBandwidth::Bw9);
473 /// # assert_eq!(MOD_PARAMS.as_slice()[5], 0x1E);
474 /// ```
475 #[must_use = "set_pulse_shape returns a modified FskModParams"]
476 pub const fn set_bandwidth(mut self, bw: FskBandwidth) -> FskModParams {
477 self.buf[5] = bw as u8;
478 self
479 }
480
481 /// Get the frequency deviation.
482 ///
483 /// # Example
484 ///
485 /// ```
486 /// use stm32wlxx_hal::subghz::{FskFdev, FskModParams};
487 ///
488 /// const FDEV: FskFdev = FskFdev::from_hertz(31_250);
489 /// const MOD_PARAMS: FskModParams = FskModParams::new().set_fdev(FDEV);
490 /// assert_eq!(MOD_PARAMS.fdev(), FDEV);
491 /// ```
492 pub const fn fdev(&self) -> FskFdev {
493 let raw: u32 = u32::from_be_bytes([0, self.buf[6], self.buf[7], self.buf[8]]);
494 FskFdev::from_raw(raw)
495 }
496
497 /// Set the frequency deviation.
498 ///
499 /// # Example
500 ///
501 /// ```
502 /// use stm32wlxx_hal::subghz::{FskFdev, FskModParams};
503 ///
504 /// const FDEV: FskFdev = FskFdev::from_hertz(31_250);
505 /// const MOD_PARAMS: FskModParams = FskModParams::new().set_fdev(FDEV);
506 /// # assert_eq!(MOD_PARAMS.as_slice()[6], 0x00);
507 /// # assert_eq!(MOD_PARAMS.as_slice()[7], 0x80);
508 /// # assert_eq!(MOD_PARAMS.as_slice()[8], 0x00);
509 /// ```
510 #[must_use = "set_fdev returns a modified FskModParams"]
511 pub const fn set_fdev(mut self, fdev: FskFdev) -> FskModParams {
512 let bits: u32 = fdev.into_bits();
513 self.buf[6] = ((bits >> 16) & 0xFF) as u8;
514 self.buf[7] = ((bits >> 8) & 0xFF) as u8;
515 self.buf[8] = (bits & 0xFF) as u8;
516 self
517 }
518 /// Returns `true` if the modulation parameters are valid.
519 ///
520 /// The bandwidth must be chosen so that:
521 ///
522 /// [`FskBandwidth`] > [`FskBitrate`] + 2 × [`FskFdev`] + frequency error
523 ///
524 /// Where frequency error = 2 × HSE32<sub>FREQ</sub> error.
525 ///
526 /// The datasheet (DS13293 Rev 1) gives these requirements for the HSE32
527 /// frequency tolerance:
528 ///
529 /// * Initial: ±10 ppm
530 /// * Over temperature (-20 to 70 °C): ±10 ppm
531 /// * Aging over 10 years: ±10 ppm
532 ///
533 /// # Example
534 ///
535 /// Checking valid parameters at compile-time
536 ///
537 /// ```
538 /// extern crate static_assertions as sa;
539 /// use stm32wlxx_hal::subghz::{FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape};
540 ///
541 /// const MOD_PARAMS: FskModParams = FskModParams::new()
542 /// .set_bitrate(FskBitrate::from_bps(20_000))
543 /// .set_pulse_shape(FskPulseShape::Bt03)
544 /// .set_bandwidth(FskBandwidth::Bw58)
545 /// .set_fdev(FskFdev::from_hertz(10_000));
546 ///
547 /// // 30 PPM is wost case (if the HSE32 crystal meets requirements)
548 /// sa::const_assert!(MOD_PARAMS.is_valid(30));
549 /// ```
550 #[must_use = "the return value indicates if the modulation parameters are valid"]
551 pub const fn is_valid(&self, ppm: u8) -> bool {
552 let bw: u32 = match self.bandwidth() {
553 Ok(bw) => bw.hertz(),
554 Err(_) => return false,
555 };
556 let br: u32 = self.bitrate().as_bps();
557 let fdev: u32 = self.fdev().as_hertz();
558 let hse_err: u32 = 32 * (ppm as u32);
559 let freq_err: u32 = 2 * hse_err;
560
561 bw > br + 2 * fdev + freq_err
562 }
563
564 /// Returns `true` if the modulation parameters are valid for a worst-case
565 /// crystal tolerance.
566 ///
567 /// This is equivalent to [`is_valid`](Self::is_valid) with a `ppm` argument
568 /// of 30.
569 #[must_use = "the return value indicates if the modulation parameters are valid"]
570 pub const fn is_valid_worst_case(&self) -> bool {
571 self.is_valid(30)
572 }
573
574 /// Extracts a slice containing the packet.
575 ///
576 /// # Example
577 ///
578 /// ```
579 /// use stm32wlxx_hal::subghz::{FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape};
580 ///
581 /// const BITRATE: FskBitrate = FskBitrate::from_bps(20_000);
582 /// const PULSE_SHAPE: FskPulseShape = FskPulseShape::Bt03;
583 /// const BW: FskBandwidth = FskBandwidth::Bw58;
584 /// const FDEV: FskFdev = FskFdev::from_hertz(10_000);
585 ///
586 /// const MOD_PARAMS: FskModParams = FskModParams::new()
587 /// .set_bitrate(BITRATE)
588 /// .set_pulse_shape(PULSE_SHAPE)
589 /// .set_bandwidth(BW)
590 /// .set_fdev(FDEV);
591 ///
592 /// assert_eq!(
593 /// MOD_PARAMS.as_slice(),
594 /// &[0x8B, 0x00, 0xC8, 0x00, 0x08, 0x0C, 0x00, 0x28, 0xF5]
595 /// );
596 /// ```
597 pub const fn as_slice(&self) -> &[u8] {
598 &self.buf
599 }
600}
601
602impl Default for FskModParams {
603 fn default() -> Self {
604 Self::new()
605 }
606}
607
608/// LoRa spreading factor.
609///
610/// Argument of [`LoRaModParams::set_sf`].
611///
612/// Higher spreading factors improve receiver sensitivity, but reduce bit rate
613/// and increase power consumption.
614#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
615#[cfg_attr(feature = "defmt", derive(defmt::Format))]
616#[repr(u8)]
617pub enum SpreadingFactor {
618 /// Spreading factor 5.
619 Sf5 = 0x05,
620 /// Spreading factor 6.
621 Sf6 = 0x06,
622 /// Spreading factor 7.
623 Sf7 = 0x07,
624 /// Spreading factor 8.
625 Sf8 = 0x08,
626 /// Spreading factor 9.
627 Sf9 = 0x09,
628 /// Spreading factor 10.
629 Sf10 = 0x0A,
630 /// Spreading factor 11.
631 Sf11 = 0x0B,
632 /// Spreading factor 12.
633 Sf12 = 0x0C,
634}
635
636impl From<SpreadingFactor> for u8 {
637 fn from(sf: SpreadingFactor) -> Self {
638 sf as u8
639 }
640}
641
642/// LoRa bandwidth.
643///
644/// Argument of [`LoRaModParams::set_bw`].
645#[derive(Debug, PartialEq, Eq, Clone, Copy)]
646#[cfg_attr(feature = "defmt", derive(defmt::Format))]
647#[repr(u8)]
648pub enum LoRaBandwidth {
649 /// 7.81 kHz
650 Bw7 = 0x00,
651 /// 10.42 kHz
652 Bw10 = 0x08,
653 /// 15.63 kHz
654 Bw15 = 0x01,
655 /// 20.83 kHz
656 Bw20 = 0x09,
657 /// 31.25 kHz
658 Bw31 = 0x02,
659 /// 41.67 kHz
660 Bw41 = 0x0A,
661 /// 62.50 kHz
662 Bw62 = 0x03,
663 /// 125 kHz
664 Bw125 = 0x04,
665 /// 250 kHz
666 Bw250 = 0x05,
667 /// 500 kHz
668 Bw500 = 0x06,
669}
670
671impl LoRaBandwidth {
672 /// Get the bandwidth in hertz.
673 ///
674 /// # Example
675 ///
676 /// ```
677 /// use stm32wlxx_hal::subghz::LoRaBandwidth;
678 ///
679 /// assert_eq!(LoRaBandwidth::Bw7.hertz(), 7_810);
680 /// assert_eq!(LoRaBandwidth::Bw10.hertz(), 10_420);
681 /// assert_eq!(LoRaBandwidth::Bw15.hertz(), 15_630);
682 /// assert_eq!(LoRaBandwidth::Bw20.hertz(), 20_830);
683 /// assert_eq!(LoRaBandwidth::Bw31.hertz(), 31_250);
684 /// assert_eq!(LoRaBandwidth::Bw41.hertz(), 41_670);
685 /// assert_eq!(LoRaBandwidth::Bw62.hertz(), 62_500);
686 /// assert_eq!(LoRaBandwidth::Bw125.hertz(), 125_000);
687 /// assert_eq!(LoRaBandwidth::Bw250.hertz(), 250_000);
688 /// assert_eq!(LoRaBandwidth::Bw500.hertz(), 500_000);
689 /// ```
690 pub const fn hertz(&self) -> u32 {
691 match self {
692 LoRaBandwidth::Bw7 => 7_810,
693 LoRaBandwidth::Bw10 => 10_420,
694 LoRaBandwidth::Bw15 => 15_630,
695 LoRaBandwidth::Bw20 => 20_830,
696 LoRaBandwidth::Bw31 => 31_250,
697 LoRaBandwidth::Bw41 => 41_670,
698 LoRaBandwidth::Bw62 => 62_500,
699 LoRaBandwidth::Bw125 => 125_000,
700 LoRaBandwidth::Bw250 => 250_000,
701 LoRaBandwidth::Bw500 => 500_000,
702 }
703 }
704}
705
706impl Ord for LoRaBandwidth {
707 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
708 self.hertz().cmp(&other.hertz())
709 }
710}
711
712impl PartialOrd for LoRaBandwidth {
713 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
714 Some(self.hertz().cmp(&other.hertz()))
715 }
716}
717
718/// LoRa forward error correction coding rate.
719///
720/// Argument of [`LoRaModParams::set_cr`].
721///
722/// A higher coding rate provides better immunity to interference at the expense
723/// of longer transmission time.
724/// In normal conditions [`CodingRate::Cr45`] provides the best trade off.
725/// In case of strong interference, a higher coding rate may be used.
726#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
727#[cfg_attr(feature = "defmt", derive(defmt::Format))]
728#[repr(u8)]
729pub enum CodingRate {
730 /// No forward error correction coding rate 4/4
731 ///
732 /// Overhead ratio of 1
733 Cr44 = 0x00,
734 /// Forward error correction coding rate 4/5
735 ///
736 /// Overhead ratio of 1.25
737 Cr45 = 0x1,
738 /// Forward error correction coding rate 4/6
739 ///
740 /// Overhead ratio of 1.5
741 Cr46 = 0x2,
742 /// Forward error correction coding rate 4/7
743 ///
744 /// Overhead ratio of 1.75
745 Cr47 = 0x3,
746 /// Forward error correction coding rate 4/8
747 ///
748 /// Overhead ratio of 2
749 Cr48 = 0x4,
750}
751
752/// LoRa modulation parameters.
753#[derive(Debug, PartialEq, Eq, Clone, Copy)]
754#[cfg_attr(feature = "defmt", derive(defmt::Format))]
755
756pub struct LoRaModParams {
757 buf: [u8; 5],
758}
759
760impl LoRaModParams {
761 /// Create a new `LoRaModParams` struct.
762 ///
763 /// This is the same as `default`, but in a `const` function.
764 ///
765 /// # Example
766 ///
767 /// ```
768 /// use stm32wlxx_hal::subghz::LoRaModParams;
769 ///
770 /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new();
771 /// assert_eq!(MOD_PARAMS, LoRaModParams::default());
772 /// ```
773 pub const fn new() -> LoRaModParams {
774 LoRaModParams {
775 buf: [
776 super::OpCode::SetModulationParams as u8,
777 0x05, // valid spreading factor
778 0x00,
779 0x00,
780 0x00,
781 ],
782 }
783 }
784
785 /// Set the spreading factor.
786 ///
787 /// # Example
788 ///
789 /// ```
790 /// use stm32wlxx_hal::subghz::{LoRaModParams, SpreadingFactor};
791 ///
792 /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_sf(SpreadingFactor::Sf7);
793 /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x07, 0x00, 0x00, 0x00]);
794 /// ```
795 #[must_use = "set_sf returns a modified LoRaModParams"]
796 pub const fn set_sf(mut self, sf: SpreadingFactor) -> Self {
797 self.buf[1] = sf as u8;
798 self
799 }
800
801 /// Set the bandwidth.
802 ///
803 /// # Example
804 ///
805 /// ```
806 /// use stm32wlxx_hal::subghz::{LoRaBandwidth, LoRaModParams};
807 ///
808 /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_bw(LoRaBandwidth::Bw125);
809 /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x04, 0x00, 0x00]);
810 /// ```
811 #[must_use = "set_bw returns a modified LoRaModParams"]
812 pub const fn set_bw(mut self, bw: LoRaBandwidth) -> Self {
813 self.buf[2] = bw as u8;
814 self
815 }
816
817 /// Set the forward error correction coding rate.
818 ///
819 /// See [`CodingRate`] for more information.
820 ///
821 /// # Example
822 ///
823 /// ```
824 /// use stm32wlxx_hal::subghz::{CodingRate, LoRaModParams};
825 ///
826 /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_cr(CodingRate::Cr45);
827 /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x00, 0x01, 0x00]);
828 /// ```
829 #[must_use = "set_cr returns a modified LoRaModParams"]
830 pub const fn set_cr(mut self, cr: CodingRate) -> Self {
831 self.buf[3] = cr as u8;
832 self
833 }
834
835 /// Set low data rate optimization enable.
836 ///
837 /// For low data rates (typically high SF or low BW) and very long payloads
838 /// (may last several seconds), the low data rate optimization (LDRO) can be
839 /// enabled.
840 /// This reduces the number of bits per symbol to the given SF minus 2,
841 /// to allow the receiver to have a better tracking of the LoRa receive
842 /// signal.
843 /// Depending on the payload length, the low data rate optimization is
844 /// usually recommended when the LoRa symbol time is equal or above
845 /// 16.38 ms.
846 /// When using LoRa modulation, the total frequency drift over the packet
847 /// time must be kept lower than Freq_drift_max:
848 ///
849 /// Freq_drift_max = BW / (3 × 2SF)
850 ///
851 /// When possible, enabling the low data rate optimization, relaxes the
852 /// total frequency drift over the packet time by 16:
853 ///
854 /// Freq_drift_optimise_max = 16 × Freq_drift_max
855 ///
856 /// # Example
857 ///
858 /// ```
859 /// use stm32wlxx_hal::subghz::LoRaModParams;
860 ///
861 /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_ldro_en(true);
862 /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x00, 0x00, 0x01]);
863 /// ```
864 #[must_use = "set_ldro_en returns a modified LoRaModParams"]
865 pub const fn set_ldro_en(mut self, en: bool) -> Self {
866 self.buf[4] = en as u8;
867 self
868 }
869
870 /// Extracts a slice containing the packet.
871 ///
872 /// # Example
873 ///
874 /// ```
875 /// use stm32wlxx_hal::subghz::{CodingRate, LoRaBandwidth, LoRaModParams, SpreadingFactor};
876 ///
877 /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new()
878 /// .set_sf(SpreadingFactor::Sf7)
879 /// .set_bw(LoRaBandwidth::Bw125)
880 /// .set_cr(CodingRate::Cr45)
881 /// .set_ldro_en(false);
882 ///
883 /// assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x07, 0x04, 0x01, 0x00]);
884 /// ```
885 pub const fn as_slice(&self) -> &[u8] {
886 &self.buf
887 }
888}
889
890impl Default for LoRaModParams {
891 fn default() -> Self {
892 Self::new()
893 }
894}
895
896/// BPSK modulation parameters.
897///
898/// **Note:** There is no method to set the pulse shape because there is only
899/// one valid pulse shape (Gaussian BT 0.5).
900#[derive(Debug, PartialEq, Eq, Clone, Copy)]
901#[cfg_attr(feature = "defmt", derive(defmt::Format))]
902pub struct BpskModParams {
903 buf: [u8; 5],
904}
905
906impl BpskModParams {
907 /// Create a new `BpskModParams` struct.
908 ///
909 /// This is the same as `default`, but in a `const` function.
910 ///
911 /// # Example
912 ///
913 /// ```
914 /// use stm32wlxx_hal::subghz::BpskModParams;
915 ///
916 /// const MOD_PARAMS: BpskModParams = BpskModParams::new();
917 /// assert_eq!(MOD_PARAMS, BpskModParams::default());
918 /// ```
919 pub const fn new() -> BpskModParams {
920 const OPCODE: u8 = super::OpCode::SetModulationParams as u8;
921 BpskModParams {
922 buf: [OPCODE, 0x1A, 0x0A, 0xAA, 0x16],
923 }
924 }
925
926 /// Set the bitrate.
927 ///
928 /// # Example
929 ///
930 /// Setting the bitrate to 600 bits per second.
931 ///
932 /// ```
933 /// use stm32wlxx_hal::subghz::{BpskModParams, FskBitrate};
934 ///
935 /// const BITRATE: FskBitrate = FskBitrate::from_bps(600);
936 /// const MOD_PARAMS: BpskModParams = BpskModParams::new().set_bitrate(BITRATE);
937 /// # assert_eq!(MOD_PARAMS.as_slice()[1], 0x1A);
938 /// # assert_eq!(MOD_PARAMS.as_slice()[2], 0x0A);
939 /// # assert_eq!(MOD_PARAMS.as_slice()[3], 0xAA);
940 /// ```
941 #[must_use = "set_bitrate returns a modified BpskModParams"]
942 pub const fn set_bitrate(mut self, bitrate: FskBitrate) -> BpskModParams {
943 let bits: u32 = bitrate.into_bits();
944 self.buf[1] = ((bits >> 16) & 0xFF) as u8;
945 self.buf[2] = ((bits >> 8) & 0xFF) as u8;
946 self.buf[3] = (bits & 0xFF) as u8;
947 self
948 }
949
950 /// Extracts a slice containing the packet.
951 ///
952 /// # Example
953 ///
954 /// ```
955 /// use stm32wlxx_hal::subghz::{BpskModParams, FskBitrate};
956 ///
957 /// const BITRATE: FskBitrate = FskBitrate::from_bps(100);
958 /// const MOD_PARAMS: BpskModParams = BpskModParams::new().set_bitrate(BITRATE);
959 /// assert_eq!(MOD_PARAMS.as_slice(), [0x8B, 0x9C, 0x40, 0x00, 0x16]);
960 /// ```
961 pub const fn as_slice(&self) -> &[u8] {
962 &self.buf
963 }
964}
965
966impl Default for BpskModParams {
967 fn default() -> Self {
968 Self::new()
969 }
970}
971
972#[cfg(test)]
973mod test {
974 use super::{FskBandwidth, FskBitrate, FskFdev, LoRaBandwidth};
975
976 #[test]
977 fn fsk_bw_ord() {
978 assert!((FskBandwidth::Bw4 as u8) > (FskBandwidth::Bw5 as u8));
979 assert!(FskBandwidth::Bw4 < FskBandwidth::Bw5);
980 assert!(FskBandwidth::Bw5 > FskBandwidth::Bw4);
981 }
982
983 #[test]
984 fn lora_bw_ord() {
985 assert!((LoRaBandwidth::Bw10 as u8) > (LoRaBandwidth::Bw15 as u8));
986 assert!(LoRaBandwidth::Bw10 < LoRaBandwidth::Bw15);
987 assert!(LoRaBandwidth::Bw15 > LoRaBandwidth::Bw10);
988 }
989
990 #[test]
991 fn fsk_bitrate_ord() {
992 assert!(FskBitrate::from_bps(9600) > FskBitrate::from_bps(4800));
993 assert!(FskBitrate::from_bps(4800) < FskBitrate::from_bps(9600));
994 }
995
996 #[test]
997 fn fsk_bitrate_as_bps_limits() {
998 const ZERO: FskBitrate = FskBitrate::from_raw(0);
999 const ONE: FskBitrate = FskBitrate::from_raw(1);
1000 const MAX: FskBitrate = FskBitrate::from_raw(u32::MAX);
1001
1002 assert_eq!(ZERO.as_bps(), 0);
1003 assert_eq!(ONE.as_bps(), 1_024_000_000);
1004 assert_eq!(MAX.as_bps(), 61);
1005 }
1006
1007 #[test]
1008 fn fsk_bitrate_from_bps_limits() {
1009 const ZERO: FskBitrate = FskBitrate::from_bps(0);
1010 const ONE: FskBitrate = FskBitrate::from_bps(1);
1011 const MAX: FskBitrate = FskBitrate::from_bps(u32::MAX);
1012
1013 assert_eq!(ZERO.as_bps(), 61);
1014 assert_eq!(ONE.as_bps(), 61);
1015 assert_eq!(MAX.as_bps(), 0);
1016 }
1017
1018 #[test]
1019 fn fsk_fdev_ord() {
1020 assert!(FskFdev::from_hertz(30_000) > FskFdev::from_hertz(20_000));
1021 assert!(FskFdev::from_hertz(20_000) < FskFdev::from_hertz(30_000));
1022 }
1023
1024 #[test]
1025 fn fsk_fdev_as_hertz_limits() {
1026 const ZERO: FskFdev = FskFdev::from_raw(0);
1027 const ONE: FskFdev = FskFdev::from_raw(1);
1028 const MAX: FskFdev = FskFdev::from_raw(u32::MAX);
1029
1030 assert_eq!(ZERO.as_hertz(), 0);
1031 assert_eq!(ONE.as_hertz(), 0);
1032 assert_eq!(MAX.as_hertz(), 15_999_999);
1033 }
1034
1035 #[test]
1036 fn fsk_fdev_from_hertz_limits() {
1037 const ZERO: FskFdev = FskFdev::from_hertz(0);
1038 const ONE: FskFdev = FskFdev::from_hertz(1);
1039 const MAX: FskFdev = FskFdev::from_hertz(u32::MAX);
1040
1041 assert_eq!(ZERO.as_hertz(), 0);
1042 assert_eq!(ONE.as_hertz(), 0);
1043 assert_eq!(MAX.as_hertz(), 6_967_294);
1044 }
1045}
diff --git a/embassy-stm32/src/subghz/ocp.rs b/embassy-stm32/src/subghz/ocp.rs
deleted file mode 100644
index 81e89c217..000000000
--- a/embassy-stm32/src/subghz/ocp.rs
+++ /dev/null
@@ -1,14 +0,0 @@
1/// Power amplifier over current protection.
2///
3/// Used by [`set_pa_ocp`].
4///
5/// [`set_pa_ocp`]: super::SubGhz::set_pa_ocp
6#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8#[repr(u8)]
9pub enum Ocp {
10 /// Maximum 60mA current for LP PA mode.
11 Max60m = 0x18,
12 /// Maximum 140mA for HP PA mode.
13 Max140m = 0x38,
14}
diff --git a/embassy-stm32/src/subghz/op_error.rs b/embassy-stm32/src/subghz/op_error.rs
deleted file mode 100644
index b17b99205..000000000
--- a/embassy-stm32/src/subghz/op_error.rs
+++ /dev/null
@@ -1,48 +0,0 @@
1/// Operation Errors.
2///
3/// Returned by [`op_error`].
4///
5/// [`op_error`]: super::SubGhz::op_error
6#[derive(Debug, PartialEq, Eq, Clone, Copy)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8#[repr(u8)]
9pub enum OpError {
10 /// PA ramping failed
11 PaRampError = 8,
12 /// RF-PLL locking failed
13 PllLockError = 6,
14 /// HSE32 clock startup failed
15 XoscStartError = 5,
16 /// Image calibration failed
17 ImageCalibrationError = 4,
18 /// RF-ADC calibration failed
19 AdcCalibrationError = 3,
20 /// RF-PLL calibration failed
21 PllCalibrationError = 2,
22 /// Sub-GHz radio RC 13 MHz oscillator
23 RC13MCalibrationError = 1,
24 /// Sub-GHz radio RC 64 kHz oscillator
25 RC64KCalibrationError = 0,
26}
27
28impl OpError {
29 /// Get the bitmask for the error.
30 ///
31 /// # Example
32 ///
33 /// ```
34 /// use stm32wlxx_hal::subghz::OpError;
35 ///
36 /// assert_eq!(OpError::PaRampError.mask(), 0b1_0000_0000);
37 /// assert_eq!(OpError::PllLockError.mask(), 0b0_0100_0000);
38 /// assert_eq!(OpError::XoscStartError.mask(), 0b0_0010_0000);
39 /// assert_eq!(OpError::ImageCalibrationError.mask(), 0b0_0001_0000);
40 /// assert_eq!(OpError::AdcCalibrationError.mask(), 0b0_0000_1000);
41 /// assert_eq!(OpError::PllCalibrationError.mask(), 0b0_0000_0100);
42 /// assert_eq!(OpError::RC13MCalibrationError.mask(), 0b0_0000_0010);
43 /// assert_eq!(OpError::RC64KCalibrationError.mask(), 0b0_0000_0001);
44 /// ```
45 pub const fn mask(self) -> u16 {
46 1 << (self as u8)
47 }
48}
diff --git a/embassy-stm32/src/subghz/pa_config.rs b/embassy-stm32/src/subghz/pa_config.rs
deleted file mode 100644
index 875827bd4..000000000
--- a/embassy-stm32/src/subghz/pa_config.rs
+++ /dev/null
@@ -1,196 +0,0 @@
1/// Power amplifier configuration parameters.
2///
3/// Argument of [`set_pa_config`].
4///
5/// [`set_pa_config`]: super::SubGhz::set_pa_config
6#[derive(Debug, PartialEq, Eq, Clone, Copy)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub struct PaConfig {
9 buf: [u8; 5],
10}
11
12impl PaConfig {
13 /// Optimal settings for +15dBm output power with the low-power PA.
14 ///
15 /// This must be used with [`TxParams::LP_15`](super::TxParams::LP_15).
16 pub const LP_15: PaConfig = PaConfig::new().set_pa_duty_cycle(0x6).set_hp_max(0x0).set_pa(PaSel::Lp);
17
18 /// Optimal settings for +14dBm output power with the low-power PA.
19 ///
20 /// This must be used with [`TxParams::LP_14`](super::TxParams::LP_14).
21 pub const LP_14: PaConfig = PaConfig::new().set_pa_duty_cycle(0x4).set_hp_max(0x0).set_pa(PaSel::Lp);
22
23 /// Optimal settings for +10dBm output power with the low-power PA.
24 ///
25 /// This must be used with [`TxParams::LP_10`](super::TxParams::LP_10).
26 pub const LP_10: PaConfig = PaConfig::new().set_pa_duty_cycle(0x1).set_hp_max(0x0).set_pa(PaSel::Lp);
27
28 /// Optimal settings for +22dBm output power with the high-power PA.
29 ///
30 /// This must be used with [`TxParams::HP`](super::TxParams::HP).
31 pub const HP_22: PaConfig = PaConfig::new().set_pa_duty_cycle(0x4).set_hp_max(0x7).set_pa(PaSel::Hp);
32
33 /// Optimal settings for +20dBm output power with the high-power PA.
34 ///
35 /// This must be used with [`TxParams::HP`](super::TxParams::HP).
36 pub const HP_20: PaConfig = PaConfig::new().set_pa_duty_cycle(0x3).set_hp_max(0x5).set_pa(PaSel::Hp);
37
38 /// Optimal settings for +17dBm output power with the high-power PA.
39 ///
40 /// This must be used with [`TxParams::HP`](super::TxParams::HP).
41 pub const HP_17: PaConfig = PaConfig::new().set_pa_duty_cycle(0x2).set_hp_max(0x3).set_pa(PaSel::Hp);
42
43 /// Optimal settings for +14dBm output power with the high-power PA.
44 ///
45 /// This must be used with [`TxParams::HP`](super::TxParams::HP).
46 pub const HP_14: PaConfig = PaConfig::new().set_pa_duty_cycle(0x2).set_hp_max(0x2).set_pa(PaSel::Hp);
47
48 /// Create a new `PaConfig` struct.
49 ///
50 /// This is the same as `default`, but in a `const` function.
51 ///
52 /// # Example
53 ///
54 /// ```
55 /// use stm32wlxx_hal::subghz::PaConfig;
56 ///
57 /// const PA_CONFIG: PaConfig = PaConfig::new();
58 /// ```
59 pub const fn new() -> PaConfig {
60 PaConfig {
61 buf: [super::OpCode::SetPaConfig as u8, 0x01, 0x00, 0x01, 0x01],
62 }
63 }
64
65 /// Set the power amplifier duty cycle (conduit angle) control.
66 ///
67 /// **Note:** Only the first 3 bits of the `pa_duty_cycle` argument are used.
68 ///
69 /// Duty cycle = 0.2 + 0.04 × bits
70 ///
71 /// # Caution
72 ///
73 /// The following restrictions must be observed to avoid over-stress on the PA:
74 /// * LP PA mode with synthesis frequency > 400 MHz, `pa_duty_cycle` must be < 0x7.
75 /// * LP PA mode with synthesis frequency < 400 MHz, `pa_duty_cycle` must be < 0x4.
76 /// * HP PA mode, `pa_duty_cycle` must be < 0x4
77 ///
78 /// # Example
79 ///
80 /// ```
81 /// use stm32wlxx_hal::subghz::{PaConfig, PaSel};
82 ///
83 /// const PA_CONFIG: PaConfig = PaConfig::new().set_pa(PaSel::Lp).set_pa_duty_cycle(0x4);
84 /// # assert_eq!(PA_CONFIG.as_slice()[1], 0x04);
85 /// ```
86 #[must_use = "set_pa_duty_cycle returns a modified PaConfig"]
87 pub const fn set_pa_duty_cycle(mut self, pa_duty_cycle: u8) -> PaConfig {
88 self.buf[1] = pa_duty_cycle & 0b111;
89 self
90 }
91
92 /// Set the high power amplifier output power.
93 ///
94 /// **Note:** Only the first 3 bits of the `hp_max` argument are used.
95 ///
96 /// # Example
97 ///
98 /// ```
99 /// use stm32wlxx_hal::subghz::{PaConfig, PaSel};
100 ///
101 /// const PA_CONFIG: PaConfig = PaConfig::new().set_pa(PaSel::Hp).set_hp_max(0x2);
102 /// # assert_eq!(PA_CONFIG.as_slice()[2], 0x02);
103 /// ```
104 #[must_use = "set_hp_max returns a modified PaConfig"]
105 pub const fn set_hp_max(mut self, hp_max: u8) -> PaConfig {
106 self.buf[2] = hp_max & 0b111;
107 self
108 }
109
110 /// Set the power amplifier to use, low or high power.
111 ///
112 /// # Example
113 ///
114 /// ```
115 /// use stm32wlxx_hal::subghz::{PaConfig, PaSel};
116 ///
117 /// const PA_CONFIG_HP: PaConfig = PaConfig::new().set_pa(PaSel::Hp);
118 /// const PA_CONFIG_LP: PaConfig = PaConfig::new().set_pa(PaSel::Lp);
119 /// # assert_eq!(PA_CONFIG_HP.as_slice()[3], 0x00);
120 /// # assert_eq!(PA_CONFIG_LP.as_slice()[3], 0x01);
121 /// ```
122 #[must_use = "set_pa returns a modified PaConfig"]
123 pub const fn set_pa(mut self, pa: PaSel) -> PaConfig {
124 self.buf[3] = pa as u8;
125 self
126 }
127
128 /// Extracts a slice containing the packet.
129 ///
130 /// # Example
131 ///
132 /// ```
133 /// use stm32wlxx_hal::subghz::{PaConfig, PaSel};
134 ///
135 /// const PA_CONFIG: PaConfig = PaConfig::new()
136 /// .set_pa(PaSel::Hp)
137 /// .set_pa_duty_cycle(0x2)
138 /// .set_hp_max(0x3);
139 ///
140 /// assert_eq!(PA_CONFIG.as_slice(), &[0x95, 0x2, 0x03, 0x00, 0x01]);
141 /// ```
142 pub const fn as_slice(&self) -> &[u8] {
143 &self.buf
144 }
145}
146
147impl Default for PaConfig {
148 fn default() -> Self {
149 Self::new()
150 }
151}
152
153/// Power amplifier selection.
154///
155/// Argument of [`PaConfig::set_pa`].
156#[repr(u8)]
157#[derive(Debug, PartialEq, Eq, Clone, Copy)]
158pub enum PaSel {
159 /// High power amplifier.
160 Hp = 0b0,
161 /// Low power amplifier.
162 Lp = 0b1,
163}
164
165impl PartialOrd for PaSel {
166 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
167 Some(self.cmp(other))
168 }
169}
170
171impl Ord for PaSel {
172 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
173 match (self, other) {
174 (PaSel::Hp, PaSel::Hp) | (PaSel::Lp, PaSel::Lp) => core::cmp::Ordering::Equal,
175 (PaSel::Hp, PaSel::Lp) => core::cmp::Ordering::Greater,
176 (PaSel::Lp, PaSel::Hp) => core::cmp::Ordering::Less,
177 }
178 }
179}
180
181impl Default for PaSel {
182 fn default() -> Self {
183 PaSel::Lp
184 }
185}
186
187#[cfg(test)]
188mod test {
189 use super::PaSel;
190
191 #[test]
192 fn pa_sel_ord() {
193 assert!(PaSel::Lp < PaSel::Hp);
194 assert!(PaSel::Hp > PaSel::Lp);
195 }
196}
diff --git a/embassy-stm32/src/subghz/packet_params.rs b/embassy-stm32/src/subghz/packet_params.rs
deleted file mode 100644
index db1fb88d9..000000000
--- a/embassy-stm32/src/subghz/packet_params.rs
+++ /dev/null
@@ -1,534 +0,0 @@
1/// Preamble detection length for [`GenericPacketParams`].
2#[repr(u8)]
3#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
4#[cfg_attr(feature = "defmt", derive(defmt::Format))]
5pub enum PreambleDetection {
6 /// Preamble detection disabled.
7 Disabled = 0x0,
8 /// 8-bit preamble detection.
9 Bit8 = 0x4,
10 /// 16-bit preamble detection.
11 Bit16 = 0x5,
12 /// 24-bit preamble detection.
13 Bit24 = 0x6,
14 /// 32-bit preamble detection.
15 Bit32 = 0x7,
16}
17
18/// Address comparison/filtering for [`GenericPacketParams`].
19#[repr(u8)]
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub enum AddrComp {
23 /// Address comparison/filtering disabled.
24 Disabled = 0x0,
25 /// Address comparison/filtering on node address.
26 Node = 0x1,
27 /// Address comparison/filtering on node and broadcast addresses.
28 Broadcast = 0x2,
29}
30
31/// Packet header type.
32///
33/// Argument of [`GenericPacketParams::set_header_type`] and
34/// [`LoRaPacketParams::set_header_type`].
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36#[cfg_attr(feature = "defmt", derive(defmt::Format))]
37pub enum HeaderType {
38 /// Fixed; payload length and header field not added to packet.
39 Fixed,
40 /// Variable; payload length and header field added to packet.
41 Variable,
42}
43
44impl HeaderType {
45 pub(crate) const fn to_bits_generic(self) -> u8 {
46 match self {
47 HeaderType::Fixed => 0,
48 HeaderType::Variable => 1,
49 }
50 }
51
52 pub(crate) const fn to_bits_lora(self) -> u8 {
53 match self {
54 HeaderType::Fixed => 1,
55 HeaderType::Variable => 0,
56 }
57 }
58}
59
60/// CRC type definition for [`GenericPacketParams`].
61#[repr(u8)]
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63#[cfg_attr(feature = "defmt", derive(defmt::Format))]
64pub enum CrcType {
65 /// 1-byte CRC.
66 Byte1 = 0x0,
67 /// CRC disabled.
68 Disabled = 0x1,
69 /// 2-byte CRC.
70 Byte2 = 0x2,
71 /// 1-byte inverted CRC.
72 Byte1Inverted = 0x4,
73 /// 2-byte inverted CRC.
74 Byte2Inverted = 0x6,
75}
76
77/// Packet parameters for [`set_packet_params`].
78///
79/// [`set_packet_params`]: super::SubGhz::set_packet_params
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81#[cfg_attr(feature = "defmt", derive(defmt::Format))]
82pub struct GenericPacketParams {
83 buf: [u8; 10],
84}
85
86impl GenericPacketParams {
87 /// Create a new `GenericPacketParams`.
88 ///
89 /// This is the same as `default`, but in a `const` function.
90 ///
91 /// # Example
92 ///
93 /// ```
94 /// use stm32wlxx_hal::subghz::GenericPacketParams;
95 ///
96 /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new();
97 /// assert_eq!(PKT_PARAMS, GenericPacketParams::default());
98 /// ```
99 pub const fn new() -> GenericPacketParams {
100 const OPCODE: u8 = super::OpCode::SetPacketParams as u8;
101 // const variable ensure the compile always optimizes the methods
102 const NEW: GenericPacketParams = GenericPacketParams {
103 buf: [OPCODE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
104 }
105 .set_preamble_len(1)
106 .set_preamble_detection(PreambleDetection::Disabled)
107 .set_sync_word_len(0)
108 .set_addr_comp(AddrComp::Disabled)
109 .set_header_type(HeaderType::Fixed)
110 .set_payload_len(1);
111
112 NEW
113 }
114
115 /// Preamble length in number of symbols.
116 ///
117 /// Values of zero are invalid, and will automatically be set to 1.
118 ///
119 /// # Example
120 ///
121 /// ```
122 /// use stm32wlxx_hal::subghz::GenericPacketParams;
123 ///
124 /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_preamble_len(0x1234);
125 /// # assert_eq!(PKT_PARAMS.as_slice()[1], 0x12);
126 /// # assert_eq!(PKT_PARAMS.as_slice()[2], 0x34);
127 /// ```
128 #[must_use = "preamble_length returns a modified GenericPacketParams"]
129 pub const fn set_preamble_len(mut self, mut len: u16) -> GenericPacketParams {
130 if len == 0 {
131 len = 1
132 }
133 self.buf[1] = ((len >> 8) & 0xFF) as u8;
134 self.buf[2] = (len & 0xFF) as u8;
135 self
136 }
137
138 /// Preamble detection length in number of bit symbols.
139 ///
140 /// # Example
141 ///
142 /// ```
143 /// use stm32wlxx_hal::subghz::{GenericPacketParams, PreambleDetection};
144 ///
145 /// const PKT_PARAMS: GenericPacketParams =
146 /// GenericPacketParams::new().set_preamble_detection(PreambleDetection::Bit8);
147 /// # assert_eq!(PKT_PARAMS.as_slice()[3], 0x4);
148 /// ```
149 #[must_use = "set_preamble_detection returns a modified GenericPacketParams"]
150 pub const fn set_preamble_detection(mut self, pb_det: PreambleDetection) -> GenericPacketParams {
151 self.buf[3] = pb_det as u8;
152 self
153 }
154
155 /// Sync word length in number of bit symbols.
156 ///
157 /// Valid values are `0x00` - `0x40` for 0 to 64-bits respectively.
158 /// Values that exceed the maximum will saturate at `0x40`.
159 ///
160 /// # Example
161 ///
162 /// Set the sync word length to 4 bytes (16 bits).
163 ///
164 /// ```
165 /// use stm32wlxx_hal::subghz::GenericPacketParams;
166 ///
167 /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_sync_word_len(16);
168 /// # assert_eq!(PKT_PARAMS.as_slice()[4], 0x10);
169 /// ```
170 #[must_use = "set_sync_word_len returns a modified GenericPacketParams"]
171 pub const fn set_sync_word_len(mut self, len: u8) -> GenericPacketParams {
172 const MAX: u8 = 0x40;
173 if len > MAX {
174 self.buf[4] = MAX;
175 } else {
176 self.buf[4] = len;
177 }
178 self
179 }
180
181 /// Address comparison/filtering.
182 ///
183 /// # Example
184 ///
185 /// Enable address on the node address.
186 ///
187 /// ```
188 /// use stm32wlxx_hal::subghz::{AddrComp, GenericPacketParams};
189 ///
190 /// const PKT_PARAMS: GenericPacketParams =
191 /// GenericPacketParams::new().set_addr_comp(AddrComp::Node);
192 /// # assert_eq!(PKT_PARAMS.as_slice()[5], 0x01);
193 /// ```
194 #[must_use = "set_addr_comp returns a modified GenericPacketParams"]
195 pub const fn set_addr_comp(mut self, addr_comp: AddrComp) -> GenericPacketParams {
196 self.buf[5] = addr_comp as u8;
197 self
198 }
199
200 /// Header type definition.
201 ///
202 /// **Note:** The reference manual calls this packet type, but that results
203 /// in a conflicting variable name for the modulation scheme, which the
204 /// reference manual also calls packet type.
205 ///
206 /// # Example
207 ///
208 /// Set the header type to a variable length.
209 ///
210 /// ```
211 /// use stm32wlxx_hal::subghz::{GenericPacketParams, HeaderType};
212 ///
213 /// const PKT_PARAMS: GenericPacketParams =
214 /// GenericPacketParams::new().set_header_type(HeaderType::Variable);
215 /// # assert_eq!(PKT_PARAMS.as_slice()[6], 0x01);
216 /// ```
217 #[must_use = "set_header_type returns a modified GenericPacketParams"]
218 pub const fn set_header_type(mut self, header_type: HeaderType) -> GenericPacketParams {
219 self.buf[6] = header_type.to_bits_generic();
220 self
221 }
222
223 /// Set the payload length in bytes.
224 ///
225 /// # Example
226 ///
227 /// ```
228 /// use stm32wlxx_hal::subghz::GenericPacketParams;
229 ///
230 /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_payload_len(12);
231 /// # assert_eq!(PKT_PARAMS.as_slice()[7], 12);
232 /// ```
233 #[must_use = "set_payload_len returns a modified GenericPacketParams"]
234 pub const fn set_payload_len(mut self, len: u8) -> GenericPacketParams {
235 self.buf[7] = len;
236 self
237 }
238
239 /// CRC type definition.
240 ///
241 /// # Example
242 ///
243 /// ```
244 /// use stm32wlxx_hal::subghz::{CrcType, GenericPacketParams};
245 ///
246 /// const PKT_PARAMS: GenericPacketParams =
247 /// GenericPacketParams::new().set_crc_type(CrcType::Byte2Inverted);
248 /// # assert_eq!(PKT_PARAMS.as_slice()[8], 0x6);
249 /// ```
250 #[must_use = "set_payload_len returns a modified GenericPacketParams"]
251 pub const fn set_crc_type(mut self, crc_type: CrcType) -> GenericPacketParams {
252 self.buf[8] = crc_type as u8;
253 self
254 }
255
256 /// Whitening enable.
257 ///
258 /// # Example
259 ///
260 /// Enable whitening.
261 ///
262 /// ```
263 /// use stm32wlxx_hal::subghz::GenericPacketParams;
264 ///
265 /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_whitening_enable(true);
266 /// # assert_eq!(PKT_PARAMS.as_slice()[9], 1);
267 /// ```
268 #[must_use = "set_whitening_enable returns a modified GenericPacketParams"]
269 pub const fn set_whitening_enable(mut self, en: bool) -> GenericPacketParams {
270 self.buf[9] = en as u8;
271 self
272 }
273
274 /// Extracts a slice containing the packet.
275 ///
276 /// # Example
277 ///
278 /// ```
279 /// use stm32wlxx_hal::subghz::{
280 /// AddrComp, CrcType, GenericPacketParams, HeaderType, PreambleDetection,
281 /// };
282 ///
283 /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new()
284 /// .set_preamble_len(8)
285 /// .set_preamble_detection(PreambleDetection::Disabled)
286 /// .set_sync_word_len(2)
287 /// .set_addr_comp(AddrComp::Disabled)
288 /// .set_header_type(HeaderType::Fixed)
289 /// .set_payload_len(128)
290 /// .set_crc_type(CrcType::Byte2)
291 /// .set_whitening_enable(true);
292 ///
293 /// assert_eq!(
294 /// PKT_PARAMS.as_slice(),
295 /// &[0x8C, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x80, 0x02, 0x01]
296 /// );
297 /// ```
298 pub const fn as_slice(&self) -> &[u8] {
299 &self.buf
300 }
301}
302
303impl Default for GenericPacketParams {
304 fn default() -> Self {
305 Self::new()
306 }
307}
308
309/// Packet parameters for [`set_lora_packet_params`].
310///
311/// [`set_lora_packet_params`]: super::SubGhz::set_lora_packet_params
312#[derive(Debug, Clone, Copy, PartialEq, Eq)]
313pub struct LoRaPacketParams {
314 buf: [u8; 7],
315}
316
317impl LoRaPacketParams {
318 /// Create a new `LoRaPacketParams`.
319 ///
320 /// This is the same as `default`, but in a `const` function.
321 ///
322 /// # Example
323 ///
324 /// ```
325 /// use stm32wlxx_hal::subghz::LoRaPacketParams;
326 ///
327 /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new();
328 /// assert_eq!(PKT_PARAMS, LoRaPacketParams::default());
329 /// ```
330 pub const fn new() -> LoRaPacketParams {
331 const OPCODE: u8 = super::OpCode::SetPacketParams as u8;
332 // const variable ensure the compile always optimizes the methods
333 const NEW: LoRaPacketParams = LoRaPacketParams {
334 buf: [OPCODE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
335 }
336 .set_preamble_len(1)
337 .set_header_type(HeaderType::Fixed)
338 .set_payload_len(1)
339 .set_crc_en(true)
340 .set_invert_iq(false);
341
342 NEW
343 }
344
345 /// Preamble length in number of symbols.
346 ///
347 /// Values of zero are invalid, and will automatically be set to 1.
348 ///
349 /// # Example
350 ///
351 /// ```
352 /// use stm32wlxx_hal::subghz::LoRaPacketParams;
353 ///
354 /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_preamble_len(0x1234);
355 /// # assert_eq!(PKT_PARAMS.as_slice()[1], 0x12);
356 /// # assert_eq!(PKT_PARAMS.as_slice()[2], 0x34);
357 /// ```
358 #[must_use = "preamble_length returns a modified LoRaPacketParams"]
359 pub const fn set_preamble_len(mut self, mut len: u16) -> LoRaPacketParams {
360 if len == 0 {
361 len = 1
362 }
363 self.buf[1] = ((len >> 8) & 0xFF) as u8;
364 self.buf[2] = (len & 0xFF) as u8;
365 self
366 }
367
368 /// Header type (fixed or variable).
369 ///
370 /// # Example
371 ///
372 /// Set the payload type to a fixed length.
373 ///
374 /// ```
375 /// use stm32wlxx_hal::subghz::{HeaderType, LoRaPacketParams};
376 ///
377 /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_header_type(HeaderType::Fixed);
378 /// # assert_eq!(PKT_PARAMS.as_slice()[3], 0x01);
379 /// ```
380 #[must_use = "set_header_type returns a modified LoRaPacketParams"]
381 pub const fn set_header_type(mut self, header_type: HeaderType) -> LoRaPacketParams {
382 self.buf[3] = header_type.to_bits_lora();
383 self
384 }
385
386 /// Set the payload length in bytes.
387 ///
388 /// # Example
389 ///
390 /// ```
391 /// use stm32wlxx_hal::subghz::LoRaPacketParams;
392 ///
393 /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_payload_len(12);
394 /// # assert_eq!(PKT_PARAMS.as_slice()[4], 12);
395 /// ```
396 #[must_use = "set_payload_len returns a modified LoRaPacketParams"]
397 pub const fn set_payload_len(mut self, len: u8) -> LoRaPacketParams {
398 self.buf[4] = len;
399 self
400 }
401
402 /// CRC enable.
403 ///
404 /// # Example
405 ///
406 /// Enable CRC.
407 ///
408 /// ```
409 /// use stm32wlxx_hal::subghz::LoRaPacketParams;
410 ///
411 /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_crc_en(true);
412 /// # assert_eq!(PKT_PARAMS.as_slice()[5], 0x1);
413 /// ```
414 #[must_use = "set_crc_en returns a modified LoRaPacketParams"]
415 pub const fn set_crc_en(mut self, en: bool) -> LoRaPacketParams {
416 self.buf[5] = en as u8;
417 self
418 }
419
420 /// IQ setup.
421 ///
422 /// # Example
423 ///
424 /// Use an inverted IQ setup.
425 ///
426 /// ```
427 /// use stm32wlxx_hal::subghz::LoRaPacketParams;
428 ///
429 /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_invert_iq(true);
430 /// # assert_eq!(PKT_PARAMS.as_slice()[6], 0x1);
431 /// ```
432 #[must_use = "set_invert_iq returns a modified LoRaPacketParams"]
433 pub const fn set_invert_iq(mut self, invert: bool) -> LoRaPacketParams {
434 self.buf[6] = invert as u8;
435 self
436 }
437
438 /// Extracts a slice containing the packet.
439 ///
440 /// # Example
441 ///
442 /// ```
443 /// use stm32wlxx_hal::subghz::{HeaderType, LoRaPacketParams};
444 ///
445 /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new()
446 /// .set_preamble_len(5 * 8)
447 /// .set_header_type(HeaderType::Fixed)
448 /// .set_payload_len(64)
449 /// .set_crc_en(true)
450 /// .set_invert_iq(true);
451 ///
452 /// assert_eq!(
453 /// PKT_PARAMS.as_slice(),
454 /// &[0x8C, 0x00, 0x28, 0x01, 0x40, 0x01, 0x01]
455 /// );
456 /// ```
457 pub const fn as_slice(&self) -> &[u8] {
458 &self.buf
459 }
460}
461
462impl Default for LoRaPacketParams {
463 fn default() -> Self {
464 Self::new()
465 }
466}
467
468/// Packet parameters for [`set_bpsk_packet_params`].
469///
470/// [`set_bpsk_packet_params`]: super::SubGhz::set_bpsk_packet_params
471#[derive(Debug, Clone, Copy, PartialEq, Eq)]
472#[cfg_attr(feature = "defmt", derive(defmt::Format))]
473pub struct BpskPacketParams {
474 buf: [u8; 2],
475}
476
477impl BpskPacketParams {
478 /// Create a new `BpskPacketParams`.
479 ///
480 /// This is the same as `default`, but in a `const` function.
481 ///
482 /// # Example
483 ///
484 /// ```
485 /// use stm32wlxx_hal::subghz::BpskPacketParams;
486 ///
487 /// const PKT_PARAMS: BpskPacketParams = BpskPacketParams::new();
488 /// assert_eq!(PKT_PARAMS, BpskPacketParams::default());
489 /// ```
490 pub const fn new() -> BpskPacketParams {
491 BpskPacketParams {
492 buf: [super::OpCode::SetPacketParams as u8, 0x00],
493 }
494 }
495
496 /// Set the payload length in bytes.
497 ///
498 /// The length includes preamble, sync word, device ID, and CRC.
499 ///
500 /// # Example
501 ///
502 /// ```
503 /// use stm32wlxx_hal::subghz::BpskPacketParams;
504 ///
505 /// const PKT_PARAMS: BpskPacketParams = BpskPacketParams::new().set_payload_len(12);
506 /// # assert_eq!(PKT_PARAMS.as_slice()[1], 12);
507 /// ```
508 #[must_use = "set_payload_len returns a modified BpskPacketParams"]
509 pub const fn set_payload_len(mut self, len: u8) -> BpskPacketParams {
510 self.buf[1] = len;
511 self
512 }
513
514 /// Extracts a slice containing the packet.
515 ///
516 /// # Example
517 ///
518 /// ```
519 /// use stm32wlxx_hal::subghz::{BpskPacketParams, HeaderType};
520 ///
521 /// const PKT_PARAMS: BpskPacketParams = BpskPacketParams::new().set_payload_len(24);
522 ///
523 /// assert_eq!(PKT_PARAMS.as_slice(), &[0x8C, 24]);
524 /// ```
525 pub const fn as_slice(&self) -> &[u8] {
526 &self.buf
527 }
528}
529
530impl Default for BpskPacketParams {
531 fn default() -> Self {
532 Self::new()
533 }
534}
diff --git a/embassy-stm32/src/subghz/packet_status.rs b/embassy-stm32/src/subghz/packet_status.rs
deleted file mode 100644
index b3acd73ce..000000000
--- a/embassy-stm32/src/subghz/packet_status.rs
+++ /dev/null
@@ -1,282 +0,0 @@
1use super::{Ratio, Status};
2
3/// (G)FSK packet status.
4///
5/// Returned by [`fsk_packet_status`].
6///
7/// [`fsk_packet_status`]: super::SubGhz::fsk_packet_status
8#[derive(Clone, Copy, PartialEq, Eq)]
9pub struct FskPacketStatus {
10 buf: [u8; 4],
11}
12
13impl From<[u8; 4]> for FskPacketStatus {
14 fn from(buf: [u8; 4]) -> Self {
15 FskPacketStatus { buf }
16 }
17}
18
19impl FskPacketStatus {
20 /// Get the status.
21 ///
22 /// # Example
23 ///
24 /// ```
25 /// use stm32wlxx_hal::subghz::{CmdStatus, FskPacketStatus, Status, StatusMode};
26 ///
27 /// let example_data_from_radio: [u8; 4] = [0x54, 0, 0, 0];
28 /// let pkt_status: FskPacketStatus = FskPacketStatus::from(example_data_from_radio);
29 /// let status: Status = pkt_status.status();
30 /// assert_eq!(status.mode(), Ok(StatusMode::Rx));
31 /// assert_eq!(status.cmd(), Ok(CmdStatus::Avaliable));
32 /// ```
33 pub const fn status(&self) -> Status {
34 Status::from_raw(self.buf[0])
35 }
36
37 /// Returns `true` if a preamble error occurred.
38 pub const fn preamble_err(&self) -> bool {
39 (self.buf[1] & (1 << 7)) != 0
40 }
41
42 /// Returns `true` if a synchronization error occurred.
43 pub const fn sync_err(&self) -> bool {
44 (self.buf[1] & (1 << 6)) != 0
45 }
46
47 /// Returns `true` if an address error occurred.
48 pub const fn addr_err(&self) -> bool {
49 (self.buf[1] & (1 << 5)) != 0
50 }
51
52 /// Returns `true` if an CRC error occurred.
53 pub const fn crc_err(&self) -> bool {
54 (self.buf[1] & (1 << 4)) != 0
55 }
56
57 /// Returns `true` if a length error occurred.
58 pub const fn length_err(&self) -> bool {
59 (self.buf[1] & (1 << 3)) != 0
60 }
61
62 /// Returns `true` if an abort error occurred.
63 pub const fn abort_err(&self) -> bool {
64 (self.buf[1] & (1 << 2)) != 0
65 }
66
67 /// Returns `true` if a packet is received.
68 pub const fn pkt_received(&self) -> bool {
69 (self.buf[1] & (1 << 1)) != 0
70 }
71
72 /// Returns `true` when a packet has been sent.
73 pub const fn pkt_sent(&self) -> bool {
74 (self.buf[1] & 1) != 0
75 }
76
77 /// Returns `true` if any error occurred.
78 pub const fn any_err(&self) -> bool {
79 (self.buf[1] & 0xFC) != 0
80 }
81
82 /// RSSI level when the synchronization address is detected.
83 ///
84 /// Units are in dBm.
85 ///
86 /// # Example
87 ///
88 /// ```
89 /// use stm32wlxx_hal::{subghz::FskPacketStatus, Ratio};
90 ///
91 /// let example_data_from_radio: [u8; 4] = [0, 0, 80, 0];
92 /// let pkt_status: FskPacketStatus = FskPacketStatus::from(example_data_from_radio);
93 /// assert_eq!(pkt_status.rssi_sync().to_integer(), -40);
94 /// ```
95 pub fn rssi_sync(&self) -> Ratio<i16> {
96 Ratio::new_raw(i16::from(self.buf[2]), -2)
97 }
98
99 /// Return the RSSI level over the received packet.
100 ///
101 /// Units are in dBm.
102 ///
103 /// # Example
104 ///
105 /// ```
106 /// use stm32wlxx_hal::{subghz::FskPacketStatus, Ratio};
107 ///
108 /// let example_data_from_radio: [u8; 4] = [0, 0, 0, 100];
109 /// let pkt_status: FskPacketStatus = FskPacketStatus::from(example_data_from_radio);
110 /// assert_eq!(pkt_status.rssi_avg().to_integer(), -50);
111 /// ```
112 pub fn rssi_avg(&self) -> Ratio<i16> {
113 Ratio::new_raw(i16::from(self.buf[3]), -2)
114 }
115}
116
117#[cfg(feature = "defmt")]
118impl defmt::Format for FskPacketStatus {
119 fn format(&self, fmt: defmt::Formatter) {
120 defmt::write!(
121 fmt,
122 r#"FskPacketStatus {{
123 status: {},
124 preamble_err: {},
125 sync_err: {},
126 addr_err: {},
127 crc_err: {},
128 length_err: {},
129 abort_err: {},
130 pkt_received: {},
131 pkt_sent: {},
132 rssi_sync: {},
133 rssi_avg: {},
134}}"#,
135 self.status(),
136 self.preamble_err(),
137 self.sync_err(),
138 self.addr_err(),
139 self.crc_err(),
140 self.length_err(),
141 self.abort_err(),
142 self.pkt_received(),
143 self.pkt_sent(),
144 self.rssi_sync().to_integer(),
145 self.rssi_avg().to_integer()
146 )
147 }
148}
149
150impl core::fmt::Debug for FskPacketStatus {
151 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
152 f.debug_struct("FskPacketStatus")
153 .field("status", &self.status())
154 .field("preamble_err", &self.preamble_err())
155 .field("sync_err", &self.sync_err())
156 .field("addr_err", &self.addr_err())
157 .field("crc_err", &self.crc_err())
158 .field("length_err", &self.length_err())
159 .field("abort_err", &self.abort_err())
160 .field("pkt_received", &self.pkt_received())
161 .field("pkt_sent", &self.pkt_sent())
162 .field("rssi_sync", &self.rssi_sync().to_integer())
163 .field("rssi_avg", &self.rssi_avg().to_integer())
164 .finish()
165 }
166}
167
168/// (G)FSK packet status.
169///
170/// Returned by [`lora_packet_status`].
171///
172/// [`lora_packet_status`]: super::SubGhz::lora_packet_status
173#[derive(Clone, Copy, PartialEq, Eq)]
174pub struct LoRaPacketStatus {
175 buf: [u8; 4],
176}
177
178impl From<[u8; 4]> for LoRaPacketStatus {
179 fn from(buf: [u8; 4]) -> Self {
180 LoRaPacketStatus { buf }
181 }
182}
183
184impl LoRaPacketStatus {
185 /// Get the status.
186 ///
187 /// # Example
188 ///
189 /// ```
190 /// use stm32wlxx_hal::subghz::{CmdStatus, LoRaPacketStatus, Status, StatusMode};
191 ///
192 /// let example_data_from_radio: [u8; 4] = [0x54, 0, 0, 0];
193 /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio);
194 /// let status: Status = pkt_status.status();
195 /// assert_eq!(status.mode(), Ok(StatusMode::Rx));
196 /// assert_eq!(status.cmd(), Ok(CmdStatus::Avaliable));
197 /// ```
198 pub const fn status(&self) -> Status {
199 Status::from_raw(self.buf[0])
200 }
201
202 /// Average RSSI level over the received packet.
203 ///
204 /// Units are in dBm.
205 ///
206 /// # Example
207 ///
208 /// ```
209 /// use stm32wlxx_hal::{subghz::LoRaPacketStatus, Ratio};
210 ///
211 /// let example_data_from_radio: [u8; 4] = [0, 80, 0, 0];
212 /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio);
213 /// assert_eq!(pkt_status.rssi_pkt().to_integer(), -40);
214 /// ```
215 pub fn rssi_pkt(&self) -> Ratio<i16> {
216 Ratio::new_raw(i16::from(self.buf[1]), -2)
217 }
218
219 /// Estimation of SNR over the received packet.
220 ///
221 /// Units are in dB.
222 ///
223 /// # Example
224 ///
225 /// ```
226 /// use stm32wlxx_hal::{subghz::LoRaPacketStatus, Ratio};
227 ///
228 /// let example_data_from_radio: [u8; 4] = [0, 0, 40, 0];
229 /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio);
230 /// assert_eq!(pkt_status.snr_pkt().to_integer(), 10);
231 /// ```
232 pub fn snr_pkt(&self) -> Ratio<i16> {
233 Ratio::new_raw(i16::from(self.buf[2]), 4)
234 }
235
236 /// Estimation of RSSI level of the LoRa signal after despreading.
237 ///
238 /// Units are in dBm.
239 ///
240 /// # Example
241 ///
242 /// ```
243 /// use stm32wlxx_hal::{subghz::LoRaPacketStatus, Ratio};
244 ///
245 /// let example_data_from_radio: [u8; 4] = [0, 0, 0, 80];
246 /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio);
247 /// assert_eq!(pkt_status.signal_rssi_pkt().to_integer(), -40);
248 /// ```
249 pub fn signal_rssi_pkt(&self) -> Ratio<i16> {
250 Ratio::new_raw(i16::from(self.buf[3]), -2)
251 }
252}
253
254#[cfg(feature = "defmt")]
255impl defmt::Format for LoRaPacketStatus {
256 fn format(&self, fmt: defmt::Formatter) {
257 defmt::write!(
258 fmt,
259 r#"LoRaPacketStatus {{
260 status: {},
261 rssi_pkt: {},
262 snr_pkt: {},
263 signal_rssi_pkt: {},
264}}"#,
265 self.status(),
266 self.rssi_pkt().to_integer(),
267 self.snr_pkt().to_integer(),
268 self.signal_rssi_pkt().to_integer(),
269 )
270 }
271}
272
273impl core::fmt::Debug for LoRaPacketStatus {
274 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
275 f.debug_struct("LoRaPacketStatus")
276 .field("status", &self.status())
277 .field("rssi_pkt", &self.rssi_pkt().to_integer())
278 .field("snr_pkt", &self.snr_pkt().to_integer())
279 .field("signal_rssi_pkt", &self.signal_rssi_pkt().to_integer())
280 .finish()
281 }
282}
diff --git a/embassy-stm32/src/subghz/packet_type.rs b/embassy-stm32/src/subghz/packet_type.rs
deleted file mode 100644
index 88c62bb6a..000000000
--- a/embassy-stm32/src/subghz/packet_type.rs
+++ /dev/null
@@ -1,44 +0,0 @@
1/// Packet type definition.
2///
3/// Argument of [`set_packet_type`]
4///
5/// [`set_packet_type`]: super::SubGhz::set_packet_type
6#[repr(u8)]
7#[derive(Debug, PartialEq, Eq, Clone, Copy)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9pub enum PacketType {
10 /// FSK (frequency shift keying) generic packet type.
11 Fsk = 0,
12 /// LoRa (long range) packet type.
13 LoRa = 1,
14 /// BPSK (binary phase shift keying) packet type.
15 Bpsk = 2,
16 /// MSK (minimum shift keying) generic packet type.
17 Msk = 3,
18}
19
20impl PacketType {
21 /// Create a new `PacketType` from bits.
22 ///
23 /// # Example
24 ///
25 /// ```
26 /// use stm32wlxx_hal::subghz::PacketType;
27 ///
28 /// assert_eq!(PacketType::from_raw(0), Ok(PacketType::Fsk));
29 /// assert_eq!(PacketType::from_raw(1), Ok(PacketType::LoRa));
30 /// assert_eq!(PacketType::from_raw(2), Ok(PacketType::Bpsk));
31 /// assert_eq!(PacketType::from_raw(3), Ok(PacketType::Msk));
32 /// // Other values are reserved
33 /// assert_eq!(PacketType::from_raw(4), Err(4));
34 /// ```
35 pub const fn from_raw(bits: u8) -> Result<PacketType, u8> {
36 match bits {
37 0 => Ok(PacketType::Fsk),
38 1 => Ok(PacketType::LoRa),
39 2 => Ok(PacketType::Bpsk),
40 3 => Ok(PacketType::Msk),
41 _ => Err(bits),
42 }
43 }
44}
diff --git a/embassy-stm32/src/subghz/pkt_ctrl.rs b/embassy-stm32/src/subghz/pkt_ctrl.rs
deleted file mode 100644
index 265833e35..000000000
--- a/embassy-stm32/src/subghz/pkt_ctrl.rs
+++ /dev/null
@@ -1,247 +0,0 @@
1/// Generic packet infinite sequence selection.
2///
3/// Argument of [`PktCtrl::set_inf_seq_sel`].
4#[derive(Debug, PartialEq, Eq, Clone, Copy)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6pub enum InfSeqSel {
7 /// Preamble `0x5555`.
8 Five = 0b00,
9 /// Preamble `0x0000`.
10 Zero = 0b01,
11 /// Preamble `0xFFFF`.
12 One = 0b10,
13 /// PRBS9.
14 Prbs9 = 0b11,
15}
16
17impl Default for InfSeqSel {
18 fn default() -> Self {
19 InfSeqSel::Five
20 }
21}
22
23/// Generic packet control.
24///
25/// Argument of [`set_pkt_ctrl`](super::SubGhz::set_pkt_ctrl).
26#[derive(Debug, PartialEq, Eq, Clone, Copy)]
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28pub struct PktCtrl {
29 val: u8,
30}
31
32impl PktCtrl {
33 /// Reset value of the packet control register.
34 pub const RESET: PktCtrl = PktCtrl { val: 0x21 };
35
36 /// Create a new [`PktCtrl`] structure from a raw value.
37 ///
38 /// Reserved bits will be masked.
39 pub const fn from_raw(raw: u8) -> Self {
40 Self { val: raw & 0x3F }
41 }
42
43 /// Get the raw value of the [`PktCtrl`] register.
44 pub const fn as_bits(&self) -> u8 {
45 self.val
46 }
47
48 /// Generic packet synchronization word detection enable.
49 ///
50 /// # Example
51 ///
52 /// ```
53 /// use stm32wlxx_hal::subghz::PktCtrl;
54 ///
55 /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_sync_det_en(true);
56 /// ```
57 #[must_use = "set_sync_det_en returns a modified PktCtrl"]
58 pub const fn set_sync_det_en(mut self, en: bool) -> PktCtrl {
59 if en {
60 self.val |= 1 << 5;
61 } else {
62 self.val &= !(1 << 5);
63 }
64 self
65 }
66
67 /// Returns `true` if generic packet synchronization word detection is
68 /// enabled.
69 ///
70 /// # Example
71 ///
72 /// ```
73 /// use stm32wlxx_hal::subghz::PktCtrl;
74 ///
75 /// let pc: PktCtrl = PktCtrl::RESET;
76 /// assert_eq!(pc.sync_det_en(), true);
77 /// let pc: PktCtrl = pc.set_sync_det_en(false);
78 /// assert_eq!(pc.sync_det_en(), false);
79 /// let pc: PktCtrl = pc.set_sync_det_en(true);
80 /// assert_eq!(pc.sync_det_en(), true);
81 /// ```
82 pub const fn sync_det_en(&self) -> bool {
83 self.val & (1 << 5) != 0
84 }
85
86 /// Generic packet continuous transmit enable.
87 ///
88 /// # Example
89 ///
90 /// ```
91 /// use stm32wlxx_hal::subghz::PktCtrl;
92 ///
93 /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_cont_tx_en(true);
94 /// ```
95 #[must_use = "set_cont_tx_en returns a modified PktCtrl"]
96 pub const fn set_cont_tx_en(mut self, en: bool) -> PktCtrl {
97 if en {
98 self.val |= 1 << 4;
99 } else {
100 self.val &= !(1 << 4);
101 }
102 self
103 }
104
105 /// Returns `true` if generic packet continuous transmit is enabled.
106 ///
107 /// # Example
108 ///
109 /// ```
110 /// use stm32wlxx_hal::subghz::PktCtrl;
111 ///
112 /// let pc: PktCtrl = PktCtrl::RESET;
113 /// assert_eq!(pc.cont_tx_en(), false);
114 /// let pc: PktCtrl = pc.set_cont_tx_en(true);
115 /// assert_eq!(pc.cont_tx_en(), true);
116 /// let pc: PktCtrl = pc.set_cont_tx_en(false);
117 /// assert_eq!(pc.cont_tx_en(), false);
118 /// ```
119 pub const fn cont_tx_en(&self) -> bool {
120 self.val & (1 << 4) != 0
121 }
122
123 /// Set the continuous sequence type.
124 #[must_use = "set_inf_seq_sel returns a modified PktCtrl"]
125 pub const fn set_inf_seq_sel(mut self, sel: InfSeqSel) -> PktCtrl {
126 self.val &= !(0b11 << 2);
127 self.val |= (sel as u8) << 2;
128 self
129 }
130
131 /// Get the continuous sequence type.
132 ///
133 /// # Example
134 ///
135 /// ```
136 /// use stm32wlxx_hal::subghz::{InfSeqSel, PktCtrl};
137 ///
138 /// let pc: PktCtrl = PktCtrl::RESET;
139 /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Five);
140 ///
141 /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::Zero);
142 /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Zero);
143 ///
144 /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::One);
145 /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::One);
146 ///
147 /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::Prbs9);
148 /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Prbs9);
149 ///
150 /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::Five);
151 /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Five);
152 /// ```
153 pub const fn inf_seq_sel(&self) -> InfSeqSel {
154 match (self.val >> 2) & 0b11 {
155 0b00 => InfSeqSel::Five,
156 0b01 => InfSeqSel::Zero,
157 0b10 => InfSeqSel::One,
158 _ => InfSeqSel::Prbs9,
159 }
160 }
161
162 /// Enable infinite sequence generation.
163 ///
164 /// # Example
165 ///
166 /// ```
167 /// use stm32wlxx_hal::subghz::PktCtrl;
168 ///
169 /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_inf_seq_en(true);
170 /// ```
171 #[must_use = "set_inf_seq_en returns a modified PktCtrl"]
172 pub const fn set_inf_seq_en(mut self, en: bool) -> PktCtrl {
173 if en {
174 self.val |= 1 << 1;
175 } else {
176 self.val &= !(1 << 1);
177 }
178 self
179 }
180
181 /// Returns `true` if infinite sequence generation is enabled.
182 ///
183 /// # Example
184 ///
185 /// ```
186 /// use stm32wlxx_hal::subghz::PktCtrl;
187 ///
188 /// let pc: PktCtrl = PktCtrl::RESET;
189 /// assert_eq!(pc.inf_seq_en(), false);
190 /// let pc: PktCtrl = pc.set_inf_seq_en(true);
191 /// assert_eq!(pc.inf_seq_en(), true);
192 /// let pc: PktCtrl = pc.set_inf_seq_en(false);
193 /// assert_eq!(pc.inf_seq_en(), false);
194 /// ```
195 pub const fn inf_seq_en(&self) -> bool {
196 self.val & (1 << 1) != 0
197 }
198
199 /// Set the value of bit-8 (9th bit) for generic packet whitening.
200 ///
201 /// # Example
202 ///
203 /// ```
204 /// use stm32wlxx_hal::subghz::PktCtrl;
205 ///
206 /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_whitening_init(true);
207 /// ```
208 #[must_use = "set_whitening_init returns a modified PktCtrl"]
209 pub const fn set_whitening_init(mut self, val: bool) -> PktCtrl {
210 if val {
211 self.val |= 1;
212 } else {
213 self.val &= !1;
214 }
215 self
216 }
217
218 /// Returns `true` if bit-8 of the generic packet whitening is set.
219 ///
220 /// # Example
221 ///
222 /// ```
223 /// use stm32wlxx_hal::subghz::PktCtrl;
224 ///
225 /// let pc: PktCtrl = PktCtrl::RESET;
226 /// assert_eq!(pc.whitening_init(), true);
227 /// let pc: PktCtrl = pc.set_whitening_init(false);
228 /// assert_eq!(pc.whitening_init(), false);
229 /// let pc: PktCtrl = pc.set_whitening_init(true);
230 /// assert_eq!(pc.whitening_init(), true);
231 /// ```
232 pub const fn whitening_init(&self) -> bool {
233 self.val & 0b1 != 0
234 }
235}
236
237impl From<PktCtrl> for u8 {
238 fn from(pc: PktCtrl) -> Self {
239 pc.val
240 }
241}
242
243impl Default for PktCtrl {
244 fn default() -> Self {
245 Self::RESET
246 }
247}
diff --git a/embassy-stm32/src/subghz/pmode.rs b/embassy-stm32/src/subghz/pmode.rs
deleted file mode 100644
index 0c07f3195..000000000
--- a/embassy-stm32/src/subghz/pmode.rs
+++ /dev/null
@@ -1,27 +0,0 @@
1/// RX gain power modes.
2///
3/// Argument of [`set_rx_gain`].
4///
5/// [`set_rx_gain`]: super::SubGhz::set_rx_gain
6#[repr(u8)]
7#[derive(Debug, PartialEq, Eq, Clone, Copy)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9pub enum PMode {
10 /// Power saving mode.
11 ///
12 /// Reduces sensitivity.
13 #[allow(clippy::identity_op)]
14 PowerSaving = (0x25 << 2) | 0b00,
15 /// Boost mode level 1.
16 ///
17 /// Improves sensitivity at detriment of power consumption.
18 Boost1 = (0x25 << 2) | 0b01,
19 /// Boost mode level 2.
20 ///
21 /// Improves a set further sensitivity at detriment of power consumption.
22 Boost2 = (0x25 << 2) | 0b10,
23 /// Boost mode.
24 ///
25 /// Best receiver sensitivity.
26 Boost = (0x25 << 2) | 0b11,
27}
diff --git a/embassy-stm32/src/subghz/pwr_ctrl.rs b/embassy-stm32/src/subghz/pwr_ctrl.rs
deleted file mode 100644
index 974bddebb..000000000
--- a/embassy-stm32/src/subghz/pwr_ctrl.rs
+++ /dev/null
@@ -1,160 +0,0 @@
1/// Power-supply current limit.
2///
3/// Argument of [`PwrCtrl::set_current_lim`].
4#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Copy)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6#[repr(u8)]
7pub enum CurrentLim {
8 /// 25 mA
9 Milli25 = 0x0,
10 /// 50 mA (default)
11 Milli50 = 0x1,
12 /// 100 mA
13 Milli100 = 0x2,
14 /// 200 mA
15 Milli200 = 0x3,
16}
17
18impl CurrentLim {
19 /// Get the SMPS drive value as milliamps.
20 ///
21 /// # Example
22 ///
23 /// ```
24 /// use stm32wlxx_hal::subghz::CurrentLim;
25 ///
26 /// assert_eq!(CurrentLim::Milli25.as_milliamps(), 25);
27 /// assert_eq!(CurrentLim::Milli50.as_milliamps(), 50);
28 /// assert_eq!(CurrentLim::Milli100.as_milliamps(), 100);
29 /// assert_eq!(CurrentLim::Milli200.as_milliamps(), 200);
30 /// ```
31 pub const fn as_milliamps(&self) -> u8 {
32 match self {
33 CurrentLim::Milli25 => 25,
34 CurrentLim::Milli50 => 50,
35 CurrentLim::Milli100 => 100,
36 CurrentLim::Milli200 => 200,
37 }
38 }
39}
40
41impl Default for CurrentLim {
42 fn default() -> Self {
43 CurrentLim::Milli50
44 }
45}
46
47/// Power control.
48///
49/// Argument of [`set_bit_sync`](super::SubGhz::set_bit_sync).
50#[derive(Debug, PartialEq, Eq, Clone, Copy)]
51#[cfg_attr(feature = "defmt", derive(defmt::Format))]
52pub struct PwrCtrl {
53 val: u8,
54}
55
56impl PwrCtrl {
57 /// Power control register reset value.
58 pub const RESET: PwrCtrl = PwrCtrl { val: 0x50 };
59
60 /// Create a new [`PwrCtrl`] structure from a raw value.
61 ///
62 /// Reserved bits will be masked.
63 pub const fn from_raw(raw: u8) -> Self {
64 Self { val: raw & 0x70 }
65 }
66
67 /// Get the raw value of the [`PwrCtrl`] register.
68 pub const fn as_bits(&self) -> u8 {
69 self.val
70 }
71
72 /// Set the current limiter enable.
73 ///
74 /// # Example
75 ///
76 /// ```
77 /// use stm32wlxx_hal::subghz::PwrCtrl;
78 ///
79 /// const PWR_CTRL: PwrCtrl = PwrCtrl::RESET.set_current_lim_en(true);
80 /// # assert_eq!(u8::from(PWR_CTRL), 0x50u8);
81 /// ```
82 #[must_use = "set_current_lim_en returns a modified PwrCtrl"]
83 pub const fn set_current_lim_en(mut self, en: bool) -> PwrCtrl {
84 if en {
85 self.val |= 1 << 6;
86 } else {
87 self.val &= !(1 << 6);
88 }
89 self
90 }
91
92 /// Returns `true` if current limiting is enabled
93 ///
94 /// # Example
95 ///
96 /// ```
97 /// use stm32wlxx_hal::subghz::PwrCtrl;
98 ///
99 /// let pc: PwrCtrl = PwrCtrl::RESET;
100 /// assert_eq!(pc.current_limit_en(), true);
101 /// let pc: PwrCtrl = pc.set_current_lim_en(false);
102 /// assert_eq!(pc.current_limit_en(), false);
103 /// let pc: PwrCtrl = pc.set_current_lim_en(true);
104 /// assert_eq!(pc.current_limit_en(), true);
105 /// ```
106 pub const fn current_limit_en(&self) -> bool {
107 self.val & (1 << 6) != 0
108 }
109
110 /// Set the current limit.
111 #[must_use = "set_current_lim returns a modified PwrCtrl"]
112 pub const fn set_current_lim(mut self, lim: CurrentLim) -> PwrCtrl {
113 self.val &= !(0x30);
114 self.val |= (lim as u8) << 4;
115 self
116 }
117
118 /// Get the current limit.
119 ///
120 /// # Example
121 ///
122 /// ```
123 /// use stm32wlxx_hal::subghz::{CurrentLim, PwrCtrl};
124 ///
125 /// let pc: PwrCtrl = PwrCtrl::RESET;
126 /// assert_eq!(pc.current_lim(), CurrentLim::Milli50);
127 ///
128 /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli25);
129 /// assert_eq!(pc.current_lim(), CurrentLim::Milli25);
130 ///
131 /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli50);
132 /// assert_eq!(pc.current_lim(), CurrentLim::Milli50);
133 ///
134 /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli100);
135 /// assert_eq!(pc.current_lim(), CurrentLim::Milli100);
136 ///
137 /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli200);
138 /// assert_eq!(pc.current_lim(), CurrentLim::Milli200);
139 /// ```
140 pub const fn current_lim(&self) -> CurrentLim {
141 match (self.val >> 4) & 0b11 {
142 0x0 => CurrentLim::Milli25,
143 0x1 => CurrentLim::Milli50,
144 0x2 => CurrentLim::Milli100,
145 _ => CurrentLim::Milli200,
146 }
147 }
148}
149
150impl From<PwrCtrl> for u8 {
151 fn from(bs: PwrCtrl) -> Self {
152 bs.val
153 }
154}
155
156impl Default for PwrCtrl {
157 fn default() -> Self {
158 Self::RESET
159 }
160}
diff --git a/embassy-stm32/src/subghz/reg_mode.rs b/embassy-stm32/src/subghz/reg_mode.rs
deleted file mode 100644
index b83226954..000000000
--- a/embassy-stm32/src/subghz/reg_mode.rs
+++ /dev/null
@@ -1,18 +0,0 @@
1/// Radio power supply selection.
2#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
3#[cfg_attr(feature = "defmt", derive(defmt::Format))]
4#[repr(u8)]
5pub enum RegMode {
6 /// Linear dropout regulator
7 Ldo = 0b0,
8 /// Switch mode power supply.
9 ///
10 /// Used in standby with HSE32, FS, RX, and TX modes.
11 Smps = 0b1,
12}
13
14impl Default for RegMode {
15 fn default() -> Self {
16 RegMode::Ldo
17 }
18}
diff --git a/embassy-stm32/src/subghz/rf_frequency.rs b/embassy-stm32/src/subghz/rf_frequency.rs
deleted file mode 100644
index 3de2f50c4..000000000
--- a/embassy-stm32/src/subghz/rf_frequency.rs
+++ /dev/null
@@ -1,135 +0,0 @@
1/// RF frequency structure.
2///
3/// Argument of [`set_rf_frequency`].
4///
5/// [`set_rf_frequency`]: super::SubGhz::set_rf_frequency
6#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub struct RfFreq {
9 buf: [u8; 5],
10}
11
12impl RfFreq {
13 /// 915MHz, often used in Australia and North America.
14 ///
15 /// # Example
16 ///
17 /// ```
18 /// use stm32wlxx_hal::subghz::RfFreq;
19 ///
20 /// assert_eq!(RfFreq::F915.freq(), 915_000_000);
21 /// ```
22 pub const F915: RfFreq = RfFreq::from_raw(0x39_30_00_00);
23
24 /// 868MHz, often used in Europe.
25 ///
26 /// # Example
27 ///
28 /// ```
29 /// use stm32wlxx_hal::subghz::RfFreq;
30 ///
31 /// assert_eq!(RfFreq::F868.freq(), 868_000_000);
32 /// ```
33 pub const F868: RfFreq = RfFreq::from_raw(0x36_40_00_00);
34
35 /// 433MHz, often used in Europe.
36 ///
37 /// # Example
38 ///
39 /// ```
40 /// use stm32wlxx_hal::subghz::RfFreq;
41 ///
42 /// assert_eq!(RfFreq::F433.freq(), 433_000_000);
43 /// ```
44 pub const F433: RfFreq = RfFreq::from_raw(0x1B_10_00_00);
45
46 /// Create a new `RfFreq` from a raw bit value.
47 ///
48 /// The equation used to get the PLL frequency from the raw bits is:
49 ///
50 /// RF<sub>PLL</sub> = 32e6 × bits / 2<sup>25</sup>
51 ///
52 /// # Example
53 ///
54 /// ```
55 /// use stm32wlxx_hal::subghz::RfFreq;
56 ///
57 /// const FREQ: RfFreq = RfFreq::from_raw(0x39300000);
58 /// assert_eq!(FREQ, RfFreq::F915);
59 /// ```
60 pub const fn from_raw(bits: u32) -> RfFreq {
61 RfFreq {
62 buf: [
63 super::OpCode::SetRfFrequency as u8,
64 ((bits >> 24) & 0xFF) as u8,
65 ((bits >> 16) & 0xFF) as u8,
66 ((bits >> 8) & 0xFF) as u8,
67 (bits & 0xFF) as u8,
68 ],
69 }
70 }
71
72 /// Create a new `RfFreq` from a PLL frequency.
73 ///
74 /// The equation used to get the raw bits from the PLL frequency is:
75 ///
76 /// bits = RF<sub>PLL</sub> * 2<sup>25</sup> / 32e6
77 ///
78 /// # Example
79 ///
80 /// ```
81 /// use stm32wlxx_hal::subghz::RfFreq;
82 ///
83 /// const FREQ: RfFreq = RfFreq::from_frequency(915_000_000);
84 /// assert_eq!(FREQ, RfFreq::F915);
85 /// ```
86 pub const fn from_frequency(freq: u32) -> RfFreq {
87 Self::from_raw((((freq as u64) * (1 << 25)) / 32_000_000) as u32)
88 }
89
90 // Get the frequency bit value.
91 const fn as_bits(&self) -> u32 {
92 ((self.buf[1] as u32) << 24) | ((self.buf[2] as u32) << 16) | ((self.buf[3] as u32) << 8) | (self.buf[4] as u32)
93 }
94
95 /// Get the actual frequency.
96 ///
97 /// # Example
98 ///
99 /// ```
100 /// use stm32wlxx_hal::subghz::RfFreq;
101 ///
102 /// assert_eq!(RfFreq::from_raw(0x39300000).freq(), 915_000_000);
103 /// ```
104 pub fn freq(&self) -> u32 {
105 (32_000_000 * (self.as_bits() as u64) / (1 << 25)) as u32
106 }
107
108 /// Extracts a slice containing the packet.
109 ///
110 /// # Example
111 ///
112 /// ```
113 /// use stm32wlxx_hal::subghz::RfFreq;
114 ///
115 /// assert_eq!(RfFreq::F915.as_slice(), &[0x86, 0x39, 0x30, 0x00, 0x00]);
116 /// ```
117 pub const fn as_slice(&self) -> &[u8] {
118 &self.buf
119 }
120}
121
122#[cfg(test)]
123mod test {
124 use super::RfFreq;
125
126 #[test]
127 fn max() {
128 assert_eq!(RfFreq::from_raw(u32::MAX).freq(), 4_095_999_999);
129 }
130
131 #[test]
132 fn min() {
133 assert_eq!(RfFreq::from_raw(u32::MIN).freq(), 0);
134 }
135}
diff --git a/embassy-stm32/src/subghz/rx_timeout_stop.rs b/embassy-stm32/src/subghz/rx_timeout_stop.rs
deleted file mode 100644
index 1d4aaecee..000000000
--- a/embassy-stm32/src/subghz/rx_timeout_stop.rs
+++ /dev/null
@@ -1,21 +0,0 @@
1/// Receiver event which stops the RX timeout timer.
2///
3/// Used by [`set_rx_timeout_stop`].
4///
5/// [`set_rx_timeout_stop`]: super::SubGhz::set_rx_timeout_stop
6#[derive(Debug, PartialEq, Eq, Clone, Copy)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8#[repr(u8)]
9pub enum RxTimeoutStop {
10 /// Receive timeout stopped on synchronization word detection in generic
11 /// packet mode or header detection in LoRa packet mode.
12 Sync = 0b0,
13 /// Receive timeout stopped on preamble detection.
14 Preamble = 0b1,
15}
16
17impl From<RxTimeoutStop> for u8 {
18 fn from(rx_ts: RxTimeoutStop) -> Self {
19 rx_ts as u8
20 }
21}
diff --git a/embassy-stm32/src/subghz/sleep_cfg.rs b/embassy-stm32/src/subghz/sleep_cfg.rs
deleted file mode 100644
index 0a50e9704..000000000
--- a/embassy-stm32/src/subghz/sleep_cfg.rs
+++ /dev/null
@@ -1,107 +0,0 @@
1/// Startup configurations when exiting sleep mode.
2///
3/// Argument of [`SleepCfg::set_startup`].
4#[derive(Debug, PartialEq, Eq, Clone, Copy)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6#[repr(u8)]
7pub enum Startup {
8 /// Cold startup when exiting Sleep mode, configuration registers reset.
9 Cold = 0,
10 /// Warm startup when exiting Sleep mode,
11 /// configuration registers kept in retention.
12 ///
13 /// **Note:** Only the configuration of the activated modem,
14 /// before going to sleep mode, is retained.
15 /// The configuration of the other modes is lost and must be re-configured
16 /// when exiting sleep mode.
17 Warm = 1,
18}
19
20impl Default for Startup {
21 fn default() -> Self {
22 Startup::Warm
23 }
24}
25
26/// Sleep configuration.
27///
28/// Argument of [`set_sleep`].
29///
30/// [`set_sleep`]: super::SubGhz::set_sleep
31#[derive(Debug, PartialEq, Eq, Clone, Copy)]
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33pub struct SleepCfg(u8);
34
35impl SleepCfg {
36 /// Create a new `SleepCfg` structure.
37 ///
38 /// This is the same as `default`, but in a `const` function.
39 ///
40 /// The defaults are a warm startup, with RTC wakeup enabled.
41 ///
42 /// # Example
43 ///
44 /// ```
45 /// use stm32wlxx_hal::subghz::SleepCfg;
46 ///
47 /// const SLEEP_CFG: SleepCfg = SleepCfg::new();
48 /// assert_eq!(SLEEP_CFG, SleepCfg::default());
49 /// # assert_eq!(u8::from(SLEEP_CFG), 0b101);
50 /// ```
51 pub const fn new() -> SleepCfg {
52 SleepCfg(0).set_startup(Startup::Warm).set_rtc_wakeup_en(true)
53 }
54
55 /// Set the startup mode.
56 ///
57 /// # Example
58 ///
59 /// ```
60 /// use stm32wlxx_hal::subghz::{SleepCfg, Startup};
61 ///
62 /// const SLEEP_CFG: SleepCfg = SleepCfg::new().set_startup(Startup::Cold);
63 /// # assert_eq!(u8::from(SLEEP_CFG), 0b001);
64 /// # assert_eq!(u8::from(SLEEP_CFG.set_startup(Startup::Warm)), 0b101);
65 /// ```
66 pub const fn set_startup(mut self, startup: Startup) -> SleepCfg {
67 if startup as u8 == 1 {
68 self.0 |= 1 << 2
69 } else {
70 self.0 &= !(1 << 2)
71 }
72 self
73 }
74
75 /// Set the RTC wakeup enable.
76 ///
77 /// # Example
78 ///
79 /// ```
80 /// use stm32wlxx_hal::subghz::SleepCfg;
81 ///
82 /// const SLEEP_CFG: SleepCfg = SleepCfg::new().set_rtc_wakeup_en(false);
83 /// # assert_eq!(u8::from(SLEEP_CFG), 0b100);
84 /// # assert_eq!(u8::from(SLEEP_CFG.set_rtc_wakeup_en(true)), 0b101);
85 /// ```
86 #[must_use = "set_rtc_wakeup_en returns a modified SleepCfg"]
87 pub const fn set_rtc_wakeup_en(mut self, en: bool) -> SleepCfg {
88 if en {
89 self.0 |= 0b1
90 } else {
91 self.0 &= !0b1
92 }
93 self
94 }
95}
96
97impl From<SleepCfg> for u8 {
98 fn from(sc: SleepCfg) -> Self {
99 sc.0
100 }
101}
102
103impl Default for SleepCfg {
104 fn default() -> Self {
105 Self::new()
106 }
107}
diff --git a/embassy-stm32/src/subghz/smps.rs b/embassy-stm32/src/subghz/smps.rs
deleted file mode 100644
index 81615ea7b..000000000
--- a/embassy-stm32/src/subghz/smps.rs
+++ /dev/null
@@ -1,45 +0,0 @@
1/// SMPS maximum drive capability.
2///
3/// Argument of [`set_smps_drv`](super::SubGhz::set_smps_drv).
4#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Copy)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6#[repr(u8)]
7pub enum SmpsDrv {
8 /// 20 mA
9 Milli20 = 0x0,
10 /// 40 mA
11 Milli40 = 0x1,
12 /// 60 mA
13 Milli60 = 0x2,
14 /// 100 mA (default)
15 Milli100 = 0x3,
16}
17
18impl SmpsDrv {
19 /// Get the SMPS drive value as milliamps.
20 ///
21 /// # Example
22 ///
23 /// ```
24 /// use stm32wlxx_hal::subghz::SmpsDrv;
25 ///
26 /// assert_eq!(SmpsDrv::Milli20.as_milliamps(), 20);
27 /// assert_eq!(SmpsDrv::Milli40.as_milliamps(), 40);
28 /// assert_eq!(SmpsDrv::Milli60.as_milliamps(), 60);
29 /// assert_eq!(SmpsDrv::Milli100.as_milliamps(), 100);
30 /// ```
31 pub const fn as_milliamps(&self) -> u8 {
32 match self {
33 SmpsDrv::Milli20 => 20,
34 SmpsDrv::Milli40 => 40,
35 SmpsDrv::Milli60 => 60,
36 SmpsDrv::Milli100 => 100,
37 }
38 }
39}
40
41impl Default for SmpsDrv {
42 fn default() -> Self {
43 SmpsDrv::Milli100
44 }
45}
diff --git a/embassy-stm32/src/subghz/standby_clk.rs b/embassy-stm32/src/subghz/standby_clk.rs
deleted file mode 100644
index c130bbee4..000000000
--- a/embassy-stm32/src/subghz/standby_clk.rs
+++ /dev/null
@@ -1,20 +0,0 @@
1/// Clock in standby mode.
2///
3/// Used by [`set_standby`].
4///
5/// [`set_standby`]: super::SubGhz::set_standby
6#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8#[repr(u8)]
9pub enum StandbyClk {
10 /// RC 13 MHz used in standby mode.
11 Rc = 0b0,
12 /// HSE32 used in standby mode.
13 Hse = 0b1,
14}
15
16impl From<StandbyClk> for u8 {
17 fn from(sc: StandbyClk) -> Self {
18 sc as u8
19 }
20}
diff --git a/embassy-stm32/src/subghz/stats.rs b/embassy-stm32/src/subghz/stats.rs
deleted file mode 100644
index 41b7a200f..000000000
--- a/embassy-stm32/src/subghz/stats.rs
+++ /dev/null
@@ -1,184 +0,0 @@
1use super::Status;
2
3#[derive(Debug, PartialEq, Eq, Clone, Copy)]
4#[cfg_attr(feature = "defmt", derive(defmt::Format))]
5pub struct LoRaStats;
6
7impl LoRaStats {
8 pub const fn new() -> Self {
9 Self {}
10 }
11}
12
13#[derive(Debug, PartialEq, Eq, Clone, Copy)]
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15pub struct FskStats;
16
17impl FskStats {
18 pub const fn new() -> Self {
19 Self {}
20 }
21}
22
23/// Packet statistics.
24///
25/// Returned by [`fsk_stats`] and [`lora_stats`].
26///
27/// [`fsk_stats`]: super::SubGhz::fsk_stats
28/// [`lora_stats`]: super::SubGhz::lora_stats
29#[derive(Eq, PartialEq, Clone, Copy)]
30#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31pub struct Stats<ModType> {
32 status: Status,
33 pkt_rx: u16,
34 pkt_crc: u16,
35 pkt_len_or_hdr_err: u16,
36 ty: ModType,
37}
38
39impl<ModType> Stats<ModType> {
40 const fn from_buf(buf: [u8; 7], ty: ModType) -> Stats<ModType> {
41 Stats {
42 status: Status::from_raw(buf[0]),
43 pkt_rx: u16::from_be_bytes([buf[1], buf[2]]),
44 pkt_crc: u16::from_be_bytes([buf[3], buf[4]]),
45 pkt_len_or_hdr_err: u16::from_be_bytes([buf[5], buf[6]]),
46 ty,
47 }
48 }
49
50 /// Get the radio status returned with the packet statistics.
51 ///
52 /// # Example
53 ///
54 /// ```
55 /// use stm32wlxx_hal::subghz::{CmdStatus, FskStats, Stats, StatusMode};
56 ///
57 /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 0];
58 /// let stats: Stats<FskStats> = Stats::from_raw_fsk(example_data_from_radio);
59 /// assert_eq!(stats.status().mode(), Ok(StatusMode::Rx));
60 /// assert_eq!(stats.status().cmd(), Ok(CmdStatus::Avaliable));
61 /// ```
62 pub const fn status(&self) -> Status {
63 self.status
64 }
65
66 /// Number of packets received.
67 ///
68 /// # Example
69 ///
70 /// ```
71 /// use stm32wlxx_hal::subghz::{FskStats, Stats};
72 ///
73 /// let example_data_from_radio: [u8; 7] = [0x54, 0, 3, 0, 0, 0, 0];
74 /// let stats: Stats<FskStats> = Stats::from_raw_fsk(example_data_from_radio);
75 /// assert_eq!(stats.pkt_rx(), 3);
76 /// ```
77 pub const fn pkt_rx(&self) -> u16 {
78 self.pkt_rx
79 }
80
81 /// Number of packets received with a payload CRC error
82 ///
83 /// # Example
84 ///
85 /// ```
86 /// use stm32wlxx_hal::subghz::{LoRaStats, Stats};
87 ///
88 /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 1, 0, 0];
89 /// let stats: Stats<LoRaStats> = Stats::from_raw_lora(example_data_from_radio);
90 /// assert_eq!(stats.pkt_crc(), 1);
91 /// ```
92 pub const fn pkt_crc(&self) -> u16 {
93 self.pkt_crc
94 }
95}
96
97impl Stats<FskStats> {
98 /// Create a new FSK packet statistics structure from a raw buffer.
99 ///
100 /// # Example
101 ///
102 /// ```
103 /// use stm32wlxx_hal::subghz::{FskStats, Stats};
104 ///
105 /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 0];
106 /// let stats: Stats<FskStats> = Stats::from_raw_fsk(example_data_from_radio);
107 /// ```
108 pub const fn from_raw_fsk(buf: [u8; 7]) -> Stats<FskStats> {
109 Self::from_buf(buf, FskStats::new())
110 }
111
112 /// Number of packets received with a payload length error.
113 ///
114 /// # Example
115 ///
116 /// ```
117 /// use stm32wlxx_hal::subghz::{FskStats, Stats};
118 ///
119 /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 1];
120 /// let stats: Stats<FskStats> = Stats::from_raw_fsk(example_data_from_radio);
121 /// assert_eq!(stats.pkt_len_err(), 1);
122 /// ```
123 pub const fn pkt_len_err(&self) -> u16 {
124 self.pkt_len_or_hdr_err
125 }
126}
127
128impl Stats<LoRaStats> {
129 /// Create a new LoRa packet statistics structure from a raw buffer.
130 ///
131 /// # Example
132 ///
133 /// ```
134 /// use stm32wlxx_hal::subghz::{LoRaStats, Stats};
135 ///
136 /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 0];
137 /// let stats: Stats<LoRaStats> = Stats::from_raw_lora(example_data_from_radio);
138 /// ```
139 pub const fn from_raw_lora(buf: [u8; 7]) -> Stats<LoRaStats> {
140 Self::from_buf(buf, LoRaStats::new())
141 }
142
143 /// Number of packets received with a header CRC error.
144 ///
145 /// # Example
146 ///
147 /// ```
148 /// use stm32wlxx_hal::subghz::{LoRaStats, Stats};
149 ///
150 /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 1];
151 /// let stats: Stats<LoRaStats> = Stats::from_raw_lora(example_data_from_radio);
152 /// assert_eq!(stats.pkt_hdr_err(), 1);
153 /// ```
154 pub const fn pkt_hdr_err(&self) -> u16 {
155 self.pkt_len_or_hdr_err
156 }
157}
158
159impl core::fmt::Debug for Stats<FskStats> {
160 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
161 f.debug_struct("Stats")
162 .field("status", &self.status())
163 .field("pkt_rx", &self.pkt_rx())
164 .field("pkt_crc", &self.pkt_crc())
165 .field("pkt_len_err", &self.pkt_len_err())
166 .finish()
167 }
168}
169
170#[cfg(test)]
171mod test {
172 use super::super::{CmdStatus, LoRaStats, Stats, StatusMode};
173
174 #[test]
175 fn mixed() {
176 let example_data_from_radio: [u8; 7] = [0x54, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
177 let stats: Stats<LoRaStats> = Stats::from_raw_lora(example_data_from_radio);
178 assert_eq!(stats.status().mode(), Ok(StatusMode::Rx));
179 assert_eq!(stats.status().cmd(), Ok(CmdStatus::Avaliable));
180 assert_eq!(stats.pkt_rx(), 0x0102);
181 assert_eq!(stats.pkt_crc(), 0x0304);
182 assert_eq!(stats.pkt_hdr_err(), 0x0506);
183 }
184}
diff --git a/embassy-stm32/src/subghz/status.rs b/embassy-stm32/src/subghz/status.rs
deleted file mode 100644
index b84034f68..000000000
--- a/embassy-stm32/src/subghz/status.rs
+++ /dev/null
@@ -1,197 +0,0 @@
1/// sub-GHz radio operating mode.
2///
3/// See `Get_Status` under section 5.8.5 "Communication status information commands"
4/// in the reference manual.
5///
6/// This is returned by [`Status::mode`].
7#[repr(u8)]
8#[derive(Debug, PartialEq, Eq, Clone, Copy)]
9#[cfg_attr(feature = "defmt", derive(defmt::Format))]
10pub enum StatusMode {
11 /// Standby mode with RC 13MHz.
12 StandbyRc = 0x2,
13 /// Standby mode with HSE32.
14 StandbyHse = 0x3,
15 /// Frequency Synthesis mode.
16 Fs = 0x4,
17 /// Receive mode.
18 Rx = 0x5,
19 /// Transmit mode.
20 Tx = 0x6,
21}
22
23impl StatusMode {
24 /// Create a new `StatusMode` from bits.
25 ///
26 /// # Example
27 ///
28 /// ```
29 /// use stm32wlxx_hal::subghz::StatusMode;
30 ///
31 /// assert_eq!(StatusMode::from_raw(0x2), Ok(StatusMode::StandbyRc));
32 /// assert_eq!(StatusMode::from_raw(0x3), Ok(StatusMode::StandbyHse));
33 /// assert_eq!(StatusMode::from_raw(0x4), Ok(StatusMode::Fs));
34 /// assert_eq!(StatusMode::from_raw(0x5), Ok(StatusMode::Rx));
35 /// assert_eq!(StatusMode::from_raw(0x6), Ok(StatusMode::Tx));
36 /// // Other values are reserved
37 /// assert_eq!(StatusMode::from_raw(0), Err(0));
38 /// ```
39 pub const fn from_raw(bits: u8) -> Result<Self, u8> {
40 match bits {
41 0x2 => Ok(StatusMode::StandbyRc),
42 0x3 => Ok(StatusMode::StandbyHse),
43 0x4 => Ok(StatusMode::Fs),
44 0x5 => Ok(StatusMode::Rx),
45 0x6 => Ok(StatusMode::Tx),
46 _ => Err(bits),
47 }
48 }
49}
50
51/// Command status.
52///
53/// See `Get_Status` under section 5.8.5 "Communication status information commands"
54/// in the reference manual.
55///
56/// This is returned by [`Status::cmd`].
57#[repr(u8)]
58#[derive(Debug, PartialEq, Eq, Clone, Copy)]
59#[cfg_attr(feature = "defmt", derive(defmt::Format))]
60pub enum CmdStatus {
61 /// Data available to host.
62 ///
63 /// Packet received successfully and data can be retrieved.
64 Avaliable = 0x2,
65 /// Command time out.
66 ///
67 /// Command took too long to complete triggering a sub-GHz radio watchdog
68 /// timeout.
69 Timeout = 0x3,
70 /// Command processing error.
71 ///
72 /// Invalid opcode or incorrect number of parameters.
73 ProcessingError = 0x4,
74 /// Command execution failure.
75 ///
76 /// Command successfully received but cannot be executed at this time,
77 /// requested operating mode cannot be entered or requested data cannot be
78 /// sent.
79 ExecutionFailure = 0x5,
80 /// Transmit command completed.
81 ///
82 /// Current packet transmission completed.
83 Complete = 0x6,
84}
85
86impl CmdStatus {
87 /// Create a new `CmdStatus` from bits.
88 ///
89 /// # Example
90 ///
91 /// ```
92 /// use stm32wlxx_hal::subghz::CmdStatus;
93 ///
94 /// assert_eq!(CmdStatus::from_raw(0x2), Ok(CmdStatus::Avaliable));
95 /// assert_eq!(CmdStatus::from_raw(0x3), Ok(CmdStatus::Timeout));
96 /// assert_eq!(CmdStatus::from_raw(0x4), Ok(CmdStatus::ProcessingError));
97 /// assert_eq!(CmdStatus::from_raw(0x5), Ok(CmdStatus::ExecutionFailure));
98 /// assert_eq!(CmdStatus::from_raw(0x6), Ok(CmdStatus::Complete));
99 /// // Other values are reserved
100 /// assert_eq!(CmdStatus::from_raw(0), Err(0));
101 /// ```
102 pub const fn from_raw(bits: u8) -> Result<Self, u8> {
103 match bits {
104 0x2 => Ok(CmdStatus::Avaliable),
105 0x3 => Ok(CmdStatus::Timeout),
106 0x4 => Ok(CmdStatus::ProcessingError),
107 0x5 => Ok(CmdStatus::ExecutionFailure),
108 0x6 => Ok(CmdStatus::Complete),
109 _ => Err(bits),
110 }
111 }
112}
113
114/// Radio status.
115///
116/// This is returned by [`status`].
117///
118/// [`status`]: super::SubGhz::status
119#[derive(PartialEq, Eq, Clone, Copy)]
120pub struct Status(u8);
121
122impl From<u8> for Status {
123 fn from(x: u8) -> Self {
124 Status(x)
125 }
126}
127impl From<Status> for u8 {
128 fn from(x: Status) -> Self {
129 x.0
130 }
131}
132
133impl Status {
134 /// Create a new `Status` from a raw `u8` value.
135 ///
136 /// This is the same as `Status::from(u8)`, but in a `const` function.
137 ///
138 /// # Example
139 ///
140 /// ```
141 /// use stm32wlxx_hal::subghz::{CmdStatus, Status, StatusMode};
142 ///
143 /// const STATUS: Status = Status::from_raw(0x54_u8);
144 /// assert_eq!(STATUS.mode(), Ok(StatusMode::Rx));
145 /// assert_eq!(STATUS.cmd(), Ok(CmdStatus::Avaliable));
146 /// ```
147 pub const fn from_raw(value: u8) -> Status {
148 Status(value)
149 }
150
151 /// sub-GHz radio operating mode.
152 ///
153 /// # Example
154 ///
155 /// ```
156 /// use stm32wlxx_hal::subghz::{Status, StatusMode};
157 ///
158 /// let status: Status = 0xACu8.into();
159 /// assert_eq!(status.mode(), Ok(StatusMode::StandbyRc));
160 /// ```
161 pub const fn mode(&self) -> Result<StatusMode, u8> {
162 StatusMode::from_raw((self.0 >> 4) & 0b111)
163 }
164
165 /// Command status.
166 ///
167 /// This method frequently returns reserved values such as `Err(1)`.
168 /// ST support has confirmed that this is normal and should be ignored.
169 ///
170 /// # Example
171 ///
172 /// ```
173 /// use stm32wlxx_hal::subghz::{CmdStatus, Status};
174 ///
175 /// let status: Status = 0xACu8.into();
176 /// assert_eq!(status.cmd(), Ok(CmdStatus::Complete));
177 /// ```
178 pub const fn cmd(&self) -> Result<CmdStatus, u8> {
179 CmdStatus::from_raw((self.0 >> 1) & 0b111)
180 }
181}
182
183impl core::fmt::Debug for Status {
184 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
185 f.debug_struct("Status")
186 .field("mode", &self.mode())
187 .field("cmd", &self.cmd())
188 .finish()
189 }
190}
191
192#[cfg(feature = "defmt")]
193impl defmt::Format for Status {
194 fn format(&self, fmt: defmt::Formatter) {
195 defmt::write!(fmt, "Status {{ mode: {}, cmd: {} }}", self.mode(), self.cmd())
196 }
197}
diff --git a/embassy-stm32/src/subghz/tcxo_mode.rs b/embassy-stm32/src/subghz/tcxo_mode.rs
deleted file mode 100644
index 698dee0a6..000000000
--- a/embassy-stm32/src/subghz/tcxo_mode.rs
+++ /dev/null
@@ -1,170 +0,0 @@
1use super::Timeout;
2
3/// TCXO trim.
4///
5/// **Note:** To use V<sub>DDTCXO</sub>, the V<sub>DDRF</sub> supply must be at
6/// least + 200 mV higher than the selected `TcxoTrim` voltage level.
7///
8/// Used by [`TcxoMode`].
9#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
10#[cfg_attr(feature = "defmt", derive(defmt::Format))]
11#[repr(u8)]
12pub enum TcxoTrim {
13 /// 1.6V
14 Volts1pt6 = 0x0,
15 /// 1.7V
16 Volts1pt7 = 0x1,
17 /// 1.8V
18 Volts1pt8 = 0x2,
19 /// 2.2V
20 Volts2pt2 = 0x3,
21 /// 2.4V
22 Volts2pt4 = 0x4,
23 /// 2.7V
24 Volts2pt7 = 0x5,
25 /// 3.0V
26 Volts3pt0 = 0x6,
27 /// 3.3V
28 Volts3pt3 = 0x7,
29}
30
31impl core::fmt::Display for TcxoTrim {
32 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33 match self {
34 TcxoTrim::Volts1pt6 => write!(f, "1.6V"),
35 TcxoTrim::Volts1pt7 => write!(f, "1.7V"),
36 TcxoTrim::Volts1pt8 => write!(f, "1.8V"),
37 TcxoTrim::Volts2pt2 => write!(f, "2.2V"),
38 TcxoTrim::Volts2pt4 => write!(f, "2.4V"),
39 TcxoTrim::Volts2pt7 => write!(f, "2.7V"),
40 TcxoTrim::Volts3pt0 => write!(f, "3.0V"),
41 TcxoTrim::Volts3pt3 => write!(f, "3.3V"),
42 }
43 }
44}
45
46impl TcxoTrim {
47 /// Get the value of the TXCO trim in millivolts.
48 ///
49 /// # Example
50 ///
51 /// ```
52 /// use stm32wlxx_hal::subghz::TcxoTrim;
53 ///
54 /// assert_eq!(TcxoTrim::Volts1pt6.as_millivolts(), 1600);
55 /// assert_eq!(TcxoTrim::Volts1pt7.as_millivolts(), 1700);
56 /// assert_eq!(TcxoTrim::Volts1pt8.as_millivolts(), 1800);
57 /// assert_eq!(TcxoTrim::Volts2pt2.as_millivolts(), 2200);
58 /// assert_eq!(TcxoTrim::Volts2pt4.as_millivolts(), 2400);
59 /// assert_eq!(TcxoTrim::Volts2pt7.as_millivolts(), 2700);
60 /// assert_eq!(TcxoTrim::Volts3pt0.as_millivolts(), 3000);
61 /// assert_eq!(TcxoTrim::Volts3pt3.as_millivolts(), 3300);
62 /// ```
63 pub const fn as_millivolts(&self) -> u16 {
64 match self {
65 TcxoTrim::Volts1pt6 => 1600,
66 TcxoTrim::Volts1pt7 => 1700,
67 TcxoTrim::Volts1pt8 => 1800,
68 TcxoTrim::Volts2pt2 => 2200,
69 TcxoTrim::Volts2pt4 => 2400,
70 TcxoTrim::Volts2pt7 => 2700,
71 TcxoTrim::Volts3pt0 => 3000,
72 TcxoTrim::Volts3pt3 => 3300,
73 }
74 }
75}
76
77/// TCXO trim and HSE32 ready timeout.
78///
79/// Argument of [`set_tcxo_mode`].
80///
81/// [`set_tcxo_mode`]: super::SubGhz::set_tcxo_mode
82#[derive(Debug, PartialEq, Eq, Clone, Copy)]
83#[cfg_attr(feature = "defmt", derive(defmt::Format))]
84pub struct TcxoMode {
85 buf: [u8; 5],
86}
87
88impl TcxoMode {
89 /// Create a new `TcxoMode` struct.
90 ///
91 /// This is the same as `default`, but in a `const` function.
92 ///
93 /// # Example
94 ///
95 /// ```
96 /// use stm32wlxx_hal::subghz::TcxoMode;
97 ///
98 /// const TCXO_MODE: TcxoMode = TcxoMode::new();
99 /// ```
100 pub const fn new() -> TcxoMode {
101 TcxoMode {
102 buf: [super::OpCode::SetTcxoMode as u8, 0x00, 0x00, 0x00, 0x00],
103 }
104 }
105
106 /// Set the TCXO trim.
107 ///
108 /// **Note:** To use V<sub>DDTCXO</sub>, the V<sub>DDRF</sub> supply must be
109 /// at least + 200 mV higher than the selected `TcxoTrim` voltage level.
110 ///
111 /// # Example
112 ///
113 /// ```
114 /// use stm32wlxx_hal::subghz::{TcxoMode, TcxoTrim};
115 ///
116 /// const TCXO_MODE: TcxoMode = TcxoMode::new().set_txco_trim(TcxoTrim::Volts1pt6);
117 /// # assert_eq!(TCXO_MODE.as_slice()[1], 0x00);
118 /// ```
119 #[must_use = "set_txco_trim returns a modified TcxoMode"]
120 pub const fn set_txco_trim(mut self, tcxo_trim: TcxoTrim) -> TcxoMode {
121 self.buf[1] = tcxo_trim as u8;
122 self
123 }
124
125 /// Set the ready timeout duration.
126 ///
127 /// # Example
128 ///
129 /// ```
130 /// use core::time::Duration;
131 /// use stm32wlxx_hal::subghz::{TcxoMode, Timeout};
132 ///
133 /// // 15.625 ms timeout
134 /// const TIMEOUT: Timeout = Timeout::from_duration_sat(Duration::from_millis(15_625));
135 /// const TCXO_MODE: TcxoMode = TcxoMode::new().set_timeout(TIMEOUT);
136 /// # assert_eq!(TCXO_MODE.as_slice()[2], 0x0F);
137 /// # assert_eq!(TCXO_MODE.as_slice()[3], 0x42);
138 /// # assert_eq!(TCXO_MODE.as_slice()[4], 0x40);
139 /// ```
140 #[must_use = "set_timeout returns a modified TcxoMode"]
141 pub const fn set_timeout(mut self, timeout: Timeout) -> TcxoMode {
142 let timeout_bits: u32 = timeout.into_bits();
143 self.buf[2] = ((timeout_bits >> 16) & 0xFF) as u8;
144 self.buf[3] = ((timeout_bits >> 8) & 0xFF) as u8;
145 self.buf[4] = (timeout_bits & 0xFF) as u8;
146 self
147 }
148
149 /// Extracts a slice containing the packet.
150 ///
151 /// # Example
152 ///
153 /// ```
154 /// use stm32wlxx_hal::subghz::{TcxoMode, TcxoTrim, Timeout};
155 ///
156 /// const TCXO_MODE: TcxoMode = TcxoMode::new()
157 /// .set_txco_trim(TcxoTrim::Volts1pt7)
158 /// .set_timeout(Timeout::from_raw(0x123456));
159 /// assert_eq!(TCXO_MODE.as_slice(), &[0x97, 0x1, 0x12, 0x34, 0x56]);
160 /// ```
161 pub const fn as_slice(&self) -> &[u8] {
162 &self.buf
163 }
164}
165
166impl Default for TcxoMode {
167 fn default() -> Self {
168 Self::new()
169 }
170}
diff --git a/embassy-stm32/src/subghz/timeout.rs b/embassy-stm32/src/subghz/timeout.rs
deleted file mode 100644
index 0ae49dd90..000000000
--- a/embassy-stm32/src/subghz/timeout.rs
+++ /dev/null
@@ -1,492 +0,0 @@
1use core::time::Duration;
2
3use super::ValueError;
4
5const fn abs_diff(a: u64, b: u64) -> u64 {
6 if a > b {
7 a - b
8 } else {
9 b - a
10 }
11}
12
13/// Timeout argument.
14///
15/// This is used by:
16/// * [`set_rx`]
17/// * [`set_tx`]
18/// * [`TcxoMode`]
19///
20/// Each timeout has 3 bytes, with a resolution of 15.625µs per bit, giving a
21/// range of 0s to 262.143984375s.
22///
23/// [`set_rx`]: super::SubGhz::set_rx
24/// [`set_tx`]: super::SubGhz::set_tx
25/// [`TcxoMode`]: super::TcxoMode
26#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28pub struct Timeout {
29 bits: u32,
30}
31
32impl Timeout {
33 const BITS_PER_MILLI: u32 = 64; // 1e-3 / 15.625e-6
34 const BITS_PER_SEC: u32 = 64_000; // 1 / 15.625e-6
35
36 /// Disable the timeout (0s timeout).
37 ///
38 /// # Example
39 ///
40 /// ```
41 /// use core::time::Duration;
42 /// use stm32wlxx_hal::subghz::Timeout;
43 ///
44 /// const TIMEOUT: Timeout = Timeout::DISABLED;
45 /// assert_eq!(TIMEOUT.as_duration(), Duration::from_secs(0));
46 /// ```
47 pub const DISABLED: Timeout = Timeout { bits: 0x0 };
48
49 /// Minimum timeout, 15.625µs.
50 ///
51 /// # Example
52 ///
53 /// ```
54 /// use core::time::Duration;
55 /// use stm32wlxx_hal::subghz::Timeout;
56 ///
57 /// const TIMEOUT: Timeout = Timeout::MIN;
58 /// assert_eq!(TIMEOUT.into_bits(), 1);
59 /// ```
60 pub const MIN: Timeout = Timeout { bits: 1 };
61
62 /// Maximum timeout, 262.143984375s.
63 ///
64 /// # Example
65 ///
66 /// ```
67 /// use core::time::Duration;
68 /// use stm32wlxx_hal::subghz::Timeout;
69 ///
70 /// const TIMEOUT: Timeout = Timeout::MAX;
71 /// assert_eq!(TIMEOUT.as_duration(), Duration::from_nanos(262_143_984_375));
72 /// ```
73 pub const MAX: Timeout = Timeout { bits: 0x00FF_FFFF };
74
75 /// Timeout resolution in nanoseconds, 15.625µs.
76 pub const RESOLUTION_NANOS: u16 = 15_625;
77
78 /// Timeout resolution, 15.625µs.
79 ///
80 /// # Example
81 ///
82 /// ```
83 /// use stm32wlxx_hal::subghz::Timeout;
84 ///
85 /// assert_eq!(
86 /// Timeout::RESOLUTION.as_nanos(),
87 /// Timeout::RESOLUTION_NANOS as u128
88 /// );
89 /// ```
90 pub const RESOLUTION: Duration = Duration::from_nanos(Self::RESOLUTION_NANOS as u64);
91
92 /// Create a new timeout from a [`Duration`].
93 ///
94 /// This will return the nearest timeout value possible, or a
95 /// [`ValueError`] if the value is out of bounds.
96 ///
97 /// Use [`from_millis_sat`](Self::from_millis_sat) for runtime timeout
98 /// construction.
99 /// This is not _that_ useful right now, it is simply future proofing for a
100 /// time when `Result::unwrap` is available for `const fn`.
101 ///
102 /// # Example
103 ///
104 /// Value within bounds:
105 ///
106 /// ```
107 /// use core::time::Duration;
108 /// use stm32wlxx_hal::subghz::{Timeout, ValueError};
109 ///
110 /// const MIN: Duration = Timeout::RESOLUTION;
111 /// assert_eq!(Timeout::from_duration(MIN).unwrap(), Timeout::MIN);
112 /// ```
113 ///
114 /// Value too low:
115 ///
116 /// ```
117 /// use core::time::Duration;
118 /// use stm32wlxx_hal::subghz::{Timeout, ValueError};
119 ///
120 /// const LOWER_LIMIT_NANOS: u128 = 7813;
121 /// const TOO_LOW_NANOS: u128 = LOWER_LIMIT_NANOS - 1;
122 /// const TOO_LOW_DURATION: Duration = Duration::from_nanos(TOO_LOW_NANOS as u64);
123 /// assert_eq!(
124 /// Timeout::from_duration(TOO_LOW_DURATION),
125 /// Err(ValueError::too_low(TOO_LOW_NANOS, LOWER_LIMIT_NANOS))
126 /// );
127 /// ```
128 ///
129 /// Value too high:
130 ///
131 /// ```
132 /// use core::time::Duration;
133 /// use stm32wlxx_hal::subghz::{Timeout, ValueError};
134 ///
135 /// const UPPER_LIMIT_NANOS: u128 = Timeout::MAX.as_nanos() as u128 + 7812;
136 /// const TOO_HIGH_NANOS: u128 = UPPER_LIMIT_NANOS + 1;
137 /// const TOO_HIGH_DURATION: Duration = Duration::from_nanos(TOO_HIGH_NANOS as u64);
138 /// assert_eq!(
139 /// Timeout::from_duration(TOO_HIGH_DURATION),
140 /// Err(ValueError::too_high(TOO_HIGH_NANOS, UPPER_LIMIT_NANOS))
141 /// );
142 /// ```
143 pub const fn from_duration(duration: Duration) -> Result<Timeout, ValueError<u128>> {
144 // at the time of development many methods in
145 // `core::Duration` were not `const fn`, which leads to the hacks
146 // you see here.
147 let nanos: u128 = duration.as_nanos();
148 const UPPER_LIMIT: u128 = Timeout::MAX.as_nanos() as u128 + (Timeout::RESOLUTION_NANOS as u128) / 2;
149 const LOWER_LIMIT: u128 = (((Timeout::RESOLUTION_NANOS as u128) + 1) / 2) as u128;
150
151 if nanos > UPPER_LIMIT {
152 Err(ValueError::too_high(nanos, UPPER_LIMIT))
153 } else if nanos < LOWER_LIMIT {
154 Err(ValueError::too_low(nanos, LOWER_LIMIT))
155 } else {
156 // safe to truncate here because of previous bounds check.
157 let duration_nanos: u64 = nanos as u64;
158
159 let div_floor: u64 = duration_nanos / (Self::RESOLUTION_NANOS as u64);
160 let div_ceil: u64 = 1 + (duration_nanos - 1) / (Self::RESOLUTION_NANOS as u64);
161
162 let timeout_ceil: Timeout = Timeout::from_raw(div_ceil as u32);
163 let timeout_floor: Timeout = Timeout::from_raw(div_floor as u32);
164
165 let error_ceil: u64 = abs_diff(timeout_ceil.as_nanos(), duration_nanos);
166 let error_floor: u64 = abs_diff(timeout_floor.as_nanos(), duration_nanos);
167
168 if error_ceil < error_floor {
169 Ok(timeout_ceil)
170 } else {
171 Ok(timeout_floor)
172 }
173 }
174 }
175
176 /// Create a new timeout from a [`Duration`].
177 ///
178 /// This will return the nearest timeout value possible, saturating at the
179 /// limits.
180 ///
181 /// This is an expensive function to call outside of `const` contexts.
182 /// Use [`from_millis_sat`](Self::from_millis_sat) for runtime timeout
183 /// construction.
184 ///
185 /// # Example
186 ///
187 /// ```
188 /// use core::time::Duration;
189 /// use stm32wlxx_hal::subghz::Timeout;
190 ///
191 /// const DURATION_MAX_NS: u64 = 262_143_984_376;
192 ///
193 /// assert_eq!(
194 /// Timeout::from_duration_sat(Duration::from_millis(0)),
195 /// Timeout::MIN
196 /// );
197 /// assert_eq!(
198 /// Timeout::from_duration_sat(Duration::from_nanos(DURATION_MAX_NS)),
199 /// Timeout::MAX
200 /// );
201 /// assert_eq!(
202 /// Timeout::from_duration_sat(Timeout::RESOLUTION).into_bits(),
203 /// 1
204 /// );
205 /// ```
206 pub const fn from_duration_sat(duration: Duration) -> Timeout {
207 // at the time of development many methods in
208 // `core::Duration` were not `const fn`, which leads to the hacks
209 // you see here.
210 let nanos: u128 = duration.as_nanos();
211 const UPPER_LIMIT: u128 = Timeout::MAX.as_nanos() as u128;
212
213 if nanos > UPPER_LIMIT {
214 Timeout::MAX
215 } else if nanos < (Timeout::RESOLUTION_NANOS as u128) {
216 Timeout::from_raw(1)
217 } else {
218 // safe to truncate here because of previous bounds check.
219 let duration_nanos: u64 = duration.as_nanos() as u64;
220
221 let div_floor: u64 = duration_nanos / (Self::RESOLUTION_NANOS as u64);
222 let div_ceil: u64 = 1 + (duration_nanos - 1) / (Self::RESOLUTION_NANOS as u64);
223
224 let timeout_ceil: Timeout = Timeout::from_raw(div_ceil as u32);
225 let timeout_floor: Timeout = Timeout::from_raw(div_floor as u32);
226
227 let error_ceil: u64 = abs_diff(timeout_ceil.as_nanos(), duration_nanos);
228 let error_floor: u64 = abs_diff(timeout_floor.as_nanos(), duration_nanos);
229
230 if error_ceil < error_floor {
231 timeout_ceil
232 } else {
233 timeout_floor
234 }
235 }
236 }
237
238 /// Create a new timeout from a milliseconds value.
239 ///
240 /// This will round towards zero and saturate at the limits.
241 ///
242 /// This is the preferred method to call when you need to generate a
243 /// timeout value at runtime.
244 ///
245 /// # Example
246 ///
247 /// ```
248 /// use stm32wlxx_hal::subghz::Timeout;
249 ///
250 /// assert_eq!(Timeout::from_millis_sat(0), Timeout::MIN);
251 /// assert_eq!(Timeout::from_millis_sat(262_144), Timeout::MAX);
252 /// assert_eq!(Timeout::from_millis_sat(1).into_bits(), 64);
253 /// ```
254 pub const fn from_millis_sat(millis: u32) -> Timeout {
255 if millis == 0 {
256 Timeout::MIN
257 } else if millis >= 262_144 {
258 Timeout::MAX
259 } else {
260 Timeout::from_raw(millis * Self::BITS_PER_MILLI)
261 }
262 }
263
264 /// Create a timeout from raw bits, where each bit has the resolution of
265 /// [`Timeout::RESOLUTION`].
266 ///
267 /// **Note:** Only the first 24 bits of the `u32` are used, the `bits`
268 /// argument will be masked.
269 ///
270 /// # Example
271 ///
272 /// ```
273 /// use stm32wlxx_hal::subghz::Timeout;
274 ///
275 /// assert_eq!(Timeout::from_raw(u32::MAX), Timeout::MAX);
276 /// assert_eq!(Timeout::from_raw(0x00_FF_FF_FF), Timeout::MAX);
277 /// assert_eq!(Timeout::from_raw(1).as_duration(), Timeout::RESOLUTION);
278 /// assert_eq!(Timeout::from_raw(0), Timeout::DISABLED);
279 /// ```
280 pub const fn from_raw(bits: u32) -> Timeout {
281 Timeout {
282 bits: bits & 0x00FF_FFFF,
283 }
284 }
285
286 /// Get the timeout as nanoseconds.
287 ///
288 /// # Example
289 ///
290 /// ```
291 /// use stm32wlxx_hal::subghz::Timeout;
292 ///
293 /// assert_eq!(Timeout::MAX.as_nanos(), 262_143_984_375);
294 /// assert_eq!(Timeout::DISABLED.as_nanos(), 0);
295 /// assert_eq!(Timeout::from_raw(1).as_nanos(), 15_625);
296 /// assert_eq!(Timeout::from_raw(64_000).as_nanos(), 1_000_000_000);
297 /// ```
298 pub const fn as_nanos(&self) -> u64 {
299 (self.bits as u64) * (Timeout::RESOLUTION_NANOS as u64)
300 }
301
302 /// Get the timeout as microseconds, rounding towards zero.
303 ///
304 /// # Example
305 ///
306 /// ```
307 /// use stm32wlxx_hal::subghz::Timeout;
308 ///
309 /// assert_eq!(Timeout::MAX.as_micros(), 262_143_984);
310 /// assert_eq!(Timeout::DISABLED.as_micros(), 0);
311 /// assert_eq!(Timeout::from_raw(1).as_micros(), 15);
312 /// assert_eq!(Timeout::from_raw(64_000).as_micros(), 1_000_000);
313 /// ```
314 pub const fn as_micros(&self) -> u32 {
315 (self.as_nanos() / 1_000) as u32
316 }
317
318 /// Get the timeout as milliseconds, rounding towards zero.
319 ///
320 /// # Example
321 ///
322 /// ```
323 /// use stm32wlxx_hal::subghz::Timeout;
324 ///
325 /// assert_eq!(Timeout::MAX.as_millis(), 262_143);
326 /// assert_eq!(Timeout::DISABLED.as_millis(), 0);
327 /// assert_eq!(Timeout::from_raw(1).as_millis(), 0);
328 /// assert_eq!(Timeout::from_raw(64_000).as_millis(), 1_000);
329 /// ```
330 pub const fn as_millis(&self) -> u32 {
331 self.into_bits() / Self::BITS_PER_MILLI
332 }
333
334 /// Get the timeout as seconds, rounding towards zero.
335 ///
336 /// # Example
337 ///
338 /// ```
339 /// use stm32wlxx_hal::subghz::Timeout;
340 ///
341 /// assert_eq!(Timeout::MAX.as_secs(), 262);
342 /// assert_eq!(Timeout::DISABLED.as_secs(), 0);
343 /// assert_eq!(Timeout::from_raw(1).as_secs(), 0);
344 /// assert_eq!(Timeout::from_raw(64_000).as_secs(), 1);
345 /// ```
346 pub const fn as_secs(&self) -> u16 {
347 (self.into_bits() / Self::BITS_PER_SEC) as u16
348 }
349
350 /// Get the timeout as a [`Duration`].
351 ///
352 /// # Example
353 ///
354 /// ```
355 /// use core::time::Duration;
356 /// use stm32wlxx_hal::subghz::Timeout;
357 ///
358 /// assert_eq!(
359 /// Timeout::MAX.as_duration(),
360 /// Duration::from_nanos(262_143_984_375)
361 /// );
362 /// assert_eq!(Timeout::DISABLED.as_duration(), Duration::from_nanos(0));
363 /// assert_eq!(Timeout::from_raw(1).as_duration(), Timeout::RESOLUTION);
364 /// ```
365 pub const fn as_duration(&self) -> Duration {
366 Duration::from_nanos((self.bits as u64) * (Timeout::RESOLUTION_NANOS as u64))
367 }
368
369 /// Get the bit value for the timeout.
370 ///
371 /// # Example
372 ///
373 /// ```
374 /// use stm32wlxx_hal::subghz::Timeout;
375 ///
376 /// assert_eq!(Timeout::from_raw(u32::MAX).into_bits(), 0x00FF_FFFF);
377 /// assert_eq!(Timeout::from_raw(1).into_bits(), 1);
378 /// ```
379 pub const fn into_bits(self) -> u32 {
380 self.bits
381 }
382
383 /// Get the byte value for the timeout.
384 ///
385 /// # Example
386 ///
387 /// ```
388 /// use stm32wlxx_hal::subghz::Timeout;
389 ///
390 /// assert_eq!(Timeout::from_raw(u32::MAX).as_bytes(), [0xFF, 0xFF, 0xFF]);
391 /// assert_eq!(Timeout::from_raw(1).as_bytes(), [0, 0, 1]);
392 /// ```
393 pub const fn as_bytes(self) -> [u8; 3] {
394 [
395 ((self.bits >> 16) & 0xFF) as u8,
396 ((self.bits >> 8) & 0xFF) as u8,
397 (self.bits & 0xFF) as u8,
398 ]
399 }
400
401 /// Saturating timeout addition. Computes `self + rhs`, saturating at the
402 /// numeric bounds instead of overflowing.
403 ///
404 /// # Example
405 ///
406 /// ```
407 /// use stm32wlxx_hal::subghz::Timeout;
408 ///
409 /// assert_eq!(
410 /// Timeout::from_raw(0xFF_FF_F0).saturating_add(Timeout::from_raw(0xFF)),
411 /// Timeout::from_raw(0xFF_FF_FF)
412 /// );
413 /// assert_eq!(
414 /// Timeout::from_raw(100).saturating_add(Timeout::from_raw(23)),
415 /// Timeout::from_raw(123)
416 /// );
417 /// ```
418 #[must_use = "saturating_add returns a new Timeout"]
419 pub const fn saturating_add(self, rhs: Self) -> Self {
420 // TODO: use core::cmp::min when it is const
421 let bits: u32 = self.bits.saturating_add(rhs.bits);
422 if bits > Self::MAX.bits {
423 Self::MAX
424 } else {
425 Self { bits }
426 }
427 }
428}
429
430impl From<Timeout> for Duration {
431 fn from(to: Timeout) -> Self {
432 to.as_duration()
433 }
434}
435
436impl From<Timeout> for [u8; 3] {
437 fn from(to: Timeout) -> Self {
438 to.as_bytes()
439 }
440}
441
442#[cfg(feature = "time")]
443impl From<Timeout> for embassy_time::Duration {
444 fn from(to: Timeout) -> Self {
445 embassy_time::Duration::from_micros(to.as_micros().into())
446 }
447}
448
449#[cfg(test)]
450mod tests {
451 use core::time::Duration;
452
453 use super::{Timeout, ValueError};
454
455 #[test]
456 fn saturate() {
457 assert_eq!(Timeout::from_duration_sat(Duration::from_secs(u64::MAX)), Timeout::MAX);
458 }
459
460 #[test]
461 fn rounding() {
462 const NANO1: Duration = Duration::from_nanos(1);
463 let res_sub_1_ns: Duration = Timeout::RESOLUTION - NANO1;
464 let res_add_1_ns: Duration = Timeout::RESOLUTION + NANO1;
465 assert_eq!(Timeout::from_duration_sat(res_sub_1_ns).into_bits(), 1);
466 assert_eq!(Timeout::from_duration_sat(res_add_1_ns).into_bits(), 1);
467 }
468
469 #[test]
470 fn lower_limit() {
471 let low: Duration = (Timeout::RESOLUTION + Duration::from_nanos(1)) / 2;
472 assert_eq!(Timeout::from_duration(low), Ok(Timeout::from_raw(1)));
473
474 let too_low: Duration = low - Duration::from_nanos(1);
475 assert_eq!(
476 Timeout::from_duration(too_low),
477 Err(ValueError::too_low(too_low.as_nanos(), low.as_nanos()))
478 );
479 }
480
481 #[test]
482 fn upper_limit() {
483 let high: Duration = Timeout::MAX.as_duration() + Timeout::RESOLUTION / 2;
484 assert_eq!(Timeout::from_duration(high), Ok(Timeout::from_raw(0xFFFFFF)));
485
486 let too_high: Duration = high + Duration::from_nanos(1);
487 assert_eq!(
488 Timeout::from_duration(too_high),
489 Err(ValueError::too_high(too_high.as_nanos(), high.as_nanos()))
490 );
491 }
492}
diff --git a/embassy-stm32/src/subghz/tx_params.rs b/embassy-stm32/src/subghz/tx_params.rs
deleted file mode 100644
index 03bdb1ea8..000000000
--- a/embassy-stm32/src/subghz/tx_params.rs
+++ /dev/null
@@ -1,192 +0,0 @@
1/// Power amplifier ramp time for FSK, MSK, and LoRa modulation.
2///
3/// Argument of [`set_ramp_time`][`super::TxParams::set_ramp_time`].
4#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6#[repr(u8)]
7pub enum RampTime {
8 /// 10µs
9 Micros10 = 0x00,
10 /// 20µs
11 Micros20 = 0x01,
12 /// 40µs
13 Micros40 = 0x02,
14 /// 80µs
15 Micros80 = 0x03,
16 /// 200µs
17 Micros200 = 0x04,
18 /// 800µs
19 Micros800 = 0x05,
20 /// 1.7ms
21 Micros1700 = 0x06,
22 /// 3.4ms
23 Micros3400 = 0x07,
24}
25
26impl From<RampTime> for u8 {
27 fn from(rt: RampTime) -> Self {
28 rt as u8
29 }
30}
31
32impl From<RampTime> for core::time::Duration {
33 fn from(rt: RampTime) -> Self {
34 match rt {
35 RampTime::Micros10 => core::time::Duration::from_micros(10),
36 RampTime::Micros20 => core::time::Duration::from_micros(20),
37 RampTime::Micros40 => core::time::Duration::from_micros(40),
38 RampTime::Micros80 => core::time::Duration::from_micros(80),
39 RampTime::Micros200 => core::time::Duration::from_micros(200),
40 RampTime::Micros800 => core::time::Duration::from_micros(800),
41 RampTime::Micros1700 => core::time::Duration::from_micros(1700),
42 RampTime::Micros3400 => core::time::Duration::from_micros(3400),
43 }
44 }
45}
46
47#[cfg(feature = "time")]
48impl From<RampTime> for embassy_time::Duration {
49 fn from(rt: RampTime) -> Self {
50 match rt {
51 RampTime::Micros10 => embassy_time::Duration::from_micros(10),
52 RampTime::Micros20 => embassy_time::Duration::from_micros(20),
53 RampTime::Micros40 => embassy_time::Duration::from_micros(40),
54 RampTime::Micros80 => embassy_time::Duration::from_micros(80),
55 RampTime::Micros200 => embassy_time::Duration::from_micros(200),
56 RampTime::Micros800 => embassy_time::Duration::from_micros(800),
57 RampTime::Micros1700 => embassy_time::Duration::from_micros(1700),
58 RampTime::Micros3400 => embassy_time::Duration::from_micros(3400),
59 }
60 }
61}
62/// Transmit parameters, output power and power amplifier ramp up time.
63///
64/// Argument of [`set_tx_params`][`super::SubGhz::set_tx_params`].
65#[derive(Debug, PartialEq, Eq, Clone, Copy)]
66#[cfg_attr(feature = "defmt", derive(defmt::Format))]
67pub struct TxParams {
68 buf: [u8; 3],
69}
70
71impl TxParams {
72 /// Optimal power setting for +15dBm output power with the low-power PA.
73 ///
74 /// This must be used with [`PaConfig::LP_15`](super::PaConfig::LP_15).
75 pub const LP_15: TxParams = TxParams::new().set_power(0x0E);
76
77 /// Optimal power setting for +14dBm output power with the low-power PA.
78 ///
79 /// This must be used with [`PaConfig::LP_14`](super::PaConfig::LP_14).
80 pub const LP_14: TxParams = TxParams::new().set_power(0x0E);
81
82 /// Optimal power setting for +10dBm output power with the low-power PA.
83 ///
84 /// This must be used with [`PaConfig::LP_10`](super::PaConfig::LP_10).
85 pub const LP_10: TxParams = TxParams::new().set_power(0x0D);
86
87 /// Optimal power setting for the high-power PA.
88 ///
89 /// This must be used with one of:
90 ///
91 /// * [`PaConfig::HP_22`](super::PaConfig::HP_22)
92 /// * [`PaConfig::HP_20`](super::PaConfig::HP_20)
93 /// * [`PaConfig::HP_17`](super::PaConfig::HP_17)
94 /// * [`PaConfig::HP_14`](super::PaConfig::HP_14)
95 pub const HP: TxParams = TxParams::new().set_power(0x16);
96
97 /// Create a new `TxParams` struct.
98 ///
99 /// This is the same as `default`, but in a `const` function.
100 ///
101 /// # Example
102 ///
103 /// ```
104 /// use stm32wlxx_hal::subghz::TxParams;
105 ///
106 /// const TX_PARAMS: TxParams = TxParams::new();
107 /// assert_eq!(TX_PARAMS, TxParams::default());
108 /// ```
109 pub const fn new() -> TxParams {
110 TxParams {
111 buf: [super::OpCode::SetTxParams as u8, 0x00, 0x00],
112 }
113 }
114
115 /// Set the output power.
116 ///
117 /// For low power selected in [`set_pa_config`]:
118 ///
119 /// * 0x0E: +14 dB
120 /// * ...
121 /// * 0x00: 0 dB
122 /// * ...
123 /// * 0xEF: -17 dB
124 /// * Others: reserved
125 ///
126 /// For high power selected in [`set_pa_config`]:
127 ///
128 /// * 0x16: +22 dB
129 /// * ...
130 /// * 0x00: 0 dB
131 /// * ...
132 /// * 0xF7: -9 dB
133 /// * Others: reserved
134 ///
135 /// # Example
136 ///
137 /// Set the output power to 0 dB.
138 ///
139 /// ```
140 /// use stm32wlxx_hal::subghz::{RampTime, TxParams};
141 ///
142 /// const TX_PARAMS: TxParams = TxParams::new().set_power(0x00);
143 /// # assert_eq!(TX_PARAMS.as_slice()[1], 0x00);
144 /// ```
145 ///
146 /// [`set_pa_config`]: super::SubGhz::set_pa_config
147 #[must_use = "set_power returns a modified TxParams"]
148 pub const fn set_power(mut self, power: u8) -> TxParams {
149 self.buf[1] = power;
150 self
151 }
152
153 /// Set the Power amplifier ramp time for FSK, MSK, and LoRa modulation.
154 ///
155 /// # Example
156 ///
157 /// Set the ramp time to 200 microseconds.
158 ///
159 /// ```
160 /// use stm32wlxx_hal::subghz::{RampTime, TxParams};
161 ///
162 /// const TX_PARAMS: TxParams = TxParams::new().set_ramp_time(RampTime::Micros200);
163 /// # assert_eq!(TX_PARAMS.as_slice()[2], 0x04);
164 /// ```
165 #[must_use = "set_ramp_time returns a modified TxParams"]
166 pub const fn set_ramp_time(mut self, rt: RampTime) -> TxParams {
167 self.buf[2] = rt as u8;
168 self
169 }
170
171 /// Extracts a slice containing the packet.
172 ///
173 /// # Example
174 ///
175 /// ```
176 /// use stm32wlxx_hal::subghz::{RampTime, TxParams};
177 ///
178 /// const TX_PARAMS: TxParams = TxParams::new()
179 /// .set_ramp_time(RampTime::Micros80)
180 /// .set_power(0x0E);
181 /// assert_eq!(TX_PARAMS.as_slice(), &[0x8E, 0x0E, 0x03]);
182 /// ```
183 pub const fn as_slice(&self) -> &[u8] {
184 &self.buf
185 }
186}
187
188impl Default for TxParams {
189 fn default() -> Self {
190 Self::new()
191 }
192}
diff --git a/embassy-stm32/src/subghz/value_error.rs b/embassy-stm32/src/subghz/value_error.rs
deleted file mode 100644
index 6a0b489a8..000000000
--- a/embassy-stm32/src/subghz/value_error.rs
+++ /dev/null
@@ -1,129 +0,0 @@
1/// Error for a value that is out-of-bounds.
2///
3/// Used by [`Timeout::from_duration`].
4///
5/// [`Timeout::from_duration`]: super::Timeout::from_duration
6#[derive(Debug, PartialEq, Eq, Clone, Copy)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub struct ValueError<T> {
9 value: T,
10 limit: T,
11 over: bool,
12}
13
14impl<T> ValueError<T> {
15 /// Create a new `ValueError` for a value that exceeded an upper bound.
16 ///
17 /// Unfortunately panic is not available in `const fn`, so there are no
18 /// guarantees on the value being greater than the limit.
19 ///
20 /// # Example
21 ///
22 /// ```
23 /// use stm32wlxx_hal::subghz::ValueError;
24 ///
25 /// const ERROR: ValueError<u8> = ValueError::too_high(101u8, 100u8);
26 /// assert!(ERROR.over());
27 /// assert!(!ERROR.under());
28 /// ```
29 pub const fn too_high(value: T, limit: T) -> ValueError<T> {
30 ValueError {
31 value,
32 limit,
33 over: true,
34 }
35 }
36
37 /// Create a new `ValueError` for a value that exceeded a lower bound.
38 ///
39 /// Unfortunately panic is not available in `const fn`, so there are no
40 /// guarantees on the value being less than the limit.
41 ///
42 /// # Example
43 ///
44 /// ```
45 /// use stm32wlxx_hal::subghz::ValueError;
46 ///
47 /// const ERROR: ValueError<u8> = ValueError::too_low(200u8, 201u8);
48 /// assert!(ERROR.under());
49 /// assert!(!ERROR.over());
50 /// ```
51 pub const fn too_low(value: T, limit: T) -> ValueError<T> {
52 ValueError {
53 value,
54 limit,
55 over: false,
56 }
57 }
58
59 /// Get the value that caused the error.
60 ///
61 /// # Example
62 ///
63 /// ```
64 /// use stm32wlxx_hal::subghz::ValueError;
65 ///
66 /// const ERROR: ValueError<u8> = ValueError::too_high(101u8, 100u8);
67 /// assert_eq!(ERROR.value(), &101u8);
68 /// ```
69 pub const fn value(&self) -> &T {
70 &self.value
71 }
72
73 /// Get the limit for the value.
74 ///
75 /// # Example
76 ///
77 /// ```
78 /// use stm32wlxx_hal::subghz::ValueError;
79 ///
80 /// const ERROR: ValueError<u8> = ValueError::too_high(101u8, 100u8);
81 /// assert_eq!(ERROR.limit(), &100u8);
82 /// ```
83 pub const fn limit(&self) -> &T {
84 &self.limit
85 }
86
87 /// Returns `true` if the value was over the limit.
88 ///
89 /// # Example
90 ///
91 /// ```
92 /// use stm32wlxx_hal::subghz::ValueError;
93 ///
94 /// const ERROR: ValueError<u8> = ValueError::too_high(101u8, 100u8);
95 /// assert!(ERROR.over());
96 /// assert!(!ERROR.under());
97 /// ```
98 pub const fn over(&self) -> bool {
99 self.over
100 }
101
102 /// Returns `true` if the value was under the limit.
103 ///
104 /// # Example
105 ///
106 /// ```
107 /// use stm32wlxx_hal::subghz::ValueError;
108 ///
109 /// const ERROR: ValueError<u8> = ValueError::too_low(200u8, 201u8);
110 /// assert!(ERROR.under());
111 /// assert!(!ERROR.over());
112 /// ```
113 pub const fn under(&self) -> bool {
114 !self.over
115 }
116}
117
118impl<T> core::fmt::Display for ValueError<T>
119where
120 T: core::fmt::Display,
121{
122 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123 if self.over {
124 write!(f, "Value is too high {} > {}", self.value, self.limit)
125 } else {
126 write!(f, "Value is too low {} < {}", self.value, self.limit)
127 }
128 }
129}
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index 59f30a9be..77be46ffd 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -18,7 +18,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm
18embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } 18embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
19embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } 19embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true }
20embedded-io = "0.4.0" 20embedded-io = "0.4.0"
21embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt", "external-lora-phy"], optional = true } 21embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"], optional = true }
22lora-phy = { version = "1", optional = true } 22lora-phy = { version = "1", optional = true }
23lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } 23lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
24lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } 24lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true }
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index ca022e254..e9d2127c5 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -14,7 +14,7 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de
14embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 14embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
15embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 15embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
16embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } 16embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
17embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true } 17embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"], optional = true }
18lora-phy = { version = "1", optional = true } 18lora-phy = { version = "1", optional = true }
19lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } 19lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
20lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } 20lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true }