# 讀寫型記憶體-讀取指令(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)格式

<div align="left"><figure><img src="https://598628291-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FYbqXsfQyaXKmr6mxssGy%2Fuploads%2F9Ji0SB3Vr4jPccdWm7IQ%2Fmodbus-03-TX-diagram.png?alt=media&#x26;token=0d6d8a0b-c274-4873-a2ea-bcabd97d65ab" alt="" width="563"><figcaption></figcaption></figure></div>

<div align="left"><figure><img src="https://598628291-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FYbqXsfQyaXKmr6mxssGy%2Fuploads%2FfHl5etdDT8kNBrvFIz3A%2Fmodbus-03-RX-diagram.png?alt=media&#x26;token=5bd0e471-c29e-46ce-b383-3c14efbf8be8" alt="" width="563"><figcaption></figcaption></figure></div>

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

以主站使用功能碼 03 讀取 **A02 Preset Pipe Spec** 為例：\
根據設備規格，起始暫存器位置為 `00 00`，欲讀取暫存器數量為 3（每個暫存器 2 bytes，共 6 bytes），資料型式皆為 `int16`（16 位元整數），位元組順序為 **Big Endian**。\
主站將透過 Modbus RTU 協議發送讀取指令，從站（流量計）回覆對應的數據。

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

```
TX 請求指令： 01 03 00 00 00 03 05 CB
```

slave address: 01(本流量計地址)

function code: 03(讀取指令)

讀取數據的啟始地址: 00 00(要讀取A02 Preset Pipe Spec的數據的啟始地址)

讀取registers 總數: 00 03(從 00 00 開始讀三個 register 的記憶體)

CRC: 05 CB (依 CRC 算法計算出的糾錯碼)

### 流量計回覆(03)格式

```
RX 回覆數據：01 03 06 00 06 00 00 42 41 59 E5
```

#### 流量計回覆範例說明

slave address: 01(本流量計地址)

function code: 03(讀取指令)

回覆的Byte數: 06(3個register=6byte)

Data Byte:&#x20;

* 00 06 (目前型態選項astm sch 80 pvc)
* 00 00 (讀取A02 RS485 init)
* 42  41 (讀取A02 RS485 init)

CRC: 59 E5 (依 CRC 算法計算出的糾錯碼)

### 常見問題

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

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

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

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

<div align="left"><figure><img src="https://598628291-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FYbqXsfQyaXKmr6mxssGy%2Fuploads%2FnkXz9F5YY2xrnfnbTO52%2Fmodbus-img4-tw.jpg?alt=media&#x26;token=f90a58c3-267d-45a6-94dc-749781369ba3" 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 %}
