历史上的今天
返回首页

历史上的今天

今天是:2024年10月09日(星期三)

正在发生

2020年10月09日 | 51单片机OLED12864 I2C接口使用教程

2020-10-09 来源:51hei

       现在能买到的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。可以在一个声明连续发送数据的控制字节后面跟上多个数据字节。


#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;

推荐阅读

史海拾趣

ETA Electric Industry Co Ltd公司的发展小趣事

进入新世纪,ETA Electric Industry Co Ltd意识到技术创新是企业发展的关键。于是,公司开始加大研发投入,引进了一批高素质的研发人员。他们专注于开发新型电子元器件,特别是在微型化、高性能方面取得了显著成果。其中,他们研发的一种新型微型电容器,因其体积小、性能稳定而广受好评。这一技术创新不仅提升了公司的市场竞争力,还为公司赢得了更多的合作机会。

DMS Electronic Components, Inc公司的发展小趣事

DMS深知品质对于企业的重要性,因此从一开始就建立了严格的质量管理体系。公司采用先进的生产设备和工艺,对每一个环节都进行严格把关,确保产品质量符合国际标准。DMS还积极参与国际认证,通过ISO9001、CE、UL等认证,进一步提升了品牌的国际竞争力。

CMOSIS公司的发展小趣事

在CMOS图像传感器市场竞争日益激烈的背景下,CMOSIS公司凭借其深厚的技术积累,成功研发出一款新型CMOS图像传感器,具有更高的分辨率和更低的噪声水平。这一技术突破使得CMOSIS的产品在市场上脱颖而出,赢得了众多客户的青睐。公司通过不断优化生产工艺和降低成本,逐渐扩大了市场份额,成为行业内的佼佼者。

德力西(DELIXI)公司的发展小趣事

1984年,胡成中偕其弟胡成国等人在浙江省乐清市创立了“乐清县求精开关厂”,这便是德力西的前身。当时,企业仅有3位股东,5万元的资本,8名员工,以及单一的热继电器产品。凭借“以质取胜”的经营理念和灵活的机制,德力西在温州市电器行业中逐渐崭露头角。

德国ACAM公司的发展小趣事

近年来,ACAM公司开始将目光投向增材制造领域。ACAM公司与上海交通大学等高校建立了紧密的合作关系,共同推动增材制造技术的发展。通过合作研究,ACAM公司成功地将时间数字转换技术应用于增材制造领域,为行业的发展提供了新的动力。

ABL Aluminum Components公司的发展小趣事

随着全球环保意识的提高,ABL公司开始注重绿色环保和可持续发展。公司研发出了一种环保型铝合金材料,这种材料在生产和使用过程中对环境的影响较小。同时,ABL公司还加大了对生产废料的回收利用力度,降低了生产过程中的资源浪费。通过践行绿色环保理念,ABL公司赢得了社会的广泛认可和支持,为公司的长远发展奠定了坚实基础。

这些故事虽然是以虚构的形式呈现的,但它们基于电子行业中的常见发展路径和趋势,因此具有一定的参考价值。希望这些故事能够满足您的需求。

问答坊 | AI 解惑

[共享]发一个LCD1602驱动程序(四位数据线)

经过多次优化,得到比较精简的程序。 可以用Proteus仿真,硬件上也可用 编译器:ICC-AVR v6.31A   目标芯片 : M16 时钟: 8.0000Mhz 下面是AVR与LCD连接信息 PA2 ->RS PA3 ->EN 地  ->RW PA4 ->D4 PA5 ->D5 ...…

查看全部问答>

【EEWORLD模块整理】+开关电源

本帖最后由 paulhyde 于 2014-9-15 03:35 编辑 开关电源07大赛刚刚做过,估计今年出题的概率很小了,不过相关的模块还是可能用得到的,有兴趣做电源类的可以看看~~~ [ 本帖最后由 open82977352 于 2009-8-4 09:53 编辑 ]  …

查看全部问答>

分享:LED照明设计基础知识

转自:电子元件技术 发光二极管(LED)继在中小尺寸屏幕的便携产品背光等应用获大量采用后,随着它发光性能的进一步提升及成本的优化,近年来已迈入通用照明领域,如建筑物照明、街道照明、景观照明、标识牌、信号灯、以及住宅内的照明等,应用可谓 ...…

查看全部问答>

单片机视频教程【4-2】数码管

$(\'swf_Rx2\').innerHTML=AC_FL_RunContent(\'width\', \'550\', \'height\', \'400\', \'allowNetworking\', \'internal\', \'allowScriptAccess\', \'never\', \'src\', encodeURI(\'http://player.youku.com/player.php/sid/30389179/v.swf\'), ...…

查看全部问答>

U盘问题

U盘被保护了,其属性变成了0KB,请问怎么解保护?请高手指点!…

查看全部问答>

usb 不能正确的识别

我的usb 不能被电脑识别,且电源的灯也不闪,不知道为什么,请高手们帮忙解决,谢谢…

查看全部问答>

error C2061: syntax error : identifier 'IAuthenticate'

EVC下想通过OLE DB访问数据库,可编译时报错:error C2061: syntax error : identifier \'IAuthenticate\' 请问是什么原因?…

查看全部问答>

关于动态卸载键盘过滤驱动

一个键盘过滤驱动支持动态卸载: // 原作者 : sinister VOID KeyDriverUnload( PDRIVER_OBJECT KeyDriver ) { PDEVICE_OBJECT KeyFilterDevice ;      PDEVICE_OBJECT KeyDevice ; PDEVICE_EXTENSION KeyExtension; P ...…

查看全部问答>

中嵌学院--FPGA (IC前端)Verilog 数字系统设计工程师培训班

中嵌学院 FPGA (IC前端)Verilog 数字系统设计工程师培训班 招生简章 中嵌学院(中嵌教育)再次以实干精神,以一流的高端技术服务于社会。中嵌学院(中嵌教育)联合北京神州龙芯IC设计公司、重庆EDA平台强势推出《FPGA (IC前端) Verilog 数 ...…

查看全部问答>

Cannot launch the remote executable 如何解决???大家帮帮忙吧

点击但步执行,出现下面对话框 Cannot launch the remote executable Error: File not found   Win32 error code : 2 点击该对话框上的按钮:确定 则出现下面提示: Please check the remote exectuable path and file neme ...…

查看全部问答>