[N32WBxxx]

N32WB031移植U8g2过程

[复制链接]
279|0
手机看帖
扫描二维码
随时随地手机跟帖
uant|  楼主 | 2023-2-6 10:45 | 显示全部楼层 |阅读模式
本帖最后由 uant 于 2023-2-6 10:44 编辑

        U8g2是一个优秀的图形显示库,支持很多屏幕,U8g2可以简化显示的处理难度,并且支持丰富的功能。因支持的型号众多,这里就不一一列出了,如果需要了解所支持的屏幕,可以访问以下地址查看:
#屏幕支持列表
https://github.com/olikraus/u8g2/wiki/u8g2setupcpp


     本文将讲解U8g2移植到Keil5的过程,屏幕是OLED SSD1306&SH1106进行移植,这两个芯片的移植方式基本上是一样的。
   讲解前先来说下我的环境:
# 系统       window 11
# 开发工具    Keil 5.36
# 开发板     国民技术的 N32WB031_STB_V1.3


    下面就开始讲解移植过程:
一、获取支持文件:
     访问 https://github.com/olikraus/u8g2 ,下载代码库,可以直接下载zip包或者直接使用命令 git clone https://github.com/olikraus/u8g2 克隆一份。
    如果是压缩包请解压,文件结构与github上所见一致。
fe4809267b98b586cdd80229865135a3c2650ddc.png
    这里,我们需要移植到Keil5中,所以需要使用他的C版本,C版本文件都在 csrc 目录下,我们将目录拷到我们自己的工程目录下,这里将 csrc 目录改名为 u8g2。到这里第一步就算完成了,后续就是要进行相关配置。
二、配置过程
    1、编译环境配置:
    先将u8g2目录加入到keil5的引用路径中,以便系统可以找到所需的头文件(这里图片演示仅做简单展示,正常会用keil5都知道步聚吧):
2.png
    接下来就是创建一个目录,将需要的 *.c 文件加入到项目中,我们并不需要将所有的都加到项目中,不然就会提示空间不足了,所以先来了解下各个不同文件代表的是什么使用
u8g2_xxx 都是显示处理相关的文件,所以都需要导入
u8x8_d_xxx  都是驱动文件,所以需要按需导入
            我们显示显示屏是SSD1306,分辨率是128*64,
            所以需要将 u8x8_d_ssd1306_128x64_noname.c文件加入到工程中,
            其它 不含u8x8_d_的文件也都加到工程中


3.png

2、文件精简配置:
     因我们的MCU Flash都是非常紧张的,所以还要进一步对相关文件精简,不然会浪费额外的空间。
    第一步,精简 u8g2_d_memory.c,为了方便操作,我们先可以将所有的函数注释掉,就像这样:
4.png
    我们可以看到这里是所有支持型号初始化配置的地方,我们小小的几十k或几百k的Flash可扛不住,所以这里只需要打开我们自己型号的就可以了
# 将SSD 1306 128*64 的初始化函数,取消注释,进行启用
/* sh1106 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);
}

# 将SH1106 128*64 的初始化函数,取消注释,进行启用
/* sh1106 f */
void u8g2_Setup_sh1106_i2c_64x32_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_sh1106_64x32, u8x8_cad_ssd13xx_fast_i2c, byte_cb, gpio_and_delay_cb);
  buf = u8g2_m_8_4_f(&tile_buf_height);
u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
}


    我们可以往慢慢翻找,也可以直接搜索对应的函数名快速定位

第二步,配置 u8g2_d_memory.c 文件
    同样的,把所有的函数先注释掉,这时我们先保存编译一下,看看缺少哪些函数,再相应的启用即可(之前搜索资料时看到别人是这样做的,发现这个方法还真不错)
# 最后我们需要的函数是以下两个,如果你不是这两个,取消掉对应函数的注释就可以了

