HAL库与cubemx系列教程|采用面向对象的方法写一个OLED驱动

2022-11-16 15:38:26 浏览数 (1)

上一章节主要介绍了什么怎么样实现C语言面向对象编程,本章节来实战看看如何运用在嵌入式开发

面向对象

...有对象的面向她,没对象的,面向我,开课了...,说起面向对象编程,不得不说说面向过程编程

C语言中一般使用面向过程编程,就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步调用,在函数中对数据结构进行处理(执行算法),也就是说数据结构和算法是分开的。

C 语言把数据和算法封装在一起,形成一个整体,无论是对它的属性进行操作、还是对它的行为进行调用,都是通过一个对象来执行,这就是面向对象编程思想(摘自道哥:IOT物联网小镇)。

既然本次要实现面向对象编写OLED驱动,那就要具备上面所说的面向对象的特性,代码较多,大家可以在文末评论区找到源码链接,直接阅读源码...

IIC底层驱动封装

  • 测试平台:STM32G030C8T6
  • OLED:096寸OLED(IIC接口)

此部分参考:https://blog.csdn.net/weixin_42700740/article/details/113624909

IIC的协议就不多说了,网络上有很多讲的很形象的例子,接下来主要看代码实现就可以了

先定义一个IIC的“类”,主要包含用到的IO,IIC底层驱动函数等

代码语言:javascript复制
//定义IIC类
typedef struct IIC_Type
{
   //属性
   GPIO_TypeDef  *GPIOx_SCL;  //GPIO_SCL所属的GPIO组(如:GPIOA)
   GPIO_TypeDef  *GPIOx_SDA;  //GPIO_SDA所属的GPIO组(如:GPIOA)
   uint32_t GPIO_SCL;     //GPIO_SCL的IO引脚(如:GPIO_PIN_0)
   uint32_t GPIO_SDA;     //GPIO_SDA的IO引脚(如:GPIO_PIN_0)
   //操作
   void (*IIC_Init)(const struct IIC_Type*);        //IIC_Init
   void (*IIC_Start)(const struct IIC_Type*);       //IIC_Start
   void (*IIC_Stop)(const struct IIC_Type*);        //IIC_Stop
   uint8_t (*IIC_Wait_Ack)(const struct IIC_Type*);    //IIC_Wait_ack,返回wait失败或是成功
   void (*IIC_Ack)(const struct IIC_Type*);       //IIC_Ack,IIC发送ACK信号
   void (*IIC_NAck)(const struct IIC_Type*);       //IIC_NAck,IIC发送NACK信号
   void (*IIC_Send_Byte)(const struct IIC_Type*,uint8_t);       //IIC_Send_Byte,入口参数为要发送的字节
   uint8_t (*IIC_Read_Byte)(const struct IIC_Type*,uint8_t);     //IIC_Send_Byte,入口参数为是否要发送ACK信号
   void (*delay_us)(uint32_t);              //us延时
}IIC_TypeDef;

接下来封装

IO时钟、PIN脚:

代码语言:javascript复制
//设置SDA为输入模式
static void SDA_IN(const struct IIC_Type *IIC_Type_t)
{
 uint8_t io_num = 0; //定义io Num号
 switch (IIC_Type_t->GPIO_SDA)
 {
 case GPIO_PIN_0:
  io_num = 0;
  break;
 case GPIO_PIN_1:
  io_num = 1;
  break;
 case GPIO_PIN_2:
  io_num = 2;
  break;
 case GPIO_PIN_3:
  io_num = 3;
  break;
 case GPIO_PIN_4:
  io_num = 4;
  break;
 case GPIO_PIN_5:
  io_num = 5;
  break;
 case GPIO_PIN_6:
  io_num = 6;
  break;
 case GPIO_PIN_7:
  io_num = 7;
  break;
 case GPIO_PIN_8:
  io_num = 8;
  break;
 case GPIO_PIN_9:
  io_num = 9;
  break;
 case GPIO_PIN_10:
  io_num = 10;
  break;
 case GPIO_PIN_11:
  io_num = 11;
  break;
 case GPIO_PIN_12:
  io_num = 12;
  break;
 case GPIO_PIN_13:
  io_num = 13;
  break;
 case GPIO_PIN_14:
  io_num = 14;
  break;
 case GPIO_PIN_15:
  io_num = 15;
  break;
 }
 IIC_Type_t->GPIOx_SDA->MODER &= ~(3 << (io_num * 2)); //将GPIOx_SDA->GPIO_SDA清零
 IIC_Type_t->GPIOx_SDA->MODER |= 0 << (io_num * 2);   //将GPIOx_SDA->GPIO_SDA设置为输入模式
}

