看懂官方技术文档-->从软件和硬件理解IIC通信协议原理与使用

目录

开发环境:

开发板与其它资源:

一、什么是IIC-->从硬件方面理解

二、解析时序图->完成代码逻辑(软件方面理解)

2.1、完整的发送数据过程

2.2、开始条件

2.3、终止条件

2.4、开始与终止信号注意点

2.5、发送应答与接收应答

2.6、数据格式

2.7、数据有效性与发送一个字节数据

2.8、时间间隔

三、通讯步骤

四、51/52开发板,代码

4.1、IIC各个部分代码

4.2、IIC代码使用示例-->AT24C02

五、STM32F4系列开发板,IIC代码

5.1、IIC各个部分代码

5.2、IIC代码使用示例-->AT24C02



开发环境:

wins 10家庭版;

keil 5 编辑器;

开发板与其它资源:

51/52系列单片机板;->>模拟I/O高低电平,进行软件模拟IIC协议

STM32F4系列板;-->使用内置的IIC控制器,直接读取硬件传输的高低电平,硬件模拟IIC协议

AT24C02芯片;

为了验证代码可行性,建议使用串口(串口助手打印数据也很容易)或者显示屏(LCD1602比较容易)进行显示数据;

IIC数据手册;-->链接放在我上传的资源(需要进我的主页找),需要使用可以下载(几个积分);

注意点:

STM32代码部分,适用于STM32F4系列板,其它板能不能用不知道,没有进行测试,但代码思路的没有问题的;

51/52代码部分,适用于C51/52,S51/52板,其它系列板能不能用不知道,没测试;

一、什么是IIC-->从硬件方面理解

       Inter IC BUS,简称(又称I2C总线)IIC;

        I2C总线的意思是“完成集成电路或功能单元之间信息交换的规范或协议”。Philips公司推出的I2C总线采用一条数据线(SDA),加一条时钟线(SCL)来完成数据的传输及外围器件的扩展;对各个节点的寻址是软寻址方式,节省了片选线,标准的寻址字节SLAM为7位,可以寻址127个单元。------>在同一个数据线(SDA)传输数据,故为半双工通信。

        I2C总线有三种数据传输速度:标准,快速模式和高速模式。标准的是100Kbps,快速模式为400Kbps,高速模式支持快至3.4Mbps的速度。所有的与次之传输速度的模式都是兼容的。I2C总线支持7位和10位地址空间设备和在不同电压下运行的设备。

硬件电路总线图:


主机与从机连接在SDA与SCL线上,当需要进行通信时,一个设备发送寻址到总线,每一个设备都会收到该地址,符合该地址的设备返回应答,返回应答的设备在本次通信的过程是从机,发送寻址到总线在本次通信过程是主机;

进行发送寻址的设备是主机,寻址位有7位,故理论上最多可以存在2^7个从机;

二、解析时序图->完成代码逻辑(软件方面理解)

2.1、完整的发送数据过程



技术文档:

Every byte put on the SDA line must be eight bits long. The number of bytes that can be
transmitted per transfer is unrestricted. Each byte must be followed by an Acknowledge
bit. Data is transferred with the Most Significant Bit (MSB) first . If a slave cannot receive or transmit another complete byte of data until it has performed some other function, for example servicing an internal interrupt, it can hold the clock line SCL LOW to force the master into a wait state. Data transfer then continues when the slave is ready for another byte of data and releases clock line SCL.

理解:

放在SDA线上的每个字节必须是8位长,每次传输字节大小不受限制,每次传输1个字节必须有一个应答,数据传输先从最高有效位(MSB)开始,当从机接收不到完整数据时(1个字节),可以强制主机进行等待,当主机能继续传输时(且从机准备就绪),数据继续传输,传输结束释放总线SCL;

2.2、开始条件

时序图如下图2:

                                            图2


技术文档:A HIGH to LOW transition on the SDA line while SCL is HIGH defines a START condition.

理解:在SCL线维持高电平时,SDA线从高电平跳变到低电平

51/52单片机代码逻辑:

给SCL,I/O口;
给SDA,I/O口;

开始函数(void)
{
    SDA高电平;
    SCL维持在高电平;
    SDA跳变到低电平;
    释放SCL(为下一阶段做准备);
}

STM32代码逻辑:

