打印
[N32G45x]

I2C配置及使用分享

[复制链接]
659|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Afanx|  楼主 | 2022-7-15 23:59 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Afanx 于 2022-8-24 18:59 编辑

工程代码:
N32G45x_I2C_Demo.zip (428.74 KB)

一、I2C初始化相关配置


1、I2C的GPIO配置
N32G45x按组进行复用和重映射。具体引脚参考用户手册7.2.5.12 I2C复用功能重映射章节。
以I2C1为例子,使用默认分组,只需要把PB6和PB7配置为GPIO_Mode_AF_OD模式即可。如果需要使用PB8和PB9,调用库函数GPIO_ConfigPinRemap()配置分组1即可。
void I2C1_GPIO_Config(void)
{
GPIO_InitType GPIO_InitStructure;
/* Enable the GPIO Clock */
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO | RCC_APB2_PERIPH_GPIOB, ENABLE);

GPIO_InitStruct(&GPIO_InitStructure);
/* SCL--PB6/PB8  SDA--PB7/PB9 */
GPIO_InitStructure.Pin = GPIO_PIN_6 | GPIO_PIN_7;      // 使用默认分组0管脚
// GPIO_InitStructure.Pin = GPIO_PIN_8 | GPIO_PIN_9;  // 使用分组1 (I2C1_RMP[1:0] = 01)
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

// GPIO_ConfigPinRemap(GPIO_RMP_I2C1, ENABLE);    // 打开重映射功能,使用分组1(PB8 PB9)
}
关于GPIO_ConfigPinRemap参数说明:
库文件中参数定义如下,I2C1只有2个分组,所以默认为分组0,调用函数可配置分组1。I2C2有3个分组,默认为分组0,调用函数可配置分组1、分组3。
#define GPIO_RMP_I2C1       ((uint32_t)0x00000002) /*!< I2C1 Alternate Function mapping */   //I2C1使用分组1
#define GPIO_RMP1_I2C2     ((uint32_t)0x40160040) /*!< I2C2 Alternate Function mapping */   //I2C2使用分组1
#define GPIO_RMP3_I2C2     ((uint32_t)0x401600C0) /*!< I2C2 Alternate Function mapping */   //I2C2使用分组3
#define GPIO_RMP2_I2C3     ((uint32_t)0x40180200) /*!< I2C3 Alternate Function mapping */   //I2C3使用分组2
#define GPIO_RMP3_I2C3     ((uint32_t)0x40180300) /*!< I2C3 Alternate Function mapping */   //I2C3使用分组3
#define GPIO_RMP1_I2C4     ((uint32_t)0x401A0400) /*!< I2C4 Alternate Function mapping */   //I2C4使用分组1
#define GPIO_RMP3_I2C4     ((uint32_t)0x401A0C00) /*!< I2C4 Alternate Function mapping */   //I2C4使用分组3

2、I2C外设寄存器配置
以7位地址为例,首先定义I2C模块自身地址:
#define I2C1_OwnAddr 0x10
#define I2C2_OwnAddr 0x20
#define I2C3_OwnAddr 0x30
#define I2C4_OwnAddr 0x40
外设寄存器初始化代码:
void I2C_Config(I2C_Module* I2Cx)
{
    I2C_InitType I2C_InitStructure;

    if (I2Cx == I2C1) {
        RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_I2C1, ENABLE);
        I2C_InitStructure.OwnAddr1 = I2C1_OwnAddr;
    } else if (I2Cx == I2C2) {
        RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_I2C2, ENABLE);
        I2C_InitStructure.OwnAddr1 = I2C2_OwnAddr;
    } else if (I2Cx == I2C3) {
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_I2C3, ENABLE);
        I2C_InitStructure.OwnAddr1 = I2C3_OwnAddr;
    } else if (I2Cx == I2C4) {
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_I2C4, ENABLE);
        I2C_InitStructure.OwnAddr1 = I2C4_OwnAddr;
    }
    I2C_DeInit(I2Cx);
    I2C_InitStructure.BusMode = I2C_BUSMODE_I2C;
    I2C_InitStructure.FmDutyCycle = I2C_FMDUTYCYCLE_2;
    I2C_InitStructure.AckEnable = I2C_ACKEN;
    I2C_InitStructure.AddrMode = I2C_ADDR_MODE_7BIT;
    I2C_InitStructure.ClkSpeed = 100000;  // 100K
    I2C_Init(I2Cx, &I2C_InitStructure);   // Initial and Enable I2Cx
}
调用I2C_Init()会自动使能I2C模块,不需要额外使能。

3、I2C中断配置(如果使用中断收发,与轮询收发冲突)
定义是否开启中断:
#define I2C1_UseIT ENABLE
#define I2C2_UseIT DISABLE
#define I2C3_UseIT DISABLE
#define I2C4_UseIT DISABLE
I2C一共三个中断开关:EVTINTEN、BUFINTEN、ERRINTEN。分为两个中断入口:EV_IRQHandler、ER_IRQHandler。
中断配置代码:
void I2C_NVIC_Config(I2C_Module* I2Cx)
{
NVIC_InitType NVIC_InitStructure;

    if (I2Cx == I2C1) {
        NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelCmd = I2C1_UseIT;
    } else if (I2Cx == I2C2) {
        NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelCmd = I2C2_UseIT;
    } else if (I2Cx == I2C3) {
        NVIC_InitStructure.NVIC_IRQChannel = I2C3_EV_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelCmd = I2C3_UseIT;
    } else if (I2Cx == I2C4) {
        NVIC_InitStructure.NVIC_IRQChannel = I2C4_EV_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelCmd = I2C4_UseIT;
    }
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel++;  // 配置I2Cx_ER_IRQn
    NVIC_Init(&NVIC_InitStructure);

    I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, NVIC_InitStructure.NVIC_IRQChannelCmd); //打开I2C三个中断使能
}
4、I2C初始化以及复位
调用以上三个初始化函数进行I2C配置:
void I2C_Initial(void)
{
I2C1_GPIO_Config();
I2C_Config(I2C1);
I2C_NVIC_Config(I2C1);  // #define I2Cx_UseIT
}
硬件I2C在使用过程中容易被干扰进入错误状态,此时硬件无法自动恢复,需要手动复位。
通信错误会导致I2C状态卡死,因此I2C的软件复位非常重要!
最简单的复位方式就是重新初始化,GPIO不需要重新配置,重新初始化I2C寄存器以及中断配置即可。另外,当出现总线处于空闲状态,但I2C的Busy位置“1”,此时无法再开启新的传输,只能通过SWRESET位清除Busy位。
I2C软件复位代码:
void I2C_ResetInit(I2C_Module* I2Cx)
{
    I2Cx->CTRL1 |= 0x8000;  // Reset Busy
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    I2Cx->CTRL1 &= ~0x8000;
    I2C_Config(I2Cx);
    I2C_NVIC_Config(I2Cx);  // #define I2Cx_UseIT
}


使用特权

评论回复
沙发
Afanx|  楼主 | 2022-7-16 00:04 | 只看该作者
本帖最后由 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);



使用特权

评论回复
板凳
Afanx|  楼主 | 2022-7-19 10:35 | 只看该作者
本帖最后由 Afanx 于 2022-7-19 18:07 编辑

三、I2C做主机/从机,中断读写/收发
当I2C发送START信号时,自身作为主机,直到通信完成BUSY清0。其余时刻I2C作为从机,可以响应总线上的预设自身地址,地址匹配后作为从机收发。在程序上主从模式可以共用同一套配置和处理。

I2C中断请求表:


状态寄存器1:


状态寄存器2:


1、中断处理函数
声明I2C中断相关变量:
typedef struct {
    uint8_t dir;  // I2C_DIRECTION_SEND or I2C_DIRECTION_RECV
    uint8_t slaveAddr;
    short regAddr;  // -1 : no reg
    uint16_t dataLen;
    uint16_t dataCnt;
    uint8_t* datPtr;
} _iicMaster;

typedef struct {
    uint8_t mem[256];
    uint8_t ptr;
    uint8_t ptrUpdate;
} _iicSlave;

volatile _iicMaster iicMaster;
volatile _iicSlave iicSlave;


