本帖最后由 gaoyang9992006 于 2024-2-4 19:03 编辑
关于MCC生成的PIC18F系列的I2C库函数的写操作的应用可以查看下面的帖子
https://bbs.21ic.com/icview-3353486-1-1.html
关于IO模拟I2C的操作可以查看下面的帖子
https://bbs.21ic.com/icview-3343744-1-1.html
本帖讲解如何使用MCC生成的I2C库函数执行读内存操作。
MCC的使用方法和I2C的启用方法这里不再赘述。
这里我们使用函数
/**
* @ingroup i2c_host
* [url=home.php?mod=space&uid=247401]@brief[/url] This function writes data from the writeData to the bus
* and then reads data from the Client and stores the received in the
* readData. The function generates a Start condition on the bus and
* will then send writeLength number of bytes contained in writeData.
* The function will then insert a Repeated start condition and
* proceed to read readLength number of bytes from the client.
* The received bytes are stored in readData buffer. A Stop condition
* is generated after the last byte has been received.
*
* If the Client NAKs the request or a bus error was encountered on
* the bus, the transfer is terminated. The application can call
* I2C1_ErrorGet() function to know that cause of the error.
*
* The function is non-blocking. It initiates bus activity and returns
* immediately. The transfer is then completed in the peripheral
* interrupt. For polling mode, user has to call I2C1_Tasks
* in while loop. A transfer request cannot be placed when another
* transfer is in progress. Calling this function when another function
* is already in progress will cause the function to return false.
*
* @param [in] address - 7-bit / 10-bit Client address.
* @param [in] writeData - pointer to write data buffer.
* @param [in] writeLength - write data length in bytes.
* @param [out] readData - pointer to read data buffer.
* @param [in] readLength - read data length in bytes.
* @return
* true - The request was placed successfully and the bus activity was
* initiated.
* false - The request fails,if there was already a transfer in
* progress when this function was called
*/
bool I2C1_WriteRead(uint16_t address, uint8_t *writeData, size_t writeLength, uint8_t *readData, size_t readLength);
该函数的执行过程是:往指定的地址的设备写入数据后,再从该地址的设备读取数据。
参数一共5个:芯片的I2C总线地址,要写入的数据,写入数据的长度,读取的数据,读取的长度。
请注意写入的数据和读取的数据通过指针实现。
我们查看库函数实现的方式
bool I2C1_WriteRead(uint16_t address, uint8_t *writeData, size_t writeLength, uint8_t *readData, size_t readLength)
{
bool retStatus = false;
if (!I2C1_IsBusy())
{
i2c1Status.busy = true;
i2c1Status.address = address;
i2c1Status.switchToRead = true;
i2c1Status.writePtr = writeData;
i2c1Status.writeLength = writeLength;
i2c1Status.readPtr = readData;
i2c1Status.readLength = readLength;
i2c1Status.errorState = I2C_ERROR_NONE;
I2C1_WriteStart();
retStatus = true;
}
return retStatus;
}
通过以上代码我们了解到,这是一个非阻塞函数,进入函数后判断是否忙,如果不忙就执行相关的寄存器配置与执行操作并返回真,这里函数并未考虑到如果为假的情况,另外为真的话也未考虑是否执行成功。所以我们需要自己在程序里考虑这个问题,否则将会执行失败,或者错过执行。接下来我找一个I2C的芯片来执行读操作。
芯片的时序定义如上所示,可以看出读操作是标准的I2C时序。
示例代码如下:
int main(void)
{
uint8_t val=0;
uint8_t reg=0x00;
SYSTEM_Initialize();
// If using interrupts in PIC18 High/Low Priority Mode you need to enable the Global High and Low Interrupts
// If using interrupts in PIC Mid-Range Compatibility Mode you need to enable the Global Interrupts
// Use the following macros to:
// Enable the Global Interrupts
INTERRUPT_GlobalInterruptEnable();
// Disable the Global Interrupts
//INTERRUPT_GlobalInterruptDisable();
printf("Hello\n");
DELAY_milliseconds(3000);
while(1)
{
DELAY_milliseconds(1000);
I2C1_WriteRead(0x35,®,1,&val,1);
while(I2C1_IsBusy());
printf("REG%d=%x\n",reg,val);
reg++;
val=0;
if(reg>30) reg=0;
}
}
我们往I2C地址为0x35的设备写入reg,并将读取的数据放到val,之后通过检测是否忙,如果忙就等待,等到写入完成后再执行打印结果的操作。
这里使用了阻塞的方式判断,也可以使用非阻塞方式,增加一个时间判断,防止程序卡死。
简单的示例如下所示:
int main(void)
{
uint8_t val=0;
uint8_t reg=0x00;
uint8_t delay_time=0;
SYSTEM_Initialize();
// If using interrupts in PIC18 High/Low Priority Mode you need to enable the Global High and Low Interrupts
// If using interrupts in PIC Mid-Range Compatibility Mode you need to enable the Global Interrupts
// Use the following macros to:
// Enable the Global Interrupts
INTERRUPT_GlobalInterruptEnable();
// Disable the Global Interrupts
//INTERRUPT_GlobalInterruptDisable();
printf("Hello\n");
DELAY_milliseconds(3000);
while(1)
{
DELAY_milliseconds(1000);
I2C1_WriteRead(0x35,®,1,&val,1);
while(I2C1_IsBusy()&&(delay_time++<20));
printf("REG%d=%x\n",reg,val);
reg++;
val=0;
delay_time=0;
if(reg>30) reg=0;
}
}
最后我们看看读取的效果
最后补充一个先写后读取的示例,并测试对应的寄存器哪些位可以进行写操作。
int main(void)
{
uint8_t val=0;
uint8_t reg=0x00;
uint8_t delay_time=0;
SYSTEM_Initialize();
uint8_t daa[2];
daa[0]=0x04;
daa[1]=0x00;
// If using interrupts in PIC18 High/Low Priority Mode you need to enable the Global High and Low Interrupts
// If using interrupts in PIC Mid-Range Compatibility Mode you need to enable the Global Interrupts
// Use the following macros to:
// Enable the Global Interrupts
INTERRUPT_GlobalInterruptEnable();
// Disable the Global Interrupts
//INTERRUPT_GlobalInterruptDisable();
printf("Hello\n");
DELAY_milliseconds(3000);
while(1)
{
DELAY_milliseconds(1000);
I2C1_Write(0x35,daa,2);
while(I2C1_IsBusy()&&(delay_time++<20));delay_time=0;
reg=0x04;
I2C1_WriteRead(0x35,®,1,&val,1);
while(I2C1_IsBusy()&&(delay_time++<20));delay_time=0;
printf("DAA[%x]---REG%d=%x\n",daa[1],reg,val);
daa[1]++;
}
}
经过测试在0x04寄存器上,低4位中的BIT3是不能执行写操作的。
正常使用时候这么看明显的不好看,我们可以将函数进行封装使用,不仅方便阅读,还更容易调用,同时这么做不容易弄错。void KT_Byte_Write(uint8_t kt_reg,uint8_t kt_dat)
{
uint8_t delay_time=0;
uint8_t dats[2];
dats[0] = kt_reg;
dats[1] = kt_dat;
I2C1_Write(0x35,dats,2);
while(I2C1_IsBusy()&&(delay_time++<20));
}
uint8_t KT_Byte_Read(uint8_t kt_reg)
{
uint8_t val=0;
uint8_t delay_time=0;
I2C1_WriteRead(0x35,&kt_reg,1,&val,1);
while(I2C1_IsBusy()&&(delay_time++<20));delay_time=0;
return val;
}
调用方式int main(void)
{
SYSTEM_Initialize();
uint8_t i=0;
// Enable the Global Interrupts
INTERRUPT_GlobalInterruptEnable();
// Disable the Global Interrupts
//INTERRUPT_GlobalInterruptDisable();
printf("Hello\n");
DELAY_milliseconds(3000);
while(1)
{
for(i=0;i<10;i++)
{
DELAY_milliseconds(1000);
KT_Byte_Write(0x04,i);
printf("REG=%x\n",KT_Byte_Read(0x04));
}
}
}
好了,基于我使用的这个芯片的I2C读写使用方式讲解完了,另外只要是标准I2C协议的芯片都可以这么使用。
|