# 讀寫型記憶體-讀取指令(03)

{% hint style="info" %}
當數值較大時，若分別以不同資料格式（例如 float32 或 int64）進行讀取與換算，因內部計算方式不同，可能會出現非常小的差異，通常小於 1/10,000，屬於正常現象。

不同參數大分類不能跨區讀取，讀取指令中register長度必需小於等於 20個 register。超過該長度的指令會被忽略。
{% endhint %}

### Function Code 功能碼 03&#x20;

讀寫型記憶體 讀取指令，功能碼 (Function Code) 03 ，數據長度為1\~125 個暫存器。可讀取單個或多個保持暫存器，用於讀取設備中定義之保持暫存器（Holding Registers）內的設定參數。

### 讀寫型記憶體-讀取指令(03)格式 <a href="#kong-zhi-duan-fa-zhi-ling-04-ge-shi" id="kong-zhi-duan-fa-zhi-ling-04-ge-shi"></a>

<div align="left"><figure><img src="https://1153145047-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7cwNfn5rF1lQNDdmFBmA%2Fuploads%2F42wtdSgmHvmNmvkQXhv2%2Fmodbus-03-TX-diagram.png?alt=media&#x26;token=a24b41cf-fb97-4734-87f8-5702f0f41791" alt="" width="563"><figcaption></figcaption></figure></div>

<div align="left"><figure><img src="https://1153145047-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7cwNfn5rF1lQNDdmFBmA%2Fuploads%2FxIuQvxAb2dXpfyN7osHc%2Fmodbus-03-RX-diagram.png?alt=media&#x26;token=a88254b4-efd2-4e7b-9658-6e6287310750" alt="" width="563"><figcaption></figcaption></figure></div>

### Modbus RTU 讀取讀寫型記憶體保持暫存器 (03) 範例

以主站使用功能碼 03 讀取 "管路材質 A08 Pipe Material "的位址為例：

根據 [du-xie-xing-ji-yi-ti-du-qu-lie-biao](https://docs.lorric.com/qr/fu-lt-ultrasonic-flowmeter-manual-chinese-v1/communication/modbus-rtu-protocol/du-xie-xing-ji-yi-ti/du-xie-xing-ji-yi-ti-du-qu-lie-biao "mention")  ，起始位置為 00 0A，Reg長度為 1 (一個暫存器)，資料型式：int16 (16 位元整數)，讀取數量：00 01 (1 個暫存器)，根據設備規格，A08 Pipe Material 為 int16（16 位元整數），佔用 1 個暫存器（2 bytes），位元組順序為 Big Endian。

#### **主站發送讀取指令 (TX 訊框)**

```
TX 請求指令： 01 03 00 0A 00 01 A4 0E
```

* Slave Address = 01
* Function Code = 03
* 起始暫存器位址 = 00 0A  (A08 Pipe Material 讀取位址)
* 讀取數量 = 00 01 (1 個暫存器)
* CRC 校驗碼 = A4 0E  (依 CRC 算法計算出的糾錯碼)

#### **從站回覆數據 (RX 訊框)**

假設回覆 Pipe Material 代碼 <mark style="background-color:red;">3</mark> (Hex: 00 03)。

```
RX 回覆數據：01 03 02 00 03 B8 44
```

* Slave Address = 01
* Function Code = 03
* 回傳位元數 = 02  (1 個暫存器, 2 Bytes)
* 數據位元組 = 00 03 (Pipe Material 代碼 (PP 材質))
* CRC 校驗碼 = B8 44 (依 CRC 算法計算出的糾錯碼)

#### **數據解析與轉換 (轉回十進制)**

**步驟 A：排列數據**

* 接收數據： `00 03`
* 型式： int16 (16 位整數)

**步驟 B：轉換為十進制與材質對應**

將十六進制數 `0003` 轉換為十進制：

$$
數值 = ( 0 × 256) + 3 = 3
$$

* 最終數值： 3
* 材質對應： 查閱 A08 Pipe Material 管路材質列表，數字 3 對應的選項是 <mark style="background-color:red;">PP 材質</mark>。

### 常見問題

* **為什麼「記憶體列表」和「PLC 讀取位置」位置會有差異？**

使用者可能遇到以下情況：記憶體列表位置為 40005，但在 PLC 端需填入 4（或顯示為 0004）才能讀到需求數值。

這是因為Modbus 位址表示方式的特性，根據Modbus定義，人看的位址（文件/記憶體表）與設備實際讀取的位址（通訊位置）兩者中間會存在一個「偏移量 offset」。

功能碼03的邏輯為：將「40001」作為基底，讀取時，從這裡開始計數，40001 代表第 1 個寄存器，偏移量是0000，讀取位置是0000。

<div align="left"><figure><img src="https://1153145047-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F7cwNfn5rF1lQNDdmFBmA%2Fuploads%2FZ5TGBg5xZBive8Im3ZGl%2Fmodbus-img4-tw.jpg?alt=media&#x26;token=f0697488-4f18-4832-85df-048022d282a8" alt="" width="540"><figcaption></figcaption></figure></div>

因此當文件標示要讀取 40005 時，對應的 Modbus 功能碼 03 封包中的 實際起始位址為 0004，也就是：**實際通訊位址 = 文件位址 − 40001。**

{% hint style="info" %}
註：Modbus 協定本身規範採 0-based addressing（實際通訊封包會從 0 開始編號），因此理論通則為：「實際通訊位址 = 文件位址−40001」

不過，若客戶端使用的 PLC 或 HMI 軟體 有特別設定為 1-based addressing（軟體自動將位址加 1），此時就需要依照軟體的位址規則來填入起始位址，讀取暫存器數據。
{% endhint %}
