当前位置: 首页 > article >正文

STM32F103驱动2.4寸TFT屏实战:如何用SPI接口实现GUI图形库(画圆、写字、显示图片)

STM32F103驱动2.4寸TFT屏实战如何用SPI接口实现GUI图形库画圆、写字、显示图片在嵌入式系统开发中图形用户界面(GUI)的实现往往是一个既具挑战性又充满成就感的部分。当我们将目光投向STM32F103这类资源有限的微控制器时如何在2.4寸TFT屏幕上构建流畅的图形界面就成为了开发者需要解决的关键问题。本文将深入探讨基于SPI接口的GUI实现方案从底层驱动到高级图形功能的完整开发路径。1. 硬件基础与SPI驱动配置1.1 硬件连接与初始化STM32F103与2.4寸TFT屏的SPI连接需要精确的引脚配置。典型的连接方式如下STM32引脚TFT屏引脚功能描述PB13SCK时钟信号PB14MISO主机输入PB15MOSI主机输出PB11CS片选信号PB10DC/RS数据/命令选择PB12RESET复位信号PB9LED背光控制初始化SPI接口时需要特别注意时钟极性和相位设置。对于大多数TFT屏控制器如ILI9341正确的配置是SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; // 时钟极性低电平空闲 SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; // 数据采样在第一个边沿1.2 SPI传输优化技巧在资源受限的STM32F103上SPI传输效率直接影响GUI的流畅度。以下是几个关键优化点DMA传输对于大批量数据如图片显示启用DMA可显著提升速度双缓冲机制在内存允许的情况下使用双缓冲减少等待时间时钟分频平衡速度与稳定性通常SPI2在72MHz系统时钟下可设置为PCLK/2void SPI_SetSpeed(SPI_TypeDef* SPIx, u8 SpeedSet) { SPIx-CR1 0XFFC7; if(SpeedSet 1) { // 高速模式 SPIx-CR1 | SPI_BaudRatePrescaler_2; // FsckFpclk/2 } else { // 低速模式 SPIx-CR1 | SPI_BaudRatePrescaler_32; // FsckFpclk/32 } SPIx-CR1 | 16; // SPI设备使能 }2. 基本图形元素实现2.1 画点与画线算法所有高级图形功能都建立在最基本的画点函数之上。一个优化的画点实现应包含void LCD_DrawPoint(u16 x, u16 y) { LCD_SetCursor(x, y); // 设置光标位置 Lcd_WriteData_16Bit(POINT_COLOR); // 写入颜色数据 }基于画点函数我们可以实现Bresenham画线算法这是计算机图形学中最经典的算法之一void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2) { int dx abs(x2 - x1), sx x1 x2 ? 1 : -1; int dy -abs(y2 - y1), sy y1 y2 ? 1 : -1; int err dx dy, e2; // 误差值 while(1) { LCD_DrawPoint(x1, y1); if (x1 x2 y1 y2) break; e2 2 * err; if (e2 dy) { err dy; x1 sx; } if (e2 dx) { err dx; y1 sy; } } }2.2 圆形与矩形绘制圆形绘制同样采用Bresenham算法以下是实现代码框架void gui_circle(u16 x0, u16 y0, u16 color, u8 r, u8 fill) { int x 0, y r; int d 3 - 2 * r; while(x y) { if(fill) { LCD_DrawLine(x0 - x, y0 y, x0 x, y0 y); LCD_DrawLine(x0 - x, y0 - y, x0 x, y0 - y); LCD_DrawLine(x0 - y, y0 x, x0 y, y0 x); LCD_DrawLine(x0 - y, y0 - x, x0 y, y0 - x); } else { LCD_DrawPoint(x0 x, y0 y); LCD_DrawPoint(x0 - x, y0 y); // 其他7个对称点... } if(d 0) { d d 4 * x 6; } else { d d 4 * (x - y) 10; y--; } x; } }3. 字体显示与中文支持3.1 英文字符显示原理英文字符显示通常采用点阵字模常见尺寸有6x12、8x16等。字模数据通常以数组形式存储// 8x16字体A的点阵数据示例 const u8 Font8x16_A[] { 0x00,0x00,0x80,0xC0,0xE0,0xF0,0xF8,0xFC, 0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x00,0x00 };显示函数需要逐位判断点阵数据在相应位置绘制像素void Show_Char(u16 x, u16 y, u16 fc, u16 bc, u8 num, u8 size, u8 mode) { u8 temp, t1, t; u16 colortemp; u16 x0 x; num num - ; // 获取偏移地址 for(t0; tsize; t) { if(size12) temp asc2_1206[num][t]; else if(size16) temp asc2_1608[num][t]; for(t10; t18; t1) { if(temp0x80) colortemp fc; else colortemp bc; LCD_DrawPoint(x, y, colortemp); temp 1; x; if(xlcddev.width) return; // 超出屏幕范围 if((x-x0)size) { x x0; y; break; } } } }3.2 中文字库的实现中文字符显示原理与英文类似但需要更大的点阵通常16x16或24x24。考虑到STM32F103有限的Flash空间可以采用以下策略部分字库仅包含项目所需的汉字外置存储将完整字库存放在外部SPI Flash或SD卡中压缩算法使用简单的RLE压缩减少存储空间// 16x16汉字中的点阵数据示例 const u8 Hzk16_zhong[] { 0x00,0x40,0x00,0x80,0x01,0x00,0x06,0x00, 0x1F,0xFC,0x60,0x00,0x80,0x00,0x00,0x00, 0x00,0x00,0x80,0x00,0x60,0x00,0x1F,0xFC, 0x06,0x00,0x01,0x00,0x00,0x80,0x00,0x40 };4. 图片显示与内存优化4.1 位图转换与存储在嵌入式系统中显示图片需要先将图片转换为C数组格式。常用工具包括Image2LcdWindows下的图形转换工具Python脚本使用Pillow库自定义转换流程在线转换工具如LCD Image Converter转换后的图片数据格式如下const unsigned char gImage_qq[1536] { /* 0X00,0X10,0X30,0X00,0X30,0X00,0X01,0X1B, */ 0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, 0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF, // ...更多数据... };4.2 显存管理与局部刷新在无显存的系统中直接操作屏幕会导致频繁的SPI通信。优化策略包括局部刷新只更新发生变化的部分区域缓冲机制在RAM中建立部分显存缓冲脏矩形算法跟踪需要更新的区域以下是局部刷新的实现示例void LCD_UpdateArea(u16 x1, u16 y1, u16 x2, u16 y2) { LCD_SetWindows(x1, y1, x2, y2); LCD_CS_CLR; LCD_RS_SET; for(u16 y y1; y y2; y) { for(u16 x x1; x x2; x) { u16 color GetPixelFromBuffer(x, y); Lcd_WriteData_16Bit(color); } } LCD_CS_SET; }5. 高级GUI功能实现5.1 按钮与触摸交互在无触摸屏的TFT上可以通过外部按键实现简单交互。按钮的实现需要考虑视觉状态正常、按下、禁用事件处理按下、释放、长按回调机制typedef struct { u16 x, y, width, height; u16 normalColor, pressedColor; char* text; void (*onClick)(void); } Button; void DrawButton(Button* btn, u8 pressed) { u16 color pressed ? btn-pressedColor : btn-normalColor; LCD_Fill(btn-x, btn-y, btn-xbtn-width, btn-ybtn-height, color); Show_Str(btn-x5, btn-y5, BLACK, color, btn-text, 16, 1); } u8 CheckButtonPress(Button* btn, u16 touchX, u16 touchY) { if(touchX btn-x touchX btn-xbtn-width touchY btn-y touchY btn-ybtn-height) { if(btn-onClick) btn-onClick(); return 1; } return 0; }5.2 动画与过渡效果在资源受限的系统上实现流畅动画需要特殊技巧帧率控制保持稳定的刷新率通常15-30fps双缓冲减少闪烁硬件加速利用定时器同步刷新void SimpleAnimation(u16 x, u16 y) { for(u8 r 5; r 30; r 2) { LCD_Fill(x-r-2, y-r-2, xr2, yr2, WHITE); // 清除上一帧 gui_circle(x, y, RED, r, 0); // 绘制当前帧 delay_ms(50); // 控制帧率 } }6. 性能优化与调试6.1 SPI传输速率测试通过测量特定操作的执行时间可以评估SPI配置的合理性void Test_SPI_Speed(void) { u32 start, end; start SysTick-VAL; LCD_Fill(0, 0, 100, 100, RED); end SysTick-VAL; u32 cycles start end ? start - end : (0xFFFFFF - end) start; u32 us cycles / (SystemCoreClock / 1000000); printf(Fill 100x100 area takes %d us\r\n, us); }6.2 内存使用分析在STM32F103这类资源受限的设备上内存管理至关重要。关键指标包括栈空间使用堆 fragmentation全局变量占用可以使用以下方法进行分析extern int _estack; // 定义在链接脚本中 extern int _Min_Stack_Size; void Check_Memory_Usage(void) { register uint32_t* stack_ptr asm(sp); uint32_t stack_used (uint32_t)_estack - (uint32_t)stack_ptr; uint32_t stack_free (uint32_t)stack_ptr - (uint32_t)_Min_Stack_Size; printf(Stack used: %d bytes, free: %d bytes\r\n, stack_used, stack_free); }7. 实际项目中的应用建议在工业控制、智能家居等实际项目中应用TFT GUI时建议采用以下架构分层设计硬件抽象层SPI驱动基本图形库画点、线、圆等高级GUI组件按钮、滑块等应用逻辑层资源管理使用const修饰符将常量数据放入Flash动态内存分配谨慎使用重复利用缓冲区可维护性统一的编码风格模块化设计完善的文档注释/** * brief 初始化GUI系统 * param None * retval None * note 该函数应在系统初始化后调用 */ void GUI_Init(void) { LCD_Init(); GUI_SetFont(Font8x16); GUI_SetTextColor(BLACK, WHITE); }在开发过程中使用版本控制系统如Git管理代码变更特别是当项目涉及多种显示效果和界面布局时。同时建立一套完整的测试用例覆盖所有图形基本功能和交互逻辑可以显著提高开发效率和系统稳定性。

