Text menu driven ANSI/VT100 console test utility for LoRa transceivers
radio chip selection
Radio chip driver is not included, allowing choice of radio device.
If you're using SX1272 or SX1276, then import sx127x driver into your program.
if you're using SX1261 or SX1262, then import sx126x driver into your program.
if you're using SX1280, then import sx1280 driver into your program.
if you're using LR1110, then import LR1110 driver into your program.
If you're using NAmote72 or Murata discovery, then you must import only sx127x driver.
If you're using Type1SJ select target DISCO_L072CZ_LRWAN1
and import sx126x driver into your program.
This is VT100 text-based menu driven test program for SX12xx transceiver devices.
Serial console is divided into horizontally into top half and bottom half.
The bottom half serves as scrolling area to log activity.
The top half serves as menu, to configure the radio.
For all devices, the serial console operates at 115200 8N1, and requires terminal with ANSI-VT100 capability, such as putty/teraterm/minicom etc.
Use program only with keyboard up/down/left/right keys. Enter to change an item, or number for value item. Some items are single bit, requiring only enter key to toggle. Others with fixed choices give a drop-down menu.
radio_sx126x.cpp
- Committer:
- dudmuck
- Date:
- 2 months ago
- Revision:
- 15:703ca340d0fb
- Parent:
- 13:8ce61a1897ab
File content as of revision 15:703ca340d0fb:
#include "radio.h" #ifdef SX126x_H #define CHIP_TYPE_SX1262 0 #define CHIP_TYPE_SX1261 1 #if defined(TARGET_FF_ARDUINO) || defined(TARGET_FF_ARDUINO_UNO) #ifdef TARGET_DISCO_L072CZ_LRWAN1 /* Type1SJ */ SPI spi(PB_15, PB_14, PB_13); // mosi, miso, sclk //spi, nss, busy, dio1 SX126x Radio::radio(spi, PB_12, PC_2, PB_0); DigitalOut antswPower(PA_15); const uint8_t chipType = CHIP_TYPE_SX1262; #define PINNAME_NRST PB_1 void Radio::chipModeChange() { } #else SPI spi(D11, D12, D13); // mosi, miso, sclk //spi, nss, busy, dio1 SX126x Radio::radio(spi, D7, D3, D5); DigitalOut antswPower(D8); AnalogIn xtalSel(A3); DigitalIn chipType(A2); #define PINNAME_NRST A0 #define LED_ON 1 #define LED_OFF 0 DigitalOut tx_led(A4); DigitalOut rx_led(A5); void Radio::chipModeChange() { if (radio.chipMode == CHIPMODE_NONE) { tx_led = LED_OFF; rx_led = LED_OFF; } else if (radio.chipMode == CHIPMODE_TX) { tx_led = LED_ON; rx_led = LED_OFF; } else if (radio.chipMode == CHIPMODE_RX) { tx_led = LED_OFF; rx_led = LED_ON; } } #endif /* !TARGET_DISCO_L072CZ_LRWAN1 */ #endif /* TARGET_FF_ARDUINO */ const char* const Radio::chipNum_str = "SX126x"; ModulationParams_t Radio::mpFSK, Radio::mpLORA; PacketParams_t Radio::ppFSK, Radio::ppLORA; const RadioEvents_t* Radio::RadioEvents; LowPowerTimer Radio::lpt; uint8_t Radio::pktType; uint8_t Radio::bw_idx; uint8_t Radio::cadParams[7]; uint16_t Radio::ppg; unsigned Radio::tcxoDelayTicks; const char* opModes[] = { "SLEEP ", // 0 "STBY_RC ", // 1 "STBY_XOSC", // 2 "FS ", // 3 "RX ", // 4 "TX " // 5 }; void Radio::readChip() { bwSel_t bwSel; shapeCfg_t shapeCfg; unsigned d = radio.readReg(REG_ADDR_BITRATE, 3); printf("%06x:%u->", d ,d); printf("bitrate %ubps\r\n", (32 * XTAL_FREQ_HZ) / d); mpFSK.gfsk.bitrateHi = d >> 16; mpFSK.gfsk.bitrateMid = d >> 8; mpFSK.gfsk.bitrateLo = d; d = radio.readReg(REG_ADDR_FREQDEV, 3); printf("fdev %fKHz\r\n", d / KHZ_TO_FRF); mpFSK.gfsk.fdevHi = d >> 16; mpFSK.gfsk.fdevMid = d >> 8; mpFSK.gfsk.fdevLo = d; shapeCfg.octet = radio.readReg(REG_ADDR_SHAPECFG, 1); mpFSK.gfsk.PulseShape = shapeCfg.octet; bwSel.octet = radio.readReg(REG_ADDR_BWSEL, 1); // GFSK_RX_BW_* printf("bwsSel:%02x\r\n", bwSel.octet); mpFSK.gfsk.bandwidth = bwSel.octet; { unsigned n = radio.readReg(REG_ADDR_FSK_PREAMBLE_TXLEN , 2); ppFSK.gfsk.PreambleLengthHi = n << 8; // param1 ppFSK.gfsk.PreambleLengthLo = n;// param2 } { pktCtrl1_t pktCtrl1; pktCtrl1.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL1, 1); ppFSK.gfsk.PreambleDetectorLength = pktCtrl1.octet & 0x07; // param3 } ppFSK.gfsk.SyncWordLength = radio.readReg(REG_ADDR_FSK_SYNC_LEN, 1);// param4 ppFSK.gfsk.AddrComp = radio.readReg(REG_ADDR_NODEADDRCOMP, 1);// param5 { pktCtrl0_t pktCtrl0; pktCtrl0.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL0, 1); ppFSK.gfsk.PacketType = pktCtrl0.bits.pkt_len_format; // param6 } ppFSK.gfsk.PayloadLength = radio.readReg(REG_ADDR_FSK_PAYLOAD_LEN, 1);// param7 { pktCtrl2_t pktCtrl2; pktCtrl2.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL2, 1); ppFSK.gfsk.CRCType = pktCtrl2.octet & 0x7; // param8 ppFSK.gfsk.Whitening = pktCtrl2.bits.whit_enable; // param9 } /*******************************/ { loraConfig0_t conf0; conf0.octet = radio.readReg(REG_ADDR_LORA_CONFIG0, 1); printf("LoRa bw%u sf%u ", conf0.bits.modem_bw, conf0.bits.modem_sf); mpLORA.lora.spreadingFactor = conf0.bits.modem_sf; mpLORA.lora.bandwidth = conf0.bits.modem_bw; } { loraConfig1_t conf1; conf1.octet = radio.readReg(REG_ADDR_LORA_CONFIG1, 1); mpLORA.lora.LowDatarateOptimize = conf1.bits.ppm_offset; ppLORA.lora.HeaderType = conf1.bits.implicit_header; ppLORA.lora.InvertIQ = conf1.bits.rx_invert_iq; mpLORA.lora.codingRate = conf1.bits.tx_coding_rate; } { loraConfig2_t conf2; conf2.octet = radio.readReg(REG_ADDR_LORA_CONFIG2, 1); ppLORA.lora.CRCType = conf2.bits.tx_payload_crc16_en; } { uint32_t val = radio.readReg(REG_ADDR_LORA_PREAMBLE_SYMBNB, 2); ppLORA.lora.PreambleLengthHi = val >> 8; ppLORA.lora.PreambleLengthLo = val; } { AnaCtrl6_t AnaCtrl6; AnaCtrl7_t AnaCtrl7; PaCtrl1b_t PaCtrl1b; AnaCtrl6.octet = radio.readReg(REG_ADDR_ANACTRL6, 1); pa_config_buf[0] = AnaCtrl6.bits.pa_dctrim_select_ana; // paDutyCycle AnaCtrl7.octet = radio.readReg(REG_ADDR_ANACTRL7, 1); pa_config_buf[1] = AnaCtrl7.bits.pa_hp_sel_ana; // hpMax PaCtrl1b.octet = radio.readReg(REG_ADDR_PA_CTRL1B, 1); pa_config_buf[2] = PaCtrl1b.bits.tx_mode_bat; // deviceSel pa_config_buf[3] = 1; // paLut } { cadParams[0] = radio.readReg(REG_ADDR_LORA_CONFIG9, 1); cadParams[0] >>= 5; cadParams[1] = radio.readReg(REG_ADDR_LORA_CAD_PN_RATIO, 1); cadParams[2] = radio.readReg(REG_ADDR_LORA_CAD_MINPEAK, 1); } pa_config_buf[2] = chipType; /* auto-detect device from pin pull */ radio.xfer(OPCODE_SET_PA_CONFIG, 4, 0, pa_config_buf); } void Radio::hw_reset() { radio.hw_reset(PINNAME_NRST); } void Radio::clearIrqFlags() { uint8_t buf[2]; buf[0] = 0x03; buf[1] = 0xff; radio.xfer(OPCODE_CLEAR_IRQ_STATUS, 2, 0, buf); } uint8_t Radio::get_payload_length() { pktType = radio.getPacketType(); if (pktType == PACKET_TYPE_GFSK) { ppFSK.gfsk.PayloadLength = radio.readReg(REG_ADDR_FSK_PAYLOAD_LEN, 1); return ppFSK.gfsk.PayloadLength; } else if (pktType == PACKET_TYPE_LORA) { ppLORA.lora.PayloadLength = radio.readReg(REG_ADDR_LORA_TXPKTLEN, 1); return ppLORA.lora.PayloadLength; } else return 0; } const char* const Radio::opmode_status_strs[] = { "<0> ", // 0 "RFU ", // 1 "STBY_RC ", // 2 "STBY_XOSC", // 3 "FS ", // 4 "RX ", // 5 "TX ", // 6 "<7> ", // 7 NULL }; const char* const Radio::opmode_select_strs[] = { "SLEEP ", // 0 "STDBY_RC ", // 1 "STDBY_XOSC", // 2 "FS ", // 3 "RX ", // 4 "TX ", // 5 NULL }; unsigned Radio::opmode_read(bool forWriting) { status_t status; radio.xfer(OPCODE_GET_STATUS, 0, 1, &status.octet); if (forWriting) { /* translate opmode_status_strs to opmode_select_strs */ switch (status.bits.chipMode) { case 2: return 1; // STBY_RC case 3: return 2; // STBY_XOSC case 4: return 3; // FS case 5: return 4; // RX case 6: return 5; // TX default: return 0; } } else return status.bits.chipMode; } menuMode_e Radio::opmode_write(unsigned sel) { switch (sel) { case 0: antswPower = 0; radio.setSleep(true, false); break; case 1: antswPower = 0; radio.setStandby(STBY_RC); break; case 2: antswPower = 0; radio.setStandby(STBY_XOSC); break; case 3: antswPower = 0; radio.setFS(); break; case 4: antswPower = 1; radio.start_rx(0); break; case 5: antswPower = 1; { uint8_t buf[3]; buf[0] = 0; buf[0] = 0; buf[1] = 0; radio.xfer(OPCODE_SET_TX, 3, 0, buf); } break; } return MENUMODE_REDRAW; } void Radio::setFS() { radio.setFS(); } const char* const Radio::pktType_strs[] = { "GFSK ", "LORA ", NULL }; unsigned Radio::pktType_read(bool fw) { return radio.getPacketType(); } menuMode_e Radio::pktType_write(unsigned idx) { radio.setPacketType(idx); return MENUMODE_REINIT_MENU; } void Radio::tx_carrier() { radio.SetDIO2AsRfSwitchCtrl(1); antswPower = 1; radio.xfer(OPCODE_SET_TX_CARRIER, 0, 0, NULL); } void Radio::tx_preamble() { radio.SetDIO2AsRfSwitchCtrl(1); antswPower = 1; radio.xfer(OPCODE_SET_TX_PREAMBLE, 0, 0, NULL); } void Radio::get_rssi() { uint8_t buf[3]; radio.xfer(OPCODE_GET_RSSIINST, 0, 3, buf); log_printf("-%0.1f dBm\r\n", buf[1]/2.0); } void Radio::txPkt() { uint8_t txlen = get_payload_length(); radio.SetDIO2AsRfSwitchCtrl(1); antswPower = 1; radio.setBufferBase(0, 0); { uint8_t buf[8]; IrqFlags_t irqEnable; irqEnable.word = 0; irqEnable.bits.TxDone = 1; irqEnable.bits.Timeout = 1; buf[0] = irqEnable.word >> 8; // enable bits buf[1] = irqEnable.word; // enable bits buf[2] = irqEnable.word >> 8; // dio1 buf[3] = irqEnable.word; // dio1 buf[4] = 0; // dio2 buf[5] = 0; // dio2 buf[6] = 0; // dio3 buf[7] = 0; // dio3 radio.xfer(OPCODE_SET_DIO_IRQ_PARAMS, 8, 0, buf); } radio.start_tx(txlen); } uint8_t Radio::tx_param_buf[2]; uint8_t Radio::pa_config_buf[4]; void Radio::tx_dbm_print() { PwrCtrl_t PwrCtrl; PaCtrl1b_t PaCtrl1b; unsigned v = radio.readReg(REG_ADDR_ANACTRL16, 1); if (v & 0x10) { printf("%d", PA_OFF_DBM); return; } PwrCtrl.octet = radio.readReg(REG_ADDR_PWR_CTRL, 1); PaCtrl1b.octet = radio.readReg(REG_ADDR_PA_CTRL1B, 1); pa_config_buf[2] = PaCtrl1b.bits.tx_mode_bat; // deviceSel if (PaCtrl1b.bits.tx_mode_bat) printf("%d", PwrCtrl.bits.tx_pwr - 17); else printf("%d", PwrCtrl.bits.tx_pwr - 9); } bool Radio::tx_dbm_write(const char* str) { int dbm; unsigned v = radio.readReg(REG_ADDR_ANACTRL16, 1); sscanf(str, "%d", &dbm); if (dbm == PA_OFF_DBM) { /* bench test: prevent overloading receiving station (very low tx power) */ v |= 0x10; // pa dac atb tst radio.writeReg(REG_ADDR_ANACTRL16, v, 1); } else { tx_param_buf[0] = dbm; radio.xfer(OPCODE_SET_TX_PARAMS, 2, 0, tx_param_buf); if (v & 0x10) { v &= ~0x10; radio.writeReg(REG_ADDR_ANACTRL16, v, 1); } } return false; } const char* Radio::tx_ramp_strs[] = { "10 ", // 0 "20 ", // 1 "40 ", // 2 "80 ", // 3 "200 ", // 4 "800 ", // 5 "1700", // 6 "3400", // 7 NULL }; unsigned Radio::tx_ramp_read(bool fw) { PwrCtrl_t PwrCtrl; PwrCtrl.octet = radio.readReg(REG_ADDR_PWR_CTRL, 1); tx_param_buf[1] = PwrCtrl.octet >> 5; return PwrCtrl.bits.ramp_time; } menuMode_e Radio::tx_ramp_write(unsigned sidx) { tx_param_buf[1] = sidx; radio.xfer(OPCODE_SET_TX_PARAMS, 2, 0, tx_param_buf); return MENUMODE_REDRAW; } void Radio::set_payload_length(uint8_t len) { pktType = radio.getPacketType(); if (pktType == PACKET_TYPE_GFSK) { ppFSK.gfsk.PayloadLength = len; radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); } else if (pktType == PACKET_TYPE_LORA) { ppLORA.lora.PayloadLength = len; radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, ppLORA.buf); } } void Radio::tx_payload_length_print() { printf("%u", get_payload_length()); } bool Radio::tx_payload_length_write(const char* txt) { unsigned len; sscanf(txt, "%u", &len); set_payload_length(len); return false; } bool Radio::service(int8_t statusRow) { static uint8_t prevRxStatus; static uint8_t prevPktCtrl0; uint8_t buf[4]; static IrqFlags_t prevIrqFlags; IrqFlags_t irqFlags; bool ret = false; //static us_timestamp_t prev_now; //static uint64_t prev_now; static Kernel::Clock::time_point prev_now; //us_timestamp_t now = lpt.read_us(); //uint64_t now = Kernel::get_ms_count(); Kernel::Clock::time_point now = Kernel::Clock::now(); radio.service(); if (statusRow > 0 && now-prev_now > 50ms) { uint8_t rxStatus, pktCtrl0; bool chg = false; radio.xfer(OPCODE_GET_IRQ_STATUS, 0, 3, buf); irqFlags.word = buf[1] << 8; irqFlags.word |= buf[2]; rxStatus = radio.readReg(0x6c9, 1); if (rxStatus != prevRxStatus) { chg = true; prevRxStatus = rxStatus; } pktCtrl0 = radio.readReg(0x6b3, 1); if (pktCtrl0 != prevPktCtrl0) { chg = true; prevPktCtrl0 = pktCtrl0; } if (irqFlags.word != prevIrqFlags.word && chg) { printf("\e[%u;1f", statusRow); // set (force) cursor to row;column printf("%02x ", rxStatus); printf("%02x ", pktCtrl0); if (irqFlags.bits.TxDone) printf("TxDone "); if (irqFlags.bits.RxDone) printf("RxDone "); if (irqFlags.bits.PreambleDetected) printf("PreambleDetected "); if (irqFlags.bits.SyncWordValid) printf("SyncWordValid "); if (irqFlags.bits.HeaderValid) printf("HeaderValid "); if (irqFlags.bits.HeaderErr) printf("HeaderErr "); if (irqFlags.bits.CrCerr) printf("CrCerr "); if (irqFlags.bits.CadDone) printf("CadDone "); if (irqFlags.bits.CadDetected) printf("CadDetected "); if (irqFlags.bits.Timeout) printf("Timeout "); printf("\e[K"); ret = true; prevIrqFlags.word = irqFlags.word; } prev_now = now; } return ret; } void Radio::Rx() { antswPower = 1; { uint8_t buf[8]; IrqFlags_t irqEnable; irqEnable.word = 0; irqEnable.bits.RxDone = 1; irqEnable.bits.Timeout = 1; buf[0] = 3;//irqEnable.word >> 8; // enable bits buf[1] = 0xff;//irqEnable.word; // enable bits buf[2] = irqEnable.word >> 8; // dio1 buf[3] = irqEnable.word; // dio1 buf[4] = 0; // dio2 buf[5] = 0; // dio2 buf[6] = 0; // dio3 buf[7] = 0; // dio3 radio.xfer(OPCODE_SET_DIO_IRQ_PARAMS, 8, 0, buf); } radio.start_rx(RX_TIMEOUT_CONTINUOUS); } void Radio::rxDone(uint8_t size, float rssi, float snr) { if (pktType == PACKET_TYPE_GFSK) { int16_t cfo = radio.readReg(REG_ADDR_FSK_DEMOD_CFO, 2); // justify 12bit to 16bit signed if (cfo & 0x0800) cfo |= 0xf000; log_printf("cfo:%d\r\n", cfo); } else if (pktType == PACKET_TYPE_LORA) { const float bwkhzs[] = { 7.81, 10.42, 15.63, 20.83, 31.25, 41.67, 62.5, 125, 250, 500 }; int hz; int32_t fei; loraStatus1_t loraStatus1; loraStatus1.dword = radio.readReg(REG_ADDR_LORA_STATUS, 3); if (loraStatus1.bits.est_freq_error & 0x80000) fei = 0xfff00000 | loraStatus1.bits.est_freq_error; else fei = loraStatus1.bits.est_freq_error; //hz = fei * HZ_TO_FRF * bwkhzs[bw_idx]/500; hz = fei * -HZ_TO_FRF * bwkhzs[bw_idx]/1000; log_printf("hz:%d\r\n", hz); } RadioEvents->RxDone(size, rssi, snr); } void Radio::txDoneBottom() { if (RadioEvents->TxDone_botHalf) RadioEvents->TxDone_botHalf(); antswPower = 0; } /*void Radio::cadDone(bool det) { log_printf("cadDone "); if (det) printf("CadDetected"); printf("\r\n"); }*/ uint8_t ana_regs[128]; void Radio::boardInit(const RadioEvents_t* e) { hw_reset(); radio.txDone = txDoneBottom; radio.rxDone = rxDone; radio.cadDone = cadDone; radio.chipModeChange = chipModeChange; readChip(); RadioEvents = e; lpt.start(); radio.SetDIO2AsRfSwitchCtrl(1); tcxoDelayTicks = 3200; // some default timeout for tcxo } bool Radio::deviceSel_read() { PaCtrl1b_t PaCtrl1b; PaCtrl1b.octet = radio.readReg(REG_ADDR_PA_CTRL1B, 1); pa_config_buf[2] = PaCtrl1b.bits.tx_mode_bat; // deviceSel return PaCtrl1b.bits.tx_mode_bat; } bool Radio::deviceSel_push() { /* if (pa_config_buf[2]) pa_config_buf[2] = 0; else pa_config_buf[2] = 1;*/ pa_config_buf[2] ^= 1; radio.xfer(OPCODE_SET_PA_CONFIG, 4, 0, pa_config_buf); return pa_config_buf[2]; } const toggle_item_t Radio::deviceSel_item = { _ITEM_TOGGLE, "SX1262", "SX1261", deviceSel_read, deviceSel_push}; static const char* const paDutyCycles[] = { "0", "1", "2", "3", "4", "5", "6", "7", NULL }; unsigned Radio::paDutyCycle_read(bool forWriting) { AnaCtrl6_t AnaCtrl6; AnaCtrl6.octet = radio.readReg(REG_ADDR_ANACTRL6, 1); pa_config_buf[0] = AnaCtrl6.bits.pa_dctrim_select_ana; return AnaCtrl6.bits.pa_dctrim_select_ana; } menuMode_e Radio::paDutyCycle_write(unsigned sidx) { pa_config_buf[0] = sidx; radio.xfer(OPCODE_SET_PA_CONFIG, 4, 0, pa_config_buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::paDutyCycle_item = { _ITEM_DROPDOWN, paDutyCycles, paDutyCycles, paDutyCycle_read, paDutyCycle_write}; static const char* const hpMaxs[] = { "0", "1", "2", "3", "4", "5", "6", "7", NULL }; unsigned Radio::hpMax_read(bool forWriting) { AnaCtrl7_t AnaCtrl7; AnaCtrl7.octet = radio.readReg(REG_ADDR_ANACTRL7, 1); pa_config_buf[1] = AnaCtrl7.bits.pa_hp_sel_ana; return AnaCtrl7.bits.pa_hp_sel_ana; } menuMode_e Radio::hpMax_write(unsigned sidx) { pa_config_buf[1] = sidx; radio.xfer(OPCODE_SET_PA_CONFIG, 4, 0, pa_config_buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::hpMax_item = { _ITEM_DROPDOWN, hpMaxs, hpMaxs, hpMax_read, hpMax_write}; void Radio::ocp_print() { uint8_t ocp = radio.readReg(REG_ADDR_OCP, 1); printf("%.1f", ocp * 2.5); } bool Radio::ocp_write(const char* txt) { float mA; if (sscanf(txt, "%f", &mA) == 1) radio.writeReg(REG_ADDR_OCP, mA / 2.5, 1); return false; } const value_item_t Radio::ocp_item = { _ITEM_VALUE, 5, ocp_print, ocp_write}; void Radio::xta_print() { uint8_t trim = radio.readReg(REG_ADDR_XTA_TRIM, 1); printf("%02x", trim); } bool Radio::xta_write(const char* txt) { unsigned trim; if (sscanf(txt, "%x", &trim) == 1) radio.writeReg(REG_ADDR_XTA_TRIM, trim, 1); return false; } const value_item_t Radio::xta_item = { _ITEM_VALUE, 3, xta_print, xta_write}; void Radio::xtb_print() { uint8_t trim = radio.readReg(REG_ADDR_XTB_TRIM, 1); printf("%02x", trim); } bool Radio::xtb_write(const char* txt) { unsigned trim; if (sscanf(txt, "%x", &trim) == 1) radio.writeReg(REG_ADDR_XTB_TRIM, trim, 1); return false; } const value_item_t Radio::xtb_item = { _ITEM_VALUE, 3, xtb_print, xtb_write}; void Radio::ldo_push() { uint8_t buf = 0; radio.xfer(OPCODE_SET_REGULATOR_MODE, 1, 0, &buf); log_printf("-> LDO\r\n"); } const button_item_t Radio::ldo_item = { _ITEM_BUTTON, "LDO", ldo_push }; void Radio::dcdc_push() { uint8_t buf = 1; radio.xfer(OPCODE_SET_REGULATOR_MODE, 1, 0, &buf); log_printf("-> DC-DC\r\n"); } const button_item_t Radio::dcdc_item = { _ITEM_BUTTON, "DCDC", dcdc_push }; static const char* const rxfe_pms[] = { "LP 0dB", "HP1 2dB", "HP2 4dB", "HP3 6dB", NULL }; unsigned Radio::rxfe_pm_read(bool) { AgcSensiAdj_t agcs; agcs.octet = radio.readReg(REG_ADDR_AGC_SENSI_ADJ, 1); return agcs.bits.power_mode; } menuMode_e Radio::rxfe_pm_write(unsigned sidx) { AgcSensiAdj_t agcs; agcs.octet = radio.readReg(REG_ADDR_AGC_SENSI_ADJ, 1); agcs.bits.power_mode = sidx; radio.writeReg(REG_ADDR_AGC_SENSI_ADJ, agcs.octet, 1); return MENUMODE_REDRAW; } const dropdown_item_t Radio::rxfe_pm_item = { _ITEM_DROPDOWN, rxfe_pms, rxfe_pms, rxfe_pm_read, rxfe_pm_write}; #ifdef MEMSCAN #define SHADOW_SIZE 0x9ff uint8_t Radio::shadow_read[SHADOW_SIZE]; void Radio::memread_push() { unsigned addr; for (addr = 0; addr < SHADOW_SIZE; addr++) { shadow_read[addr] = radio.readReg(addr, 1); } log_printf("memread\r\n"); } const button_item_t Radio::memread_item = { _ITEM_BUTTON, "MRead", memread_push }; void Radio::memcmp_push() { unsigned addr; uint8_t r; for (addr = 0; addr < SHADOW_SIZE; addr++) { r = radio.readReg(addr, 1); if (shadow_read[addr] != r) { log_printf("%03x: %02x -> %02x\r\n", addr, shadow_read[addr], r); } } log_printf("memcmpDone\r\n"); } const button_item_t Radio::memcmp_item = { _ITEM_BUTTON, "MCmp", memcmp_push }; #endif /* MEMSCAN */ void to_big_endian24(uint32_t in, uint8_t *out) { out[2] = in & 0xff; in >>= 8; out[1] = in & 0xff; in >>= 8; out[0] = in & 0xff; } void Radio::tcxo_volts_print(void) { // yyy; } bool Radio::tcxo_volts_write(const char *txt) { uint8_t buf[4]; float volts; sscanf(txt, "%f", &volts); if (volts > 3.15) buf[0] = 7; // 3.3v else if (volts > 2.85) buf[0] = 6; // 3.0v else if (volts > 2.55) buf[0] = 5; // 2.7v else if (volts > 2.3) buf[0] = 4; // 2.4v else if (volts > 2.3) buf[0] = 3; // 2.2v else if (volts > 2.0) buf[0] = 2; // 1.8v else if (volts > 1.65) buf[0] = 1; // 1.7v else buf[0] = 0; // 1.6v to_big_endian24(tcxoDelayTicks, buf+1); radio.xfer(OPCODE_SET_DIO3_AS_TCXO_CTRL, 4, 0, buf); log_printf("set txco %u, %u\r\n", buf[0], tcxoDelayTicks); return false; } const value_item_t Radio::tcxo_volts_item = { _ITEM_VALUE, 3, tcxo_volts_print, tcxo_volts_write}; void Radio::tcxo_delay_print(void) { printf("%u", tcxoDelayTicks / 64); } bool Radio::tcxo_delay_write(const char *txt) { unsigned ms; sscanf(txt, "%u", &ms); tcxoDelayTicks = ms * 64; return false; } const value_item_t Radio::tcxo_delay_item = { _ITEM_VALUE, 5, tcxo_delay_print, tcxo_delay_write}; const menu_t Radio::common_menu[] = { { {FIRST_CHIP_MENU_ROW, 1}, "deviceSel:", &deviceSel_item, FLAG_MSGTYPE_ALL, &tx_dbm_item }, { {FIRST_CHIP_MENU_ROW, 18}, "paDutyCycle:", &paDutyCycle_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW, 33}, "hpMax:", &hpMax_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW, 42}, "ocp mA:", &ocp_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW, 55}, "XTA:", &xta_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW, 62}, "XTB:", &xtb_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+1, 1}, NULL, &ldo_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+1, 5}, NULL, &dcdc_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+1, 12}, "rxfe power:", &rxfe_pm_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+1, 35}, "tcxoVolts:", &tcxo_volts_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+1, 50}, "tcxoDelay(ms):", &tcxo_delay_item, FLAG_MSGTYPE_ALL }, #ifdef MEMSCAN { {LAST_CHIP_MENU_ROW, 1}, NULL, &memread_item, FLAG_MSGTYPE_ALL }, { {LAST_CHIP_MENU_ROW, 10}, NULL, &memcmp_item, FLAG_MSGTYPE_ALL }, #endif /* MEMSCAN */ { {0, 0}, NULL, NULL } }; const uint8_t loraBWs[] = { LORA_BW_7, LORA_BW_10, LORA_BW_15, LORA_BW_20, LORA_BW_31, LORA_BW_41, LORA_BW_62, LORA_BW_125, LORA_BW_250, LORA_BW_500 }; static const char* const lora_bwstrs[] = { " 7.81KHz", "10.42KHz", "15.63KHz", "20.83KHz", "31.25KHz", "41.67KHz", " 62.5KHz", " 125KHz", " 250KHz", " 500KHz", NULL }; unsigned Radio::lora_bw_read(bool forWriting) { unsigned n; loraConfig0_t conf0; conf0.octet = radio.readReg(REG_ADDR_LORA_CONFIG0, 1); mpLORA.lora.bandwidth = conf0.bits.modem_bw; for (n = 0; n < sizeof(loraBWs); n++) { if (conf0.bits.modem_bw == loraBWs[n]) { bw_idx = n; return n; } } return sizeof(loraBWs); } menuMode_e Radio::lora_bw_write(unsigned sidx) { mpLORA.lora.bandwidth = loraBWs[sidx]; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 4, 0, mpLORA.buf); bw_idx = sidx; return MENUMODE_REDRAW; } const dropdown_item_t Radio::lora_bw_item = { _ITEM_DROPDOWN, lora_bwstrs, lora_bwstrs, lora_bw_read, lora_bw_write}; void Radio::lora_sf_print() { loraConfig0_t conf0; conf0.octet = radio.readReg(REG_ADDR_LORA_CONFIG0, 1); mpLORA.lora.spreadingFactor = conf0.bits.modem_sf; printf("%u", conf0.bits.modem_sf); } bool Radio::lora_sf_write(const char* str) { unsigned sf; if (sscanf(str, "%u", &sf) == 1) { mpLORA.lora.spreadingFactor = sf; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 4, 0, mpLORA.buf); } return false; } const value_item_t Radio::lora_sf_item = { _ITEM_VALUE, 3, lora_sf_print, lora_sf_write }; static const char* const lora_crs[] = { "4/5 ", "4/6 ", "4/7 ", "4/8 ", NULL }; unsigned Radio::lora_cr_read(bool forWriting) { loraConfig1_t conf1; conf1.octet = radio.readReg(REG_ADDR_LORA_CONFIG1, 1); mpLORA.lora.codingRate = conf1.bits.tx_coding_rate; return conf1.bits.tx_coding_rate; } menuMode_e Radio::lora_cr_write(unsigned sidx) { mpLORA.lora.codingRate = sidx; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 4, 0, mpLORA.buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::lora_cr_item = { _ITEM_DROPDOWN, lora_crs, lora_crs, lora_cr_read, lora_cr_write}; bool Radio::ppmOffset_read() { loraConfig1_t conf1; conf1.octet = radio.readReg(REG_ADDR_LORA_CONFIG1, 1); mpLORA.lora.LowDatarateOptimize = conf1.bits.ppm_offset; return conf1.bits.ppm_offset; } bool Radio::ppmOffset_push() { if (mpLORA.lora.LowDatarateOptimize) mpLORA.lora.LowDatarateOptimize = 0; else mpLORA.lora.LowDatarateOptimize = 1; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 4, 0, mpLORA.buf); return mpLORA.lora.LowDatarateOptimize; } const toggle_item_t Radio::lora_ppmOffset_item = { _ITEM_TOGGLE, "LowDatarateOptimize", NULL, ppmOffset_read, ppmOffset_push}; void Radio::lora_pblLen_print() { unsigned val = radio.readReg(REG_ADDR_LORA_PREAMBLE_SYMBNB, 2); ppLORA.lora.PreambleLengthHi = val >> 8; ppLORA.lora.PreambleLengthLo = val; printf("%u", val); } bool Radio::lora_pblLen_write(const char* txt) { unsigned n; if (sscanf(txt, "%u", &n) == 1) { ppLORA.lora.PreambleLengthHi = n >> 8; ppLORA.lora.PreambleLengthLo = n; radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, ppLORA.buf); } return false; } const value_item_t Radio::lora_pblLen_item = { _ITEM_VALUE, 5, lora_pblLen_print, lora_pblLen_write}; bool Radio::lora_headerType_read() { loraConfig1_t conf1; conf1.octet = radio.readReg(REG_ADDR_LORA_CONFIG1, 1); ppLORA.lora.HeaderType = conf1.bits.implicit_header; return conf1.bits.implicit_header; } bool Radio::lora_headerType_push() { if (ppLORA.lora.HeaderType) ppLORA.lora.HeaderType = 0; else ppLORA.lora.HeaderType = 1; radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, ppLORA.buf); return ppLORA.lora.HeaderType; } const toggle_item_t Radio::lora_headerType_item = { _ITEM_TOGGLE, "EXPLICIT", "IMPLICIT", lora_headerType_read, lora_headerType_push}; bool Radio::lora_crcon_read() { loraConfig2_t conf2; conf2.octet = radio.readReg(REG_ADDR_LORA_CONFIG2, 1); ppLORA.lora.CRCType = conf2.bits.tx_payload_crc16_en; return conf2.bits.tx_payload_crc16_en; } bool Radio::lora_crcon_push() { if (ppLORA.lora.CRCType) ppLORA.lora.CRCType = 0; else ppLORA.lora.CRCType = 1; radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, ppLORA.buf); return ppLORA.lora.CRCType; } const toggle_item_t Radio::lora_crcon_item = { _ITEM_TOGGLE, "CrcOn", NULL, lora_crcon_read, lora_crcon_push}; bool Radio::lora_inviq_read() { loraConfig1_t conf1; conf1.octet = radio.readReg(REG_ADDR_LORA_CONFIG1, 1); ppLORA.lora.InvertIQ = conf1.bits.rx_invert_iq; return conf1.bits.rx_invert_iq; } bool Radio::lora_inviq_push() { if (ppLORA.lora.InvertIQ) ppLORA.lora.InvertIQ = 0; else ppLORA.lora.InvertIQ = 1; radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, ppLORA.buf); return ppLORA.lora.InvertIQ; } const toggle_item_t Radio::lora_inviq_item = { _ITEM_TOGGLE, "InvertIQ", NULL, lora_inviq_read, lora_inviq_push}; void Radio::lora_ppg_print() { uint8_t val; ppg = radio.readReg(REG_ADDR_LORA_SYNC, 2); val = (ppg >> 8) & 0xf0; val |= (ppg & 0xf0) >> 4; printf("%02x", val); } bool Radio::lora_ppg_write(const char* txt) { unsigned val; if (sscanf(txt, "%x", &val) == 1) { ppg &= 0x0707; ppg |= (val & 0xf0) << 8; ppg |= (val & 0x0f) << 4; radio.writeReg(REG_ADDR_LORA_SYNC, ppg, 2); } return false; } const value_item_t Radio::lora_ppg_item = { _ITEM_VALUE, 4, lora_ppg_print, lora_ppg_write}; bool Radio::lora_sdmode_read() { sdCfg0_t sdcfg; sdcfg.octet = radio.readReg(REG_ADDR_SDCFG0, 1); return sdcfg.bits.sd_mode; } bool Radio::lora_sdmode_push() { sdCfg0_t sdcfg; sdcfg.octet = radio.readReg(REG_ADDR_SDCFG0, 1); sdcfg.bits.sd_mode ^= 1; radio.writeReg(REG_ADDR_SDCFG0, sdcfg.octet, 1); return sdcfg.bits.sd_mode; } const toggle_item_t Radio::lora_sdmode_item = { _ITEM_TOGGLE, "sd_mode", NULL, lora_sdmode_read, lora_sdmode_push}; void Radio::cad_push() { { uint8_t buf[8]; IrqFlags_t irqEnable; irqEnable.word = 0; irqEnable.bits.RxDone = 1; irqEnable.bits.Timeout = 1; irqEnable.bits.CadDetected = 1; irqEnable.bits.CadDone = 1; buf[0] = 3;//irqEnable.word >> 8; // enable bits buf[1] = 0xff;//irqEnable.word; // enable bits buf[2] = irqEnable.word >> 8; // dio1 buf[3] = irqEnable.word; // dio1 buf[4] = 0; // dio2 buf[5] = 0; // dio2 buf[6] = 0; // dio3 buf[7] = 0; // dio3 radio.xfer(OPCODE_SET_DIO_IRQ_PARAMS, 8, 0, buf); } radio.setCAD(); } const button_item_t Radio::lora_cad_item = { _ITEM_BUTTON, "CAD", cad_push }; static const char* const lora_cadsymbs[] = { " 1", " 2", " 4", " 8", "16", NULL }; unsigned Radio::lora_cadsymbs_read(bool forWriting) { cadParams[0] = radio.readReg(REG_ADDR_LORA_CONFIG9, 1); cadParams[0] >>= 5; return cadParams[0]; } menuMode_e Radio::lora_cadsymbs_write(unsigned sidx) { cadParams[0] = sidx; radio.xfer(OPCODE_SET_CAD_PARAM, 7, 0, cadParams); return MENUMODE_REDRAW; } const dropdown_item_t Radio::lora_cadsymbs_item = { _ITEM_DROPDOWN, lora_cadsymbs, lora_cadsymbs, lora_cadsymbs_read, lora_cadsymbs_write}; void Radio::lora_cadpnratio_print() { cadParams[1] = radio.readReg(REG_ADDR_LORA_CAD_PN_RATIO, 1); printf("%u", cadParams[1]); } bool Radio::lora_cadpnratio_write(const char* txt) { unsigned n; sscanf(txt, "%u", &n); cadParams[1] = n; radio.xfer(OPCODE_SET_CAD_PARAM, 7, 0, cadParams); return false; } const value_item_t Radio::lora_cadpnratio_item = { _ITEM_VALUE, 4, lora_cadpnratio_print, lora_cadpnratio_write}; void Radio::lora_cadmin_print() { cadParams[2] = radio.readReg(REG_ADDR_LORA_CAD_MINPEAK, 1); printf("%u", cadParams[2]); } bool Radio::lora_cadmin_write(const char* txt) { unsigned n; sscanf(txt, "%u", &n); cadParams[2] = n; radio.xfer(OPCODE_SET_CAD_PARAM, 7, 0, cadParams); return false; } const value_item_t Radio::lora_cadmin_item = { _ITEM_VALUE, 4, lora_cadmin_print, lora_cadmin_write}; bool Radio::lora_cadexit_read() { return cadParams[3]; } bool Radio::lora_cadexit_push() { if (cadParams[3]) cadParams[3] = 0; else cadParams[3] = 1; radio.xfer(OPCODE_SET_CAD_PARAM, 7, 0, cadParams); return cadParams[3]; } const toggle_item_t Radio::lora_cadexit_item = { _ITEM_TOGGLE, "CAD_ONLY", "CAD_RX ", lora_cadexit_read, lora_cadexit_push}; void Radio::lora_cadtimeout_print(void) { unsigned n; n = cadParams[4]; n <<= 8; n += cadParams[5]; n <<= 8; n += cadParams[6]; printf("%u", n); } bool Radio::lora_cadtimeout_write(const char* txt) { unsigned n; float ticks; sscanf(txt, "%u", &n); ticks = n / 15.625; n = ticks; cadParams[4] = n >> 16; cadParams[5] = n >> 8; cadParams[6] = n; return false; } const value_item_t Radio::lora_cadtimeout_item = { _ITEM_VALUE, 4, lora_cadtimeout_print, lora_cadtimeout_write}; const menu_t Radio::lora_menu[] = { { {FIRST_CHIP_MENU_ROW+2, 1}, NULL, &lora_bw_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 12}, "sf:", &lora_sf_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 20}, "cr:", &lora_cr_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 30}, NULL, &lora_ppmOffset_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+3, 1}, "PreambleLength:", &lora_pblLen_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+3, 22}, NULL, &lora_headerType_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+3, 32}, NULL, &lora_crcon_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+3, 39}, NULL, &lora_inviq_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+3, 49}, "ppg:", &lora_ppg_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+3, 58}, NULL, &lora_sdmode_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+4, 1}, NULL, &lora_cad_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+4, 5}, "symbols:", &lora_cadsymbs_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+4, 20}, "peak/noise:", &lora_cadpnratio_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+4, 35}, "min:", &lora_cadmin_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+4, 45}, "exit:", &lora_cadexit_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+4, 62}, "timeout us:", &lora_cadtimeout_item, FLAG_MSGTYPE_ALL }, { {0, 0}, NULL, NULL } }; void Radio::test() { pktType = radio.getPacketType(); if (pktType == PACKET_TYPE_GFSK) { } else if (pktType == PACKET_TYPE_LORA) { } } void Radio::gfsk_bitrate_print() { unsigned d = radio.readReg(REG_ADDR_BITRATE, 3); float f = d / 32.0; printf("%u", (unsigned)(XTAL_FREQ_HZ / f)); mpFSK.gfsk.bitrateHi = d >> 16; mpFSK.gfsk.bitrateMid = d >> 8; mpFSK.gfsk.bitrateLo = d; } bool Radio::gfsk_bitrate_write(const char* txt) { unsigned bps, br; if (sscanf(txt, "%u", &bps) == 1) { br = 32 * (XTAL_FREQ_HZ / (float)bps); mpFSK.gfsk.bitrateHi = br >> 16; mpFSK.gfsk.bitrateMid = br >> 8; mpFSK.gfsk.bitrateLo = br; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 8, 0, mpFSK.buf); } return false; } const value_item_t Radio::gfsk_bitrate_item = { _ITEM_VALUE, 8, gfsk_bitrate_print, gfsk_bitrate_write}; static const char* const gfsk_bts[] = { "off", // 0 "0.3", // 1 "0.5", // 2 "0.7", // 3 "1.0", // 4 NULL }; unsigned Radio::gfsk_bt_read(bool forWriting) { shapeCfg_t shapeCfg; shapeCfg.octet = radio.readReg(REG_ADDR_SHAPECFG, 1); mpFSK.gfsk.PulseShape = shapeCfg.octet; if (shapeCfg.bits.pulse_shape) return shapeCfg.bits.bt + 1; else return 0; } menuMode_e Radio::gfsk_bt_write(unsigned sidx) { switch (sidx) { case 0: mpFSK.gfsk.PulseShape = GFSK_SHAPE_NONE; break; case 1: mpFSK.gfsk.PulseShape = GFSK_SHAPE_BT0_3; break; case 2: mpFSK.gfsk.PulseShape = GFSK_SHAPE_BT0_5; break; case 3: mpFSK.gfsk.PulseShape = GFSK_SHAPE_BT0_7; break; case 4: mpFSK.gfsk.PulseShape = GFSK_SHAPE_BT1_0; break; } radio.xfer(OPCODE_SET_MODULATION_PARAMS, 8, 0, mpFSK.buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::gfsk_bt_item = { _ITEM_DROPDOWN, gfsk_bts, gfsk_bts, gfsk_bt_read, gfsk_bt_write}; static const uint8_t rx_bws[] = { GFSK_RX_BW_4800, GFSK_RX_BW_5800, GFSK_RX_BW_7300, GFSK_RX_BW_9700, GFSK_RX_BW_11700, GFSK_RX_BW_14600, GFSK_RX_BW_19500, GFSK_RX_BW_23400, GFSK_RX_BW_29300, GFSK_RX_BW_39000, GFSK_RX_BW_46900, GFSK_RX_BW_58600, GFSK_RX_BW_78200, GFSK_RX_BW_93800, GFSK_RX_BW_117300, GFSK_RX_BW_156200, GFSK_RX_BW_187200, GFSK_RX_BW_234300, GFSK_RX_BW_312000, GFSK_RX_BW_373600, GFSK_RX_BW_467000 }; static const char* const rxbw_str[] = { " 4.8KHz", " 5.8KHz", " 7.3KHz", " 9.7KHz", " 11.7KHz", " 14.6KHz", " 19.5KHz", " 23.4KHz", " 29.3KHz", " 39.0KHz", " 46.9KHz", " 58.6KHz", " 78.2KHz", " 93.8KHz", "117.3KHz", "156.2KHz", "187.2KHz", "234.3KHz", "312.0KHz", "373.6KHz", "467.0KHz", NULL }; unsigned Radio::gfsk_rxbw_read(bool forWriting) { unsigned n; bwSel_t bwSel; bwSel.octet = radio.readReg(REG_ADDR_BWSEL, 1); mpFSK.gfsk.bandwidth = bwSel.octet; for (n = 0; n < sizeof(rx_bws); n++) { if (bwSel.octet == rx_bws[n]) return n; } return sizeof(rx_bws); } menuMode_e Radio::gfsk_rxbw_write(unsigned sidx) { mpFSK.gfsk.bandwidth = rx_bws[sidx]; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 8, 0, mpFSK.buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::gfsk_rxbw_item = { _ITEM_DROPDOWN, rxbw_str, rxbw_str, gfsk_rxbw_read, gfsk_rxbw_write}; void Radio::gfsk_fdev_print() { unsigned d = radio.readReg(REG_ADDR_FREQDEV, 3); printf("%u", (unsigned)(d * FREQ_STEP)); } bool Radio::gfsk_fdev_write(const char* txt) { unsigned hz, fdev; if (sscanf(txt, "%u", &hz) == 1) { fdev = hz / FREQ_STEP; mpFSK.gfsk.fdevHi = fdev >> 16; mpFSK.gfsk.fdevMid = fdev >> 8; mpFSK.gfsk.fdevLo = fdev; radio.xfer(OPCODE_SET_MODULATION_PARAMS, 8, 0, mpFSK.buf); } return false; } const value_item_t Radio::gfsk_fdev_item = { _ITEM_VALUE, 8, gfsk_fdev_print, gfsk_fdev_write}; void Radio::gfsk_pblLen_print() { unsigned n = radio.readReg(REG_ADDR_FSK_PREAMBLE_TXLEN , 2); ppFSK.gfsk.PreambleLengthHi = n << 8; // param1 ppFSK.gfsk.PreambleLengthLo = n;// param2 printf("%u", n); } bool Radio::gfsk_pblLen_write(const char* txt) { unsigned n; if (sscanf(txt, "%u", &n) == 1) { ppFSK.gfsk.PreambleLengthHi = n << 8; // param1 ppFSK.gfsk.PreambleLengthLo = n;// param2 radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); } return false; } const value_item_t Radio::gfsk_pblLen_item = { _ITEM_VALUE, 5, gfsk_pblLen_print, gfsk_pblLen_write}; static const char* const fsk_detlens[] = { " off ", " 8bits", "16bits", "24bits", "32bits", NULL }; unsigned Radio::gfsk_pblDetLen_read(bool forWriting) { pktCtrl1_t pktCtrl1; pktCtrl1.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL1, 1); ppFSK.gfsk.PreambleDetectorLength = pktCtrl1.octet & 0x07; // param3 if (pktCtrl1.bits.preamble_det_on) return pktCtrl1.bits.preamble_len_rx + 1; else return 0; } menuMode_e Radio::gfsk_pblDetLen_write(unsigned sidx) { if (sidx == 0) ppFSK.gfsk.PreambleDetectorLength = 0; else ppFSK.gfsk.PreambleDetectorLength = sidx + 3; radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::gfsk_pblDetLen_item = { _ITEM_DROPDOWN, fsk_detlens, fsk_detlens, gfsk_pblDetLen_read, gfsk_pblDetLen_write}; void Radio::gfsk_swl_print() { ppFSK.gfsk.SyncWordLength = radio.readReg(REG_ADDR_FSK_SYNC_LEN, 1);// param4 printf("%u", ppFSK.gfsk.SyncWordLength); } bool Radio::gfsk_swl_write(const char* txt) { unsigned n; unsigned r; r = sscanf(txt, "%u", &n); if (r == 1) { ppFSK.gfsk.SyncWordLength = n; radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); } return false; } const value_item_t Radio::gfsk_swl_item = { _ITEM_VALUE, 3, gfsk_swl_print, gfsk_swl_write}; void Radio::gfsk_syncword_print() { unsigned addr = REG_ADDR_SYNCADDR; uint8_t swl_bits = radio.readReg(REG_ADDR_FSK_SYNC_LEN, 1); if (swl_bits & 7) { swl_bits |= 7; swl_bits++; } while (swl_bits > 0) { printf("%02x", (unsigned)radio.readReg(addr++, 1)); swl_bits -= 8; } } bool Radio::gfsk_syncword_write(const char* txt) { const char* ptr = txt; unsigned addr = REG_ADDR_SYNCADDR; int8_t swl_bits = radio.readReg(REG_ADDR_FSK_SYNC_LEN, 1); if (swl_bits & 7) { swl_bits |= 7; swl_bits++; } while (swl_bits > 0) { char buf[3]; unsigned n; buf[0] = ptr[0]; buf[1] = ptr[1]; buf[2] = 0; sscanf(buf, "%x", &n); radio.writeReg(addr++, n, 1); ptr += 2; swl_bits -= 8; } return false; } const value_item_t Radio::gfsk_syncword_item = { _ITEM_VALUE, 17, gfsk_syncword_print, gfsk_syncword_write}; bool Radio::gfsk_fixLen_read() { pktCtrl0_t pktCtrl0; pktCtrl0.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL0, 1); ppFSK.gfsk.PacketType = pktCtrl0.bits.pkt_len_format; // param6 return pktCtrl0.bits.pkt_len_format; } bool Radio::gfsk_fixLen_push() { if (ppFSK.gfsk.PacketType) ppFSK.gfsk.PacketType = 0; else ppFSK.gfsk.PacketType = 1; radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); return ppFSK.gfsk.PacketType; } const toggle_item_t Radio::gfsk_fixLen_item = { _ITEM_TOGGLE, "fixed ", "variable", gfsk_fixLen_read, gfsk_fixLen_push }; static const char* const addrcomps[] = { " off ", "NodeAddress ", "NodeAddress+broadcast", NULL }; unsigned Radio::gfsk_addrcomp_read(bool forWriting) { ppFSK.gfsk.AddrComp = radio.readReg(REG_ADDR_NODEADDRCOMP, 1);// param5 return ppFSK.gfsk.AddrComp; } menuMode_e Radio::gfsk_addrcomp_write(unsigned sidx) { ppFSK.gfsk.AddrComp = sidx; radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::gfsk_addrcomp_item = { _ITEM_DROPDOWN, addrcomps, addrcomps, gfsk_addrcomp_read, gfsk_addrcomp_write}; void Radio::gfsk_nodeadrs_print() { printf("%02x", (unsigned)radio.readReg(REG_ADDR_NODEADDR, 1)); } bool Radio::gfsk_nodeadrs_write(const char* txt) { unsigned v; if (sscanf(txt, "%x", &v) == 1) radio.writeReg(REG_ADDR_NODEADDR, v, 1); return false; } const value_item_t Radio::gfsk_nodeadrs_item = { _ITEM_VALUE, 3, gfsk_nodeadrs_print, gfsk_nodeadrs_write}; void Radio::gfsk_broadcast_print() { printf("%02x", (unsigned)radio.readReg(REG_ADDR_BROADCAST, 1)); } bool Radio::gfsk_broadcast_write(const char* txt) { unsigned v; if (sscanf(txt, "%x", &v) == 1) radio.writeReg(REG_ADDR_BROADCAST, v, 1); return false; } const value_item_t Radio::gfsk_broadcast_item = { _ITEM_VALUE, 3, gfsk_broadcast_print, gfsk_broadcast_write}; static const char* crctypes[] = { " off ", // 0 "1 Byte ", // 1 "2 Byte ", // 2 "1 Byte inv", // 3 "2 Byte inv", // 4 NULL }; unsigned Radio::gfsk_crctype_read(bool forWriting) { pktCtrl2_t pktCtrl2; pktCtrl2.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL2, 1); ppFSK.gfsk.CRCType = pktCtrl2.octet & 0x7; // param8 switch (ppFSK.gfsk.CRCType) { case GFSK_CRC_OFF: return 0; case GFSK_CRC_1_BYTE: return 1; case GFSK_CRC_2_BYTE: return 2; case GFSK_CRC_1_BYTE_INV: return 3; case GFSK_CRC_2_BYTE_INV: return 4; default: return 5; } } menuMode_e Radio::gfsk_crctype_write(unsigned sidx) { switch (sidx) { case 0: ppFSK.gfsk.CRCType = GFSK_CRC_OFF; break; case 1: ppFSK.gfsk.CRCType = GFSK_CRC_1_BYTE; break; case 2: ppFSK.gfsk.CRCType = GFSK_CRC_2_BYTE; break; case 3: ppFSK.gfsk.CRCType = GFSK_CRC_1_BYTE_INV; break; case 4: ppFSK.gfsk.CRCType = GFSK_CRC_2_BYTE_INV; break; } radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); return MENUMODE_REDRAW; } const dropdown_item_t Radio::gfsk_crctype_item = { _ITEM_DROPDOWN, crctypes, crctypes, gfsk_crctype_read, gfsk_crctype_write}; bool Radio::gfsk_white_read() { pktCtrl2_t pktCtrl2; pktCtrl2.octet = radio.readReg(REG_ADDR_FSK_PKTCTRL2, 1); ppFSK.gfsk.Whitening = pktCtrl2.bits.whit_enable; // param9 return pktCtrl2.bits.whit_enable; } bool Radio::gfsk_white_push() { if (ppFSK.gfsk.Whitening) ppFSK.gfsk.Whitening = 0; else ppFSK.gfsk.Whitening = 1; radio.xfer(OPCODE_SET_PACKET_PARAMS, 9, 0, ppFSK.buf); return ppFSK.gfsk.Whitening; } const toggle_item_t Radio::gfsk_white_item = { _ITEM_TOGGLE, "Whitening", NULL, gfsk_white_read, gfsk_white_push}; void Radio::gfsk_crcinit_print() { printf("%04x", (unsigned)radio.readReg(REG_ADDR_FSK_CRCINIT, 2)); } bool Radio::gfsk_crcinit_write(const char* txt) { unsigned v; if (sscanf(txt, "%x", &v) == 1) radio.writeReg(REG_ADDR_FSK_CRCINIT, v, 2); return false; } const value_item_t Radio::gfsk_crcinit_item = { _ITEM_VALUE, 5, gfsk_crcinit_print, gfsk_crcinit_write}; void Radio::gfsk_crcpoly_print() { printf("%04x", (unsigned)radio.readReg(REG_ADDR_FSK_CRCPOLY, 2)); } bool Radio::gfsk_crcpoly_write(const char* txt) { unsigned v; if (sscanf(txt, "%x", &v) == 1) radio.writeReg(REG_ADDR_FSK_CRCPOLY, v, 2); return false; } const value_item_t Radio::gfsk_crcpoly_item = { _ITEM_VALUE, 5, gfsk_crcpoly_print, gfsk_crcpoly_write}; void Radio::gfsk_whiteInit_print() { PktCtrl1a_t PktCtrl1a; PktCtrl1a.word = radio.readReg(REG_ADDR_FSK_PKTCTRL1A, 2); printf("%x", PktCtrl1a.bits.whit_init_val); } bool Radio::gfsk_whiteInit_write(const char* txt) { unsigned n; PktCtrl1a_t PktCtrl1a; PktCtrl1a.word = radio.readReg(REG_ADDR_FSK_PKTCTRL1A, 2); if (sscanf(txt, "%x", &n) == 1) { PktCtrl1a.bits.whit_init_val = n; radio.writeReg(REG_ADDR_FSK_PKTCTRL1A, PktCtrl1a.word, 2); } return false; } const value_item_t Radio::gfsk_whiteInit_item = { _ITEM_VALUE, 5, gfsk_whiteInit_print, gfsk_whiteInit_write}; const menu_t Radio::gfsk_menu[] = { { {FIRST_CHIP_MENU_ROW+2, 1}, "bps:", &gfsk_bitrate_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 15}, "bt:", &gfsk_bt_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 23}, "rxbw:", &gfsk_rxbw_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 39}, "fdev:", &gfsk_fdev_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+2, 53}, NULL, &gfsk_fixLen_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+3, 1}, "PreambleLength:", &gfsk_pblLen_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+3, 21}, "PreambleDetectorLength:", &gfsk_pblDetLen_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+3, 51}, "SyncWordLength bits:", &gfsk_swl_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+4, 1}, "SyncWord:", &gfsk_syncword_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+5, 1}, "AddrComp:", &gfsk_addrcomp_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+5, 33}, "NodeAdrs:", &gfsk_nodeadrs_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+5, 47}, "broadcast:", &gfsk_broadcast_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+6, 1}, "crcType:", &gfsk_crctype_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+6, 21}, "crcInit:", &gfsk_crcinit_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+6, 34}, "crcPoly:", &gfsk_crcpoly_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+7, 1}, NULL, &gfsk_white_item, FLAG_MSGTYPE_ALL }, { {FIRST_CHIP_MENU_ROW+7, 12}, "lfsr init:", &gfsk_whiteInit_item, FLAG_MSGTYPE_ALL }, //12345678901234567890123456789012 { {0, 0}, NULL, NULL } }; const menu_t* Radio::get_modem_sub_menu() { return NULL; } const menu_t* Radio::get_modem_menu() { pktType = radio.getPacketType(); if (pktType == PACKET_TYPE_LORA) { return lora_menu; } else if (pktType == PACKET_TYPE_GFSK) { return gfsk_menu; } return NULL; } unsigned Radio::read_register(unsigned addr) { return radio.readReg(addr, 1); } void Radio::write_register(unsigned addr, unsigned val) { radio.writeReg(addr, val, 1); } #endif /* ..SX126x_H */