![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
unlimited packet length example for sx1272 or sx1276
operating
user button on MCU board toggles between receive operation on radio and transmit. (Latching PTT). Look at serial terminal at 9600 to see operation. User button is pressed once to start transmit, and again to end transmit: it doesnt need to be held down during TX.
example payload
The example payload is a continuously incrementing byte, with zero at start of packet.
End of packet is detected by four consecutive bytes which dont increment with expected value: background noise, this indicates transmitter has finished. This serves only as an example, because some other method would likely be needed in an application, such as unique data indicating end, or the length sent at begin of packet.
flow control
FifoThreshold is used for transmit flow control. With fifo is below threshold, bytes are send to the radio's fifo. While fifo is above threshold, nothing is sent until the fifo threshold pin returns low. AKA FifoLevel
pin.
On receive, the packet reception is initialized by SyncAddress
pin going high, indicating start of packet. Bytes are read from FIFO when the FifoEmpty
pin is low.
The packet buffer (AKA FIFO) in sx127x is only 64 bytes, meaning the maximum time between FifoFull
and FifoEmpty
would be 512 bit periods. This needs to be considered: the MCU latency of servicing FifoLevel
, FifoEmpty
pins during packet, otherwise empty byte would be sent on transmit or byte missed on receiver.
FSK reception
The rxTrigger is used on preamble to enable start-of-frame detection, aka sync word. When end of packet is detected, the receiver is restarted. In this case, restarted with PLL when AFC is used, or without PLL when AFC not enabled.
The serial terminal will show when packet reception starts, and when there are incorrect bytes received over the air. This will always happen and end of packet for this example.
Diff: main.cpp
- Revision:
- 0:df5ce1bc5036
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Tue Sep 14 22:19:57 2021 +0000 @@ -0,0 +1,399 @@ +#include "mbed.h" +#include "sx127x_fsk.h" + +using namespace std::chrono; + +#define FSK_BIT_RATE 10000 +#define TX_FREQ_DEVIATION_HZ 20000 +#define RX_BW 30000 +#define RX_BW_AFC 30000 +#define START_OF_FRAME 0xd42d +#define RADIO_FREQUENCY_MHZ 915.0 +//#define ENABLE_AFC + +SPI spi(D11, D12, D13); // mosi, miso, sclk +// dio0, dio1, nss, spi, rst +SX127x radio( D2, D3, D10, spi, A0); // sx127[62] arduino shield +SX127x_fsk fsk(radio); +DigitalInOut rfsw(A4); + +DigitalIn dio1(D3); // for FifoLevel +InterruptIn dio2(D4); // for syncAddress +DigitalIn dio3(D5); // for FifoEmpty + +DigitalIn button(/*USER_BUTTON*/ BUTTON1); +bool button_pressed; +#define BUTTON_DEBOUNCE_MS 10 +bool txing; +bool end_tx; +bool rxSync; + +typedef enum { + SHIELD_TYPE_NONE = 0, + SHIELD_TYPE_LAS, + SHIELD_TYPE_MAS, +} shield_type_e; +shield_type_e shield_type; + +void rfsw_callback() +{ + if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER) + rfsw = 1; + else + rfsw = 0; +} + +void get_rf_board_type() +{ + rfsw.input(); + if (rfsw.read()) { + shield_type = SHIELD_TYPE_LAS; + printf("LAS\r\n"); + } else { + shield_type = SHIELD_TYPE_MAS; + printf("MAS\r\n"); + } + + rfsw.output(); +} + +void ocp(uint8_t ma) +{ + if (ma < 130) + radio.RegOcp.bits.OcpTrim = (ma - 45) / 5; + else + radio.RegOcp.bits.OcpTrim = (ma + 30) / 10; + radio.write_reg(REG_OCP, radio.RegOcp.octet); + + radio.RegOcp.octet = radio.read_reg(REG_OCP); + if (radio.RegOcp.bits.OcpTrim < 16) + ma = 45 + (5 * radio.RegOcp.bits.OcpTrim); + else if (radio.RegOcp.bits.OcpTrim < 28) + ma = (10 * radio.RegOcp.bits.OcpTrim) - 30; + else + ma = 240; +} + +void +set_tx_dbm(int8_t dbm) +{ + RegPdsTrim1_t pds_trim; + uint8_t v, adr, pa_test_adr; + + if (radio.type == SX1276) { + adr = REG_PDSTRIM1_SX1276; + pa_test_adr = REG_PATEST_SX1276; + } else { + adr = REG_PDSTRIM1_SX1272; + pa_test_adr = REG_PATEST_SX1272; + } + + v = radio.read_reg(pa_test_adr); + /*if (dbm == PA_OFF_DBM) { + // for bench testing: prevent overloading receiving station (very low TX power) + v &= ~0x20; // turn off pu_regpa_n: disable PA + radio.write_reg(pa_test_adr, v); + return; + } else if ((v & 0x20) == 0) { + v |= 0x20; // turn on pu_regpa_n: enable PA + radio.write_reg(pa_test_adr, v); + }*/ + + pds_trim.octet = radio.read_reg(adr); + + if (shield_type == SHIELD_TYPE_LAS) + radio.RegPaConfig.bits.PaSelect = 1; + else + radio.RegPaConfig.bits.PaSelect = 0; + + if (radio.RegPaConfig.bits.PaSelect) { + /* PABOOST used: +2dbm to +17, or +20 */ + if (dbm > 17) { + if (dbm > 20) + dbm = 20; + dbm -= 3; + pds_trim.bits.prog_txdac = 7; + radio.write_reg(adr, pds_trim.octet); + ocp(150); + } else + ocp(120); + + if (dbm > 1) + radio.RegPaConfig.bits.OutputPower = dbm - 2; + } else { + /* RFO used: -1 to +14dbm */ + ocp(80); + if (dbm < 15) + radio.RegPaConfig.bits.OutputPower = dbm + 1; + } + radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet); + + radio.RegPaConfig.octet = radio.read_reg(REG_PACONFIG); + if (radio.RegPaConfig.bits.PaSelect) { + dbm = radio.RegPaConfig.bits.OutputPower + pds_trim.bits.prog_txdac - 2; + } else { + dbm = radio.RegPaConfig.bits.OutputPower - 1; + } +} + +void start_receive() +{ + fsk.RegRxConfig.octet = radio.read_reg(REG_FSK_RXCONFIG); + fsk.RegRxConfig.bits.RxTrigger = 6; // trigger receiver on preamble detect + fsk.RegRxConfig.bits.AgcAutoOn = 1; // radio controls its LNA +#ifdef ENABLE_AFC + fsk.RegRxConfig.bits.AfcAutoOn = 1; + fsk.RegRxConfig.bits.RestartRxWithPllLock = 1; +#endif /* ENABLE_AFC */ + radio.write_reg(REG_FSK_RXCONFIG, fsk.RegRxConfig.octet); + +#ifdef ENABLE_AFC + fsk.RegAfcFei.octet = radio.read_reg(REG_FSK_AFCFEI); + fsk.RegAfcFei.bits.AfcAutoClearOn = 1; + fsk.RegAfcFei.bits.AfcClear = 1; + radio.write_reg(REG_FSK_AFCFEI, fsk.RegAfcFei.octet); +#endif /* ENABLE_AFC */ + + radio.set_opmode(RF_OPMODE_RECEIVER); +} + +#define FIRST_PAYLOAD_BYTE 0 +uint8_t expected_rx; +uint8_t rx_err_cnt; +unsigned rx_bytes_ok; + +void dio2_callback() +{ + if (!txing) { + expected_rx = FIRST_PAYLOAD_BYTE; + rxSync = true; + rx_err_cnt = 0; + rx_bytes_ok = 0; + } else { + // fifoFull in TX + } + +} + +/* return non-zero at end of packet detection */ +int take_rx_byte(uint8_t in) +{ + int ret; + + if (in != expected_rx) { + printf("(got %02x, wanted %02x)\r\n", in, expected_rx); + if (++rx_err_cnt == 4) + ret = -1; // too much consecutive noise + else + ret = 0; + } else { + rx_bytes_ok++; + rx_err_cnt = 0; + ret = 0; + } + + expected_rx++; + + return ret; +} + +uint8_t tx_byte_cnt; +uint8_t get_tx_byte() +{ + return tx_byte_cnt++; +} + +void start_transmit() +{ + fsk.RegFifoThreshold.octet = radio.read_reg(REG_FSK_FIFOTHRESH); + fsk.RegFifoThreshold.bits.FifoThreshold = 48; + radio.write_reg(REG_FSK_FIFOTHRESH, fsk.RegFifoThreshold.octet); + + tx_byte_cnt = FIRST_PAYLOAD_BYTE; + radio.set_opmode(RF_OPMODE_TRANSMITTER); +} + +void end_transmit() +{ + printf("wait fifoEmpty...\r\n"); + while (dio3.read() == 0) + ; + + radio.set_opmode(RF_OPMODE_STANDBY); + printf("tx-end\r\n"); +} + +int main() +{ + long button_release_start = -1; + + + radio.rf_switch = rfsw_callback; + get_rf_board_type(); + ThisThread::sleep_for(30ms); // from cold power-up, both radio and mcu started + + radio.hw_reset(); + + if (shield_type == SHIELD_TYPE_LAS) + set_tx_dbm(13); + else + set_tx_dbm(17); + + radio.set_frf_MHz(RADIO_FREQUENCY_MHZ); + + fsk.enable(true); + fsk.set_tx_fdev_hz(TX_FREQ_DEVIATION_HZ); + + fsk.set_bitrate(FSK_BIT_RATE); + + fsk.set_rx_dcc_bw_hz(RX_BW, 0); + fsk.set_rx_dcc_bw_hz(RX_BW_AFC, 1); + + fsk.RegSyncConfig.octet = radio.read_reg(REG_FSK_SYNCCONFIG); + fsk.RegSyncConfig.bits.SyncSize = 2 - 1; + radio.write_reg(REG_FSK_SYNCCONFIG, fsk.RegSyncConfig.octet); + radio.write_reg(REG_FSK_SYNCVALUE1, 0xd4); + radio.write_reg(REG_FSK_SYNCVALUE1, 0x2d); + + // dio3 to FifoEmpty + // dio2 to syncAddress + // dio1 to FifoLevel + radio.RegDioMapping1.octet = radio.read_reg(REG_DIOMAPPING1); + radio.RegDioMapping1.bits.Dio3Mapping = 3; // + radio.RegDioMapping1.bits.Dio2Mapping = 3; // + radio.RegDioMapping1.bits.Dio1Mapping = 0; // + radio.write_reg(REG_DIOMAPPING1, radio.RegDioMapping1.octet); + dio2.rise(dio2_callback); + + fsk.RegPktConfig1.octet = radio.read_reg(REG_FSK_PACKETCONFIG1); + fsk.RegPktConfig1.bits.PacketFormatVariable = 0; + radio.write_reg(REG_FSK_PACKETCONFIG1, fsk.RegPktConfig1.octet); + + // unlimited payload-length operation + fsk.RegPktConfig2.word = radio.read_u16(REG_FSK_PACKETCONFIG2); + fsk.RegPktConfig2.bits.PayloadLength = 0; + radio.write_u16(REG_FSK_PACKETCONFIG2, fsk.RegPktConfig2.word); + + fsk.RegPreambleDetect.octet = radio.read_reg(REG_FSK_PREAMBLEDETECT); + fsk.RegPreambleDetect.bits.PreambleDetectorTol = 10; + fsk.RegPreambleDetect.bits.PreambleDetectorSize = 1; + fsk.RegPreambleDetect.bits.PreambleDetectorOn = 1; + radio.write_reg(REG_FSK_PREAMBLEDETECT, fsk.RegPreambleDetect.octet); + + //printf("preamblesize: %02x ", radio.read_reg(REG_FSK_PREAMBLEMSB)); + //printf("%02x\r\n", radio.read_reg(REG_FSK_PREAMBLELSB)); + start_receive(); + + while (true) { + if (button_pressed) { + if (button.read()) { + // button released + button_pressed = false; + auto now_tp = time_point_cast<milliseconds>(Kernel::Clock::now()); + button_release_start = now_tp.time_since_epoch().count(); + } + } else { + if (!button.read()) { + // button pressed + button_pressed = true; + button_release_start = -1; + } + } + + if (button_release_start != -1) { + // debounce button + auto now_tp = time_point_cast<milliseconds>(Kernel::Clock::now()); + long now_ms = now_tp.time_since_epoch().count(); + if ((now_ms - button_release_start) > BUTTON_DEBOUNCE_MS) { + // button released sufficiently long enough + button_release_start = -1; + if (txing) { + printf("toRX\r\n"); + end_tx = true; + } else { + printf("toTX\r\n"); + start_transmit(); + txing = true; + } + } + } + + if (txing) { + /* transmitting */ + if (dio1) { + /* fifo above threshold, let the radio send it */ + /* alternately, send until FifoFull */ + } else { + /* fifo below threshold */ + radio.m_cs = 0; + radio.m_spi.write(REG_FIFO | 0x80); // bit7 is high for writing to radio + while (dio1.read() == 0) { + if (end_tx) { + // something to send at end : radio.m_spi.write(<end-of-tx>); + break; + } else + radio.m_spi.write(get_tx_byte()); + } + + radio.m_cs = 1; + + if (end_tx) { + end_transmit(); + txing = false; + end_tx = false; + start_receive(); + } + } + } else { + /* receving */ + if (!dio3) { + bool restart_rx = false; + radio.m_cs = 0; + radio.m_spi.write(REG_FIFO); // bit7 is low for reading from radio + while (dio3.read() == 0) { + if (take_rx_byte(radio.m_spi.write(0)) != 0) { + restart_rx = true; + break; + } + + } + radio.m_cs = 1; + + if (restart_rx) { + printf("rxRestart %u\r\n", rx_bytes_ok); + fsk.RegRxConfig.octet = radio.read_reg(REG_FSK_RXCONFIG); + #ifdef ENABLE_AFC + fsk.RegRxConfig.bits.AfcAutoOn = 1; + fsk.RegRxConfig.bits.RestartRxWithPllLock = 1; + #else + fsk.RegRxConfig.bits.RestartRxWithoutPllLock = 1; + #endif + radio.write_reg(REG_FSK_RXCONFIG, fsk.RegRxConfig.octet); + + if (!dio3) { + /* dump whatever is remaining in fifo */ + radio.m_cs = 0; + while (dio3.read() == 0) { + radio.m_spi.write(0); + } + radio.m_cs = 1; + } + +/* + fsk.RegAfcFei.octet = radio.read_reg(REG_FSK_AFCFEI); + fsk.RegAfcFei.bits.AfcAutoClearOn = 1; + fsk.RegAfcFei.bits.AfcClear = 1; + radio.write_reg(REG_FSK_AFCFEI, fsk.RegAfcFei.octet); + */ + } + } + + if (rxSync) { + rxSync = false; + printf("rxSync\r\n"); + } + } // ..rxing + + + } // .. while(true) +}