相关文章:

STM32F103驱动2.4寸TFT屏实战:如何用SPI接口实现GUI图形库(画圆、写字、显示图片)

STM32F103驱动2.4寸TFT屏实战:如何用SPI接口实现GUI图形库(画圆、写字、显示图片) 在嵌入式系统开发中,图形用户界面(GUI)的实现往往是一个既具挑战性又充满成就感的部分。当我们将目光投向STM32F103这类资源有限的微控制器时&…...

EF Core 10 Vector Search扩展正式发布后,92%开发者踩中的5个语义检索陷阱及修复代码模板

第一章:EF Core 10 Vector Search扩展概述与核心价值 EF Core 10 Vector Search 扩展是微软官方在 Entity Framework Core 10 中引入的首个原生向量搜索支持模块,旨在将语义检索能力深度集成至 ORM 层。它并非独立 SDK,而是通过 Microsoft.En…...

蓝凌EKP V16.0二次开发实战:从日志规范到E签宝集成的全流程指南

1. 蓝凌EKP V16.0二次开发环境准备 刚接手蓝凌EKP V16.0二次开发任务时,我建议先搭建好开发环境。这个版本最大的变化是采用了SLF4JLogback日志框架,替代了之前的log4j。在实际项目中,我发现这种变化带来的性能提升确实很明显,特别…...

