## Arduino I2C (Wire) 模块 API 手册

### 1. 概述

K230 内部集成了五个 I2C 硬件模块，支持标准模式（100kHz）、快速模式（400kHz）等多种速率。Arduino环境下提供了标准的Wire接口，实现了I2C主机模式的通信功能。当前版本暂不支持从机模式。

### 2. API 介绍

TwoWire 类位于 `arduino` 命名空间中，提供了五个预定义的Wire实例：

- `Wire` - I2C0 实例（默认引脚：SCL=32, SDA=33）
- `Wire1` - I2C1 实例（默认引脚：SCL=9, SDA=10）
- `Wire2` - I2C2 实例（默认引脚：SCL=11, SDA=12）
- `Wire3` - I2C3 实例（默认引脚：SCL=36, SDA=37）
- `Wire4` - I2C4 实例（默认引脚：SCL=7, SDA=8）

#### 2.1 构造函数

Wire实例已经预定义，无需手动构造。

#### 2.2 `begin` 方法

```cpp
bool begin()
bool begin(int sda, int scl, uint32_t frequency = 100000)
```

初始化I2C主机模式。

**参数**

- `sda`: SDA引脚编号（-1表示使用默认引脚）
- `scl`: SCL引脚编号（-1表示使用默认引脚）
- `frequency`: I2C时钟频率（Hz）

**返回值**

- `true`: 初始化成功
- `false`: 初始化失败

#### 2.3 `end` 方法

```cpp
bool end()
```

关闭I2C并释放资源。

**参数**
无

**返回值**

- `true`: 关闭成功
- `false`: 关闭失败

#### 2.4 `setPins` 方法

```cpp
bool setPins(int sda, int scl)
```

设置I2C引脚（必须在begin之前调用）。

**参数**

- `sda`: SDA引脚编号
- `scl`: SCL引脚编号

**返回值**

- `true`: 设置成功
- `false`: 设置失败

#### 2.5 `setClock` 方法

```cpp
bool setClock(uint32_t freq)
```

设置I2C时钟频率。

**参数**

- `freq`: 时钟频率（Hz）

**返回值**

- `true`: 设置成功
- `false`: 设置失败

#### 2.6 `getClock` 方法

```cpp
uint32_t getClock()
```

获取当前I2C时钟频率。

**参数**
无

**返回值**
当前时钟频率（Hz）

#### 2.7 `setTimeOut` 方法

```cpp
void setTimeOut(uint16_t timeOutMillis)
```

设置I2C超时时间。

**参数**

- `timeOutMillis`: 超时时间（毫秒）

**返回值**
无

#### 2.8 `getTimeOut` 方法

```cpp
uint16_t getTimeOut()
```

获取当前超时时间设置。

**参数**
无

**返回值**
超时时间（毫秒）

#### 2.9 `setBufferSize` 方法

```cpp
size_t setBufferSize(size_t bSize)
```

设置I2C缓冲区大小。

**参数**

- `bSize`: 缓冲区大小（最小32字节）

**返回值**
实际设置的缓冲区大小，失败返回0

#### 2.10 `beginTransmission` 方法

```cpp
void beginTransmission(uint8_t address)
```

开始向指定地址的I2C设备发送数据。

**参数**

- `address`: 设备地址（7位）

**返回值**
无

#### 2.11 `endTransmission` 方法

```cpp
uint8_t endTransmission()
uint8_t endTransmission(bool stopBit)
```

结束数据传输。

**参数**

- `stopBit`:
  - `true`: 发送停止位（默认）
  - `false`: 不发送停止位，用于后续的重复起始条件操作。数据会被缓存，直到下一次`requestFrom`调用时一起发送

**返回值**

- `0`: 成功
- `5`: 超时
- `其他值`: 其他错误

**注意**：当`stopBit`为`false`时，实际的I2C传输会被延迟到后续的`requestFrom`调用，此时会使用重复起始条件。

#### 2.12 `requestFrom` 方法

```cpp
size_t requestFrom(uint8_t address, size_t len)
size_t requestFrom(uint8_t address, size_t len, bool stopBit)
```

从I2C设备读取数据。

