OLED显示实验(8080接口方式)
OLED接口说明
读写流程简述
先根据要写入/读取的数据的类型,设置 DC 为高(针对数据)/低(针对命令),然后拉低片选(选中 SSD1306芯片),接着我们根据是读数据,还是要写数据置 RD/WR为低或者高,如果是写数据或者写命令,就在RD的上升沿,使数据/命令锁存到数据线(D[7:0])上;如果是读数据或者读状态,就在WR的上升沿,使状态/数据所存至数据线(D[7:0])上。
SSD1306 的 8080 并口读写时序解析
OLED显存的排列
在单片机的内部建立一个OLED的GRAM(需要128*8 个字节),在每次修改的时候,只是修改单片机上的GRAM(实际上就是 SRAM),在修改完了之后,一次性把单片机内部的 GRAM写入到OLED的GRAM中。
OLED基本操作流程
OLED官方库函数的功能
OLED操作库函数
void OLED_WR_Byte(u8 dat,u8 cmd)
写一个字节
void OLED_Display_On(void)
显示开
void OLED_Display_Off(void)
显示关
void OLED_Refresh_Gram(void)
更新OLED的GRAM
void OLED_Init(void)
OLED初始化
void OLED_Clear(void)
清空OLED的GPAM/清屏操作
void OLED_DrawPoint(u8 x,u8 y,u8 t)
画点操作
void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot)
矩形区域填充操作
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode)
显示字符
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size)
显示数字
void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size)
显示字符串
OLED官方库函数解析
数据写入操作函数
//dat:要写入的数据/命令
//cmd:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 cmd)
{
DATAOUT(dat); // 以字节为单位写入PortC端口中
OLED_RS=cmd; // 写入数据/命令
OLED_CS=0; // 使能片选功能
OLED_WR=0;
OLED_WR=1; // WR=0,WR=1,是为了实现上升沿
OLED_CS=1; // 关闭片选段
OLED_RS=1; // DC电平恢复至初始状态
}
数据写入操作函数的使用
当我们在使用void OLED_Refresh_Gram(void) 函数将我们的128*8的数组写入OLED的GRAM中时,我们必须触发OLED中的WR的上升沿,以便将OLED的数据按字节为单位从D[7:0]写入OLED中。
//更新显存到LCD
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++)OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);
}
}
画点操作函数
//画点
//x:0~127
//y:0~63
//t:1 填充 0,清空
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{
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; // 清空
}
画点操作函数的使用
#include "oled.h"
#include "stm32f10x.h"
#include "delay.h"
int main()
{
u8 x=10, y=10, mode=1;
delay_init(); // 定时器初始化
OLED_Init(); // OLED初始化
delay_ms(10); // 等待初始化
while(1)
{
OLED_DrawPoint(x, y, mode);
x++;y++;
OLED_Refresh_Gram(); // 将数据更新至OLED的GRAM的缓存中
x &= 0x1F;
y &= 0x1F;
}
}
矩形区域填充函数解析
//x1,y1,x2,y2 填充区域的对角坐标
//确保x1<=x2;y1<=y2 0<=x1<=127 0<=y1<=63
//dot:0,清空;1,填充
void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot)
{
u8 x,y;
for(x=x1;x<=x2;x++) // 进行行的填充
{
for(y=y1;y<=y2;y++) // 进行列的填充
OLED_DrawPoint(x,y,dot);
}
OLED_Refresh_Gram();//更新显示
}
矩形区域填充函数的使用
#include "oled.h"
#include "stm32f10x.h"
#include "delay.h"
int main()
{
u8 x=10, y=10, mode=1;
delay_init(); // 定时器初始化
OLED_Init(); // OLED初始化
delay_ms(10); // 等待初始化
while(1)
{
OLED_ShowString(10,10,"Hello World",16); // 用2412字体写入(单个字符占24行12列)
OLED_ShowString(10,26,"Today:",24); // 用1608字体写入(单个字符占16行8列)
OLED_Fill(10,50,30,70,1); // 填充(10,50)到(30,70)的区域
OLED_Refresh_Gram(); // 将128*64的数据装入OLED的GRAM中
}
}
显示字符函数
//在指定位置显示一个字符,包括部分字符
//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<csize;t++) // 更新csize个字节的信息
{
if(size==12)temp=asc2_1206[chr][t]; //调用1206字体
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) // 判断最高位是否为1
OLED_DrawPoint(x,y,mode);
else
OLED_DrawPoint(x,y,!mode);
temp<<=1; // 不断向左移位(与上面的判断相呼用来将temp中为1的点点亮)
y++;
if((y-y0)==size) // 当读到结尾就停(读取长度=字体的长度)
{
y=y0; // 从第一行开始读
x++; // 列+1
break;
}
}
}
}
显示字符函数的使用
#include "oled.h"
#include "stm32f10x.h"
#include "delay.h"
int main()
{
u8 x=10, y=10, mode=1;
delay_init(); // 定时器初始化
OLED_Init(); // OLED初始化
delay_ms(10); // 等待初始化
while(1)
{
OLED_ShowString(10,10,"Hello World",16); // 用1608字体写入(单个字符占16行8列)
OLED_ShowChar(10,26,"T",24); // 用2412字体写入(单个字符占24行12列)
OLED_Refresh_Gram(); // 将128*64的数据装入OLED的GRAM中
}
}
显式数字的函数
//m^n函数
u32 mypow(u8 m,u8 n)
{
u32 result=1;
while(n--)result*=m;
return result;
}
//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式 0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size)
{
u8 t,temp;
u8 enshow=0;
for(t=0;t<len;t++)
{
temp=(num/mypow(10,len-t-1))%10;
if(enshow==0&&t<(len-1))
{
if(temp==0)
{
OLED_ShowChar(x+(size/2)*t,y,' ',size,1);
continue;
}else enshow=1;
}
OLED_ShowChar(x+(size/2)*t,y,temp+'0',size,1); // 显示数字对应的ASCII码
}
}
// 其实显示数字就是显示数字对应的ASCII字符
显式数字函数的使用
#include "oled.h"
#include "stm32f10x.h"
#include "delay.h"
int main()
{
u8 x=10, y=10, mode=1;
delay_init(); // 定时器初始化
OLED_Init(); // OLED初始化
delay_ms(10); // 等待初始化
while(1)
{
OLED_ShowString(10,10,"Hello World",16); // 用2412字体写入(单个字符占24行12列)
OLED_ShowString(10,26,"Today:",24); // 用1608字体写入(单个字符占16行8列)
OLED_ShowNum(10,50,20200904,8,12); // 将数字转换为字符进行写入
OLED_Refresh_Gram(); // 将128*64的数据装入OLED的GRAM中
}
}
显式字符串的函数
//显示字符串(注意:字符串还有一个未显式显示的’\0’,因此才会出现如下if中的情况)
//x,y:起点坐标
//size:字体大小
//*p:字符串起始地址
void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size)
{
while((*p<='~')&&(*p>=' '))//判断是不是非法字符!
{
if(x>(128-(size/2))) // 如果字符总长度大于128-(size/2)
{x=0;y+=size;} // 直接进行下一行字符的显示
if(y>(64-size)) // 如果字符总列数超出了64-size
{y=x=0;OLED_Clear();} // 清空GRAM中旧的缓存
OLED_ShowChar(x,y,*p,size,1); // 更新OLED中GRAM的缓存
x+=size/2; // 直接进行下一列字符的显示
p++;
}
// ‘ ‘是ASCII码的第一个字符,用char-‘ ‘ 得出的是char在ASCII中的相对位置
}
// 注意:这里的一个字符的列宽为size/2,行宽为size
显式字符串函数的使用
#include "oled.h"
#include "stm32f10x.h"
#include "delay.h"
int main()
{
u8 x=10, y=10, mode=1;
delay_init(); // 定时器初始化
OLED_Init(); // OLED初始化
delay_ms(10); // 等待初始化
while(1)
{
OLED_ShowString(10,10,"Hello World",16); // 用1608字体写入(单个字符占16行8列)
OLED_ShowString(10,26,"Today:",24); // 用2412字体写入(单个字符占24行12列)
OLED_ShowString(10,50,"2020/9/4",12); // 用1206字体写入(单个字符占12行6列)
OLED_Refresh_Gram(); // 将128*64的数据装入OLED的GRAM中
}
}
OLED简单显示实验
#include "delay.h"
#include "sys.h"
#include "oled.h"
int main(void)
{
u8 t;
delay_init(); //延时函数初始化
delay_init(); // 初始化定时器
OLED_Init(); //初始化OLED
delay_ms(10); // 延迟10ms
OLED_ShowString(0,0,"ALIENTEK",24);
OLED_ShowString(0,24, "0.96' OLED TEST",16);
OLED_ShowString(0,40,"ATOM 2015/1/14",12);
OLED_ShowString(0,52,"ASCII:",12);
OLED_ShowString(64,52,"CODE:",12);
OLED_Refresh_Gram(); //更新显示到OLED
while(1)
{
OLED_ShowChar(48,48,t,16,1);//显示ASCII字符
OLED_Refresh_Gram();
t++;
if(t>'~')t=' ';
OLED_ShowNum(103,48,t,3,16);//显示ASCII字符的码值
}
}
OLED自定义显示的原理
我们知道正点原子提供给我们的库函数操作其实就是先刷新一个128*64大小的数组,然后将个更新过的数组通过刷新GRAM的函数写入OLED的GRAM中。数组如下:
//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127
//[1]0 1 2 3 ... 127
//[2]0 1 2 3 ... 127
//[3]0 1 2 3 ... 127
//[4]0 1 2 3 ... 127
//[5]0 1 2 3 ... 127
//[6]0 1 2 3 ... 127
//[7]0 1 2 3 ... 127
u8 OLED_GRAM[128][8];
我们的显存中有128*64个像素点,就相当于有128*64个状态点,每个状态点仅仅有两种状态:亮/灭,对应的二进制为1/0。因此相当于有128*64个bits比特,当我们用16进制表示时,我们用1byte表示8个bits,因此数组中一共有128*8个元素,每个元素为一个字节,分别控制8个像素点的状态。
数组中每个字节与像素点的对应关系
例如:我们的画布时10*10一共100个像素点,我们需要20个byte,即20个元素的数组,数组的size是怎么来的呢?
图示如下:
当我们在10*10的像素点阵中取如下点:
我们会得到如下包含20个元素的数组:
{0x2C,0x00,0x40,0x00,0x02,0x00,0x61,0x00,0x01,0x00,0x41,0x00,0x40,0x00,0x06,0x00,0x40,0x00,0x08,0x00}
OLED自定义显示的程序设计
#include "oled.h"
#include "stm32f10x.h"
#include "delay.h"
u8 Image[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x20,0x00,0x00,0x00,0x00,0x00,0x90,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,
0x00,0x08,0x00,0x00,0x00,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x80,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x01,0x00,0x00,0x00,0x00,0x20,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x80,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0xC0,0x00,0x00,0x80,0x00,0x00,0x40,0x40,0x00,0x00,0x00,0x20,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x02,0x60,0x00,0x00,0x00,0x40,
0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x20,0x04,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x40,0x00,0x00,0x00,
0x00,0x04,0x00,0x00,0x00,0x04,0x08,0x20,0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x80,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,
0x00,0x00,0x04,0x00,0x06,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x40,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
void OLED_Show(u8 x,u8 y,u16 bytenumber, u8 height, u8* Image, u8 mode);
int main()
{
u8 x=10, y=10, mode=1;
delay_init(); // 定时器初始化
OLED_Init(); // OLED初始化
delay_ms(10); // 等待初始化
while(1)
{
OLED_Show(10,10,350, 50, Image, 1);
OLED_Refresh_Gram(); // 将128*64的数据装入OLED的GRAM中
}
}
/* 万能的OLED自定义显示图像的函数 */
// x,y:图像左上角的坐标
// bytenumber:像素点的个数
// height:像素点的高度,即每一列总共有几行
// Image:包含图像数据的一维数组首地址
// mode:当像素点的值为1时,是点亮数据点还是清除数据点
void OLED_Show(u8 x,u8 y,u16 bytenumber, u8 height, u8* Image, u8 mode)
{
u16 i=0;
u8 y0=y, temp=0, j=0;
for(i=0; i<bytenumber; i++)
{
temp=Image[i];
for(j=0; j<8; j++)
{
if(temp&0x80)
OLED_DrawPoint(x,y,mode);
else
OLED_DrawPoint(x,y,!mode);
temp <<= 1;
y++;
if(y-y0 == height)
{
y=y0;
x++;
break;
}
}
}
}
其中,图像如下:
图像对应数据如下:
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x20,0x00,0x00,0x00,0x00,0x00,0x90,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,
0x00,0x08,0x00,0x00,0x00,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x80,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x01,0x00,0x00,0x00,0x00,0x20,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x80,0x00,0x00,0x00,0x80,0x10,0x00,0x00,0xC0,0x00,0x00,0x80,0x00,0x00,0x40,0x40,0x00,0x00,0x00,0x20,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x02,0x60,0x00,0x00,0x00,0x40,
0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x20,0x04,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x40,0x00,0x00,0x00,
0x00,0x04,0x00,0x00,0x00,0x04,0x08,0x20,0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x80,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,
0x00,0x00,0x04,0x00,0x06,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x40,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
运行结果如下: