打印
[活动专区]

【AT-START-F425测评】使用面向对象实现模拟IIC驱动OLED

[复制链接]
1073|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
#申请原创#
一、简介
使用面向的编程思想封装I2C驱动,将I2C属性、操作封装成库,在需要创建一个I2C设备时只需要实例化一个I2C对象,本文基于AT32F425和标准库做进一步封装。完成I2C驱动的封装后,依据继承特性实现OLED液晶屏的驱动开发及封装。
二、I2C驱动面向对象封装
hal_i2c.h头文件类定义如下:
#ifndef __HAL_I2C_H__
#define __HAL_I2C_H__

#include "at32f425.h"

#define HAL_ERROR 0
#define HAL_OK 1

//定义I2C类
typedef struct I2C_Type
{
    //属性
    gpio_type *GPIOx_SCL; //SCL所属gpio组,如:GPIOA
    gpio_type *GPIOx_SDA; //SDA所属gpio组,如:GPIOA
    unsigned int GPIO_SCL; //SCL引脚,如:GPIO_PINS_2
    unsigned int GPIO_SDA; //SDA引脚,如:GPIO_PINS_2
    //操作
    void (*I2C_Init)(const struct I2C_Type*);
    void (*I2C_Start)(const struct I2C_Type*);
    void (*I2C_Stop)(const struct I2C_Type*);
    unsigned char (*I2C_Wait_Ack)(const struct I2C_Type*);
    void (*I2C_Ack)(const struct I2C_Type*);
    void (*I2C_NAck)(const struct I2C_Type*);
    void (*I2C_Send_Byte)(const struct I2C_Type*, unsigned char);
    unsigned char (*I2C_Read_Byte)(const struct I2C_Type*, unsigned char);
    void (*delay_us)(unsigned int);

} I2C_TypeDef;

extern I2C_TypeDef I2C_t;

#endif
hal_i2c.c文件具体操作函数如下:
#include "hal_i2c.h"
#include "delay.h"

//设置SDA输入模式
static void SDA_IN(const struct I2C_Type* I2C_Type_t)
{
    gpio_init_type gpio_init_struct;

    //GPIO_SDA初始化设置
    gpio_init_struct.gpio_pins = I2C_Type_t->GPIO_SDA;
    gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init(I2C_Type_t->GPIOx_SDA, &gpio_init_struct);
}

//设置SDA为输出模式
static void SDA_OUT(const struct I2C_Type* I2C_Type_t)
{
    gpio_init_type gpio_init_struct;

    //GPIO_SDA初始化设置
    gpio_init_struct.gpio_pins = I2C_Type_t->GPIO_SDA;
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init(I2C_Type_t->GPIOx_SDA, &gpio_init_struct);
}

//设置SCL电平
static void I2C_SCL(const struct I2C_Type* I2C_Type_t, int n)
{
    if(n == 1)
    {
        gpio_bits_write(I2C_Type_t->GPIOx_SCL, I2C_Type_t->GPIO_SCL, TRUE); //设置SCL为高电平
    }
    else
    {
        gpio_bits_write(I2C_Type_t->GPIOx_SCL, I2C_Type_t->GPIO_SCL, FALSE); //设置SCL为低电平
    }
}

//设置SDA电平
static void I2C_SDA(const struct I2C_Type* I2C_Type_t, int n)
{
    if(n == 1)
    {
        gpio_bits_write(I2C_Type_t->GPIOx_SDA, I2C_Type_t->GPIO_SDA, TRUE); //设置SDA为高电平
    }
    else
    {
        gpio_bits_write(I2C_Type_t->GPIOx_SDA, I2C_Type_t->GPIO_SDA, FALSE); //设置SDA为低电平
    }
}

//读取SDA电平
static unsigned char READ_SDA(const struct I2C_Type* I2C_Type_t)
{
    return gpio_input_data_bit_read(I2C_Type_t->GPIOx_SDA, I2C_Type_t->GPIO_SDA); //读取SDA电平
}