**参数**

- `address`: 设备地址
- `len`: 要读取的字节数
- `stopBit`: 是否发送停止位（当前实现中此参数无效，总是会发送停止位）

**返回值**
实际读取的字节数

**注意**：如果之前的`endTransmission(false)`调用缓存了数据，此函数会先发送缓存的写数据，然后使用重复起始条件进行读操作，最后发送停止位。

#### 2.13 `write` 方法

```cpp
size_t write(uint8_t data)
size_t write(const uint8_t *data, size_t quantity)
```

向I2C设备写入数据。

**参数**

- `data`: 要写入的数据
- `quantity`: 数据长度（批量写入时）

**返回值**
写入的字节数

#### 2.14 `available` 方法

```cpp
int available()
```

返回接收缓冲区中可读取的字节数。

**参数**
无

**返回值**
可读取的字节数

#### 2.15 `read` 方法

```cpp
int read()
```

从接收缓冲区读取一个字节。

**参数**
无

**返回值**
读取的字节值（0-255），如果没有数据返回-1

#### 2.16 `peek` 方法

```cpp
int peek()
```

查看接收缓冲区的下一个字节但不移除它。

**参数**
无

**返回值**
下一个字节值，如果没有数据返回-1

#### 2.17 `flush` 方法

```cpp
void flush()
```

清空缓冲区。

**参数**
无

**返回值**
无

#### 2.18 `getBusNum` 方法

```cpp
uint8_t getBusNum()
```

获取I2C总线编号。

**参数**
无

**返回值**
总线编号（0-4）

### 3. 使用说明

1. **引脚配置**：
   - 每个I2C实例都有默认引脚
   - 可以使用`setPins()`在`begin()`之前更改引脚
   - 引脚必须支持对应的I2C功能

2. **缓冲区管理**：
   - 默认缓冲区大小为128字节
   - 可以使用`setBufferSize()`调整大小

3. **错误处理**：
   - `endTransmission()`返回错误码
   - 检查返回值以确认操作是否成功

### 4. 示例程序

