IIC通讯(软件模拟+硬件IIC)

阅读: 评论:0

IIC通讯(软件模拟+硬件IIC)

IIC通讯(软件模拟+硬件IIC)

工程文件链接:

=6666 
提取码:6666

由于C8T6有两个硬件IIC我把硬件IIC和软件IIC结合在一起了,只用修改两个宏定义就可以来回切换硬件IIC和软件模拟IIC ,其他基本不用动,目前里面的例子是MPU6050,大家可以自行修改

软件IIC模拟

我们假设用PB10和PB11作为IIC的SCL和SDA

注意SCL拉高读取数据,SCL拉低允许数据变化

注意SCL,SDA默认都是高电平

注意操作完某根线后记得放开对该线的操作权

GPIO配置

#define IIC_SCL(x)		MyI2C_W_SCL(x)
#define IIC_SDA(x)		MyI2C_W_SDA(x)
#define IIC_Read_SDA    MyI2C_R_SDA()  //读数据/********************配置GPIO**************************************/
void MyI2C_W_SCL(uint8_t BitValue)
{GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);Delay_us(10);
}void MyI2C_W_SDA(uint8_t BitValue)
{GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);Delay_us(10);
}uint8_t MyI2C_R_SDA(void)
{uint8_t BitValue;BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);Delay_us(10);return BitValue;
}void MyI2C_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}
/********************配置GPIO结束**************************************/

IIC状态配置 之(呼叫IIC总线上的设备看看有没有反应)

1.起始信号

起始条件:SCL为高电平期间SDA从高电平切换到低电平

注意:实际实验得出  最后一步IIC_SCL(0) 绝对不能省略,要不然产生不了起始信号

/*当SCL和SDA都处于高电平,我们的SDA由高电平变到低电平并保存一定时间,意味着起始信号的产生*/
//起始信号
void IIC_Start(void)
{IIC_SDA(1);IIC_SCL(1);IIC_SDA(0);IIC_SCL(0);
}

2.停止信号

 终止条件:SCL为高电平期间SDA从低电平切换到高电平期间

/*SCL为高电平期间SDA从低电平切换到高电平期间*/
/*结束信号*/
void IIC_End(void)
{IIC_SDA(0);IIC_SCL(1);	IIC_SDA(1);
}

3.发送一个字节

 注意:下面这种错误示范    看似执行效果和正确的一样,但是实际效果不能这样

        IC_SCL(0);IC_SDA( value&(0x80>>i));//把数据放到SDA上IIC_SCL(1);//释放掉SCL

这才是正确的(因为前面起始信号最后已经给了一个IIC_SCL(0))所以我们才可以直接操作SDA

所以我才说起始信号最后一步IIC_SCL(0) 绝对不能省略,要不然产生不了起始信号

/*SCL低电平期间主机把数据位依次放到SDA总线上(高位先行MSB),然后释放SCL,复这种时序8次就发送完成了一个字节*/
/*发送一个字节(MSB高位先行)*/
void IIC_Write_Byte(uint8_t value)
{for(int i=0;i<8;i++){IIC_SDA( value&(0x80>>i));IIC_SCL(1);IIC_SCL(0);}
}

 4.接收应答

可以理解为读取从机数据,而读取数据呢需要把SCL拉高才能读取走数据

按照上面解释这样就可以了,然而实际实验确实可以但是有一定弊端,可能出现读取不了或者数组错乱的现象

uint8_t IIC_Read_ACK(void)
{uint8_t byte=0x00;IIC_SCL(1);byte=IIC_Read_SDA;IIC_SCL(0);return byte;
}

所以可以参照下面这样读取之前加一个 IIC_SDA(1);,为什么加这个呢?因为前面我们发送数据完成后这个SDA引脚的状态是不确定的不知道SDA这根线是不是还是被控制着的。我们IIC通讯SCL和SDA初始值都是高电平,一般我们操作某一根线后都需要把这跟线还原成原来的状态,也叫交出控制权。从上面发送一个字节也可以看出每次都是操作完成后交出了总线的控制权。

// 接收应答:主机发送完成一个字节后下一个时钟接收一位数据位,判断从机是否应答(0表示应答)
uint8_t IIC_Read_ACK(void)
{uint8_t byte=0x00;IIC_SDA(1);IIC_SCL(1);byte=IIC_Read_SDA;IIC_SCL(0);return byte;
}

呼叫总线上的设备

完成上面这些步骤就可以呼叫总线上的设备了

	
IIC_Start();
IIC_Write_Byte(0xD0);
uint8_t s= IIC_Read_ACK();
IIC_End();
if(s==1)
{//完了没有IIC设备
}	

接收一个字节

注意这里最开头的IIC_SDA(1);效果也是和发送一样让总线控制权交出来,下面的接收就直接拉高SCL让数据被读取走最后复位SCL的状态方便下次传输

/*从机将在SCL高电平期间读取数据位,所以SCL为高电平期间SDA不允许有数据变化,重复这种时序8次就接收完成了一个字节*/
/*读取一个字节*/
uint8_t IIC_Read_Byte(void)
{IIC_SDA(1);uint8_t byte=0x00;for(int i=0;i<8;i++){IIC_SCL(1);if(IIC_Read_SDA==1){ byte|=(0x80>>i);}//是1则改变,是0则不变IIC_SCL(0);}return byte;
}

发送应答

注意SCL拉高读取数据,SCL拉低允许数据变化

//发送应答:主机接收完成一个字节后,下一个时钟发送一个数据位表示应答(0应答,1非应答)
void IIC_Write_ACK(uint8_t value)
{ IIC_SDA( value);//把应答放到SDA上IIC_SCL(1);//释放掉SCLIIC_SCL(0);//拉低scl表示要开始发数据了}

接下来实现硬件IIC

库函数IIC

void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);
发送7位IIC地址void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);
产生起始信号
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);
产生起结束信号void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);
//主机收到一个字节后是否给从机应答  接收应答
void I2C_OwnAddress2Config(I2C_TypeDef* I2Cx, uint8_t Address);
//主机发送完成后是否给出应答        发送应答void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);
//IIC发送一个字节
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);
//IIC接收一个字节FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);//读取标志位
void I2C_ClearFlag(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);//清除标志位
ITStatus I2C_GetITStatus(I2C_TypeDef* I2Cx, uint32_t I2C_IT);//读取中断标志位
void I2C_ClearITPendingBit(I2C_TypeDef* I2Cx, uint32_t I2C_IT);//清除中断标志位

看框架图片可以得出IIC1挂载到APB1上面的

 可以看出IIC1对应的引脚是PA6,PA7

我们对照这张硬件IIC时序图片来写

第一步打开时钟,并初始化要用到的GPIO

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10| GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);

发送起始信号并检查EV5标志

//硬件IIC产生起始信号
void YJ_IIC_Start(void){	int i=100;I2C_GenerateSTART(YJ_IICx, ENABLE);//EV5while(I2C_CheckEvent(YJ_IICx,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS){Delay_us(1);i--;if(i==10){break;}}
}

发送停止信号

//硬件IIC结束信号
void YJ_IIC_Stop(void){I2C_GenerateSTOP(YJ_IICx,ENABLE);}	

发送器件地址+(读写方向0或者1)并检查EV6事件

void YJ_IIC_Write_7bitAddr( uint8_t Data)
{int i=100;if((Data&0x01)==1)//是读取{//YJ_IIC_Write_byte(Data);I2C_Send7bitAddress(YJ_IICx,Data,I2C_Direction_Receiver);//检查EV6while(I2C_CheckEvent(YJ_IICx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)!=SUCCESS&&i!=0){Delay_us(10);i--;}	}else{I2C_Send7bitAddress(YJ_IICx,Data,I2C_Direction_Transmitter);//检查EV6while(I2C_CheckEvent(YJ_IICx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS&&i!=0){Delay_us(10);i--;}}
}	

发送一个字节数据(非最后一个字节才能这样发送)并检查EV8事件

//硬件IIC发送数据
void YJ_IIC_Write_byte( uint8_t Data)
{int i=100;	 //EV8I2C_SendData(YJ_IICx,Data);while(I2C_CheckEvent(YJ_IICx,I2C_EVENT_MASTER_BYTE_TRANSMITTING)!=SUCCESS&&i!=0){Delay_us(10);i--;}
}	

 发送一个字节数据(这个数据是最后一个字节)并检查EV8_1事件

//硬件IIC发送最后一个数据(结束信号的前一个数据)
void YJ_IIC_Write_byteLastof( uint8_t Data)
{int i=100;	 I2C_SendData(YJ_IICx,Data);//EV8while(I2C_CheckEvent(YJ_IICx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS&&i!=0){Delay_us(10);i--;}
}	

接收一个字节数据

 读取最后一个字节数据并检查标志位

//硬件IIC读取数据(一个字节)
//读取多个字节自己加循环并且最后一个数据之前要提升发送非应答和停止
uint8_t YJ_IIC_Reade_byte(void)
{int i=100;	I2C_AcknowledgeConfig(YJ_IICx,DISABLE);I2C_GenerateSTOP(YJ_IICx,ENABLE);while(I2C_CheckEvent(YJ_IICx,I2C_EVENT_MASTER_BYTE_RECEIVED)!=SUCCESS&&i!=0){Delay_us(10);i--;}uint8_t dat= I2C_ReceiveData(YJ_IICx);I2C_AcknowledgeConfig(YJ_IICx,ENABLE);return dat;
}

接收多个字节数据

void YJ_IIC_Reade_bytes(uint8_t num,uint8_t nums[])
{int i=100;	for(int z=0;z<num;z++){if(z==num-1){I2C_AcknowledgeConfig(YJ_IICx,DISABLE);I2C_GenerateSTOP(YJ_IICx,ENABLE);}while(I2C_CheckEvent(YJ_IICx,I2C_EVENT_MASTER_BYTE_RECEIVED)!=SUCCESS&&i!=0)//I2C_EVENT_MASTER_BYTE_RECEIVED{Delay_us(10);i--;}nums[i]= I2C_ReceiveData(YJ_IICx);
if(z==num-1)
{I2C_AcknowledgeConfig(YJ_IICx,ENABLE);	
}}	
}

最总代码

使用着基本参照上面例子任意修改发送的数据和器件地址,换成硬件IIC或者软件IIC都不需要修改其他东西,函数名称已经互相兼容了.

本文发布于:2024-01-28 02:42:33,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/17063809614193.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:通讯   硬件   软件   IIC
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23