在嵌入式设计中,E2PROM存储芯片常被应用于需要掉电存储,且容量不大的场合。对比flash存储芯片,E2PROM具有擦写次数多,允许字节编程的特点,因此,更适合于数据量存储不大,且更需要频率擦写的应用场景。今天,就以HC32F460为平台,简单介绍下如何驱动E2PROM存储芯片AT24C6。
从芯片手册我们可以找到关于芯片的引脚图和引脚说明如下
其中,A0A1A2这三个脚为设备地址输入脚,当有多个E2PROM设备级联时,通过此引脚的定义来实现对不同设备的寻址。SDA和SDL为标志的IIC接口,WP为写保护脚(默认接地即可)。
AT24C64典型的应用电路原理图如下:
关于IIC接口的时序,这个是老生常谈的问题了,在此就不再赘述。这里就直接开始介绍如何通过HC32F460的硬件IIC来驱动AT24C64.
首先是在库函数配置文件ddl_config.h中打开宏定义
#define DDL_I2C_ENABLE (DDL_ON)
工程中添加相关的.c文件
接着是引脚定义,这里我们选择PC10和PC11脚,并将之复用为IIC接口
/* Define I2C unit used for the example */
#define I2C_UNIT (M4_I2C1)
/* Define port and pin for SDA and SCL */
#define I2C_SCL_PORT (PortC)
#define I2C_SCL_PIN (Pin10)
#define I2C_SDA_PORT (PortC)
#define I2C_SDA_PIN (Pin11)
#define I2C_GPIO_SCL_FUNC (Func_I2c1_Scl)
#define I2C_GPIO_SDA_FUNC (Func_I2c1_Sda)
#define I2C_FCG_USE (PWC_FCG1_PERIPH_I2C1)
AT24C64相关的宏定义
/* Define E2PROM device address */
#define E2_ADDRESS (0x50u)
/* 24c256 page length */
#define E2_PAGE_LEN (32u)
#define E2_MEM_ADR_LEN (2u)
/* Define test address for read and write */
#define DATA_TEST_ADDR (0x0000u)
这些定义都可以在数据手册中找到相关的说明,但是有一点需要注意的是,这里我们将E2PROM的设备地址定义为0x50,但是实际上在手册中,我们查到的设备地址是0xA0
这是因为我们MCU这边设置默认的是7bit的从机地址,这里还有一位是不包括最低位的读写操作位,因此,去掉最低位的读写位,可以得到实际的设备地址是0x50 。
初始化代码如下:
/**
******************************************************************************
** \brief Initialize the I2C peripheral for at24cxx
** \param None
** \retval en_result_t Enumeration value:
** - Ok: Success
** - ErrorTimeout: Time out
******************************************************************************/
en_result_t At24cxx_Initialize(void)
{
stc_i2c_init_t stcI2cInit;
en_result_t enRet;
float32_t fErr;
/* Initialize I2C port*/
PORT_SetFunc(I2C_SCL_PORT, I2C_SCL_PIN, I2C_GPIO_SCL_FUNC, Disable);
PORT_SetFunc(I2C_SDA_PORT, I2C_SDA_PIN, I2C_GPIO_SDA_FUNC, Disable);
/* Enable I2C Peripheral*/
PWC_Fcg1PeriphClockCmd(I2C_FCG_USE, Enable);
I2C_DeInit(I2C_UNIT);
MEM_ZERO_STRUCT(stcI2cInit);
stcI2cInit.u32ClockDiv = I2C_CLK_DIV8;
stcI2cInit.u32Baudrate = 100000ul;
stcI2cInit.u32SclTime = 0ul;
enRet = I2C_Init(I2C_UNIT, &stcI2cInit, &fErr);
I2C_BusWaitCmd(I2C_UNIT, Enable);
return enRet;
}
AT24C64写:
/**
******************************************************************************
** \brief at24cxx memory write
**
** \param u8DevAddr The slave address
** \param u16MemAddr The memory address
** \param pu8Data Pointer to the data buffer
** \param u32Size Data size
** \param u32TimeOut Time out count
** \retval en_result_t Enumeration value:
** - Ok: Success
** - Error: Receive NACK
** - ErrorTimeout: Time out
******************************************************************************/
en_result_t At24cxx_Mem_Write(uint16_t u8DevAddr, uint16_t u16MemAddr, uint8_t *pu8Data, uint32_t u32Size, uint32_t u32TimeOut)
{
en_result_t enRet;
uint16_t u16MemAddrTemp;
u16MemAddrTemp = ((u16MemAddr >> 8) & 0xFFul) + ((u16MemAddr << 8) & 0xFF00ul);
I2C_Cmd(I2C_UNIT, Enable);
I2C_SoftwareResetCmd(I2C_UNIT, Enable);
I2C_SoftwareResetCmd(I2C_UNIT, Disable);
enRet = I2C_Start(I2C_UNIT,u32TimeOut);
if(Ok == enRet)
{
enRet = I2C_TransAddr(I2C_UNIT, u8DevAddr, I2CDirTrans, u32TimeOut);
if(Ok == enRet)
{
enRet = I2C_TransData(I2C_UNIT, (uint8_t *)&u16MemAddrTemp, E2_MEM_ADR_LEN, u32TimeOut);
if(Ok == enRet)
{
enRet = I2C_TransData(I2C_UNIT, pu8Data, u32Size, u32TimeOut);
}
}
}
I2C_Stop(I2C_UNIT,u32TimeOut);
I2C_Cmd(I2C_UNIT, Disable);
return enRet;
}
AT24C64读:
/**
******************************************************************************
** \brief at24cxx memory read
**
** \param u8DevAddr The slave address
** \param u16MemAddr The memory address
** \param pu8Data Pointer to the data buffer
** \param u32Size Data size
** \param u32TimeOut Time out count
** \retval en_result_t Enumeration value:
** - Ok: Success
** - ErrorTimeout: Time out
******************************************************************************/
en_result_t At24cxx_Mem_Read(uint8_t u8DevAddr, uint16_t u16MemAddr, uint8_t *pu8Data, uint32_t u32Size, uint32_t u32TimeOut)
{
en_result_t enRet;
uint16_t u16MemAddrTemp;
u16MemAddrTemp = ((u16MemAddr >> 8) & 0xFFul) + ((u16MemAddr << 8) & 0xFF00ul);
I2C_Cmd(I2C_UNIT, Enable);
I2C_SoftwareResetCmd(I2C_UNIT, Enable);
I2C_SoftwareResetCmd(I2C_UNIT, Disable);
enRet = I2C_Start(I2C_UNIT,u32TimeOut);
if(Ok == enRet)
{
enRet = I2C_TransAddr(I2C_UNIT, (uint8_t)u8DevAddr, I2CDirTrans, u32TimeOut);
if(Ok == enRet)
{
enRet = I2C_TransData(I2C_UNIT, (uint8_t *)&u16MemAddrTemp, E2_MEM_ADR_LEN, u32TimeOut);
if(Ok == enRet)
{
enRet = I2C_Restart(I2C_UNIT,u32TimeOut);
if(Ok == enRet)
{
if(1ul == u32Size)
{
I2C_AckConfig(I2C_UNIT, I2c_NACK);
}
enRet = I2C_TransAddr(I2C_UNIT, (uint8_t)u8DevAddr, I2CDirReceive, u32TimeOut);
if(Ok == enRet)
{
enRet = I2C_MasterDataReceiveAndStop(I2C_UNIT, pu8Data, u32Size, u32TimeOut);
}
I2C_AckConfig(I2C_UNIT, I2c_ACK);
}
}
}
}
if(Ok != enRet)
{
I2C_Stop(I2C_UNIT,u32TimeOut);
}
I2C_Cmd(I2C_UNIT, Disable);
return enRet;
}
测试代码:
void At24cxx_Test(void)
{
uint8_t u8TxBuf[E2_PAGE_LEN];
uint8_t u8RxBuf[E2_PAGE_LEN];
uint32_t i;
for(i=0ul; i<E2_PAGE_LEN; i++)
{
u8TxBuf[i] = (uint8_t)i+1u;
}
memset(u8RxBuf, 0x00, E2_PAGE_LEN);
/* Initialize I2C peripheral and enable function*/
At24cxx_Initialize();
/* E2prom byte write*/
At24cxx_Mem_Write(E2_ADDRESS, DATA_TEST_ADDR, u8TxBuf, 1u, TIMEOUT);
/* 5mS delay for e2prom*/
Ddl_Delay1ms(5ul);
/* E2prom ramdom read*/
At24cxx_Mem_Read(E2_ADDRESS, DATA_TEST_ADDR, u8RxBuf, 1u, TIMEOUT);
/* Compare the data */
if(0x01u != u8RxBuf[0])
{
/* e2prom byte write error */
PRO_LOG(LOG_DEBUG, "at24cxx byte write error! \r\n");
}
/* 5mS delay for e2prom*/
Ddl_Delay1ms(5ul);
/* E2prom page write*/
At24cxx_Mem_Write(E2_ADDRESS, DATA_TEST_ADDR, u8TxBuf, E2_PAGE_LEN, TIMEOUT);
/* 5mS delay for e2prom*/
Ddl_Delay1ms(5ul);
/* E2prom sequential read*/
At24cxx_Mem_Read(E2_ADDRESS, DATA_TEST_ADDR, u8RxBuf, E2_PAGE_LEN, TIMEOUT);
/* Compare the data */
for(i=0ul; i<E2_PAGE_LEN; i++)
{
if(u8TxBuf[i] != u8RxBuf[i])
{
/* e2prom page write error*/
PRO_LOG(LOG_DEBUG, "at24cxx page write error! \r\n");
}
}
}
烧录编译,使用逻辑分析仪查看波形,波形图如下:
关于E2PROM的驱动部分就暂时介绍到这里了,若是上述有误,欢迎大家在评论区指出。