概述
文章目录
- 一、OLED相关
- 1、OLED简介
- 2、0.96寸的OLED模块概述
- 3、模块引脚说明
- 4、汉字点阵编码原理
- 二、硬件SPI
- 1、SPI简介
- 2、SPI的引脚映射关系
- 三、SPI驱动的OLED
- 1、实验要求
- 2、实验环境
- 3、实验结果
- (1)显示自己的学号和姓名
- (2)显示AHT20的温度和湿度
- (3)上下或左右的滑动显示长字符
- 四、总结
一、OLED相关
1、OLED简介
-
OLED即有机发光二级管(Organic Light-Emitting Diode,OLED)。OLED 显示技术具有自发光、广视角、几乎无穷高的对比度、较低耗电、极高反应速度、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优点,被认为是下一代的平面显示器新兴应用技术。
-
OLED 显示和传统的 LCD 显示不同,其可以自发光,所以不需要背光灯,这使得OLED 显示屏相对于LCD显示屏尺寸更薄,同时显示效果更优。
-
LCD都需要背光,而OLED不需要,因为它是自发光的。这样同样的显示,OLED效果要来得好一些。以目前的技术,OLED的尺寸还难以大型化,但是分辨率确可以做到很高。
2、0.96寸的OLED模块概述
模块背面图
该款OLED模块显示尺寸为0.96寸,拥有128x64分辨率。可以选择3线制、4线制SPI以及lIC三种通信方式,驱动IC为SSD1306。包含显示黑色、蓝色或者黄蓝双色三款模块。
具体说明如下:
- A、使用4.7K电阻只焊接R3、R4电阻,则选择4线制SPI总线接口(默认)﹔
- B、使用4.7K电阻只焊接R2、R3电阻,则选择3线制SPI总线接口;
- C、使用4.7K电阻只焊接R1、R4、R6、R7、R8电阻,则选择IIC总线接口;
3、模块引脚说明
接口总线模式切换后,需要选择相应配套的软件和相应的接线引脚,模块才能正常运行。相应的接线引脚说明如下:
-
A、选择4线制SPI总线接口,所有的引脚都需要使用;
-
B、选择3线制SPI总线接口,只有DC引脚不需要使用(可以不接),其他引脚都需要使用;
-
C、选择IIC总线接口,只需要使用GND、VCC、D0、D1这四个引脚,同时将RES引脚接高电平(可以接VCC),将DC和CS引脚接电源地;
4、汉字点阵编码原理
汉字点阵编码
-
在汉字的点阵字库中,每个字节的每个位都代表一个汉字的一个点,每个汉字都是由一个矩形的点阵组成,0 代表没有点,1 代表有点,将 0 和 1 分别用不同颜色画出,就形成了一个汉字,常用的点阵矩阵有
12*12, 14*14, 16*16
三 种字库。 -
字库根据字节所表示点的不同有分为横向矩阵和纵向矩阵,目前多数的字库都是横向矩阵的存储方式(用得最多的应该是早期 UCDOS 字库),纵向矩阵一 般是因为有某些液晶是采用纵向扫描显示法,为了提高显示速度,于是便把字库 矩阵做成纵向,省得在显示时还要做矩阵转换。
OLED点阵显示
-
点阵屏像素按128列X64行组织,每一行128个像素单元的阴极是连接在一起,作为公共极(COM),每一列64个像素单元的阳极也连接在一起,作为一段(SEG)。行列交叉点上的LED就是一个显示单元,即一个像素。要点亮一个像素,只要在该像素所在列电极上加上正电压、行电极接地。同样,要驱动一整行图像,就需要同时把128列信号加载到列电极上,把该行行电极接地。该行显示时,其他63行均不能显示,其行电极应为高电平或悬空。
-
可见,整屏的显示,只能分时扫描进行,一行一行的显示,每次显示一行。行驱依次产生低电平扫描各行,列驱动读取显示数据依次加载到列电极上。扫描一行的时间称为行周期,完成一次全屏扫描,就叫做一帧。一般帧频大于60,人眼观察不到逐行显示。每行扫描显示用时叫占空比,占空比小,为达到相同的显示亮度,驱动电流就大。SSD1306段驱动最大电流为100uA,当整行128个像素全部点亮时,行电极就要流过12.8mA的电流。
二、硬件SPI
OLED 驱动方式
- 此模块支持四线 SPI、三线 SPI、IIC 接口和 6800、8080 并口方式,模块的通信接口是通过 BS0,BS1,BS2 三个管脚来配置。
此处使用四线 SPI。
1、SPI简介
- SPI是串行外设接口(Serial Peripheral Interface)的简写,其实就是一种总线通信协议,该总线以主从方式工作,通常是一个主设备和一个或多个从设备,一般需要4根线来进行数据通信,即SDI(数据输入)、SDO(数据输出)、SCLK(时钟)、CS(片选),相应的,有一套时序逻辑规定了通信过程。
- 一般使用SPI的设备都配备了现成的SPI模块,只需要对相应的寄存器写入指令就可以进行通信了,如果没有SPI接口,也可以软件实现,就是根据时序图手动操作GPIO来实现通信,这就比较费劲了。
2、SPI的引脚映射关系
- SS( Slave Select):从设备选择信号线,常称为片选信号线。
- SCK (Serial Clock):时钟信号线,用于通讯数据同步。
- MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。
- MISO(Master Input,,Slave Output):主设备输入/从设备输出引脚。
三、SPI驱动的OLED
1、实验要求
-
显示自己的学号和姓名;
-
显示AHT20的温度和湿度;
-
上下或左右的滑动显示长字符,比如歌词、诗词。(最好使用硬件刷屏模式)
2、实验环境
-
keil5编程软件
-
野火STM32F103指南者开发板
-
0.96寸的OLED模块
3、实验结果
(1)显示自己的学号和姓名
实验代码
测试例程下载地址:
http://www.lcdwiki.com/zh/0.96inch_SPI_OLED_Module#.E7.A8.8B.E5.BA.8F.E4.B8.8B.E8.BD.BD
打开keil工程文件
利用取模软件,写好自已要显示的内容
取模时注意要设置参数
取模软件: PCtoLCD2002完美版.zip
提取码:jfbr
将生成的点阵添加进数组
注意:在添加字模时,要添加进汉字显示对应的数组。(此处我选择的是16 * 16大小的OLED汉字显示)
修改main.c如下
int main(void)
{
delay_init(); //延时函数初始化
NVIC_Configuration(); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
OLED_Init(); //初始化OLED
OLED_Clear(0); //清屏(全黑)
while(1)
{
TEST_MainPage(); //主界面显示测试
}
}
修改test.c如下:
void TEST_MainPage(void)
{
GUI_ShowCHinese(28,20,16,"XX",1); //XX为名字
GUI_ShowString(4,48,"YY",16,1); //YY为学号
delay_ms(1500);
delay_ms(1500);
}
函数说明
GUI_ShowString() 的参数
参数一:X 坐标
参数二:Y 坐标
参数三:字符串(ASCLL码中的)
参数四:bit (表示字符显示格式,这里我用的 16 ,和汉字一样高)
参数五:显示样式(1:白字黑底;0:黑字白底)GUI_ShowChinese() 的参数
参数一:X 坐标
参数二:Y 坐标
参数三:汉字点阵大小(这里使用的是 16×16 的,参数应该是 16)
参数四:要显示的汉字
参数五:显示样式(1:白字黑底;0:黑字白底)
编译运行生成.hex文件
屏幕显示
按照下图对应连接好STM32开发版和OLED模块
注:其中CS不接
连接好后如下图
将代码烧进板子里(.hex文件)
最终OLED模块显示如下图
(2)显示AHT20的温度和湿度
实验代码
AHT20的温湿度的读取工程:https://github.com/Thee24LYJ/STM32_AHT20
将AHT20 温湿度采集工程文件中的部分代码文件移植到OLED显示的工程文件中:
注意要把文件路径添加进去
bsp_i2c.c修改如下
uint8_t t;
u8 *strTemp1;
u8 *strTemp2;
u8 *strTemp3;
u8 *strHumi1;
u8 *strHumi2;
u8 *strHumi3;
void read_AHT20(void)
{
uint8_t i;
//初始化 readByte 数组
for(i=0; i<6; i++)
{
readByte[i]=0;
}
I2C_Start();
//通过发送 0x71 可以获取一个字节的状态字
I2C_WriteByte(0x71);
ack_status = Receive_ACK();
//接收 6 个 8 bit的数据
readByte[0]= I2C_ReadByte();
//发送 ACK 信号
Send_ACK();
readByte[1]= I2C_ReadByte();
Send_ACK();
readByte[2]= I2C_ReadByte();
Send_ACK();
readByte[3]= I2C_ReadByte();
Send_ACK();
readByte[4]= I2C_ReadByte();
Send_ACK();
readByte[5]= I2C_ReadByte();
//发送 NACK 信号
SendNot_Ack();
I2C_Stop();
//温湿度的二进制数据处理
//0x68 = 0110 1000
//0x08 = 0000 1000
if( (readByte[0] & 0x68) == 0x08 )
{
H1 = readByte[1];
//H1 左移 8 位并与 readByte[2] 相或
H1 = (H1<<8) | readByte[2];
H1 = (H1<<8) | readByte[3];
//H1 右移 4 位
H1 = H1>>4;
H1 = (H1*1000)/1024/1024;
T1 = readByte[3];
//与运算
T1 = T1 & 0x0000000F;
T1 = (T1<<8) | readByte[4];
T1 = (T1<<8) | readByte[5];
T1 = (T1*2000)/1024/1024 - 500;
AHT20_OutData[0] = (H1>>8) & 0x000000FF;
AHT20_OutData[1] = H1 & 0x000000FF;
AHT20_OutData[2] = (T1>>8) & 0x000000FF;
AHT20_OutData[3] = T1 & 0x000000FF;
}
else
{
AHT20_OutData[0] = 0xFF;
AHT20_OutData[1] = 0xFF;
AHT20_OutData[2] = 0xFF;
AHT20_OutData[3] = 0xFF;
}
/*
printf("完成!n");
printf("----温度:%d%d.%d °Cn",T1/100,(T1/10)%10,T1%10);
printf("----湿度:%d%d.%d %%",H1/100,(H1/10)%10,H1%10);
printf("nn");
*/
t = T1/100;
switch(t)
{
case 0:break;
case 1:strTemp1 = "1";break;
case 2:strTemp1 = "2";break;
case 3:strTemp1 = "3";break;
case 4:strTemp1 = "4";break;
case 5:strTemp1 = "5";break;
case 6:strTemp1 = "6";break;
case 7:strTemp1 = "7";break;
case 8:strTemp1 = "8";break;
case 9:strTemp1 = "9";break;
}
t = (T1/10)%10;
switch(t)
{
case 0:strTemp2 = "0";break;
case 1:strTemp2 = "1";break;
case 2:strTemp2 = "2";break;
case 3:strTemp2 = "3";break;
case 4:strTemp2 = "4";break;
case 5:strTemp2 = "5";break;
case 6:strTemp2 = "6";break;
case 7:strTemp2 = "7";break;
case 8:strTemp2 = "8";break;
case 9:strTemp2 = "9";break;
}
t = T1%10;
switch(t)
{
case 0:strTemp3 = "0";break;
case 1:strTemp3 = "1";break;
case 2:strTemp3 = "2";break;
case 3:strTemp3 = "3";break;
case 4:strTemp3 = "4";break;
case 5:strTemp3 = "5";break;
case 6:strTemp3 = "6";break;
case 7:strTemp3 = "7";break;
case 8:strTemp3 = "8";break;
case 9:strTemp3 = "9";break;
}
t = H1/100;
switch(t)
{
case 0:break;
case 1:strHumi1 = "1";break;
case 2:strHumi1 = "2";break;
case 3:strHumi1 = "3";break;
case 4:strHumi1 = "4";break;
case 5:strHumi1 = "5";break;
case 6:strHumi1 = "6";break;
case 7:strHumi1 = "7";break;
case 8:strHumi1 = "8";break;
case 9:strHumi1 = "9";break;
}
t = H1/100;
switch(t)
{
case 0:strHumi2 = "0";break;
case 1:strHumi2 = "1";break;
case 2:strHumi2 = "2";break;
case 3:strHumi2 = "3";break;
case 4:strHumi2 = "4";break;
case 5:strHumi2 = "5";break;
case 6:strHumi2 = "6";break;
case 7:strHumi2 = "7";break;
case 8:strHumi2 = "8";break;
case 9:strHumi2 = "9";break;
}
t = H1/100;
switch(t)
{
case 0:strHumi3 = "0";break;
case 1:strHumi3 = "1";break;
case 2:strHumi3 = "2";break;
case 3:strHumi3 = "3";break;
case 4:strHumi3 = "4";break;
case 5:strHumi3 = "5";break;
case 6:strHumi3 = "6";break;
case 7:strHumi3 = "7";break;
case 8:strHumi3 = "8";break;
case 9:strHumi3 = "9";break;
}
GUI_ShowString(40,32," ",16,1);
GUI_ShowString(40,48," ",16,1);
GUI_ShowCHinese(0,32,16,"温度:",1);
GUI_ShowString(40,32,strTemp1,16,1);
GUI_ShowString(48,32,strTemp2,16,1);
GUI_ShowString(56,32,".",16,1);
GUI_ShowString(64,32,strTemp3,16,1);
GUI_ShowCHinese(72,32,16,"℃",1);
GUI_ShowCHinese(0,48,16,"湿度:",1);
GUI_ShowString(40,48,strHumi1,16,1);
GUI_ShowString(48,48,strHumi2,16,1);
GUI_ShowString(56,48,".",16,1);
GUI_ShowString(64,48,strHumi3,16,1);
GUI_ShowCHinese(72,48,16,"%",1);
}
oledfont.h中的修改如下
const typFNT_GB16 cfont16[] =
{
"温",0x00,0x00,0x23,0xF8,0x12,0x08,0x12,0x08,0x83,0xF8,0x42,0x08,0x42,0x08,0x13,0xF8,
0x10,0x00,0x27,0xFC,0xE4,0xA4,0x24,0xA4,0x24,0xA4,0x24,0xA4,0x2F,0xFE,0x00,0x00,/*"温",0*/
"度",0x01,0x00,0x00,0x80,0x3F,0xFE,0x22,0x20,0x22,0x20,0x3F,0xFC,0x22,0x20,0x22,0x20,
0x23,0xE0,0x20,0x00,0x2F,0xF0,0x24,0x10,0x42,0x20,0x41,0xC0,0x86,0x30,0x38,0x0E,/*"度",0*/
"湿",0x00,0x00,0x27,0xF8,0x14,0x08,0x14,0x08,0x87,0xF8,0x44,0x08,0x44,0x08,0x17,0xF8,
0x11,0x20,0x21,0x20,0xE9,0x24,0x25,0x28,0x23,0x30,0x21,0x20,0x2F,0xFE,0x00,0x00,/*"湿",0*/
":",0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x00,0x00,/*":",0*/
"℃",0x60,0x00,0x91,0xF4,0x96,0x0C,0x6C,0x04,0x08,0x04,0x18,0x00,0x18,0x00,0x18,0x00,
0x18,0x00,0x18,0x00,0x18,0x00,0x08,0x00,0x0C,0x04,0x06,0x08,0x01,0xF0,0x00,0x00,/*"℃",0*/
"%",0x00,0x00,0x18,0x04,0x24,0x08,0x24,0x10,0x24,0x20,0x24,0x40,0x24,0x80,0x19,0x00,
0x02,0x60,0x04,0x90,0x08,0x90,0x10,0x90,0x20,0x90,0x40,0x90,0x00,0x60,0x00,0x00,/*"%",0*/
};
主函数修改如下(main)
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "gui.h"
#include "test.h"
#include "bsp_i2c.h"
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
OLED_Init(); //初始化OLED
IIC_Init(); //初始化IIC
OLED_Clear(0); //清屏(全黑)
while(1)
{
read_AHT20_once(); //读取温度并显示
delay_ms(1500);
}
}
屏幕显示
(3)上下或左右的滑动显示长字符
实验代码
OLED屏的滚屏方式命令
- 水平左右滑动
OLED_WR_Byte(0x2E,OLED_CMD); //关闭滚动
OLED_WR_Byte(0x26,OLED_CMD); //水平向左或者右滚动 26/27
OLED_WR_Byte(0x00,OLED_CMD); //虚拟字节
OLED_WR_Byte(0x00,OLED_CMD); //起始页 0
OLED_WR_Byte(0x07,OLED_CMD); //滚动时间间隔
OLED_WR_Byte(0x07,OLED_CMD); //终止页 7
OLED_WR_Byte(0x00,OLED_CMD); //虚拟字节
OLED_WR_Byte(0xFF,OLED_CMD); //虚拟字节
OLED_WR_Byte(0x2F,OLED_CMD); //开启滚动
- 垂直和水平滚动
OLED_WR_Byte(0x2e,OLED_CMD); //关闭滚动
OLED_WR_Byte(0x29,OLED_CMD); //水平垂直和水平滚动左右 29/2a
OLED_WR_Byte(0x00,OLED_CMD); //虚拟字节
OLED_WR_Byte(0x00,OLED_CMD); //起始页 0
OLED_WR_Byte(0x07,OLED_CMD); //滚动时间间隔
OLED_WR_Byte(0x07,OLED_CMD); //终止页 1
OLED_WR_Byte(0x01,OLED_CMD); //垂直滚动偏移量
OLED_WR_Byte(0x2F,OLED_CMD); //开启滚动
参考:0.96oled屏,驱动ssd1306 一些不常用的命令用法和效果
修改mian函数(向左滑动)
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "gui.h"
#include "test.h"
int main(void)
{
delay_init(); //延时函数初始化
NVIC_Configuration(); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
OLED_Init(); //初始化OLED
OLED_Clear(0); //清屏(全黑)
OLED_WR_Byte(0x2E,OLED_CMD); //关闭滚动
OLED_WR_Byte(0x27,OLED_CMD); //水平向左或者右滚动 26/27
OLED_WR_Byte(0x00,OLED_CMD); //虚拟字节
OLED_WR_Byte(0x00,OLED_CMD); //起始页 0
OLED_WR_Byte(0x07,OLED_CMD); //滚动时间间隔
OLED_WR_Byte(0x07,OLED_CMD); //终止页 7
OLED_WR_Byte(0x00,OLED_CMD); //虚拟字节
OLED_WR_Byte(0xFF,OLED_CMD); //虚拟字节
TEST_MainPage();
OLED_WR_Byte(0x2F,OLED_CMD); //开启滚动
}
显示数据函数
void TEST_MainPage(void)
{
GUI_ShowCHinese(10,20,16,"白头搔更短 浑欲不胜簪",2);
delay_ms(1500);
delay_ms(1500);
}
用取模软件获取相应文字的点阵十六进制表示
const typFNT_GB16 cfont16[] =
{
"白",0x01,0x00,0x02,0x00,0x04,0x00,0x3F,0xF8,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,
0x3F,0xF8,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x3F,0xF8,0x20,0x08,/*"白",0*/
"头",0x00,0x80,0x00,0x80,0x08,0x80,0x04,0x80,0x24,0x80,0x10,0x80,0x10,0x80,0x00,0x80,
0xFF,0xFE,0x01,0x00,0x01,0x40,0x02,0x20,0x04,0x10,0x08,0x08,0x30,0x04,0xC0,0x04,/*"头",1*/
"搔",0x20,0x00,0x23,0xF8,0x21,0x48,0x21,0x10,0xF8,0xA0,0x20,0x40,0x21,0xB0,0x2E,0x4E,
0x30,0x40,0xE3,0xF8,0x22,0x48,0x22,0x48,0x23,0xF8,0x20,0x44,0xA7,0xFC,0x40,0x04,/*"搔",2*/
"更",0x00,0x00,0xFF,0xFE,0x01,0x00,0x01,0x00,0x3F,0xF8,0x21,0x08,0x21,0x08,0x3F,0xF8,
0x21,0x08,0x21,0x08,0x3F,0xF8,0x11,0x00,0x0A,0x00,0x06,0x00,0x19,0xC0,0xE0,0x3E,/*"更",3*/
"短",0x20,0x00,0x21,0xFE,0x3C,0x00,0x50,0x00,0x90,0xFC,0x10,0x84,0x10,0x84,0xFE,0x84,
0x10,0xFC,0x10,0x00,0x10,0x84,0x28,0x44,0x24,0x48,0x44,0x00,0x41,0xFE,0x80,0x00,/*"短",4*/
"浑",0x00,0x00,0x23,0xFE,0x12,0x02,0x14,0x44,0x80,0x40,0x43,0xFC,0x48,0x80,0x08,0xA0,
0x11,0x20,0x11,0xFC,0xE0,0x20,0x20,0x20,0x27,0xFE,0x20,0x20,0x20,0x20,0x00,0x20,/*"浑",5*/
"欲",0x14,0x20,0x12,0x20,0x21,0x20,0x49,0x7E,0x08,0x42,0x14,0x84,0x22,0x10,0x41,0x10,
0xBE,0x10,0x22,0x10,0x22,0x28,0x22,0x28,0x22,0x48,0x3E,0x44,0x22,0x84,0x01,0x02,/*"欲",6*/
"不",0x00,0x00,0x7F,0xFC,0x00,0x80,0x00,0x80,0x01,0x00,0x01,0x00,0x03,0x40,0x05,0x20,
0x09,0x10,0x11,0x08,0x21,0x04,0x41,0x04,0x81,0x00,0x01,0x00,0x01,0x00,0x01,0x00,/*"不",7*/
"胜",0x00,0x20,0x78,0x20,0x49,0x20,0x49,0x20,0x49,0xFC,0x79,0x20,0x4A,0x20,0x48,0x20,
0x48,0x20,0x79,0xFC,0x48,0x20,0x48,0x20,0x48,0x20,0x48,0x20,0x4B,0xFE,0x98,0x00,/*"胜",8*/
"簪",0x20,0x40,0x3F,0x7E,0x48,0x90,0x85,0x08,0x3E,0x7C,0x08,0x10,0x48,0x90,0x7E,0xFE,
0x14,0x28,0x24,0x4A,0x46,0x86,0x3F,0xF8,0x20,0x08,0x3F,0xF8,0x20,0x08,0x3F,0xF8,/*"簪",9*/
};
放进对应数组
屏幕显示
编译运行,生成.hex文件并烧录
实验结果如下
四、总结
以上便是STM32硬件SPI驱动OLED的实验内容,通过此次试验,将SPI驱动和I2C驱动方式进行对比,发现SPI的速度要比I2C的传输速度更快,更适合用作高速传输通信。
最后
以上就是细腻鸡翅为你收集整理的STM32硬件SPI驱动OLED一、OLED相关二、硬件SPI三、SPI驱动的OLED四、总结的全部内容,希望文章能够帮你解决STM32硬件SPI驱动OLED一、OLED相关二、硬件SPI三、SPI驱动的OLED四、总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复