# SPI Wrapper Module

## Overview

The SPI wrapper module provides a C++ object-oriented interface for ESP-IDF SPI master functionality. This module encapsulates the ESP-IDF SPI master driver functions and provides a clean, easy-to-use API for SPI communication with peripheral devices.

## Features

- **Multi-Host Support**: Support for SPI1, SPI2, and SPI3 hosts
- **Device Management**: Add/remove multiple devices per SPI bus
- **Flexible Transfers**: Support for TX-only, RX-only, and full-duplex transfers
- **Command/Address Phases**: Support for command and address phases
- **DMA Support**: Automatic DMA channel allocation for high-speed transfers
- **Multiple SPI Modes**: Support for all 4 SPI modes (0-3)
- **Error Handling**: Comprehensive error checking and logging

## Architecture

### Class Diagram

```
┌─────────────────────────────────────┐
│              Spi                    │
├─────────────────────────────────────┤
│ - m_isInitialized_[3]: bool         │
├─────────────────────────────────────┤
│ + Spi()                             │
│ + ~Spi()                            │
│ + initializeBus(host, cfg): bool    │
│ + deinitializeBus(host): bool       │
│ + addDevice(host, cfg, hdl): bool   │
│ + removeDevice(handle): bool        │
│ + transmit(...): int32_t            │
│ + transmitOnly(...): int32_t        │
│ + receiveOnly(...): int32_t         │
│ + transmitWithCmdAddr(...): int32_t │
│ + isInitialized(host): bool         │
│ + getDefaultBusConfig(): SpiBusCfg  │
│ + getDefaultDeviceConfig(): SpiDev  │
│ - convertHost(host): spi_host_t     │
│ - convertBusConfig(cfg): spi_bus_t  │
│ - convertDeviceConfig(cfg): spi_dev │
│ - convertMode(mode): uint32_t       │
└─────────────────────────────────────┘
```

### Enumerations

#### SpiHost
- `SPI1_HOST`: SPI1 host (typically used for flash)
- `SPI2_HOST`: SPI2 host (HSPI)
- `SPI3_HOST`: SPI3 host (VSPI)

#### SpiMode
- `MODE_0`: CPOL=0, CPHA=0 (Clock idle low, data sampled on rising edge)
- `MODE_1`: CPOL=0, CPHA=1 (Clock idle low, data sampled on falling edge)
- `MODE_2`: CPOL=1, CPHA=0 (Clock idle high, data sampled on falling edge)
- `MODE_3`: CPOL=1, CPHA=1 (Clock idle high, data sampled on rising edge)

### Configuration Structures

#### SpiBusConfig
```cpp
struct SpiBusConfig {
    uint32_t mosiPin;          // MOSI pin number
    uint32_t misoPin;          // MISO pin number
    uint32_t sclkPin;          // SCLK pin number
    uint32_t quadwpPin;        // Quad SPI write protect pin (optional)
    uint32_t quadhdPin;        // Quad SPI hold pin (optional)
    uint32_t maxTransferSize;  // Maximum transfer size in bytes
};
```

#### SpiDeviceConfig
```cpp
struct SpiDeviceConfig {
    uint32_t csPin;            // Chip select pin number
    uint32_t clockSpeedHz;     // Clock speed in Hz
    SpiMode mode;              // SPI mode (0-3)
    uint32_t queueSize;        // Transaction queue size
    uint32_t commandBits;      // Command phase bits
    uint32_t addressBits;      // Address phase bits
    uint32_t dummyBits;        // Dummy phase bits
    bool csEnaPretrans;        // CS setup time
    bool csEnaPosttrans;       // CS hold time
};
```

## Usage Examples

### Basic SPI Communication

```cpp
#include "spi.hpp"

// Create SPI instance
Spi spi;

// Get default bus configuration
SpiBusConfig busConfig = Spi::getDefaultBusConfig();
busConfig.mosiPin = 23;
busConfig.misoPin = 19;
busConfig.sclkPin = 18;

// Initialize SPI bus
spi.initializeBus(SpiHost::SPI2_HOST, busConfig);

// Get default device configuration
SpiDeviceConfig deviceConfig = Spi::getDefaultDeviceConfig();
deviceConfig.csPin = 5;
deviceConfig.clockSpeedHz = 1000000;  // 1MHz
deviceConfig.mode = SpiMode::MODE_0;

// Add device to bus
spi_device_handle_t deviceHandle;
spi.addDevice(SpiHost::SPI2_HOST, deviceConfig, &deviceHandle);

// Transmit and receive data
uint8_t txData[] = {0x01, 0x02, 0x03};
uint8_t rxData[3];
spi.transmit(deviceHandle, txData, rxData, sizeof(txData));
```

### Transmit-Only Operation

```cpp
// Send data without receiving
uint8_t command[] = {0xAA, 0x55, 0xFF};
spi.transmitOnly(deviceHandle, command, sizeof(command));
```

### Receive-Only Operation

```cpp
// Receive data without sending
uint8_t buffer[10];
spi.receiveOnly(deviceHandle, buffer, sizeof(buffer));
```

### Command and Address Phases

```cpp
// Configure device with command and address bits
SpiDeviceConfig config = Spi::getDefaultDeviceConfig();
config.commandBits = 8;
config.addressBits = 24;
config.csPin = 5;

spi_device_handle_t device;
spi.addDevice(SpiHost::SPI2_HOST, config, &device);

// Send command and address with data
uint8_t data[4];
spi.transmitWithCmdAddr(device, 0x03, 0x000000, nullptr, data, sizeof(data));
```

### Multiple Devices on Same Bus

```cpp
// Device 1 configuration
SpiDeviceConfig device1Config = Spi::getDefaultDeviceConfig();
device1Config.csPin = 5;
device1Config.clockSpeedHz = 1000000;

// Device 2 configuration
SpiDeviceConfig device2Config = Spi::getDefaultDeviceConfig();
device2Config.csPin = 15;
device2Config.clockSpeedHz = 5000000;
device2Config.mode = SpiMode::MODE_3;

// Add both devices
spi_device_handle_t device1, device2;
spi.addDevice(SpiHost::SPI2_HOST, device1Config, &device1);
spi.addDevice(SpiHost::SPI2_HOST, device2Config, &device2);

// Use devices independently
uint8_t data1[] = {0x01, 0x02};
uint8_t data2[] = {0xAA, 0xBB};
spi.transmitOnly(device1, data1, sizeof(data1));
spi.transmitOnly(device2, data2, sizeof(data2));
```

## API Reference

### Constructor/Destructor

- **Spi()**: Initialize SPI wrapper instance
- **~Spi()**: Clean up resources and deinitialize all hosts

### Bus Management

- **initializeBus(host, config)**: Initialize SPI bus with configuration
- **deinitializeBus(host)**: Deinitialize SPI bus
- **isInitialized(host)**: Check if host is initialized

### Device Management

- **addDevice(host, config, handle)**: Add device to SPI bus
- **removeDevice(handle)**: Remove device from SPI bus

### Data Transfer Methods

- **transmit(handle, txData, rxData, length)**: Full-duplex transfer
- **transmitOnly(handle, txData, length)**: Transmit-only transfer
- **receiveOnly(handle, rxData, length)**: Receive-only transfer
- **transmitWithCmdAddr(handle, cmd, addr, txData, rxData, length)**: Transfer with command/address

### Configuration Methods

- **getDefaultBusConfig()**: Get default bus configuration
- **getDefaultDeviceConfig()**: Get default device configuration

## Error Handling

The module provides comprehensive error handling:
- Host validation and initialization checks
- Device handle validation
- ESP-IDF error codes are caught and logged
- Return values indicate success/failure for all operations
- Detailed logging for debugging and troubleshooting

## Dependencies

- ESP-IDF SPI master driver (`driver/spi_master.h`)
- ESP-IDF error handling (`esp_err.h`)
- ESP-IDF logging (`esp_log.h`)

## Thread Safety

The SPI wrapper uses ESP-IDF's thread-safe SPI master driver. Multiple tasks can safely use different devices on the same bus simultaneously.

## Memory Usage

- Fixed memory footprint per instance
- DMA buffers allocated automatically by ESP-IDF
- Transaction queues configurable per device

## Performance Considerations

- Direct ESP-IDF function calls for optimal performance
- DMA support for high-speed transfers
- Configurable transaction queue sizes
- Clock speeds up to 80MHz (depending on ESP32 variant)

## SPI Timing Modes

| Mode | CPOL | CPHA | Clock Idle | Data Sample Edge |
|------|------|------|------------|------------------|
| 0    | 0    | 0    | Low        | Rising           |
| 1    | 0    | 1    | Low        | Falling          |
| 2    | 1    | 0    | High       | Falling          |
| 3    | 1    | 1    | High       | Rising           |

## Common Use Cases

### Flash Memory
- Command/address phases for read/write operations
- High-speed transfers with DMA
- Mode 0 or Mode 3 typically used

### Display Controllers
- Command/data distinction using DC pin
- High-speed pixel data transfers
- Various modes depending on controller

### Sensors
- Register-based communication
- Lower speed requirements
- Mode 0 most common

## Troubleshooting

### Common Issues

1. **Clock Speed**: Ensure clock speed is within device specifications
2. **SPI Mode**: Verify correct CPOL/CPHA settings for device
3. **CS Timing**: Some devices require specific CS setup/hold times
4. **Wire Length**: Long wires can cause signal integrity issues
5. **Pull-up Resistors**: MISO line may need pull-up resistor