完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
OLED简介
OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD)。 OLED 由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。 LCD 都需要背光,而 OLED 不需要,因为它是自发光的。这样同样的显示, OLED 效果要来得好一些。 以目前的技术, OLED 的尺寸还难以大型化,但是分辨率确可以做到很高。 本文选用0.96寸OLED,该模块有以下特点: 1)尺寸小,显示尺寸为 0.96 寸,而模块的尺寸仅为 27mm*26mm 大小; 2)高分辨率,该模块的分辨率为 128x64。 3)多种接口方式,OLED模块提供了总共 4 种接口包括: 6800、 8080 两种并行接口方式、 4线 SPI 接口方式以及 IIC 接口方式(只需要 2 根线就可以控制 OLED 了!),本文选用的是SPI接口; 4) 不需要高压,直接接 3.3V 就可以工作了; ==注意:==该模块不和 5.0V 接口兼容,所以请大家在使用的时候一定要小心,别直接接到 5V 的系统上去,否则可能烧坏模块。 D0(SCLK):串行时钟线。 D1(SDIN):串行数据线。 RST:硬复位 OLED。 D/C:命令/数据标志(0,读写命令; 1,读写数据)。 CS:OLED 片选信号。 每个数据长度均为 8 位,在 SCLK 的上升沿,数据从 SDIN 移入到SSD1306,并且是高位在前的。 DC 线还是用作命令/数据的标志线。在 4 线 SPI 模式下,写操作的时序如图所示: OLED模块显存 SSD1306显存总共为128*64bit,SSD1306将这些显存分为8页,每页包含128个字节,总共8页,这也刚好是128x64的点阵大小。 即每页包含8行,操作显示时以页为单位操作显示。 SSD1306的命令 第一个命令为 0X81,用于设置对比度的,这个命令包含了两个字节,第一个 0X81 为命令,随后发送的一个字节为要设置的对比度的值。这个值设置得越大屏幕就越亮。 第二个命令为 0XAE/0XAF。 0XAE 为关闭显示命令; 0XAF 为开启显示命令。 第三个命令为 0X8D,该指令也包含 2 个字节,第一个为命令字,第二个为设置值,第二个字节的 BIT2 表示电荷泵的开关状态,该位为 1,则开启电荷泵,为 0 则关闭。在模块初始化的时候,这个必须要开启,否则是看不到屏幕显示的。 第四个命令为 0XB0~B7,该命令用于设置页地址,其低三位的值对应着 GRAM 的页地址。 第五个指令为 0X00~0X0F,该指令用于设置显示时的起始列地址低四位。 第六个指令为 0X10~0X1F,该指令用于设置显示时的起始列地址高四位 程序显示原理 在STM32的内部建立一个缓存(共128x8个字节),每次修改显示时,只是修改STM32上的缓存(实际上就是SRAM),在修改完了之后,一次性把STM32上的缓存数据写入到OLED的GRAM。 1、引脚连接 D0–PC6 D1–PC7 RST–PG15 D/C–PD6 CS–PB7 OLED初始化 void OLED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOG, ENABLE);//使能时钟 #if OLED_MODE==1 //8080并口模式 //GPIO初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 ; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7 ; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_11; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_5; GPIO_Init(GPIOE, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; GPIO_Init(GPIOG, &GPIO_InitStructure); OLED_WR=1; OLED_RD=1; #else //使用4线SPI 串口模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 ; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; GPIO_Init(GPIOG, &GPIO_InitStructure); OLED_SDIN=1; OLED_SCLK=1; #endif OLED_CS=1; OLED_RS=1; OLED_RST=0; delay_ms(100); OLED_RST=1; OLED_WR_Byte(0xAE,OLED_CMD); //关闭显示 OLED_WR_Byte(0xD5,OLED_CMD); //设置时钟分频因子,震荡频率 OLED_WR_Byte(80,OLED_CMD); //[3:0],分频因子;[7:4],震荡频率 OLED_WR_Byte(0xA8,OLED_CMD); //设置驱动路数 OLED_WR_Byte(0X3F,OLED_CMD); //默认0x3F(1/64) OLED_WR_Byte(0xD3,OLED_CMD); //设置显示偏移 OLED_WR_Byte(0X00,OLED_CMD); //默认为0 OLED_WR_Byte(0x40,OLED_CMD); //设置显示开始行[5:0],行数. OLED_WR_Byte(0x8D,OLED_CMD); //电荷泵设置 OLED_WR_Byte(0x14,OLED_CMD); //bit2,开启/关闭 OLED_WR_Byte(0x20,OLED_CMD); //设置内存地址模式 OLED_WR_Byte(0x02,OLED_CMD); //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10; OLED_WR_Byte(0xA1,OLED_CMD); //段重定义设置,bit0:0,0->0;1,0->127; OLED_WR_Byte(0xC0,OLED_CMD); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数 OLED_WR_Byte(0xDA,OLED_CMD); //设置COM硬件引脚配置 OLED_WR_Byte(0x12,OLED_CMD); //[5:4]配置 OLED_WR_Byte(0x81,OLED_CMD); //对比度设置 OLED_WR_Byte(0xEF,OLED_CMD); //1~255;默认0x7F(亮度设置,越大越亮) OLED_WR_Byte(0xD9,OLED_CMD); //设置预充电周期 OLED_WR_Byte(0xf1,OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2; OLED_WR_Byte(0xDB,OLED_CMD); //设置VCOMH 电压倍率 OLED_WR_Byte(0x30,OLED_CMD); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc; OLED_WR_Byte(0xA4,OLED_CMD); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏) OLED_WR_Byte(0xA6,OLED_CMD); //设置显示方式;bit0:1,反向显示;0,正常显示 OLED_WR_Byte(0xAF,OLED_CMD); //开启显示 OLED_Clear();//清屏 } OLED更新缓存,显示内容 我们在 STM32F4内部定义了一个块GRAM:u8 OLED_GRAM[128][8];此部分 GRAM 对应 OLED 模块上的 GRAM。在操作的时候,我们只要修改 STM32F4 内部的 GRAM 就可以了,然后通过 OLED_Refresh_Gram 函数把 GRAM 一次刷新到 OLED 的 GRAM 上。 u8 OLED_GRAM[128][8]; // 更新显存到OLED void OLED_Refresh_Gram(void) { u8 i,n; for(i=0;i<8;i++) { OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7) OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址 OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址 for(n=0;n<128;n++) //对每行128列 写字节 OLED_WR_Byte(OLED_GRAM[n],OLED_DATA); } OLED_GRAM[128][8]中的 128 代表列数(x 坐标),而 8 代表的是页, 每页又包含 8 行, 总共 64 行(y 坐标)。从高到低对应行数从小到大。比如,我们要在 x=100, y=29 这个点写入1,则可以用这个句子实现: OLED_GRAM[100][4]|=1<<2; 1 一个通用的在点(x, y)置 1 表达式为: OLED_GRAM[x][7-y/8]|=1<<(7-y%8); 1 其中 x 的范围为:0~127; y 的范围为: 0~63。 OLED写一个字节 //dat:要写的数据/命令 //cmd:数据/命令标志 0,表示命令;1,表示数据 void OLED_WR_Byte(u8 dat,u8 cmd) { u8 i; OLED_RS=cmd; //写命令 OLED_CS=0; //拉低进行片选 for(i=0;i<8;i++) //因为是串行,每次需要写8位 { OLED_SCLK=0; if(dat&0x80)OLED_SDIN=1; else OLED_SDIN=0; OLED_SCLK=1; //开启时钟 dat<<=1; //传送数据 } OLED_CS=1; //禁止片选 OLED_RS=1; } OLED画点函数 void OLED_DrawPoint(u8 x,u8 y,u8 t) //t=1为该点显示,t=0为该点不显示 { u8 pos,bx,temp=0; if(x>127||y>63)return;//超出范围了 pos=7-y/8; bx=y%8; temp=1<<(7-bx); if(t)OLED_GRAM[x][pos]|=temp; else OLED_GRAM[x][pos]&=~temp; } OLED字符显示 OLED的字符显示需要用到字符提取软件制作字符码表,请看后续博客 //x:0~127 //y:0~63 //mode:0,反白显示;1,正常显示 //size:选择字体 12/16/24 void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode) { u8 temp,t,t1; u8 y0=y; u8 csize=(size/8+((size%8)?1:0))*(size/2); //得到字体一个字符对应点阵集所占的字节数 chr=chr-' ';//得到偏移后的值 for(t=0;t if(size==12)temp=asc2_1206[chr][t]; //调用 1206 字体 表示每个字符占OLED 12行6列 else if(size==16)temp=asc2_1608[chr][t]; //调用 1608 字体 else if(size==24)temp=asc2_2412[chr][t]; //调用 2412 字体 else return; //没有的字库 for(t1=0;t1<8;t1++) { if(temp&0x80)OLED_DrawPoint(x,y,mode); else OLED_DrawPoint(x,y,!mode); temp<<=1; y++; if((y-y0)==size) { y=y0; x++; break; } } } } 主函数 int main(void) { u8 t=0; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组 2 delay_init(168); uart_init(115200); LED_Init(); OLED_Init(); /* //OLED 满屏检验屏幕坏点程序 for(x=0;x<128;x++) {for(y=0;y<64;y++) OLED_DrawPoint(x,y,1); } OLED_Refresh_Gram();//更新显示到OLED */ OLED_ShowString(0,0,"HhL",24); OLED_ShowString(0,24, "0.96' OLED TEST",16); OLED_ShowString(0,40,"ATOM 2020/3/6",12); OLED_ShowString(0,52,"ASCII:",12); OLED_ShowString(64,52,"CODE:",12); OLED_Refresh_Gram();//更新显示到 OLED t=' '; while(1) { OLED_ShowChar(36,52,t,12,1);//显示 ASCII 字符 OLED_ShowNum(94,52,t,3,12); //显示 ASCII 字符的码值 OLED_Refresh_Gram(); //更新显示到 OLED t++; if(t>'~')t=' '; delay_ms(500); LED0=!LED0; } } 上传连接好硬件,上传程序显示效果如下: 可以看到显示中有部分行无法显示,此时需要检测是程序问题还是显示屏的问题,进行满屏显示检测,主程序中有满屏坏点检测程序,检测效果如下: 从图中可以看出并非程序的问题而是硬件损坏导致,因为疫情导致,一直在家中,手边没有新的OLED因此不能更换,敬请谅解。 |
|
|
|
只有小组成员才能发言,加入小组>>
3329 浏览 9 评论
3009 浏览 16 评论
3503 浏览 1 评论
9085 浏览 16 评论
4102 浏览 18 评论
1212浏览 3评论
626浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
611浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2352浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1915浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-6 17:34 , Processed in 1.004125 second(s), Total 78, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号