LoRa Development and Application of IoT (LoRaPingPang System Design)

In-depth understanding of LoRa technology principles

Content summary :

1. The principle of LoRa spread spectrum communication

2. LoRa key technical parameters

3. LoRa data sending and receiving tasks

Analog wireless communication :

Digital Wireless Communications :

Wireless communication propagation mode : ground wave propagation (below 2MHz), sky wave propagation (2MHz~30MHz), straight line propagation (above 30MHz)

Wireless Communication Propagation Path : Reflection, Scattering, Diffraction

Wireless communication noise :

Spread Spectrum Communication Technology : Gaining Immunity from Various Types of Noise and Multipath Distortion

Spread spectrum communication algorithm : C means signal quality

The principle of spread spectrum communication : user data and spread spectrum data are XORed to get the transmitted data, which increases the signal bandwidth and improves the signal quality

Signal Bandwidth (BW) :

By increasing the BW, the effective data rate can be increased to shorten the transmission time, but at the expense of some receiver sensitivity. For the LoRa chip SX127x, the LoRa bandwidth is the double-sided bandwidth (full channel bandwidth), and the BW of the FSK modulation method refers to the single-sided bandwidth.

Spreading factor (SF) : The signal originally represented by 1 bit becomes multi-bit to represent the signal, improving the communication quality of the signal

LoRa uses multiple information chips to represent each bit of the payload information. The transmission speed of the spreading information is called the symbol rate (Rs), and the ratio of the chip rate to the nominal Rs is the spreading factor (SF, SpreadingFactor ), indicating the number of symbols sent per information bit.

Code Rate (CR) : Redundancy to improve signal quality, improve data reliability

The coding rate (or information rate) is the proportion of the useful (non-redundant) portion of the data stream. That is, if the coding rate is k/n, then for every k bits of useful information, the encoder produces a total of n bits of data, where nk is redundant.

LoRa uses cyclic error correction coding for forward error detection and error correction. . Using this method will incur transmission overhead.

LoRa key technical parameters :

LoRa symbol rate Rs calculation: Rs=BW/(2^SF)

LoRa data rate DR calculation: DR= SF*( BW/2^SF)*CR

LoRaWAN primarily uses the 125kHz signal bandwidth setting, but other proprietary protocols can utilize other signal bandwidth (BW) settings. Changing the BW, SF, and CR also changes the link budget and transmission time, requiring a trade-off between battery life and distance.

LoRa parameter settings :

LoRa data transmission sequence :

Data sending process: (1) LoRa module standard mode -> (2) Sending mode -> (3) Write data to the sending queue -> (4) Send data -> (5) Wait for the completion of sending (judging whether the data is sent or not) , if the transmission is completed, enter the standard mode again, if there is still data to be sent, enter the third step)

LoRa data receiving sequence :

Data receiving process:

Radio event task : (event processing task provided by the official firmware)

LoRa firmware related code : parameter settings and event handling functions during initialization

​​sx1276-LoRa.c

// Default settings
tLoRaSettings LoRaSettings =       //设置LoRa参数
{
    870000000,        // RFFrequency
    20,               // Power
    9,                // SignalBw [0: 7.8kHz, 1: 10.4 kHz, 2: 15.6 kHz, 3: 20.8 kHz, 4: 31.2 kHz,
                      // 5: 41.6 kHz, 6: 62.5 kHz, 7: 125 kHz, 8: 250 kHz, 9: 500 kHz, other: Reserved]
    7,                // SpreadingFactor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096  chips]
    2,                // ErrorCoding [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
    true,             // CrcOn [0: OFF, 1: ON]
    false,            // ImplicitHeaderOn [0: OFF, 1: ON]
    1,                // RxSingleOn [0: Continuous, 1 Single]
    0,                // FreqHopOn [0: OFF, 1: ON]
    4,                // HopPeriod Hops every frequency hopping period symbols
    100,              // TxPacketTimeout
    100,              // RxPacketTimeout
    128,              // PayloadLength (used for implicit header mode)
};





/*!
 * \brief Process the LoRa modem Rx and Tx state machines depending on the
 *        SX1276 operating mode.
 *
 * \retval rfState Current RF state [RF_IDLE, RF_BUSY, 
 *                                   RF_RX_DONE, RF_RX_TIMEOUT,
 *                                   RF_TX_DONE, RF_TX_TIMEOUT]
 */
