[N32G45x] I2C配置及使用分享

[复制链接]
1574|7
 楼主| Afanx 发表于 2022-7-15 23:59 | 显示全部楼层 |阅读模式
本帖最后由 Afanx 于 2022-8-24 18:59 编辑

工程代码:
N32G45x_I2C_Demo.zip (428.74 KB, 下载次数: 47)

一、I2C初始化相关配置


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

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

  13. // GPIO_ConfigPinRemap(GPIO_RMP_I2C1, ENABLE);    // 打开重映射功能,使用分组1(PB8 PB9)
  14. }
关于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模块自身地址:
  1. #define I2C1_OwnAddr 0x10
  2. #define I2C2_OwnAddr 0x20
  3. #define I2C3_OwnAddr 0x30
  4. #define I2C4_OwnAddr 0x40
外设寄存器初始化代码:
  1. void I2C_Config(I2C_Module* I2Cx)
  2. {
  3.     I2C_InitType I2C_InitStructure;

  4.     if (I2Cx == I2C1) {
  5.         RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_I2C1, ENABLE);
  6.         I2C_InitStructure.OwnAddr1 = I2C1_OwnAddr;
  7.     } else if (I2Cx == I2C2) {
  8.         RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_I2C2, ENABLE);
  9.         I2C_InitStructure.OwnAddr1 = I2C2_OwnAddr;
  10.     } else if (I2Cx == I2C3) {
  11.         RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_I2C3, ENABLE);
  12.         I2C_InitStructure.OwnAddr1 = I2C3_OwnAddr;
  13.     } else if (I2Cx == I2C4) {
  14.         RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_I2C4, ENABLE);
  15.         I2C_InitStructure.OwnAddr1 = I2C4_OwnAddr;
  16.     }
  17.     I2C_DeInit(I2Cx);
  18.     I2C_InitStructure.BusMode = I2C_BUSMODE_I2C;
  19.     I2C_InitStructure.FmDutyCycle = I2C_FMDUTYCYCLE_2;
  20.     I2C_InitStructure.AckEnable = I2C_ACKEN;
  21.     I2C_InitStructure.AddrMode = I2C_ADDR_MODE_7BIT;
  22.     I2C_InitStructure.ClkSpeed = 100000;  // 100K
  23.     I2C_Init(I2Cx, &I2C_InitStructure);   // Initial and Enable I2Cx
  24. }
调用I2C_Init()会自动使能I2C模块,不需要额外使能。

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

  4.     if (I2Cx == I2C1) {
  5.         NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
  6.         NVIC_InitStructure.NVIC_IRQChannelCmd = I2C1_UseIT;
  7.     } else if (I2Cx == I2C2) {
  8.         NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
  9.         NVIC_InitStructure.NVIC_IRQChannelCmd = I2C2_UseIT;
  10.     } else if (I2Cx == I2C3) {
  11.         NVIC_InitStructure.NVIC_IRQChannel = I2C3_EV_IRQn;
  12.         NVIC_InitStructure.NVIC_IRQChannelCmd = I2C3_UseIT;
  13.     } else if (I2Cx == I2C4) {
  14.         NVIC_InitStructure.NVIC_IRQChannel = I2C4_EV_IRQn;
  15.         NVIC_InitStructure.NVIC_IRQChannelCmd = I2C4_UseIT;
  16.     }
  17.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
  18.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
  19.     NVIC_Init(&NVIC_InitStructure);

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

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


 楼主| Afanx 发表于 2022-7-16 00:04 | 显示全部楼层
本帖最后由 Afanx 于 2022-7-19 18:02 编辑

二、I2C做主机,轮询读写

1、I2C主机轮询发送
主机发送序列.png
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信号;