IIC开始函数
{

    SDA模式设置为输出
    总线默认空闲状态
    
    SCL高电平
    SDA_OUT高电平    
    延迟5us保持电平稳定
    SDA_OUT在SCL高电平期间拉低
    延迟5us保持电平稳定
    SCL拉低  //为下一极端做准备
    
}

2.3、终止条件

时序图如下图3:

                                图3


技术文档:A LOW to HIGH transition on the SDA line while SCL is HIGH defines a STOP condition.

理解:在SCL线维持高电平时,SDA线从低电平跳变到高电平

51/52代码逻辑:

给SCL,I/O口;
给SDA,I/O口;


终止函数(void)
{
    SDA从低电平;
    SCL维持在高电平期间;
    SDA跳变到高电平;
    //二者都是高电平,空闲状态
}

STM32代码逻辑:

IIC停止函数
{
    SDA模式设置为输出

    SCL拉低
    SDA_OUT低电平
    延迟5us保持电平稳定
    SCL拉高
    延迟5us保持电平稳定
    SDA_OUT拉高
    
}

2.4、开始与终止信号注意点

技术文档:

1)START and STOP conditions are always generated by the master. The bus is considered to be busy after the START condition. The bus is considered to be free again a certain time after the STOP condition;
2)The bus stays busy if a repeated START (Sr) is generated instead of a STOP condition. In this respect, the START (S) and repeated START (Sr) conditions are functionallyidentical.

理解:

1)起始和停止条件总是由主机产生,在启动条件之后,总线被认为是繁忙的。在停止条件之后的某个时间,总线被认为是再次空闲的;

2)如果在开始信号完成后,重复的依旧是开始信号(而不是终止信号),那么总线保持忙碌状态;在终止信号完成后,重复的依旧是终止信号,那么总线保持空闲状态;

2.5、发送应答与接收应答

时序图如下图4:

                          图4

技术文档:

When SDA remains HIGH during this ninth clock pulse, this is defined as the Not
Acknowledge signal

理解:

在第九个脉冲周期内(ack),SCL线在高电平期间,SDA线稳定在高电平(1)表示主机不应答,SDA线稳定在低电平(0)表示主机应答(必须是稳定的信号)-->主机接收与发送应答都是一样的(但接收应答判断的是从机是否应答,且需要提前释放SDA)

发送应答代码逻辑(51/52):

给SCL,I/O口;
给SDA,I/O口;

//主机发送ack给从机,1表示不应答,0表示应答
void IIC发送ack函数(定义一个状态变量用来给SDA赋状态)
{
    把状态变量赋给SDA; # 
    SCL高电平;
    SCL跳变到低电平;
}

接收应答代码逻辑(51/52):

给SCL,I/O口;
给SDA,I/O口;
//主机接收从机的应答
void IIC接收ack函数()
{
    定义一个数据变量;
    SDA维持在高电平;  注释:

    SCL维持在高电平,SDA读取数据;
    数据线SDA赋值给状态变量;  
    SCL跳变到低电平;
    返回数据变量;  注释:把电平信号返回,判断从机是否应答
    
}

发送应答代码逻辑(STM32):

//1无效,0有效
主机发送应答函数(ack)
{

    SDA模式设置为输出

    SCL拉低,让主机改变SDA电平
    //根据应答设置SDA电平
    if(ack)
    {
        SDA_OUT=1
    }
    else
    {
        SDA_OUT=0
    }
    延迟保持数据有效性
    SCL拉高进行读取
    延迟保持数据有效性
    SCL拉低
    
    
    
}

接收应答代码逻辑(STM32):

//1是无效应答,0是有效应答
//相当于接收应答函数
IIC主机等待从机应答函数()
{
    SDA模式设置为接收
    应答变量=0
    SCL拉高,让从机操作SDA
    if(SDA状态)
    {
        应答变量 = SDA  //相当于 应答变量 = 1
        //接收到无效应答,停止传输
        IIC停止函数
    }
    // 因为默认的应答变量是有效应答,故SDA传递的是0有效应答时,不需要第二次给应答变量赋值0
    延迟保持数据有效性
    SCL拉低 //拉高,读取
    延迟保持数据有效性

    返回应答变量
    
}

2.6、数据格式

帧格式如下图5:

图5

第一个从机地址应该是:整个从机在硬件的地址;A6-A0,R/W一共八位

第二个从机地址应该是:具体要把数据发送到从机的哪一个寄存器里(1-255)