uint32_t SX1276LoRaProcess( void )   //事件任务处理函数,里面有多种工作模式:接收、发送等。。。
{
    uint32_t result = RF_BUSY;
    
    switch( RFLRState )
    {
    case RFLR_STATE_IDLE:
        break;
    case RFLR_STATE_RX_INIT:
        
        SX1276LoRaSetOpMode( RFLR_OPMODE_STANDBY );

        SX1276LR->RegIrqFlagsMask = RFLR_IRQFLAGS_RXTIMEOUT |
                                    //RFLR_IRQFLAGS_RXDONE |
                                    //RFLR_IRQFLAGS_PAYLOADCRCERROR |
                                    RFLR_IRQFLAGS_VALIDHEADER |
                                    RFLR_IRQFLAGS_TXDONE |
                                    RFLR_IRQFLAGS_CADDONE |
                                    //RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL |
                                    RFLR_IRQFLAGS_CADDETECTED;
        SX1276Write( REG_LR_IRQFLAGSMASK, SX1276LR->RegIrqFlagsMask );

        if( LoRaSettings.FreqHopOn == true )
        {
            SX1276LR->RegHopPeriod = LoRaSettings.HopPeriod;

            SX1276Read( REG_LR_HOPCHANNEL, &SX1276LR->RegHopChannel );
            SX1276LoRaSetRFFrequency( HoppingFrequencies[SX1276LR->RegHopChannel & RFLR_HOPCHANNEL_CHANNEL_MASK] );
        }
        else
        {
            SX1276LR->RegHopPeriod = 255;
        }
        
        SX1276Write( REG_LR_HOPPERIOD, SX1276LR->RegHopPeriod );
                
                                    // RxDone                    RxTimeout                   FhssChangeChannel           CadDone
        SX1276LR->RegDioMapping1 = RFLR_DIOMAPPING1_DIO0_00 | RFLR_DIOMAPPING1_DIO1_00 | RFLR_DIOMAPPING1_DIO2_00 | RFLR_DIOMAPPING1_DIO3_00;
                                    // CadDetected               ModeReady
        SX1276LR->RegDioMapping2 = RFLR_DIOMAPPING2_DIO4_00 | RFLR_DIOMAPPING2_DIO5_00;
        SX1276WriteBuffer( REG_LR_DIOMAPPING1, &SX1276LR->RegDioMapping1, 2 );
    
        if( LoRaSettings.RxSingleOn == true ) // Rx single mode
        {

            SX1276LoRaSetOpMode( RFLR_OPMODE_RECEIVER_SINGLE );
        }
        else // Rx continuous mode
        {
            SX1276LR->RegFifoAddrPtr = SX1276LR->RegFifoRxBaseAddr;
            SX1276Write( REG_LR_FIFOADDRPTR, SX1276LR->RegFifoAddrPtr );
            
            SX1276LoRaSetOpMode( RFLR_OPMODE_RECEIVER );
        }
        
        memset( RFBuffer, 0, ( size_t )RF_BUFFER_SIZE );

        PacketTimeout = LoRaSettings.RxPacketTimeout;
        RxTimeoutTimer = GET_TICK_COUNT( );
        RFLRState = RFLR_STATE_RX_RUNNING;
        break;
    case RFLR_STATE_RX_RUNNING:
        
        if( DIO0 == 1 ) // RxDone
        {
            RxTimeoutTimer = GET_TICK_COUNT( );
            if( LoRaSettings.FreqHopOn == true )
            {
                SX1276Read( REG_LR_HOPCHANNEL, &SX1276LR->RegHopChannel );
                SX1276LoRaSetRFFrequency( HoppingFrequencies[SX1276LR->RegHopChannel & RFLR_HOPCHANNEL_CHANNEL_MASK] );
            }
            // Clear Irq
            SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXDONE  );
            RFLRState = RFLR_STATE_RX_DONE;
        }
        if( DIO2 == 1 ) // FHSS Changed Channel
        {
            RxTimeoutTimer = GET_TICK_COUNT( );
            if( LoRaSettings.FreqHopOn == true )
            {
                SX1276Read( REG_LR_HOPCHANNEL, &SX1276LR->RegHopChannel );
                SX1276LoRaSetRFFrequency( HoppingFrequencies[SX1276LR->RegHopChannel & RFLR_HOPCHANNEL_CHANNEL_MASK] );
            }
            // Clear Irq
            SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL );
            // Debug
            RxGain = SX1276LoRaReadRxGain( );
        }

        if( LoRaSettings.RxSingleOn == true ) // Rx single mode
        {
            if( ( GET_TICK_COUNT( ) - RxTimeoutTimer ) > PacketTimeout )
            {
                RFLRState = RFLR_STATE_RX_TIMEOUT;
            }
        }
        break;
    case RFLR_STATE_RX_DONE:
        SX1276Read( REG_LR_IRQFLAGS, &SX1276LR->RegIrqFlags );
        if( ( SX1276LR->RegIrqFlags & RFLR_IRQFLAGS_PAYLOADCRCERROR ) == RFLR_IRQFLAGS_PAYLOADCRCERROR )
        {
            // Clear Irq
            SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_PAYLOADCRCERROR  );
            
            if( LoRaSettings.RxSingleOn == true ) // Rx single mode
            {
                RFLRState = RFLR_STATE_RX_INIT;
            }
            else
            {
                RFLRState = RFLR_STATE_RX_RUNNING;
            }
            break;
        }
        
        {
            uint8_t rxSnrEstimate;
            SX1276Read( REG_LR_PKTSNRVALUE, &rxSnrEstimate );
            if( rxSnrEstimate & 0x80 ) // The SNR sign bit is 1
            {
                // Invert and divide by 4
                RxPacketSnrEstimate = ( ( ~rxSnrEstimate + 1 ) & 0xFF ) >> 2;
                RxPacketSnrEstimate = -RxPacketSnrEstimate;
            }
            else
            {
                // Divide by 4
                RxPacketSnrEstimate = ( rxSnrEstimate & 0xFF ) >> 2;
            }
        }
        
        SX1276Read( REG_LR_PKTRSSIVALUE, &SX1276LR->RegPktRssiValue );
    
        if( LoRaSettings.RFFrequency < 860000000 )  // LF
        {    
            if( RxPacketSnrEstimate < 0 )
            {
                RxPacketRssiValue = RSSI_OFFSET_LF + ( ( double )SX1276LR->RegPktRssiValue ) + RxPacketSnrEstimate;
            }
            else
            {
                RxPacketRssiValue = RSSI_OFFSET_LF + ( 1.0666 * ( ( double )SX1276LR->RegPktRssiValue ) );
            }
        }
        else                                        // HF
        {    
            if( RxPacketSnrEstimate < 0 )
            {
                RxPacketRssiValue = RSSI_OFFSET_HF + ( ( double )SX1276LR->RegPktRssiValue ) + RxPacketSnrEstimate;
            }
            else
            {    
                RxPacketRssiValue = RSSI_OFFSET_HF + ( 1.0666 * ( ( double )SX1276LR->RegPktRssiValue ) );
            }
        }

        if( LoRaSettings.RxSingleOn == true ) // Rx single mode
        {
            SX1276LR->RegFifoAddrPtr = SX1276LR->RegFifoRxBaseAddr;
            SX1276Write( REG_LR_FIFOADDRPTR, SX1276LR->RegFifoAddrPtr );

            if( LoRaSettings.ImplicitHeaderOn == true )
            {
                RxPacketSize = SX1276LR->RegPayloadLength;
                SX1276ReadFifo( RFBuffer, SX1276LR->RegPayloadLength );
            }
            else
            {
                SX1276Read( REG_LR_NBRXBYTES, &SX1276LR->RegNbRxBytes );
                RxPacketSize = SX1276LR->RegNbRxBytes;
                SX1276ReadFifo( RFBuffer, SX1276LR->RegNbRxBytes );
            }
        }
        else // Rx continuous mode
        {
            SX1276Read( REG_LR_FIFORXCURRENTADDR, &SX1276LR->RegFifoRxCurrentAddr );

            if( LoRaSettings.ImplicitHeaderOn == true )
            {
                RxPacketSize = SX1276LR->RegPayloadLength;
                SX1276LR->RegFifoAddrPtr = SX1276LR->RegFifoRxCurrentAddr;
                SX1276Write( REG_LR_FIFOADDRPTR, SX1276LR->RegFifoAddrPtr );
                SX1276ReadFifo( RFBuffer, SX1276LR->RegPayloadLength );
            }
            else
            {
                SX1276Read( REG_LR_NBRXBYTES, &SX1276LR->RegNbRxBytes );
                RxPacketSize = SX1276LR->RegNbRxBytes;
                SX1276LR->RegFifoAddrPtr = SX1276LR->RegFifoRxCurrentAddr;
                SX1276Write( REG_LR_FIFOADDRPTR, SX1276LR->RegFifoAddrPtr );
                SX1276ReadFifo( RFBuffer, SX1276LR->RegNbRxBytes );
            }
        }
        
        if( LoRaSettings.RxSingleOn == true ) // Rx single mode
        {
            RFLRState = RFLR_STATE_RX_INIT;
        }
        else // Rx continuous mode
        {
            RFLRState = RFLR_STATE_RX_RUNNING;
        }
        result = RF_RX_DONE;
        break;
    case RFLR_STATE_RX_TIMEOUT:
        RFLRState = RFLR_STATE_RX_INIT;
        result = RF_RX_TIMEOUT;
        break;
    case RFLR_STATE_TX_INIT:

        SX1276LoRaSetOpMode( RFLR_OPMODE_STANDBY );

        if( LoRaSettings.FreqHopOn == true )
        {
            SX1276LR->RegIrqFlagsMask = RFLR_IRQFLAGS_RXTIMEOUT |
                                        RFLR_IRQFLAGS_RXDONE |
                                        RFLR_IRQFLAGS_PAYLOADCRCERROR |
                                        RFLR_IRQFLAGS_VALIDHEADER |
                                        //RFLR_IRQFLAGS_TXDONE |
                                        RFLR_IRQFLAGS_CADDONE |
                                        //RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL |
                                        RFLR_IRQFLAGS_CADDETECTED;
            SX1276LR->RegHopPeriod = LoRaSettings.HopPeriod;

            SX1276Read( REG_LR_HOPCHANNEL, &SX1276LR->RegHopChannel );
            SX1276LoRaSetRFFrequency( HoppingFrequencies[SX1276LR->RegHopChannel & RFLR_HOPCHANNEL_CHANNEL_MASK] );
        }
        else
        {
            SX1276LR->RegIrqFlagsMask = RFLR_IRQFLAGS_RXTIMEOUT |
                                        RFLR_IRQFLAGS_RXDONE |
                                        RFLR_IRQFLAGS_PAYLOADCRCERROR |
                                        RFLR_IRQFLAGS_VALIDHEADER |
                                        //RFLR_IRQFLAGS_TXDONE |
                                        RFLR_IRQFLAGS_CADDONE |
                                        RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL |
                                        RFLR_IRQFLAGS_CADDETECTED;
            SX1276LR->RegHopPeriod = 0;
        }
        SX1276Write( REG_LR_HOPPERIOD, SX1276LR->RegHopPeriod );
        SX1276Write( REG_LR_IRQFLAGSMASK, SX1276LR->RegIrqFlagsMask );

        // Initializes the payload size
        SX1276LR->RegPayloadLength = TxPacketSize;
        SX1276Write( REG_LR_PAYLOADLENGTH, SX1276LR->RegPayloadLength );
        
        SX1276LR->RegFifoTxBaseAddr = 0x00; // Full buffer used for Tx
        SX1276Write( REG_LR_FIFOTXBASEADDR, SX1276LR->RegFifoTxBaseAddr );

        SX1276LR->RegFifoAddrPtr = SX1276LR->RegFifoTxBaseAddr;
        SX1276Write( REG_LR_FIFOADDRPTR, SX1276LR->RegFifoAddrPtr );
        
        // Write payload buffer to LORA modem
        SX1276WriteFifo( RFBuffer, SX1276LR->RegPayloadLength );
                                        // TxDone               RxTimeout                   FhssChangeChannel          ValidHeader         
        SX1276LR->RegDioMapping1 = RFLR_DIOMAPPING1_DIO0_01 | RFLR_DIOMAPPING1_DIO1_00 | RFLR_DIOMAPPING1_DIO2_00 | RFLR_DIOMAPPING1_DIO3_01;
                                        // PllLock              Mode Ready
        SX1276LR->RegDioMapping2 = RFLR_DIOMAPPING2_DIO4_01 | RFLR_DIOMAPPING2_DIO5_00;
        SX1276WriteBuffer( REG_LR_DIOMAPPING1, &SX1276LR->RegDioMapping1, 2 );

        SX1276LoRaSetOpMode( RFLR_OPMODE_TRANSMITTER );

        RFLRState = RFLR_STATE_TX_RUNNING;
        break;
    case RFLR_STATE_TX_RUNNING:
        if( DIO0 == 1 ) // TxDone
        {
            // Clear Irq
            SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_TXDONE  );
            RFLRState = RFLR_STATE_TX_DONE;   
        }
        if( DIO2 == 1 ) // FHSS Changed Channel
        {
            if( LoRaSettings.FreqHopOn == true )
            {
                SX1276Read( REG_LR_HOPCHANNEL, &SX1276LR->RegHopChannel );
                SX1276LoRaSetRFFrequency( HoppingFrequencies[SX1276LR->RegHopChannel & RFLR_HOPCHANNEL_CHANNEL_MASK] );
            }
            // Clear Irq
            SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL );
        }
        break;
    case RFLR_STATE_TX_DONE:
        // optimize the power consumption by switching off the transmitter as soon as the packet has been sent
        SX1276LoRaSetOpMode( RFLR_OPMODE_STANDBY );

        RFLRState = RFLR_STATE_IDLE;
        result = RF_TX_DONE;
        break;
    case RFLR_STATE_CAD_INIT:    
        SX1276LoRaSetOpMode( RFLR_OPMODE_STANDBY );
    
        SX1276LR->RegIrqFlagsMask = RFLR_IRQFLAGS_RXTIMEOUT |
                                    RFLR_IRQFLAGS_RXDONE |
                                    RFLR_IRQFLAGS_PAYLOADCRCERROR |
                                    RFLR_IRQFLAGS_VALIDHEADER |
                                    RFLR_IRQFLAGS_TXDONE |
                                    //RFLR_IRQFLAGS_CADDONE |
                                    RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL; // |
                                    //RFLR_IRQFLAGS_CADDETECTED;
        SX1276Write( REG_LR_IRQFLAGSMASK, SX1276LR->RegIrqFlagsMask );
           
                                    // RxDone                   RxTimeout                   FhssChangeChannel           CadDone
        SX1276LR->RegDioMapping1 = RFLR_DIOMAPPING1_DIO0_00 | RFLR_DIOMAPPING1_DIO1_00 | RFLR_DIOMAPPING1_DIO2_00 | RFLR_DIOMAPPING1_DIO3_00;
                                    // CAD Detected              ModeReady
        SX1276LR->RegDioMapping2 = RFLR_DIOMAPPING2_DIO4_00 | RFLR_DIOMAPPING2_DIO5_00;
        SX1276WriteBuffer( REG_LR_DIOMAPPING1, &SX1276LR->RegDioMapping1, 2 );
            
        SX1276LoRaSetOpMode( RFLR_OPMODE_CAD );
        RFLRState = RFLR_STATE_CAD_RUNNING;
        break;
    case RFLR_STATE_CAD_RUNNING:
        if( DIO3 == 1 ) //CAD Done interrupt
        { 
            // Clear Irq
            SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_CADDONE  );
            if( DIO4 == 1 ) // CAD Detected interrupt
            {
                // Clear Irq
                SX1276Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_CADDETECTED  );
                // CAD detected, we have a LoRa preamble
                RFLRState = RFLR_STATE_RX_INIT;
                result = RF_CHANNEL_ACTIVITY_DETECTED;
            } 
            else
            {    
                // The device goes in Standby Mode automatically    
                RFLRState = RFLR_STATE_IDLE;
                result = RF_CHANNEL_EMPTY;
            }
        }   
        break;
    
    default:
        break;
    } 
    return result;
}

 LoRaPingPong system design

