U8G2移植到STM32,SSD13XXXOLED(硬件SPI DMA通讯)

文章目录

  • 一、前言
    • 1.1 U8g2的特点
    • 1.2 U8G2的优势
    • 1.3 U8G2的下载地址
    • 1.4 U8g2支持的显示控制器
  • 二、STM32Cubexm SPI DMA配置
    • 2.1 SPI设置为半双工模式
    • 2.2 SPI DMA设置
    • 2.3 oled其他引脚配置
  • 三、移植U8G2框架
    • 3.1 精简U8G2库文件
    • 3.2 去掉csrc文件夹中无用的驱动文件
    • 3.3 文件移动到keil
    • 3.4 精简u8g2_d_setup.c(注意不是u8x8_setup.c)
    • 3.5 精简u8g2_d_memory.c
    • 3.6 编写回调函数
    • 3.7 main函数调用
  • 四、最终效果
  • 五、工程下载

一、前言

在嵌入式开发中,显示屏是常见的外设之一。OLED屏幕因其高对比度和低功耗,被广泛应用于各种嵌入式系统中。本文将详细介绍如何在STM32平台上使用SPI DMA方式移植U8G2库到 SSD1306 OLED屏幕。

1.1 U8g2的特点

U8G2是一个流行的图形显示库,用于控制嵌入式系统中的各种图形显示器。它由Oliver Kraus开发和维护,广泛支持多种显示屏控制器。U8G2库特别适合资源受限的微控制器平台,如Arduino、STM32和ESP8266/ESP32等。

  • U8g2的特点
    1. 多种显示器支持U8G2库支持许多不同的显示器控制器,包括常见的OLEDLCD显示器。例如,SSD1306、SSD1322、ST7920等控制器都在U8G2库的支持范围内。
    2. 多种接口支持U8G2支持多种接口协议,如I2CSPI和并行接口。这使得它可以与不同类型的显示器和微控制器配合使用。
    3. 多种字体和图形功能:U8G2库内置了丰富的字体和图形功能,可以显示文本、图形、图标和形状。用户可以选择不同的字体大小和样式来满足不同的显示需求。
    4. 双缓冲和单缓冲模式:U8G2支持双缓冲和单缓冲模式。双缓冲模式可以实现更流畅的屏幕刷新效果,但需要更多的内存;单缓冲模式则适用于内存较为紧张的系统。
    5. 易于使用的API:U8G2提供了一组易于使用的API,使得开发者可以快速实现复杂的图形显示功能。其API设计简洁明了,适合各种编程水平的用户。

1.2 U8G2的优势

  • 跨平台性:U8G2库可以在多种平台上使用,包括Arduino、STM32、ESP8266/ESP32等。这使得开发者可以在不同的硬件平台上复用相同的代码。
  • 丰富的文档和社区支持:U8G2库有详细的文档和大量的示例代码,方便开发者快速上手。此外,U8G2社区活跃,开发者可以在GitHub和各大技术论坛上找到许多有价值的讨论和资源。
  • 性能优化:U8G2库经过优化,可以在资源受限的微控制器上高效运行。同时,U8G2还支持硬件加速和DMA传输等高级功能,以提高显示性能。

1.3 U8G2的下载地址

u8g2 git链接

1.4 U8g2支持的显示控制器

SSD1305SSD1306SSD1309SSD1312SSD1316SSD1320
SSD1322SSD1325SSD1327SSD1329SSD1606SSD1607
SH1106SH1107SH1108SH1122T6963RA8835
LC7981PCD8544PCF8812HX1230UC1601UC1604
UC1701UC1608UC1610UC1611UC1617UC1638
ST7511ST7528ST7565ST7567ST7571ST7586
ST7588ST75256ST75320NT7534ST7920IST3020
IST7920LD7032KS0108KS0713HD44102T7932
SED1520SBN1661IL3820MAX7219

可以说,基本上主流的显示控制器都支持,比如我们常见的SSD1306等,读者在使用该库之前请查阅自己的OLED显示控制器是否处于支持列表中。

