在论坛里面看到有哥们写了一个I2C裸机调试EEPROM的帖子,根据他的代码认真分析了一段时间I2C的相关知识。发现他的做法是错的,第一,写EEPROM是成功了,但读取到的是不对的。第二,显示出来的数据不是VILATILE类型,其实没有被修改过,仅仅是写数组的数据。
经过好几天的研究,现在把I2C调试EEPROM的裸机完整程序给大家看看,已经过仔细验证。
#define RESET 0xaf
#define MASTER_TX_START 0xf0
#define MASTER_TX_STOP 0xd0
#define MASTER_RX_START 0xB0
#define MASTER_RX_STOP 0x90
#define READ 0
#define READ_END 1
#define WRITE 2
#define WRITE_END 3
#define READ_ADDR 4
#define BYTE_OP 0
#define PAGE_OP 1
#define BYTE_NUM 16 //多字节读写的最大字节数,一次只允许写入16字节,可以分几次写
volatile char num;
//volatile char data2;
volatile char data_add=0;
volatile char TYPE;
volatile char mode;
volatile int w_addr=0;
//volatile int times_total = 10;
volatile char * p_data;
volatile char * p_data2;
#define CON_RESET 0xae
void __irq I2CIntHandle(void)
{
Uart_Printf("int %d: %d %d, %d, %d\n", mode, rIICADD0, rIICCON0, rIICSTAT0, rIICDS0);
switch(mode)
{
case WRITE:
if ((!(rIICSTAT0 & 0x01))) //ACK接收正常
{
if (w_addr == 0)
{
rIICDS0 = data_add; //写入E2PROM的WORD ADDRESS
rIICCON0 &= ~(1<<4);
w_addr ++;
break;
}
else
{
if (TYPE == BYTE_OP)
{
rIICDS0 = *p_data; //写入数据
rIICCON0 &= ~(1<<4);
}
else if (TYPE == PAGE_OP)
{
if (num < BYTE_NUM)
{
rIICDS0 = *(p_data+num); //写入数据
rIICCON0 &= ~(1<<4);
num ++;
Delay(2);
break;
}
}
}
}
mode = WRITE_END;
break;
case WRITE_END:
rIICSTAT0 = MASTER_TX_STOP;
rIICCON0 &= ~(1<<4);
Delay(1);
break;
case READ:
if (!(rIICSTAT0 & 0x01)) //ACK接收正常
{
if (TYPE == BYTE_OP)
{
//*p_data2= rIICDS0;
if (num == 0)
{
rIICCON0 &= ~(1<<4); //读数据一次
num++;
break;
}
}
else if (TYPE == PAGE_OP)
{
if (num < BYTE_NUM)
{
if (num >= 1)
*(p_data2+num-1) = rIICDS0; //读取数据
rIICCON0 &= ~(1<<4);
num ++;
Delay(2);
break;
}
}
}
*(p_data2+num-1)= rIICDS0; //取数据一次
rIICCON0 &= ~(1<<4); //清楚中断等待标志位
rIICCON0 &= ~(1<<7); //读取停止条件,设置ACK无效
mode = READ_END;
break;
case READ_END:
rIICSTAT0 = MASTER_RX_STOP;
rIICCON0 &= ~(1<<4);
Delay(1);
break;
case READ_ADDR:
rIICDS0 = data_add;
rIICCON0 &= ~(1<<4);
mode = WRITE_END;
break;
}
INTC_ClearVectAddr();
}
void I2CInit(void)
{
rGPBCON = (rGPBCON & ~(0xff<<20)) | 0x22<<20;
INTC_Init();
INTC_SetIntISR(INT_I2C0, I2CIntHandle);
INTC_Enable(INT_I2C0);
}
void WriteTest(char addr, volatile char* ch, char type)
{
p_data = ch;
w_addr = 0;
num = 0;
data_add = addr;
mode = WRITE;
TYPE = type;
rIICSTAT0 = MASTER_TX_STOP;
rIICCON0 = rIICCON0 | (1<<7) | (1<<5);
rIICDS0 = 0xa0;
rIICSTAT0 = MASTER_TX_START;
while (rIICSTAT0 & 0x20); //等待停止条件生效
}
void ReadTest(char addr, volatile char* ch, char type)
{
p_data2 = ch;
data_add = addr;
num = 0;
TYPE = type;
mode = READ_ADDR;
rIICSTAT0 = MASTER_TX_STOP;
rIICCON0 = rIICCON0 | (1<<7) | (1<<5); //设置ACK有效且中断有效
rIICDS0 = 0xa0; //从属器地址
rIICSTAT0 = MASTER_TX_START;
while (rIICSTAT0 & 0x20); //写WORD ADDRESS地址结束
mode = READ;
rIICCON0 = rIICCON0 | (1<<7) | (1<<5);
rIICDS0 = 0xa1;
rIICSTAT0 = MASTER_RX_START;
while (rIICSTAT0 & 0x20); //读取结束
}
void IICTest(void)
{
char i = 0;
char word = 8; //单字节写
char Pword[BYTE_NUM] = {1,2,3,4,5,6,7,8,8,7,6,5,4,3,2,1}; //多字节写数组
volatile char word2 = 0; //单字节读
volatile char Pword2[BYTE_NUM] = {0}; //多字节读数组
char * ch;
volatile char * ch_res;
char addr = 0; //WORD ADDRESS,从第几个自己读写
char type =
PAGE_OP; //单字节操作,或者多字节操作、
if(type == BYTE_OP)
{
ch = & word;
ch_res = & word2;
}
else
{
ch = Pword;
ch_res = Pword2;
}
I2CInit();
WriteTest(addr, ch, type);
Delay(5000); //休眠5秒钟
ReadTest(addr, ch_res, type);
while (i < BYTE_NUM)
{
if(type == BYTE_OP)
{
Uart_Printf("IICTest %d\n", *ch_res); //打印读出的数据
break;
}
else
Uart_Printf("IICTest %d\n", *(ch_res+i));
i++;
}
}
void _main(void)
{
Uart_Init();
Port_Init();
IICTest();
while(1);
}
修改PAGE_OP值可以实现单字节读写和页面读写切换。
详细资料参考datasheet.
[ 此帖被乌鸦在2011-12-01 10:46重新编辑 ]