//设置SDA为输出模式
static void SDA_OUT(const struct IIC_Type *IIC_Type_t)
{
 uint8_t io_num = 0; //定义io Num号
 switch (IIC_Type_t->GPIO_SDA)
 {
 case GPIO_PIN_0:
  io_num = 0;
  break;
 case GPIO_PIN_1:
  io_num = 1;
  break;
 case GPIO_PIN_2:
  io_num = 2;
  break;
 case GPIO_PIN_3:
  io_num = 3;
  break;
 case GPIO_PIN_4:
  io_num = 4;
  break;
 case GPIO_PIN_5:
  io_num = 5;
  break;
 case GPIO_PIN_6:
  io_num = 6;
  break;
 case GPIO_PIN_7:
  io_num = 7;
  break;
 case GPIO_PIN_8:
  io_num = 8;
  break;
 case GPIO_PIN_9:
  io_num = 9;
  break;
 case GPIO_PIN_10:
  io_num = 10;
  break;
 case GPIO_PIN_11:
  io_num = 11;
  break;
 case GPIO_PIN_12:
  io_num = 12;
  break;
 case GPIO_PIN_13:
  io_num = 13;
  break;
 case GPIO_PIN_14:
  io_num = 14;
  break;
 case GPIO_PIN_15:
  io_num = 15;
  break;
 }
 IIC_Type_t->GPIOx_SDA->MODER &= ~(3 << (io_num * 2)); //将GPIOx_SDA->GPIO_SDA清零
 IIC_Type_t->GPIOx_SDA->MODER |= 1 << (io_num * 2);   //将GPIOx_SDA->GPIO_SDA设置为输出模式
}
//设置SCL电平
static void IIC_SCL(const struct IIC_Type *IIC_Type_t, int n)
{
 if (n == 1)
 {
  HAL_GPIO_WritePin(IIC_Type_t->GPIOx_SCL, IIC_Type_t->GPIO_SCL, GPIO_PIN_SET); //设置SCL为高电平
 }
 else
 {
  HAL_GPIO_WritePin(IIC_Type_t->GPIOx_SCL, IIC_Type_t->GPIO_SCL, GPIO_PIN_RESET); //设置SCL为低电平
 }
}
//设置SDA电平
static void IIC_SDA(const struct IIC_Type *IIC_Type_t, int n)
{
 if (n == 1)
 {
  HAL_GPIO_WritePin(IIC_Type_t->GPIOx_SDA, IIC_Type_t->GPIO_SDA, GPIO_PIN_SET); //设置SDA为高电平
 }
 else
 {
  HAL_GPIO_WritePin(IIC_Type_t->GPIOx_SDA, IIC_Type_t->GPIO_SDA, GPIO_PIN_RESET); //设置SDA为低电平
 }
}
//读取SDA电平
static uint8_t READ_SDA(const struct IIC_Type *IIC_Type_t)
{
 return HAL_GPIO_ReadPin(IIC_Type_t->GPIOx_SDA, IIC_Type_t->GPIO_SDA); //读取SDA电平
}
// IIC初始化
void IIC_Init_t(const struct IIC_Type *IIC_Type_t)
{
 GPIO_InitTypeDef GPIO_Initure;

 //根据GPIO组初始化GPIO时钟
 if (IIC_Type_t->GPIOx_SCL == GPIOA || IIC_Type_t->GPIOx_SDA == GPIOA)
 {
  __HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
 }
 if (IIC_Type_t->GPIOx_SCL == GPIOB || IIC_Type_t->GPIOx_SDA == GPIOB)
 {
  __HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB时钟
 }
 if (IIC_Type_t->GPIOx_SCL == GPIOC || IIC_Type_t->GPIOx_SDA == GPIOC)
 {
  __HAL_RCC_GPIOC_CLK_ENABLE(); //使能GPIOC时钟
 }
 if (IIC_Type_t->GPIOx_SCL == GPIOD || IIC_Type_t->GPIOx_SDA == GPIOD)
 {
  __HAL_RCC_GPIOD_CLK_ENABLE(); //使能GPIOD时钟
 }

 // GPIO_SCL初始化设置
 GPIO_Initure.Pin = IIC_Type_t->GPIO_SCL;
 GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP;  //推挽输出
 GPIO_Initure.Pull = GPIO_PULLUP;    //上拉
 GPIO_Initure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; //快速
 HAL_GPIO_Init(IIC_Type_t->GPIOx_SCL, &GPIO_Initure);
 // GPIO_SDA初始化设置
 GPIO_Initure.Pin = IIC_Type_t->GPIO_SDA;
 GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP;  //推挽输出
 GPIO_Initure.Pull = GPIO_PULLUP;    //上拉
 GPIO_Initure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; //快速
 HAL_GPIO_Init(IIC_Type_t->GPIOx_SDA, &GPIO_Initure);

 // SCL与SDA的初始化均为高电平
 IIC_SCL(IIC_Type_t, 1);
 IIC_SDA(IIC_Type_t, 1);
}

