一、模块介绍
1、常见的显示设备
LED、数码管、点阵、LCD屏(1602/12864)、OLED屏(消费电子)
2、OLED屏的概述
OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD)。因为具备轻薄、省电等特性,因此从2003 年开始,这种显示设备在 MP3 播放器上得到了广泛应用,而对于同属数码类产品的 DC与手机,此前只是在一些展会上展示过采用 OLED 屏幕的工程样品。自 2007 年后,寿命得到很大提高,具备了许多 LCD 不可比拟的优势。
3、特性
型号:SH1106
尺寸:132 * 64个像素点(前两列和最后的两列显示不了东西)
像素点:一帧画面所能表示的最小的显示单位。
分辨率:横向像素点的个数 * 竖向像素点个数
通信方式:
硬线接线四线SPI的通信方式
二、硬件分析
1、引脚分布
A0管脚如果是低电平就是发送命令过去
A0管脚如果是高电平就是发送显示数据过去
OLED_SCL -- 时钟线
(MOSI)OLED_SI -- 输入对于OLED的驱动芯片
OLED_CS -- 片选线
OLED_RES -- 复位管脚
OLED_D/C -- 命令或数据引脚
IO口模拟SPI
使用SPI控制器
三、SPI通信
1、通信特点
4线SPI:同步串行全双工
3线SPI:同步串行半双工
2、通信速度
几十Mhz以上
3、通信模式
MODE0和MODE3:上升沿接收数据,下降沿发送数据
MODE1和MODE2:下降沿接收数据,上升沿发送数据
器件支持MODE0模式也支持MODE3,支持MODE1模式也支持MODE2
4、通讯图
四、软件编写
1、时序图
1. CS线要拉低
2. SPI符合模式0或者模式3,上升沿接收数据,下降沿发送数据
3. A0线拉低代表把刚刚传输过去的八位数据给到命令寄存器里,拉高代表传输到显存
2、OLED相关命令
假如现在想要从第1页开始显示就要把0xB0的指令发送出去
设置列地址分为了高四字节和低四字节
如果要去设置列地址就要分两次命令发送
当前如果要在第1列显示数据:
发送低位的列地址就是00
发送高位的列地址就是10
3、字模提取:
4、图像提取
五、流程
1、初始化GPIO口
复用功能
复用到哪里
PB3 -- SCL -- SPI1_SCL
PB5 -- SI -- SPI1_MOSI
PB13 -- RES -- 推挽输出
PB7 -- CS --? 推挽输出
PA15 -- DC --? 推挽输出
2、初始化SPI控制器
使用库函数:
先打开外设时钟
找Init函数
#include "spi.h"
/****************************
函数功能:SPI管脚初始化
函数形参:u32 nms
函数返回值:void
函数说明:
PB3 -- SCL -- SPI1_SCL
PB5 -- SI -- SPI1_MOSI
PB13 -- RES -- 推挽输出
PB7 -- CS -- 推挽输出
PA15 -- DC -- 推挽输出
****************************/
void Spi_Port_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;//定义了一个结构体变量
SPI_InitTypeDef SPI_InitStruct;
//1. 打开GPIOA的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
//2. 配置GPIO口功能
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;//配置输出模式
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//配置为推挽
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_13;//选择7号管脚
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;//无上下拉
GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;//低速
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;//选择15号管脚
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;//配置复用模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_5;//选择3和5号管脚
GPIO_Init(GPIOB, &GPIO_InitStruct);
//3.复用到哪里
GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1);
//4.SPI控制器初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//选择二分频
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;//前沿采集
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;//空闲时为低电平
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;//8位数据为一帧
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//选择全双工
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;//高位先发
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;//主模式
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;//软件从模式管理
SPI_Init(SPI1,&SPI_InitStruct);
SPI_Cmd(SPI1,ENABLE);//使能SPI1
}
/****************************
函数功能:SPI发送一个字节数据
函数形参:u8 data
函数返回值:void
函数说明:
****************************/
void Spi_SendByte(u8 data)
{
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET)//当前发送缓冲区为空才能跳出循环
{
}
SPI_I2S_SendData(SPI1,data);//发送数据
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) == RESET)//当前接收缓冲区为空才能跳出循环
{
}
SPI_I2S_ReceiveData(SPI1);//接收数据
}
#include "oled.h"
/**********************************************************************************************************
* 函数名 : OLED_writeByte
* 功能描述 : oled字节写,向SH1106写入一个字节。
* 参数说明 : dat:要写入的数据/命令
* cmd:数据/命令标志 0,表示命令;1,表示数据;
* 返回值说明: none
* 修改记录 :
* 其他 :
**********************************************************************************************************/
void OLED_writeByte(u8 dat, u8 cmd)
{
cmd ? OLED_DC_SET() : OLED_DC_CLR();
OLED_select();
Spi_SendByte(dat);
OLED_deSelect();
}
/**********************************************************************************************************
* 函数名 : OLED_clear
* 功能描述 : 清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
* 参数说明 : none
* 返回值说明: none
* 修改记录 :
* 其他 :
OLED;8页,每页132字节,
清屏:首先清除第1页,从第一个字节开始
2
前面 & 后面 4列 因边框问题不处理
**********************************************************************************************************/
void OLED_clear(void)
{
u8 i,n;
for(i=0;i<8;i++) //8页
{
OLED_writeByte (0xb0+i,OLED_CMD); //设置页地址(0~7)
//从第2列(0x02)开始
OLED_writeByte (0x02,OLED_CMD); //设置显示位置—列低地址
OLED_writeByte (0x10,OLED_CMD); //设置显示位置—列高地址
for(n=0; n<130; n++)//到第130列
{
OLED_writeByte(0,OLED_DATA); //息屏整个屏幕是黑色的!和没点亮一样!!!
}
} //更新显示
}
/**********************************************************************************************************
* 函数名 : Oled_init
* 功能描述 : oled初始化
* 参数说明 : none
* 返回值说明: none
* 修改记录 :
* 其他 :
**********************************************************************************************************/
void Oled_init(void)
{
Spi_Port_Init(); //管脚初始化
OLED_RES_L;
Delay_ms(100);
OLED_RES_H;
OLED_writeByte(0xAE,OLED_CMD);//--turn off oled panel
OLED_writeByte(0x02,OLED_CMD);//---SET low column address
OLED_writeByte(0x10,OLED_CMD);//---SET high column address
OLED_writeByte(0x40,OLED_CMD);//--SET start line address SET Mapping RAM Display Start Line (0x00~0x3F)
OLED_writeByte(0x81,OLED_CMD);//--SET contrast control register
OLED_writeByte(0xCF,OLED_CMD); // SET SEG Output Current Brightness
OLED_writeByte(0xA1,OLED_CMD);//--SET SEG/Column Mapping 0xa0左右反置 0xa1正常
OLED_writeByte(0xC8,OLED_CMD);//SET COM/Row Scan Direction 0xc0上下反置 0xc8正常
OLED_writeByte(0xA6,OLED_CMD);//--SET normal display
OLED_writeByte(0xA8,OLED_CMD);//--SET multiplex ratio(1 to 64)
OLED_writeByte(0x3f,OLED_CMD);//--1/64 duty
OLED_writeByte(0xD3,OLED_CMD);//-SET display offSET Shift Mapping RAM Counter (0x00~0x3F)
OLED_writeByte(0x00,OLED_CMD);//-not offSET
OLED_writeByte(0xd5,OLED_CMD);//--SET display clock divide ratio/oscillator frequency
OLED_writeByte(0x80,OLED_CMD);//--SET divide ratio, SET Clock as 100 Frames/Sec
OLED_writeByte(0xD9,OLED_CMD);//--SET pre-charge period
OLED_writeByte(0xF1,OLED_CMD);//SET Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_writeByte(0xDA,OLED_CMD);//--SET com pins hardware configuration
OLED_writeByte(0x12,OLED_CMD);
OLED_writeByte(0xDB,OLED_CMD);//--SET vcomh
OLED_writeByte(0x40,OLED_CMD);//SET VCOM Deselect Level
OLED_writeByte(0x20,OLED_CMD);//-SET Page Addressing Mode (0x00/0x01/0x02)
OLED_writeByte(0x02,OLED_CMD);//
OLED_writeByte(0x8D,OLED_CMD);//--SET Charge Pump enable/disable
OLED_writeByte(0x14,OLED_CMD);//--SET(0x10) disable
OLED_writeByte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
OLED_writeByte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)
OLED_writeByte(0xAF,OLED_CMD);//--turn on oled panel
OLED_writeByte(0xAF,OLED_CMD); /*display ON*/
OLED_clear();
}
/**********************************************************************************************************
* 函数名 : OLED_setPos
* 功能描述 :
* 参数说明 :
X: 列坐标 0----131
Y: 页坐标 0----7
列总共由8个位组成
* 返回值说明: none
* 修改记录 :
* 其他 :
**********************************************************************************************************/
void OLED_setPos(unsigned char x, unsigned char y) //34 - 0010 0010
{
x += 2;//SSH1106 要加这个
y &= 7;//保证页的地址不超
OLED_writeByte(0xb0+y,OLED_CMD); //设置页地址
OLED_writeByte(((x&0xf0)>>4)|0x10, OLED_CMD);//设置显示位置—列高地址
//0010 0010 & 1111 0000
//0010 0000 >> 4 -- 0000 0010
OLED_writeByte((x&0x0f),OLED_CMD);
//0010 0010 & 0000 1111 -- 0000 0010
}
/**********************************************************************************************************
* 函数名 : Show_Signal_Word
* 功能描述 : 显示单个字符
* 参数说明 : u8 page, u8 col, u8 ch
* 返回值说明: none
* 修改记录 :
* 其他 :
**********************************************************************************************************/
void Show_Signal_Word(u8 page, u8 col, u8 ch)//'a' -- 97
{
u8 i;
u8 n;
u8 index = ch - ' ';//得到当前这个字符在ASCII_816的位置,97 - 32 = 65
if(col > 120)
{
col = 0;
page += 2;
}
OLED_setPos(col,page);
//由于一页只有八行,一个字符有16行,所以需要换两页
for(i = 0; i < 2; i++)
{
//由于一个字符只有8列
for(n=0; n< 8; n++)//到第130列
{
OLED_writeByte(ASCII_816[index*16 + i*8 + n],OLED_DATA); //息屏整个屏幕是黑色的!和没点亮一样!!!
}
OLED_setPos(col,page+1);
}
}
/**********************************************************************************************************
* 函数名 : Show_String
* 功能描述 : 显示字符串
* 参数说明 : u8 page, u8 col, u8 *str, u8 num
* 返回值说明: none
* 修改记录 :
* 其他 :
**********************************************************************************************************/
void Show_String(u8 page, u8 col, u8 *str, u8 num)//"sakdjfhjk",0
{
u8 i;
OLED_setPos(col,page);
for(i = 0; i < num; i++)
{
Show_Signal_Word(page,col,*str);
str++;
col += 8;//为了保证下一个字符和前一个字符不重合,根据字体大小所以+8
if(col > 120)
{
col = 0;
page += 2;
}
OLED_setPos(col,page);
}
}
/**********************************************************************************************************
* 函数名 : Show_Chinese
* 功能描述 : 显示字符串
* 参数说明 : u8 page, u8 col, u8 *str, u8 num
* 返回值说明: none
* 修改记录 :
* 其他 :
**********************************************************************************************************/
void Show_Chinese( u8 page, u8 col, u8 *str, u8 num)
{
u8 i,j;
u8 n;
if(col > 112)
{
col = 0;
page += 2;
}
OLED_setPos(col,page);
for(j = 0; j < num; j++)
{
for(i = 0; i < 2; i++)
{
//由于一个字符只有8列
for(n=0; n < 16; n++)//代表有16列的数据
{
OLED_writeByte(str[j *32 + i*16 + n],OLED_DATA); //息屏整个屏幕是黑色的!和没点亮一样!!!
}
OLED_setPos(col,page+1);
}
col += 16;
if(col > 112)
{
col = 0;
page += 2;
}
OLED_setPos(col,page);
}
}
/**********************************************************************************************************
* 函数名 : Show_Pic
* 功能描述 : 显示图片
* 参数说明 : u8 page, u8 col, u8 *pic
* 返回值说明: none
* 修改记录 :
* 其他 :
**********************************************************************************************************/
void Show_Pic(u8 page, u8 col, u8 *pic)
{
u8 weight = pic[2];
u8 height = pic[4];
u8 i,j;
pic += 6;//先偏移掉数据头的数据
if(weight > 129 - col)
{
col = 0;
page += 2;
}
OLED_setPos(col,page);
for(i = 0; i < height / 8; i++)
{
for(j = 0;j < weight; j++)
{
OLED_writeByte(*pic,OLED_DATA);
pic++;
}
page += 1;
OLED_setPos(col,page);
}
}