别再死记硬背了!用‘邻居’和‘广播’的故事,5分钟搞懂ISIS里的LSP和LSA区别

用生活故事解锁IS-IS协议:LSP的村民自治法则 想象一个与世隔绝的村庄,每当新村民加入时,大家会通过传阅自我介绍信来了解彼此——这恰似IS-IS协议中LSP的工作方式。在复杂的网络协议世界里,IS-IS的链路状态协议数据单元&#xff0…...

从零到一:Open5GS 5G核心网实战搭建与避坑指南(基于Ubuntu 22.04)

从零构建Open5GS 5G核心网:Ubuntu 22.04全流程实战手册 1. 环境准备与系统配置 在Ubuntu 22.04上部署Open5GS 5G核心网需要先搭建稳定的基础环境。建议使用物理服务器或配置不低于4核CPU/8GB内存/100GB存储的云实例,避免资源不足导致组件异常。 关键依赖…...

告别手动计算!用Xilinx DDS Compiler 4.0 IP核快速生成可调频调相的正弦波(附Modelsim仿真步骤)

基于Xilinx DDS Compiler 4.0的智能信号生成实战指南 在FPGA开发中,快速生成高精度、可动态调整的正弦波信号是通信系统测试、雷达信号处理等场景的刚需。传统手动编写DDS代码不仅耗时,还容易引入相位误差和频率分辨率问题。Xilinx的DDS Compiler 4.0 IP…...

Java 19+ Loom响应式改造:从Spring WebFlux到VirtualThread的4步平滑迁移路径(含可运行验证代码)

第一章:Java 19 Loom响应式改造:从Spring WebFlux到VirtualThread的4步平滑迁移路径(含可运行验证代码)Java 19 正式引入 Project Loom 的虚拟线程(Virtual Thread)作为预览特性,并在 Java 21 成…...

Elasticsearch LogsDB 发展历程:如何在不降低吞吐量的情况下,将索引大小减少多达 75%