起始信号:

代码语言:javascript复制
// IIC Start
void IIC_Start_t(const struct IIC_Type *IIC_Type_t)
{
 SDA_OUT(IIC_Type_t); // sda线输出
 IIC_SDA(IIC_Type_t, 1);
 IIC_SCL(IIC_Type_t, 1);
 IIC_Type_t->delay_us(4);
 IIC_SDA(IIC_Type_t, 0); // START:when CLK is high,DATA change form high to low
 IIC_Type_t->delay_us(4);
 IIC_SCL(IIC_Type_t, 0); //钳住I2C总线,准备发送或接收数据
}

停止信号:

代码语言:javascript复制
// IIC Stop
void IIC_Stop_t(const struct IIC_Type *IIC_Type_t)
{
 SDA_OUT(IIC_Type_t); // sda线输出
 IIC_SCL(IIC_Type_t, 0);
 IIC_SDA(IIC_Type_t, 0); // STOP:when CLK is high DATA change form low to high
 IIC_Type_t->delay_us(4);
 IIC_SCL(IIC_Type_t, 1);
 IIC_SDA(IIC_Type_t, 1); //发送I2C总线结束信号
 IIC_Type_t->delay_us(4);
}

等待应答信号:

代码语言:javascript复制
// IIC_Wait_ack 返回HAL_OK表示wait成功,返回HAL_ERROR表示wait失败
uint8_t IIC_Wait_Ack_t(const struct IIC_Type *IIC_Type_t) // IIC_Wait_ack,返回wait失败或是成功
{
 uint8_t ucErrTime = 0;
 SDA_IN(IIC_Type_t); // SDA设置为输入
 IIC_SDA(IIC_Type_t, 1);
 IIC_Type_t->delay_us(1);
 IIC_SCL(IIC_Type_t, 1);
 IIC_Type_t->delay_us(1);
 while (READ_SDA(IIC_Type_t))
 {
  ucErrTime  ;
  if (ucErrTime > 250)
  {
   IIC_Type_t->IIC_Stop(IIC_Type_t);
   return HAL_ERROR;
  }
 }
 IIC_SCL(IIC_Type_t, 0); //时钟输出0
 return HAL_OK;
}

产生ACK信号:

代码语言:javascript复制
//产生ACK应答
void IIC_Ack_t(const struct IIC_Type *IIC_Type_t)
{
 IIC_SCL(IIC_Type_t, 0);
 SDA_OUT(IIC_Type_t);
 IIC_SDA(IIC_Type_t, 0);
 IIC_Type_t->delay_us(2);
 IIC_SCL(IIC_Type_t, 1);
 IIC_Type_t->delay_us(2);
 IIC_SCL(IIC_Type_t, 0);
}

产生NACK信号:

代码语言:javascript复制
//产生NACK应答
void IIC_NAck_t(const struct IIC_Type *IIC_Type_t)
{
 IIC_SCL(IIC_Type_t, 0);
 SDA_OUT(IIC_Type_t);
 IIC_SDA(IIC_Type_t, 1);
 IIC_Type_t->delay_us(2);
 IIC_SCL(IIC_Type_t, 1);
 IIC_Type_t->delay_us(2);
 IIC_SCL(IIC_Type_t, 0);
}

发送一字节:

