stm32 IIC通讯协议(软件模拟)

一,IIC的物理层
stm32 IIC通讯协议(软件模拟)_第1张图片
1.从上图可以看出,IIC通讯的物理层仅需要两根总线SDA(数据传输总线).SCL(时钟控制总线),它不需要外部收发设备,因此结构简单,广泛应用于设备内部的通讯。它还支持多个主设备和多个从设备,每个设备拥有一个独一无二的地址,凭借该地址进行通讯。
2.IIC通讯有三种通讯速度:
A.标准模式(速度为100k bit/s)
B.快速模式(速度为400k bit/s)
C.高速模式(速度为3.4M bit/s)但目前大多数IIC尚不支持该速度
二.IIC的协议层
1.主机模式stm32 IIC通讯协议(软件模拟)_第2张图片
stm32 IIC通讯协议(软件模拟)_第3张图片
stm32 IIC通讯协议(软件模拟)_第4张图片
其中我们常用的为符合通讯方式,即为某一指定位置里面写/读数据
IIC的起始信号:
stm32 IIC通讯协议(软件模拟)_第5张图片
即为在SCL为高的情况下,SDA由高电平转为低电平
IIC的终止信号:
在这里插入图片描述
即为在SCL为高的情况下,SDA由低电平转为高电平
三.软件实现部分

