1、i2c协议简要分析
i2c总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备。
它具有以下特点。
- 1、只有两条双向信号线:一条串行数据线SDA,一条串行时钟线SCL。
- 2、每个连接到总线的器件都可以使用软件根据它的唯一的地址来确定。
- 3、传输数据的设备之间是简单的主从关系。
- 4、主机可以用作主机发送器或者主机接收器。
- 5、它是一个真正的多主机总线,两个或多个主机同时发起数据传输时,可以通过冲突检测和仲裁来防止数据被破坏。
- 6、串行的8位双向传输,位速在标准模式下可达 100kbit/s,在快速模式下可达400kbit/s,在高速模式下可待3.4Mbit/s。
- 7、片上的滤波器可以增加抗干扰能力,保证数据的完整性。
- 8、连接到同一总线上的IC数量只受到总线的最大电容400Pf的限制。
如上图所示,启动一个传输时,主机先发送一个S信号,然后发送8位数据。这8位数据的前7位为从机地址,第八位表示传输的方向(0表示写,1表示读),如果有数据则继续发送,最后发出P信号停止。
系统中的所有外围器件都具有一个7位的从器件专用地址码,其中高4位为器件类型,由生产厂家制定,在生产厂商生产是就已经固定到芯片上的了,低3位为器件引脚定义地址,由使用者定义。主设备发送地址时,主线上的每个从设备都将这7位地址码与自己的地址进行比较,如果相同,则认为自己正在被主设备寻址。根据R/W位将自己定义为发送器或者接收器。
信号类型:
注意:
- 正常数据传输时,SDA 在 SCL 为低电平时改变,在 SCL 为高电平时保持稳定。即当SCL 由高电平变为为低电平时,表示这个bit(0/1)已经传输完成;SDA 状态开始变为下一个比特(0/1),此时SCL 由低电平变为高电平,确定要传输的时(0/1)。这样就完成了数据的传输。
- 开始信号 S 信号:
SCL 为高电平时,SDA由高电平向低电平跳变,开始传送数据。 - 结束信号 P 信号:
SCL 为高电平时,SDA由低电平向高电平跳变,结束传送数据。 - 响应信号 ACK:
接收器在接收到8位数据后,在第9个时钟周期,拉低 SDA 电平
注意:
在第9个时钟周期,发送器保持SDA为高,如果有ACK,那么第9个时钟周期SDA为低电平,如果没有为高电平,发送器根据电平高低分辨是否有ACK信号。
如果使能了IIC中断,发送完8bit数据后,主机自动进入中断处理函数,此时SCL被发送器拉低,让接收器被迫等待。恢复传输只需要清除中断挂起。
i2c设置中断的原因
IIC接收数据只是存到指定的寄存器中,如果你不取走,下次再接收数据就直接冲掉了,所以IIC接收到数据之后给CPU中断,中断服务程序去处理这些收到的数据!发送数据也类似!
2、 s3c2440 读写流程
-
1、设置传输模式 IICSTAT[7-6],我们做实验与AT24C08通信时,2440作为主机,因此只用到主机发模式和主机收模式。
-
2、写入从机地址到 IICDS[7-1],此时IICDS[7-1]位表示从机地址,第0位不关心。如 AT24C08 为 0xA0(最低位写0了,发送到数据线上的7位地址的后边以为才表示收发,这里虽然写0但并不是根据这里的0来真正发送的)。
-
3、写 0xF0(写) 或 0xB0(读)到 IICSTAT 寄存器, 高两位表示 传输模式前边设置过了,设置IICSTT[5-4] 为 11,使能传输,发送S信号。
-
4、IIC控制器自动将第2步中设置的 IICDS[7-1] 再根据 传输模式 补充 IICDS[0]位,发送出去。
-
5、进入第9个时钟周期,此时,从机决定是否发出ACK信号,主机进入中断,判断是否收到ACK信号,以及是否继续传输。
-
6、继续发送:
6.1、将数据写入 IICDS
6.2、清除中断挂起,SCL时钟恢复,IICDS的数据被自动发送到 SDA 线上,回到第5步。 -
7、停止发送:
7.1、写 0xD0(写) 和 0x90(读) 到 IICATAT ,IICATAT[7-6]还是表示的传输模式,IICATAT[5-4] == 0 1,发送停止信号
7.2、清除中断挂起,SCL时钟恢复,发出停止信号
7.3、延时,等待停止信号发出
读写操作中的中断:
发送模式中,当发送数据时, 在(IICDS)寄存器收到新数据之前 IIC 总线接口将会一直等待。即在新数据写入到寄存器之前,SCL 线将会保持为低,然后在其写入后发送的时候释放。S3C2440A 应该等待中断来确定当前数据是否发送完成。在 CPU 收到中断请求后,表示第一次数据发送完成,这次发送的是从设备地址,需要再次写一个新数据到 IICDS 寄存器中,这次的数据才是真正的数据。接收模式中,当收到了数据时,在读取 IICDS 寄存器前 IIC 接口将会一直等待。在新数据读出前,SCL 线将会保持为低,然后在其读取后释放。S3C2440A 应该等待中断来确定当前数据接收的完成。在 CPU 收到中断请求后,需要从 IICDS 寄存器中读取数据。
3、 AT24C08 读写分析
3.1、写过程
写过程与2440芯片的里的写流程相一致,按照流程写就OK
3.2、读过程
读过程是由2440芯片里的一个写流程加一个读流程组合而成,其中写流程结束没有发出P信号,而是直接发出了S信号开始读流程,也就是我为什么加了一道红线的原因。
4、裸机程序
附上一份简单的裸机程序,仅供参考:基于MINI2440
#include <stdio.h>
#include "s3c2440.h"
void Delay(int time);
#define WRDATA (1)
#define RDDATA (2)
typedef struct tI2C {
unsigned char *pData; /* 数据缓冲区 */
volatile int DataCount; /* 等待传输的数据长度 */
volatile int Status; /* 状态 */
volatile int Mode; /* 模式:读/写 */
volatile int Pt; /* pData中待传输数据的位置 */
}tS3C24xx_I2C, *ptS3C24xx_I2C;
static tS3C24xx_I2C g_tS3C24xx_I2C;
/*
* I2C初始化
*/
void i2c_init(void)
{
GPEUP |= 0xc000; // 禁止内部上拉
/*
* AT24C08 两根线 I2CSCL I2CSDA 与 2440芯片相连
* 配置2440 GPECON GPE15 GPE14引脚为I2C功能
*/
GPECON |= 0xa0000000; // 选择引脚功能:GPE15:IICSDA, GPE14:IICSCL
/* 开INT_IIC中断 */
//INTMSK &= ~(BIT_IIC);
/* bit[7] = 1, 使能ACK
* bit[6] = 0, IICCLK = PCLK/16
* bit[5] = 1, 使能中断
* bit[3:0] = 0xf, Tx clock = IICCLK/16
* PCLK = 50MHz, IICCLK = 3.125MHz, Tx Clock = 0.195MHz
*/
IICCON = (1<<7) | (0<<6) | (1<<5) | (0xf); // 0xaf
//IICADD = 0x10; // S3C24xx slave address = [7:1]
IICSTAT = 0x10; // I2C串行输出使能(Rx/Tx)
}
void I_Write(unsigned int slvaddr, unsigned char addr, unsigned char data)
{
unsigned int ack;
// 写从地址
IICSTAT |= 0x1<<6;//主机写模式
IICSTAT |= 0x1<<7;
IICDS = slvaddr;//0xa0; //write slave address to IICDS
IICCON&=~0x10; //clear pending bit
IICSTAT = 0xf0; //(M/T start)
while((IICCON & 1<<4) == 0);//udelay(10);//ack period and then interrupt is pending
// 写寄存器地址
IICDS = addr;
IICCON&=~0x10; //clear pending bit
while((IICCON & 1<<4) == 0);//udelay(10);//ack period and then interrupt is pending
// 写数据
IICDS = data;
IICCON&=~0x10; //clear pending bit
while((IICCON & 1<<4) == 0);//udelay(10);//ack period and then interrupt is pending
// 发出停止信号
IICSTAT = 0xD0; //write (M/T stop to IICSTAT)
IICCON&=~0x10; //clear pending bit
while((IICSTAT & 1<<5) == 1);
}
unsigned char I_Read(unsigned int slvaddr, unsigned char addr)
{
unsigned char data = 1;
int ack;
// 写从地址
IICSTAT |= 0x1<<6;//主机写模式
IICSTAT |= 0x1<<7;
slvaddr = 0xA0;
IICDS = slvaddr;//0xa0; //write slave address to IICDS
IICCON&=~0x10; //clear pending bit
IICSTAT = 0xf0; //(M/T start)
while((IICCON & 1<<4) == 0);//udelay(10);//ack period and then interrupt is pending
// 写寄存器地址
IICDS = addr;
IICCON&=~0x10; //clear pending bit
while((IICCON & 1<<4) == 0);//udelay(10);//ack period and then interrupt is pending
// 写从地址(读模式)
slvaddr = 0xA1;
IICSTAT &= ~(0x1<<6);//主机接受模式
IICSTAT |= 0x1<<7;
IICDS = slvaddr;
IICCON&=~0x10; //clear pending bit
IICSTAT = 0xb0; //(M/R Start)
while((IICCON & 1<<4) == 0);//udelay(10);//uart_SendByte('o');//ack period and then interrupt is pending::
// 读数据
data = IICDS;
//IICCON&=~0x10; //clear pending bit
IICCON = 0x2f; //清挂起状态,并设置无应答
while((IICCON & 1<<4) == 0);//udelay(10);//ack period and then interrupt is pending
data = IICDS;
//IICCON&=~0x10; //clear pending bit
IICCON = 0x2f; //清挂起状态,并设置无应答
while((IICCON & 1<<4) == 0);//udelay(10);//ack period and then interrupt is pending
IICSTAT = 0x90;
IICCON = 0xaf;
//IICCON &= ~0x10; //clear pending bit
while((IICSTAT & 1<<5) == 1);
return data;
}