STM32-IIC通信解析

一、IIC通信
I2C,两线式串行总线,由数据线SDA和时钟SCL构成的穿行总线,可发送和接受数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。半双工通信方式
二、IIC协议

1. 空闲状态:I2C总线,总线的SDA和SCL同时处于高电平时,规定为总线的空闲状态。
2. 起始信号和停止信号:

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

  • 停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
    在这里插入图片描述

  • 代码解析

void IIC_Start(void)
{
	
	SDA_H;						
	SCL_H;						
	DelayUs(iic_info.speed);	//延时,速度控制
	
	SDA_L;						//当SCL线为高时,SDA线一个下降沿代表开始信号
	DelayUs(iic_info.speed);	
	SCL_L;						//钳住SCL线,以便发送数据	

}
void IIC_Stop(void)
{

	SDA_L;						
	SCL_L;						
	DelayUs(iic_info.speed);	
	
	SCL_H;						
	SDA_H;						//拉高SDA线,当SCL为高时,SDA线一个上升沿代表停止
	DelayUs(iic_info.speed);

}

3. 应答信号ACK

  • 发送器每发送一个字节(8位),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答为)表示接收器成功接收该字节;应答信号为高电平,规定为费应答位(NACK),一般表示接收器接收该字节没有成功。 ***对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。***如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P
    在这里插入图片描述
  • 代码解析
void IIC_Ack(void)
{
	
	SCL_L;						
	SDA_L;						
	DelayUs(iic_info.speed);
	SCL_H;						
	DelayUs(iic_info.speed);
	SCL_L;						
	
}
void IIC_NAck(void)
{
	
	SCL_L;						
	SDA_H;						
	DelayUs(iic_info.speed);
	SCL_H;						
	DelayUs(iic_info.speed);
	SCL_L;						
	
}

4.数据有效性

  • I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据上的高电平或低电平状态才允许变化。
  • 即数据在SCL的上升沿到来之前就需要准备好。并在下降沿来到之前必须稳定
    在这里插入图片描述

5.数据的传送

  • 在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。具体的数据传输方式可以看https://www.cnblogs.com/aaronLinux/p/6218660.html
  • 代码解析结合上图
void IIC_SendByte(unsigned char byte)
{

	unsigned char count = 0;
	
    SCL_L;							//拉低时钟开始数据传输
	
    for(; count < 8; count++)		//循环8次,每次发送一个bit
    {
		if(byte & 0x80)				//发送最高位
			SDA_H;
		else
			SDA_L;
		
		byte <<= 1;					//byte左移一位
		
		DelayUs(iic_info.speed);
		SCL_H;
		DelayUs(iic_info.speed);
		SCL_L;
    }

}

三、代码
1.i2c.c

#include "stm32f10x.h"

#include "i2c.h"
#include "delay.h"
#include "usart.h"

IIC_INFO iic_info;
/*
************************************************************
*	函数名: IIC_SpeedCT1
*
*	函数功能:	软件IIC速度控制
*
*	入口参数:	speed:延时参数
*
*	返回参数:	无
*
*	说明:		单位:微妙
************************************************************
*/
void IIC_SpeedCtl(unsigned short speed)
{
	iic_info.speed = speed;
}
/*
************************************************************
*	函数名:	IIC_Init
*
*	函数功能:	软件IIC总线IO初始化
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:使用开漏方式,不用切换IO口输入输出方向
************************************************************
*/
void IIC_Init(void)
{

	GPIO_InitTypeDef gpio_initstruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	gpio_initstruct.GPIO_Mode = GPIO_Mode_Out_OD;	//开漏,不用切换出入输出方向		
	gpio_initstruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &gpio_initstruct);
	
	IIC_SpeedCtl(5);
	
	SDA_H;			//拉高SDA线,处于空闲状态									
	SCL_H;			//拉高SCL,处于空闲状态										

}

