物联网之LoRa开发与应用四(LoRaPingPang系统设计)

深入了解LoRa技术原理

内容概要:

1、LoRa扩频通信原理

2、LoRa关键技术参数

3、LoRa数据收发任务

模拟无线通信:

数字无线通信:

无线通信传播方式:地波传播(低于2MHz)、天波传播(2MHz~30MHz)、直线传播(30MHz以上)

无线通信传播路径:反射、散射、衍射

无线通信噪声:

扩频通信技术:从各种类型的噪声和多径失真中获得免疫性

扩频通信算法:C表示信号质量

扩频通信原理:用户数据和扩频数据异或得到发送数据,增加了信号带宽,提高了信号质量

信号带宽(BW):

增加BW,可以提高有效数据速率以缩短传输时间,但是 以牺牲部分接受灵敏度为代价。对于LoRa芯片SX127x,LoRa带宽为双边带宽(全信道带宽),而FSK调制方式的BW是指单边带宽。

扩频因子(SF):原本使用1位来表示的信号变成多位来表示这个信号,提高信号的通讯质量

LoRa采用多个信息码片来代表有效负载信息的每个位,扩频信息的发送速度称为符号速率(Rs),而码片速率与标称的Rs比值即为扩频因子(SF,SpreadingFactor),表示了每个信息位发送的符号数量。

编码率(CR):提高信号质量的冗余,提高数据的可靠性

编码率(或信息率)是数据流中有用部分(非冗余)的比例。也就是说,如果编码率是k/n,则对每k位有用信息,编码器总共产生n位的数据,其中n-k是多余的。

LoRa采用循环纠错编码进行前向错误检测与纠错。。使用该方式会产生传输开销。

LoRa关键技术参数:

LoRa符号速率Rs计算:Rs=BW/(2^SF)

LoRa数据速率DR计算:DR= SF*( BW/2^SF)*CR

LoRaWAN主要使用了125kHz信号带宽设置,但其他专用协议可以利用其他的信号带宽(BW)设置。改变BW、SF和CR也就改变了链路预算和传输时间,需要在电池寿命和距离上做个权衡。

LoRa参数设置:

LoRa数据发送序列:

数据发送流程:(1)LoRa模块标准模式—>(2)发送模式—>(3)将数据写入发送队列—>(4)发送数据—>(5)等待发送完成(判断数据是否发送完成,如果发送完成则再次进入标准模式,如果还有数据需要发送,则进入第三步)

LoRa数据接收序列:

数据接收流程:

Radio事件任务:(官方固件提供的事件处理的任务)

LoRa固件相关代码:初始化时的参数设置 和 事件处理函数

​​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 {
if( RxPacketSnrEstimate {
RxPacketRssiValue = RSSI_OFFSET_LF + ( ( double )SX1276LR->RegPktRssiValue ) + RxPacketSnrEstimate;
}
else
{
RxPacketRssiValue = RSSI_OFFSET_LF + ( 1.0666 * ( ( double )SX1276LR->RegPktRssiValue ) );
}
}
else // HF
{
if( RxPacketSnrEstimate {
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系统设计

内容概要:

1、PingPong系统设计需求

2、PingPong系统通信机制

3、PingPong系统业务流程

PingPong系统设计需求:

将LoRa终端定义成两种角色:Master(主机)和Slave(从机)

Master主动发送PING数据,接收PANG数据

Slave如果接收到PING数据,回应PANG数据

终端在LCD屏幕上显示终端类型及收发数据包个数

PingPong通信机制:

//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业务流程-初始化:

PingPong业务流程-Master:

PingPong业务流程-Slave:

LoRa参数设置:

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

数据包结构:

LoRaPingPang系统功能开发

内容概要:

1、IAR工程配置

2、搭建框架

3、编码

IAR工程配置:配置两个工程:Master 和 Slave(两个工程公用一个代码,修改一个工程下的代码,所有工程代码一起改变)

建立功能函数:建立四个函数(LCD菜单显示信息、无线收发任务处理)

//**********************************//
//
//函数名称: 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)//无线任务处理函数会在下面补齐,这里只是搭建了一个框架
{

}

建立数据结构:声明全部变量、进行赋值初始化

#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


进行初始化,返回一个初始化的结构体指针*/

无线收发任务初始化函数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;
}

编码:

功能函数编码

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

//**********************************//
//
//函数名称: 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函数编码

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驱动源码修改

1、注释掉不用的代码:

2、设置LoRa参数

3、因为LoRa无线相关函数涉及到FSK的判断,所以需要添加FSK的相关函数,否则编译不通过:


LoRaPingPong系统功能调试

内容概要:

1、硬件准备

2、程序烧写

3、调试信息

硬件准备:

LoRa设备X2

STlinkX1

USBmini线X2

程序烧写:

选择不同的工程进行分别烧录

调试信息:

串口调试信息

屏幕调试信息
———————
作者:许新天
来源:CSDN
原文:https://blog.csdn.net/weixin_39148042/article/details/81588897

说明:LoRa应用学习站通过公开互联网收集、整理并转载有关LoRa及物联网应用解决方案,以供广大LoRa应用开发者和爱好者共同学习交流和参考运用到实际生产生活中。本站所有转载的文章、图片、音频、视频等资料的版权归版权所有人所有并衷心感谢您的付出,由于本站采用的非本站原创文章及图片等内容无法一一联系确认版权者,如果本网所选内容的文章原创作者认为其作品不宜放在本站,请及时通过以下留言功能通知我们采取适当措施,避免给双方造成不必要的经济损失。如果您希望保留本文在LoRa学习站,但希望文章末尾提供对作者的致谢或者产品、网站交换链接的,也请将需求写入以下留言栏中,谢谢您的支持。让我们共同努力,打造万物互联的未来美好生活!

您的留言或需求:

  • Hubery
    回复

    你好,请问我使用了LoRa的Pingpong传输系统,是否有必要在代码中加入检测丢包的代码?或者说LoRaPingpong系统有必要检测丢包吗?如果是可以或者必要的话,能否给些提示可以怎么测量丢包,例如信息的序列号检测?谢谢!