OLED自定义显示图像实验(基础->进阶)

tech2024-11-18  17

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}

 

运行结果如下:

 

最新回复(0)