一、OLED的基礎介紹
OLED的定義和優勢
OLED,即有機發光二極管(Organic Light-Emitting Diode),又稱為有機電激光顯示(Organic Electroluminesence Display, OELD)。OLED由於同時具備自發光,不需背光源、對比度高、厚度薄、視角廣、反應速度快、可用於撓曲性面板、使用溫度范圍廣、構造及制程較簡單等優異之特性,被認為是下一代的平面顯示器新興應用技術。OLED顯示技術具有自發光的特性,采用非常薄的有機材料塗層和玻璃基板,當有電流通過時,這些有機材料就會發光,而且OLED顯示屏幕可視角度大,並且能夠節省電能。
二、ALINETEK的0.96寸OLED模塊
1.模塊有單色和雙色兩種可選,單色為純藍色,而雙色則為黃藍雙色。單色模塊每個像素點只有亮與不亮兩種情況,沒有顏色區分;
2.尺寸小,顯示尺寸為0.96寸,而模塊尺寸為 27mm(長)*26mm(寬)*4mm(高);
3.高分辨率,該模塊的分辨率為128*64;
4.多種接口方式,該模塊提供了總共4種接口。包括:6800、8080兩種並行接口方式、 4線的串行SPI接口方式、IIC接口方式;
5.不需要高壓,直接接3.3V就可以工作了。帶字庫,可顯示標准的國標簡體(GB2312)漢字、8*16 點 ASCII 粗體字庫、7*8點 ASCII 字庫、5*7 點 ASCII 字庫。
6.這里要提醒大家的是,有的模塊不和5.0V接口兼容,所以請大家在使用的時候一定要小心,別直接接到5V的系統上去,否則可能燒壞模塊。以上4種模式通過模塊的BS0~2設置,BS0~2的設置與模塊接口模式的關系如表所示:

OLED模塊接口方式設置表(表中:“1”代表接VCC,而“0”代表接GND。)
三、OLED模塊實物圖與原理圖
模塊采用8*2的2.54排針與外部連接,總共有16個管腳,在16條線中,我們只用了15條,有一個是懸空的,除掉電源和地線占了2條,還剩下13條信號線。在8080模式下,需要全部13條,而在IIC模式下,僅需要2條線就夠了。這其中有一條是共同的,那就是復位線RST(RES),RST上的低電平,將導致OLED復位,在每次初始化之前,都應該復位一下OLED模塊。下面是OLED模塊的具體實物圖:


ALIENTEK OLED模塊默認設置的是BS0接GND,BS1和BS2接VCC ,即使用8080並口方式,如果你想要設置為其他模式,則需要在OLED的背面,用烙鐵修改BS0~2的設置。
一種是8080的並口方式,另外一種是4線SPI方式。
首先我們介紹一下模塊的8080並行接口,8080並行接口的發明者是INTEL,該總線也被廣泛應用於各類液晶顯示器,ALIENTEK OLED模塊也提供了這種接口,使得MCU可以快速的訪問OLED。ALIENTEK OLED模塊的8080接口方式需要如下一些信號線:
CS:OLED片選信號。
WR:向OLED寫入數據。
RD:從OLED讀取數據。
D[7:0]:8位雙向數據線。
RST(RES):硬復位OLED。
DC:命令/數據標志(0,讀寫命令;1,讀寫數據)。

圖1.1 OLED的8080並行接口方式原理圖 圖1.2 OLED的4線SPI接口方式
四、OLED8080並口讀寫過程
模塊的8080並口讀/寫的過程為:
1、將數據放到數據口;
2、根據要寫入/讀取的數據的類型,設置DC(RS)為高(數據)/低(命令);
3、拉低片選,選中SSD1306;
4、接着我們根據是讀數據,還是要寫數據置RD/WR為低;
5、讀數據過程:在RD的上升沿, 使數據鎖存到數據線(D[7:0])上;
6、寫數據過程:在WR的上升沿,使數據寫入到SSD1306里面;
7、拉高CS和DC(RS)。
SSD1306的8080並口寫時序圖如圖1.3所示: SSD1306的8080並口讀時序圖如圖1.4所示


圖1.3 8080並口寫時序圖 圖1.4 8080並口讀時序圖

在8080方式下讀數據操作的時候,我們有時候(例如讀顯存的時候)需要一個假讀命(Dummy Read),以使得微控制器的操作頻率和顯存的操作頻率相匹配。在讀取真正的數據之前,由一個的假讀的過程。這里的假讀,其實就是第一個讀到的字節丟棄不要,從第二個開始,才是我們真正要讀的數據。一個典型的讀顯存的時序圖,如圖1.5所示:

圖1.5 讀顯存時序圖
可以看到,在發送了列地址之后,開始讀數據,第一個是Dummy Read,也就是假讀,我們從第二個開始,才算是真正有效的數據。
五、OLED模塊顯存已經顯示原理
OLED本身是沒有顯存的,它的顯存是依賴於SSD1306提供的(之后講解的TFTLCD是本身自帶顯存,利用FSMC來進行控制)。而SSD1306提供一塊顯存,芯片具體的講解見下文。SSD1306的顯存總共為128*64bit大小,SSD1306將這些顯存分為了8頁。每頁包含了128個字節,總共8頁,這樣剛好是128*64的點陣大小。

程序顯示原理
在STM32的內部建立一個緩存(共128*8個字節),在每次修改的時候,只是修改STM32上的緩存(實際上就是SRAM),在修改完了之后,一次性把STM32上的緩存數據寫入到OLED的GRAM。
SSD1306芯片簡介
SSD1306是一個單片CMOS、OLED/PLED驅動芯片可以驅動有機/聚合發光二極管點陣圖形顯示系統。由128 segments 和64 Commons組成。該芯片專為共陰極OLED面板設計。
SSD1306中嵌入了對比度控制器、顯示RAM和晶振,並因此減少了外部器件和功耗。有256級亮度控制。數據/命令的發送有三種接口可選擇:6800/8000串口,I2C接口或SPI接口。適用於多數簡介的應用,注入移動電話的屏顯,MP3播放器和計算器等。
六、GPIO模擬SPI
1.引腳定義
GND 地
VCC 電源,3.3V~5V
D0 4 線 ISP 接口模式:時鍾線(CLK) PA4
D1 4 線 ISP 接口模式:串行數據線(MOSI)PA3
RES 4 線 ISP 接口模式:命令/數據標志位(RET復位)PA2
DC 命令/數據標志位 A1
CS OLED 片選
2.時序圖

模塊只支持向模塊寫數據不能讀數據,所以只需要寫SPI發送即可
七、SSD1306的命令
1.SSD1306顯存為128*64bit大小, SSD1306將全部顯存分為8頁,每頁128字節

2.ssd1306常用命令

第一個命令為0X81,用於設置對比度的,這個命令包含了兩個字節,第一個0X81為命令,隨后發送的一個字節為要設置的對比度的值。這個值設置得越大屏幕就越亮。
第二個命令為0XAE/0XAF。0XAE為關閉顯示命令;0XAF為開啟顯示命令。
第三個命令為0X8D,該指令也包含2個字節,第一個為命令字,第二個為設置值,第二個字節的BIT2表示電荷泵的開關狀態,該位為1,則開啟電荷泵,為0則關閉。在模塊初始化的時候,這個必須要開啟,否則是看不到屏幕顯示的。
第四個命令為0XB0~B7,該命令用於設置頁地址,其低三位的值對應着GRAM的頁地址。發送一個字節,高5位固定。位0-位2, X[2:0]共3位, 值范圍0-7對應頁0-7地址(共8頁)
第五個指令為0X00~0X0F,該指令用於設置顯示時的起始列地址低四位。高四位固定,低四位設置列地址起始低四位。
第六個指令為0X10~0X1F,該指令用於設置顯示時的起始列地址高四位。高四位固定,低四位設置列地址起始高四位。
其他命令,我們就不在這里一一介紹了,大家可以參考SSD1306 datasheet的第28頁。從這頁開始,對SSD1306的指令有詳細的介紹。
最后,我們再來介紹一下OLED模塊的初始化過程,SSD1306的典型初始化參考下面初始化OLED模塊程序步驟。

