环境:F103芯片,使用PB6、PB7引脚做软件IIC引脚 

本例程用于和LSM6DSR通讯

void udelay(uint32_t us)
{
	uint32_t i;
	for(i=0;i<10*us;i++)
	{
		__NOP();
	}
}

/** I2C Device Address 8 bit format  if SA0=0 -> D5 if SA0=1 -> D7 **/
#define LSM6DSR_I2C_ADD_L                    0xD5U //d5
#define LSM6DSR_I2C_ADD_H                    0xD7U
#define WHO_AM_I                0x0F


#define TEMP_WORK 1

#define I2C1_SCL_Pin              (GPIO_Pin_6)
#define I2C1_SCL_GPIO_Port        (GPIOB)
#define I2C1_SDA_Pin              (GPIO_Pin_7)
#define I2C1_SDA_GPIO_Port        (GPIOB)

#define i2c_Delay()		udelay(1)

void I2C1_SCL(uint8_t BitValue)  				{GPIO_WriteBit(GPIOB, I2C1_SCL_Pin,(BitAction)BitValue);  i2c_Delay();}
void I2C1_SDA(uint8_t BitValue)  				{GPIO_WriteBit(GPIOB, I2C1_SDA_Pin,(BitAction)BitValue);	i2c_Delay();}		
uint8_t I2C1_SDA_READ()  		{uint8_t data = GPIO_ReadInputDataBit(I2C1_SDA_GPIO_Port, I2C1_SDA_Pin); i2c_Delay(); return data;}	/* 读SDA口线状态 */



int i2c1_mem_read(uint8_t DevAddress, uint8_t MemAddress, uint8_t *ndata, uint8_t size, uint32_t Timeout);
int i2c1_mem_write(uint8_t DevAddress, uint8_t MemAddress, uint8_t *ndata, uint8_t size, uint32_t Timeout);

int iic_lsm6dsr_write(uint8_t cmd, uint8_t *data, uint32_t len) 
{
	i2c1_mem_write(LSM6DSR_I2C_ADD_L,cmd,data,len,100);
  return 0;
}

//0 成功 -1 失败
int iic_lsm6dsr_read(uint8_t cmd, uint8_t *data, uint32_t len) 
{
	uint8_t res = 1;
	res = i2c1_mem_read(LSM6DSR_I2C_ADD_L,cmd,data,len,100);
	if(res == 0)
	{
		printf("read fault\n");
	}
	return 0;
}

uint8_t I2C_R_SDA(void)
{
    uint8_t BitValue;
    BitValue = GPIO_ReadInputDataBit(I2C1_SDA_GPIO_Port, I2C1_SDA_Pin);
    i2c_Delay();
    return BitValue;
}


/*
*********************************************************************************************************
*	函 数 名: i2c_CfgGpio
*	功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_CfgGpio(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	/* 打开GPIO时钟 */

	GPIO_InitStructure.GPIO_Pin = I2C1_SCL_Pin | I2C1_SDA_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  	/* 开漏输出 */
	GPIO_Init(GPIOB, &GPIO_InitStructure);

}

void i2c1_Start(void)
{
	/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */	
	I2C1_SDA(1);
	I2C1_SCL(1);

	I2C1_SDA(0);
	I2C1_SCL(0);

}


/*
*********************************************************************************************************
*	函 数 名: i2c1_Start
*	功能说明: CPU发起I2C总线停止信号
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c1_Stop(void)
{

	/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
	I2C1_SDA(0);
	I2C1_SCL(1);
	I2C1_SDA(1);
}

/*
*********************************************************************************************************
*	函 数 名: i2c1_SendByte
*	功能说明: CPU向I2C总线设备发送8bit数据
*	形    参:_ucByte : 等待发送的字节
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c1_SendByte(uint8_t _ucByte)
{
    uint8_t i;
    for (i = 0; i < 8; i ++)	//循环8次
    {
        I2C1_SDA(_ucByte & (0x80 >> i));//取决于Byte的最高位xxxx xxxx & 1000 0000 = x000 0000 = 0x80(1) | 0x00(0)
        I2C1_SCL(1);		
        I2C1_SCL(0);
    }
}

/*
*********************************************************************************************************
*	函 数 名: i2c1_ReadByte
*	功能说明: CPU从I2C总线设备读取8bit数据
*	形    参:无
*	返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t I2C_ReceiveByte(void)
{
   uint8_t i, Byte = 0x00;
    I2C1_SDA(1);	//主机释放SDA//从机把数据放到SDA
    for (i = 0; i < 8; i ++)	//循环8次
    {
        I2C1_SCL(1);		//高电平读取
        if (I2C_R_SDA() == 1)//判断SDA数据是否为高电平
        {
            Byte |= (0x80 >> i);//如果为高电平,将高电平写入Byte,不是高电平则不需要修改(Byte初始值默认低电平)
        }
        I2C1_SCL(0);		//拉低SCL,从机继续写入
    }
    return Byte;
}



/*
*********************************************************************************************************
*	函 数 名: i2c1_Ack
*	功能说明: CPU产生一个ACK信号
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c1_Ack(uint8_t AckBit)
{
		I2C1_SDA(AckBit);//主机把应答位放到SDA上
    I2C1_SCL(1);//拉高SCL,从机读取
    I2C1_SCL(0);//拉低SCL,进入下一个时序单元
		
		I2C1_SDA(1);
}

void i2c1_Nack(uint8_t AckBit)
{
		I2C1_SDA(AckBit);//主机把应答位放到SDA上
    I2C1_SCL(1);//拉高SCL,从机读取
    I2C1_SCL(0);//拉低SCL,进入下一个时序单元
}

/*
*********************************************************************************************************
*	函 数 名: i2c_WaitAck
*	功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
*	形    参:无
*	返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck(void)
{
	uint8_t re;

	I2C1_SDA(1);	/* CPU释放SDA总线 */
	
	I2C1_SCL(1);	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */

	if (I2C_R_SDA())	/* CPU读取SDA口线状态 */
	{
		re = 1;
	}
	else
	{
		re = 0;
	}
	I2C1_SCL(0);
	return re;
}

