通过SPI驱动TFT彩屏是我测试计划中的一个部分,使用的是一块3.5寸TFT彩色屏幕,背光控制使用之前在PWM测试时预留的CH3通道,以便在之后的应用中可以自行调节背光的亮度。下图是本次测试过程的照片,TFT彩屏已经安装在一个塑料盒盖上,通过杜邦线连接:

引脚安排如下:

其中的SPI通讯是对应SPI2的PB13、PB14和PB15,其中的SPI_MISO在本次测试中没有使用。
使用模拟SPI驱动时,这三个引脚分别设置为T_SCL、T_SDA和T_SDO。
驱动代码是统一的,在编译时通过是否定义了T_SCL_Pin来判断编译模拟SPI代码还是硬件SPI代码。
下面是硬件SPI的配置:

这是GPIO的配置,引脚速度已经配置为最高:

这是TFT的驱动代码:
/****************************************************************************************************
// 硬件SPI驱动
// LCD模块 STM32C071RB单片机
//=========================================电源接线================================================//
// VCC DC3.3V //电源
// GND GND //电源地
//=======================================液晶屏数据线接线==========================================//
// SDI(MOSI) PB15 //液晶屏SPI总线数据写信号
// SDO(MISO) PB14 //液晶屏SPI总线数据读信号
// SCK PB13 //液晶屏SPI总线时钟信号
//=======================================液晶屏控制线接线==========================================//
// LED PB10 //液晶屏背光控制信号,由TIM3_CH3输出PWM控制
// DC/RS PB1 //液晶屏数据/命令控制信号
// RST PD8 //液晶屏复位控制信号
// CS PC0 //液晶屏片选控制信号
****************************************************************************************************/
#include "lcd.h"
//#include "stdlib.h"
#ifndef T_SCL_Pin
#include "spi.h"
#endif
#include "font.h"
//管理LCD重要参数
//默认为竖屏
_lcd_dev lcddev;
//画笔颜色,背景颜色
uint16_t POINT_COLOR = 0x0000,BACK_COLOR = 0xFFFF;
//uint16_t DeviceCode;
//定义颜色数组
//uint16_t ColorTab[5]={RED,GREEN,BLUE,YELLOW,BRED};
/*****************************************************************************
* 函数名: void SPI_WriteByte(uint8_t Data)
* 编制日期: 2025-03-24
* 作用: 以SPI模式0传输单字节数据
* 参数: data 要写入的数据
* 返回值: 无
* 备注: 无
******************************************************************************/
void SPI_WriteByte(uint8_t Data)
{
#ifdef T_SCL_Pin
uint8_t i;
LCD_SCL_SET;
for(i=8;i>0;i--){
if(Data&0x80)
LCD_SDA_SET; //输出数据
else
LCD_SDA_CLR;
LCD_SCL_CLR;
LCD_SCL_SET;
Data<<=1;
}
#else
HAL_SPI_Transmit(&hspi2,&Data,1,0xffff);
#endif
}
/*****************************************************************************
* 函数名: void SPI_WriteBuff(uint8_t *buff,uint8_t len)
* 编制日期: 2025-03-24
* 作用: 以SPI模式0传输多字节数据
* 参数: buff 要写入的数组指针
* len 数组长度
* 返回值: 无
* 备注: 无
******************************************************************************/
void SPI_WriteBuff(uint8_t *buff,uint8_t len)
{
#ifdef T_SCL_Pin
uint8_t i,j,d;
LCD_SCL_SET;
for(j=0; j<len; j++){
d = buff[j];
for(i=8;i>0;i--){
if(d&0x80)
LCD_SDA_SET; //输出数据
else
LCD_SDA_CLR;
LCD_SCL_CLR;
LCD_SCL_SET;
d<<=1;
}
}
#else
HAL_SPI_Transmit(&hspi2,buff,len,0xffff);
#endif
}
/*****************************************************************************
* 函数名: void LCD_WR_REG(uint8_t data)
* 编制日期: 2025-03-04
* 作用: 写入单字节的命令
* 参数: data 要写入的命令
* 返回值: 无
* 备注: 无
******************************************************************************/
void LCD_WR_REG(uint8_t data)
{
LCD_CS_CLR;
LCD_DC_CLR;
SPI_WriteByte(data);
LCD_CS_SET;
}
/*****************************************************************************
* 函数名: void LCD_WR_DATA(uint8_t data)
* 编制日期: 2025-03-04
* 作用: 写入单字节的数据
* 参数: data 要写入的数据
* 返回值: 无
* 备注: 无
******************************************************************************/
void LCD_WR_DATA(uint8_t data)
{
LCD_CS_CLR;
LCD_DC_SET;
SPI_WriteByte(data);
LCD_CS_SET;
}
/*****************************************************************************
* 函数名: void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
* 编制日期: 2025-03-10
* 作用: 将双字节数据写入指定的寄存器(地址)
* 参数: LCD_Reg 寄存器(地址)
* LCD_RegValue 要写入的双字节数据
* 返回值: 无
* 备注: 无
******************************************************************************/
void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
{
uint8_t Dat[2];
Dat[0] = LCD_RegValue>>8;
Dat[1] = LCD_RegValue&0xff;
LCD_CS_CLR;
LCD_DC_CLR;
SPI_WriteByte(LCD_Reg);
LCD_DC_SET;
SPI_WriteBuff(Dat,2);
LCD_CS_SET;
}
/*****************************************************************************
* 函数名: void Lcd_WriteData_16Bit(uint16_t c)
* 编制日期: 2025-03-10
* 作用: 在当前位置写入像素(颜色)数据
* 参数: Data 16位的三基色数据
* 返回值: 无
* 备注: 无
******************************************************************************/
void Lcd_WriteData_16Bit(uint16_t c)
{
uint8_t Dat[3];
Dat[0] = (c>>8)&0xF8; //RED
Dat[1] = (c>>3)&0xFC; //GREEN
Dat[2] = c<<3; //BLUE
LCD_CS_CLR;
LCD_DC_SET;
SPI_WriteBuff(Dat,3);
LCD_CS_SET;
}
/*****************************************************************************
* 函数名: void Lcd_WriteData_16Bit(uint16_t Data)
* 编制日期: 2025-03-04
* 作用: 在指定位置写入像素数据
* 参数: x 列坐标点
* y 行坐标点
* 返回值: 无
* 备注: 无
******************************************************************************/
void LCD_DrawPoint(uint16_t x,uint16_t y)
{
LCD_SetCursor(x,y); //设置光标位置
Lcd_WriteData_16Bit(POINT_COLOR);
}
/*****************************************************************************
* 函数名: void LcdClear(uint16_t Color)
* 编制日期: 2025-03-04
* 作用: 按指定颜色清屏
* 参数: Color 16位颜色
* 返回值: 无
* 备注: 无
******************************************************************************/
void LcdClear(uint16_t Color)
{
uint16_t i, m;
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);
for(i=0; i<lcddev.height; i++)
{
for(m=0; m<lcddev.width; m++)
{
Lcd_WriteData_16Bit(Color);
}
}
}
/*****************************************************************************
* 函数名: void LCD_RESET(void)
* 编制日期: 2025-03-04
* 作用: LCD复位
* 参数: 无
* 返回值: 无
* 备注: 无
******************************************************************************/
void LCD_RESET(void)
{
LCD_RST_CLR;
delay_ms(100);
LCD_RST_SET;
delay_ms(50);
}
/*****************************************************************************
* 函数名: void LcdInit(void)
* 编制日期: 2025-03-10
* 作用: LCD初始化
* 参数: 无
* 返回值: 无
* 备注: 无
******************************************************************************/
void LcdInit(void)
{
uint8_t Reg,Dat[15];
LCD_RESET(); //LCD 复位
//************* ILI9488初始化**********//
/*
LCD_WR_REG(0xF7); //02
LCD_WR_DATA(0xA9);
LCD_WR_DATA(0x51);
LCD_WR_DATA(0x2C);
LCD_WR_DATA(0x82);
LCD_WR_REG(0xC0); //功率控制
LCD_WR_DATA(0x11);
LCD_WR_DATA(0x09);
LCD_WR_REG(0xC1); //功率控制
LCD_WR_DATA(0x41);
LCD_WR_REG(0XC5); //VCOM控制
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x0A);
LCD_WR_DATA(0x80);
LCD_WR_REG(0xB1); //帧速率控制(正常模式/全色)
LCD_WR_DATA(0xB0);
LCD_WR_DATA(0x11);
LCD_WR_REG(0xB4); //显示反转控制
LCD_WR_DATA(0x02);
LCD_WR_REG(0xB6); //显示功能控制
LCD_WR_DATA(0x02);
LCD_WR_DATA(0x42);
LCD_WR_REG(0xB7); //进入模式设置(NO)
LCD_WR_DATA(0xc6);
LCD_WR_REG(0xBE); //HS通道控制NO
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x04);
LCD_WR_REG(0xE9); //设置图像功能
LCD_WR_DATA(0x00);
LCD_WR_REG(0x36); //内存访问 0x48
LCD_WR_DATA((1<<3)|(0<<7)|(1<<6)|(1<<5));
LCD_WR_REG(0x3A); //界面像素格式
LCD_WR_DATA(0x66);
LCD_WR_REG(0xE0); //PGAMCTRL(正伽马对照)
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x07);
LCD_WR_DATA(0x10);
LCD_WR_DATA(0x09);
LCD_WR_DATA(0x17);
LCD_WR_DATA(0x0B);
LCD_WR_DATA(0x41);
LCD_WR_DATA(0x89);
LCD_WR_DATA(0x4B);
LCD_WR_DATA(0x0A);
LCD_WR_DATA(0x0C);
LCD_WR_DATA(0x0E);
LCD_WR_DATA(0x18);
LCD_WR_DATA(0x1B);
LCD_WR_DATA(0x0F);
LCD_WR_REG(0XE1); //NGAMCTRL(负伽马对照)
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x17);
LCD_WR_DATA(0x1A);
LCD_WR_DATA(0x04);
LCD_WR_DATA(0x0E);
LCD_WR_DATA(0x06);
LCD_WR_DATA(0x2F);
LCD_WR_DATA(0x45);
LCD_WR_DATA(0x43);
LCD_WR_DATA(0x02);
LCD_WR_DATA(0x0A);
LCD_WR_DATA(0x09);
LCD_WR_DATA(0x32);
LCD_WR_DATA(0x36);
LCD_WR_DATA(0x0F);
LCD_WR_REG(0x11); //唤醒睡眠(00x10进入睡眠)
HAL_Delay(120);
LCD_WR_REG(0x29); //开显示(0x28关显示)
*/
LCD_CS_CLR;
LCD_DC_CLR;
SPI_WriteByte(0xF7); //02
Dat[0] = 0xA9;
Dat[1] = 0x51;
Dat[2] = 0x2C;
Dat[3] = 0x82;
LCD_DC_SET;
SPI_WriteBuff(Dat,4);
LCD_DC_CLR;
SPI_WriteByte(0xC0); //功率控制
Dat[0] = 0x11;
Dat[1] = 0x09;
LCD_DC_SET;
SPI_WriteBuff(Dat,2);
LCD_DC_CLR;
SPI_WriteByte(0xC1); //功率控制
LCD_DC_SET;
SPI_WriteByte(0x41);
LCD_DC_CLR;
SPI_WriteByte(0XC5); //VCOM控制
LCD_DC_SET;
Dat[0] = 0x00;
Dat[1] = 0x0A;
Dat[2] = 0x80;
SPI_WriteBuff(Dat,3);
LCD_DC_CLR;
SPI_WriteByte(0xB1); //帧速率控制(正常模式/全色)
LCD_DC_SET;
Dat[0] = 0xB0;
Dat[1] = 0x11;
SPI_WriteBuff(Dat,2);
LCD_DC_CLR;
SPI_WriteByte(0xB4); //显示反转控制
LCD_DC_SET;
// Reg = 0x20;
// SPI_WriteByte(Reg);
LCD_WR_DATA(0x02);
LCD_CS_CLR;
LCD_DC_CLR;
SPI_WriteByte(0xB6); //显示功能控制
LCD_DC_SET;
Dat[0] = 0x02;
Dat[1] = 0x42;
SPI_WriteBuff(Dat,2);
LCD_DC_CLR;
SPI_WriteByte(0xB7); //进入模式设置(NO)
LCD_DC_SET;
SPI_WriteByte(0xc6);
LCD_DC_CLR;
SPI_WriteByte(0xBE); //HS通道控制NO
LCD_DC_SET;
Dat[0] = 0x00;
Dat[1] = 0x04;
SPI_WriteBuff(Dat,2);
LCD_DC_CLR;
SPI_WriteByte(0xE9); //设置图像功能
LCD_DC_SET;
SPI_WriteByte(0x00);
LCD_DC_CLR;
SPI_WriteByte(0x36); //内存访问 0x48
LCD_DC_SET;
SPI_WriteByte((1<<3)|(0<<7)|(1<<6)|(1<<5));
LCD_DC_CLR;
SPI_WriteByte(0x3A); //界面像素格式
LCD_DC_SET;
SPI_WriteByte(0x66);
LCD_DC_CLR;
SPI_WriteByte(0xE0); //PGAMCTRL(正伽马对照)
LCD_DC_SET;
Dat[0] = 0x00;
Dat[1] = 0x07;
Dat[2] = 0x10;
Dat[3] = 0x09;
Dat[4] = 0x17;
Dat[5] = 0x0B;
Dat[6] = 0x41;
Dat[7] = 0x89;
Dat[8] = 0x4B;
Dat[9] = 0x0A;
Dat[10] = 0x0C;
Dat[11] = 0x0E;
Dat[12] = 0x18;
Dat[13] = 0x1B;
Dat[14] = 0x0F;
SPI_WriteBuff(Dat,15);
LCD_DC_CLR;
SPI_WriteByte(0XE1); //NGAMCTRL(负伽马对照)
LCD_DC_SET;
Dat[0] = 0x00;
Dat[1] = 0x17;
Dat[2] = 0x1A;
Dat[3] = 0x04;
Dat[4] = 0x0E;
Dat[5] = 0x06;
Dat[6] = 0x2F;
Dat[7] = 0x45;
Dat[8] = 0x43;
Dat[9] = 0x02;
Dat[10] = 0x0A;
Dat[11] = 0x09;
Dat[12] = 0x32;
Dat[13] = 0x36;
Dat[14] = 0x0F;
SPI_WriteBuff(Dat,15);
LCD_DC_CLR;
SPI_WriteByte(0x11); //唤醒睡眠(00x10进入睡眠)
LCD_CS_SET;
HAL_Delay(120);
LCD_CS_CLR;
LCD_DC_CLR;
SPI_WriteByte(0x29); //开显示(0x28关显示)
LCD_CS_SET;
LcdDirection(3); //设置LCD显示方向为270度(横屏)
// LCD_BL_ON; //点亮背光
LcdClear(WHITE); //清全屏白色
}
/*****************************************************************************
* 函数名: void LCD_SetWindows(uint16_t xStar, uint16_t yStar, uint16_t xEnd, uint16_t yEnd)
* 编制日期: 2025-03-10
* 作用: 设置LCD的显示窗口
* 参数: xStar 列开始坐标点
* yStar 行开始坐标点
* xEnd 列结束坐标点
* yEnd 行结束坐标点
* 返回值: 无
* 备注: 无
******************************************************************************/
void LCD_SetWindows(uint16_t xStar, uint16_t yStar, uint16_t xEnd, uint16_t yEnd)
{
uint8_t Reg,Dat[4];
Dat[0] = xStar>>8;
Dat[1] = 0x00FF&xStar;
Dat[2] = xEnd>>8;
Dat[3] = 0x00FF&xEnd;
LCD_CS_CLR;
LCD_DC_CLR;
SPI_WriteByte(lcddev.setxcmd);
LCD_DC_SET;
SPI_WriteBuff(Dat,4);
Dat[0] = yStar>>8;
Dat[1] = 0x00FF&yStar;
Dat[2] = yEnd>>8;
Dat[3] = 0x00FF&yEnd;
LCD_DC_CLR;
SPI_WriteByte(lcddev.setycmd);
LCD_DC_SET;
SPI_WriteBuff(Dat,4);
LCD_DC_CLR;
SPI_WriteByte(lcddev.wramcmd); //开始写入GRAM
LCD_CS_SET;
}
/*****************************************************************************
* 函数名: void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
* 编制日期: 2025-03-05
* 作用: 设置LCD光标位置
* 参数: Xpos 列坐标点
* Ypos 行坐标点
* 返回值: 无
* 备注: 无
******************************************************************************/
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
{
LCD_SetWindows(Xpos,Ypos,Xpos,Ypos);
}
/*****************************************************************************
* 函数名: LcdDirection(uint8_t direction)
* 编制日期: 2025-03-10
* 作用: 设置LCD方向
* 参数: 0 0度
* 1 90度
* 2 180度
* 3 270度
* 返回值: 无
* 备注: 无
******************************************************************************/
void LcdDirection(uint8_t direction)
{
uint8_t Reg,Dat;
lcddev.setxcmd=0x2A;
lcddev.setycmd=0x2B;
lcddev.wramcmd=0x2C;
switch(direction){
case 0:
Dat = 0x08; //0000 1000 = 0x08 反向0100 1000 = 0x48
lcddev.width = LCD_W;
lcddev.height = LCD_H;
break;
case 1:
Dat = 0x40 | 0x20 | 0x08; //0110 1000 = 0x68 反向0010 1000 = 0x28
lcddev.width = LCD_H;
lcddev.height = LCD_W;
break;
case 2:
Dat = 0x80 | 0x40 | 0x08; //1100 1000 = 0xc8 反向1000 1000 = 0x88
lcddev.width = LCD_W;
lcddev.height = LCD_H;
break;
case 3:
Dat = 0x80 | 0x20 | 0x08; //1010 1000 = 0xa8 反向1110 1000 = 0xe8
lcddev.width = LCD_H;
lcddev.height = LCD_W;
break;
default:
break;
}
LCD_CS_CLR;
LCD_DC_CLR;
SPI_WriteByte(0x36);
LCD_DC_SET;
SPI_WriteByte(Dat);
LCD_CS_SET;
}
/*****************************************************************************
* 函数名: void LcdDrawHline(uint16_t x,uint16_t y,uint16_t l,uint16_t c)
* 编制日期: 2024-01-28
* 作用: 画垂直线(由上往下画)
* 参数: x 起点坐标
* y
* l 线长(点)
* c 16位RBG颜色
* 返回值: 无
* 备注: 无
******************************************************************************/
void LcdDrawHline(uint16_t x,uint16_t y,uint16_t l,uint16_t c)
{
uint16_t i;
POINT_COLOR=c;
LCD_CS_CLR;
for (i=0; i<l; i++){
LCD_DrawPoint(x,y+i);
}
LCD_CS_SET;
}
/*****************************************************************************
* 函数名: void LcdDrawVline(uint16_t x,uint16_t y,uint16_t l,uint16_t c)
* 编制日期: 2024-01-28
* 作用: 画水平线(由左往右画)
* 参数: x 起点坐标
* y
* l 线长(点)
* c 16位RBG颜色
* 返回值: 无
* 备注: 无
******************************************************************************/
void LcdDrawVline(uint16_t x,uint16_t y,uint16_t l,uint16_t c)
{
uint16_t i;
LCD_CS_CLR;
// LCD_SetCursor(x,y); //设置光标位置
POINT_COLOR=c;
for (i=0; i<l; i++){
// Lcd_WriteData_16Bit(c);
LCD_DrawPoint(x+i,y);
}
LCD_CS_SET;
}
/*****************************************************************************
* 函数名: void LcdDrawLine(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t c)
* 编制日期: 2024-01-29
* 作用: 两点之间画线
* 参数: x0 起点坐标
* y0
* x1 终点坐标
* y1
* c 16位RBG颜色
* 返回值: 无
* 备注: 无
******************************************************************************/
void LcdDrawLine(uint16_t x0,uint16_t y0,uint16_t x1,uint16_t y1,uint16_t c)
{
int dx,dy,dx2,dy2,x_inc,y_inc,er,id;
LCD_CS_CLR;
dx = x1 - x0;
dy = y1 - y0;
if(dx >= 0){
x_inc = 1;
}else{
x_inc = -1;
dx = -dx;
}
if(dy >= 0){
y_inc = 1;
}else{
y_inc = -1;
dy = -dy;
}
dx2 = dx << 1;
dy2 = dy << 1;
POINT_COLOR=c;
if(dx > dy){
/* initialize error */
er = dy2 - dx;
/* draw the line */
for(id = 0;id <= dx;id++){
LCD_DrawPoint(x0,y0);
/* test if error has overflowed */
if(0 <= er){
er -= dx2;
/* move to next line */
y0 += y_inc;
}
/* adjust the error term */
er += dy2;
/* move to the next pixel */
x0 += x_inc;
}
}else{
/* initialize error term */
er = dx2 - dy;
/* draw the linedraw the line*/
for(id= 0;id <= dy;id++){
/* set the pixel */
LCD_DrawPoint(x0,y0);
/* test if error overflowed */
if(0 <= er){
er -= dy2;
/* move to next line */
x0 += x_inc;
}
/* adjust the error term */
er += dx2;
/* move to the next pixel */
y0 += y_inc;
}
}
LCD_CS_SET;
}
/*****************************************************************************
* 函数名: void LcdDrawCircle(uint16_t x,uint16_t y,uint16_t r,uint16_t c)
* 编制日期: 2024-01-27
* 作用: 画对称圆算法
* 参数: xc 圆形中心的x坐标
* yc 圆形中心的y坐标
* x 相对于圆形中心的x坐标
* y 相对于圆形中心的y坐标
* c 16位RBG颜色
* 返回值: 无
* 备注: 无
******************************************************************************/
/*void LCD_DrawCircle(uint16_t x,uint16_t y,uint16_t r,uint16_t c)
{
POINT_COLOR=c;
LCD_DrawPoint(xc + x, yc + y);
LCD_DrawPoint(xc - x, yc + y);
LCD_DrawPoint(xc + x, yc - y);
LCD_DrawPoint(xc - x, yc - y);
LCD_DrawPoint(xc + y, yc + x);
LCD_DrawPoint(xc - y, yc + x);
LCD_DrawPoint(xc + y, yc - x);
LCD_DrawPoint(xc - y, yc - x);
}
*/
/*****************************************************************************
* 函数名: void LcdDrawCircle(uint16_t x,uint16_t y,uint16_t r,uint16_t c)
* 编制日期: 2024-01-27
* 作用: 画圆
* 参数: x 圆心坐标
* y
* c 16位RBG颜色
* 返回值: 无
* 备注: 无
******************************************************************************/
void LcdDrawCircle(uint16_t x,uint16_t y,uint16_t r,uint16_t c)
{
unsigned short a,b;
int n;
a=0;
b=r;
n=3-2*r;
POINT_COLOR=c;
while(a < b){
// draw points on the lcd
LCD_DrawPoint(x+a, y+b);
LCD_DrawPoint(x-a, y+b);
LCD_DrawPoint(x+a, y-b);
LCD_DrawPoint(x-a, y-b);
LCD_DrawPoint(x+b, y+a);
LCD_DrawPoint(x-b, y+a);
LCD_DrawPoint(x+b, y-a);
LCD_DrawPoint(x-b, y-a);
if(n<0)
n=n+4*a+6;
else{
n=n+4*(a-b)+10;
b-=1;
}
a+=1;
}
if(a==b){
// draw points on the lcd
LCD_DrawPoint(x+a, y+b);
LCD_DrawPoint(x+a, y+b);
LCD_DrawPoint(x+a, y-b);
LCD_DrawPoint(x-a, y-b);
LCD_DrawPoint(x+b, y+a);
LCD_DrawPoint(x-b, y+a);
LCD_DrawPoint(x+b, y-a);
LCD_DrawPoint(x-b, y-a);
}
}
/*****************************************************************************
* 函数名: void LcdDrawRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t c)
* 编制日期: 2025-03-05
* 作用: 画矩形
* 参数: x 起点坐标
* y
* w 宽度
* h
* c 16位RBG颜色
* 返回值: 无
* 备注: 无
******************************************************************************/
void LcdDrawRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t c)
{
LcdDrawVline(x,y,w,c);
LcdDrawHline(x,y,h,c);
LcdDrawVline(x,y+h,w,c);
LcdDrawHline(x+w,y,h,c);
}
/*****************************************************************************
* 函数名: LcdGuiRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t c)
* 编制日期: 2025-03-05
* 作用: 填充矩形
* 参数: x 起点坐标
* y
* w 宽度
* h
* c 16位RBG颜色
* 返回值: 无
* 备注: 无
******************************************************************************/
void LcdGuiRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t c)
{
uint16_t i,j;
LCD_SetWindows(x,y,x+w,y+h); //设置显示窗口
for(i=0; i<h; i++)
{
for(j=0; j<w; j++)
Lcd_WriteData_16Bit(c); //写入数据
}
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口设置为全屏
}
/*****************************************************************************
* 函数名: void (uint16_t x,uint16_t y,uint16_t fc,uint16_t bc,char *str,uint8_t size)
* 编制日期: 2024-07-03
* 作用: 显示多种字号的字符串
* 参数: x 起点坐标
* y
* fc 16位RBG前景色
* bc 16位RBG背景色(背景色与前景色相同则为透明显示,即不改变背景色)
* *str 字符串指针
* size 字体的高度(行数)16,24,40
* 返回值: 无
* 备注: 无
******************************************************************************/
void LcdDrawFont(uint16_t x,uint16_t y,uint16_t fc,uint16_t bc,char *str,uint8_t size)
{
uint8_t temp,i,j,k,h,s;
uint16_t x0=x;
while(*str){ //开始字符循环
if(((uint8_t)(*str))<128){ //ASCII字符
k=*str;
//处理换行符及其它控制符
if(13==k){ //换行符
x=x0;
y+=size;
}else{
if(k>32) //字符码减32,等于字符序号
k-=32;
else
k=0; //其他不可显示字符用空格替代
}
//确定字符占用的块(宽8b)
if(size<24)
s=1; //计算重复次数(16*8点阵1块,24*12点阵2块,40*20点阵3块,
else if(size<40)
s=2;
else
s=3;
//开始写点阵
for(h=0; h<s; h++){ //逐块写
for(i=0; i<size; i++){ //逐行写
if(s==1)
temp=asc_16[k][i]; //调用16*8点阵字体数据
else if(s==2)
temp=asc_24[k][i*2+h]; //调用24*12点阵字体数据
else
temp=asc_40[k][i*3+h]; //调用24*12点阵字体数据
for(j=0; j<8; j++){
if(temp & 0x80){ //写一点
POINT_COLOR=fc;
LCD_DrawPoint(x+j+h*8, y+i);
}else{
if(bc!=fc){ //写背景色
POINT_COLOR=bc;
LCD_DrawPoint(x+j+h*8, y+i);
} //写背景色结束
}
temp<<=1;
} //写一行结束
}
} //单个字符写结束
x+=size/2; //移到下一字符位置
str++; //移动字符串指针
}
else{ //汉字字符
if(size==16){
for(k=0; k<hz16_num; k++){ //查找汉字序号(k=数组下标)
if((font16[k].ID[0]==*(str)) && (font16[k].ID[1]==*(str+1))){
break;
}
}
}else{
for(k=0; k<hz24_num; k++){
if((font24[k].ID[0]==*(str)) && (font24[k].ID[1]==*(str+1))){
break;
}
}
}
for(h=0; h<(size/8); h++){ //16点阵字库h=2块,24点阵字库h=3块
for(i=0; i<size; i++){ //开始循环写一列(8位)
if(size==16)
temp=font16[k].Msk[i*2+h]; //取16点阵字模
else
temp=font24[k].Msk[i*3+h]; //取24点阵字模
for(j=0; j<8; j++){
if(temp & 0x80){ //写一点
POINT_COLOR=fc;
LCD_DrawPoint(x+j+h*8, y+i);
}else{
if(bc!=fc){ //写背景色
POINT_COLOR=bc;
LCD_DrawPoint(x+j+h*8, y+i);
} //写背景色结束
} //写一点结束
temp<<=1;
} //写一行结束
}
} //写n块结束
x+=size; //移到下一字符位置
str+=2; //移动字符串指针
}
}
}
/*****************************************************************************
* 函数名: void LcdDrawASCII(uint16_t x,uint16_t y,uint16_t fc,uint16_t bc,char code,uint8_t size)
* 编制日期: 2024-07-03
* 作用: 显示多种字号的单个ASCII字符
* 参数: x 起点坐标
* y
* fc 16位RBG前景色
* bc 16位RBG背景色(背景色与前景色相同则为透明显示,即不改变背景色)
* code ASCII字符码
* size 字体的高度(行数)16,24,40
* 返回值: 无
* 备注: 无
******************************************************************************/
void LcdDrawASCII(uint16_t x,uint16_t y,uint16_t fc,uint16_t bc,char code,uint8_t size)
{
uint8_t h,i,j,temp,s;
if(code<32) //非可显示字符
return;
if(size<24)
s=1; //计算重复次数(16*8点阵1块,24*12点阵2块,40*20点阵3块,
else if(size<40)
s=2;
else
s=3;
//开始写点阵
for(h=0; h<s; h++){ //逐块写
for(i=0; i<size; i++){ //逐行写
if(size==16)
temp=asc_16[code-32][i]; //调用16*8点阵字体数据
else if(size==24)
temp=asc_24[code-32][i*2+h]; //调用24*12点阵字体数据
else if(size==40)
temp=asc_40[code-32][i*3+h]; //调用24*12点阵字体数据
else
temp=asc_48[code-32][i*3+h]; //调用24*12点阵字体数据
for(j=0; j<8; j++){
if(temp & 0x80){ //写一点
POINT_COLOR=fc;
LCD_DrawPoint(x+j+h*8, y+i);
}else{
if(bc!=fc){ //写背景色
POINT_COLOR=bc;
LCD_DrawPoint(x+j+h*8, y+i);
} //写背景色结束
}
temp<<=1;
} //写一行结束
}
} //单个字符写结束
}
/*****************************************************************************
* 函数名: void LcdDrawValue(uint16_t x,uint16_t y,uint16_t v,uint8_t l,uint8_t d,uint8_t z,uint8_t t,uint16_t fc,uint16_t bc)
* 编制日期: 2024-07-03
* 作用: 显示多种字号的变量值
* 参数: x 起点坐标
* y
* v 变量
* l 显示的字符数(含小数点)
* d 小数位数
* z 0=显示前导零 1=空格替换前导零
* t 字体的高度(行数)16,24,40
* fc 16位RBG前景色
* bc 16位RBG背景色(背景色与前景色相同则为透明显示,即不改变背景色)
* 返回值: 无
* 备注: 无
******************************************************************************/
void LcdDrawValue(uint16_t x,uint16_t y,uint16_t v,uint8_t l,uint8_t d,uint8_t z,uint8_t t,uint16_t fc,uint16_t bc)
{
uint8_t i,c,f=0,x1; //字循环、位循环、当前数字,前导零参数,字符宽度
uint16_t n,x0; //当前余数、当前x坐标
uint32_t m; //当前倍数
if(z>0) //前导零用空格替换
f=16;
x0=x;
n=v;
m=1;
if(48==t)
x1=24;
else if(16==t)
x1=7;
else
x1=t/2;
for(i=0; i<l; i++) //计算最高位的倍数
m=m*10;
for(i=l; i>0; i--) //开始循环处理
{
m=i<2 ? 1: m/10; //计算当前位的倍数
c=n/m; //当前位数字
n%=m; //求余数
if(f>0){
if((c>0)|(i==(d+1))) //有有效数字或是个位,显示零
f=0;
}
if((d>0) & (d==i)){ //写小数点
LcdDrawASCII(x0,y,fc,bc,'.',t);
x0+=x1; //移到下一位
} //写小数点结束
LcdDrawASCII(x0,y,fc,bc,48+c-f,t); //写数字
x0+=x1; //移到下一位
}
}
/*****************************************************************************
* 函数名: void LcdDrawBmp16(uint16_t x,uint16_t y,uint16_t w,uint16_t h,const unsigned char *p)
* 编制日期: 2025-03-07
* 作用: 显示bmp图片
* 参数: x 起点坐标
* y
* w 图片宽度
* h 图片高度
* *p 图片数据指针
* 返回值: 无
* 备注: 无
******************************************************************************/
/*void LcdDrawBmp16(uint16_t x,uint16_t y,uint16_t w,uint16_t h,const unsigned char *p)
{
int i;
uint8_t picH,picL;
LCD_SetWindows(x,y,x+w-1,y+h-1); //窗口设置
for(i=0; i<w*h; i++)
{
picL=*(p+i*2); //数据低位在前
picH=*(p+i*2+1);
Lcd_WriteData_16Bit(picH<<8|picL);
}
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复显示窗口为全屏
}
*/
下面是模拟SPI的配置:

因为代码是通用的,不需要作其它修改,直接编译就成。
这是硬件SPI驱动TFT彩屏的动画:

这是模拟SPI驱动TFT彩屏的动画,可以看出明显比硬件SPI驱动时的刷新快很多:
