LoRa协议在Arduino上的应用——原理及代码分析(二)

article/2025/3/16 7:27:35

目录

  • `LoRa`调制与解调模式
  • 代码分析
    • 主程序
      • 开始发包
      • `Packet Structure`
      • 数据传输时间
      • 重置FIFO地址与payload长度
      • 结束发包
      • 中断源
    • `Receiver`
      • `parsePacket`
      • 是否存在数据包
      • 数据读取
      • `RSSI`

前述文章链接在此~~
LoRa协议在Arduino上的应用——原理及代码分析(一).

LoRa调制与解调模式

扩频循环纠错编码相结合
在这里插入图片描述
可以看到,LoRa调制模式下,有一个独立的双端口数据缓冲区FIFO,可通过所有模式共有的SPI接口进行访问。
可针对特定的应用优化LoRa调制:扩频因子调制带宽错误编码率
在这里插入图片描述
先说一下扩频:其实通俗来讲,原来我发送一个信号,这个信号中只包含两个bit10,现在我发送一个信号,这个信号中包含16个bit,这样我就相当于将信号拓宽,扩频因子就是16/2=8。那么我们就可以理解为什么扩频技术是扩展信号的带宽:把信道想象成一个通道,原来一次只能通过两个bit,现在一次性可以通过16个bit,信道是不是变宽了?
在这里插入图片描述
但有一点需要注意,就是扩频前后信号的能量是不变的
至于扩频的好处:可以根据香农公式,在相同的信息速率下,带宽和信噪比可以互换,扩频就是用大带宽,换接收端信噪比的低要求

再来看下循环纠错编码
其实就是使用纠错编码中的循环码:网上资料很多,不赘述了

代码分析

主程序

int counter = 0;
...
void loop() {...// send packetLoRa.beginPacket();LoRa.print("hello ");LoRa.print(counter);LoRa.endPacket();}

开始发包

int LoRaClass::beginPacket(int implicitHeader)
{if (isTransmitting()) {return 0;}// put in standby modeidle();if (implicitHeader) {implicitHeaderMode();} else {explicitHeaderMode();}// reset FIFO address and paload lengthwriteRegister(REG_FIFO_ADDR_PTR, 0);writeRegister(REG_PAYLOAD_LENGTH, 0);return 1;
}

在头文件中,已经设置implicitHeader默认为false,因此会进入explicitHeaderMode()函数

void LoRaClass::explicitHeaderMode()
{_implicitHeaderMode = 0;writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe);
}

可以看到这个函数就是把原来调制模式配置寄存器中的数值读出来,将最后一位设置为0,表示Explicit Header mode,然后写回。

Packet Structure

LoRa的数据包包含三个部分:
在这里插入图片描述

  1. A preamble:前导码
    用于保持接收机与输入的数据流同步,作用提醒接收芯片,即将发送的是有效信号。默认size是12个符号长度,有时为了缩短接收机占空比,可以缩短前导码长度。接收机会定期检测前导码,因此接收和发射前导码长度需要一致。
  2. An optional header
    可以选择显示(explicit)或隐式(implicit)两种类型。
    显式报头:包括Payload长度,前向纠错编码率,是否使用CRC(16bit)
    隐式报头:需要手动设置无线链路两端的Payload长度、错误编码率、CRC(如果扩频因子SF=6,只能使用隐式报头模式
  3. The data payload
    长度不固定,在FIFO中读写

数据传输时间

LoRa数据包总传输时间 = 前导码传输时间Tpre + 数据包传输时间Tpay
前导码传输时间为:Tpre = (Npre + 4.25)*Tpay
Npre表示已设定的前导码长度(读取RegPreambleMsbRegPreambleLsb寄存器得到)
计算Payload符号数的公式为:
在这里插入图片描述
PL表示Payload字节数,H=0表示header使能,DE=1表示LowDataRateOptimize=1CR表示编码速率

重置FIFO地址与payload长度

结束发包

int LoRaClass::endPacket(bool async)
{if ((async) && (_onTxDone))writeRegister(REG_DIO_MAPPING_1, 0x40); // DIO0 => TXDONE// put in TX modewriteRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX);if (!async) {// wait for TX donewhile ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) {yield();}// clear IRQ'swriteRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK);}return 1;
}

默认async = false_onTxDone(NULL)
在这里插入图片描述
在这里插入图片描述
SX112系列的6个通用DIO引脚在LoRa模式下均可用,其映射关系取决于RegDioMapping1RegDioMapping2这两个寄存器
第一句代码的意思就是如果处于异步通信状态,且在发射完成状态(此处有疑义),就将DIO0引脚映射为发射完成指示符
设置为发射模式,如果不处于异步通信状态,就等待发送完成,然后清除中断。
这里讲的不是很清楚,我们再来具体看一下:

  1. async:意思是异步,是在non-blocking mode下使用的一个变量。由于网关可能不会向后发送任何数据,因此我们在尝试接收之前使套接字成为非阻塞状态,以防止卡在等待永远不会到达的数据包中
    同步、异步、阻塞、非阻塞这四个是进程间通信的概念,进程间通信通过send()receive()两种基本操作完成:
    阻塞式发送:发送方进程会一直被阻塞,直到消息被接收方进程收到;
    非阻塞式发送:发送方调用send()后,可以一直进行其他操作;
    阻塞式接收:接收方进程会一直被阻塞,直到消息到达可用;
    非阻塞式接收:接收方调用receive()后,要么得到一个有效的效果,要么得到一个空值
    (此处参考文章)
  2. void (*_onTxDone)();
    可以看到,这是一个类内私有的函数指针,_onTxDone是一个指针,该指针指向的函数的返回值是void
    初始化时,该指针指向的确实是一个NULL,但是如果执行了这个函数:
void LoRaClass::onTxDone(void(*callback)())
{_onTxDone = callback;.......
}

那么这个指针就不为空,也就是满足这个判断条件:

if ((async) && (_onTxDone))

在这里插入图片描述

writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX);

