STM32 I2C硬件的结构

发布时间:2023-08-16  

引子

STM32的硬件I2C很多人都对它望而却步。因为很多电工都说,STM32 硬件 I2C有BUG、不稳定、死机等等……最后都使用GPIO模拟I2C。


的确,模拟I2C好用。但是在我看来在一个72M的Cortex-M3的MCU上这样做非常不妥。一般来说I2C是一种慢速总线,就算工作在400kHz的快速模式上,I2C传送每个字节仍需要至少23us——还没有计算地址、起始信号和结束信号的发送。如果使用GPIO模拟的I2C,这23us的CPU时间都在空转中浪费了,而这23us已经可以做不少的事情了,所以在STM32上I2C还是使用硬件为佳——虽然它多多少少有点缺陷。


这篇文章不是给完全没有接触过STM32 硬件I2C的新手看的,看这篇文章之前至少先阅读STM32的参考手册(RM0008)。


概览

我们先来看一下STM32 I2C硬件的结构

我们可以看见STM32的硬件I2C有两个和数据有关的寄存器“数据寄存器(Data register)”(DR)和“数据移位寄存器(Data shift register)”(DSR),我们的软件写入的是DR, DSR用于I2C数据的移位发送和接收,DR和DSR的数据交换由硬件控制——发送时DSR为空,DR不为空时,硬件自动把DR的数据写进DSR;接收时DR为空,DSR不为空,硬件自动把DSR数据写进DR。连续数据传输时,这样两个寄存器的数据交换使得软件读出和写入DR不会影响I2C总线中的数据接收和发送,使I2C的效率更高,这看起来十分美好,但是正是这个特点在某些情况下会变成电工们的噩梦。原因有二。


1、硬件上,DR和DSR的交换机制存在缺陷。 2、软件上,因为DR和DSR一共能容纳两个字节的数据,导致接收时候NACK的设置有一定的不可预料性。


硬件

硬件I2C上的缺陷,新版英文ErrSheet已经写得很清楚,就不引用了,这里只简单说说要点和一些个人总结。


1、EV7, EV7_1, EV6_1, EV6_3, EV2, EV8 和 EV3 必须在当前字节传输前处理完成,不然,有可能会导致数据出错。


这几个事件都涉及到DR和DSR,个人猜测(主要是有个”may be”才敢猜测)可能是读出或者写入DR的同时DSR被填满或清空,导致数据出错。理想情况下“读出或者写入DR的同时DSR被填满或清空”是不可能发生的,中断一来临的时候,CPU马上处理中断请求,读出或者写入DR数据,这时DSR的数据还是“新鲜滚热辣”的,可能连一位都没有接收或发送。但是,在实际使用时,可能有别的中断优先级比I2C的事件中断要高,I2C事件没有及时处理而出现了上述的情况。所以,ST建议把I2C的事件中断设置成最高优先级。


2、产生STOP前DSR必须为空,不然,会导致DSR里的数据左移一位。

这个没什么好说的,就是一个硬件的BUG,保证发送STOP前DSR没有数据就可以了。


3、总线上,开始条件(S)后没有进行数据传输就马上设置停止条件(P),或者S后忘记P会导致硬件I2C不能再次产生S,必须软复位I2C。

这个ST解释成是,STM32严格按照了I2C的标准,S之后没有数据传输是不能P的。其实这点可以体谅,但是,这点如果没有处理好,总线上的错误会导致STM32 I2C陷入瘫痪。


软件

由于DR和DSR的存在,编程上需要一些技巧,新版英文ErrSheet和参考手册(RM0008)都有相关的操作介绍(Closing the communication),排除硬件上的缺陷,编程的难点主要在接收时如何可靠地设置NACK上。


在只有DSR的MCU上设置NACK是非常简单的,在读出倒数第二个数据前设置一下就可以了,但是个方法在似乎在STM32上行不通,因为STM32有DR和DSR,在倒数第二个数据被接收的时候(RxNE置位),马上设置NACK,理想情况下没有任何问题,NACK也被正确的发送,但是如果有其他更高优先级的中断打断了这个过程,NACK就不能及时设置,导致从器件收到的是ACK没有释放总线……


ST提供的资料上(笔者所见),给电工们的建议。

1、接收2个字节或1个字节时,切换GPIO模式为OD,然后软件下拉SCL引脚,使硬件I2C发生时钟延展,把下一个字节开始传输的时机延后,设置完NACK后,再把GPIO设置回AFOD,但是这只能解决小于两个字节的接收。

2、大于2个字节用DMA,DMA可以说是特效药,“屡试不爽”。不过要注意,接收大于或等于2个字节时才能使用DMA,不然不能产生EOT-1事件导致NACK不能正确发送。

3、设置I2C事件中断为最高优先等级。


方案

读到这里你可能会想,硬件有缺陷,软件也得这么“猥琐”,可以说是寸步难行。真的没有其他办法了吗?其实,我们可以把DR和DSR两个当一个用,全部判断BTF,不理会TxE和RxE,用时间来换稳定性,慢点就慢点总比没得用好。发送时:开始,发送写地址,器件应答,清ADDR,一字节数据到写DR,硬件把DR数据写入到DSR,当DSR传输完毕时,DR也为空,BTF置位,这时我们再写一字节数据到DR,如此循环,最后一次BTF置位的时候发送P或者重起始(R)。这样操作,“硬件把DR数据写入到DSR”执行的时间是我们可以预料的,不存在上面提及的冲突问题。接收时:1、接收一个字节:按照ST给的方法。开始,发送读地址,器件应答,清ADDR前软件下拉SCL,写完NACK、STOP和DR后软件再释放SCL。RxNE时读DR。 2、接收两个字节:也是按照ST的方法。开始,发送读地址,器件应答,设置POS和ACK,下拉SCL,清ADDR,设置NACK,释放SCL。BTF时,软件拉低SCL,发送STOP,读DR,释放SCL,再读DR。 3、接收两个以上字节:开始,发送读地址,器件应答,直接清ADDR。BTF时,读DR一次。再BTF,再读DR一次,如此循环。倒数第二次BTF时设置NACK(注意DR和DSR各有一字节的数据),读DR一次。再等到最后一次BTF时,软件拉低SCL,发送STOP,读DR,释放SCL,再读DR。 4、请注意在读取SR2到操作其他I2C寄存器期间使用软件产生时钟延展。


干扰

当总线空闲时,无论是SCL的跳变(电平高低高),还是SDA的跳变,都会导致STM32的硬件I2C瘫痪,不能产生下一个S。当总线正在传输数据时,总线上的信号干扰对STM32的硬件I2C来说是致命的。


1、空闲时SDA跳变,会产生一个S和一个P,幸好这个P会产生一个中断,我们可以用一个收到P就软复位硬件I2C的策略。这样能避免空闲时SDA跳变带来的干扰。

2、空闲时SCL跳变,这是一个I2C的错误信号,但是STM32却会认为这是一个S,所以SCL跳变会导致BUSY置位,而且不会像SDA跳变那样会产生一个P中断。如果在单主的情况下,你可以为I2C的S做一个超时,超时了就软复位I2C就可以,当然最简单的方法还是空闲时关闭I2C(PE置零)。在多从机的情况下,只能等待别的主机发送的一个P,或者伺机软复位。

3、传输途中因干扰,产生总线错误(BERR)。单主接收途中出现BERR,可以在关闭硬件I2C后,连续模拟产生9个以上的SCL,在保证SDA为高电平的情况下软复位I2C。

4、传输途中因干扰,导致仲裁丢失(ARLO)。单主时和BERR的处理方法相同。


其他

还有什么值得注意的?

很多电工们反映,上电也是一个大问题,I2C一上电就马上BUSY了,第一次的S都不能发送,我是没有遇上这个问题。Google了一下很多都说是初始化顺序的问题。说说我的初始化吧,打开I2C外设的时钟、打开I2C引脚所在的GPIO的时钟、配置 GPIO_AF_OD、 I2C_DeInit、 I2C_Init、 I2C_Cmd,没有什么特别。还有一种可能就是,上电时上电的脉冲干扰了总线,导致某个从设锁死了总线(拉低了SDA)导致的BUSY置位,这个可以用处理BERR的方法,使总线恢复正常。


总线上的P产生后最好不要配置CR1的ACK位。STOP发送后配置ACK位——作为主机接收最后一字节时需要发送NACK,同时我们需要响应自己的从机地址,这时需要重新配置ACK为”1″——有可能导致下一次作为主机通信发送地址时,硬件不发送地址而直接发送P——这应该是一个硬件BUG,暂时还没有看见相关资料——具体表现为EV6死循环。推荐的做法是设置P前,软件下拉SCL,设置P,设置ACK,释放SCL,这样总线上的P将在释放SCL后产生。


总结

这些都是我调STM32硬件I2C的一些心得。上文提及到的中断接收和发送方法,我用TIM自动更新,产生最高占先优先级的中断,并在中断里停留70us左右,且重装载值是一个素数的情况下,STM32F103VET6 400kHz的I2C跑了近一周没有发现数据错误。


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>