Search code examples
c++esp32spilcd

How To Configure SPI 3 Line Half Duplex Master Mode Without using Premade ESP Libraries?


I'm working on building out some custom drivers for the ESP-Wroom-32.

After trying to use the pre-made arduino and esp32 driver code without success, I've decided to just build the libraries from scratch.

I'm trying to control a Gc9a01 LCD screen, which I've been able to successfully drive using an STM32 processor.

For the life of me, I cannot identify what's broken/not working.

I know for certain that I'm accessing the correct memory addresses, as I can validate the default register values.

I'm fairly certain that I've configured the SPI interface incorrectly; I'm not getting any response out of my LCD screen, which I know works.

Below is my source code for my SPI driver and link to the technical manual, I know that basically nobody knows how this works; but if any gurus out there can provide some insight to my process, specifically some insight into the 3-line half duplex master mode initlaization function and the spi send function, I'd be able to move forward.

esp32_reference_manual

SPI Code

/*
 *  Structs contining register bit positions
 * */
struct spi_user_reg{
    const uint32_t address = BT_ESP32_SPI_USER_REG;
    const uint32_t spi_doutdin = 0;
    const uint32_t spi_cs_hold = 4;
    const uint32_t spi_cs_setup = 5;
    const uint32_t spi_clk_i_edge = 6;
    const uint32_t spi_clk_out_edge = 7;
    const uint32_t spi_rd_byte_order = 10;
    const uint32_t spi_wr_byte_order = 11;
    const uint32_t spi_fwrite_duel = 12;
    const uint32_t spi_fwrite_quad = 13;
    const uint32_t spi_fwrite_dio = 14;
    const uint32_t spi_fwrite_qio = 15;
    const uint32_t spi_sio = 16;
    const uint32_t spi_usr_miso_highpart = 24;
    const uint32_t spi_usr_mosi_highpart = 25;
    const uint32_t spi_usr_dummy_idle = 26;
    const uint32_t spi_usr_mosi= 27;
    const uint32_t spi_usr_miso = 28;
    const uint32_t spi_usr_dummy = 29;
    const uint32_t spi_usr_addr = 30;
    const uint32_t spi_usr_command = 31;
};

struct spi_slv_rdbuf_dlen_reg{
    const uint32_t address = BT_ESP32_SPI_SLV_RDBUF_DLEN_REG;
    const uint32_t spi_slv_rdbuf_dbitlen_low = 0;
    const uint32_t spi_slv_rdbuf_dbitlen_high = 23;
};

struct spi_slv_wrbuf_dlen_reg{
        const uint32_t address = BT_ESP32_SPI_SLV_WRBUF_DLEN_REG;
        const uint32_t spi_slv_wrbuf_dbitlen_low = 0;
    const uint32_t spi_slv_wrbuf_dbitlen_high = 23;
};

struct spi_miso_dlen_reg{
    const uint32_t address = BT_ESP32_SPI_MISO_DLEN_REG;
    const uint32_t spi_usr_miso_dbitlen_low = 0;
    const uint32_t spi_usr_miso_dbitlen_high = 23;
};

struct spi_mosi_dlen_reg{
    const uint32_t address = BT_ESP32_SPI_MOSI_DLEN_REG;
    const uint32_t spi_usr_mosi_dbitlen_start = 0;
    const uint32_t spi_usr_mosi_dbitlen_end = 23;
};

struct spi_addr_reg{
    const uint32_t address = BT_ESP32_SPI_ADDR_REG;
};

struct spi_slave1_reg{
    const uint32_t address = BT_ESP32_SPI_SLAVE1_REG;
    const uint32_t spi_slv_rdbuf_dummy_en = 0;
    const uint32_t spi_slv_wrbuf_dummy_en = 1;
    const uint32_t spi_slv_rdsta_dummy_en = 2;
    const uint32_t spi_slv_wrsta_dummy_en = 3;
    const uint32_t spi_slv_wr_addr_bitlen_stat = 4;
    const uint32_t spi_slv_wr_addr_bitlen_end = 9;
    const uint32_t spi_slv_rd_addr_bitlen_start = 10;
    const uint32_t spi_slv_rd_addr_bitlen_end = 15;
    const uint32_t spi_slv_status_readback = 25;
    const uint32_t spi_slv_status_fast_en = 26;
    const uint32_t spi_slv_status_bitlen_start = 27;
    const uint32_t spi_slv_status_bitlen_end = 31;
};

struct spi_clock_reg{
    const uint32_t address = BT_ESP32_SPI_CLOCK_REG;
    const uint32_t spi_clkcnt_l_start = 0;
    const uint32_t spi_clkcnt_l_end = 5;
    const uint32_t spi_clkcnt_h_start = 6;
    const uint32_t spi_clkcnt_h_end = 11;
    const uint32_t spi_clkcnt_n_start = 12;
    const uint32_t spi_clkcnt_n_end = 17;
    const uint32_t spi_clkdiv_pre_start = 18;
    const uint32_t spi_clkdiv_pre_end = 30;
    const uint32_t spi_clk_equ_sysclk = 31;
};

struct spi_pin_reg{
    const uint32_t address = BT_ESP32_SPI_PIN_REG;
    const uint32_t spi_cs0_dis = 0;
    const uint32_t spi_cs1_dis = 1;
    const uint32_t spi_cs2_dis = 2;
    const uint32_t spi_ck_dis = 3;
    const uint32_t spi_master_cs_pol_start = 11;
    const uint32_t spi_master_cs_pol_end = 13;
    const uint32_t spi_ck_idle_edge = 29;
    const uint32_t spi_cs_keep_active = 30;
};

struct spi_ctrl2_reg{
    const uint32_t address = BT_ESP32_SPI_CTRL2_REG;
    const uint32_t spi_setup_time_start = 0;
    const uint32_t spi_setup_time_end = 3;
    const uint32_t spi_hold_time_start = 4;
    const uint32_t spi_hold_time_end = 7;
    const uint32_t spi_ck_out_high_mode_start = 12;
    const uint32_t spi_ck_out_high_mode_end = 15;
    const uint32_t spi_miso_delay_mode_start = 16;
    const uint32_t spi_miso_delay_mode_end = 17;
    const uint32_t spi_miso_delay_num_start = 18;
    const uint32_t spi_miso_delay_num_end = 20;
    const uint32_t spi_mosi_delay_mode_start = 21;
    const uint32_t spi_mosi_delay_mode_end = 22;
    const uint32_t spi_mosi_delay_num_start = 23;
    const uint32_t spi_mosi_delay_num_end = 25;
    const uint32_t spi_cs_delay_mod_start = 26;
    const uint32_t spi_cs_delay_mod_end = 27;
    const uint32_t spi_cs_delay_num_start = 28;
    const uint32_t spi_cs_delay_num_end = 31;
};

struct spi_cmd_reg{
    const uint32_t address = BT_ESP32_SPI_CMD_REG;
    const uint32_t spi_usr = 18;
};

struct spi_user2_reg{
    const uint32_t address = BT_ESP32_SPI_USER2_REG;
    const uint32_t spi_usr_command_value_start = 0;
    const uint32_t spi_usr_command_value_end = 15;
    const uint32_t spi_usr_command_bitlen_start = 28;
    const uint32_t spi_usr_command_bitlen_end = 31;
};

struct spi_ctrl_reg{
    const uint32_t address = BT_ESP32_SPI_CTRL_REG;
    const uint32_t spi_fastrd_mode = 13;
    const uint32_t spi_fread_dual = 14;
    const uint32_t spi_fread_quad = 20;
    const uint32_t spi_wp = 21;
    const uint32_t spi_fread_dio = 23;
    const uint32_t spi_fread_qio = 24;
    const uint32_t spi_rd_bit_order = 25;
    const uint32_t spi_wr_bit_order = 26;
};

struct spi_slave_reg{
    uint32_t address = BT_ESP32_SPI_SLAVE_REG;
    uint32_t spi_slv_rd_buf_done = 0;
    uint32_t spi_slv_wr_buf_done = 1;
    uint32_t spi_slv_rd_sta_done = 2;
    uint32_t spi_slv_wr_sta_done = 3;
    uint32_t spi_trans_done = 4;
    uint32_t spi_slv_rd_buf_inten = 5;
    uint32_t spi_slv_wr_buf_inten = 6;
    uint32_t spi_slv_rd_sta_inten = 7;
    uint32_t spi_slv_wr_sta_inten = 8;
    uint32_t spi_trans_inten = 9;
        uint32_t spi_cs_i_mode_start = 10;
    uint32_t spi_cs_i_mode_end = 11;
        uint32_t spi_slv_last_command_start = 17;
    uint32_t spi_slv_last_command_end = 19;
        uint32_t spi_slv_last_state_start = 20;
    uint32_t spi_slv_last_state_end = 22;
    uint32_t spi_trans_cnt_start = 23;
    uint32_t spi_trans_cnt_end = 26; 
    uint32_t spi_slv_cmd_defome = 27;
    uint32_t spi_slv_wr_rd_sta_en = 28;
    uint32_t spi_slv_wr_rd_buf_en = 29;
    uint32_t spi_slave_mode = 30;
    uint32_t spi_sync_reset = 31;
};