这一句对应的就是上图的Mode Request Tx

while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0)

这一句就是在等待中断

中断源

  1. FifoEmpty:当整个FIFO为空,中断源为高,从FIFO检索数据的时候,FifoEmpty在NSS下降沿更新。也就是说每次读取操作之后检查FifoEmpty状态,以决定是否读取下一个字节,FifoEmpty=1表示不需要读取更多字节
  2. FifoFull
  3. FifoOverrunFlag:is set when a new byte is written by the user (in Tx or Standby modes) or the SR (in Rx mode) while the FIFO is already full,在FIFO已满的情况下,有一个新的字节被写入的时候。数据丢失,并且标志应通过写1清除,同时FIFO也将被清除。
  4. PacketSent:when the SR’s last bit has been sent
  5. FifoLevel

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#define IRQ_TX_DONE_MASK           0x08

只把TxDoneMask置为1,其余全部置为0。而如果要满足while中的循环条件,则REG_IRQ_FLAGS这个寄存器中的第3bit位TxDone必须为0,如果不满足就yield()就是一直执行这个空循环
跳出这个循环之后,就清除中断标志,并且设置TX完成标志位

Receiver

void loop() {// try to parse packetint packetSize = LoRa.parsePacket();if (packetSize) {// received a packetSerial.print("Received packet '");// read packetwhile (LoRa.available()) {Serial.print((char)LoRa.read());}// print RSSI of packetSerial.print("' with RSSI ");Serial.println(LoRa.packetRssi());}
}

parsePacket

int LoRaClass::parsePacket(int size)
{int packetLength = 0;int irqFlags = readRegister(REG_IRQ_FLAGS);if (size > 0) {implicitHeaderMode();writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);} else {explicitHeaderMode();}// clear IRQ'swriteRegister(REG_IRQ_FLAGS, irqFlags);if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {// received a packet_packetIndex = 0;// read packet lengthif (_implicitHeaderMode) {packetLength = readRegister(REG_PAYLOAD_LENGTH);} else {packetLength = readRegister(REG_RX_NB_BYTES);}// set FIFO address to current RX addresswriteRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));// put in standby modeidle();} else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) {// not currently in RX mode// reset FIFO addresswriteRegister(REG_FIFO_ADDR_PTR, 0);// put in single RX modewriteRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE);}return packetLength;
}

这个函数很长,我们逐步分析。
首先观察返回值,可以知道这个函数是计算数据包的长度
在头文件中默认定义int parsePacket(int size = 0);
也就是说会进入显式数据包模式

#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20
#define IRQ_RX_DONE_MASK           0x40

接收一个数据包的条件是:从中断寄存器中的读取的数据第6位为0或者第5位为0
在这里插入图片描述
读取完数据包长度之后,还需要设置FIFO的地址
在这里插入图片描述
具体就是跟着这张图来

是否存在数据包

int LoRaClass::available()
{return (readRegister(REG_RX_NB_BYTES) - _packetIndex);
}

这个看起来有点像串口检测字符的那个函数。。。。。
如果存在数据包,REG_RX_NB_BYTES这个寄存器的值就不为0,就会开始读取数据

