use bbqueue::{bbq, BBQueue}; use core::{cell::RefCell, ops::DerefMut}; use cortex_m::{self, interrupt::Mutex}; use log::info; use nrf52840_hal::target::{interrupt, FICR, RADIO, TIMER0}; use rubble::{ gatt::BatteryServiceAttrs, l2cap::{BleChannelMap, L2CAPState}, link::{ ad_structure::AdStructure, queue, AddressKind, DeviceAddress, HardwareInterface, LinkLayer, Responder, MIN_PDU_BUF, }, security_manager::NoSecurity, time::{Duration, Timer}, }; use rubble_nrf52::{ radio::{BleRadio, PacketBuffer}, timer::BleTimer, }; struct NRF52840 {} impl HardwareInterface for NRF52840 { type Timer = BleTimer; type Tx = BleRadio; } type Global = Mutex>>; static BLE_LL: Global> = Mutex::new(RefCell::new(None)); static BLE_RADIO: Global = Mutex::new(RefCell::new(None)); pub fn setup( radio: RADIO, timer: TIMER0, ficr: FICR, ) -> Responder> { // make sure hfclock is started for timer0 to work at the right // resolution. let ble_timer = BleTimer::init(timer); let devaddr_lo: &[u8] = &ficr.deviceaddr[0].read().bits().to_le_bytes(); let devaddr_hi: &[u8] = &(ficr.deviceaddr[1].read().bits() as u16).to_le_bytes(); let mut devaddr: [u8; 6] = [0; 6]; for (i, a) in [devaddr_lo, devaddr_hi] .iter() .copied() .flatten() .enumerate() { devaddr[i] = *a; } let addrkind = if ficr.deviceaddrtype.read().deviceaddrtype().is_public() { AddressKind::Public } else { AddressKind::Random }; let addr = DeviceAddress::new(devaddr, addrkind); info!("my address: {:?}", addr); static mut TX_BUF: PacketBuffer = [0; MIN_PDU_BUF]; static mut RX_BUF: PacketBuffer = [0; MIN_PDU_BUF]; let (tx_prod, tx_cons) = queue::create(bbq![MIN_PDU_BUF * 2].expect("creating tx queue")); let (rx_prod, rx_cons) = queue::create(bbq![MIN_PDU_BUF * 2].expect("creating rx queue")); // `responder` used on main loop. let resp = Responder::new( tx_prod, rx_cons, L2CAPState::new(BleChannelMap::with_attributes(BatteryServiceAttrs::new())), ); // `ble_radio` and `ll` used on both `timer` and `radio` // interrupts. // may be able to use ring buffer to avoid sharing? let mut ble_radio = unsafe { BleRadio::new(radio, &mut TX_BUF, &mut RX_BUF) }; let mut ble_ll = LinkLayer::::new(addr, ble_timer); let next_update = ble_ll .start_advertise( Duration::from_millis(200), &[AdStructure::CompleteLocalName("bleusb")], &mut ble_radio, tx_cons, rx_prod, ) .expect("scheduling link layer update"); ble_ll.timer().configure_interrupt(next_update); cortex_m::interrupt::free(|cs| { BLE_LL.borrow(cs).replace(Some(ble_ll)); BLE_RADIO.borrow(cs).replace(Some(ble_radio)); }); resp } #[interrupt] fn TIMER0() { cortex_m::interrupt::free(|cs| { if let (Some(ref mut ble_ll), Some(ref mut ble_radio)) = ( BLE_LL.borrow(cs).borrow_mut().deref_mut(), BLE_RADIO.borrow(cs).borrow_mut().deref_mut(), ) { if !ble_ll.timer().is_interrupt_pending() { return; } ble_ll.timer().clear_interrupt(); let cmd = ble_ll.update(ble_radio); ble_radio.configure_receiver(cmd.radio); ble_ll.timer().configure_interrupt(cmd.next_update); } }) } #[interrupt] fn RADIO() { cortex_m::interrupt::free(|cs| { if let (Some(ref mut ble_ll), Some(ref mut ble_radio)) = ( BLE_LL.borrow(cs).borrow_mut().deref_mut(), BLE_RADIO.borrow(cs).borrow_mut().deref_mut(), ) { let next_update = ble_radio.recv_interrupt(ble_ll.timer().now(), ble_ll); ble_ll.timer().configure_interrupt(next_update); } }) }