本帖最后由 Afanx 于 2022-7-19 18:02 编辑
二、I2C做主机,轮询读写
1、I2C主机轮询发送
EV5:STARTBF=1,起始位产生。 EV6:ADDRF=1,从机响应地址。 EV8_1/EV8:TXDATE=1,发送寄存器为空,写DAT寄存器。 EV8_2:TXDATE =1,BSF=1,请求设置停止位。
主机发送步骤:
(1)保证I2C不在使用中,等待BUSY=0; (2)发送START信号,等待EV5 (STARTBF=1); (3)写从机地址,等待EV6 (ADDRF=1); (4)发送数据,发送之前判断TXDATE标志,保证DAT为空开始写入数据; (5)等待最后一个字节位发送完成; (6)发送STOP信号;
注意在发送从机地址后写DAT后,数据会立即发送至移位缓冲器中,所以此时TXDATE不会清0(EV8_1),可以继续写下一个数据。在程序中只需要关注在TXDATE置1时写DAT即可。 写完第N个数据后,停止写数据,此时移位寄存器正在发送第N-1个数据。等移位寄存器发送第N个数据时可以不关心TXDATE置1,发送完成后DAT寄存器和移位寄存器同时为空时,BSF置1。此时发送STOP,等待BUSY清0,结束一次通信。 定义超时时间和错误状态: #define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000)
#define I2CT_LONG_TIMEOUT ((uint32_t)(3 * I2CT_FLAG_TIMEOUT))
typedef enum {
MASTER_OK = 0,
MASTER_BUSY,
MASTER_MODE,
MASTER_TXMODE,
MASTER_RXMODE,
MASTER_SENDING,
MASTER_SENDED,
MASTER_RECVD,
MASTER_BYTEF,
MASTER_BUSERR,
MASTER_UNKNOW,
SLAVE_OK = 20,
SLAVE_BUSY,
SLAVE_MODE,
SLAVE_BUSERR,
SLAVE_UNKNOW,
} ErrCode_t;
主机轮询发送函数:int I2C_Write(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
{
uint32_t I2CTimeout;
if (regAddr < 0 && len == 0) return 0; /* 至少发送一个数据 */
/* 1.保证I2C不在使用中 */
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_BUSY;
}
}
/* 2.发送START信号 */
I2C_GenerateStart(I2Cx, ENABLE);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG)) { // EV5
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_MODE;
}
}
/* 3.写从机地址 */
I2C_SendAddr7bit(I2Cx, slaveAddr, I2C_DIRECTION_SEND);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG)) { // EV6
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_TXMODE;
}
}
/* 4.写寄存器地址 */
if (regAddr >= 0) { /* 寄存器写 */
I2C_SendData(I2Cx, (uint8_t)regAddr);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDING)) { // EV8
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_SENDING;
}
}
}
/* 4.发送数据 */
while (len--) {
I2C_SendData(I2Cx, *datPtr++);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDING)) { // EV8
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_SENDING;
}
}
}
/* 5.等待最后一个字节发送完成 */
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED)) { // EV8_2
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_SENDED;
}
}
/* 6.发送STOP信号 */
I2C_GenerateStop(I2Cx, ENABLE);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_BUSY;
}
}
/* 7.发送完成 */
return MASTER_OK;
}
int I2C_Write(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len); 参数说明: uint8_t slaveAddr:Bit[7:1]为从机地址,不关心最低位。 short regAddr:发送从器件寄存器地址(8bit),如果不需要发送,填-1 。【寄存器写】uint8_t* datPtr:发送数据Buf的地址。
返回值:MASTER_OK表示通信正常,其他表示错误。
2、I2C主机轮询接收
EV5:STARTBF=1,起始位产生。 EV6:ADDRF=1,从机响应地址。 EV7/EV7_1: RXDATNE=1,读 DAT 寄存器。
主机接收步骤:
(1)保证I2C不在使用中,等待BUSY=0; (2)发送START信号,等待EV5 (STARTBF=1); (3)写从机地址,等待EV6 (ADDRF=1); (4)打开接收数据应答ACK,等待接收数据完成,判断RXDATNE标志读DAT,在接收最后一个字节完成前设置NACK和STOP信号。 (5)等待STOP发送,等待BUSY信号;
主机轮询接收函数: int I2C_Read(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
{
uint32_t I2CTimeout;
int ret;
if (len == 0) return 0; /* 至少读取一个数据 */
if (regAddr >= 0) { /* 寄存器读 */
ret = I2C_Write(I2Cx, slaveAddr, regAddr, datPtr, 0);
if (ret != MASTER_OK) return ret;
}
/* 1.保证I2C不在使用中 */
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_BUSY;
}
}
/* 2.发送START信号 */
I2C_GenerateStart(I2Cx, ENABLE);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG)) { // EV5
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_MODE;
}
}
/* 3.写从机地址 */
I2C_SendAddr7bit(I2Cx, slaveAddr, I2C_DIRECTION_RECV);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_RXMODE_FLAG)) { // EV6
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_TXMODE;
}
}
/* 4.接收数据 */
I2C_ConfigAck(I2Cx, ENABLE); /* 开启应答 */
while (len--) {
if (len == 0) { /* 接收最后一个数据前关闭应答,准备STOP */ // EV7_1
I2C_ConfigAck(I2Cx, DISABLE);
I2C_GenerateStop(I2Cx, ENABLE);
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_GetFlag(I2Cx, I2C_FLAG_RXDATNE)) { // EV7
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_BYTEF;
}
}
*datPtr++ = I2C_RecvData(I2Cx);
}
/* 5.等待BUSY */
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
if ((I2CTimeout--) == 0) {
I2C_ResetInit(I2Cx);
return MASTER_BUSY;
}
}
/* 6.接收完成 */
return MASTER_OK;
}
如果需要【寄存器读】,在读之前写一个数据。设置regAddr值>=0,否则直接读,设置regAddr为-1即可。
主机轮询读写函数声明: int I2C_Write(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);
int I2C_Read(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);
|