struct spi_ext2_reg{
    uint32_t address = BT_ESP32_SPI_EXT2_REG;
    uint32_t spi_st_start = 0;
    uint32_t spi_st_end = 2;
};

struct spi_dma_conf_reg{
    uint32_t address = BT_ESP32_SPI_DMA_CONF_REG;
    uint32_t spi_in_rst = 2;
    uint32_t spi_out_rst = 3;
    uint32_t spi_ahbm_fifo_rst = 4;
    uint32_t spi_ahbm_rst = 5;
    uint32_t spi_out_eof_mode = 9;
    uint32_t spi_outdscr_burst_en = 10;
    uint32_t spi_indscr_burst_en = 11;
    uint32_t spi_out_data_burst_en = 12;
    uint32_t spi_dma_rx_stop = 14;
    uint32_t spi_dma_tx_stop = 15;
    uint32_t spi_dma_continue = 16;
};

struct spi_dma_out_link_reg{
    uint32_t address = BT_ESP32_SPI_DMA_OUT_LINK_REG;
    uint32_t spi_outlink_addr_start = 0;
    uint32_t spi_outlink_addr_end = 19;
    uint32_t spi_outlink_stop = 28;
    uint32_t spi_outlink_start = 29;
    uint32_t spi_outlink_restart = 30;

};

struct spi_dma_in_link_reg{
    uint32_t address = BT_ESP32_SPI_DMA_IN_LINK_REG;
    uint32_t spi_inlink_addr_start = 0;
        uint32_t spi_inlink_addr_end = 19;
    uint32_t spi_inlink_auto_ret = 20;
        uint32_t spi_inlink_stop = 28;
        uint32_t spi_inlink_start = 29;
        uint32_t spi_inlink_restart = 30;
};

struct spi_dma_status_reg{
    uint32_t address = BT_ESP32_SPI_DMA_STATUS_REG;
    uint32_t spi_dma_rx_en = 0;
    uint32_t spi_dma_tx_en = 1;
};

class Esp32Spi;

class Esp32Spi : public BtEsp32Register{
private:
    int mosi = -1;
    int miso = -1;
    int clk = -1;
    int cs = -1;
    uint32_t SPIx = BT_ESP32_PERF_SPI3;
    bool GPSPI = false;
    bool QSQPI = false;
    bool threeLineMaster = false;

    int bitcount = 0;

    struct spi_user_reg spiUserReg;
    struct spi_slave1_reg spiSlave1Reg;
    struct spi_addr_reg spiAddrReg;
    struct spi_mosi_dlen_reg spiMosiDlenReg;
    struct spi_miso_dlen_reg spiMisoDlenReg;
    struct spi_slv_wrbuf_dlen_reg spiSlvWebufDlenReg;
    struct spi_slv_rdbuf_dlen_reg spiSlvRdbufDlenReg;
    struct spi_clock_reg spiClockReg;
    struct spi_pin_reg spiPinReg;
    struct spi_ctrl2_reg spiCtrl2Reg;
    struct spi_cmd_reg spiCmdReg;
    struct spi_user2_reg spiUser2Reg;
    struct spi_ctrl_reg spiCtrlReg;
    struct spi_slave_reg spiSlaveReg;
    struct spi_ext2_reg spiExt2Reg;
    struct spi_dma_conf_reg spiDmaConfReg;
    struct spi_dma_out_link_reg spiDmaOutLinkReg;
    struct spi_dma_in_link_reg spiDmaInLinkReg;
    struct spi_dma_status_reg spiDmaStatusReg;

    void setClock(uint32_t ildeEdge, uint32_t outEdge, uint32_t misoDelayMode, uint32_t misoDelayNum, uint32_t mosiDelayMode, uint32_t mosiDelayNum){
        uint32_t SPI_PIN_REG = BtEsp32Register::getRegisterX(this->spiPinReg.address);
                SPI_PIN_REG = BtEsp32Register::setBit(SPI_PIN_REG, this->spiPinReg.spi_ck_idle_edge, 0, ildeEdge);
                BtEsp32Register::setRegisterX(this->spiPinReg.address, SPI_PIN_REG);

                uint32_t SPI_USER_REG = BtEsp32Register::getRegisterX(this->spiUserReg.address);
                SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_clk_out_edge, 0, outEdge);
                BtEsp32Register::setRegisterX(this->spiUserReg.address, SPI_USER_REG);

