接收一个bit信息的操作:(1)SCL低电平期间,从机将数据位依次放到SDA线上(高位在前),(2)然后拉高SCL(相当于通知从机主机正在读取这个bit的数据),主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化。(主机在接收之前,需要释放SDA)
具体的过程如图所示:
这个过程我个人的理解是:
释放SDA(拉高电平),将写入数据的主动权交给从机
从机写入下一位bit到SDA中
主机拉高SCL,提示从机我正在读取这个bit的数据,你先不要变化
主机读取完数据,将SCL拉低,实际上这个过程是在告诉从机我已经读完这个bit了,你给我下一个bit的数据吧,然后如果还未满8位则转到第2步继续接受数据,如果满了一个字节则接收结束
对比发送数据和接收数据可以发现,这两个过程非常的相似,只不过(1)发送信息时写入信息的一方是主机,而接收信息时写入数据的是从机,(2)发送信息时拉高和拉低SCL电平是通知从机读取信息,而在接收信息时则是通知从机主机当前正在读取信息。
可以发现单从开始时的SCL和SDA的状态是无法区分主机是想发送还是接收信息的,其实接收数据还是发送数据是由后面的时序过程(数据帧)所决定的,不需要起始状态进行区分。
2.5 发送应答在接收完一个字节之后,主机在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
其实发送应答的操作就是发送一个bit的操作而已:
2.6 接收应答在发送完一个字节之后,主机在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
和发送应答类似,接收应答的操作也就是接收一个bit数据的操作。
3. I2C数据帧 3.1 发送一帧数据其过程是(过程中的接收应答省略不写了):
发送一个开始信号
发送从机地址(加上写入标记,最后一位为0)
循环发送字节数据
发送完后发送结束信号
3.2 接收一帧数据过程和发送一帧数据的过程非常类似,只是中间的发送字节数据变成了接收字节数据,此外还有地址部分的最后一位为1,代表读取数据。
3.3 先发送后接收数据 4. AT24C02数据帧AT24C02是使用I2C通讯协议进行通讯的,I2C数据帧相当于是一辆卡车,而AT24C02数据帧则是在原来卡车的基础上装上了特定货物的卡车。
4.1 字节写在WORD ADDRESS处写入数据DATA
4.2 随机读读出在WORD ADDRESS处的数据DATA
AT24C02的固定地址为1010,可配置地址本开发板上为000,所以SLAVE ADDRESS+W为0xA0,SLAVE ADDRESS+R为0xA1
四、代码实现 1. I2C模块实现I2C模块,即实现上面介绍的6块拼图。
首先定义出SCL和SDA连接的引脚:
sbit I2C_SCL = P2 ^ 1; sbit I2C_SDA = P2 ^ 0; 1.1 发送起始信息和发送终止信息 void I2C_Start() { I2C_SDA = 1; I2C_SCL = 1; // 在SCL为高电平拉低SDA I2C_SDA = 0; I2C_SCL = 0; } void I2C_Stop() { I2C_SDA = 0; // 在SCL为高电平时拉高SDA I2C_SCL = 1; I2C_SDA = 1; } 1.2 发送字节信息和接收字节信息 void I2C_SendByte(unsigned char byte) { unsigned char i = 0; for (i = 0; i < 8; i++) { // 先将一个bit的数据写入到SDA中 I2C_SDA = byte & (0x80 >> i); // SCL先拉高,后拉低,通知从机接收数据 I2C_SCL = 1; I2C_SCL = 0; } } unsigned char I2C_ReceiveByte() { unsigned char byte = 0x00; unsigned char i; // 先释放SDA(将主动权交给从机) I2C_SDA = 1; for (i = 0; i < 8; i++) { // 通知从机主机正在读取数据 I2C_SCL = 1; // 如果是1则置一,否则默认为0 if (I2C_SDA) { byte |= (0x80 >> i); } // 通知从机这个bit已经读取完毕,可以发送下一个bit I2C_SCL = 0; } return byte; } 1.3 发送应答和接收应答 void I2C_SendAck(unsigned char AckBit) { I2C_SDA = AckBit; // SCL先拉高,后拉低,通知从机接收数据 I2C_SCL = 1; I2C_SCL = 0; } unsigned char I2C_ReceiveAck() { unsigned char AckBit; // 先释放SDA(将主动权交给从机) I2C_SDA = 1; // 通知从机主机正在读取数据 I2C_SCL = 1; AckBit = I2C_SDA; // 通知从机这个bit已经读取完毕 I2C_SCL = 0; return AckBit; } 2. AT24C02模块这个模块依赖于I2C模块,即利用I2C发送和接收数据。
首先定义从机地址:
// 最后一位为0代表写,即发送数据,为1代表读,即接受数据 #define AT24C02_ADDRESS 0xA0 2.1 字节写