发新帖本帖赏金 100.00元(功能说明)我要提问
返回列表
打印
[MM32生态]

【MM32+模块】系列:10、OLED显示驱动及u8g2移植与演示

[复制链接]
1005|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
#申请原创#   @21小跑堂




简介

OLED,是一种有机电质发光器件,又称为有机电激光显示。与之前提到的LCD1602、LCD1284相比,具有更低的功耗、更快的响应速度、更宽的可视角度、更高的显示分辨率、更轻的质量等等优点,而被广泛使用。淘宝上找到一款常见的是由SSD1306驱动的0.96寸的OLED,驱动接口有3线串行、4线串行和I2C这3种接口类型;另外OLED还有支持并行接口驱动的,这个会在后面的帖子来给大家分享。

u8g2是用于嵌入式设备支持单色OLED或LCD的图形库软件,支持市面上决大多数的显示屏驱动控制器,SSD1306就包含在内。u8g2图形库包含3个部分:u8g2、u8x8和u8log,其中u8g2包括了所有的图形功能,支持多种字体;u8x8可以直接将显示内容输出到显示屏显示,无需显示缓存;u8log则是一个输出终端的功能。另外u8g2还支持3种不同的绘图模式:全屏缓冲模式、页面模式和u8x8仅字符模式;全屏缓冲模式和页面模式支持所有的图形,区别在于所占用的显示缓存大小不同,从而致使刷新速度的快慢;而u8x8仅字符模式不需要显示缓存,可以直接将需要显示的内容输出显示,缺点就是不支持u8g8的图形显示,同时也依赖于显示驱动芯片是否支持该模式。


实现功能

1、OLED底层驱动:分别使用硬件I2C、软件模拟I2C、硬件SPI、软件模拟SPI这4种方式来实现对OLED显示屏的驱动,并实现一幅动画效果的演示。
2、u8g2移植:基于I2C和SPI这两种不同接口的驱动方式,分别移植u8g2的GUI,对移植过程和注意事项进行说明。
3、u8g2演示:通过一个简单的示例完成u8g2的效果演示。



1、OLED底层驱动

使用自制的MM32F0140最小系统板,结合I2C和SPI接口的两块0.96寸的OLED模块,实现驱动显示。在上图中SPI接口的OLED模块中,CS是片选信号连接PA4、DC是数据或命令切换连接PB0、RES是复位连接PB1、DI是SPI数据线连接PB5、DO是SPI时钟线连接PB7;在I2C接口的OLED中,SCL和SDA与MM32F0140的硬件I2C相连接,分别连接PB10和PB11。按照这样线序连接方式,一方面是按照MCU引脚I2C/SPI复用功能连接的,可以通过硬件I2C/SPI接口来驱动OLED显示;另一方面可以直接使用GPIO来模拟I2C/SPI时序来驱动OLED显示,如下图所示:

在OLED初始化过程中先根据宏定义对MCU的I2C和SPI进行初始化配置,然后配置OLED显示屏有参数并清空显示内容,最后通过TASK调用OLED_Handler来显示一个动画效果。对于使用硬件I2C还是软件模拟I2C、硬件SPI还是软件模拟SPI可以通过OLED_HW_I2C和OLED_HW_SPI宏来配置,参考代码如下所示:
void OLED_I2C_SendData(uint8_t Address, uint8_t Data)
{
#if OLED_HW_I2C /* 硬件I2C */

    I2C_SendData(I2C1, Address);
    while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));

    I2C_SendData(I2C1, Data);
    while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));

    I2C_GenerateSTOP(I2C1, ENABLE);
    while(!I2C_GetFlagStatus(I2C1, I2C_FLAG_STOP_DET));

#else           /* 模拟I2C */

    sI2C_GenerateStart(&sI2C_OLED);

    sI2C_WriteByte(&sI2C_OLED, 0x78);

    if(sI2C_GetACK(&sI2C_OLED))
    {
        sI2C_GenerateStop(&sI2C_OLED);    return;
    }

    sI2C_WriteByte(&sI2C_OLED, Address);

    if(sI2C_GetACK(&sI2C_OLED))
    {
        sI2C_GenerateStop(&sI2C_OLED);    return;
    }

    sI2C_WriteByte(&sI2C_OLED, Data);

    if(sI2C_GetACK(&sI2C_OLED))
    {
        sI2C_GenerateStop(&sI2C_OLED);    return;
    }

    sI2C_GenerateStop(&sI2C_OLED);

