STM32之I2C模块调试总结

       前一段时间对STM32的I2C模块进行了调试,今天做一个总结。关于I2C协议的知识,这里就不再赘述,网上有很多介绍I2C协议的文章。目前实现I2C协议的方式有两种,一是采用GPIO口来模拟I2C协议,另外一种是使用STM32自带的I2C模块。虽说使用GPIO口模拟I2C协议较为复杂,需要详细了解I2C协议的内容,但是实现这种方式的资料也非常多,网上都有对应的源码实现,只需要简单修改,就可以实现功能。而针对使用STM32自带的I2C模块,网络上贬斥的声音较多,说是模块本身自带bug,容易出问题,甚至还有人说是史上最难调的I2C模块。当然了,这些问题我自己目前还没有遇到,可能需要以后来验证了。好了,言归正传,今天主要记录一下调试过程以及需要注意的地方。      

//功能:初始化IIC接口
void SSX1207_Init(void)
{
 GPIO_InitTypeDef  GPIO_InitStructure;
 I2C_InitTypeDef   I2C_InitStructure;
 
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
 //GPIOB6,B7初始化设置,PB6=SCL,PB7=SDA
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用模式
 GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//开漏模式
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//
 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化

 GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_I2C1);//将PB6连接到SCL
 GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_I2C1);//将PB7连接到SDA

 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//使能I2C1时钟
 //配置I2C参数
 I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;
 I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2;
 I2C_InitStructure.I2C_ClockSpeed=I2C_Standard_Speed;
 I2C_InitStructure.I2C_OwnAddress1 = 0x00;
 I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
 I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
 I2C_Init(I2C1,&I2C_InitStructure);//初始化
 I2C_Cmd(I2C1,ENABLE);//使能I2C
}

该函数完成对I2C模块的初始化,首先要确定使用的GPIO口,然后对GPIO口的参数进行配置,这一部分也是参数网上的一下资料进行配置的,需要注意的是GPIO_Mode要配置成复用模式,GPIO_OType要配置成开漏模式,如果使用别的库可能对应的参数有差异,但基本功能应该是一样的,需要将GPIO口配置成复用开漏模式。上面只是对要使用到的GPIO口的配置,还需要配置I2C的参数,这部分只需要注意一下OwnAddress1即可,该参数只需要跟总线上的其他I2C设备的地址不一样就行了,是用户自己定义的。

    初始化函数记录完成之后,下面记录主I2C设备对从I2C设备写数据的过程。

//功能:将指定数量的数据写入到IIC设备中
//参数:
//  pBuffer--要写入的数据数组
//  NumToWrite--写入数据的个数
void SSX1207_WriteByteArray(u8 *pBuffer,u16 NumToWrite)
{
 uint32_t flag=0;
 I2C_AcknowledgeConfig(I2C1,ENABLE);//ACK置1
 I2C_GenerateSTART(I2C1,ENABLE);//产生一个启动信号
 timeout=0x1000;
 while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT))//EV5事件
 {
  if((--timeout)==0)
  {
   printf("EV5 fail\r\n");
   break;
  } 
 }
 flag = I2C_GetLastEvent(I2C1);
 printf("flag=%x\r\n",flag);
 if(timeout!=0)
 printf("EV5 sucess\r\n");
 I2C_Send7bitAddress(I2C1,0X50,I2C_Direction_Transmitter);//发送安全芯片地址和写命令
 timeout=0x1000;
 while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))//EV6事件
 {
  if((--timeout)==0)
  {
   printf("EV6 fail\r\n");
   break;
  } 
 }
 flag = I2C_GetLastEvent(I2C1);
 printf("flag=%x\r\n",flag);
 if(timeout!=0)
 printf("EV6 sucess\r\n");
 while(NumToWrite--)
 {
  timeout=0x1000;
  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING))//EV8事件
  {
   if((--timeout)==0)
   {
   printf("EV8 fail\r\n");
   break;
   } 
  }
  flag = I2C_GetLastEvent(I2C1);
  printf("flag=%x\r\n",flag);
  if(timeout!=0)
  printf("EV8 sucess\r\n");
  I2C_SendData(I2C1,*pBuffer);
  pBuffer++;
 }
 timeout=0x1000;
 while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED))//EV8_2事件
 {
  if((--timeout)==0)
  {
   printf("EV8_2 fail\r\n");
   break;
  } 
 }
 flag = I2C_GetLastEvent(I2C1);
 printf("flag=%x\r\n",flag);
 if(timeout!=0)
 printf("EV8_2 sucess\r\n");
 I2C_GenerateSTOP(I2C1,ENABLE);//产生一个停止信号
}

