手把手教你用STM32F103C8T6和CubeMX点亮1.3寸TFT屏(附HAL库驱动代码)

📅 2026/7/3 7:13:29 👁️ 阅读次数 📝 编程学习
手把手教你用STM32F103C8T6和CubeMX点亮1.3寸TFT屏(附HAL库驱动代码)

从零玩转STM32F103C8T6驱动1.3寸TFT屏实战指南

第一次拿到STM32开发板和那块小巧的1.3寸TFT屏时,我和大多数初学者一样既兴奋又忐忑。这块240×240分辨率的彩色屏幕能显示文字、图形甚至图片,是嵌入式人机交互的入门神器。但真正动手时才发现,从硬件连接到代码移植处处是坑——USB供电不足导致屏幕闪烁、排针接触不良引发显示异常、HAL库函数在中断中的诡异卡死、汉字显示莫名乱码...这些问题不解决,连最基本的"Hello World"都显示不出来。

本文将用最直白的语言,带你一步步避开这些新手陷阱。不同于网上零散的教程,我们会从某宝同款开发板(大越创新STM32F103C8T6核心板)和屏幕的实拍接线开始,到CubeMX的SPI配置细节,最后用经过实战检验的HAL库驱动代码点亮屏幕。特别提醒:文末提供的驱动代码已修复所有已知BUG,包括那个让无数人抓狂的HAL_Delay()中断卡死问题。

1. 硬件连接:那些商家不会告诉你的细节

1.1 供电方案选择

很多新手接好线后发现屏幕背光不亮或闪烁,第一反应是代码有问题,其实80%的情况是供电不足。实测发现:

  • USB供电(不推荐):电脑USB口5V/500mA输出,经开发板LDO降压到3.3V后,带不动屏幕背光(峰值电流可达300mA)
  • ST-Link供电(推荐):通过调试接口的3.3V引脚直接供电,电流更稳定
  • 外接电源(最佳):使用5V/1A以上的手机充电器接开发板DC口

提示:正常供电时屏幕背光应常亮白色(未烧录程序时可能是黑色,这属于正常现象)

1.2 排针接触不良的应急处理

开发板与屏幕通常通过2.54mm排针连接,常见问题及解决方案:

问题现象可能原因解决方法
屏幕完全不亮排针未插到底用力按压屏幕与开发板接合处
显示花屏/闪烁接触电阻过大用酒精清洁排针后重新插拔
复位后显示正常接触不良在排针两侧点少量热熔胶固定
// 快速检测屏幕是否通电的代码片段 while(1) { LCD_Clear(RED); // 全屏红色 HAL_Delay(500); LCD_Clear(BLUE); // 全屏蓝色 HAL_Delay(500); }

1.3 SPI引脚定义对照

必须严格对应开发板上的SPI2接口(不同厂商引脚可能不同):

屏幕引脚STM32F103C8T6引脚功能说明
SCLPB13SPI2_SCK
SDAPB15SPI2_MOSI
RESPB12复位信号
DCPB11数据/命令选择
CSPB10片选信号
BLKPB1背光控制(可选)

2. CubeMX配置:避开SPI的隐藏陷阱

2.1 时钟树配置要点

虽然CubeMX可以自动生成时钟配置,但TFT屏对SPI时钟稳定性要求较高,建议:

  1. 选择外部晶振(HSE)作为时钟源
  2. 系统时钟设为72MHz(STM32F103最大值)
  3. SPI2时钟分频系数设为4(得到18MHz SPI时钟)

2.2 SPI参数详解

Connectivity > SPI2中需要特别注意:

  • Mode:Full-Duplex Master
  • Hardware NSS:Disable(使用软件片选)
  • Data Size:8 bits
  • First Bit:MSB First
  • Baud Rate:Prescaler 4(实际速率=18MHz)
  • CPOL/CPHA:Low/1 Edge(模式0)
