前言:前面几篇文章详细介绍了ASR6505外设的使用,从本篇开始介绍ASR6505关于LoRa的使用。
1、关于ASR6505的LoRa
ASR6505是SIP封装的STM8L和SX1262,因此对SX1262的操作实质就是对一个SPI外设的操作。
ASR6505内部连接:
SX1262 STM8L
RESET <--------------> PH_0
MOSI <--------------> PI_2
MISO <--------------> PI_3
SCLK <--------------> PI_1
BUSY <--------------> PH_1
DIO_1 <--------------> PH_2
BUSY:SX1262忙检测引脚,DIO1:通常配置为外部中断,来唤醒mcu起来处理射频事件。
另外SX1262的NSS以及射频开关电源控制引脚连接:
NSS <---------------> PD_7
SWITCH_POWER <--------------> PG_6
因此,后续的射频操作都是基于上面的连接进行的。LoRa的寄存器繁多,所幸的是,官方已经提供了完整的驱动,我们首先是要会用,然后再去深究原理。
2、PingPong通信
所谓“PingPong”:就是一发一收、一发一收,如此反复循环,即就是通常所说的点对点通讯,市面上的大部分私有协议(非LoRaWAN)也是在此基础上改进的。一些小型物联网搭建,就可以用点对点通讯。
3、无线射频参数
频率:无线最重要的参数,虽然SX1262支持150-960MHZ,但是为了更好的硬件射频性能,硬件已经匹配为470-510M。
发射功率:0-22dBm,功率越大,功耗越大。
扩频因子:也可以理解成速率,取值6-12,6速率最快,12最慢,速率越快,抗干扰越差,距离越近,反之。
编码率:取值1-4,分别对应4/5,4/6, 4/7, 4/8。
IQ极性:收发要保持相同。
4、实验设计
两个开发板相互发送数据,串口打印接收的数据,并打印RSSI和SNR。
5、核心代码
int main( void )
{
bool isMaster = true;// 主从机,从机的时候改为false
uint8_t i;
// Target board initialization
BoardInitMcu( );
BoardInitPeriph( );
printf("ASR6505 PingPong test !\r\n");
// Radio initialization
RadioEvents.TxDone = OnTxDone; //注册回调函数,下同
RadioEvents.RxDone = OnRxDone;
RadioEvents.TxTimeout = OnTxTimeout;
RadioEvents.RxTimeout = OnRxTimeout;
RadioEvents.RxError = OnRxError;
Radio.Init( &RadioEvents );
Radio.SetChannel( RF_FREQUENCY );
//配置射频收发参数
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true );
Radio.Rx( RX_TIMEOUT_VALUE );
while( 1 )
{
switch( State )
{
case RX:
if( isMaster == true )
{
if( BufferSize > 0 )
{
if( strncmp( ( const char* )Buffer, ( const char* )PongMsg, 4 ) == 0 )
{
printf("Received: PONG,rssi=%d,snr=%d\r\n",RssiValue,SnrValue);
// Send the next PING frame
Buffer[0] = 'P';
Buffer[1] = 'I';
Buffer[2] = 'N';
Buffer[3] = 'G';
// We fill the buffer with numbers for the payload
for( i = 4; i < BufferSize; i++ )
{
Buffer[i] = i - 4;
}
DelayMs( 1 );
printf("Send: PING\r\n");
Radio.Send( Buffer, BufferSize );
}
else
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 )
{ // A master already exists then become a slave
isMaster = false;
Radio.Rx( RX_TIMEOUT_VALUE );
}
else // valid reception but neither a PING or a PONG message
{ // Set device as master ans start again
isMaster = true;
Radio.Rx( RX_TIMEOUT_VALUE );
}
}
}
else
{
if( BufferSize > 0 )
{
if( strncmp( ( const char* )Buffer, ( const char* )PingMsg, 4 ) == 0 )
{
printf("Received: PING,rssi=%d,snr=%d\r\n",RssiValue,SnrValue);
// Send the reply to the PONG string
Buffer[0] = 'P';
Buffer[1] = 'O';
Buffer[2] = 'N';
Buffer[3] = 'G';
// We fill the buffer with numbers for the payload
for( i = 4; i < BufferSize; i++ )
{
Buffer[i] = i - 4;
}
DelayMs( 1 );
Radio.Send( Buffer, BufferSize );
printf("Send: PONG\r\n");
}
else // valid reception but not a PING as expected
{ // Set device as master and start again
isMaster = true;
Radio.Rx( RX_TIMEOUT_VALUE );
}
}
}
State = LOWPOWER;
break;
case TX:
Radio.Rx( RX_TIMEOUT_VALUE );
State = LOWPOWER;
break;
case RX_TIMEOUT:
case RX_ERROR:
if( isMaster == true )
{
// Send the next PING frame
Buffer[0] = 'P';
Buffer[1] = 'I';
Buffer[2] = 'N';
Buffer[3] = 'G';
for( i = 4; i < BufferSize; i++ )
{
Buffer[i] = i - 4;
}
DelayMs( 1 );
Radio.Send( Buffer, BufferSize );
printf("Sent: PING\r\n");
}
else
{
Radio.Rx( RX_TIMEOUT_VALUE );
}
State = LOWPOWER;
break;
case TX_TIMEOUT:
Radio.Rx( RX_TIMEOUT_VALUE );
State = LOWPOWER;
break;
case LOWPOWER:
default:
// Set low power
break;
}
TimerLowPowerHandler( );
// Process Radio IRQ
Radio.IrqProcess( );
}
}
6、代码分析
主机上电后,处于接收状态,若在一定时间内(设定的RX_TIMEOT)没有收到从机的回复数据,则主机主动发送“Ping”数据。发送完成后处于接收状态,若在一定时间内若收到从机的回复,则从接收转变为发送,继续发送“Ping”数据。若收到从机的回复数据,先判断数据的正确性,然后再打印出来。
从机上电后,处于接收状态,若接收超时,则继续重新接收,直到收到主机的数据。收到数据后,先判断数据的正确性,然后打印RSSI和SNR,最后再发送“Pong”数据回复主机。
7、实现现象