//I2C初始化
static void I2C_Init_t(const struct I2C_Type* I2C_Type_t)
{
    gpio_init_type gpio_init_struct;

    //根据GPIO组初始化GPIO时钟
    if(I2C_Type_t->GPIOx_SCL == GPIOA || I2C_Type_t->GPIOx_SDA == GPIOA)
    {
        crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); //使能GPIOA时钟
    }

    if(I2C_Type_t->GPIOx_SCL == GPIOB || I2C_Type_t->GPIOx_SDA == GPIOB)
    {
        crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE); //使能GPIOB时钟
    }

    if(I2C_Type_t->GPIOx_SCL == GPIOC || I2C_Type_t->GPIOx_SDA == GPIOC)
    {
        crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE); //使能GPIOC时钟
    }

    if(I2C_Type_t->GPIOx_SCL == GPIOD || I2C_Type_t->GPIOx_SDA == GPIOD)
    {
        crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE); //使能GPIOD时钟
    }

    if(I2C_Type_t->GPIOx_SCL == GPIOF || I2C_Type_t->GPIOx_SDA == GPIOF)
    {
        crm_periph_clock_enable(CRM_GPIOF_PERIPH_CLOCK, TRUE); //使能GPIOF时钟
    }

    //GPIO_SCL初始化设置
    gpio_init_struct.gpio_pins = I2C_Type_t->GPIO_SCL;
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; //gpio output mode
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN; //output open-drain
    gpio_init_struct.gpio_pull = GPIO_PULL_UP; //pull-up
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; //stronger sourcing/sinking strength
    gpio_init(I2C_Type_t->GPIOx_SCL, &gpio_init_struct);
    //GPIO_SDA初始化设置
    gpio_init_struct.gpio_pins = I2C_Type_t->GPIO_SDA;
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN; //output open-drain
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init(I2C_Type_t->GPIOx_SDA, &gpio_init_struct);
    //SCL、SDA的初始化均为高电平
    I2C_SCL(I2C_Type_t, 1);
    I2C_SDA(I2C_Type_t, 1);
}

//I2C Start
static void I2C_Start_t(const struct I2C_Type* I2C_Type_t)
{
    SDA_OUT(I2C_Type_t);
    I2C_SDA(I2C_Type_t, 1);
    I2C_SCL(I2C_Type_t, 1);
    I2C_Type_t->delay_us(4);
    I2C_SDA(I2C_Type_t, 0); //START:when CLK is high,DATA change form high to low
    I2C_Type_t->delay_us(4);
    I2C_SCL(I2C_Type_t, 0); //钳住I2C总线,准备发送或接收数据
}

//I2C Stop
static void I2C_Stop_t(const struct I2C_Type* I2C_Type_t)
{
    SDA_OUT(I2C_Type_t);
    I2C_SCL(I2C_Type_t, 0);
    I2C_SDA(I2C_Type_t, 0); //STOP:when CLK is high DATA change form low to high
    I2C_Type_t->delay_us(4);
    I2C_SCL(I2C_Type_t, 1);
    I2C_SDA(I2C_Type_t, 1); //发送I2C总线结束信号
    I2C_Type_t->delay_us(4);
}

//I2C_Wait_ack 返回HAL_OK表示wait成功,返回HAL_ERROR表示wait失败
static unsigned char I2C_Wait_Ack_t(const struct I2C_Type* I2C_Type_t) //IIC_Wait_ack,返回wait失败或是成功
{
    unsigned char ucErrTime = 0;

    SDA_IN(I2C_Type_t);
    I2C_SDA(I2C_Type_t, 1);
    I2C_Type_t->delay_us(1);
    I2C_SCL(I2C_Type_t, 1);
    I2C_Type_t->delay_us(1);

    while(READ_SDA(I2C_Type_t))
    {
        ucErrTime++;

        if(ucErrTime > 250)
        {
            I2C_Type_t->I2C_Stop(I2C_Type_t);
            return HAL_ERROR;
        }
    }

    I2C_SCL(I2C_Type_t, 0);
    return HAL_OK;
}

