OLED屏幕
同样的oled屏幕也连接在I2C1总线上。
屏幕驱动芯片用来操控屏幕上的8000个小灯的亮灭
常见的CH1116芯片和SSD1306芯片都可以驱动128X64的OLED屏幕
其工作原理:
将整块屏幕划分为了8个page,或者我们称其为页
比如芯片通过I2C通信,将第0页第3列设置为0xC1 (1100 0001)
则会按低位到高位依次点亮为1的像素,使其亮起
CH1116的I2C地址为0x7A即OLED_ADDRESS
1.指令
0x7A 0x00 0xXX (0xXX为要发送的指令)
如何设置页地址和列地址
将页地址设置为0 ,指令最后一位为0xB0
将页地址设置为7 ,指令最后一位为0xB7
设置列地址的指令则分为两次(列地址0-128)
将列地址设置为90 ,即0x5A,则要发送指令0x0A将列地址低4位设置为A,然后发送指令0x15将列地址高4位设置为5
2.数据
0x7A 0x40 0xXX 0xXX (0xXX为任意数量的数据)即控制小灯的亮灭数据。
比如0x7A 0x40 0x01 0x02 0x03
发送
第一个数据为0x01 控制指令所设置的页地址和列地址上的小灯亮灭设置为00000001,即最低位亮起,并将列地址自动加1,接着读取第二个数据。
第二个数据为0x02 小灯亮灭设置为00000010 第2位上亮起并将列地址自动加1接着读取第3个数据
第三个数据为0x03 以此类推。
用指令设置第0页第0列,发送128个显示数据,就可以控制第一页
整页的像素设置。
接着将也地址递增,如此循环8次,就可以完成整个屏幕的像素设置。
控制OLED的步骤:
1.
因为要发送控制屏幕亮灭的数据,需要设置I2C的速度模式改为Fast Mode。
I2C的速度从100k hz 提升到了 400K hz
同时可以使用开发板上额外准备的外部晶振给STM32提速。
确定后稍等片刻,cubeMX就自动将时钟源切换到了外部晶振。实现了72MHz的时钟频率(时钟树相关知识。)
为每个外设单独生成.c/.h 文件
接着,像AHT20一样为oled单独创建.c & .h文件并在oled.c中include “i2c.h”
1.点亮屏幕
oled.c中
#include"oled.h"
#define OLED_ADRESS 0x7A //通信地址:0x7A
void OLED_SendCmd(uint8_t data){
uint8_t sendBuffer[2] = {0x00,data};
HAL_I2C_Master_Transmit(&hi2c1, OLED_ADRESS, sendBuffer, sizeof(sendBuffer), HAL_MAX_DELAY);
}
void OledInit(){
OLED_SendCmd(0xAE); /*关闭显示 display off*/
OLED_SendCmd(0x02); /*设置列起始地址 set lower column address*/
OLED_SendCmd(0x10); /*设置列结束地址 set higher column address*/
OLED_SendCmd(0x40); /*设置起始行 set display start line*/
OLED_SendCmd(0xB0); /*设置页地址 set page address*/
OLED_SendCmd(0x81); /*设置对比度 contract control*/
OLED_SendCmd(0xCF); /*128*/
OLED_SendCmd(0xA1); /*设置分段重映射 从右到左 set segment remap*/
OLED_SendCmd(0xA6); /*正向显示 normal / reverse*/
OLED_SendCmd(0xA8); /*多路复用率 multiplex ratio*/
OLED_SendCmd(0x3F); /*duty = 1/64*/
OLED_SendCmd(0xAD); /*设置启动电荷泵 set charge pump enable*/
OLED_SendCmd(0x8B); /*启动DC-DC */
OLED_SendCmd(0x33); /*设置泵电压 set VPP 10V */
OLED_SendCmd(0xC8); /*设置输出扫描方向 COM[N-1]到COM[0] Com scan direction*/
OLED_SendCmd(0xD3); /*设置显示偏移 set display offset*/
OLED_SendCmd(0x00); /* 0x00 */
OLED_SendCmd(0xD5); /*设置内部时钟频率 set osc frequency*/
OLED_SendCmd(0xC0);
OLED_SendCmd(0xD9); /*设置放电/预充电时间 set pre-charge period*/
OLED_SendCmd(0x1F); /*0x22*/
OLED_SendCmd(0xDA); /*设置引脚布局 set COM pins*/
OLED_SendCmd(0x12);
OLED_SendCmd(0xDB); /*设置电平 set vcomh*/
OLED_SendCmd(0x40);
// OLED_NewFrame();
// OLED_ShowFrame();
OLED_SendCmd(0xAF); /*开启显示 display ON*/
}
void Oled_Test(){
OLED_SendCmd(0xB0);
OLED_SendCmd(0x00);
OLED_SendCmd(0x10);
uint8_t sendBuffer[] = {0x40,0xAA};
HAL_I2C_Master_Transmit(&hi2c1, OLED_ADRESS, sendBuffer, sizeof(sendBuffer), HAL_MAX_DELAY);
}
在oled.h中注册
#ifndef INC_OLED_H_
#define INC_OLED_H_
#include "i2c.h";
void OLED_SendCmd(uint8_t data);
void OledInit();
void Oled_Test();
#endif /* INC_OLED_H_ */
在main.c中使用
/* USER CODE BEGIN 2 */
OledInit();
Oled_Test();
/* USER CODE END 2 */
启动后,可以看到第一页中第一列并不是我们设想的那样,其原因是屏幕驱动芯片的第一列地址是从0x02开始的
因此需要将Oled_Test()函数改成
void Oled_Test(){
OLED_SendCmd(0xB0);
OLED_SendCmd(0x02); //低地址2
OLED_SendCmd(0x10); //高地址0
uint8_t sendBuffer[] = {0x40,0x00};
HAL_I2C_Master_Transmit(&hi2c1, OLED_ADRESS, sendBuffer, sizeof(sendBuffer), HAL_MAX_DELAY);
}
此时运行程序可以看到屏幕花屏且第一排像素全部熄灭
这是因为未对其他像素进行初始化的操作,于是就随机控制像素的亮灭,就造成了花屏的现象。
我们在oled.c中编写以下代码:
uint8_t GRAM[8][128];
void OLED_NewFrame(){
memset(GRAM,0,sizeof(GRAM));
//清空屏幕
}
void OLED_ShowFrame(){
uint8_t sendBuffer[129] ;
sendBuffer[0] = 0x40;
for (uint8_t i = 0; i < 8; ++ i) {
for (uint8_t j = 0; j < 128; ++j) {
sendBuffer[j+1] = GRAM[i][j];
}
OLED_SendCmd(0xB0+i);
OLED_SendCmd(0x02);
OLED_SendCmd(0x10);
HAL_I2C_Master_Transmit(&hi2c1, OLED_ADRESS, sendBuffer, sizeof(sendBuffer), HAL_MAX_DELAY);
}
}
接着在oled.h中声明
在main.c中使用:
/* USER CODE BEGIN 2 */
OledInit();
OLED_NewFrame();
OLED_ShowFrame();
Oled_Test();
/* USER CODE END 2 */
运行后可以看到就是我们想要的前几排亮起
其实控制屏幕就是控制GRAM [ 8 ] [ 128 ] 这个数组,并将其数据发送给屏幕驱动芯片就能控制屏幕了。
在oled.c中
void OLED_SetPixel(uint8_t x, uint8_t y){
if(x >= 128 || y >= 64) return ;
GRAM[y/8][x] |= 0x01 << (y % 8);
}
接着在oled.h中声明
在main.c中使用:
while (1)
{
/* USER CODE END WHILE */
for (int i = 0; i < 64; ++ i) {
OLED_NewFrame();
OLED_SetPixel(2*i, i);
OLED_ShowFrame();
}
/* USER CODE BEGIN 3 */
}
就可以看到变化了
以上就是oled显示的基本原理、但是,在真正使用的时候,并不需要这样自己造轮子,而是直接使用已经写好的代码,只需要会自己调用。
使用方法:
https://led.baud-dance.com/
去下载在对应oled的驱动
将对应的font.c和oled.c复制覆盖到Src文件夹中
将对应的font.h和oled.h复制覆盖到Inc文件夹中
接着直接在main.c中include一下
/* USER CODE BEGIN 2 */
OLED_Init();
OLED_NewFrame();
OLED_ShowFrame();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
for (int i = 0; i < 64; ++ i) {
OLED_NewFrame();
OLED_DrawCircle(64, 32, i,OLED_COLOR_NORMAL );
OLED_DrawCircle(64, 32, i*2,OLED_COLOR_NORMAL );
OLED_DrawCircle(64, 32, i*3,OLED_COLOR_NORMAL );
OLED_ShowFrame();
}
for (int i = 64; i > 0;-- i) {
OLED_NewFrame();
OLED_DrawCircle(64, 32, 64-i,OLED_COLOR_NORMAL );
OLED_DrawCircle(64, 32, 64-i*2,OLED_COLOR_NORMAL );
OLED_DrawCircle(64, 32, 64-i*3,OLED_COLOR_NORMAL );
OLED_ShowFrame();
}
/* USER CODE BEGIN 3 */
就可以直接使用oled.c中写好的函数了。
扩展,如果想绘制图片或复杂的字符则可以用网站上 提供的取模工具实现
如我想在显示屏上
显示这种图片组合则可以
复制到font.c中 接着在font.h中进行extern 就可以在main.c中使用
OLED_PrintString(70, 30,"基尼台妹", &font15x15, OLED_COLOR_NORMAL);
即可显示文字
图片则用
选择合适的参数后复制与文字操作类似复制到font.c中 接着在font.h中进行extern 就可以在main.c中使用
OLED_DrawImage(0, 0, &giegieImg, *OLED_COLOR_NORMAL*);
即可显示图片。
至此整个OLED驱动讲解完毕,试着在屏幕上打印温湿度信息吧。