主机发送序列2.png
注意在发送从机地址后写DAT后,数据会立即发送至移位缓冲器中,所以此时TXDATE不会清0(EV8_1),可以继续写下一个数据。在程序中只需要关注在TXDATE置1时写DAT即可。
写完第N个数据后,停止写数据,此时移位寄存器正在发送第N-1个数据。等移位寄存器发送第N个数据时可以不关心TXDATE置1,发送完成后DAT寄存器和移位寄存器同时为空时,BSF置1。此时发送STOP,等待BUSY清0,结束一次通信。
定义超时时间和错误状态:
  1. #define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000)
  2. #define I2CT_LONG_TIMEOUT ((uint32_t)(3 * I2CT_FLAG_TIMEOUT))

  3. typedef enum {
  4.     MASTER_OK = 0,
  5.     MASTER_BUSY,
  6.     MASTER_MODE,
  7.     MASTER_TXMODE,
  8.     MASTER_RXMODE,
  9.     MASTER_SENDING,
  10.     MASTER_SENDED,
  11.     MASTER_RECVD,
  12.     MASTER_BYTEF,
  13.     MASTER_BUSERR,
  14.     MASTER_UNKNOW,
  15.     SLAVE_OK = 20,
  16.     SLAVE_BUSY,
  17.     SLAVE_MODE,
  18.     SLAVE_BUSERR,
  19.     SLAVE_UNKNOW,
  20. } ErrCode_t;
主机轮询发送函数:
  1. int I2C_Write(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
  2. {
  3.     uint32_t I2CTimeout;
  4.     if (regAddr < 0 && len == 0) return 0; /* 至少发送一个数据 */

  5.     /* 1.保证I2C不在使用中 */
  6.     I2CTimeout = I2CT_LONG_TIMEOUT;
  7.     while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
  8.         if ((I2CTimeout--) == 0) {
  9.             I2C_ResetInit(I2Cx);
  10.             return MASTER_BUSY;
  11.         }
  12.     }
  13.     /* 2.发送START信号 */
  14.     I2C_GenerateStart(I2Cx, ENABLE);
  15.     I2CTimeout = I2CT_LONG_TIMEOUT;
  16.     while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG)) {  // EV5
  17.         if ((I2CTimeout--) == 0) {
  18.             I2C_ResetInit(I2Cx);
  19.             return MASTER_MODE;
  20.         }
  21.     }
  22.     /* 3.写从机地址 */
  23.     I2C_SendAddr7bit(I2Cx, slaveAddr, I2C_DIRECTION_SEND);
  24.     I2CTimeout = I2CT_LONG_TIMEOUT;
  25.     while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG)) {  // EV6
  26.         if ((I2CTimeout--) == 0) {
  27.             I2C_ResetInit(I2Cx);
  28.             return MASTER_TXMODE;
  29.         }
  30.     }
  31.     /* 4.写寄存器地址 */
  32.     if (regAddr >= 0) { /* 寄存器写 */
  33.         I2C_SendData(I2Cx, (uint8_t)regAddr);
  34.         I2CTimeout = I2CT_LONG_TIMEOUT;
  35.         while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDING)) {  // EV8
  36.             if ((I2CTimeout--) == 0) {
  37.                 I2C_ResetInit(I2Cx);
  38.                 return MASTER_SENDING;
  39.             }
  40.         }
  41.     }
  42.     /* 4.发送数据 */
  43.     while (len--) {
  44.         I2C_SendData(I2Cx, *datPtr++);
  45.         I2CTimeout = I2CT_LONG_TIMEOUT;
  46.         while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDING)) {  // EV8
  47.             if ((I2CTimeout--) == 0) {
  48.                 I2C_ResetInit(I2Cx);
  49.                 return MASTER_SENDING;
  50.             }
  51.         }
  52.     }
  53.     /* 5.等待最后一个字节发送完成 */
  54.     I2CTimeout = I2CT_LONG_TIMEOUT;
  55.     while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED)) {  // EV8_2
  56.         if ((I2CTimeout--) == 0) {
  57.             I2C_ResetInit(I2Cx);
  58.             return MASTER_SENDED;
  59.         }
  60.     }
  61.     /* 6.发送STOP信号 */
  62.     I2C_GenerateStop(I2Cx, ENABLE);
  63.     I2CTimeout = I2CT_LONG_TIMEOUT;
  64.     while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
  65.         if ((I2CTimeout--) == 0) {
  66.             I2C_ResetInit(I2Cx);
  67.             return MASTER_BUSY;
  68.         }
  69.     }
  70.     /* 7.发送完成 */
  71.     return MASTER_OK;
  72. }
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主机轮询接收 主机接收序列.png
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信号;

