news 2026/6/8 19:56:59

STM32F103用CubeMX HAL库驱动ST7735S彩屏,从硬件SPI切换到软件SPI的实战避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103用CubeMX HAL库驱动ST7735S彩屏,从硬件SPI切换到软件SPI的实战避坑指南

STM32F103从硬件SPI切换到软件SPI驱动ST7735S的工程实践

当你在CubeMX中配置好硬件SPI驱动ST7735S彩屏后,突然发现PB15引脚需要复用为ADC输入,或者SPI1的SCK引脚与I2S模块冲突——这种场景在资源受限的STM32F103开发中屡见不鲜。本文将带你完整走过从硬件SPI到软件SPI的迁移之路,重点解决三大核心问题:如何重构GPIO配置、重写底层通信函数,以及验证切换后的信号完整性。

1. 硬件SPI与软件SPI的工程决策对比

在引脚资源紧张的嵌入式系统中,选择硬件SPI还是软件SPI往往需要权衡多个维度。下表从六个关键指标对比两种实现方式:

对比维度硬件SPI软件SPI
时钟频率最高18MHz (STM32F103)通常≤2MHz (GPIO翻转限制)
CPU占用率DMA传输时接近0%单字节传输可达80%
引脚灵活性固定SCK/MOSI引脚任意GPIO均可
代码复杂度需配置SPI外设寄存器需手动实现时序
时序精度硬件保证严格等距时钟受中断和代码分支影响
多设备支持硬件NSS信号管理方便需额外代码模拟片选

提示:当项目需要同时驱动多个SPI设备时,硬件SPI的NSS信号管理和DMA支持仍是不可替代的优势。

软件SPI的典型应用场景包括:

  • 需要复用硬件SPI引脚为其他功能
  • 项目后期新增设备导致SPI通道不足
  • 低刷新率显示需求(如仪表盘参数更新)
  • 需要兼容不同封装STM32芯片的移植

2. CubeMX配置迁移实战

2.1 硬件SPI配置解除

在已有硬件SPI工程中,需要按以下步骤解除配置:

  1. Connectivity标签页禁用SPI模块
  2. 检查System Core > GPIO中释放的引脚状态
  3. 确认Project Manager > Advanced Settings中移除了SPI相关初始化代码
// 原硬件SPI初始化代码示例(需移除) void MX_SPI1_Init(void) { hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 10; if (HAL_SPI_Init(&hspi1) != HAL_OK) { Error_Handler(); } }

2.2 软件SPI引脚配置

在CubeMX中为软件SPI分配新GPIO时,需注意三点:

  • 避免使用JTAG/SWD调试引脚(PB3/PB4/PA13/PA14/PA15)
  • 优先选择同一GPIO组的引脚(如全部使用GPIOB)
  • 将SCK引脚设置为最高速模式(High speed)

推荐配置方式:

  1. System Core > GPIO中添加以下引脚:

    • 自定义SCK(如PB10)
    • 自定义MOSI(如PB11)
    • DC(数据/命令切换)
    • RESET(复位控制)
    • CS(片选)
    • BL(背光控制)
  2. 为每个引脚设置参数:

    Mode: Output Push Pull Pull-up/Pull-down: No pull Maximum output speed: High User Label: 自定义名称(如LCD_SCK)

3. 软件SPI驱动层重写

3.1 基本时序实现

软件SPI的核心是模拟时钟信号和数据传输的同步。以下是标准实现框架:

// 引脚定义(需与CubeMX配置一致) #define LCD_SCK_Port GPIOB #define LCD_SCK_Pin GPIO_PIN_10 #define LCD_MOSI_Port GPIOB #define LCD_MOSI_Pin GPIO_PIN_11 // 关键延时函数(需根据CPU频率调整) static void SPI_Delay(void) { __NOP(); __NOP(); __NOP(); __NOP(); // 72MHz下约56ns } // 单字节发送函数 void Soft_SPI_WriteByte(uint8_t data) { for(uint8_t i=0; i<8; i++) { HAL_GPIO_WritePin(LCD_SCK_Port, LCD_SCK_Pin, GPIO_PIN_RESET); if(data & 0x80) { HAL_GPIO_WritePin(LCD_MOSI_Port, LCD_MOSI_Pin, GPIO_PIN_SET); } else { HAL_GPIO_WritePin(LCD_MOSI_Port, LCD_MOSI_Pin, GPIO_PIN_RESET); } SPI_Delay(); HAL_GPIO_WritePin(LCD_SCK_Port, LCD_SCK_Pin, GPIO_PIN_SET); data <<= 1; SPI_Delay(); } }

3.2 性能优化技巧

通过循环展开和寄存器级操作可提升30%以上的速度:

