单片机学习(十一)I2C总线和AT24C02的使用 (2)

接收一个bit信息的操作:(1)SCL低电平期间从机将数据位依次放到SDA线上(高位在前),(2)然后拉高SCL(相当于通知从机主机正在读取这个bit的数据),主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化。(主机在接收之前,需要释放SDA

具体的过程如图所示:

image-20210908155949532

这个过程我个人的理解是:

释放SDA(拉高电平),将写入数据的主动权交给从机

从机写入下一位bit到SDA中

主机拉高SCL,提示从机我正在读取这个bit的数据,你先不要变化

主机读取完数据,将SCL拉低,实际上这个过程是在告诉从机我已经读完这个bit了,你给我下一个bit的数据吧,然后如果还未满8位则转到第2步继续接受数据,如果满了一个字节则接收结束

对比发送数据和接收数据可以发现,这两个过程非常的相似,只不过(1)发送信息时写入信息的一方是主机,而接收信息时写入数据的是从机,(2)发送信息时拉高和拉低SCL电平是通知从机读取信息,而在接收信息时则是通知从机主机当前正在读取信息。

可以发现单从开始时的SCL和SDA的状态是无法区分主机是想发送还是接收信息的,其实接收数据还是发送数据是由后面的时序过程(数据帧)所决定的,不需要起始状态进行区分

2.5 发送应答

在接收完一个字节之后,主机在下一个时钟发送一位数据数据0表示应答数据1表示非应答

其实发送应答的操作就是发送一个bit的操作而已:

image-20210908160852670

2.6 接收应答

在发送完一个字节之后,主机在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答数据1表示非应答(主机在接收之前,需要释放SDA)

和发送应答类似,接收应答的操作也就是接收一个bit数据的操作。

image-20210908161026090

3. I2C数据帧 3.1 发送一帧数据

其过程是(过程中的接收应答省略不写了):

发送一个开始信号

发送从机地址(加上写入标记,最后一位为0)

循环发送字节数据

发送完后发送结束信号

image-20210908161143468

3.2 接收一帧数据

image-20210908161500947

过程和发送一帧数据的过程非常类似,只是中间的发送字节数据变成了接收字节数据,此外还有地址部分的最后一位为1,代表读取数据。

3.3 先发送后接收数据

image-20210908161722293

4. AT24C02数据帧

AT24C02是使用I2C通讯协议进行通讯的,I2C数据帧相当于是一辆卡车,而AT24C02数据帧则是在原来卡车的基础上装上了特定货物的卡车。

4.1 字节写

在WORD ADDRESS处写入数据DATA

image-20210908162104316

4.2 随机读

读出在WORD ADDRESS处的数据DATA

image-20210908162140492

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 发送起始信息和发送终止信息

image-20210908152835490

image-20210908153046203

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 发送字节信息和接收字节信息

image-20210908153940500

image-20210908155949532

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 字节写

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwfxps.html