八、STM32控制程序編寫
- 設置STM32與OLED模塊相連接的IO設置為輸出;
- 初始化OLED模塊(步驟:硬復位SSD1306RST=0 延時10ms RST=1、驅動IC初始化程序建議復位所有寄存器、開啟顯示、清零顯存、開始顯示);
- 通過函數將字符和數字顯示到OLED模塊上。
"SPI.h"
#define OLED_CMD 0 //命令聲明
#define OLED_DATA 1 //數據聲明
#define OLED_CLK PAout(4) // CLK時鍾 d0
#define OLED_MOSI PAout(3) // MOSI d1
#define OLED_RST PAout(2) // RET復位 ret
#define OLED_DC PAout(1) // 命令|數據 dc (0表傳輸命令1表傳輸數據)
void OLED_SPI_Init(void); //配置MCU的SPI
void SPI_WriteByte(uint8_t addr,uint8_t data); //向寄存器地址寫一個byte的數據
void WriteCmd(unsigned char cmd); //寫命令
void WriteDat(unsigned char data); //寫數據
"SPI.c"
/*************************************************************************/ /*函數功能: GPIO模擬SPI端口初始化 */ /*************************************************************************/ void OLED_SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);//使能PA端口時鍾
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //端口配置 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽輸出 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO口速度為50MHz GPIO_Init(GPIOA,&GPIO_InitStructure);//根據設定參數初始化GPIOA } /*************************************************************************/ /*函數功能: 通過SPIO軟件模擬SPI通信協議,向模塊(SSD1306)寫入一個字節 */ /*入口參數: */ /* data:要寫入的數據/命令 */ /* cmd :數據/命令標志 0,表示命令;1,表示數據; */ /*************************************************************************/ void SPI_WriteByte(unsigned char data,unsigned char cmd) { unsigned char i=0; OLED_DC =cmd; OLED_CLK=0; //OLED_CS = 0 for(i=0;i<8;i++) { OLED_CLK=0; if(data&0x80)OLED_MOSI=1; //從高位到低位 else OLED_MOSI=0; OLED_CLK=1; data<<=1; } OLED_CLK=1; //OLED_CS = 1 OLED_DC=1; } /*************************************************************************/ /*函數功能: 寫命令 */ /*************************************************************************/ void WriteCmd(unsigned char cmd) { SPI_WriteByte(cmd,OLED_CMD); } /*************************************************************************/ /*函數功能: 寫數據 */ /*************************************************************************/ void WriteData(unsigned char data) { SPI_WriteByte(data,OLED_DATA); }
代碼注意事項,關於片選引腳問題OLED_CS = 0/1;在硬件上已經解決,軟件就不需要。
"OLED.h"
void OLED_Init(void);//初始化OLED
void OLED_ON(void);//喚醒OLED
void OLED_OFF(void);//OLED休眠
void OLED_Refresh_Gram(void);//更新顯存到OLED
void OLED_Clear(void);//清屏
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);//顯示2個數字
void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size);//顯示字符串
"OLED.c"
//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]; //更新顯存函數當中使用WriteData(OLED_GRAM[n][i]); 完成液晶刷新
//此部分GRAM對應OLED模塊上的GRAM。在操作的時候,我們只需要修改STM32內部的GRAM,然后通過OLED_Refresh_Gram()函數將GRAM一次性刷新到OLED的GRAM中。此函數的具體內容先設置頁地址,然后寫入列地址,然后從0開始寫入128個字節,這樣就將一頁的內容刷新過去。重復8次,將8頁的內容全部刷新過去。
/*************************************************************************/
/*函數功能: 軟延時 */
/*************************************************************************/
void OLED_DLY_ms(unsigned int ms)
{
unsigned int a;
while(ms)
{
a=1335;
while(a--);
ms--;
}
}
/*************************************************************************/
/*函數功能: 初始化OLED模塊 */
/*************************************************************************/
void OLED_Init(void)
{
OLED_SPI_Init();
OLED_CLK = 1;
OLED_RST = 0;
OLED_DLY_ms(100);
OLED_RST = 1;
//從上電到下面開始初始化要有足夠的時間,即等待RC復位完畢
WriteCmd(0xAE); // Display Off (0x00)//關閉顯示
WriteCmd(0xD5); //設置時鍾分頻因子,振盪頻率
WriteCmd(0x80); // Set Clock as 100 Frames/Sec //[3:0],分頻因子;[7:4],震盪頻率
WriteCmd(0xA8); //設置驅動路數
WriteCmd(0x3F); // 1/64 Duty (0x0F~0x3F) //默認0X3F(1/64)
WriteCmd(0xD3); //設置顯示偏移
WriteCmd(0x00); // Shift Mapping RAM Counter (0x00~0x3F) //默認為0
WriteCmd(0x40 | 0x00); // Set Mapping RAM Display Start Line (0x00~0x3F) //設置顯示開始行 [5:0],行數.
WriteCmd(0x8D); //電荷泵設置, //bit2,開啟/關閉
WriteCmd(0x10 | 0x04); // Enable Embedded DC/DC Converter (0x00/0x04)
WriteCmd(0x20); //設置內存尋址模式,//最后兩位[1:0],列地址模式;01,行地址模式;10,頁地址模式;默認10
WriteCmd(0x02); // Set Page Addressing Mode (0x00/0x01/0x02)確定使用頁尋址模式
WriteCmd(0xA0 | 0x01); // Set SEG/Column Mapping //段重定義設置,bit0:0,0->0;1,0->127;
WriteCmd(0xC0); // Set COM/x Scan Direction //設置COM掃描方向;bit3:0,普通模式;1,重定義模式 COM[N-1]->COM0;N:驅動路數
WriteCmd(0xDA); //設置COM硬件引腳配置
WriteCmd(0x02 | 0x10); // Set Sequential Configuration (0x00/0x10) //[5:4]配置
WriteCmd(0x81); //對比度設置
WriteCmd(0xCF); // Set SEG Output Current //1~255;默認0X7F (亮度設置,越大越亮)
WriteCmd(0xD9); //設置預充電周期
WriteCmd(0xF1); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock //[3:0],PHASE 1;[7:4],PHASE 2;
WriteCmd(0xDB); //設置VCOMH 電壓倍率
WriteCmd(0x40); // Set VCOM Deselect Level //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
WriteCmd(0xA4 | 0x00); // Disable Entire Display On (0x00/0x01) //全局顯示開啟;bit0:1,開啟;0,關閉;(白屏/黑屏)
WriteCmd(0xA6 | 0x00); // Disable Inverse Display On (0x00/0x01) //設置顯示方式;bit0:1,反相顯示;0,正常顯示
WriteCmd(0xAE | 0x01); // Display On (0x01) //開啟顯示
OLED_Clear(); //初始清屏
}
/*************************************************************************/
/*函數功能: 將OLED從休眠中喚醒 */
/*************************************************************************/
void OLED_ON(void)
{
WriteCmd(0X8D); //設置電荷泵
WriteCmd(0X14); //開啟電荷泵
WriteCmd(0XAF); //OLED喚醒
}
/*************************************************************************/
/*函數功能: 將OLED休眠 -- 休眠模式下,OLED功耗不到10uA */
/*************************************************************************/
void OLED_OFF(void)
{
WriteCmd(0X8D); //設置電荷泵
WriteCmd(0X10); //關閉電荷泵
WriteCmd(0XAE); //OLED休眠
}
/*************************************************************************/
/*函數功能: 更新顯存到OLED */
/*************************************************************************/
void OLED_Refresh_Gram(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
WriteCmd(0xb0+i); //設置頁地址(0~7)
WriteCmd(0x00); //設置顯示位置—列低地址
WriteCmd(0x10); //設置顯示位置—列高地址
for(n=0;n<128;n++)WriteData(OLED_GRAM[n][i]);
}
}
/*************************************************************************/
/*函數功能: 清屏 */
/*************************************************************************/
void OLED_Clear(void)
{
u8 i,n;
for(i=0;i<8;i++)for(n=0;n<128;n++)OLED_GRAM[n][i]=0X00;
OLED_Refresh_Gram();//更新顯示
}
/*************************************************************************/
/*函數功能: 畫點 */
/*入口參數: */
/* x:橫坐標 0~127 */
/* y:縱坐標 0~63 */
/* dot:0,清空;1,填充 */
/*************************************************************************/
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;
}
/*************************************************************************/
/*函數功能: 填充(已知兩點坐標繪畫線) */
/*入口參數: */
/* 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();//更新顯示
}
/*************************************************************************/
/*函數功能: 在指定位置顯示一個字符,包括部分字符 */
/*入口參數: */
/* x:0~12 */
/* y:0~63 */
/* mode:0,反白顯示;1,正常顯示 */
/* size:選擇字體 24/16/12 */
/*************************************************************************/
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++)
{
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)OLED_DrawPoint(x,y,mode);
else OLED_DrawPoint(x,y,!mode);
temp<<=1;
y++;
if((y-y0)==size)
{
y=y0;
x++;
break;
}
}
}
}
/*************************************************************************/
/*
顯示一個漢字
x,y:起點坐標
num :字庫中第幾個漢字
size:字體大小
mode:模式
*/
/*************************************************************************/
void OLED_ShowGBK(u8 x, u8 y, u8 num, u8 size,u8 mode)
{
u8 temp,t,t1;
u8 y0=y;
//u8 size = 16;
u8 csize=(size/8 + ((size%8)?1:0)) * size; //得到字體一個字符對應點陣集所占的字節數
for(t=0;t<csize;t++)
{
// 我只定義了16,12號字體 沒有聲明其他字體
if(size==12) temp = gbk_1212[num][t]; //調用1212字體
else if(size==16) temp = gbk_1616[num][t]; //調用1616字體
// 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;
}
}
}
}
//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);
}
}
/*************************************************************************/
/*函數功能: 顯示字符串 */
/*入口參數: */
/* x,y:起點坐標 */
/* size:字體大小 */
/* *p:字符串起始地址 */
/*************************************************************************/
void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size)
{
while((*p<='~')&&(*p>=' '))//判斷是不是非法字符!
{
if(x>(128-(size/2))){x=0;y+=size;}
if(y>(64-size)){y=x=0;OLED_Clear();}
OLED_ShowChar(x,y,*p,size,1);
x+=size/2;
p++;
}
}
STM32控制程序分析
OLED_Refresh_Gram()函數:更新顯存到OLED。 在STM32內部定義了一個塊GRAM: u8 OLED_GRAM[128][8];
此部分GRAM對應OLED模塊上的GRAM。在操作的時候,我們只需要修改STM32內部的GRAM,然后通過OLED_Refresh_Gram()函數將GRAM一次性刷新到OLED的GRAM中。
void OLED_Refresh_Gram(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
WriteCmd(0xb0+i); //設置頁地址(0~7)
WriteCmd(0x00); //設置顯示位置—列低地址
WriteCmd(0x10); //設置顯示位置—列高地址
for(n=0;n<128;n++)WriteData(OLED_GRAM[n][i]);
}
}
函數的具體內容先設置頁地址,然后寫入列地址,然后從0開始寫入128個字節,這樣就將一頁的內容刷新過去。重復8次,將8頁的內容全部刷新過去。
OLED_WR_Byte()函數:向SSD1306寫入數據或命令(參數cmd為1時表示數據,為0時表示命令)。
如果#define OLED_MODE 0 代表使用8080並行接口 ,如果 #define OLED_MODE 1代表使用4線SPI接口。
如果是8080並行接口,向SSD1306寫入一個字節,在OLED_WR_Byte(u8 dat,u8 cmd)函數當中,需要首先DATAOUT(dat)函數將數據放到數據口。其中DATAOUT()是一個宏定義:#define DATAOUT(x) GPIO_Write(GPIOC,x);
如果是4線SPI串行接口,向SSD1306寫入一個字節,OLED_WR_Byte(u8 dat,u8 cmd)函數當中就能實現8次移位傳輸,在判斷cmd參數是命令還是數據,如果是命令,DC置高;如果是數據,DC置低。接下來,拉低片選,將WR拉低再拉高產生一個上升沿。這樣數據就寫入到了控制器。最后,拉高片選、DC。
OLED_DrawPoint()函數:畫點函數,這里有一個對應關系需要理解。
OLED_GRAM[128][8]中的128代表列數(x坐標),而8代表的是頁,每頁又包含8行,總共是64行(y坐標)。從高到低對應行數從小到大。比如,我們要在x=100,y=29這個點寫入1,則可以用這個句子實現:
OLED_GRAM[100][4]=1<<2;
一個通用的點(x,y)置1的表達式為:
OLED_GRAM[x][7-y/8]=1<<(7-y%8);
其中,x的取值范圍為0-127;y的取值范圍為0-63。
OLED_ShowChar()函數:顯示字符。這里的字符采用16*8的顯示方式,也就是說在OLED上16*8數目大小的點陣表示一個字符,即128個點。
下面截取了一部分16*8的字符庫的內容,一個字符用16個u8類型的數字表示:(取模方式設置:陰碼+逐列式+順向+C51格式)
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" " ,0*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xCC,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
{0x00,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x08,0x00,0x30,0x00,0x60,0x00,0x00,0x00},/*""",2*/
{0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x03,0xFC,0x1E,0x20,0x02,0x20,0x00,0x00},/*"#",3*/
{0x00,0x00,0x0E,0x18,0x11,0x04,0x3F,0xFF,0x10,0x84,0x0C,0x78,0x00,0x00,0x00,0x00},/*"$",4*/
{0x0F,0x00,0x10,0x84,0x0F,0x38,0x00,0xC0,0x07,0x78,0x18,0x84,0x00,0x78,0x00,0x00},/*"%",5*/
{0x00,0x78,0x0F,0x84,0x10,0xC4,0x11,0x24,0x0E,0x98,0x00,0xE4,0x00,0x84,0x00,0x08},/*"&",6*/
取模方式