//产生ACK应答
static void I2C_Ack_t(const struct I2C_Type* I2C_Type_t)
{
    I2C_SCL(I2C_Type_t, 0);
    SDA_OUT(I2C_Type_t);
    I2C_SDA(I2C_Type_t, 0);
    I2C_Type_t->delay_us(2);
    I2C_SCL(I2C_Type_t, 1);
    I2C_Type_t->delay_us(2);
    I2C_SCL(I2C_Type_t, 0);
}

//产生NACK应答
static void I2C_NAck_t(const struct I2C_Type* I2C_Type_t)
{
    I2C_SCL(I2C_Type_t, 0);
    SDA_OUT(I2C_Type_t);
    I2C_SDA(I2C_Type_t, 1);
    I2C_Type_t->delay_us(2);
    I2C_SCL(I2C_Type_t, 1);
    I2C_Type_t->delay_us(2);
    I2C_SCL(I2C_Type_t, 0);
}

//I2C_Send_Byte,入口参数为要发送的字节
static void I2C_Send_Byte_t(const struct I2C_Type* I2C_Type_t, unsigned char txd)
{
    unsigned char cnt = 0;

    SDA_OUT(I2C_Type_t);
    I2C_SCL(I2C_Type_t, 0);

    for(cnt = 0; cnt < 8; cnt++)
    {
        I2C_SDA(I2C_Type_t, (txd & 0x80) >> 7);
        txd <<= 1;
        //I2C_Type_t->delay_us(2); //OLED显示刷新慢,取消延时
        I2C_SCL(I2C_Type_t, 1);
        //I2C_Type_t->delay_us(2);
        I2C_SCL(I2C_Type_t, 0);
        //I2C_Type_t->delay_us(2);
    }
}

//I2C_Read_Byte,入口参数为是否要发送ACK信号
static unsigned char I2C_Read_Byte_t(const struct I2C_Type* I2C_Type_t, unsigned char ack)
{
    unsigned char cnt, rec = 0;

    SDA_IN(I2C_Type_t);

    for(cnt = 0; cnt < 8; cnt++)
    {
        I2C_SCL(I2C_Type_t, 0);
        //I2C_Type_t->delay_us(2);
        I2C_SCL(I2C_Type_t, 1);
        rec <<= 1;

        if(READ_SDA(I2C_Type_t))
        {
            rec++;
        }

        //I2C_Type_t->delay_us(1);
    }

    if(!ack)
    {
        I2C_Type_t->I2C_NAck(I2C_Type_t);
    }
    else
    {
        I2C_Type_t->I2C_Ack(I2C_Type_t);
    }

    return rec;
}

//实例化一个I2C外设,相当于一个结构体变量,可以直接在其他文件中使用
I2C_TypeDef I2C_t =
{
    .GPIOx_SCL = GPIOF, //GPIO组为GPIOF
    .GPIOx_SDA = GPIOF, //GPIO组为GPIOF
    .GPIO_SCL = GPIO_PINS_5, //GPIO为PIN5
    .GPIO_SDA = GPIO_PINS_4, //GPIO为PIN4
    .I2C_Init = I2C_Init_t,
    .I2C_Start = I2C_Start_t,
    .I2C_Stop = I2C_Stop_t,
    .I2C_Wait_Ack = I2C_Wait_Ack_t,
    .I2C_Ack = I2C_Ack_t,
    .I2C_NAck = I2C_NAck_t,
    .I2C_Send_Byte = I2C_Send_Byte_t,
    .I2C_Read_Byte = I2C_Read_Byte_t,
    .delay_us = Delay_Us, //需自己外部实现delay_us函数
};
三、0.96OLED驱动封装
hal_0.96oled.h头文件类定义如下:
#ifndef __HAL_096OLED_H__
#define __HAL_096OLED_H__

#include "hal_i2c.h"

//定义0.96OLED类
typedef struct OLED_Type
{
    //属性
    //操作
    I2C_TypeDef *I2C; //
    void (*OLED_Write_CMD)(const struct OLED_Type*, unsigned char); //向设备写命令
    void (*OLED_Write_Date)(const struct OLED_Type*, unsigned char); //向设备写数据
    void (*OLED_Set_Pos)(const struct OLED_Type*, unsigned char, unsigned char); //坐标设置
    void (*OLED_Display_On)(const struct OLED_Type*); //开启OLED显示
    void (*OLED_Display_Off)(const struct OLED_Type*); //关闭OLED显示
    void (*OLED_Clear)(const struct OLED_Type*); //OLED清屏
    void (*OLED_Clear_Row)(const struct OLED_Type*, unsigned char); //OLED清行
    void (*OLED_Fill)(const struct OLED_Type*); //OLED填满屏幕
    void (*OLED_ShowChar)(const struct OLED_Type*, unsigned char, unsigned char, unsigned char, unsigned char); //指定位置显示一个字符
    void (*OLED_ShowNum)(const struct OLED_Type*, unsigned char, unsigned char, unsigned int, unsigned char, unsigned char); //指定位置显示数字
    void (*OLED_ShowString)(const struct OLED_Type*, unsigned char, unsigned char, unsigned char*, unsigned char); //指定位置显示字符串
    void (*OLED_ShowCHinese)(const struct OLED_Type*, unsigned char, unsigned char, unsigned char); //显示汉字
    void (*OLED_DrawBMP)(const struct OLED_Type* OLED_Type_t, unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char BMP[]); //显示图片
    void (*OLED_DrawGIF)(const struct OLED_Type* OLED_Type_t, unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char k, int m, const unsigned char GIF[][m]); //显示动画
    void (*OLED_Init)(const struct OLED_Type*); //初始化OLED

} OLED_TypeDef;

extern OLED_TypeDef _0_96OLED;

#endif
hal_0.96oled.c文件具体操作函数如下:
#include "hal_0.96oled.h"
#include "oledfont.h"
#include "delay.h"

//向设备写控制命令
static void OLED_Write_CMD_t(const struct OLED_Type* OLED_Type_t, unsigned char cmd)
{
    OLED_Type_t->I2C->I2C_Start(OLED_Type_t->I2C);
    OLED_Type_t->I2C->I2C_Send_Byte(OLED_Type_t->I2C, 0x78);
    OLED_Type_t->I2C->I2C_Wait_Ack(OLED_Type_t->I2C);
    OLED_Type_t->I2C->I2C_Send_Byte(OLED_Type_t->I2C, 0x00);
    OLED_Type_t->I2C->I2C_Wait_Ack(OLED_Type_t->I2C);
    OLED_Type_t->I2C->I2C_Send_Byte(OLED_Type_t->I2C, cmd);
    OLED_Type_t->I2C->I2C_Wait_Ack(OLED_Type_t->I2C);
    OLED_Type_t->I2C->I2C_Stop(OLED_Type_t->I2C);
}

//向设备写数据
static void OLED_Write_Date_t(const struct OLED_Type* OLED_Type_t, unsigned char date)
{
    OLED_Type_t->I2C->I2C_Start(OLED_Type_t->I2C);
    OLED_Type_t->I2C->I2C_Send_Byte(OLED_Type_t->I2C, 0x78);
    OLED_Type_t->I2C->I2C_Wait_Ack(OLED_Type_t->I2C);
    OLED_Type_t->I2C->I2C_Send_Byte(OLED_Type_t->I2C, 0x40);
    OLED_Type_t->I2C->I2C_Wait_Ack(OLED_Type_t->I2C);
    OLED_Type_t->I2C->I2C_Send_Byte(OLED_Type_t->I2C, date);
    OLED_Type_t->I2C->I2C_Wait_Ack(OLED_Type_t->I2C);
    OLED_Type_t->I2C->I2C_Stop(OLED_Type_t->I2C);
}

//坐标设置
static void OLED_Set_Pos_t(const struct OLED_Type* OLED_Type_t, unsigned char x, unsigned char y)
{
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xB0 + y);
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, ((x & 0xF0) >> 4) | 0x10);
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, x & 0x0F);
}

