模拟IIC(基于STM32)

4 篇文章 0 订阅
3 篇文章 0 订阅

IIC协议

I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、 CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
I2C物理层

  • 它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯
  • 一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
  • 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。主机及多个通讯从机。

特点:

  • 总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
  • 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
  • 具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多I2C设备尚不支持高速模式。
  • 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制 。

GPIO配置

STM32的8种引脚模式

void IIC_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE);

	GPIO_InitStructure.GPIO_Pin = IIC_SCL_Pin|IIC_SDA_Pin; 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(IIC_GPIO, &GPIO_InitStructure);	  
 	GPIO_SetBits(IIC_GPIO,IIC_SCL_Pin|IIC_SDA_Pin);	
}

IIC的两个引脚模式配置为开漏输出GPIO_Mode_Out_OD,要得到高电平状态需要上拉电阻才行。将多个开漏输出的引脚连接到一条线上时,通过一个上拉电阻,在不增加任何器件的情况下,形成”线与“关系,这就是IIC、SMBus等总线判断总线占用状态的原理。

所谓的"线与"指的是多个信号线直接连接在一起,只有当所有信号全部为高电平时,合在一起的总线为高电平;只要有任意一个或者多个信号为低电平,则总线为低电平。而推挽输出就不行,如果高电平和低电平连在一起,会出现电流倒灌,损坏器件。

根据时序图完成代码

时序图
I2C通信本是连贯的,在编程时把它拆分,怎么拆就见仁见智了。在I2C尚未完成通信时,将SCL拉低是为了钳住IIC总线,准备收发数据。不拉为低,你的接下来操作都可能被误认为是起始或终止。

当SCL 为1时,SDA的电平变换会被认为是起始和结束信号,所以通讯未结束一定要拉低SCL!!!!

本项目IIC通讯未结束时,SCL线统一为0。
延时函数

*********************************************************************************************************
*	函 数 名: i2c_Delay
*	功能说明: I2C总线位延迟,最快400KHz
*	形    参:无
*	返 回 值:*********************************************************************************************************
*/
static void i2c_Delay(void)
{
	uint8_t i;

	/* 
	 	下面的时间是通过逻辑分析仪测试得到的。
    工作条件:CPU主频72MHz ,MDK编译环境,1级优化
  
		循环次数为10时,SCL频率 = 205KHz 
		循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us 
	 	循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us 
	*/
	for (i = 0; i < 10; i++);
}

起始与停止信号

起始与停止信号

/*
 * @brief IIC起始信号
 */
void IIC_Start(void)
{
	IIC_SDA_1();//先拉高SDA再拉高SCL,防止出现错误信号
	IIC_SCL_1();
	i2c_Delay();//延时保证SCL高电平时,SDA下降沿
	IIC_SDA_0();
	IIC_SCL_0();//如果不拉低SCL线,那么数据传输过程中的SDA的电平变换就会被认为是起始和结束信号
}

/*
 * @brief IIC停止信号
 */
void IIC_Stop(void)
{
	IIC_SDA_0();//先拉低SDA再拉高SCL,防止出现错误信号
	IIC_SCL_1();
	i2c_Delay();//延时保证SCL高电平时,SDA上升沿沿
	IIC_SDA_1();
	/*这里就不用拉低SCL了,因为IIC通讯已经结束*/
}

等待应答

等待应答

在第9个时钟(SCL高电平)时,数据发送端会释放SDA的控制权,由数据接收端控制SDA,若SDA为高电平,表示非应答信号(NACK),低电平表示应答信号(ACK)。
一定要先拉高SDA,再拉高SCL。否者就可能变成起始信号和结束信号了。

void IIC_Wait_Ack(void)
{
	//一定要先拉高SDA,再拉高SCL。否者就可能变成起始信号和结束信号了
	IIC_SDA_1();
	IIC_SCL_1();
	while(IIC_Read_SDA());
	IIC_SCL_0();//IIC通讯没结束,拉低SCL线
}

IIC主机不应答:

void IIC_No_Ack(void)
{
	IIC_SDA_1();
	IIC_SCL_1();
	IIC_SCL(GPIO_PIN_RESET);//通讯未结束,拉低SCL线
}

IIC主机应答:
主机在发出应答信号后一定要释放SDA线(拉高SDA线),否者SDA线处于占用状态无法进行数据传输,线与性质

void IIC_Ack(void)
{
	IIC_SDA_0();
	IIC_SCL_1();
	IIC_SCL_0();//通讯未结束,拉低SCL线
	IIC_SDA_1();//主机释放SDA线
}

读写字节

I2C 总线的数据传输
由时序图可知,字节数据MSB先行,SCL低电平进行数据变换,高电平写入(如果在SCL高电平时进行数据变换,从机会理解成起始或结束信号)。

/*
 * @brief IIC写字节
 * @para  待写入的字节数据
 */
void Write_IIC_Byte(u8 IIC_Byte)
{
	u8 i,data;
	data = IIC_Byte;
	for(i=0;i<8;i++)		
	{
		IIC_SCL_0();
		if(data&0x80)
			IIC_SDA_1();
		else 
			IIC_SDA_0();
		data=data<<1;
		IIC_SCL_1();
	}
	IIC_SCL_0();//通讯未结束,拉低SCL
}

