使用STM32L496 HAL库实现I2C与M24C32 EEPROM的数据读写

I2C原理与配置

IIC原理超详细讲解—值得一看
【嵌入式硬件芯片开发笔记】EEPROM芯片M24C32配置流程
STM32硬件I2C与软件模拟I2C超详解

M24C32芯片了解

实现通信功能的芯片为M24C32,对此,芯片手册上第一页就有对其概括描述。

Automotive 32-Kbit serial I²C bus EEPROM with 1 MHz clock

启动/停止条件:当串行时钟(SCL)位于高电平状态,串行数据(SDA)位于下降沿时,M24C32开始接收数据;串行数据(SDA)位于上升沿时,M24C32停止接收数据。
数据输入:SCL上升沿时SDA进行采样。SDA必须在SCL的上升沿期间保持稳定,且当SCL被驱动为低电平时,SDA才改变电平状态。
设备寻址:设备选择代码由一个4位设备类型标识符和一个3位芯片使能地址(E2、E1、E0)组成,设备类型标识符中,1010b为选择存储器(to select the memory),1011b为选择标识页(to select the Identification page)。
在一条I2C总线上最多可连接8个存储器设备。每个片上使能输入(E2、E1、E0)上都有一个唯一的3位代码;当收到设备选择代码时,只有当芯片使能地址与E2、E1、E0输入解码值相同时,存储器设备才会响应。
第八位是读写位,1=read,0=write.
设备寻址表

读操作

看到Current Address Read这行,它是一次读当前地址数据的过程。在开始信号发出后,主设备会发出一个7位片选信号,第八位是设备读/写模式,ACK是从设备应答信号,当从设备发来一个应答信号时,主设备会发送数据,数据传输完成后,从设备不需要发应答信号,最后是主设备发送停止位结束这一次的读操作。Random Address Read是随机地址读操作,而后面的Sequential Current Read就是按顺序读了。Random Address ReadSequential Random Read模式下,需要设备发送地址才能读,所以有两次发送地址的序列。
Read operations
读指令后,若总线发送额外的时钟脉冲并确认每个传输的数据字节,则设备可按顺序输出下一字节。若终止字节流,总线必须不确认最后一个字节,并且必须生成一个停止条件。
读模式下确认:对于所有读指令,设备在发送每个字节后,在第9位时间内等待一个确认标识符,若总线主设备不发送确认(主驱动器SDA在第9位时间为高),则设备终止数据传输并进入待机模式。

写操作

看到Byte Wirte这行,它是一次写操作过程。在开始信号发出后,主设备会发出一个7位片选信号,第八位是设备读/写模式,ACK是从设备应答信号,当从设备发来一个应答信号时,主设备会给从设备发送一个字节地址,如果从设备发来应答信号,主设备此时会再发一次字节地址,当从设备应答后,主设备才会发送数据,数据传输完成后,从设备发来应答信号,最后是主设备发送停止位结束这一次的写操作。Page Write是连续写操作,前面的发片选和地址和Byte Write一样,不一样的是主设备会一直发数据。
Write operations

HAL库配置及初始化

根据原理图和芯片手册配置相关参数
M24C32引脚图

引脚配置参数配置
配置完成后,会生成一个i2c的句柄,当我们需要对i2c进行读写等操作时,对hi2c取地址,它就会调用HAL库中的寄存器回调,然后实现i2c规范中的功能(比如说init、status、mode、errcode这种)。

I2C_HandleTypeDef hi2c1;

部分代码

一开始列出的大概框架如下。

int i2c_write(const unsigned char *pwbuf, const unsigned short wbuflen);

int i2c_read(unsigned char *prBuf, const unsigned short *rbuflen);

void main(void)
{
	//write
	unsigned short wBuflen = 128;
	unsigned char wBuf[wBuflen] = {0};
	for(unsigned char i =0 ;i<wBuflen;i++)
	{
		wBuf[i] = i;
	}	
	int wret = i2c_write(wBuf, wBuflen);

	//read
	unsigned char rBuf[128] = {0};
	unsigned short rlen = 128;
	int rret = i2c_read(rBuf, rlen);
}

