diff options
| author | Guilherme S. Salustiano <[email protected]> | 2024-02-07 15:25:07 +0100 |
|---|---|---|
| committer | Guilherme S. Salustiano <[email protected]> | 2024-02-07 15:38:39 +0100 |
| commit | 847b8be81480b249a2e8b1ff502e6188d2dadf04 (patch) | |
| tree | b85a06f534950ed5db592467da2afd13803de530 | |
| parent | 2c5426aa5c41563f3b225d99d39793a765ec9204 (diff) | |
feat/implement ble radio on nrf
| -rw-r--r-- | embassy-nrf/Cargo.toml | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52805.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52810.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52811.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52820.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52832.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52833.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52840.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf5340_net.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/lib.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/radio/ble.rs | 432 | ||||
| -rw-r--r-- | embassy-nrf/src/radio/mod.rs | 89 | ||||
| -rw-r--r-- | examples/nrf52840/Cargo.toml | 8 | ||||
| -rw-r--r-- | examples/nrf52840/src/bin/radio_ble_advertising.rs | 42 |
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 |
| 58 | gpiote = [] | 58 | gpiote = [] |
| 59 | 59 | ||
| 60 | ## Enable radio driver | ||
| 61 | radio = ["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 |
| 61 | time-driver-rtc1 = ["_time-driver"] | 64 | time-driver-rtc1 = ["_time-driver"] |
| 62 | 65 | ||
| @@ -150,6 +153,8 @@ embedded-storage-async = "0.4.0" | |||
| 150 | cfg-if = "1.0.0" | 153 | cfg-if = "1.0.0" |
| 151 | document-features = "0.2.7" | 154 | document-features = "0.2.7" |
| 152 | 155 | ||
| 156 | jewel = { version = "0.1.0", git = "https://github.com/jewel-rs/jewel", optional = true } | ||
| 157 | |||
| 153 | nrf51-pac = { version = "0.12.0", optional = true } | 158 | nrf51-pac = { version = "0.12.0", optional = true } |
| 154 | nrf52805-pac = { version = "0.12.0", optional = true } | 159 | nrf52805-pac = { version = "0.12.0", optional = true } |
| 155 | nrf52810-pac = { version = "0.12.0", optional = true } | 160 | nrf52810-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 | ||
| 134 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); | 137 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); |
| @@ -209,6 +212,9 @@ impl_ppi_channel!(PPI_CH31, 31 => static); | |||
| 209 | impl_saadc_input!(P0_04, ANALOG_INPUT2); | 212 | impl_saadc_input!(P0_04, ANALOG_INPUT2); |
| 210 | impl_saadc_input!(P0_05, ANALOG_INPUT3); | 213 | impl_saadc_input!(P0_05, ANALOG_INPUT3); |
| 211 | 214 | ||
| 215 | #[cfg(feature = "radio")] | ||
| 216 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 217 | |||
| 212 | embassy_hal_internal::interrupt_mod!( | 218 | embassy_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 | ||
| 140 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); | 143 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); |
| @@ -235,6 +238,9 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); | |||
| 235 | impl_saadc_input!(P0_30, ANALOG_INPUT6); | 238 | impl_saadc_input!(P0_30, ANALOG_INPUT6); |
| 236 | impl_saadc_input!(P0_31, ANALOG_INPUT7); | 239 | impl_saadc_input!(P0_31, ANALOG_INPUT7); |
| 237 | 240 | ||
| 241 | #[cfg(feature = "radio")] | ||
| 242 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 243 | |||
| 238 | embassy_hal_internal::interrupt_mod!( | 244 | embassy_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 | ||
| 140 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); | 143 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); |
| @@ -237,6 +240,9 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); | |||
| 237 | impl_saadc_input!(P0_30, ANALOG_INPUT6); | 240 | impl_saadc_input!(P0_30, ANALOG_INPUT6); |
| 238 | impl_saadc_input!(P0_31, ANALOG_INPUT7); | 241 | impl_saadc_input!(P0_31, ANALOG_INPUT7); |
| 239 | 242 | ||
| 243 | #[cfg(feature = "radio")] | ||
| 244 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 245 | |||
| 240 | embassy_hal_internal::interrupt_mod!( | 246 | embassy_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 | ||
| 135 | impl_usb!(USBD, USBD, USBD); | 138 | impl_usb!(USBD, USBD, USBD); |
| @@ -224,6 +227,9 @@ impl_ppi_channel!(PPI_CH29, 29 => static); | |||
| 224 | impl_ppi_channel!(PPI_CH30, 30 => static); | 227 | impl_ppi_channel!(PPI_CH30, 30 => static); |
| 225 | impl_ppi_channel!(PPI_CH31, 31 => static); | 228 | impl_ppi_channel!(PPI_CH31, 31 => static); |
| 226 | 229 | ||
| 230 | #[cfg(feature = "radio")] | ||
| 231 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 232 | |||
| 227 | embassy_hal_internal::interrupt_mod!( | 233 | embassy_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 | ||
| 155 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); | 158 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); |
| @@ -264,6 +267,9 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); | |||
| 264 | 267 | ||
| 265 | impl_i2s!(I2S, I2S, I2S); | 268 | impl_i2s!(I2S, I2S, I2S); |
| 266 | 269 | ||
| 270 | #[cfg(feature = "radio")] | ||
| 271 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 272 | |||
| 267 | embassy_hal_internal::interrupt_mod!( | 273 | embassy_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 | ||
| 175 | impl_usb!(USBD, USBD, USBD); | 178 | impl_usb!(USBD, USBD, USBD); |
| @@ -306,6 +309,9 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); | |||
| 306 | 309 | ||
| 307 | impl_i2s!(I2S, I2S, I2S); | 310 | impl_i2s!(I2S, I2S, I2S); |
| 308 | 311 | ||
| 312 | #[cfg(feature = "radio")] | ||
| 313 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 314 | |||
| 309 | embassy_hal_internal::interrupt_mod!( | 315 | embassy_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 | ||
| 178 | impl_usb!(USBD, USBD, USBD); | 181 | impl_usb!(USBD, USBD, USBD); |
| @@ -311,6 +314,9 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); | |||
| 311 | 314 | ||
| 312 | impl_i2s!(I2S, I2S, I2S); | 315 | impl_i2s!(I2S, I2S, I2S); |
| 313 | 316 | ||
| 317 | #[cfg(feature = "radio")] | ||
| 318 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 319 | |||
| 314 | embassy_hal_internal::interrupt_mod!( | 320 | embassy_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 | ||
| 253 | impl_uarte!(SERIAL0, UARTE0, SERIAL0); | 256 | impl_uarte!(SERIAL0, UARTE0, SERIAL0); |
| @@ -345,6 +348,9 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); | |||
| 345 | impl_ppi_channel!(PPI_CH30, 30 => configurable); | 348 | impl_ppi_channel!(PPI_CH30, 30 => configurable); |
| 346 | impl_ppi_channel!(PPI_CH31, 31 => configurable); | 349 | impl_ppi_channel!(PPI_CH31, 31 => configurable); |
| 347 | 350 | ||
| 351 | #[cfg(feature = "radio")] | ||
| 352 | impl_radio!(RADIO, RADIO, RADIO); | ||
| 353 | |||
| 348 | embassy_hal_internal::interrupt_mod!( | 354 | embassy_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; | |||
| 45 | pub mod gpio; | 45 | pub mod gpio; |
| 46 | #[cfg(feature = "gpiote")] | 46 | #[cfg(feature = "gpiote")] |
| 47 | pub mod gpiote; | 47 | pub mod gpiote; |
| 48 | |||
| 49 | #[cfg(feature = "radio")] | ||
| 50 | pub mod radio; | ||
| 51 | #[cfg(all(feature = "radio", feature = "_nrf9160"))] | ||
| 52 | compile_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"))] |
| 49 | pub mod i2s; | 55 | pub mod i2s; |
| 50 | pub mod nvmc; | 56 | pub 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 | |||
| 14 | use core::future::poll_fn; | ||
| 15 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 16 | use core::task::Poll; | ||
| 17 | |||
| 18 | use embassy_hal_internal::drop::OnDrop; | ||
| 19 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 20 | use jewel::phy::{ | ||
| 21 | AdvertisingChannel, Channel, ChannelTrait, HeaderSize, Mode, Radio as BleRadio, ADV_ADDRESS, ADV_CRC_INIT, | ||
| 22 | CRC_POLY, MAX_PDU_LENGTH, | ||
| 23 | }; | ||
| 24 | use pac::radio::mode::MODE_A as PacMode; | ||
| 25 | use pac::radio::pcnf0::PLEN_A as PreambleLength; | ||
| 26 | // Re-export SVD variants to allow user to directly set values. | ||
| 27 | pub use pac::radio::{state::STATE_A as RadioState, txpower::TXPOWER_A as TxPower}; | ||
| 28 | |||
| 29 | use crate::interrupt::typelevel::Interrupt; | ||
| 30 | use crate::radio::*; | ||
| 31 | use 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] | ||
| 37 | pub 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. | ||
| 47 | pub struct Radio<'d, T: Instance> { | ||
| 48 | _p: PeripheralRef<'d, T>, | ||
| 49 | } | ||
| 50 | |||
| 51 | impl<'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 | |||
| 254 | impl<'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 | |||
| 428 | impl<'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. | ||
| 9 | pub mod ble; | ||
| 10 | |||
| 11 | use core::marker::PhantomData; | ||
| 12 | |||
| 13 | use crate::{interrupt, pac, Peripheral}; | ||
| 14 | |||
| 15 | /// Interrupt handler | ||
| 16 | pub struct InterruptHandler<T: Instance> { | ||
| 17 | _phantom: PhantomData<T>, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<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 | |||
| 32 | pub(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 | |||
| 46 | pub(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 | |||
| 67 | macro_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. | ||
| 86 | pub 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" | |||
| 8 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 8 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| 9 | embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } | 10 | embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } |
| 11 | embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy-time = { version = "0.3.0", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } | 12 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time", "radio"]} |
| 13 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } | 13 | embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } |
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } | 15 | embedded-io = { version = "0.6.0", features = ["defmt-03"] } |
| @@ -35,6 +35,10 @@ embedded-hal-async = { version = "1.0" } | |||
| 35 | embedded-hal-bus = { version = "0.1", features = ["async"] } | 35 | embedded-hal-bus = { version = "0.1", features = ["async"] } |
| 36 | num-integer = { version = "0.1.45", default-features = false } | 36 | num-integer = { version = "0.1.45", default-features = false } |
| 37 | microfft = "0.5.0" | 37 | microfft = "0.5.0" |
| 38 | jewel = { version = "0.1.0", git = "https://github.com/jewel-rs/jewel"} | ||
| 39 | |||
| 40 | [patch.crates-io] | ||
| 41 | embassy-time = { version = "0.3.0", path = "../../embassy-time"} | ||
| 38 | 42 | ||
| 39 | [profile.release] | 43 | [profile.release] |
| 40 | debug = 2 | 44 | debug = 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 | |||
| 4 | use defmt::{info, unwrap}; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_nrf::{bind_interrupts, peripherals, radio}; | ||
| 7 | use embassy_time::Timer; | ||
| 8 | use jewel::phy::Radio; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | bind_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] | ||
| 17 | async 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 | } | ||
