标题: 51单片机OLED12864 I2C接口使用教程 含注释详细的源代码 [打印本页]
作者: AceCLee 时间: 2018-3-24 13:09
标题: 51单片机OLED12864 I2C接口使用教程 含注释详细的源代码
OLED12864_I2C接口使用教程_51单片机
现在能买到的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)。
对于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。可以在一个声明连续发送数据的控制字节后面跟上多个数据字节。
作者:Ace
转载请注明作者,谢谢!
发现直接贴代码有问题,,所以就把源代码作为附件传上来。
Oled12864_SRC.rar
(39.86 KB, 下载次数: 869)
注释详细的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;
- }
- /*执行从机应答检测函数,如果从机发送了非应答信号,那么就退出数据发送函数*/
- 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;
- }
- /*执行从机应答检测函数,如果从机发送了非应答信号,那么就退出数据发送函数*/
- }
-
- I2C_stop();
- return 1;
- }
-
- //Set the cursor position of start
- void OLED12864_SetPos(uint8_t x, uint8_t y)
- {
- OLED12864_Write_Commmand(0xb0+y,1,0);
- OLED12864_Write_Commmand(((x&0xf0)>>4)|0x10,0,0);
- OLED12864_Write_Commmand((x&0x0f)|0x01,0,1);
- }
-
- //Fill screen wit data
- //0x00 is black
- //0xff is blue
- void OLED12864_Fill(uint8_t fill_Data)
- {
- uint8_t m;
- //uint8_t n;
- for(m=0;m<8;m++)
- {
- OLED12864_Write_Commmand(0xb0+m,1,0); //page0-page1
- OLED12864_Write_Commmand(0x00,0,0); //low column start address
- OLED12864_Write_Commmand(0x10,0,1); //high column start address
- OLED12864_Write_Same_Continuous_Data(fill_Data, 128);
- }
- }
-
- void OLED12864_CLS_LINE(uint8_t rowIndex)
- {
- uint8_t n=0;
- OLED12864_Write_Commmand(0xb0+rowIndex,1,0); //page0-page1
- OLED12864_Write_Commmand(0x00,0,0); //low column start address
- OLED12864_Write_Commmand(0x10,0,1); //high column start address
- OLED12864_Write_Same_Continuous_Data(0x00, 128);
- }
-
- //clear screen( fill screen with black)
- void OLED12864_CLS(void)
- {
- OLED12864_Fill(0x00);
- }
-
- //--------------------------------------------------------------
- // wake up screen from hibernation
- //--------------------------------------------------------------
- void OLED12864_ON(void)
- {
- OLED12864_Write_Commmand(0X8D,1,0); //set charge
- OLED12864_Write_Commmand(0X14,0,0); //open charge
- OLED12864_Write_Commmand(0XAF,0,1); //OLED wake up
- }
-
- //--------------------------------------------------------------
- // Prototype : void OLED12864_OFF(void)
- // Calls :
- // Parameters : none
- // Description : ?OLED?? -- ?????,OLED????10uA
- //--------------------------------------------------------------
- void OLED12864_OFF(void)
- {
- OLED12864_Write_Commmand(0X8D,1,0); //set charge
- OLED12864_Write_Commmand(0X10,0,0); //close charge
- OLED12864_Write_Commmand(0XAE,0,1); //OLED hibernate
- }
- bit OLED12864_Initial()
- {
- Delay300ms(); // very important delay
-
- OLED12864_Write_Commmand(0xAE,1,0); //display off
- OLED12864_Write_Commmand(0x20,0,0); //Set Memory Addressing Mode
- OLED12864_Write_Commmand(0x10,0,0); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
- OLED12864_Write_Commmand(0xb0,0,0); //Set Page Start Address for Page Addressing Mode,0-7
- OLED12864_Write_Commmand(0xc8,0,0); //Set COM Output Scan Direction
- OLED12864_Write_Commmand(0x00,0,0); //---set low column address
- OLED12864_Write_Commmand(0x10,0,0); //---set high column address
- OLED12864_Write_Commmand(0x40,0,0); //--set start line address
- OLED12864_Write_Commmand(0x81,0,0); //--set contrast control register
- OLED12864_Write_Commmand(0xff,0,0); //???? 0x00~0xff
- OLED12864_Write_Commmand(0xa1,0,0); //--set segment re-map 0 to 127
- OLED12864_Write_Commmand(0xa6,0,0); //--set normal display
- OLED12864_Write_Commmand(0xa8,0,0); //--set multiplex ratio(1 to 64)
- OLED12864_Write_Commmand(0x3F,0,0); //
- OLED12864_Write_Commmand(0xa4,0,0); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
- OLED12864_Write_Commmand(0xd3,0,0); //-set display offset
- OLED12864_Write_Commmand(0x00,0,0); //-not offset
- OLED12864_Write_Commmand(0xd5,0,0); //--set display clock divide ratio/oscillator frequency
- OLED12864_Write_Commmand(0xf0,0,0); //--set divide ratio
- OLED12864_Write_Commmand(0xd9,0,0); //--set pre-charge period
- OLED12864_Write_Commmand(0x22,0,0); //
- OLED12864_Write_Commmand(0xda,0,0); //--set com pins hardware configuration
- OLED12864_Write_Commmand(0x12,0,0);
- OLED12864_Write_Commmand(0xdb,0,0); //--set vcomh
- OLED12864_Write_Commmand(0x20,0,0); //0x20,0.77xVcc
- OLED12864_Write_Commmand(0x8d,0,0); //--set DC-DC enable
- OLED12864_Write_Commmand(0x14,0,0); //
- OLED12864_Write_Commmand(0xaf,0,1); //--turn on oled panel
-
- OLED12864_CLS();
- return 1;
- }
-
- //--------------------------------------------------------------
- // show string
- // x,y -- start position(x:0~127,column; y:0~7,row);
- // ch[] -- the string to show;
- // TextSize -- (1:6*8 ; 2:8*16)
- //OLED12864_ShowStr(0,3,"I2C Test",1);// 6*8
- //OLED12864_ShowStr(0,4,"Hello Delta",2) //8*16
- //--------------------------------------------------------------
- void OLED12864_ShowStr(uint8_t x, uint8_t y, uint8_t ch[], uint8_t TextSize)
- {
- uint8_t c = 0,i = 0,j = 0;
- switch(TextSize)
- {
- case 1:
- {
- while(ch[j] != '\0' && ch[j] !='\n')
- {
- c = ch[j] - 32;
- if(x > 126)
- {
- x = 0;
- y++;
- }
- OLED12864_SetPos(x,y);
- /*for(i=0;i<6;i++)
- OLED12864_Write_Data(F6x8[c][i],1,1);*/
- OLED12864_Write_Continuous_Data(&F6x8[c][0],6,1,1);//将该位的指针传给函数,并且写连续的6个数据即可
- x += 6;
- j++;
- }
- }break;
- case 2:
- {
- while(ch[j] != '\0' && ch[j] !='\n')
- {
- c = ch[j] - 32;
- if(x > 120)
- {
- x = 0;
- y++;
- }
- OLED12864_SetPos(x,y);
- /*for(i=0;i<8;i++)
- OLED12864_Write_Data(F8X16[c*16+i],1,1);
- OLED12864_SetPos(x,y+1);
- for(i=0;i<8;i++)
- OLED12864_Write_Data(F8X16[c*16+i+8],1,1);*/
- OLED12864_Write_Continuous_Data(&F8X16[c*16],8,1,1);//将该位的指针传给函数,并且写连续的8个数据即可
- OLED12864_SetPos(x,y+1);
- OLED12864_Write_Continuous_Data(&F8X16[c*16+8],8,1,1);//将该位的指针传给函数,并且写连续的8个数据即可
- x += 8;
- j++;
- }
- }break;
- }
- }
- uint8_t rowNum = 0;
-
- //--------------------------------------------------------------
- // x,y -- start position(x:0~127,column; y:0~7,row);
- // the N is the chinese index of F16x16 in codetab
- /*
- for(i=0;i<5;i++)
- {
- OLED12864_ShowCN(22+i*16,0,i);
- }
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
作者: jjj12345 时间: 2018-4-16 05:17
不错,可以参考,看能不能搞出svr程序
作者: pavlos96 时间: 2018-7-19 10:54
好像很有趣,想下载看看
作者: pavlos96 时间: 2018-7-24 09:41
可不可以在程序里建一个字库
作者: guangshi_wq 时间: 2018-7-28 01:15
你的东西搞得我好累啊.好不容易才搞好了.看看我弄的效果,有图有真象
-
IMG_0193.JPG
(2.01 MB, 下载次数: 177)
-
-
OLED12864.zip
74.58 KB, 下载次数: 421, 下载积分: 黑币 -5
作者: sd19941129 时间: 2018-10-27 10:11
guangshi_wq 发表于 2018-7-28 01:15
你的东西搞得我好累啊.好不容易才搞好了.看看我弄的效果,有图有真象
确实不错,找了超长时间的资料。
作者: heropp 时间: 2018-12-14 00:45
真透彻的资料
作者: GG555 时间: 2018-12-25 22:16
很不错的程序
作者: 大雨 时间: 2018-12-26 16:42
很有帮助
作者: 大雨 时间: 2018-12-29 15:23
好东西,好好学习
作者: 梁光阳 时间: 2018-12-29 19:21
可以的,我就在用
作者: awsl7758258 时间: 2019-3-11 12:42
清楚,可以
作者: 51.h 时间: 2019-3-27 22:40
厉害了,老哥
作者: azad 时间: 2019-4-3 11:25
顶一下!
作者: hufeng 时间: 2019-4-3 17:03
顶一下,问一下为什么没有主函数
作者: 有朋自远方来 时间: 2019-4-10 21:03
很给力!
作者: 晓雾将歇 时间: 2019-4-10 23:02
很好的东西,学到了
作者: 晓雾将歇 时间: 2019-4-10 23:02
看了之后感觉很有用,给力
作者: techhuai 时间: 2019-4-15 00:15
谢谢老哥。棒
作者: ptlantu 时间: 2019-4-15 10:21
感谢分享
作者: 1146734128 时间: 2019-4-15 12:59
感谢分享
作者: 小敢心 时间: 2019-4-24 16:51
很给力
作者: junxuqiqi 时间: 2019-5-1 16:34
谢谢楼主分享好东西
作者: qazqaz111 时间: 2019-6-6 21:06
可以可以
作者: flying8702 时间: 2019-6-13 01:23
代码注释很全,也很规范,值得学习
作者: 凌殇 时间: 2019-6-19 10:58
非常的好
作者: lxy04231 时间: 2019-7-14 12:17
谢谢分享!
作者: 奇异的想法 时间: 2019-7-17 16:08
非常好,感谢LZ
作者: 初学者10086 时间: 2019-7-18 16:45
谢谢分享,前来学习。
作者: 絮絮叨叨的 时间: 2019-7-24 17:04
厉害了,大佬
作者: 7631001 时间: 2019-8-25 16:40
不错,不错,可以参考的代码
作者: n1oble 时间: 2019-8-26 21:51
不太明白为什么第一行是黄色的 后面的是蓝色呢 这个是如何设置的啊
作者: 哟呵呵呵呵 时间: 2019-8-30 00:35
大佬,厉害了,感谢分享
作者: 啊哈哈哈哈III 时间: 2019-8-30 21:43
为什么65行data要++呢?(小白一个
)
作者: dd2dd 时间: 2019-8-31 00:31
谢谢分享,不错不错
作者: 王秋冬 时间: 2019-8-31 09:28
代码注释很全,也很规范,值得学习,确实很详细的分析了,有这样的教程,是不是学习更给力。
作者: 鹏博士PBs 时间: 2019-8-31 17:01
感谢楼主分享
作者: wanzhikun 时间: 2019-9-23 16:47
想把这个OLED玩好没这么简单,以前用过LCD1602/LCD12864。现在学习STM32,看了正点原子的视频,感觉视频里讲的只是一个点。程序为什么要这样写。怎么写。把每个函数都写一遍,都能讲很久。感觉正点原子的原子和视频有点坑了。或着是我学艺不精。
作者: dianzi1987 时间: 2019-10-21 10:01
发现了学单片机一定要来51hei,这里的资料真棒。
作者: 619136674 时间: 2019-10-25 10:24
-
-
SH1106 V2.3.pdf
736.37 KB, 下载次数: 31, 下载积分: 黑币 -5
作者: 619136674 时间: 2019-10-25 15:39
谢谢老哥!
作者: xc2016133226 时间: 2019-12-11 18:14
感谢分享,很详细
作者: dd2dd 时间: 2019-12-18 16:19
感谢分享,感谢感谢感谢
作者: lyl_420819 时间: 2019-12-22 10:49
很好的OLED资料,但我还没有下载就扣分了,不知何故,惶恐!
作者: heyanjun1223 时间: 2020-1-20 17:52
厉害了我的哥
作者: heyanjun1223 时间: 2020-1-20 17:52
感谢楼主分享
作者: JASSON588 时间: 2020-3-4 10:48
注释很详细,很赞,对我帮助很大!!
作者: sjzsyd 时间: 2020-4-30 11:22
感谢分享,代码注释很全,也很规范,值得学习,赞一个!
作者: ricebucket 时间: 2020-5-26 16:09
正在关注这个OLED屏的使用方法,楼主这篇分享很实用,代码注释很详细,值得学习,点赞并感谢!
作者: lizhendong 时间: 2020-5-27 21:36
支持支持一下,谢谢了
作者: 912865498@qq.co 时间: 2020-5-28 09:51
添加代码在keil5运行有10warning
作者: tilblackout 时间: 2020-7-6 19:22
多谢,可是不会弄到51中去
作者: shinezhangxiao 时间: 2020-7-8 02:57
说明如此详细者少见!点个赞!


作者: wtryb 时间: 2020-7-13 08:25
I2C_send_byte(0x80 | 0x00 | 0x00);大佬这句什么意思???
作者: a837213699 时间: 2020-7-30 09:23
太难了,写的很棒
作者: jafeco1 时间: 2020-8-3 23:58
OLED需要的资料,感谢分享
作者: ddsone 时间: 2020-8-4 00:15
新手来围观大虾
作者: LDHMMX 时间: 2020-8-5 02:49
请问下大神们,这种屏有办法做成能播放GIF动画吗?
作者: 腾飞的龙 时间: 2020-10-9 16:12
感谢分享,楼主辛苦。学习啦。
作者: aa.123 时间: 2021-4-11 09:30
学习学习
作者: 3486635230 时间: 2021-4-13 21:40
很有用, spi的程序已经搞定了
作者: 菜鸟yc 时间: 2021-4-14 11:02
这个不是设置的,是买这个oled屏的时候选型的。也就是不是软件设置的,是硬件决定的。
作者: usaboy 时间: 2021-4-15 08:32
Great Job, thank you!
作者: jmmakeit 时间: 2024-4-26 21:57
厉害
作者: simulinkzwl 时间: 2024-5-24 10:18
不错,可以参考,我要显示gps数据。
欢迎光临 (http://www.51hei.com/bbs/) |
Powered by Discuz! X3.1 |