๐Ÿ”Œ BaseSpi API Reference

๐Ÿ”„ Unified SPI abstraction for high-speed serial communication

๐Ÿ“š Table of Contents


๐ŸŽฏ Overview

The BaseSpi class provides a comprehensive SPI abstraction that serves as the unified interface for all Serial Peripheral Interface operations in the HardFOC system. It supports multi-device communication, configurable modes, and high-speed data transfer.

โœจ Key Features

  • ๐Ÿ”„ Multi-Device Support - Simultaneous communication with multiple SPI devices
  • โšก High-Speed Transfer - Configurable clock frequencies up to 80 MHz
  • ๐ŸŽ›๏ธ Flexible Modes - Support for all SPI modes (0, 1, 2, 3)
  • ๐Ÿ“Š DMA Support - Hardware-accelerated data transfer
  • ๐Ÿ›ก๏ธ Robust Error Handling - Comprehensive validation and error reporting
  • ๐ŸŽ๏ธ Performance Optimized - Minimal overhead for critical applications
  • ๐Ÿ”Œ Platform Agnostic - Works with various SPI hardware implementations
  • ๐Ÿ“ˆ Real-time Control - Low-latency communication for time-critical applications

๐Ÿ”Œ Supported Applications

Application Speed Description

|โ€”โ€”โ€”โ€”-|โ€”โ€”-|โ€”โ€”โ€”โ€”-|

Sensor Communication 1-10 MHz Temperature, pressure, IMU sensors
Display Control 10-40 MHz LCD, OLED, TFT displays
Memory Access 20-80 MHz Flash, EEPROM, FRAM
Motor Control 1-20 MHz Motor driver ICs
Audio Codecs 1-50 MHz Digital audio interfaces

๐Ÿ—๏ธ Class Hierarchy

classDiagram
    class BaseSpi {
        <<abstract>>
        +Initialize() hf_spi_err_t
        +Deinitialize() hf_spi_err_t
        +ConfigureDevice(device_id, config) hf_spi_err_t
        +TransmitReceive(device_id, tx_data, rx_data, length) hf_spi_err_t
        +Transmit(device_id, data, length) hf_spi_err_t
        +Receive(device_id, data, length) hf_spi_err_t
        +GetDeviceStatus(device_id, status) hf_spi_err_t
        +GetCapabilities(capabilities) hf_spi_err_t
    }
    
    class EspSpi {
        +EspSpi(host, cs_pin)
        +GetHost() spi_host_device_t
        +GetCsPin() hf_pin_num_t
    }
    
    BaseSpi <|-- EspSpi

๐Ÿ“‹ Error Codes

โœ… Success Codes

Code Value Description

|โ€”โ€”|โ€”โ€”-|โ€”โ€”โ€”โ€”-|

SPI_SUCCESS 0 โœ… Operation completed successfully

โŒ General Error Codes

Code Value Description Resolution

|โ€”โ€”|โ€”โ€”-|โ€”โ€”โ€”โ€”-|โ€”โ€”โ€”โ€”|

SPI_ERR_FAILURE 1 โŒ General operation failure Check hardware and configuration
SPI_ERR_NOT_INITIALIZED 2 โš ๏ธ SPI not initialized Call Initialize() first
SPI_ERR_ALREADY_INITIALIZED 3 โš ๏ธ SPI already initialized Check initialization state
SPI_ERR_INVALID_PARAMETER 4 ๐Ÿšซ Invalid parameter Validate input parameters
SPI_ERR_NULL_POINTER 5 ๐Ÿšซ Null pointer provided Check pointer validity
SPI_ERR_OUT_OF_MEMORY 6 ๐Ÿ’พ Memory allocation failed Check system memory

๐Ÿ”ง Device Error Codes

Code Value Description Resolution

|โ€”โ€”|โ€”โ€”-|โ€”โ€”โ€”โ€”-|โ€”โ€”โ€”โ€”|

SPI_ERR_INVALID_DEVICE 7 ๐Ÿšซ Invalid SPI device Use valid device numbers
SPI_ERR_DEVICE_BUSY 8 ๐Ÿ”„ Device already in use Wait or use different device
SPI_ERR_DEVICE_NOT_AVAILABLE 9 โš ๏ธ Device not available Check device availability
SPI_ERR_DEVICE_NOT_CONFIGURED 10 โš™๏ธ Device not configured Configure device first
SPI_ERR_DEVICE_NOT_RESPONDING 11 ๐Ÿ”‡ Device not responding Check device power

โšก Transfer Error Codes

Code Value Description Resolution

|โ€”โ€”|โ€”โ€”-|โ€”โ€”โ€”โ€”-|โ€”โ€”โ€”โ€”|

SPI_ERR_TRANSFER_TIMEOUT 12 โฐ Transfer timeout Check clock frequency and device
SPI_ERR_TRANSFER_FAILURE 13 โŒ Transfer failed Check connections and device state
SPI_ERR_TRANSFER_INCOMPLETE 14 ๐Ÿ“Š Transfer incomplete Check data length and buffer size
SPI_ERR_TRANSFER_ABORTED 15 โน๏ธ Transfer aborted Check abort conditions

๐ŸŽ›๏ธ Configuration Error Codes

Code Value Description Resolution

|โ€”โ€”|โ€”โ€”-|โ€”โ€”โ€”โ€”-|โ€”โ€”โ€”โ€”|

SPI_ERR_INVALID_CONFIGURATION 16 โš™๏ธ Invalid configuration Check configuration parameters
SPI_ERR_UNSUPPORTED_MODE 17 ๐Ÿšซ Unsupported SPI mode Use supported mode
SPI_ERR_UNSUPPORTED_FREQUENCY 18 ๐Ÿšซ Unsupported frequency Use supported frequency range
SPI_ERR_UNSUPPORTED_DATA_SIZE 19 ๐Ÿšซ Unsupported data size Use supported data size
SPI_ERR_PIN_CONFLICT 20 ๐Ÿ”Œ Pin already in use Use different pins

๐ŸŒ Hardware Error Codes

Code Value Description Resolution

|โ€”โ€”|โ€”โ€”-|โ€”โ€”โ€”โ€”-|โ€”โ€”โ€”โ€”|

SPI_ERR_HARDWARE_FAULT 21 ๐Ÿ’ฅ Hardware fault Check power and connections
SPI_ERR_COMMUNICATION_FAILURE 22 ๐Ÿ“ก Communication failure Check bus connections
SPI_ERR_DMA_ERROR 23 ๐Ÿ’พ DMA error Check DMA configuration
SPI_ERR_RESOURCE_BUSY 24 ๐Ÿ”„ Resource busy Wait for resource availability

๐Ÿ”ง Core API

๐Ÿ—๏ธ Initialization Methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
 * @brief Initialize the SPI peripheral
 * @return hf_spi_err_t error code
 * 
 * ๐Ÿ“ Sets up SPI hardware, configures devices, and prepares for communication.
 * Must be called before any SPI operations.
 * 
 * @example
 * EspSpi spi(SPI2_HOST, 5);  // SPI2, CS on pin 5
 * hf_spi_err_t result = spi.Initialize();
 * if (result == hf_spi_err_t::SPI_SUCCESS) {
 *     // SPI ready for use
 * }
 */
virtual hf_spi_err_t Initialize() noexcept = 0;

/**
 * @brief Deinitialize the SPI peripheral
 * @return hf_spi_err_t error code
 * 
 * ๐Ÿงน Cleanly shuts down SPI hardware and releases resources.
 */
virtual hf_spi_err_t Deinitialize() noexcept = 0;

/**
 * @brief Check if SPI is initialized
 * @return true if initialized, false otherwise
 * 
 * โ“ Query initialization status without side effects.
 */
[[nodiscard]] bool IsInitialized() const noexcept;