```cpp
#include "Arduino.h"

using namespace arduino;

// AT24C02 EEPROM 相关定义
#define EEPROM_ADDR     0x50    // AT24C02的I2C地址
#define EEPROM_SIZE     256     // 256字节容量
#define PAGE_SIZE       8       // 页大小为8字节

// 测试数据
uint8_t test_data[] = "Hello K230 I2C!"; // 字符串长度15 + 结尾'\0' = 16字节
uint8_t read_buffer[32];

// 通用写入函数，可以处理跨页写入
bool eepromWrite(uint16_t addr, const uint8_t* data, size_t len) {
    size_t bytes_written = 0;

    while (bytes_written < len) {
        // 计算当前页还剩下多少空间
        uint8_t offset_in_page = addr % PAGE_SIZE;
        uint8_t bytes_to_write_in_page = PAGE_SIZE - offset_in_page;

        // 计算本次实际要写入的字节数
        // 取「本页剩余空间」和「数据总剩余长度」中的较小值
        size_t chunk_len = len - bytes_written;
        if (chunk_len > bytes_to_write_in_page) {
            chunk_len = bytes_to_write_in_page;
        }

        // 开始I2C传输
        Wire2.beginTransmission(EEPROM_ADDR);
        Wire2.write(addr & 0xFF); // AT24C02 地址
        Wire2.write(data + bytes_written, chunk_len);
        
        // 结束传输，如果失败则返回
        if (Wire2.endTransmission() != 0) {
            Serial.println("   [Write Error] endTransmission failed!");
            return false;
        }

        // 更新地址和已写入字节数
        addr += chunk_len;
        bytes_written += chunk_len;

        // EEPROM写入后需要一个短暂的延时来完成内部写周期
        delay(5); // 5ms延时足够
    }
    
    return true;
}


void setup() {
    Serial.begin(115200);
    delay(1000);
    
    Serial.println("AT24C02 EEPROM Test (Corrected Version)");
    Serial.println("=======================================");
    Serial.println("Using I2C2: SCL=GPIO11, SDA=GPIO12");
    
    // 初始化I2C2，使用GPIO11作为SCL，GPIO12作为SDA
    if (!Wire2.begin(12, 11, 100000)) {  // 100kHz
        Serial.println("I2C2 initialization failed!");
        while (1);
    }
    
    Serial.println("I2C2 initialized successfully");
    Serial.print("Clock frequency: ");
    Serial.print(Wire2.getClock());
    Serial.println(" Hz");
    
    // 设置超时时间
    Wire2.setTimeOut(50);  // 50ms超时
}

void loop() {
    static int test_count = 0;
    
    Serial.print("\n--- Test ");
    Serial.print(++test_count);
    Serial.println(" ---");
    
    // 测试1：写入单字节
    Serial.println("1. Writing single byte...");
    if (eepromWriteByte(0x00, 0xAA)) {
        Serial.println("   Success!");
    } else {
        Serial.println("   Failed!");
    }
    delay(10);  // 写周期延时
    
    // 测试2：读取单字节
    Serial.println("2. Reading single byte...");
    uint8_t data = eepromReadByte(0x00);
    Serial.print("   Read: 0x");
    Serial.println(data, HEX);
    
    // =================================================================
    // 测试3：使用新的通用写入函数，写入超过一页的数据
    // =================================================================
    Serial.println("3. Generic write test (writing 16 bytes)...");
    uint16_t addr = 0x10;
    // 调用新的、可以处理跨页的写入函数
    if (eepromWrite(addr, test_data, sizeof(test_data))) {
        Serial.println("   Write success!");
    } else {
        Serial.println("   Write failed!");
    }
    // eepromWrite函数内部已经有延时，这里可以不用额外delay
    
    // 测试4：连续读取
    Serial.println("4. Sequential read test...");
    memset(read_buffer, 0, sizeof(read_buffer));
    if (eepromSequentialRead(addr, read_buffer, sizeof(test_data))) {
        Serial.print("   Read: ");
        Serial.println((char*)read_buffer); // 这里现在应该能正确显示完整字符串
    } else {
        Serial.println("   Sequential read failed!");
    }
    
    // 测试5：使用重复起始条件的读操作
    Serial.println("5. Read with repeated start test...");
    uint8_t reg_data = eepromReadWithRepeatedStart(0x00);
    Serial.print("   Read from address 0x00: 0x");
    Serial.println(reg_data, HEX);
    
    // 测试6：固定地址写入和读取测试
    Serial.println("6. Fixed address write/read test...");
    uint16_t test_addrs[] = {0x00, 0x20, 0x40, 0x60, 0x80};
    uint8_t test_values[] = {0x11, 0x22, 0x33, 0x44, 0x55};
    
    for (int i = 0; i < 5; i++) {
        Serial.print("   Address 0x");
        Serial.print(test_addrs[i], HEX);
        Serial.print(": Write 0x");
        Serial.print(test_values[i], HEX);
        
        eepromWriteByte(test_addrs[i], test_values[i]);
        delay(10);
        
        uint8_t read_value = eepromReadByte(test_addrs[i]);
        Serial.print(" -> Read 0x");
        Serial.print(read_value, HEX);
        
        if (read_value == test_values[i]) {
            Serial.println(" [OK]");
        } else {
            Serial.println(" [FAIL]");
        }
    }
    
    // 测试7：扫描I2C总线
    Serial.println("7. Scanning I2C bus...");
    scanI2CBus();
    
    // 测试8：批量读取使用重复起始
    Serial.println("8. Bulk read with repeated start...");
    demonstrateBulkReadWithRepeatedStart();
    
    delay(5000);  // 5秒后进行下一轮测试
}

// 写入单字节到EEPROM
bool eepromWriteByte(uint16_t addr, uint8_t data) {
    Wire2.beginTransmission(EEPROM_ADDR);
    Wire2.write(addr & 0xFF);  // AT24C02只有8位地址
    Wire2.write(data);         // 数据
    return (Wire2.endTransmission() == 0);
}

// 从EEPROM读取单字节（传统方法）
uint8_t eepromReadByte(uint16_t addr) {
    Wire2.beginTransmission(EEPROM_ADDR);
    Wire2.write(addr & 0xFF);  // AT24C02只有8位地址
    
    if (Wire2.endTransmission() != 0) {
        return 0xFF;  // 错误
    }
    
    Wire2.requestFrom(EEPROM_ADDR, (size_t)1);
    if (Wire2.available()) {
        return Wire2.read();
    }
    return 0xFF;  // 错误
}

// 使用重复起始条件读取
uint8_t eepromReadWithRepeatedStart(uint16_t addr) {
    Wire2.beginTransmission(EEPROM_ADDR);
    Wire2.write(addr & 0xFF);  // AT24C02只有8位地址
    
    // 不发送停止位，准备重复起始
    Wire2.endTransmission(false);
    
    // 使用重复起始条件读取
    Wire2.requestFrom(EEPROM_ADDR, (size_t)1);
    
    if (Wire2.available()) {
        return Wire2.read();
    }
    return 0xFF;  // 错误
}

// 页写入（最多8字节）- 有写入长度限制
bool eepromPageWrite(uint16_t addr, const uint8_t* data, size_t len) {
    if (len > PAGE_SIZE) len = PAGE_SIZE;
    
    Wire2.beginTransmission(EEPROM_ADDR);
    Wire2.write(addr & 0xFF);  // AT24C02只有8位地址
    
    for (size_t i = 0; i < len; i++) {
        Wire2.write(data[i]);
    }
    
    return (Wire2.endTransmission() == 0);
}

// 连续读取（使用重复起始）
bool eepromSequentialRead(uint16_t addr, uint8_t* buffer, size_t len) {
    // 设置起始地址
    Wire2.beginTransmission(EEPROM_ADDR);
    Wire2.write(addr & 0xFF);  // AT24C02只有8位地址
    
    // 使用重复起始条件
    if (Wire2.endTransmission(false) != 0) {
        return false; // 如果设置地址失败，则退出
    }
    
    // 读取数据
    size_t received = Wire2.requestFrom(EEPROM_ADDR, len);
    if (received != len) {
        return false;
    }
    
    for (size_t i = 0; i < len; i++) {
        if (Wire2.available()) {
            buffer[i] = Wire2.read();
        } else {
            return false;
        }
    }
    
    return true;
}

// 扫描I2C总线上的设备
void scanI2CBus() {
    int devices = 0;
    
    Serial.println("   Scanning addresses 0x00 to 0x7F...");
    
    for (uint8_t addr = 0; addr < 128; addr++) {
        Wire2.beginTransmission(addr);
        uint8_t error = Wire2.endTransmission();
        
        if (error == 0) {
            Serial.print("   Device found at address 0x");
            if (addr < 16) Serial.print("0");
            Serial.println(addr, HEX);
            devices++;
        }
    }
    
    if (devices == 0) {
        Serial.println("   No I2C devices found!");
    } else {
        Serial.print("   Total devices found: ");
        Serial.println(devices);
    }
}

// 演示批量读取使用重复起始
void demonstrateBulkReadWithRepeatedStart() {
    uint8_t start_addr = 0x30;
    uint8_t write_data[] = {0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6};
    
    // 先写入测试数据
    Serial.print("   Writing test data at 0x");
    Serial.print(start_addr, HEX);
    Serial.print(": ");
    for (size_t i = 0; i < sizeof(write_data); i++) {
        eepromWriteByte(start_addr + i, write_data[i]);
        Serial.print("0x");
        Serial.print(write_data[i], HEX);
        Serial.print(" ");
        delay(5);
    }
    Serial.println();
    
    // 使用重复起始条件读取多个字节
    Serial.print("   Reading 6 bytes with repeated start: ");
    
    Wire2.beginTransmission(EEPROM_ADDR);
    Wire2.write(start_addr);
    Wire2.endTransmission(false);  // 不发送停止位
    
    Wire2.requestFrom(EEPROM_ADDR, (size_t)6);  // 重复起始后读取6字节
    
    while (Wire2.available()) {
        uint8_t val = Wire2.read();
        Serial.print("0x");
        Serial.print(val, HEX);
        Serial.print(" ");
    }
    Serial.println();
}
```