/*
************************************************************
*	函数名: IIC_Start
*
*	函数功能:	软件IIC开始信号
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
void IIC_Start(void)
{
	
	SDA_H;						
	SCL_H;						
	DelayUs(iic_info.speed);	//延时,速度控制
	
	SDA_L;						//当SCL线为高时,SDA线一个下降沿代表开始信号
	DelayUs(iic_info.speed);	
	SCL_L;						//钳住SCL线,以便发送数据	

}

/*
************************************************************
*	函数名: IIC_Stop
*
*	函数功能:	软件IIC停止信号
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
void IIC_Stop(void)
{

	SDA_L;						
	SCL_L;						
	DelayUs(iic_info.speed);	
	
	SCL_H;						
	SDA_H;						//拉高SDA线,当SCL为高时,SDA线一个上升沿代表停止
	DelayUs(iic_info.speed);

}

/*
************************************************************
*	函数名: IIC_SpeedCT1
*
*	函数功能:	软件IIC等待应答
*
*	入口参数:	timeOut:超时时间
*
*	返回参数:	无
*
*	说明:		单位:微妙
************************************************************
*/
_Bool IIC_WaitAck(unsigned int timeOut)
{
	
	
	SDA_H;DelayUs(iic_info.speed);			
	SCL_H;DelayUs(iic_info.speed);			
	
	while(SDA_R)							//如果读到SDA线为1,等待,应答信号应是0
	{
		if(--timeOut == 0)
		{
			UsartPrintf(USART1, "WaitAck TimeOut\r\n");

			IIC_Stop();						//超时未收到应答,则停止总线
			
			return IIC_Err;					//返回失败
		}
		
		DelayUs(iic_info.speed);
	}
	
	SCL_L;									
	
	return IIC_OK;							//返回成功
	
}

/*
************************************************************
*	函数名: IIC_ACK
*
*	函数功能:	软件IIC产生一个应答
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		当SDA线为低时,SCL线一个正脉冲代表发送一个应答信号
************************************************************
*/
void IIC_Ack(void)
{
	
	SCL_L;						
	SDA_L;						
	DelayUs(iic_info.speed);
	SCL_H;						
	DelayUs(iic_info.speed);
	SCL_L;						
	
}

/*
************************************************************
*	函数名: IIC_ACK
*
*	函数功能:	软件IIC产生一个非应答
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		当SDA线为高时,SCL线一个正脉冲代表发送一个非应答信号
************************************************************
*/
void IIC_NAck(void)
{
	
	SCL_L;						
	SDA_H;						
	DelayUs(iic_info.speed);
	SCL_H;						
	DelayUs(iic_info.speed);
	SCL_L;						
	
}

/*
************************************************************
*	函数名: IIC_SendByte
*
*	函数功能:	软件IIC发送一个字节
*
*	入口参数:	byte:需要发送的字节
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
void IIC_SendByte(unsigned char byte)
{

	unsigned char count = 0;
	
    SCL_L;							//拉低时钟开始数据传输
	
    for(; count < 8; count++)		//循环8次,每次发送一个bit
    {
		if(byte & 0x80)				//发送最高位
			SDA_H;
		else
			SDA_L;
		
		byte <<= 1;					//byte左移一位
		
		DelayUs(iic_info.speed);
		SCL_H;
		DelayUs(iic_info.speed);
		SCL_L;
    }

}

/*
************************************************************
*	函数名: IIC_RecvByte
*
*	函数功能:	软件IIC接受一个字节
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
unsigned char IIC_RecvByte(void)
{
	
	unsigned char count = 0, receive = 0;
	
	SDA_H;							//拉高SDA线,开漏状态下,需线拉高以便读取数据
	
    for(; count < 8; count++ )		//循环8次,每次发送一个bit
	{
		SCL_L;
		DelayUs(iic_info.speed);
		SCL_H;
		
        receive <<= 1;				
		
        if(SDA_R)					//如果SDA线为1,则receive变量自增,每次自增都是对bit0的+1,然后下一次循环会先左移一次
			receive++;
		
		DelayUs(iic_info.speed);
    }
	
    return receive;
	
}

/*
************************************************************
*	函数名: IIC_WriteByte
*
*	函数功能:	软件IIC写一个数据
*
*	入口参数:	slaveAddr:从机地址
*				regAddr:寄存器地址
*				byte:需要写入的数据
*
*	返回参数:	0-写入成功 1-写入失败
*
*	说明:		*byte是缓存写入数据的变量的地址,因为有些寄存器只需要控制下寄存器,并不需要写入值
************************************************************
*/
_Bool I2C_WriteByte(unsigned char slaveAddr, unsigned char regAddr, unsigned char *byte)
{

	unsigned char addr = 0;

	addr = slaveAddr << 1;		//IIC地址是7bit,这里需要左移1位,bit0:1-读 0-写
	
	IIC_Start();				//起始信号
	
	IIC_SendByte(addr);			//发送设备地址(写)
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	IIC_SendByte(regAddr);		//发送寄存器地址
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	if(byte)
	{
		IIC_SendByte(*byte);	//发送数据
		if(IIC_WaitAck(5000))	//等待应答
			return IIC_Err;
	}
	
	IIC_Stop();					//停止信号
	
	return IIC_OK;

}