#endif
}

void OLED_SPI_SendData(uint8_t Data)
{
#if OLED_HW_SPI /* 硬件SPI */

    uint32_t Timeout = 0;

    SPI_SendData(SPI1, Data);
    while(!SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT))
    {
        if(Timeout++ > 0xFFFF) break;
    }

#else           /* 模拟SPI */

    for(uint8_t i = 0; i < 8; i++)
    {
        if((Data << i) & 0x80) OLED_DI_H();
        else                   OLED_DI_L();

        OLED_DO_H();
        OLED_DO_L();
    }

#endif
}

void OLED_WriteCMD(uint8_t Command)
{
    OLED_I2C_SendData(0x00, Command);

    OLED_DC_L();
    OLED_CS_L();
    OLED_SPI_SendData(Command);
    OLED_CS_H();
}

void OLED_WriteDAT(uint8_t Data)
{
    OLED_I2C_SendData(0x40, Data);

    OLED_DC_H();
    OLED_CS_L();
    OLED_SPI_SendData(Data);
    OLED_CS_H();
}

void OLED_WriteBuffer(uint8_t *Buffer, uint8_t Length)
{
    for(uint8_t i = 0; i < Length; i++)
    {
        OLED_WriteDAT(*Buffer++);
    }
}

void OLED_InitI2C(void)
{
#if OLED_HW_I2C /* 硬件I2C */

    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef  I2C1_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1ENR_I2C1, ENABLE);

    I2C_StructInit(&I2C1_InitStructure);
    I2C1_InitStructure.I2C_Mode       = I2C_Mode_MASTER;
    I2C1_InitStructure.I2C_OwnAddress = 0;
    I2C1_InitStructure.I2C_Speed      = I2C_Speed_FAST;
    I2C1_InitStructure.I2C_ClockSpeed = 400000;
    I2C_Init(I2C1, &I2C1_InitStructure);

    I2C_Send7bitAddress(I2C1, 0x78, I2C_Direction_Transmitter);

    I2C_Cmd(I2C1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_1);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

#else           /* 模拟I2C */

    sI2C_Init(&sI2C_OLED);

#endif
}

void OLED_InitSPI(void)
{
#if OLED_HW_SPI /* 硬件SPI */

    GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_SPI1, ENABLE);

    SPI_StructInit(&SPI_InitStructure);
    SPI_InitStructure.SPI_Mode              = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize          = SPI_DataSize_8b;
    SPI_InitStructure.SPI_DataWidth         = 8;
    SPI_InitStructure.SPI_CPOL              = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA              = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS               = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
    SPI_InitStructure.SPI_FirstBit          = SPI_FirstBit_MSB;
    SPI_Init(SPI1, &SPI_InitStructure);

    SPI_Cmd(SPI1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_0);   /* PA4 SPI1_NSS  */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_0);   /* PA5 SPI1_SCK  */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_0);   /* PA6 SPI1_MISO */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_0);   /* PA7 SPI1_MOSI */

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Rx);
    SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

#else           /* 模拟SPI */

    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    OLED_CS_H();  OLED_DC_L();
    OLED_DI_L();  OLED_DO_L();

#endif

    OLED_RES_H(); SysTick_DelayMS(100);
    OLED_RES_L(); SysTick_DelayMS(100);
    OLED_RES_H(); SysTick_DelayMS(100);  
}

