OLED的使用
1.使用IIC通讯协议进行数据通信
IIC通讯协议
两根总线 SDA数据总线 SCL时钟总线(主机提供)
IIC的起始信号SCL为高电平 SDA由高电平变成低电平 SCL变成低电平
结束信号 SCL为高电平 SDA由低电平变成高电平
响应信号ACK 系统传输完成一个字节8bit之后 会把SDA总线的控制权给从机
SCL为高电平 SDA为高电平 表示非应答信号
SDA为低电平 表示应答信号
SCL为高电平的时候 进行SDA线数据的读取
SCL为低电平的时候 进行SDA线数据的转换
- 软件模拟IIC的起始信号
void IIC_Start(void)
{
//起使SCL SDA都是高电平
OLED_SCL_HIGH();
OLED_SDA_HIGH();
i2c_Delay();
OLED_SDA_LOW();//此时SDA产生开始信号
i2c_Delay();
OLED_SCL_LOW();//SCL线也变成了低电平
i2c_Delay();
}
- 软件模拟IIC的结束信号
void IIC_Stop(void)
{
OLED_SCL_HIGH();
// OLED_SCLK_Clr();
OLED_SDA_LOW();
i2c_Delay();
OLED_SDA_HIGH();//产生结束信号
}
- 应答信号
void i2c_Ack(void)
{
//产生应答信号 SDA线为低电平
OLED_SDA_LOW(); /* CPU驱动SDA = 0 */
i2c_Delay();
OLED_SCL_HIGH(); /* CPU产生1个时钟 */
i2c_Delay();
OLED_SCL_LOW();
i2c_Delay();
//设置完成应答信号 CPU释放
OLED_SDA_HIGH(); /* CPU释放SDA总线 */
}
2.OLED函数
- 写函数
使用该函数 进行命令和数据的区分
/*
#define OLED_CMD 0 //写命令
#define OLED_DATA 1 //写数据
*/
void OLED_WR_Byte(unsigned dat,unsigned cmd)
{
if(cmd){
Write_IIC_Data(dat);
}else {
Write_IIC_Command(dat);//写命令
}
}
- 数据区分
// IIC Write Command 写命令
IIC设备在数据传输之前都必须识别从机地址。SSD1306的从机地址有 0111100b 和 0111101b 两种,
0x78 0x79
将SA0(D/C#)脚下拉到低电平可以设置从机地址第七位为 0
0为写模式
通过SA0(D/C#)脚的上拉和下拉来设置从机地址,从而令总线上可以存在最多2个SSD1306驱动器。
CO 和 D/C# 位后面再加上六个0组成的。(控制字节组成如下图所示)Co D/C# 00 0000
如果Co为0,后面传输的信息就只包含数据字节。
D/C# 位决定了下个数据字节是作为命令还是数据。
D/C# 为0时,下一个数据被视为命令;
DC# 为1时,下一个数据被视为显示数据,存储到GDDRAM中。
根据时序图可知
发送数据或者指令的时候,首先发送地址0x78(写模式最后一位为0),然后发送判断是数据函数命令 发送指令的时候为0x00 发送数据的时候为0x40
void Write_IIC_Command(unsigned char IIC_Command)
{
IIC_Start();
Write_IIC_Byte(0x78); //Slave address,SA0=0
i2c_Ack();
Write_IIC_Byte(0x00); //write command D/C#为 0 因此是0x00
i2c_Ack();
Write_IIC_Byte(IIC_Command);
i2c_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();
}
- 写字节函数
写字节函数通过移位一位一位的写入
当读取到的是1 拉高SDA
读取到的是0 拉低SDA
void Write_IIC_Byte(unsigned char IIC_Byte)
{
unsigned char i;
unsigned char m,da;
da=IIC_Byte;
OLED_SCL_LOW();//低电平进行数据的切换
for(i=0;i<8;i++)
{
m=da;
m=m&0x80;
if(m==0x80){//等于1000 0000 表示该位的数据是1
OLED_SDA_HIGH();//将SDA拉高
}else{
OLED_SDA_LOW();//将SDA拉低
}
da=da<<1;//0100 0001 << 1000 0010 通过左移进行数据的读取
OLED_SCL_HIGH();//高电平读取信息
OLED_SCL_LOW();//转换成低电平
}
}
- 设置坐标函数
/*128*64 128列 64行
*坐标设置 y表示的是页数 从第几页开始显示 一页有8行 一共8页 组成64行
* 一共8页 因此最多显示 4行 8*16 16行表示的是2页
* 最多显示 8行 6*8
* x表示的是列数 从第几列开始显示
* x&1111 0000 >>4 |0x0001 0000
*/
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);
}
- 清屏函数
设置页的起使列地址,和页的结束列地址
0x00 0x10 一共16列
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!! 不太明白
//RAM有也是128*64个字节 RAM划分成为8页 把数据通过RAM传递到OLED上面
void OLED_Clear(void)
{
u8 i,n;
for(i=0;i<8;i++) //因为有8页 因此要循环8次
{
OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)1011 0000 第五位规定为0 后三位用来设置页地址
//1011 0001
OLED_WR_Byte (0x00,OLED_CMD); //页寻址模式 设置起使列地址低位 0x00·0x0F 0000 0000 0000 1111
OLED_WR_Byte (0x10,OLED_CMD); //页寻址模式 设置起使列地址高位 0x10 ~ 0x1F 0001 0000 0001 1111
for(n=0;n<128;n++){
OLED_WR_Byte(0,OLED_DATA);//向每一列的每个元素写入0 显示黑色
}
} //更新显示
}
- 字符显示函数
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
//生成的方法是列行式 因此需要先对上面的值进行处理 8*16相当于上面一行是8 下面一行也是8 8列16行
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{
unsigned char c=0,i=0;
c=chr-' ';//得到偏移后的值 通过ASCII码计算出和第一个差多少
//假设是写0 那么他和最开始的相比16-0 等于16 表示要在17行开始
if(x>Max_Column-1){//MAX_Column = 128
x=0;y=y+2;//加入x输入的大于128 就让x在第一列显示 y在下两行显示
}
if(Char_Size ==16){ //字体大小是16
OLED_Set_Pos(x,y);
for(i=0;i<8;i++){
OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);//8*16的点阵 表示先显示上面行的8
}
OLED_Set_Pos(x,y+1); //y加1
for(i=0;i<8;i++){
OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA); //显示下面的8列元素
}
}else {
OLED_Set_Pos(x,y);
for(i=0;i<6;i++){
OLED_WR_Byte(F6x8[c][i],OLED_DATA);
}
}
}