本帖最后由 风雨欲来ds 于 2021-8-30 20:13 编辑

在I2C设备中,通信是借助设备地址寻址实现的,大致可以分为两类:一对多、多对多通信。在多主机通信时,一个从机如果想接收多个主机的数据,就需要使用到从机多地址的功能,该功能在光模块中普遍会用到。
I2C从机模式最高速率一般可达3.4Mbps,I2C主机想访问I2C从机设备,需要知道I2C从机设备的地址,需要通过配置I2C_SLAVMASK寄存器来实现的。通过I2C_SAR寄存器配置从机地址后,再配置I2C_SLAVMASK寄存器。一般I2C设备地址分为7位地址和10位地址格式,所以需要按照自己的实际情况配置I2C_SLAVMASK寄存器的低九位,I2C_SLVRCVADDR寄存器会给出真实地址。
注意:在I2C中有些特殊地址是不会产生响应的。
涉及到的寄存器描述
1532613511986006702th.png
软件功能配置流程
制作一个主机设备发送16个字节的字符串,连接两块开发板的SDA/SCL,通过主机向从机发送数据,分别将目标地址设置为:0xA00xA20xA40xA60xA80xAA0xAC0xAE进行通信测试。
I2C 的主机程序配置
void I2C_WRTest(void)
  • {
  •     Write(0x00, gTxData, 0x10);
  •     DELAY_Ms(100);
  •     Read(0x00, gRxData, 0x10);
  •     DELAY_Ms(100);
  • }
  • s32 main(void)
  • {
  •     DELAY_Init();
  •     I2C_WRInit();
  •     for(int i=0;i<8;i++)
  •     {
  •         I2C_Cmd(I2C1, DISABLE);
  •         I2C_SetDeviceAddr(I2C1, EEPROM_ADDR+2*i);
  •         I2C_Cmd(I2C1, ENABLE);
  •         I2C_WRTest();
  •     }
  •     While(1)
  • {
  •     }
  • }
  • 复制代码
    I2C 的从机初始化
    void I2C_NVIC_SlaveInit(void)
  • {
  •     NVIC_InitTypeDef NVIC_InitStructure;
  • GPIO_InitTypeDef  GPIO_InitStructure;
  •     NVIC_InitStructure.NVIC_IRQChannel = I2C1_IRQn;
  •     NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
  •     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  •     NVIC_Init(&NVIC_InitStructure);
  •    
  •     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
  •     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_1);
  •     GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_1);
  •     GPIO_StructInit(&GPIO_InitStructure);
  •     GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_7;
  •     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  •     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //Need extra plus pull
  •     GPIO_Init(GPIOB, &GPIO_InitStructure);
  •     GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_6;  //I2C1 remap IO port
  •     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;  // clock input
  •     GPIO_Init(GPIOB, &GPIO_InitStructure);
  •    
  •     I2C_SlaveMode();
  • }
  • void I2C_NVIC_SlaveTest()
  • {
  •     u32 i;
  •     while( gTxFlag | gRxFlag);
  •     for(i = 0; i < 16; i++)
  • {
  •         printf("TX data%d is  : %x \r\n", i, gTxBuff[i]);
  •     }
  •     for(i = 0; i < 16; i++) {
  •         printf("RX data%d is  : %x \r\n", i, gRxBuff[i]);
  •     }
  •     gTxFlag = 1;
  •     gRxFlag = 1;
  • }
  • void I2C_SlaveMode()
  • {
  •     I2C_InitTypeDef I2C_InitStructure;
  •     I2C_StructInit(&I2C_InitStructure);
  •     RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
  •     I2C_DeInit(I2C1);
  •     I2C_InitStructure. Mode = (I2C_CR_MASTER >> 1);
  •     I2C_InitStructure. OwnAddress = 0;
  •     I2C_InitStructure. Speed = I2C_CR_STD;
  •     I2C_InitStructure. ClockSpeed = 100000;
  •     I2C_Init(I2C1, &I2C_InitStructure);
  •     I2C_ITConfig( I2C1, I2C_IT_RD_REQ, ENABLE );//Read request
  •     I2C_ITConfig( I2C1, I2C_IT_RX_FULL, ENABLE );//Receive interrupt
  •     I2C_SendSlaveAddress(I2C1, 0xA8);
  •     I2C1->SLVMASK = 0x0F;
  •     I2C_Cmd(I2C1, ENABLE);
  • }
  • s32 main(void)
  • {
  •     CONSOLE_Init(115200);
  •     I2C_NVIC_SlaveInit();
  •     I2C_NVIC_SlaveTest();
  •     while(1)
  • {
  •     }
  • }
  • 复制代码
    以上程序将设备配置为从机模式,使能读请求/接收缓冲非空中断,从机地址配置为0xA8SLVMASK配置为0x0F表示地址的低四位不进行比较,则从机设备可以从总线上接受地址为0xA00xA20xA40xA60xA80xAA0xAC0xAE的数据包。
    从机中断处理多地址
    void I2C1_IRQHandler(void)
  • {
  •     u16 stop_flag, start_flag;
  •     if(I2C_GetITStatus(I2C1, I2C_IT_RD_REQ))
  • {
  •         I2C1->RD_REQ;
  •         while(1){
  •             I2C1->DR = (u8)gTxBuff[gTxCnt];
  •             I2C_TX_EmptyCheck(I2C1);
  •             gTxCnt ++;
  •             if(gTxCnt == 16) {
  •                 gTxCnt = 0;
  •                 I2C_GenerateSTOP( I2C1, ENABLE );
  •                 break;
  •              }
  •             }
  •         gTxFlag = 0;      
  •     }
  •     // interrupt receive
  •     if(I2C_GetITStatus(I2C1, I2C_IT_RX_FULL))
  • {
  • //Master sends slave receive
  •         gRxBuff[gRxCnt++] = I2C_ReceiveData(I2C1);
  •         while(!(I2C1->SR & 0x4));
  •         if(gRxCnt == 16) {
  •             gRxCnt = 0;
  • I2C_GenerateSTOP( I2C1, ENABLE );
  •        }
  •         gRxFlag = 0;
  •     }
  •     stop_flag = I2C1->STOP;
  •     start_flag = I2C1->START;
  •     if((stop_flag  & start_flag) != ((u32)RESET)) //slave receive
  • {
  •         I2C_ClearITPendingBit(I2C1,I2C_IT_STOP_DET);
  •         I2C_ClearITPendingBit(I2C1, I2C_IT_START_DET);   
  • }
  • }
  • 复制代码
    测试验证
    通过UART1打印接收及发送I2C从机通讯数据,通过逻辑分析仪抓取I2C波形如下:
    可以看出来,I2C 从机可以被设置为多个地址,分别为 0xA00xA20xA40xA60xA80xAA0xAc0xAE,并且都能够与主机进行正常的数据包交互,通过该特性即可实现应用中的多个地址需求。