/**
 * @brief Ensure SPI is initialized (lazy initialization)
 * @return true if initialized successfully, false otherwise
 * 
 * ๐Ÿ”„ Automatically initializes SPI if not already initialized.
 */
bool EnsureInitialized() noexcept;

โš™๏ธ Device Configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * @brief Configure an SPI device
 * @param device_id Device identifier
 * @param config Device configuration structure
 * @return hf_spi_err_t error code
 * 
 * โš™๏ธ Configures device parameters including mode, frequency, data size,
 * and pin assignments.
 * 
 * @example
 * hf_spi_device_config_t config;
 * config.mode = hf_spi_mode_t::MODE_0;
 * config.frequency_hz = 1000000;  // 1 MHz
 * config.data_size = hf_spi_data_size_t::DATA_8BIT;
 * config.cs_pin = 5;
 * config.cs_active_low = true;
 * 
 * hf_spi_err_t result = spi.ConfigureDevice(0, config);
 */
virtual hf_spi_err_t ConfigureDevice(uint8_t device_id,
                                   const hf_spi_device_config_t &config) noexcept = 0;

๐Ÿ”„ Data Transfer Methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
 * @brief Transmit and receive data simultaneously
 * @param device_id Device identifier
 * @param tx_data Transmit data buffer
 * @param rx_data Receive data buffer
 * @param length Number of bytes to transfer
 * @return hf_spi_err_t error code
 * 
 * ๐Ÿ”„ Performs full-duplex SPI transfer. Both transmit and receive
 * buffers must be at least 'length' bytes.
 * 
 * @example
 * uint8_t tx_data[] = {0x01, 0x02, 0x03};
 * uint8_t rx_data[3];
 * hf_spi_err_t result = spi.TransmitReceive(0, tx_data, rx_data, 3);
 * if (result == hf_spi_err_t::SPI_SUCCESS) {
 *     printf("Received: %02X %02X %02X\n", rx_data[0], rx_data[1], rx_data[2]);
 * }
 */
virtual hf_spi_err_t TransmitReceive(uint8_t device_id, const uint8_t *tx_data,
                                   uint8_t *rx_data, size_t length) noexcept = 0;

/**
 * @brief Transmit data only
 * @param device_id Device identifier
 * @param data Transmit data buffer
 * @param length Number of bytes to transmit
 * @return hf_spi_err_t error code
 * 
 * ๐Ÿ“ค Performs SPI transmit operation. Receive data is discarded.
 * 
 * @example
 * uint8_t command[] = {0xAA, 0x55, 0x01};
 * hf_spi_err_t result = spi.Transmit(0, command, 3);
 */
virtual hf_spi_err_t Transmit(uint8_t device_id, const uint8_t *data,
                            size_t length) noexcept = 0;

/**
 * @brief Receive data only
 * @param device_id Device identifier
 * @param data Receive data buffer
 * @param length Number of bytes to receive
 * @return hf_spi_err_t error code
 * 
 * ๐Ÿ“ฅ Performs SPI receive operation. Transmit data is zeros.
 * 
 * @example
 * uint8_t response[4];
 * hf_spi_err_t result = spi.Receive(0, response, 4);
 */
virtual hf_spi_err_t Receive(uint8_t device_id, uint8_t *data,
                           size_t length) noexcept = 0;

๐Ÿ“Š Status and Capabilities

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * @brief Get device status information
 * @param device_id Device identifier
 * @param status [out] Status information structure
 * @return hf_spi_err_t error code
 * 
 * ๐Ÿ“Š Retrieves comprehensive status information about a device.
 */
virtual hf_spi_err_t GetDeviceStatus(uint8_t device_id,
                                   hf_spi_device_status_t &status) const noexcept = 0;

/**
 * @brief Get SPI capabilities
 * @param capabilities [out] Capability information structure
 * @return hf_spi_err_t error code
 * 
 * ๐Ÿ“‹ Retrieves hardware capabilities and limitations.
 */
virtual hf_spi_err_t GetCapabilities(hf_spi_capabilities_t &capabilities) const noexcept = 0;