代码语言:javascript复制
// IIC_Send_Byte,入口参数为要发送的字节
void IIC_Send_Byte_t(const struct IIC_Type *IIC_Type_t, uint8_t txd)
{
 uint8_t t = 0;
 SDA_OUT(IIC_Type_t);
 IIC_SCL(IIC_Type_t, 0); //拉低时钟开始数据传输
 for (t = 0; t < 8; t  )
 {
  IIC_SDA(IIC_Type_t, (txd & 0x80) >> 7);
  txd <<= 1;
  IIC_Type_t->delay_us(2); //对TEA5767这三个延时都是必须的
  IIC_SCL(IIC_Type_t, 1);
  IIC_Type_t->delay_us(2);
  IIC_SCL(IIC_Type_t, 0);
  IIC_Type_t->delay_us(2);
 }
}

读取一字节:

代码语言:javascript复制
uint8_t IIC_Read_Byte_t(const struct IIC_Type *IIC_Type_t, uint8_t ack)
{
 uint8_t i, receive = 0;
 SDA_IN(IIC_Type_t); // SDA设置为输入
 for (i = 0; i < 8; i  )
 {
  IIC_SCL(IIC_Type_t, 0);
  IIC_Type_t->delay_us(2);
  IIC_SCL(IIC_Type_t, 1);
  receive <<= 1;
  if (READ_SDA(IIC_Type_t))
   receive  ;
  IIC_Type_t->delay_us(1);
 }
 if (!ack)
  IIC_Type_t->IIC_NAck(IIC_Type_t); //发送nACK
 else
  IIC_Type_t->IIC_Ack(IIC_Type_t); //发送ACK
 return receive;
}

最后,来实例化一个“对象”,主要包含了供外部调用的一些成员,对于外部用户来说,他只需要关注这个结构体里面的成员,而不需要去关注内部如何实现的

代码语言:javascript复制
//实例化一个IIC1外设,相当于一个结构体变量,可以直接在其他文件中使用
IIC_TypeDef IIC1 = {
 .GPIOx_SCL = GPIOB,  // GPIO组为GPIOB
 .GPIOx_SDA = GPIOB,  // GPIO组为GPIOB
 .GPIO_SCL = GPIO_PIN_9, // GPIO为PIN9
 .GPIO_SDA = GPIO_PIN_8, // GPIO为PIN8
 .IIC_Init = IIC_Init_t,
 .IIC_Start = IIC_Start_t,
 .IIC_Stop = IIC_Stop_t,
 .IIC_Wait_Ack = IIC_Wait_Ack_t,
 .IIC_Ack = IIC_Ack_t,
 .IIC_NAck = IIC_NAck_t,
 .IIC_Send_Byte = IIC_Send_Byte_t,
 .IIC_Read_Byte = IIC_Read_Byte_t,
 .delay_us = delay_us //需自己外部实现delay_us函数
};

OLED封装

依然是先封装一个“OLED类”

代码语言:javascript复制
//定义RT_OLED类
typedef struct RT_OLED_TYPE
{
 //操作
 IIC_TypeDef IIC;       //IIC驱动

 void (*rt_oled_write_byte)(const struct RT_OLED_TYPE*,uint8_t,uint8_t,uint8_t);
 void (*rt_oled_init)(const struct RT_OLED_TYPE*);
 void (*rt_oled_display_on)(const struct RT_OLED_TYPE*,uint8_t);
 void (*rt_oled_clear)(const struct RT_OLED_TYPE*);
 void (*rt_oled_displayall)(const struct RT_OLED_TYPE*);
 void (*rt_oled_showchar)(const struct RT_OLED_TYPE*,uint8_t,uint8_t,uint8_t,uint8_t);
 void (*rt_oled_shownum)(const struct RT_OLED_TYPE*,uint8_t,uint8_t,uint32_t,uint8_t,uint8_t);
 void (*rt_oled_showstring)(const struct RT_OLED_TYPE*,uint8_t,uint8_t,uint8_t *,uint8_t);
 void (*rt_oled_showchinese)(const struct RT_OLED_TYPE*,uint8_t,uint8_t,uint8_t);
 void (*rt_oled_showbmp)(const struct RT_OLED_TYPE*,uint8_t,uint8_t,uint8_t,uint8_t,unsigned char *);
 void (*rt_oled_fillpicture)(const struct RT_OLED_TYPE*,unsigned char);
}RT_OLED_TypeDef;

extern RT_OLED_TypeDef rt_oled;              //外部声明实例化RT_OLED对象

写数据、命令封装:

代码语言:javascript复制
/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled写数据
 * @param
 * @return NULL
 */