//开启OLED显示
static void OLED_Display_On_t(const struct OLED_Type* OLED_Type_t)
{
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x8D); //SET DCDC命令
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x14); //DCDC ON
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xAF); //DISPLAY ON
}

//关闭OLED显示
static void OLED_Display_Off_t(const struct OLED_Type* OLED_Type_t)
{
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x8D); //SET DCDC命令
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x10); //DCDC OFF
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xAE); //DISPLAY OFF
}

//OLED清屏
static void OLED_Clear_t(const struct OLED_Type* OLED_Type_t)
{
    unsigned char cnt, count;

    for(cnt = 0; cnt < 8; cnt++)
    {
        OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xB0 + cnt);
        OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x00);
        OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x10);

        for(count = 0; count < 128; count++)
        {
            OLED_Type_t->OLED_Write_Date(OLED_Type_t, 0x00);
        }
    }
}

//OLED清行
static void OLED_Clear_Row_t(const struct OLED_Type* OLED_Type_t, unsigned char n)
{
    unsigned char count;

    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xB0 + n);
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x00);
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x10);

    for(count = 0; count < 128; count++)
    {
        OLED_Type_t->OLED_Write_Date(OLED_Type_t, 0x00);
    }
}

//OLED填满屏幕
static void OLED_Fill_t(const struct OLED_Type* OLED_Type_t)
{
    unsigned char cnt, count;

    for(cnt = 0; cnt < 8; cnt++)
    {
        OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xB0 + cnt); //设置页地址(0~7)
        OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x00); //设置显示位置—列低地址
        OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x10); //设置显示位置—列高地址

        for(count = 0; count < 128; count++)
        {
            OLED_Type_t->OLED_Write_Date(OLED_Type_t, 0x01);
        }
    }
}

//指定位置显示一个字符
//x:0~127
//y:0~63
//chr:字符
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
static void OLED_ShowChar_t(const struct OLED_Type* OLED_Type_t, unsigned char x, unsigned char y, unsigned char chr, unsigned char size)
{
    unsigned char offset = 0, cnt = 0;

    offset = chr - ' '; //计算偏移量

    if(x > 128 - 1)
    {
        x = 0;
        y = y + 2;
    }

    if(size == 16)
    {
        OLED_Type_t->OLED_Set_Pos(OLED_Type_t, x, y);

        for(cnt = 0; cnt < 8; cnt++)
        {
            OLED_Type_t->OLED_Write_Date(OLED_Type_t, F8x16[offset * 16 + cnt]);
        }

        OLED_Type_t->OLED_Set_Pos(OLED_Type_t, x, y + 1);

        for(cnt = 0; cnt < 8; cnt++)
        {
            OLED_Type_t->OLED_Write_Date(OLED_Type_t, F8x16[offset * 16 + cnt + 8]);
        }
    }
    else
    {
        OLED_Type_t->OLED_Set_Pos(OLED_Type_t, x, y);

        for(cnt = 0; cnt < 6; cnt++)
        {
            OLED_Type_t->OLED_Write_Date(OLED_Type_t, F6x8[offset][cnt]);
        }
    }
}

unsigned int oled_pow(unsigned char m, unsigned char n)
{
    unsigned int result = 1;

    while(n--)
    {
        result *= m;
    }

    return result;
}

//指定位置显示一个数字
//x,y:起点坐标
//num:数值(0~4294967295)
//len:数字的位数
//size:字体大小
//mode:模式        0,填充模式;1,叠加模式
static void OLED_ShowNum_t(const struct OLED_Type* OLED_Type_t, unsigned char x, unsigned char y, unsigned int num, unsigned char len, unsigned char size)
{
    unsigned char cnt, temp;
    unsigned char show = 0;

    for(cnt = 0; cnt < len; cnt++)
    {
        temp = (num / oled_pow(10, len - cnt - 1)) % 10;

        if(show == 0 && cnt < (len - 1))
        {
            if(temp == 0)
            {
                OLED_Type_t->OLED_ShowChar(OLED_Type_t, x + (size / 2) * cnt, y, ' ', size);
                continue;
            }
            else
            {
                show = 1;
            }
        }

        OLED_Type_t->OLED_ShowChar(OLED_Type_t, x + (size / 2) * cnt, y, temp + '0', size);
    }
}