void OLED_InitCFG(void)
{
    SysTick_DelayMS(1000);  /* 这里的延时很重要,上电后延时,没有错误的冗余设计 */

    OLED_WriteCMD(0xAE);    /* Display Off */
    OLED_WriteCMD(0x20);    /* Set Memory Addressing Mode */
    OLED_WriteCMD(0x10);    /* 00  Horizontal Addressing Mode
                               01  Vertical Addressing Mode
                               10  Page Addressing Mode (RESET)
                               11  Invalid */
    OLED_WriteCMD(0xB0);    /* Set Page Start Address for Page Addressing Mode,0-7 */
    OLED_WriteCMD(0xC8);    /* Set COM Output Scan Direction */
    OLED_WriteCMD(0x00);    /* Set Low  Column Address */
    OLED_WriteCMD(0x10);    /* Set High Column Address */
    OLED_WriteCMD(0x40);    /* Set Start Line Address */
    OLED_WriteCMD(0x81);    /* Set Contrast Control Register */
    OLED_WriteCMD(0xFF);    /* Brightness 0x00~0xff */
    OLED_WriteCMD(0xA1);    /* Set Segment Re-map 0 to 127 */
    OLED_WriteCMD(0xA6);    /* Set Normal Display */
    OLED_WriteCMD(0xA8);    /* Set Multiplex Ratio(1 to 64) */
    OLED_WriteCMD(0x3F);
    OLED_WriteCMD(0xA4);    /* 0xA4: Output Follows RAM Content
                               0xA5: Output Ignores RAM Content */
    OLED_WriteCMD(0xD3);    /* Aet Display offset */
    OLED_WriteCMD(0x00);    /* Not Offset */
    OLED_WriteCMD(0xD5);    /* Set Display Clock Divide Ratio/Oscillator Frequency */
    OLED_WriteCMD(0xF0);    /* Set Divide Ratio */
    OLED_WriteCMD(0xD9);    /* Set Pre-charge Period */
    OLED_WriteCMD(0x22);
    OLED_WriteCMD(0xDA);    /* Set Com Pins Hardware Configuration */
    OLED_WriteCMD(0x12);
    OLED_WriteCMD(0xDB);    /* Set VCOMH */
    OLED_WriteCMD(0x20);    /* 0x20=0.77*VCC */
    OLED_WriteCMD(0x8D);    /* Set DC-DC Enable */
    OLED_WriteCMD(0x14);
    OLED_WriteCMD(0xAF);    /* Turn On OLED Panel */
}

void OLED_SetPosition(uint8_t x, uint8_t y)
{
        OLED_WriteCMD(0xB0 + y);
        OLED_WriteCMD(((x & 0xF0) >> 4) | 0x10);
        OLED_WriteCMD(((x & 0x0F) >> 0) | 0x00);
}

void OLED_Clear(uint8_t Data)
{
    uint8_t x = 0, y = 0;

    for(y = 0; y < 8; y++)
    {
        OLED_SetPosition(0, y);

        for(x = 0; x < 128; x++)
        {
            OLED_WriteDAT(Data);
        }
    }
}

void OLED_Init(void)
{
    OLED_InitI2C();

    OLED_InitSPI();

    OLED_InitCFG();

    OLED_Clear(0x00);

    TASK_Append(TASK_ID_OLED, OLED_Handler, 50);
}

#include "DOG02.c"
#include "DOG03.c"
#include "DOG04.c"
#include "DOG05.c"
#include "DOG06.c"
#include "DOG07.c"
#include "DOG08.c"
#include "DOG09.c"
#include "DOG10.c"
#include "DOG11.c"
#include "DOG12.c"
#include "DOG13.c"
#include "DOG14.c"
#include "DOG15.c"
#include "DOG16.c"
#include "DOG17.c"
#include "DOG18.c"
#include "DOG19.c"
#include "DOG20.c"
#include "DOG21.c"
#include "DOG22.c"
#include "DOG23.c"
#include "DOG24.c"
#include "DOG25.c"
#include "DOG26.c"
#include "DOG27.c"

void OLED_DrawImage(uint8_t Width, uint8_t Height, const uint8_t *ImageData)
{
    uint8_t  x = 0, y = 0;
    uint8_t  Buffer[0x80];
    uint16_t Index    = 0;

    for(y = 0; y < Height / 8; y++)
    {
        OLED_SetPosition(32, y);

        for(x = 0; x < Width; x++)
        {
            Buffer[x] = ~ImageData[Index++];
        }

        OLED_WriteBuffer(Buffer, Width);
    }
}

void OLED_Handler(void)
{
    static uint8_t Index = 0;

    switch(Index)
    {
        case  0 : OLED_DrawImage(64, 64, gImage_DOG02); break;
        case  1 : OLED_DrawImage(64, 64, gImage_DOG03); break;
        case  2 : OLED_DrawImage(64, 64, gImage_DOG04); break;
        case  3 : OLED_DrawImage(64, 64, gImage_DOG05); break;
        case  4 : OLED_DrawImage(64, 64, gImage_DOG06); break;
        case  5 : OLED_DrawImage(64, 64, gImage_DOG07); break;
        case  6 : OLED_DrawImage(64, 64, gImage_DOG08); break;
        case  7 : OLED_DrawImage(64, 64, gImage_DOG09); break;
        case  8 : OLED_DrawImage(64, 64, gImage_DOG10); break;
        case  9 : OLED_DrawImage(64, 64, gImage_DOG11); break;
        case  10: OLED_DrawImage(64, 64, gImage_DOG12); break;
        case  11: OLED_DrawImage(64, 64, gImage_DOG13); break;
        case  12: OLED_DrawImage(64, 64, gImage_DOG14); break;
        case  13: OLED_DrawImage(64, 64, gImage_DOG15); break;
        case  14: OLED_DrawImage(64, 64, gImage_DOG16); break;
        case  15: OLED_DrawImage(64, 64, gImage_DOG17); break;
        case  16: OLED_DrawImage(64, 64, gImage_DOG18); break;
        case  17: OLED_DrawImage(64, 64, gImage_DOG19); break;
        case  18: OLED_DrawImage(64, 64, gImage_DOG20); break;
        case  19: OLED_DrawImage(64, 64, gImage_DOG21); break;
        case  20: OLED_DrawImage(64, 64, gImage_DOG22); break;
        case  21: OLED_DrawImage(64, 64, gImage_DOG23); break;
        case  22: OLED_DrawImage(64, 64, gImage_DOG24); break;
        case  23: OLED_DrawImage(64, 64, gImage_DOG25); break;
        case  24: OLED_DrawImage(64, 64, gImage_DOG26); break;
        case  25: OLED_DrawImage(64, 64, gImage_DOG27); break;
    }

    Index = (Index + 1) % 26;
}

OLED底层驱动显示效果:


2、u8g2移植

首先我们到https://github.com/olikraus/u8g2这个网址下载开源代码,当前版本中有基于C和C++这两种代码库,并提供了示例参考例程;我们使用的是基于C的开发库,所以将cscr文件中的源代码添加到工程中,根据文件类型和功能,对其进行分文件夹归类,最终如附件源码工程中所示;然后将u8g2的源代码添加到工程中,其中包含了u8g2的图形实现的功能程序,也有根据显示驱动来添加的底层驱动文件,最后对工程头文件包含路径进行设置,具体参考附件中的源代码工程;接下来我们就可以通过代码来实现,因为我们同时是驱动两个OLED,分别是I2CSPI接口的,所以在移植的时候,也同时包含了这两种实现方式;我们在main.c文件中实现接口和功能,先根据不同的接口定义2u8g2结构体变量并进行初始化:
u8g2_t u8g2_spi;
u8g2_t u8g2_i2c;

void u8g2_UserInit(void)
{
    /* 初始化u8g2 */
    u8g2_Setup_ssd1306_128x64_noname_f(&u8g2_spi, U8G2_R0, u8x8_byte_4wire_sw_spi, u8x8_mm32_spi_gpio_and_delay);

    /* 初始化显示器 */
    u8g2_InitDisplay(&u8g2_spi);

    /* 唤醒显示器 */
    u8g2_SetPowerSave(&u8g2_spi, 0);

    /* 设置英文字体 */
    u8g2_SetFont(&u8g2_spi, u8g2_font_6x12_mr);


    /* 初始化u8g2 */
    u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2_i2c, U8G2_R0, u8x8_byte_sw_i2c, u8x8_mm32_i2c_gpio_and_delay);

    /* 初始化显示器 */
    u8g2_InitDisplay(&u8g2_i2c);

    /* 唤醒显示器 */
    u8g2_SetPowerSave(&u8g2_i2c, 0);

    /* 设置英文字体 */
    u8g2_SetFont(&u8g2_i2c, u8g2_font_6x12_mr);


    TASK_Append(TASK_ID_U8G2, u8g2_UserHandler, 50);
}

在初始化的时候,我们发现I2C和SPI调用的接口函数是不一样的,SPI接口调用的是u8g2_Setup_ssd1306_128x64_noname_f函数,I2C接口调用的是u8g2_Setup_ssd1306_i2c_128x64_noname_f函数;在这两个函数中形参功能都是类似的,第一个参数是全局u8g2结构体变量,第二个参数用于指示显示方向,第3个参数用于指明u8g2驱动操作函数,第4个参数用于指明u8g2底层GPIO操作的回调函数;不管是SPI还是I2C,对于u8g2底层驱动来说,都是用模拟时序的方式来实现的,所以在移植u8g2时,对之前的OLED_HW_I2C和OLED_HW_SPI这两个宏都要设置为0

u8g2_Setup_ssd1306_128x64_noname_f和u8g2_Setup_ssd1306_i2c_128x64_noname_f这两个函数在u8g2_d_setup.c文件中,仅需要留下这两个函数就可以,将其它的函数进行屏蔽。u8g2_d_memory.c文件中定义memory操作功能函数,我们这边选用u8g2_m_16_8_f这个函数,将其它的函数进行屏蔽。在后面示例中需要显示字符,所以就需要选择一个系统字库,通过调用u8g2_SetFont来进行设置;字体的选择在u8g2_fonts.c文件中,保留需要使用的字库,对其它的字库进行屏蔽。如上这些对没有使用的函数或者字体进行屏蔽是根据显示驱动、MEMORY管理和字体选择来决定的,屏蔽也是为了节省代码空间和未使用到的报错。

最后就是要实现底层对GPIO操作和延时的回调函数,供u8g2底层驱动来调用;在SPI接口调用时使用到SPI_DATA、SPI_CLOCK、SPI_CS、DC、RESET和DELAY_NANO,在I2C接口调用时使用到了I2C_DATA、I2C_CLOCK和DELAY_I2C;具体的函数接口实现如下所示:
uint8_t u8x8_mm32_spi_gpio_and_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:
            OLED_Init();                    
            break;

        case U8X8_MSG_GPIO_SPI_DATA:
            if(arg_int) OLED_DI_H();
            else        OLED_DI_L();
            break;

        case U8X8_MSG_GPIO_SPI_CLOCK:
            if(arg_int) OLED_DO_H();
            else        OLED_DO_L();
            break;

        case U8X8_MSG_GPIO_CS:
            if(arg_int) OLED_CS_H();
            else        OLED_CS_L();
            break;

        case U8X8_MSG_GPIO_DC:
            if(arg_int) OLED_DC_H();
            else        OLED_DC_L();
            break;

        case U8X8_MSG_GPIO_RESET:
            if(arg_int) OLED_RES_H();
            else        OLED_RES_L();
            break;

        case U8X8_MSG_DELAY_NANO:
            __nop();
            break;

        default: return 0;
    }

    return 1;
}

uint8_t u8x8_mm32_i2c_gpio_and_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:
            OLED_Init();                    
            break;

        case U8X8_MSG_GPIO_I2C_DATA:
            if(arg_int) OLED_SDA_H();
            else        OLED_SDA_L();
            break;

        case U8X8_MSG_GPIO_I2C_CLOCK:
            if(arg_int) OLED_SCL_H();
            else        OLED_SCL_L();
            break;

        case U8X8_MSG_DELAY_I2C:
            __nop();
            break;

        default: return 0;
    }

    return 1;
}

至此u8g2移植和初始化就完成了。


3、u8g2演示
演示的功能是分别在I2C和SPI接口的OLED显示屏上显示3个半径相同坐标不同的圆,圆半径由小到大,再由大到小进行变化显示,在屏幕顶端居中的位置显示当前的圆半径数值,具体代码实现如下:
void u8g2_UserHandler(void)
{
    static int rad = 0, direction = 0, select = 0;

    char str[10];
    memset(str, 0, sizeof(str));

    if(direction == 0)
    {
        if(++rad == 0x1F) direction = 1;
    }
    else
    {
        if(--rad == 0x01) direction = 0;
    }

    sprintf(str, "%02d", rad);

    if(select == 0)
    {
        /* 清空缓冲区的内容 */
        u8g2_ClearBuffer(&u8g2_spi);

        /* 画圆 */
        u8g2_DrawCircle(&u8g2_spi, 32, 32, rad, U8G2_DRAW_ALL);
        u8g2_DrawCircle(&u8g2_spi, 64, 32, rad, U8G2_DRAW_ALL);
        u8g2_DrawCircle(&u8g2_spi, 96, 32, rad, U8G2_DRAW_ALL);

        u8g2_DrawStr(&u8g2_spi, 58, 10, str);

        /* 绘制缓冲区的内容 */
        u8g2_SendBuffer(&u8g2_spi);
    }
    else
    {
        /* 清空缓冲区的内容 */
        u8g2_ClearBuffer(&u8g2_i2c);

        /* 画圆 */
        u8g2_DrawCircle(&u8g2_i2c, 32, 32, rad, U8G2_DRAW_ALL);
        u8g2_DrawCircle(&u8g2_i2c, 64, 32, rad, U8G2_DRAW_ALL);
        u8g2_DrawCircle(&u8g2_i2c, 96, 32, rad, U8G2_DRAW_ALL);

        u8g2_DrawStr(&u8g2_i2c, 58, 10, str);

        /* 绘制缓冲区的内容 */
        u8g2_SendBuffer(&u8g2_i2c);
    }

    if((direction == 0) && (rad == 1))
    {
        if(select == 0) select = 1;
        else            select = 0;
    }
}

