前言
使用stm32 驱动4 Pin 的OLED, 现在网上开源的资料多的是,但是为了锻炼自己使用第一手资料的能力,今天我还是从数据手册开始,从头造一波轮子,同时也是为了加深自己对 IIC 协议的理解 ,本系列内容我会从单片机和linux两个板子做一些OLED的 验证,希望后面大家在学习IIC 相关内容的时候,可以少走一些弯路。为了大家学习方面,这些文章也会在我的微信公众号同步,方便大家随时查看,欢迎大家扫码关注。
1. 查阅数据手册
在我们实际工作中,需要我们从头造轮子的机会其实并不多,但是作为搞嵌入式这个行业的,阅读一些简单的数据手册的能力还是要有的,万一碰到碰到一个比较棘手的问题,说不定就需要我们不得不去看芯片的参考手册了。本篇我就以4Pin 的OLED 使用的SSD1306为例,通过stm32 实现对他的驱动。
我们 打开他的数据手册,首先不要被全英文的吓到了,现在网上各种的翻译软件都比较给力。再说了,这个手册其实并不需要从头到尾一字不落的读完,首先我们看一下芯片的简单说明,了解芯片的大致的特性。
其次:根据自己选择的芯片,使用的通讯方式,着重去看相对应的章节,比如我们本次使用的就是4 Pin的 IIC通信的,所以我们直接去找参考手册中介绍IIC 的.
a. IIC 从机地址是有SA0 决定的,并且R/W# 决定是读取数据还是写入数据。R/W# = 0 为写模式,R/W# = 1 为读数据。
IIC 通信协议:
S 信号 和 P 信号:
ACK 信号 和NAck 信号:
数据传输的时候要保证SDA 总线上数据稳定:
发送数据还是命令室友D/C# 引脚决定的: D/C# = 1 发送data , D/C# = 0 发送的是命令;
SSD 1306 显存大小: 128列 * 64行
如果要驱动SSD1306 首先要实现IIC 总线的驱动代码,即IIC 协议的实现,如果使用的是stm32单片机 来驱动的话,其实可以直接使用硬件IIC,当然了软件IIC也是可以的,本篇我们就先以软件模拟IIC来驱动他。
2. 软件模拟IIC 实现
2.1
#define SCL_Pin GPIO_Pin_6
#define SDA_Pin GPIO_Pin_7
#define IIC_GPIO_Port GPIOB
#define OLED_SCLK_LOW GPIO_ResetBits(GPIOB,GPIO_Pin_6)
#define OLED_SCLK_HIGH GPIO_SetBits(GPIOB,GPIO_Pin_6)
#define OLED_SDA_LOW GPIO_ResetBits(GPIOB,GPIO_Pin_7)
#define OLED_SDA_HIGH GPIO_SetBits(GPIOB,GPIO_Pin_7)
void OLED_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitStructure.GPIO_Pin = SCL_Pin|SDA_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IIC_GPIO_Port, &GPIO_InitStructure);
}
/*IIC 起始信号*/
void i2c_start()
{
OLED_SCLK_HIGH;
OLED_SDA_HIGH;
OLED_SDA_LOW;
OLED_SCLK_LOW;
}
/*IIC 停止信号*/
void i2c_stop()
{
OLED_SCLK_HIGH;
OLED_SDA_LOW;
OLED_SDA_HIGH;
}
/*等待应答:提供一个scl 时钟周期*/
void i2c_wait_ack(void)
{
OLED_SCLK_HIGH;
OLED_SCLK_LOW;
}
void Write_IIC_Byte(unsigned char IIC_Byte)
{
unsigned char i;
unsigned char m,da;
da=IIC_Byte;
OLED_SCLK_LOW;
for(i=0;i<8;i++)
{
m=da;
m=m&0x80;
if(m==0x80)
{
OLED_SDA_HIGH;
}
else
OLED_SDA_LOW;
da=da<<1;
OLED_SCLK_HIGH;
OLED_SCLK_LOW;
}
}
/**********************************************
// IIC Write Command
**********************************************/
void Write_IIC_Command(unsigned char IIC_Command)
{
i2c_start();
Write_IIC_Byte(0x78); //Slave address,SA0=0
i2c_wait_ack();
Write_IIC_Byte(0x00); //write command
i2c_wait_ack();
Write_IIC_Byte(IIC_Command);
i2c_wait_ack();
i2c_stop();
}
/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data)
{
i2c_start();
Write_IIC_Byte(0x78); //D/C#=0; R/W#=0
i2c_wait_ack();
Write_IIC_Byte(0x40); //write data
i2c_wait_ack();
Write_IIC_Byte(IIC_Data);
i2c_wait_ack();
i2c_stop();
}
void OLED_WR_Byte(unsigned dat,unsigned cmd)
{
if(cmd)
{
Write_IIC_Data(dat);
}
else
{
Write_IIC_Command(dat);
}
}
3. OLED 设备驱动程序
void OLED_Init(void)
{
OLED_Config();
GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);
delay_ms(100);
OLED_WR_Byte(0xAE,OLED_CMD);//--display off
OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
OLED_WR_Byte(0x40,OLED_CMD);//--set start line address
OLED_WR_Byte(0xB0,OLED_CMD);//--set page address
OLED_WR_Byte(0x81,OLED_CMD); // contract control
OLED_WR_Byte(0xFF,OLED_CMD);//--128
OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap
OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse
OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty
OLED_WR_Byte(0xC8,OLED_CMD);//Com scan direction
OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset
OLED_WR_Byte(0x00,OLED_CMD);//
OLED_WR_Byte(0xD5,OLED_CMD);//set osc division
OLED_WR_Byte(0x80,OLED_CMD);//
OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off
OLED_WR_Byte(0x05,OLED_CMD);//
OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period
OLED_WR_Byte(0xF1,OLED_CMD);//
OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion
OLED_WR_Byte(0x12,OLED_CMD);//
OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh
OLED_WR_Byte(0x30,OLED_CMD);//
OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable
OLED_WR_Byte(0x14,OLED_CMD);//
OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
}
void OLED_Set_Pos(unsigned char x, unsigned char y)
{
OLED_WR_Byte(0xb0+y,OLED_CMD);
OLED_WR_Byte(((x&0xF0) >> 4)|0x10, OLED_CMD);
OLED_WR_Byte( (x&0x0F), OLED_CMD);
}
void OLED_Display_On(void)
{
OLED_WR_Byte(0x8D,OLED_CMD); //设置电荷泵
OLED_WR_Byte(0x14,OLED_CMD); //开启电荷泵
OLED_WR_Byte(0xAF,OLED_CMD); //OLED唤醒
}
void OLED_Display_Off(void)
{
OLED_WR_Byte(0x8D,OLED_CMD); //设置电荷泵
OLED_WR_Byte(0x10,OLED_CMD); //关闭电荷泵
OLED_WR_Byte(0xAE,OLED_CMD); //关闭屏幕显示
}
void OLED_Clear(void)
{
u8 i = 0, n = 0;
for(i = 0; i < 8; i++)
{
OLED_WR_Byte(0xb0+i,OLED_CMD); //设置页地址
OLED_WR_Byte(0x00,OLED_CMD); // 设置显示位置-列低地址
OLED_WR_Byte(0x10,OLED_CMD); // 设置显示位置-列高地址
for(n = 0; n < 128; n++) OLED_WR_Byte(0,OLED_DATA);
}
}
void OLED_ShowChar(u8 x, u8 y, u8 chr, u8 Char_size)
{
unsigned char c = 0, i = 0;
c = chr - ' ';
if(x > 128 - 1)
{
x = 0;
y += 2;
}
if(Char_size == 8)
{
OLED_Set_Pos(x,y);
for(i = 0; i < 8; i++)
OLED_WR_Byte(F8X16[c*16+i],OLED_DATA); //显示低字节
OLED_Set_Pos(x,y+1);
for(i = 0; i < 8; i++ )
OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA); //显示高字节
}
else
{
字体为6号
OLED_Set_Pos(x,y);
for(i=0;i<6;i++)
OLED_WR_Byte(F6x8[c][i],OLED_DATA);
}
}
void OLED_ShowString(u8 x, u8 y,u8* chr, u8 Char_size)
{
while(*chr != '\0')
{
OLED_ShowChar(x,y,*chr, Char_size);
x+=Char_size;
if(x > 120)
{
x = 0;
y += 2;
}
chr++;
}
}