Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
radio_sx126x.cpp
- Committer:
- dudmuck
- Date:
- 2021-09-16
- Revision:
- 14:14b9e1c08bfc
- Parent:
- 13:8ce61a1897ab
- Child:
- 15:703ca340d0fb
File content as of revision 14:14b9e1c08bfc:
#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::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 */