                uint32_t SPI_CTRL2_REG = BtEsp32Register::getRegisterX(this->spiCtrl2Reg.address);
                SPI_CTRL2_REG = BtEsp32Register::setBit(SPI_CTRL2_REG, this->spiCtrl2Reg.spi_miso_delay_mode_start, this->spiCtrl2Reg.spi_miso_delay_mode_end, misoDelayMode);
                SPI_CTRL2_REG = BtEsp32Register::setBit(SPI_CTRL2_REG, this->spiCtrl2Reg.spi_miso_delay_num_start, this->spiCtrl2Reg.spi_miso_delay_num_end, misoDelayNum);
                SPI_CTRL2_REG = BtEsp32Register::setBit(SPI_CTRL2_REG, this->spiCtrl2Reg.spi_mosi_delay_mode_start, this->spiCtrl2Reg.spi_mosi_delay_mode_end, mosiDelayMode);
                SPI_CTRL2_REG = BtEsp32Register::setBit(SPI_CTRL2_REG, this->spiCtrl2Reg.spi_mosi_delay_num_start, this->spiCtrl2Reg.spi_mosi_delay_num_end, mosiDelayNum);
                BtEsp32Register::setRegisterX(this->spiCtrl2Reg.address, SPI_CTRL2_REG);

    }

    /*
     * CPOL=0, CPHA=0
     * */
    void clockMode0(bool isMaster){
        if(isMaster)
            this->setClock(0, 0, 2, 0, 0, 0);
    }

    /*
     * CPOL=0 CPHA=1
     * */
    void clockMode1(bool isMaster){
        if(isMaster){
            this->setClock(0, 1, 1, 0, 0, 0);
        }
        }

    /*
     * CPOL=1 CPHA=0
     * */
    void clockMode2(bool isMaster){
        if(isMaster){
            this->setClock(1, 1, 1, 0, 0, 0);
        }
        }

    /*
     * CPOL=1 CPHA=1
     * */
    void clockMode3(bool isMaster){
        if(isMaster){
            this->setClock(1, 0, 2, 0, 0, 0);
        }
        }
