I2C总线协议

工作用到模拟I2C,以下为在网上查到的一些资料,以及实现方式,由于是从不同的文章中引用过来的,没有标明出处,请谅解。

普通模式:100kHz

快速模式:400kHz

快速模式+:1MHz

高速模式:3.4MHz

超高速模式:5MHz

I2C可以支持mul-master系统,允许有多个master并且每个master都可以与所有的slaves通信。

I2C协议两种消息类型:地址帧/数据帧

开始条件:master设备将SCL置为高电平(总线空闲时,SDA和SCL都处于高电平),然后SDA拉低,所有slave设备就会知道传输即将开始。如果两个master设备在同一时刻都希望获得总线的所有权,那么谁先拉低SDA,谁就获得总线控制权。

地址帧:在一次通信的最开始出现,一个7bit的地址从最高位(MSB)开始发送,地址后面会跟1bit操作符,1代表读操作,0代表写操作。8bit发送完后,接收端的设备获得SDA控制权,在第9个时钟脉冲之前回复ACK(将SDA拉低)以表示接收正常。

数据帧:地址帧发送之后,开始传输数据,master继续产生时钟脉冲,数据由master或slave放到SDA上,每个数据帧8bits,数据帧的数量可以是任意的,直到产生停止条件。每一帧数据传输之后,接收方需要回复一个ACK或NACK。

停止条件:当所有数据发送完成时,master将产生一个停止条件,停止条件定义为:在SDA置于低电平时,将SCL拉高并保持高电平,然后将SDA拉高。

重复开始条件:有时master需要再一次通信中进行多次消息交换,并且期间不希望被其他master干扰,这时可以使用“重复开始条件”—在一次通信中,master可以产生多次start condition,最后阐述一个stop condition结束整个通信过程。为了产生一个重复的开始条件,SDA在SCL低电平时拉高,然后SCL拉高。接着master就可以产生一个开始条件继续新的消息传输。

注意:

  1. 地址的8位传送完毕后,成功配置地址的slave设备必须发送ACK,否则一定时间之后master视为超时,将放弃数据传送,发送stop。
  2. 当写数据时,master每发送完8个数据位,slave设备如果还有空间接收下一个字节应回复ACK,slave设备如果没有空间接收更多字节应回复NACK,master当接收到NACK或一定时间之后没收到任何数据视为超时,此时master放弃数据传送,发送stop。

当读数据时,slave设备每发送完8个数据位,如果master希望继续读下一个字节,master回复ACK以提示slave准备下一个数据,如果master不希望读取更多字节,master回复NACK以提示slave设备准备接收stop信号。

#define SCL_H 	LL_GPIO_SetOutputPin(II2C_Port, II2C_SCL_Pin)
#define SCL_L 	LL_GPIO_ResetOutputPin(II2C_Port, II2C_SCL_Pin)

#define SDA_H 	LL_GPIO_SetOutputPin(II2C_Port, II2C_SDA_Pin)
#define SDA_L 	LL_GPIO_ResetOutputPin(II2C_Port, II2C_SDA_Pin)

#define SCL_read (LL_GPIO_ReadInputPort(II2C_Port) & II2C_SCL_Pin)
#define SDA_read (LL_GPIO_ReadInputPort(II2C_Port) & II2C_SDA_Pin)

#define dTime 5

void SI2C_Init(void)
{
  LL_GPIO_InitTypeDef GPIO_InitStruct;

  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);

  GPIO_InitStruct.Pin = II2C_SDA_Pin | II2C_SCL_Pin;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  LL_GPIO_Init(II2C_Port, &GPIO_InitStruct);	
}

static void SDA_IN(void)
{
	LL_GPIO_InitTypeDef GPIO_InitStruct;	
	GPIO_InitStruct.Pin = II2C_SDA_Pin;
	GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT;
	GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
	LL_GPIO_Init(II2C_Port, &GPIO_InitStruct);
}
static void SDA_OUT(void)
{
	LL_GPIO_InitTypeDef GPIO_InitStruct;	
	GPIO_InitStruct.Pin = II2C_SDA_Pin;
	GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
	GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
	LL_GPIO_Init(II2C_Port, &GPIO_InitStruct);	
}

static void Delay_Us(uint8_t nus)
{
	uint8_t i = 0;
	while(nus--)            
	{
		i = 2;                       //需要根据实际情况设定i值大小,以得到正确的时钟
		while(i--);
	}
}

static void i2c_start(void)         //开始条件
{
	SDA_OUT();
	SDA_H;
	SCL_H;
	Delay_Us(dTime);
	SDA_L;
	Delay_Us(dTime);
	SCL_L;
}
static void i2c_stop(void)          //停止条件
{
	SDA_OUT();
	SCL_L;
	SDA_L;
	Delay_Us(dTime);
	SCL_H;
	SDA_H;
	Delay_Us(dTime);
}
static bool i2c_rd_ack(void)        //发送数据后,等待从设备应答ack,超时返回错误
{
	uint8_t ucErrTime;
	SDA_H;
	SDA_IN();
	SCL_H;
	Delay_Us(dTime);
	while(SDA_read)
	{
		ucErrTime++;
		if(ucErrTime > 250)
		{
			return false;
		}
	}
	SCL_L;
	return true;
}
static bool i2c_sb(uint8_t data)    //发送一个字节数据
{
	uint8_t cnt;
	SDA_OUT();
	SCL_L;
	for(cnt=0; cnt<8; cnt++)
	{
		if(data&0x80)
		{
			SDA_H;
		}		
		else
		{
			SDA_L;
		}
		Delay_Us(dTime);
		SCL_H;
		Delay_Us(dTime);
		SCL_L;
		data<<=1;
	}
	return i2c_rd_ack();
}
static uint8_t i2c_rb(void)       //读取一个字节数据
{
	uint8_t cnt;
	uint8_t data = 0;
	SDA_IN();
	for(cnt=0; cnt<8; cnt++)
	{
		SCL_L;
		Delay_Us(dTime);
		data <<= 1;
		SCL_H;
		if(SDA_read)
		{
			data |= 0x01;
		}
		Delay_Us(dTime);
	}
	return data;
}
static void i2c_wr_ack(bool ACK)     //向从设备写ack/nack
{
	SCL_L;
	SDA_OUT();
	if(ACK)
	{
		SDA_L;
	}
	else
	{
		SDA_H;
	}
	Delay_Us(dTime);
	SCL_H;
	Delay_Us(dTime);
	SCL_L;
}

bool si2c_read_addr8_data(uint8_t addr, uint8_t *wdatabuf, uint8_t len)
{
	uint8_t saddr = (addr<<1 |0x01);
	 
	bool isack = false;
	i2c_start();
	isack = i2c_sb(saddr);
	if(isack)
	{
		while(len)
		{
			*wdatabuf = i2c_rb();
			if(len == 1)
			{
				i2c_wr_ack(false);
			}
			else
			{
				i2c_wr_ack(true);
			}
			wdatabuf++;
			len--;
		}
		i2c_stop();
		return true;
	}
	else
	{
		i2c_stop();
		Delay_Us(dTime);
		return false;
	}
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值