Elasticsearch 最初是作为搜索引擎构建的。这种传承在日志存储方面是有代价的:每个事件都会扩散到多个磁盘结构中,每个结构都针对检索而非压缩进行了优化。LogsDB 改变了这一切。在我们的每晚基准测试中,企业模式(Enterprise mode…...

2026 最强本地 AI 神器!OpenClaw 一键部署教程

🚀 前言 2026 年开源圈爆火的「数字员工」OpenClaw(昵称小龙虾),GitHub 星标狂揽 28 万 ,凭「本地运行 零代码操作 自动干活」的核心优势圈粉无数!很多人误以为它是普通聊天 AI,实则是能真正…...

保姆级教程:用ESP32和Mixly做个电压监测器,手机实时看数据还能微信报警

智能家居电压监测系统:用ESP32与Mixly打造实时报警装置 最近在整理工作室时,发现角落里闲置的ESP32开发板,突然想到可以用它做个实用的家庭电压监测器。家里老房子电路老化,时不时会出现电压不稳的情况,之前烧坏过两台…...

面试官最爱问的模型评估指标:从电商推荐到风控模型,说说准确率、精确率、召回率怎么选

模型评估指标实战指南:从电商推荐到金融风控的指标选择艺术 当面试官抛出那个经典问题——"在电商推荐系统中,你会优先考虑精确率还是召回率?"时,大多数候选人会条件反射般背诵公式定义。但真正的高手,会先反…...

告别ION!Android 12 GKI 2.0 后,手把手教你用 DMA-BUF Heap 分配共享内存

Android内存管理演进:从ION到DMA-BUF Heap的迁移实战指南 在移动设备性能需求爆炸式增长的今天,内存管理子系统正经历着前所未有的变革。Android 12引入的GKI 2.0规范彻底重构了内核驱动开发范式,其中最关键的转变之一就是用DMA-BUF Heap全面…...

在FreeRTOS上跑NRF52低功耗,别让空闲任务和日志打印毁了你的电池计划

FreeRTOS与nRF52低功耗协同设计实战指南 引言 在嵌入式物联网设备开发中,nRF52系列芯片凭借其优异的低功耗特性成为众多无线连接方案的首选。但当开发者将FreeRTOS引入项目后,常常会遇到一个令人困扰的现象:原本在裸机环境下运行良好的低功耗…...

超越按键:用51单片机外部中断INT0实现红外遥控与旋转编码器计数

51单片机外部中断实战:红外遥控解码与旋转编码器计数进阶指南 当我们需要处理实时性要求极高的信号时,51单片机的外部中断功能就成为了不可或缺的利器。不同于轮询方式的低效,外部中断能够在信号到来时立即响应,为嵌入式系统带来真…...

别再手动敲AT指令了!用Python脚本自动化BC26连接OneNet全流程(附源码)

Python自动化BC26连接OneNet全攻略:告别AT指令手敲时代 每次调试NB-IoT设备时,重复输入几十条AT指令是否让您感到效率低下?当您需要在多个BC26模块上重复配置MQTT连接时,是否渴望一种更智能的工作方式?本文将带您用Pyt…...

你的竞争对手已经用 AI 降本增效,你还在纠结要不要投入?——2026企业大模型落地与Token降本实战指南

站在2026年4月的门槛上,企业间的竞争维度已经发生了根本性偏移。 当部分企业还在纠结AI投入的ROI(投资回报率)时,领先者早已完成了从“技术试水”到“全量智能”的跨越。 根据2026年一季度的最新数据,中国外贸枢纽义乌…...

实在 Agent 企业级智能体深度评测:从参数解析到全场景落地验证

① 核心架构解析与 TARS 大模型能力基线测试 在深入体验实在 Agent 之前,我们首先对其底层架构进行了拆解。这款产品最显著的特征在于其“大脑”与“手脚”的深度融合:自研的 TARS 大模型作为决策中枢,负责理解自然语言指令、拆解复杂任务逻辑…...

从splrep到splev:深入SciPy样条插值底层,看懂tck三元组,实现自定义插值控制

从splrep到splev:掌握SciPy样条插值的底层控制艺术 在数据科学和工程计算领域,插值技术就像一位隐形的调音师,能够将离散的数据点转化为流畅的曲线。当大多数用户满足于interp1d这类"一键式"解决方案时,真正的高手已经开…...

别再死记硬背公式了!用Python+SymPy实战拉格朗日乘子法,5分钟搞定约束优化问题

用PythonSymPy自动化求解约束优化问题:拉格朗日乘子法实战指南 在工程优化和机器学习领域,我们经常遇到需要在特定约束条件下寻找最优解的问题。传统的手工推导不仅耗时耗力,还容易在复杂的数学运算中出错。本文将带你用Python的SymPy库&…...

别再只会用Excel了!用Pandas的‘与’‘或’筛选,处理万行数据快10倍

别再只会用Excel了!用Pandas的‘与’‘或’筛选,处理万行数据快10倍 当Excel表格加载超过1万行数据时,滚动条开始变得迟缓,筛选菜单弹出需要等待,复杂的多条件公式让文件体积膨胀——这是许多数据分析师每天面对的困境…...

Docker 27日志审计增强配置,从默认file驱动到syslog+loki双活采集链路搭建

第一章:Docker 27 日志审计增强配置Docker 27 引入了更细粒度的日志审计能力,支持将容器运行时事件(如启动、停止、exec、pull、push)实时捕获并结构化输出至外部审计后端。默认的 json-file 驱动仅记录容器标准输出/错误&#xf…...

PyQt5 + HFSS:给你的仿真脚本做个专属GUI界面(零基础搭建指南)

PyQt5 HFSS:零基础打造专业仿真GUI全攻略 当你的HFSS脚本开始变得复杂,每次运行都要在命令行里输入一堆参数时,是否想过给它穿上得体的"外衣"?想象一下:一个直观的界面,同事只需点击几下就能启动…...

MATLAB调试进阶:巧用assignin和evalin实时查看和修改函数内部变量

MATLAB调试进阶:巧用assignin和evalin实时查看和修改函数内部变量 调试复杂算法时,最令人头疼的莫过于那些难以复现的边界条件错误。想象这样一个场景:你的粒子群优化算法在迭代到第137次时突然偏离预期轨迹,但断点调试会破坏时序…...

从仿真动画到数据分析:手把手教你用MATLAB给六杆机构做一次“全身检查”

从仿真动画到数据分析:手把手教你用MATLAB给六杆机构做一次"全身检查" 当机械工程师面对一个复杂的六杆机构时,单纯依靠数值计算结果往往难以直观理解机构的真实运动特性。就像医生需要通过X光片、CT扫描来全面诊断病人身体状况一样&#xff0…...

Hadoop 3.1.3集群部署后,你必须检查的5个关键点(附Web UI访问与进程状态排查)

Hadoop 3.1.3集群部署后必须验证的5个核心环节 当你完成Hadoop集群的基础部署后,真正的挑战才刚刚开始。许多新手在启动集群后陷入"看似正常却隐患重重"的困境——控制台没有报错,但数据处理时频繁出现诡异问题。本文将带你用系统化的验收清单…...

宝塔面板MySQL数据库意外停止怎么解决_优化my.cnf配置文件增加缓冲池

MySQL服务突然停止需先查mysqld状态和错误日志,常见原因包括内存不足、端口占用、buffer_pool配置过大或不合法;修改my.cnf前须确认版本、内存可用量及参数兼容性,并清理旧日志文件后重启。MySQL 服务突然停止,先看 mysqld 进程和…...

黄仁勋跑遍全球,到底在急什么?

我是地鼠,主要分享企业AI落地提效的实战经验。黄仁勋近期密集的全球行程和激烈言论,核心在于他正全力推动英伟达从一家芯片公司,转型为掌控全球AI基础设施“从电力到智能”转换权的关键枢纽,并为此应对来自竞争对手、供应链瓶颈和…...

为什么你的车载Docker镜像无法通过AUTOSAR CP兼容性测试?Docker 27的cgroups v2+seccomp-bpf深度配置清单曝光

第一章:车载Docker 27容器部署的AUTOSAR CP合规性总览在经典平台(CP)AUTOSAR架构中,严格的时间确定性、内存隔离、启动时序控制与功能安全(ISO 26262 ASIL-B及以上)要求与通用Linux容器运行时存在天然张力。…...

Java静态编译内存优化实战手册(GraalVM 24.1 LTS深度适配版)

第一章:Java静态编译与内存优化的范式变革长期以来,Java 依赖 JVM 动态加载、JIT 编译与垃圾回收机制,带来跨平台优势的同时也引入启动延迟、内存开销不可控及冷启动瓶颈。随着 GraalVM 的成熟与 JDK 21 对 java -jar --static(实…...

【Docker 27 AI容器调度终极指南】:20年SRE亲授GPU/内存/拓扑感知配置黄金参数(含实测QPS提升3.7倍数据)

第一章:Docker 27 AI容器调度演进与核心变革Docker 27 引入了面向AI工作负载的原生调度增强机制,标志着容器运行时从通用编排向智能感知型调度的关键跃迁。其核心变革在于将传统基于CPU/内存阈值的静态资源分配,升级为融合GPU显存占用率、CUD…...