Created
October 27, 2025 17:53
-
-
Save Staars/1e4da8472178b37429d266c8b655f0bd to your computer and use it in GitHub Desktop.
save for later
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef _UDISPLAY_SPI_CONTROLLER_H_ | |
| #define _UDISPLAY_SPI_CONTROLLER_H_ | |
| #include <Arduino.h> | |
| #include <SPI.h> | |
| #ifdef ESP32 | |
| #include "soc/spi_reg.h" | |
| #include "soc/spi_struct.h" | |
| #include "esp32-hal-spi.h" | |
| #include "driver/spi_master.h" | |
| #include "soc/gpio_periph.h" | |
| #endif | |
| #ifndef ESP32 | |
| #include "spi_register.h" | |
| #endif | |
| struct SPIControllerConfig { | |
| uint8_t bus_nr; | |
| int8_t cs; | |
| int8_t clk; | |
| int8_t mosi; | |
| int8_t dc; | |
| int8_t miso; | |
| uint32_t speed; | |
| }; | |
| /** | |
| * Minimal SPIController - wraps low-level SPI functions | |
| * Extracted from uDisplay_spi.cpp | |
| */ | |
| class SPIController { | |
| public: | |
| SPIController(const SPIControllerConfig& config); | |
| ~SPIController() = default; | |
| // ===== Pin Control ===== | |
| void csLow(); | |
| void csHigh(); | |
| void dcLow(); | |
| void dcHigh(); | |
| // ===== Transaction Control ===== | |
| void beginTransaction(); | |
| void endTransaction(); | |
| // ===== High-Level Write Functions ===== | |
| void writeCommand(uint8_t cmd); | |
| void writeData8(uint8_t data); | |
| void writeData16(uint16_t data); | |
| void writeData32(uint32_t data); | |
| // ===== RA8876 Specific ===== | |
| uint8_t writeReg16(uint8_t reg, uint16_t wval); | |
| uint8_t readData(void); | |
| uint8_t readStatus(void); | |
| // ===== Direct Access ===== | |
| SPIClass* getSPI() { return spi; } | |
| // SPISettings getSPISettings() { return spi_settings; } | |
| // ===== DMA ===== | |
| #ifdef ESP32 | |
| bool initDMA(uint16_t width, uint16_t flushlines, uint8_t data); | |
| // void dmaPreCallback(spi_transaction_t *t); | |
| void dmaWait(void); | |
| void pushPixelsDMA(uint16_t* image, uint32_t len); | |
| void pushPixels3DMA(uint8_t* image, uint32_t len); | |
| #endif | |
| SPIControllerConfig spi_config; // make this private in the future again! | |
| private: | |
| SPIClass* spi; | |
| SPISettings spi_settings; | |
| // ===== Low-Level Write Functions ===== | |
| void write8(uint8_t val); | |
| void write8_slow(uint8_t val); | |
| void write9(uint8_t val, uint8_t dc); | |
| void write9_slow(uint8_t val, uint8_t dc); | |
| void write16(uint16_t val); | |
| void write32(uint32_t val); | |
| void hw_write9(uint8_t val, uint8_t dc); | |
| #ifdef ESP32 | |
| bool dma_enabled = false; | |
| bool async_dma_enabled = false; | |
| spi_host_device_t spi_host = VSPI_HOST; | |
| bool DMA_Enabled = false; | |
| uint8_t spiBusyCheck; | |
| spi_device_handle_t dmaHAL = nullptr; // For DMA | |
| // Transaction pool - must persist until get_trans_result | |
| // 6 transactions per dirty region (2 cmds + 2 data + 1 cmd + 1 pixels) | |
| static constexpr int MAX_QUEUED_TRANS = 8; | |
| spi_transaction_t trans_pool[MAX_QUEUED_TRANS]; | |
| int trans_pool_idx = 0; | |
| #endif //ESP32 | |
| }; | |
| #endif // _UDISPLAY_SPI_CONTROLLER_H_ | |
| #include "uDisplay_SPI_controller.h" | |
| // ===== GPIO Macros ===== | |
| #ifdef ESP8266 | |
| #define PIN_OUT_SET 0x60000304 | |
| #define PIN_OUT_CLEAR 0x60000308 | |
| #define GPIO_SET(A) WRITE_PERI_REG(PIN_OUT_SET, 1 << A) | |
| #define GPIO_CLR(A) WRITE_PERI_REG(PIN_OUT_CLEAR, 1 << A) | |
| #define GPIO_SET_SLOW(A) digitalWrite(A, HIGH) | |
| #define GPIO_CLR_SLOW(A) digitalWrite(A, LOW) | |
| #else // ESP32 | |
| #if CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32P4 | |
| #define GPIO_CLR(A) GPIO.out_w1tc.val = (1 << A) | |
| #define GPIO_SET(A) GPIO.out_w1ts.val = (1 << A) | |
| #else // plain ESP32 or S3 | |
| #define GPIO_CLR(A) GPIO.out_w1tc = (1 << A) | |
| #define GPIO_SET(A) GPIO.out_w1ts = (1 << A) | |
| #endif | |
| #define GPIO_SET_SLOW(A) digitalWrite(A, HIGH) | |
| #define GPIO_CLR_SLOW(A) digitalWrite(A, LOW) | |
| #endif | |
| // ===== RA8876 Constants ===== | |
| static constexpr uint8_t RA8876_DATA_WRITE = 0x80; | |
| static constexpr uint8_t RA8876_DATA_READ = 0xC0; | |
| static constexpr uint8_t RA8876_CMD_WRITE = 0x00; | |
| static constexpr uint8_t RA8876_STATUS_READ = 0x40; | |
| extern void AddLog(uint32_t loglevel, const char* formatP, ...); | |
| SPIController::SPIController(const SPIControllerConfig& config) | |
| : spi_config(config) | |
| { | |
| if (spi_config.dc >= 0) { | |
| pinMode(spi_config.dc, OUTPUT); | |
| digitalWrite(spi_config.dc, HIGH); | |
| } | |
| if (spi_config.cs >= 0) { | |
| pinMode(spi_config.cs, OUTPUT); | |
| digitalWrite(spi_config.cs, HIGH); | |
| } | |
| #ifdef ESP8266 | |
| if (spi_config.bus_nr <= 1) { | |
| SPI.begin(); | |
| spi = &SPI; | |
| } else { | |
| pinMode(spi_config.clk, OUTPUT); | |
| digitalWrite(spi_config.clk, LOW); | |
| pinMode(spi_config.mosi, OUTPUT); | |
| digitalWrite(spi_config.mosi, LOW); | |
| if (spi_config.miso >= 0) { | |
| pinMode(spi_config.miso, INPUT_PULLUP); | |
| } | |
| } | |
| #endif // ESP8266 | |
| #ifdef ESP32 | |
| if (spi_config.bus_nr == 1) { | |
| spi = &SPI; | |
| spi->begin(spi_config.clk, spi_config.miso, spi_config.mosi, -1); | |
| } else if (spi_config.bus_nr == 2) { | |
| spi = new SPIClass(HSPI); | |
| spi->begin(spi_config.clk, spi_config.miso, spi_config.mosi, -1); | |
| } else { | |
| pinMode(spi_config.clk, OUTPUT); | |
| digitalWrite(spi_config.clk, LOW); | |
| pinMode(spi_config.mosi, OUTPUT); | |
| digitalWrite(spi_config.mosi, LOW); | |
| if (spi_config.miso >= 0) { | |
| pinMode(spi_config.miso, INPUT_PULLUP); | |
| } | |
| } | |
| #endif // ESP32 | |
| spi_settings = SPISettings((uint32_t)spi_config.speed*1000000, MSBFIRST, SPI_MODE3); | |
| } | |
| // ===== Pin Control ===== | |
| void SPIController::csLow() { | |
| #ifdef ESP32 | |
| // In DMA mode, CS is controlled by ESP-IDF SPI driver | |
| if (DMA_Enabled) return; | |
| #endif | |
| if (spi_config.cs >= 0) GPIO_CLR_SLOW(spi_config.cs); | |
| } | |
| void SPIController::csHigh() { | |
| #ifdef ESP32 | |
| // In DMA mode, CS is controlled by ESP-IDF SPI driver | |
| if (DMA_Enabled) return; | |
| #endif | |
| if (spi_config.cs >= 0) GPIO_SET_SLOW(spi_config.cs); | |
| } | |
| void SPIController::dcLow() { | |
| #ifdef ESP32 | |
| // In DMA mode, D/C is controlled by pre-transfer callback | |
| if (DMA_Enabled) return; | |
| #endif | |
| if (spi_config.dc >= 0) GPIO_CLR_SLOW(spi_config.dc); | |
| } | |
| void SPIController::dcHigh() { | |
| #ifdef ESP32 | |
| // In DMA mode, D/C is controlled by pre-transfer callback | |
| if (DMA_Enabled) return; | |
| #endif | |
| if (spi_config.dc >= 0) GPIO_SET_SLOW(spi_config.dc); | |
| } | |
| // ===== Transaction Control ===== | |
| void SPIController::beginTransaction() { | |
| #ifdef ESP32 | |
| if (DMA_Enabled) { | |
| // Wait for any previous transactions to complete | |
| dmaWait(); | |
| // Reset pool index for new transaction sequence | |
| trans_pool_idx = 0; | |
| // Acquire bus for DMA transaction sequence | |
| // spi_device_acquire_bus(dmaHAL, portMAX_DELAY); | |
| return; | |
| } | |
| #endif | |
| if (spi_config.bus_nr <= 2) spi->beginTransaction(spi_settings); | |
| } | |
| void SPIController::endTransaction() { | |
| #ifdef ESP32 | |
| if (DMA_Enabled) { | |
| // Wait for all queued transactions to complete | |
| // dmaWait(); | |
| // Release bus | |
| // spi_device_release_bus(dmaHAL); | |
| return; | |
| } | |
| #endif | |
| if (spi_config.bus_nr <= 2) spi->endTransaction(); | |
| } | |
| // ===== Low-Level Write Functions ===== | |
| void SPIController::writeCommand(uint8_t cmd) { | |
| #ifdef ESP32 | |
| if (DMA_Enabled && spi_config.dc >= 0) { | |
| // Get transaction from pool | |
| spi_transaction_t *t = &trans_pool[trans_pool_idx++ % MAX_QUEUED_TRANS]; | |
| memset(t, 0, sizeof(*t)); | |
| t->length = 8; // 8 bits | |
| t->tx_data[0] = cmd; | |
| t->user = (void*)0; // D/C = LOW for command | |
| t->flags = SPI_TRANS_USE_TXDATA; | |
| AddLog(3, PSTR("DMA: Queue CMD 0x%02X"), cmd); | |
| esp_err_t ret = spi_device_queue_trans(dmaHAL, t, portMAX_DELAY); | |
| assert(ret == ESP_OK); | |
| spiBusyCheck++; | |
| dmaWait(); | |
| return; | |
| } | |
| #endif | |
| if (spi_config.dc < 0) { | |
| // 9-bit mode | |
| if (spi_config.bus_nr > 2) { | |
| if (spi_config.bus_nr == 3) write9(cmd, 0); | |
| else write9_slow(cmd, 0); | |
| } else { | |
| hw_write9(cmd, 0); | |
| } | |
| } else { | |
| // 8-bit mode | |
| dcLow(); | |
| writeData8(cmd); | |
| dcHigh(); | |
| } | |
| } | |
| void SPIController::writeData8(uint8_t data) { | |
| if (spi_config.dc < 0) { | |
| // 9-bit mode | |
| if (spi_config.bus_nr > 2) { | |
| if (spi_config.bus_nr == 3) write9(data, 1); | |
| else write9_slow(data, 1); | |
| } else { | |
| hw_write9(data, 1); | |
| } | |
| } else { | |
| // 8-bit mode | |
| if (spi_config.bus_nr > 2) { | |
| if (spi_config.bus_nr == 3) write8(data); | |
| else write8_slow(data); | |
| } else { | |
| spi->write(data); | |
| } | |
| } | |
| } | |
| void SPIController::writeData16(uint16_t data) { | |
| if (spi_config.dc < 0) { | |
| // 9-bit: break into bytes | |
| writeData8(data >> 8); | |
| writeData8(data); | |
| } else { | |
| // 8-bit mode | |
| if (spi_config.bus_nr > 2) { | |
| if (spi_config.bus_nr == 3) write16(data); | |
| else { | |
| // Slow mode: break into bytes | |
| writeData8(data >> 8); | |
| writeData8(data); | |
| } | |
| } else { | |
| spi->write16(data); // Assume SPI has write16 | |
| } | |
| } | |
| } | |
| void SPIController::writeData32(uint32_t data) { | |
| #ifdef ESP32 | |
| if (DMA_Enabled && spi_config.dc >= 0) { | |
| // Get transaction from pool | |
| spi_transaction_t *t = &trans_pool[trans_pool_idx++ % MAX_QUEUED_TRANS]; | |
| memset(t, 0, sizeof(*t)); | |
| t->length = 32; // 32 bits | |
| t->tx_data[0] = (data >> 24) & 0xFF; | |
| t->tx_data[1] = (data >> 16) & 0xFF; | |
| t->tx_data[2] = (data >> 8) & 0xFF; | |
| t->tx_data[3] = data & 0xFF; | |
| t->user = (void*)1; // D/C = HIGH for data | |
| t->flags = SPI_TRANS_USE_TXDATA; | |
| AddLog(3, PSTR("DMA: Queue DATA32 0x%08X -> [%02X %02X %02X %02X]"), | |
| data, t->tx_data[0], t->tx_data[1], t->tx_data[2], t->tx_data[3]); | |
| esp_err_t ret = spi_device_queue_trans(dmaHAL, t, portMAX_DELAY); | |
| assert(ret == ESP_OK); | |
| spiBusyCheck++; | |
| dmaWait(); | |
| return; | |
| } | |
| #endif | |
| if (spi_config.dc < 0) { | |
| // 9-bit mode: break into bytes | |
| writeData8(data >> 24); | |
| writeData8(data >> 16); | |
| writeData8(data >> 8); | |
| writeData8(data); | |
| } else { | |
| // 8-bit mode | |
| if (spi_config.bus_nr > 2) { | |
| if (spi_config.bus_nr == 3) { | |
| write32(data); // Fast bit-banging | |
| } else { | |
| // Slow mode: break into bytes | |
| writeData8(data >> 24); | |
| writeData8(data >> 16); | |
| writeData8(data >> 8); | |
| writeData8(data); | |
| } | |
| } else { | |
| // Hardware SPI | |
| spi->write32(data); // Assume SPI has write32 on ESP32 | |
| } | |
| } | |
| } | |
| // ===== Low-Level Write Functions ===== | |
| void SPIController::write8(uint8_t val) { | |
| for (uint8_t bit = 0x80; bit; bit >>= 1) { | |
| GPIO_CLR(spi_config.clk); | |
| if (val & bit) GPIO_SET(spi_config.mosi); | |
| else GPIO_CLR(spi_config.mosi); | |
| GPIO_SET(spi_config.clk); | |
| } | |
| } | |
| void SPIController::write8_slow(uint8_t val) { | |
| for (uint8_t bit = 0x80; bit; bit >>= 1) { | |
| GPIO_CLR_SLOW(spi_config.clk); | |
| if (val & bit) GPIO_SET_SLOW(spi_config.mosi); | |
| else GPIO_CLR_SLOW(spi_config.mosi); | |
| GPIO_SET_SLOW(spi_config.clk); | |
| } | |
| } | |
| void SPIController::write9(uint8_t val, uint8_t dc) { | |
| GPIO_CLR(spi_config.clk); | |
| if (dc) GPIO_SET(spi_config.mosi); | |
| else GPIO_CLR(spi_config.mosi); | |
| GPIO_SET(spi_config.clk); | |
| for (uint8_t bit = 0x80; bit; bit >>= 1) { | |
| GPIO_CLR(spi_config.clk); | |
| if (val & bit) GPIO_SET(spi_config.mosi); | |
| else GPIO_CLR(spi_config.mosi); | |
| GPIO_SET(spi_config.clk); | |
| } | |
| } | |
| void SPIController::write9_slow(uint8_t val, uint8_t dc) { | |
| GPIO_CLR_SLOW(spi_config.clk); | |
| if (dc) GPIO_SET_SLOW(spi_config.mosi); | |
| else GPIO_CLR_SLOW(spi_config.mosi); | |
| GPIO_SET_SLOW(spi_config.clk); | |
| for (uint8_t bit = 0x80; bit; bit >>= 1) { | |
| GPIO_CLR_SLOW(spi_config.clk); | |
| if (val & bit) GPIO_SET_SLOW(spi_config.mosi); | |
| else GPIO_CLR_SLOW(spi_config.mosi); | |
| GPIO_SET_SLOW(spi_config.clk); | |
| } | |
| } | |
| void SPIController::write16(uint16_t val) { | |
| for (uint16_t bit = 0x8000; bit; bit >>= 1) { | |
| GPIO_CLR(spi_config.clk); | |
| if (val & bit) GPIO_SET(spi_config.mosi); | |
| else GPIO_CLR(spi_config.mosi); | |
| GPIO_SET(spi_config.clk); | |
| } | |
| } | |
| void SPIController::write32(uint32_t val) { | |
| for (uint32_t bit = 0x80000000; bit; bit >>= 1) { | |
| GPIO_CLR(spi_config.clk); | |
| if (val & bit) GPIO_SET(spi_config.mosi); | |
| else GPIO_CLR(spi_config.mosi); | |
| GPIO_SET(spi_config.clk); | |
| } | |
| } | |
| // ===== Hardware 9-bit Mode ===== | |
| #ifdef ESP32 | |
| void SPIController::hw_write9(uint8_t val, uint8_t dc) { | |
| if (spi_config.dc < -1) { | |
| // RA8876 mode | |
| if (!dc) { | |
| spi->write(RA8876_CMD_WRITE); | |
| spi->write(val); | |
| } else { | |
| spi->write(RA8876_DATA_WRITE); | |
| spi->write(val); | |
| } | |
| } else { | |
| uint32_t regvalue = val >> 1; | |
| if (dc) regvalue |= 0x80; | |
| else regvalue &= 0x7f; | |
| if (val & 1) regvalue |= 0x8000; | |
| REG_SET_BIT(SPI_USER_REG(3), SPI_USR_MOSI); | |
| REG_WRITE(SPI_MOSI_DLEN_REG(3), 9 - 1); | |
| uint32_t *dp = (uint32_t*)SPI_W0_REG(3); | |
| *dp = regvalue; | |
| REG_SET_BIT(SPI_CMD_REG(3), SPI_USR); | |
| while (REG_GET_FIELD(SPI_CMD_REG(3), SPI_USR)); | |
| } | |
| } | |
| #else | |
| void SPIController::hw_write9(uint8_t val, uint8_t dc) { | |
| if (spi_config.dc < -1) { | |
| // RA8876 mode | |
| if (!dc) { | |
| spi->write(RA8876_CMD_WRITE); | |
| spi->write(val); | |
| } else { | |
| spi->write(RA8876_DATA_WRITE); | |
| spi->write(val); | |
| } | |
| } else { | |
| uint32_t regvalue; | |
| uint8_t bytetemp; | |
| if (!dc) { | |
| bytetemp = (val >> 1) & 0x7f; | |
| } else { | |
| bytetemp = (val >> 1) | 0x80; | |
| } | |
| regvalue = ((8 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) | ((uint32)bytetemp); | |
| if (val & 0x01) regvalue |= BIT15; | |
| while (READ_PERI_REG(SPI_CMD(1)) & SPI_USR); | |
| WRITE_PERI_REG(SPI_USER2(1), regvalue); | |
| SET_PERI_REG_MASK(SPI_CMD(1), SPI_USR); | |
| } | |
| } | |
| #endif | |
| // DMA | |
| #ifdef ESP32 | |
| static gpio_num_t pin_num_dc; | |
| static void dmaPreCallback(spi_transaction_t *t) | |
| { | |
| int dc = (int)t->user; | |
| gpio_set_level(pin_num_dc, dc); | |
| } | |
| bool SPIController::initDMA(uint16_t width, uint16_t flushlines, uint8_t data) { | |
| AddLog(3,"init dma %u %u %d",flushlines,data, spi_config.cs); | |
| if (!spi && spi_config.cs == -1) return false; | |
| if((data&1) == 0){ | |
| AddLog(3,"no dma selected"); | |
| return false; | |
| } | |
| if (spi_config.bus_nr == 1){ | |
| AddLog(3,"dma spi 1"); | |
| } else if (spi_config.bus_nr == 2){ | |
| AddLog(3,"dma spi 2"); | |
| spi_host = HSPI_HOST; | |
| } else { | |
| return false; | |
| } | |
| spi->end(); | |
| pin_num_dc = (gpio_num_t)spi_config.dc; | |
| esp_err_t ret; | |
| spi_bus_config_t buscfg = { | |
| .mosi_io_num = spi_config.mosi, | |
| .miso_io_num = -1, | |
| .sclk_io_num = spi_config.clk, | |
| .quadwp_io_num = -1, | |
| .quadhd_io_num = -1, | |
| .max_transfer_sz = width * flushlines * 2 + 8, | |
| .flags = 0, | |
| .intr_flags = 0 | |
| }; | |
| spi_device_interface_config_t devcfg = { | |
| .command_bits = 0, | |
| .address_bits = 0, | |
| .dummy_bits = 0, | |
| .mode = 3, | |
| .duty_cycle_pos = 0, | |
| .cs_ena_pretrans = 0, | |
| .cs_ena_posttrans = 0, | |
| .clock_speed_hz = (int)spi_config.speed * 1000000, // Convert MHz to Hz | |
| .input_delay_ns = 0, | |
| .spics_io_num = spi_config.cs, | |
| .flags = SPI_DEVICE_NO_DUMMY, | |
| .queue_size = 16, | |
| .pre_cb = dmaPreCallback, | |
| .post_cb = 0 | |
| }; | |
| ret = spi_bus_initialize(spi_host, &buscfg, SPI_DMA_CH_AUTO); | |
| if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) { | |
| AddLog(3,"init dma bus init failed: %d", ret); | |
| return false; | |
| } | |
| if (ret == ESP_ERR_INVALID_STATE) { | |
| AddLog(3,"init dma bus already initialized (OK)"); | |
| } | |
| ret = spi_bus_add_device(spi_host, &devcfg, &dmaHAL); | |
| if (ret == ESP_OK) { | |
| DMA_Enabled = true; | |
| async_dma_enabled = ((data&4) != 0); | |
| dma_enabled = true; | |
| spiBusyCheck = 0; | |
| spi_device_acquire_bus(dmaHAL, portMAX_DELAY); | |
| AddLog(3,"init dma succes"); | |
| return true; | |
| } | |
| return false; | |
| } | |
| // just a placeholder | |
| // void SPIController::deInitDMA(void) { | |
| // if (!DMA_Enabled) return; | |
| // spi_bus_remove_device(dmaHAL); | |
| // spi_bus_free(spi_host); | |
| // DMA_Enabled = false; | |
| // } | |
| void SPIController::dmaWait(void) { | |
| if (!DMA_Enabled || !spiBusyCheck) return; | |
| spi_transaction_t *rtrans; | |
| esp_err_t ret; | |
| for (int i = 0; i < spiBusyCheck; ++i) { | |
| ret = spi_device_get_trans_result(dmaHAL, &rtrans, portMAX_DELAY); | |
| assert(ret == ESP_OK); | |
| } | |
| spiBusyCheck = 0; | |
| } | |
| void SPIController::pushPixelsDMA(uint16_t* image, uint32_t len) { | |
| if(!DMA_Enabled){ | |
| getSPI()->writePixels(image, len * 2); | |
| return; | |
| } | |
| if (len == 0) return; | |
| // Get transaction from pool | |
| spi_transaction_t *t = &trans_pool[trans_pool_idx++ % MAX_QUEUED_TRANS]; | |
| memset(t, 0, sizeof(*t)); | |
| t->user = (void *)1; // D/C = HIGH for data | |
| t->tx_buffer = image; | |
| t->length = len * 16; // Length in bits | |
| t->flags = 0; | |
| AddLog(3, PSTR("DMA: Queue PIXELS len=%d"), len); | |
| esp_err_t ret = spi_device_queue_trans(dmaHAL, t, portMAX_DELAY); | |
| assert(ret == ESP_OK); | |
| spiBusyCheck++; | |
| } | |
| void SPIController::pushPixels3DMA(uint8_t* image, uint32_t len) { | |
| if ((len == 0) || (!DMA_Enabled)) return; | |
| // Get transaction from pool | |
| spi_transaction_t *t = &trans_pool[trans_pool_idx++ % MAX_QUEUED_TRANS]; | |
| memset(t, 0, sizeof(*t)); | |
| t->user = (void *)1; // D/C = HIGH for data | |
| t->tx_buffer = image; | |
| t->length = len * 24; // Length in bits (3 bytes per pixel) | |
| t->flags = 0; | |
| esp_err_t ret = spi_device_queue_trans(dmaHAL, t, portMAX_DELAY); | |
| assert(ret == ESP_OK); | |
| spiBusyCheck++; | |
| } | |
| #endif // ESP32 | |
| // ===== RA8876 Specific ===== | |
| uint8_t SPIController::writeReg16(uint8_t reg, uint16_t wval) { | |
| hw_write9(reg, 0); | |
| hw_write9(wval, 1); | |
| hw_write9(reg + 1, 0); | |
| hw_write9(wval >> 8, 1); | |
| return 0; | |
| } | |
| uint8_t SPIController::readData(void) { | |
| if (!spi) return 0; | |
| spi->write(RA8876_DATA_READ); | |
| return spi->transfer(0); | |
| } | |
| uint8_t SPIController::readStatus(void) { | |
| if (!spi) return 0; | |
| spi->write(RA8876_STATUS_READ); | |
| return spi->transfer(0); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment