STM32學習--OLED
OLED
1. 簡述
現在單色點陣的小顯示模組的使用場影逐漸變少,但做為紅極一時的顯示應用還是有學習的意義。我們以正點原子的OLED模組為物件開展學習。
單色點陣的顯示模組主要有5種介面方式:並行的有8080和6800,序列的主要是I2C和三線、四線的SPI。一般的驅動晶片均支援這些模式,但具體晶片可能有差別。
這裡我們主要聊8080和四線的SPI,因為正點原子有相應的例項,易於理解,其實其他的思考方式也差不多。
先了解一下正點原子的OLED模組,它使用SSD1306驅動晶片。晶片內部GRAM組成為8頁,每頁126位元組,對應128X64點陣,所以我們顯示時只要在對應的點上寫0或1來控制對應點是否點亮。
其有很多控制指令,一般應用下列指令就OK,如果有其他使用參見其手冊。
第一個命令為0X81,用於設定對比度的,這個命令包含了兩個位元組,第一個 0X81 為命令,
隨後傳送的一個位元組為要設定的對比度的值。這個值設定得越大螢幕就越亮。
第二個命令為0XAE/0XAF。0XAE 為關閉顯示命令; 0XAF 為開啟顯示命令。
第三個命令為0X8D,該指令也包含2 個位元組,第一個為命令字,第二個為設定值,第二
個位元組的BIT2 表示電荷泵的開關狀態,該位為 1,則開啟電荷泵,為 0 則關閉。在模組初始化
的時候,這個必須要開啟,否則是看不到螢幕顯示的。
第五個指令為0X00~0X0F,該指令用於設定顯示時的起始列地址低四位。
第六個指令為0X10~0X1F,該指令用於設定顯示時的起始列地址高四位。
操作顯示模組主要就是四種通訊:寫資料、寫指令、讀資料、讀狀態。而寫資料和寫指令僅在DC口的狀態不同,其他時序都一樣所以只用寫一個WR_OLED_BYTE(dat,cmd)函式;同樣讀操作也可以只用RD_OLED_(dat,cmd)函式實現。
而且每次寫入都是按位元組寫入的,這就存在一個問題,如果我們使用只寫方式操作模組,那麼,每次要寫 8 個點,這樣,我們在畫點的時候,就必須把要設定的點所在的位元組的每個位都搞清楚當前的狀態( 0/1?),否則寫入的資料就會覆蓋掉之前的狀態,結果就是有些不需要顯示的點,顯示出來了,或者該顯示的沒有顯示了。這個問題在能讀的模式下,我們可以先讀出來要寫入的那個位元組,得到當前狀況,在修改了要改寫的位之後再寫進 GRAM,這樣就不會影響到之前的狀況了。但是這樣需要能讀GRAM,對於 3 線或 4 線 SPI 模式,模組是不支援讀的,而且讀->改->寫的方式速度也比較慢。所以採用的辦法是在STM32 的內部建立一個 OLED 的 GRAM(共 128 個位元組),在每次修改的時候,只是修改 STM32 上的 GRAM(實際上就是 SRAM),在修改完了之後,一次性把 STM32 上的 GRAM 寫入到 OLED 的 GRAM。當然這個方法也有壞處,就是對於那些 SRAM很小的微控制器(比如 51 系列)就比較麻煩了。
瞭解了大概原理,就開展幹吧。
2. 8080介面
2.1 8080協議簡述
8080 並行介面的發明者是 INTEL,6800發明者是摩托羅拉,兩者應用都很廣。
8080有如下介面:
CS:OLED 片選訊號。
WR:向OLED 寫入資料。
RD:從OLED 讀取資料。
D[7:0]: 8 位雙向資料線。
RST(RES):硬復位OLED
DC:命令/資料標誌(0,讀寫命令;1,讀寫資料)。
其實現四種基本操作時,狀態表如下。
2.2 硬體介面
如圖所示為OLED的介面,在MCU端需11個GPIO口進行介面,D[7:0]最好是連續的8個埠,易於操作,但不是必需。
2.3 讀寫操作
對SSD1306的寫時序圖如下,需注意的是在WR上升沿時其他口狀態需穩定,在此時刻驅動晶片會讀資料線上資料。
寫過程:①寫D[7:0]口資料;②拉低片選,選中SSD1306;③置WR為低RD為高;④根據寫的資料或指令置DC為高或低;⑤拉高WR電平;⑥恢復片選為高,DC置為高。
在寫之前應把IO初始化為推輓輸出。
程式設計實現如下,省略IO初始化過程:
#define DATAOUT(DataValue){GPIO_Write(GPIOB,(GPIO_ReadOutputData(GPIOB)&0xff00)|(DataValue&0x00FF));}
void OLED_WR_Byte(u8 dat,u8 cmd)
{
DATAOUT(dat); //①
OLED_RS=cmd; // ④
OLED_CS=0; //②
OLED_WR=0; //③
OLED_WR=1; //⑤
OLED_CS=1;
OLED_RS=1; //⑥
}
讀過程
對SSD1306的讀時序圖如下,需注意的是在WR上升沿時其他口狀態穩定時進行讀取,在此時刻驅動晶片資料準備完成。
讀過程:①拉低片選,選中SSD1306;②置WR為高RD為低;③根據讀的資料或狀態置DC為高或低;④讀取D[7:0]上資料;⑤拉高RD電平;⑥恢復片選為高,DC置為高。
在寫之前應把IO初始化為上位輸入。
在8080 方式下讀資料操作的時候,我們有時候(例如讀視訊記憶體的時候)需要一個假讀命
(Dummy Read),以使得微控制器的操作頻率和視訊記憶體的操作頻率相匹配。在讀取真正的資料之
前,由一個的假讀的過程。這裡的假讀,其實就是第一個讀到的位元組丟棄不要,從第二個開始,
才是我們真正要讀的資料。
3. SPI四線介面
四線串列埠模式使用的訊號線有如下幾條:
CS:OLED 片選訊號。
RST(RES):硬復位OLED。
DC:命令/資料標誌(0,讀寫命令;1,讀寫資料)。
SCLK:序列時鐘線。在 4 線序列模式下,D0 訊號線作為序列時鐘線 SCLK。
SDIN:序列資料線。在 4 線序列模式下,D1 訊號線作為序列資料線 SDIN。
模組的D2 需要懸空,其他引腳可以接到 GND。在 4 線序列模式下,只能往模組寫資料而
不能讀資料。
在四線SPI 模式下,每個資料長度均為 8 位,在SCLK 的上升沿,資料從 SDIN 移入到
SSD1306,並且是高位在前的。 DC 線還是用作命令/資料的標誌線。
軟體實現:
void OLED_WR_Byte(u8 dat,u8 cmd)
{
u8i;
OLED_RS=cmd;
OLED_CS=0;
for(i=0;i<8;i++)
{
OLED_SCLK=0;
if(dat&0x80)OLED_SDIN=1;
elseOLED_SDIN=0;
OLED_SCLK=1;
dat<<=1;
}
OLED_CS=1;
OLED_RS=1;
}
4. 軟體編寫
前面聊過我們需對應GRAM大小在MCU內部設定相應大小的內部空間。
u8 OLED_GRAM[128][8];
//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
寫GRAM時,一次性更新GRAM,省略讀的時間,以空間換時間。而且SPI不支援讀操作,也增加可移植性。這需要一個重要函式,更新視訊記憶體到LCD。使用巢狀迴圈實現更新操作。
void OLED_Refresh_Gram(void)
{
u8i,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);
}
}
後面要畫點、畫線、寫字元、數字只用寫在內部的OLED_GRAM[128][8]中,然後整個重新整理就可以。在顯示字元時我們先要得到這個字符集的點陣資料,這裡我們介紹一個款很好的字元提取軟體:PCtoLCD2002 完美版。該軟體可以提供各種字元,包括漢字(字型和大小都可以自己設定)陣提取,且取模方式可以設定好幾種,常用的取模方式,該軟體都支援。該軟體還支援圖形模式,也就是使用者可以自己定義圖片的大小,然後畫圖,根據所畫的圖形再生成點陣資料,這功能在製作圖示或圖片的時候很有用。
關於OLED初始化,我們直接使用廠家推薦的設定就可以了,只要對細節部分進行一些
修改,使其滿足我們自己的要求即可,其他不需要變動。