设计支持
搜索 搜索
Cubemx+硬件I2C驱动OLED屏幕

基本原理

硬件I2C相比于软件I2C,就是通过HAL库自动生成的I2C代码,使用专门的函数实现I2C协议代替手动去设置协议

 

首先打开Cubemx,进行时钟等基本配置,这里就不做过多赘述,然后我们直接打开I2C,不需要做其他配置(如果你想加快刷新率可以将Speed Modes改成Fast)

 

配置完成后我们生成代码,然后将OLED驱动库导入,导入的方法这里也不过多赘述

 

导入完成后我们只需要

#include”oled.h”

然后初始化oled

OLED_Init();

之后就可以使用函数驱动OLED了

 

 

接下来我们开始本章的重点,OLED驱动库的原理解析

打开oled.c的文件

 

我们可以找到传输函数的实例:

void OLED_WR_CMD(uint8_t cmd)

{

HAL_I2C_Mem_Write(&hi2c1 ,0x78,0x00,I2C_MEMADD_SIZE_8BIT,&cmd,1,0x100);

}

&hi2c1: 这是 I2C 外设的句柄,用于指定使用的 I2C 总线。

0x78: 这是 OLED 显示屏的 I2C 地址,指定要通信的设备。

0x00: 这是 OLED 显示屏的内存地址,指定要写入数据的位置。在这里,它是命令地址,用于发送命令给 OLED 显示屏。

I2C_MEMADD_SIZE_8BIT: 这是内存地址的大小,指定地址的精度为 8 位。

&cmd: 这是指向要发送的命令数据的指针。在这里,它是 uint8_t 类型的变量 cmd 的地址。

1: 这是要写入的数据的字节数,这里是一个字节的命令。

0x100: 这是超时参数,指定在发生超时之前等待的时间。

 

 

了解了传输函数,我们只需要简单了解初始化显示范围的设定,就可以理解OLED驱动库的原理,还是在oled的源文件中

void OLED_Set_Pos(uint8_t x, uint8_t y)

{

    OLED_WR_CMD(0xB0 + y);

    OLED_WR_CMD(0x10 + ((x & 0xF0) >> 4));

    OLED_WR_CMD(0x00 + (x & 0x0F));

}

这是设置显示位置的函数,其中0xB0,0x10,0x00定义了0.96寸屏幕的初始像素点,不同屏幕的初始像素点不同,比如通过测试0.42寸的屏幕的初始像素点为0xB3,0x2B,0x1C,然后通过过传递X和Y来和初始位置进行相加操作来设置显示的像素点。(网上找的代码是用了“或”运算符导致“0xB0 | 1”和“0xB1 | 1”是同一个值,坑死我了)

(如果想要自定义初始位置,可以自己去调整这三个数,其中0xB0每加1,则y初始像素点下移8像素,0x10(x的高位)每加1,则x的初始像素点右移8像素,0x01(x的低位每加1,则x的初始像素点右移1像素))

 

然后我们需要了解一下初始化显示点的机制,设置初始点需要设置其页地址和列地址,其中页地址就是Y,列地址就是X,而页地址是每八个像素点算一页,也就是说,0.96寸的OLED是64X128分辨率,那就有8页(0~7)(也就是说0xB0和0xB1这两个像素点之间隔了8个像素)

我们结合show_char的函数来分析一下其原理:

void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)

{      

uint8_t i;

OLED_Set_Pos((Column - 1) * 8, (Line - 1) * 2); //设置光标位置在上半部分

for (i = 0; i < 8; i++)

{

OLED_WR_DATA(F8X16[Char - ' '][i]); //显示上半部分内容

}

OLED_Set_Pos((Column - 1) * 8, (Line -1) * 2+1 ); //设置光标位置在下半部分

for (i = 0; i < 8; i++)

{

OLED_WR_DATA(F8X16[Char - ' '][i + 8]); //显示下半部分内容

}

}

首先假设我们line和column传进来是1和1,那么第一个初始点设置的代码为:

OLED_Set_Pos((Column - 1) * 8, (Line - 1) * 2);

也就是传进去set_pos函数的x和y都为0

void OLED_Set_Pos(uint8_t x, uint8_t y)

{

    OLED_WR_CMD(0xB0 + y);

    OLED_WR_CMD(0x10 + ((x & 0xF0) >> 4));

    OLED_WR_CMD(0x00 + (x & 0x0F));

}

然后在这个函数内,首先OLED_WR_CMD(0xB0 + y)对0xB0和y进行一个加运算,传进来多少,就往下多少页,比如现在传进来的y是0,那么就加0页,还是0xB0,就是初始位置。

然后在这个函数内,首先OLED_WR_CMD(0xB0 + y)对0xB0和y进行一个加运算,传进来多少,就往下多少页,比如现在传进来的y是0,那么就加0页,还是0xB0,就是初始位置。

然后对于x的值我们用了两次传输,这是因为x是0~128,而y只是0~7,所以x需要八位二进制数来表示,那我们就需要将其分开来传输,分为高四位和低四位, OLED_WR_CMD(0x10 + ((x & 0xF0) >> 4));

其中0xF0转换为二进制数就是11110000,和x进行与操作,那么低四位就被置零,高四位保持不变,然后因为数据传输是从右往左的,所以我们要将与完的数据右移四位也就是变成0000xxxx,其中“xxxx”是传进来的x的高四位二进制数,这样我们就能将数据发出去了。

对于第四位同理,不过因为第四位x是与0x0F也就是00001111进行与操作,那么与完之后就不需要右移可以之间发送。

 

以上我们就完成了对显示位置的自定义

 

接下来只需要让对应的像素点亮起来就行了

for (i = 0; i < 8; i++)

{

OLED_WR_DATA(F8X16[Char - ' '][i]); //显示上半部分内容

}

这段代码的作用就是让对应的像素点亮起来

首先我们要了解一下字模库

我们打开oledfont.h

const unsigned char F8X16[][16]=

{

 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//sp /0

 0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//!  /1

 0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//"  /2

 0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//#  /3

}

这是只截取了非常一小部分的8X16字模库,也就是0.96寸oled适配的字模

其中我们定义了一个字符二维数组F8X16[][16],其中第二维是我们限制为16,也就是字模的列数,例如第二行是“!”的字模,它一共由16个元素组成,因为我们的所以字模都是由16个元素组成,那么我们只需要更改第一维的数字,就可以选择显示不同的字符,比如循环显示从F8X16[3][0]到F8X16[3][16]可以显示字符“#”

但是由于我们的y每一页只有8像素,而F8X16,需要在8X16的分辨率显示,那么我吗就只能将字模分为上半部分和下半部分分别显示

for (i = 0; i < 8; i++)

{

OLED_WR_DATA(F8X16[Char - ' '][i]); //显示上半部分内容

}

这段代码就是只让其显示前八个元素,显示下半部分内容同理

 

以上我们就完成的OLED的显示

这样我们的一个OLED显示的库就完成了,我们只需要做到字符的显示,那么之后的数字,有符号数字,二进制数,十六进制数,字符串等,都可以通过一些运算和循环调用字符显示来完成。


作者:石珂鸣

标签: MCU
1
点赞
1
收藏
评论
0/1000
全部评论 (0)条
热点标签
更多
相关文章推荐

云FAE请选择您要咨询的方向,专业工程师为您服务!咨询客服