news 2026/8/12 17:24:36

GD32VW553驱动0.96寸SPI单色OLED屏(SSD1306)移植与实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GD32VW553驱动0.96寸SPI单色OLED屏(SSD1306)移植与实战

GD32VW553驱动0.96寸SPI单色OLED屏(SSD1306)移植与实战

最近在玩GD32VW553这块RISC-V的开发板,想给它接个小屏幕显示点信息,0.96寸的OLED屏是个不错的选择,小巧省电。网上找了一圈,发现很多STM32的驱动代码,但直接用在GD32VW553上不行,得自己动手移植。今天我就把整个移植过程,从硬件连接到代码修改,一步步分享给大家,保证你跟着做就能点亮屏幕。

1. 硬件准备与连接

1.1 认识我们的OLED屏

咱们用的这块屏是市面上最常见的0.96寸OLED,驱动芯片是SSD1306,通信接口是SPI。先看看它的基本参数:

参数规格
工作电压3.3V
工作电流约15mA
模块尺寸27.3 x 27.8 mm
分辨率128 x 64 像素
驱动芯片SSD1306
通信协议SPI
管脚数量7 Pin (2.54mm间距排针)

注意:这块屏是3.3V供电的,千万别接5V,会烧掉!

1.2 引脚定义与连接

OLED模块有7个引脚,每个引脚的作用如下:

OLED引脚功能说明连接GD32VW553引脚
GND电源地GND
VCC电源正(3.3V)3V3
SCLSPI时钟线(SCK)PA11
SDASPI数据线(MOSI)PA9
RES复位信号PA7
DC数据/命令选择PA6
CS片选信号PB11

这里我选择的引脚都是GD32VW553上比较常用的GPIO,你也可以根据自己需求调整,只要保证SPI引脚对应正确就行。

连接的时候要注意:

  1. 先断电再接线,防止短路
  2. 杜邦线要插牢,接触不良最头疼
  3. 电源和地线一定要接对

2. 工程搭建与文件准备

2.1 获取驱动源码

首先需要找到OLED的驱动代码。我用的这份代码是从一个通用的SSD1306驱动修改过来的,包含了基本的显示功能。

提示:如果你手头没有现成的驱动代码,可以到文章开头提到的资料链接下载,提取码是8888。

2.2 创建工程模板

如果你还没有GD32VW553的工程,建议先创建一个基础工程模板。GD32官方提供了完整的开发环境配置指南,这里我就不赘述了。

关键步骤:

  1. 使用GD32VW553的官方固件库
  2. 配置好系统时钟和延时函数
  3. 确保SPI外设能正常使用

2.3 添加OLED驱动文件

把下载的OLED驱动文件复制到你的工程目录里,主要需要两个文件:

  • oled.c- 驱动实现文件
  • oled.h- 头文件,包含函数声明和宏定义

在工程中添加这两个文件,然后在main.c中包含oled.h头文件。

3. 驱动代码移植与修改

3.1 修改头文件配置

首先打开oled.h文件,我们需要修改引脚定义和数据类型定义。

#ifndef __OLED_H #define __OLED_H #include "gd32vw55x.h" #include "systick.h" // 定义常用的数据类型 #ifndef u8 #define u8 uint8_t #endif #ifndef u16 #define u16 uint16_t #endif #ifndef u32 #define u32 uint32_t #endif // 延时函数宏定义 #ifndef delay_ms #define delay_ms(x) delay_1ms(x) #endif #ifndef delay_us #define delay_us(x) delay_1us(x) #endif //-----------------OLED端口定义---------------- // 使能相关外设时钟 #define OLED_RCU_ENABLE() rcu_periph_clock_enable(RCU_GPIOA); \ rcu_periph_clock_enable(RCU_GPIOB); \ rcu_periph_clock_enable(RCU_SPI) // SPI引脚定义 #define OLED_SCL_PORT GPIOA #define OLED_SCL_PIN GPIO_PIN_11 #define OLED_SCL_AF GPIO_AF_0 #define OLED_SDA_PORT GPIOA #define OLED_SDA_PIN GPIO_PIN_9 #define OLED_SDA_AF GPIO_AF_0 // 控制引脚定义 #define OLED_RES_PORT GPIOA #define OLED_RES_PIN GPIO_PIN_7 #define OLED_DC_PORT GPIOA #define OLED_DC_PIN GPIO_PIN_6 #define OLED_CS_PORT GPIOB #define OLED_CS_PIN GPIO_PIN_11 // 控制引脚操作宏 #define OLED_RES_Clr() gpio_bit_write(OLED_RES_PORT, OLED_RES_PIN, 0) // RES低电平 #define OLED_RES_Set() gpio_bit_write(OLED_RES_PORT, OLED_RES_PIN, 1) // RES高电平 #define OLED_DC_Clr() gpio_bit_write(OLED_DC_PORT, OLED_DC_PIN, 0) // DC低电平 #define OLED_DC_Set() gpio_bit_write(OLED_DC_PORT, OLED_DC_PIN, 1) // DC高电平 #define OLED_CS_Clr() gpio_bit_write(OLED_CS_PORT, OLED_CS_PIN, 0) // CS低电平 #define OLED_CS_Set() gpio_bit_write(OLED_CS_PORT, OLED_CS_PIN, 1) // CS高电平 // 命令/数据模式定义 #define OLED_CMD 0 // 写命令 #define OLED_DATA 1 // 写数据 // 函数声明 void OLED_Init(void); void OLED_Clear(void); void OLED_Refresh(void); void OLED_ShowChar(u8 x, u8 y, u8 chr, u8 size1, u8 mode); void OLED_ShowString(u8 x, u8 y, u8 *chr, u8 size1, u8 mode); void OLED_ShowChinese(u8 x, u8 y, u8 num, u8 size1, u8 mode); void OLED_ShowNum(u8 x, u8 y, u32 num, u8 len, u8 size1, u8 mode); void OLED_ShowPicture(u8 x, u8 y, u8 sizex, u8 sizey, u8 BMP[], u8 mode); // ... 其他函数声明 #endif

这里有几个关键点需要注意:

  1. 把原来的#include "sys.h"改成了#include "systick.h",因为GD32的延时函数在systick.h
  2. 引脚定义要和你实际连接的引脚一致
  3. GPIO复用功能GPIO_AF_0表示SPI功能,这是GD32的配置方式

3.2 修改初始化函数

接下来修改oled.c中的OLED_Init()函数,这是驱动的核心初始化部分。

void OLED_Init(void) { // 1. 使能相关外设时钟 OLED_RCU_ENABLE(); // 2. 配置SPI引脚为复用功能 gpio_af_set(OLED_SCL_PORT, OLED_SCL_AF, OLED_SCL_PIN); gpio_af_set(OLED_SDA_PORT, OLED_SDA_AF, OLED_SDA_PIN); // 3. 设置引脚模式 gpio_mode_set(OLED_SCL_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, OLED_SCL_PIN); gpio_mode_set(OLED_SDA_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, OLED_SDA_PIN); gpio_mode_set(OLED_RES_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, OLED_RES_PIN); gpio_mode_set(OLED_DC_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, OLED_DC_PIN); gpio_mode_set(OLED_CS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, OLED_CS_PIN); // 4. 设置输出选项 gpio_output_options_set(OLED_SCL_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, OLED_SCL_PIN); gpio_output_options_set(OLED_SDA_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, OLED_SDA_PIN); gpio_output_options_set(OLED_RES_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, OLED_RES_PIN); gpio_output_options_set(OLED_DC_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, OLED_DC_PIN); gpio_output_options_set(OLED_CS_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, OLED_CS_PIN); // 5. 初始化控制引脚状态 OLED_CS_Set(); // 片选拉高,初始不选中 OLED_DC_Set(); // 数据/命令线拉高 // 6. 配置SPI参数 spi_parameter_struct spi_init_struct; /* 配置SPI参数 */ spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; // 全双工模式 spi_init_struct.device_mode = SPI_MASTER; // 主机模式 spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; // 8位数据帧 spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE; // 时钟极性相位 spi_init_struct.nss = SPI_NSS_SOFT; // 软件控制片选 spi_init_struct.prescale = SPI_PSC_32; // 32分频 spi_init_struct.endian = SPI_ENDIAN_MSB; // 高位在前 // 7. 初始化SPI spi_init(&spi_init_struct); /* 使能SPI */ spi_enable(); // 8. OLED硬件复位 OLED_RES_Clr(); // 复位引脚拉低 delay_ms(200); // 保持低电平200ms OLED_RES_Set(); // 复位引脚拉高 // 9. 发送初始化命令序列 OLED_WR_Byte(0xAE, OLED_CMD); // 关闭显示 OLED_WR_Byte(0x00, OLED_CMD); // 设置低列地址 OLED_WR_Byte(0x10, OLED_CMD); // 设置高列地址 OLED_WR_Byte(0x40, OLED_CMD); // 设置起始行地址 OLED_WR_Byte(0x81, OLED_CMD); // 设置对比度控制 OLED_WR_Byte(0xCF, OLED_CMD); // 设置对比度值 OLED_WR_Byte(0xA1, OLED_CMD); // 设置段重映射,正常显示 OLED_WR_Byte(0xC8, OLED_CMD); // 设置COM扫描方向,正常显示 OLED_WR_Byte(0xA6, OLED_CMD); // 设置正常显示 OLED_WR_Byte(0xA8, OLED_CMD); // 设置多路复用比率 OLED_WR_Byte(0x3f, OLED_CMD); // 1/64占空比 OLED_WR_Byte(0xD3, OLED_CMD); // 设置显示偏移 OLED_WR_Byte(0x00, OLED_CMD); // 无偏移 OLED_WR_Byte(0xd5, OLED_CMD); // 设置显示时钟分频比 OLED_WR_Byte(0x80, OLED_CMD); // 设置时钟为100帧/秒 OLED_WR_Byte(0xD9, OLED_CMD); // 设置预充电周期 OLED_WR_Byte(0xF1, OLED_CMD); // 预充电15个时钟,放电1个时钟 OLED_WR_Byte(0xDA, OLED_CMD); // 设置COM引脚硬件配置 OLED_WR_Byte(0x12, OLED_CMD); // 备用配置 OLED_WR_Byte(0xDB, OLED_CMD); // 设置VCOMH OLED_WR_Byte(0x40, OLED_CMD); // 设置VCOM取消选择级别 OLED_WR_Byte(0x20, OLED_CMD); // 设置页寻址模式 OLED_WR_Byte(0x02, OLED_CMD); // 页寻址模式 OLED_WR_Byte(0x8D, OLED_CMD); // 电荷泵设置 OLED_WR_Byte(0x14, OLED_CMD); // 使能电荷泵 OLED_WR_Byte(0xA4, OLED_CMD); // 禁用整个显示开启 OLED_WR_Byte(0xA6, OLED_CMD); // 禁用反色显示 // 10. 清屏并开启显示 OLED_Clear(); OLED_WR_Byte(0xAF, OLED_CMD); // 开启显示 }

这个初始化函数做了很多事情,我简单解释一下关键步骤:

  • 步骤1-4:配置GPIO,SPI引脚要设置为复用功能,控制引脚设置为推挽输出
  • 步骤5:初始化控制引脚状态,防止误操作
  • 步骤6-7:配置SPI参数,这里用的是软件控制片选,时钟32分频
  • 步骤8:硬件复位OLED,这是必须的,确保OLED从确定状态开始工作
  • 步骤9:发送一系列初始化命令,这些命令是SSD1306芯片规定的
  • 步骤10:清屏并开启显示

注意:SPI的时钟极性相位配置SPI_CK_PL_HIGH_PH_2EDGE要和OLED屏的时序要求一致,不同厂家的屏可能不同。

3.3 修改字节写入函数

SPI通信的核心是OLED_WR_Byte()函数,它负责通过SPI发送一个字节的数据或命令。

void OLED_WR_Byte(u8 dat, u8 cmd) { uint8_t recv_data = 0; // 1. 设置DC引脚:命令模式还是数据模式 if(cmd) OLED_DC_Set(); // 数据模式 else OLED_DC_Clr(); // 命令模式 // 2. 拉低片选,选中OLED OLED_CS_Clr(); // 3. 等待发送缓冲区为空 while(RESET == spi_flag_get(SPI_FLAG_TBE)); // 4. 通过SPI发送数据 spi_data_transmit(dat); // 5. 等待接收缓冲区不为空(全双工模式需要读取) while(RESET == spi_flag_get(SPI_FLAG_RBNE)); recv_data = spi_data_receive(); // 6. 拉高片选,取消选中 OLED_CS_Set(); OLED_DC_Set(); // 恢复DC为高电平 }

这个函数的关键点:

  1. DC引脚控制:OLED通过DC引脚区分发送的是命令还是数据,0是命令,1是数据
  2. 片选控制:每次通信前要拉低CS,通信完成后拉高
  3. SPI发送:使用GD32的SPI库函数发送数据
  4. 接收数据:全双工模式下发送的同时也会接收,虽然用不上但要读取以清空缓冲区

4. 编写测试程序

4.1 主函数测试代码

现在驱动已经移植好了,我们来写个测试程序验证一下。在main.c中添加以下代码:

#include "gd32vw55x.h" #include "systick.h" #include <stdio.h> #include "main.h" #include "gd32vw553h_eval.h" #include "oled.h" #include "bmp.h" // 如果有图片数据的话 int main(void) { // 1. 系统初始化 systick_config(); // 配置系统滴答定时器 eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1); // 2. 初始化外设(可选) gd_eval_led_init(LED1); gd_eval_com_init(EVAL_COM0); // 3. 初始化OLED OLED_Init(); OLED_ColorTurn(0); // 0正常显示,1反色显示 OLED_DisplayTurn(0); // 0正常方向,1旋转180度 uint8_t t = ' '; // 用于显示变化的ASCII字符 while(1) { // 测试1:显示图片(如果有图片数据) // OLED_ShowPicture(0, 0, 128, 64, BMP1, 1); // OLED_Refresh(); // delay_1ms(1000); // OLED_Clear(); // 测试2:显示汉字 OLED_ShowChinese(0, 0, 0, 16, 1); // 显示第0个汉字,16x16大小 OLED_ShowChinese(18, 0, 1, 16, 1); // 显示第1个汉字 OLED_ShowChinese(36, 0, 2, 16, 1); // 显示第2个汉字 OLED_ShowChinese(54, 0, 3, 16, 1); // 显示第3个汉字 OLED_ShowChinese(72, 0, 4, 16, 1); // 显示第4个汉字 // 测试3:显示英文字符串 OLED_ShowString(8, 16, "GD32VW553", 16, 1); OLED_ShowString(20, 32, "OLED Test", 16, 1); // 测试4:显示ASCII字符 OLED_ShowString(0, 48, "ASCII:", 16, 1); OLED_ShowChar(48, 48, t, 16, 1); // 显示ASCII字符 t++; if(t > '~') t = ' '; // 循环显示 // 测试5:显示数字 OLED_ShowNum(90, 48, 1234, 4, 16, 1); OLED_Refresh(); // 更新显示 delay_1ms(500); // 延时500ms OLED_Clear(); // 清屏 // 测试6:不同大小的字符 OLED_ShowString(0, 0, "ABC", 8, 1); // 6x8字体 OLED_ShowString(0, 8, "ABC", 12, 1); // 6x12字体 OLED_ShowString(0, 20, "ABC", 16, 1); // 8x16字体 OLED_ShowString(0, 36, "ABC", 24, 1); // 12x24字体 OLED_Refresh(); delay_1ms(500); OLED_Clear(); } }

4.2 添加字库文件

要显示汉字和字符,还需要字库文件。通常是一个oledfont.h文件,里面包含了各种字体的点阵数据。

如果你没有现成的字库,可以自己生成或者从网上下载。字库文件通常包含:

  • ASCII字符的点阵数据(6x8, 8x16等)
  • 常用汉字的点阵数据(16x16, 24x24等)

oled.c中需要包含这个字库文件:

#include "oledfont.h"

5. 常见问题与调试技巧

5.1 屏幕不亮或显示异常

如果屏幕完全不亮,按以下步骤排查:

  1. 检查电源:用万用表测量VCC和GND之间是否为3.3V
  2. 检查接线:确认所有引脚连接正确,特别是SCL、SDA、DC、RES、CS这5根线
  3. 检查复位:RES引脚要先拉低200ms再拉高,这个时序很重要
  4. 检查SPI配置:确认SPI时钟频率不要太高,OLED屏一般支持到10MHz左右

5.2 显示内容错乱

如果屏幕能亮但显示内容不对:

  1. 检查DC引脚:发送命令时DC要拉低,发送数据时DC要拉高
  2. 检查SPI模式:SSD1306通常使用模式0或模式3,确认SPI_CK_PL_HIGH_PH_2EDGE是否正确
  3. 检查初始化命令:确保所有初始化命令都正确发送
  4. 检查显存更新:修改显存后要调用OLED_Refresh()才能更新到屏幕

5.3 显示有残影或闪烁

  1. 调整对比度:修改初始化命令中的对比度值(0xCF)
  2. 检查电源稳定性:OLED对电源比较敏感,确保3.3V稳定
  3. 调整刷新频率:降低刷新频率或增加延时

5.4 性能优化建议

  1. 减少全屏刷新:只刷新变化的部分,不要每次都全屏刷新
  2. 使用局部更新:SSD1306支持设置显示区域,可以只更新特定区域
  3. 优化SPI速度:在保证稳定的前提下提高SPI时钟频率
  4. 使用DMA传输:如果需要大量数据传输,可以考虑使用SPI的DMA功能

6. 实际应用扩展

移植成功后,你可以在实际项目中使用这个OLED驱动了。这里分享几个我在项目中常用的技巧:

显示实时数据:比如显示传感器数据、系统状态等

// 显示温度数据 void show_temperature(float temp) { char buffer[20]; sprintf(buffer, "Temp: %.1fC", temp); OLED_ShowString(0, 0, buffer, 16, 1); OLED_Refresh(); }

制作简单菜单:通过按键切换显示内容

typedef enum { MENU_MAIN, MENU_SETTINGS, MENU_INFO } menu_state_t; menu_state_t current_menu = MENU_MAIN; void update_display(void) { OLED_Clear(); switch(current_menu) { case MENU_MAIN: OLED_ShowString(0, 0, "Main Menu", 16, 1); OLED_ShowString(0, 20, "1. Settings", 16, 1); OLED_ShowString(0, 40, "2. Info", 16, 1); break; case MENU_SETTINGS: // 显示设置菜单 break; case MENU_INFO: // 显示信息 break; } OLED_Refresh(); }

显示进度条:用于显示进度或电量

void draw_progress_bar(u8 x, u8 y, u8 width, u8 height, u8 progress) { // 画边框 OLED_DrawLine(x, y, x + width, y, 1); OLED_DrawLine(x, y + height, x + width, y + height, 1); OLED_DrawLine(x, y, x, y + height, 1); OLED_DrawLine(x + width, y, x + width, y + height, 1); // 填充进度 u8 fill_width = (width - 2) * progress / 100; for(u8 i = 0; i < height - 2; i++) { OLED_DrawLine(x + 1, y + 1 + i, x + 1 + fill_width, y + 1 + i, 1); } OLED_Refresh(); }

移植过程中最常遇到的问题就是引脚配置不对和SPI时序问题。如果遇到问题,建议先用逻辑分析仪或示波器抓一下SPI波形,看看时钟、数据、片选这些信号是否正常。

还有一个坑要注意:GD32的SPI库函数和STM32的有些差异,特别是时钟配置部分。如果发现通信不正常,可以尝试调整SPI的预分频值,降低通信速度试试。

好了,以上就是GD32VW553驱动0.96寸OLED屏的完整移植过程。按照这个步骤来,应该能顺利点亮屏幕。在实际项目中,你可以根据需要添加更多显示功能,比如图表、动画等。

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

YOLOv8图像实例分割训练中的关键配置与优化技巧

1. YOLOv8实例分割入门指南 第一次接触YOLOv8做实例分割时&#xff0c;我被它的"一站式"解决方案惊艳到了。相比传统需要组合多个模型才能完成的任务&#xff0c;YOLOv8只需要几行代码就能实现从训练到部署的全流程。实例分割这个技术简单来说就是让AI不仅能找到图片…

作者头像 李华
网站建设 2026/7/14 15:45:26

Flowise国际化:多语言界面与内容生成支持

Flowise国际化&#xff1a;多语言界面与内容生成支持 1. 为什么需要多语言支持&#xff1f; 当你第一次打开Flowise界面时&#xff0c;看到的可能是英文界面。如果你的团队中有不熟悉英语的成员&#xff0c;或者你的产品需要面向全球用户&#xff0c;这就成了一个实实在在的问…

作者头像 李华
网站建设 2026/7/14 15:45:25

告别手动复制!PDF-Parser-1.0一键提取文字、表格、公式

告别手动复制&#xff01;PDF-Parser-1.0一键提取文字、表格、公式 1. 为什么需要PDF解析工具 在日常工作和学习中&#xff0c;我们经常需要从PDF文档中提取内容。无论是学术论文中的研究数据、商业报告中的关键信息&#xff0c;还是技术文档中的代码示例&#xff0c;手动复制…

作者头像 李华
网站建设 2026/7/14 15:45:26

腾讯又打钱了!力压百度成“榜一大哥”了

哈喽&#xff0c;各位&#xff0c;我是小程程。原以为腾讯和龙虾之间的瓜就算完事了。晚上刷到有推友还在吐槽昨天「腾讯轻量云」给 &#x1f99e; 打钱少了。留言区有网友补充说&#xff0c;腾讯又打钱了。于是我去龙虾的 GitHub 仓库刷了一下&#xff0c;发现腾讯确实又给 &a…

作者头像 李华
网站建设 2026/7/14 15:45:27

基于Hunyuan-MT-7B的卷积神经网络翻译质量评估系统

基于Hunyuan-MT-7B的卷积神经网络翻译质量评估系统 1. 系统效果展示 今天给大家展示一个很有意思的项目&#xff1a;用卷积神经网络来评估翻译质量&#xff0c;结合了Hunyuan-MT-7B的强大翻译能力。这个系统不仅能自动给翻译打分&#xff0c;还能给出具体的优化建议&#xff…

作者头像 李华