F103芯片使用软件IIC(拿去就能用系列)
·
环境: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;
}
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)