雕虫小技

PIC24F32 I2C 学习

1. MCC生成代码解析
主要定义了如下结构体,导致整程序阅读理解比较复杂
下面的结构体定义了I2C请求操作模块(trb),包含了读写操作需要的地址,指向待写数据的指针,数据的字节数信息
typedef struct
{
    uint16_t  address;          // Bits <10:1> are the 10 bit address.
                                // Bits <7:1> are the 7 bit address
                                // Bit 0 is R/W (1 for read)
    uint8_t   length;           // the # of bytes in the buffer
    uint8_t   *pbuffer;         // a pointer to a buffer of length bytes
} I2C1_TRANSACTION_REQUEST_BLOCK;

下面的结构体定义了一个队列单元条目,包含trb个数,指向上述trb模块的指针,I2C状态机状态信息

typedef struct
{
    uint8_t                         count;          // a count of trb's in the trb list
    I2C1_TRANSACTION_REQUEST_BLOCK  *ptrb_list;     // pointer to the trb list
    I2C1_MESSAGE_STATUS             *pTrFlag;       // set with the error of the last trb sent.
                                                    // if all trb's are sent successfully,
                                                    // then this is I2C1_MESSAGE_COMPLETE
} I2C_TR_QUEUE_ENTRY;

下面的结构体定义了一个I2C对象,包含两个分别指向trb头尾的指针,trb队列状态位,I2C完成标志,I2C累积错误数

typedef struct
{
    /* Read/Write Queue */
    I2C_TR_QUEUE_ENTRY          *pTrTail;       // tail of the queue
    I2C_TR_QUEUE_ENTRY          *pTrHead;       // head of the queue
    I2C_TR_QUEUE_STATUS         trStatus;       // status of the last transaction
    uint8_t                     i2cDoneFlag;    // flag to indicate the current
                                                // transaction is done
    uint8_t                     i2cErrors;      // keeps track of errors


} I2C_OBJECT ;

MCC生成的驱动代码主要围绕I2C_OBJECT进行操作,首先它定义了如下全局变量,为I2C处理队列条目预分配内存

static I2C_TR_QUEUE_ENTRY i2c1_tr_queue[I2C1_CONFIG_TR_QUEUE_LENGTH];

I2C1_CONFIG_TR_QUEUE_LENGTH默认值为1,意味着默认I2C仅仅能创建一个处理请求。

其次它定义了一个I2C对象类型的全局变量

static I2C_OBJECT i2c1_object;

变量加上限定词static应该是为了防止用户程序(为了便利性等)直接对此变量进行操作

在I2C初始化函数里,除了I2C硬件寄存器的初始化,也对i2c1_object进行了初始化

i2c1_object.pTrHead = i2c1_tr_queue;
i2c1_object.pTrTail = i2c1_tr_queue;
i2c1_object.trStatus.s.empty = true;
i2c1_object.trStatus.s.full = false;

i2c1_object.i2cErrors = 0;

用户主要调用I2C_MasterWrite() 以及I2C_MasterRead()函数实现对i2c设备的访问操作。调用I2C_MasterWrite()后,首先创建一个trb类型局部静态变量,并将数据指针,数据长度,外设地址传递给trb。然后尝试在i2c1_object对象里插入一个trb单元,成功后置位I2C中断标志位,触发中断处理例程。

void I2C1_MasterWrite(
                                uint8_t *pdata,
                                uint8_t length,
                                uint16_t address,
                                I2C1_MESSAGE_STATUS *pstatus)
{
    static I2C1_TRANSACTION_REQUEST_BLOCK   trBlock;

    // check if there is space in the queue
    if (i2c1_object.trStatus.s.full != true)
    {
        I2C1_MasterWriteTRBBuild(&trBlock, pdata, length, address);
        I2C1_MasterTRBInsert(1, &trBlock, pstatus);
    }
    else
    {
        *pstatus = I2C1_MESSAGE_FAIL;
    }

}
void I2C1_MasterWriteTRBBuild(
                                I2C1_TRANSACTION_REQUEST_BLOCK *ptrb,
                                uint8_t *pdata,
                                uint8_t length,
                                uint16_t address)
{
    ptrb->address = address << 1;
    ptrb->length  = length;
    ptrb->pbuffer = pdata;
}
void I2C1_MasterTRBInsert(
                                uint8_t count,
                                I2C1_TRANSACTION_REQUEST_BLOCK *ptrb_list,
                                I2C1_MESSAGE_STATUS *pflag)
{

    // check if there is space in the queue
    if (i2c1_object.trStatus.s.full != true)
    {
        *pflag = I2C1_MESSAGE_PENDING;

        i2c1_object.pTrTail->ptrb_list = ptrb_list;
        i2c1_object.pTrTail->count     = count; //count fixed to 1....
        i2c1_object.pTrTail->pTrFlag   = pflag;
        i2c1_object.pTrTail++;

        // check if the end of the array is reached
        if (i2c1_object.pTrTail == (i2c1_tr_queue + I2C1_CONFIG_TR_QUEUE_LENGTH))
        {
            // adjust to restart at the beginning of the array
            i2c1_object.pTrTail = i2c1_tr_queue;
        }

        // since we added one item to be processed, we know
        // it is not empty, so set the empty status to false
        i2c1_object.trStatus.s.empty = false;

        // check if full
        if (i2c1_object.pTrHead == i2c1_object.pTrTail)
        {
            // it is full, set the full status to true
            i2c1_object.trStatus.s.full = true;
        }

        // for interrupt based
        if(i2c1_state == S_MASTER_IDLE)
        {    
            // force the task to run since we know that the queue has
            // something that needs to be sent
            IFS1bits.MI2C1IF = 1;
        }           
        
    }
    else
    {
        *pflag = I2C1_MESSAGE_FAIL;
    }

}

2. 用户程序编写

I2C写的话主要调用I2C1_MasterWrite(pdata,length,slave_address,pstatus)函数,其中pdata为指向待写数据(包含寄存器地址等待写信息)的指针,length为数据长度,slave_address为从设备地址,pstatus为I2C模块的状态标志位,用于判断操作I2c之后的返回状态以及I2C状态机的状态信息。

执行写操作的一般步骤如下

  1. 判断I2C状态是否为异常,如果否,则调用I2Cx_MasterWrite函数在TrB队列里面插入一个处理请求
  2. 判断I2C状态是否是pending状态,如果是,表明上一次操作还没有完成。此时唯有等待(设置超时机制)
  3. I2C状态为非pending状态,或者等待超时后,判断I2C状态是否为完成状态,如果是,函数返回;或者超时已经达到预设最大次数,此时函数也返回,但是写操作失败
  4. 如果I2C状态为其它状态,则函数试图重新调用写函数,直到重试次数达到预设最大值

执行读操作的一般步骤

  1. 判断I2C状态机是否处于fail状态,如果否,则进行下一步
  2. 调用I2C_Master_Write()函数写入待读寄存器地址
  3. 判断I2C写是否成功,如果成功,则进行下一步,否则退出
  4. 调用I2C_Master_Read()函数

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *