#申请原创#  
OLED屏是一种自发光的显示器件,它有多种类型,按接口分有I2C接口和SPI接口,按色彩分有单色、双色及彩色屏,按显示规格分为0.91、0.96寸等。 本篇将介绍2种OLED屏的显示驱动,其中一款是I2C接口的 0.96寸双色OLED屏,另一款则是SPI接口的0.96寸彩色OLED屏,开发工具仍为RT_Thread。 1. I2C_OLED屏 I2C接口的OLED屏是最节省I/O口的显示屏,它只需2个GPIO口即可驱动其显示,这里选用的是0.96寸的双色屏,其显示分辨率为128*64像素点。实则它是一款准双色屏。因为尽管它有黄蓝2种色彩,但色彩是分区域使用的,而不能自主的设置色彩来进行显示。 使用NuMaker--PFM--M487驱动OLED双色显示屏其显示效果如图1所示,由于这里是采用GPIO口模拟I2C的方式来实现,故可以不受I2C引脚的限制,能直接插入开发板的Arduino接口上来直接使用。  
图1 显示效果 在上图的连接方式中,该显示屏与开发板的连接关系为: SDA-- PA1 SCL-- PA2  
图2  所用Arduino接口 为使定义的引脚输出高低电平,所定义的语句为: #define CLK_PIN    NU_GET_PININDEX(NU_PA, 2) #define DIN_PIN    NU_GET_PININDEX(NU_PA, 1)  
#define OLED_SCLK_Clr()  rt_pin_write(CLK_PIN, PIN_LOW) #define OLED_SCLK_Set()  rt_pin_write(CLK_PIN, PIN_HIGH) #define OLED_SDIN_Clr()  rt_pin_write(DIN_PIN, PIN_LOW) #define OLED_SDIN_Set()  rt_pin_write(DIN_PIN, PIN_HIGH) 相应的OLED屏初始化函数为: - void OLED_Init(void)
 
  
- {
 
  
-     OLED_SCLK_Set();
 
  
-     OLED_SDIN_Set();
 
  
-     rt_thread_mdelay(800);
 
  
-     OLED_WR_Byte(0xAE,OLED_CMD);
 
  
-     OLED_WR_Byte(0x02,OLED_CMD);
 
  
-     OLED_WR_Byte(0x10,OLED_CMD);
 
  
-     OLED_WR_Byte(0x40,OLED_CMD);
 
  
-     OLED_WR_Byte(0x81,OLED_CMD);
 
  
-     OLED_WR_Byte(0xff,OLED_CMD);
 
  
-     OLED_WR_Byte(0xA1,OLED_CMD);
 
  
-     OLED_WR_Byte(0xC8,OLED_CMD);
 
  
-     OLED_WR_Byte(0xA6,OLED_CMD);
 
  
-     OLED_WR_Byte(0xA8,OLED_CMD);
 
  
-     OLED_WR_Byte(0x3f,OLED_CMD);
 
  
-     OLED_WR_Byte(0xD3,OLED_CMD);
 
  
-     OLED_WR_Byte(0x00,OLED_CMD);
 
  
-     OLED_WR_Byte(0xd5,OLED_CMD);
 
  
-     OLED_WR_Byte(0x80,OLED_CMD);
 
  
-     OLED_WR_Byte(0xD9,OLED_CMD);
 
  
-     OLED_WR_Byte(0xF1,OLED_CMD);
 
  
-     OLED_WR_Byte(0xDA,OLED_CMD);
 
  
-     OLED_WR_Byte(0x12,OLED_CMD);
 
  
-     OLED_WR_Byte(0xDB,OLED_CMD);
 
  
-     OLED_WR_Byte(0x40,OLED_CMD);
 
  
-     OLED_WR_Byte(0x20,OLED_CMD);
 
  
-     OLED_WR_Byte(0x02,OLED_CMD);
 
  
-     OLED_WR_Byte(0x8D,OLED_CMD);
 
  
-     OLED_WR_Byte(0x14,OLED_CMD);
 
  
-     OLED_WR_Byte(0xA4,OLED_CMD);
 
  
-     OLED_WR_Byte(0xA6,OLED_CMD);
 
  
-     OLED_WR_Byte(0xAF,OLED_CMD);
 
  
-     OLED_WR_Byte(0xAF,OLED_CMD);
 
  
- }
 
  
以I/O口模拟I2C方式发送字节数据的函数为: - void Write_IIC_Byte(unsigned char IIC_Byte)
 
  
- {
 
  
-     unsigned char i;
 
  
-     unsigned char m,da;
 
  
-     da=IIC_Byte;
 
  
-     OLED_SCLK_Clr();
 
  
-     for(i=0;i<8;i++)
 
  
-     {
 
  
-         m=da;
 
  
-         m=m&0x80;
 
  
-         if(m==0x80)
 
  
-         {OLED_SDIN_Set();}
 
  
-         else OLED_SDIN_Clr();
 
  
-         rt_hw_us_delay(2);
 
  
-             da=da<<1;
 
  
-         OLED_SCLK_Set();
 
  
-         rt_hw_us_delay(2);
 
  
-         OLED_SCLK_Clr();
 
  
-         rt_hw_us_delay(2);
 
  
-         }
 
  
- }
 
  
OLED屏用于清屏的函数为: - void OLED_Clear(void)
 
  
- {
 
  
-     uint8_t i,n;
 
  
-     for(i=0;i<8;i++)
 
  
-     {
 
  
-         OLED_WR_Byte(0xb0+i,OLED_CMD);
 
  
-         OLED_WR_Byte(0x00,OLED_CMD);
 
  
-         OLED_WR_Byte(0x10,OLED_CMD);
 
  
-         for(n=0;n<128;n++) OLED_WR_Byte(0,OLED_DATA);
 
  
-     }
 
  
- }
 
  
使OLED屏显示字符的函数为: - void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size)
 
  
- {
 
  
-         unsigned char c=0,i=0;
 
  
-         c=chr-' ';
 
  
-         if(x>Max_Column-1)
 
  
-         {
 
  
-             x=0;
 
  
-             y=y+2;
 
  
-         }
 
  
-         if(Char_Size ==16)
 
  
-         {
 
  
-             OLED_Set_Pos(x,y);
 
  
-             for(i=0;i<8;i++) OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
 
  
-             OLED_Set_Pos(x,y+1);
 
  
-             for(i=0;i<8;i++) OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
 
  
-          }
 
  
-          else
 
  
-          {
 
  
-                 OLED_Set_Pos(x,y);
 
  
-                 for(i=0;i<6;i++) OLED_WR_Byte(F6x8[c][i],OLED_DATA);
 
  
-          }
 
  
- }
 
  
使OLED屏显示字符串的函数为: - void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t Char_Size)
 
  
- {
 
  
-     unsigned char j=0;
 
  
-     while (chr[j]!='\0')
 
  
-     {
 
  
-             OLED_ShowChar(x,y,chr[j],Char_Size);
 
  
-             x+=8;
 
  
-             if(x>120)
 
  
-             {
 
  
-                 x=0;
 
  
-                 y+=2;
 
  
-             }
 
  
-             j++;
 
  
-     }
 
  
- }
 
  
实现图示效果的主程序为: - void main(void)
 
  
- {
 
  
-     rt_pin_mode(CLK_PIN, PIN_MODE_OUTPUT);
 
  
-     rt_pin_mode(DIN_PIN, PIN_MODE_OUTPUT);
 
  
-     OLED_Init();
 
  
-     OLED_Clear();
 
  
-     OLED_ShowString(20,0,"OLED TEST",16);
 
  
-     OLED_ShowString(20,2,"NuMarker-",16);
 
  
-     OLED_ShowString(36,4,"-PFM-M487",16);
 
  
-     while (1);
 
  
- }
 
  
经编译处理,其结果如图3所示。  
图3 编译结果  
2. SPI_OLED屏 相较于前面介绍的双色OLED屏,基于SPI接口的彩色OLED屏更具色彩表现力,为此它出了用于信息显示外,其更主要的用途则是显示图像信息,其显示效果如图4所示。 该显示屏的分辨率为160*80像素点,支持16位色彩显示。  
图4 显示效果 在上图的连接方式中,本可以直接将显示屏直接插入Arduino接口来使用,但在实际测试是却发现,连接CS引脚的PA4却无法提供片选控制的低电平信号,故只能由GND来为其提供低电平,故该显示屏与开发板的连接关系为: VCC---3.3V GND---GND SCL---PA2 SDA--- PA1 RES--- PA0 DC--- PA3 CS--- GND BLK--- PA5 使相关引脚输出高低电平的语句定义为: #defineOLED_SCLK_Set()   rt_pin_write(SCL,PIN_HIGH)  //CLK #defineOLED_SCLK_Clr()   rt_pin_write(SCL,PIN_LOW) #defineOLED_SDIN_Set()   rt_pin_write(SDA,PIN_HIGH)  //DIN #defineOLED_SDIN_Clr()   rt_pin_write(SDA,PIN_LOW) #defineOLED_RST_Set()    rt_pin_write(RES,PIN_HIGH)  //RES #defineOLED_RST_Clr()    rt_pin_write(RES,PIN_LOW) #defineOLED_DC_Set()     rt_pin_write(DC,PIN_HIGH)  //DC #defineOLED_DC_Clr()     rt_pin_write(DC,PIN_LOW) #defineOLED_BLK_Set()    rt_pin_write(BLK,PIN_HIGH);  //BLK  
该显示屏的初始化函数为:  
 
