作者:ZeroGrid 實驗室
🎯 本篇目標
- 倉庫地圖:深入理解 256 Bytes Data Buffer 的記憶體配置 (
TxBaseAddressvsRxBaseAddress)。 - 聽懂門鈴:解析 IRQ (中斷) 的觸發邏輯,特別是
RxDone,CrcErr,HeaderErr的位元定義。 - 取貨 SOP:掌握 Datasheet 規定的標準讀取流程:
GetRxBufferStatus->ReadBuffer->ClearIrqStatus。 - 實驗室透視:利用
/monitor指令,我們將跳過應用層,直接觀察從晶片緩衝區挖出來的 Raw Hex Data。
1. 前言:資料的停泊港
經過了 LBT 的禮貌避讓,無線電波終於抵達了我們的天線。 SX1262 的解調器 (Demodulator) 努力工作,把那些啾啾叫的聲波還原成了 0 和 1。
但這些資料並不會直接出現在你的 MCU (RP2040) 變數裡。 它們會先被卸貨到 SX1262 內部一塊僅有 256 Bytes 的 SRAM Data Buffer 中。
此刻,SX1262 就像一個剛收到包裹的警衛伯伯,他把包裹鎖進了櫃子,然後按了一下你家的門鈴 (DIO1)。 你的任務就是:聽到門鈴,下樓,找到正確的櫃子,把包裹拿走。
2. 倉庫地圖:256 Bytes 的空間規劃

Data Buffer
SX1262 內部的 Data Buffer 只有 256 Bytes (Address 0x00 ~ 0xFF)。 這塊空間是 TX (發送) 與 RX (接收) 共用的。如果沒有規劃好,你準備發出去的信可能會蓋掉剛剛收到的信。
為了避免這種慘劇,我們必須在初始化時使用指令 SetBufferBaseAddress (0x8F) 來劃分領土:
- TxBaseAddress:發送緩衝區的起始地址。
- 實驗室設定:0x00。
- RxBaseAddress:接收緩衝區的起始地址。
- 實驗室設定:0x80 (128)。
這樣一來,0x00-0x7F 是發件區,0x80-0xFF 是收件區,井水不犯河水。
🛠️ 核心邏輯展示
// [Initialization]
// 告訴晶片:發送放前面,接收放後面
uint8_t params[] = { 0x00, 0x80 }; // TX Base, RX Base
SpiWriteCmd(0x8F, params, 2); // OpCode 0x8F: SetBufferBaseAddress
3. DIO1:聽懂門鈴的含義 (IRQ)
當 SX1262 拉高 DIO1 腳位時,代表發生了「某件事」。但到底是好事(收到信)還是壞事(信爛掉了)? 這需要透過指令 GetIrqStatus (0x12) 來讀取 IRQ 暫存器 (16 bits)。
Datasheet 定義了幾個我們必須處理的關鍵位元:
- RxDone (Bit 1):【成功】 完整收到一個封包,且 CRC 校驗通過。
- CrcErr (Bit 6):【失敗】 收到封包,但在 Payload CRC 檢查時失敗(資料損毀)。
- HeaderErr (Bit 5):【失敗】 連 Header 都解不出來(通常是干擾太強或 Sync Word 不對)。
- TxDone (Bit 0):【通知】 發送完成。
⚠️ 技術長筆記: 為什麼有些人的 LoRa 程式跑一跑會「卡死」? 因為他們只處理
RxDone,卻忽略了CrcErr。當雜訊導致 CRC 錯誤時,DIO1 拉高,但 MCU 卻因為沒看到 RxDone 而不作為。結果 DIO1 一直卡在 High,新的資料再也進不來(因為門鈴已經按到底了)。 正確做法:無論是成功還是失敗,最後都要清除 IRQ。
4. 取貨 SOP:標準三步驟
當 MCU 偵測到 RxDone 後,不能閉著眼睛亂抓。 雖然我們設定了 RxBaseAddress 是 0x80,但 SX1262 為了管理方便,每一包資料的實際起始點可能會浮動。
你必須嚴格遵守 Datasheet 規定的取貨流程:
Step 1: 詢問包裹資訊 (GetRxBufferStatus, 0x13)
你要問晶片兩個問題:這包貨放在哪?有多重? 發送指令 0x13,晶片會回傳兩個 Bytes:
- RxPayloadLength:資料長度 (Bytes)。
- RxStartBufferPointer:這包資料的實際起始地址。
- 注意:這個 Pointer 會指向 Buffer 中的某個位置,我們必須用它來讀取,而不是用預設的 0x80。
Step 2: 搬運貨物 (ReadBuffer, 0x1E)
知道了地址 (Pointer) 和長度 (Length),我們就可以派 SPI 卡車去載貨了。 發送指令 0x1E,先送出 RxStartBufferPointer 作為偏移量 (Offset),然後連續讀取 RxPayloadLength 個 Bytes。
Step 3: 簽收並掛斷 (ClearIrqStatus, 0x02)
這一步最重要!資料拿完了,DIO1 此時還是 High。 你必須發送指令 ClearIrqStatus (0x02) 告訴晶片:「我收到了,把 DIO1 拉低吧。」
🛠️ 標準實作邏輯 (Pseudo-code)
void OnDio1Interrupt() {
uint16_t irq = ReadIrqStatus();
// 1. 檢查是否接收成功
if (irq & IRQ_RX_DONE) {
// 2. 問位置與長度
uint8_t status[2];
SpiReadCmd(0x13, status, 2);
uint8_t len = status[0]; // RxPayloadLength
uint8_t ptr = status[1]; // RxStartBufferPointer
// 3. 搬運貨物
uint8_t buffer[256];
SpiReadBuffer(ptr, buffer, len);
// 4. 印出資料 (這就是 Monitor 模式在做的事)
PrintHex(buffer, len);
}
// 5. [重要] 清除所有 IRQ (包含 RxDone, CrcErr 等),重置 DIO1
SpiWriteCmd(0x02, (uint8_t[]){0xFF, 0xFF}, 2);
}
5. ZeroGrid 實驗室:Monitor Mode 透視眼
在我們的實驗室指令中,有一個強大的除錯工具:/monitor。 它不是讀取靜態記憶體 (/mem),而是掛載在上述的 RxDone 流程中,動態攔截並顯示剛收到的 Raw Packet。
🧪 實驗步驟:
- 開啟監聽: 輸入
/monitor on。這時裝置進入「抓包模式」。 - 發送訊號: 用另一台設備發送
Hello。 - 觀察數據: 你會看到類似這樣的 Hex Dump:
Plaintext
0000: FF FF FF FF FF FF FF FF E6 63 B0 35 97 5B 0F 38 | .........c.5.[.8
0010: 00 01 01 48 65 6C 6C 6F |...Hello
解讀:
🤔 為什麼前面有一大堆垃圾? 很多讀者會問:「我明明只傳了 'Hello',為什麼多了前面那 20 個 Bytes?」
這就是 「物理層 (PHY) vs. 應用層 (App)」 的差異。
- 物理層的 Payload:對 SX1262 來說,上面這 26 Bytes 全部都是 Payload。它只負責搬運,不管內容。就像快遞員只管箱子,不管裡面裝的是 iPhone 還是石頭。
- 應用層的 Payload:對我們來說,只有最後的
48 65 6C 6C 6F("Hello") 是有意義的。
前面那些 FF FF... 是什麼? 那就像是網購包裹裡的 「氣泡紙」與「出貨單」。 在軟體層 (Protocol Stack) 中,為了確保資料能送到正確的人手上,我們會加上:
- Destination ID (那串 FF FF)
- Source ID (那串 E6 63...)
- Control Flags (那個 00)
- Sequence Number (那個 01)
當 SX1262 把這整箱東西交給 MCU 後,MCU 的軟體會負責「拆開氣泡紙」(解析 Header),確認收件人是自己後,才把最後的禮物 ("Hello") 拿出來給你。
/monitor 讓你看到的,就是這個還沒拆封的原始包裹。
如果沒有這一套「問位置 -> 讀取 -> 清除」的 SOP,這些資料就只是晶片倉庫裡沒人領取的包裹,永遠不會變成螢幕上的文字。
下一課預告
我們現在已經能完美地控制 SX1262 收發資料了。 但是,為了讓這顆晶片一直工作,我們必須一直供電給它嗎? LoRa 裝置通常是用電池供電的,如何讓它在沒事做的時候「裝死」,有事的時候「秒醒」?
下一篇 #8 貪睡的藝術,我們將不談協定,而是深入 Datasheet 的 Power Management 章節,探討 Sleep Mode (睡眠模式) 與 Warm Start (暖啟動) 的奧義,看看如何把電流壓榨到 nA 等級。













