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

如何正确使用MCC生成的I2C库函数执行读取内存操作

[复制链接]
604|3
手机看帖
扫描二维码
随时随地手机跟帖
gaoyang9992006|  楼主 | 2024-2-4 12:29 | 显示全部楼层 |阅读模式
本帖最后由 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的芯片来执行读操作。
4140165bf12f6eceeb.png
芯片的时序定义如上所示,可以看出读操作是标准的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,&reg,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,&reg,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;
            
    }   
}


最后我们看看读取的效果
3034265bf148fcf417.png
最后补充一个先写后读取的示例,并测试对应的寄存器哪些位可以进行写操作。

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]++;      
    }   
}
6510965bf4e19c5fa3.png
经过测试在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协议的芯片都可以这么使用。


使用特权

评论回复
caigang13| | 2024-2-4 12:37 | 显示全部楼层
MCC还是差了点便捷性,还需继续优化。

使用特权

评论回复
gaoyang9992006|  楼主 | 2024-2-4 12:38 | 显示全部楼层
caigang13 发表于 2024-2-4 12:37
MCC还是差了点便捷性,还需继续优化。

是的,我刚更新了帖子。

使用特权

评论回复
幸福小强| | 2024-2-4 17:48 | 显示全部楼层
掌握住诀窍了。

使用特权

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

本版积分规则

认证:西安公路研究院南京院
简介:主要工作从事监控网络与通信网络设计,以及从事基于嵌入式的通信与控制设备研发。擅长单片机嵌入式系统物联网设备开发,音频功放电路开发。

1878

主题

15576

帖子

197

粉丝