public:

    void debug(void){
        uint32_t SPI_USR_REG = BtEsp32Register::getRegisterX(this->spiUserReg.address);
        printf("SPI_USR_REG : 0x%x\n", SPI_USR_REG);
    }


    Esp32Spi() : BtEsp32Register(BT_ESP32_PERF_SPI3){
        
    }

    void setSpiPins(int miso, int mosi, int clk, int cs){
        this->miso = miso;
        this->mosi = mosi;
        this->clk = clk;
        this->cs = cs;
    }

    void setSpiX(uint32_t spix){
        this->SPIx = spix;
    }

    void setTransaction(int val){
        uint32_t reg = BtEsp32Register::getRegisterX(this->spiCmdReg.address);
        reg = BtEsp32Register::setBit(reg, this->spiCmdReg.spi_usr, 0, val);
        BtEsp32Register::setRegisterX(this->spiCmdReg.address, reg);
    }

    void pushToDataBuffer(int page, uint32_t value){
        if(page < 0 || page> 15)
            page = 0;
        uint32_t reg = BtEsp32Register::getRegisterX(BT_ESP32_SPI_WX_REG(page));
        reg = BtEsp32Register::setBit(reg, 0, 31, value);
        BtEsp32Register::setRegisterX(BT_ESP32_SPI_WX_REG(page), reg);
    }

    uint32_t pullFromDataBuffer(int page){
        if(page < 0 || page> 15)
                        page = 0;
                uint32_t reg = BtEsp32Register::getRegisterX(BT_ESP32_SPI_WX_REG(page));
        return reg;
    }


    /**/
    void send16(uint16_t value){
        this->pushToDataBuffer(0, value);
        this->setCommand(7, value);
        this->setTransaction(1);
    }

    uint32_t getSpiState(){
        uint32_t reg = BtEsp32Register::getRegisterX(this->spiExt2Reg.address);
        reg = BtEsp32Register::setBit(reg, 3, 31, 0);
        return reg;
    }

    void setDummyIdlePhase(int val){
                uint32_t SPI_USR_REG = BtEsp32Register::getRegisterX(this->spiUserReg.address);
                SPI_USR_REG = BtEsp32Register::setBit(SPI_USR_REG, this->spiUserReg.spi_usr_dummy_idle, 0, val);
                BtEsp32Register::setRegisterX(this->spiUserReg.address, SPI_USR_REG);
        }

    void setDummyPhase(int val){
        uint32_t SPI_USR_REG = BtEsp32Register::getRegisterX(this->spiUserReg.address);
                SPI_USR_REG = BtEsp32Register::setBit(SPI_USR_REG, this->spiUserReg.spi_usr_dummy, 0, val);
                BtEsp32Register::setRegisterX(this->spiUserReg.address, SPI_USR_REG);
    }

    void setAddressPhase(int val){
                uint32_t SPI_USR_REG = BtEsp32Register::getRegisterX(this->spiUserReg.address);
                SPI_USR_REG = BtEsp32Register::setBit(SPI_USR_REG, this->spiUserReg.spi_usr_addr, 0, val);
                BtEsp32Register::setRegisterX(this->spiUserReg.address, SPI_USR_REG);
        }

    void setMisoPhase(int val){
                uint32_t SPI_USR_REG = BtEsp32Register::getRegisterX(this->spiUserReg.address);
                SPI_USR_REG = BtEsp32Register::setBit(SPI_USR_REG, this->spiUserReg.spi_usr_miso, 0, val);
                BtEsp32Register::setRegisterX(this->spiUserReg.address, SPI_USR_REG);
        }

    void setMosiPhase(int val){
        uint32_t SPI_USR_REG = BtEsp32Register::getRegisterX(this->spiUserReg.address);
                SPI_USR_REG = BtEsp32Register::setBit(SPI_USR_REG, this->spiUserReg.spi_usr_mosi, 0, val);
                BtEsp32Register::setRegisterX(this->spiUserReg.address, SPI_USR_REG);
    }

    void setCommandPhase(int val){
        uint32_t SPI_USR_REG = BtEsp32Register::getRegisterX(this->spiUserReg.address);
                SPI_USR_REG = BtEsp32Register::setBit(SPI_USR_REG, this->spiUserReg.spi_usr_command, 0, val);
                BtEsp32Register::setRegisterX(this->spiUserReg.address, SPI_USR_REG);
    }

    void setCommand(int len, int command){
        uint32_t reg = BtEsp32Register::getRegisterX(this->spiUser2Reg.address);
        reg = BtEsp32Register::setBit(
            reg, 
            this->spiUser2Reg.spi_usr_command_bitlen_start, 
            this->spiUser2Reg.spi_usr_command_bitlen_end, 
            len
        );
        reg = BtEsp32Register::setBit(
                        reg,
                        this->spiUser2Reg.spi_usr_command_value_start,
                        this->spiUser2Reg.spi_usr_command_value_end,
                        command
                );
        BtEsp32Register::setRegisterX(this->spiUser2Reg.address, command);
    }

    void setMosiLen(int len){
        uint32_t reg = BtEsp32Register::getRegisterX(this->spiMosiDlenReg.address);
        reg = BtEsp32Register::setBit(
            reg, 
            this->spiMosiDlenReg.spi_usr_mosi_dbitlen_start, 
            this->spiMosiDlenReg.spi_usr_mosi_dbitlen_end, 
            len
        );
        BtEsp32Register::setRegisterX(this->spiMosiDlenReg.address, reg);
    }

    void setChipSignal(int chipId, int val){
        uint32_t reg = BtEsp32Register::getRegisterX(this->spiPinReg.address);
        switch(chipId){
            case 0:
                reg = BtEsp32Register::setBit(reg, this->spiPinReg.spi_cs0_dis, 0, val);
                break;
            case 1:
                reg = BtEsp32Register::setBit(reg, this->spiPinReg.spi_cs1_dis, 0, val);
                break;
            case 2:
                reg = BtEsp32Register::setBit(reg, this->spiPinReg.spi_cs2_dis, 0, val);
                break;
        }
        BtEsp32Register::setRegisterX(this->spiPinReg.address, reg);
    }

    /*
     * - This function must enable "GP-SPI" mode.
     * - This function must enable the "programmable clock".
     * */
    void initSpiThreeLineMaster(uint32_t spix, int bitCount, bool useMsb, int mode, bool useLittleEndian){
        this->SPIx = spix;
        BtEsp32Register::updateBaseAddr(spix);
        this->threeLineMaster = true;
        this->bitcount = bitCount;
        
        printf("Configuring 3-line master mode\n");

        // Configure CTRL Reg
        uint32_t SPI_CTRL_REG = BtEsp32Register::getRegisterX(this->spiCtrlReg.address);
        SPI_CTRL_REG = BtEsp32Register::setBit(SPI_CTRL_REG, this->spiCtrlReg.spi_wr_bit_order, 0, (useMsb == true ? 0 : 1));
        BtEsp32Register::setRegisterX(this->spiCtrlReg.address, SPI_CTRL_REG);
        
        // Configure SPI clock reg
        uint32_t SPI_CLOCK_REG = BtEsp32Register::getRegisterX(this->spiClockReg.address);
        SPI_CLOCK_REG = BtEsp32Register::setBit(SPI_CLOCK_REG, this->spiClockReg.spi_clk_equ_sysclk, 0, 0);
        SPI_CLOCK_REG = BtEsp32Register::setBit(SPI_CLOCK_REG, 
            this->spiClockReg.spi_clkdiv_pre_start, 
            this->spiClockReg.spi_clkdiv_pre_end, 
            1
        ); // Can only be set if spi_clk_equ_sysclk is 0.
        SPI_CLOCK_REG = BtEsp32Register::setBit(SPI_CLOCK_REG,
                        this->spiClockReg.spi_clkcnt_n_start,
                        this->spiClockReg.spi_clkcnt_n_end,
                        0
                ); // Can only be set if spi_clk_equ_sysclk is 0.
        SPI_CLOCK_REG = BtEsp32Register::setBit(SPI_CLOCK_REG,
                        this->spiClockReg.spi_clkcnt_h_start,
                        this->spiClockReg.spi_clkcnt_h_end,
                        0
                ); // Can only be set if spi_clk_equ_sysclk is 0.
        SPI_CLOCK_REG = BtEsp32Register::setBit(SPI_CLOCK_REG,
                        this->spiClockReg.spi_clkcnt_l_start,
                        this->spiClockReg.spi_clkcnt_l_end,
                        0
                ); // Can only be set if spi_clk_equ_sysclk is 0.
        BtEsp32Register::setRegisterX(this->spiClockReg.address, SPI_CLOCK_REG);
        

        // Configure SPI USER REG
        uint32_t SPI_USER_REG = BtEsp32Register::getRegisterX(this->spiUserReg.address);
        SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_usr_command, 0, 1);
                SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_usr_addr, 0, 1);
                SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_usr_dummy, 0, 1);
                SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_usr_miso, 0, 0);
                SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_usr_mosi, 0, 1);
                SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_usr_dummy_idle, 0, 0);
        SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_sio, 0, 1);
        SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_usr_mosi_highpart, 0, 0);
        SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_usr_miso_highpart, 0, 0);
        SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_wr_byte_order, 0, (useLittleEndian == true ? 0 : 1));
        SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_rd_byte_order, 0, (useLittleEndian == true ? 0 : 1));
        SPI_USER_REG = BtEsp32Register::setBit(SPI_USER_REG, this->spiUserReg.spi_doutdin, 0, 0);
        BtEsp32Register::setRegisterX(this->spiUserReg.address, SPI_USER_REG);

        this->setCommand(7, 0x06); // <-- Causes a panic
        this->setMosiLen(bitCount);
            
        // Configure spi slave reg
        uint32_t SPI_SLAVE_REG = BtEsp32Register::getRegisterX(this->spiSlaveReg.address);
        SPI_SLAVE_REG = BtEsp32Register::setBit(SPI_SLAVE_REG, this->spiSlaveReg.spi_slave_mode, 0, 0); // master mode
        SPI_SLAVE_REG = BtEsp32Register::setBit(SPI_SLAVE_REG, this->spiSlaveReg.spi_trans_inten, 0, 0);
        SPI_SLAVE_REG = BtEsp32Register::setBit(SPI_SLAVE_REG, this->spiSlaveReg.spi_slv_wr_sta_inten, 0, 0);
        SPI_SLAVE_REG = BtEsp32Register::setBit(SPI_SLAVE_REG, this->spiSlaveReg.spi_slv_rd_sta_inten, 0, 0);
        SPI_SLAVE_REG = BtEsp32Register::setBit(SPI_SLAVE_REG, this->spiSlaveReg.spi_slv_wr_buf_inten, 0, 0);
        SPI_SLAVE_REG = BtEsp32Register::setBit(SPI_SLAVE_REG, this->spiSlaveReg.spi_slv_rd_buf_inten, 0, 0);
        BtEsp32Register::setRegisterX(this->spiSlaveReg.address, SPI_SLAVE_REG);
        
        /*
         * Set the clock mode. 
         * */
        switch(mode){
            case 0: 
                this->clockMode0(true);
                break;
            case 1:
                this->clockMode1(true);
                break;
            case 2:
                this->clockMode2(true);
                break;
            case 3:
                this->clockMode3(true);
                break;
        }
        this->setDummyPhase(0);
        
    }
};

