【STM32 MCU】使用标准外设库(SPL)移植 u8g2 图形库

这篇具有很好参考价值的文章主要介绍了【STM32 MCU】使用标准外设库(SPL)移植 u8g2 图形库。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

STM32: port u8g2 library using Standard Peripheral Library

MCU STM32F103C8T6
Module 0.96 inch OLED with SSD1306
Library Standard peripheral library

1.删除不需要的文件

Porting to new MCU platform · olikraus/u8g2 Wiki (github.com)

为了减小编译后的大小,我们需要删除或精简以下的文件

U8g2 的源码为了支持多种控制器(controller),包含了许多兼容性的代码。首先,类似 u8x8_d_xxx.c 命名的文件中包含 U8x8 的驱动兼容,文件名包括控制器的型号和屏幕分辨率,因此需要删除无用的文件,只保留当前控制器的文件。例如,本次使用的是 128x64 OLED with SSD1306 controller,那么只需要保留 u8x8_d_ssd1306_128x64_noname.c 文件,删除其它类似的文件即可。

u8g2_d_setup.c

删去其他函数,只保留 u8g2_Setup_ssd1306_i2c_128x64_noname_f()

根据 u8g2/doc/faq.txt at master · olikraus/u8g2 (github.com),我们可以知道 F 意为 full buffer mode,需要 RAM 大小 1024 bytes

u8g2_d_memory.c

u8g2_d_memory.c 文件也是同理,它需要根据 u8g2_d_setup.c 中的调用情况决定用到哪些函数。由于 u8g2_Setup_ssd1306_i2c_128x64_noname_f() 函数只用到 u8g2_m_16_8_f() 这一个函数,因此只需要保留它,其余函数全部删除即可。

Fonts

还有一处必要的精简是字体文件 u8x8_fonts.cu8g2_fonts.c ,尤其是 u8g2_fonts.c ,该文件提供了包括汉字在内的几万个文字的多种字体,仅源文件就有 30MB ,编译后占据的内存非常大。

字体类型的变量非常多,建议先复制一个备份后将所有变量删除,之后视情况再添加字体。字体变量的命名大致遵循以下规则:

<prefix> '_' <name> '_' <purpose> <charset>

其中:

  • <prefix> 前缀基本上以 u8g2 开头;
  • <name> 字体名,其中可能包含字符大小
  • 各种 <purpose> 含义如下表所示:
名称 描述
t 透明字体形式
h 所有字符等高
m monospace 字体(等宽字体)
8 每一个字符都是 8x8 大小的
  • <charset> 是字体支持的字符集,如下表所示:
名称 描述
f 只包含单字节字符
r 只包含 ASCII 范围为 32~127 的字符
u 只包含 ASCII 范围为 32~95 的字符,即不包括小写英文
n 只包含数字及一些特殊用途字符
还包括许多自定义的字符集,例如有一些结尾带 gb2312 或 Chinese 的字体名就包括中文

一般建议只保留需要的字体即可。

2.实现 callback 函数

官方提供了 GPIO and Delay callback 函数模板