๐Ÿ“Š Data Structures

โš™๏ธ Device Configuration

1
2
3
4
5
6
7
8
9
10
struct hf_spi_device_config_t {
    hf_spi_mode_t mode;              ///< SPI mode (0-3)
    uint32_t frequency_hz;           ///< Clock frequency in Hz
    hf_spi_data_size_t data_size;    ///< Data size (8, 16, 32 bit)
    hf_pin_num_t cs_pin;             ///< Chip select pin
    bool cs_active_low;              ///< CS active low (true) or high (false)
    hf_spi_bit_order_t bit_order;    ///< Bit order (MSB or LSB first)
    uint32_t timeout_ms;             ///< Transfer timeout in milliseconds
    bool use_dma;                    ///< Use DMA for transfers
};

๐Ÿ“Š Device Status

1
2
3
4
5
6
7
8
9
10
struct hf_spi_device_status_t {
    bool is_configured;        ///< Device is configured
    bool is_busy;              ///< Device is currently busy
    hf_spi_mode_t current_mode; ///< Current SPI mode
    uint32_t current_frequency; ///< Current frequency in Hz
    uint32_t bytes_transferred; ///< Total bytes transferred
    uint32_t transfer_errors;   ///< Number of transfer errors
    hf_spi_err_t last_error;    ///< Last error that occurred
    uint32_t timestamp_us;      ///< Timestamp of last operation
};

๐Ÿ“‹ SPI Capabilities

1
2
3
4
5
6
7
8
9
10
struct hf_spi_capabilities_t {
    uint8_t max_devices;           ///< Maximum number of devices
    uint32_t min_frequency_hz;     ///< Minimum frequency
    uint32_t max_frequency_hz;     ///< Maximum frequency
    uint8_t supported_modes;       ///< Bit mask of supported modes
    uint8_t supported_data_sizes;  ///< Bit mask of supported data sizes
    bool supports_dma;             ///< Supports DMA transfers
    bool supports_quad_spi;        ///< Supports quad SPI
    uint32_t max_transfer_size;    ///< Maximum transfer size in bytes
};

๐Ÿ“ˆ SPI Statistics

1
2
3
4
5
6
7
8
9
10
11
12
struct hf_spi_statistics_t {
    uint32_t total_transfers;      ///< Total transfers performed
    uint32_t successful_transfers; ///< Successful transfers
    uint32_t failed_transfers;     ///< Failed transfers
    uint32_t bytes_transmitted;    ///< Total bytes transmitted
    uint32_t bytes_received;       ///< Total bytes received
    uint32_t average_transfer_time_us; ///< Average transfer time
    uint32_t max_transfer_time_us; ///< Maximum transfer time
    uint32_t min_transfer_time_us; ///< Minimum transfer time
    uint32_t timeout_errors;       ///< Timeout errors
    uint32_t communication_errors; ///< Communication errors
};

๐Ÿ“Š Usage Examples

๐Ÿ“ก Sensor Communication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "mcu/esp32/EspSpi.h"
#include "utils/memory_utils.h"

class SensorController {
private:
    EspSpi spi*;
    static constexpr uint8_t SENSOR_DEVICE = 0;
    
public:
    bool initialize() {
        spi* = EspSpi(SPI2_HOST, 5);  // SPI2, CS on pin 5
        
        if (!spi*.EnsureInitialized()) {
            printf("โŒ SPI initialization failed\n");
            return false;
        }
        
        // Configure for sensor communication
        hf_spi_device_config_t config;
        config.mode = hf_spi_mode_t::MODE_0;
        config.frequency_hz = 1000000;  // 1 MHz
        config.data_size = hf_spi_data_size_t::DATA_8BIT;
        config.cs_pin = 5;
        config.cs_active_low = true;
        config.bit_order = hf_spi_bit_order_t::MSB_FIRST;
        config.timeout_ms = 100;
        config.use_dma = false;
        
        hf_spi_err_t result = spi*.ConfigureDevice(SENSOR_DEVICE, config);
        if (result != hf_spi_err_t::SPI_SUCCESS) {
            printf("โŒ Sensor configuration failed: %s\n", HfSpiErrToString(result));
            return false;
        }
        
        printf("โœ… Sensor controller initialized\n");
        return true;
    }
    
