U8g2库的STM32硬件SPI(DMA)移植教程

U8g2库的STM32硬件SPI移植教程(HAL、OLED显示、四线SPI)前言U8g2简介U8g2是什么U8g2支持的显示控制器U8g2的优势

CubexMX的配置RCC配置外部高速晶振(精度更高)——HSE:SYS配置:Debug设置成Serial Wire(否则可能导致芯片自锁):时钟树配置:SPI1配置半双工(全双工):作为OLED的通讯方式:(注意这里的配置)DMA配置:工程配置:

U8g2移植准备U8g2库文件精简U8g2库文件去掉csrc文件夹中无用的驱动文件精简u8g2_d_setup.c(注意不是u8x8_setup.c)精简u8g2_d_memory.c

将精简后的U8g2库添加至Keil

代码Oled回调函数main函数

最终效果总结

U8g2库的STM32硬件SPI移植教程(HAL、OLED显示、四线SPI)

前言

本文教你把U8g2图形库移植到STM32上,基于STM32的硬件SPI、CubeMXU8g2库Github网址:https://github.com/olikraus/u8g2U8g2库CSDN镜像网址:https://gitcode.net/mirrors/olikraus/u8g2?utm_source=csdn_github_accelerator硬件准备:STM32C8T6(STM32系列芯片)、0.96寸OLED(128×64)、J-Link(或其他)引脚连接:

//----------------------------------------------------------------

本次用的是SPI1、DMA、全双工(当然半双工也没什么问题、CS、DC、RS是普通GPIO,自行切换)

GND -----------> 电源地

VCC -----------> 接5V或3.3v电源

D0 -----------> 接PA5(SCL)

D1 -----------> 接PA7(SDA)

RES -----------> 接PB0

DC -----------> 接PB1

CS -----------> 接PA4

//----------------------------------------------------------------

U8g2简介

出自此篇基于STM32移植U8g2图形库——OLED显示

U8g2是什么

   U8g2是GitHub上一款十分优秀的开源图形库(GUI库),其本质是嵌入式设备的单色图形库。在 Github 上超过3.2K Star,2.6K Commit。其开发语言90%为C语言,且代码简洁干练便于移植与后期修改。

U8g2支持的显示控制器

   U8g2支持单色OLED和LCD,包括以下控制器:

SSD1305SSD1306SSD1309SSD1312SSD1316SSD1320SSD1322SSD1325SSD1327SSD1329SSD1606SSD1607SH1106SH1107SH1108SH1122T6963RA8835LC7981PCD8544PCF8812HX1230UC1601UC1604UC1608UC1610UC1611UC1617UC1638UC1701ST7511ST7528ST7565ST7567ST7571ST7586ST7588ST75256ST75320NT7534ST7920IST3020IST7920LD7032KS0108KS0713HD44102T7932SED1520SBN1661IL3820MAX7219

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

U8g2的优势

U8g2库平台支持性好,基本上支持绝大部分Arduino与STM32开发板,也包含物联网比较常用的esp8266;U8g2库显示控制器支持性好,基本上市面上的OLED都完美支持;U8g2库 API函数众多,特别支持了中文,支持了不同字体,这是一个对于开发者俩说不小的福利;U8g2 库移植简单,容易使用(这一点也是笔者比较钟意的);

  其实,我们可以把U8g2当作一个工具箱,需要使用的时候就去打开工具箱,使用里面的已经写好的API函数去实现我们需要达到的显示效果。(当然,前提是需要熟悉U8g2的使用,这一点网上有很多用法博客写得都很详细,感兴趣的读者朋友可以去看看这篇:深入学习Arduino u8g2 OLED库,一篇就够)

CubexMX的配置

RCC配置外部高速晶振(精度更高)——HSE:

SYS配置:Debug设置成Serial Wire(否则可能导致芯片自锁):

时钟树配置:

SPI1配置半双工(全双工):作为OLED的通讯方式:(注意这里的配置)

  半双工:   全双工:

DMA配置:

工程配置:

  这里我们不用的库不加,减小代码体积 然后生成

U8g2移植

准备U8g2库文件

准备U8g2库文件---------->U8g2下载地址: https://github.com/olikraus/u8g2 下载压缩包Git用---------->git clone https://ghproxy.com/https://github.com/olikraus/u8g2.git

精简U8g2库文件

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

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

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

精简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_1u8g2_Setup_ssd1306_128x64_noname_2u8g2_Setup_ssd1306_128x64_noname_fu8g2_Setup_ssd1306_i2c_128x64_noname_1u8g2_Setup_ssd1306_i2c_128x64_noname_2u8g2_Setup_ssd1306_i2c_128x64_noname_f

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

1:128字节2:256字节f:1024字节

精简u8g2_d_memory.c

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

将精简后的U8g2库添加至Keil

  Keil工程目录添加精简后U8g2库文件中的csrc文件夹,然后再添加U8g2的头文件搜寻目录(U8g2_lib里都是csrc文件里面的文件,可以根据自己的需要删减),如下:

代码

Oled回调函数

oled_driver.c:

#include "oled_driver.h"

#include "stdlib.h"

#include "spi.h"

#include "dma.h"

#include "u8g2.h"

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,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, arg_int);

break;

case U8X8_MSG_GPIO_DC: /*设置DC引脚,表明发送的是数据还是命令*/

HAL_GPIO_WritePin(OLED_DC_GPIO_Port,OLED_DC_Pin,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");

}

/********************************* end_of_file **********************************/

GPIO定义的时候在CubeMX设置别名能更容易的切换IO口,CS拉高,如下 oled_driver.h:

#ifndef __MD_OLED_DRIVER_H

#define __MD_OLED_DRIVER_H

#include "stdlib.h"

#include "main.h"

#include "gpio.h"

#include "u8g2.h"

//-----------------OLED端口定义----------------

#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)

//OLED控制用函数

uint8_t u8x8_byte_4wire_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int,void *arg_ptr);

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) ;

void u8g2Init(u8g2_t *u8g2);

void draw(u8g2_t *u8g2);

#endif

  上述编写的移植函数代码属于HAL库下的代码,标准库的代码其实差不多,有个别地方需要注意修改。有一定MCU编程基础的朋友应该很简单就可以做到仿写。移植代码的本质:这些函数代码就是对应的U8g2图形库的接口函数,通过这些函数去启用U8g2图形库。

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_SPI1_Init();

/* Initialize interrupts */

MX_NVIC_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 */

u8g2_FirstPage(&u8g2);

do

{

draw(&u8g2);

} while (u8g2_NextPage(&u8g2));

}

/* USER CODE END 3 */

}

最终效果

总结

  U8g2图形库可以说目前小尺寸OLED首选的GUI,其可以呈现出的图形远不止上述中的图形,更多的功能还需要读者朋友们自己去好好发掘。优秀GUI的移植是一名合格嵌入式工程师必须掌握的技能之一,其可以达到大大缩短开发周期,优化UI界面等目的。LCD屏幕也存在类似的优秀开源GUI库,后续笔者会进行更新,感兴趣的读者朋友可以点波关注,感谢!!! ———————————————— 原文链接:https://blog.csdn.net/black_sneak/article/details/126312657

开源代码:https://gitee.com/ljs_ice/STM32_u8g2

好文链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: