基于篇一我已经介绍了IIC的一些基本时序,本篇文章将教会你们如何初始化程序以及点亮像素点。
一、OLED屏幕的简单介绍:
1.OLED显存介绍:
我所使用的OLED为128*64分辨率的,即RAM为128*64位。而RAM分为8个页面,从PAGE0到PAGE7,如图所示:
而每一页有8个像素,即八位,而刚好一个字节的数据就是八位,8*8=64就是这么来的。我想这不是巧合,可能就是为了让我们可以用一个字节的数据表示某一列的八个像素点的亮灭。从下图可知,其低位是在最上面的,高位依次往下。
而我们点亮OLED相当于将这128*64个像素点点亮的过程,和点阵屏类似。
下面来介绍如何点亮OLED:
二、OLED屏幕的驱动
1.向屏幕写数据:OLED_WR_Byte
IIC写时序图如下:
首先开始IIC通信(START),然后发送器件地址(DEVICE ADDRESS) :0X78;等待应答,然后发送命令(OLED命令寄存器地址:0x40;数据寄存器地址:0x00),等待应答,发送数据,等待应答,结束信号;
//发送一个字节
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 mode)
{
I2C_Start();
Send_Byte(0x78);
I2C_WaitAck();
if(mode)
Send_Byte(0x40);
else
Send_Byte(0x00);
I2C_WaitAck();
Send_Byte(dat);
I2C_WaitAck();
I2C_Stop();
}
2.OLED的初始化
初始化流程图:
//OLED的初始化
void OLED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能A端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA0,1
GPIO_SetBits(GPIOA,GPIO_Pin_11|GPIO_Pin_12);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA2
GPIO_SetBits(GPIOA,GPIO_Pin_15);
OLED_WR_Byte(0xAE,OLED_CMD);//关闭显示(进入睡眠模式)
OLED_WR_Byte(0x02,OLED_CMD);//设置低列地址
OLED_WR_Byte(0x10,OLED_CMD);//设置高列地址
OLED_WR_Byte(0xD5,OLED_CMD);//设置显示时钟分频值/震荡频率(刷新率)
OLED_WR_Byte(0x80,OLED_CMD);//设置分割比率,设置时钟为100帧/秒
OLED_WR_Byte(0xA8,OLED_CMD);//设置复用率(0-63)
OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
OLED_WR_Byte(0xD3,OLED_CMD);//设置显示偏移一位映射RAM计数器(0x00~0x3F)
OLED_WR_Byte(0x00,OLED_CMD);//不偏移
OLED_WR_Byte(0x40,OLED_CMD);//设置起始地址 设置映射RAM显示起始行(0x00~0x3F)
OLED_WR_Byte(0xA1,OLED_CMD);//设置左右方向显示 0xa1正常 0xa0左右反置
OLED_WR_Byte(0xC8,OLED_CMD);//设置上下方向显示 0xc0上下反置 0xc8正常
OLED_WR_Byte(0xDA,OLED_CMD);//设置列引脚硬件配置
OLED_WR_Byte(0x12,OLED_CMD);//12864->0x12;128*32->0x02;
OLED_WR_Byte(0x81,OLED_CMD);//设置对比度控制寄存器
OLED_WR_Byte(0xCF,OLED_CMD);//设置对比度控制为0xcf(0x00-0xff)
OLED_WR_Byte(0xD9,OLED_CMD);//设置充电周期
OLED_WR_Byte(0xF1,OLED_CMD);//设置预充电为15个时钟,放电为1个时钟
OLED_WR_Byte(0xDB,OLED_CMD);//设置VCOMH反压值
OLED_WR_Byte(0x30,OLED_CMD);//设置VCOM取消选择电平
OLED_WR_Byte(0x20,OLED_CMD);//设置页面寻址模式(0x00/0x01/0x02)
OLED_WR_Byte(0xA6,OLED_CMD);//设置正常显示
OLED_Clear();
OLED_WR_Byte(0x8D,OLED_CMD);//设置电荷泵
OLED_WR_Byte(0x14,OLED_CMD);//设置电荷泵开启/(0x10)禁用
OLED_WR_Byte(0xAF,OLED_CMD);//开启显示(进入工作模式)
}
3.点亮一个像素点:
在OLED的这么多指令中,我们最需要了解的是如何设置列地址与页地址。在此OLED有八页,第一页page0的指令为0XB0、Page1为0xB1、依次类推,Page7则是0xB7;而列地址的设置则需要两次,比如列地址为50,也就是0x32;我们则需要发送指令0x02将列地址第四位设置为2,再发送指令0x13将列高四位设置为3;设置好列地址与页地址就可以发送一串数据点亮某一页的某一列像素亮灭了,相当于点亮八个LED灯。
eg:将第一页的第1列全部点亮:
OLED_WR_Byte(0xB0,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0x10,OLED_CMD);
OLED_WR_Byte(0xff,OLED_DATA);
此时就成功点亮OLED了!
但是,这样的效率是比较低的,下面我来介绍另一种方法:首先定义一个二维数组,代表OLED显存,128代表其128列,8则代表8页,u8代表一页的八位数据:
u8 OLED_GRAM[128][8];
假如要想实现上面的效果,只需要
OLED_GRAM[0][0] = 0xff;
OLED_Refresh();
就能实现和上面同样的效果啦,这里加了OLED显存更新函数,目的是将二维数组OLED_GRAM里面存放的数据显示出来。
4.更新显存函数
下面来介绍更新显存函数:i代表页数,n代表列数。i=0:写入第0页,执行for语句128次便将二维数组中存的数据都显示出来,依次类推....第一个for循环里面的代码与上述一样原理,设置页地址,i等于0时为0xb0,第0页。唯一不同的是我们没有依次设置列地址了,这是由于CH1116芯片有十分人性化的特性:设置完一个字节的8个像素后,其列地址会自动加1!故我们不需要频繁改变列地址;
void OLED_Refresh(void)
{
u8 i,n;
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(OLED_GRAM[n][i],OLED_DATA);
}
I2C_Stop();
}
}
有了此函数,我们就自需把像素数据存入此二维数组,然后调用此函数就可以了!
5.清屏函数
如何实现对OLED的清屏呢,现在很简单了,只需要把二维数组的数据都清零就可以了
void OLED_Clear(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
for(n=0;n<128;n++)
{
OLED_GRAM[n][i]=0;//清除所有数据
}
}
OLED_Refresh();//更新显示
}
清除二维数组的方法与显存更新函数大差不差,这里就不再介绍了,自此,控制点亮像素点的方法你已经掌握,还是很简单的吧!
下面附上我的代码:
oled.c
#include "oled.h"
u8 OLED_GRAM[128][8];
//延时
void IIC_delay(void)
{
u8 t=3; while(t--);
}
//起始信号
void I2C_Start(void)
{
SDA_OUT();//将SDA口配置成输出
OLED_SDA(1);
OLED_SCL(1);
IIC_delay();
OLED_SDA(0);
IIC_delay();
OLED_SCL(0);
IIC_delay();
}
//结束信号,SCL高电平情况下,SDA信号由低变高
void I2C_Stop(void)
{
SDA_OUT();//将SDA口配置成输出
OLED_SDA(0);
OLED_SCL(1);
IIC_delay();
OLED_SDA(1);
}
//等待信号响应
void I2C_WaitAck(void) //测数据信号的电平
{
SDA_IN();//SDA设置为输入
IIC_delay();
OLED_SCL(1);
IIC_delay();
OLED_SCL(0);
IIC_delay();
}
//写入一个字节
void Send_Byte(u8 dat)
{
u8 i;
SDA_OUT();//将SDA口配置成输出
OLED_SCL(0);
for(i=0;i<8;i++)
{
OLED_SDA((BitAction)(dat&(0x80>>i)));
IIC_delay();
OLED_SCL(1);
IIC_delay();
OLED_SCL(0);
}
}
//发送一个字节
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 mode)
{
I2C_Start();
Send_Byte(0x78);
I2C_WaitAck();
if(mode)
Send_Byte(0x40);
else
Send_Byte(0x00);
I2C_WaitAck();
Send_Byte(dat);
I2C_WaitAck();
I2C_Stop();
}
//更新显存到OLED
void OLED_Refresh(void)
{
u8 i,n;
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(OLED_GRAM[n][i],OLED_DATA);
}
I2C_Stop();
}
}
//清屏函数
void OLED_Clear(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
for(n=0;n<128;n++)
{
OLED_GRAM[n][i]=0;//清除所有数据
}
}
OLED_Refresh();//更新显示
}
void OLED_Test(void)
{
// OLED_WR_Byte(0xB0,OLED_CMD);
// OLED_WR_Byte(0x00,OLED_CMD);
// OLED_WR_Byte(0x10,OLED_CMD);
// OLED_WR_Byte(0xff,OLED_DATA);
OLED_GRAM[0][0] = 0xff;
OLED_Refresh();//更新显示
}
//OLED的初始化
void OLED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能A端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA0,1
GPIO_SetBits(GPIOA,GPIO_Pin_11|GPIO_Pin_12);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA2
GPIO_SetBits(GPIOA,GPIO_Pin_15);
OLED_WR_Byte(0xAE,OLED_CMD);//关闭显示(进入睡眠模式)
OLED_WR_Byte(0x02,OLED_CMD);//设置低列地址
OLED_WR_Byte(0x10,OLED_CMD);//设置高列地址
OLED_WR_Byte(0xD5,OLED_CMD);//设置显示时钟分频值/震荡频率(刷新率)
OLED_WR_Byte(0x80,OLED_CMD);//设置分割比率,设置时钟为100帧/秒
OLED_WR_Byte(0xA8,OLED_CMD);//设置复用率(0-63)
OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
OLED_WR_Byte(0xD3,OLED_CMD);//设置显示偏移一位映射RAM计数器(0x00~0x3F)
OLED_WR_Byte(0x00,OLED_CMD);//不偏移
OLED_WR_Byte(0x40,OLED_CMD);//设置起始地址 设置映射RAM显示起始行(0x00~0x3F)
OLED_WR_Byte(0xA1,OLED_CMD);//设置左右方向显示 0xa1正常 0xa0左右反置
OLED_WR_Byte(0xC8,OLED_CMD);//设置上下方向显示 0xc0上下反置 0xc8正常
OLED_WR_Byte(0xDA,OLED_CMD);//设置列引脚硬件配置
OLED_WR_Byte(0x12,OLED_CMD);//12864->0x12;128*32->0x02;
OLED_WR_Byte(0x81,OLED_CMD);//设置对比度控制寄存器
OLED_WR_Byte(0xCF,OLED_CMD);//设置对比度控制为0xcf(0x00-0xff)
OLED_WR_Byte(0xD9,OLED_CMD);//设置充电周期
OLED_WR_Byte(0xF1,OLED_CMD);//设置预充电为15个时钟,放电为1个时钟
OLED_WR_Byte(0xDB,OLED_CMD);//设置VCOMH反压值
OLED_WR_Byte(0x30,OLED_CMD);//设置VCOM取消选择电平
OLED_WR_Byte(0x20,OLED_CMD);//设置页面寻址模式(0x00/0x01/0x02)
OLED_WR_Byte(0xA6,OLED_CMD);//设置正常显示
OLED_Clear();
OLED_WR_Byte(0x8D,OLED_CMD);//设置电荷泵
OLED_WR_Byte(0x14,OLED_CMD);//设置电荷泵开启/(0x10)禁用
OLED_WR_Byte(0xAF,OLED_CMD);//开启显示(进入工作模式)
}
oled.h
#ifndef __OLED_H
#define __OLED_H
#include "sys.h"
#include "stdlib.h"
//-----------------OLED端口定义----------------
#define GPIO_SCL_PORT GPIOA
#define GPIO_SCL_PIN GPIO_Pin_11
#define GPIO_SCL_ENABLE() do{ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); }while(0) /* 所在IO口时钟使能 */
#define GPIO_SDA_PORT GPIOA
#define GPIO_SDA_PIN GPIO_Pin_12
#define GPIO_SDA_ENABLE() do{ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); }while(0) /* 所在IO口时钟使能 */
#define OLED_SCL(x) GPIO_SCL_PORT->BSRR = GPIO_SCL_PIN << (16*(!x))
#define OLED_SDA(x) GPIO_SDA_PORT->BSRR = GPIO_SDA_PIN << (16*(!x))
#define SDA_IN() {GPIOA->CRH &= 0XFFF0FFFF;GPIOA->CRH |= (u32)8<<16;}
#define SDA_OUT() {GPIOA->CRH &= 0XFFF0FFFF;GPIOA->CRH |= (u32)3<<16;}
#define READ_SDA (BitAction)(GPIOA->IDR & GPIO_Pin_12)
#define OLED_CMD 0 //写命令
#define OLED_DATA 1 //写数据
void I2C_Start(void);
void I2C_Stop(void);
void I2C_WaitAck(void);
void Send_Byte(u8 dat);
void OLED_WR_Byte(u8 dat,u8 mode);
void OLED_Refresh(void);
void OLED_Clear(void);
void OLED_Init(void);
void OLED_Test(void);
#endif
main.c
int main(void)
{
OLED_Init();
while (1)
{
OLED_Test();
}
}