深入了解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 < 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系统设计
内容概要:
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
程序烧写:
选择不同的工程进行分别烧录
调试信息:
串口调试信息
屏幕调试信息