// 正确的SPI初始化代码(由CubeMX生成) hspi2.Instance = SPI2; hspi2.Init.Mode = SPI_MODE_MASTER; hspi2.Init.Direction = SPI_DIRECTION_2LINES; hspi2.Init.DataSize = SPI_DATASIZE_8BIT; hspi2.Init.CLKPolarity = SPI_POLARITY_LOW; hspi2.Init.CLKPhase = SPI_PHASE_1EDGE; hspi2.Init.NSS = SPI_NSS_SOFT; hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi2.Init.TIMode = SPI_TIMODE_DISABLE; hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi2.Init.CRCPolynomial = 10;

2.3 GPIO配置的隐藏技巧

除了SPI引脚,还需要配置控制引脚:

  1. 将RES、DC、CS引脚设为GPIO_Output
  2. 初始电平设置:
    • RES:High(低电平复位)
    • DC:High(默认数据模式)
    • CS:High(默认不选中)

注意:PB1(背光控制)建议配置为PWM输出,可实现亮度调节

3. 驱动代码移植:修复HAL库的那些坑

3.1 文件结构规划

将驱动代码整合到工程中时,建议采用以下结构:

Core/ ├── Inc/ │ ├── lcd.h # 屏幕驱动头文件 │ └── font.h # 字库定义 ├── Src/ │ ├── lcd.c # 屏幕驱动实现 │ └── font.c # 字库数据 ├── Drivers/ │ └── STM32F1xx_HAL_Driver/ └── STM32F103C8T6_FLASH.ld # 链接脚本

3.2 HAL_Delay()中断卡死解决方案

这是HAL库的经典BUG,解决方法如下:

  1. stm32f1xx_it.c中找到SysTick_Handler
  2. 修改系统定时器中断优先级:
    HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); // 最高优先级
  3. 确保其他中断优先级数值更大(如定时器设为1)

3.3 汉字显示乱码问题分析

乱码通常由以下原因导致:

  • 字库编码不匹配:确认使用GBK编码字库
  • 函数调用位置:在lcd.c中封装显示函数
  • 内存对齐问题:添加__attribute__((aligned(4)))
// 正确显示汉字的封装函数 void Show_Chinese(uint16_t x, uint16_t y, uint16_t color, char *text) { Draw_Font24B(x, y, color, text); // 实际调用放在lcd.c中 }

4. 实战应用:从显示变量到绘制图形

4.1 动态数据显示技巧

在嵌入式UI中,频繁刷新会导致闪烁,推荐采用局部更新:

// 显示动态变量的优化方法 uint8_t old_value = 0; uint8_t sensor_value = Get_Sensor_Data(); if(sensor_value != old_value) { LCD_DrawRectangle(50, 50, 100, 30, WHITE); // 清空区域 LCD_ShowNum(50, 50, sensor_value, 3, 16, BLACK); old_value = sensor_value; }

4.2 绘图性能优化

240x240分辨率全屏刷新约需58ms(@18MHz SPI),优化策略:

  1. 使用DMA+SPI传输(速度提升3倍)
  2. 实现双缓冲机制
  3. 限制刷新区域(LCD_SetWindows
// DMA传输配置示例 hdma_spi2_tx.Instance = DMA1_Channel5; hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE; hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_spi2_tx.Init.Mode = DMA_NORMAL; hdma_spi2_tx.Init.Priority = DMA_PRIORITY_HIGH;

4.3 实用代码片段

  • 进度条绘制

    void Draw_ProgressBar(uint16_t x, uint16_t y, uint16_t width, uint8_t percent) { LCD_DrawRectangle(x, y, width, 20, GRAY); LCD_DrawRectangle(x, y, width*percent/100, 20, BLUE); }
  • 菜单系统框架

    typedef struct { char *text; void (*action)(void); } MenuItem; MenuItem main_menu[] = { {"温度监控", Show_Temp}, {"系统设置", Enter_Setting}, {"关于", Show_About} };

在调试过程中,最耗时的往往是硬件连接这类基础问题。记得第一次成功点亮屏幕时,那种成就感至今难忘——虽然只是显示了一行简单的"Hello World",但这意味着你已经打通了STM32与外部设备的通信链路。后续的图形界面、触摸控制等功能,都是在这个基础上搭建起来的。