(一)Ai-M61-32s 硬件i2c移植u8g2库点亮0.96oled详细教程

[复制链接]
查看1901 | 回复10 | 2023-12-17 15:01:41 | 显示全部楼层 |阅读模式

u8g2是一个十分优秀的单色屏幕图形库,今天来把他移植到AI-M61-32s上。

一、硬件i2c初步了解

i2c 传输时需要填充的信息

struct bflb_i2c_msg_s {
uint16_t addr;
uint16_t flags;
uint8_t *buffer;
uint16_t length;
};

flags可以是以下参数:

#define I2C_M_READ    0x0001
#define I2C_M_TEN     0x0002
#define I2C_M_DMA     0x0004
#define I2C_M_NOSTOP  0x0040
#define I2C_M_NOSTART 0x0080

flags可以是0表示传输,我使用的模式是这个模式。

其他gpio和i2c具体初始化过程可以参考这篇文章

二、u8g2移植过程

1,源码获取和处理

olikraus/u8g2: U8glib library for monochrome displays, version 2 (github.com)

首先从上面的链接中获取源码并打开,如下图:

1.png

打开其中的csrc文件夹。

2.png

我们移植仅需要i2c屏幕部分,可以把不需要的源码删除。

屏幕驱动芯片是ssd1306,我们把u8x8_d_ssd1306_128x64_noname.c文件以外的其他类似于这样的驱动文件删除。

最后得到的是以下结果:

3.png

仅保留u8x8_d_ssd1306_128x64_noname.c文件。

2,移植源码

将上面得代码文件复制到工程文件中,并把他们放入一个新建的文件夹中。

如下图所示,我把代码文件放入到了u8g2中。

4.png

5.png

编写Cmakelist.txt文件,把头文件和源文件加进来。

cmake_minimum_required(VERSION 3.15)

include(proj.conf)

find_package(bouffalo_sdk REQUIRED HINTS $ENV{BL_SDK_BASE})

file(GLOB_RECURSE sources "${CMAKE_CURRENT_SOURCE_DIR}/u8g2/*.c")

sdk_add_compile_definitions(-DCONFIG_CLI_CMD_ENABLE)

sdk_add_include_directories(.)
sdk_add_include_directories(u8g2)

target_sources(app PRIVATE ${sources})
sdk_set_main_file(main.c)

project(helloworld)

3,初始化

首先将头文件引入。

6.png

初始化需要我们实现三个函数,这三个函数如下:

void i2c_transfer(uint8_t addr, size_t len, uint8_t *buffer);
uint8_t u8x8_gpio_and_delay_template(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
uint8_t u8x8_byte_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)

我们先实现最简单的**u8x8_gpio_and_delay_template函数。我们只需要在case U8X8_MSG_DELAY_10MICRO:case U8X8_MSG_DELAY_MILLI:**后添加两个延时函数即可。代码如下

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
        bflb_mtimer_delay_us(10 * arg_int);//第一个填充
        break;
    case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second
        bflb_mtimer_delay_ms(arg_int);//第二个填充
        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;
}

再实现u8x8_byte_i2c函数。我们要补充的部分是i2c初始化和i2c传输部分。在case U8X8_MSG_BYTE_INIT:后添加i2c初始部分。实现 case U8X8_MSG_BYTE_END_TRANSFER:后的i2c_transfer(u8x8_GetI2CAddress(u8x8) >> 1, buf_idx, buffer);函数。

代码如下:

uint8_t u8x8_byte_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{

    struct bflb_device_s *gpio;
    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 */

        gpio = bflb_device_get_by_name("gpio");
        /* I2C0_SCL */
        bflb_gpio_init(gpio, GPIO_PIN_0, GPIO_FUNC_I2C0 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);
        /* I2C0_SDA */
        bflb_gpio_init(gpio, GPIO_PIN_1, GPIO_FUNC_I2C0 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);

        i2c0 = bflb_device_get_by_name("i2c0");
        bflb_i2c_init(i2c0, 400000);
        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:
        i2c_transfer(u8x8_GetI2CAddress(u8x8) >> 1, buf_idx, buffer);

        break;
    default:
        return 0;
    }
    return 1;
}
void i2c_transfer(uint8_t addr, size_t len, uint8_t *buffer)
{
    struct bflb_i2c_msg_s msgs;

    msgs.addr = addr;
    msgs.flags = 0;
    msgs.buffer = buffer;
    msgs.length = len;

    bflb_i2c_transfer(i2c0, &msgs, 1);
}

注意:这里使用的gpioi2c0**都是全局变量,平时写代码还是不要这么写。

之后就可以在主函数中初始化了。

**首先定义一个句柄**u8g2

7.png

我们主要调u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2,U8G2_R0,u8x8_byte_i2c,u8x8_gpio_and_delay_template);

u8g2_InitDisplay(&u8g2);

u8g2_SetPowerSave(&u8g2, 0);

三个函数。

主函数代码如下:

