#申请原创#
在之前多天实验的基础上,今天通过将地址数据添加到要写入的数据之前的办法,实现了多字节数据写入,可以不用之前的通过循环逐个写入的笨办法。至此,PIC18F16Q40核心板的硬件I2C测试全部达到了目的,尽管使用了一些比较牵强附会的办法,如通过预先将双字节地址数据交换来解决高低字节地址错位、通过将地址信息添加到要写入的数据之前、重构数组的办法实现多字节数据的连续写入等。
之所以本次执著地测试硬件I2C通讯,是因为以前的屡次测试均没有取得过成功,每次都是半途而废改用模拟I2C。相对而言,模拟I2C看得见、摸得着,动用起来更得心应手。而硬件I2C则因为不熟悉各种参数如何设置,也看不明白数据手册,只能凭着一知半解揣摩各个参数的应用。这次花费了一周时间,通过逻辑分析仪抓取时序,有针对性地尝试变更各个参数并观察实际结果,总算基本完成了测试计划,尽管有些代码或参数设置可能是多余的,代码也肯定有优化空间,但I2C通讯的测试将暂告一段落。
本次分别测试了8位寄存器地址和16位寄存器地址的器件,单个字节和多个字节的读写操作,共计8种操作方式。除了8位地址的单字节读写操作和多字节读操作使用的是范例中现成的函数外,其他8位地址的多字节写、16位地址的单字节和多字节的读写操作都自己改写的函数。
下面是这8个函数列表:
uint8_t I2C1_Read1ByteRegister(i2c1_address_t address, uint8_t reg); //8位地址单字节读
void I2C1_Write1ByteRegister(i2c1_address_t address, uint8_t reg, uint8_t data); //8位地址单字节写
void I2C1_ReadDataBlock(i2c1_address_t address, uint8_t reg, uint8_t *data, size_t len); //8位地址多字节读
void I2C_8addrBuffWrite(i2c1_address_t address, uint8_t reg, uint8_t *data, uint8_t len); //8位地址多字节写
uint8_t I2C_16addr_ByteRead(i2c1_address_t address, uint16_t reg); //16位地址单字节读
void I2C_16addr_BuffRead(i2c1_address_t address,uint16_t reg,uint8_t *data,uint8_t len); //16位地址多字节读
void I2C_16addr_ByteWrite(i2c1_address_t address,uint16_t reg,uint8_t data); //16位地址单字节写
void I2C_16addr_BuffWrite(i2c1_address_t address, uint16_t reg, uint8_t *data, uint8_t len); //16位地址多字节写
这是我自己改写的5个操作函数代码:
void I2C_8addrBuffWrite(i2c1_address_t address, uint8_t reg, uint8_t *data, uint8_t len)
{
uint8_t i,Buffer[20];
Buffer[0] = reg;
for(i=1; i<(len+1); i++){
Buffer[i] = data[i-1];
}
i = i + 1;
while(!I2C1_Open(address)); // sit here until we get the bus..
I2C1_SetBuffer(Buffer,i);
I2C1_SetAddressNackCallback(NULL,NULL); //NACK polling?
I2C1_MasterWrite();
while(I2C1_BUSY == I2C1_Close()); // sit here until finished.
}
uint8_t I2C_16addr_ByteRead(i2c1_address_t address, uint16_t reg)
{
uint8_t dat = 0x00;
uint16_t addr = reg<<8 | reg>>8;
while(!I2C1_Open(address)); // 打开I2C设备.
I2C1_SetDataCompleteCallback(rd1RegCompleteHandler,&dat); // 传递数据指针
I2C1_SetBuffer(&addr,2); // 指定数据地址
I2C1_SetAddressNackCallback(NULL,NULL); // 等待应答
I2C1_MasterWrite();
while(I2C1_BUSY == I2C1_Close()); // 等待操作结束
return dat;
}
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)
{
uint8_t Buffer[3];
Buffer[0] = reg>>8;
Buffer[1] = reg;
Buffer[2] = data;
while(!I2C1_Open(address)); // 打开I2C器件
I2C1_SetBuffer(Buffer,3); // 写入3个字节数据(地址2+数据1)
I2C1_SetAddressNackCallback(NULL,NULL); // NACK polling?
I2C1_MasterWrite();
while(I2C1_BUSY == I2C1_Close()); // 等待写操作完成
}
void I2C_16addr_BuffWrite(i2c1_address_t address, uint16_t reg, uint8_t *data, uint8_t len)
{
uint8_t i,Buffer[20];
Buffer[0] = reg>>8; // 地址高字节
Buffer[1] = reg; // 地址低字节
for(i=2; i<(len+2); i++){
Buffer[i] = data[i-2]; // 将要写入的数据依次排列在数组后面
}
i = len + 2;
while(!I2C1_Open(address)); // 打开I2C器件
I2C1_SetBuffer(Buffer,i); // 写入多个字节数据
I2C1_SetAddressNackCallback(NULL,NULL); // NACK polling?
I2C1_MasterWrite();
while(I2C1_BUSY == I2C1_Close()); // 等待写操作完成
}
下面是测试成功抓取的时序图:
这是AT24C32EEPROM的16位地址多字节读取的时序:
这是读完多字节之后紧接着再读单个字节的时序,可以看出两次操作之间的间隔虽短,但不影响读操作进行:
这是16位地址多字节写操作的时序:
写完多字节数据之后,紧接着又进行写入单个字节的操作,但这两个操作之间必须间隔一定时间,否则后面的写操作不能正常完成:
下图中可以完整看出两个写操作的过程:
最后是本次测试过程中的照片:
|
|