主机轮询接收函数:
  1. int I2C_Read(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
  2. {
  3.     uint32_t I2CTimeout;
  4.     int ret;
  5.     if (len == 0) return 0; /* 至少读取一个数据 */
  6.     if (regAddr >= 0) {     /* 寄存器读 */
  7.         ret = I2C_Write(I2Cx, slaveAddr, regAddr, datPtr, 0);
  8.         if (ret != MASTER_OK) return ret;
  9.     }

  10.     /* 1.保证I2C不在使用中 */
  11.     I2CTimeout = I2CT_LONG_TIMEOUT;
  12.     while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
  13.         if ((I2CTimeout--) == 0) {
  14.             I2C_ResetInit(I2Cx);
  15.             return MASTER_BUSY;
  16.         }
  17.     }
  18.     /* 2.发送START信号 */
  19.     I2C_GenerateStart(I2Cx, ENABLE);
  20.     I2CTimeout = I2CT_LONG_TIMEOUT;
  21.     while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG)) {  // EV5
  22.         if ((I2CTimeout--) == 0) {
  23.             I2C_ResetInit(I2Cx);
  24.             return MASTER_MODE;
  25.         }
  26.     }
  27.     /* 3.写从机地址 */
  28.     I2C_SendAddr7bit(I2Cx, slaveAddr, I2C_DIRECTION_RECV);
  29.     I2CTimeout = I2CT_LONG_TIMEOUT;
  30.     while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_RXMODE_FLAG)) {  // EV6
  31.         if ((I2CTimeout--) == 0) {
  32.             I2C_ResetInit(I2Cx);
  33.             return MASTER_TXMODE;
  34.         }
  35.     }
  36.     /* 4.接收数据 */
  37.     I2C_ConfigAck(I2Cx, ENABLE); /* 开启应答 */
  38.     while (len--) {
  39.         if (len == 0) { /* 接收最后一个数据前关闭应答,准备STOP */  // EV7_1
  40.             I2C_ConfigAck(I2Cx, DISABLE);
  41.             I2C_GenerateStop(I2Cx, ENABLE);
  42.         }
  43.         I2CTimeout = I2CT_LONG_TIMEOUT;
  44.         while (!I2C_GetFlag(I2Cx, I2C_FLAG_RXDATNE)) {  // EV7
  45.             if ((I2CTimeout--) == 0) {
  46.                 I2C_ResetInit(I2Cx);
  47.                 return MASTER_BYTEF;
  48.             }
  49.         }
  50.         *datPtr++ = I2C_RecvData(I2Cx);
  51.     }
  52.     /* 5.等待BUSY */
  53.     I2CTimeout = I2CT_LONG_TIMEOUT;
  54.     while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY)) {
  55.         if ((I2CTimeout--) == 0) {
  56.             I2C_ResetInit(I2Cx);
  57.             return MASTER_BUSY;
  58.         }
  59.     }
  60.     /* 6.接收完成 */
  61.     return MASTER_OK;
  62. }
如果需要【寄存器读】,在读之前写一个数据。设置regAddr值>=0,否则直接读,设置regAddr为-1即可。

主机轮询读写函数声明:
  1. int I2C_Write(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len);
  2. 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中断请求表:
中断请求.png

状态寄存器1:
状态寄存器1.png

