搜索
bottom↓
回复: 59

再发一个基于DMA解决STM32 I2C的程序(应该是目前比较完善...

  [复制链接]

出0入76汤圆

发表于 2013-10-23 19:00:13 | 显示全部楼层 |阅读模式
本帖最后由 foxpro2005 于 2013-10-23 19:02 编辑

首先,非感谢原作者lj2505大侠提供的例子“相信这是ouravr共享中,目前最完美的STM32读写EEPROM驱动,硬件I2C中断加DMA方式(原创)”
http://www.amobbs.com/forum.php? ... highlight=I2C%2BDMA

目前,我在此基础上再次进行了修改与完善:
对原来存在的一些bug, 基本上移出while轮询, 并加入超时特性 防止等死, 并对数据传递方式进行了部分改进....
应该是目前比较完善的版本..., 在程序中做大量的中文注释, 方便大家阅读....(相信应该不需要我再整一个流程图或状态图了吧, ...)

并在野火M3板上测试通过,手上只有at2402,已测试验证通过..., 现欢迎大家进行测试,以及讨论...

更新及具体特性如下:
1.支持EEPROM: AT24C01 ~ AT24C512
2.修正了lijie大侠发布的原版中,不能读取1个字节数据的BUG
3.支持了对于有I2C1, I2C2模块的选择配置, 只要需要在头文件中一条宏开关即可快速完成
4.支持了I2C1引脚重映射功能选择配置,也是在头文件中一条宏即可快速完成
5.增加了对函数调用参考的有效性检测
6.修改了数据的读/写操作方式:

从EEPROM中读取数据
读取数量 = 0 : 返回错误, 一般不会这么操作吧!
读取数量 = 1 : 只读取1个字节数据,使用中断直接完成的方式。
读取数量 > 1 : 采用DMA方式, 并使用中断进行辅助配合完成。

向EEPROM写入数据
写入数量 = 0 : 只是对从器件内部单元地址指针进行设定,不执行数据写入,相当于骚扰一下从器件。
写入数量 = 1 : 只写一个字节数据,只采用中断直接完成的方式。
写入数量 > 1 : 采用DMA方式, 并使用中断进行辅助配合完成。

I2C中断
它只是辅助进行器件寻址, 从器件内部单元地址指针的设定,当读/写数据的数量大于1时,数据的传递主要是使用DMA完成, 这样可以大大减轻CPU的负担, 只是在数据的开始和结束时,才需要CPU的参与(I2C中断,DMA传输完成中断部分),所以大家不要担心会占用太多的CPU资源。




代码下载
I2C_EE_DMA:
STM32F103工程文件:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

阿莫论坛20周年了!感谢大家的支持与爱护!!

如果想吃一顿饺子,就得从冰箱里取出肉,剁馅儿,倒面粉、揉面、醒面,擀成皮儿,下锅……
一整个繁琐流程,就是为了出锅时那一嘴滚烫流油的热饺子。

如果这个过程,禁不住饿,零食下肚了,饺子出锅时也就不香了……《非诚勿扰3》

出0入0汤圆

发表于 2013-10-23 19:34:39 | 显示全部楼层
沙发!
改天有空试试!
谢了

出0入76汤圆

 楼主| 发表于 2013-10-23 19:39:14 | 显示全部楼层
不客气,感谢进行测试...
欢迎讨论.

出0入0汤圆

发表于 2013-10-23 19:45:11 | 显示全部楼层
这个要标记下,好东西,可以省很多事情

出0入24汤圆

发表于 2013-10-23 20:31:21 | 显示全部楼层
前排留名,感谢楼主

出0入0汤圆

发表于 2013-10-23 20:37:36 | 显示全部楼层
楼主可以试试开多个中断,同事进行工作,看看eeprom是否能够正常工作。

出100入101汤圆

发表于 2013-10-23 20:42:53 | 显示全部楼层
“I2C总线受到干扰,从机一直把SDA拉到低电平”,这种情况没法解决的。

出100入101汤圆

发表于 2013-10-23 20:47:03 | 显示全部楼层
i2c- sda挂死分析
I2C是由Philips公司发明的一种串行数据通信协议,仅使用两根信号线:SerialClock(简称SCL)和SerialData(简称SDA)。I2C是总线结构,1个Master,1个或多个Slave,各Slave设备以7位地址区分,地址后面再跟1位读写位,表示读(=1)或者写(=0),所以我们有时也可看到8位形式的设备地址,此时每个设备有读、写两个地址,高7位地址其实是相同的。
I2C数据格式如下:
无数据:SCL=1,SDA=1;
开始位(Start):当SCL=1时,SDA由1向0跳变;
停止位(Stop):当SCL=1时,SDA由0向1跳变;
数据位:当SCL由0向1跳变时,由发送方控制SDA,此时SDA为有效数据,不可随意改变SDA;
当SCL保持为0时,SDA上的数据可随意改变;
地址位:定义同数据位,但只由Master发给Slave;
应答位(ACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=0;
否应答位(NACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=1。
当数据为单字节传送时,格式为:
开始位,8位地址位(含1位读写位),应答,8位数据,应答,停止位。
当数据为一串字节传送时,格式为:
开始位,8位地址位(含1位读写位),应答,8位数据,应答,8位数据,应答,……,8位数据,应答,停止位。
需要注意的是:
1,SCL一直由Master控制,SDA依照数据传送的方向,读数据时由Slave控制SDA,写数据时由Master控制SDA。当8位数据传送完毕之后,应答位或者否应答位的SDA控制权与数据位传送时相反。
2,开始位“Start”和停止位“Stop”,只能由Master来发出。
3,地址的8位传送完毕后,成功配置地址的Slave设备必须发送“ACK”。否则否则一定时间之后Master视为超时,将放弃数据传送,发送“Stop”。
4,当写数据的时候,Master每发送完8个数据位,Slave设备如果还有空间接受下一个字节应该回答“ACK”,Slave设备如果没有空间接受更多的字节应该回答“NACK”,Master当收到“NACK”或者一定时间之后没收到任何数据将视为超时,此时Master放弃数据传送,发送“Stop”。
5,当读数据的时候,Slave设备每发送完8个数据位,如果Master希望继续读下一个字节,Master应该回答“ACK”以提示Slave准备下一个数据,如果Master不希望读取更多字节,Master应该回答“NACK”以提示Slave设备准备接收Stop信号。
6,当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。
在实际应用中,并没有强制规定数据接收方必须对于发送的8位数据做出回应,尤其是在Master和Slave端都是用GPIO软件模拟的方法来实现的情况下,编程者可以事先约定数据传送的长度,slave不检查NACK,有时可以起到减少系统开销的效果。但是如果slave方是硬件i2c要求一定要标准的NACK,master方是GPIO软件模拟i2c并没有正确的发送NACK,就会出现“slave收不到stop”导致i2c挂死。

在正常情况下,I2C总线协议能够保证总线正常的读写操作。但是,当I2C主设备异常复位时(看门狗动作,板上电源异常导致复位芯片动作,手动按钮复位等等)有可能导致I2C总线死锁产生。下面详细说明一下总线死锁产生的原因。
    在I2C主设备进行读写操作的过程中.主设备在开始信号后控制SCL产生8个时钟脉冲,然后拉低SCL信号为低电平,在这个时候,从设备输出应答信号,将SDA信号拉为低电平。如果这个时候主设备异常复位,SCL就会被释放为高电平。此时,如果从设备没有复位,就会继续I2C的应答,将SDA一直拉为低电平,直到SCL变为低电平,才会结束应答信号。而对于I2C主设备来说.复位后检测SCL和SDA信号,如果发现SDA信号为低电平,则会认为I2C总线被占用,会一直等待SCL和SDA信号变为高电平。这样,I2C主设备等待从设备释放SDA信号,而同时I2C从设备又在等待主设备将SCL信号拉低以释放应答信号,两者相互等待,I2C总线进人一种死锁状态。同样,当I2C进行读操作,I2C从设备应答后输出数据,如果在这个时刻I2C主设备异常复位而此时I2C从设备输出的数据位正好为0,也会导致I2C总线进入死锁状态。

方法

    (1)尽量选用带复位输人的I2C从器件。

    (2)将所有的从I2C设备的电源连接在一起,通过MOS管连接到主电源,而MOS管的导通关断由I2C主设备来实现。
    (3)在I2C从设备设计看门狗的功能。

    (4)在I2C主设备中增加I2C总线恢复程序。

        每次I2C主设备复位后,如果检测到SDA数据线被拉低,则控制I2C中的SCL时钟线产生9个时钟脉冲(针对8位数据的情况,“9个clk可以激活”的方法来自NXP的文档,NXP(Philips)作为I2C总线的鼻祖,这样的说法是可信的),这样I2C从设备就可以完成被挂起的读操作,从死锁状态中恢复过来。



        这种方法有很大的局限性,因为大部分主设备的I2C模块由内置的硬件电路来实现,软件并不能够直接控制SCL信号模拟产生需要时钟脉冲。

        或者,发送I2C_Stop条件也能让从设备释放总线。


        如果是GPIO模拟I2C总线实现,那么在I2C操作之前,加入I2C总线状态检测 I2C_Probe ,如果总线被占用,则可尝试恢复总线,待总线释放后,再进行操作。要保证I2C操作最小单元的完整性,不被其他事件(中断、高优先级线程,等)打断。


  (5)在I2C总线上增加一个额外的总线恢复设备。这个设备监视I2C总线。当设备检测到SDA信号被拉低超过指定时间时,就在SCL总线上产生9个时钟脉冲,使I2C从设备完成读操作,从死锁状态上恢复出来。总线恢复设备需要有具有编程功能,一般可以用单片机或CPLD实现这一功能。

  (6)在I2C上串人一个具有死锁恢复的I2C缓冲器,如Linear公司的LTC4307是一个双向的I2C总线缓冲器,并且具有I2C总线死锁恢复的功能。LTC4307总线输入侧连接主设备,总线输出侧连接所有从设备。当LTC4307检测到输出侧SDA或SCL信号被拉低30ms时,就自动断开I2C总线输入侧与输出侧的连接.并且在输出侧SCL信号上产生16个时钟脉冲来释放总线。当总线成功恢复后,LTC4307会再次连接输入输出侧,使总线能够正常工作。

出0入76汤圆

 楼主| 发表于 2013-10-23 21:07:26 | 显示全部楼层
本帖最后由 foxpro2005 于 2013-10-23 21:09 编辑
fengyunyu 发表于 2013-10-23 20:42
“I2C总线受到干扰,从机一直把SDA拉到低电平”,这种情况没法解决的。


这种情况只要不是硬件故障, 是可以解决的...

进行故障的恢复方法:
第1步 :  关闭I2C模块
第2步 :  将 IO配置普通IO, 准备以模拟I2C时序输出。 这里要注意:最好不要输出推挽模式, 防止外部总线故障--比如对GND短路;为OD输出,
              靠外部上拉。
第3步 :  输出时钟(9个), 同时输出START信号 + 8(从机地址) + 看从机应答 否 + STOP信号, 可以判断出从机是否故障了, 如果正常应答, 那么就恢复了。 否则 EEPROM 或 I2C总线上有故障,(可以进行两次确认..)

本来这个准备做的,但这段时间比较忙,  等后续有空再增加。(或者那有同学有空也可进行完善,~ ^|^ ~ ....)

出0入76汤圆

 楼主| 发表于 2013-10-23 21:16:02 | 显示全部楼层
本帖最后由 foxpro2005 于 2013-10-23 21:20 编辑
wuguoyan 发表于 2013-10-23 20:37
楼主可以试试开多个中断,同事进行工作,看看eeprom是否能够正常工作。


I2C过程启动操作的时候 ,优先级:  DMA传送结束 —> 最高, 接着 I2C 错误中断 ,然后再是I2C事件中断, 最后 才是其它的中断,
所以不会有补打断的情况..., 并且I2C的中断不会占用太多的CPU时间去处理, 它只是负责启动,寻址,设定器件内部单元地址, 及等待页写时换页处理, 最后写操作的结束。

或者哪会同学可以再次验证一下...

出100入101汤圆

发表于 2013-10-23 21:29:29 | 显示全部楼层
foxpro2005 发表于 2013-10-23 21:07
这种情况只要不是硬件故障, 是可以解决的...

进行故障的恢复方法:

这个其实很难判断,特别是可以热插拔的设备。因为你不清楚,是设备是被拔掉了,还是设备把SDA总线拉低了。

出0入76汤圆

 楼主| 发表于 2013-10-23 21:35:07 | 显示全部楼层
本帖最后由 foxpro2005 于 2013-10-23 21:36 编辑
fengyunyu 发表于 2013-10-23 21:29
这个其实很难判断,特别是可以热插拔的设备。因为你不清楚,是设备是被拔掉了,还是设备把SDA总线拉低了 ...


I2C热插拔的应该不会用到吧, 并且它不应该被热插拔..., 不能像USB那样....
另外, 你都拔掉了,还想各它通信么...,, 除非你定时寻址它被恢复了没.

出100入101汤圆

发表于 2013-10-23 21:47:55 | 显示全部楼层
foxpro2005 发表于 2013-10-23 21:35
I2C热插拔的应该不会用到吧, 并且它不应该被热插拔..., 不能像USB那样....
另外, 你都拔掉了,还想各 ...

所谓热插拔,就是带电拔下来还可以再插上

出0入0汤圆

发表于 2013-10-23 22:09:37 | 显示全部楼层
I2C挂死原因分析很不错,先MARK下来今晚看

出0入0汤圆

发表于 2013-10-23 22:23:17 | 显示全部楼层
niuB哈哈哈,改天用到在看

出0入0汤圆

发表于 2013-10-23 23:33:15 | 显示全部楼层
标记一下,有空来看下

出0入0汤圆

发表于 2013-10-24 01:02:55 | 显示全部楼层
怎么下载不了?

出0入0汤圆

发表于 2013-10-24 06:09:57 来自手机 | 显示全部楼层
学习,测试一下。

出0入0汤圆

发表于 2013-10-24 08:45:38 | 显示全部楼层
mark一下,留着以后研究

出0入0汤圆

发表于 2013-10-24 08:49:07 | 显示全部楼层
好的
         

出0入0汤圆

发表于 2013-10-24 09:10:10 | 显示全部楼层
用不明白尽量不用!比较难搞!

出0入0汤圆

发表于 2013-10-24 09:17:00 | 显示全部楼层
下来学习。

出0入0汤圆

发表于 2013-10-24 09:18:01 | 显示全部楼层
mark

出0入0汤圆

发表于 2013-10-24 13:22:02 | 显示全部楼层
收下啦

出0入0汤圆

发表于 2013-12-3 16:44:02 | 显示全部楼层
本帖最后由 32MCU 于 2013-12-3 16:46 编辑

STM32的I2C也容易出现i2c- sda挂死!!特别是1个I2C总线上挂有2个I2C器件时。

出0入0汤圆

发表于 2013-12-3 17:16:45 | 显示全部楼层

出0入0汤圆

发表于 2013-12-3 21:38:27 | 显示全部楼层
下载看看

出0入0汤圆

发表于 2013-12-3 21:55:44 | 显示全部楼层
好东西,标记一下~

出0入0汤圆

发表于 2013-12-3 22:45:35 | 显示全部楼层
硬件I2C好像是和SPI、UART有冲突,一般用硬件SPI和UART的时候要多些,I2C都软仿了,除非是用作从设备。

出0入0汤圆

发表于 2013-12-13 18:12:09 | 显示全部楼层
给力啊,有图有代码

出0入0汤圆

发表于 2013-12-13 18:32:01 | 显示全部楼层
学习学习!

出0入0汤圆

发表于 2013-12-15 09:27:51 来自手机 | 显示全部楼层
学习了!!!!!

出0入0汤圆

发表于 2013-12-15 13:09:37 | 显示全部楼层
非常感谢,先收藏了

出0入0汤圆

发表于 2013-12-15 16:56:41 | 显示全部楼层
好东西,收藏了

出0入8汤圆

发表于 2014-4-27 19:14:51 | 显示全部楼层
感谢      

出0入0汤圆

发表于 2014-4-27 21:20:09 | 显示全部楼层
路过路过赞下吧

出0入0汤圆

发表于 2014-4-27 21:58:04 | 显示全部楼层
foxpro2005 发表于 2013-10-23 21:35
I2C热插拔的应该不会用到吧, 并且它不应该被热插拔..., 不能像USB那样....
另外, 你都拔掉了,还想各 ...

I2C热插拔 我在一些比较大点的交换机上看到有用   他的各种功能模块都是支持热插拔的

  
   

出0入76汤圆

 楼主| 发表于 2014-4-27 22:04:30 | 显示全部楼层
LSZD 发表于 2014-4-27 21:58
I2C热插拔 我在一些比较大点的交换机上看到有用   他的各种功能模块都是支持热插拔的

  

好奇一下,他们之间也是用的I2C通信? 还是基于别的总线结构?

出0入26汤圆

发表于 2014-4-27 22:10:30 | 显示全部楼层
记号一个,有个项目需要 正好用得上

出0入0汤圆

发表于 2014-4-27 22:32:23 | 显示全部楼层
foxpro2005 发表于 2014-4-27 22:04
好奇一下,他们之间也是用的I2C通信? 还是基于别的总线结构?

EEPROM 在这种交换机模块上面 仅仅只是  用来做标识  就如内存条 上的那个一样      真正的数据通信多是一些并行的总线接口

I2C总线在交换机上表现出的特点有:

共享式总线,多个设备共享同一总线。极大地节省了PCB布线资源。主从式结构,各从设备由主设备统一进行管理,方便设备驱动程序的编写,适合低速设备(如光模块、小容量EEPROM)等的管理。


光模块  就是激光通信的模块   它里面也有E2PROM  

比如有些交换机的单板模块上共有48个光模块,加上温度传感器、EEPROM等,该单板上共有53个I2C设备!  一根I2C 上能挂多少个EEPROM呢?这可能么?呵呵 当然可能因为可以用CPLD分组控制

出0入76汤圆

 楼主| 发表于 2014-4-27 22:43:28 | 显示全部楼层
LSZD 发表于 2014-4-27 22:32
EEPROM 在这种交换机模块上面 仅仅只是  用来做标识  就如内存条 上的那个一样      真正的数据通信多是 ...

首先说说I2C BUS上可以挂接的设备数量, 理论上可以支持到  2^7个节点。

出0入0汤圆

发表于 2014-4-28 08:20:15 | 显示全部楼层
foxpro2005 发表于 2014-4-27 22:43
首先说说I2C BUS上可以挂接的设备数量, 理论上可以支持到  2^7个节点。

I2C总线的扩展:
使用CPLD给光模块分配时钟。任一时刻只有被选中的光模块有时钟输入。
使用双向三态开关分组隔离48个光模块。有效降低总线负载。


这样就能分批次完成所有的控制了

出0入0汤圆

发表于 2014-4-28 08:44:21 | 显示全部楼层
谢谢分享        

出0入0汤圆

发表于 2014-4-28 22:01:24 | 显示全部楼层
mark以下,谢谢分享

出0入0汤圆

发表于 2014-7-12 15:48:34 | 显示全部楼层
支持一个。

出0入53汤圆

发表于 2014-7-12 16:15:22 | 显示全部楼层
先标记一下用到的时候过来看看

出0入0汤圆

发表于 2014-7-13 23:01:02 | 显示全部楼层
mark..............

出0入0汤圆

发表于 2014-9-4 20:04:37 | 显示全部楼层
这个一定要支持。。。

出0入0汤圆

发表于 2014-9-14 07:13:27 | 显示全部楼层
这个是个好东东啊

出0入0汤圆

发表于 2014-9-14 08:33:56 | 显示全部楼层
谢谢分享

出0入0汤圆

发表于 2014-9-14 23:07:56 | 显示全部楼层
这个必须标记

出0入147汤圆

发表于 2014-9-18 10:18:28 | 显示全部楼层
提个BUG,i2c_err_isr里面只处理了AF和BERR两个标志位,昨天测试人为切断IIC线路后,出现ARLO故障没有处理,导致一直进中断,程序卡死。

出0入0汤圆

发表于 2019-5-28 10:35:04 | 显示全部楼层
谢谢 楼主分享

出0入0汤圆

发表于 2019-6-18 10:18:20 | 显示全部楼层
学到了,谢谢

出0入0汤圆

发表于 2019-8-19 10:50:33 | 显示全部楼层
学习,谢谢!!!

出0入0汤圆

发表于 2019-12-9 22:19:55 来自手机 | 显示全部楼层
感谢分享

出0入0汤圆

发表于 2019-12-9 23:19:30 来自手机 | 显示全部楼层
谢谢 楼主分享

出0入0汤圆

发表于 2021-5-28 21:38:55 | 显示全部楼层
虚心学习

出1000入0汤圆

发表于 2021-5-29 09:03:45 | 显示全部楼层
学习,谢谢分享
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-28 16:43

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表