unlimited packet length example for sx1272 or sx1276

Dependencies:   SX127x

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)
}