状态寄存器2:
状态寄存器2.png

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

  9. typedef struct {
  10.     uint8_t mem[256];
  11.     uint8_t ptr;
  12.     uint8_t ptrUpdate;
  13. } _iicSlave;

  14. volatile _iicMaster iicMaster;
  15. volatile _iicSlave iicSlave;


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

  4.     // GPIO_LOG(GPIOA, GPIO_PIN_8, last_event);

  5.     if ((last_event & I2C_ROLE_MASTER) == I2C_ROLE_MASTER) { /* MASTER Mode */
  6.         switch (last_event) {
  7.             case I2C_EVT_MASTER_MODE_FLAG:  // 0x00030001.EV5 (STARTBF)
  8.                 I2C_SendAddr7bit(I2Cx, iicMaster->slaveAddr, iicMaster->dir);
  9.                 break;
  10.                 /* Master Tx */
  11.             case I2C_EVT_MASTER_TXMODE_FLAG:  // 0x00070082.EV6.EV8_1 (ADDRF TXDATE)
  12.                 if (iicMaster->regAddr < 0)
  13.                     I2C_SendData(I2Cx, iicMaster->datPtr[iicMaster->dataCnt++]);
  14.                 else
  15.                     I2C_SendData(I2Cx, (uint8_t)iicMaster->regAddr);
  16.                 break;
  17.             case I2C_EVT_MASTER_DATA_SENDING:  // 0x00070080.EV8 (TXDATE)
  18.             case I2C_EVT_MASTER_DATA_SENDED:   // 0x00070084.EV8_2 (TXDATE BSF)
  19.                 if (iicMaster->dataCnt < iicMaster->dataLen) {
  20.                     I2C_SendData(I2Cx, iicMaster->datPtr[iicMaster->dataCnt++]);
  21.                 } else {
  22.                     I2C_SendData(I2Cx, 0);  // clear TXDATE
  23.                     I2C_GenerateStop(I2Cx, ENABLE);
  24.                 }
  25.                 break;
  26.                 /* Master Rx */
  27.             case I2C_EVT_MASTER_RXMODE_FLAG:  // 0x00030002.EV6 (ADDRF)
  28.                 if (iicMaster->dataLen == 1) {
  29.                     I2C_ConfigAck(I2Cx, DISABLE);
  30.                     I2C_GenerateStop(I2Cx, ENABLE);
  31.                 } else {
  32.                     I2C_ConfigAck(I2Cx, ENABLE);
  33.                 }
  34.                 break;
  35.             case I2C_EVT_MASTER_DATA_RECVD_FLAG:      // 0x00030040.EV7 (RXDATNE)
  36.             case I2C_EVT_MASTER_SFT_DATA_RECVD_FLAG:  // 0x00030044.EV7 (RXDATNE BSF)
  37.                 iicMaster->datPtr[iicMaster->dataCnt++] = I2C_RecvData(I2Cx);
  38.                 if (iicMaster->dataCnt == (iicMaster->dataLen - 1)) {
  39.                     I2C_ConfigAck(I2Cx, DISABLE);
  40.                     I2C_GenerateStop(I2Cx, ENABLE);
  41.                 }
  42.                 break;
  43.             default:
  44.                 I2C_ResetInit(I2Cx);
  45.                 break;
  46.         }
  47.     } else { /* Slave Mode */
  48.         switch (last_event) {
  49.                 /* Slave Tx */
  50.             case I2C_EVT_SLAVE_SEND_ADDR_MATCHED:                    // 0x00060082.EV1.EV3_1 (ADDRF TXDATE)
  51.             case I2C_EVT_SLAVE_DATA_SENDING:                         // 0x00060080.EV3 (TXDATE)
  52.             case I2C_EVT_SLAVE_DATA_SENDED:                          // 0x00060084.EV3_2 (TXDATE BSF)
  53.                 I2C_SendData(I2Cx, iicSlave->mem[iicSlave->ptr++]);  // clear TXDATE
  54.                 break;
  55.                 /* Slave Rx */
  56.             case I2C_EVT_SLAVE_RECV_ADDR_MATCHED:  // 0x00020002.EV1 (ADDRF)
  57.                 iicSlave->ptrUpdate = 1;
  58.                 break;
  59.             case I2C_EVT_SLAVE_DATA_RECVD:  // 0x00020040.EV2 (RXDATNE)
  60.                 if (iicSlave->ptrUpdate) {
  61.                     iicSlave->ptrUpdate = 0;
  62.                     iicSlave->ptr = I2C_RecvData(I2Cx);  // clear RXDATNE
  63.                 } else {
  64.                     iicSlave->mem[iicSlave->ptr++] = I2C_RecvData(I2Cx);  // clear RXDATNE
  65.                 }
  66.                 break;
  67.             case I2C_EVT_SLAVE_STOP_RECVD:  // 0x00000010.EV4 (STOPF)
  68.                 I2C_Enable(I2Cx, ENABLE);   // clear STOPF
  69.                 break;
  70.             default:
  71.                 I2C_ResetInit(I2Cx);
  72.                 break;
  73.         }
  74.     }
  75. }
  1. void I2C1_EV_IRQHandler(void) { I2C_EV_IRQ_CallBack(I2C1, &iicMaster, &iicSlave); }
  2. void I2C2_EV_IRQHandler(void) { I2C_EV_IRQ_CallBack(I2C2, &iicMaster, &iicSlave); }
  3. void I2C3_EV_IRQHandler(void) { I2C_EV_IRQ_CallBack(I2C3, &iicMaster, &iicSlave); }
  4. void I2C4_EV_IRQHandler(void) { I2C_EV_IRQ_CallBack(I2C4, &iicMaster, &iicSlave); }