uint8_t SendByte_EE(uint8_t addr,uint8_t data){//往外设中指定位置写一个字节
	uint8_t wait;
	IIC_Start();
	IIC_SendByte(AT24C02_Write);//AT24C02_Write发送IIC从机的地址(7位)+1位读写位
	wait=IIC_Wait_Ack();
	if(!wait){
		IIC_SendByte(addr);//发送要写尽外设内存的地址
		wait=IIC_Wait_Ack();
		if(!wait){
			IIC_Start();
			IIC_SendByte(AT24C02_Write);//继续写命令
			wait=IIC_Wait_Ack();
			if(!wait){
				IIC_SendByte(data);//发送要写的内容
				IIC_Stop();
				Delay_us(10);
			}
		}
	}
	return 0;
}
uint8_t RecvByte_EE(uint8_t addr){//从外设中指定位置读一个字节
	uint8_t value,wait;
	IIC_Start();
	IIC_SendByte(AT24C02_Write);
	wait=IIC_Wait_Ack();
	if(!wait){
		IIC_SendByte(addr);
		wait=IIC_Wait_Ack();
		if(!wait){
			IIC_Start();
			IIC_SendByte(AT24C02_Read);
			wait=IIC_Wait_Ack();
			if(!wait){
				value=IIC_RecvByte(0);
				IIC_Stop();
			}
		}
	}
	return value;
}
void IIC_Start(void){//起始信号,SCL为高的情况下SDA变低然后拉低SCL方便进行下面的数据传输防止数据发送与起始信号搞混
	IIC_SDA_OUT();//将IO口配置为输出模式
	GPIO_SetBits(IIC_SCL_Port,IIC_SCL_Pin);
	GPIO_SetBits(IIC_SDA_Port,IIC_SDA_Pin);
	
	Delay_us(5);
	GPIO_ResetBits(IIC_SDA_Port,IIC_SDA_Pin);//产生起始信号
	Delay_us(5);
	GPIO_ResetBits(IIC_SCL_Port,IIC_SCL_Pin);
	
}
void IIC_Stop(void){//终止信号
	
	IIC_SDA_OUT();	
	GPIO_ResetBits(IIC_SDA_Port,IIC_SDA_Pin);
	GPIO_ResetBits(IIC_SCL_Port,IIC_SCL_Pin);
	Delay_us(5);
	GPIO_SetBits(IIC_SCL_Port,IIC_SCL_Pin);
	GPIO_SetBits(IIC_SDA_Port,IIC_SDA_Pin);
	Delay_us(5);
	
}
void IIC_SendByte(uint8_t value){//发送一个字节的数据包
	
	uint8_t i=0;
	IIC_SDA_OUT();
	GPIO_ResetBits(IIC_SCL_Port,IIC_SCL_Pin);
	
	for(i=0;i<8;i++){
		if(value & 0x80){
			GPIO_SetBits(IIC_SDA_Port,IIC_SDA_Pin);		
		}
		else{
			GPIO_ResetBits(IIC_SDA_Port,IIC_SDA_Pin);	
		}
		Delay_us(5);
		GPIO_SetBits(IIC_SCL_Port,IIC_SCL_Pin);
		Delay_us(5);
		GPIO_ResetBits(IIC_SCL_Port,IIC_SCL_Pin);
		value<<=1;
		Delay_us(5);
	}
}
uint8_t IIC_RecvByte(uint8_t ack){//接受一个字节的数据包
	uint8_t value=0,i;
	IIC_SDA_IN();
	for(i=0;i<8;i++){
		GPIO_SetBits(IIC_SCL_Port,IIC_SCL_Pin);
		Delay_us(5);
		value<<=1;
		if(GPIO_ReadInputDataBit(IIC_SDA_Port,IIC_SDA_Pin)){
			value++;
		}
		GPIO_ResetBits(IIC_SCL_Port,IIC_SCL_Pin);//让总线保持繁忙状态
	}
	  if (!ack)
        IIC_NACK();//发送nACK
    else
        IIC_ACK(); //发送ACK
	return value;
}
//若第九个数据信号为低电平则为应答信号,为高电平则为非应答信号
void IIC_ACK(void){
	
	GPIO_ResetBits(IIC_SCL_Port,IIC_SCL_Pin);
	GPIO_ResetBits(IIC_SDA_Port,IIC_SDA_Pin);
	Delay_us(5);
	GPIO_SetBits(IIC_SCL_Port,IIC_SCL_Pin);
	Delay_us(5);
	GPIO_ResetBits(IIC_SCL_Port,IIC_SCL_Pin);//因为当SCL和SDA均为高的时候总线为空闲,因此需要钳住SCL
}
void IIC_NACK(void){
	
	GPIO_ResetBits(IIC_SCL_Port,IIC_SCL_Pin);
	GPIO_SetBits(IIC_SDA_Port,IIC_SDA_Pin);
	
	Delay_us(5);
	GPIO_SetBits(IIC_SCL_Port,IIC_SCL_Pin);
	Delay_us(5);
	GPIO_ResetBits(IIC_SCL_Port,IIC_SCL_Pin);//因为当SCL和SDA均为高的时候总线为空闲,因此需要钳住SCL
}
uint8_t IIC_Wait_Ack(void){
	uint8_t i,flag=0;
	IIC_SDA_IN();
	GPIO_SetBits(IIC_SDA_Port,IIC_SDA_Pin);
	GPIO_SetBits(IIC_SCL_Port,IIC_SCL_Pin);
	Delay_us(3);
	
	for(i=0;i<250;i++){
		if(!GPIO_ReadInputDataBit(IIC_SDA_Port,IIC_SDA_Pin)){
			flag=1;
			break;
		}
	}
	if(flag==0){
		IIC_Stop();
		return 1;
	}	
	GPIO_ResetBits(IIC_SCL_Port,IIC_SCL_Pin);
	return 0;
}
void IIC_SDA_IN(){
	GPIO_InitTypeDef GPIO_structure;
	GPIO_structure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_structure.GPIO_Pin=IIC_SDA_Pin;
	GPIO_structure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(IIC_SDA_Port,&GPIO_structure);

}

void IIC_SDA_OUT(){
	GPIO_InitTypeDef GPIO_structure;
	GPIO_structure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_structure.GPIO_Pin=IIC_SDA_Pin;
	GPIO_structure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(IIC_SDA_Port,&GPIO_structure);

}

你可能感兴趣的:(笔记,stm32,单片机,arm)