datasheet

浅谈 STM32 硬件I2C的使用 (中断方式 无DMA 无最高优先级)

2019-07-13来源: eefocus关键字:STM32  硬件I2C  中断方式  DMA

引子

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寄存器期间使用软件产生时钟延展。 2016.02.03  thx:iguesser


干扰

当总线空闲时,无论是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的方法,使总线恢复正常。(2012 Jun 6)


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


总结

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

至此,STM32 I2C的问题基本解决,欢迎广大电工们指正、反馈。


关键字:STM32  硬件I2C  中断方式  DMA

编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic467659.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:STM32F103C8T6最小系统地的构建(3)_时钟源电路-晶振
下一篇:STM32总结之I2C协议

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

STM32使用FreeRTOS中的链表创建节点与列表项

使用了STM32F103的FreeRTOS库来创建一个列表,其实FreeRTOS的list中链表是双向的循环链表,为了简单介绍,我直接说是列表了。 芯片使用的是GD32F103CBT6,该芯片兼容ST的库,不影响使用效果。 以下代码不全,仅提供思路,如有不足,请给我留言。第一步先定义变量,初始化列表及列表项nodeDevice node_t; //节点数据nodeDevice * pNode; //节点指针 xList list_t; //列表#define pList ((xList *) &list_t) //列表指针 xListItem item_t; //列表
发表于 2019-07-18

FreeRTOS学习笔记——基础知识与移植(STM32F103)

添加文件打开基础工程,新建分组FreeRTOS_CORE 和FreeRTOS_PORTABLE,然后向这两个分组中添加文件,如图2.2.1.4 所示:分组FreeRTOS_CORE 中的文件在什么地方就不说了,打开FreeRTOS 源码一目了然。重点来说说FreeRTOS_PORTABLE 分组中的port.c 和heap_4.c 是怎么来的,port.c 是RVDS 文件夹下的ARM_CM3 中的文件,因为STM32F103 是Cortex-M3 内核的,因此要选择ARM_CM3中的port.c 文件。heap_4.c 是MemMang 文件夹中的,前面说了MemMang 是跟内存管理相关的,里面有5 个c 文件:heap_1.c
发表于 2019-07-18
FreeRTOS学习笔记——基础知识与移植(STM32F103)

STM32CubeMX+FreeRTOS实验---使用两个实例,共用一个task

, 0, 128);  LED2FlashHandle = osThreadCreate(osThread(LED2Flash), NULL);只是一个定义osThreadDef只是一个定义,把相关的内容绑在一起,不是函数。osThreadCreate才是一个函数。第二个参数就是*argument,对应StartLEDFlashTask函数的输入参数。因此在osThreadDef之前加入code。/*import user code,may be changed by stm32cubeMX*//*take attention */    uint16_t led1pin,led2pin
发表于 2019-07-18
STM32CubeMX+FreeRTOS实验---使用两个实例,共用一个task

stm32 FreeRTOS中如何创建任务

#include "config.h"#include "global.h"#include "stdio.h"#include "PC.h" #include "FreeRTOS.h"#include "task.h"#include "list.h" void vLED1Task(void *pvParameters){ while(1) { GPIO_Reverse (); vTaskDelay(1000/portTICK_RATE_MS); }}void
发表于 2019-07-18

STM32CubeMX学习笔记——FreeRTOS_任务挂起与继续

Githubhttps://github.com/HaHaHaHaHaGe/Planof2019_half/tree/master/Course_Project/FreeRTOS/Class02_SuspendResume简介任务挂起与继续的作用,用于暂停运行某些任务与继续运行某些任务。就像用视频播放器看电影的播放按键与暂停按键一样简单。在STM32CubeMX上配置FreeRTOS非常的简单,几乎不需要做任何操作,只需要勾选移植FreeRTOS就成功了,关于在非Cube上的RTOS配置文件内的相关内容,也在下方的Configuration栏中给好了所有的配置都可以在这里完成任务创建有别于在没有使用Cube下的创建方式,在Cube
发表于 2019-07-18
STM32CubeMX学习笔记——FreeRTOS_任务挂起与继续

STM32 RTOS系统 学习笔记(一)

基本概念               RTOS全称为:Real Time OS,就是实时操作系统,强调的是:实时性。实时操作系统又分为硬实时和软实时。硬实时要求在规定的时间内必须完成操作 ,硬实时系统不允许超时,在软实时里面处理过程超时的后果就没有那么严格。       在实时操作系统中,我们可以把要实现的功能划分为多个任务,每个任务负责实现其中的一部分,每个任务都是一个很简单的程序,通常是一个死循环。
发表于 2019-07-18
STM32 RTOS系统 学习笔记(一)

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved