软件模拟IIC

最近做平衡车的时候用到了IIC总线的知识,在这里就总结一下吧!

1,IIC总线的连接

IIC传输数据的时候只用其实只需要两根线,一根是“SCL”为时钟线,一根是“SDA”为数据线

我们来看一下器件是怎么连接在IIC总线上的!


可以看到,SDA和SCL都接了上拉电阻,在总线空闲的时候,SDA和SCL都应该为高电平,当总线上的任何一个器件输出低电平,那总线都将变为低电平。

数据有效性

我们记住只要记住一条:IIC总线在进行数据传输时,当SCL线为高,SDA线必须保持稳定,也就是说,在当拉高SCL线的时候,SDA线不能改变其电平,只有当SCL为低电平的时候才允许SDA线进行跳变,需要注意的是这个规定只是在进行数据传输的时候起作用,其他时候比如生成起始信号的时候可以不理睬。

2,IIC的电平信号

在使用IIC总线的时候我们主要有以下几个信号。

一,起始信号当我们使用IIC总线的时候第一个发送的就是这个信号

IIC协议规定当SCL为高电平的时候,SDA由高到低跳变,为起始信号

下面来看一下代码

//起始信号
void IIC_Start(void)
{
	SDA = 1;        //先将SDA和SCL都拉高为起始信号做准备
	SCL = 1;
	Delay5us();    //延时稳定
	SDA = 0;        //拉低SDA线,又高到低跳变,起始
	Delay5us();    //延时稳定
	SCL = 0;       //拉低SCL线,钳住IIC总线
}

二,停止信号


IIC协议规定当SCL为高电平的时候,SDA由低到高跳变,为停止信号,和起始信号正好相反

下面来看一下代码

//停止
void IIC_Stop(void)
{	
	SCL = 0;        //先将SDA和SCL都拉低为起始信号做准备
	SDA = 0;
	Delay2us();    //延时稳定
	SCL = 1;        //拉高SCL等待SDA上升沿
        Delay2us();    //延时稳定
	SDA = 1;        //拉高SDA
	Delay5us();
	SCL = 0;         //拉低SCL,钳住总线
	
}

三,应答信号


IIC协议规定当SCL为高电平的时候,SDA为低,为起始信号,注意这里是需要在SCL的高电平到来之前SDA线需先将低电平准备好,并在SCL为高期间稳定

下面来看代码

//答应
void IIC_Ack(void)
{
	SCL = 0;                //先将SDA和SCL都拉低为起始信号做准备
	SDA = 0;
	Delay2us();            //延时稳定
	SCL = 1;                //将SCL拉高
	Delay5us();               //在此延时阶段,SDA一直为低
	SCL = 0;                    //拉低SCL,钳住总线
}

四,非应答信号


这个就和答应信号相反,SCL为高期间,SDA也为高并保持稳定就可以了!

下面看代码

//不答应
void IIC_NAck(void)
{
	SCL = 0;                //将SCL拉低为高电平做准备
	SDA = 1;                //将SDA先拉高
	Delay2us();
	SCL = 1;            //拉高SCL
	Delay5us();
	SCL = 0;            //钳住总线
}

五,等待答应信号

发送器每发送完一个字节(8个脉冲),在第9个脉冲间释放总线,接收器返回一个ACK信号,协议规定,低电平为有效应答,高电平为无效应答。


看以下代码

//等待答应
bit IIC_Wait_Ack(void)
{
	uint8_t temp = 0;                    //定义临时计数变量
	SDA = 1;                                   //先拉高SDA
	Delay5us();                                //延时稳定
	SCL = 1;                                    //拉高SCL准备读取SDA线,SDA和SCL同时为高,释放总线控制权
	Delay5us();
	while(SDA)                                //当SDA拉低变为低电平的时候表示有效答应,调出循环
	{
		temp++;
		if(temp>250)                //当循环250次后SDA还没有拉低,则表示没有答应信号(不准确的延时)
		{
			IIC_Stop();
			return 1;		//没有答应返回1            
		}
	}
	SCL = 0;
	return 0;			//有答应,返回0
}

六,发送1个字节

//发动1个字节
void IIC_Send_Byte(uint8_t dat)
{
	uint8_t t;                                //临时计数变量
	SCL = 0;                                //拉低SCL,钳住总线,准备发送数据
	for(t=0;t<8;t++)                  //循环8次,一次发送1位
	{
		SDA = (dat&0x80)>>7;            //去低四位,然后左移七位,先发高位
		dat=dat<<1;                              //将数据右移一位,等待下一此发送
		SCL = 1;                                       //拉高SCL,等待从器件读取SDA
		Delay2us();                                //延时稳定
		SCL= 0;                                    //拉低SCL,钳住总线,准备发送数据
		Delay2us();                              //延时稳定
	}
}
七,读取一个字节
//	功能说明: CPU从I2C总线设备读取8bit数据
//	返 回 值: 读到的数据
uint8_t IIC_Read_Byte(uint8_t ack)
{

	uint8_t i,value;

	/* 读到第1个bit为数据的bit7 ,先读高位后读低位*/
	value = 0;
	for (i = 0; i < 8; i++)
	{
		value <<= 1;    数据左移,为下一此读取腾出位置
		SCL = 1;
		Delay2us();
		if (SDA)                //如果SDA为1,则value自加
		{
			value++;
		}
		SCL = 0;                //钳住总线,准备下一此读取
		Delay2us();
	}
	if(ack==0)                      //读取完毕,判断答应信号
		i2c_NAck();
	else
		i2c_Ack();
	return value;               //返回读取到的值
}


好了,以上就是模拟IIC的基本使用方法了!



### 软件模拟IIC协议的方法 #### IIC协议简介 IIC(Inter-Integrated Circuit),即内部集成电路总线,是由飞利浦公司于20世纪80年代开发的一种串行通信协议。该协议主要用于短距离、低速率的芯片间通信,如传感器、存储器和显示器等设备间的交互[^2]。 #### 主要特点 - **两根线传输**:SCL(时钟线)和SDA(数据线) - **多主/从模式支持** - **简单的硬件接口** #### 实现步骤详解 ##### 初始化配置 初始化阶段需设置GPIO引脚为开漏输出模式,并确保上拉电阻已连接至电源电压。这一步骤对于保证信号完整性至关重要[^5]。 ```c void IIC_Init(void){ GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOB_CLK_ENABLE(); // 配置 SDA 和 SCL 作为开漏输出 GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } ``` ##### 启动条件 当SCL保持高电平时,SDA由高变低表示启动条件;反之则代表停止条件。此操作用于通知所有挂载在IIC总线上方器件准备接收命令或数据流[^1]。 ```c void IIC_Start(void){ IIC_SDA_High(); IIC_SCL_High(); delay_us(4); IIC_SDA_Low(); delay_us(4); IIC_SCL_Low(); } ``` ##### 停止条件 同样地,在SCL处于高位状态下使能SDA下降沿触发来标志一次完整的事务处理完毕并释放总线控制权给其他潜在请求者[^4]。 ```c void IIC_Stop(void){ IIC_SDA_Low(); IIC_SCL_High(); delay_us(4); IIC_SDA_High(); delay_us(4); } ``` ##### 发送字节函数 为了向目标节点传递信息包,定义了一个专门负责打包单个字符并通过序列化方式逐位发出的过程。每次发送前先确认ACK响应状态以判断对方是否准备好接受新消息片段[^3]。 ```c uint8_t IIC_SendByte(uint8_t byteToSend){ uint8_t i=0; for(i=0;i<8;i++){ (byteToSend&0x80)?IIC_SDA_High():IIC_SDA_Low(); byteToSend<<=1; IIC_SCL_High(); delay_us(2); IIC_SCL_Low(); delay_us(2); } return IIC_WaitAck(); } ``` ##### 接收应答机制 每传送一字节后都需要等待来自接收端返回的一个肯定答复(Acknowledge),以此验证当前链接正常运作且后续可以继续进行更多有效负载交换活动。 ```c uint8_t IIC_WaitAck(void){ uint8_t ack; IIC_SDA_High(); IIC_SCL_High(); delay_us(2); ack=(READ_IIC_SDA())?0:1; IIC_SCL_Low(); return ack; } ``` 通过以上几个核心组件组合起来便构成了一个基本可用版本的纯软件层面实现方案。当然实际应用场景下还需要针对具体型号做适当调整优化才能达到最佳性能表现水平。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值