EV_IRQ中断处理函数:
void I2C_EV_IRQ_CallBack(I2C_Module* I2Cx, volatile _iicMaster* iicMaster, volatile _iicSlave* iicSlave)
{
    uint32_t last_event = I2C_GetLastEvent(I2Cx);

    // GPIO_LOG(GPIOA, GPIO_PIN_8, last_event);

    if ((last_event & I2C_ROLE_MASTER) == I2C_ROLE_MASTER) { /* MASTER Mode */
        switch (last_event) {
            case I2C_EVT_MASTER_MODE_FLAG:  // 0x00030001.EV5 (STARTBF)
                I2C_SendAddr7bit(I2Cx, iicMaster->slaveAddr, iicMaster->dir);
                break;
                /* Master Tx */
            case I2C_EVT_MASTER_TXMODE_FLAG:  // 0x00070082.EV6.EV8_1 (ADDRF TXDATE)
                if (iicMaster->regAddr < 0)
                    I2C_SendData(I2Cx, iicMaster->datPtr[iicMaster->dataCnt++]);
                else
                    I2C_SendData(I2Cx, (uint8_t)iicMaster->regAddr);
                break;
            case I2C_EVT_MASTER_DATA_SENDING:  // 0x00070080.EV8 (TXDATE)
            case I2C_EVT_MASTER_DATA_SENDED:   // 0x00070084.EV8_2 (TXDATE BSF)
                if (iicMaster->dataCnt < iicMaster->dataLen) {
                    I2C_SendData(I2Cx, iicMaster->datPtr[iicMaster->dataCnt++]);
                } else {
                    I2C_SendData(I2Cx, 0);  // clear TXDATE
                    I2C_GenerateStop(I2Cx, ENABLE);
                }
                break;
                /* Master Rx */
            case I2C_EVT_MASTER_RXMODE_FLAG:  // 0x00030002.EV6 (ADDRF)
                if (iicMaster->dataLen == 1) {
                    I2C_ConfigAck(I2Cx, DISABLE);
                    I2C_GenerateStop(I2Cx, ENABLE);
                } else {
                    I2C_ConfigAck(I2Cx, ENABLE);
                }
                break;
            case I2C_EVT_MASTER_DATA_RECVD_FLAG:      // 0x00030040.EV7 (RXDATNE)
            case I2C_EVT_MASTER_SFT_DATA_RECVD_FLAG:  // 0x00030044.EV7 (RXDATNE BSF)
                iicMaster->datPtr[iicMaster->dataCnt++] = I2C_RecvData(I2Cx);
                if (iicMaster->dataCnt == (iicMaster->dataLen - 1)) {
                    I2C_ConfigAck(I2Cx, DISABLE);
                    I2C_GenerateStop(I2Cx, ENABLE);
                }
                break;
            default:
                I2C_ResetInit(I2Cx);
                break;
        }
    } else { /* Slave Mode */
        switch (last_event) {
                /* Slave Tx */
            case I2C_EVT_SLAVE_SEND_ADDR_MATCHED:                    // 0x00060082.EV1.EV3_1 (ADDRF TXDATE)
            case I2C_EVT_SLAVE_DATA_SENDING:                         // 0x00060080.EV3 (TXDATE)
            case I2C_EVT_SLAVE_DATA_SENDED:                          // 0x00060084.EV3_2 (TXDATE BSF)
                I2C_SendData(I2Cx, iicSlave->mem[iicSlave->ptr++]);  // clear TXDATE
                break;
                /* Slave Rx */
            case I2C_EVT_SLAVE_RECV_ADDR_MATCHED:  // 0x00020002.EV1 (ADDRF)
                iicSlave->ptrUpdate = 1;
                break;
            case I2C_EVT_SLAVE_DATA_RECVD:  // 0x00020040.EV2 (RXDATNE)
                if (iicSlave->ptrUpdate) {
                    iicSlave->ptrUpdate = 0;
                    iicSlave->ptr = I2C_RecvData(I2Cx);  // clear RXDATNE
                } else {
                    iicSlave->mem[iicSlave->ptr++] = I2C_RecvData(I2Cx);  // clear RXDATNE
                }
                break;
            case I2C_EVT_SLAVE_STOP_RECVD:  // 0x00000010.EV4 (STOPF)
                I2C_Enable(I2Cx, ENABLE);   // clear STOPF
                break;
            default:
                I2C_ResetInit(I2Cx);
                break;
        }
    }
}
void I2C1_EV_IRQHandler(void) { I2C_EV_IRQ_CallBack(I2C1, &iicMaster, &iicSlave); }
void I2C2_EV_IRQHandler(void) { I2C_EV_IRQ_CallBack(I2C2, &iicMaster, &iicSlave); }
void I2C3_EV_IRQHandler(void) { I2C_EV_IRQ_CallBack(I2C3, &iicMaster, &iicSlave); }
void I2C4_EV_IRQHandler(void) { I2C_EV_IRQ_CallBack(I2C4, &iicMaster, &iicSlave); }
ER_IRQ中断处理函数:
void I2C_ER_IRQ_CallBack(I2C_Module* I2Cx)
{
    if ((I2C_GetLastEvent(I2Cx) & I2C_EVT_SLAVE_ACK_MISS) == I2C_EVT_SLAVE_ACK_MISS) {
        I2C_ClrFlag(I2Cx, I2C_FLAG_ACKFAIL);
    } else {
        I2C_ResetInit(I2Cx);
    }
}
void I2C1_ER_IRQHandler(void) { I2C_ER_IRQ_CallBack(I2C1); }
void I2C2_ER_IRQHandler(void) { I2C_ER_IRQ_CallBack(I2C2); }
void I2C3_ER_IRQHandler(void) { I2C_ER_IRQ_CallBack(I2C3); }
void I2C4_ER_IRQHandler(void) { I2C_ER_IRQ_CallBack(I2C4); }