2.7、数据有效性与发送一个字节数据

        在进行数据传输时,需要在数据有效时间进行传输数据,否则可能无法传输数据;

        发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位在前),然后拉高SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节;

        接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位在前),然后拉高SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA);

 图1

技术文档:

The data on the SDA line must be stable during the HIGH period of the clock. The HIGH
or LOW state of the data line can only change when the clock signal on the SCL line is
LOW . One clock pulse is generated for each data bit transferred.

理解:

在时钟的高电平期间,SDA线上的数据必须稳定。只有当SCL线上的时钟信号为低电平时,数据线的高电平或低电平状态才会改变,并且每传输一个数据位产生一个时钟脉冲。

51/52发送一个字节代码逻辑:

给SCL,I/O口;
给SDA,I/O口;

发送字节函数(定义数据位)
{
    定义变量进行遍历:
    循环8次
    {
        SDA=数据位&(0x80>>递增的遍历变量); 注释:从高位开始读取,依次移动
        拉高SCL;
        SCL低电平期间;
    }

}

STM32发送一个字节代码逻辑:

发送一个字节函数(数据变量)
{
    SDA模式设置为输出
    定义遍历变量
    
    SCL拉低 //让主机把数据放到SDA线
    循环发数据8次
    {

        SDA_OUT= 数据变量&(0x80>>每次移1位)
        //或者使用
        //if( 数据变量&(0x80>>每次移1位))
        //{
        //    SDA_OUT = 1
        //}
         // else
        //    {
        //        SDA_OUT = 0
        //    }
        //
        拉高SCL,从机进行读取数据
        延迟保持数据有效性
        SCL拉低,为下次传输做准备
    }
    
}

51/52接收一个字节代码逻辑:

给SCL,I/O口;
给SDA,I/O口;

接收字节函数(void)
{
    定义接收数据的变量; 注释:这里使用移位变量进行遍历
    定义循环变量;
    SDA拉高; 注释:保持数据有效性
    循环
    {
        SCL拉高;
        如果(if)SDA,读取数据;数据变量|=(数据变量>>i)
        SCL拉低;
    }
    返回数据
}

STM32接收一个字节代码逻辑:

IIC接收一个字节函数
{
    SDA模式设置为输入
    定义循环变量与接收数据变量
    SCL拉低,让从机放数据到SDA
    循环八次,一次读取一位
    {
        SCL拉高,开始读取数据
        if(SDA_IN)
        {
            数据变量 = 读SDA数据,高位开始读
        }

        延迟保持数据有效性
        SCL拉低
    }
    返回数据
}

2.8、时间间隔

如下表1:

表1

可知高低电平跳变最块时间为:4.7us+4.0us=8.7us-->时间间隔至少要大于这个时间,给出高低电平跳变反应时间(标准模式下)

三、通讯步骤

3.1、发送开始信号,连接在总线上的设备发送寻址

3.2、连接在总线上设备符合该地址的设备进行应答

3.3、主机选择从机的片内地址(这时已经产生了主机和从机,发送寻址的是主机,符合地址并应答的是从机),并进行发送

3.4、从机接受到主机发送的片内地址,进行应答

3.5、主机接受到应答,开始准备进行数据的传输

3.6、主机进行数据传输

3.7、每传输一个字节进行一次应答,当主机发的是:不应答(1),发送停止信号,通讯停止

四、51/52开发板,代码

4.1、IIC各个部分代码

#include <REGX52.H>

sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;

void I2C_Star(void)
{
	I2C_SDA=1;
	I2C_SCL=1;
	I2C_SDA=0;
	I2C_SCL=0;
}

void I2C_Stop(void)
{
	I2C_SDA=0;
	I2C_SCL=1;
	I2C_SDA=1;
}


void I2C_SendByte(unsigned char Byte)
{
	 unsigned char i;
	 for(i=0;i<8;i++)
	 {
	 	I2C_SDA=Byte&(0x80>>i);
	 	I2C_SCL=1;
	 	I2C_SCL=0;
	 }
}


unsigned char I2C_ReceiveByte(void)
{
	unsigned char Byte=0x00;
	unsigned char i;
	I2C_SDA=1;
	for(i=0;i<8;i++)
	{
		I2C_SCL=1;
		if(I2C_SDA){Byte |= (0x80>>i);}
		I2C_SCL=0;
	}
	return Byte;
}


void I2C_SendAck(unsigned char AckBit)
{
	I2C_SDA=AckBit;
	I2C_SCL=1;
	I2C_SCL=0;
}



