日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/9 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LoRa协议在Arduino上的应用——原理及代码分析(二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • `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的數據包包含三個部分:

  • A preamble:前導碼
    用于保持接收機與輸入的數據流同步,作用提醒接收芯片,即將發送的是有效信號。默認size是12個符號長度,有時為了縮短接收機占空比,可以縮短前導碼長度。接收機會定期檢測前導碼,因此接收和發射前導碼長度需要一致。
  • An optional header
    可以選擇顯示(explicit)或隱式(implicit)兩種類型。
    顯式報頭:包括Payload長度,前向糾錯編碼率,是否使用CRC(16bit)
    隱式報頭:需要手動設置無線鏈路兩端的Payload長度、錯誤編碼率、CRC(如果擴頻因子SF=6,只能使用隱式報頭模式
  • The data payload
    長度不固定,在FIFO中讀寫
  • 數據傳輸時間

    LoRa數據包總傳輸時間 = 前導碼傳輸時間Tpre + 數據包傳輸時間Tpay
    前導碼傳輸時間為:Tpre = (Npre + 4.25)*Tpay
    Npre表示已設定的前導碼長度(讀取RegPreambleMsb和RegPreambleLsb寄存器得到)
    計算Payload符號數的公式為:

    PL表示Payload字節數,H=0表示header使能,DE=1表示LowDataRateOptimize=1,CR表示編碼速率

    重置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模式下均可用,其映射關系取決于RegDioMapping1和RegDioMapping2這兩個寄存器
    第一句代碼的意思就是如果處于異步通信狀態,且在發射完成狀態(此處有疑義),就將DIO0引腳映射為發射完成指示符
    設置為發射模式,如果不處于異步通信狀態,就等待發送完成,然后清除中斷。
    這里講的不是很清楚,我們再來具體看一下:

  • async:意思是異步,是在non-blocking mode下使用的一個變量。由于網關可能不會向后發送任何數據,因此我們在嘗試接收之前使套接字成為非阻塞狀態,以防止卡在等待永遠不會到達的數據包中
    同步、異步、阻塞、非阻塞這四個是進程間通信的概念,進程間通信通過send()和receive()兩種基本操作完成:
    阻塞式發送:發送方進程會一直被阻塞,直到消息被接收方進程收到;
    非阻塞式發送:發送方調用send()后,可以一直進行其他操作;
    阻塞式接收:接收方進程會一直被阻塞,直到消息到達可用;
    非阻塞式接收:接收方調用receive()后,要么得到一個有效的效果,要么得到一個空值
    (此處參考文章)
  • 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)

    這一句就是在等待中斷

    中斷源

  • FifoEmpty:當整個FIFO為空,中斷源為高,從FIFO檢索數據的時候,FifoEmpty在NSS下降沿更新。也就是說每次讀取操作之后檢查FifoEmpty狀態,以決定是否讀取下一個字節,FifoEmpty=1表示不需要讀取更多字節
  • FifoFull
  • 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也將被清除。
  • PacketSent:when the SR’s last bit has been sent
  • 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上的应用——原理及代码分析(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。