【0.96寸 OLED屏实现1500Fps的帧率】STM32 软件、硬件SPI、I2C驱动总结

STM32F103VET6
STM32 Cube IDE


SPI版

OLED SPI 端口定义

本节引自STM32驱动0.96寸OLED液晶屏(12864液晶屏) —— 小牧同学
在这里插入图片描述
两种屏幕的引脚数不一样,左边的有7个引脚,而右边的只有6个。其次,端口的标号也不完全一样,第一个分别标为GND,VCC,D0,D1,RES,DC和CS第二个分别标为GND,VCC,SCL,SDA,RST,D/C。

七针OLED引脚定义

GND — 接地端口
VCC — 接3.3V电源端口
D0 — CLK时钟信号(等同于上面的SCL)
D1 — 数据端口(等同于上面的SDA)
RES — 复位端口(等同于上面的RST)
DC — 数据/命令选择引脚(等同于上面的D/C)
CS — 片选引脚(低电平有效,也就是所需要接低电平,我实际试验过不接该引脚也是可以正常使用的)

六针OLED引脚定义

GND — 接地端口
VCC — 接3.3V电源端口
SCL — CLK时钟信号端口
SDA — MOSI数据端口
RST — 复位端口
D/C — 数据/命令选择引脚

软件SPI

       指令解读见【51单片机快速入门指南】4.2: SSD1306 OLED屏(0.96寸、1.3寸)的I2C控制详解

       各引脚初始化如下,均为推挽输出:
在这里插入图片描述
       从【51单片机快速入门指南】5:软件SPI获取软件SPI程序。

修改控制电平的函数:

    GPIOx->BSRR = GPIO_Pin;

    GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;

由于我们需要的只是单向的、半双工的SPI,只需修改部分函数即可
在这里插入图片描述

       从SPI驱动0.96/1.3寸 OLED屏幕,易修改为DMA控制获取SPI版驱动程序。
将51特色的code改为const
在这里插入图片描述
修改对应引脚
在这里插入图片描述
修改延时函数
在这里插入图片描述
在这里插入图片描述
在主函数中添加测试程序:
在这里插入图片描述
如图,屏幕已轻松点亮。
在这里插入图片描述
帧率为180(见后文)
在这里插入图片描述
使用虚拟显存是时为46帧
在这里插入图片描述

硬件SPI

如图,使用硬件SPI
在这里插入图片描述
移除软件SPI的程序,修改OLED_WR_Byte函数
在这里插入图片描述

extern SPI_HandleTypeDef hspi1;
void OLED_WR_Byte(uint8_t dat, uint8_t cmd)
{
    OLED_CS_L();
    if (cmd)
        OLED_DC_H();
    else
        OLED_DC_L();
//    SOFT_SPI_RW_MODE2(dat);
    HAL_SPI_Transmit(&hspi1, &dat, 1, 10);
    OLED_DC_H();
    OLED_CS_H();
}

再次测试:
在这里插入图片描述
成功点亮
在这里插入图片描述
帧率为410
在这里插入图片描述

在oled.h中启用虚拟显存
在这里插入图片描述
修改OLED_Refresh_Gram函数,实现1024 Byte 显存连续写入
在这里插入图片描述

extern SPI_HandleTypeDef hspi1;
void OLED_Refresh_Gram(void)
{
#if OLED_BUFFER_MODE
//    uint16_t i;
    OLED_Set_Pos(0, 128);
//    for (i = 0; i < Max_Row / 8 * Max_Column; i++)
//    {
//        OLED_WR_Byte(OLED_GRAM[0][i], OLED_DATA);
//    }
    OLED_CS_L();
    OLED_DC_H();
    HAL_SPI_Transmit(&hspi1, OLED_GRAM[0], Max_Row / 8 * Max_Column, 10);
    OLED_DC_H();
    OLED_CS_H();
#endif
}

再次测试,仍能成功点亮,帧率为969。
在这里插入图片描述

启用DMA