uint8_t u8x8_gpio_and_delay_template(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  switch(msg)
  {
    case U8X8_MSG_GPIO_AND_DELAY_INIT:	// called once during init phase of u8g2/u8x8
      break;							// can be used to setup pins
    case U8X8_MSG_DELAY_NANO:			// delay arg_int * 1 nano second
      break;    
    case U8X8_MSG_DELAY_100NANO:		// delay arg_int * 100 nano seconds
      break;
    case U8X8_MSG_DELAY_10MICRO:		// delay arg_int * 10 micro seconds
      break;
    case U8X8_MSG_DELAY_MILLI:			// delay arg_int * 1 milli second
      break;
    case U8X8_MSG_DELAY_I2C:				// arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
      break;							// arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
    case U8X8_MSG_GPIO_D0:				// D0 or SPI clock pin: Output level in arg_int
    //case U8X8_MSG_GPIO_SPI_CLOCK:
      break;
    case U8X8_MSG_GPIO_D1:				// D1 or SPI data pin: Output level in arg_int
    //case U8X8_MSG_GPIO_SPI_DATA:
      break;
    case U8X8_MSG_GPIO_D2:				// D2 pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_D3:				// D3 pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_D4:				// D4 pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_D5:				// D5 pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_D6:				// D6 pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_D7:				// D7 pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_E:				// E/WR pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_CS:				// CS (chip select) pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_DC:				// DC (data/cmd, A0, register select) pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_RESET:			// Reset pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_CS1:				// CS1 (chip select) pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_CS2:				// CS2 (chip select) pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_I2C_CLOCK:		// arg_int=0: Output low at I2C clock pin
      break;							// arg_int=1: Input dir with pullup high for I2C clock pin
    case U8X8_MSG_GPIO_I2C_DATA:			// arg_int=0: Output low at I2C data pin
      break;							// arg_int=1: Input dir with pullup high for I2C data pin
    case U8X8_MSG_GPIO_MENU_SELECT:
      u8x8_SetGPIOResult(u8x8, /* get menu select pin state */ 0);
      break;
    case U8X8_MSG_GPIO_MENU_NEXT:
      u8x8_SetGPIOResult(u8x8, /* get menu next pin state */ 0);
      break;
    case U8X8_MSG_GPIO_MENU_PREV:
      u8x8_SetGPIOResult(u8x8, /* get menu prev pin state */ 0);
      break;
    case U8X8_MSG_GPIO_MENU_HOME:
      u8x8_SetGPIOResult(u8x8, /* get menu home pin state */ 0);
      break;
    default:
      u8x8_SetGPIOResult(u8x8, 1);			// default return value
      break;
  }
  return 1;
}

一个示例:

void HW_I2C_Init(void)
{

	RCC_APB2PeriphClockCmd(I2C_RCC_APBx_GPIOx, ENABLE);

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStructure.GPIO_Pin = SCL_Pin | SDA_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(I2C_GPIOx, &GPIO_InitStructure);

	RCC_APB1PeriphClockCmd(I2C_RCC_APBx_I2Cx, ENABLE);

	I2C_InitTypeDef I2C_InitStructure;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStructure.I2C_ClockSpeed = 400000;
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
	I2C_InitStructure.I2C_Ack = I2C_Ack_Disable;
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;
	I2C_Init(I2Cx, &I2C_InitStructure);

	I2C_Cmd(I2Cx, ENABLE);
}


uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  switch (msg)
  {
  case U8X8_MSG_GPIO_AND_DELAY_INIT: // called once during init phase of u8g2/u8x8
    SW_I2C_Init();
    break;                   // can be used to setup pins
  case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second
    Delay_ms(1);
    break;
  case U8X8_MSG_DELAY_I2C:      // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
    Delay_us(1);                // 1us = 500kHz, just for SW I2C
    break;                      // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
  case U8X8_MSG_GPIO_I2C_CLOCK: // arg_int=0: Output low at I2C clock pin
    if (arg_int == 0)
    {
      SW_MCU_SCL(Bit_RESET);
    }
    else
    {
      SW_MCU_SCL(Bit_SET);
    }
    break;                     // arg_int=1: Input dir with pullup high for I2C clock pin
  case U8X8_MSG_GPIO_I2C_DATA: // arg_int=0: Output low at I2C data pin
    if (arg_int == 0)
    {
      SW_MCU_SDA(Bit_RESET);
    }
    else
    {
      SW_MCU_SDA(Bit_SET);
    }
    break; // arg_int=1: Input dir with pullup high for I2C data pin
  default:
    u8x8_SetGPIOResult(u8x8, 1); // default return value
    break;
  }
  return 1;
}

使用

u8g2_t u8g2; // a structure which will contain all the data for one display
...
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay);  		// 初始化 u8g2 结构体
u8g2_InitDisplay(u8g2);     // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态
u8g2_SetPowerSave(u8g2, 0); // 打开显示器