ER_IRQ中断处理函数:
  1. void I2C_ER_IRQ_CallBack(I2C_Module* I2Cx)
  2. {
  3.     if ((I2C_GetLastEvent(I2Cx) & I2C_EVT_SLAVE_ACK_MISS) == I2C_EVT_SLAVE_ACK_MISS) {
  4.         I2C_ClrFlag(I2Cx, I2C_FLAG_ACKFAIL);
  5.     } else {
  6.         I2C_ResetInit(I2Cx);
  7.     }
  8. }
  1. void I2C1_ER_IRQHandler(void) { I2C_ER_IRQ_CallBack(I2C1); }
  2. void I2C2_ER_IRQHandler(void) { I2C_ER_IRQ_CallBack(I2C2); }
  3. void I2C3_ER_IRQHandler(void) { I2C_ER_IRQ_CallBack(I2C3); }
  4. void I2C4_ER_IRQHandler(void) { I2C_ER_IRQ_CallBack(I2C4); }

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

  4.     Wait_IIC_Busy(I2Cx, I2CT_LONG_TIMEOUT);
  5.     iicMaster.dir = I2C_DIRECTION_SEND;
  6.     iicMaster.slaveAddr = slaveAddr;
  7.     iicMaster.regAddr = regAddr;
  8.     iicMaster.dataLen = len;
  9.     iicMaster.dataCnt = 0;
  10.     iicMaster.datPtr = datPtr;
  11.     I2C_GenerateStart(I2Cx, ENABLE);
  12. }
开启中断读:
  1. void I2C_Read_IT(I2C_Module* I2Cx, uint8_t slaveAddr, short regAddr, uint8_t* datPtr, uint16_t len)
  2. {
  3.     if (len == 0) return; /* 至少读取一个数据 */

  4.     if (regAddr >= 0) { /* 寄存器读 */
  5.         Wait_IIC_Busy(I2Cx, I2CT_LONG_TIMEOUT);
  6.         iicMaster.dir = I2C_DIRECTION_SEND;
  7.         iicMaster.slaveAddr = slaveAddr;
  8.         iicMaster.regAddr = regAddr;
  9.         iicMaster.dataLen = 0;
  10.         iicMaster.dataCnt = 0;
  11.         iicMaster.datPtr = datPtr;
  12.         I2C_GenerateStart(I2Cx, ENABLE);
  13.         Wait_IIC_Busy(I2Cx, I2CT_LONG_TIMEOUT);
  14.     }

  15.     Wait_IIC_Busy(I2Cx, I2CT_LONG_TIMEOUT);
  16.     iicMaster.dir = I2C_DIRECTION_RECV;
  17.     iicMaster.slaveAddr = slaveAddr;
  18.     iicMaster.regAddr = -1;
  19.     iicMaster.dataLen = len;
  20.     iicMaster.dataCnt = 0;
  21.     iicMaster.datPtr = datPtr;
  22.     I2C_GenerateStart(I2Cx, ENABLE);
  23. }

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

  3. 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 编辑

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

中断接收波形图分析:
中断接收波形.png


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

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


小黄鸭 发表于 2022-8-24 18:53 | 显示全部楼层
感谢大佬的分享,太牛了,对学习硬件I2C帮助很大
小黄鸭 发表于 2022-8-24 18:56 | 显示全部楼层
请教一下大佬,我主机发送最后一个数据后频繁触发中断,这个是为啥
 楼主| Afanx 发表于 2022-8-25 10:30 | 显示全部楼层
小黄鸭 发表于 2022-8-24 18:56
请教一下大佬,我主机发送最后一个数据后频繁触发中断,这个是为啥

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

本版积分规则

13

主题

51

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部