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.
main.cpp
- Committer:
- dudmuck
- Date:
- 2021-09-14
- Revision:
- 0:df5ce1bc5036
File content as of revision 0:df5ce1bc5036:
#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)
}