OT_WEAK void radio_init( ) { /// Transceiver implementation dependent //vlFILE* fp; /// Set SPIRIT1-dependent initialization defaults //rfctl.flags = RADIO_FLAG_XOON; rfctl.flags = 0; rfctl.nextcal = 0; /// Set universal Radio module initialization defaults //radio.state = RADIO_Idle; //radio.evtdone = &otutils_sig2_null; /// Initialize the bus between SPIRIT1 and MCU, and load defaults. /// SPIRIT1 starts-up in Idle (READY), so we set the state and flags /// to match that. Then, init the bus and send RADIO to sleep. /// SPIRIT1 can do SPI in Sleep. spirit1_init_bus(); spirit1_load_defaults(); /// Do this workaround (SPIRIT1 Errata DocID023165 R5, section 1.2) to fix /// the shutdown current issue for input voltages <= 2.6V. For input /// voltages > 2.6V, it does not hurt anything. spirit1_write(RFREG(PM_TEST), 0xCA); spirit1_write(RFREG(TEST_SELECT), 0x04); spirit1_write(RFREG(TEST_SELECT), 0x00); /// Done with the radio init //spirit1drv_smart_standby(); radio_sleep(); /// Initialize RM2 elements such as channels, link-params, etc. rm2_init(); }
void spirit1_clockout_off() { /// This is the reverse of spirit1_clockout_on(), described above. #if (BOARD_FEATURE_RFXTALOUT) spirit1.clkreq = False; spirit1_write(RFREG(GPIO3_CONF), RFGPO(GND)); spirit1_write(RFREG(MCU_CK_CONF), 0); spirit1drv_smart_standby(); #endif }
/** Special Functions <BR> * ========================================================================<BR> * Notably, these two control the clock-out feature of the SPIRIT1, which is * handy if you have a 48MHz crystal and you want to drive an otherwise * crystal-less MCU for USB. */ void spirit1_clockout_on(ot_u8 clk_param) { /// Set the SPIRIT1 to idle, then configure the driver so it never goes into sleep /// or standby, and finally configure the SPIRIT1 to output the clock. #if (BOARD_FEATURE_RFXTALOUT) spirit1drv_smart_ready(); spirit1.clkreq = True; spirit1_write(RFREG(MCU_CK_CONF), clk_param); spirit1_write(RFREG(GPIO3_CONF), (_GPIO_SELECT(RFGPO_MCU_CLK) | _GPIO_MODE_HIDRIVE)); #endif }
void spirit1_wfe_aes() { // Kill any interrupts and activate the WFE event source (always pin 2) EXTI->IMR &= (RFI_SOURCE2 | RFI_SOURCE1 | RFI_SOURCE0); EXTI->EMR |= RFI_SOURCE2; // read-out all IRQ_STATUS bits to clear { ot_u8 cmd[2]; cmd[0] = 1; cmd[1] = RFREG(IRQ_STATUS3); spirit1_spibus_io(2, 4, (ot_u8*)cmd); } // write AES to IRQ MASK spirit1_write(0x40, RFREG(IRQ_MASK3)); }
void spirit1_set_txpwr(ot_u8* pwr_code) { /// Sets the tx output power. /// "pwr_code" is a value, 0-127, that is: eirp_code/2 - 40 = TX dBm /// i.e. eirp_code=0 => -40 dBm, eirp_code=80 => 0 dBm, etc static const ot_u8 pa_lut[84] = { 87, 0x57, 0x56, 0x55, 0x54, 0x53, 0x53, 0x52, 0x52, 0x50, //-30 to -25.5 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4B, 0x4A, 0x49, 0x48, 0x47, //-25 to -20.5 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3C, //-20 to -15.5 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x34, 0x33, 0x32, 0x31, //-15 to -10.5 0x30, 0x2F, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x27, 42, 0x25, //-10 to -5.5 0x24, 0x23, 0x22, 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x19, //-5 to -0.5 30, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, // 0 to 4.5 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, // 5 to 9.5 0x04, 0x03, 1, 0x01 // 10 to 11.5 }; ot_u8 pa_table[10]; ot_u8* cursor; ot_int step; ot_int eirp_val; ///@todo autoscaling algorithm, and refresh value in *pwr_code // Autoscaling: Try to make RSSI at receiver be -90 < RX_RSSI < -80 // The simple algorithm uses the last_rssi and last_linkloss values //if (*pwr_code & 0x80) { //} //else { //} // Not autoscaling: extract TX power directly from pwr_code eirp_val = *pwr_code; // Offset SPIRIT1 PA CFG to match DASH7 PA CFG, plus antenna losses // SPIRIT1: 0 --> -30 dBm, 83 --> 11.5 dBm, half-dBm steps // DASH7: 0 --> -40 dBm, 127 --> 23.5 dBm, half-dBm steps eirp_val += (-10*2) + (RF_HDB_ATTEN); // Adjust base power code in case it is out-of-range: // SPIRIT1 PA starts at -30, DASH7 pwr_code starts at -40. if (eirp_val < 0) eirp_val = 0; else if (eirp_val > 83) eirp_val = 83; // Build PA RAMP using 8 steps of variable size. pa_table[0] = 0; pa_table[1] = RFREG(PAPOWER8); cursor = &pa_table[2]; step = eirp_val >> 3; do { *cursor++ = pa_lut[eirp_val]; eirp_val -= step; } while (cursor != &pa_table[9]); // Write new PA Table to device spirit1_spibus_io(10, 0, pa_table); }
OT_WEAK void rm2_enter_channel(ot_u8 old_chan_id, ot_u8 old_tx_eirp) { static const ot_u8 dr_matrix[32] = { 0, RFREG(MOD1), DRF_MOD1_SS, DRF_MOD0_SS, DRF_FDEV0, DRF_CHFLT_SS, 0, 0, 0, RFREG(MOD1), DRF_MOD1_LS, DRF_MOD0_LS, DRF_FDEV0, DRF_CHFLT_LS, 0, 0, 0, RFREG(MOD1), DRF_MOD1_HS, DRF_MOD0_HS, DRF_FDEV0, DRF_CHFLT_HS, 0, 0, /// @todo change HS to MS (mid speed) 0, RFREG(MOD1), DRF_MOD1_HS, DRF_MOD0_HS, DRF_FDEV0, DRF_CHFLT_HS, 0, 0, }; ot_u8 fc_i; /// Flag PA table reprogram (done before TX): only flag if power is different if (old_tx_eirp != phymac[0].tx_eirp) { rfctl.flags |= RADIO_FLAG_SETPWR; } /// Configure data rate: only change registers if required if ((old_chan_id ^ phymac[0].channel) & 0x30) { ot_u8 offset; offset = (phymac[0].channel & 0x30) >> 1; spirit1_spibus_io(6, 0, (ot_u8*)&dr_matrix[offset]); }
void sx127x_wfe(ot_u16 ifg_sel) { do { __WFE(); } while((EXTI->PR & ifg_sel) == 0); // clear IRQ value in SX127x by setting IRQFLAGS to 0xFF sx127x_write(RFREG(LR_IRQFLAGS), 0xFF); // clear pending register(s) EXTI->PR = ifg_sel; }
void spirit1_wfe() { do { __WFE(); } while((EXTI->PR & RFI_SOURCE2) == 0); // clear pending register EXTI->PR = RFI_SOURCE2; // clear IRQ value in SPIRIT by setting IRQMASK to 0 { ot_u8 cmd[8]; *(ot_u32*)&cmd[0] = 0; *(ot_u32*)&cmd[4] = 0; cmd[1] = RFREG(IRQ_MASK3); spirit1_burstwrite(6, 0, cmd); } }
ot_u8 spirit1_rssi() { return spirit1_read( RFREG(RSSI_LEVEL) ); }
ot_u8 spirit1_txbytes() { return spirit1_read( RFREG(LINEAR_FIFO_STATUS1) ); }
ot_u8 spirit1_rxtime() { return spirit1_read( RFREG(TIMERS4) ); }
ot_u8 spirit1_ldcrtime() { return spirit1_read( RFREG(TIMERS0) ); }
ot_u16 spirit1_mcstate() { static const ot_u8 cmd[2] = { 1, RFREG(MC_STATE1) }; spirit1_spibus_io(2, 2, (ot_u8*)cmd); return (ot_u16)*((ot_u16*)spirit1.busrx); }
} OT_WEAK void spirit1_int_on() { ot_u32 ie_sel; switch (spirit1.imode) { case MODE_Listen: ie_sel = RFI_LISTEN; case MODE_RXData: ie_sel = RFI_RXDATA; case MODE_CSMA: ie_sel = RFI_CSMA; case MODE_TXData: ie_sel = RFI_TXFIFO; default: ie_sel = 0; } spirit1_int_config(ie_sel); } static const ot_u8 gpio_rx[5] = { 0, RFREG(GPIO2_CONF), RFGPO(RX_FIFO_ALMOST_FULL), //indicate buffer threshold condition (kept for RX) RFGPO(SYNC_WORD), //indicate when sync word is qualified RFGPO(TRX_INDICATOR) //indicate when RX is active (falling edge) }; static const ot_u8 gpio_tx[5] = { 0, RFREG(GPIO2_CONF), RFGPO(TX_FIFO_ALMOST_EMPTY), //indicate buffer threshold condition RFGPO(RSSI_ABOVE_THR), //indicate if RSSI goes above/below CCA threshold RFGPO(TRX_INDICATOR) //indicate when TX or RX is active }; inline void spirit1_iocfg_rx() { spirit1_int_clearall(); spirit1_spibus_io(5, 0, (ot_u8*)gpio_rx);