LoRa终端设备ASR6505之PingPong通信
前言:前面几篇文章详细介绍了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 !rn");
// 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=%drn",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: PINGrn");
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=%drn",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: PONGrn");
}
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: PINGrn");
}
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、实现现象
- CSS Selectors Level 4新特性全面解析
- 巧用外部表避免大量的insert (r4笔记第71天)
- 如何用java语言实现C#中的ref关键字(按引用传递参数)的效果
- 16(01)总结List的子类,ArrayList,Vector,LinkedList
- 16(02)总结泛型
- 基于 React + Webpack 的音乐相册项目(上)
- 16(03)总结增强for循环,静态导入,可变参数
- 基于 React + Webpack 的音乐相册项目(下)
- 通过图表简化sql语句的表关联(r4笔记第70天)
- 通过编程控制CPU利用率(r4笔记第69天)
- Java基础(01)-15总结对象数组,集合Collection,集合List
- 【专业技术】Android内存泄漏简介
- 设计模式之工厂方法(r4笔记第89天)
- delete相关的pl/sql调优(r4笔记第87天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 了解 Swift 的 Result 类型
- Java自动化测试(特殊元素处理 26)
- Java自动化测试(web自动化测试框架 28)
- 你想要拥有自己的搜索引擎吗?
- JAVA三年面试总结,金九银十,你准备好了吗?
- Best Cow Line(POJ 3617)
- Flask单点登录竟然只要几行代码就能搞定!
- 区间调度问题
- 迷宫的最短路径
- Lake Counting (POJ No.2386)
- 部分和问题(DFS)
- 为什么 React Hooks useState 更新不符预期?
- 技术分享 | MySQL 使用 MariaDB 审计插件
- 第12期:压缩表性能监测
- 第05期:使用 prometheus 监控 clickhouse 集群