CH585+SPI Flash+TFT屏幕实现硬件直驱_SoftWare

LCD 主要代码

//LCD端口定义
#define LCD_RST_CLR()   (GPIOB_ResetBits(LCD_RST)) //RST
#define LCD_RST_SET()   (GPIOB_SetBits(LCD_RST))

#define LCD_DC_CLR()   (GPIOB_ResetBits(LCD_DC)) //dc for choose cmd or data
#define LCD_DC_SET()   (GPIOB_SetBits(LCD_DC)) //  Set is data

#define LCD_CS_CLR()   (GPIOB_ResetBits(LCD_CS)) //CS, low is enable
#define LCD_CS_SET()   (GPIOB_SetBits(LCD_CS)) //disable

#define LCD_BLK_CLR()    (GPIOB_ResetBits(LCD_BLK)) //BLK, BLK must on
#define LCD_BLK_SET()   (GPIOB_SetBits(LCD_BLK))


// LCD初始化
void TFT_LCD_Init(void)
{
    LCD_SPI_Init();
    LCD_CS_CLR();
    LCD_RESET();
// ST7789 init 按手册配置初始化命令
    LCD_WR_REG(0x11);
    LCD_WR_REG(0x36);
    if (USE_HORIZONTAL == 0)    LCD_WR_DATA(0x00);
    else if (USE_HORIZONTAL == 1)    LCD_WR_DATA(0xC0);
    else if (USE_HORIZONTAL == 2)    LCD_WR_DATA(0x70);
    else LCD_WR_DATA(0xA0);
    LCD_WR_REG(0x3A);
    LCD_WR_DATA(0x05);

    LCD_WR_REG(0xB2);
    LCD_WR_DATA(0x0C);
    LCD_WR_DATA(0x0C);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x33);
    LCD_WR_DATA(0x33);

    LCD_WR_REG(0xB7);
    LCD_WR_DATA(0x35);

    LCD_WR_REG(0xBB);
    LCD_WR_DATA(0x35);

    LCD_WR_REG(0xC0);
    LCD_WR_DATA(0x2C);

    LCD_WR_REG(0xC2);
    LCD_WR_DATA(0x01);    

    LCD_WR_REG(0xC3);
    LCD_WR_DATA(0x13);  

    LCD_WR_REG(0xC4);
    LCD_WR_DATA(0x20);  

    LCD_WR_REG(0xC6);
    LCD_WR_DATA(0x0F);  

    LCD_WR_REG(0xD0);
    LCD_WR_DATA(0xA4);  
    LCD_WR_DATA(0xA1);  

    LCD_WR_REG(0xD6);
    LCD_WR_DATA(0xA1);  

    LCD_WR_REG(0xE6);
    LCD_WR_DATA(0xF0);  
    LCD_WR_DATA(0x00);  
    LCD_WR_DATA(0x04);  
    LCD_WR_DATA(0x04);
    LCD_WR_DATA(0x04);    
    LCD_WR_DATA(0x05);  
    LCD_WR_DATA(0x29);  
    LCD_WR_DATA(0x33);  
    LCD_WR_DATA(0x3E);  
    LCD_WR_DATA(0x38);  
    LCD_WR_DATA(0x12);  
    LCD_WR_DATA(0x12);  
    LCD_WR_DATA(0x28);  
    LCD_WR_DATA(0x30);  

    LCD_WR_REG(0xE1);
    LCD_WR_DATA(0xF0);  
    LCD_WR_DATA(0x07);  
    LCD_WR_DATA(0x0A);  
    LCD_WR_DATA(0x0D);  
    LCD_WR_DATA(0x0B);  
    LCD_WR_DATA(0x07);  
    LCD_WR_DATA(0x28);  
    LCD_WR_DATA(0x33);  
    LCD_WR_DATA(0x3E);  
    LCD_WR_DATA(0x36);  
    LCD_WR_DATA(0x14);  
    LCD_WR_DATA(0x14);  
    LCD_WR_DATA(0x29);  
    LCD_WR_DATA(0x32); 

    LCD_WR_REG(0x21);
    LCD_WR_REG(0x11);
    DelayMs(120);
    LCD_WR_REG(0x29);
    LCD_CS_SET();
}

