文章目录
- 一、前言
- 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的特点
- 多种显示器支持:
U8G2
库支持许多不同的显示器控制器,包括常见的OLED
和LCD
显示器。例如,SSD1306、SSD1322、ST7920等控制器都在U8G2库的支持范围内。 - 多种接口支持:
U8G2
支持多种接口协议,如I2C
、SP
I和并行接口
。这使得它可以与不同类型的显示器和微控制器配合使用。 - 多种字体和图形功能:U8G2库内置了丰富的字体和图形功能,可以显示文本、图形、图标和形状。用户可以选择不同的字体大小和样式来满足不同的显示需求。
- 双缓冲和单缓冲模式:U8G2支持双缓冲和单缓冲模式。双缓冲模式可以实现更流畅的屏幕刷新效果,但需要更多的内存;单缓冲模式则适用于内存较为紧张的系统。
- 易于使用的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支持的显示控制器
SSD1305 | SSD1306 | SSD1309 | SSD1312 | SSD1316 | SSD1320 |
---|---|---|---|---|---|
SSD1322 | SSD1325 | SSD1327 | SSD1329 | SSD1606 | SSD1607 |
SH1106 | SH1107 | SH1108 | SH1122 | T6963 | RA8835 |
LC7981 | PCD8544 | PCF8812 | HX1230 | UC1601 | UC1604 |
UC1701 | UC1608 | UC1610 | UC1611 | UC1617 | UC1638 |
ST7511 | ST7528 | ST7565 | ST7567 | ST7571 | ST7586 |
ST7588 | ST75256 | ST75320 | NT7534 | ST7920 | IST3020 |
IST7920 | LD7032 | KS0108 | KS0713 | HD44102 | T7932 |
SED1520 | SBN1661 | IL3820 | MAX7219 |
可以说,基本上主流的显示控制器都支持,比如我们常见的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.c,xxx包括驱动的型号和屏幕分辨率。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
点击链接加入群聊【蜡笔小芯的嵌入式交流群】