#申请原创#
在完成了I2C对DS1307日历模块读写操作之后,紧接着开始了对AT24C32EEPROM读写操作的实验。因为AT24C32是16位地址,而范例中又没有对应的代码,只能在原来8位地址的代码上修改。
读多个字节的函数是在原来8位地址的代码上修改过来的,原始的代码如下:
void I2C1_ReadDataBlock(i2c1_address_t address, uint8_t reg, uint8_t *data, size_t len)
{
i2c1_buffer_t bufferBlock; // result is little endian
bufferBlock.data = data;
bufferBlock.len = len;
while(!I2C1_Open(address)); // sit here until we get the bus..
I2C1_SetDataCompleteCallback(rdBlkRegCompleteHandler,&bufferBlock);
I2C1_SetBuffer(®,1);
I2C1_SetAddressNackCallback(NULL,NULL); //NACK polling?
I2C1_MasterWrite();
while(I2C1_BUSY == I2C1_Close()); // sit here until finished.
}
起初我对上述倒数第二行代码感到困惑,明明是读取数据,可这里却发出了写命令。在这此测试中才知道这行命令是操作开始时写地址用的,后面还会详细叙述。
修改的思路是其他不用变动,仅修改发送地址的部分。经过分析代码,除了将地址参数修改成16位之外,还需要将倒数第四行的参数修改成2,即两个字节,修改后的16位地址读取多个字节的代码如下:
void I2C_16addr_BuffRead(i2c1_address_t address,<b>uint16_t</b> reg,uint8_t *data,uint8_t len)
{
i2c1_buffer_t bufferBlock; // result is little endian
bufferBlock.data = data;
bufferBlock.len = len;
while(!I2C1_Open(address)); // sit here until we get the bus..
I2C1_SetDataCompleteCallback(rdBlkRegCompleteHandler,&bufferBlock);
I2C1_SetBuffer(®,<b>2</b>);
I2C1_SetAddressNackCallback(NULL,NULL); //NACK polling?
I2C1_MasterWrite();
while(I2C1_BUSY == I2C1_Close()); // sit here until finished.
}
经过测试,上述代码可以读出指定地址的数据,下面是逻辑分析仪抓取的时序图:
读操作看似顺利完成了,但实际上却仍是有问题的,但当初并不清楚,后面会再叙述。接着理所当然就开始的写操作的测试,可是写操作的测试却困难得多,连续测试了多天,至今都没有获得完全成功。
首先是在读数据的代码基础上改为写数据,改写的代码如下:
void I2C_16addr_BuffWrite(i2c1_address_t address, uint16_t reg, uint8_t *data, uint8_t len)
{
i2c1_buffer_t bufferBlock; // result is little endian
bufferBlock.data = data;
bufferBlock.len = len;
while(!I2C1_Open(address)); // sit here until we get the bus..
I2C1_SetDataCompleteCallback(<b>wrBlkRegCompleteHandler</b>,&bufferBlock);
I2C1_SetBuffer(®,2);
I2C1_SetAddressNackCallback(NULL,NULL); //NACK polling?
I2C1_MasterWrite();
while(I2C1_BUSY == I2C1_Close()); // sit here until finished.
}
开始的以为之前读数据的代码中,倒数第二行用的是I2C1_MarsetrWrite(),写数据会不会是用I2C_MarstRead(),但经过测试,从逻辑分析仪抓取的时序和数据看到其发给I2C器件的地址是读命令而不是写地址命令,说明此行仅仅是将要操作的地址写入I2C器件,解了之前的疑惑。
然后对照读函数代码进行分析,尝试将第五行代码的参数(上述粗体字)进行修改,原来是rdBlkRegCompleteHandler,我修改为wrBlkRegCompleteHandler,为此添加了一个函数(下面的代码中第一个是原有的,第二个是我尝试添加的):
static i2c1_operations_t rdBlkRegCompleteHandler(void *ptr)
{
I2C1_SetBuffer(((i2c1_buffer_t *)ptr)->data,((i2c1_buffer_t*)ptr)->len);
I2C1_SetDataCompleteCallback(NULL,NULL);
return I2C1_RESTART_READ;
}
static i2c1_operations_t wrBlkRegCompleteHandler(void *ptr)
{
I2C1_SetBuffer(((i2c1_buffer_t *)ptr)->data,((i2c1_buffer_t*)ptr)->len);
I2C1_SetDataCompleteCallback(NULL,NULL);
return I2C1_RESTART_WRITE;
}
经过测试,没有成功,从逻辑分析仪抓取的时序看,仅仅发出的地址,之后便无其他操作:
此路不通,我就延用8位地址写操作的用循环操作逐个写入的办法进行测试,代码如下:
void I2C_16addr_ByteWrite(i2c1_address_t address,uint16_t reg,uint8_t *data)
{
while(!I2C1_Open(address)); // sit here until we get the bus..
I2C1_SetDataCompleteCallback(wr1RegCompleteHandler,&data);
I2C1_SetBuffer(®,2);
I2C1_SetAddressNackCallback(NULL,NULL); //NACK polling?
I2C1_MasterWrite();
while(I2C1_BUSY == I2C1_Close()); // sit here until finished.
}
void I2C_16addr_BuffWrite(i2c1_address_t address, uint16_t reg, uint8_t *data, uint8_t len)
{
uint16_t i;
for(i=0; i<len; i++){
I2C_16addr_ByteWrite(address,reg+i,data[i]);
<b>DELAY_milliseconds(1); //此延时是后加的</b>
}
}
通过逻辑分析仪的时序图发现循环写操作不正常,每完整写入一个数据后,连续有三个写操作不完整,如下图:
连续写16个字节,仅有4个字节写操作正常:
怎么会有如此现象,仔细分析之后认为应该是AT24C32响应不及时造成的,于是乎在循环中添加了1毫秒的延时,也就是上面代码中粗体部分,再测试貌似基本正常了:
继续测试发现读出的数据并不完全是写入的数据,我测试时读写都是从0地址开始,除了第一个数据一致外,其他读出的数据并不是写入的数据。仔细观察逻辑分析仪抓取的数据,发现通过修SetBuffer()函数的参数传入双字节数据时,其低字节在前,高字节在后。但AT24C32器件要求是地址高字节在前,低字节在后,所以除了0地址的读写操作一致外,其他地址写操作并是不连续的,而是被写入了不同的页面上了:
我回过头再测试读操作,用地址300(0x12C)读取,果然发现也是双字节地址位置相反:
至此,测试宣告失败。于是我改用模拟I2C来操作AT24C32(DS1307仍用硬件I2C),但在编译过程中报错,提示复用引脚错误,下面是对引脚的宏定义:
#define SCL_1() Key1_SetHigh() //写I2C时钟端口(PB6)
#define SCL_0() Key1_SetLow()
#define SDA_1() Key1_SetHigh() //写I2C数据端口(PB4)
#define SDA_0() Key1_SetLow()
#define SDA_X() Key1_GetValue() //读I2C数据端口状态(PB4)
#define SDA_OUT() Key1_SetDigitalOutput() //设置数据端口模式(PB4)
#define SDA_IN() Key1_SetDigitalInput()
这是编译报错的提示信息:
本次测试总结:
1、范例提供的代码似乎不太完整,按理应该有8位地址的单字节和多字节读写操作(4个函数),还应该有16位地址的单字节和多字节读写操作(也是4个函数),这样就方便初学者直接引用了;
2、从我的测试过程看,失败的原因是没有正确解决设置双字节地址,若能解决这个问题,测试应该得到基本成功。
|