数据读取

int LoRaClass::read()
{if (!available()) {return -1;}_packetIndex++;return readRegister(REG_FIFO);
}

返回从FIFO中读取的数据

RSSI

received signal strength indicator:接收信号强度指示符
常规
RSSI (dBm) = -157 + Rssi, (高频口)
RSSI (dBm) = -164 + Rssi, (低频口)

int LoRaClass::packetRssi()
{return (readRegister(REG_PKT_RSSI_VALUE) - (_frequency < 868E6 ? 164 : 157));
}

在这里插入图片描述
在这里插入图片描述


http://chatgpt.dhexx.cn/article/0MDu3pgv.shtml

相关文章

【从0到1搭建LoRa物联网】12、LoRa网关与平台通讯协议

系列文章&#xff1a; 【从0到1搭建LoRa物联网】1、LoRa物联网的架构 【从0到1搭建LoRa物联网】2、终端设备开发方式 【从0到1搭建LoRa物联网】3、国产LoRa终端ASR6505软硬件 【从0到1搭建LoRa物联网】4、国产LoRa终端ASR6505普通GPIO 【从0到1搭建LoRa物联网】5、国产LoRa终端…

物联网LoRa系列-11:LoRa终端--基于物理层协议的PingPong应用程序的软件架构

至此&#xff0c;我们已经搭建好LoRa终端的软硬件开发环境&#xff0c;接下来&#xff0c;将解构和实现LoRa终端的应用程序。 LoRa终端的应用程序&#xff0c;&#xff08;1&#xff09;可以直接基于LoRa的物理层协议&#xff08;LoRa芯片实现&#xff09;&#xff1b;&#x…

自有协议的LoRa模块应用

LoRa主要是由SemTech公司开发并拥有专利的物理层的调制技术。基于LoRa&#xff0c;LoRa联盟也开发和开放了的LoRaWAN的协议&#xff0c;用于组建网络和实现应用。Ali在LoRaWAN基础上又开发了LPWAN的物联网应用平台&#xff0c;这些都需要遵从专有的LoRa芯片&#xff0c;专门协议…

LoRa协议在Arduino上的应用——原理及代码分析(一)

目录 LoRa简介硬件设备源码分析Sender 两种调制方式 LoRa简介 详细的LoRaWAN协议解析在这里 本文主要介绍LoRa在Arduino上的实现过程&#xff0c;从而学习LoRa协议的实现。 硬件设备 包括传统的GFSK调制技术以及LoRa&#xff08;远程&#xff09;扩频技术 这里说一下啥叫LoR…

物联网LoRa系列-25:LoRa终端--LoRaWAN协议简介与LoRa WAN终端软件选择

前言&#xff1a; 从本篇文章开始&#xff1a; 从通信节点的角度度看&#xff0c;将从Lra终端对点对通信转到LoRa终端与LoRa网关的通信、LoRa终端与LoRa云服务器的通信&#xff1b; 从协议栈的角度看&#xff0c;将从物理层上升到LoRa的MAC层&#xff0c; 即LoRaWAN&#xf…

lora三层服务器协议,LoRa以及LoRa包含的几种协议

为何LoRa成为“一夜爆红”的新技术呢?一定是因为它具有非常显著的特点: 长距离:1~20km 节点数:万级,甚至百万级 电池寿命:3~10年 数据速率3~50kbps LoRa作为一种无线技术,基于Sub-GHz的频段使其更易以较低功耗远距离通信,可以使用电池供电或者其他能量收集的方式供电。…

Lora如何组网?有哪些简单的Lora组网协议?

我从事物联网设计10余年&#xff0c;对无线通讯技术的ASK、FSK、Zigbee、Z-Wave、NB-Iot、Lora及LoraWan等都有一些或多或少的接触。 其中Lora无线通讯技术对我影响比较深刻&#xff0c;了解也相对比较透彻。 我在2016年接触的Lora无线通讯技术&#xff0c;但是&#xff0c;在…

什么是LoRa协议?

1 开场白 这篇文章给读者分享LoRa和LoRaWAN相关的技术简介, Low-Power, Wide-Area Networks(LPWAN)为实现数十亿的万物互联设备而生, LoRaWAN专为LPWAN网络优化更长的电池寿命,更长的距离和更低的代价。 [欢迎大家关注公众号:LoRa万物互联,获取更多LoRa资讯] 2 LoRa是什…

物联网LoRa系列-2:LoRa系统架构与协议栈详解