因为之前设置的是七位设备地址(第八位是读写位),所以在读写时需要左移一位,HAL库中I2C的读写存储器比较方便,只需要调用HAL_I2C_Mem_WriteHAL_I2C_Mem_Read函数即可。

int i2c_write(uint8_t devadd,uint16_t memadd,uint16_t memsize,uint8_t *pwbuf, const unsigned short wbuflen)
{
	devadd = (devadd<<1)&0xFF;
	if(pwbuf != NULL || wbuflen != 0)
	{
		if(HAL_I2C_Mem_Write(&hi2c1,devadd,memadd,memsize,pwbuf, wbuflen,0xFFFF)==HAL_OK)
		{
			return 1;
		}
	}
	else
	{
		return 0;
	}
}

int i2c_read(uint8_t devadd,uint16_t memadd,uint16_t memsize,uint8_t *prbuf, const unsigned short rbuflen)
{
	devadd = (devadd<<1)&0xFF;
	
	if(prbuf != NULL || rbuflen != 0)
	{
		if(HAL_I2C_Mem_Read(&hi2c1, devadd, memadd,memsize, prbuf,rbuflen,0xFFFF)==HAL_OK)
		{
			return 1;
		}
	}
	else
	{
		return 0;
	}
}

这两个函数在stm32l4xx_hal_i2c.c文件下

HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress,
                                    uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress,
                                   uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)

实际实现示例,当i2c开始写的时候,因为是一个字一个字地写,所以存储器地址每次加i,即0x0000+i,并且需要延迟一会,否则太快了芯片来不及存数据。
读就直接读,从0x0000开始读。

void I2C_ReadorWrite(uint8_t flag)
{
	if(flag == 1)
	{
		//write
		unsigned char i;
		uint8_t dat=0;
		if(HAL_I2C_IsDeviceReady(&hi2c1,M24C32_ADD<<1,2,0x00ff)==HAL_OK)
		{
			i=0;
		}
		unsigned short wBuflen = 128;
		for(i = 0;i<wBuflen;i++)//i2c clear
		{
			dat=0;
			i2c_write(M24C32_ADD,0x0000+i,I2C_MEMADD_SIZE_16BIT,&dat,1);
			delay_ms(10);
		}	
		for(i = 0;i<wBuflen;i++)//i2c write
		{
			dat=i;
			i2c_write(M24C32_ADD,0x0000+i,I2C_MEMADD_SIZE_16BIT,&dat,1);
			delay_ms(10);
		}	
	}
	else if(flag == 0)
	{
		//read
		unsigned char rBuf[128] = {0};
		unsigned short rlen = 128;
		i2c_read(M24C32_ADD,0x0000,I2C_MEMADD_SIZE_16BIT,rBuf,rlen);
//	for(i=0;i<rlen;i++)
//	{
//		printf("rBuf[%d] = %02X \n",i,rBuf[i]);
//	}
		//uint16_t re_dat=0;
		//i2c_write(M24C32_ADD,0x0000,I2C_MEMADD_SIZE_16BIT,&dat,1);
		//delay_ms(55);

		//i2c_write(M24C32_ADD,0x0000,I2C_MEMADD_SIZE_16BIT,&dat,1);
		//delay_ms(50);
		//i2c_read(M24C32_ADD,0x0000,I2C_MEMADD_SIZE_16BIT,&re_dat,1);
	}
}

遇到的问题

要了解手头这个芯片的i2c地址位数,一般都是7位,在读写时不能忘记移位。
在存储数据时记得调用delay,否则会出现输出的数据有漏掉的情况。
除了这个EEPROM芯片还有一个LP87702芯片的,在熟悉中。

物联沃分享整理
物联沃-IOTWORD物联网 » 使用STM32L496 HAL库实现I2C与M24C32 EEPROM的数据读写

发表评论