// LCD显示图片
void LCD_Draw_Image(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t* pic)
{
        uint16_t xend  = x + width;
    uint16_t yend  = y + height;
    uint32_t count = (uint32_t)width * height;
    uint32_t i;

    LCD_SetWindows(x, y, xend - 1, yend - 1); //显示范围

    LCD_CS_CLR(); // 拉低片选
    LCD_RST_SET();

    for (i = 0; i < count; i++)
    {
        LCD_WriteByte(*pic++);
        LCD_WriteByte(*pic++); //每个像素占用两字节
    }

    LCD_CS_SET();
}

flash W25Q 主要代码

// W25Q初始化
void W25Q_Init()
{
    GPIOA_ModeCfg(W25Q_CS_PIN , GPIO_ModeOut_PP_5mA); 
    W25Q_CS_SET();
}

//W25Q全部擦除函数
void W25Q_EraseAll()
{
    W25Q_WaitForWriteEnd();
    W25Q_WriteEnable();
    W25Q_CS_RESET();
    W25Q_WriteByte(W25Q_CMD_CHIP_ERASE);
    W25Q_CS_SET();
    W25Q_WaitForWriteEnd();
}

//===================
//addr:起始页地址,必须地址对齐
//len:要写入的数据的字节数
//buf:要写入的数据所在的缓冲区地址
//返回值:此次写入占用页的数量
unsigned int W25Q_MemWrite(unsigned long addr, unsigned long len, const unsigned char *buf)
{
    unsigned int pagenum1, pagenum2, var = 0;
    pagenum1 = len >> 8;//计算页的数量(等效于len / 256)
    pagenum2 = len & 255;//计算不足一页的字节数(等效于len % 256)
    for (var = 0; var < pagenum1; ++var)
    {
        W25Q_PageWrite(addr + (var << 8), buf + var * 256, 256);//(等效于var * 256)
    }
    if(pagenum2)
    {
        W25Q_PageWrite(addr + pagenum1 * 256, buf + pagenum1 * 256, pagenum2);//不足一页部分占用一页
        return pagenum1 + 1;
    }
    else
    {
        return pagenum1;
    }
}


void W25Q_PageWrite(unsigned long addr, const unsigned char *buf, unsigned int len)
{
//============================【按页写入】================================
//参数:
//addr:页地址,必须地址对齐
//buf:要写入的数据所在的缓冲区地址,缓冲区大小不能超过256
//=======================================================================
    if (len > 256)
        len = 256;
    W25Q_WaitForWriteEnd();
    W25Q_WriteEnable();
    W25Q_CS_RESET();
    W25Q_WriteByte(W25Q_CMD_PAGE_PROGRAM);
    W25Q_WriteByte((addr & 0xFF0000) >> 16);
    W25Q_WriteByte((addr & 0x00FF00) >> 8);
    W25Q_WriteByte(addr & 0x000000);//强制地址256对齐

    while (len--)
    {
        W25Q_WriteByte(*buf++);
    }
    W25Q_CS_SET();
    W25Q_WaitForWriteEnd();
}

//按字节写入flash
void W25Q_WriteByte(uint8_t data)
{
#if SPI_TYPE == SPI_HARDWARE
    // GPIOA_ModeCfg(SPI_SCLK_PIN | SPI_MISO_PIN, GPIO_ModeOut_PP_5mA);
    // SPI_2WIRE_MasterOutputInit();

    // R8_SPI0_CTRL_MOD |= RB_SPI_MISO_OE;//MISO 输出使能,用作两线SPI模式下的SPI输出

    SPI0_MasterSendByte(data);
#else
    for (uint8_t i = 0; i < 8; i ++)//循环8次,依次交换每一位数据
    {
        if(data & 0x80)
        {
            GPIOA_SetBits(SPI_MOSI_PIN);
        }
        else
        {
            GPIOA_ResetBits(SPI_MOSI_PIN);
        }
        DelayUs(4);
        data <<= 1;

        GPIOA_SetBits(SPI_SCLK_PIN);//SCK上升沿发送MOSI数据,主机提前把数据放在MOSI线上,拉高时钟线让从机来读取
        DelayUs(4);
        // DelayUs(2);

        GPIOA_ResetBits(SPI_SCLK_PIN);//SCK上升沿发送MOSI数据,主机提前把数据放在MOSI线上,拉高时钟线让从机来读取
        DelayUs(4);
        // DelayUs(2);
    }
#endif
}