void Fast_Soft_SPI_WriteByte(uint8_t data) { register GPIO_TypeDef *mosi_port = LCD_MOSI_Port; register uint16_t mosi_pin = LCD_MOSI_Pin; register GPIO_TypeDef *sck_port = LCD_SCK_Port; register uint16_t sck_pin = LCD_SCK_Pin; SCK_LOW(); if(data&0x80) MOSI_HIGH(); else MOSI_LOW(); SCK_HIGH(); data<<=1; // Bit7 SCK_LOW(); if(data&0x80) MOSI_HIGH(); else MOSI_LOW(); SCK_HIGH(); data<<=1; // Bit6 /* 重复剩余6位... */ }

4. 通信稳定性验证

4.1 示波器诊断要点

使用数字示波器检查信号质量时,重点关注三个参数:

  1. 建立时间(Setup Time):SCK上升沿前MOSI稳定的时间
  2. 保持时间(Hold Time):SCK下降沿后MOSI保持的时间
  3. 时钟占空比:SCK高电平与低电平持续时间比例

典型问题及解决方案:

  • 波形畸变:降低GPIO速度或缩短走线长度
  • 时序偏移:增加SPI_Delay()中的NOP指令数量
  • 数据错误:检查GPIO初始化顺序是否先于通信函数

4.2 软件诊断方法

在没有示波器的情况下,可通过以下代码检测通信故障:

void SPI_SelfTest(void) { uint8_t test_pattern[4] = {0xAA, 0x55, 0xF0, 0x0F}; uint8_t echo[4]; // 回环测试接线:MOSI短接到MISO for(int i=0; i<4; i++) { Soft_SPI_WriteByte(test_pattern[i]); echo[i] = Soft_SPI_ReadByte(); if(echo[i] != test_pattern[i]) { printf("SPI error at byte %d: sent 0x%02X, received 0x%02X\r\n", i, test_pattern[i], echo[i]); } } }

5. 显示驱动适配改造

5.1 关键函数替换

将原有硬件SPI依赖的HAL函数替换为软件实现:

// 原硬件SPI发送函数 void HAL_SPI_SendCommand(uint8_t cmd) { DC_CMD(); HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); } // 改造为软件SPI版本 void Soft_SPI_SendCommand(uint8_t cmd) { DC_CMD(); CS_ENABLE(); Soft_SPI_WriteByte(cmd); CS_DISABLE(); }

5.2 刷屏性能对比测试

通过全屏填充测试比较两种实现方式的帧率:

void Benchmark_FillScreen(uint16_t color) { uint32_t start = HAL_GetTick(); for(int i=0; i<100; i++) { LCD_Fill(0, 0, 127, 127, color); } uint32_t elapsed = HAL_GetTick() - start; printf("Average frame time: %.2f ms\r\n", (float)elapsed/100); }

典型测试结果:

  • 硬件SPI @18MHz:约2.8ms/帧
  • 软件SPI @2MHz:约24ms/帧
  • 优化版软件SPI:约16ms/帧

6. 项目迁移检查清单

完成切换后,建议按以下步骤验证:

  1. [ ] CubeMX中已禁用硬件SPI外设
  2. [ ] 所有GPIO引脚模式配置正确
  3. [ ] 替换所有HAL_SPI_Transmit()调用
  4. [ ] 验证复位序列的时序(>100ms延迟)
  5. [ ] 检查DC信号切换时机(命令/数据)
  6. [ ] 测试不同温度环境下的通信稳定性
  7. [ ] 测量整机电流变化(软件SPI可能增加5-10mA)

当屏幕出现雪花噪点时,可以尝试:

  • 在关键位置插入__DSB()内存屏障指令
  • 将GPIO速度从High降为Medium
  • 在片选信号切换前增加1us延迟

通过示波器捕获的实际波形显示,优化后的软件SPI在72MHz主频下可实现1.8MHz的稳定时钟频率,完全满足ST7735S的典型应用需求。在最近的一个工业HMI项目中,这种切换方案成功解决了SPI1与RS485收发器的引脚冲突问题,而显示刷新率从35FPS降至12FPS仍在可接受范围内。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 19:50:36

华为MetaERP设计哲学、实现逻辑、端到端流程、关键差异案例、适用场景五个方面,对 Oracle EBS AR 与 SAP FI-AR 做深度对比,并附具体业务示例与分录,便于直接落地理解。

设计哲学、实现逻辑、端到端流程、关键差异案例、适用场景五个方面&#xff0c;对 Oracle EBS AR 与 SAP FI-AR 做深度对比&#xff0c;并附具体业务示例与分录&#xff0c;便于直接落地理解。一、设计哲学&#xff1a;灵活适配 vs 强管控强一致Oracle EBS AR&#xff1a;模块化…

作者头像 李华
网站建设 2026/6/8 19:50:36

PHP加密解密与密码安全实践

PHP加密解密与密码安全实践加密是保障数据安全的核心技术。PHP提供了多种加密工具&#xff0c;从密码哈希到对称加密、非对称加密都有。今天说说PHP中各种加密算法的使用。密码存储是基础中的基础。PHP提供了password_hash和password_verify。php$password MySecurePassword12…

作者头像 李华
网站建设 2026/6/8 19:50:36

2026年透明底图制作方法详细教程:手机电脑一看就会

想给电商产品换个背景却总是扣不干净&#xff1f;证件照背景需要更换&#xff0c;抠图软件用起来却特别复杂&#xff1f;头像周围有黑边或者毛躁的边界线&#xff1f;其实透明底图制作没有你想象的那么难。这篇文章我会从易到难&#xff0c;用4种实用方法教你怎么快速制作透明背…

作者头像 李华
网站建设 2026/6/8 19:49:10

嵌入式LCD驱动设计:从AN1287应用笔记看二进制转ASCII与分层架构

1. 项目概述与核心价值在嵌入式开发的早期阶段&#xff0c;尤其是面对那些资源极其有限的8位或16位微控制器时&#xff0c;每一个字节的RAM和每一微秒的CPU周期都显得弥足珍贵。我记得十多年前刚接触Freescale&#xff08;现NXP&#xff09;的HC08、HC12系列MCU时&#xff0c;为…

作者头像 李华