1 概述:
IIC是用两条双向的线,一条SDA(serial data line),一条SCL(serial clock).
SCL:上升沿将数据输入到每个EEPROM器件中,下降沿驱动EEPROM器件输出数据(边沿触发)
SDA:双向数据线,为OD门,与其它任意数量的OD与OC门成“线与”关系
2 输出级
每一个IIC总线器件内部的SDA、SCL引脚电路结构都是一样的,引脚的输出驱动与输入缓存连在一起。其中输出为漏极开路的场效应管,输入缓存为一只高输入阻抗的同向器,这种电路具有两个特点
1)由于SDA、SCL为漏极开路结构,因此它们必须接有上拉电阻,阻值大小为1K8(1800o)、4K7(4700o)、10K,但1K8时性能最好;当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线与关系。
2)引脚在输出信号的同时还将引脚上的电平进行检测,检测是否与刚才输出一致,为“时钟同步”和“总线仲裁”提供了硬件条件
3 主设备与从设备
系统中的所有外围器件都具有一个7位的“从器件专用地址码”,其中高4位为器件类型,由生产厂家制定,低3位为器件引脚定义地址,由使用者定义。主控器件通过地址码建立多机通信的机制,因此IIC总线省去了外围器件的片选线,这样无论总线上挂接多少个器件,其系统仍然为简约的二线结构。终端挂载在总线上,有主端和从端之分,主端必须是带有CPU逻辑模块,在同一总线上同一时刻使能有一个主端,可以有多个从端,从端的数量受地址空间和总线的最大电容400pF的限制。
主端主要用来驱动SCL line;
从设备对主设备产生响应;
二者都可以传输数据,但是从设备不能发起传输,且传输是受到主设备控制的。
4 速率
普通模式:100kHz
快速模式:400kHz
高速模式:3.4MHz
5协议
5.1 空闲状态
IIC总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个期间的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
5.2 起始信号与停止信号
起始信号:当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号
停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号
5.3 ACK
发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。
如下图逻辑分析仪的采样结果:释放总线后,如果没有应答信号,SDA应该一直维持在高电平,但是如图中蓝色虚线部分所示,它被拉低为低电平,证明收到了应答信号。
总结:1)接收器在SCL的上升沿到来之前的低电平期间拉低SDA;
2)应答信号一直保持到SCL的下降沿结束;
5.4 数据的有效性
IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
个人理解:虽然只要求在高电平期间保持稳定,但是要有一个提前量,也就是数据在SCL的上升沿到来之前就准备好,因为数据在SCL的上升沿打入到器件(EEPROM)中的。
5.5 数据的传送:
在IIC总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发.
6 工作过程
总线上的所有通信都是由主控器引发的。在一次通信中,主控器与被控器总是在扮演着两种不同的角色。
6.1 主设备向从设备发送数据
主设备发送起始位,这会通知总线上的所有设备传输开始了,接下来主机发送设备地址,与这一地址匹配的slave将继续这一传输过程,而其他slave将会忽略接下来的传输并等待下一次传输开始。主设备寻址到从设备后,发送它所要读取或写入的从设备的内部寄存器地址;之后,发送数据。数据发送完毕后,发送停止位。
写入过程如下:
1 发送起始位
2 发送从设备的地址和读/写选择位;释放总线,等到EEPROM拉低总线进行应答;如果EEPROM接收成功,则进行应答;若没有握手成功或者发送的数据错误时EEPROM不产生应答,此时要求重发或者终止
3 发送想要写入的内部寄存器地址,EEPROM对其发出应答
4 发送数据
5 发送停止位
6 EEPROM收到停止信号后,进入到一个内部的写入周期,大概需要10ms,此期间任何操作都不会被EEPROM响应:(因此以这种方式的两次写入之间要插入一个延时,否则会导致失败)
详细
说明:主控器通过发送地址码与对应的被控器建立了通信关系,而挂接在总线上的其他被控器虽然同时也收到了地址码,但因为与其自身的地址不相符合,因此提前退出与主控器的通信
6.2 主控器读取数据的过程
在从slave读取数据前,必须先要告诉它那个内部寄存器是你想要读取的,因此必须先对其进行写入(dummy write)
读取过程
1 发送起始位
2 发送slave地址 + write bit set
3 发送内部寄存器地址
4 重新发送起始位,即restart
5 重新发送slave地址 + write bit set
6 读取数据
主机接收器在接收到最后一个字节后,也不会发出ACK信号。于是,从机发送器释放SDA线,以允许主机发出P信号结束传输。
7 发送停止位
详细
6.3 页写
6.3.1 页写数据过程
不同的器件页写的字节数也不同,2402是按8字节/页写,2404-08-16是按16字节/页写。而2432-2464则是按32字节页写;
页写初始化和字节写相同,只是主器件不会在第一个数据后发送停止条件,而是EEPROM的ACK以后,接着发送(7个)、(15个)、(31个)数据。EEPROM在收到数据后每个都应答0,最后仍需要主器件发送停止条件。终止页写条件。
在接收或发送每个数据后,字地址的(低3位)、(低4位)、(低5位)内部自动加1,高位地址不变,维持当前页。当内部产生的字地址达到该页边界地址时,随后的数据将写入该页的页首,这是会将先前的字节覆盖掉,要注意。
6.3.2 读页数据过程
主器件接收到一个数据后,应答ACK。只要EEPROM接收到ACK,将自动增加字地址并继续随时钟发送后面的数据。若达到存储器的地址末尾,地址自动回转到0.仍可继续顺序读取数据。主器件不应答0,而发送停止条件,即可结束顺序读操作。
7 实例程序
1 void SCL_Init() 2 { 3 GPIO_InitTypeDef GPIO_InitStructure; 4 5 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); 6 7 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; 8 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; 9 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; 10 GPIO_Init(GPIOB,&GPIO_InitStructure); 11 } 12 13 void SDA_Out_Init() 14 { 15 GPIO_InitTypeDef GPIO_InitStructure; 16 17 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); 18 19 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11; 20 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; 21 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; 22 GPIO_Init(GPIOB,&GPIO_InitStructure); 23 } 24 25 void SDA_In_Init() 26 { 27 GPIO_InitTypeDef GPIO_InitStructure; 28 29 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); 30 31 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11; 32 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; 33 GPIO_Init(GPIOB,&GPIO_InitStructure); 34 } 35 36 void IIC_Init() //初始化总线 37 { 38 SCL_OUT=1; 39 SDA_OUT=1; 40 delay_us(2); 41 } 42 43 void IIC_Start() //开始发送 SCL为高,SDA由高变低 44 { 45 SDA_Out_Init(); 46 SCL_OUT=1; 47 SDA_OUT=1; 48 49 delay_us(5); 50 SDA_OUT=0; 51 delay_us(6); 52 53 SCL_OUT=0; 54 } 55 56 void IIC_Stop() //停止 SCL为高,SDA由低变高 57 { 58 SDA_Out_Init(); 59 SCL_OUT=0; 60 SDA_OUT=0; 61 SCL_OUT=1; 62 delay_us(6); 63 64 SDA_OUT=1; 65 delay_us(6); 66 } 67 68 u8 IIC_Wait_Ack() //等待应答 69 { 70 u8 time=0; 71 72 SDA_OUT=1; 73 delay_us(1); 74 SDA_In_Init(); 75 SCL_OUT=1; 76 delay_us(1); 77 while(SDA_IN) //#define SDA_IN PBin(11) 78 { 79 time++; 80 if(time>250) //如果超过250还无应答,IIC停止 81 { 82 IIC_Stop(); 83 return 1; 84 } 85 } 86 SCL_OUT=0; 87 return 0; 88 } 89 90 void IIC_Ack() //应答信号 91 { 92 SCL_OUT=0; 93 SDA_Out_Init(); 94 SDA_OUT=0; 95 delay_us(2); 96 97 SCL_OUT=1; 98 delay_us(5); 99 SCL_OUT=0; 100 } 101 102 void IIC_NAck() //非应答 103 { 104 SCL_OUT=0; 105 SDA_Out_Init(); 106 SDA_OUT=1; 107 delay_us(2); 108 109 SCL_OUT=1; 110 delay_us(5); 111 SCL_OUT=0; 112 } 113 114 void IIC_Write_Byte(u8 data) //写一个字节数据 115 { 116 int i,receive=0; 117 SDA_Out_Init(); 118 119 receive=data; 120 SCL_OUT=0; 121 for(i=0;i<8;i++) //相当于SCL低电平期间准备好要写入的数据,SCL跳变为高电平进行发送 122 { 123 if(receive&0x80) 124 SDA_OUT=1; 125 else 126 SDA_OUT=0; 127 receive=receive<<1; 128 delay_us(2); 129 130 SCL_OUT=1; 131 delay_us(2); 132 SCL_OUT=0; 133 delay_us(2); 134 } 135 } 136 137 u8 IIC_Read_Byte(u8 ack) //读一个字节 138 { 139 int i,txdata=0; 140 SDA_In_Init(); 141 142 SCL_OUT=0; // 143 delay_us(2); 144 for(i=0;i<8;i++) 145 { 146 SCL_OUT=1; //高电平保持数据的稳定;来一个上升沿,数据就将要读取的一位放在数据线SDA上,所以接下来就得左移,才能读取下一位数据 147 delay_us(2); 148 txdata=txdata<<1; 149 if(SDA_IN) 150 txdata++; 151 delay_us(1); 152 153 SCL_OUT=0; 154 delay_us(2); 155 } 156 if(!ack) 157 IIC_NAck(); 158 else 159 IIC_Ack(); 160 return txdata; 161 } 162 163 void write_addr(u8 address,u8 data) 164 { 165 IIC_Start(); 166 IIC_Write_Byte(0xA0); //发送从器件芯片的地址,由于IIC可以挂接好多芯片,所以需要先找到该芯片 167 IIC_Ack(); 168 169 IIC_Write_Byte(address);//发送芯片的字节地址,需要确定将数据发送到芯片的某个位置,芯片大小有2K 170 IIC_Ack(); 171 172 IIC_Write_Byte(data); //将数据写入到芯片内 173 IIC_Ack(); 174 175 IIC_Stop(); 176 delay_ms(10); //数据写完之后需等10ms时间才能再次发送起始信号,这个时间叫写周期 177 } 178 179 u8 read_addr(u8 address) 180 { 181 u8 temp; 182 183 IIC_Start(); 184 IIC_Write_Byte(0xA0); //器件地址 185 IIC_Ack(); 186 IIC_Write_Byte(address);//想要读取的器件中的地址0到255 187 IIC_Ack(); 188 189 IIC_Start(); 190 IIC_Write_Byte(0xA1); //1表示读取 191 IIC_Ack(); 192 193 temp=IIC_Read_Byte(0); 194 IIC_Ack(); 195 196 IIC_Stop(); 197 198 return temp; 199 } 200 201 void write_len(u16 address,u32 data,u8 len) 202 { 203 u8 i; 204 for(i=0;i<len;i++) 205 write_addr(address+i,(data<<(8*i)&0xff)); 206 } 207 208 u32 read_len(u16 address,u8 len) 209 { 210 u8 j; 211 u32 temp; 212 for(j=0;j<len;j++) 213 { 214 temp=temp<<8; 215 temp=temp+read_addr(address+len-j-1); 216 } 217 return temp; 218 } 219 220 /************************************** 221 将数据写入指定的地址里,num要写入数据个数 222 *************************************/ 223 void write(u8 addr,u8 *buffer,u8 num) 224 { 225 while(num--) 226 { 227 write_addr(addr,*buffer); //含有了stop 228 addr++; 229 buffer++; 230 } 231 } 232 233 /************************************** 234 在指定的地址读取数据 235 *******************************************/ 236 u8 read(u8 addr,u8 *buffer,u8 num) 237 { 238 while(num) 239 { 240 *buffer++=read_addr(addr++); 241 num--; 242 } 243 return *buffer; 244 } 245 246 void write_twobyte(u8 address) 247 { 248 u8 i; 249 u8 tx[3]={11,12,13}; 250 IIC_Start(); 251 IIC_Write_Byte(0xA0); //发送的是从器件芯片地址,由于IIC可以挂接好多芯片,所以需要先找到该芯片 252 IIC_Ack(); 253 254 IIC_Write_Byte(address);//发送芯片的字节地址,需要确定将数据发送到芯片的某个位置,芯片大小2K 255 IIC_Ack(); 256 257 for(i=0;i<3;i++) 258 { 259 IIC_Write_Byte(tx[i]); //将数据写入到芯片内 260 IIC_Ack(); 261 } 262 263 IIC_Stop(); 264 delay_ms(10); 265 }