## Arduino SPI 模块 API 手册

### 1. 概述

K230 内部集成了三个 SPI 硬件模块，支持片选极性配置和可调时钟速率。Arduino环境下提供了标准的SPI接口，支持多种数据传输模式和灵活的配置选项。

### 2. API 介绍

SPI 类位于全局命名空间中，提供了三个预定义的SPI实例：`spi0`、`spi1` 和 `spi2`。

#### 2.1 构造函数

SPI实例已经预定义，无需手动构造：

- `spi0` - SPI0 实例
- `spi1` - SPI1 实例  
- `spi2` - SPI2 实例

#### 2.2 `begin` 方法

```cpp
bool begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
```

初始化SPI模块。

**参数**

- `sck`: 时钟引脚
- `miso`: 主机输入从机输出引脚
- `mosi`: 主机输出从机输入引脚
- `ss`: 片选引脚（默认为软件片选）

**返回值**

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

#### 2.3 `end` 方法

```cpp
void end()
```

关闭SPI模块并释放资源。

**参数**
无

**返回值**
无

#### 2.4 `setFrequency` 方法

```cpp
void setFrequency(uint32_t freq)
```

设置SPI时钟频率。

**参数**

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

**返回值**
无

#### 2.5 `setDataMode` 方法

```cpp
void setDataMode(uint8_t dataMode)
```

设置SPI数据模式（时钟极性和相位）。

**参数**

- `dataMode`: 数据模式，可选值：
  - `SPI_MODE0`: CPOL=0, CPHA=0
  - `SPI_MODE1`: CPOL=0, CPHA=1
  - `SPI_MODE2`: CPOL=1, CPHA=0
  - `SPI_MODE3`: CPOL=1, CPHA=1

**返回值**
无

#### 2.6 `setBitOrder` 方法

```cpp
void setBitOrder(uint8_t bitOrder)
```

设置数据位传输顺序。

**参数**

- `bitOrder`: 位顺序
  - `SPI_MSBFIRST`: 高位先传输
  - `SPI_LSBFIRST`: 低位先传输

**返回值**
无

#### 2.7 `setHwCs` 方法

```cpp
void setHwCs(bool use)
```

设置是否使用硬件片选。

**参数**

- `use`: true使用硬件片选，false使用软件片选

**返回值**
无

#### 2.8 `setSSInvert` 方法

```cpp
void setSSInvert(bool invert)
```

设置片选信号极性。必须在调用`setHwCs`之前使用。

**参数**

- `invert`: true反转片选极性，false正常极性

**返回值**
无

#### 2.9 `beginTransaction` 方法

```cpp
void beginTransaction(SPISettings settings)
```

开始SPI事务，使用指定的设置。

**参数**

- `settings`: SPISettings对象，包含时钟频率、位顺序和数据模式

**返回值**
无

#### 2.10 `endTransaction` 方法

```cpp
void endTransaction()
```

结束SPI事务。

**参数**
无

**返回值**
无

#### 2.11 `write` 方法

```cpp
void write(uint8_t data)
```

发送一个字节数据。

**参数**

- `data`: 要发送的数据

**返回值**
无

#### 2.12 `write16` 方法

```cpp
void write16(uint16_t data)
```

发送16位数据。

**参数**

- `data`: 要发送的16位数据

**返回值**
无

#### 2.13 `write32` 方法

```cpp
void write32(uint32_t data)
```

发送32位数据。

**参数**

- `data`: 要发送的32位数据

**返回值**
无

#### 2.14 `transfer` 方法

```cpp
uint8_t transfer(uint8_t data)
```

发送一个字节并接收一个字节。

**参数**

- `data`: 要发送的数据

**返回值**
接收到的数据

#### 2.15 `transfer16` 方法

```cpp
uint16_t transfer16(uint16_t data)
```

发送16位数据并接收16位数据。

**参数**

- `data`: 要发送的16位数据

**返回值**
接收到的16位数据

#### 2.16 `transfer32` 方法

```cpp
uint32_t transfer32(uint32_t data)
```

发送32位数据并接收32位数据。

**参数**

- `data`: 要发送的32位数据

**返回值**
接收到的32位数据

#### 2.17 `transfer` 方法（批量传输）

```cpp
void transfer(void *data, uint32_t size)
```

批量发送和接收数据，使用同一个缓冲区。

**参数**

- `data`: 数据缓冲区指针
- `size`: 数据长度（字节）

**返回值**
无

#### 2.18 `writeBytes` 方法

```cpp
void writeBytes(const uint8_t *data, uint32_t size)
```

发送多个字节数据。

**参数**

- `data`: 数据缓冲区指针
- `size`: 数据长度（字节）

**返回值**
无

#### 2.19 `transferBytes` 方法

```cpp
void transferBytes(const uint8_t *data, uint8_t *out, uint32_t size)
```

同时发送和接收多个字节。

**参数**

- `data`: 发送数据缓冲区（可为NULL进行只读操作）
- `out`: 接收数据缓冲区（可为NULL进行只写操作）
- `size`: 数据长度（字节）

**返回值**
无

#### 2.20 `transferBits` 方法

```cpp
void transferBits(uint32_t data, uint32_t *out, uint8_t bits)
```

传输指定位数的数据。

**参数**

- `data`: 要发送的数据
- `out`: 接收数据的指针（可为NULL）
- `bits`: 要传输的位数（最大32位）

**返回值**
无

#### 2.21 `writePixels` 方法

```cpp
void writePixels(const void *data, uint32_t size)
```

发送像素数据，兼容ILI9341等显示屏。

**参数**

- `data`: 像素数据缓冲区
- `size`: 数据大小（字节）

**返回值**
无

#### 2.22 `writePattern` 方法

```cpp
void writePattern(const uint8_t *data, uint8_t size, uint32_t repeat)
```

重复发送指定的数据模式。

**参数**

- `data`: 数据模式缓冲区
- `size`: 模式大小（最大64字节）
- `repeat`: 重复次数

**返回值**
无

#### 2.23 `bus` 方法

```cpp
drv_spi_inst_t bus()
```

获取底层SPI实例句柄。

**参数**
无

**返回值**
SPI实例句柄

#### 2.24 `pinSS` 方法

```cpp
int8_t pinSS()
```

获取片选引脚编号。

**参数**
无

**返回值**
片选引脚编号

### 3. SPISettings 类

用于配置SPI事务参数。

#### 3.1 构造函数

```cpp
SPISettings()
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
```

**参数**

- `clock`: 时钟频率（Hz），默认1MHz
- `bitOrder`: 位顺序，默认SPI_MSBFIRST
- `dataMode`: 数据模式，默认SPI_MODE0

---

### 4. 示例程序