    uint16_t read_temperature() {
        // Send temperature read command
        uint8_t tx_cmd[] = {0x03, 0x00, 0x00};  // Read temperature command
        uint8_t rx_data[3];
        
        hf_spi_err_t result = spi*.TransmitReceive(SENSOR_DEVICE, tx_cmd, rx_data, 3);
        if (result != hf_spi_err_t::SPI_SUCCESS) {
            printf("โŒ Temperature read failed: %s\n", HfSpiErrToString(result));
            return 0xFFFF;  // Error value
        }
        
        // Convert response to temperature (example conversion)
        uint16_t raw_temp = (rx_data[1] << 8) | rx_data[2];
        float temperature = (raw_temp * 175.0f / 65535.0f) - 45.0f;
        
        printf("๐ŸŒก๏ธ Temperature: %.1fยฐC\n", temperature);
        return raw_temp;
    }
    
    void write_config(uint8_t config_register, uint8_t value) {
        // Send configuration write command
        uint8_t tx_data[] = {0x02, config_register, value};  // Write command
        uint8_t rx_data[3];
        
        hf_spi_err_t result = spi*.TransmitReceive(SENSOR_DEVICE, tx_data, rx_data, 3);
        if (result == hf_spi_err_t::SPI_SUCCESS) {
            printf("โœ… Config written: 0x%02X = 0x%02X\n", config_register, value);
        } else {
            printf("โŒ Config write failed: %s\n", HfSpiErrToString(result));
        }
    }
    
    void read_sensor_data(uint8_t* data, size_t length) {
        // Read sensor data using nothrow allocation
        uint8_t tx_cmd[] = {0x04, 0x00};  // Read data command
        auto rx_data = hf::utils::make_unique_array_nothrow<uint8_t>(length + 2);
        if (!rx_data) {
            printf("โŒ Failed to allocate memory for receive buffer\n");
            return;
        }
        
        hf_spi_err_t result = spi*.TransmitReceive(SENSOR_DEVICE, tx_cmd, rx_data.get(), length + 2);
        if (result == hf_spi_err_t::SPI_SUCCESS) {
            // Copy data (skip command bytes)
            memcpy(data, rx_data.get() + 2, length);
            printf("๐Ÿ“Š Read %zu bytes of sensor data\n", length);
        } else {
            printf("โŒ Sensor data read failed: %s\n", HfSpiErrToString(result));
        }
        
        // rx_data automatically cleaned up when going out of scope
    }
};

๐Ÿ–ฅ๏ธ Display Control

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include "mcu/esp32/EspSpi.h"
#include "utils/memory_utils.h"

class DisplayController {
private:
    EspSpi spi*;
    static constexpr uint8_t DISPLAY_DEVICE = 0;
    static constexpr uint16_t DISPLAY_WIDTH = 240;
    static constexpr uint16_t DISPLAY_HEIGHT = 320;
    
public:
    bool initialize() {
        spi* = EspSpi(SPI2_HOST, 15);  // SPI2, CS on pin 15
        
        if (!spi*.EnsureInitialized()) {
            return false;
        }
        
        // Configure for display communication
        hf_spi_device_config_t config;
        config.mode = hf_spi_mode_t::MODE_0;
        config.frequency_hz = 40000000;  // 40 MHz for display
        config.data_size = hf_spi_data_size_t::DATA_8BIT;
        config.cs_pin = 15;
        config.cs_active_low = true;
        config.bit_order = hf_spi_bit_order_t::MSB_FIRST;
        config.timeout_ms = 1000;
        config.use_dma = true;  // Use DMA for large transfers
        
        hf_spi_err_t result = spi*.ConfigureDevice(DISPLAY_DEVICE, config);
        if (result != hf_spi_err_t::SPI_SUCCESS) {
            printf("โŒ Display configuration failed\n");
            return false;
        }
        
        // Initialize display
        init_display();
        printf("โœ… Display controller initialized\n");
        return true;
    }
    
private:
    void init_display() {
        // Display initialization sequence
        uint8_t init_commands[] = {
            0x01, 0x00,  // Software reset
            0x11, 0x00,  // Sleep out
            0x29, 0x00   // Display on
        };
        
        for (size_t i = 0; i < sizeof(init_commands); i += 2) {
            send_command(init_commands[i]);
            if (init_commands[i + 1] != 0) {
                send_data(&init_commands[i + 1], 1);
            }
            vTaskDelay(pdMS_TO_TICKS(10));
        }
    }
    