void W25Q_DMAMemRead(unsigned long addr, unsigned long len, unsigned char *buf)
{
//============================【不定长连续DMA读】================================
//参数:
//addr:起始页地址
//len:要读出的数据的字节数
//buf:存储读出数据的缓冲区地址
//==========================================================================
    unsigned long remaining = len;
    unsigned long offset = 0;
    W25Q_WaitForWriteEnd();
    GPIOB_SetBits(LCD_CS);
    W25Q_CS_RESET();
    W25Q_WriteByte(W25Q_CMD_READ_DATA);
    W25Q_WriteByte((addr & 0xFF0000) >> 16);
    W25Q_WriteByte((addr & 0x00FF00) >> 8);
    W25Q_WriteByte(addr & 0x0000FF) ;
    MCU_MOSI_DIABLE();     // 失能mcu MOSI
    GPIOB_SetBits(LCD_RST);
    GPIOB_ResetBits(LCD_CS); //注意此时必须拉低LCD片选
    while(remaining > 0) /*DMA传输一次最大字节为4095,图片数据远超最大字节,需要分次传输*/
    {
        unsigned long chunk = (remaining > 4095) ? 4095 : remaining;
        SPI0_MasterDMARecv(buf + offset, chunk);
        remaining -= chunk;
        offset += chunk;
    }
    //SPI0_MasterDMARecv(buf, len);
    W25Q_CS_SET();
    MCU_MOSI_ENABLE();
}

main函数实现

__attribute__((aligned(4))) uint8_t ReadBuf[1200];

void DebugInit(void) // SPI0与例程惯用串口打印引脚冲突,所以此例程改用UART1_输出打印
{
    GPIOB_SetBits(GPIO_Pin_13);
    GPIOPinRemap(ENABLE, RB_PIN_UART1);
    GPIOB_ModeCfg(GPIO_Pin_12, GPIO_ModeIN_PU);
    GPIOB_ModeCfg(GPIO_Pin_13, GPIO_ModeOut_PP_5mA);
    UART1_DefInit();
}

// 图片页对齐,必须根据flash页大小对齐地址
#define PAGE_SIZE 256
#define ALIGN_PAGE(x) (((x) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) 

// 定义图片存储地址
#define IMG1_ADDR  0x00000000
#define IMG2_ADDR  ALIGN_PAGE(IMG1_ADDR + sizeof(gImage_001))
#define IMG3_ADDR  ALIGN_PAGE(IMG2_ADDR + sizeof(gImage_002))

// 图片数组,用于循环显示
const uint8_t *img_ptr[] = {gImage_001, gImage_002, gImage_003};
const uint32_t img_addr[] = {IMG1_ADDR, IMG2_ADDR, IMG3_ADDR,IMG4_ADDR};
const uint32_t img_size[] = {sizeof(gImage_001), sizeof(gImage_002), sizeof(gImage_003), sizeof(gImage_004)};
const uint8_t img_count = 4;

/*********************************************************************
 * @fn      main
 *
 * @brief   主函数
 *
 * @return  none
 */
 __HIGH_CODE
__attribute__((noinline))
int main()
{
    uint8_t i;
    uint32_t Flash_ID;
    uint32_t pages, addr;
    HSECFG_Capacitance(HSECap_18p);
    SetSysClock(SYSCLK_FREQ);
    /* 配置串口调试 */
    DebugInit();
    PRINT("Start @ChipID=%02X\n", R8_CHIP_ID);

#if SPI_TYPE == SPI_HARDWARE
    BSP_Hard_SPI_Init();
#else
    BSP_Soft_SPI_Init();
#endif
    TFT_LCD_Init(); //LCD初始化
    LCD_Fill(0, 0, LCD_W, LCD_H, LCD_WHITE); //背景色填充
    W25Q_Init(); //flash初始化
    W25Q_EraseAll(); //flash擦除
    LCD_Draw_Image(10,10,200, 200, gImage_001); 
    W25Q_MemWrite(IMG1_ADDR, sizeof(gImage_001), gImage_001); //向flash中写入图片数据
    W25Q_MemWrite(IMG2_ADDR, sizeof(gImage_002), gImage_002);
    W25Q_MemWrite(IMG3_ADDR, sizeof(gImage_003), gImage_003);
    uint8_t current_img = 0;
    while (1) {
        GPIOB_SetBits(LCD_DC);
        W25Q_DMAMemRead(img_addr[current_img], img_size[current_img], ReadBuf); //循环显示flash中的图片,ReadBuf为空
        current_img = (current_img + 1) % img_count;
	DelayMs(10); //注意调整延时时间,否则图片显示乱码

    }
}
posted @ 2026-03-25 16:05  J_02346  阅读(52)  评论(0)    收藏  举报