uint8_t *u8g2_m_16_8_1(uint8_t *page_cnt)
{
  #ifdef U8G2_USE_DYNAMIC_ALLOC
  *page_cnt = 1;
  return 0;
  #else
  static uint8_t buf[128;
  *page_cnt = 1;
  return buf;
  #endif
}

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 文件
     这个是字库文件,我下载过来的文件查看了下,好家伙,有35万行,这就得几百K空间啊,我们可怜的flash可真吃不下,所以呢!全部注释掉,哈哈!然后看看哪些我们需要,就把对应的字库取消注释就好了。
     你可能会说,我也不知道要用哪些字库啊,怎么办呢?这个也好办,U8g2提供了一个可以在线查看样式的地址,我们访问以下网址就可以看到样式了,需要哪个就把哪个的注释取消就可以啦。
# 字体预览页面
https://github.com/olikraus/u8g2/wiki/fntlistall

5.png
6.png
3、编写驱动文件
    以上配置好就需要配置自己的驱动文件了,这里使用的是GPIO直接驱动I2C,这个会比较简单一些。这里我们创建好驱动文件,分别命名为: u8g2_drv.h 和 u8g2_drv.c,接下来就是编写内容了
u8g2_drv.h 文件
#ifndef U8G2_DRV_H_
#define U8G2_DRV_H_


#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"
#include "u8g2.h"

// OLED配置
// 使用的引脚 SCL=PB4 SDA=PB3
#define OLED_SCL_RCC                                RCC_APB2_PERIPH_GPIOB
#define OLED_SDA_RCC                                RCC_APB2_PERIPH_GPIOB

#define OLED_SCL_PORT                                GPIOB
#define OLED_SDA_PORT                                GPIOB

#define OLED_SCL_PIN                                GPIO_PIN_4
#define OLED_SDA_PIN                                GPIO_PIN_3

#define OLED_SCL_Clr                                GPIO_ResetBits(OLED_SCL_PORT,OLED_SCL_PIN)//SCL
#define OLED_SCL_Set                                GPIO_SetBits(OLED_SCL_PORT,OLED_SCL_PIN)

#define OLED_SDA_Clr                                GPIO_ResetBits(OLED_SDA_PORT,OLED_SDA_PIN)//SDA
#define OLED_SDA_Set                                GPIO_SetBits(OLED_SDA_PORT,OLED_SDA_PIN)


// I2C初始化
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);
// 显示测试
void draw(u8g2_t *u8g2);

#ifdef __cplusplus
}
#endif

#endif


u8g2_drv.c 文件

#include "u8g2_drv.h"

#define delay_us delay_n_us
#define delay_ms delay_n_ms

void IIC_Init(void)
{
        GPIO_InitType GPIO_InitStructure;

        RCC_EnableAPB2PeriphClk(OLED_SCL_RCC | OLED_SDA_RCC, ENABLE);

        GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_HIGH;
        GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStructure.GPIO_Alternate = GPIO_DC_HIGH;
        GPIO_InitStructure.Pin = OLED_SCL_PIN;
        GPIO_InitPeripheral(OLED_SCL_PORT, &GPIO_InitStructure);
      
        GPIO_InitStructure.Pin = OLED_SDA_PIN;
        GPIO_InitPeripheral(OLED_SDA_PORT, &GPIO_InitStructure);
}

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
                if(arg_int == 1)
                {
                        OLED_SCL_Set;
                }
                else if(arg_int == 0)
                {
                        OLED_SCL_Clr;
                }  
        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 == 1)
                {
                        OLED_SDA_Set;
                }
                else if(arg_int == 0)
                {
                        OLED_SDA_Clr;  
                }
        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 u8g2Init(u8g2_t *u8g2)
{
        u8g2_Setup_sh1106_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);
}

void draw(u8g2_t *u8g2)
{
        u8g2_ClearDisplay(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");

}


4、实际显示测试
    至此,移植过程已经全部完成了,我们可以在main 函数中添加测试代码了,首先肯定是要先引入我们编写的驱动文件 u8g2_drv.h,以下是测代码内容:
#include "main.h"
#include "u8g2_drv.h"

int main(void)
{
      
        IIC_Init();

        u8g2_t u8g2;
        u8g2Init(&u8g2);

        u8g2_FirstPage(&u8g2);
        do
        {
                draw(&u8g2);
        } while (u8g2_NextPage(&u8g2));

        while(1)
        {
               
        }
      
}


来看看实际的效果吧!
7.jpeg
    总结,整个移植过程其实非常简单,主要还是配置以减少内存的占用为主,毕竟U8g2用于Arduino中比较多,像之前的ESP8266和ESP32,这些模块封装的Flash都有4M,所以足够U8g2的开销,但如果不外置Flash情况下,考虑比较多的还是减少空间的占用了。最后测试显示中文显示不出来,目前还没找到具体的原因,如果之后解决了再更新吧!
三、生成自己的字库
    在正常显示之后,大家应该就会考虑如何生成自己的字库了吧!其实这部分已经有人写成了现成的工具,这里就直接贴出原作者的链接了,至于流程想要了解的话可以搜索下:“U8g2字体生成”。
# 字库生成工具原作者地址,这里感谢下

https://oshwhub.com/article/Easy-u8g2-font-generate-tools

    最后,将示例程序上传到了网盘,需要可以自己下载:
   
链接: https://pan.baidu.com/s/1-tU8tW526h0Mb7Evog_s6Q?pwd=gv9w 提取码: gv9w

本文也同步发在了B站,欢迎大家关注  https://www.bilibili.com/read/cv21546269?spm_id_from=333.999.0.0

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

19

帖子

1

粉丝