【U8G2库移植到STM32F103C8T6上】


前言

最近在B站上刷到了一位up主设计的oled丝滑界面WouoUI,一个优雅得比较像话的UI框架,128 * 64 经过询问,得知up用的是U8G2库,U8G2多用于arduino编译器上,但是我们学校常用的是stm32。
所以经过一段时间的研究,我将U8G2库移植到了stm32f103c8t6上进行使用。


一、U8G2是什么?

关于U8G2的使用,可以参考一下这个博主的文章:深入学习Arduino u8g2 OLED库,一篇就够,写的非常详细。但是他介绍的是arduino平台的使用,stm32用法也差不多。

二、移植步骤

1.软件i2c移植

移植参考了以下两篇文章:U8G2库移植到STM32平台上
U8g2图形库与STM32移植(I2C,软件与硬件)

(1).U8G2库的下载

U8G2库是一个开源的github项目,通过github可以直接下载 。U8G2下载地址

(2).新建一个工程STM32工程

新建一个工程,并且初始化一个IIC。
建立oled.h和oled.c文件

oled.c

#include "sys.h"
#include "oled.h"
//初始化IIC
void IIC_Init(void)
{					     
 	RCC->APB2ENR|=1<<3;		//使能IO PORTBA时钟							 
	GPIOB->CRL&=0X00FFFFFF;	//PB6/7 推挽输出
	GPIOB->CRL|=0X33000000;	   
}

oled.h

#ifndef __OLED_H
#define __OLED_H
#include "sys.h"
void IIC_Init(void);
#endif

(3).修改U8G2库

打开下载的u8g2库,我们用到的只有crsc这个文件夹。
打开csrc文件夹,可以看到里面有许多.c文件。有许多U8x8_d_*****类型的c文件,这是针对不同的驱动芯片所写的驱动程序,我们只需要选择我们需要的就行了,其他的在移植到自己的工程的时候可以都删除。我们此次使用的是ssd1306,因此在移植的时候,只保留U8x8_d_ssd1306_128x64_noname.c相关的驱动程序就可以了。
在这里插入图片描述

然后将csrc文件夹下面的文件都复制到stm32的工程中,建立好的工程如下图。
在这里插入图片描述

修改u8g2_d_setup.c中的函数,这里只保留u8g2_Setup_ssd1306_i2c_128x64_noname_f()这个函数,其他的可以注释或者删除。

/* ssd1306 f */
void u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
{
  uint8_t tile_buf_height;
  uint8_t *buf;
  u8g2_SetupDisplay(u8g2, u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_fast_i2c, byte_cb, gpio_and_delay_cb);
  buf = u8g2_m_16_8_f(&tile_buf_height);
  u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
}

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

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用的,函数最后的数字或字母,代表显示时的buf大小:
1:128字节
2:256字节
f:1024字节

再修改u8g2_d_memory.c中的函数,由于用到的u8g2_Setup_ssd1306_i2c_128x64_noname_f()函数中,只调用了u8g2_m_16_8_f()这个函数,所以留下这个函数,其它的函数一定要删掉或注释掉,否则编译时很可能会提示内存不足。

uint8_t *u8g2_m_16_8_f(uint8_t *page_cnt)
{
  #ifdef U8G2_USE_DYNAMIC_ALLOC
  *page_cnt = 8;
  return 0;
  #else
  static uint8_t buf[1024];
  *page_cnt = 8;
  return buf;
  #endif
}

接下来修改字库u8g2_fonts.c,选择要使用的字库,否则会提示内存不足。这里我们保留
uint8_t u8g2_font_inb30_mn[807]
uint8_t u8g2_font_inb24_mf[8821]
uint8_t u8g2_font_4x6_tr[723]
这三个字库。

(4).编写配置函数

U8g2 已经包含了 SSD1306 的驱动,只需要添加一个函数u8x8_gpio_and_delay() 用于模拟时序即可。官方文件给出了一个函数的编写模板为:

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
            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;
        /* and many other cases */
        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;
}

以下是一个写法案例:

uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
    switch (msg) {
        case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds
            __NOP();
            break;
        case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds
            for (uint16_t n = 0; n < 320; n++)
                __NOP();
            break;
        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(5);
            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
            arg_int ? (GPIOB->BSRR=1<<6) : (GPIOB->BRR=1<<6);  
            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
            arg_int ? (GPIOB->BSRR=1<<7) : (GPIOB->BRR=1<<7);   
            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;
            u8x8_SetGPIOResult(u8x8, 1); // default return value
            break;
				default:
					return 0;
    }
    return 1;
}