这里需要调用之前保留的 u8g2_Setup_ssd1306_128x64_noname_f() 函数,该函数的4个参数,其含义为:

  • u8g2 :需要配置的 U8g2 结构体
  • rotation :配置屏幕是否要旋转,默认使用 U8G2_R0 即可
  • byte_cb :传输字节的方式,这里使用软件 I2C 驱动,因此使用 U8g2 提供的 u8x8_byte_sw_i2c() 函数。如果是硬件 I2C 的话,可以参照编写自己的函数
  • gpio_and_delay_cb :提供给软件模拟 I2C 的 GPIO 输出和延时,使用之前编写的配置函数 u8x8_gpio_and_delay()

使用硬件 I2C,则需要实现 u8x8_byte_hw_i2c(),官方文档给的模板实现后无法正常工作。
在此我自己实现,并解决了问题。

uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  uint8_t *data = (uint8_t *)arg_ptr;
  uint8_t data_length = arg_int;
  uint8_t retry = 0;
  switch (msg)
  {
  case U8X8_MSG_BYTE_INIT:
    /* add your custom code to init i2c subsystem */
    HW_I2C_Init();
    break;
  case U8X8_MSG_BYTE_START_TRANSFER:
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) && retry < 200)
    {
      retry++;
    }
    retry = 0;
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && retry < 200)
    {
      retry++;
    }
    retry = 0;
    I2C_Send7bitAddress(I2C1, u8x8_GetI2CAddress(u8x8), I2C_Direction_Transmitter);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) && retry < 200)
    {
      retry++;
    }
    break;
  case U8X8_MSG_BYTE_SEND:
    for (int i = 0; i < data_length; i++)
    {
      I2C_SendData(I2C1, data[i]);
      while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == RESET)
      {
      }
    }
    break;
  case U8X8_MSG_BYTE_END_TRANSFER:
    I2C_GenerateSTOP(I2C1, ENABLE);
    break;
  default:
    return 0;
  }
  return 1;
}

使用

u8g2_t u8g2; // a structure which will contain all the data for one display
...
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8x8_gpio_and_delay);  		// 初始化 u8g2 结构体
u8g2_InitDisplay(u8g2);     // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态
u8g2_SetPowerSave(u8g2, 0); // 打开显示器

Reference:

[1]: U8g2图形库与STM32移植(I2C,软件与硬件) - 冰封残烛 - 博客园 (cnblogs.com)文章来源地址https://www.toymoban.com/news/detail-846718.html