/*
************************************************************
*	函数名: IIC_ReadByte
*
*	函数功能:	软件IIC写一个数据
*
*	入口参数:	slaveAddr:从机地址
*				regAddr:寄存器地址
*				byte:需要写入的数据
*
*	返回参数:	0-写入成功 1-写入失败
*
*	说明:		*byte是缓存写入数据的变量的地址,因为有些寄存器只需要控制下寄存器,并不需要写入值
************************************************************
*/
_Bool I2C_ReadByte(unsigned char slaveAddr, unsigned char regAddr, unsigned char *val)
{

	unsigned char addr = 0;

    addr = slaveAddr << 1;		//IIC地址是7bit,这里需要左移1位,bit0:1-读 0-写
	
	IIC_Start();				//起始信号
	
	IIC_SendByte(addr);			//发送设备地址(写)
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	IIC_SendByte(regAddr);		//发送寄存器地址
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	IIC_Start();				//重启信号
	
	IIC_SendByte(addr + 1);		//发送设备地址(读)
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	*val = IIC_RecvByte();		//接收
	IIC_NAck();					//产生一个非应答信号,代表读取接收
	
	IIC_Stop();					//停止信号
	
	return IIC_OK;

}

/*
************************************************************
*	函数名: IIC_WriteBytes
*
*	函数功能:	软件IIC写多个数据
*
*	入口参数:	slaveAddr:从机地址
*				regAddr:寄存器地址
*				byte:需要写入的数据
*
*	返回参数:	0-写入成功 1-写入失败
*
*	说明:		*buf是一个数组或指向一个缓存区的指针
************************************************************
*/
_Bool I2C_WriteBytes(unsigned char slaveAddr, unsigned char regAddr, unsigned char *buf, unsigned char num)
{

	unsigned char addr = 0;

	addr = slaveAddr << 1;		//IIC地址是7bit,这里需要左移1位,bit0:1-读 0-写
	
	IIC_Start();				//起始信号
	
	IIC_SendByte(addr);			//发送设备地址(写)
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	IIC_SendByte(regAddr);		//发送寄存器地址
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	while(num--)				//循环写入数据
	{
		IIC_SendByte(*buf);		//发送数据
		if(IIC_WaitAck(5000))	//等待应答
			return IIC_Err;
		
		buf++;					//数据指针偏移到下一个
		
		DelayUs(10);
	}
	
	IIC_Stop();					//停止信号
	
	return IIC_OK;

}

/*
************************************************************
*	函数名: IIC_ReadBytes
*
*	函数功能:	软件IIC读多个数据
*
*	入口参数:	slaveAddr:从机地址
*				regAddr:寄存器地址
*				byte:需要写入的数据
*
*	返回参数:	0-写入成功 1-写入失败
*
*	说明:		*buf是一个数组或指向一个缓存区的指针
************************************************************
*/
_Bool I2C_ReadBytes(unsigned char slaveAddr, unsigned char regAddr, unsigned char *buf, unsigned char num)
{

	unsigned short addr = 0;

    addr = slaveAddr << 1;		//IIC地址是7bit,这里需要左移1位,bit0:1-读 0-写
	
	IIC_Start();				//起始信号
	
	IIC_SendByte(addr);			//发送设备地址(写)
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	IIC_SendByte(regAddr);		//发送寄存器地址
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	IIC_Start();				//循环写入数据
	
	IIC_SendByte(addr + 1);		//发送设备地址(读)
	if(IIC_WaitAck(5000))		//等待应答
		return IIC_Err;
	
	while(num--)
	{
		*buf = IIC_RecvByte();
		buf++;					//偏移下一个数据存储地址
		
		if(num == 0)
        {
           IIC_NAck();			//最后一个数据需要回NOACK
        }
        else
        {
          IIC_Ack();			//回应ACK
		}
	}
	
	IIC_Stop();
	
	return IIC_OK;

}

2.i2c.h

#ifndef _I2C_H_
#define _I2C_H_





#define IIC_OK		0

#define IIC_Err		1


//SDA		PB11
//SCL		PB10
#define SDA_H	GPIO_SetBits(GPIOB, GPIO_Pin_11)
#define SDA_L	GPIO_ResetBits(GPIOB, GPIO_Pin_11)
#define SDA_R	GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11)

#define SCL_H	GPIO_SetBits(GPIOB, GPIO_Pin_10)
#define SCL_L	GPIO_ResetBits(GPIOB, GPIO_Pin_10)


typedef struct
{

	unsigned short speed;

} IIC_INFO;

extern IIC_INFO iic_info;


void IIC_Init(void);

void IIC_SpeedCtl(unsigned short speed);

_Bool I2C_WriteByte(unsigned char slaveAddr, unsigned char regAddr, unsigned char *byte);

_Bool I2C_ReadByte(unsigned char slaveAddr, unsigned char regAddr, unsigned char *val);

