前言:前面几篇文章详细介绍了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、实现现象