    void send_command(uint8_t command) {
        // Set DC pin low for command
        gpio_set_level(16, 0);  // DC pin on GPIO 16
        
        uint8_t rx_data;
        hf_spi_err_t result = spi*.TransmitReceive(DISPLAY_DEVICE, &command, &rx_data, 1);
        if (result != hf_spi_err_t::SPI_SUCCESS) {
            printf("โŒ Command send failed: %s\n", HfSpiErrToString(result));
        }
    }
    
    void send_data(const uint8_t* data, size_t length) {
        // Set DC pin high for data
        gpio_set_level(16, 1);  // DC pin on GPIO 16
        
        auto rx_data = hf::utils::make_unique_array_nothrow<uint8_t>(length);
        if (!rx_data) {
            printf("โŒ Failed to allocate memory for receive buffer\n");
            return;
        }
        
        hf_spi_err_t result = spi*.TransmitReceive(DISPLAY_DEVICE, data, rx_data.get(), length);
        if (result != hf_spi_err_t::SPI_SUCCESS) {
            printf("โŒ Data send failed: %s\n", HfSpiErrToString(result));
        }
        
        // rx_data automatically cleaned up when going out of scope
    }
    
public:
    void set_window(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end) {
        // Set display window for drawing
        uint8_t caset_cmd[] = {0x2A, 0x00, (x_start >> 8) & 0xFF, x_start & 0xFF,
                              0x00, (x_end >> 8) & 0xFF, x_end & 0xFF};
        uint8_t raset_cmd[] = {0x2B, 0x00, (y_start >> 8) & 0xFF, y_start & 0xFF,
                              0x00, (y_end >> 8) & 0xFF, y_end & 0xFF};
        
        send_command(0x2A);  // Column address set
        send_data(caset_cmd + 1, 6);
        
        send_command(0x2B);  // Row address set
        send_data(raset_cmd + 1, 6);
        
        send_command(0x2C);  // Memory write
    }
    
    void fill_screen(uint16_t color) {
        set_window(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
        
        // Prepare color data
        uint8_t color_data[2] = {(color >> 8) & 0xFF, color & 0xFF};
        
        // Fill screen with color
        for (int i = 0; i < DISPLAY_WIDTH * DISPLAY_HEIGHT; i++) {
            send_data(color_data, 2);
        }
    }
    
    void draw_pixel(uint16_t x, uint16_t y, uint16_t color) {
        set_window(x, y, x, y);
        
        uint8_t color_data[2] = {(color >> 8) & 0xFF, color & 0xFF};
        send_data(color_data, 2);
    }
};

๐Ÿ’พ Memory Access

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include "mcu/esp32/EspSpi.h"
#include "utils/memory_utils.h"

class MemoryController {
private:
    EspSpi spi*;
    static constexpr uint8_t MEMORY_DEVICE = 0;
    static constexpr uint32_t MEMORY_SIZE = 1024 * 1024;  // 1MB
    
public:
    bool initialize() {
        spi* = EspSpi(SPI2_HOST, 5);  // SPI2, CS on pin 5
        
        if (!spi*.EnsureInitialized()) {
            return false;
        }
        
        // Configure for memory access
        hf_spi_device_config_t config;
        config.mode = hf_spi_mode_t::MODE_0;
        config.frequency_hz = 80000000;  // 80 MHz for fast memory access
        config.data_size = hf_spi_data_size_t::DATA_8BIT;
        config.cs_pin = 5;
        config.cs_active_low = true;
        config.bit_order = hf_spi_bit_order_t::MSB_FIRST;
        config.timeout_ms = 100;
        config.use_dma = true;
        
        hf_spi_err_t result = spi*.ConfigureDevice(MEMORY_DEVICE, config);
        if (result != hf_spi_err_t::SPI_SUCCESS) {
            printf("โŒ Memory configuration failed\n");
            return false;
        }
        
        printf("โœ… Memory controller initialized\n");
        return true;
    }
    
