STM32 I2C通信:完整图文指南及示例代码

目录

I2C硬件实现协议:

硬件I2C:

引脚选择:PB6 –SCL ;PB7 –SDA

I2C 初始化结构体:

I2C配置代码:

I2C作为主设备发送数据:

 I2C发送(写)数据代码:

I2C软件模拟协议:

I2C软件通信:

1、空闲状态:

2、开始信号:

3、停止信号:

4、应答信号:

5、数据的有效性:

各个信号产生的时间间隔:

 起始信号:

停止信号:

应答信号ACK:

等待应答:

I2C写字节:


 

I2C硬件实现协议:

由STM32的IIC片上外设专门负责实现IIC通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理IIC协议的方式减轻了CPU的工作,且使软件设计更加简单。     STM32的IIC外设可用作通讯的主机及从机,支持100Kbit/s和400Kbits/s的速率,支持7位、10位设备地址,支持DMA数据传输,并具有数据校验功能。

硬件I2C:

引脚选择:PB6 –SCL ;PB7 –SDA

3bfd5df9fbe14a8ea073bd5a70aa16bc.png

I2C 初始化结构体:

0a10526d2a38476ab2a19478a2fd43df.png

I2C配置代码:

void I2C_init(void)
{

    I2C_InitTypeDef   I2C_InitStructure;
    GPIO_InitTypeDef   GPIO_InitStructure;
	
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ,  ENABLE);
   RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE );
	
	//PB6 --SCL ;PB7 --SDA
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;    //复用开漏
	GPIO_InitStructure.GPIO_Pin   =  GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed =  GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    I2C_DeInit(I2C1);
    I2C_InitStructure.I2C_Ack  =  I2C_Ack_Enable; //应答使能
	I2C_InitStructure.I2C_AcknowledgedAddress =  I2C_AcknowledgedAddress_7bit; //地址长 
    度,选择7位(可为7或10)
	I2C_InitStructure.I2C_ClockSpeed = 400000 ; 
	//时钟占空比,可选low/high=2:0或16:9,这里我们选择2:0
	I2C_InitStructure.I2C_DutyCycle  = I2C_DutyCycle_2 ;  
	I2C_InitStructure.I2C_Mode =  I2C_Mode_I2C;
	I2C_InitStructure.I2C_OwnAddress1 = 0X30 ;  //自身IIC设备地址
	I2C_Init(I2C1,&I2C_InitStructure );
    I2C_Cmd(I2C1,ENABLE);
}

注意:I2C_OwnAddress1是STM32设备本身的地址,一般STM32作为主设备,可以不用关心这个地址设置,随意设置个数就行,但是如果STM32作为从设备使用时,必须进行配置。
I2C_Send7bitAddress(I2Cx, address, direction)这个address指的是外设器件从设备地址,比如挂载EEPROM时,通常是0xA0.这个地址不能和 I2C_OwnAddress1混淆。

I2C作为主设备发送数据:

主发送器通讯过程,即作为 I2C 通讯的主机端时,向外发送数据时的过程。

c3ef7045863e496bbd007d7bd536c4c5.png

主发送器发送流程及事件说明如下:

(1) 控制产生起始信号 (S),当发生起始信号后,它产生事件“EV5”,并会对 SR1 寄存器的“SB”

位置 1,表示起始信号已经发送;

(2) 紧接着发送设备地址并等待应答信号,若有从机应答,则产生事件“EV6”及“EV8”,这时

SR1 寄存器的“ADDR”位及“TXE”位被置 1,ADDR 为 1 表示地址已经发送,TXE 为 1 表示

数据寄存器为空;

(3) 以上步骤正常执行并对 ADDR 位清零后,我们往 I2C 的“数据寄存器 DR”写入要发送的数

据,这时 TXE 位会被重置 0,表示数据寄存器非空,I2C 外设通过 SDA 信号线一位位把数据发送

出去后,又会产生“EV8”事件,即 TXE 位被置 1,重复这个过程,就可以发送多个字节数据了;

(4) 当我们发送数据完成后,控制 I2C 设备产生一个停止信号 (P),这个时候会产生 EV8_2 事件,

SR1 的 TXE 位及 BTF 位都被置 1,表示通讯结束。

 I2C发送(写)数据代码:

代码逻辑就是根据I2C主设备发送数据的时序来编写的。

void I2C_WriteByte(uint8_t addr,uint8_t data)
{
     //FlagStatus bitstatus = RESET
    while (I2C_GetFlagStatus(I2C1,  I2C_FLAG_BUSY));  //检查I2C总线是否繁忙	
    I2C_GenerateSTART(I2C1,  ENABLE); //打开I2C1 
    //ErrorStatus status = ERROR,   ERROR是个枚举类型,值为0        
    while( !I2C_CheckEvent(I2C1,  I2C_EVENT_MASTER_MODE_SELECT)); //EV5,主模式	
	 I2C_Send7bitAddress(I2C1,OLED_ADDRESS, I2C_Direction_Transmitter); //配置STM32的IIC设备自己的地址,每个连接到IIC总线上的设备都有一个自己的地址,作为主机也不例外。
	 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));   //EV6
	 I2C_SendData(I2C1,  addr);   //寄存器地址
     while(!I2C_CheckEvent(I2C1,  I2C_EVENT_MASTER_BYTE_TRANSMITTING));  //EV8,等待发送数据 
     完成
	I2C_SendData(I2C1,  data);   //发送数据
	 while(!I2C_CheckEvent(I2C1,  I2C_EVENT_MASTER_BYTE_TRANSMITTING)); //判断是否发送完成, 
     EV8,等待发送数据完成
    I2C_GenerateSTOP( I2C1,  ENABLE); //关闭I2C总线
}

I2C软件模拟协议:使用CPU直接控制通讯引脚的电平,产生出符合通讯协议标准的逻辑。

I2C软件通信:

1、空闲状态:

IIC总线的SDA和SCL两条信号线同时处于高电平时,规定位总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

I2C时序图:3f543a2b9d6747a08ee8527df9fe382e.png

 

2、开始信号:当SCL为高电平期间,SDA有高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。

3、停止信号:当SCL为高电平期间,SDA由低到高的跳变;停止信号也是一种高电平跳变时序信号,而不是一个电平信号( 起始信号和停止信号一般由主机产生)

4、应答信号:   发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

988dd252cb6c4f16b6d4e100efb2ae12.png

 

5、数据的有效性:   IIC总线进行数据传输时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。SDA数据线在SCL的每个时钟周期传输一位数据。(数据在SCL的上升沿到来之前就需准备好。并在下降沿到来之前必须稳定  )

9599cc61ef624fdd9f8e7e8cf21c9467.png

 

各个信号产生的时间间隔:

 

d7b44ed230ed4ca29f26a7e08c0669ac.png 

 

 起始信号:

void I2C_Start(void)
{
    I2C_SDA_H;//把数据线拉高
    I2C_SCL_H;//把时钟线拉高
    delay_us(5);//延时5微秒,要求大于4.7微秒
    I2C_SDA_L; //拉低,产生下降沿
    delay_us(5);//这个过程大于4.7微秒
}

停止信号:

void I2C_Stop(void)
{
   I2C_SCL_L;
   I2C_SDA_L;
   I2C_SCL_H;
   delay_us(5);
   I2C_SDA_H;
   delay_us(5);
}

应答信号ACK:低电平0表示应答,1表示非应答

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * 函数名:SendAck
 * 参数:
 * 返回:
 * 描述:应答
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
#define IIC_DELAY_TIME	 10
static void SendAck(u8 _type){
    IIC_SCL_LOW();
    DelayUs(IIC_DELAY_TIME); 	    
    if(_type == 0)
        IIC_SDA_LOW();
    else
        IIC_SDA_HIGH();
    DelayUs(IIC_DELAY_TIME);
    IIC_SCL_HIGH();
    DelayUs(IIC_DELAY_TIME);
    IIC_SCL_LOW();
    DelayUs(IIC_DELAY_TIME); 
}

等待应答:

static unsigned char IIC_Wait_Ack(void)
{
	unsigned char ack;
	OLED_SCLK_CLr();	//时钟线置低
	 delay_us(1);
	OLED_SDIN_Set();	//信号线置高
	 delay_us(1);
	OLED_SCLK_Set();	//时钟线置高
	delay_us(1);

	if(OLED_READ_SDIN())	//读取SDA的电平
		ack = IIC_NO_ACK;	//如果为1,则从机没有应答
	else
		ack = IIC_ACK;		//如果为0,则从机应答

	OLED_SCLK_CLr();//时钟线置低
	 delay_us(1);
	return ack;	//返回读取到的应答信息
}

 

I2C写字节:

static void Write_IIC_Byte(unsigned char IIC_Byte)
{
	unsigned char i;  //定义变量
	for(i=0;i<8;i++) //for循环8次
	{
		OLED_SCLK_CLr();	//时钟线置低,为传输数据做准备
		 delay_us(1);

		if(IIC_Byte & 0x80)	//读取最高位
		  	OLED_SDIN_Set();//最高位为1
		else
			OLED_SDIN_CLr();	//最高位为0

		IIC_Byte <<= 1;  //数据左移1位
		 delay_us(1);
		OLED_SCLK_Set(); //时钟线置高,产生上升沿,把数据发送出去
		 delay_us(1);
	}
	OLED_SCLK_CLr();	//时钟线置低
		 delay_us(1);

	while(IIC_Wait_Ack());	//从机应答
}

 

 

 

 

 

 

 

 

物联沃分享整理
物联沃-IOTWORD物联网 » STM32 I2C通信:完整图文指南及示例代码

发表评论