Content summary :

1. PingPong system design requirements

2. PingPong system communication mechanism

3. PingPong system business process

PingPong system design requirements :

Define the LoRa terminal into two roles: Master (host) and Slave (slave)

Master actively sends PING data and receives PANG data

Slave responds to PANG data if it receives PING data

The terminal displays the terminal type and the number of data packets sent and received on the LCD screen

PingPong communication mechanism :

//sx1276-LoRa.c

void SX1276LoRaGetRxPacket( void *buffer, uint16_t *size )//LoRa无线数据接收
{
    *size = RxPacketSize;
    RxPacketSize = 0;
    memcpy( ( void * )buffer, ( void * )RFBuffer, ( size_t )*size );
}

void SX1276LoRaSetTxPacket( const void *buffer, uint16_t size )//LoRa无线数据发送
{
    TxPacketSize = size;
    memcpy( ( void * )RFBuffer, buffer, ( size_t )TxPacketSize ); 

    RFLRState = RFLR_STATE_TX_INIT;//状态设置为发送初始化
}
sx1276-LoRa.h

typedef enum  //LoRa的所有状态。监听和设置这些状态来实现数据的收发
{
    RFLR_STATE_IDLE,           //空闲模式
    RFLR_STATE_RX_INIT,        //接收初始化
    RFLR_STATE_RX_RUNNING,     //接收进行
    RFLR_STATE_RX_DONE,        //接收完成
    RFLR_STATE_RX_TIMEOUT,     //接收超时
    RFLR_STATE_TX_INIT,        //发送初始化
    RFLR_STATE_TX_RUNNING,     //发送进行
    RFLR_STATE_TX_DONE,        //发送完成
    RFLR_STATE_TX_TIMEOUT,     //发送超时
    RFLR_STATE_CAD_INIT,       //
    RFLR_STATE_CAD_RUNNING,
}tRFLRStates;

 PingPong business process - initialization :

