STM32硬件IIC移植U8g2库教程详解

STM32 硬件I2C 移植U8G2

需要准备的东西

  • STM32 硬件I2C 移植U8G2
  • u8g2配置
  • U8g2源码下载
  • U8g2源码配置
  • STM32CubeMX代码生成
  • 生成32工程
  • 导入KEIL
  • 适配驱动
  • 初始化
  • 优化

  • Background: U8g2是Arduino上一个广泛使用的屏幕显示库,有着丰富的库函数实现功能,之前做的OLED桌面天气小摆件就是基于U8g2库的,最近在使用STM32 想着移植U8g2 发现大多数是基于软件I2C的,于是准备使用硬件I2C驱动OLED显示屏 0.96寸OLED SSD1306

    参考链接:
    u8g2stm32 移植记录


    视频链接:
    Bilibili STM32 HAL库硬件IIC移植u8g2库

    u8g2配置

    U8g2源码下载

    U8g2源码下载

    U8g2源码配置

    u8g2-master\csrc 中

    1. 留下**u8x8_d_ssd1306_128x64_noname.c **为了减少编译文件体积以及速度 删除其他的后缀中带_d的配置文件

    2. 在u8g2_d_setup.c中修改文件

    我们使用的驱动时ssd1306 s所以只关注和1306 相关的 主要有以下函数

    其中第一个代码块主要是关于i2c的驱动,第二个代码块主要是关于spi的驱动,

    /*i2c*/
    u8g2_Setup_ssd1306_i2c_128x64_noname_1
    u8g2_Setup_ssd1306_i2c_128x64_noname_2   
    u8g2_Setup_ssd1306_i2c_128x64_noname_f
    
    /*spi*/
    u8g2_Setup_ssd1306_128x64_noname_1
    u8g2_Setup_ssd1306_128x64_noname_2
    u8g2_Setup_ssd1306_128x64_noname_f
    

    函数不同的后缀表示的含义是其代表的缓冲区大小不同,

    1代表128字节、2代表256字节、f代表1024字节 ,主要区别在于缓冲区小的刷新OLED就比较耗时,这个根据自己单片机实际的需求选取。我们使用的是STM32F103C8T6选则的函数是u8g2_Setup_ssd1306_i2c_128x64_noname_f

    1. 修改u8g2_d_memory.c

    在上面的的u8g2_Setup_ssd1306_i2c_128x64_noname_f函数中,初始化只用到的u8g2_m_16_8_f函数,所以我们在menmoey.c文件中只留下这个函数 其余的均删除(如果不删除,那么编译的时候会编译进去,增大文件体积),效果如下图所示

    1. 修改u8g2.h

    在第3步,我们删除了大量的函数,这些函数是声明在u8g2.h 中所以我们在头文件中将这些声明删除

    u8g2修改完成,现在我们生成STM32的工程,进行下一步测试

    STM32CubeMX代码生成

    生成32工程

    1. 配置时钟

    1. 配置调试接口

    2. 配置I2C 选择快速模式 400khz 其他默认设置

    1. 生成文件 编译测试 没有错误的话就进行下一步

    导入KEIL

    上面我的修改是在csrc文件下进行的,在移植的时候我们只要这个文件夹。

    1. 拷贝csrc文件夹到生成的32工程的Drivers目录下 并重命名为U8g2

    1. 点击keil中的"品"的图标 新建工程目录 将上一步U8g2中的文件全部加进来

    1. 导入头文件路径

    点击魔术棒,进入c/c++设置 加入u8g2头文件的路径

    1. 尝试编译

    在main函数中加入#include “u8g2.h” 然后尝试编译

    编译成功,没有错误 只有一些关于最后一行后面没有回车的警告,暂时忽略

    自此,将代码导入到KEIL工程已经结束,接下来我们需要完成几个函数 以实现硬件I2C的适配

    适配驱动

    在这一步我们主要完成两个操作,实现stm32的延时函数 一个是使用硬件I2C的数据传输函数适配官方的

    1. 实现延时回调函数回调函数

    这一步主要是适配STM32 HAL库中的HAL_Delay,函数如下,函数名称无所谓,主要是函数中的参数一致即可

    uint8_t u8g2_stm32_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:
    	    break;
    		
    	case U8X8_MSG_DELAY_MILLI:
    		HAL_Delay(arg_int);//change it!
    	    break;
    		
    	default:	
    		return 0;
    	}
    	return 1; // command processed successfully.
    }
    
    1. 实现u8x8_byte_i2c的函数

    这里我们主要修改U8X8_MSG_BYTE_END_TRANSFER下的使用硬件IIC发送数据的库函数,在官方的库中使用sw(software)的方式适配如下 由于我们是硬件 i2c所以只关注数据传输方面

    uint8_t u8x8_byte_sw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
    {
      uint8_t *data;
    
      switch(msg)
      {
        case U8X8_MSG_BYTE_SEND:
          data = (uint8_t *)arg_ptr;
        
          while( arg_int > 0 )
          {
    	i2c_write_byte(u8x8, *data);
    	data++;
    	arg_int--;
          }
          
          break;
          
        case U8X8_MSG_BYTE_INIT:
          i2c_init(u8x8);
          break;
        case U8X8_MSG_BYTE_SET_DC:
          break;
        case U8X8_MSG_BYTE_START_TRANSFER:
          i2c_start(u8x8);
          i2c_write_byte(u8x8, u8x8_GetI2CAddress(u8x8));
          //i2c_write_byte(u8x8, 0x078);
          break;
        case U8X8_MSG_BYTE_END_TRANSFER:
          i2c_stop(u8x8);
          break;
        default:
          return 0;
      }
      return 1;
    }
    

    在STM32 HAL库中这个函数为

    HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
    
    /*
    hi2c:i2c实例化对象
    DevAddress:设备地址 这里我们使用 u8x8_GetI2CAddress(u8x8) 函数得到
    pData:要传输的数据
    Size:数据大小
    Timeout:超时时间
    */    
    

    适配如下

    uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
    {
      static uint8_t buffer[32];		/* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
      static uint8_t buf_idx;
      uint8_t *data;
     
      switch(msg)
      {
        case U8X8_MSG_BYTE_SEND:
          data = (uint8_t *)arg_ptr;      
          while( arg_int > 0 )
          {
    	        buffer[buf_idx++] = *data;
    	        data++;
    	        arg_int--;
          }      
          break;
        case U8X8_MSG_BYTE_INIT:
          /* add your custom code to init i2c subsystem */
          break;
        case U8X8_MSG_BYTE_SET_DC:
          /* ignored for i2c */
          break;
        case U8X8_MSG_BYTE_START_TRANSFER:
          buf_idx = 0;
          break;
        case U8X8_MSG_BYTE_END_TRANSFER:
          HAL_I2C_Master_Transmit(&hi2c1,u8x8_GetI2CAddress(u8x8), buffer, buf_idx,0x100);//change it
          break;
        default:
          return 0;
      }
      return 1;
    }
    

    这个函数是我们新写的,可以定义在任意位置,如main.c中,但是我为了统一,将其放在了u8x8_byte.c 中 大概在577行 这几行附近主要是关于驱动方式的初始化 软件i2c spi等 ,配置完成我们将这个函数在u8x8.h的头文件中声明

    上面初始化用到了 hi2c1 这个函数包含在i2c.h 中 所以我们在u8x8_byte.c中加入对i2c.h的引用 防止报错

    初始化

    经过上面的移植,我们已经完成了驱动部分的适配,接下来就要对他进行初始化,主要使用到了以下函数

    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)
    
    /*
    *u8g2 实例化的u8g2  对象的地址
    *rotation:旋转方向 对应参数如下 具体效果可以自己测试
    *	U8G2_R0	
        U8G2_R1	
        U8G2_R2	
        U8G2_R3	
        U8G2_MIRROR
        U8G2_MIRROR_VERTICAL
    *byte_cb:字节传输函数 是上面我们适配的u8x8_byte_hw_i2c
    *gpio_and_delay_cb:延时函数 是上面我们适配的u8g2_stm32_delay
    */
    

    主要适配代码如下

    #include "u8g2.h"
    
    u8g2_t u8g2;
    u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2,U8G2_R0,u8x8_byte_hw_i2c ,u8g2_gpio_and_delay_stm32);
    
    u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,
    u8g2_SetPowerSave(&u8g2, 0); // wake up display
    u8g2_ClearDisplay(&u8g2);
    u8g2_SetFont(&u8g2, u8g2_font_wqy16_t_chinese1);
    u8g2_DrawBox(&u8g2,60,10,20,20);
    u8g2_DrawUTF8(&u8g2,10,50,"hi,world");
    u8g2_SendBuffer(&u8g2);
    

    效果展示

    优化

    在显示汉字时,汉字部分显示不了,经过搜索是keil 默认使用GB2312 将其切换成UTF-8编码

    在edit->configuration->Editor->Encoding

    修改后的代码

    u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,
    u8g2_SetPowerSave(&u8g2, 0); // wake up display
    u8g2_ClearDisplay(&u8g2);
    u8g2_SetFont(&u8g2, u8g2_font_wqy16_t_chinese1);
    u8g2_DrawBox(&u8g2,60,10,20,20);
    u8g2_DrawUTF8(&u8g2,14,50,"你好,world");
    u8g2_SendBuffer(&u8g2);
    

    效果

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32硬件IIC移植U8g2库教程详解

    发表评论