目錄
1.前言
因爲疫情原因,宅在家無聊,翻到了之前的一些STM32的開發板,還有兩塊0.96的OLED屏,於是想着再來折騰下電子,雖然我已經有5年時間沒碰過電子了(畢業後本人就轉行Android和Java開發了),但是還是覺得搞電子比較有趣,回憶從前玩單片機都是拿來主義,很少從代碼層去深究實現原理,很多屏幕的驅動都是抄現成的代碼,有時候不好用了又不知道怎麼辦,所以奉勸學電子的後來人,如果打算在這條路上走下去,還是得搞清楚原理,才能以不變應萬變,成爲真正的大神。而這篇博文呢,只是爲了給自己一個警醒,凡是切勿淺嘗輒止,要深入研究,同時也給驅動OLED同樣有困惑的人一些經驗借鑑。好了,廢話不多說了。
2.準備工作
工慾善其事必先利其器,所以先把需要用到的工具啥的準備好。
需要安裝的軟件 : Keil5(V-5.25.2.0) STM32-CubeMX(V-5.6.0)
需要準備的硬件 : STM32系列開發板(本人使用的是STM32F103C8T6),OLED屏幕(4線SPI),ST-Link燒錄器
軟件下載放上分享鏈接:
鏈接:https://pan.baidu.com/s/1MgRisNjq3rcqYgmM6Jmpiw
提取碼:vkmn
關於環境搭建,大家可以自行百度,我上面分享的也不一定是最新的軟件版本,stm32cubeMX的安裝需要java支持。
例如安裝教程:
STM32CubeMx入門系列教程----安裝教程(適用萌新)
關於KEIL的安裝強調一點:安裝keil的時候,破解軟件運行的時候要使用右鍵以管理員身份運行,同樣keil也要使用右鍵管理員方式運行,然後複製CID破解。如果不是管理員身份運行,破解的時候會出現如下的報錯:
You are not logged in as an "Administrator" !!!!
同樣也給出瞭解決方法 Solution:用管理員賬號登陸或者Run as Administrator(以管理員方式運行)
3.關於OLED驅動,模擬SPI和硬件SPI
本文驅動OLED使用的是4線SPI方式驅動,當然如果你的外設IO口有限,也可以選擇IIC方式驅動。我這裏先就SPI驅動來談論,下面放上兩段代碼對比。
//向SSD1306寫入一個字節。
//dat:要寫入的數據/命令
//cmd:數據/命令標誌 0,表示命令;1,表示數據;
void OLED_WR_Byte(u8 dat ,u8 cmd)
{
if(cmd)
OLED_DC_Set(); // 控制腳拉高,寫數據
else
OLED_DC_Clr(); // 控制腳拉低,寫命令
// 硬件SPI寫法
HAL_SPI_Transmit(&hspi2,&dat,sizeof(dat),1000); // SPI寫數據或者命令
OLED_DC_Set();
}
//向SSD1306寫入一個字節。
//dat:要寫入的數據/命令
//cmd:數據/命令標誌 0,表示命令;1,表示數據;
void OLED_WR_Byte(u8 dat ,u8 cmd)
{
u8 i;
// 根據傳入的CMD,判斷是寫數據還是寫命令
if(cmd)
OLED_DC_Set(); // 控制腳拉高,寫數據
else
OLED_DC_Clr(); // 控制腳拉低,寫命令
// 模擬SPI數據傳輸寫法
OLED_CS_Clr(); // 片選拉低,開始寫數據或命令
for(i=0;i<8;i++) // 一字節有8bit,所以需要循環8次
{
OLED_SCLK_Clr(); // 時鐘置低
dat&0x80 ? OLED_SDIN_Set():OLED_SDIN_Clr(); // 與位判斷,最終數據腳寫0或1就是根據這個
OLED_SCLK_Set(); // 時鐘拉高
dat<<=1; // 數據或命令左移一位
}
OLED_CS_Set(); // 片選拉高,結束寫數據或命令
OLED_DC_Set();
}
上面第一段代碼是硬件SPI的寫法,下面一段是模擬SPI的寫法。可以看到硬件SPI驅動的寫法相較模擬SPI少了很多。我放個圖上來對比下二者的寫法
模擬SPI和硬件SPI這兩部分實現的功能其實是一模一樣的,都是有序的將數據傳輸到OLED驅動芯片SSD1306上。而使用STM32的硬件SPI,我們可以少關注CS拉低開始寫入,開關時鐘SCLK,8bit依次判斷寫0或1,然後CS拉高禁止寫入的整個過程。其實懂了這個之後對於之後移植這個驅動就很簡單了。下面我們對照着代碼來分析下SSD1306數據手冊中的時序圖
對照着手冊再回過頭來看代碼,發現是能夠一一對應的。
4.IIC的驅動OLED的例子
用IIC來驅動OLED相比複雜度更高,因爲只用到了時鐘和數據兩根線,所以代碼上也會複雜一點,我找一段IIC的驅動代碼貼上來給大家品鑑下。
void IIC_Start()
{
OLED_SCLK_Set() ;
OLED_SDIN_Set();
OLED_SDIN_Clr();
OLED_SCLK_Clr();
}
void IIC_Stop()
{
OLED_SCLK_Set() ;
OLED_SDIN_Clr();
OLED_SDIN_Set();
}
void IIC_Wait_Ack()
{
OLED_SCLK_Set() ;
OLED_SCLK_Clr();
}
/**********************************************
// IIC Write byte
**********************************************/
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();
da=da<<1;
OLED_SCLK_Set();
OLED_SCLK_Clr();
}
}
/**********************************************
// IIC Write Command
**********************************************/
void Write_IIC_Command(unsigned char IIC_Command)
{
IIC_Start();
Write_IIC_Byte(0x78); //Slave address,SA0=0
IIC_Wait_Ack();
Write_IIC_Byte(0x00); //write command
IIC_Wait_Ack();
Write_IIC_Byte(IIC_Command);
IIC_Wait_Ack();
IIC_Stop();
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data)
{
IIC_Start();
Write_IIC_Byte(0x78); //D/C#=0; R/W#=0
IIC_Wait_Ack();
Write_IIC_Byte(0x40); //write data
IIC_Wait_Ack();
Write_IIC_Byte(IIC_Data);
IIC_Wait_Ack();
IIC_Stop();
}
/**********************************************
// OLED寫數據或命令 cmd=1:數據,cmd=0:命令
**********************************************/
void OLED_WR_Byte(unsigned dat,unsigned cmd)
{
if(cmd)
Write_IIC_Data(dat);
else
Write_IIC_Command(dat);
}
因爲IIC的通訊方式和SPI的通訊協議的不同,導致二者在驅動OLED的時候有差異,我們主要看下核心函數
void Write_IIC_Byte(unsigned char IIC_Byte),其實這個函數的實現也是數據和時鐘腳在工作,高低交替,8bit數據在一位一位的傳輸。跟SPI驅動所不同的是,IIC寫命令和寫數據不再有DC腳和CS腳來控制,而是全由時鐘腳和數據腳來控制,另外增加了數據傳輸的等待操作和增加了從地址質量操作,所以代碼寫法上相對更復雜些。傳輸速率上,SPI和比IIC的快,對IIC感興趣的同學可以對照着手冊自行研究,我這裏就不深入了。
5.開始驅動OLED,使用STM32-CubeMX生成代碼
關於stm32-cubeMX的操作步驟,我這裏截了長圖,大家可以看下,主要是配置GPIO口和SW下載調試口,時鐘保持默認是哦給你內部時鐘就可以了。
具體需要注意的事項我已經在圖上做了標註,其他有疑問的地方大家可以自行查閱資料。
記得生成的代碼,KEIL打開後,需要先全部rebuild一遍!!!
記得生成的代碼,KEIL打開後,需要先全部rebuild一遍!!!
記得生成的代碼,KEIL打開後,需要先全部rebuild一遍!!!
看下KEIL的注意事項
按要求添加好自己的.h文件和.c文件,我這裏添加兩個文件。然後貼上代碼
下面是 oled.h
/***************************************
** OLED 頭文件
** 作者:菜鳥江濤
** 時間:2020-03-17
** CSDN: https://blog.csdn.net/u010898329
**************************************/
#ifndef __OLED_H
#define __OLED_H
// 由於會使用到GPIO口,所以引入相關的頭文件
#include "main.h"
// 常量定義
#define u8 unsigned char // 將unsigned char 定義簡寫
#define OLED_CMD 1 // 寫命令
#define OLED_DATA 0 // 寫數據
// 端口高低操縱定義
#define OLED_RST_H HAL_GPIO_WritePin(OLED_RST_GPIO_Port,OLED_RST_Pin,GPIO_PIN_SET) // 復位腳高
#define OLED_RST_L HAL_GPIO_WritePin(OLED_RST_GPIO_Port,OLED_RST_Pin,GPIO_PIN_RESET) // 復位腳低
// 對應有些OLED模塊的絲印是 D0
#define OLED_SCK_H HAL_GPIO_WritePin(OLED_SCK_GPIO_Port,OLED_SCK_Pin,GPIO_PIN_SET) // 時鐘腳高
#define OLED_SCK_L HAL_GPIO_WritePin(OLED_SCK_GPIO_Port,OLED_SCK_Pin,GPIO_PIN_RESET) // 時鐘腳低
// 對應有些OLED模塊的絲印是 D1
#define OLED_SDA_H HAL_GPIO_WritePin(OLED_SDA_GPIO_Port,OLED_SDA_Pin,GPIO_PIN_SET) // 數據腳高
#define OLED_SDA_L HAL_GPIO_WritePin(OLED_SDA_GPIO_Port,OLED_SDA_Pin,GPIO_PIN_RESET) // 數據腳低
#define OLED_CS_H HAL_GPIO_WritePin(OLED_CS_GPIO_Port,OLED_CS_Pin,GPIO_PIN_SET) // 片選腳高
#define OLED_CS_L HAL_GPIO_WritePin(OLED_CS_GPIO_Port,OLED_CS_Pin,GPIO_PIN_RESET) // 片選腳低
#define OLED_DC_H HAL_GPIO_WritePin(OLED_DC_GPIO_Port,OLED_DC_Pin,GPIO_PIN_SET) // 控制腳高
#define OLED_DC_L HAL_GPIO_WritePin(OLED_DC_GPIO_Port,OLED_DC_Pin,GPIO_PIN_RESET) // 控制腳低
// 函數定義
// OLED的基礎操作函數
void OLED_Write_Byte(u8 data , u8 cmd);
void OLED_Init(void);
void OLED_On(void);
void OLED_Off(void);
void OLCD_Set_Pos(u8 x, u8 y);
// OLED的顯示操作函數
void OLED_Clear(void);
void OLED_Show_Char(u8 x, u8 y , u8 chr);
#endif
接着是 oled.c
#include "oled.h"
#include "ascii.h"
/****************
** 向OLED寫一個字節的數據或是命令
** cmd: 1=命令,0=數據
** data: 待寫入的字節(命令/數據)
*******************/
void OLED_Write_Byte( u8 data , u8 cmd )
{
u8 i=0;
// 這裏直接使用三目運算符,不使用if/else寫法,看起來簡潔些
cmd ? OLED_DC_L : OLED_DC_H ; // 寫命令DC輸出低,寫數據DC輸出高
// 模擬SPI數據傳輸寫法
OLED_CS_L; // 片選拉低,開始寫數據或命令
for(i=0; i<8; i++) // 一字節有8bit,所以需要循環8次
{
OLED_SCK_L; // 時鐘置低
data&0x80 ? OLED_SDA_H:OLED_SDA_L; // 與位判斷,最終數據腳寫0或1就是根據這個
OLED_SCK_H; // 時鐘拉高
data<<=1; // 數據或命令左移一位
}
OLED_CS_H; // 片選拉高,結束寫數據或命令
OLED_DC_H; // 控制腳拉高,置成寫數據狀態
}
/*******************
** OLED初始化
********************/
void OLED_Init(void)
{
// 製造一個先低後高的電平變換,達到復位的效果
OLED_RST_L;
HAL_Delay(500);
OLED_RST_H;
OLED_Write_Byte(0xAE,OLED_CMD);//--turn off oled panel
OLED_Write_Byte(0x02,OLED_CMD);//---set low column address
OLED_Write_Byte(0x10,OLED_CMD);//---set high column address
OLED_Write_Byte(0x40,OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
OLED_Write_Byte(0x81,OLED_CMD);//--set contrast control register
OLED_Write_Byte(0xff,OLED_CMD);// Set SEG Output Current Brightness
OLED_Write_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常
OLED_Write_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
OLED_Write_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_Write_Byte(0x3f,OLED_CMD);//--1/64 duty
OLED_Write_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
OLED_Write_Byte(0x00,OLED_CMD);//-not offset
OLED_Write_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
OLED_Write_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
OLED_Write_Byte(0xD9,OLED_CMD);//--set pre-charge period
OLED_Write_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_Write_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
OLED_Write_Byte(0x12,OLED_CMD);
OLED_Write_Byte(0xDB,OLED_CMD);//--set vcomh
OLED_Write_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
OLED_Write_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
OLED_Write_Byte(0x02,OLED_CMD);//
OLED_Write_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
OLED_Write_Byte(0x14,OLED_CMD);//--set(0x10) disable
OLED_Write_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
OLED_Write_Byte(0xA6,OLED_CMD);// 開啓正常顯示 (0xA6 = 正常 / 0xA7 = 反顯)
OLED_Write_Byte(0xAF,OLED_CMD);//--turn on oled panel
}
/******************
** 開啓OLED顯示
*******************/
void OLED_On(void)
{
OLED_Write_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_Write_Byte(0X14,OLED_CMD); //DCDC ON
OLED_Write_Byte(0XAF,OLED_CMD); //DISPLAY ON
}
/******************
** 關閉OLED顯示
*******************/
void OLED_Off(void)
{
OLED_Write_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_Write_Byte(0X10,OLED_CMD); //DCDC OFF
OLED_Write_Byte(0XAE,OLED_CMD); //DISPLAY OFF
}
/****************************
** 設置座標點
** x: 行座標 0~127
** y: 頁座標 0~7
*****************************/
void OLCD_Set_Pos(u8 x, u8 y)
{
OLED_Write_Byte(0xb0+y,OLED_CMD);
OLED_Write_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
OLED_Write_Byte((x&0x0f)|0x01,OLED_CMD);
}
/********************
** OLED清屏操作
*********************/
void OLED_Clear(void)
{
u8 i,n;
for(i=0; i<8; i++)
{
OLED_Write_Byte (0xb0+i,OLED_CMD); //設置頁地址(0~7)
OLED_Write_Byte (0x00,OLED_CMD); //設置顯示位置—列低地址
OLED_Write_Byte (0x10,OLED_CMD); //設置顯示位置—列高地址
for(n=0; n<128; n++)OLED_Write_Byte(0,OLED_DATA); // 每一列都置零
}
}
/**************************************
** 顯示一個字符
** x: 列座標位置
** y: 頁座標位置
** chr: 待顯示的字符
***************************************/
void OLED_Show_Char(u8 x, u8 y , u8 chr)
{
u8 i=0 ,c=0;
c = chr - ' '; // 從空字符串算起,得到偏移後的值
OLCD_Set_Pos(x,y); // 設置顯示位置
for(i=0; i<6; i++)
{
OLED_Write_Byte(F6x8[c][i],OLED_DATA);
}
}
還有字體文件 ascii.h
#ifndef __ASCII_H
#define __ASCII_H
//常用ASCII表
//偏移量32
//ASCII字符集
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,// ,
0x00, 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,// ;
0x00, 0x08, 0x14, 0x22, 0x41, 0x00,// <
0x00, 0x14, 0x14, 0x14, 0x14, 0x14,// =
0x00, 0x00, 0x41, 0x22, 0x14, 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,// ]
0x00, 0x04, 0x02, 0x01, 0x02, 0x04,// ^
0x00, 0x40, 0x40, 0x40, 0x40, 0x40,// _
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
};
#endif
字體有三種,後面會完善三種字體的顯示函數,目前的代碼我先僅僅展示如何寫一個 6*8 的字體到 OLED上,主要用到了OLED_Show_Char(u8 x , u8 y , u8 chr); 看下main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "oled.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
OLED_Init();
OLED_Clear();
OLED_Show_Char(0,0,'E');
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
注意我自己代碼放的位置
看下顯示效果,我手頭的是黃藍雙色的屏幕。
至此,屏幕的驅動就基本大功告成了, 後面我們接着學習更爲複雜的顯示函數,其實都是基於這個函數來改造的。
6.常規顯示函數
6.0 字符顯示拓展,顯示三種不同大小的字符
/**************************************
** 顯示一個字符
** x: 列座標位置
** y: 頁座標位置
** chr: 待顯示的字符
***************************************/
void OLED_Show_Char(u8 x, u8 y , u8 chr , enum Font_Size size)
{
u8 i=0 ,c=0;
c = chr - ' '; // 從空字符串算起,得到偏移後的值
switch(size)
{
case SMALL:
OLCD_Set_Pos(x,y); // 設置顯示位置
for(i=0; i< size ; i++)
OLED_Write_Byte(F6x8[c][i],OLED_DATA);
break;
case MEDIA:
OLCD_Set_Pos(x,y); // 設置顯示位置
for(i=0; i< 6 ; i++)
OLED_Write_Byte(F12X6[c][i],OLED_DATA);
OLCD_Set_Pos(x,y+1); // 設置顯示位置
for(i=6; i< 12 ; i++)
OLED_Write_Byte(F12X6[c][i],OLED_DATA);
break;
case BIG:
OLCD_Set_Pos(x,y); // 設置顯示位置
for(i=0; i< 8 ; i++)
OLED_Write_Byte(F16x8[c][i],OLED_DATA);
OLCD_Set_Pos(x,y+1); // 設置顯示位置
for(i=8; i< 16 ; i++)
OLED_Write_Byte(F16x8[c][i],OLED_DATA);
break;
}
}
這裏我準備了三種字體大小,定義了一個字體的枚舉類,使用這個函數的時候需要傳入枚舉值,例如
OLED_Show_Char(0,0,'A',SMALL);
OLED_Show_Char(12,0,'A',MEDIA);
OLED_Show_Char(24,0,'A',BIG);
OLED_Show_String(0,2,"123ABC",SMALL);
OLED_Show_String(0,3,"123ABC",MEDIA);
OLED_Show_String(0,5,"123ABC",BIG);
6.1 字符串顯示函數
/****************************************
** 顯示字符串
** x: 起始列座標
** y: 起始頁
** chr: 待顯示的字符串
*****************************************/
void OLED_Show_String(u8 x, u8 y , u8 *chr)
{
u8 i = 0;
while(chr[i] != '\0') {
OLED_Show_Char(x,y,chr[i]);
x+=6; // 字體寬高 6*8
if(x>120) // 如果大於了極限值 128
{
x = 0 ; // 列從零開始
y += 1; // 另起一頁
}
i ++ ; // 循環繼續,直到字符串都寫完了爲止
}
}
注意,上面顯示的字符串的寬高是6*8 ,而屏幕的像素點是128*64 。 上面有個大於120的判斷,是因爲如果啓示橫座標是120,那麼整個字體顯示完整就到了到 126 , 如果繼續執行就會出現 126+6 = 132 , 超過了最大限制,會出現重疊,所以會以120作爲判斷標準。後面會將此處的設置爲動態判斷,因爲肯定會出現寬度不是6個像素的字體,這個留着到後面分析。
6.2 改造後的字符串顯示函數,支持傳入字體枚舉值
/****************************************
** 顯示字符串
** x: 起始列座標
** y: 起始頁
** chr: 待顯示的字符串
*****************************************/
void OLED_Show_String(u8 x, u8 y , u8 *chr , enum Font_Size size)
{
u8 i = 0;
u8 page = (size>8 ? size >> 3 : 1 ); // 字體大小決定翻幾頁
u8 len = (size>8 ? size >>1 : size ) ; // 字體寬度,最後結果對應了字體表中的每行長度索引
u8 limit = MAX_LEN/len - 1 ;
while(chr[i] != '\0') {
OLED_Show_Char(x,y,chr[i],size);
x+=len; // 字體寬高 6*8
if(x>(len*limit-1)) // 如果大於了極限值 128
{
x = 0 ; // 列從零開始
y += page; // 另起一頁
}
i ++ ; // 循環繼續,直到字符串都寫完了爲止
}
}
這裏有個常量定義,MAX_LEN = 128,定義的是屏幕寬度的最大像素點。
如果字體高度大於8像素,肯定是要翻頁的,我這裏做了判斷,page = size/8 或者 page=1(至少一頁) ,limit爲一行能最多能顯示的字符個數,len*limit -1 就是最後一個字符開始的位置,跟上面的字符顯示函數做了對應了,這裏改成了動態了。
看下三個的顯示效果,我都顯示在了一個屏幕上了
如果大家有其他的字體需要,可以自行去使用取模軟件生成ASCII碼,放上ASCII如下
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
6.2 自定義數字顯示
自定義數字顯示我們需要用到取模軟件,取模軟件一併在分享文件中有。軟件取模時候的配置如下
這裏的取模方式使用 逐列 或者 列行 都可以,只要是右側那個取模演示中是從上往下填充的方式都可以實現。具體是什麼原因呢,還得看SSD1306的數據手冊。
自定義的數字,我這裏使用三種字體大小,分別是 寬x高:12*24,16*32,20*40三種字體,下面貼上我的字體頭文件 number.h
#ifndef __NUMBER_H
#define __NUMBER_H
//寬x高:12*24
const unsigned char N24x12[][12]={
{0x00,0x00,0xC0,0xE0,0x30,0x10,0x10,0x30,0xE0,0xC0,0x00,0x00},
{0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00},
{0x00,0x00,0x03,0x07,0x0C,0x08,0x08,0x0C,0x07,0x03,0x00,0x00},/*"0",0*/
{0x00,0x00,0x20,0x20,0x20,0xE0,0xF0,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x08,0x08,0x08,0x0F,0x0F,0x08,0x08,0x08,0x00,0x00},/*"1",1*/
{0x00,0xC0,0xA0,0x10,0x10,0x10,0x10,0x30,0xE0,0xC0,0x00,0x00},
{0x00,0x01,0x01,0x00,0x80,0x60,0x30,0x18,0x0F,0x03,0x00,0x00},
{0x00,0x0C,0x0E,0x09,0x08,0x08,0x08,0x08,0x08,0x0F,0x00,0x00},/*"2",2*/
{0x00,0xE0,0xE0,0x10,0x10,0x10,0x30,0xE0,0xC0,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x08,0x08,0x0C,0x17,0xF3,0xC0,0x00,0x00},
{0x00,0x07,0x07,0x08,0x08,0x08,0x08,0x0C,0x07,0x03,0x00,0x00},/*"3",3*/
{0x00,0x00,0x00,0x00,0x00,0x80,0x40,0xF0,0xF0,0x00,0x00,0x00},
{0x00,0x60,0x50,0x4C,0x42,0x41,0x40,0xFF,0xFF,0x40,0x40,0x40},
{0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x0F,0x0F,0x08,0x08,0x00},/*"4",4*/
{0x00,0x00,0xF0,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00},
{0x00,0x80,0x9F,0x08,0x04,0x04,0x04,0x0C,0xF8,0xF0,0x00,0x00},
{0x00,0x03,0x05,0x08,0x08,0x08,0x08,0x0E,0x07,0x01,0x00,0x00},/*"5",5*/
{0x00,0x00,0xC0,0xE0,0x20,0x10,0x10,0x10,0x70,0x60,0x00,0x00},
{0x00,0xFE,0xFF,0x10,0x08,0x04,0x04,0x04,0x0C,0xF8,0xF0,0x00},
{0x00,0x00,0x03,0x06,0x0C,0x08,0x08,0x08,0x04,0x07,0x01,0x00},/*"6",6*/
{0x00,0x00,0xE0,0x30,0x10,0x10,0x10,0x10,0x90,0x70,0x30,0x00},
{0x00,0x00,0x00,0x00,0x00,0x80,0xF0,0x0C,0x03,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0x00,0x00,0x00,0x00,0x00},/*"7",7*/
{0x00,0xC0,0xE0,0x30,0x10,0x10,0x10,0x10,0x30,0xE0,0xC0,0x00},
{0x00,0xC1,0xE7,0x36,0x0C,0x08,0x18,0x18,0x34,0xE7,0xC1,0x00},
{0x00,0x03,0x07,0x04,0x08,0x08,0x08,0x08,0x0C,0x07,0x03,0x00},/*"8",8*/
{0x00,0x80,0xE0,0x60,0x10,0x10,0x10,0x10,0x60,0xC0,0x00,0x00},
{0x00,0x0F,0x1F,0x30,0x20,0x20,0x20,0x10,0x88,0xFF,0x7F,0x00},
{0x00,0x00,0x06,0x0E,0x08,0x08,0x08,0x04,0x07,0x01,0x00,0x00},/*"9",9*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x0E,0x0E,0x0E,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x0E,0x0E,0x0E,0x00,0x00,0x00,0x00},/*":",10*/
{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},/*" ",11*/
};
//寬x高:16*32
const unsigned char N32x16[][16]={
{0x00,0x00,0x00,0x80,0xC0,0xE0,0x70,0x30,0x10,0x10,0x70,0xE0,0xC0,0x80,0x00,0x00},
{0x00,0x00,0xFC,0xFF,0xFF,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0xFF,0xFC,0x00},
{0x00,0x00,0x0F,0x7F,0xFF,0xF0,0x80,0x00,0x00,0x00,0x80,0xE0,0xFF,0x7F,0x0F,0x00},
{0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x03,0x02,0x02,0x03,0x01,0x00,0x00,0x00,0x00},/*"0",0*/
{0x00,0x00,0x00,0x40,0x40,0x40,0x40,0xE0,0xF0,0xF0,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x02,0x02,0x02,0x03,0x03,0x03,0x03,0x03,0x02,0x02,0x02,0x00,0x00},/*"1",1*/
{0x00,0x00,0x80,0xC0,0x60,0x30,0x10,0x10,0x10,0x10,0x30,0x70,0xE0,0xE0,0x80,0x00},
{0x00,0x00,0x07,0x07,0x06,0x00,0x00,0x00,0x00,0x80,0xC0,0xF0,0x7F,0x3F,0x0F,0x00},
{0x00,0x00,0x80,0xC0,0x60,0x30,0x1C,0x0E,0x07,0x03,0x01,0x00,0x80,0xF0,0xF0,0x00},
{0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00},/*"2",2*/
{0x00,0x00,0xC0,0xE0,0xE0,0x30,0x10,0x10,0x10,0x30,0x70,0xE0,0xC0,0x80,0x00,0x00},
{0x00,0x00,0x03,0x03,0x03,0x00,0x40,0x40,0x40,0xE0,0xF0,0xBF,0x9F,0x0F,0x00,0x00},
{0x00,0x00,0xF0,0xF0,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x81,0xFF,0xFF,0x3C,0x00},
{0x00,0x00,0x00,0x01,0x01,0x03,0x02,0x02,0x02,0x02,0x03,0x01,0x01,0x00,0x00,0x00},/*"3",3*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xF0,0xF0,0xF0,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x80,0xE0,0x78,0x1C,0x07,0x03,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00},
{0x00,0x0C,0x0F,0x0B,0x09,0x08,0x08,0x08,0x08,0xFF,0xFF,0xFF,0x08,0x08,0x08,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x02,0x03,0x03,0x03,0x02,0x02,0x02,0x00},/*"4",4*/
{0x00,0x00,0x00,0xC0,0xF0,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x00},
{0x00,0x00,0x00,0xFF,0xFF,0x60,0x30,0x10,0x10,0x10,0x30,0x70,0xE0,0xC0,0x00,0x00},
{0x00,0x00,0xF0,0xF0,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xFF,0xFF,0x3F,0x00},
{0x00,0x00,0x00,0x01,0x01,0x03,0x02,0x02,0x02,0x02,0x03,0x01,0x01,0x00,0x00,0x00},/*"5",5*/
{0x00,0x00,0x00,0x00,0xC0,0xE0,0x60,0x30,0x10,0x10,0x10,0xF0,0xE0,0xE0,0x00,0x00},
{0x00,0x00,0xF8,0xFF,0xFF,0xE1,0x30,0x30,0x10,0x10,0x30,0x70,0xE0,0xC0,0x00,0x00},
{0x00,0x00,0x1F,0x7F,0xFF,0xE0,0x80,0x00,0x00,0x00,0x00,0x80,0xFF,0xFF,0x3F,0x00},
{0x00,0x00,0x00,0x00,0x01,0x01,0x03,0x03,0x02,0x02,0x03,0x03,0x01,0x00,0x00,0x00},/*"6",6*/
{0x00,0x00,0x80,0xF0,0xF0,0x30,0x30,0x30,0x30,0x30,0x30,0xB0,0xF0,0x70,0x30,0x00},
{0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0xC0,0xF8,0x3E,0x07,0x01,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"7",7*/
{0x00,0x00,0x80,0xC0,0xE0,0x70,0x30,0x10,0x10,0x10,0x30,0x70,0xE0,0xC0,0x80,0x00},
{0x00,0x00,0x0F,0x9F,0xBF,0xFC,0x70,0x70,0xE0,0xC0,0xE0,0xB0,0x9F,0x1F,0x07,0x00},
{0x00,0x7C,0xFF,0xFF,0x83,0x00,0x00,0x00,0x00,0x01,0x03,0x87,0xFF,0xFF,0x7C,0x00},
{0x00,0x00,0x00,0x01,0x01,0x03,0x02,0x02,0x02,0x02,0x03,0x01,0x01,0x00,0x00,0x00},/*"8",8*/
{0x00,0x00,0xC0,0xE0,0xE0,0x30,0x10,0x10,0x10,0x10,0x30,0xE0,0xC0,0x80,0x00,0x00},
{0x00,0x7F,0xFF,0xFF,0xC0,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xFF,0xFF,0xFE,0x00},
{0x00,0x00,0xC0,0xC1,0xC3,0x03,0x02,0x02,0x02,0x83,0xC1,0xF9,0x7F,0x1F,0x07,0x00},
{0x00,0x00,0x01,0x01,0x03,0x02,0x02,0x02,0x03,0x03,0x01,0x00,0x00,0x00,0x00,0x00},/*"9",9*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x78,0x78,0x78,0x30,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xC0,0xC0,0x80,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x03,0x03,0x01,0x00,0x00,0x00,0x00,0x00},/*":",10*/
{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,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},/*" ",11*/
};
const unsigned char N40x20[][20]={
{0x00,0x00,0x00,0x00,0x80,0xC0,0xE0,0xE0,0x70,0x30,0x10,0x10,0x30,0xE0,0xE0,0xC0,0x80,0x00,0x00,0x00},
{0x00,0x00,0xF0,0xFE,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFE,0xF0,0x00},
{0x00,0x00,0x7F,0xFF,0xFF,0xFF,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x7F,0x00},
{0x00,0x00,0x00,0x03,0x0F,0x1F,0x3F,0x38,0x60,0x60,0x40,0x40,0x60,0x38,0x3F,0x1F,0x0F,0x03,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"0",0*/
{0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0xC0,0xE0,0xF0,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x60,0x7F,0x7F,0x7F,0x7F,0x60,0x40,0x40,0x40,0x40,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"1",1*/
{0x00,0x00,0x00,0xC0,0xC0,0x60,0x20,0x30,0x10,0x10,0x10,0x30,0x30,0xF0,0xE0,0xE0,0xC0,0x00,0x00,0x00},
{0x00,0x00,0x1F,0x1F,0x1F,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xFF,0xFF,0x7F,0x1F,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1E,0x0F,0x07,0x03,0x01,0x00,0x00,0x00,0x00},
{0x00,0x00,0x70,0x78,0x7C,0x6F,0x67,0x63,0x60,0x60,0x60,0x60,0x60,0x60,0x70,0x78,0x7E,0x0E,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"2",2*/
{0x00,0x00,0x80,0xC0,0xE0,0x60,0x30,0x10,0x10,0x10,0x30,0x30,0xF0,0xE0,0xE0,0xC0,0x00,0x00,0x00,0x00},
{0x00,0x00,0x07,0x0F,0x0F,0x0E,0x00,0x00,0x00,0x00,0x80,0x80,0xE0,0xFF,0x7F,0x3F,0x1F,0x00,0x00,0x00},
{0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x01,0x01,0x01,0x03,0x07,0x0E,0xFE,0xFC,0xF8,0xE0,0x00,0x00},
{0x00,0x00,0x0F,0x1F,0x3F,0x37,0x60,0x40,0x40,0x40,0x40,0x60,0x70,0x38,0x3F,0x1F,0x0F,0x03,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"3",3*/
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xE0,0xF0,0xF0,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xE0,0x78,0x1C,0x0F,0x03,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00},
{0x00,0x40,0x60,0x78,0x5C,0x4F,0x43,0x40,0x40,0x40,0x40,0x40,0xFF,0xFF,0xFF,0x40,0x40,0x40,0x40,0x40},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x40,0x7F,0x7F,0x7F,0x60,0x40,0x40,0x40,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"4",4*/
{0x00,0x00,0x00,0x00,0xF0,0xF0,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x00},
{0x00,0x00,0x00,0xF8,0xFF,0x07,0x80,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x80,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x87,0x87,0x87,0x01,0x01,0x00,0x00,0x00,0x00,0x01,0x07,0xFF,0xFF,0xFE,0xF8,0x00,0x00},
{0x00,0x00,0x07,0x1F,0x3F,0x23,0x60,0x40,0x40,0x40,0x40,0x60,0x70,0x3C,0x3F,0x1F,0x0F,0x03,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"5",5*/
{0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xE0,0x60,0x30,0x10,0x10,0x10,0x10,0xF0,0xF0,0xE0,0xC0,0x00,0x00},
{0x00,0x00,0xE0,0xFC,0xFF,0xFF,0x07,0x80,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC1,0x81,0x81,0x01,0x00,0x00},
{0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x07,0x03,0x01,0x00,0x00,0x00,0x01,0x01,0x07,0xFF,0xFF,0xFE,0xF8,0x00},
{0x00,0x00,0x00,0x07,0x0F,0x1F,0x3E,0x38,0x60,0x60,0x40,0x40,0x40,0x60,0x38,0x3F,0x1F,0x0F,0x03,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"6",6*/
{0x00,0x00,0x00,0x80,0xF0,0xF0,0x70,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0xF0,0xF0,0x70,0x30,0x00},
{0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xF0,0x3C,0x0F,0x07,0x01,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFC,0xFF,0x0F,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x7F,0x7F,0x7F,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},/*"7",7*/
{0x00,0x00,0x00,0x00,0xC0,0xE0,0xE0,0xF0,0x30,0x10,0x10,0x10,0x10,0x30,0xF0,0xE0,0xE0,0xC0,0x00,0x00},
{0x00,0x00,0x00,0x1F,0x7F,0xFF,0xFF,0xF0,0xE0,0xC0,0x80,0x80,0x00,0x80,0xE0,0xFF,0x7F,0x3F,0x1F,0x00},
{0x00,0x00,0xE0,0xF0,0xF8,0xFC,0x1E,0x07,0x03,0x03,0x07,0x07,0x0F,0x1F,0x3F,0xFC,0xFC,0xF0,0xE0,0x00},
{0x00,0x00,0x07,0x0F,0x1F,0x3F,0x38,0x60,0x60,0x40,0x40,0x40,0x40,0x60,0x38,0x3F,0x1F,0x0F,0x07,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"8",8*/
{0x00,0x00,0x00,0x80,0xC0,0xE0,0xE0,0x70,0x30,0x10,0x10,0x10,0x30,0x70,0xE0,0xC0,0x80,0x00,0x00,0x00},
{0x00,0x00,0xFE,0xFF,0xFF,0xFF,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x81,0xFF,0xFF,0xFF,0xF8,0x00},
{0x00,0x00,0x01,0x07,0x0F,0x0F,0x1F,0x1C,0x1C,0x18,0x18,0x18,0x0C,0x0E,0xC7,0xFF,0xFF,0xFF,0x1F,0x00},
{0x00,0x00,0x00,0x1C,0x3C,0x7C,0x7C,0x40,0x40,0x40,0x60,0x60,0x38,0x3C,0x1F,0x0F,0x07,0x01,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"9",9*/
{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,0xC0,0xE0,0xE0,0xE0,0xE0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x03,0x03,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x7C,0x7C,0x7C,0x7C,0x38,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},/*":",10*/
{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,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,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},/*" ",11*/
};
#endif
這個是用取模軟件自己生成的,大家也可以自己動手生成自己的一份。看下數字顯示函數。
/*********************************************************
** 自定義數字顯示函數
** x: 列起始位置 0~(127-len) 取值從0到最後一個字符的起始位置
** y: 頁起始位置 0~7
** num: 待顯示的數字 0~9
** size: 字體大小枚舉值
*********************************************************/
void OLED_Show_Num(u8 x, u8 y , u8 num , enum Num_Size size)
{
u8 i,j ;
u8 len = size >> 1 ; // 字體寬度,這裏直接處以2,因爲字體寬度正好是高度的一半
u8 page = size >> 3 ; // 這裏處以8直接用移位操作,高度大於8就要翻頁
for(i = 0 ; i < page ; i ++)
{
OLCD_Set_Pos(x,y+i); // 設置顯示位置
switch(size)
{
case SMALL_NUM:
for(j=0; j<len; j++)
OLED_Write_Byte(N24x12[num*page + i][j],OLED_DATA); // 寫數據
break;
case MEDIA_NUM:
for(j=0; j<len; j++)
OLED_Write_Byte(N32x16[num*page + i][j],OLED_DATA); // 寫數據
break;
case BIG_NUM:
for(j=0; j<len; j++)
OLED_Write_Byte(N40x20[num*page + i][j],OLED_DATA); // 寫數據
break;
}
}
}
下面是oled.h中的枚舉定義。
// 定義字體枚舉,值爲字體的高度
enum Font_Size {SMALL = 6 , MEDIA = 12 , BIG = 16};
// 定義數字枚舉,值爲數字的高度
enum Num_Size {SMALL_NUM = 24 , MEDIA_NUM = 32 , BIG_NUM = 40};
看下自定義數字的顯示效果
如果想要其他風格或者大小不同的字體,大家可以自己去取模生成。
6.3 自定義漢字顯示
/*********************************************************
** 自定義漢字顯示函數
** x: 列起始位置 0~(127-len) 取值從0到最後一個字符的起始位置
** y: 頁起始位置 0~7
** index: 待顯示的漢字序號
** size: 字體大小枚舉值
*********************************************************/
void OLED_Show_Chinese(u8 x, u8 y , u8 index , enum Zh_Size size)
{
u8 i,j ;
u8 len = size ; // 字體寬度,等於字體高度
u8 page = size >> 3 ; // 這裏處以8直接用移位操作,高度大於8就要翻頁
for(i = 0 ; i < page ; i ++)
{
OLCD_Set_Pos(x,y+i); // 設置顯示位置
switch(size)
{
case ZH16:
for(j=0; j<len; j++)
OLED_Write_Byte(ZH16x16[index*page + i][j],OLED_DATA); // 寫數據
break;
case ZH32:
for(j=0; j<len; j++)
OLED_Write_Byte(ZH32x32[index*page + i][j],OLED_DATA); // 寫數據
break;
}
}
}
自定義漢字的顯示其實和上面的顯示數字類似,不過是數字的寬高是1:2,漢字的寬高是1:1,就只有這裏有區別而已,我只取16*16的字體和32*32的字體給大家演示下。
6.4 自定義圖標顯示。
放上我的圖標頭文件
#ifndef __ICON_H
#define __ICON_H
//寬x高:16*16
const unsigned char ICON16[][16]={
{0xC0,0xF0,0x10,0xD0,0xD0,0xD0,0x10,0xD0,0xD0,0xD0,0x10,0xD0,0xD0,0xD0,0xF0,0x00},
{0x07,0x1F,0x10,0x17,0x17,0x17,0x10,0x17,0x17,0x17,0x10,0x17,0x17,0x17,0x1F,0x00},/*"電池",0*/
{0x08,0x18,0x28,0x48,0x88,0x48,0x28,0x18,0x08,0x00,0x00,0x00,0xE0,0x00,0xF8,0x00},
{0x00,0x00,0x40,0x40,0x7F,0x40,0x40,0x00,0x7C,0x00,0x7F,0x00,0x7F,0x00,0x7F,0x00},/*"信號",1*/
{0x00,0x00,0x00,0x08,0x10,0x20,0x40,0xFE,0x86,0xC4,0x68,0x30,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x10,0x08,0x04,0x02,0x7F,0x61,0x23,0x16,0x0C,0x00,0x00,0x00,0x00},/*"藍牙",2*/
};
#endif
這些圖標是我在取模軟件中用圖像模式,用鼠標一個一個點畫出來的,可能有些醜,大家其實也可以自己畫。至於圖標的顯示函數,把上面的字符顯示的函數改一改就可以用了
** 自定義圖標顯示
** x: 列起始位置 0~(127-len) 取值從0到最後一個字符的起始位置
** y: 頁起始位置 0~7
** index: 待顯示的圖標序號,圖標大小爲16*16
*********************************************************/
void OLED_Show_Icon(u8 x, u8 y , u8 index)
{
u8 i,j ;
u8 len = 16 ;
u8 page = 2 ;
for(i = 0 ; i < page ; i ++)
{
OLCD_Set_Pos(x,y+i); // 設置顯示位置
for(j=0; j<len; j++)
OLED_Write_Byte(ICON16[index*page + i][j],OLED_DATA); // 寫數據
}
}
看下效果
7.功能顯示函數
7.1 開關屏幕
/******************
** 開啓OLED顯示
*******************/
void OLED_On(void)
{
OLED_Write_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_Write_Byte(0X14,OLED_CMD); //DCDC ON
OLED_Write_Byte(0XAF,OLED_CMD); //DISPLAY ON
}
/******************
** 關閉OLED顯示
*******************/
void OLED_Off(void)
{
OLED_Write_Byte(0X8D,OLED_CMD); //SET DCDC命令
OLED_Write_Byte(0X10,OLED_CMD); //DCDC OFF
OLED_Write_Byte(0XAE,OLED_CMD); //DISPLAY OFF
}
開關屏幕其實是根據SSD1306手冊中的寫命令來,這兩個函數可以直接拷貝使用。
7.2 正常顯示和反顯
- 全屏幕反顯,使用一條寫命令語句就可以了
OLED_Write_Byte(0xA6,OLED_CMD);// 開啓正常顯示 (0xA6 = 正常 / 0xA7 = 反顯)
- 字符本身反顯,其實也很簡單,將待寫入的數據取反操作後寫入
/*********************************************************
** 自定義漢字顯示函數
** x: 列起始位置 0~(127-len) 取值從0到最後一個字符的起始位置
** y: 頁起始位置 0~7
** index: 待顯示的漢字序號
** size: 字體大小枚舉值
** reverse: 是否反顯 1=是,0=否
*********************************************************/
void OLED_Show_Chinese(u8 x, u8 y , u8 index , enum Zh_Size size , u8 reverse)
{
u8 i,j ;
u8 len = size ; // 字體寬度,等於字體高度
u8 page = size >> 3 ; // 這裏處以8直接用移位操作,高度大於8就要翻頁
for(i = 0 ; i < page ; i ++)
{
OLCD_Set_Pos(x,y+i); // 設置顯示位置
switch(size)
{
case ZH16:
for(j=0; j<len; j++)
reverse?OLED_Write_Byte(~ZH16x16[index*page + i][j],OLED_DATA):OLED_Write_Byte(ZH16x16[index*page + i][j],OLED_DATA); // 寫數據
break;
case ZH32:
for(j=0; j<len; j++)
reverse?OLED_Write_Byte(~ZH32x32[index*page + i][j],OLED_DATA):OLED_Write_Byte(ZH32x32[index*page + i][j],OLED_DATA); // 寫數據
break;
}
}
}
我改造了下漢字顯示的函數,增加了是否反顯的標誌位 ,用來控制是否反顯。
7.3 屏幕滾動
/************************************************************
** 滾動顯示某一頁
** start: 開始頁 0 - 7
** end : 結束頁 0 - 7
** dirct:方向枚舉,left向左,right向右
**************************************************************/
void OLED_Scroll_Display(u8 start,u8 end,enum DIRECTION dirct)
{
if(start > 7 || end > 7) return; // 頁碼超出
OLED_Write_Byte(0x2E ,OLED_CMD); // 關閉滾動
OLED_Write_Byte(dirct,OLED_CMD); // 向左/右滾動
OLED_Write_Byte(0x00 ,OLED_CMD); // 空制令
OLED_Write_Byte(start,OLED_CMD); // 起始頁
OLED_Write_Byte(0x0F ,OLED_CMD); // 滾動間隔,0=不滾動,值越大,滾動越快
OLED_Write_Byte(end ,OLED_CMD); // 結束頁
OLED_Write_Byte(0x00 ,OLED_CMD); // 空指令
OLED_Write_Byte(0xFF ,OLED_CMD); // 空指令,加兩條空指令,不然不會生效
OLED_Write_Byte(0x2F ,OLED_CMD); // 開啓滾動
}
方向枚舉的定義如下
// 滾動方向枚舉
enum DIRECTION { LEFT = 0x27 , RIGHT = 0x26};
看下演示效果。
如果有感興趣的可以加羣交流,以上的代碼我會在羣裏分享。QQ羣:1087285029
或者掃碼