unsigned char I2C_ReceiveAck(void)
{
	unsigned char AckBit;
	I2C_SDA=1;
	I2C_SCL=1;
	AckBit=I2C_SDA;
	I2C_SCL=0;
	return AckBit;
}

4.2、IIC代码使用示例-->AT24C02

#include <REGX52.H>

#include "I2C.h"


void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
	I2C_Star();
	I2C_SendByte(0xA0);
	I2C_ReceiveAck();
	I2C_SendByte(WordAddress);
	I2C_ReceiveAck();
	I2C_SendByte(Data);
	I2C_ReceiveAck();
	I2C_Stop();
	
}


unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
	unsigned char Data;
	I2C_Star();
	I2C_SendByte(0xA0);
	I2C_ReceiveAck();
	I2C_SendByte(WordAddress);
	I2C_ReceiveAck();
	I2C_Star();
	I2C_SendByte(0xA0|0x01);
	I2C_ReceiveAck();
	Data=I2C_ReceiveByte();
	I2C_SendAck(1);
	I2C_Stop();
	return Data;
}

五、STM32F4系列开发板,IIC代码

5.1、IIC各个部分代码

// 初始化I0口
void iic_ init (void)
{

    //GPI0初始化结构

    GPI0_ InitTypeDef GPIO_ InitStruct;


    //1.开启GPIOB时钟

    RCC_ AHB1PeriphClockCmd (RCC_ AHB1Periph_ _GPIOB, ENABLE) ;


    //2.初始化PB8 PB9

    GPIO_ InitStruct. GPI0_ Mode = GPI0_ _Mode_ _OUT;//输出模式

    GPIO_ InitStruct. GPIO_ _0Type = GPIO_ 0Type_ _PP;//推挽输出

    GPIO_ InitStruct. GPI0_ Speed = GPIO_ Speed_ 50MHz;//输出速度

    GPI0_ InitStruct. GPI0_ _PuPd = GPI0_ _PuPd_ _NOPULL;//无 上下拉

    GPIO_ InitStruct. GPIO_ Pin = GPIO_ Pin_ 8 [GPIO_ _Pin_ _9;//

GPIO_ Init (GPIOB, &GPI0_ InitStruct) ;
}

 //设置SDA的方向
void iic_ sda_ _mode (GP IOMode_ _TypeDef I0)
{

    GPIO_ InitTypeDef GPIO_ InitStruct;

    //2.初始化PB8 PB9

    GPIO_ InitStruct. GPI0_ _Mode = I0;//

    GPI0_ InitStruct. GPI0_ _OType = GPIO_ 0Type_ PP;//推挽输出

    GPI0_ InitStruct. GPI0_ Speed = GPI0_ Speed_ _50MHz;//输出速度

    GPIO_ InitStruct. GPI0_ PuPd = GPI0_ PuPd_ _NOPULL;//无 上下拉
    GPIO_ InitStruct. GPIO_ Pin = GPIO_ Pin_ 9;//

    GPIO_ Init (GPIOB, &GPI0_ InitStruct) ;
}

//起始信号
void iic_start(void)
{
	iic_sda_mode(GPIO_Mode_OUT);	
	SCL = 1;
	SDA_OUT = 1;
	delay_us(5);
	SDA_OUT = 0;
	delay_us(5);
	SCL = 0;
}
//停止信号
void iic_stop(void)
{
	iic_sda_mode(GPIO_Mode_OUT);
	
	SCL = 0;
	SDA_OUT = 0;
	delay_us(5);

	SCL = 1;
	delay_us(5);

	SDA_OUT = 1;
}
//主机发应答
iic_send_ack(void)
{
	iic_sda_mode(GPIO_Mode_IN)
	u8 ack;
	SCL = 1;
	delay_us(5);
	
	if(SDA_IN)
	{
		ack = 1;	
		iic_stop();
	}
	else
	{
		ack  = 0;
	}
	SCL = 0;
	delay_us(5);

	return ack;

}
//主机接受应答
void iic_reciver_ack(u8 ack)
{
	iic_sda_mode(GPIO_Mode_OUT);

	SCL = 0;
	delay_us(5);
	
	if(ack)
	{
		SDA_OUT = 1;
	}
	else
	{
		SDA_OUT = 0;
	}
	SCL = 1;
	delay_us(5);
	SCL = 0;
}

//发送一个字节
void iic_send_byte(u8 data)
{
	u8 i;

	iic_sda_mode(GPIO_Mode_OUT);
	SCL = 0;
	
	for(i=0;i<8;i++)
	{
		if(data&1<<(7-i))
		{
			SDA_OUT = 1;
		}
		else
		{
			SDA_OUT = 0;
		}
	}
	SCL = 1;
	delay_us(5);
	SCL = 0;
}

//接收一个字节
void iic_reciver_byte(void)
{
	u8 i;
	u8 data=0;
	iic_sda_mode(GPIO_Mode_IN);
	SCL = 0;
	
	for(i=0;i<8;i++)
	{
		delay_us(5);
		SCL = 1;
		if(SDA_IN)
		{
			data= 1;
		}
		else
		{
			data = 0;
		}
		delay_us(5);
		SCL = 0;
	}
	return data;
}


5.2、IIC代码使用示例-->AT24C02

//AT24C02写1字节数据
void at24c02_write_byte(u8 address, u8 data)
{
    u8 ack;
    //起始信号
    iic_start();
    ack=iic_reciver_ack();

    //发送从机地址和写操作, b6-b0,R/W, 0xa0
    iic_send_byte(0xa0);
    ack = iic_reciver()
    //等待ack
    if(ack)
    {
        print("ack 1 failed\r\n")
        return;
    }


    //发送片内地址
    iic_send_byte(address);
    //等待ack
    ack=iic_reciver_ack();
    if(ack)
    {
        printf("ack 2 failed\r\n")
        return;
    }

    //发送数据
    iic_send_byte(data);
    //等待ack
    ack=iic_reciver_ack();
    if(ack)
    {
        printf("ack 3 failed\r\n") //这里是串口打印错误信息
        return;
    }

    //停止信号
    iic_stop();

}


//at24c02读1字节数据
u8 at24c02_read_byte(u8 address)
{
    u8 ack;
    u8 data;
    //起始信号
    iic_start();
    
    //发送从机地址和写操作, b6-b0,R/W, 0xa0
    iic_send_byte(0xa0);
    ack=iic_reciver_ack();
    //等待ack
    if(ack)
    {
        print("ack 1 failed\r\n")
        return;
    }


    //发送片内地址
    iic_send_byte(address);
    //等待ack
    ack=iic_reciver_ack();
    if(ack)
    {
        printf("ack 2 failed\r\n")
        return;
    }

    //再次开始信号
    iic_start();

    //发送数据
    iic_send_byte(0xa1);
    //等待ack
    ack=iic_reciver_ack();
    if(ack)
    {
        printf("ack 3 failed\r\n") //这里是串口打印错误信息
        return;
    }

    data = iic_reciver_data();
    
    //停止
    iic_stop();
    return data;

}








  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
IIC(Inter-Integrated Circuit)是一种串行通信协议,也被称为I2C(Inter-IC)协议。它是由飞利浦公司(现在的恩智浦半导体)在上世纪80年代开发的,用于在集成电路之间进行通信。 IIC协议使用两根信号线:串行数据线(SDA)和串行时钟线(SCL)。所有的设备都通过这两根线连接在一起,形成一个多主从结构的总线系统。 工作原理如下: 1. 总线状态:SDA和SCL线上的电平可以是高电平(逻辑1)或低电平(逻辑0)。在空闲状态下,这两根线都是高电平。 2. 起始和停止条件:当主设备要发送数据时,首先发送一个起始条件,即将SDA从高电平变为低电平,然后再将SCL线变为低电平。当主设备发送完数据后,会发送一个停止条件,即将SDA从低电平变为高电平,然后再将SCL线变为高电平。 3. 数据传输:在起始条件之后,主设备会发送一个7位的从设备地址,包括读/写位。接着是数据的传输阶段,每个字节的数据后面都有一个应答位。主设备发送字节后,从设备会返回一个应答位,以表示数据是否成功接收。如果从设备接收到数据,则应答位为低电平,否则为高电平。 4. 时钟同步:SCL线上的时钟信号用于同步数据的传输。数据的传输在时钟的上升沿和下降沿进行。 IIC协议的优点包括: - 仅使用两根信号线,简化了硬件连接。 - 支持多主从结构,多个设备可以共享同一条总线。 - 支持高速传输和低功耗模式。 - 可以通过硬件软件来实现总线冲突的解决。 总之,IIC通信协议通过串行数据线和串行时钟线实现设备之间的通信,在嵌入式系统和集成电路中被广泛应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值