int main(void)
{

    board_init();

    u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_i2c, u8x8_gpio_and_delay_template);
    u8g2_InitDisplay(&u8g2);//初始化显示
    u8g2_SetPowerSave(&u8g2, 0);//关闭省电

    gpio = bflb_device_get_by_name("gpio");

    bflb_gpio_init(gpio, GPIO_PIN_12, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
    bflb_gpio_init(gpio, GPIO_PIN_14, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
    bflb_gpio_init(gpio, GPIO_PIN_15, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);

    bflb_gpio_reset(gpio, GPIO_PIN_12);
    bflb_gpio_reset(gpio, GPIO_PIN_14);
    bflb_gpio_reset(gpio, GPIO_PIN_15);


    bflb_gpio_set(gpio, GPIO_PIN_12);

    u8g2_SetContrast(&u8g2,50);//亮度设置

    while (1)
    {
        u8g2_ClearBuffer(&u8g2);

        //u8g2_DrawBox(&u8g2, 0, 0, 128, 64);
        //LOG_F("2");
        u8g2_SetFont(&u8g2,u8g2_font_wqy12_t_chinese3);
        for(int i=1;i<=4;i++){
            u8g2_DrawUTF8(&u8g2,0, i *16-1, "安信可");
        }
        //u8g2_DrawGlyph(&u8g2,5, 20, 0x2603);
        //u8g2_DrawGlyph(&u8g2,31, 20, 0x23f0);
        u8g2_SendBuffer(&u8g2);
        //LOG_F("3");
    }
}

4,效果

8.png

5,主函数代码

要是想快速移植,就把代码文件处理好,并写好cmakelist.txt,再将下面的main.c文件复制进自己的主函数文件中即可。

#include "bflb_mtimer.h"
#include "board.h"
#include "bflb_gpio.h"
#include "bflb_i2c.h"
#define DBG_TAG "MAIN"
#include "log.h"

#include "u8g2.h"

struct bflb_device_s *gpio;
u8g2_t u8g2;
static struct bflb_device_s *i2c0;

void i2c_transfer(uint8_t addr, size_t len, uint8_t *buffer)
{
    struct bflb_i2c_msg_s msgs;

    msgs.addr = addr;
    msgs.flags = 0;
    msgs.buffer = buffer;
    msgs.length = len;

    bflb_i2c_transfer(i2c0, &msgs, 1);
}

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
        bflb_mtimer_delay_us(10 * arg_int);
        break;
    case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second
        bflb_mtimer_delay_ms(arg_int);
        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;
}

uint8_t u8x8_byte_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{

    struct bflb_device_s *gpio;
    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 */

        gpio = bflb_device_get_by_name("gpio");
        /* I2C0_SCL */
        bflb_gpio_init(gpio, GPIO_PIN_0, GPIO_FUNC_I2C0 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);
        /* I2C0_SDA */
        bflb_gpio_init(gpio, GPIO_PIN_1, GPIO_FUNC_I2C0 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);

        i2c0 = bflb_device_get_by_name("i2c0");
        bflb_i2c_init(i2c0, 400000);
        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:
        i2c_transfer(u8x8_GetI2CAddress(u8x8) >> 1, buf_idx, buffer);

        break;
    default:
        return 0;
    }
    return 1;
}

int main(void)
{

    board_init();

    u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_i2c, u8x8_gpio_and_delay_template);
    u8g2_InitDisplay(&u8g2);
    u8g2_SetPowerSave(&u8g2, 0);

    gpio = bflb_device_get_by_name("gpio");

    bflb_gpio_init(gpio, GPIO_PIN_12, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
    bflb_gpio_init(gpio, GPIO_PIN_14, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
    bflb_gpio_init(gpio, GPIO_PIN_15, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);

    bflb_gpio_reset(gpio, GPIO_PIN_12);
    bflb_gpio_reset(gpio, GPIO_PIN_14);
    bflb_gpio_reset(gpio, GPIO_PIN_15);


    bflb_gpio_set(gpio, GPIO_PIN_12);//点亮红灯

    u8g2_SetContrast(&u8g2,50);//亮度设置

    while (1)
    {
        u8g2_ClearBuffer(&u8g2);
        

        //u8g2_DrawBox(&u8g2, 0, 0, 128, 64);
        //LOG_F("2");
        u8g2_SetFont(&u8g2,u8g2_font_wqy12_t_chinese3);
        for(int i=1;i<=4;i++){
            u8g2_DrawUTF8(&u8g2,0, i *16-1, "安信可");
        }
        

        //u8g2_DrawGlyph(&u8g2,5, 20, 0x2603);
        //u8g2_DrawGlyph(&u8g2,31, 20, 0x23f0);


        u8g2_SendBuffer(&u8g2);
        //LOG_F("3");
    }
}

三、总结

具体函数api在github上,写的十分详细,可以自行查看尝试。

最近考试很多,拖了好久才把这篇文章写完,今天总算是把他写完了。

本帖被以下淘专辑推荐:

回复

使用道具 举报

1084504793 | 2023-12-17 17:22:26 | 显示全部楼层
学到了
回复

使用道具 举报

san | 2023-12-17 21:48:00 | 显示全部楼层
学到了
回复

使用道具 举报

lazy | 2023-12-17 22:41:55 | 显示全部楼层
学习了
回复

使用道具 举报

沈夜 | 2023-12-17 23:23:57 | 显示全部楼层
大佬  有无完整的工程项目
回复 支持 反对

使用道具 举报

AXK123456 | 2023-12-18 08:19:56 | 显示全部楼层
学到了学到了
回复 支持 反对

使用道具 举报

WT_0213 | 2023-12-18 09:11:37 | 显示全部楼层
回复

使用道具 举报

bzhou830 | 2023-12-18 11:36:20 | 显示全部楼层
点赞
选择去发光,而不是被照亮
回复

使用道具 举报

一个认真的人 | 2023-12-18 19:11:03 | 显示全部楼层
沈夜 发表于 2023-12-17 23:23
大佬  有无完整的工程项目

文件超过2m了发不出来
回复 支持 反对

使用道具 举报

沈夜 | 2023-12-18 20:47:04 | 显示全部楼层
一个认真的人 发表于 2023-12-18 19:11
文件超过2m了发不出来

build 目录不要打包,就够了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则