STM32 SPI驱动ST7798:从初始化到图形绘制的实战指南

张开发
2026/4/17 4:53:15 15 分钟阅读

分享文章

STM32 SPI驱动ST7798:从初始化到图形绘制的实战指南
1. 硬件连接与SPI基础配置第一次用STM32驱动ST7798屏幕时我对着数据手册研究了整整两天才搞明白硬件连接。SPI接口虽然只有四根线但每个引脚的功能都不能接错。CS片选接PA4DC数据/命令选择接PA8RST复位接PA15背光控制接PB3。SPI主接口用SPI1MOSI接PA7SCK接PA5。硬件连接完成后SPI初始化是关键。我习惯用标准库配置先开启GPIO和SPI时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);SPI参数配置有个坑要注意ST7798的SPI模式必须设为CPOL1、CPHA1即模式3。我最初用模式0导致屏幕完全没反应SPI_InitStructure.SPI_CPOL SPI_CPOL_High; // 空闲时高电平 SPI_InitStructure.SPI_CPHA SPI_CPHA_2Edge; // 第二个边沿采样波特率预分频建议先用低速如256分频确保初始化稳定后面再提速SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_256;2. ST7798初始化序列详解屏幕初始化就像给设备开机引导必须严格按照手册顺序发送命令。ST7798的初始化有30多个步骤但核心流程可以归纳为硬件复位拉低RST引脚至少10mslcd_rst0; delay_ms(20); lcd_rst1; delay_ms(120);退出睡眠模式发送0x11命令后需等待120mslcd_write_cmd(0x11); delay_ms(120);设置像素格式我用RGB565格式0x05lcd_write_cmd(0x3A); lcd_write_data(0x05);伽马校正这部分参数厂家已经优化好直接照搬即可lcd_write_cmd(0xE0); lcd_write_data(0xD0); lcd_write_data(0x00); // 后续还有13个参数...实测中发现初始化完成后如果不设置显示区域可能会出现花屏。建议立即设置全屏显示范围lcd_address_set(0, 0, LCD_Width-1, LCD_Height-1);3. 底层绘图函数实现画点函数是所有图形操作的基础。ST7798需要先设置坐标窗口再写入颜色值void lcd_draw_point(u16 x, u16 y, u16 color) { lcd_address_set(x, y, x, y); // 设置单点坐标 lcd_write_halfword(color); // 写入16位色值 }清屏操作优化有个技巧设置全屏地址后连续发送像素数据比单点写入快10倍void lcd_clear(u16 color) { u8 data[2] {color8, color0xFF}; lcd_address_set(0, 0, LCD_Width-1, LCD_Height-1); lcd_dc1; lcd_cs0; for(int i0; iLCD_Width*LCD_Height; i) { SPI_I2S_SendData(SPI1, data[0]); while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)RESET); SPI_I2S_SendData(SPI1, data[1]); while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)RESET); } lcd_cs1; }画圆函数采用Bresenham算法实测在240x320分辨率下画半径50的圆只需2msvoid lcd_draw_circle(u16 x0, u16 y0, u8 r, u16 color) { int a0, br, di3-(r1); while(ab) { // 对称绘制8个点 lcd_draw_point(x0b, y0-a, color); lcd_draw_point(x0a, y0-b, color); // 省略其他6个点... a; di di0 ? di4*a6 : di104*(a-b--); } }4. 字符与图片显示优化显示字符需要先处理字模数据。我常用的16x8点阵字库每个字符占16字节void lcd_show_char(u16 x, u16 y, char chr, u8 size, u16 color, u16 bgcolor) { chr - ; // ASCII码偏移 u8 *font asc2_1608[chr]; // 字模指针 for(int t0; t16; t) { u8 temp font[t]; for(int t10; t18; t1) { lcd_write_halfword(temp0x80 ? color : bgcolor); temp 1; } } }图片显示要注意取模格式。推荐用Image2Lcd工具设置如下输出格式C语言数组扫描方式水平扫描色深16位真彩色(RGB565)高位在前(MSB First)显示图片时直接批量发送数据void lcd_show_image(u16 x, u16 y, u16 w, u16 h, const u8 *p) { lcd_address_set(x, y, xw-1, yh-1); lcd_dc1; lcd_cs0; for(int i0; iw*h*2; i) { while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)RESET); SPI_I2S_SendData(SPI1, p[i]); } lcd_cs1; }5. SPI性能优化实战初始化的低速模式256分频刷新率只有5fps通过以下优化可提升到30fps提升SPI时钟初始化后切到2分频void lcd_set_speed(u8 prescaler) { SPI1-CR1 0xFFC7; SPI1-CR1 | prescaler; SPI_Cmd(SPI1, ENABLE); } // 初始化完成后调用 lcd_set_speed(SPI_BaudRatePrescaler_2);减少CS引脚操作连续发送数据时保持CS为低void lcd_write_bulk(u8 *data, u32 len) { lcd_dc1; lcd_cs0; for(int i0; ilen; i) { while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)RESET); SPI_I2S_SendData(SPI1, data[i]); } lcd_cs1; }DMA传输对于全屏刷新使用DMA可降低CPU占用率void lcd_dma_send(u8 *data, u32 len) { DMA_InitTypeDef DMA_InitStructure; // 配置DMA1通道3SPI1_TX DMA_InitStructure.DMA_PeripheralBaseAddr (u32)SPI1-DR; DMA_InitStructure.DMA_MemoryBaseAddr (u32)data; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize len; DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode DMA_Mode_Normal; DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel3, DMA_InitStructure); SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); DMA_Cmd(DMA1_Channel3, ENABLE); while(DMA_GetFlagStatus(DMA1_FLAG_TC3)RESET); DMA_ClearFlag(DMA1_FLAG_TC3); }

更多文章