/***********************************************************
*@fuction	:MyI2C_ReceiveAck
*@brief		:接收应答
*@param		:--
*@return	:uint8_t AckBit
*@author	:--
*@date		:
***********************************************************/

uint8_t I2C_ReceiveAck(void)
{
    uint8_t AckBit;
    I2C1_SDA(1);		//主机释放SDA
    I2C1_SCL(1);		//拉高SCL,读取SDA数据
    AckBit = I2C_R_SDA();//读取数据放到AckBit上
    I2C1_SCL(0);		//拉低SCL,进入下一个时序单元
    return AckBit;
}


void I2C_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	i2c1_Start();
	i2c1_SendByte(0xD4);	//发送从机地址
	I2C_ReceiveAck();					//接收应答;返回应答位;可判断
	i2c1_SendByte(RegAddress);			//发送指定寄存器里的内容;MPU6050当前地址指针里
	I2C_ReceiveAck();					//接收应答
	/****************************
	重新指定读写位
	****************************/
	i2c1_Start();								//重复起始位
	i2c1_SendByte(0xD4);		//或上0x01 将写地址 改为 读地址
	I2C_ReceiveAck();
	/****************************
	后两句套起来循环,可以写多个字节
	****************************/
	i2c1_SendByte(Data);				//发送字节:写入指定寄存器地址下的数据
	I2C_ReceiveAck();					//接收应答
	
	i2c1_Stop();						//终止时序
}

//指定地址读一个字节
uint8_t I2C_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;		//存储读的字节
	/****************************
	指定地址
	****************************/
	i2c1_Start();
	i2c1_SendByte(0xD4);
	I2C_ReceiveAck();
	i2c1_SendByte(RegAddress);
	I2C_ReceiveAck();
	/****************************
	重新指定读写位
	****************************/
	i2c1_Start();								//重复起始位
	i2c1_SendByte((LSM6DSR_I2C_ADD_L) | 0x01);		//或上0x01 将写地址 改为 读地址
	I2C_ReceiveAck();
	/****************************
	后两句套起来循环,可以读多个字节
	****************************/
	Data = I2C_ReceiveByte();					//接收字节
	i2c1_Ack(1);							//不给从机应答
	
	i2c1_Stop();								//终止时序
	
	return Data;
}

uint8_t Lsm6dsr_GetID(void)
{
	return I2C_ReadReg(WHO_AM_I);
}


int i2c1_mem_read(uint8_t DevAddress, uint8_t MemAddress, uint8_t *ndata, uint8_t size, uint32_t Timeout)
{
		uint8_t i = 0;
		uint8_t Data;		//存储读的字节
		uint16_t time_out=0;
	/****************************
	指定地址
	****************************/
	i2c1_Start();
	i2c1_SendByte(0xD4);
	if(I2C_ReceiveAck() != 0)
	{
		goto cmd_fail;	/* 器件无应答 */
	}		
	i2c1_SendByte(MemAddress);
	if(I2C_ReceiveAck() != 0)
	{
		goto cmd_fail;	/* 器件无应答 */
	}		
	/****************************
	重新指定读写位
	****************************/
	i2c1_Start();								//重复起始位
	i2c1_SendByte((LSM6DSR_I2C_ADD_L) | 0x01);		//或上0x01 将写地址 改为 读地址
	if(I2C_ReceiveAck() != 0)
	{
		goto cmd_fail;	/* 器件无应答 */
	}		
	/****************************
	后两句套起来循环,可以读多个字节
	****************************/	
	for (i = 0; i < size - 1; i++)
	{
		ndata[i] = I2C_ReceiveByte();					//接收字节
		i2c1_Ack(0);							//1不给从机应答		
	}
	
	ndata[i] = I2C_ReceiveByte();					//接收字节	
	i2c1_Nack(1);
	
	i2c1_Stop();								//终止时序
	
	return 1;
	
	cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
	/* 发送I2C总线停止信号 */
	i2c1_Stop();
	return 0;
}

int i2c1_mem_write(uint8_t DevAddress, uint8_t MemAddress, uint8_t *ndata, uint8_t size, uint32_t Timeout)
{
	uint8_t i = 0;
	
	i2c1_Start();
	i2c1_SendByte(0xD4);	//发送从机地址
	I2C_ReceiveAck();					//接收应答;返回应答位;可判断
	i2c1_SendByte(MemAddress);			//发送指定寄存器里的内容;
	I2C_ReceiveAck();					//接收应答
//	/****************************
//	重新指定读写位
//	****************************/
//	i2c1_Start();								//重复起始位
//	i2c1_SendByte(0xD4);		//或上0x01 将写地址 改为 读地址
//	I2C_ReceiveAck();	
	
	/****************************
	后两句套起来循环,可以写多个字节
	****************************/
	for (i = 0; i < size; i++)
	{
		i2c1_SendByte(ndata[i]);				//发送字节:写入指定寄存器地址下的数据
		I2C_ReceiveAck();					//接收应答
	}
	i2c1_Stop();						//终止时序	
	
	
	return 0;
}

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