    bool read_memory(uint32_t address, uint8_t* data, size_t length) {
        // Send read command using nothrow allocation
        uint8_t tx_cmd[] = {0x03, (address >> 16) & 0xFF, (address >> 8) & 0xFF, address & 0xFF};
        auto rx_data = hf::utils::make_unique_array_nothrow<uint8_t>(length + 4);
        if (!rx_data) {
            printf("โŒ Failed to allocate memory for receive buffer\n");
            return false;
        }
        
        hf_spi_err_t result = spi*.TransmitReceive(MEMORY_DEVICE, tx_cmd, rx_data.get(), length + 4);
        if (result == hf_spi_err_t::SPI_SUCCESS) {
            // Copy data (skip command bytes)
            memcpy(data, rx_data.get() + 4, length);
            printf("๐Ÿ“– Read %zu bytes from address 0x%06X\n", length, address);
            return true;
        } else {
            printf("โŒ Memory read failed: %s\n", HfSpiErrToString(result));
            return false;
        }
        // rx_data automatically cleaned up when going out of scope
    }
    
    bool write_memory(uint32_t address, const uint8_t* data, size_t length) {
        // Send write enable command
        uint8_t write_enable = 0x06;
        uint8_t rx_data;
        hf_spi_err_t result = spi*.TransmitReceive(MEMORY_DEVICE, &write_enable, &rx_data, 1);
        if (result != hf_spi_err_t::SPI_SUCCESS) {
            printf("โŒ Write enable failed\n");
            return false;
        }
        
        // Send write command using nothrow allocation
        auto tx_data = hf::utils::make_unique_array_nothrow<uint8_t>(length + 4);
        if (!tx_data) {
            printf("โŒ Failed to allocate memory for transmit buffer\n");
            return false;
        }
        
        tx_data[0] = 0x02;  // Page program command
        tx_data[1] = (address >> 16) & 0xFF;
        tx_data[2] = (address >> 8) & 0xFF;
        tx_data[3] = address & 0xFF;
        memcpy(tx_data.get() + 4, data, length);
        
        result = spi*.Transmit(MEMORY_DEVICE, tx_data.get(), length + 4);
        if (result == hf_spi_err_t::SPI_SUCCESS) {
            printf("โœ๏ธ Wrote %zu bytes to address 0x%06X\n", length, address);
            return true;
        } else {
            printf("โŒ Memory write failed: %s\n", HfSpiErrToString(result));
            return false;
        }
        // tx_data automatically cleaned up when going out of scope
    }
    
    bool erase_sector(uint32_t address) {
        // Send write enable command
        uint8_t write_enable = 0x06;
        uint8_t rx_data;
        hf_spi_err_t result = spi*.TransmitReceive(MEMORY_DEVICE, &write_enable, &rx_data, 1);
        if (result != hf_spi_err_t::SPI_SUCCESS) {
            printf("โŒ Write enable failed\n");
            return false;
        }
        
        // Send sector erase command
        uint8_t erase_cmd[] = {0x20, (address >> 16) & 0xFF, (address >> 8) & 0xFF, address & 0xFF};
        result = spi*.Transmit(MEMORY_DEVICE, erase_cmd, 4);
        if (result == hf_spi_err_t::SPI_SUCCESS) {
            printf("๐Ÿ—‘๏ธ Erased sector at address 0x%06X\n", address);
            return true;
        } else {
            printf("โŒ Sector erase failed: %s\n", HfSpiErrToString(result));
            return false;
        }
    }
    
