AT24C02D
-
AT24C02D由于不是标准的I2C总线,所以没有完全使用I2C的8字节地址总线,而是兼容I2c,估计是为了逃避NXP的专利费,使用了5+3外部引脚设置(A2,A1,A0)来决定和哪路器件通讯的,一个i2c总线上最多能挂载8个AT24C02D,刚好是2的三次方。
-
不明白为什么AT24C02D把i2c里面的sub address叫做Word Address,很是奇怪,我这里表述都按照sub address来表述,下同。
-
AT24C02D 作为CC2538外挂的eeprom,通过i2c总线和CC2538通讯,实现读写,花了很长时间一直搞不定,网上找的十有八九都是什么GPIO模拟i2c,我的CC2538本身就有i2c接口,还模拟什么模拟。
-
开始没搞懂读写AT24C02D eeprom,以为和普通的i2c器件一样,写的话,发送slave address+ sub address+data,就可以写了,读的话,发送slave address+sub address,就可以读出指定地址的数据了,其实是错的。
-
我们都知道,i2c有起始位,也就是Start,有停止位,也就是Stop。写的话,按照eeprom芯片手册给的时序图,就是Start以后,发送从机地址slave address,发送完从机地址,是不能有Stop的,必须接着发送sub address,然后也不能有Stop,接着发送data,就是需要写入的数据,最后再发送Stop,才算是完成了一次写操作,真麻烦。
-
读的话,芯片手册里面提到的有三种读的方式,分别为Current Address Read、Random Address Read、Sequential Read,
-
在真正写入数据的时候,eeprom需要有一个时间等待写入完成,叫做self-write time,简称Twr,数据手册里面提到最长是5ms,很多人就会写个循环让等5ms,而且还为了能达到5ms循环反复做测试,这样其实非常愚蠢,合适的做法数据手册里面已经给出了,就是5.3 Acknowledge Polling提到的,原理就是在写入指令发送完成以后,继续发送写入,如果此时eeprom正在self-write,那么肯定就不会有ACK应答,这样一直轮询,直到self-write完成之后才会有ACK应答,这样就可以不需要固定的延时5ms了。
CC2538 I2C功能
- 找遍互联网,也找不到CC2538 I2C相关的示例代码,除了一个master_slave_loopback.c示例回环读写i2c之外,别的要么是什么gpio模拟i2c,要么是什么firmware foundation提供的示例代码,竟没有一个是真正能成功使用过的。看来使用CC2538的人少的很,多数人都用CC2530带十几个二十个节点满足home应用就完事了。
头文件
/*
Atmel AT24C02 EEPROM read and write
*/
#ifndef HAL_EEPROM_H
#define HAL_EEPROM_H
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
/*
* PC.6->I2C_SCL
* PC.7->I2C_SDA
*/
#define I2C_SCL GPIO_PIN_6
#define I2C_SDA GPIO_PIN_7
#define GPIO_I2C_BASE GPIO_C_BASE
void HalEepromWriteByte(uint8 slaveAddr,uint32_t subAddr,uint32_t data);
uint32_t HalEepromReadByte(uint8 slaveAddr,uint32_t subAddr);
void HalEepromWriteWord(uint8 slaveAddr,uint32_t subAddr,uint32_t data);
uint32_t HalEepromReadWord(uint8 slaveAddr,uint32_t subAddr);
void HalEepromWriteDword(uint8 slaveAddr,uint32_t subAddr,uint32_t data);
uint32_t HalEepromReadDword(uint8 slaveAddr,uint32_t subAddr);
#ifdef __cplusplus
}
#endif
#endif
源文件
/**************************************************************************************************
* INCLUDES
*
* Description: Atmel Inc AT24C02D eeprom chip write and read. For CC2538 cortex-M3 core
*
*
**************************************************************************************************/
#include <string.h>
#include "hw_i2cm.h"
#include "hal_types.h"
#include "hal_eeprom.h"
#include "hw_ioc.h"
#include "hal_mcu.h"
/* ref. AT24C02 DATASHEET 5.3 Acknowledge Polling*/
static void waitSlaveACK(uint8 slaveAddr)
{
/*used volatile prevent compiler optimize*/
volatile bool busy=true;
while(busy == true){
I2CMasterSlaveAddrSet(slaveAddr,false); //false:write
I2CMasterControl(I2C_MASTER_CMD_SINGLE_SEND); //star+run+stop
busy=I2CMasterBusy();
}
}
static void dummyWrite(uint8 slaveAddr,uint32_t subAddr)
{
#if (HAL_EEPROM == TRUE)
if(subAddr < 0xFF)
{
HalEepromInit(); //we have to reinitialization i2c module every read/write cycle
//1. dummy write
//set slave address(Device Address)
I2CMasterSlaveAddrSet(slaveAddr,false); //false:write
//set sub address(Word Address)
I2CMasterDataPut(subAddr);
I2CMasterControl(I2C_MASTER_CMD_BURST_SEND_START);
while(I2CMasterBusy()){}
}
#endif
}
static void HalEepromInit(void)
{
#if (HAL_EEPROM == TRUE)
//
// The I2C peripheral must be enabled before use.
//
SysCtrlPeripheralEnable(SYS_CTRL_PERIPH_I2C);
//
// Do reset of I2C module
//
SysCtrlPeripheralReset(SYS_CTRL_PERIPH_I2C);
//
// Configure I2C physical pins
//
GPIOPinTypeI2C(GPIO_I2C_BASE, I2C_SCL);
GPIOPinTypeI2C(GPIO_I2C_BASE, I2C_SDA);
// Configure pins as peripheral input and output
IOCPinConfigPeriphInput(GPIO_I2C_BASE, I2C_SCL,IOC_I2CMSSCL); //this may be caused "while(I2CMasterBusy()){}" infinite loop,so we have to comment it
IOCPinConfigPeriphInput(GPIO_I2C_BASE, I2C_SDA,IOC_I2CMSSDA);
IOCPinConfigPeriphOutput(GPIO_I2C_BASE, I2C_SCL,IOC_MUX_OUT_SEL_I2C_CMSSCL);
IOCPinConfigPeriphOutput(GPIO_I2C_BASE, I2C_SDA,IOC_MUX_OUT_SEL_I2C_CMSSDA);
// data rate setting
// fase:100kbps
// true:400kbps
I2CMasterInitExpClk(SysCtrlClockGet(), false);
#endif
}
/*
*Write data to external eeprom(AT24C02D)
*
@param 7-bit of slave address
@param 8-bit of sub-address
@param byte to be write
*/
void HalEepromWriteByte(uint8 slaveAddr,uint32_t subAddr,uint32_t data)
{
#if (HAL_EEPROM == TRUE)
if(subAddr < 0xFF)
{
HalEepromInit(); //we have to reinitialization i2c module every read/write cycle
//set slave address(Device Address)
I2CMasterSlaveAddrSet(slaveAddr,false); //false:write
//set sub address(Word Address)
I2CMasterDataPut(subAddr);
I2CMasterControl(I2C_MASTER_CMD_BURST_SEND_START); //star+run
// Wait until master module is done transferring.
while(I2CMasterBusy()){}
// Place the data to be sent in the data register
I2CMasterDataPut(data);
I2CMasterControl(I2C_MASTER_CMD_BURST_SEND_FINISH); //run+stop
// Wait until master module is done transferring.
while(I2CMasterBusy()){}
waitSlaveACK(slaveAddr);
}
#endif
}
/*
* read one byte
@param index of total eeprom(0~255)
*/
uint32_t HalEepromReadByte(uint8 slaveAddr,uint32_t subAddr)
{
#if (HAL_EEPROM == TRUE)
uint32_t uRead=0;
if(subAddr < 0xFF)
{
HalEepromInit(); //we have to reinitialization i2c module every read/write cycle
dummyWrite(slaveAddr,subAddr);
//2. actual read
I2CMasterSlaveAddrSet(slaveAddr,true);
I2CMasterControl(I2C_MASTER_CMD_SINGLE_RECEIVE);
while(I2CMasterBusy()){}
uRead = I2CMasterDataGet();
}
return uRead;
#endif
}
void HalEepromWriteWord(uint8 slaveAddr,uint32_t subAddr,uint32_t data)
{
#if (HAL_EEPROM == TRUE)
if(subAddr < 0xFF)
{
uint8 i=0;
HalEepromInit(); //we have to reinitialization i2c module every read/write cycle
//set slave address(Device Address)
I2CMasterSlaveAddrSet(slaveAddr,false); //false:write
//set sub address(Word Address)
I2CMasterDataPut(subAddr);
I2CMasterControl(I2C_MASTER_CMD_BURST_SEND_START); //star+run
while(I2CMasterBusy()){}
// Place the data to be sent in the data register
for(i=sizeof(uint16_t);i>0;i--)
{
I2CMasterDataPut(data>>(8*(i-1))&0xFF);
if(i != 1)
{
I2CMasterControl(I2C_MASTER_CMD_BURST_SEND_CONT); //run
}else
{
I2CMasterControl(I2C_MASTER_CMD_BURST_SEND_FINISH); //run+stop
}
while(I2CMasterBusy()){}
}
waitSlaveACK(slaveAddr);
}
#endif
}
/*
* dummy write for read purpose
@param index of total eeprom(0~255)
*/
uint32_t HalEepromReadWord(uint8 slaveAddr,uint32_t subAddr)
{
#if (HAL_EEPROM == TRUE)
uint32_t uRead=0;
if(subAddr < 0xFF)
{
uint8 i=0;
HalEepromInit(); //we have to reinitialization i2c module every read/write cycle
dummyWrite(slaveAddr,subAddr);
//read
I2CMasterSlaveAddrSet(slaveAddr,true);
I2CMasterControl(I2C_MASTER_CMD_BURST_RECEIVE_START);
while(I2CMasterBusy()){}
for(i=0;i<sizeof(uint16_t);i++)
{
// Read the data from the master.
uRead |=I2CMasterDataGet()&0xFF;
if(i != 1)
{
uRead<<=8;
I2CMasterControl(I2C_MASTER_CMD_BURST_RECEIVE_CONT); //run+ack
}else
{
I2CMasterControl(I2C_MASTER_CMD_BURST_RECEIVE_FINISH); //run+stop (nack)
}
while(I2CMasterBusy()){}
}
}
return uRead;
#endif
}
/*
*Write data to external eeprom(AT24C02D)
*
@param 7-bit of slave address
@param 8-bit of sub-address
@param DWORD to be write
*/
void HalEepromWriteDword(uint8 slaveAddr,uint32_t subAddr,uint32_t data)
{
#if (HAL_EEPROM == TRUE)
if(subAddr < 0xFF)
{
uint8 i=0;
HalEepromInit(); //we have to reinitialization i2c module every read/write cycle
//set slave address(Device Address)
I2CMasterSlaveAddrSet(slaveAddr,false); //false:write
//set sub address(Word Address)
I2CMasterDataPut(subAddr);
I2CMasterControl(I2C_MASTER_CMD_BURST_SEND_START); //star+run
while(I2CMasterBusy()){}
// Place the data to be sent in the data register
for(i=sizeof(uint32_t);i>0;i--)
{
I2CMasterDataPut(data>>(8*(i-1))&0xFF);
if(i != 1)
{
I2CMasterControl(I2C_MASTER_CMD_BURST_SEND_CONT); //run
}else
{
I2CMasterControl(I2C_MASTER_CMD_BURST_SEND_FINISH); //run+stop
}
while(I2CMasterBusy()){}
}
waitSlaveACK(slaveAddr);
}
#endif
}
/*
* dummy write for read purpose
@param index of total eeprom(0~255)
*/
uint32_t HalEepromReadDword(uint8 slaveAddr,uint32_t subAddr)
{
#if (HAL_EEPROM == TRUE)
uint32_t uRead=0;
if(subAddr < 0xFF)
{
uint8 i=0;
HalEepromInit(); //we have to reinitialization i2c module every read/write cycle
dummyWrite(slaveAddr,subAddr);
//actual read
I2CMasterSlaveAddrSet(slaveAddr,true);
I2CMasterControl(I2C_MASTER_CMD_BURST_RECEIVE_START);
while(I2CMasterBusy()){}
for(i=0;i<sizeof(uint32_t);i++)
{
// Read the data from the slave
uRead |=I2CMasterDataGet()&0xFF;
if(i != 3)
{
uRead<<=8;
I2CMasterControl(I2C_MASTER_CMD_BURST_RECEIVE_CONT); //run+ack
}else
{
I2CMasterControl(I2C_MASTER_CMD_BURST_RECEIVE_FINISH); //run+stop (nack)
}
while(I2CMasterBusy()){}
}
}
return uRead;
#endif
}
示例代码
#define EERPOM_ADDRESS 0x50
// 8-bit word address (total 256 pages)
#define EEPROM_PAGE_0 (0<<3) // eeprom page 0(0~7 byte)
#define EEPROM_PAGE_1 (1<<3) // eeprom page 1(8~15 byte)
#define EEPROM_PAGE_2 (2<<3)
#define EEPROM_PAGE_3 (3<<3)
#define EEPROM_PAGE_4 (4<<3)
#define EEPROM_PAGE_5 (5<<3)
//Word Address MACRO
#define ADDR_VALUE1 ((EEPROM_PAGE_0)+0x01) //index of page
#define ADDR_VALUE2 ((EEPROM_PAGE_0)+0x02) //index of page
/* write one byte */
uint8_t writeByte=0x88;
HalEepromWriteByte(EERPOM_ADDRESS,ADDR_VALUE1,writeByte);
/* read one byte */
uint8_t readByte= HalEepromReadByte(EERPOM_ADDRESS,ADDR_VALUE1);