2、I2C主机中断读写
开启中断写:
void I2C_Write_IT(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
{
    if (regAddr < 0 && len == 0) return; /* 至少发送一个数据 */

    Wait_IIC_Busy(I2Cx, I2CT_LONG_TIMEOUT);
    iicMaster.dir = I2C_DIRECTION_SEND;
    iicMaster.slaveAddr = slaveAddr;
    iicMaster.regAddr = regAddr;
    iicMaster.dataLen = len;
    iicMaster.dataCnt = 0;
    iicMaster.datPtr = datPtr;
    I2C_GenerateStart(I2Cx, ENABLE);
}
开启中断读:
void I2C_Read_IT(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
{
    if (len == 0) return; /* 至少读取一个数据 */

    if (regAddr >= 0) { /* 寄存器读 */
        Wait_IIC_Busy(I2Cx, I2CT_LONG_TIMEOUT);
        iicMaster.dir = I2C_DIRECTION_SEND;
        iicMaster.slaveAddr = slaveAddr;
        iicMaster.regAddr = regAddr;
        iicMaster.dataLen = 0;
        iicMaster.dataCnt = 0;
        iicMaster.datPtr = datPtr;
        I2C_GenerateStart(I2Cx, ENABLE);
        Wait_IIC_Busy(I2Cx, I2CT_LONG_TIMEOUT);
    }

    Wait_IIC_Busy(I2Cx, I2CT_LONG_TIMEOUT);
    iicMaster.dir = I2C_DIRECTION_RECV;
    iicMaster.slaveAddr = slaveAddr;
    iicMaster.regAddr = -1;
    iicMaster.dataLen = len;
    iicMaster.dataCnt = 0;
    iicMaster.datPtr = datPtr;
    I2C_GenerateStart(I2Cx, ENABLE);
}

等待BUSY信号清0:
void Wait_IIC_Busy(I2C_Module* I2Cx, uint32_t timeOut)
{
    while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
        if ((timeOut--) == 0) {
            I2C_ResetInit(I2Cx);
            return;
        }
    }
}
声明中断读写函数以及等待通信完成:
void I2C_Write_IT(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);
void I2C_Read_IT(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);

void Wait_IIC_Busy(I2C_Module* I2Cx, uint32_t timeOut);

3、I2C从机中断收发
I2C器件主从模式在配置上没有区别,只是主动发送时作为主机(标志位MSMODE置1),响应自身地址时作为从机(标志位MSMODE置1)。因此主从模式可以共用同一套代码,只需要判断MSMODE标志位做区分即可。在上面的例程中,主机读写之余,将自身模拟成一个256字节的EEPROM器件。总线上的其他主机可以用读写EEPROM的方式访问本机的256字节RAM数据。



使用特权

评论回复
地板
Afanx|  楼主 | 2022-7-19 10:36 | 只看该作者
本帖最后由 Afanx 于 2022-8-11 16:08 编辑

主机模式:
中断发送波形图分析:

中断接收波形图分析:



从机模式:
从机中断发送波形图分析:


从机中断接收波形图分析:



使用特权

评论回复
5
小黄鸭| | 2022-8-24 18:53 | 只看该作者
感谢大佬的分享,太牛了,对学习硬件I2C帮助很大

使用特权

评论回复
6
小黄鸭| | 2022-8-24 18:56 | 只看该作者
请教一下大佬,我主机发送最后一个数据后频繁触发中断,这个是为啥

使用特权

评论回复
7
Afanx|  楼主 | 2022-8-25 10:30 | 只看该作者
小黄鸭 发表于 2022-8-24 18:56
请教一下大佬,我主机发送最后一个数据后频繁触发中断,这个是为啥

应该是程序发送最后一个字节后没有清除TXDATE,写完需要发送的数据后,在最后一个字节发送过程中产生的TXDATE中断写任意值清除标志即可,这个值不会发送出去,因为发送最后一个字节后就产生STOP信号了。

使用特权

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

本版积分规则

13

主题

51

帖子

1

粉丝