GUI演示效果:

附件
软件工程源代码: OLED.part1.rar (5 MB) OLED.part2.rar (3.12 MB)

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 100.00 元 2022-04-24
理由:恭喜通过原创文章审核!请多多加油哦!

评论
21小跑堂 2022-4-24 15:51 回复TA
使用了软件和硬件两种方式,运用了SPI和I2C两种通信,在MM32平台均实现了OLED的显示,并移植了基本涵盖了u8g2,用户基于该**可快速在灵动平台搭建OLED开发,其软件模拟也可移植到其他平台。功能齐全,讲解详细。 
沙发
xld0932|  楼主 | 2022-4-21 18:07 | 只看该作者
本帖分别使用硬件I2C、SPI、软件模拟I2C、SPI来驱动OLED显示,并基于I2C、SPI这两种不同的接口移植u8g2来进行演示

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
wujac + 1 很给力!
板凳
www5911839| | 2022-4-21 22:38 | 只看该作者

使用特权

评论回复
地板
www5911839| | 2022-4-21 22:48 | 只看该作者
个人感觉 gif 动图感觉太小了,效果没体现出来。大侠,方不方便录制成视频,上传到 B 站呢

使用特权

评论回复
5
xld0932|  楼主 | 2022-4-22 07:56 | 只看该作者
www5911839 发表于 2022-4-21 22:48
个人感觉 gif 动图感觉太小了,效果没体现出来。大侠,方不方便录制成视频,上传到 B 站呢 ...

已经反馈需求了,等后面21ic可以在帖子里上传视频就完美了其实后面有很多是需要结合视频演示的分享呢,文字结合视频效果会更好些,现在只能上传GIF的动图,每次都是把视频转换成GIF,质量和大小都有缺失

使用特权

评论回复
6
terryzhouhz| | 2022-4-28 09:13 | 只看该作者

使用特权

评论回复
7
xld0932|  楼主 | 2022-4-28 10:44 | 只看该作者

使用特权

评论回复
8
tpgf| | 2022-5-4 10:10 | 只看该作者
特羡慕这种显示的

使用特权

评论回复
9
drer| | 2022-5-4 10:24 | 只看该作者
图像真是很不错

使用特权

评论回复
10
tfqi| | 2022-5-4 10:31 | 只看该作者
这是什么种类的屏啊

使用特权

评论回复
11
coshi| | 2022-5-4 10:39 | 只看该作者
楼主简直就是全能啊

使用特权

评论回复
12
kxsi| | 2022-5-4 10:47 | 只看该作者
视频还是非常不错的

使用特权

评论回复
13
wiba| | 2022-5-4 10:54 | 只看该作者
一般这种移植 修改的额地方多吗

使用特权

评论回复
14
xld0932|  楼主 | 2022-5-4 21:26 | 只看该作者
wiba 发表于 2022-5-4 10:54
一般这种移植 修改的额地方多吗

如果是u8g2支持的屏驱动,只需要根据硬件配置相应的GPIO,对LCD进行初始化配置,然后再实现u8g2驱动架构的回调函数,就可以啦

使用特权

评论回复
15
xld0932|  楼主 | 2022-5-4 21:51 | 只看该作者
coshi 发表于 2022-5-4 10:39
楼主简直就是全能啊

使用特权

评论回复
16
xld0932|  楼主 | 2022-5-4 21:52 | 只看该作者
tfqi 发表于 2022-5-4 10:31
这是什么种类的屏啊

OLED显示屏,128*64点阵

使用特权

评论回复
17
xld0932|  楼主 | 2022-5-4 21:53 | 只看该作者
drer 发表于 2022-5-4 10:24
图像真是很不错

嗯,还是要靠取模软件来取数据的

使用特权

评论回复
18
fuyuqingfeng| | 2022-10-13 16:21 | 只看该作者
楼主,请教下u8g2中文怎么显示?我这边显示英文 数字 符号都没问题,就中文显示不出来

使用特权

评论回复
发新帖 本帖赏金 100.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:上海灵动微电子股份有限公司资深现场应用工程师
简介:诚信·承诺·创新·合作

67

主题

2993

帖子

29

粉丝