# EEPROM

和 RAM 不同，EEPROM 提供在系統斷電後仍能儲存資料的空間 (就像顆小硬碟)。透過使用 Arduino 的 [EEPROM 函式庫](https://www.arduino.cc/en/Reference/EEPROM)開發者可以讀寫 LinkIt 7697 上虛擬 (\*) 的 EEPROM，且此 EEPROM 總容量大小為 **1KB**。

(\*) LinkIt 7697 的非揮發性儲存媒介只有 serial flash、並沒有 EEPROM。但為了相容於 Arduino 開發環境，透過使用 LinkIt SDK 裡的 [NVDM](http://labs.mediatek.com/api/mt7697/group___n_v_d_m.html) (**N**on-**V**olatile **D**ata **M**anagement) 模組模擬出一顆具有 1KB 大小的 EEPROM。

{% hint style="danger" %}
NVDM 所管理的總空間為 64KB，而這個空間由 LFlash 與 EEPROM 兩個函式庫共同分享使用。也就是說，如果開發者將 EEPROM 的 1KB 空間都使用完畢，那麼 LFlash 就只剩下 64 - 1 = 63KB 可以使用。反之，若開發者完全不使用 EEPROM，那麼 LFlash 就可以使用 NVDM 完整的 64KB 儲存空間。
{% endhint %}

### 使用方法 <a href="#eeprom-shi-yong-fang-fa" id="eeprom-shi-yong-fang-fa"></a>

在使用 EEPROM APIs 前，須先加入相關的 header file：

```
#include <EEPROM.h>
```

存取 EEPROM 會使用到下列六個 APIs：

* length()
* read()
* write()
* update()
* get()
* put()

#### 基本操作 <a href="#eeprom-ji-ben-cao-zuo" id="eeprom-ji-ben-cao-zuo"></a>

若要查詢 EEPROM 的總空間大小，可呼叫 *EEPROM.length()*，在 LinkIt 7697 上它的回傳值會是 1024 (也就是 1KB)。

若要進行單一位元組的存取，可以使用 *EEPROM.read()*、*EEPROM.write()* 和 *EEPROM.update()* 等 API。這些函式的原型宣告如下：

```
uint8_t read(int idx);
void write(int idx, uint8_t val);
void update(int idx, uint8_t val);
```

API 參數定義為：

* **idx**：要讀寫的位元組的位置。有效的位置範圍為 0 \~ EEPROM.length() - 1。
* **val**：要寫入 EEPROM 的資料。

由於 flash 記憶體有最大寫入次數的物理限制，因此不建議對 flash 空間進行頻繁的寫入操作造成使用壽命下降。為了改善這個狀況，可以使用 *.update()* API 取代 *.write()* API 進行寫入。因為 *.update()* 會在每次寫入前，會先檢查該位置的資料是否與欲寫入的資料相同，若不相同才會真的觸發 flash 寫入。反之，若該位置的資料已經與欲寫入的資料相同，則 *.update()* 就不會觸發 flash 寫入。

相對的，若使用 *.write()* 進行寫入，則不會檢查欲寫入位置的資料為何，一律都會觸發 flash 寫入操作。

#### 使用 \[] 運算子進行存取 <a href="#eeprom-shi-yong-yun-suan-zi-jin-hang-cun-qu" id="eeprom-shi-yong-yun-suan-zi-jin-hang-cun-qu"></a>

除了呼叫 API 之外，也可以使用 \[] 運算子存取 EEPROM 如以下範例：

```
// read the byte from address 88 and store it to the 'data' variable
uint8_t data = EEPROM[88];
// write a byte (the value is 97) to address 76
EEPROM[76] = 97;
// increase the value in address 2 by 1
EEPROM[2]++;
```

#### 多位元組存取 <a href="#eeprom-duo-wei-yuan-zu-cun-qu" id="eeprom-duo-wei-yuan-zu-cun-qu"></a>

讀寫 EEPROM 時除了一次讀寫一個位元組，EEPROM 函式庫亦提供了 *.get()* 與 *.put()* 這兩個讓開發者可以同時存取多位元組的 API：

```
template<typename T> T &get(int idx, T &t);
template<typename T> const T &put(int idx, const T &t);
```

API 參數定義如下：

* **idx**：要讀寫資料的起始位置。
* **t**：用來儲存要讀出或寫入 EEPROM 的位元組的資料結構。 &#x20;

以下是使用 *.get()* 與 *.put()* 的操作範例：

```
// define the data type
typedef struct
{
  int id;
  float value;
  char ch;
} data_t;
 
data_t input, output;
int address = 76;
 
// init the values for input
input.id = 10;
input.value = 123.456f;
input.ch = 'c';
 
// store the input data to the EEPROM
put(address, &input);
// after the input is stored, we can read it back from the same address
get(address, &output);
```

在這個範例裡，透過呼叫 *.put()* 將一多位元組的資料結構 (*data\_t*) 寫入 EEPROM。寫入後，可再透過 *.get()* 讀出來。讀出來的結果應該和寫入的內容是一致的。

若要參考更多範例，請點選 Arduino IDE 裡的 **Files / Examples / EEPROM** 選單，裡面提供了更多不同的 EEPROM 應用範例。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://cavedu.gitbook.io/linkit-7697/linkit-7697-arduinoide/kai-fa-zhi-nan/eeprom.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