/*
 * @brief IIC读字节
 * @return 返回读取到的一个字节  
 */
u8 Read_IIC_Byte(void)
{
	u8 i,value=0;
	for(i=0;i<8;i++)
	{
		value <<= 1;
		//数据变化
		IIC_SCL_0();
		i2c_Delay();
		//读取数据
		IIC_SCL_1();
		if(IIC_Read_SDA())
			value++;
	}
	IIC_SCL_0();//通讯未结束,拉低SCL
	return value;
}

检查设备是否连接

发送地址呼叫从机,如果有应答就代表连接成功

/*
 * @brief 检测设备是否连接
 */
void Check_IIC_Device(u8 slave_addr)
{
	IIC_Start();
	Write_IIC_Byte((slave_addr<<1)|write_flag);
	IIC_Wait_Ack();
	IIC_Stop();
}

在这里插入图片描述

  • 16
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
IIC是一种串行通信协议,通常用于连接微控制器和各种外设,如传感器和存储器。在STM32中,可以使用硬件IIC接口来进行通信,但是有时候可能需要在没有硬件IIC接口的情况下进行IIC通信,这时候可以使用软件模拟IIC。 在STM32HAL库中,可以使用GPIO模块来模拟IIC通信。具体实现步骤如下: 1. 定义IIC的GPIO引脚 首先需要定义IIC的GPIO引脚,包括SCL和SDA引脚。可以使用STM32的GPIO模块来定义这些引脚。 ```c #define SCL_PIN GPIO_PIN_10 #define SCL_PORT GPIOB #define SDA_PIN GPIO_PIN_11 #define SDA_PORT GPIOB ``` 2. 初始化GPIO引脚 在使用GPIO引脚之前,需要进行初始化。可以使用HAL库提供的GPIO初始化函数来初始化引脚。 ```c GPIO_InitTypeDef GPIO_InitStruct = {0}; /*Configure GPIO pins : SCL_PIN SDA_PIN */ GPIO_InitStruct.Pin = SCL_PIN|SDA_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 设置为开漏输出模式 GPIO_InitStruct.Pull = GPIO_NOPULL; // 不使用上拉或下拉电阻 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // GPIO时钟频率 HAL_GPIO_Init(SCL_PORT, &GPIO_InitStruct); ``` 3. 定义IIC操作函数 定义IIC的操作函数,包括发送数据、接收数据和等待应答等。 ```c void IIC_Start(void) { SDA_HIGH(); SCL_HIGH(); HAL_Delay(1); SDA_LOW(); HAL_Delay(1); SCL_LOW(); } void IIC_Stop(void) { SDA_LOW(); SCL_HIGH(); HAL_Delay(1); SDA_HIGH(); HAL_Delay(1); } void IIC_SendByte(uint8_t byte) { uint8_t i; for (i = 0; i < 8; i++) { if ((byte & 0x80) == 0x80) SDA_HIGH(); else SDA_LOW(); HAL_Delay(1); SCL_HIGH(); HAL_Delay(1); SCL_LOW(); byte <<= 1; } SDA_HIGH(); HAL_Delay(1); SCL_HIGH(); HAL_Delay(1); SCL_LOW(); } uint8_t IIC_RecvByte(void) { uint8_t i; uint8_t byte = 0; SDA_HIGH(); HAL_Delay(1); for (i = 0; i < 8; i++) { byte <<= 1; SCL_HIGH(); HAL_Delay(1); if (SDA_READ()) byte++; SCL_LOW(); HAL_Delay(1); } return byte; } uint8_t IIC_WaitAck(void) { uint8_t ack; SDA_HIGH(); HAL_Delay(1); SCL_HIGH(); HAL_Delay(1); if (SDA_READ()) ack = 1; else ack = 0; SCL_LOW(); HAL_Delay(1); return ack; } void IIC_SendAck(uint8_t ack) { if (ack) SDA_HIGH(); else SDA_LOW(); HAL_Delay(1); SCL_HIGH(); HAL_Delay(1); SCL_LOW(); HAL_Delay(1); } ``` 4. 定义宏函数 定义宏函数来简化IIC操作函数的调用。例如,可以定义宏函数`SDA_HIGH()`来调用`HAL_GPIO_WritePin()`函数来设置SDA引脚为高电平。 ```c #define SDA_HIGH() HAL_GPIO_WritePin(SDA_PORT, SDA_PIN, GPIO_PIN_SET) #define SDA_LOW() HAL_GPIO_WritePin(SDA_PORT, SDA_PIN, GPIO_PIN_RESET) #define SDA_READ() HAL_GPIO_ReadPin(SDA_PORT, SDA_PIN) #define SCL_HIGH() HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, GPIO_PIN_SET) #define SCL_LOW() HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, GPIO_PIN_RESET) ``` 5. 调用IIC操作函数 使用定义的宏函数和IIC操作函数来进行IIC通信。例如,可以使用以下代码来发送一个字节。 ```c IIC_Start(); IIC_SendByte(0xA0); IIC_WaitAck(); IIC_SendByte(0x01); IIC_WaitAck(); IIC_Stop(); ``` 以上就是基于STM32HAL库的软件模拟IIC的实现步骤。需要注意的是,在实现过程中需要根据具体的硬件和需求进行修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值