```cpp
#include "Arduino.h"

using namespace arduino;

// W25Q128 Flash命令定义
#define W25Q_WRITE_ENABLE       0x06
#define W25Q_WRITE_DISABLE      0x04
#define W25Q_READ_STATUS_REG1   0x05
#define W25Q_READ_STATUS_REG2   0x35
#define W25Q_WRITE_STATUS_REG   0x01
#define W25Q_PAGE_PROGRAM       0x02
#define W25Q_SECTOR_ERASE       0x20
#define W25Q_BLOCK_ERASE_32K    0x52
#define W25Q_BLOCK_ERASE_64K    0xD8
#define W25Q_CHIP_ERASE         0xC7
#define W25Q_READ_DATA          0x03
#define W25Q_FAST_READ          0x0B
#define W25Q_DEVICE_ID          0x90
#define W25Q_JEDEC_ID           0x9F
#define W25Q_UNIQUE_ID          0x4B

// SPI引脚定义
#define SPI_CS_PIN    14
#define SPI_SCK_PIN   15
#define SPI_MOSI_PIN  16
#define SPI_MISO_PIN  17

// 测试缓冲区
uint8_t write_buffer[256];
uint8_t read_buffer[256];

void setup() {
    Serial.begin(115200);

    Serial.println("W25Q128 SPI Flash Test Program");
    Serial.println("==============================");

    // 初始化SPI（传入所有引脚，包括SS）
    if (!spi0.begin(SPI_SCK_PIN, SPI_MISO_PIN, SPI_MOSI_PIN, SPI_CS_PIN)) {
        Serial.println("SPI initialization failed!");
        while (1);
    }

    // 设置为软件片选模式
    spi0.setHwCs(false);

    // 配置SS引脚为输出
    pinMode(spi0.pinSS(), OUTPUT);
    digitalWrite(spi0.pinSS(), HIGH);

    // 配置SPI参数
    spi0.setFrequency(10000000);  // 10MHz
    spi0.setDataMode(SPI_MODE0);
    spi0.setBitOrder(SPI_MSBFIRST);

    Serial.println("SPI initialized successfully");
    Serial.print("Using SS pin: ");
    Serial.println(spi0.pinSS());

    // 读取Flash ID
    uint32_t jedec_id = readJEDECID();
    Serial.print("JEDEC ID: 0x");
    Serial.println(jedec_id, HEX);

    // 验证是否为W25Q128
    if ((jedec_id & 0xFFFFFF) == 0xEF4018) {
        Serial.println("W25Q128 detected!");
    } else {
        Serial.println("Unknown Flash device!");
    }

    // 准备测试数据
    for (int i = 0; i < 256; i++) {
        write_buffer[i] = i;
    }
}

void loop() {
    static uint32_t test_address = 0;

    Serial.println("\n--- Test Round ");

    // 1. 擦除扇区
    Serial.print("Erasing sector at address 0x");
    Serial.print(test_address, HEX);
    Serial.print("...");
    eraseSector(test_address);
    Serial.println(" Done!");

    // 2. 写入数据
    Serial.print("Writing 256 bytes...");
    pageProgram(test_address, write_buffer, 256);
    Serial.println(" Done!");

    // 3. 读取数据
    Serial.print("Reading 256 bytes...");
    readData(test_address, read_buffer, 256);
    Serial.println(" Done!");

    // 4. 验证数据
    Serial.print("Verifying data...");
    bool verify_ok = true;
    for (int i = 0; i < 256; i++) {
        if (read_buffer[i] != write_buffer[i]) {
            verify_ok = false;
            Serial.print("\nError at offset ");
            Serial.print(i);
            Serial.print(": expected 0x");
            Serial.print(write_buffer[i], HEX);
            Serial.print(", got 0x");
            Serial.println(read_buffer[i], HEX);
        }
    }

    if (verify_ok) {
        Serial.println(" Success!");
    } else {
        Serial.println(" Failed!");
    }

    // 移动到下一个扇区
    test_address += 0x1000;  // 4KB扇区
    if (test_address >= 0x1000000) {  // 16MB
        test_address = 0;
    }

    // 修改测试数据
    for (int i = 0; i < 256; i++) {
        write_buffer[i] = (write_buffer[i] + 1) & 0xFF;
    }

    delay(5000);  // 等待5秒进行下一轮测试
}

// 片选控制 - 使用pinSS()获取引脚号
void flashSelect() {
    digitalWrite(spi0.pinSS(), LOW);
}

void flashDeselect() {
    digitalWrite(spi0.pinSS(), HIGH);
}

// 等待Flash空闲
void waitBusy() {
    uint8_t status;
    do {
        flashSelect();
        spi0.write(W25Q_READ_STATUS_REG1);
        status = spi0.transfer(0x00);
        flashDeselect();
    } while (status & 0x01);  // 检查BUSY位
}

// 写使能
void writeEnable() {
    flashSelect();
    spi0.write(W25Q_WRITE_ENABLE);
    flashDeselect();
}

// 读取JEDEC ID
uint32_t readJEDECID() {
    uint32_t id = 0;

    spi0.beginTransaction(SPISettings(10000000, SPI_MSBFIRST, SPI_MODE0));
    flashSelect();
    spi0.write(W25Q_JEDEC_ID);
    id |= ((uint32_t)spi0.transfer(0x00)) << 16;
    id |= ((uint32_t)spi0.transfer(0x00)) << 8;
    id |= spi0.transfer(0x00);
    flashDeselect();
    spi0.endTransaction();

    return id;
}

// 擦除扇区（4KB）
void eraseSector(uint32_t address) {
    waitBusy();
    writeEnable();

    spi0.beginTransaction(SPISettings(10000000, SPI_MSBFIRST, SPI_MODE0));
    flashSelect();
    spi0.write(W25Q_SECTOR_ERASE);
    spi0.write((address >> 16) & 0xFF);
    spi0.write((address >> 8) & 0xFF);
    spi0.write(address & 0xFF);
    flashDeselect();
    spi0.endTransaction();

    waitBusy();
}

// 页编程（最多256字节）
void pageProgram(uint32_t address, const uint8_t* data, uint16_t length) {
    if (length > 256) length = 256;

    waitBusy();
    writeEnable();

    spi0.beginTransaction(SPISettings(10000000, SPI_MSBFIRST, SPI_MODE0));
    flashSelect();
    spi0.write(W25Q_PAGE_PROGRAM);
    spi0.write((address >> 16) & 0xFF);
    spi0.write((address >> 8) & 0xFF);
    spi0.write(address & 0xFF);
    spi0.writeBytes(data, length);
    flashDeselect();
    spi0.endTransaction();

    waitBusy();
}

// 读取数据
void readData(uint32_t address, uint8_t* buffer, uint16_t length) {
    waitBusy();

    spi0.beginTransaction(SPISettings(10000000, SPI_MSBFIRST, SPI_MODE0));
    flashSelect();
    spi0.write(W25Q_READ_DATA);
    spi0.write((address >> 16) & 0xFF);
    spi0.write((address >> 8) & 0xFF);
    spi0.write(address & 0xFF);
    spi0.transferBytes(NULL, buffer, length);
    flashDeselect();
    spi0.endTransaction();
}

```
