STM32软件模拟IIC通讯(还没完善)

写一页数据的时序图为:

读一个字节的时序图为:

将PB6和PB7和PC8配置为如下,PB6作为时钟线SCL,PB7作为数据线SDA,PC8为读使能

代码如下:

相关宏定义:

#define SCL_Pin GPIO_PIN_6
#define	SCL_GPIO_Port GPIOB
#define SDA_Pin GPIO_PIN_7
#define	SDA_GPIO_Port GPIOB
#define	I2C_READ	0xA1
#define	I2C_WRITE	0xA0
#define	I2C_WRITE_ENABLE	HAL_GPIO_WritePin(I2C1_WP_GPIO_Port, I2C1_WP_Pin, GPIO_PIN_RESET)
#define	I2C_WRITE_DISABLE	HAL_GPIO_WritePin(I2C1_WP_GPIO_Port, I2C1_WP_Pin, GPIO_PIN_SET)
#define SCL_OUT(X) if(X){HAL_GPIO_WritePin(SCL_GPIO_Port,SCL_Pin,GPIO_PIN_SET);}else{HAL_GPIO_WritePin(SCL_GPIO_Port,SCL_Pin,GPIO_PIN_RESET);}
#define SDA_OUT(X) if(X){HAL_GPIO_WritePin(SDA_GPIO_Port,SDA_Pin,GPIO_PIN_SET);}else{HAL_GPIO_WritePin(SDA_GPIO_Port,SDA_Pin,GPIO_PIN_RESET);}
#define SDA_IN	HAL_GPIO_ReadPin(SDA_GPIO_Port,SDA_Pin) //推挽模式下只有配置输入模式才能读取电平状态(开漏模式下可以随便读)
 起始信号:
