【微控制器筆記】OLED控制器SSD1306及驅動程式碼
阿新 • • 發佈:2019-02-20
前言:
很長一段時間內用OLED的機會都很少,即使用了也是參考的大牛的程式碼,最近迫不得已又仔細研究了一遍。記下來省的忘記。
手頭上用到的是0.61寸的OLED解析度是96*16,也就是橫向有96個點,縱向有16個點,貼上這個的尺寸圖
我並沒有看太多的資料,網上的資料也非常多,大多數是將SSD1306的,這裡就簡單通俗的記錄一下,拿這個96*16的為例子,SSD把縱向(y方向)做了分頁處理,每8個畫素點作為一組(8個畫素點=8個bit=一個位元組),那麼對於y方向有16個點的那就是分成兩頁了,從0開始就是0和1。對於其他解析度的也一樣,向上加就對了。在驅動的時候最常規的方法是從左到右,從上到下。值得注意的是不同型號可能取模的方式不一樣,有點高位在前也有的低位在前,我現在用的是低位在前,這個手冊裡面還是有說的。下面附上取模的軟體和配置選項:
軟體用的是:PCtoLCD2002
取模配置如下:
效果圖:
最後附上詳細的程式碼(理論上支援所有的SSD1306控制器)我在12832也試過沒有問題,需要自行修改巨集定義,程式碼都有詳細的中文註釋:
#include "fy_oled12832.h" #include "fy_oledfont.h" #define OLED_IIC_SCL_PIN GPIO_PIN_4 #define OLED_IIC_SCL_L() GPIO_WriteLow(OLED_IIC_SCL_PORT,OLED_IIC_SCL_PIN) //SCLIIC介面的時鐘訊號 #define OLED_IIC_SCL_H() GPIO_WriteHigh(OLED_IIC_SCL_PORT,OLED_IIC_SCL_PIN)//PB4 #define OLED_IIC_SDA_PORT GPIOB #define OLED_IIC_SDA_PIN GPIO_PIN_5 #define OLED_IIC_SDA_L() GPIO_WriteLow(OLED_IIC_SDA_PORT,OLED_IIC_SDA_PIN) //SDA IIC介面的資料訊號 #define OLED_IIC_SDA_H() GPIO_WriteHigh(OLED_IIC_SDA_PORT,OLED_IIC_SDA_PIN)//PB5 /* 96*16畫素 x[0-95] y[0-15] 注意:y方向分成了兩頁 每頁8個畫素點 剛好是一個位元組 */ #define Max_x 96 #define Max_y 2 #define OLED_IIC_SCL_PORT GPIOB #define OLED_CMD 0X00 //寫命令 #define OLED_DATA 0X40 //寫資料 static void Oled_IIC_Configuration(void) { GPIO_Init(OLED_IIC_SCL_PORT,OLED_IIC_SCL_PIN,GPIO_MODE_OUT_PP_LOW_FAST);//設定PB4為推輓輸出 GPIO_Init(OLED_IIC_SDA_PORT,OLED_IIC_SDA_PIN,GPIO_MODE_OUT_PP_LOW_FAST);//設定PB5為推輓輸出 } //IIC Start static void Oled_IIC_Start(void) { OLED_IIC_SCL_H(); OLED_IIC_SDA_H(); OLED_IIC_SDA_L(); OLED_IIC_SCL_L(); } //IIC Stop static void Oled_IIC_Stop(void) { OLED_IIC_SCL_L(); OLED_IIC_SDA_L(); OLED_IIC_SDA_H(); OLED_IIC_SCL_H() ; } // IIC Send byte static void Oled_IIC_SendByte(u8 dat) { u8 i; for(i=0;i<8;i++) { OLED_IIC_SCL_L(); if(dat & 0x80) OLED_IIC_SDA_H(); else OLED_IIC_SDA_L(); dat<<=1; OLED_IIC_SCL_H(); } OLED_IIC_SCL_L(); OLED_IIC_SCL_H(); } //OLED寫一個位元組 //dat:寫入的位元組 //cmd:命令或者資料 void Oled_WriteByte(u8 dat,u8 cmd) { Oled_IIC_Start(); Oled_IIC_SendByte(0x78); Oled_IIC_SendByte(cmd); Oled_IIC_SendByte(dat); Oled_IIC_Stop(); } //座標設定 void Oled_SetPos(u8 x, u8 y) { Oled_WriteByte(0xb0+y,OLED_CMD); Oled_WriteByte(((x&0xf0)>>4)|0x10,OLED_CMD); Oled_WriteByte((x&0x0f),OLED_CMD); } //清屏 void Oled_Clear(void) { u8 i,n; for(i=0;i<Max_y;i++) //有多少個頁 { Oled_WriteByte (0xb0+i,OLED_CMD); //設定頁地址(0~1) Oled_WriteByte (0x00,OLED_CMD); //設定顯示位置—列低地址 Oled_WriteByte (0x10,OLED_CMD); //設定顯示位置—列高地址 for(n=0;n<Max_x;n++) Oled_WriteByte(0,OLED_DATA); //每頁有多少列 } //更新顯示 } //在指定位置顯示一個字元 void Oled_ShowChar(u8 x,u8 y,u8 chr) { u8 c=0,i=0; c=chr-' ';//得到偏移後的值 if(x>Max_x-1) { x=0; y++; if(y>Max_y-1) return; }//這裡的y代表的頁地址 6*8的字元所以頁加一 Oled_SetPos(x,y); for(i=0;i<6;i++)//6*8的字元 點陣佔6列沒列8個畫素點(一個位元組),也就是一頁的6列 Oled_WriteByte(F6x8[c][i],OLED_DATA); } //顯示一個字元號串 void Oled_ShowString(u8 x,u8 y,u8 *chr) { u8 j=0; if(y>Max_y-1) return; while (chr[j]!='\0') { Oled_ShowChar(x,y,chr[j]); x+=6;//6*8的字元 點陣佔6列所以加6 if(x>Max_x-6){x=0;y++;} j++; } } //顯示漢字 16*16的漢字 void Oled_ShowCHinese(u8 x,u8 y,u8 no) { u8 t; Oled_SetPos(x,y); for(t=0;t<16;t++)//先寫第一頁 { Oled_WriteByte(Hzk[2*no][t],OLED_DATA); } Oled_SetPos(x,y+1); for(t=0;t<16;t++)//再寫第二頁 { Oled_WriteByte(Hzk[2*no+1][t],OLED_DATA); } } //初始化SSD1306 void Oled_Configuration(void) { Oled_IIC_Configuration(); Delay_ms(100); Oled_WriteByte(0xAE,OLED_CMD);//--display off Oled_WriteByte(0x00,OLED_CMD);//---set low column address Oled_WriteByte(0x10,OLED_CMD);//---set high column address Oled_WriteByte(0x40,OLED_CMD);//--set start line address Oled_WriteByte(0xB0,OLED_CMD);//--set page address Oled_WriteByte(0x81,OLED_CMD); // contract control Oled_WriteByte(0xFF,OLED_CMD);//--128 Oled_WriteByte(0xA1,OLED_CMD);//set segment remap Oled_WriteByte(0xA6,OLED_CMD);//--normal / reverse Oled_WriteByte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64) Oled_WriteByte(0x0F,OLED_CMD);//--1/16 duty Oled_WriteByte(0xC8,OLED_CMD);//Com scan direction Oled_WriteByte(0xD3,OLED_CMD);//-set display offset Oled_WriteByte(0x00,OLED_CMD);// Oled_WriteByte(0xD5,OLED_CMD);//set osc division Oled_WriteByte(0x80,OLED_CMD);// Oled_WriteByte(0xD9,OLED_CMD);//set pre-charge period Oled_WriteByte(0x22,OLED_CMD);// Oled_WriteByte(0xDA,OLED_CMD);//set COM pins Oled_WriteByte(0x02,OLED_CMD);// Oled_WriteByte(0xDB,OLED_CMD);//set vcomh Oled_WriteByte(0x40,OLED_CMD);// Oled_WriteByte(0x8D,OLED_CMD);//set charge pump enable Oled_WriteByte(0x14,OLED_CMD);// Oled_WriteByte(0xAF,OLED_CMD);//--turn on oled panel } void Oled_TEST(void) { Delay_ms(200); //延時200ms Oled_Configuration(); //OLED初始化 Delay_ms(200); //延時200ms Oled_Clear(); //清屏 Delay_ms(200); //延時200ms while (1) { Oled_Clear();//清屏 Oled_ShowString(0,0,"012345678ABCDEFGQWERTYUIOO");//顯示數字 Delay_ms(2000); Oled_Clear(); //清屏 Oled_ShowCHinese(8,0,0); //顯示漢字“火” Oled_ShowCHinese(24,0,1); //顯示漢字“星” Oled_ShowCHinese(40,0,2); //顯示漢字“科” Oled_ShowCHinese(56,0,3); //顯示漢字“技” Delay_ms(2000); } }
字型檔:
#ifndef __FY_OLEDFONT_H #define __FY_OLEDFONT_H const unsigned char F6x8[][6] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // sp { 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00 }, // ! { 0x00, 0x00, 0x07, 0x00, 0x07, 0x00 }, // " { 0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14 }, // # { 0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12 }, // $ { 0x00, 0x62, 0x64, 0x08, 0x13, 0x23 }, // % { 0x00, 0x36, 0x49, 0x55, 0x22, 0x50 }, // & { 0x00, 0x00, 0x05, 0x03, 0x00, 0x00 }, // ' { 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00 }, // ( { 0x00, 0x00, 0x41, 0x22, 0x1c, 0x00 }, // ) { 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14 }, // * { 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08 }, // + { 0x00, 0x00, 0x00, 0xA0, 0x60, 0x00 }, // , { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, // - { 0x00, 0x00, 0x60, 0x60, 0x00, 0x00 }, // . { 0x00, 0x20, 0x10, 0x08, 0x04, 0x02 }, // / { 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E }, // 0 { 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00 }, // 1 { 0x00, 0x42, 0x61, 0x51, 0x49, 0x46 }, // 2 { 0x00, 0x21, 0x41, 0x45, 0x4B, 0x31 }, // 3 { 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10 }, // 4 { 0x00, 0x27, 0x45, 0x45, 0x45, 0x39 }, // 5 { 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30 }, // 6 { 0x00, 0x01, 0x71, 0x09, 0x05, 0x03 }, // 7 { 0x00, 0x36, 0x49, 0x49, 0x49, 0x36 }, // 8 { 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E }, // 9 { 0x00, 0x00, 0x36, 0x36, 0x00, 0x00 }, // : { 0x00, 0x00, 0x56, 0x36, 0x00, 0x00 }, // ; { 0x08, 0x1C, 0x2A, 0x49, 0x08, 0x08 }, // < { 0x00, 0x14, 0x14, 0x14, 0x14, 0x14 }, // = { 0x08, 0x08, 0x49, 0x2A, 0x1C, 0x08 }, // > { 0x00, 0x02, 0x01, 0x51, 0x09, 0x06 }, // ? { 0x00, 0x32, 0x49, 0x59, 0x51, 0x3E }, // @ { 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C }, // A { 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36 }, // B { 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22 }, // C { 0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C }, // D { 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41 }, // E { 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01 }, // F { 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A }, // G { 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F }, // H { 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00 }, // I { 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01 }, // J { 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41 }, // K { 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40 }, // L { 0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F }, // M { 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F }, // N { 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E }, // O { 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06 }, // P { 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E }, // Q { 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46 }, // R { 0x00, 0x46, 0x49, 0x49, 0x49, 0x31 }, // S { 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01 }, // T { 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F }, // U { 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F }, // V { 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F }, // W { 0x00, 0x63, 0x14, 0x08, 0x14, 0x63 }, // X { 0x00, 0x07, 0x08, 0x70, 0x08, 0x07 }, // Y { 0x00, 0x61, 0x51, 0x49, 0x45, 0x43 }, // Z { 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00 }, // [ { 0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55 }, // 55 { 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00 }, // ] { 0x04, 0x02, 0xFF, 0x02, 0x04, 0x00 }, // ^ { 0x00, 0x20, 0x40, 0xFF, 0x40, 0x20 }, // _ { 0x00, 0x00, 0x01, 0x02, 0x04, 0x00 }, // ' { 0x00, 0x20, 0x54, 0x54, 0x54, 0x78 }, // a { 0x00, 0x7F, 0x48, 0x44, 0x44, 0x38 }, // b { 0x00, 0x38, 0x44, 0x44, 0x44, 0x20 }, // c { 0x00, 0x38, 0x44, 0x44, 0x48, 0x7F }, // d { 0x00, 0x38, 0x54, 0x54, 0x54, 0x18 }, // e { 0x00, 0x08, 0x7E, 0x09, 0x01, 0x02 }, // f { 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C }, // g { 0x00, 0x7F, 0x08, 0x04, 0x04, 0x78 }, // h { 0x00, 0x00, 0x44, 0x7D, 0x40, 0x00 }, // i { 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00 }, // j { 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00 }, // k { 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00 }, // l { 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78 }, // m { 0x00, 0x7C, 0x08, 0x04, 0x04, 0x78 }, // n { 0x00, 0x38, 0x44, 0x44, 0x44, 0x38 }, // o { 0x00, 0xFC, 0x24, 0x24, 0x24, 0x18 }, // p { 0x00, 0x18, 0x24, 0x24, 0x18, 0xFC }, // q { 0x00, 0x7C, 0x08, 0x04, 0x04, 0x08 }, // r { 0x00, 0x48, 0x54, 0x54, 0x54, 0x20 }, // s { 0x00, 0x04, 0x3F, 0x44, 0x40, 0x20 }, // t { 0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C }, // u { 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C }, // v { 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C }, // w { 0x00, 0x44, 0x28, 0x10, 0x28, 0x44 }, // x { 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C }, // y { 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44 }, // z { 0x14, 0x14, 0x14, 0x14, 0x14, 0x14 } // horiz lines }; char Hzk[][32]={ {0x00,0x00,0xC0,0x38,0x00,0x00,0x00,0xFF,0x00,0x00,0x40,0x20,0x18,0x00,0x00,0x00}, {0x80,0x81,0x40,0x20,0x10,0x0C,0x03,0x00,0x03,0x0C,0x10,0x20,0x40,0x80,0x80,0x00},/*"火",0*/ {0x00,0x00,0x00,0xBE,0x2A,0x2A,0x2A,0xEA,0x2A,0x2A,0x2A,0x3E,0x00,0x00,0x00,0x00}, {0x00,0x44,0x42,0x49,0x49,0x49,0x49,0x7F,0x49,0x49,0x49,0x49,0x41,0x40,0x00,0x00},/*"星",1*/ {0x24,0x24,0xA4,0xFE,0xA3,0x22,0x00,0x22,0xCC,0x00,0x00,0xFF,0x00,0x00,0x00,0x00}, {0x08,0x06,0x01,0xFF,0x00,0x01,0x04,0x04,0x04,0x04,0x04,0xFF,0x02,0x02,0x02,0x00},/*"科",2*/ {0x10,0x10,0x10,0xFF,0x10,0x90,0x08,0x88,0x88,0x88,0xFF,0x88,0x88,0x88,0x08,0x00}, {0x04,0x44,0x82,0x7F,0x01,0x80,0x80,0x40,0x43,0x2C,0x10,0x28,0x46,0x81,0x80,0x00},/*"技",3*/ }; #endif /*********************************************END OF FILE**********************************************/
測試例程:
void Oled_TEST(void)
{
Delay_ms(200); //延時200ms
Oled_Configuration(); //OLED初始化
Delay_ms(200); //延時200ms
Oled_Clear(); //清屏
Delay_ms(200); //延時200ms
while (1)
{
Oled_Clear();//清屏
Oled_ShowString(0,0,"012345678ABCDEFGQWERTYUIOO");//顯示字元
Delay_ms(2000);
Oled_Clear(); //清屏
Oled_ShowCHinese(8,0,0); //顯示漢字“火”
Oled_ShowCHinese(24,0,1); //顯示漢字“星”
Oled_ShowCHinese(40,0,2); //顯示漢字“科”
Oled_ShowCHinese(56,0,3); //顯示漢字“技”
Delay_ms(2000);
}
}
至此完畢!
By Urien 2017年11月12日 13:30:22