在这里插入图片描述

再次修改OLED_Refresh_Gram函数

extern SPI_HandleTypeDef hspi1;
void OLED_Refresh_Gram(void)
{
#if OLED_BUFFER_MODE
//    uint16_t i;
    OLED_Set_Pos(0, 128);
//    for (i = 0; i < Max_Row / 8 * Max_Column; i++)
//    {
//        OLED_WR_Byte(OLED_GRAM[0][i], OLED_DATA);
//    }
    OLED_CS_L();
    OLED_DC_H();
//    HAL_SPI_Transmit(&hspi1, OLED_GRAM[0], Max_Row / 8 * Max_Column, 10);
    HAL_SPI_Transmit_DMA(&hspi1, OLED_GRAM[0], Max_Row / 8 * Max_Column);
    while(hspi1.State != HAL_SPI_STATE_READY);
    OLED_DC_H();
    OLED_CS_H();
#endif
}
帧率测试

帧率的显示:
在这里插入图片描述
在1ms中断中统计1s内的帧数
在这里插入图片描述
在每次更新屏幕内容时FPS_Count自加1
在这里插入图片描述
这个帧率已经远远超过屏幕本身所能提供的刷新率了。
在这里插入图片描述

I2C 版

软件I2C

将SCL设为推挽输出,SDA设为开漏上拉输出
在这里插入图片描述

【51单片机快速入门指南】4.2: SSD1306 OLED屏(0.96寸、1.3寸)的I2C控制详解【51单片机快速入门指南】4: 软件 I2C获取控制程序。

修改对应引脚,其他部分同SPI的步骤。
在这里插入图片描述

//SCL拉高 移植时需修改
void I2C_SCL_H(void)
{
	OLED_SCL_GPIO_Port->BSRR = OLED_SCL_Pin;
}

//SCL拉低 移植时需修改
void I2C_SCL_L(void)
{
	OLED_SCL_GPIO_Port->BSRR = (uint32_t)OLED_SCL_Pin << 16u;
}

//SDA拉高 移植时需修改
void I2C_SDA_H(void)
{
	OLED_SDA_GPIO_Port->BSRR = OLED_SDA_Pin;
}

//SDA拉低 移植时需修改
void I2C_SDA_L(void)
{
	OLED_SDA_GPIO_Port->BSRR = (uint32_t)OLED_SDA_Pin << 16u;
}

//读取SDA 移植时需修改
uint8_t I2C_SDA_Read(void)
{
	OLED_SDA_GPIO_Port->BSRR = (uint32_t)OLED_SDA_Pin << 16u;
	i2c_delay();
	return ((OLED_SDA_GPIO_Port->IDR & OLED_SDA_Pin) == 0?0:1);
}

测试:
在这里插入图片描述
帧率约为60帧
在这里插入图片描述

硬件I2C

在这里插入图片描述
修改OLED_WR_Byte函数

extern I2C_HandleTypeDef hi2c1;
void OLED_WR_Byte(uint8_t dat, uint8_t cmd)
{
	if (cmd)
		HAL_I2C_Mem_Write(&hi2c1, OLED_ADDRESS << 1, OLED_WriteData_Addr, I2C_MEMADD_SIZE_8BIT, &dat, 1, 10);
	else
		HAL_I2C_Mem_Write(&hi2c1, OLED_ADDRESS << 1, OLED_WriteCom_Addr, I2C_MEMADD_SIZE_8BIT, &dat, 1, 10);
}

此时帧率为49
在这里插入图片描述
在这里插入图片描述
开虚拟显存模式,修改OLED_Refresh_Gram函数

extern I2C_HandleTypeDef hi2c1;
void OLED_Refresh_Gram(void)
{
#if OLED_BUFFER_MODE
//    uint16_t i;
    OLED_Set_Pos(0, 128);
//    for (i = 0; i < Max_Row / 8 * Max_Column; i++)
//    {
//        OLED_WR_Byte(OLED_GRAM[0][i], OLED_DATA);
//    }
    HAL_I2C_Mem_Write(&hi2c1, OLED_ADDRESS << 1, OLED_WriteData_Addr, I2C_MEMADD_SIZE_8BIT, OLED_GRAM[0], Max_Row / 8 * Max_Column, 1000);
#endif
}

