PIC24F32 I2C 学习
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状态机的状态信息。
执行写操作的一般步骤如下
- 判断I2C状态是否为异常,如果否,则调用I2Cx_MasterWrite函数在TrB队列里面插入一个处理请求
- 判断I2C状态是否是pending状态,如果是,表明上一次操作还没有完成。此时唯有等待(设置超时机制)
- I2C状态为非pending状态,或者等待超时后,判断I2C状态是否为完成状态,如果是,函数返回;或者超时已经达到预设最大次数,此时函数也返回,但是写操作失败
- 如果I2C状态为其它状态,则函数试图重新调用写函数,直到重试次数达到预设最大值
执行读操作的一般步骤
- 判断I2C状态机是否处于fail状态,如果否,则进行下一步
- 调用I2C_Master_Write()函数写入待读寄存器地址
- 判断I2C写是否成功,如果成功,则进行下一步,否则退出
- 调用I2C_Master_Read()函数