到了这里,关于【STM32 MCU】使用标准外设库(SPL)移植 u8g2 图形库的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32 使用 U8g2 显示自定义图像

    首先移植 U8g2 图形库到 STM32,本文不赘述了。 本问针对 SSD1306 128*64 屏幕 采用 PCtoLCD2002完美版 字模软件对图像取模,具体操作如下: 新建图像: 图像长宽分别为 128 , 64 根据需要绘制图像,例如: 设置字模格式: 需要注意:取模方式 取模走向 C51格式 生成字模,将字模保

    2024年02月04日
    浏览(34)
  • [低端局][cx32L003] 移植U8G2

    在低端芯片上使用U8G2 U8g2:单色显示库,版本2 U8g2是一个用于嵌入式设备的单色图形库。U8g2支持单色oled和lcd,其中包括以下控制器:Ssd1305、ssd1306、ssd1309、ssd1312、ssd1316、ssd1318、ssd1320、ssd1322、ssd1325、ssd1327、ssd1327、ssd1327、ssd1327、ssd1327、ssd1327、ssd1106、sh1107、sh1108、ssd1607、

    2024年02月13日
    浏览(21)
  • HAL/LL/STD STM32 U8g2库 +I2C SSD1306/sh1106 WouoUI磁贴案例

    📍基于STM32F103C8T6 LL库驱动版本: https://gitee.com/chcsx/platform-test/tree/master/MDK-ARM 🎬视频演示: WouoUI移植磁贴案例,新增确认弹窗 📍基于标准库驱动,STM32F103RCT6移植u8g2 硬件i2c WouoUI磁贴: https://gitee.com/henrycrh/stm32-f103-rct6_-wouo-ui/tree/master/STM32F10x_FWlib/src 🎬视频演示: 🔖使用LL库

    2024年02月22日
    浏览(21)
  • 【ESP32之旅】U8g2 在线仿真和UI调试

    几乎每个玩屏幕的电子DIYer都知道万能的屏幕驱动中间件u8g2库,这个库提供了强大的驱动适配和ui设计能力。但是官方没有一个好用的ui设计和仿真软件,在设计UI布局的时候对单片机频繁的烧录调试浪费了大量的时间。最近在论坛看到有一个第三方维护的在线u8g2 UI设计软件

    2024年02月09日
    浏览(24)
  • esp32 Arduino(九)u8g2图形库 IIC驱动OLED

    LED驱动芯片:SSD1306 开发平台: vscode+platformIO 开发板:ESP32 Divkit v1 目前Arduino平台上使用最广泛的OLED库: U8G2 github地址: https://github.com/olikraus/u8g2/ 设置和参考手册:https://github.com/olikraus/u8g2/wiki U8g2:单色显示器库,版本2 U8g2是用于嵌入式设备的单色图形库。U8g2支持单色OLED和LCD,包括

    2023年04月09日
    浏览(36)
  • u8g2库的使用及驱动LCD显示中文

    1、准备工作 Env 工具包含了 RT-Thread 源代码开发编译环境和软件包管理系统。 从 RT-Thread 官网下载 Env 工具。https://www.rt-thread.org/download.html。 在电脑上装好 git,软件包管理功能需要 git 的支持。git 的下载地址为https://git-scm.com/downloads,根据向导正确安装 git,并将 git 添加到系

    2024年02月02日
    浏览(25)
  • U8G2中文显示问题(u8g2.drawStr()中文不显示)

            在使用u8g2进行中文先显示时,踩到一个坑,其他大佬也没有专门提过这一点,现跟大家分享一下。         起因是在看一些例程时,直接采用u8g2.drawStr()来进行英文输出,在想用该方法进行中文输出时,无法显示中文。 运行代码如下: 显示效果为: 只显示\\\"Hello\\\",

    2024年02月11日
    浏览(25)
  • ESP32 入门笔记04: 0.96寸OLED 显示屏 + u8g2库丝滑显示UI (ESP32 for Arduino IDE)

    先导知识 ESP32 入门笔记01:开发板信息、开发环境搭建以及学资料准备 ESP32 入门笔记02: GPIO参考指南 在本例中,我们使用 I2C 通信协议。ESP32 中最适合 I2C 通信的引脚是通用输入输出接口 22(SCL) 和通用输入输出接口 21(SDA) 。 安装 SSD1306 OLED 库 – ESP32 (也可以用u8g2库,在Ardu

    2024年01月19日
    浏览(27)
  • ESP8266基于Lua开发使用U8g2模块驱动 i2c ssd1306 OLED显示

    📍相关篇《ESP8266基于Lua开发点灯示例》 📖U8g2对应的API接口函数: https://nodemcu.readthedocs.io/en/release/modules/u8g2/ 📺驱动显示效果: 🔖官方U8g2驱动Demo : https://github.com/nodemcu/nodemcu-firmware/blob/release/lua_examples/u8g2/graphics_test.lua ✨在使用U8g2时,可能会出现上传过程中报错的问题:

    2024年02月06日
    浏览(21)
  • Adafruit_GFX u8g2库添加中文字体

    u8g2字体生成工具 下载完成后,将文件解压至没有中文字符的路径下。 免费字体下载网站 我这里选择下载优设标题圆这一字体。 打开解压好的字体生成工具,将下载好的ttf字体复制至font文件夹中。 并将字体文件重命名,名称不能包含中文字符,否则会转换失败。 接下来,

    2024年02月13日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包