打印
[PIC®/AVR®/dsPIC®产品]

【CuriosityNano测评报告】05.I2C通讯实验(续)及对AT24C32读写

[复制链接]
75147|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
#申请原创#

    在完成了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(&reg,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(&reg,<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(&reg,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(&reg,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、从我的测试过程看,失败的原因是没有正确解决设置双字节地址,若能解决这个问题,测试应该得到基本成功。

使用特权

评论回复
沙发
huquanz711| | 2021-6-8 07:33 | 只看该作者
还是硬件IIC效率高

使用特权

评论回复
板凳
hu9jj|  楼主 | 2021-6-8 11:36 | 只看该作者
本帖最后由 hu9jj 于 2021-6-8 11:38 编辑

    忽然脑洞大开,既然写双字节地址时高低字节反了,那我就先将双字节地址进行交换,这样写地址不就正确了么?于是乎就修改代码进行测试,下面是修改后的代码:
void I2C_16addr_BuffRead(i2c1_address_t address,uint16_t reg,uint8_t *data,uint8_t len)
{
    uint16_t addr;
   
    addr = reg<<8 | reg>>8;
   
    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(&addr,2);
    I2C1_SetAddressNackCallback(NULL,NULL); //NACK polling?
    I2C1_MasterWrite();
    while(I2C1_BUSY == I2C1_Close()); // sit here until finished.
   
}


void I2C_16addr_ByteWrite(i2c1_address_t address,uint16_t reg,uint8_t *data)
{
    uint16_t addr;
   
    addr = reg<<8 | reg>>8;

    while(!I2C1_Open(address)); // sit here until we get the bus..
    I2C1_SetDataCompleteCallback(wr1RegCompleteHandler,&data);
    I2C1_SetBuffer(&addr,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]);
        DELAY_milliseconds(1);
    }
}


    编译测试,果然达到了目的,成功地对16位地址的I2C部件进行了读写操作,下面是串口调试助手接收到的读写数据:


    至此,除了写操作是依靠循环逐个写入外,读操作完全达到了目的。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:Microchip
简介:让我们来为您提供帮助。我们可提供各种资源来帮助您解决一切问题。是否需要与我们的客户支持团队联系?您可以通过电话、在线聊天功能或电子邮件与他们联系。

151

主题

1059

帖子

11

粉丝