static void rt_oled_write_data_t(const struct RT_OLED_TYPE *rt_oled_type_t, uint8_t chipid, uint8_t dat)
{
 rt_oled_type_t->IIC.IIC_Start(&rt_oled_type_t->IIC);
 rt_oled_type_t->IIC.IIC_Send_Byte(&rt_oled_type_t->IIC, chipid); // D/C#=0; R/W#=0
 rt_oled_type_t->IIC.IIC_Ack(&rt_oled_type_t->IIC);
 rt_oled_type_t->IIC.IIC_Send_Byte(&rt_oled_type_t->IIC, 0x40);
 rt_oled_type_t->IIC.IIC_Ack(&rt_oled_type_t->IIC);
 rt_oled_type_t->IIC.IIC_Send_Byte(&rt_oled_type_t->IIC, dat);
 rt_oled_type_t->IIC.IIC_Ack(&rt_oled_type_t->IIC);
 rt_oled_type_t->IIC.IIC_Stop(&rt_oled_type_t->IIC);
}

/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled写命令
 * @param
 * @return NULL
 */
static void rt_oled_write_cmd_t(const struct RT_OLED_TYPE *rt_oled_type_t, uint8_t chipid, uint8_t cmd)
{
 rt_oled_type_t->IIC.IIC_Start(&rt_oled_type_t->IIC);
 rt_oled_type_t->IIC.IIC_Send_Byte(&rt_oled_type_t->IIC, chipid); // D/C#=0; R/W#=0
 rt_oled_type_t->IIC.IIC_Ack(&rt_oled_type_t->IIC);
 rt_oled_type_t->IIC.IIC_Send_Byte(&rt_oled_type_t->IIC, 0x00);
 rt_oled_type_t->IIC.IIC_Ack(&rt_oled_type_t->IIC);
 rt_oled_type_t->IIC.IIC_Send_Byte(&rt_oled_type_t->IIC, cmd);
 rt_oled_type_t->IIC.IIC_Ack(&rt_oled_type_t->IIC);
 rt_oled_type_t->IIC.IIC_Stop(&rt_oled_type_t->IIC);
}

/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled写数据或者写命令
 * @param
 * @return NULL
 */
static void rt_oled_write_byte_t(const struct RT_OLED_TYPE *rt_oled_type_t, uint8_t chipid, uint8_t dat, uint8_t cmd)
{
 if (cmd) //写数据
 {
  rt_oled_write_data_t(rt_oled_type_t, chipid, dat);
 }
 else //写命令
 {
  rt_oled_write_cmd_t(rt_oled_type_t, chipid, dat);
 }
}

接下来是一些对OLED的显示操作,就不一一介绍了:

代码语言:javascript复制
/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled坐标设置
 * @param
 * @return NULL
 */

static void rt_oled_setpos_t(const struct RT_OLED_TYPE *rt_oled_type_t, uint8_t x, uint8_t y)
{

 rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0xb0   y, OLED_CMD);
 rt_oled_write_byte_t(rt_oled_type_t, 0x78, ((x & 0xf0) >> 4) | 0x10, OLED_CMD);
 rt_oled_write_byte_t(rt_oled_type_t, 0x78, (x & 0x0f), OLED_CMD);
}

/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled显示开关
 * @param
 * @return NULL
 */

static void rt_oled_display_on_t(const struct RT_OLED_TYPE *rt_oled_type_t, uint8_t ON)
{
 if (ON)
 {
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0X8D, OLED_CMD);
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0X14, OLED_CMD);
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0XAF, OLED_CMD);
 }
 else
 {
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0X8D, OLED_CMD);
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0X14, OLED_CMD);
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0XAE, OLED_CMD);
 }
}
/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled清屏
 * @param
 * @return NULL
 */
static void rt_oled_clear_t(const struct RT_OLED_TYPE *rt_oled_type_t)
{
 uint8_t i, n;
 for (i = 0; i < 8; i  )
 {
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0xb0   i, OLED_CMD);
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0x00, OLED_CMD);
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0x10, OLED_CMD);

  for (n = 0; n < 128; n  )
   rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0, OLED_DATA);
 } //更新显示
}

/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled全显示
 * @param
 * @return NULL
 */