void IIC_Start(void)
{
	SDA_OUT(1);
	SCL_OUT(1);
	SDA_OUT(0);
	SCL_OUT(0);
}
停止信号:
void IIC_Stop(void)
{
	SDA_OUT(0);
	SCL_OUT(1);
	SDA_OUT(1);
}
等待应答:
uint8_t IIC_Wait_Ack(void) 
{
	SDA_OUT(1);
	SCL_OUT(1);//SCL拉高以后,主机便可以去读取从机给的信号
	if(SDA_IN)
		{
		printf("iic wait Ack fail
");
		}
	SCL_OUT(0);
	return 0;
}
主机发送的应答信号ACK:低电平0表示应答,1表示非应答
void IIC_Ack(void)	//主机发送应答信号Ack
{
	SDA_OUT(0);
	SCL_OUT(1);
	SCL_OUT(0);
}

void IIC_NAck(void) //主机发送非应答信号NAck
{
	SDA_OUT(1);
	SCL_OUT(1);
	SCL_OUT(0);
}
主机发送8位数据给从机MSB 高位先发:
void IIC_Send_Byte(uint8_t byte)
{
	for(uint8_t i=0;i<8;i++)
		{
		SDA_OUT(byte & (0x80>>i));
		
		SCL_OUT(1);	//拉高时钟线,让从机读取数据
		SCL_OUT(0); //拉低时钟线,告诉从机别读,主机准备发送下一位数据
		}
}

从机发送8位数据给主机:

uint8_t IIC_Read_Byte(void)
{
	uint8_t byte = 0;
	//SDA配置为输入模式
	//IIC_SDA_Mode_IN();
	
	//SCL_OUT(0);//先拉低时钟线,等待从机发送数据
	SDA_OUT(1);	//主机需要释放SDA,这时从机会自己占据SDA线给主机发送数据
	for(uint8_t i=0;i<8;i++)
	{
		SCL_OUT(1);//拉高时钟线,主机开始接收数据
		if(SDA_IN == GPIO_PIN_SET)
			{
			byte |= (0x80>>i); //数据为     1
			}
		SCL_OUT (0);//告诉对方此时准备数据
	}
	
	return byte;
}

I2C写一个字节:

void IIC_Mem_Write_Byte(uint8_t dev_addr, uint16_t mem_addr,uint8_t pData)
{
    IIC_Start();
    IIC_Send_Byte(dev_addr);//写设备地址
  	IIC_Wait_Ack();
	
    IIC_Send_Byte(mem_addr>>8);//写内存地址高8位
  	IIC_Wait_Ack();
	IIC_Send_Byte(mem_addr&0xff);//写内存地址低8位
  	IIC_Wait_Ack();

    IIC_Send_Byte(pData);
  	IIC_Wait_Ack();
	
    IIC_Stop();
}

I2C读一个字节:

uint8_t IIC_Mem_Read_Byte(uint8_t dev_addr, uint16_t mem_addr)
{
	uint8_t data;
    IIC_Start();
    IIC_Send_Byte(dev_addr);//写设备地址
  	IIC_Wait_Ack();
	
    IIC_Send_Byte(mem_addr>>8);//写内存地址高8位
  	IIC_Wait_Ack();
	IIC_Send_Byte(mem_addr&0xff);//写内存地址低8位
  	IIC_Wait_Ack();
	
    IIC_Start(); //再次发送一次起始信号
    IIC_Send_Byte(dev_addr | 0x01);//再发送一次写设备地址(最后一位置1,表示对从机进行数据读操作)
  	IIC_Wait_Ack();

    data = IIC_Read_Byte();
  	IIC_NAck();
    IIC_Stop();
	return data;
}

I2C写一页数据:

void IIC_Mem_Write_Page_Data(uint8_t dev_addr, uint16_t mem_addr,uint8_t *pData,uint16_t size)
{
	uint8_t i=0;
    IIC_Start();
    IIC_Send_Byte(I2C_WRITE);//设备地址
  	IIC_Wait_Ack();

    IIC_Send_Byte(mem_addr>>8);//内存地址高8位
  	IIC_Wait_Ack();
	
	IIC_Send_Byte(mem_addr&0xff);//内存地址低8位
  	IIC_Wait_Ack();
		
	while(size>0)
		{
	    IIC_Send_Byte(*pData++);
	  	IIC_Wait_Ack();
		size--;
		i++;
		if(i == 32)	//写满一页就退出
			{
			break;
			}
		}
	
    IIC_Stop();
}

I2C读一页数据:

void IIC_Mem_Read_Page_Data(uint8_t dev_addr, uint16_t mem_addr,uint8_t *pData,uint16_t size)
{
    IIC_Start();
    IIC_Send_Byte(I2C_WRITE);//设备地址
  	IIC_Wait_Ack();

    IIC_Send_Byte(mem_addr>>8);//内存地址高8位
  	IIC_Wait_Ack();
	IIC_Send_Byte(mem_addr&0xff);//内存地址低8位
  	IIC_Wait_Ack();
	
    IIC_Start(); //再次发送一次起始信号
    IIC_Send_Byte(I2C_READ);//再发送一次设备地址(最后一位置1,表示对从机进行数据读操作)
  	IIC_Wait_Ack();
	
	while(size>1)
		{
		*pData++ = IIC_Read_Byte();
		IIC_Ack();
		size--;
		}
	*pData++ = IIC_Read_Byte();
	IIC_NAck();
	
    IIC_Stop();
}

部分测试代码(一个字节):

    uint8_t dev_Addr = 0xA0;//设备地址,不同设备可能不一样
    uint16_t memAddr = 0x0000;
    uint8_t sendData = 0x55;
    uint8_t Data;
  
	I2C_WRITE_ENABLE;    //打开I2C写使能
	IIC_Mem_Write_Byte(dev_Addr , memAddr, sendData);
	Delay_ms(5); //这里一定得有延时,具体多少不清楚
	I2C_WRITE_DISABLE;

	Data= IIC_Mem_Read_Byte(dev_Addr , memAddr);
	printf("%d
",Data);

结果:

部分测试代码(读取一页数据和写一页数据):

uint8_t Pri_read_from_eprom(void)
{
	
	IIC_Mem_Read_Page_Data(I2C_WRITE,(4 <<5),(uint8_t *)&config_value, 32);

	IIC_Mem_Read_Page_Data(I2C_WRITE,(5 <<5),(uint8_t *)&config_set, 16);
	
	IIC_Mem_Read_Page_Data(I2C_WRITE,(6 <<5),(uint8_t *)&config2_value2, 32);

}
uint8_t Restor_All_Init_to_E2P(void)//备份所有数据到EEPROM
{
	I2C_WRITE_ENABLE;
	Delay_ms(10);

	for(uint8_t j=0;j<8;j++)
		{
		IIC_Mem_Write_Page_Data(I2C_WRITE,((7 + (3*j))<<5),(uint8_t *)(&config_value_init[j]), 32);
	    Delay_ms(10);

		
		IIC_Mem_Write_Page_Data(I2C_WRITE,((8 + (3*j))<<5),(uint8_t *)(&config_set_init[j]), 32);
	    Delay_ms(10);


		IIC_Mem_Write_Page_Data(I2C_WRITE,((9 + (3*j))<<5),(uint8_t *)(&config2_value2_init[j]), 32);
	    Delay_ms(10);
		}
	I2C_WRITE_DISABLE;	

}