开发者学堂课程【HaaS 物联网应用开发课程:5_1_2_首页信息展示屏_OLED 开发与体验】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/801/detail/13843
5_1_2_首页信息展示屏_OLED 开发与体验
内容介绍:
一、OLED开 发与体验
1.OLED 显示屏
2. OLED 硬件
3.OLED 扫描模式
4.SH1106扫描方式
5.SH1106刷新整个屏幕的 sample code
6.SH1106画点原理
7.SH1106画点代码
8.SH1106驱动代码路径:
9. HaaS EDU K1 homepage 页面的代码
一、OLED 开发与体验
1.OLED 显示屏
OLED ( Organic Light-Emitting Diode )有机电激光显示
也称为有机发光半导体,属于一种电流型的有机发光器件,是通过载流子的注入和复合而致发光的现象,发光强度与注入的电流成正比。
如图所示,上面阳极,下面是金属阴极,阳极表面会形成空穴传输层,阴极表面会形成电子传输层,通电之后,OLED在电场的作用下,阳极产生的空穴和阴极产生的电子就会发生移动,分别向空穴传输层和电子传输层注入,迁移到发光层。当二者在发光层相遇时,产生能量激子,从而激发发光分子最终产生可见光。
OLED 屏幕上有些区域变亮,有些区域不亮就形成我们所看到的图案。
2. OLED 硬件
HaaS EDU K1内置了一块单色 OLED 屏
驱动芯片 |
SH1106 |
使用接口 |
SPI |
分辨率 |
132×64 |
显示尺寸 |
0.96寸 |
显示颜色 |
白色 |
工作电压 |
3.3V/ 5V |
OLED 屏幕的驱动芯片是 SH1106
SH1106和 HaaS1000主芯片之间是通过 SPIO 接口进行通信
上图是 SH1106和 HaaS1000主芯片的电路连线图,除了 OLED 模块的最小电路之外,和 HaaS1000的连线有四根,分别是 RES、D/C、SCLK、SDIN
器件引脚 |
引脚功能 |
对应原理图引脚名 |
RES |
data/cmd |
OLED_RST |
D/C |
此引脚拉高时传输数据,拉低时传输指令 |
SPI_DIO |
SCLK |
数据时钟 |
SPIO_CLK |
SDIN |
接收串行数据 |
SPIO_DIO |
一个 SPI 接口一般有4根引脚:
①主设备输出(MOSI/Master Out Slave In)
②主设备输入(MISO/ Master In Slave Out)
③数据时钟(SCLK)
④片选(CS / Chip Select)
一条 spi 总线上面支持一个主设备和多个从设备,主设备是怎样决定当前 spi 通信是和哪个主设备进行通信的?是通过CS 片选信号进行选择,在 SPI 传输之前会先将从设备的 SPI 设置成有效状态,对应的从设备便知道接下来的 SPI 通信是自己要参与的,又因为在原理图当中,OLED 对应的 CS 引脚是直接经过一个十倍的电阻连接到地上的,这是因为在设计过程中,将 SPI0作为专门控制 OLED 的通道,因此又可以省掉一根 CS 引脚。
SH1106上面的 D/C 引脚是 Data、Command 控制信号的意思。引脚拉高时,SPI 的 MOSI 上的数据代表的是实际的数据信息,D/C 引脚拉低时,MOSI 上传输的信息是控制指令,SCLK 就是S PI 的时钟信号,SDIN 就是 MOSI 的引脚。
3.OLED 扫描模式
接下来讲述 OLED 显示数据的模式。前面说到 OLED 的屏幕分辨率是132乘64,每个像素点占用数据的一个 bit,一般的数据传输是以字节(byte)为单位进行传输的。
也就是说传输一个字节的数据可以控制8个像素点的量,在收到一个 bit 之后,OLED 屏幕可以先刷新一行或一列,按照刷新模式的不同,OLED 共有四种刷新模式:第一种是逐行扫描,在这种模式下面,收到第一个 byte 之后,会先刷新第一行的前八个像素;接下来第二个 byte 刷新第一行的第9到第16个像素;收到后面的数据,以此类推,先把第一行的所有像素刷新完成之后,再一次刷新第二行、第三行,直到刷新完整个屏幕。
第二种是逐列式,收到第一个 byte 之后,它会先把第一列的前八个像素点刷新上去;然后再收到第二 个byte,则会把第一列的第9到第16个像素点刷新上去;收到后面的数据则以此类推,先把第一列的所有像素更新完,然后再更新第二列,直到把整个屏幕都更新完成。
第三种是行列式,行列式是收到第一个 byte 之后,先刷新第一行的前八个像素点;然后收到第二个 byte,刷新第二行的前八个像素点。以此类推,刷新第三行的前八个像素点,把第N行的前八个像素点全刷新,完成之后才刷新第一行的第九到第16个 byte,第二行的第九到第16个呗。然后以此类推,把整个屏幕都刷新完成。
最后一种就是列行式。列行式是在收到第一个 byte 之后,先刷新第一列的前八个像素,然后接下来的第二个 byte 刷新第二列前八个像素。
之后以此类推,在刷新完所有列的前八个像素之后,再从第一列的第九到第16个像素开始刷新,直到刷新完整个屏幕。
4.SH1106扫描方式
在它的 data sheet 当中提到屏幕从上到下,共8个 page;屏幕从左到右,共 O0H-83H , 132列;每个 page 中的每—列都是一个完整的 Byte (下一页),刷新时会逐个配置进行刷新,也就是先刷新 page0的第一列,再刷新第二列,一直刷新到第132列;再刷新 page1的第一列第二列,直到最后把 page7的132列全刷新完成。按照上一个知识点的定义,我们可以看到 SH1106采用的是列行式的刷新方式。
在代码中用一个二维数组代表整个屏幕的所有数据信息,数组为:
uint8_t OLED_GRAM[8][132];它可以代表// 8bit*132列*8页
下图为 data sheet 中描述的填充数据的标准操作流程:
通过设置 Page 地址和 Column 地址,我们可以确定整个 RAM 中的唯一一处 Byte。每个 Page 包含了8行*132列,而每输入一个 Byte,其实是纵向填充每页中的一列。例如,Byte1的位置对应了 Page 0的 Column 1。
每个显示数据读/写命令都会使指定的 Column 地址递增(+1)。这样可以连续访问 MPU 显示数据。由于列地址与页面地址无关。因此,例如,当从页面0列83H,移至页面1列00H 时,必须重新指定页面地址和列地址。
通过持续写入数据和变换页地址,我们就能够将整个屏幕填充为我们想要的图像。
设置 Page 地址及 Column 地址的指令
其中,列地址的高四个 byte 和低四个 byte 需要分别用两条指令来进行设定,配置地址则可以通过一条指令设定完成。比如说,要向 page1的第0列写入一个数据的时,则需要一次发送(0XB1,0X10,0X00)给 SH1006。
5.SH1106刷新整个屏幕的 sample code
根据前面的描述,如果要画一个点到屏幕上,只需要将对应像素点所在字节中的正确的 bit 进行修改,然后将OLED_GRAM 整个结构体的数据刷新到屏幕上。
下面是刷新屏幕的三步 code:
uint8_t OLED_GRAM[8] [132]; l / 8bit*132列*8页
void OLED_Refresh_GRAM( void)
{
uint8_t i,j;
for (i = 0; i < 8; i++)
{
/*设置显示的起始地址*/
write_command ( 0xB0 + i); //设置页地址(行)
write_command ( 0x00 ) ; //设置列地址的低四位
write_command (0x10); /设置列地址的高四位
write_data_page(OLED_GRAM [i],132);
}
因为 SH1106是列行式的刷新模式,所以在写入数据的时,需要先设定操作的行数。然后依次写入132个 byte,就可以将配置地址为 i 的这一整行都进行刷新,然后循环八次就可以把 page0到 page7整个屏幕刷新一遍。
6.SH1106画点原理
由于前面实现了向 OLED 填充整个帧的方法,因此我们所有的操作只需要在 OLED_GRAM 上进行即可。
计算像素点三元素:Page 地址、Column 地址、当前位置的数据( byte )
设屏幕的左上角为原点,对于一个像素点( x,y ),可以知道:
Page 地址: y/8 Column 地址:x
目标 Byte:在原有数据的基础上进行 bit 与、或及异或操作
② 计算 bit 的偏移量: bit_offset = y%8
②进行位操作
将某个比特置0熄灭像素): byte &= ~(0x01 << bit_offset)
将某个比特置1(点亮像素): byte |=(0x01<< bit_offset)
将某个比特置反(翻转像素):byte ^ =(0x01 << bit_offset)
7.SH1106画点代码
void OLED_DrawPoint(int16_t x, int16_t y, uint8_t mode)
{
if ((x > (131)) (y > 63)lx <0 ly < 0)
return;
//如果坐标超出范围则不显示
uint8_t page = y / 8;
//计算页数例如像素点(7,13)则 page = 13/8 = 1该像素点所在位置为 PAGE1的第7列
uint8_t bit_offset = y % 8;
//计算像素点 bit 在对应 Byte 中的偏移例如像素点(7,13)则 bit_offset = 13%8 = 5 该像素点在该 PAGE 的第5行
uint8_t mask = 0x01 << bit_offset;
//生成一个像素点 例如像素点(7,13) 则 mask = 0b 0010 0000它正好遮罩了目标像素点
if (mode == 0) // mode = 0时反显
OLED_GRAM[page] [x] &= ~mask; //该像素点位置 置
0
if (mode == 1) // mode = 1 时正显
OLED_GRAM[page] [x] |= mask;
//该像素点位置 置
1
1if (mode == 2) // mode = 2时翻转
OLED_GRAM[page] [x] ^= mask;v //该像素点位置翻转
}
有了最基本的像素绘制后,我们就可以进行各种图案的绘制。目前支持的绘画直线、矩形、圆形等多种方式,都是基于画点来实现。
8.SH1106驱动代码路径:
Application\example\edk_demo\sensors\oled\lsh1106.c
Application\example\edk_demo\sensors\oled\sh1106.h
Application\example\edk_demo\sensors\oled\font.h(写入字符的时候需要查询此表
,此表提供了画圆、线、字符、字串等API
)
void oLED_Refresh_GRAM(void) ;
void OLED_Clear(void);
void OLED_Full(void);
void OLED_DrawPoint(int16_t x,int16_t y,uint8_t mode);
void OLED_DrawLine(int16_t x0,int16_t y0,int16_t x1,int16_t y1,uint8_t mode);
void OLED_DrawVerticalLine(uint8_t x,uint8_t y,uint8_t length,uint8_t mode);
void OLED_DrawHorizontalLine(uint8_t x,uint8_t y,uint8_t length,uint8_t mode);
void OLED_DrawRect(uint8_t x,uint8_t y,uint8_t width,uint8_t height,uint8_t mode);
void OLED_FillRect(uint8_t xMove,uint8_t yMove,uint8_t width,uint8_t height,uint8_t mode);
void OLED_DrawCircle(uint8_t x0,uint8_t y0,uint8_t radius,uint8_t width,uint8_t mode);
void OLED_FillCircle(uint8_t x0,uint8_t y0,uint8_t r,uint8_t mode);
void OLED_Show_Char(uint8_t x,uint8_t y,uint8_t chr,uint8_t size,uint8_t mode);
void OLED_Show_String(uint8_t x,uint8_t y, const uint8_t *p,uint8_t size,uint8_t mode);
void OLED_Icon_Draw(int16_t x, int16_t y, icon_t *icon,uint8_t mode);
void OLED_test(int flag);
9. HaaS EDU K1 homepage 页面的代码
接下来讲述首页信息屏的部分代码:
application/example/edu_demo/k1_apps/homepage/homepage.c
void homepage_task(void)
{
unsigned char c = 0;
struct tm*info;
struct timespec tv;
uint8_t image_version[22];
uint8_t tmp[22];
netmgr_stats_t stats;
while (1)
{
OLED_Clear(;
/*获取GMT时间*/
清空屏幕
clock_gettime(CLOCK_REALTIME,&tv);
/
/ printf("\r\n=====timespec is : %d===\r\n", tv);
获取系统时间
info = gmtime(&tv);
生成时间字串
snprintf(tmp,21,"%2d:%02d",(info->tm_hour) % 24, info--tmmin2Y;
OLED_Show_String(0,12* 0, tmp,12,1);
将时间字串显示在屏幕上
if (wifi_connected)
{
如果
Wi-Fi连线成功
OLED_Icon_Draw(86,0,&icon_wifi_on_12_12,0); 则将Wi-Fi图标显示在屏幕上行
}