//指定位置显示字符串
static void OLED_ShowString_t(const struct OLED_Type* OLED_Type_t, unsigned char x, unsigned char y, unsigned char *chr, unsigned char size)
{
    unsigned char cnt = 0;

    while(chr[cnt] != '\0')
    {
        OLED_Type_t->OLED_ShowChar(OLED_Type_t, x, y, chr[cnt], size);
        x += 8;

        if(x > 120)
        {
            x = 0;
            y += 2;
        }

        cnt++;
    }
}

//显示汉字
static void OLED_ShowCHinese_t(const struct OLED_Type* OLED_Type_t, unsigned char x, unsigned char y, unsigned char no)
{
    unsigned char cnt, addr = 0;

    OLED_Type_t->OLED_Set_Pos(OLED_Type_t, x, y);

    for(cnt = 0; cnt < 16; cnt++)
    {
        OLED_Type_t->OLED_Write_Date(OLED_Type_t, Hzk[2 * no][cnt]);
        addr++;
    }

    OLED_Type_t->OLED_Set_Pos(OLED_Type_t, x, y + 1);

    for(cnt = 0; cnt < 16; cnt++)
    {
        OLED_Type_t->OLED_Write_Date(OLED_Type_t, Hzk[2 * no + 1][cnt]);
        addr++;
    }
}

//显示图片
/*
        @brief                        显示图片
        @param                        x0:起始列地址
                                        y0:起始页地址
                                        x1:终止列地址
                                        y1:终止页地址
                                        BMP[]:存放图片代码的数组
        @retval                        无
*/
static void OLED_DrawBMP_t(const struct OLED_Type* OLED_Type_t, unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char BMP[])
{
    unsigned int j = 0; //定义变量
    unsigned char x, y; //定义变量

    if(y1 % 8 == 0)
    {
        y = y1 / 8; //判断终止页是否为8的整数倍
    }
    else
    {
        y = y1 / 8 + 1;
    }

    for(y = y0; y < y1; y++) //从起始页开始,画到终止页
    {
        OLED_Type_t->OLED_Set_Pos(OLED_Type_t, x0, y); //在页的起始列开始画

        for(x = x0; x < x1; x++) //画x1 - x0 列
        {
            OLED_Type_t->OLED_Write_Date(OLED_Type_t, BMP[j++]); //画图片的点
        }
    }
}

//显示动图
/*
        @brief                        显示动图
        @param                        x0:起始列地址
                                y0:起始页地址
                                x1:终止列地址
                                y1:终止页地址
                                k: 帧个数
                                m: 单帧数组大小
                                BMP[][m]:存放动图代码的数组
        @retval                        无
*/
static void OLED_DrawGIF_t(const struct OLED_Type* OLED_Type_t, unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char k, int m, const unsigned char GIF[][m])
{
    unsigned int j = 0; //定义变量
    unsigned char x, y, i; //定义变量

    if(y1 % 8 == 0)
    {
        y = y1 / 8; //判断终止页是否为8的整数倍
    }
    else
    {
        y = y1 / 8 + 1;
    }

    for (i = 0; i < k; i++) //从第一帧开始画
    {
        j = 0;

        for(y = y0; y < y1; y++) //从起始页开始,画到终止页
        {
            OLED_Type_t->OLED_Set_Pos(OLED_Type_t, x0, y); //在页的起始列开始画

            for(x = x0; x < x1; x++) //画x1 - x0 列
            {
                OLED_Type_t->OLED_Write_Date(OLED_Type_t, GIF[i][j++]); //画图片的点
            }
        }

        //delay_ms(80);
    }
}

//OLED初始化
static void OLED_Init_t(const struct OLED_Type* OLED_Type_t)
{
    OLED_Type_t->I2C->I2C_Init(OLED_Type_t->I2C);
    Delay_Ms(200);
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xAE); //display off
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x00); //set low column address
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x10); //set high column address
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x40); //set start line address
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xB0); //set page address
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x81); //contract control
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xFF); //128
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xA1); //set segment remap
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xA6); //normal / reverse
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xA8); //set multiplex ratio(1 to 64)
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x3F); //1/32 duty
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xC8); //Com scan direction
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xD3); //set display offset
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x00); //
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xD5); //set osc division
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x80); //
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xD8); //set area color mode off
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x05); //
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xD9); //Set Pre-Charge Period
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xF1); //
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xDA); //set com pin configuartion
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x12); //
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xDB); //set Vcomh
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x30); //
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x8D); //set charge pump enable
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0x14); //
    OLED_Type_t->OLED_Write_CMD(OLED_Type_t, 0xAF); //turn on oled panel
}

//实例化
OLED_TypeDef _0_96OLED =
{
    .I2C = &I2C_t,
    .OLED_Write_CMD = OLED_Write_CMD_t,
    .OLED_Write_Date = OLED_Write_Date_t,
    .OLED_Set_Pos = OLED_Set_Pos_t,
    .OLED_Display_On = OLED_Display_On_t,
    .OLED_Display_Off = OLED_Display_Off_t,
    .OLED_Clear = OLED_Clear_t,
    .OLED_Clear_Row = OLED_Clear_Row_t,
    .OLED_Fill = OLED_Fill_t,
    .OLED_ShowChar = OLED_ShowChar_t,
    .OLED_ShowNum = OLED_ShowNum_t,
    .OLED_ShowString = OLED_ShowString_t,
    .OLED_ShowCHinese = OLED_ShowCHinese_t,
    .OLED_DrawBMP = OLED_DrawBMP_t,
    .OLED_DrawGIF = OLED_DrawGIF_t,
    .OLED_Init = OLED_Init_t,

};
以上可看出OLED类中包含I2C类的成员对象,由于OLED的实现需要调用I2C的成员方法,I2C相当于OLED的下一层的驱动,因此将I2C类对象作为OLED类的成员;我们在使用OLED的时候需要实例化OLED类对象,由于I2C和OLED是分别在不同的.c文件中,在实例化OLED外设时,结构体中包含的是I2C设备的指针,而不是实体,同时在I2C.c文件中应完成实例化一个I2C对象;从而对外提供了OLED的封装接口。
四、main调用
在main中直接使用_0_96OLED来实现所有操作,如下:
int main(void)
{
    /* initial system clock */
    system_clock_config();

    /* config nvic priority group */
    nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);

    Delay_Init();
    hal_ledInit();
    hal_timerInit();

    //I2C_t.I2C_Init(&I2C_t);
    _0_96OLED.OLED_Init(&_0_96OLED);
    _0_96OLED.OLED_Clear(&_0_96OLED);

    _0_96OLED.OLED_ShowString(&_0_96OLED, 16, 0, "www.21ic.com", 16);
    _0_96OLED.OLED_ShowString(&_0_96OLED, 14, 2, "AT32F425 TEST", 16);
    _0_96OLED.OLED_ShowString(&_0_96OLED, 28, 4, "2022/5/31", 16);
    _0_96OLED.OLED_ShowString(&_0_96OLED, 14, 6, "ID:zhouminjie", 16);

    Delay_Ms(1000);

    while(1)
    {
        _0_96OLED.OLED_DrawGIF(&_0_96OLED, 0, 0, 128, 8, 29, 1024, GIF);
    }
}
五、总结
面向对象方法实现I2C驱动封装及OLED驱动封装,对外仅提供一个OLED操作对象接口,提高了代码的复用性及封装性,方便了代码后期的维护修改。
参考:https://blog.csdn.net/weixin_42700740/article/details/113624909
六、效果及测试代码


AT32F425_SW_I2C_0.96OLED_oo.zip (382.25 KB)


使用特权

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

本版积分规则

32

主题

136

帖子

3

粉丝