void spirit1_write(ot_u8 addr, ot_u8 data) { ot_u8 cmd[3]; cmd[0] = 0; cmd[1] = addr; cmd[2] = data; spirit1_spibus_io(3, 0, cmd); }
ot_u8 spirit1_read(ot_u8 addr) { ot_u8 cmd[2]; cmd[0] = 1; cmd[1] = addr; spirit1_spibus_io(2, 1, cmd); return spirit1.busrx[0]; }
void spirit1_burstread(ot_u8 start_addr, ot_u8 length, ot_u8* data) { ot_u8 cmd[2]; cmd[0] = 1; cmd[1] = start_addr; spirit1_spibus_io(2, length, (ot_u8*)cmd); memcpy(data, spirit1.busrx, length); }
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); }
void em2_encode_data() { ot_int fill; ot_int load; ot_u8 save[2]; ot_u8* cmd; // Loop unrolling for FIFO loading load = (rfctl.txlimit - spirit1_txbytes()); while (1) { fill = (load < em2.bytes) ? load : em2.bytes; if (fill <= 0) break; if (fill > 24) fill = 24; load -= fill; em2.bytes -= fill; if (txq.options.ubyte[UPPER] != 0) { crc_calc_nstream(fill); # if (M2_FEATURE(RSCODE)) if (em2.lctl & 0x40) { em2_rs_encode(fill); } # endif } cmd = txq.getcursor; save[0] = *(--cmd); *cmd = 0xff; save[1] = *(--cmd); *cmd = 0x00; txq.getcursor += fill; spirit1_spibus_io(fill+2, 0, cmd); *cmd++ = save[1]; *cmd = save[0]; } /// dummy SPI access to complete fill //spirit1_read(RFREG(IRQ_STATUS0)); *(ot_u16*)save = PLATFORM_ENDIAN16_C(0x8000); spirit1_spibus_io(2, 0, save); }
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 em2_decode_data() { static const ot_u8 cmd[] = { 0x01, 0xFF }; ot_u16 grab; em2_decode_data_TOP: grab = spirit1_rxbytes(); if (grab != 0) { if (grab > 24) grab = 24; spirit1_spibus_io(2, grab, (ot_u8*)cmd); q_writestring(&rxq, spirit1.busrx, grab); if (em2.state == 0) { ot_int ext_bytes; em2.state--; em2.bytes = 1 + (ot_int)rxq.front[0]; rxq.front[1] &= ~0x20; // always clear this bit em2.lctl = rxq.front[1]; em2.crc5 = em2_check_crc5(); if (em2.crc5 != 0) { return; } ext_bytes = 0; if (em2.lctl & 0x40) { ext_bytes = em2_rs_init_decode(&rxq); } crc_init_stream(False, em2.bytes-ext_bytes, rxq.getcursor); } crc_calc_nstream(grab); ///@todo we can optimize this also by waiting until crc is done, /// and then verifying that it is not accurate. but we need /// better speed profiling before doing that. # if (M2_FEATURE(RSCODE)) if (em2.lctl & 0x40) { em2_rs_decode(grab); } # endif em2.bytes -= grab; if (em2.bytes > 0) { goto em2_decode_data_TOP; } } }
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 spirit1_load_defaults() { /// The data ordering is: WRITE LENGTH, WRITE HEADER (0), START ADDR, VALUES /// Ignore registers that are set later, are unused, or use the hardware default values. static const ot_u8 spirit1_defaults[] = { 15, 0, 0x01, DRF_ANA_FUNC_CONF0, RFGPO(READY), RFGPO(GND), RFGPO(GND), RFGPO(GND), DRF_MCU_CK_CONF, DRF_IF_OFFSET_ANA, DRF_SYNT3, DRF_SYNT2, DRF_SYNT1, DRF_SYNT0, DRF_CHSPACE, DRF_IF_OFFSET_DIG, 3, 0, 0xB4, DRF_XO_RCO_TEST, 4, 0, 0x9E, DRF_SYNTH_CONFIG1, DRF_SYNTH_CONFIG0, 3, 0, 0x18, DRF_PAPOWER0, 6, 0, 0x1C, DRF_FDEV0, DRF_CHFLT_LS, DRF_AFC2, DRF_AFC1, 7, 0, 0x23, DRF_CLOCKREC, DRF_AGCCTRL2, DRF_AGCCTRL1, DRF_AGCCTRL0, DRF_ANT_SELECT_CONF, 3, 0, 0x3A, DRF_QI, 3, 0, 0x41, DRF_FIFO_CONFIG0, 4, 0, 0x4F, DRF_PCKT_FLT_OPTIONS, DRF_PROTOCOL2, //3, 0, 0x93, RFINT_TX_FIFO_ERROR, 6, 0, 0xA3, DRF_DEM_ORDER, DRF_PM_CONFIG2, DRF_PM_CONFIG1, DRF_PM_CONFIG0, 0 //Terminating 0 }; ot_u8* cursor; cursor = (ot_u8*)spirit1_defaults; while (*cursor != 0) { ot_u8 cmd_len = *cursor++; ot_u8* cmd = cursor; cursor += cmd_len; spirit1_spibus_io(cmd_len, 0, cmd); } // Early debugging test to make sure data was written (look at first write block) //{ // volatile ot_u8 test; // ot_u8 i; // for (i=0x01; i<=0x0D; ++i) { // test = spirit1_read(i); // } //} }
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); }
void spirit1_burstwrite(ot_u8 start_addr, ot_u8 length, ot_u8* cmd_data) { cmd_data[0] = 0; cmd_data[1] = start_addr; spirit1_spibus_io((2+length), 0, cmd_data); }
void spirit1_strobe(ot_u8 strobe) { ot_u8 cmd[2]; cmd[0] = 0x80; cmd[1] = strobe; spirit1_spibus_io(2, 0, cmd); }
inline void spirit1_iocfg_tx() { spirit1_int_clearall(); spirit1_spibus_io(5, 0, (ot_u8*)gpio_tx); }