该函数是将指定数量的数据写入到I2C从设备中,整个流程是参照手册的上的流程来编码的。

//功能:从IIC设备读取指定长度的数据
//参数:
//  pBuffer:要读取数据的存放数组
//  NumToRead:读取数据的数量
void SSX1207_ReadByteArray(u8 *pBuffer,u16 NumToRead)
{
 uint32_t flag=0;
 u16 NumToRead_FLAG=NumToRead;
 
 if(NumToRead_FLAG==8)
 {
  I2C_GenerateSTART(I2C1,ENABLE);//产生一个启动信号
  timeout=0x1000;
  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT))//EV5事件
  {
   if((--timeout)==0)
   {
    printf("EV5 fail!\r\n");
    break;
   } 
  }
  flag = I2C_GetLastEvent(I2C1);
  printf("flag=%x\r\n",flag);
  if(timeout!=0)
  printf("EV5 sucess!\r\n");
  I2C_Send7bitAddress(I2C1,0X50,I2C_Direction_Receiver);//发送安全芯片地址和读命令
  timeout=0x1000;
  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))//EV6事件
  {
   if((--timeout)==0)
   {
    printf("EV6 fail!\r\n");
    break;
   } 
  }
  flag = I2C_GetLastEvent(I2C1);
  printf("flag=%x\r\n",flag);
  if(timeout!=0)
  printf("EV6 sucess!\r\n");
 }
 while(NumToRead)
 {
  printf("len=%d\r\n",NumToRead);
  NumToRead--;
  if((NumToRead==2)&&(NumToRead_FLAG!=8))
  {
   I2C_AcknowledgeConfig(I2C1,DISABLE);//ACK位清零 
  }

  if((NumToRead==1)&&(NumToRead_FLAG!=8))
  {
   I2C_GenerateSTOP(I2C1,ENABLE);//产生一个停止信号
  }

  timeout=0x1000;
  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED))//EV7事件
  {
   if((--timeout)==0)
   {
    printf("EV7 fail!\r\n");
    break;
   } 
  }
  *pBuffer=I2C_ReceiveData(I2C1);
  flag = I2C_GetLastEvent(I2C1);
  printf("flag=%x\r\n",flag);
  if(timeout!=0)
  printf("EV7 sucess! *pBuffer=%02x\r\n",*pBuffer);
  pBuffer++; 
 }
 printf("NumToRead=%d\r\n",NumToRead);
}

该函数是主设备向从I2C设备读取指定长度的数据。整个流程也是参考手册的流程来实现的。

读取数据的过程主要是注意要读取的数据还剩三个字节的时候CR1寄存器中ACK位和STOP位的变化情况,这里所说的情况是要读取的数据大于两个字节的情况。

如代码中红色标注的所示,此时倒数第三个字节在DR寄存器中,需要将ACK复位,然后读取该字节,但STOP位不能置1(务必要注意这一点,网上有在此时就将STOP位置1的,导致后续的操作失败)。当要读取倒数第二个字节时,将STOP位置1,如代码中紫色标注的部分。另外,为了后续正常读写数据,需要将ACK位再次置1,本次是在写数据函数内实现的。以上是操作I2C设备的三个重要的函数,针对测试的主函数就不再记录了。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值