LCD Code

#include "./defines.h"

class Esp32Gc9a01{
private:
    uint32_t dc;    
    uint32_t cs;    
    uint32_t sck;   
    uint32_t miso;  
    uint32_t mosi;  
    uint32_t res;   
    uint32_t blk;   

    uint32_t spix;
    
    Esp32Spi spi;
    Esp32Gpio gpio;
    Esp32Dport dport;
public:

    void debug(){
        this->spi.debug();
    }
    int startScreen(){
        printf("Starting screen\n");
        this->gpio.simpleOutput(this->blk, 1);
        this->gpio.simpleOutput(this->cs, 1);
        this->gpio.simpleOutput(this->res, 1);
        btSleep(10000);
        this->gpio.simpleOutput(this->res, 0);
            btSleep(100);
        this->gpio.simpleOutput(this->res, 1);
        btSleep(100);


        this->writeCommand(GC9A01_CMD_InnerRegisterEnable1);
        this->writeCommand(GC9A01_CMD_InnerRegisterEnable2);


        this->writeCommand(GC9A01_CMD_DisplayFunctionControl);
            this->writeData(0x00);
            this->writeData(0b00100000);

            this->writeCommand(GC9A01_CMD_MemoryAccessControl);
            this->writeData(0x48);

        this->writeCommand(GC9A01_CMD_PixelFormatSet);
            this->writeData(GC9A01_12_PIXEL);

        this->writeCommand(GC9A01_CMD_PowerCriterionControl);
        this->writeData(0x00000000);

            this->writeCommand(GC9A01_CMD_Vreg1aVoltageControl);
            this->writeData(0x3c);

            this->writeCommand(GC9A01_CMD_Vreg1bVoltageControl);
            this->writeData(0x3c);

            this->writeCommand(GC9A01_CMD_Vreg2aVoltageControl);
            this->writeData(0x28);

            this->writeCommand(GC9A01_CMD_SetGamma1);
            this->writeData(0x45);
            this->writeData(0x09);
            this->writeData(0x08);
            this->writeData(0x08);
            this->writeData(0x26);
            this->writeData(0x2a);

        this->writeCommand(GC9A01_CMD_SetGamma2);
            this->writeData(0x43);
            this->writeData(0x70);
            this->writeData(0x72);
            this->writeData(0x36);
            this->writeData(0x37);
            this->writeData(0x6f);

            this->writeCommand(GC9A01_CMD_SetGamma3);
            this->writeData(0x45);
            this->writeData(0x09);
            this->writeData(0x08);
            this->writeData(0x08);
            this->writeData(0x26);
            this->writeData(0x2a);

            this->writeCommand(GC9A01_CMD_SetGamma4);
            this->writeData(0x43);
            this->writeData(0x70);
            this->writeData(0x72);
            this->writeData(0x36);
            this->writeData(0x37);
            this->writeData(0x6f);


            this->writeCommand(GC9A01_CMD_FrameRate);
            this->writeData(GC9A01_FRAMERATE_8DOT_INVERSION);

        /*
         * War Crimes, these enable even and odd lines to be filled.
         * */
        this->writeCommand(0x66); // x
            this->writeData(0x3C);
            this->writeData(0x00);
            this->writeData(0xCD);
            this->writeData(0x67);
            this->writeData(0x45);
            this->writeData(0x45);
            this->writeData(0x10);
            this->writeData(0x00);
            this->writeData(0x00);
            this->writeData(0x00);

            this->writeCommand(0x67); // x
            this->writeData(0x00);
            this->writeData(0x3C);
            this->writeData(0x00);
            this->writeData(0x00);
            this->writeData(0x00);
            this->writeData(0x01);
            this->writeData(0x54);
            this->writeData(0x10);
            this->writeData(0x32);
            this->writeData(0x98);

            this->writeCommand(GC9A01_CMD_TearingEffectLineOn);
            this->writeCommand(GC9A01_CMD_DisplayInversionOn);

        this->writeCommand(GC9A01_CMD_InterfaceControl);
        this->writeData(0b11001011);
        this->writeCommand(GC9A01_CMD_Spi2DataControl);
            this->writeData(0b00101100);

        this->writeCommand(GC9A01_CMD_WriteCtrlDisplay);
        this->writeData(0b00101100);

        /*
         * Enable display
         * */
        this->writeCommand(GC9A01_CMD_NormalDisplayModeOn);
            this->writeCommand(GC9A01_CMD_SleepOut);
            btSleep(120);
            this->writeCommand(GC9A01_CMD_DisplayOn);
            btSleep(20);
    

    //  while(1){btSleep(1000);}
        return 1;

    }