PingPong Business Process-Master :

PingPong Business Process-Slave :

LoRa parameter settings :

// Default settings
tLoRaSettings LoRaSettings =       //设置LoRa参数
{
    870000000,        // RFFrequency
    20,               // Power
    9,                // SignalBw [0: 7.8kHz, 1: 10.4 kHz, 2: 15.6 kHz, 3: 20.8 kHz, 4: 31.2 kHz,
                      // 5: 41.6 kHz, 6: 62.5 kHz, 7: 125 kHz, 8: 250 kHz, 9: 500 kHz, other: Reserved]
    7,                // SpreadingFactor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096  chips]
    2,                // ErrorCoding [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
    true,             // CrcOn [0: OFF, 1: ON]
    false,            // ImplicitHeaderOn [0: OFF, 1: ON]
    1,                // RxSingleOn [0: Continuous, 1 Single]
    0,                // FreqHopOn [0: OFF, 1: ON]
    4,                // HopPeriod Hops every frequency hopping period symbols
    100,              // TxPacketTimeout
    100,              // RxPacketTimeout
    128,              // PayloadLength (used for implicit header mode)
};

Packet structure:

LoRaPingPang system function development

Content summary :

1. IAR project configuration

2. Build the framework

3. Coding

IAR project configuration : Configure two projects: Master and Slave ( two projects share one code, modify the code under one project, and all project codes are changed together )