发现会更慢
在这里插入图片描述

DMA

在这里插入图片描述
开中断
在这里插入图片描述

再次修改OLED_Refresh_Gram函数

extern I2C_HandleTypeDef hi2c1;
void OLED_Refresh_Gram(void)
{
#if OLED_BUFFER_MODE
//    uint16_t i;
    OLED_Set_Pos(0, 128);
//    for (i = 0; i < Max_Row / 8 * Max_Column; i++)
//    {
//        OLED_WR_Byte(OLED_GRAM[0][i], OLED_DATA);
//    }
//    HAL_I2C_Mem_Write(&hi2c1, OLED_ADDRESS << 1, OLED_WriteData_Addr, I2C_MEMADD_SIZE_8BIT, OLED_GRAM[0], Max_Row / 8 * Max_Column, 1000);
    while(hi2c1.State != HAL_I2C_STATE_READY);
    HAL_I2C_Mem_Write_DMA(&hi2c1, OLED_ADDRESS << 1, OLED_WriteData_Addr, I2C_MEMADD_SIZE_8BIT, OLED_GRAM[0], Max_Row / 8 * Max_Column);
#endif
}

如图,瓶颈应为400kHz的I2C速度
在这里插入图片描述

<think>好的,我现在需要帮用户解决关于STM32F103C8T6使用OLED显示界面的问题。首先,我需要回顾一下这个芯片和OLED模块的基本情况。STM32F103C8T6是ST的Cortex-M3微控制器,有足够的资源驱动小型OLED,通常用的是SSD1306驱动0.96或1.3SPII2C接口。用户可能想知道如何连接硬件、初始化、编写驱动以及设计界面。 首先,硬件连接部分。用户需要确定OLED模块的接口类型,常见的是4线SPII2C。如果是SPI,需要连接CS、DC、RES、DIN、CLK这些引脚;I2C的话则用SCL和SDA,可能还有RES和DC,具体看模块设计。要提醒用户注意模块的工作电压,有些是3.3V,有些是5V,STM32是3.3V电平,需要匹配。 然后是软件部分。用户需要初始化OLED,可能包括GPIO配置、SPI/I2C外设初始化、发送初始化命令序列。接着是驱动函数的编写,比如清、写数据、画点等。之后是界面设计,比如显示字符、汉字、图形,可能还需要菜单结构或动画效果。 需要考虑用户可能遇到的常见问题,比如显示乱码、通信失败、刷新慢等。可能的原因包括引脚配置错误、通信速率过高、初始化命令不正确,或者内存不足导致缓冲区溢出。这时候需要建议用户检查硬件连接,使用示波器或逻辑分析仪抓波形,或者分步骤调试初始化代码。 另外,用户可能希望使用现有的库来简化开发,比如使用ST的HAL库或者第三方开源驱动。这时候可以推荐一些常用的库,或者提供代码示例。比如,使用HAL库的I2C函数发送命令和数据,或者模拟GPIO的方式实现SPI通信,特别是在高速情况下可能需要优化时序。 界面显示方面,可能需要分层次设计,比如底层驱动函数、中间件(显示字符、图形)、上层应用(菜单系统)。用户可能需要了解如何管理显示内容,比如双缓冲机制避免闪烁,或者局部刷新提高效率。 还要提醒用户注意资源限制,STM32F103C8T6的Flash和RAM较小,如果界面复杂可能需要优化代码,或者使用压缩的字体和图像数据。同时,如果使用中文字库,需要考虑存储空间,可能需要外部存储或者精简字库。 最后,可能的扩展功能,比如通过按键切换界面,或者与传感器数据结合显示实时信息。这时候需要涉及中断处理、状态机设计等,可能需要用户进一步学习相关内容。 总结下来,回答的结构应该是硬件连接、软件驱动、界面设计、常见问题及优化建议,同时提供示例代码和资源推荐。需要确保步骤清晰,用户能逐步实现,避免遗漏关键点,比如初始化序列或通信协议的正确配置。同时,使用中文,保持口语化,但技术术语准确,确保用户理解。</think>在STM32F103C8T6上使用OLED实现界面显示可分为以下步骤: --- ### 一、硬件准备 1. **OLED模块** - 推荐0.96SSD1306驱动芯片的OLED,支持I2C/SPI接口 - 分辨率:128x64像素 - 工作电压:3.3V(与STM32电平兼容) 2. **接线方式(以I2C为例)** | OLED引脚 | STM32引脚 | |----------|-----------| | SDA | PB7 | | SCL | PB6 | | VCC | 3.3V | | GND | GND | --- ### 二、软件驱动开发 1. **CubeMX配置** - 启用I2C1(模式:I2C,速度:400kHz) - 配置PB6(SCL)和PB7(SDA)为复用开漏模式 2. **OLED初始化代码** ```c void OLED_Init(void) { HAL_Delay(100); OLED_WriteCmd(0xAE); // 关闭显示 OLED_WriteCmd(0xD5); // 设置时钟分频 OLED_WriteCmd(0x80); OLED_WriteCmd(0xA8); // 设置复用率 OLED_WriteCmd(0x3F); OLED_WriteCmd(0xD3); // 设置显示偏移 OLED_WriteCmd(0x00); OLED_WriteCmd(0x40); // 设置起始行 // ...完整初始化序列参考SSD1306手册 OLED_WriteCmd(0xAF); // 开启显示 } ``` --- ### 三、显示功能实现 1. **基础显示函数** ```c // 清函数 void OLED_Clear(void) { memset(OLED_Buffer, 0, sizeof(OLED_Buffer)); OLED_Refresh(); } // 刷新函数(将显存数据写入OLED) void OLED_Refresh(void) { for(uint8_t page=0; page<8; page++) { OLED_WriteCmd(0xB0 + page); // 设置页地址 OLED_WriteCmd(0x02); // 列地址低4位 OLED_WriteCmd(0x10); // 列地址高4位 HAL_I2C_Mem_Write(&hi2c1, OLED_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &OLED_Buffer[page*128], 128, 100); } } ``` 2. **显示汉字(需字库)** ```c void OLED_ShowChinese(uint8_t x, uint8_t y, uint8_t index) { uint8_t i; for(i=0; i<16; i++) OLED_Buffer[x + (y+i/8)*128] = ChineseLib[index][i]; } ``` --- ### 四、界面设计技巧 1. **分层架构** - 底层驱动:`OLED_WriteCmd()`/`OLED_WriteData()` - 中间层:字符/图形绘制函数 - 应用层:菜单系统、动画逻辑 2. **菜单系统示例** ```c typedef struct { char *text; void (*func)(void); } MenuItem; MenuItem mainMenu[] = { {"温度监控", ShowTemp}, {"设置参数", EnterSetting}, {"系统信息", ShowSysInfo} }; ``` --- ### 五、常见问题解决 1. **无显示** - 检查I2C地址(通常0x78或0x7A) - 确认初始化序列正确 - 测量SCL/SDA波形 2. **显示乱码** - 检查显存刷新逻辑 - 确认字库数据对齐 - 降低I2C时钟频率测试 --- ### 六、优化建议 1. 使用双缓冲机制减少闪烁 2. 采用局部刷新策略(仅更新变化区域) 3. 通过DMA传输提升刷新效率 完整工程建议参考STM32CubeIDE+HAL库实现,典型帧率可达30fps以上。实际开发时可借助现成的开源驱动库(如`Arduino_SSD1306`移植版)加快开发进度。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乙酸氧铍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值