IIC通信原理
IIC总线物理层特点
总线通过上拉电阻接到电源。当IIC设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态,由上拉电阻把总线拉成高电平。
多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定哪个设备占用总线。
具有三种传输模式:标准模式传输速率为100kbit/s,快速模式为400kbit/s,高速模式下可达3.4M/s,但目前大多IIC设备尚不支持高速模式。一般情况下使用的都是400kbit/s
硬件IIC
硬件IIC:对应芯片上的IIC外设,有相对应的IIC驱动电路,其所使用的IIC管教也是专用的;
软件IIC
软件IIC:一般是用GPIO管教,用软件控制管脚状态以及模拟IIC通信波形;
区别
硬件IIC的效率要远高于软件的,而软件IIC不受引脚限制,接口比较灵活。
软件IIC是通过GPIO,软件模拟寄存器的工作方式,而硬件IIC是直接调用内部寄存器进行配置。
如果要从具体硬件上来看,可以去看下芯片手册。因为固件IIC的端口是固定的,所以会有所区别。
区分
1.硬件IIC用法复杂,模拟IIC流程更加清楚
2.硬件IIC速度比模拟快,并且可以用DMA
3.模拟IIC可以在任何管脚上,硬件IIC在固定管脚上
IIC总线协议层
S:数据由主机传输至从机
P :数据传输结束
SLAVE ADDRESS : 从机地址 起始信号产生后,所有从机就开始紧接下来广播的从机地址信号。IIC总线,每个设备的地址都是唯一的,当主机广播的地址与某个设备的地址相同时,这个设备就被选中了,没被选中的设备讲会忽略之后的数据信号。根据IIC协议,这个从机地址可以是7位或10位。
地址位之后,传输方向选择位,为0:表示数据传输方向是由主机传输至从机,即主机向从机写数据。为1:则相反。从机接收传输方向选择位后,主机或从机会返回一个应答(ACK)或非应答(NACK)信号,只有接收到应答信号后,主机才能继续发送或接收数据。
读数据: 配置方向传输位为”读数据”方向。广播完地址后,接收到应答信号后,从机开始向主机返回数据(DATA),数据包大小也为8位,从机每发送完一个数,都会等待主机的应答信号(ACK),重复这个过程,可以返回N个数据,N没有限制大小。当主机希望停止接收数据时,就向从机返回一个非应答信号(NCAK),则从机自动停止数据传输。
复合格式,该传输过程有两次起始信号(S),在第一次传输过程中,主机通过SLAVE_ADDRESS寻找到从设备后,发送一段”数据”,这段数据通常用于表示从设备内部的寄存器或存储器地址;第二次传输中,对该地址的内容进行读或写。也就是说,第一次通讯是告诉从机读写地址,第二次则是读写的实际内容。
IIC通信
空闲状态
IIC总线的SDA和SCL两条信号线同时处于高电平时,规定位总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
开始信号
起始信号:当SCL为高电平期间,SDA有高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
停止信号
停止信号:当SCL为高电平期间,SDA由低到高的跳变;停止信号也是一种高电平跳变时序信号,而不是一个电平信号
应答信号
发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位) 表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。 对于反馈有效应答位ACK的要求是,接收器在第九个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。 如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。
数据的有效性
IIC总线进行数据传输时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。SDA数据线在SCL的每个时钟周期传输一位数据。
即:数据在SCL的上升沿到来之前就需准备好。并在下降沿到来之前必须稳定
数据传输
在IIC总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。
主发送器
控制产生起始信号(S),当发生起始信号后,它产生事件”EV5”,并会对SR1寄存器的 SB 位置1,表示起始信号已经发生。 ·
发生设备地址并等待应答信号,若有从机应答,则产生时间 EV6 及 EV8 ,这时SR1寄存器的 ADDR位及 TXE位被置1,ADDR位1 表示地址已经发送,TEX表示数据寄存器为空。
OLED屏幕
OLED即有机发光管(Organic Light-Emitting Diode,OLED)。OLED显示技术具有自发光、广视角、几乎无穷高的对比度、较低功耗、极高反应速度、可用于绕曲性面板、使用温度范围广、构造及制程简单等有点,被认为是下一代的平面显示屏新兴应用技术。 · OLED显示和传统的LCD显示不同,其可以自发光,所以不需要背光灯,这使得OLED显示屏相对于LCD显示屏尺寸更薄,同时显示效果更优。 常用的OLED屏幕有蓝色、黄色、白色等几种。屏的大小为0.96寸,像素点为 128*64,所以我们称为0.96oled屏或者12864屏。
OLED屏幕特点
1.模块尺寸:23.7 *23.8mm 2.电源电压:3.3-5.5V 3.驱动芯片:SSD1306 4.测试平台:提供 k60/k10,9s12XS128,51,stm32,stm8等单片机
OLED屏幕常用指令
命令0X81: 设置对比度。包含两个字节,第一个0X81为命令,随后方法是的一个字节要设置这个对比度,值越大屏幕越亮。 ·
命令0XAE/0XAF: 0XAE为关闭显示命令,0XAF为开启显示命令
0X8D: 包含两个字节,第一个为命令字,第二个为设置值,第二个字节的BIT2表示电荷泵的开关状态,该位为1开启电荷泵,为0则关闭。模块初始化的时候,这个必须要开启,否则看不到屏幕显示。
命令0XB0~B7:用于设置页地址,其低三位的值对应GRAM页地址。 ·
命令0X00~0X0F:用于设置显示时的起始列地址低四位。
命令0X10~0X1F: 用于设置显示时的起始列地址高四位。
代码实现
GPIO初始化
void I2C_Configuration(void)
{
I2C_InitTypeDef I2C_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE );
//PB6 --SCL ;PB7 --SDA
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_DeInit(I2C1);
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 400000 ;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2 ;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_OwnAddress1 = 0X30 ;
I2C_Init(I2C1,&I2C_InitStructure );
I2C_Cmd(I2C1,ENABLE);
}
写一个字节
void I2C_WriteByte(uint8_t addr,uint8_t data)
{
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); //¼ì²éI2C×ÜÏßÊÇ·ñ·±Ã¦
I2C_GenerateSTART(I2C1, ENABLE); //¿ªÆôI2C1
while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //EV5,Ö÷ģʽ
I2C_Send7bitAddress(I2C1,OLED_ADDRESS, I2C_Direction_Transmitter); //·¢ËÍÆ÷¼þµØÖ·
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1, addr); //¼Ä´æÆ÷µØÖ·
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING));
I2C_SendData(I2C1, data); //·¢ËÍÊý¾Ý
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING));
I2C_GenerateSTOP( I2C1, ENABLE); //¹Ø±ÕI2C×ÜÏß
}
写数据
void WriteDat(unsigned char I2C_Data)
{
I2C_WriteByte(0x40,I2C_Data);
}
写命令
void WriteCmd(unsigned char I2C_Command)
{
I2C_WriteByte(0X00,I2C_Command);
}
OLED屏幕初始化
这个是厂家提供的代码直接使用即可
void OLED_Init(void)
{
delay_ms(100);
WriteCmd(0xAE); //display off
WriteCmd(0x20); //Set Memory Addressing Mode
WriteCmd(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
WriteCmd(0xb0); //Set Page Start Address for Page Addressing Mode,0-7
WriteCmd(0xc8); //Set COM Output Scan Direction
WriteCmd(0x00); //---set low column address
WriteCmd(0x10); //---set high column address
WriteCmd(0x40); //--set start line address
WriteCmd(0x81); //--set contrast control register
WriteCmd(0xff); //ÁÁ¶Èµ÷½Ú 0x00~0xff
WriteCmd(0xa1); //--set segment re-map 0 to 127
WriteCmd(0xa6); //--set normal display
WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
WriteCmd(0x3F); //
WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
WriteCmd(0xd3); //-set display offset
WriteCmd(0x00); //-not offset
WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
WriteCmd(0xf0); //--set divide ratio
WriteCmd(0xd9); //--set pre-charge period
WriteCmd(0x22); //
WriteCmd(0xda); //--set com pins hardware configuration
WriteCmd(0x12);
WriteCmd(0xdb); //--set vcomh
WriteCmd(0x20); //0x20,0.77xVcc
WriteCmd(0x8d); //--set DC-DC enable
WriteCmd(0x14); //
WriteCmd(0xaf); //--turn on oled panel
}
设置起始坐标
void OLED_SetPos(unsigned char x,unsigned char y)
{
WriteCmd(0xb0 +y);
WriteCmd((x&0xf0)>>4|0x10);
WriteCmd((x&0x0f)|0x01);
}
全屏填充
void OLED_Fill(unsigned char Fill_Data)
{
unsigned char m,n;
for(m=0;m<8;m++)
{
WriteCmd(0xb0+m);
WriteCmd(0x00);
WriteCmd(0x10);
for(n=0;n<128;n++)
{
WriteDat(Fill_Data);
}
}
}
清屏
void OLED_CLS(void)
{
OLED_Fill(0x00);
}
OLED打开
void OLED_ON(void)
{
WriteCmd(0X8D); //ÉèÖõçºÉ±Ã
WriteCmd(0X14); //¿ªÆôµçºÉ±Ã
WriteCmd(0XAF); //OLED»½ÐÑ
}
OLED关闭
void OLED_OFF(void)
{
WriteCmd(0X8D); //ÉèÖõçºÉ±Ã
WriteCmd(0X10); //¹Ø±ÕµçºÉ±Ã
WriteCmd(0XAE); //¹Ø±ÕOLED
}
显示字符串
void OLED_ShowStr(unsigned char x,unsigned y,unsigned char ch[],unsigned TextSize)
{
unsigned char c = 0,i = 0,j = 0;
switch(TextSize)
{
case 1:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x>126)
{
x= 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<6;i++)
WriteDat( F6x8[c][i] );
x+=6 ;
j++;
}
}break;
case 2:
{
while(ch[j] !='\0')
{
c= ch[j] - 32;
if(x >120)
{
x = 0;
y++ ;
}
OLED_SetPos(x,y);
for(i = 0;i<8;i++)
WriteDat( F8X16[c*16+i] );
OLED_SetPos(x,y+1);
for(i = 0;i<8;i++)
WriteDat( F8X16[c*16+i+8] );
x+=8;
j++;
}
}break;
}
}
显示汉字
这个需要一个软件PCtoLCD2002,128x64取字软件
点击模式选择字符模式
点击选项,按照以下方式配置
完成后,点击确定
输入你想生成的内容,并点击生成字模
将其复制到自己的代码中,
void OLED_ShowCN(unsigned char x,unsigned char y,unsigned char N)
{
unsigned char wm=0;
unsigned int addr = 32*N;//Ò»¸ö×ÖÓÐ32λ±íʾ
OLED_SetPos(x,y);
for(wm=0;wm<16;wm++)
{
WriteDat( F16X16[addr]);
addr +=1;
}
OLED_SetPos(x,y+1);
for(wm=0;wm<16;wm++)
{
WriteDat( F16X16[addr]);
addr +=1;
}
}
注意显示文字,主要看有几个字,在主函数的for循环中就循环几次,我这里显示是五个字,就循环五次
显示图片
随机选一张图片,最好是黑白的
右键点击编辑,画图
重新调整一下大小(统称为128x64),再点击文件另存为.bmp格式
点击打开,设置完成后,点击保存,把生成的txt文档当中的内容复制到自己的代码中
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
unsigned int j=0;
unsigned char x,y;
if(y1%8 == 0)//È·¶¨ÊDz»ÊÇ8µÄ±¶Êý
y = y1/8;
else
y = y1/8 +1;
for(y=y0;y<y1;y++)
{
OLED_SetPos(x0,y);
for(x=x0;x<x1;x++)
{
WriteDat(BMP[j++]);
}
}
}