_Bool I2C_WriteBytes(unsigned char slaveAddr, unsigned char regAddr, unsigned char *buf, unsigned char num);

_Bool I2C_ReadBytes(unsigned char slaveAddr, unsigned char regAddr, unsigned char *buf, unsigned char num);

void IIC_Start(void);

void IIC_Stop(void);

_Bool IIC_WaitAck(unsigned int timeOut);

void IIC_Ack(void);

void IIC_NAck(void);

void IIC_SendByte(unsigned char byte);

unsigned char IIC_RecvByte(void);


#endif
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32模拟IIC从机是一种在STM32微处理器上模拟实现IIC协议的从设备。STM32模拟IIC从机采用GPIO模拟SCL和SDA信号线,实现利用STM32处理器来处理IIC协议的通信数据。在实现时,需要根据IIC从机的协议规定,按照IIC协议通信标准,对数据进行协议解析、处理和传输。 STM32模拟IIC从机实现的关键是通过软件模拟实现IIC协议的主机向从机发送数据,以及从机将数据回传给主机。在实现时,需要定义SCL和SDA GPIO引脚的输入输出、读写判断等功能。在收到数据时,从机需要对其进行解析,判断是否是对其提出的请求,若是,则进行正确的应答,将请求的数据发送给主机。 STM32模拟IIC从机的实现优点是使用相比硬件IIC更加具有灵活性,可根据具体需求自定义协议和时序,支持不同的命令、控制和处理数据。同时,在系统嵌入式设计过程中具有更好的扩展性和可维护性,易于进行系统升级和调试。 当然,在实现过程中,也存在一些缺点,比如说在处理速度上会存在一定的限制,无法达到硬件IIC的速度,同时实现成本较高。需要综合考虑具体的应用场景,选择合适的IIC通信方式。 ### 回答2: STM32模拟IIC从机是基于STM32芯片实现的一种IIC协议通讯方式。IIC通讯是一种串行通讯协议,它具有双线通讯的特点,并且只需要两根信号线就可以实现通讯。STM32模拟IIC从机是一种通讯从属设备,用于与IIC主机进行通讯,主机与从机之间可以传输数据、控制信息等等。 模拟IIC从机的实现需要使用STM32芯片内部的GPIO外设模块,通过对这些模块的配置和控制,实现IIC信号的检测、接收、转发等功能。一般来说,STM32模拟IIC从机的实现需要设置IIC传输的时钟频率,以及接收和发送的数据格式等等。具体的实现方法可以参考芯片的用户手册和工具手册。 在实现STM32模拟IIC从机时,需要注意以下几点: 1. 确定IIC通讯的时钟频率和数据格式,并进行相应的配置和控制; 2. 对STM32芯片进行编程,并设置相关的GPIO口的参数和寄存器状态,以便实现数据收发和控制; 3. 在实现通讯时,需要注意正确的应答和错误处理,以保证通讯的稳定性和可靠性; 4. 在应用中,可以根据需要添加其他的功能模块,例如IIC中断处理模块、自动重传机制等等,以满足不同的通讯需求。 总之,STM32模拟IIC从机是一个非常方便和实用的通讯技术,可以广泛应用于工控领域、智能家居、汽车电子等众多应用场合。通过合理的配置和控制,可以实现高效、低耗能的数据传输和控制,提高设备的性能和用户体验。 ### 回答3: STM32模拟IIC从机是一种基于STM32芯片的I2C从机接口实现方案。I2C总线是一种常用的串行通信协议,用于连接芯片、传感器和其他设备,而模拟IIC接口是一种实现I2C协议的方法。 STM32芯片具有多种硬件接口,包括SPI、I2C、UART等,同时还支持模拟IIC接口的软件实现。在STM32的软件库中,提供了丰富的I2C和模拟IIC从机的驱动库,以方便开发者快速实现I2C从机功能。 对于STM32模拟IIC从机的实现,主要需要根据I2C协议实现从机接收和发送数据、从机地址配置和维护、ACK/NACK的应答等功能。在STM32模拟IIC从机使用时,需要将I2C主机的接口配置为模拟IIC接口,同时使用I2C从机的接口连接到STM32芯片的相应引脚。 由于模拟IIC是通过GPIO口来实现的,因此需要设计相应的GPIO引脚为I2C从机的SCL和SDA信号线。需要注意的是,模拟IIC从机的速度较慢,可能会受到外界干扰等影响,因此在实际应用时需要进行一定的优化和测试。 总之,利用STM32芯片的强大功能,我们可以轻松实现模拟IIC从机的接口,通过I2C总线连接其他设备并实现数据传输。同时,还需要对状态和错误进行检测和处理,以确保I2C通信的稳定和可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值