    uint32_t read_device_id() {
        uint8_t tx_cmd[] = {0x90, 0x00, 0x00, 0x00};  // Read ID command
        uint8_t rx_data[4];
        
        hf_spi_err_t result = spi*.TransmitReceive(MEMORY_DEVICE, tx_cmd, rx_data, 4);
        if (result == hf_spi_err_t::SPI_SUCCESS) {
            uint32_t device_id = (rx_data[1] << 16) | (rx_data[2] << 8) | rx_data[3];
            printf("๐Ÿ†” Device ID: 0x%06X\n", device_id);
            return device_id;
        } else {
            printf("โŒ Device ID read failed: %s\n", HfSpiErrToString(result));
            return 0;
        }
    }
};

๐Ÿงช Best Practices

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// โœ… Always check initialization
if (!spi.EnsureInitialized()) {
    printf("โŒ SPI initialization failed\n");
    return false;
}

// โœ… Validate device configuration
hf_spi_capabilities_t caps;
if (spi.GetCapabilities(caps) == hf_spi_err_t::SPI_SUCCESS) {
    if (device_id >= caps.max_devices) {
        printf("โŒ Device %u exceeds maximum (%u)\n", device_id, caps.max_devices);
        return;
    }
}

// โœ… Use appropriate frequency for your application
// Sensors: 1-10 MHz
// Displays: 10-40 MHz
// Memory: 20-80 MHz

// โœ… Handle transfer errors gracefully
hf_spi_err_t result = spi.TransmitReceive(device_id, tx_data, rx_data, length);
if (result != hf_spi_err_t::SPI_SUCCESS) {
    printf("โš ๏ธ SPI Error: %s\n", HfSpiErrToString(result));
    // Implement retry logic or error recovery
}

// โœ… Use DMA for large transfers
config.use_dma = (length > 32);  // Use DMA for transfers > 32 bytes

// โœ… Check device status before operations
hf_spi_device_status_t status;
if (spi.GetDeviceStatus(device_id, status) == hf_spi_err_t::SPI_SUCCESS) {
    if (status.is_busy) {
        printf("โณ Device %u is busy\n", device_id);
        return;
    }
}

โŒ Common Pitfalls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// โŒ Don't ignore initialization
spi.TransmitReceive(0, tx_data, rx_data, length);  // May fail silently

// โŒ Don't use invalid frequencies
spi.ConfigureDevice(0, {mode: MODE_0, frequency_hz: 100000000});  // Too high

// โŒ Don't use invalid device numbers
spi.ConfigureDevice(99, config);  // Invalid device

// โŒ Don't ignore transfer timeouts
// Large transfers may timeout - check return values

// โŒ Don't assume all modes are supported
// Check capabilities before using specific modes

// โŒ Don't forget to handle CS pin manually when needed
// Some devices require manual CS control

๐ŸŽฏ Performance Optimization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ๐Ÿš€ Use appropriate frequency for your application
// Higher frequency = faster transfers but may cause errors

// ๐Ÿš€ Use DMA for large transfers
// DMA reduces CPU overhead for transfers > 32 bytes

// ๐Ÿš€ Minimize CS toggling
// Keep CS low for multiple transfers to the same device

// ๐Ÿš€ Use appropriate data size
// 8-bit: Most common, good compatibility
// 16-bit: Faster for 16-bit data
// 32-bit: Fastest for 32-bit data

// ๐Ÿš€ Batch operations when possible
// Configure all devices before starting transfers

// ๐Ÿš€ Use appropriate timeout values
// Short timeouts for fast devices
// Longer timeouts for slow devices

๐Ÿ”— Navigation

Documentation Structure