主要内容 1. 什么是Lora&#xff1f; 2. LoRa的主要特点&#xff1f; 3. LoRa的系统架构&#xff1f; 4. LoRa终端节点的网络连接方式&#xff1f; 5. LoRa终端节点的三种工作模式&#xff1f; 6. LoRa的协议栈&#xff1f; 1. 什么是LoRa LoRa是semtech公司创建的低功耗…

LoRa协议

注意&#xff1a;本页提供的信息来自LoRa™Alliance于2015年1月发布的LoRaWAN Specification V1.0。LoRa联盟负责随时更改规格&#xff0c;恕不另行通知。RF Wireless World不对任何与此相关的问题负责。请参阅LoRa Alliance&#xff08;https://www.lora-alliance.org&#xf…

用命令:tar -zxvf,解压tar.gz包失败的问题解决。

问题描述&#xff1a; 主机上&#xff0c;使用tar -zxvf 解压tar.gz包的时候&#xff0c;出现以下错误&#xff1a; gzip: stdin: not in gzip format tar: Child returned status 1 tar: Error is not recoverable: exiting now 问题办法&#xff1a; 开门见山&#xff0c;…

Liunx tar -zxvf 命令解压失败

使用tar -zxvf 命令解压mysql.tar报错 [rootiZ2ze8f0mv6pvfs1n3rpgaZ mysql]# tar -zxvf mysql.tar gzip: stdin: not in gzip format tar: Child returned status 1 tar: Error is not recoverable: exiting now 解决方案 去掉z参数&#xff0c;使用 tar -xvf 解压正常 […

tar -zxvf是什么意思

先上DJ&#xff0c;先上DJ 含义&#xff1a;使用gzip工具&#xff08;-z&#xff09;解压&#xff08;-x&#xff09;由参数-f指定的文件&#xff0c;并显示压缩过程中详细信息&#xff08;-v&#xff09; 参数含义tarLinux压缩/解压缩命令-z代表gzip&#xff0c;使用gzip工具…

word标题设置级别

想要这样子: 点击标题1,标题1前面就带有级别. 新建word是这样的,没有带级别: 转自:https://blog.csdn.net/xtggbmdk/article/details/82591717 选择多级列表菜单 “2”级的右侧选“标题2” .在标题3的右侧选择标题3

NPOI Word 多级标题结构设置

通过NPOI实现以下功能 首先通过Word新建个样式模板&#xff0c;该样式模板包括你想要的几个标题样式&#xff0c;比如我这里就需要三个样式&#xff0c;标题、标题1、标题2 打开Word,新建文档&#xff0c;然后输入内容&#xff0c;更改样式&#xff0c;另存为word模板&#xf…

word:如何一键修改同一级别标题

word&#xff1a;如何一键修改同一级别标题 1.选中任意一个三级标题——开始——编辑——选择——选中格式相似的文本 2.修改字体样式&#xff1a;开始——样式——标题 3 ——右击——修改 样式名称不要修改——举例字体更改为&#xff1a;微软雅黑、五号、加粗、深红色——…

Word标题中按级别自动添加编号

一 大标题 1 标题1 1.1 标题1.1 1.1.1 标题添加 1.1.2 zaitian 1.2 新添加 1.2.1 在添加 1.3 标题1.2 2 标题2 2.1 标题2.1 2.2 添加新标题 3 标题2.2 3.1 标题天机 3.1.1 标题3 教程&#xff1a;为了实现上述的样式&#xff0c;即标题编号按照&#xff1a; 标题大级别.中级级…

Word文档标题自动增加序号

在编写Word文档时&#xff0c;经常需要使用到多级标题&#xff0c;为了可读性&#xff0c;一般需要给标题增加序号&#xff0c;该文档就是作者实际操作过程中遇到的问题和解决方法。 第一步&#xff1a; 选择“开始”-----》“段落”---》“多级列表”---》“定义新的多级列表…

word设置标题多个级别

https://jingyan.baidu.com/article/1876c852468dd8890b1376a9.html 1.在开始菜单里面看看标题 2.选择多级列表菜单 3.点击下面更多按钮&#xff0c;在”1“的右侧选”标题1“ 4.在“2”级的右侧选“标题2” 5.在标题3的右侧选择标题3

java生成word带多级标题,word自动生成多级标题的方法

毕业论文格式是一件很麻烦的事&#xff0c;通过修改各级标题来实现。如何快速地修改格式呢?接下来学习啦小编为你分享word自动生成多级标题的方法。希望对你有帮助! word自动生成多级标题的方法 使WORD里面的文章自动生成目录: 假如文章中标题格式为 第一节……大标题(一级) 1…