以指定色彩清除显示屏的函数为: - void LCD_Clear(u16 Color)
 
  
- {
 
  
-     u16 i,j;
 
  
-     LCD_Address_Set(0,0,LCD_W-1,LCD_H-1);
 
  
-     for(i=0;i<LCD_W;i++)
 
  
-     {
 
  
-             for (j=0;j<LCD_H;j++)
 
  
-             {
 
  
-                     LCD_WR_DATA(Color);
 
  
-             }
 
  
-       }
 
  
- }
 
  
显示满屏图片显示的函数为: - void LCD_ShowPicture(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
 
  
- {
 
  
-       uint16_t i;
 
  
-       LCD_Address_Set(x1,y1,x2,y2);
 
  
-       for(i=0;i<12800;i++)
 
  
-       {
 
  
-             LCD_WR_DATA8(gImage_RW[i*2]);
 
  
-             LCD_WR_DATA8(gImage_RW[i*2+1]);
 
  
-       }
 
  
- }
 
  
其中的数值gImage_RW[],用于存放显示的图片数据。 实现图示效果的主程序为: - void main(void)
 
  
- {
 
  
-     uint8_t i;
 
  
-     rt_pin_mode(SCL_PIN, PIN_MODE_OUTPUT);
 
  
-     rt_pin_mode(SDA_PIN, PIN_MODE_OUTPUT);
 
  
-     rt_pin_mode(RES_PIN, PIN_MODE_OUTPUT);
 
  
-     rt_pin_mode(DC_PIN, PIN_MODE_OUTPUT);
 
  
-     rt_pin_mode(CS_PIN, PIN_MODE_OUTPUT);
 
  
-     rt_pin_mode(BLK_PIN, PIN_MODE_OUTPUT);
 
  
-     Lcd_Init();
 
  
-     LCD_Clear(BLACK);
 
  
-     LCD_ShowPicture(0,0,159,79);
 
  
-     while (1);
 
  
- }
 
  
为自己需要的图片内容,可使用相应的图片处理工具将图片的内容转换为相应格式的文件,图5所示的就是这样一种处理工具。用它可生成一个C语言数组,并以*.h为文件后缀加以保存。 这样就可以同前面给出的图像显示函数LCD_ShowPicture()相匹配,若显示的图片规格小于屏幕的尺寸,还可指定图片的显示位置。当然,若图片的规格小于屏幕的尺寸则需要适当地调整循环的次数,以匹配图片的字节数。  
图5 数据文件生成工具  
此外,在配备文字显示函数的情况下,可完成图6所示的信息显示。  
图6 位置显示效果  
其中用于字符及字符串显示的函数分别为: - void LCD_ShowChar(u16 x,u16 y,u8 num,u8 mode,u16 color)
 
  
- {
 
  
-     u8 temp;
 
  
-     u8 pos,t;
 
  
-     u16 x0=x;
 
  
-     if(x>LCD_W-16||y>LCD_H-16) return;
 
  
-     num=num-' ';
 
  
-     LCD_Address_Set(x,y,x+8-1,y+16-1);
 
  
-     if(!mode)
 
  
-     {
 
  
-         for(pos=0;pos<16;pos++)
 
  
-         {
 
  
-             temp=asc2_1608[(u16)num*16+pos];
 
  
-             for(t=0;t<8;t++)
 
  
-           {
 
  
-                 if(temp&0x01)LCD_WR_DATA(color);
 
  
-                 else LCD_WR_DATA(BACK_COLOR);
 
  
-                 temp>>=1;
 
  
-                 x++;
 
  
-           }
 
  
-             x=x0;
 
  
-             y++;
 
  
-         }
 
  
-     }
 
  
-     else
 
  
-     {
 
  
-         for(pos=0;pos<16;pos++)
 
  
-         {
 
  
-             temp=asc2_1608[(u16)num*16+pos];
 
  
-             for(t=0;t<8;t++)
 
  
-             {
 
  
-                 if(temp&0x01)LCD_DrawPoint(x+t,y+pos,color);
 
  
-                 temp>>=1;
 
  
-             }
 
  
-         }
 
  
-     }
 
  
- }
 
  
-  
 
  
- void LCD_ShowString(u16 x,u16 y,const u8 *p,u16 color)
 
  
- {
 
  
-     while(*p!='\0')
 
  
-     {
 
  
-         if(x>LCD_W-16){x=0;y+=16;}
 
  
-         if(y>LCD_H-16){y=x=0;LCD_Clear(RED);}
 
  
-         LCD_ShowChar(x,y,*p,0,color);
 
  
-         x+=8;
 
  
-         p++;
 
  
-     }
 
  
- }
 
  
中文显示函数为: - void LCD_ShowChinese(uint16_t x,uint16_t y,uint8_t index,uint8_t size,uint16_t color)
 
  
- {
 
  
-     uint8_t i,j;
 
  
-     uint8_t *temp,size1;
 
  
-     if(size==16)temp=Hzk16;
 
  
-     if(size==32)temp=Hzk32;
 
  
-     LCD_Address_Set(x,y,x+size-1,y+size-1);
 
  
-     size1=size*size/8;
 
  
-     temp+=index*size1;
 
  
-     for(j=0;j<size1;j++)
 
  
-     {
 
  
-         for(i=0;i<8;i++)
 
  
-         {
 
  
-             if((*temp&(1<<i))!=0)
 
  
-             {
 
  
-                 LCD_WR_DATA(color);
 
  
-             }
 
  
-             else
 
  
-             {
 
  
-                 LCD_WR_DATA(BACK_COLOR);
 
  
-             }
 
  
-         }
 
  
-         temp++;
 
  
-      }
 
  
- }
 
  
所用的16和32点阵的字模数组格式为: u8 Hzk16[]={ 0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0xFC,0x1F,0x84,0x10,0x84,0x10,0x84,0x10, 0x84,0x10,0x84,0x10,0xFC,0x1F,0x84,0x10,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,/*"中",0*/ ... } u8 Hzk32[]={  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x01,0x00, 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00, 0x10,0x80,0x01,0x0C,0xF0,0xFF,0xFF,0x0F,0x30,0x80,0x01,0x04,0x30,0x80,0x01,0x04, 0x30,0x80,0x01,0x04,0x30,0x80,0x01,0x04,0x30,0x80,0x01,0x04,0x30,0x80,0x01,0x04, 0x30,0x80,0x01,0x04,0x30,0x80,0x01,0x04,0xF0,0xFF,0xFF,0x07,0x30,0x80,0x01,0x04, 0x30,0x80,0x01,0x04,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00, 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00, 0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,/*"中",0*/ ... }  
对应于图6显示效果的主程序为: - void main(void)
 
  
- {
 
  
-     rt_pin_mode(SCL_PIN, PIN_MODE_OUTPUT);
 
  
-     rt_pin_mode(SDA_PIN, PIN_MODE_OUTPUT);
 
  
-     rt_pin_mode(RES_PIN, PIN_MODE_OUTPUT);
 
  
-     rt_pin_mode(DC_PIN, PIN_MODE_OUTPUT);
 
  
-     rt_pin_mode(CS_PIN, PIN_MODE_OUTPUT);
 
  
-     rt_pin_mode(BLK_PIN, PIN_MODE_OUTPUT);
 
  
-     Lcd_Init();
 
  
-     LCD_Clear(BLACK);
 
  
-     LCD_ShowString(0,0, "NuMaker-PFM-M487",YELLOW);
 
  
-     LCD_ShowString(20,20,"0.96'   OLED",YELLOW);
 
  
-     LCD_ShowChinese(20,60,0,16,RED);
 
  
-     LCD_ShowChinese(40,60,1,16,RED);
 
  
-     LCD_ShowChinese(60,60,3,16,RED);
 
  
-     LCD_ShowChinese(80,60,4,16,RED);
 
  
-     while (1);
 
  
- }
 
  
 
除了OLED屏,还有其他的显示器类型,如TFT显示屏、LCD1602、LCD12864及LCD5110等,后面再陆续与大家分享其驱动的方法。 3. 软件包的使用 前面曾说过使用RT_Thread的优点之一就是可以用软件包来扩展对外设的使用,对于OLED屏在软件包中也是通过支持的。 那该如何使用它呢? 其具体步骤如下: 1)创建一个基于M487开发板的项目 2)点击
 
 
3)点击
 
 
,从中选取ssd1306,见图7所示。  
图7 选取器件  
图8 相关介绍 4)选取
 
 
下的“配置项”,再由“软件包”选项卡,按图9加以设置。  
图9 选项设置 5)选取“硬件”选项卡,并按图10进行设置。然后,加以全部保存”。  
图10选项设置 6)完成主程序的编写 主程序的内容如下: - #include <stdio.h>
 
  
- #include <stdlib.h>
 
  
- #include <rtthread.h>
 
  
- #include <rtdevice.h>
 
  
- #include <board.h>
 
  
- #include <string.h>
 
  
- #include "ssd1306.h"
 
  
- int main(int argc, char **argv)
 
  
- {
 
  
-     ssd1306_TestAll();
 
  
-     return 0;
 
  
- }
 
  
-  
 
  
- void ssd1306_TestAll()
 
  
- {
 
  
-     ssd1306_Init();
 
  
-     ssd1306_TestFPS();
 
  
-     rt_thread_mdelay(3000);
 
  
-     ssd1306_TestBorder();
 
  
-     ssd1306_TestFonts();
 
  
-     rt_thread_mdelay(3000);
 
  
-     ssd1306_Fill(Black);
 
  
-     ssd1306_TestRectangle();
 
  
-     ssd1306_TestLine();
 
  
-     rt_thread_mdelay(3000);
 
  
-     ssd1306_Fill(Black);
 
  
-     ssd1306_TestPolyline();
 
  
-     rt_thread_mdelay(3000);
 
  
-     ssd1306_Fill(Black);
 
  
-     ssd1306_TestArc();
 
  
-     rt_thread_mdelay(3000);
 
  
-     ssd1306_Fill(Black);
 
  
-     ssd1306_TestCircle();
 
  
-     rt_thread_mdelay(3000);
 
  
- }
 
  
7)完成程序的编译,以生成目标文件rtthread.bin。 8)连接OLED屏,该显示屏与开发板的连接关系如图11所示。  
图11 引脚连接 9)完成程序下载,其运行效果如图12和图13所示。  
图12 显示字符  
图13显示图形 视频效果: NuMaker--PFM--M487驱动OLED屏 https://www.bilibili.com/video/BV1j94y1o79Y/  
 
 
 
 
 
 
 
 
 
 
 
 
 
  |