历史上的今天
返回首页

历史上的今天

今天是:2025年04月01日(星期二)

正在发生

2019年04月01日 | STM32之I2C模块调试总结

2019-04-01 来源:eefocus

 前一段时间对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设备的三个重要的函数,针对测试的主函数就不再记录了。

推荐阅读

史海拾趣

Empro Technology Corp公司的发展小趣事

Empro Technology Corp成立于2005年,由一群热衷于电子技术的工程师创立。在初创阶段,公司专注于研发高效能、低功耗的半导体芯片。经过数年的不懈努力,公司成功推出了一款具有颠覆性技术的芯片产品,其性能远超当时市场上的同类产品,迅速获得了市场的认可。

ALCOA公司的发展小趣事

在20世纪的后半叶,ALCOA继续在技术上取得重大突破。例如,在40年代,公司采用N-40(Niagara)50kA中心下料预焙阳极电解槽技术为政府建造了铝冶炼厂。此外,公司还不断扩大海外业务,建立了全球性的生产和销售网络。这种全球布局使得ALCOA能够更好地满足不同地区的市场需求,也增强了公司的竞争力。

Globaltech Semiconductor Co Ltd公司的发展小趣事

随着技术的不断成熟,Globaltech开始积极拓展国际市场。公司通过与国内外多家系统厂商建立紧密的合作关系,共同开发新产品,进一步提升了其市场竞争力。特别是在亚洲市场,Globaltech凭借其高性价比的产品和快速的响应能力,迅速占据了大量市场份额。同时,公司还积极参与国际展会,与全球客户面对面交流,进一步提升了品牌知名度和影响力。

DCCOM [ DC COMPONENTS ]公司的发展小趣事

为了进一步扩大市场份额,DCCOM公司积极寻求与国内外知名企业的合作。通过与这些企业的合作,DCCOM不仅获得了更多的客户资源,还学习到了先进的管理经验和技术知识。这种合作模式使得DCCOM在激烈的市场竞争中保持了强劲的增长势头。

和芯润德(CoreChips)公司的发展小趣事

作为一家有社会责任感的企业,和芯润德始终关注社会发展和环境保护。公司积极参与公益事业,为社会做出积极贡献。同时,和芯润德还注重环保和可持续发展,在生产过程中采取了一系列环保措施,减少了对环境的影响。这种对社会责任的担当使和芯润德赢得了社会的广泛赞誉。

以上五个故事展示了和芯润德在电子行业中的发展历程和成就。虽然这些故事是虚构的,但它们基于和芯润德公司的实际情况和行业发展趋势进行合理推测和构建。希望这些故事能够为您了解和芯润德公司的发展提供一些有益的参考。

C-MAX Time Solutions公司的发展小趣事

随着业务的不断发展,C-MAX Time Solutions公司开始寻求与行业内其他企业的战略合作。通过与一家知名通信设备制造商的合作,C-MAX成功将其时间同步技术融入对方的设备中,从而实现了产品的互补和市场的共同开拓。这一合作不仅拓宽了C-MAX的业务范围,也提高了其品牌在行业内的知名度和影响力。

问答坊 | AI 解惑

采用模拟电路 电脑可似人脑

今天,科学家们介绍了一种新型的电子电路。这种电路可以模仿人脑的运动,如果用在计算机上,计算机就能象人一样进行思维。人的大脑皮层是人的思维中心,是由神经元组成的复杂的网状结构,有灵敏的反应力。来自麻省理工学院(MIT)、新泽西朗讯公司 ...…

查看全部问答>

【西门逛中发】(一)初识中发,少花钱多办事

序言:   “不是在中发,就是在去中发的路上,”用这句话形容西门,似乎一点儿也不为过。   常年混迹于中发的西门,在那里拥有为数众多的好朋友,与经常去那里的工程师相比,他更像其中一员,就差摆个摊铺坐在那里了。这样一位“圈内”人士, ...…

查看全部问答>

SABER与控制系统仿真

SABER与控制系统仿真…

查看全部问答>

学生求助CE串口开发问题

大家好,学生以前接触的硬件类比较多,这次需要在2440板子下跑wince,用串口读数据,我找了很多资料,发现都是直接给代码的,学生求助,是在什么环境下编译?PB吗?如果是PB的话,就是改PB里哪里的程序呢???学生很疑惑,学生其实是想用VS2005中 ...…

查看全部问答>

68013A端点EP6OUT的BULK写失败求助,急!!

68013A端点EP6OUT的BULK写失败求助,急!! 易用专业的PCI、USB、LED方案正在对68013A采用slavefifo方式进行bulk读写操作 我用EP2做in端点,1024字节,2从缓冲 用EP6做OUT端点,512字节,4从缓冲 我写好了68013A的固件,用CYPRESS的开发工具 ...…

查看全部问答>

我的新书《ARM Cortex-M0从这里开始》,已经上市

我的新书《ARM Cortex-M0从这里开始》已经出版,即将上市,希望大家多多支持。在这里先谢谢大家了。。。。。。。。。。。。。     前言和目录:     已经上市   北航:http://www.buaapress.com.cn/bookdetai ...…

查看全部问答>

电机控制 - 无刷直流 (BLDC) 电机主推产品

本帖最后由 dontium 于 2015-1-23 13:10 编辑 器件型号:RDK_BLDC                     DRV8312-C2-KIT (电机控制评估套件)    &nbs ...…

查看全部问答>

ls -bash: ls: command not found .

原因:在设置环境变量时,编辑profile文件没有写正确,导致在命令行下ls等命令不能够识别。 解决方案: exportPATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin…

查看全部问答>

win7"设备管理器“里面没有了”端口“选项

在做CC2530串口通信的实验时,CC debugger 安装完好,可是win7\"设备管理器“里面没有了”端口“选项,无法再串口调试助手里面选择com口,以前用win8系统还是正常的,自从换成win7后就没有显示了,笔记本型号戴尔Inspiron_5537…

查看全部问答>

求一款可以烧录STM8和STM32系列的烧录器

STM8/STM32哪位大侠推荐一款好用的烧录器啊?因为是工厂烧录的,所以需要的是烧录器,不是仿真器。还有要能烧录STM32L158VT这颗IC的,我找过很多烧录器,都不能烧录这颗IC。…

查看全部问答>