之前介绍了STM32软件IIC和硬件IIC的区别,本期利用STM32上的硬件IIC实现OLED显示屏的使用。
这种0.96寸OLED可能是接触嵌入式的第一个IIC器件,大部分网上实现的方法都是利用软件IIC,但是软件IIC在不同设备之间的移植可能发生时序错误,本期我们利用硬件IIC来实现OLED屏幕的显示。
CubeMX中启用硬件I2C2,连接好SDA和SCL。 由于这块OLED的内部驱动是SSD1306,因此我们需要寻找SSD1306的驱动参考手册。
驱动上的SA0被拉低了,因此SSD1306的地址是0x78.
-
0xAE
:关闭显示(睡眠模式) -
0x00
、0x10
:设置较低列地址和较高列地址 -
0x40
:设置显示起始行 -
0xB0
:设置页地址 -
0x81
、0xFF
:对比度控制 -
0xA1
:设置段重新映射(列地址127映射到SEG0) -
0xA6
:设置正常显示(像素亮度1为开启) -
0xA8
、0x3F
:设置多路复用比(占空比1/64) -
0xC8
:COM扫描方向:COM63~COM0 -
0xD3
、0x00
:设置显示偏移 -
0xD5
、0x80
:设置显示时钟分频比/振荡器频率 -
0xD8
、0x05
:设置分频比和COM/SEG方向 -
0xD9
、0xF1
:设置预充电周期 -
0xDA
、0x12
:设置COM引脚硬件配置 -
0xD8
、0x30
:在显示时启用充电泵 -
0x8D
、0x14
:设置VCOMH取消选择电平 -
0xAF
:正常模式下打开显示
这是一些关于启动的命令,向0x00地址寄存器发送这些命令,可以配置SSD1306的启动。
uint8_t CMD_Data[]={0xAE, 0x00, 0x10, 0x40, 0xB0, 0x81, 0xFF, 0xA1, 0xA6, 0xA8, 0x3F, 0xC8, 0xD3, 0x00, 0xD5, 0x80, 0xD8, 0x05, 0xD9, 0xF1, 0xDA, 0x12, 0xD8, 0x30, 0x8D, 0x14, 0xAF}; //初始化命令
void WriteCmd(void){ uint8_t i = 0; for(i=0; i<27; i++) { HAL_I2C_Mem_Write(&hi2c2 ,0x78,0x00,I2C_MEMADD_SIZE_8BIT,CMD_Data+i,1,0x100); }}
//写命令void OLED_WR_CMD(uint8_t cmd){ HAL_I2C_Mem_Write(&hi2c2 ,0x78,0x00,I2C_MEMADD_SIZE_8BIT,&cmd,1,0x100);}//向设备写数据void OLED_WR_DATA(uint8_t data){ HAL_I2C_Mem_Write(&hi2c2 ,0x78,0x40,I2C_MEMADD_SIZE_8BIT,&data,1,0x100);}
这个为基础,参考驱动手册编写清空的函数。
void OLED_Clear(void){ uint8_t i, n;
// 遍历每一页(典型的SSD1306显示器有8页) for (i = 0; i < 8; i++) { // 设置页地址 OLED_WR_CMD(0xb0 + i); // 设置较低列地址为0 OLED_WR_CMD(0x00); // 设置较高列地址为0x10 OLED_WR_CMD(0x10);
// 遍历每一列(典型的SSD1306显示器有128列) for (n = 0; n < 128; n++) { // 向显示数据写入0,即关闭像素 OLED_WR_DATA(0); } }}
设置光标位置
void OLED_Set_Pos(uint8_t x, uint8_t y) { OLED_WR_CMD(0xb0 + y); // 设置页地址 OLED_WR_CMD(((x & 0xf0) >> 4) | 0x10); // 设置较高列地址 OLED_WR_CMD(x & 0x0f); // 设置较低列地址}
-
OLED_ShowNum
函数:-
参数:
x
、y
表示起点坐标,num
表示要显示的数字,len
表示数字的位数,size2
表示字体大小。 -
作用:在OLED屏幕上显示一个数字。
-
通过循环取出数字的每一位,根据字体大小在指定位置显示。可以选择填充模式或叠加模式。
-
-
OLED_ShowChar
函数:-
参数:
x
、y
表示起点坐标,chr
表示要显示的字符,Char_Size
表示选择的字体大小。 -
作用:在OLED屏幕上显示一个字符。
-
根据选择的字体大小,选择相应的字体数组,然后在指定位置显示字符。
-
-
OLED_ShowString
函数:-
参数:
x
、y
表示起点坐标,*chr
表示要显示的字符串,Char_Size
表示选择的字体大小。 -
作用:在OLED屏幕上显示一个字符串。
-
循环遍历字符串中的每个字符,调用
OLED_ShowChar
函数逐个显示字符。在显示每个字符后,横坐标x
增加 8(字符宽度),当横坐标x
超过一定范围后,纵坐标y
增加 2(字符高度),横坐标x
归零。
-
//显示2个数字//x,y :起点坐标 //len :数字的位数//size:字体大小//mode:模式 0,填充模式;1,叠加模式//num:数值(0~4294967295); void OLED_ShowNum(uint8_t x,uint8_t y,unsigned int num,uint8_t len,uint8_t size2){ uint8_t t,temp; uint8_t enshow=0; for(t=0;t<len;t++) { temp=(num/oled_pow(10,len-t-1))%10; if(enshow==0&&t<(len-1)) { if(temp==0) { OLED_ShowChar(x+(size2/2)*t,y,' ',size2); continue; }else enshow=1; } OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2); }} //在指定位置显示一个字符,包括部分字符//x:0~127//y:0~63//mode:0,反白显示;1,正常显示 //size:选择字体 16/12 void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size){ unsigned char c=0,i=0; c=chr-' ';//得到偏移后的值 if(x>128-1){x=0;y=y+2;} if(Char_Size ==16) { OLED_Set_Pos(x,y); for(i=0;i<8;i++) OLED_WR_DATA(F8X16); OLED_Set_Pos(x,y+1); for(i=0;i<8;i++) OLED_WR_DATA(F8X16); } else { OLED_Set_Pos(x,y); for(i=0;i<6;i++) OLED_WR_DATA(F6x8[i]); }} //显示一个字符号串void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t Char_Size){ unsigned char j=0; while (chr[j]!='') { OLED_ShowChar(x,y,chr[j],Char_Size); x+=8; if(x>120){x=0;y+=2;} j++; }}
这里需要有字库文件用来显示字符位置。 接着我们测试一下代码
OLED_Init();//OLED初始化 OLED_Clear();//刷新屏幕 OLED_ShowString(0,2,"Hello",2);//显示Hello
结合上期介绍的血氧模块,我们可以获得血氧并且显示。