STM32移植U8g2图形库实践指南

STM32移植U8g2图形库

  • 目录标题
  • 前言
  • U8G2图形库介绍
  • 移值
  • 精简c源码
  • 精简u8g2_d_setup.c
  • 精简u8g2_d_memory.c
  • 编写移植函数
  • GPIO初始化
  • u8x8_gpio_and_delay
  • 2.2.3 u8g2Init
  • 显示测试函数
  • 源码加入到MDK编译
  • 主函数
  • 测试效果
  • 目录标题

    前言

    一直想找一个可移值性强的显示驱动,偶然发现U8G2这个图像库,发现这个库能实现许多好玩有趣的动画,可以编一些界面,作为知识储备。

    U8G2图形库介绍

    U8g2 是一个用于嵌入式设备的简易图形库,可以在多种 OLED 和 LCD 屏幕上,支持包括 SSD1306 (新版型号为SSD1315)等多种类型的底层驱动,并可以很方便地移植到 Arduino 、树莓派、NodeMCU 和 ARM 上。
    U8g2 库同时包含了 U8x8 绘图库,两者的区别为:
    U8g2 包含各种简单及复杂图形的绘制,并支持各种形式的字体,但需要占用一定单片机的内存作为绘图缓存
    U8x8 只包含简单的显示文本功能,且只支持简单、定宽的字体。它直接绘制图形,没有缓存功能
    U8g2 库的 GitHub 地址为:https://github.com/olikraus/u8g2 ,可以从中获取到源码与文档帮助

    移值

    本次以将 U8g2 移植到 STM32 单片机与 SSD1306 通过 I2C 驱动的 128×64 OLED 为例,介绍移植的方法。不同单片机和驱动的移植可以参考这一过程,也可以参考 U8g2 的官方移植教程。
    https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform 首先下载或克隆 U8g2 的源码,这里主要是使用 C 语言编写,所以只需要用到 csrc 目录下的文件。
    下载完成后,将 csrc 目录拷贝或移动到工程目录里,并重命名为合适的目录名例如 u8g2lib 。

    精简c源码

    U8g2支持多种显示驱动的屏幕,因为源码中也包含了各个驱动对应的文件,为了减小整个工程的代码体积,在移植U8g2时,可以删除一些无用的文件。
    去掉无用的驱动文件

    精简u8g2_d_setup.c

    由于我的OLED是IIC接口,只留一个本次要用到的u8g2_Setup_ssd1306_i2c_128x64_noname_f就好(如果是SPI接口,需要使用u8g2_Setup_ssd1306_128x64_noname_f这个函数),其它的可以删掉或注释掉

    #include "u8g2.h"
    
    /* 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这个函数,所以留下这个函数,其它的函数一定要删掉或注释掉,否则编译时很可能会提示内存不足!!!

    #include "u8g2.h"
    
    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
    }
    

    编写移植函数

    精简源码之后,还需要编写如下的配置函数。

    GPIO初始化

    对OLED用到的IIC接口进行GPIO的初始化配置:

    #define SCL_Pin GPIO_Pin_6
    #define SDA_Pin GPIO_Pin_7
    #define IIC_GPIO_Port GPIOB
    void IIC_Init(void)
    {					     
    	GPIO_InitTypeDef GPIO_InitStructure;
    	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	
    	GPIO_InitStructure.GPIO_Pin = SCL_Pin|SDA_Pin;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(IIC_GPIO_Port, &GPIO_InitStructure);
    }
    

    如果是SPI接口,则初始化对应的SPI接口即可。

    u8x8_gpio_and_delay

    这个函数也需要自己写,主要的修改包括:
    赋予U8g2相应的延时函数,比如下面的delay_ms和delay_us
    为U8g2提供IIC接口的高低电平调用:

  • U8X8_MSG_GPIO_I2C_CLOCK:IIC的SCL
  • U8X8_MSG_GPIO_I2C_DATA:IIC的SDA
  • uint8_t u8g2_gpio_and_delay_stm32(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_DELAY_MILLI:
       			delay_ms(arg_int);
       			break;
       		case U8X8_MSG_DELAY_10MICRO:
       			delay_us(10);
       			break;
       		case U8X8_MSG_DELAY_100NANO:
       			__NOP();
       			break;
       		case U8X8_MSG_GPIO_I2C_CLOCK:
       			if (arg_int) I2C_OLED_SCL_H();
       			else I2C_OLED_SCL_L();
       			break;
       		case U8X8_MSG_GPIO_I2C_DATA:
       			if (arg_int) I2C_OLED_SDA_H();
       			else I2C_OLED_SDA_L();
       			break;
       		default:
       			return 0;
       }
       return 1;
    }`
    

    如果是SPI接口,可以参考如下写法:

    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_SPI_DATA:
                lcd_sdin((uint8_t)arg_int); //SPI - MOSI
                break;
            case U8X8_MSG_GPIO_SPI_CLOCK: //SPI - CLK
                lcd_sclk(arg_int);
                break;
            case U8X8_MSG_GPIO_AND_DELAY_INIT:
                oled_init(); //OLED初始化
                Delay(1);
                break;
            case U8X8_MSG_DELAY_MILLI:
                Delay(arg_int); //延时
                break;
            case U8X8_MSG_GPIO_CS: //SPI - CS
                lcd_cs((uint8_t)arg_int);
            case U8X8_MSG_GPIO_DC:
                lcd_dc((uint8_t)arg_int); //SPI - MISO
                break;
            case U8X8_MSG_GPIO_RESET:
                break;
        }
        return 1;
    }
    

    可以看出,对于IIC与SPI接口,只有分别进行对应的配置即可。

    2.2.3 u8g2Init

    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)
    {
    	OLED12864_I2C_Init();
    	u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8g2_gpio_and_delay_stm32);
    	u8g2_InitDisplay(u8g2);     //初始化
    	u8g2_SetPowerSave(u8g2, 0);
    	u8g2_ClearBuffer(u8g2);//清屏
    }
    

    显示测试函数

    使用U8g2提供的测试函数,用于查看显示效果

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

    源码加入到MDK编译

    主函数

    主函数中,首先是IIC的初始化和U8g2的初始化,然后就可以测试U8g2的图形显示功能了:

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

    测试效果

    ## 总结
    本篇介绍了如何将U8g2图形库移植到STM32中,其中主要的修改包括:

  • 精简源码中的u8g2_d_setup.c和u8g2_d_memory.c
  • OLED所用IIC接口的GPIO初始化
  • 编写u8x8_gpio_and_delay和u8g2Init
  • 其中,u8g2_d_memory.c文件一定要去掉无用的函数,否则编译时会提示内存不足;对于SPI接口的OLED,参考IIC接口进行类似的修改即可。
  • 作者:蒲公英666

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32移植U8g2图形库实践指南

    发表回复