Establish function functions : establish four functions (LCD menu display information, wireless transceiver task processing)

 

//**********************************//
//
//函数名称:  MLCD_Show 
//
//函数描述:   主机显示任务
//
//函数参数:   无
//
//返回值:     无
//
//创建者:     
//*******************************//

void MLCD_Show(void)
{
  uint8_t str[20] = {0};
  //LCDgpio重新初始化
  LCD_GPIO_Init();

  sprintf((char*)str,"%d",Master_RxNumber);
  show_rx(str);////将RX数据显示到屏幕上
  memset((char*)str,0,strlen((const char*)str));


  sprintf((char*)str,"%d",Master_TxNumber);
  show_tx(str);////将TX数据显示到屏幕上

  //SPI重新初始化
  HAL_SPI_DeInit(&hspi1);
  MX_SPI1_Init();
}


//**********************************//
//
//函数名称:  SLCD_Show 
//
//函数描述:   从机显示任务
//
//函数参数:   无
//
//返回值:     无
//
//创建者:    
//*******************************//

void SLCD_Show(void)
{
  uint8_t str[20] = {0};
  //LCDgpio重新初始化
  LCD_GPIO_Init();

  sprintf((char*)str,"%d",Slave_RxNumber);
  show_rx(str);////将RX数据显示到屏幕上
  memset((char*)str,0,strlen((const char*)str));


  sprintf((char*)str,"%d",Slave_TxNumber);
  show_tx(str);////将TX数据显示到屏幕上

  //SPI重新初始化
  HAL_SPI_DeInit(&hspi1);
  MX_SPI1_Init();
}

//**********************************//
//
//函数名称:   Master_Task
//
//函数描述:   主机无线任务
//
//函数参数:   无
//
//返回值:     无
//
//创建者:     
//*******************************//

void Master_Task(void)//无线任务处理函数会在下面补齐,这里只是搭建了一个框架
{
  
}


//**********************************//
//
//函数名称:   Slave_Task
//
//函数描述:   从机无线任务
//
//函数参数:   无
//
//返回值:     无
//
//创建者:     
//*******************************//

void Slave_Task(void)//无线任务处理函数会在下面补齐,这里只是搭建了一个框架
{

}

Establish data structure : declare all variables, perform assignment initialization

#define BUFFERSIZE 4

uint8_t PingMsg[] = "PING";//PING数据
uint8_t PongMsg[] = "PONG";//PONG数据

uint16_t BufferSize = BUFFERSIZE;
uint8_t Buffer[BUFFERSIZE];//接收数据缓存

#ifdef MASTER
uint8_t EnbleMaster = true;
#else
uint8_t EnbleMaster = false;
#endif

uint32_t Master_TxNumber = 0;//发送数据计数
uint32_t Master_RxNumber = 0;//接收数据计数

uint32_t Slave_TxNumber = 0;//发送数据计数
uint32_t Slave_RxNumber = 0;//接收数据计数

tRadioDriver *Radio = NULL;/*如果需要收发任务,则要获取无线收发的数据结构:
                            在main函数中调用无线收发任务函数RadioDriverInit
                           进行初始化,返回一个初始化的结构体指针*/

Wireless transceiver task initialization function RadioDriverInit:

 

tRadioDriver RadioDriver;

tRadioDriver* RadioDriverInit( void )//如果需要使用无线收发任务,则需要在main函数中调用该函数进行初始化,返回一个初始化的指针
{
#if defined( USE_SX1232_RADIO )
    RadioDriver.Init = SX1232Init;
    RadioDriver.Reset = SX1232Reset;
    RadioDriver.StartRx = SX1232StartRx;
    RadioDriver.GetRxPacket = SX1232GetRxPacket;
    RadioDriver.SetTxPacket = SX1232SetTxPacket;
    RadioDriver.Process = SX1232Process;
#elif defined( USE_SX1272_RADIO )
    RadioDriver.Init = SX1272Init;
    RadioDriver.Reset = SX1272Reset;
    RadioDriver.StartRx = SX1272StartRx;
    RadioDriver.GetRxPacket = SX1272GetRxPacket;
    RadioDriver.SetTxPacket = SX1272SetTxPacket;
    RadioDriver.Process = SX1272Process;
#elif defined( USE_SX1276_RADIO )
    RadioDriver.Init = SX1276Init;
    RadioDriver.Reset = SX1276Reset;
    RadioDriver.StartRx = SX1276StartRx;
    RadioDriver.GetRxPacket = SX1276GetRxPacket;
    RadioDriver.SetTxPacket = SX1276SetTxPacket;
    RadioDriver.Process = SX1276Process;
#else
    #error "Missing define: USE_XXXXXX_RADIO (ie. USE_SX1272_RADIO)"
#endif    

    return &RadioDriver;
}

Encoding :

function function code

//上面已经对功能函数搭建好框架,现将实现代码填充进去

//**********************************//
//
//函数名称:  MLCD_Show 
//
//函数描述:   主机显示任务
//
//函数参数:   无
//
//返回值:     无
//
//创建者:     
//*******************************//

void MLCD_Show(void)
{
  uint8_t str[20] = {0};
  //LCDgpio重新初始化
  LCD_GPIO_Init();

  sprintf((char*)str,"%d",Master_RxNumber);
  show_rx(str);////将RX数据显示到屏幕上
  memset((char*)str,0,strlen((const char*)str));


  sprintf((char*)str,"%d",Master_TxNumber);
  show_tx(str);////将TX数据显示到屏幕上

  //SPI重新初始化
  HAL_SPI_DeInit(&hspi1);
  MX_SPI1_Init();
}


//**********************************//
//
//函数名称:  SLCD_Show 
//
//函数描述:   从机显示任务
//
//函数参数:   无
//
//返回值:     无
//
//创建者:    
//*******************************//

void SLCD_Show(void)
{
  uint8_t str[20] = {0};
  //LCDgpio重新初始化
  LCD_GPIO_Init();

  sprintf((char*)str,"%d",Slave_RxNumber);
  show_rx(str);////将RX数据显示到屏幕上
  memset((char*)str,0,strlen((const char*)str));


  sprintf((char*)str,"%d",Slave_TxNumber);
  show_tx(str);////将TX数据显示到屏幕上

  //SPI重新初始化
  HAL_SPI_DeInit(&hspi1);
  MX_SPI1_Init();
}

//**********************************//
//
//函数名称:   Master_Task
//
//函数描述:   主机无线任务
//
//函数参数:   无
//
//返回值:     无
//
//创建者:     
//*******************************//

