aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf/Cargo.toml5
-rw-r--r--embassy-nrf/src/chips/nrf52805.rs6
-rw-r--r--embassy-nrf/src/chips/nrf52810.rs6
-rw-r--r--embassy-nrf/src/chips/nrf52811.rs6
-rw-r--r--embassy-nrf/src/chips/nrf52820.rs6
-rw-r--r--embassy-nrf/src/chips/nrf52832.rs6
-rw-r--r--embassy-nrf/src/chips/nrf52833.rs6
-rw-r--r--embassy-nrf/src/chips/nrf52840.rs6
-rw-r--r--embassy-nrf/src/chips/nrf5340_net.rs6
-rw-r--r--embassy-nrf/src/lib.rs6
-rw-r--r--embassy-nrf/src/radio/ble.rs432
-rw-r--r--embassy-nrf/src/radio/mod.rs89
-rw-r--r--examples/nrf52840/Cargo.toml8
-rw-r--r--examples/nrf52840/src/bin/radio_ble_advertising.rs42
14 files changed, 628 insertions, 2 deletions
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index 7e161df9b..dcdc7f313 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -57,6 +57,9 @@ unstable-pac = []
57## Enable GPIO tasks and events 57## Enable GPIO tasks and events
58gpiote = [] 58gpiote = []
59 59
60## Enable radio driver
61radio = ["dep:jewel"]
62
60## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz 63## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz
61time-driver-rtc1 = ["_time-driver"] 64time-driver-rtc1 = ["_time-driver"]
62 65
@@ -150,6 +153,8 @@ embedded-storage-async = "0.4.0"
150cfg-if = "1.0.0" 153cfg-if = "1.0.0"
151document-features = "0.2.7" 154document-features = "0.2.7"
152 155
156jewel = { version = "0.1.0", git = "https://github.com/jewel-rs/jewel", optional = true }
157
153nrf51-pac = { version = "0.12.0", optional = true } 158nrf51-pac = { version = "0.12.0", optional = true }
154nrf52805-pac = { version = "0.12.0", optional = true } 159nrf52805-pac = { version = "0.12.0", optional = true }
155nrf52810-pac = { version = "0.12.0", optional = true } 160nrf52810-pac = { version = "0.12.0", optional = true }
diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs
index 624d6613d..b97c85f9e 100644
--- a/embassy-nrf/src/chips/nrf52805.rs
+++ b/embassy-nrf/src/chips/nrf52805.rs
@@ -129,6 +129,9 @@ embassy_hal_internal::peripherals! {
129 129
130 // QDEC 130 // QDEC
131 QDEC, 131 QDEC,
132
133 // RADIO
134 RADIO,
132} 135}
133 136
134impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 137impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -209,6 +212,9 @@ impl_ppi_channel!(PPI_CH31, 31 => static);
209impl_saadc_input!(P0_04, ANALOG_INPUT2); 212impl_saadc_input!(P0_04, ANALOG_INPUT2);
210impl_saadc_input!(P0_05, ANALOG_INPUT3); 213impl_saadc_input!(P0_05, ANALOG_INPUT3);
211 214
215#[cfg(feature = "radio")]
216impl_radio!(RADIO, RADIO, RADIO);
217
212embassy_hal_internal::interrupt_mod!( 218embassy_hal_internal::interrupt_mod!(
213 POWER_CLOCK, 219 POWER_CLOCK,
214 RADIO, 220 RADIO,
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs
index 002feab3b..03548d03f 100644
--- a/embassy-nrf/src/chips/nrf52810.rs
+++ b/embassy-nrf/src/chips/nrf52810.rs
@@ -135,6 +135,9 @@ embassy_hal_internal::peripherals! {
135 135
136 // PDM 136 // PDM
137 PDM, 137 PDM,
138
139 // Radio
140 RADIO,
138} 141}
139 142
140impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 143impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -235,6 +238,9 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
235impl_saadc_input!(P0_30, ANALOG_INPUT6); 238impl_saadc_input!(P0_30, ANALOG_INPUT6);
236impl_saadc_input!(P0_31, ANALOG_INPUT7); 239impl_saadc_input!(P0_31, ANALOG_INPUT7);
237 240
241#[cfg(feature = "radio")]
242impl_radio!(RADIO, RADIO, RADIO);
243
238embassy_hal_internal::interrupt_mod!( 244embassy_hal_internal::interrupt_mod!(
239 POWER_CLOCK, 245 POWER_CLOCK,
240 RADIO, 246 RADIO,
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs
index 5952907f8..992fbd129 100644
--- a/embassy-nrf/src/chips/nrf52811.rs
+++ b/embassy-nrf/src/chips/nrf52811.rs
@@ -135,6 +135,9 @@ embassy_hal_internal::peripherals! {
135 135
136 // PDM 136 // PDM
137 PDM, 137 PDM,
138
139 // Radio
140 RADIO,
138} 141}
139 142
140impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 143impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -237,6 +240,9 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
237impl_saadc_input!(P0_30, ANALOG_INPUT6); 240impl_saadc_input!(P0_30, ANALOG_INPUT6);
238impl_saadc_input!(P0_31, ANALOG_INPUT7); 241impl_saadc_input!(P0_31, ANALOG_INPUT7);
239 242
243#[cfg(feature = "radio")]
244impl_radio!(RADIO, RADIO, RADIO);
245
240embassy_hal_internal::interrupt_mod!( 246embassy_hal_internal::interrupt_mod!(
241 POWER_CLOCK, 247 POWER_CLOCK,
242 RADIO, 248 RADIO,
diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs
index c2f792cb9..f241f4ea3 100644
--- a/embassy-nrf/src/chips/nrf52820.rs
+++ b/embassy-nrf/src/chips/nrf52820.rs
@@ -130,6 +130,9 @@ embassy_hal_internal::peripherals! {
130 130
131 // QDEC 131 // QDEC
132 QDEC, 132 QDEC,
133
134 // Radio
135 RADIO,
133} 136}
134 137
135impl_usb!(USBD, USBD, USBD); 138impl_usb!(USBD, USBD, USBD);
@@ -224,6 +227,9 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
224impl_ppi_channel!(PPI_CH30, 30 => static); 227impl_ppi_channel!(PPI_CH30, 30 => static);
225impl_ppi_channel!(PPI_CH31, 31 => static); 228impl_ppi_channel!(PPI_CH31, 31 => static);
226 229
230#[cfg(feature = "radio")]
231impl_radio!(RADIO, RADIO, RADIO);
232
227embassy_hal_internal::interrupt_mod!( 233embassy_hal_internal::interrupt_mod!(
228 POWER_CLOCK, 234 POWER_CLOCK,
229 RADIO, 235 RADIO,
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs
index 65d52364d..6bbdd9a63 100644
--- a/embassy-nrf/src/chips/nrf52832.rs
+++ b/embassy-nrf/src/chips/nrf52832.rs
@@ -150,6 +150,9 @@ embassy_hal_internal::peripherals! {
150 150
151 // PDM 151 // PDM
152 PDM, 152 PDM,
153
154 // Radio
155 RADIO,
153} 156}
154 157
155impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 158impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
@@ -264,6 +267,9 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
264 267
265impl_i2s!(I2S, I2S, I2S); 268impl_i2s!(I2S, I2S, I2S);
266 269
270#[cfg(feature = "radio")]
271impl_radio!(RADIO, RADIO, RADIO);
272
267embassy_hal_internal::interrupt_mod!( 273embassy_hal_internal::interrupt_mod!(
268 POWER_CLOCK, 274 POWER_CLOCK,
269 RADIO, 275 RADIO,
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs
index 7c9b66d69..e137e4dc6 100644
--- a/embassy-nrf/src/chips/nrf52833.rs
+++ b/embassy-nrf/src/chips/nrf52833.rs
@@ -170,6 +170,9 @@ embassy_hal_internal::peripherals! {
170 170
171 // I2S 171 // I2S
172 I2S, 172 I2S,
173
174 // Radio
175 RADIO,
173} 176}
174 177
175impl_usb!(USBD, USBD, USBD); 178impl_usb!(USBD, USBD, USBD);
@@ -306,6 +309,9 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
306 309
307impl_i2s!(I2S, I2S, I2S); 310impl_i2s!(I2S, I2S, I2S);
308 311
312#[cfg(feature = "radio")]
313impl_radio!(RADIO, RADIO, RADIO);
314
309embassy_hal_internal::interrupt_mod!( 315embassy_hal_internal::interrupt_mod!(
310 POWER_CLOCK, 316 POWER_CLOCK,
311 RADIO, 317 RADIO,
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs
index 51c55cd4d..2d805f871 100644
--- a/embassy-nrf/src/chips/nrf52840.rs
+++ b/embassy-nrf/src/chips/nrf52840.rs
@@ -173,6 +173,9 @@ embassy_hal_internal::peripherals! {
173 173
174 // I2S 174 // I2S
175 I2S, 175 I2S,
176
177 // Radio
178 RADIO,
176} 179}
177 180
178impl_usb!(USBD, USBD, USBD); 181impl_usb!(USBD, USBD, USBD);
@@ -311,6 +314,9 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
311 314
312impl_i2s!(I2S, I2S, I2S); 315impl_i2s!(I2S, I2S, I2S);
313 316
317#[cfg(feature = "radio")]
318impl_radio!(RADIO, RADIO, RADIO);
319
314embassy_hal_internal::interrupt_mod!( 320embassy_hal_internal::interrupt_mod!(
315 POWER_CLOCK, 321 POWER_CLOCK,
316 RADIO, 322 RADIO,
diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs
index a7cf82872..3248bde52 100644
--- a/embassy-nrf/src/chips/nrf5340_net.rs
+++ b/embassy-nrf/src/chips/nrf5340_net.rs
@@ -248,6 +248,9 @@ embassy_hal_internal::peripherals! {
248 P1_13, 248 P1_13,
249 P1_14, 249 P1_14,
250 P1_15, 250 P1_15,
251
252 // Radio
253 RADIO,
251} 254}
252 255
253impl_uarte!(SERIAL0, UARTE0, SERIAL0); 256impl_uarte!(SERIAL0, UARTE0, SERIAL0);
@@ -345,6 +348,9 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
345impl_ppi_channel!(PPI_CH30, 30 => configurable); 348impl_ppi_channel!(PPI_CH30, 30 => configurable);
346impl_ppi_channel!(PPI_CH31, 31 => configurable); 349impl_ppi_channel!(PPI_CH31, 31 => configurable);
347 350
351#[cfg(feature = "radio")]
352impl_radio!(RADIO, RADIO, RADIO);
353
348embassy_hal_internal::interrupt_mod!( 354embassy_hal_internal::interrupt_mod!(
349 CLOCK_POWER, 355 CLOCK_POWER,
350 RADIO, 356 RADIO,
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 358a7cc27..961928d11 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -45,6 +45,12 @@ pub mod buffered_uarte;
45pub mod gpio; 45pub mod gpio;
46#[cfg(feature = "gpiote")] 46#[cfg(feature = "gpiote")]
47pub mod gpiote; 47pub mod gpiote;
48
49#[cfg(feature = "radio")]
50pub mod radio;
51#[cfg(all(feature = "radio", feature = "_nrf9160"))]
52compile_error!("feature `radio` is not valid for nRF91 series chips.");
53
48#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] 54#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
49pub mod i2s; 55pub mod i2s;
50pub mod nvmc; 56pub mod nvmc;
diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs
new file mode 100644
index 000000000..a5d9f447b
--- /dev/null
+++ b/embassy-nrf/src/radio/ble.rs
@@ -0,0 +1,432 @@
1//! Radio driver implementation focused on Bluetooth Low-Energy transmission.
2//!
3//! The radio can calculate the CRC, perform data whitening,
4//! automatically send the right preamble.
5//! Most of the configuration is done automatically when you choose the mode and this driver.
6//!
7//! Some configuration can just be done when de device is disabled,
8//! and the configuration varies depending if is a transmitter or a receiver.
9//! Because of that we have a state machine to keep track of the state of the radio.
10//! The Radio is the disable radio which configure the common parameters between
11//! the bluetooth protocols, like the package format, the CRC and the whitening.
12//! The TxRadio radio enable and configured as a transmitter with the specific parameters.
13
14use core::future::poll_fn;
15use core::sync::atomic::{compiler_fence, Ordering};
16use core::task::Poll;
17
18use embassy_hal_internal::drop::OnDrop;
19use embassy_hal_internal::{into_ref, PeripheralRef};
20use jewel::phy::{
21 AdvertisingChannel, Channel, ChannelTrait, HeaderSize, Mode, Radio as BleRadio, ADV_ADDRESS, ADV_CRC_INIT,
22 CRC_POLY, MAX_PDU_LENGTH,
23};
24use pac::radio::mode::MODE_A as PacMode;
25use pac::radio::pcnf0::PLEN_A as PreambleLength;
26// Re-export SVD variants to allow user to directly set values.
27pub use pac::radio::{state::STATE_A as RadioState, txpower::TXPOWER_A as TxPower};
28
29use crate::interrupt::typelevel::Interrupt;
30use crate::radio::*;
31use crate::util::slice_in_ram_or;
32
33/// UART error.
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36#[non_exhaustive]
37pub enum Error {
38 /// Buffer was too long.
39 BufferTooLong,
40 /// Buffer was to short.
41 BufferTooShort,
42 /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash.
43 BufferNotInRAM,
44}
45
46/// Radio driver.
47pub struct Radio<'d, T: Instance> {
48 _p: PeripheralRef<'d, T>,
49}
50
51impl<'d, T: Instance> Radio<'d, T> {
52 /// Create a new radio driver.
53 pub fn new(
54 radio: impl Peripheral<P = T> + 'd,
55 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
56 ) -> Self {
57 // From 5.4.1 of the nRF52840 Product Specification:
58 // > The HFXO must be running to use the RADIO or the calibration mechanism associated with the 32.768 kHz RC oscillator.
59 // Currently the jewel crate don't implement the calibration mechanism, so we need to ensure that the HFXO is running
60 utils::check_xtal();
61
62 into_ref!(radio);
63
64 let r = T::regs();
65
66 r.pcnf1.write(|w| unsafe {
67 // It is 0 bytes long in a standard BLE packet
68 w.statlen()
69 .bits(0)
70 // MaxLen configures the maximum packet payload plus add-on size in
71 // number of bytes that can be transmitted or received by the RADIO. This feature can be used to ensure
72 // that the RADIO does not overwrite, or read beyond, the RAM assigned to the packet payload. This means
73 // that if the packet payload length defined by PCNF1.STATLEN and the LENGTH field in the packet specifies a
74 // packet larger than MAXLEN, the payload will be truncated at MAXLEN
75 //
76 // To simplify the implementation, I'm setting the max length to the maximum value
77 // and I'm using only the length field to truncate the payload
78 .maxlen()
79 .bits(255)
80 // Configure the length of the address field in the packet
81 // The prefix after the address fields is always appended, so is always 1 byte less than the size of the address
82 // The base address is truncated from the least significant byte if the BALEN is less than 4
83 //
84 // BLE address is always 4 bytes long
85 .balen()
86 .bits(3) // 3 bytes base address (+ 1 prefix);
87 // Configure the endianess
88 // For BLE is always little endian (LSB first)
89 .endian()
90 .little()
91 // Data whitening is used to avoid long sequences of zeros or
92 // ones, e.g., 0b0000000 or 0b1111111, in the data bit stream.
93 // The whitener and de-whitener are defined the same way,
94 // using a 7-bit linear feedback shift register with the
95 // polynomial x7 + x4 + 1.
96 //
97 // In BLE Whitening shall be applied on the PDU and CRC of all
98 // Link Layer packets and is performed after the CRC generation
99 // in the transmitter. No other parts of the packets are whitened.
100 // De-whitening is performed before the CRC checking in the receiver
101 // Before whitening or de-whitening, the shift register should be
102 // initialized based on the channel index.
103 .whiteen()
104 .set_bit() // Enable whitening
105 });
106
107 // Configure CRC
108 r.crccnf.write(|w| {
109 // In BLE the CRC shall be calculated on the PDU of all Link Layer
110 // packets (even if the packet is encrypted).
111 // So here we skip the address field
112 w.skipaddr()
113 .skip()
114 // In BLE 24-bit CRC = 3 bytes
115 .len()
116 .three()
117 });
118
119 r.crcpoly.write(|w| unsafe {
120 // Configure the CRC polynomial
121 // Each term in the CRC polynomial is mapped to a bit in this
122 // register which index corresponds to the term's exponent.
123 // The least significant term/bit is hard-wired internally to
124 // 1, and bit number 0 of the register content is ignored by
125 // the hardware. The following example is for an 8 bit CRC
126 // polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 .
127 w.crcpoly().bits(CRC_POLY & 0xFFFFFF)
128 });
129 // The CRC initial value varies depending of the PDU type
130
131 // Ch map between 2400 MHZ .. 2500 MHz
132 // All modes use this range
133 r.frequency.write(|w| w.map().default());
134
135 // Configure shortcuts to simplify and speed up sending and receiving packets.
136 r.shorts.write(|w| {
137 // start transmission/recv immediately after ramp-up
138 // disable radio when transmission/recv is done
139 w.ready_start().enabled().end_disable().enabled()
140 });
141
142 // Enable NVIC interrupt
143 T::Interrupt::unpend();
144 unsafe { T::Interrupt::enable() };
145
146 let mut radio = Self { _p: radio };
147
148 // set defaults
149 radio.set_mode(Mode::Ble1mbit);
150 radio.set_tx_power(0);
151 radio.set_header_size(HeaderSize::TwoBytes);
152 radio.set_access_address(ADV_ADDRESS);
153 radio.set_crc_init(ADV_CRC_INIT);
154 radio.set_channel(AdvertisingChannel::Ch39.into());
155
156 radio
157 }
158
159 #[allow(dead_code)]
160 fn trace_state(&self) {
161 let r = T::regs();
162
163 match r.state.read().state().variant().unwrap() {
164 RadioState::DISABLED => trace!("radio:state:DISABLED"),
165 RadioState::RX_RU => trace!("radio:state:RX_RU"),
166 RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"),
167 RadioState::RX => trace!("radio:state:RX"),
168 RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"),
169 RadioState::TX_RU => trace!("radio:state:TX_RU"),
170 RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"),
171 RadioState::TX => trace!("radio:state:TX"),
172 RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"),
173 }
174 }
175
176 async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce() -> ()) {
177 //self.trace_state();
178
179 let r = T::regs();
180 let s = T::state();
181
182 // If the Future is dropped before the end of the transmission
183 // we need to disable the interrupt and stop the transmission
184 // to keep the state consistent
185 let drop = OnDrop::new(|| {
186 trace!("radio drop: stopping");
187
188 r.intenclr.write(|w| w.end().clear());
189 r.events_end.reset();
190
191 r.tasks_stop.write(|w| w.tasks_stop().set_bit());
192
193 // The docs don't explicitly mention any event to acknowledge the stop task
194 // So I guess it's the same as end
195 while r.events_end.read().events_end().bit_is_clear() {}
196
197 trace!("radio drop: stopped");
198 });
199
200 /* Config interrupt */
201 // trace!("radio:enable interrupt");
202 // Clear some remnant side-effects (I'm unsure if this is needed)
203 r.events_end.reset();
204
205 // Enable interrupt
206 r.intenset.write(|w| w.end().set());
207
208 compiler_fence(Ordering::SeqCst);
209
210 // Trigger the transmission
211 trigger();
212 // self.trace_state();
213
214 // On poll check if interrupt happen
215 poll_fn(|cx| {
216 s.end_waker.register(cx.waker());
217 if r.events_end.read().events_end().bit_is_set() {
218 // trace!("radio:end");
219 return core::task::Poll::Ready(());
220 }
221 Poll::Pending
222 })
223 .await;
224
225 compiler_fence(Ordering::SeqCst);
226 r.events_disabled.reset(); // ACK
227
228 // Everthing ends fine, so we can disable the drop
229 drop.defuse();
230 }
231
232 /// Disable the radio.
233 fn disable(&mut self) {
234 let r = T::regs();
235
236 compiler_fence(Ordering::SeqCst);
237 // If is already disabled, do nothing
238 if !r.state.read().state().is_disabled() {
239 trace!("radio:disable");
240 // Trigger the disable task
241 r.tasks_disable.write(|w| w.tasks_disable().set_bit());
242
243 // Wait until the radio is disabled
244 while r.events_disabled.read().events_disabled().bit_is_clear() {}
245
246 compiler_fence(Ordering::SeqCst);
247
248 // Acknowledge it
249 r.events_disabled.reset();
250 }
251 }
252}
253
254impl<'d, T: Instance> BleRadio for Radio<'d, T> {
255 type Error = Error;
256
257 fn set_mode(&mut self, mode: Mode) {
258 let r = T::regs();
259 r.mode.write(|w| {
260 w.mode().variant(match mode {
261 Mode::Ble1mbit => PacMode::BLE_1MBIT,
262 //Mode::Ble2mbit => PacMode::BLE_2MBIT,
263 })
264 });
265
266 r.pcnf0.write(|w| {
267 w.plen().variant(match mode {
268 Mode::Ble1mbit => PreambleLength::_8BIT,
269 //Mode::Ble2mbit => PreambleLength::_16BIT,
270 })
271 });
272 }
273
274 fn set_header_size(&mut self, header_size: HeaderSize) {
275 let r = T::regs();
276
277 let s1len: u8 = match header_size {
278 HeaderSize::TwoBytes => 0,
279 HeaderSize::ThreeBytes => 8, // bits
280 };
281
282 r.pcnf0.write(|w| unsafe {
283 w
284 // Configure S0 to 1 byte length, this will represent the Data/Adv header flags
285 .s0len()
286 .set_bit()
287 // Configure the length (in bits) field to 1 byte length, this will represent the length of the payload
288 // and also be used to know how many bytes to read/write from/to the buffer
289 .lflen()
290 .bits(8)
291 // Configure the lengh (in bits) of bits in the S1 field. It could be used to represent the CTEInfo for data packages in BLE.
292 .s1len()
293 .bits(s1len)
294 });
295 }
296
297 fn set_channel(&mut self, channel: Channel) {
298 let r = T::regs();
299
300 r.frequency
301 .write(|w| unsafe { w.frequency().bits((channel.central_frequency() - 2400) as u8) });
302 r.datawhiteiv
303 .write(|w| unsafe { w.datawhiteiv().bits(channel.whitening_init()) });
304 }
305
306 fn set_access_address(&mut self, access_address: u32) {
307 let r = T::regs();
308
309 // Configure logical address
310 // The byte ordering on air is always least significant byte first for the address
311 // So for the address 0xAA_BB_CC_DD, the address on air will be DD CC BB AA
312 // The package order is BASE, PREFIX so BASE=0xBB_CC_DD and PREFIX=0xAA
313 r.prefix0
314 .write(|w| unsafe { w.ap0().bits((access_address >> 24) as u8) });
315
316 // The base address is truncated from the least significant byte (because the BALEN is less than 4)
317 // So we need to shift the address to the right
318 r.base0.write(|w| unsafe { w.bits(access_address << 8) });
319
320 // Don't match tx address
321 r.txaddress.write(|w| unsafe { w.txaddress().bits(0) });
322
323 // Match on logical address
324 // For what I understand, this config only filter the packets
325 // by the address, so only packages send to the previous address
326 // will finish the reception
327 r.rxaddresses.write(|w| {
328 w.addr0()
329 .enabled()
330 .addr1()
331 .enabled()
332 .addr2()
333 .enabled()
334 .addr3()
335 .enabled()
336 .addr4()
337 .enabled()
338 });
339 }
340
341 fn set_crc_init(&mut self, crc_init: u32) {
342 let r = T::regs();
343
344 r.crcinit.write(|w| unsafe { w.crcinit().bits(crc_init & 0xFFFFFF) });
345 }
346
347 fn set_tx_power(&mut self, power_db: i8) {
348 let r = T::regs();
349
350 let tx_power: TxPower = match power_db {
351 8..=i8::MAX => TxPower::POS8D_BM,
352 7 => TxPower::POS7D_BM,
353 6 => TxPower::POS6D_BM,
354 5 => TxPower::POS5D_BM,
355 4 => TxPower::POS4D_BM,
356 3 => TxPower::POS3D_BM,
357 1..=2 => TxPower::POS2D_BM,
358 -3..=0 => TxPower::_0D_BM,
359 -7..=-4 => TxPower::NEG4D_BM,
360 -11..=-8 => TxPower::NEG8D_BM,
361 -15..=-12 => TxPower::NEG12D_BM,
362 -19..=-16 => TxPower::NEG16D_BM,
363 -29..=-20 => TxPower::NEG20D_BM,
364 -39..=-30 => TxPower::NEG30D_BM,
365 i8::MIN..=-40 => TxPower::NEG40D_BM,
366 };
367
368 r.txpower.write(|w| w.txpower().variant(tx_power));
369 }
370
371 fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
372 // Because we are serializing the buffer, we should always have the buffer in RAM
373 slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
374
375 if buffer.len() > MAX_PDU_LENGTH {
376 return Err(Error::BufferTooLong);
377 }
378
379 let r = T::regs();
380
381 // Here we are considering that the length of the packet is
382 // correctly set in the buffer, otherwise we will sending
383 // unowned regions of memory
384 let ptr = buffer.as_ptr();
385
386 // Configure the payload
387 r.packetptr.write(|w| unsafe { w.bits(ptr as u32) });
388
389 Ok(())
390 }
391
392 /// Send packet
393 async fn transmit(&mut self) {
394 let r = T::regs();
395
396 self.trigger_and_wait_end(move || {
397 // Initialize the transmission
398 // trace!("txen");
399 r.tasks_txen.write(|w| w.tasks_txen().set_bit());
400 })
401 .await;
402 }
403
404 /// Send packet
405 async fn receive(&mut self) {
406 let r = T::regs();
407
408 self.trigger_and_wait_end(move || {
409 // Initialize the transmission
410 // trace!("rxen");
411 r.tasks_rxen.write(|w| w.tasks_rxen().set_bit());
412
413 // Await until ready
414 while r.events_ready.read().events_ready().bit_is_clear() {}
415
416 compiler_fence(Ordering::SeqCst);
417
418 // Acknowledge it
419 r.events_ready.reset();
420
421 // trace!("radio:start");
422 r.tasks_start.write(|w| w.tasks_start().set_bit());
423 })
424 .await;
425 }
426}
427
428impl<'d, T: Instance> Drop for Radio<'d, T> {
429 fn drop(&mut self) {
430 self.disable();
431 }
432}
diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs
new file mode 100644
index 000000000..91cc2c0a7
--- /dev/null
+++ b/embassy-nrf/src/radio/mod.rs
@@ -0,0 +1,89 @@
1//! Integrated 2.4 GHz Radio
2//!
3//! The 2.4 GHz radio transceiver is compatible with multiple radio standards
4//! such as 1Mbps, 2Mbps and Long Range Bluetooth Low Energy.
5
6#![macro_use]
7
8/// Bluetooth Low Energy Radio driver.
9pub mod ble;
10
11use core::marker::PhantomData;
12
13use crate::{interrupt, pac, Peripheral};
14
15/// Interrupt handler
16pub struct InterruptHandler<T: Instance> {
17 _phantom: PhantomData<T>,
18}
19
20impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
21 unsafe fn on_interrupt() {
22 let r = T::regs();
23 let s = T::state();
24
25 if r.events_end.read().events_end().bit_is_set() {
26 s.end_waker.wake();
27 r.intenclr.write(|w| w.end().clear());
28 }
29 }
30}
31
32pub(crate) mod utils {
33 use super::*;
34
35 // Check if the HFCLK is XTAL is enabled
36 pub fn check_xtal() {
37 // safe: only reading the value
38 let is_xtal = unsafe {
39 let r = &*pac::CLOCK::ptr();
40 r.hfclkstat.read().src().is_xtal()
41 };
42 assert!(is_xtal, "HFCLK must be XTAL");
43 }
44}
45
46pub(crate) mod sealed {
47 use embassy_sync::waitqueue::AtomicWaker;
48
49 pub struct State {
50 /// end packet transmission or reception
51 pub end_waker: AtomicWaker,
52 }
53 impl State {
54 pub const fn new() -> Self {
55 Self {
56 end_waker: AtomicWaker::new(),
57 }
58 }
59 }
60
61 pub trait Instance {
62 fn regs() -> &'static crate::pac::radio::RegisterBlock;
63 fn state() -> &'static State;
64 }
65}
66
67macro_rules! impl_radio {
68 ($type:ident, $pac_type:ident, $irq:ident) => {
69 impl crate::radio::sealed::Instance for peripherals::$type {
70 fn regs() -> &'static pac::radio::RegisterBlock {
71 unsafe { &*pac::$pac_type::ptr() }
72 }
73
74 fn state() -> &'static crate::radio::sealed::State {
75 static STATE: crate::radio::sealed::State = crate::radio::sealed::State::new();
76 &STATE
77 }
78 }
79 impl crate::radio::Instance for peripherals::$type {
80 type Interrupt = crate::interrupt::typelevel::$irq;
81 }
82 };
83}
84
85/// Radio peripheral instance.
86pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
87 /// Interrupt for this peripheral.
88 type Interrupt: interrupt::typelevel::Interrupt;
89}
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index abb995be6..0239583cd 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0"
8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 8embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
9embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } 9embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
10embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } 10embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
11embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 11embassy-time = { version = "0.3.0", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 12embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time", "radio"]}
13embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } 13embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
15embedded-io = { version = "0.6.0", features = ["defmt-03"] } 15embedded-io = { version = "0.6.0", features = ["defmt-03"] }
@@ -35,6 +35,10 @@ embedded-hal-async = { version = "1.0" }
35embedded-hal-bus = { version = "0.1", features = ["async"] } 35embedded-hal-bus = { version = "0.1", features = ["async"] }
36num-integer = { version = "0.1.45", default-features = false } 36num-integer = { version = "0.1.45", default-features = false }
37microfft = "0.5.0" 37microfft = "0.5.0"
38jewel = { version = "0.1.0", git = "https://github.com/jewel-rs/jewel"}
39
40[patch.crates-io]
41embassy-time = { version = "0.3.0", path = "../../embassy-time"}
38 42
39[profile.release] 43[profile.release]
40debug = 2 44debug = 2
diff --git a/examples/nrf52840/src/bin/radio_ble_advertising.rs b/examples/nrf52840/src/bin/radio_ble_advertising.rs
new file mode 100644
index 000000000..8898c2418
--- /dev/null
+++ b/examples/nrf52840/src/bin/radio_ble_advertising.rs
@@ -0,0 +1,42 @@
1#![no_std]
2#![no_main]
3
4use defmt::{info, unwrap};
5use embassy_executor::Spawner;
6use embassy_nrf::{bind_interrupts, peripherals, radio};
7use embassy_time::Timer;
8use jewel::phy::Radio;
9use {defmt_rtt as _, panic_probe as _};
10
11bind_interrupts!(struct Irqs {
12 RADIO => radio::InterruptHandler<peripherals::RADIO>;
13});
14
15// For a high-level API look on jewel examples
16#[embassy_executor::main]
17async fn main(_spawner: Spawner) {
18 let mut config = embassy_nrf::config::Config::default();
19 config.hfclk_source = embassy_nrf::config::HfclkSource::ExternalXtal;
20 let p = embassy_nrf::init(config);
21
22 info!("Starting BLE radio");
23 let mut radio = radio::ble::Radio::new(p.RADIO, Irqs);
24
25 let pdu = [
26 0x46u8, // ADV_NONCONN_IND, Random address,
27 0x18, // Length of payload
28 0x27, 0xdc, 0xd0, 0xe8, 0xe1, 0xff, // Adress
29 0x02, 0x01, 0x06, // Flags
30 0x03, 0x03, 0x09, 0x18, // Complete list of 16-bit UUIDs available
31 0x0A, 0x09, // Length, Type: Device name
32 b'H', b'e', b'l', b'l', b'o', b'R', b'u', b's', b't',
33 ];
34
35 unwrap!(radio.set_buffer(pdu.as_ref()));
36
37 loop {
38 info!("Sending packet");
39 radio.transmit().await;
40 Timer::after_millis(500).await;
41 }
42}