目录

  • `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));
}


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

  1. 对dpdk的rte_ring实现原理和代码分析

    对dpdk的rte_ring实现原理和代码分析 前言 dpdk的rte_ring是借鉴了linux内核的kfifo实现原理,这里统称为无锁环形缓冲队列. 环形缓冲区通常有一个读指针和一个写指针.读指针 ...

  2. TrueCrypt 6.2a原理及代码分析

    TrueCrypt 6.2a原理及代码分析 3 comments 25th Apr 10 rafa 1 项目物理布局 Project     |____ Boot /* MBR部分的代码 */     ...

  3. 免费的Lucene 原理与代码分析完整版下载

    Lucene是一个基于Java的高效的全文检索库. 那么什么是全文检索,为什么需要全文检索? 目前人们生活中出现的数据总的来说分为两类:结构化数据和非结构化数据.很容易理解,结构化数据是有固定格式和结 ...

  4. Lucene原理与代码分析(高手博客备忘)

    2019独角兽企业重金招聘Python工程师标准>>> 随笔 - 69  文章 - 77  评论 - 687 随笔分类 - Lucene原理与代码分析 Lucene 4.X 倒排索引 ...

  5. Lucene 原理与代码分析完整版

    原文地址为: Lucene 原理与代码分析完整版 Lucene 原理与代码分析系列文章已经基本告一段落,可能问题篇还会有新的更新. 完整版pdf可由以下链接下载. Lucene 原理与代码分析完整版 ...

  6. OpenStack 虚拟机冷/热迁移的实现原理与代码分析

    目录 文章目录 目录 前文列表 冷迁移代码分析(基于 Newton) Nova 冷迁移实现原理 热迁移代码分析 Nova 热迁移实现原理 向 libvirtd 发出 Live Migration 指令 ...

  7. 海思AI芯片(Hi3519A/3559A)方案学习(十七)开发板上运行yolo3模型的代码分析

    前言 前面的博客系列 已经介绍了如何将caffemodel转换成wk文件,如何将jpg文件转成bgr格式数据以及如何在PC上仿真模型推理等,基于这些基础,本文来结合代码分析如何在板子上推理yolov3 ...

  8. stm32-通用定时器原理及代码分析

    目录 定时器:基本,通用 一,基本定时器: 作用: 结构图: 二.通用定时器: 作用: 结构图: 三.代码分析: 1.选择时钟 2.配置时基单元 3.产生中断 4.使用定时器 定时器:基本,通用 一, ...

  9. LR_scheduler及warmup底层原理和代码分析

    LR_scheduler LR_scheduler是用于调节学习率lr的,在代码中,我们经常看到这样的一行代码 scheduler.step() 通过这行代码来实现lr的更新的,那么其中的底层原理是什 ...

  10. 单点登录(SSO)原理以及代码分析

    什么是单点登录: 简单概括就是,对于一个系统有多个应用(淘宝有淘宝,天猫)的情况,用户只需登陆一次,就可以访问所有该系统下的其它应用(登录了淘宝后,跳转到天猫也不用登陆了). 登录的问题: 首先要知道 ...

最新文章

  1. Python程序打包
  2. 在GridView里做单选按钮,总结了三种方法
  3. db2v9/9.5高级应用开发_Spark v2.4.3应用程序开发入门-基于IDEA/Maven 构建简单应用
  4. python将列表横着输出来
  5. iOS开发笔记[13/50]:解决SenTestingKit/SenTestingKit.h: No such file or directory问题
  6. B端产品思维全解析,提升产品经理核心竞争力
  7. 基础网络和关键基础设施
  8. 快速掌握MATLAB应用,从这一步开始
  9. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
  10. 计算机硬件基础大纲,计算机硬件基础教学大纲..docx
  11. Apache Flume 简介
  12. 【高斯模糊算法的理解】简单易懂
  13. git base cli
  14. 2017年北京共享单车数据(订单数据)
  15. WTL for MFC Programmers, Part V - Advanced Dialog UI Classes
  16. SEO网站内容优化的6点干货分享分享-飞鱼SEO
  17. 2. Mac 命令行走代理服务器
  18. 非对称加密之公钥密码体系 【五】
  19. Pandas-DataFrame使用
  20. ARM编译中的RO、RW和ZI DATA区段

热门文章

  1. ,PLCSIM、SIMIT、Amesim、NX MCD、 Process Simulate、Plant Simulation,一文带你了解西门子整个虚拟调试与仿真软硬件体系
  2. 蚂蚁课堂视频笔记思维导图-3期 七、互联网高并发解决方案
  3. 红米k30pro工程测试代码_红米K30Pro-MIUI12测试版|全局小窗|隐秘面具|拦截网|光锥动效|超多功能|步数修改-刷机之家...
  4. 设计模式之模板模式(模板方法)
  5. VMware许可证过期解决方案
  6. vue中动态加载图片路径
  7. libreoffice word转pdf时中文乱码问题解决
  8. NB-iot的M5310A的学习
  9. python ui自动化_python-UI自动化
  10. 常用身份证识别 OCR 技术 手机拍照识别身份证