现在能买到的OLED12864显示屏大多为SPI和I2C接口的,I2C通信协议只需要两条总线就可以进行通信,下面介绍一下如何用51单片机使用I2C接口的OLED12864。
首先介绍一下I2C通信协议,I2C(Inter-Integrated Circuit)字面上的意思是集成电路之间,它其实是I2CBus简称,所以中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。I2C的正确读法为“I平方C”("I-squared-C")。
I2C只使用两条双向漏极开路(Open Drain)(串行数据(SDA)及串行时钟频率(SCL))总线,且利用上拉电阻将两条总线的电位上拉。I2C允许相当大的工作电压范围,但典型的电压准位为+3.3V或+5V。
I2C的参考设计使用一个7比特长度的地址空间但保留了16个地址,所以在一组总线最多可和112个节点通信[a]。常见的I2C总线依传输速率的不同而有不同的模式:标准模式(100 Kbit/s)、低速模式(10 Kbit/s),但时钟频率可被允许下降至零,这代表可以暂停通信。而新一代的I2C总线可以和更多的节点(支持10比特长度的地址空间)以更快的速率通信:快速模式(400 Kbit/s)、高速模式(3.4 Mbit/s)。
我们在51单片机中使用I2C通信协议的时候,需要编写程序去模拟I2C总线的通信,详细的I2C通信协议的介绍可以参考:http://www.51hei.com/bbs/dpj-110328-1.html
对于I2C通信协议,需要补充的一点是:在实际通信传输数据时,SCL总线拉高的时间只要大于1.5μs都能够正常传输数据。
OLED12864的裸屏是由SSD1306驱动的,I2C接口的OLED12864模块对外一共有4个接口,从左到右分别是GND(接地)、VCC(电源正极,可加3.3V,也可加5V)、SCL(时钟总线)、SDA(数据总线):
模块背面的IIC ADRESSSELECT表示该模块在I2C通信作为从机时的地址,当中间的脚用电阻和左边接起来时,地址为0x78,当和右边接起来时,地址为0x7A。
SSD1306的I2C总线数据格式,可以看出,往OLED12864写数据时,先发送一个起始信号,接着发送从机地址,从机地址带有读写位(低电平为写),之后就可以发送指令或数据。在发送指令或数据之前,一般都需要发送一个控制字节,如图,控制字节的最高位为连续位(如果连续位为0,接下来发送的信息只能包含数据字节),次高位为数据/指令选择位(该位声明接下来发送的是数据还是指令,0为指令,1为数据),控制字节的低六位为0。可以在一个声明连续发送数据的控制字节后面跟上多个数据字节。
注释详细的51单片机源程序如下(IIC.c):
#include "IIC.h"
void delay5us()
{
}
void I2C_init() //初始化
{
SDA = 1;
_nop_();
SCL = 1;
_nop_(); //空闲时,两条线均为高电平
}
/*I2C通信起始信号*/
void I2C_start()
{
SCL = 1; //此时主机有总线控制权,先把SCL线拉高
_nop_(); //稳定一下
SDA = 1; //把SDA线拉高,以便发出起始信号(不确定是否为高)
delay5us();//通信协议规定延时大于4.7us
SDA = 0; //拉低SDA线,制造下降沿的起始信号
delay5us();//通信协议规定延时大于4us
}
/*I2C通信终止信号*/
void I2C_stop()
{
SDA = 0; //拉低SDA线,以便发出终止信号
_nop_(); //稳定一下
SCL = 1; //拉高SCL线
delay5us();//通信协议规定延时大于4us
SDA = 1; //拉高SDA线
delay5us();//通信协议规定延时大于4.7us
}
/*从机应答检测*/
bit Test_ack()
{
SCL = 1;
//拉高SCL线,以便主机读取从机发出的应答或非应答信号
delay5us();//通信协议规定延时大于4us
if(SDA)
//主机读取的SDA线为高,说明从机发送了一个非应答信号
{
SCL = 0;/*接下来准备发送停止信号,所以让时钟总线SCL拉低,
让I2C_stop();函数中的SDA可变为0*/
_nop_();//稳定总线
I2C_stop();
return 0;//结束检测从机应答函数
}
else
//主机读取的SDA线为低,说明从机发送了一个应答信号
{
SCL = 0;/*将时钟总线SCL拉低,此时SDA上数据的变化才有效,
因为接下来会继续发数据*/
_nop_();//稳定总线
return 1;
}
}
/*I2C通信:发送一个字节*/
void I2C_send_byte(uint8_t byte)
{
uint8_t i;//声明一个计数变量i
for(i = 0; i < 8; i++)//一个字节有8位,发8次
{
SCL = 0;//拉低SCL,让数据总线SDA变化有效
_nop_();//稳定总线
if(byte & 0x80)//1000 0000 & byte
/*如果(要发送数据的当前传输位)byte的最高位为1,执行该if语句,
如果(要发送数据的当前传输位)byte的最高位为0,不执行该if语句*/
{
SDA = 1;
//(当前传输位)byte的最高位为1,即把1放到SDA线上
_nop_();//稳定总线
}
else
//如果(当前发送位)byte的最高位为0(不为1),给SDA送0
{
SDA = 0;
_nop_();//稳定总线
}
SCL = 1;//拉高SCL线,使从机能够从SDA线上读取到当前的数据
_nop_();//稳定总线
byte <<= 1;
/*使byte左移一位,即原来已被发送到SDA线上的最高位被移除,
第七位(还未发送的数据位)变成最高位变为下一次循环的当前发送位*/
}
SCL = 0;
//发送完数据之后,将SCL拉低,以便从机改变SDA线,发出应答信号
_nop_();//稳定总线
SDA = 1;//释放总线控制权
_nop_();//稳定总线
}
OLED_12864.c
#include "OLED_12864.h"
void Delay300ms()
{
}
/*写指令函数,第一个参数为指令,第二、三个参数选择是否需要通信开始和结束函数,=1有,=0没有*/
bit OLED12864_Write_Commmand(uint8_t cmd, bit start, bit stop)
{
if(start)
{
I2C_start();
I2C_send_byte(OLED_12864_Address+0);//写从机地址,并且加上读写标志位(最后一位)
if(!Test_ack())
{
return 0;
}
/*执行从机应答检测函数,如果从机发送了非应答信号,那么就退出数据发送函数*/
}
I2C_send_byte(0x80 | 0x00 | 0x00); //Co位为1(接下来要传指令),DC为0(接下来是指令)
if(!Test_ack())
{
return 0;
}
/*执行从机应答检测函数,如果从机发送了非应答信号,那么就退出数据发送函数*/
I2C_send_byte(cmd);
if(!Test_ack())
{
return 0;
}
/*执行从机应答检测函数,如果从机发送了非应答信号,那么就退出数据发送函数*/
if(stop)
I2C_stop();
return 1;
}
/*写连续数据函数,第一个参数为数据,第二个参数为发送连多少位连续的数据,第三、四个参数选择是否需要通信开始和结束函数,=1有,=0没有*/
bit OLED12864_Write_Continuous_Data(uint8_t* dat, uint8_t count, bit start, bit stop)
{
uint8_t i = 0;//定义计数变量
if(start)
{
I2C_start();
I2C_send_byte(OLED_12864_Address+0);//写从机地址,并且加上读写标志位(最后一位)
if(!Test_ack())
{
return 0;
}
/*执行从机应答检测函数,如果从机发送了非应答信号,那么就退出数据发送函数*/
}
I2C_send_byte(0x00 | 0x40 | 0x00); //Co位为0(接下来只传数据),DC为1(接下来是数据)
if(!Test_ack())
{
return 0;
}
/*执行从机应答检测函数,如果从机发送了非应答信号,那么就退出数据发送函数*/
for(i = 0 ;i < count ;i++)
{
I2C_send_byte(*dat++);
if(!Test_ack())
{
return 0;
}
/*执行从机应答检测函数,如果从机发送了非应答信号,那么就退出数据发送函数*/
}
if(stop)
I2C_stop();
return 1;
}
/*写相同的连续数据函数,第一个参数为数据,第二参数为发送的次数*/
bit OLED12864_Write_Same_Continuous_Data(uint8_t dat, uint8_t count)
{
uint8_t i = 0;//定义计数变量
I2C_start();
I2C_send_byte(OLED_12864_Address+0);//写从机地址,并且加上读写标志位(最后一位)
if(!Test_ack())
{
return 0;
上一篇:基于51单片机的自行车测速测距程序
下一篇:电子密码锁设计24c01+12864
推荐阅读最新更新时间:2024-11-17 12:38
设计资源 培训 开发板 精华推荐
- LTC4440 的典型应用 - 高速、高电压高压侧栅极驱动器
- 带看门狗定时器的 TB9000F 5V 稳压器的典型应用
- 使用 Richtek Technology Corporation 的 RT8153C 的参考设计
- LT4320HDD 理想二极管桥控制器的典型应用电路
- 使用 Analog Devices 的 LT1184CS 的参考设计
- Sg1526数字控制端口原理图典型应用
- 长方形版-FE.1S一拖四集线器
- NCV7718GEVB:具有保护功能的六角半桥驱动器评估板
- 用于 LCD 显示器的 5V DC 至 DC 单路输出电源
- LTC3630AEMSE 4V 至 63V 输入至 -12V 输出、正负转换器的典型应用电路