    void setBackgroundColor(uint16_t color, int xMax, int yMax){
        this->setWindowSize(0, xMax, 0, yMax);
        this->writeCommand(GC9A01_CMD_MemoryWrite);
        for(int i=0; i<xMax*yMax; i++){
            this->writeData(color);
            this->writeData(color>>8);
        }
    }

    void drawPixel(uint16_t color, int x, int y){
        int pixelSize = 1;
        this->setWindowSize(x, x+pixelSize, y, y+pixelSize);
        this->writeCommand(GC9A01_CMD_MemoryWrite);
        for(int j=0; j<pixelSize*pixelSize; j++){
            this->writeData(color);
            this->writeData(color>>8);
        }
        
    }

    void initPins(uint32_t spix, uint32_t _dc, uint32_t _cs, uint32_t _sck, uint32_t _miso, uint32_t _mosi, uint32_t _res, uint32_t _blk){
        this->dc = _dc;
        this->cs = _cs;
        this->sck = _sck;
        this->miso = _miso;
        this->mosi = _mosi;
        this->res = _res;
        this->blk = _blk;
        this->spi.setSpiPins(this->miso, this->mosi, this->sck, this->cs);
        this->spix = spix;


        this->gpio.configureOutputPeriph(this->mosi, BT_ESP32_VSPID_OUT);
        this->gpio.configureOutputPeriph(this->sck, BT_ESP32_VSPICLK_OUT_MUX);
        this->gpio.configureOutputPeriph(this->cs, BT_ESP32_VSPICS0_OUT);

        this->gpio.setPinSimpleOut(this->dc);
        this->gpio.setPinSimpleOut(this->res);
        this->gpio.setPinSimpleOut(this->blk);

        // Validate that the below is being set properly.
        int spiTarget = this->dport.dportPeripRstEnReg.dport_spi3_rst;
        this->dport.setPerphReset(spiTarget, 1);
        this->dport.setPerphReset(spiTarget, 0);
        this->dport.setSpiDmaChannel(3, 0);
        this->dport.setPeripheralClock(this->dport.dportPeripClkEnReg.dport_spi3_clk_en, 1);

        int mode = 3;
                int bitCount = 8;
                bool msb = true;
        bool littleEndian = true;
        // Validate the below configures as expected.
                this->spi.initSpiThreeLineMaster(spix, bitCount, msb, mode, littleEndian);

        printf("Initilized...\n");
    };

    void setWindowSize(uint16_t x, uint16_t y, uint16_t _x, uint16_t _y){
            //set the X coordinates
            this->writeCommand(GC9A01_CMD_RowAddressSet);
            this->writeData((x>>8)&0xff);
            this->writeData(x & 0xff);
            this->writeData((y>>8)&0xff);
            this->writeData(y&0xff);

            //set the Y coordinates
            this->writeCommand(GC9A01_CMD_ColumnAddressSet);
            this->writeData((_x>>8)&0xff);
            this->writeData(_x & 0xff);
            this->writeData((_y>>8)&0xff);
            this->writeData(_y&0xff);
    }

    void startDrawing(){
        this->writeCommand(GC9A01_CMD_MemoryWrite);
    }

    void continueDrawing(void){
                this->writeCommand(GC9A01_CMD_WriteMemoryContinue);
        }

    void writeCommand(uint16_t command){
        // dc pin to 0
        this->gpio.simpleOutput(this->dc, 0);
        // set cs pin to 0
        this->gpio.simpleOutput(this->cs, 0);
        this->spi.setChipSignal(0, 0);
        // spi write
        this->spi.send16(command);
        // set cs pin to 1
        this->spi.setChipSignal(0, 1);
        this->gpio.simpleOutput(this->cs, 1);
    }
    void writeData(uint16_t data){
        // set dc pin to 1
        this->gpio.simpleOutput(this->dc, 1);
        // set cs pin to 0
        this->gpio.simpleOutput(this->cs, 0);
        this->spi.setChipSignal(0, 0);
        // spi write
        this->spi.send16(data);
        // set cs pin to 1
        this->spi.setChipSignal(0, 1);
        this->gpio.simpleOutput(this->cs, 1);
    }
};

Key Notes,

  • The StartScreen function works as expected and fails because of the SPI send functions.
  • The simpleOutput functions properly set the gpio pins to 0 or 1 respectively, the setChipSignal function was an experiemnt.
  • The issue is somewhere in the spi send and initalize functions.

Solution

  • After days of no sleep, I've figured it out.

    Here are some things you can try if you find yourself in having the same issue.

    • Make sure your clock modes are configured properly, this includes making sure the SPI_CLOCK_REG is proper. Set spi_clk_equ_sysclk to 1.
    • Make sure you enable the PLL clock via the RTC registers.
    • For 3-line half duplex, you only need to enable the spi_usr_mosi bit, the other phases can be set to 0.
    • Make sure that your gpio code is properly handling gpio pins greater than 31. These pins use their own register to enable.

    If you double check these things, assuming your code is similar to the example provided by the question, you should be able to resolve the issue like I have.