void Master_Task(void)//无线任务处理函数会在下面补齐,这里只是搭建了一个框架
{
  switch(Radio->Process())//执行无线功能任务处理函数SX1276LoRaProcess,返回一个无线功能状态值,然后根据状态值做相应的处理
  {
    
    case RF_RX_DONE:
         Radio->GetRxPacket(Buffer,&BufferSize);//接收数据
         printf("Master_Task:RX_____%s\n",Buffer);//串口打印接收的数据
         if(strncmp((const char*)Buffer,(const char*)PongMsg,strlen((const char*)PongMsg)) == 0)//判断接收的是否为PONG数据
         {
          LedToggle(LED_RX);//发送指示灯翻转
          Master_RxNumber++;//发送计数
          Radio->SetTxPacket(PingMsg,strlen((const char*)PingMsg));//打开发送模式
          HAL_Delay(200);
         }
    break;
    case RF_TX_DONE:
          LedToggle(LED_TX);//接收指示灯翻转
          Master_TxNumber++;//接收计数
          Radio->StartRx();//打开接收模式
    break;
    default :
    break;
  }
}


//**********************************//
//
//函数名称:   Slave_Task
//
//函数描述:   从机无线任务
//
//函数参数:   无
//
//返回值:     无
//
//创建者:     
//*******************************//

void Slave_Task(void)//无线任务处理函数会在下面补齐,这里只是搭建了一个框架
{

  switch(Radio->Process())
  {
    
    case RF_RX_DONE:
         Radio->GetRxPacket(Buffer,&BufferSize);
         printf("Slave_Task:RX_____%s\n",Buffer);
         if(strncmp((const char*)Buffer,(const char*)PingMsg,strlen((const char*)PingMsg)) == 0)
         {
          LedToggle(LED_RX);
          Slave_RxNumber++;
          Radio->SetTxPacket(PongMsg,strlen((const char*)PongMsg));
          HAL_Delay(200);
         }
    break;
    case RF_TX_DONE:
          LedToggle(LED_TX);
          Slave_TxNumber++;
          Radio->StartRx();
    break;
    default :
    break;
  }
}

Main function code

  Lcd_Init();//LCD初始化函数
  
  
  Lcd_Clear_xy(0,0,GREEN);
  Lcd_Clear_xy(0,45,YELLOW);
 
  Gui_DrawFont_GBK16(12, 10, RED, GREEN, "LoRa Topology");
#ifdef MASTER
  Gui_DrawFont_GBK16(40, 26, RED, GREEN, "Master");
#else
  Gui_DrawFont_GBK16(40, 30, RED, GREEN, "SLAVE");
#endif
  Gui_DrawFont_GBK16(12, 50, BLACK, YELLOW, "SSID:");
  Gui_DrawFont_GBK16(12, 77, BLACK, YELLOW, "RX:");
  Gui_DrawFont_GBK16(12, 104, BLACK, YELLOW, "TX:");
  
  show_ssid("ERROR");
  show_rx("ERROR");
  show_tx("ERROR");
  
  Lcd_WriteIndex(0x29);//Display on  打开LCD屏幕显示
  
  Radio = RadioDriverInit();//如果需要使用无线收发任务,则需要在main函数中调用该函数进行初始化,返回一个初始化的指针
  Radio->Init();//调用Radio初始化函数之后才能正确调用Radio中的其他函数
  
  #ifdef MASTER
  Radio->SetTxPacket(PingMsg,strlen((const char*)PingMsg));//如果是主机,则发送PING数据
  printf("I am Master!\n");
#else
  Radio->StartRx();//如果是从机,则设置为接收状态
  printf("I am Slave!\n");
#endif

  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
    
    if(EnbleMaster == true)
    {
      MLCD_Show();
      Master_Task();
    }
    else
    {
      SLCD_Show();
      Slave_Task();
    }

  }

LoRa driver source code modification

    1. Comment out the unused code:

    2. Set LoRa parameters

    3. Because the LoRa wireless related functions involve the judgment of FSK, it is necessary to add the related functions of FSK, otherwise the compilation will not pass:

 

LoRaPingPong system function debugging

Content summary :

1. Hardware preparation

2. Program programming

3. Debug information

Hardware preparation :

LoRa device X2

STlinkX1

USBmini cable X2

Program programming :

Select different projects to burn separately

Debug info :

Serial debugging information

screen debug info

Related Posts