STM32软件模拟IIC读写BL24C32(EEPROM)

本文详细描述了I2C通信中芯片时序图的基本元素,如起始、停止、读写信号,以及如何使用HAL库配置GPIO和实现I2C操作,包括起始、停止信号的发送,等待应答,以及数据的传输。
摘要由CSDN通过智能技术生成
不同的芯片的时序图可能不一样,但起始信号、等待应答信号、发送应答信号、读写一个字节、停止信号都是一样的,然后根据不同时序将这些组合起来就可以了。
写一页数据的时序图为(其他的时序图就不放了):

读一个字节的时序图为:

直接在HAL库里面配置成以下:

PB6作为时钟线SCL,PB7作为数据线SDA,PC8为读使能

代码如下:

相关宏定义(因为给SDA配置的是开漏模式,所以不需要变化模式,直接读即可):

#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) //推挽模式下只有配置输入模式才能读取电平状态(开漏模式下可以随便读)
 起始信号:(我的IO口的速度较快不需要延时的,其他的大多数都是需要有延时穿插的)
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\n");
		}
	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_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写一页数据(可以写32个字节以下任何数量):

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读一页数据(可以读32个字节以下任何数量):

void IIC_Mem_Read_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();
	
    IIC_Start(); //再次发送一次起始信号
    IIC_Send_Byte(I2C_READ);//再发送一次设备地址(最后一位置1,表示对从机进行数据读操作)
  	IIC_Wait_Ack();
	
	while(size>1)
		{
		*pData++ = IIC_Read_Byte();
		IIC_Ack();
		size--;
        i++;
		if(i == 32)	//读完一页就退出
			{
			break;
			}
		}
	*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(10); //这里一定得有延时,具体多少不清楚
	I2C_WRITE_DISABLE;

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

结果:

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


    uint8_t con_value[5]={1,2,3,4,5};
  
	  I2C_WRITE_ENABLE;
	  Delay_ms(10);
	  IIC_Mem_Write_Page_Data(I2C_WRITE,(18<<5),(uint8_t *)con_value, 5);
	  Delay_ms(10);
	  I2C_WRITE_DISABLE;  
  
	  con_value[0]=5;
	  con_value[1]=4;
	  con_value[2]=3;
	  con_value[3]=2;
	  con_value[4]=1;
	  
	  IIC_Mem_Read_Page_Data(I2C_WRITE,(18<<5),(uint8_t *)con_value, 5);
	  printf("temp1=%d\n",con_value[0]);
	  printf("temp2=%d\n",con_value[1]);
	  printf("temp3=%d\n",con_value[2]);
	  printf("temp4=%d\n",con_value[3]);
	  printf("temp5=%d\n",con_value[4]);

结果

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值