取模方式:從上到下,從左到右,高位在前。就是這樣的取模方式,將字符集按照16*8的大小取模出來。1表示亮,0表示暗。
具體的顯示方式如下圖所示:


顯示字符函數的具體實現:
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;
}
這里也是按照從上到下,從左到右的取模方式來進行的。先得到最高位,然后判斷是寫1還是0,畫點;接着讀第二位,如此循環,直到一個字符的點陣全部取完為止。這里涉及到的列地址和行地址的自增,不難理解。
建立中文字庫(以16號字體為例)
推薦使用PCtoLCD2002.exe漢字取模(取模方式已給出)
//16x16 漢字點陣
//每個漢字占32個字節
//PC2LCD2002取模方式設置:陰碼+逐列式+順向+C51格式 //注意中景園官方范例(陰碼+列行式+逆向+C51格式)
//每個漢字所占用的字節數為:(size/8+((size%8)?1:0))*(size),
//其中size:是字庫生成時的點陣大小(12/16/24...)
const unsigned char gbk_1616[4][32]={ //16x16
{0x08,0x20,0x08,0x22,0x08,0x41,0xFF,0xFE,
0x08,0x80,0x0A,0x41,0x22,0x41,0x2A,0x52,
0xA6,0x6A,0x63,0xC4,0x22,0x44,0x26,0x4A,
0x2A,0x72,0x22,0x41,0x02,0x40,0x00,0x00},/*"接",0*/
{0x00,0x00,0x3F,0xF0,0x20,0x20,0x20,0x20,
0x3F,0xF0,0x00,0x01,0x00,0x06,0x3F,0xF8,
0x22,0x00,0x22,0x00,0x22,0x00,0x43,0xFF,
0xC2,0x00,0x42,0x00,0x02,0x00,0x00,0x00},/*"聽",1*/
{0x08,0x20,0x08,0x22,0x08,0x41,0xFF,0xFE,
0x08,0x80,0x08,0x00,0x02,0x02,0x22,0x22,
0x22,0x22,0x22,0x22,0xFE,0xFE,0x22,0x22,
0x22,0x22,0x22,0x22,0x02,0x02,0x00,0x00},/*"掛",2*/
{0x00,0x00,0x3F,0xFC,0x04,0x44,0x24,0x84,
0x15,0x04,0xFF,0xF4,0x15,0x04,0x24,0xC5,
0x00,0x06,0x3F,0xF8,0x22,0x00,0x22,0x00,
0x23,0xFF,0x42,0x00,0x02,0x00,0x00,0x00},/*"斷",3*/
};