static void rt_oled_displayall_t(const struct RT_OLED_TYPE *rt_oled_type_t)
{
 uint8_t i, n;
 for (i = 0; i < 8; i  )
 {
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0xb0   i, OLED_CMD);
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0x00, OLED_CMD);
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0x10, OLED_CMD);

  for (n = 0; n < 128; n  )
   rt_oled_write_byte_t(rt_oled_type_t, 0x78, 1, OLED_DATA);
 } //更新显示
}
/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled显示一个字符
 * @param
 * @return NULL
 */
static void rt_oled_showchar_t(const struct RT_OLED_TYPE *rt_oled_type_t, uint8_t x, uint8_t y, uint8_t chr, uint8_t Char_Size)
{
 unsigned char c = 0, i = 0;
 c = chr - ' '; //得到偏移后的值

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

 if (Char_Size == 16)
 {
  rt_oled_setpos_t(rt_oled_type_t, x, y);
  for (i = 0; i < 8; i  )
  {
   rt_oled_write_byte_t(rt_oled_type_t, 0x78, F8X16[c * 16   i], OLED_DATA);
  }
  rt_oled_setpos_t(rt_oled_type_t, x, y   1);

  for (i = 0; i < 8; i  )
  {
   rt_oled_write_byte_t(rt_oled_type_t, 0x78, F8X16[c * 16   i   8], OLED_DATA);
  }
 }
 else
 {
  rt_oled_setpos_t(rt_oled_type_t, x, y);
  for (i = 0; i < 6; i  )
  {
   rt_oled_write_byte_t(rt_oled_type_t, 0x78, F6x8[c][i], OLED_DATA);
  }
 }
}
/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled m^n
 * @param
 * @return NULL
 */
static uint32_t rt_oled_pow_t(uint8_t m, uint8_t n)
{
 uint32_t result = 1;
 while (n--)
  result *= m;
 return result;
}
/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled显示一个字符
 * @param
 * @return NULL

//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式 0,填充模式;1,叠加模式
//num:数值(0~4294967295);
*/
static void rt_oled_shownum_t(const struct RT_OLED_TYPE *rt_oled_type_t, uint8_t x, uint8_t y, uint32_t num, uint8_t size2, uint8_t len)
{
 uint8_t t, temp;
 uint8_t enshow = 0;
 for (t = 0; t < len; t  )
 {
  temp = (num / rt_oled_pow_t(10, len - t - 1)) % 10;
  if (enshow == 0 && t < (len - 1))
  {
   if (temp == 0)
   {
    rt_oled_showchar_t(rt_oled_type_t, x   (size2 / 2) * t, y, ' ', size2);
    continue;
   }
   else
    enshow = 1;
  }
  rt_oled_showchar_t(rt_oled_type_t, x   (size2 / 2) * t, y, temp   '0', size2);
 }
}
/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled显示一个字符号串
 * @param
 * @return NULL
 */
static void rt_oled_showstring_t(const struct RT_OLED_TYPE *rt_oled_type_t, uint8_t x, uint8_t y, uint8_t *chr, uint8_t charsize)
{
 unsigned char j = 0;
 while (chr[j] != '')
 {
  rt_oled_showchar_t(rt_oled_type_t, x, y, chr[j], charsize);
  x  = 8;
  if (x > 120)
  {
   x = 0;
   y  = 2;
  }
  j  ;
 }
}
/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled显示汉字
 * @param
 * @return NULL
 */
static void rt_oled_showchinese_t(const struct RT_OLED_TYPE *rt_oled_type_t, uint8_t x, uint8_t y, uint8_t no)
{
 uint8_t t, adder = 0;
 rt_oled_setpos_t(rt_oled_type_t, x, y);
 for (t = 0; t < 16; t  )
 {
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, Hzk[2 * no][t], OLED_DATA);
  adder  = 1;
 }
 rt_oled_setpos_t(rt_oled_type_t, x, y   1);
 for (t = 0; t < 16; t  )
 {
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, Hzk[2 * no   1][t], OLED_DATA);
  adder  = 1;
 }
}
/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: 显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7
 * @param
 * @return NULL
 */
static void rt_oled_showbmp_t(const struct RT_OLED_TYPE *rt_oled_type_t, uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, unsigned char BMP[])
{
 unsigned int j = 0;
 unsigned char x, y;

 if (y1 % 8 == 0)
  y = y1 / 8;
 else
  y = y1 / 8   1;
 for (y = y0; y < y1; y  )
 {
  rt_oled_setpos_t(rt_oled_type_t, x0, y);
  for (x = x0; x < x1; x  )
  {
   rt_oled_write_byte_t(rt_oled_type_t, 0x78, BMP[j  ], OLED_DATA);
  }
 }
}
/**
 * @author:小飞哥玩嵌入式-小飞哥
 * @TODO: oled写数据或者写命令
 * @param
 * @return NULL
 */
static void rt_oled_fillpicture_t(const struct RT_OLED_TYPE *rt_oled_type_t, unsigned char fill_Data)
{
 unsigned char m, n;
 for (m = 0; m < 8; m  )
 {
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0xb0   m, OLED_CMD);
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0x00, OLED_CMD);
  rt_oled_write_byte_t(rt_oled_type_t, 0x78, 0x10, OLED_CMD);

  for (n = 0; n < 128; n  )
  {
   rt_oled_write_byte_t(rt_oled_type_t, 0x78, fill_Data, OLED_DATA);
  }
 }
}

//初始化SSD1306
void rt_oled_init_t(const struct RT_OLED_TYPE *rt_oled_type_t)
{
 rt_oled_type_t->IIC.IIC_Init(&rt_oled_type_t->IIC); // IIC初始化

 HAL_Delay(800);
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xAE, OLED_CMD); //--display off
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0x00, OLED_CMD); //---set low column address
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0x10, OLED_CMD); //---set high column address
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0x40, OLED_CMD); //--set start line address
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xB0, OLED_CMD); //--set page address
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0x81, OLED_CMD); // contract control
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xFF, OLED_CMD); //--128
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xA1, OLED_CMD); // set segment remap
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xA6, OLED_CMD); //--normal / reverse
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xA8, OLED_CMD); //--set multiplex ratio(1 to 64)
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0x3F, OLED_CMD); //--1/32 duty
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xC8, OLED_CMD); // Com scan direction
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xD3, OLED_CMD); //-set display offset
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0x00, OLED_CMD); //

 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xD5, OLED_CMD); // set osc division
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0x80, OLED_CMD); //

 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xD8, OLED_CMD); // set area color mode off
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0x05, OLED_CMD); //

 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xD9, OLED_CMD); // Set Pre-Charge Period
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xF1, OLED_CMD); //

 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xDA, OLED_CMD); // set com pin configuartion
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0x12, OLED_CMD); //

 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xDB, OLED_CMD); // set Vcomh
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0x30, OLED_CMD); //

 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0x8D, OLED_CMD); // set charge pump enable
 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0x14, OLED_CMD); //

 rt_oled_write_byte_t(rt_oled_type_t, CHIPID, 0xAF, OLED_CMD); //--turn on oled panel
}

实例化OLED对象:

代码语言:javascript复制
//实例化RT_OLED对象
RT_OLED_TypeDef rt_oled = {
 //操作
 .IIC = {
  .GPIOx_SCL = GPIOB,
  .GPIOx_SDA = GPIOB,
  .GPIO_SCL = OLED_SCL_Pin,
  .GPIO_SDA = OLED_SDA_Pin,
  .IIC_Init = IIC_Init_t,
  .IIC_Start = IIC_Start_t,
  .IIC_Stop = IIC_Stop_t,
  .IIC_Wait_Ack = IIC_Wait_Ack_t,
  .IIC_Ack = IIC_Ack_t,
  .IIC_NAck = IIC_NAck_t,
  .IIC_Send_Byte = IIC_Send_Byte_t,
  .IIC_Read_Byte = IIC_Read_Byte_t,
  .delay_us = delay_us}, // IIC驱动
 .rt_oled_write_byte = rt_oled_write_byte_t,
 .rt_oled_init = rt_oled_init_t,
 .rt_oled_display_on = rt_oled_display_on_t,
 .rt_oled_displayall = rt_oled_displayall_t,
 .rt_oled_showchar = rt_oled_showchar_t,
 .rt_oled_shownum = rt_oled_shownum_t,
 .rt_oled_showstring = rt_oled_showstring_t,
 .rt_oled_showchinese = rt_oled_showchinese_t,
 .rt_oled_showbmp = rt_oled_showbmp_t,
 .rt_oled_fillpicture = rt_oled_fillpicture_t,
 .rt_oled_clear = rt_oled_clear_t
 };

如何调用?

对于使用者来讲,面向对象最终要实现的效果是他不需要关注底层驱动,只需要应用层即可,直接调用“类”中的“方法”即可,非常简单

看看实现效果:

0 人点赞