二、STM32Cubexm SPI DMA配置

这里就不说怎么创建工程,怎么设置时钟,基本配置自行配置

2.1 SPI设置为半双工模式

OLED只有发送数据的模式,所以只需要发送就可以了。(速度不要大于9M)

在这里插入图片描述

2.2 SPI DMA设置

在这里插入图片描述

2.3 oled其他引脚配置

CS拉高 其他拉低
在这里插入图片描述

然后就可以生成代码了

三、移植U8G2框架

3.1 精简U8G2库文件

如果没下载,下载地址在 1.3 请自行下载

  • U8g2支持多种显示驱动的屏幕,因为源码中也包含了各个驱动对应的文件(因此不需要自己去写屏幕底层驱动了),为了减小整个工程的代码体积和芯片资源占用,在移植U8g2时,可以删除一些无用的文件。
  • 这里我们主要关注的是U8g2库文件中的csrc文件夹
    在这里插入图片描述

3.2 去掉csrc文件夹中无用的驱动文件

这些驱动文件通常是u8x8_d_xxx.cxxx包括驱动的型号屏幕分辨率ssd1306驱动芯片的OLED,使用u8x8_ssd1306_128x64_noname.c这个文件,其它的屏幕驱动和分辨率的文件可以删掉。

在这里插入图片描述

3.3 文件移动到keil

  • .c 文件加入keil 工程

在这里插入图片描述

  • 添加 .h 文件的路径

在这里插入图片描述

3.4 精简u8g2_d_setup.c(注意不是u8x8_setup.c)

由于本文使用的OLED是SPI接口,只留一个本次要用到u8g2_Setup_ssd1306_128x64_noname_f就好(如果是IIC接口,需要使用u8g2_Setup_ssd1306_i2c_128x64_noname_f这个函数,多了i2c注意区分),其它的可以删掉或注释掉。
在这里插入图片描述

注意,与这个函数看起来十分相似的函数的有:

  • u8g2_Setup_ssd1306_128x64_noname_1
  • u8g2_Setup_ssd1306_128x64_noname_2
  • u8g2_Setup_ssd1306_128x64_noname_f
  • u8g2_Setup_ssd1306_i2c_128x64_noname_1
  • u8g2_Setup_ssd1306_i2c_128x64_noname_2
  • u8g2_Setup_ssd1306_i2c_128x64_noname_f

其中,前面3个,是给SPI接口的OLED用的,后面3个,是给I2C用的函数最后的数字或字母,代表显示时的buf大小:

  • 1:128字节

  • 2:256字节

  • f:1024字节也有可能是512字节

3.5 精简u8g2_d_memory.c

u8g2_d_memory.c文件中,由于用到的u8g2_Setup_ssd1306_128x64_noname_f函数中,只调用了u8g2_m_16_8_f这个函数,所以只用留下这个函数,如果你用的其他函数,在8g2_d_memory.c留下它相对应调用到的函数即可,其它的函数要删掉或注释,否则编译时很可能会导致内存不足
在这里插入图片描述

3.6 编写回调函数

#include "u8g2.h"

#define MD_OLED_RST_Clr() HAL_GPIO_WritePin(OLED_RES_GPIO_Port,OLED_RES_Pin,GPIO_PIN_RESET) //oled 复位端口操作
#define MD_OLED_RST_Set() HAL_GPIO_WritePin(OLED_RES_GPIO_Port,OLED_RES_Pin,GPIO_PIN_SET)

uint8_t u8x8_byte_4wire_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int,void *arg_ptr)
{
    switch (msg)
    {
        case U8X8_MSG_BYTE_SEND: /*通过SPI发送arg_int个字节数据*/
//           HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)arg_ptr, arg_int);while(hspi1.TxXferCount);
			/*配置了DMA取消上一行注释即可*/
			HAL_SPI_Transmit(&hspi1,(uint8_t *)arg_ptr,arg_int,200);
			/*这是CubeMX生成的初始化*/
            break;
        case U8X8_MSG_BYTE_INIT: /*初始化函数*/
            break;
        case U8X8_MSG_BYTE_SET_DC: /*设置DC引脚,表明发送的是数据还是命令*/
			HAL_GPIO_WritePin(OLED_DC_GPIO_Port,OLED_DC_Pin,(GPIO_PinState)arg_int);
            break;
        case U8X8_MSG_BYTE_START_TRANSFER: 
            u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_enable_level);
            u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->post_chip_enable_wait_ns, NULL);
            break;
        case U8X8_MSG_BYTE_END_TRANSFER: 
            u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->pre_chip_disable_wait_ns, NULL);
            u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);
            break;
        default:
            return 0;
    }
    return 1;
}

uint8_t u8x8_stm32_gpio_and_delay(U8X8_UNUSED u8x8_t *u8x8,
    U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int,
    U8X8_UNUSED void *arg_ptr) 
{
    switch (msg)
    {
        case U8X8_MSG_GPIO_AND_DELAY_INIT: /*delay和GPIO的初始化,在main中已经初始化完成了*/
            break;
        case U8X8_MSG_DELAY_MILLI: /*延时函数*/
            HAL_Delay(arg_int);     //调用谁stm32系统延时函数
            break;
        case U8X8_MSG_GPIO_CS: /*片选信号*/ //由于只有一个SPI设备,所以片选信号在初始化时已经设置为常有效
            HAL_GPIO_WritePin(OLED_CS_GPIO_Port, OLED_CS_Pin, (GPIO_PinState)arg_int);
            break;
        case U8X8_MSG_GPIO_DC: /*设置DC引脚,表明发送的是数据还是命令*/
            HAL_GPIO_WritePin(OLED_DC_GPIO_Port,OLED_DC_Pin,(GPIO_PinState)arg_int);
            break;
        case U8X8_MSG_GPIO_RESET:
            break;
    }
    return 1;
}

void u8g2Init(u8g2_t *u8g2)
{
/********************************************     
U8G2_R0     //不旋转,不镜像     
U8G2_R1     //旋转90度
U8G2_R2     //旋转180度   
U8G2_R3     //旋转270度
U8G2_MIRROR   //没有旋转,横向显示左右镜像
U8G2_MIRROR_VERTICAL    //没有旋转,竖向显示镜像
********************************************/
//    u8g2_Setup_sh1106_128x64_noname_2(u8g2, U8G2_R0, u8x8_byte_4wire_hw_spi, u8x8_stm32_gpio_and_delay);  // 初始化1.3寸OLED u8g2 结构体
	u8g2_Setup_ssd1306_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_4wire_hw_spi, u8x8_stm32_gpio_and_delay);  // 初始化0.96寸OLED u8g2 结构体
	u8g2_InitDisplay(u8g2);     //初始化显示
	u8g2_SetPowerSave(u8g2, 0); //开启显示
}
/*官方logo的Demo*/
void draw(u8g2_t *u8g2)
{
    u8g2_SetFontMode(u8g2, 1); /*字体模式选择*/
    u8g2_SetFontDirection(u8g2, 0); /*字体方向选择*/
    u8g2_SetFont(u8g2, u8g2_font_inb24_mf); /*字库选择*/
    u8g2_DrawStr(u8g2, 0, 20, "U");
    
    u8g2_SetFontDirection(u8g2, 1);
    u8g2_SetFont(u8g2, u8g2_font_inb30_mn);
    u8g2_DrawStr(u8g2, 21,8,"8");
        
    u8g2_SetFontDirection(u8g2, 0);
    u8g2_SetFont(u8g2, u8g2_font_inb24_mf);
    u8g2_DrawStr(u8g2, 51,30,"g");
    u8g2_DrawStr(u8g2, 67,30,"\xb2");
    
    u8g2_DrawHLine(u8g2, 2, 35, 47);
    u8g2_DrawHLine(u8g2, 3, 36, 47);
    u8g2_DrawVLine(u8g2, 45, 32, 12);
    u8g2_DrawVLine(u8g2, 46, 33, 12);
  
    u8g2_SetFont(u8g2, u8g2_font_4x6_tr);
    u8g2_DrawStr(u8g2, 1,54,"github.com/olikraus/u8g2");
}

3.7 main函数调用

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_SPI1_Init();
  /* USER CODE BEGIN 2 */
u8g2_t u8g2; // 显示器初始化结构体
  MD_OLED_RST_Set(); //显示器复位拉高
  u8g2Init(&u8g2);   //显示器调用初始化函数
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

四、最终效果

在这里插入图片描述

五、工程下载

代码工程下载


文章是自己总结而记录,有些知识点没说明白的,请各位看官多多提意见,多多交流,欢迎大家留言
如果技术交流可以加以下群,方便沟通
QQ群:370278903
点击链接加入群聊【蜡笔小芯的嵌入式交流群】
![])

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/649338.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PLC无线通讯模块在触摸屏与台达PLC之间的应用教程

本方案可以作为信捷触摸屏与台达PLC之间在台达专用协议下将有线通讯改为无线通讯的应用实例。此方案中信捷TG765触摸屏作为主站,台达DVP-32EH PLC作为从站,采用西安达泰电子日系PLC无线通讯终端——DTD435MA-V384,作为实现无线通讯的硬件设备…

【面试必看】系统设计

系统设计 1. 认证与授权 Authentication(认证) 是验证您的身份的凭据(例如用户名/用户 ID 和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户,也叫做身份/用户…

所有平台均可发布,矩阵操作+工具+素材,自动混剪8090后怀旧视频

“怀旧”这个词对于80、90后来说,总能勾起一阵阵心中的涟漪。无论是那些留存在记忆深处的动画经典,还是代代相传的游戏主题曲,亦或是那个时代特有的玩具,都构成了他们共同的美好回忆。就像乘坐一艘穿梭机,怀旧视频能够…

无人机行业所需企业服务等级证书详解

无人机行业中的民用无人机企业服务等级证书是对企业在无人机服务领域的能力、技术、管理和服务质量等方面的一种全面认可和评定。这种证书的存在,不仅有助于企业了解自身服务能力和水平,提升服务质量,增强市场竞争力,促进规范化管…

【有效的数独】python

目录 很好,超级暴力做法,能过就行,优雅个锤子啊 优雅的做法 ,绅士,噢听说叫模拟 很好,超级暴力做法,能过就行,优雅个锤子啊 我直接11个2层for循环暴力秒杀 class Solution:def is…

使用 SwanLab 进行可视化 MNIST 手写体识别训练

使用 SwanLab 进行可视化 MNIST 手写体识别训练 在线演示demo 本案例主要: 使用pytorch进行CNN(卷积神经网络)的构建、模型训练与评估使用swanlab跟踪超参数、记录指标和可视化监控整个训练周期 一、相关简介 SwanLab SwanLab是一款开源…

Vue基础(数据绑定、export使用)

1、简介 在使用vue开发的过程中,经常会遇到一些容易混淆的问题,因此,在本文中进行汇总操作,只有通过不断总结学习,才能更好掌握vue的使用(每天进步一点)。 2、数据绑定 在js中定义数据&#xf…

三分钟一条AI小和尚视频 ,日引300+创业粉。单日变现四位数 全套工具

经过六个月的不懈努力和无数次的尝试错误,我终于找到了一个高效引流和积累粉丝的新策略,并愿意与大家无私分享。这一次,我将详尽地介绍这个方法,建议朋友们多次观看以彻底掌握其精髓。 简而言之,该策略主要依托于AI绘…

Spring 原理详解

1. Bean的作用域 Bean在Spring中表示的是Spring管理的对象,Bean的作用域是只Bean在Spring框架中的某种行为模式。 在Spring中,支持6中作用域: singleton:单例作用域,在整个 Spring IoC 容器中,只创建一个…

Json差异比较

json差异比较 如何比较两个json的差异 代码实现 导入依赖 <dependency><groupId>cn.xiaoandcai</groupId><artifactId>json-diff</artifactId><!-- 旧版本可能存在某些缺陷。版本请以maven仓库最版为准。 --><version>4.1.3-RC1-R…

没想到,一个小妙招让桌面运维效率翻倍

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 我的网工朋友大家好。 咱们都知道&#xff0c;电脑用久了&#xff0c;总会出些小毛病&#xff0c;比如桌面图标不显示了&#xff0c;C盘又满了&a…

springboot+minio 文件上传

前期准备 需要先安装minio文件服务器&#xff0c;请参考我上一篇文章 pom.xml 版本 本次使用的是springboot2.7.16 版本&#xff0c; minio 版本是8.2.2 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-pare…

利用AI办公工具类API,大幅提高办公效率

AI办公工具类API是一项革命性的技术&#xff0c;利用人工智能的力量为办公场景提供了许多创新的解决方案。借助AI办公工具类API&#xff0c;用户可以实现自动化的文档处理、语音转文字、图像识别、数据分析等多种功能&#xff0c;大大提高了办公效率和工作质量。此外&#xff0…

Uni-App开发 导入(引入)Vant-Weapp组件;支持vue3/vue2版本和微信小程序

文章目录 目录 文章目录 操作流程 小结 概要安装流程技术细节小结 概要 Vant Weapp官网&#xff1a;Vant Weapp - 轻量、可靠的小程序 UI 组件库 准备工作&#xff0c;需要确保自己的电脑上已安装Hbuilde和node 全程操作的环境都需要这些配合才能运行上&#xff0c;可参考作者…

如何彻底搞懂组合(Composite)设计模式?

当我们在设计系统对象关系时&#xff0c;有时候会碰到这样一种场景&#xff0c;一个对象中包含了另一组对象&#xff0c;两者构成一种”部分-整体”的关联关系。 正如上图中所展示的&#xff0c;当我们面对这样一种对象关系时&#xff0c;通常都需要分别构建单独的访问方式&…

数据挖掘案例-航空公司客户价值分析

文章目录 1. 案例背景2. 分析方法与过程2.1 分析流程步骤2.2 分析过程1. 数据探索分析2. 描述性统计分析3. 分布分析1.客户基本信息分布分析2. 客户乘机信息分布分析3. 客户积分信息分布分析 4. 相关性分析 3. 数据预处理3.1 数据清洗3.2 属性约束3. 3 数据转换 4. 模型构建4. …

【面经】单片机

1、单片机IO口工作方式 输入 模拟输入&#xff08;GPIO_Mode_AIN&#xff09;&#xff1a;关闭施密特触发器&#xff0c;将电压信号传送到片上外设模块&#xff0c;通常用于连接模拟信号源。浮空输入&#xff08;GPIO_Mode_IN_FLOATING&#xff09;&#xff1a;在浮空输入状态…

回收站清空的文件怎么恢复?8个方法公开(2024更新版)

“我太粗心了&#xff0c;刚想恢复部分回收站中误删的重要文件&#xff0c;一不小心把回收站清空了&#xff0c;现在还有什么方法可以恢复它们吗&#xff1f;” 在数字时代&#xff0c;电脑已经成为我们日常生活和工作中不可或缺的工具。然而&#xff0c;随着我们对电脑的依赖加…

etcd 和 MongoDB 的混沌(故障注入)测试方法

最近在对一些自建的数据库 driver/client 基础库的健壮性做混沌&#xff08;故障&#xff09;测试, 去验证了解业务的故障处理机制和恢复时长. 主要涉及到了 MongoDB 和 etcd 这两个基础组件. 本文会介绍下相关的测试方法. MongoDB 中的故障测试 MongoDB 是比较世界上热门的文…

【算法】排序——加更

补充1个排序&#xff1a;希尔排序 思路&#xff1a;首先定义一个gap,从第0个数开始&#xff0c;每隔一个gap取出一个数&#xff0c;将取出来的数进行比较&#xff0c;方法类似插入排序。第二轮从第二个数开始&#xff0c;每隔一个gap取出一个数再进行插入排序。四轮就可以取完…