概述
I2C简介
I2C是由Philips公司发明的一种串行数据通信协议,仅使用两根信号线:SerialClock(简称SCL)和SerialData(简称SDA)。I2C是总线结构,1个Master,1个或多个Slave,各Slave设备以7位地址区分,地址后面再跟1位读写位,表示读(=1)或者写(=0),所以我们有时也可看到8位形式的设备地址,此时每个设备有读、写两个地址,高7位地址其实是相同的。
I2C的数据格式
无数据:SCL=1,SDA=1;
开始位(Start):当SCL=1时,SDA由1向0跳变;
停止位(Stop):当SCL=1时,SDA由0向1跳变;
数据位:当SCL由0向1跳变时,由发送方控制SDA,此时SDA为有效数据,不可随意改变SDA;
当SCL保持为0时,SDA上的数据可随意改变;
地址位:定义同数据位,但只由Master发给Slave;
应答位(ACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=0;
否应答位(NACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=1。
当数据为单字节传送时,格式为:
开始位,8位地址位(含1位读写位),应答,8位数据,应答,停止位。
当数据为一串字节传送时,格式为:
开始位,8位地址位(含1位读写位),应答,8位数据,应答,8位数据,应答,……,8位数据,应答,停止位。
几点注意事项
SCL一直由Master控制,SDA依照数据传送的方向,读数据时由Slave控制SDA,写数据时由Master控制SDA。当8位数据传送完毕之后,应答位或者否应答位的SDA控制权与数据位传送时相反。
开始位“Start”和停止位“Stop”,只能由Master来发出。
地址的8位传送完毕后,成功配置地址的Slave设备必须发送“ACK”。否则否则一定时间之后Master视为超时,将放弃数据传送,发送“Stop”。
当写数据的时候,Master每发送完8个数据位,Slave设备如果还有空间接受下一个字节应该回答“ACK”,Slave设备如果没有空间接受更多的字节应该回答“NACK”,Master当收到“NACK”或者一定时间之后没收到任何数据将视为超时,此时Master放弃数据传送,发送“Stop”。
当读数据的时候,Slave设备每发送完8个数据位,如果Master希望继续读下一个字节,Master应该回答“ACK”以提示Slave准备下一个数据,如果Master不希望读取更多字节,Master应该回答“NACK”以提示Slave设备准备接收Stop信号。
当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。
在实际应用中,并没有强制规定数据接收方必须对于发送的8位数据做出回应,尤其是在Master和Slave端都是用GPIO软件模拟的方法来实现的情况下,编程者可以事先约定数据传送的长度,slave不检查NACK,有时可以起到减少系统开销的效果。但是如果slave方是硬件i2c要求一定要标准的NACK,master方是GPIO软件模拟i2c并没有正确的发送NACK,就会出现“slave收不到stop”导致i2c挂死。
---------------------
作者:liebecl
来源:CSDN
原文:https://blog.csdn.net/liebecl/article/details/76563083?utm_source=copy
版权声明:本文为博主原创文章,转载请附上博文链接!
//
1.I2C协议
2条双向串行线,一条数据线SDA,一条时钟线SCL。
SDA传输数据是大端传输,每次传输8bit,即一字节。
支持多主控(multimastering),任何时间点只能有一个主控。
总线上每个设备都有自己的一个addr,共7个bit,广播地址全0.
系统中可能有多个同种芯片,为此addr分为固定部分和可编程部份,细节视芯片而定,看datasheet。
1.1 I2C位传输
数据传输:SCL为高电平时,SDA线若保持稳定,那么SDA上是在传输数据bit;
若SDA发生跳变,则用来表示一个会话的开始或结束(后面讲)
数据改变:SCL为低电平时,SDA线才能改变传输的bit
1.2 I2C开始和结束信号
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
1.3 I2C应答信号
Master每发送完8bit数据后等待Slave的ACK。
即在第9个clock,若从IC发ACK,SDA会被拉低。
若没有ACK,SDA会被置高,这会引起Master发生RESTART或STOP流程,如下所示:
1.4 I2C写流程
写寄存器的标准流程为:
1. Master发起START
2. Master发送I2C addr(7bit)和w操作0(1bit),等待ACK
3. Slave发送ACK
4. Master发送reg addr(8bit),等待ACK
5. Slave发送ACK
6. Master发送data(8bit),即要写入寄存器中的数据,等待ACK
7. Slave发送ACK
8. 第6步和第7步可以重复多次,即顺序写多个寄存器
9. Master发起STOP
写一个寄存器
写多个寄存器
1.5 I2C读流程
读寄存器的标准流程为:
1. Master发送I2Caddr(7bit)和 W操作1(1bit),等待ACK
2. Slave发送ACK
3. Master发送reg addr(8bit),等待ACK
4. Slave发送ACK
5. Master发起START
6. Master发送I2C addr(7bit)和 R操作1(1bit),等待ACK
7. Slave发送ACK
8. Slave发送data(8bit),即寄存器里的值
9. Master发送ACK
10. 第8步和第9步可以重复多次,即顺序读多个寄存器
读一个寄存器
读多个寄存器
2. PowerPC的I2C实现
Mpc8560的CCSR中控制I2C的寄存器共有6个。
2.1 I2CADR 地址寄存器
CPU也可以是I2C的Slave,CPU的I2C地址有 I2CADR指定
2.2 I2CFDR 频率设置寄存器
The serial bit clock frequency of SCL is equal to the CCB clock divided by the divider.
用来设置I2C总线频率
2.3 I2CCR 控制寄存器
MEN: Module Enable. 置1时,I2C模块使能
MIEN:Module Interrupt Enable. 置1时,I2C中断使能。
MSTA:Master/slave mode. 1 Master mode,0 Slave mode.
当1->0时,CPU发起STOP信号
当0->1时,CPU发起START信号
MTX:Transmit/receive mode select.0 Receive mode,1 Transmit mode
TXAK:Transfer acknowledge. 置1时,CPU在9th clock发送ACK拉低SDA
RSTA:Repeat START. 置1时,CPU发送REPEAT START
BCST:置1,CPU接收广播信息(信息的slave addr为7个0)
2.4 I2CSR 状态寄存器
MCF:0 Byte transfer is in process
1 Byte transfer is completed
MAAS:当CPU作为Slave时,若I2CDR与会话中Slaveaddr匹配,此bit被置1
MBB:0 I2C bus idle
1 I2C bus busy
MAL:若置1,表示仲裁失败
BCSTM:若置1,表示接收到广播信息
SRW:When MAAS is set, SRW indicates the value of the R/W command bit of the calling address, which is sent from the master.
0 Slave receive, master writing to slave
1 Slave transmit, master reading from slave
MIF:Module interrupt. The MIF bit is set when an interrupt is pending, causing a processor interrupt request(provided I2CCR[MIEN] is set)
RXAK:若置1,表示收到了ACK
2.5 I2CDR 数据寄存器
这个寄存器储存CPU将要传输的数据。
3. PPC-Linux中I2C的实现
内核代码中,通过I2C总线存取寄存器的函数都在文件drivers/i2c/busses/i2c-mpc.c中
最重要的函数是mpc_xfer.
- static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, intnum)
- {
- struct i2c_msg *pmsg;
- int i;
- int ret = 0;
- unsigned long orig_jiffies = jiffies;
- struct mpc_i2c *i2c = i2c_get_adapdata(adap);
- mpc_i2c_start(i2c); // 设置I2CCR[MEN], 使能I2C module
- /* Allow bus up to 1s to become not busy */
- //一直读I2CSR[MBB],等待I2C总线空闲下来
- while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
- if (signal_pending(current)) {
- pr_debug("I2C: Interruptedn");
- writeccr(i2c, 0);
- return -EINTR;
- }
- if (time_after(jiffies, orig_jiffies + HZ)) {
- pr_debug("I2C: timeoutn");
- if (readb(i2c->base + MPC_I2C_SR) ==
- (CSR_MCF | CSR_MBB | CSR_RXAK))
- mpc_i2c_fixup(i2c);
- return -EIO;
- }
- schedule();
- }
- for (i = 0; ret >= 0 && i < num; i++) {
- pmsg = &msgs[i];
- pr_debug("Doing %s %d bytes to 0x%02x - %d of %d messagesn",
- pmsg->flags & I2C_M_RD ? "read" : "write",
- pmsg->len, pmsg->addr, i + 1, num);
- //根据消息里的flag进行读操作或写操作
- if (pmsg->flags & I2C_M_RD)
- ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
- else
- ret = mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
- }
- mpc_i2c_stop(i2c); //保证为I2CCSR[MSTA]为0,保证能触发STOP
- return (ret < 0) ? ret : num;
- }
- static int mpc_write(struct mpc_i2c *i2c, int target,
- const u8 * data, int length, int restart)
- {
- int i;
- unsigned timeout = i2c->adap.timeout;
- u32 flags = restart ? CCR_RSTA : 0;
- /* Start with MEN */ //以防万一,保证I2C模块使能起来
- if (!restart)
- writeccr(i2c, CCR_MEN);
- /* Start as master */ //写了I2CCR[CCR_MSTA],触发CPU发起START信号
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
- /* Write target byte */ //CPU发送一个字节,slave I2C addr和0 (写操作bit)
- writeb((target << 1), i2c->base + MPC_I2C_DR);
- if (i2c_wait(i2c, timeout, 1) < 0) //等待slave 发ACK
- return -1;
- for (i = 0; i < length; i++) {
- /* Write data byte */
- writeb(data[i], i2c->base + MPC_I2C_DR); //CPU接着发数据,包括reg addr和data
- if (i2c_wait(i2c, timeout, 1) < 0) //等待slave 发ACK
- return -1;
- }
- return 0;
- }
- static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
- {
- unsigned long orig_jiffies = jiffies;
- u32 x;
- int result = 0;
- if (i2c->irq == 0)
- { //循环读I2CSR,直到I2CSR[MIF]置1
- while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) {
- schedule();
- if (time_after(jiffies, orig_jiffies + timeout)) {
- pr_debug("I2C: timeoutn");
- writeccr(i2c, 0);
- result = -EIO;
- break;
- }
- }
- x = readb(i2c->base + MPC_I2C_SR);
- writeb(0, i2c->base + MPC_I2C_SR);
- } else {
- /* Interrupt mode */
- result = wait_event_interruptible_timeout(i2c->queue,
- (i2c->interrupt & CSR_MIF), timeout * HZ);
- if (unlikely(result < 0)) {
- pr_debug("I2C: wait interruptedn");
- writeccr(i2c, 0);
- } else if (unlikely(!(i2c->interrupt & CSR_MIF))) {
- pr_debug("I2C: wait timeoutn");
- writeccr(i2c, 0);
- result = -ETIMEDOUT;
- }
- x = i2c->interrupt;
- i2c->interrupt = 0;
- }
- if (result < 0)
- return result;
- if (!(x & CSR_MCF)) {
- pr_debug("I2C: unfinishedn");
- return -EIO;
- }
- if (x & CSR_MAL) { //仲裁失败
- pr_debug("I2C: MALn");
- return -EIO;
- }
- if (writing && (x & CSR_RXAK)) {//写后没收到ACK
- pr_debug("I2C: No RXAKn");
- /* generate stop */
- writeccr(i2c, CCR_MEN);
- return -EIO;
- }
- return 0;
- }
- static int mpc_read(struct mpc_i2c *i2c, int target,
- u8 * data, int length, int restart)
- {
- unsigned timeout = i2c->adap.timeout;
- int i;
- u32 flags = restart ? CCR_RSTA : 0;
- /* Start with MEN */ //以防万一,保证I2C模块使能
- if (!restart)
- writeccr(i2c, CCR_MEN);
- /* Switch to read - restart */
- //注意这里,再次把CCR_MSTA置1,再触发 START
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
- /* Write target address byte - this time with the read flag set */
- //CPU发送slave I2C addr和读操作1
- writeb((target << 1) | 1, i2c->base + MPC_I2C_DR);
//等待Slave发ACK
- if (i2c_wait(i2c, timeout, 1) < 0)
- return -1;
- if (length) {
- if (length == 1)
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
- else //为什么不置 TXAK
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA);
- /* Dummy read */
- readb(i2c->base + MPC_I2C_DR);
- }
- for (i = 0; i < length; i++) {
- if (i2c_wait(i2c, timeout, 0) < 0)
- return -1;
- /* Generate txack on next to last byte */
- //注意这里TXAK置1,表示CPU每收到1byte数据后,会发送ACK
- if (i == length - 2)
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
- /* Generate stop on last byte */
- //注意这里CCR_MSTA [1->0] CPU会触发STOP
- if (i == length - 1)
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK);
- data[i] = readb(i2c->base + MPC_I2C_DR);
- }
- return length;
- }
最后
以上就是忧虑老师为你收集整理的i2c协议的全部内容,希望文章能够帮你解决i2c协议所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复