U8g2的初始化,需要调用下面这个u8g2_Setup_ssd1306_128x64_noname_f函数,该函数的4个参数含义:
u8g2:传入的U8g2结构体
U8G2_R0:默认使用U8G2_R0即可(用于配置屏幕是否要旋转)
u8x8_byte_sw_i2c:使用软件IIC驱动,该函数由U8g2源码提供
u8x8_gpio_and_delay:就是上面我们写的配置函数

void u8g2Init(u8g2_t *u8g2)
{
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_ClearBuffer(u8g2);
} 

至此,我们的U8G2已经移植好了,oled.h和oled.c应该是这样的:

oled.c

#include "sys.h"
#include "delay.h"
#include "u8g2.h"
#include "oled.h"
//初始化IIC
//SCL->PB6 ; SDA->PB7
void IIC_Init(void)
{					     
 	RCC->APB2ENR|=1<<3;		//使能IO PORTB时钟						 
	GPIOB->CRL&=0X00FFFFFF;	//PB6/7 推挽输出
	GPIOB->CRL|=0X33000000;	   
}

uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
    switch (msg) {
        case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds
            __NOP();
            break;
        case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds
            for (uint16_t n = 0; n < 320; n++)
                __NOP();
            break;
        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(5);
            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
            arg_int ? (GPIOB->BSRR=1<<6) : (GPIOB->BRR=1<<6);  
            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
            arg_int ? (GPIOB->BSRR=1<<7) : (GPIOB->BRR=1<<7);   
            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;
				default:
					return 0;
    }
    return 1;
}

void u8g2Init(u8g2_t *u8g2)
{
u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay);  
u8g2_InitDisplay(u8g2); 
u8g2_SetPowerSave(u8g2, 0); 
u8g2_ClearBuffer(u8g2);
} 

oled.h

#ifndef __OLED_H
#define __OLED_H
#include "sys.h"
#include "u8g2.h"
void IIC_Init(void);
uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
void u8g2Init(u8g2_t *u8g2);
#endif

(5).测试代码

这是一个U8G2的测试代码,显示出来U8G2的logo:

void draw(u8g2_t *u8g2)
{
	u8g2_ClearBuffer(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");
	 u8g2_SendBuffer(u8g2);
	 delay_ms(1000);
}

(6).主函数代码

main.c

#include "sys.h"
#include "delay.h"
#include "oled.h"

int main(void)
{
	u8g2_t u8g2;
	Stm32_Clock_Init(9);
	delay_init(72);	
	IIC_Init();
	u8g2Init(&u8g2);
	draw(&u8g2);
	while(1)
	{
		
  } 
}

或者:

#include "delay.h"
#include "sys.h"
#include "u8g2.h"

int main(void)
{
	delay_init();
	IIC_Init();
	u8g2_t u8g2;
	u8g2Init(&u8g2);
	while(1)
	{
      u8g2_FirstPage(&u8g2);
      do
      {
  		draw(&u8g2);
      } while (u8g2_NextPage(&u8g2));
    }
}

2.实验现象

(1)proteus中查看实验现象

在这里插入图片描述

  • 8
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
您好!要在STM32F103C8T6移植u8g2,您可以按照以下步骤进行操作: 1. 下载u8g2:您可以从u8g2的GitHub存储(https://github.com/olikraus/u8g2)中下载最新版本的。 2. 配置硬件连接:确保将OLED显示屏正确连接到STM32F103C8T6开发板的相应引脚上。您需要根据OLED显示屏的数据手册,将其连接到正确的引脚上(例如I2C或SPI引脚)。 3. 创建STM32Cube工程:使用STM32CubeIDE或其他任何您喜欢的开发环境,创建一个新的STM32工程。 4. 添加u8g2文件:将下载的u8g2中的源代码和头文件添加到您的STM32工程中。确保将这些文件正确地放置在您的工程目录结构中,并在编译设置中包含它们。 5. 配置I2C或SPI接口:根据您的OLED显示屏使用的接口(I2C或SPI),在STM32CubeMX或其他配置工具中配置相应的接口和引脚。 6. 初始化u8g2:在您的代码中,使用适当的函数初始化u8g2。具体初始化步骤和函数取决于您的OLED显示屏和接口类型。您可以参考u8g2的文档和示例代码,以了解如何正确初始化和使用。 7. 编写显示代码:使用u8g2提供的函数,编写您的显示代码。您可以选择在OLED上显示文本、图像或其他内容。 8. 编译和上传:编译您的代码,并将生成的可执行文件上传到STM32F103C8T6开发板上进行测试。 这是一个简单的概述,您可能需要根据您的具体需求和硬件配置进行一些调整。希望这些步骤能够帮助您成功移植u8g2STM32F103C8T6开发板上!如果您有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值