搜索
bottom↓
回复: 93

再问STM32的硬件I2C与软件模拟

  [复制链接]

出0入0汤圆

发表于 2017-3-2 15:18:29 | 显示全部楼层 |阅读模式
坛里的帖子看了很多,大致了解了STM32的硬件I2C的确有一些BUG,或者说使用时有很多不方便满足的要求,比如DMA加最高中断。所以思考是否使用模拟方式替代。但有种观点是模拟I2C太耗资源。我认真分析了下,其实大部分I2C读写操作在程序中都是顺序类型的,及当读、写I2C从机时,必须读、写到想要的数据才能继续往下执行,如果其中出错,还必须重新来过。所以不管是用硬件查询,中断,DMA等方式还是模拟方式,都必须等待从机完成,所花时间决定与I2C速度和从机响应速度。如果可以这么理解,是否可以说其实模拟方式没有任何风险,速度差不多,故而是最好的选择?

望对这个问题有经验的朋友们不吝赐教!

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

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

 楼主| 发表于 2017-3-2 17:54:26 | 显示全部楼层
有朋友了解这个问题吗?

出0入0汤圆

发表于 2017-3-2 18:13:23 | 显示全部楼层
我用STM32F103按照手册编写了IIC通信,未使用DMA,通信OK呀。

出0入0汤圆

 楼主| 发表于 2017-3-3 00:31:01 | 显示全部楼层
问题主要在使用其它比较频繁发生的高级中断后,I2C会被干扰而发生错误挂起。网上搜搜都是这种问题。

出0入0汤圆

发表于 2017-3-3 08:37:55 | 显示全部楼层
如果你的系统只有个iic功能,那模拟可能不影响。
如果进程多了绝对是大影响。别人都在纠结哪个中断先响应,你却让系统暂停ms级别去读个iic。那相当于你坐个火车,每个站都停2小时,你愿意不?
硬件IIC很好用。用心调式一下就行了。

出0入0汤圆

发表于 2017-3-3 09:30:24 | 显示全部楼层
其硬件应该是没问题的,网上很多人说有问题只不过是使用不当或对其理解不够

出0入0汤圆

 楼主| 发表于 2017-3-3 10:40:12 | 显示全部楼层
ycping 发表于 2017-3-3 08:37
如果你的系统只有个iic功能,那模拟可能不影响。
如果进程多了绝对是大影响。别人都在纠结哪个中断先响应, ...

其实没有停顿呀。这里I2C等待的时间是无法省掉的。比如大多数情况下,读、写I2C一个参数,再根据此参数进行下一步行动,不管用硬件查询、中断、DMA或是模拟I2C,都要等到从器件返回结果后再进行。另外在模拟I2C收发数据时,其他关键中断仍然执行,不影响系统实时性。且在有操作系统时,将模拟I2C中延时函数用系统延时代替,甚至不影响关键任务切换。
不知我这样分析是否可行?

出0入0汤圆

 楼主| 发表于 2017-3-3 10:50:40 | 显示全部楼层
s1j2h3 发表于 2017-3-3 09:30
其硬件应该是没问题的,网上很多人说有问题只不过是使用不当或对其理解不够 ...

多谢回复!STM32的I2C严格讲是兼容性不好,在很多特殊状态下的确出现死锁现象,官方一些辅助文档明确承认并给出了软件解决方案。 F1/F4/F7都存在。我也遇到了。如此多的工程师发现的问题,也不是空穴来风吧。最不解的是官方并没有以标准文档形式提供一个全面指导文件,所以靠工程师自己摸索,往往不能药到病除。这里探讨的也是其中一个解决方案而已。希望朋友们多给意见,谢谢!

出0入0汤圆

 楼主| 发表于 2017-3-3 10:55:30 | 显示全部楼层
mll2015 发表于 2017-3-2 18:13
我用STM32F103按照手册编写了IIC通信,未使用DMA,通信OK呀。


问题主要在使用其它比较频繁发生的高级中断后,I2C会被干扰而发生错误挂起。网上搜搜都是这种问题。
如果不开高级中断,硬件I2C肯定是没问题的, 毕竟是最基本的外设呀。

出0入0汤圆

发表于 2017-3-3 12:30:38 | 显示全部楼层
szszjdb 发表于 2017-3-3 10:40
其实没有停顿呀。这里I2C等待的时间是无法省掉的。比如大多数情况下,读、写I2C一个参数,再根据此参数进 ...

你的思维还停留在学习板的阶段。

出0入0汤圆

 楼主| 发表于 2017-3-3 13:13:52 | 显示全部楼层
ycping 发表于 2017-3-3 12:30
你的思维还停留在学习板的阶段。

的确没什么经验。还望兄弟明示! 多谢!

出0入0汤圆

发表于 2017-3-3 17:42:32 | 显示全部楼层
我对硬件iic已经无爱了,自从因为硬件iic导致固定翼炸鸡,现在一直用模拟。

出0入0汤圆

 楼主| 发表于 2017-3-3 17:47:27 | 显示全部楼层
这个的确是一个很难搞得事,没有一个官方的系统解决方案,难以避免。

出0入0汤圆

发表于 2017-3-3 17:53:34 | 显示全部楼层
用的模拟,还没用过硬件的。

出0入0汤圆

 楼主| 发表于 2017-3-3 20:42:23 | 显示全部楼层
Zwiic 发表于 2017-3-3 17:53
用的模拟,还没用过硬件的。

希望更多对硬件I2C了解的朋友给些意见! 多谢大家了!

出0入0汤圆

发表于 2017-3-3 22:17:44 | 显示全部楼层
建议去看一下STM32系列的芯片缺陷文档,里面有详细的应对或者是绕开的措施。

出0入0汤圆

 楼主| 发表于 2017-3-4 10:55:27 | 显示全部楼层
maxwell_lee 发表于 2017-3-3 22:17
建议去看一下STM32系列的芯片缺陷文档,里面有详细的应对或者是绕开的措施。 ...

文档早看过,很多朋友几年之前也研究过了,ST的文档并没有公开承认此问题,如图。STM32众多外设, 唯I2C被诟病之多,实在不是被黑呀!
本帖目的不是接力吐槽,而是希望找到一个较好的方法去使用I2C。望知者直言,谢谢!

本帖子中包含更多资源

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

x

出0入0汤圆

发表于 2017-3-4 11:44:27 | 显示全部楼层
ST的硬件I2C我调通过,没有使用I2C中断,而且其他中断也比较少,而且I2C使用的是查询来实现通信的!!!!那么ST的问题就是硬件I2C很容易出现一些莫名其妙的问题,比如用手触碰一下通信总线,基本就死了,而且死的现象是,某一个标志位寄存器和总线的现象不符合,举个例子,比如忙标志位置位,但是总线却都是高电压。。。。要么重启硬件I2C,但是有时候并不一定会解决问题,还有就是同一个程序,在不同的MCU表现不太一样....还有就是同一个MCU今天行,完全没问题,说不定明天就又不行,不行的现象就是查询某些标志位的时候,和总线产生的现象不符合.....所以最后用模拟的,稳定,安全!!!

出0入0汤圆

 楼主| 发表于 2017-3-4 14:05:46 | 显示全部楼层
OneRain 发表于 2017-3-4 11:44
ST的硬件I2C我调通过,没有使用I2C中断,而且其他中断也比较少,而且I2C使用的是查询来实现通信的!!!! ...

多谢兄弟直言! 的确如此,您说的现象真正用过硬件I2C的朋友都发现了,目前好多解决方案就是复位I2C模块,带来程序的复杂和时间的耽搁。我在本帖开头提出的对软件模拟I2C并不真正延迟程序执行,降低效率之看法,不知大家是否赞同?希望对此有更深入讨论,谢谢!

出0入0汤圆

发表于 2017-3-6 09:36:06 | 显示全部楼层
szszjdb 发表于 2017-3-3 10:55
问题主要在使用其它比较频繁发生的高级中断后,I2C会被干扰而发生错误挂起。网上搜搜都是这种问题。
如 ...

这样啊,学习了,目前还没涉及复杂系统,所以没遇到相关问题,看来还要继续努力啊

出0入0汤圆

 楼主| 发表于 2017-3-6 09:55:37 | 显示全部楼层
mll2015 发表于 2017-3-6 09:36
这样啊,学习了,目前还没涉及复杂系统,所以没遇到相关问题,看来还要继续努力啊  ...

大家多交流。有些坑真的是掉进去才知道,这里吼吼,希望大家帮忙一起把咱们拽上来,谢谢了!

出0入0汤圆

发表于 2017-3-7 07:49:12 | 显示全部楼层
硬件IC2的确很头疼,我也为此调了很久,也纠结了很长一段时间,到底是软件模拟好呢,还是硬件I2C好呢,最后对他研究更深入后,发现硬件I2C用DMA方式读写基本不会出错,很稳定,当然这种用在还有其他外设需要频繁读写的场合(比如串口及SPI),软件模拟还是用在其他外设操作少或对时间要求不高的场合,可以等软件I2C读写完成再去干其他的事。目前,这两种操作I2C的方式我都会用在项目中,根据实际情况做选择。

出0入0汤圆

发表于 2017-3-7 09:13:53 | 显示全部楼层
szszjdb 发表于 2017-3-3 10:50
多谢回复!STM32的I2C严格讲是兼容性不好,在很多特殊状态下的确出现死锁现象,官方一些辅助文档明确承认 ...

硬件是stm32L100+实时时钟rx8010,采用硬件IIC通信,跑freertos,间隔2s读一次时间,发现个别终端跑个几天就会死机,导致看门狗复位。后面改为模拟IIC就没再出现问题。不知道复位是因为软件问题,还是IIC硬件问题?请赐教

出0入0汤圆

发表于 2017-3-7 09:23:45 | 显示全部楼层
一直在用模拟的IIC 什么温湿度SHT20,RTC时钟PCF8266.......

在我开始用IIC的时候,同事就说stm32硬件IIC千万别用,但也说不出个所以然,我也很好奇这个问题。

出0入0汤圆

 楼主| 发表于 2017-3-7 11:33:32 | 显示全部楼层
liaoze22 发表于 2017-3-7 07:49
硬件IC2的确很头疼,我也为此调了很久,也纠结了很长一段时间,到底是软件模拟好呢,还是硬件I2C好呢,最后 ...

多谢兄弟的建议。如果非要用硬件,DMA应该是不错的选择。 我只是觉得对大部分I2C应用场合,都需要从机应答再决定下一步程序分支,所以即便使用DMA也需要等待I2C从机完成响应。因此DMA并不能节省CPU资源,仅仅是能够避免I2C出错。 不知在下的看法是否正确,请不吝赐教,谢谢!

出0入0汤圆

 楼主| 发表于 2017-3-7 11:37:05 | 显示全部楼层
syj0925 发表于 2017-3-7 09:13
硬件是stm32L100+实时时钟rx8010,采用硬件IIC通信,跑freertos,间隔2s读一次时间,发现个别终端跑个几 ...

大家都反应是I2C本身的问题,兼容性不好,易出错。这是ST家独有的问题。

出0入0汤圆

发表于 2017-3-7 11:49:43 | 显示全部楼层
我用的是模拟的,可移植性非常高。IIC有时钟先,属于同步传输,所以时钟快点慢点哪怕被中断暂停了也不影响通信完整性,所以用模拟的在效率上并不会降低很多,反而在灵活性上要好很多。如果是模拟串口的话就比较麻烦了,通信中不能被打断,效率和可靠性都很差。

出0入0汤圆

 楼主| 发表于 2017-3-7 13:08:55 | 显示全部楼层
科技猎人 发表于 2017-3-7 11:49
我用的是模拟的,可移植性非常高。IIC有时钟先,属于同步传输,所以时钟快点慢点哪怕被中断暂停了也不影响 ...

没错,不怕中断,这是最大优点。 只是如果用操作系统,在I2C进行时不能进行任务切换,稍微影响实时性,对吧?

出0入0汤圆

发表于 2017-3-7 14:56:51 | 显示全部楼层
szszjdb 发表于 2017-3-7 13:08
没错,不怕中断,这是最大优点。 只是如果用操作系统,在I2C进行时不能进行任务切换,稍微影响实时性,对 ...

用硬件IIC也不能中途切换任务吧

出0入0汤圆

发表于 2017-3-8 09:58:48 | 显示全部楼层
我告诉你,stm32的IIC没有问题。只不过使用稍微麻烦一点,还有必须是I2C+DMA,中断不必最高优先级,还有I2C+dma时和任务没有关系,中间也可以被打断,至于为什么可以被打断,可以试想数据发送以后ACK是由硬件检测,产生一个事件中断,所以这个ACK已经被硬件检测到,在中断进行下一个I2C字节发送(或者其他)的操作,只要能检测到这个ack,这个中间就可以没打断,我亲自测试过。在I2C事件中断中打断点,一个一个字节发送完全可以执行。还有就是要处理好I2C错误中断和错误发生时I2C总线电平状态(很重要),以防万一。

出0入0汤圆

 楼主| 发表于 2017-3-8 10:17:19 | 显示全部楼层
godsend 发表于 2017-3-8 09:58
我告诉你,stm32的IIC没有问题。只不过使用稍微麻烦一点,还有必须是I2C+DMA,中断不必最高优先级,还有I2C+ ...

多谢,同意您的看法! 不少朋友也是用DMA。我这里提到的观点就是用硬件I2C加DMA,不见得比模拟I2C节省资源,两者CPU都要实际等待I2C从机完成应答,这个看法正确吗?

出0入0汤圆

 楼主| 发表于 2017-3-8 10:18:40 | 显示全部楼层
科技猎人 发表于 2017-3-7 14:56
用硬件IIC也不能中途切换任务吧

好像是的。至少查询方式的硬件是不行的,其他两种都可以吧。

出0入0汤圆

发表于 2017-3-8 10:24:20 | 显示全部楼层
szszjdb 发表于 2017-3-8 10:17
多谢,同意您的看法! 不少朋友也是用DMA。我这里提到的观点就是用硬件I2C加DMA,不见得比模拟I2C节省资 ...

等待ACK?为什么要等待?这个是中断干的事。。。总线产生ACK,中断自己检测,又不要CPU参与。

出0入0汤圆

发表于 2017-3-8 11:30:58 | 显示全部楼层
这个帖子说很详细,点赞,我现在也是基本用模式i2c方式做得更多,了解上面信息后硬件ic2后期用上,谢谢上面各位大牛

出0入0汤圆

 楼主| 发表于 2017-3-8 13:04:16 | 显示全部楼层
godsend 发表于 2017-3-8 10:24
等待ACK?为什么要等待?这个是中断干的事。。。总线产生ACK,中断自己检测,又不要CPU参与。 ...

这里不是说ACK和NACK,而是当操作I2C时,往往都是关键数据,是必须确认已经成功了才能继续下一步操作的。比如上电时从I2C读回设置参数,必须读了,且读到了,才能进行下一步处理。DMA只是过程中不用程序关注,但结果还是要等的。所以才推断硬件和模拟I2C其实效能差不多这个结论。

出0入0汤圆

发表于 2017-3-10 08:47:02 | 显示全部楼层
szszjdb 发表于 2017-3-8 13:04
这里不是说ACK和NACK,而是当操作I2C时,往往都是关键数据,是必须确认已经成功了才能继续下一步操作的。 ...

发送和接收数据都是有DMA完成,我不知道你说的CPU在等什么?你管他完成没有,反正从机接收会给个ACK,I2C事件中断去检测ACK.

出0入0汤圆

发表于 2017-3-10 08:48:38 | 显示全部楼层
szszjdb 发表于 2017-3-8 13:04
这里不是说ACK和NACK,而是当操作I2C时,往往都是关键数据,是必须确认已经成功了才能继续下一步操作的。 ...

数据是否发送完成也是DMA干的事,CPU操的哪门子心。

出0入0汤圆

发表于 2017-3-10 08:54:32 | 显示全部楼层
szszjdb 发表于 2017-3-8 13:04
这里不是说ACK和NACK,而是当操作I2C时,往往都是关键数据,是必须确认已经成功了才能继续下一步操作的。 ...

你说的这种情况,如果DMA读不到,模拟也肯定读不到,我相信dma时序严格性远比模拟要好,数据是否被读到,已经不是I2C的问题了。

出0入0汤圆

发表于 2017-3-10 08:58:40 | 显示全部楼层
godsend 发表于 2017-3-10 08:54
你说的这种情况,如果DMA读不到,模拟也肯定读不到,我相信dma时序严格性远比模拟要好,数据是否被读到, ...

模拟在操作I2C时cpu只能干等,中断+dma操作过程中,cpu少许干预,其他时间可以干别的事。你说哪个效率好点?

出0入0汤圆

发表于 2017-3-10 09:13:07 | 显示全部楼层
godsend 发表于 2017-3-10 08:58
模拟在操作I2C时cpu只能干等,中断+dma操作过程中,cpu少许干预,其他时间可以干别的事。你说哪个效率好 ...

不需要提高优先级吗?请教一下,老兄对这个观点怎么看?
http://www.amobbs.com/thread-5615257-1-1.html

出0入0汤圆

发表于 2017-3-10 09:40:36 | 显示全部楼层
int 发表于 2017-3-10 09:13
不需要提高优先级吗?请教一下,老兄对这个观点怎么看?
http://www.amobbs.com/thread-5615257-1-1.html ...

我在i2c事件中断打断点也可以执行。

出0入0汤圆

发表于 2017-3-10 09:42:41 | 显示全部楼层
一直用的软件IIC     学习一下

出0入0汤圆

发表于 2017-3-10 09:56:10 | 显示全部楼层
godsend 发表于 2017-3-10 09:40
我在i2c事件中断打断点也可以执行。

老兄方便放一个工程吗?

出0入0汤圆

发表于 2017-3-10 10:05:20 | 显示全部楼层
int 发表于 2017-3-10 09:56
老兄方便放一个工程吗?

晚上回去贴出来

出0入0汤圆

发表于 2017-3-10 10:33:42 | 显示全部楼层
本帖最后由 laoshuhunya 于 2017-3-10 10:37 编辑
szszjdb 发表于 2017-3-8 10:18
好像是的。至少查询方式的硬件是不行的,其他两种都可以吧。


   不管是硬件还是软件模拟,都可以中途切换任务。你可以把切换任务看成执行时间较长的中断,要注意的是有些I2C器件有等待时间限制。
   题外话:我们测试过AT24C02和SST25VF020B,在RTOS中开4个同优先级任务,第一个任务点灯,第二个任务做浮点运算,第三个任务运行模拟I2C反复读写AT24C02的00H地址单元,第四个任务运行模拟SPI反复读写SST25VF020B的00H地址单元(写入之前执行整片擦除)。任务3和4中,访问AT24C02和SST25VF020B的任何错误都会设置一个错误标志,任务1检测到这个错误后会停止LED闪烁并保持常亮。软件每秒钟读写一次AT24C02和SST25VF020B,连续运行两天两夜没出现任何错误,第三天开始出现频繁错误(大约每10次读写就有一次错误),10几个小时后又逐渐恢复正常,但之后又开始频繁出错,每10次有2、3次错误。之后,我们修改读写SST25VF020B的单元地址为01H,重新测试8小时完全正常,再把读写地址改回00H,立即出现频繁出错,这说明SST25VF020B的00H单元已经达到擦写寿命。

出0入0汤圆

发表于 2017-3-10 13:52:08 | 显示全部楼层
网上找过几个STM32硬件iic的例程,不能正常运行,或者运行几下就挂了。

后来仔细分析协议,发现还是写例程的写的有bug导致的出错。稍微改改,就能用了。

稳定运行了好几个月没再出过问题。

出0入0汤圆

发表于 2017-3-10 14:13:48 | 显示全部楼层
godsend 发表于 2017-3-10 10:05
晚上回去贴出来

好的,谢谢老哥!

出0入8汤圆

发表于 2017-3-10 14:43:03 | 显示全部楼层
szszjdb 发表于 2017-3-7 13:08
没错,不怕中断,这是最大优点。 只是如果用操作系统,在I2C进行时不能进行任务切换,稍微影响实时性,对 ...

应该是在 I2C 通讯的时间内,不要去主动进行任务切换,这时间太小,不要主动加大系统负担。
而被动被剥夺,还是允许的。
关于 I2C 模拟的一些讨论,可以看这个帖子,我在里面给出了一些回复,就不再重复打字了。
有兴趣,戳这里:「uCOSIII 中能否建立一个IO模拟IIC的任务

出0入0汤圆

发表于 2017-3-11 09:04:12 | 显示全部楼层
本帖最后由 godsend 于 2017-3-11 09:09 编辑
int 发表于 2017-3-10 14:13
好的,谢谢老哥!


这块我删除了,还没编辑完成不小心就发送了。然后恢复了一下不知道怎么的就成了两个回复。

出0入0汤圆

发表于 2017-3-11 09:04:47 | 显示全部楼层
本帖最后由 godsend 于 2017-3-11 09:06 编辑
int 发表于 2017-3-10 14:13
好的,谢谢老哥!


uint8_t I2C1_TransferFlag = 0;
uint8_t I2C1_Step = 0;
uint8_t I2C1_Mode = 0;
uint8_t I2C1_DeviceAddress = 0;
uint8_t I2C1_RegisterAddress = 0;
uint8_t I2C1_DeviceMeneyType = 0;
uint8_t I2C1_DataLength = 0;
uint8_t I2C1_RxBuff[I2C1_RxMax];
uint8_t I2C1_TxBuff[I2C1_TxMax];


void I2C1_InitStructure(void)
{
  I2C_InitTypeDef I2C1_InitSturcture;
  DMA_InitTypeDef DMA_InitStrecture;
  
  I2C_DeInit(I2C1);
  DMA_DeInit(DMA1_Stream0);
  DMA_DeInit(DMA1_Stream6);

  I2C1_InitSturcture.I2C_ClockSpeed = 100000;
  I2C1_InitSturcture.I2C_Mode = I2C_Mode_I2C;
  I2C1_InitSturcture.I2C_DutyCycle = I2C_DutyCycle_2;
  I2C1_InitSturcture.I2C_OwnAddress1 = 0x00;
  I2C1_InitSturcture.I2C_Ack = I2C_Ack_Enable;
  I2C1_InitSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

  I2C_ITConfig(I2C1, I2C_IT_EVT|I2C_IT_ERR, ENABLE);
  I2C_Init(I2C1, &I2C1_InitSturcture);
  I2C_DMACmd(I2C1, ENABLE);
  I2C_Cmd(I2C1, ENABLE);
  
  DMA_InitStrecture.DMA_Channel = DMA_Channel_1;//I2C1_RX
  DMA_InitStrecture.DMA_PeripheralBaseAddr = (uint32_t)(&(I2C1->DR));
  DMA_InitStrecture.DMA_Memory0BaseAddr = (uint32_t)I2C1_RxBuff;
  DMA_InitStrecture.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStrecture.DMA_BufferSize = I2C1_RxMax;
  DMA_InitStrecture.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStrecture.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStrecture.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStrecture.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStrecture.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStrecture.DMA_Priority = DMA_Priority_High;
  DMA_InitStrecture.DMA_FIFOMode = DMA_FIFOMode_Disable;
  DMA_InitStrecture.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStrecture.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStrecture.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  
  //DMA_ITConfig(DMA1_Stream0, DMA_IT_TC, ENABLE);
  DMA_ITConfig(DMA1_Stream0, DMA_IT_HT, ENABLE);
  DMA_Init(DMA1_Stream0, &DMA_InitStrecture);
  DMA_Cmd(DMA1_Stream0, DISABLE);  
}


void I2C1_SendData(uint8_t DeviceAddress, uint16_t RegisterAddress, uint8_t DeviceMeneyType, uint8_t DataLength)
{
  if(!I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
  {
    I2C1_TransferFlag = 1;
   
    I2C_ClearFlag(I2C1, I2C_FLAG_BUSY);
    I2C_ClearFlag(I2C1, I2C_FLAG_BERR);

    I2C1_Step = 0;
    I2C1_Mode = I2C1_WriteMode;
    I2C1_DeviceAddress = DeviceAddress;
    I2C1_RegisterAddress = RegisterAddress;
    I2C1_DataLength = DataLength;
   
    I2C_AcknowledgeConfig(I2C1, ENABLE);
    I2C_GenerateSTART(I2C1, ENABLE);
  }
}


void I2C1_ReceiveData(uint8_t DeviceAddress, uint16_t RegisterAddress, uint8_t DeviceMeneyType, uint8_t DataLength)
{
  if(!I2C1_TransferFlag)
  {
    if(!I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
    {
      I2C1_TransferFlag = 1;
      
      I2C1_Step = 0;
      I2C1_Mode = I2C1_ReadMode;
      I2C1_DeviceAddress = DeviceAddress;
      I2C1_RegisterAddress = RegisterAddress;
      I2C1_DeviceMeneyType = DeviceMeneyType;
      I2C1_DataLength = DataLength;
      
      I2C_ReceiveData(I2C1);
      I2C_AcknowledgeConfig(I2C1, ENABLE);
      I2C_ITConfig(I2C1, I2C_IT_EVT|I2C_IT_ERR, ENABLE);
      I2C_GenerateSTART(I2C1, ENABLE);
    }
  }
}



void I2C1_EV_IRQHandler(void)
{
  LastEven = I2C_GetLastEvent(I2C1);
  if(I2C1_Mode == I2C1_ReadMode)
  {
    switch(LastEven)
    {
      case I2C_EVENT_MASTER_MODE_SELECT:
           if(I2C1_Step == 0)
           {
            I2C_Send7bitAddress(I2C1, I2C1_DeviceAddress, I2C_Direction_Transmitter);
           }
           
           if(I2C1_DeviceMeneyType == I2C1_MeneyType8)
           {
             if(I2C1_Step == 4)
             {
              I2C_Send7bitAddress(I2C1, I2C1_DeviceAddress, I2C_Direction_Receiver);
             }
           }
           else
           {
             if(I2C1_Step == 5)
             {
              I2C_Send7bitAddress(I2C1, I2C1_DeviceAddress, I2C_Direction_Receiver);
             }
           }
           
           I2C1_Step++;
           break;
      case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED:
           I2C_SendData(I2C1, ((uint8_t) (I2C1_RegisterAddress)));
           I2C1_Step++;
           break;
      case I2C_EVENT_MASTER_BYTE_TRANSMITTED:
           if(I2C1_DeviceMeneyType == I2C1_MeneyType8)
           {
             if(I2C1_Step == 3)
             {
              I2C_GenerateSTART(I2C1, ENABLE);
             }
             else
             {
              //I2C_SendData(I2C1, RegisterAddress);
             }
           }
           else
           {
            if(I2C1_Step == 3)
            {
              I2C_SendData(I2C1, ((uint8_t) (I2C1_RegisterAddress >> 8)));
            }
        
            if(I2C1_Step == 4)
            {
              I2C_GenerateSTART(I2C1, ENABLE);
            }
           I2C1_Step++;
           break;
      case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED:
           DMA_ClearITPendingBit(DMA1_Stream0, DMA_IT_HTIF0);
           DMA_ClearITPendingBit(DMA1_Stream0, DMA_IT_TCIF0);
           DMA_ITConfig(DMA1_Stream0, DMA_IT_HT, ENABLE);
           DMA_ITConfig(DMA1_Stream0, DMA_IT_TC, ENABLE);
           DMA_SetCurrDataCounter(DMA1_Stream0, I2C1_DataLength*2);
           DMA_Cmd(DMA1_Stream0, ENABLE);
           I2C_ITConfig(I2C1, I2C_IT_EVT, DISABLE);
           I2C1_Step++;
           break;
      default:I2C1_Step = 0; break;
      }
    }
   }
  
  if(I2C1_Mode == I2C1_WriteMode)
  {
  
  }
}



void I2C1_ER_IRQHandler(void)
{
  if(I2C_GetITStatus(I2C1, I2C_IT_TIMEOUT))
  {
    I2C_ClearITPendingBit(I2C1, I2C_IT_TIMEOUT);
  }
  
  if(I2C_GetITStatus(I2C1, I2C_IT_PECERR))
  {
    I2C_ClearITPendingBit(I2C1, I2C_IT_PECERR);
  }
  
  if(I2C_GetITStatus(I2C1, I2C_IT_AF))
  {
    I2C_ClearITPendingBit(I2C1, I2C_IT_AF);
  }
  
  if(I2C_GetITStatus(I2C1, I2C_IT_BERR))
  {
    I2C_ClearITPendingBit(I2C1, I2C_IT_BERR);
  }
  I2C_GenerateSTOP(I2C1, ENABLE);
}




程序就是这个样子,只写了8位地址,16位的没有写。你看看有什么不对的地方还请支出共同学习。

出0入0汤圆

发表于 2017-3-11 15:01:06 | 显示全部楼层
godsend 发表于 2017-3-11 09:04
uint8_t I2C1_TransferFlag = 0;
uint8_t I2C1_Step = 0;
uint8_t I2C1_Mode = 0;

谢谢。DMA中断应该还需要设置标志吧。

出0入0汤圆

发表于 2017-3-11 15:38:19 | 显示全部楼层
int 发表于 2017-3-11 15:01
谢谢。DMA中断应该还需要设置标志吧。

我没有使用DMA中断,发送之前使用I2C_BUSY来检查总线

出0入0汤圆

 楼主| 发表于 2017-3-11 16:45:35 | 显示全部楼层
security 发表于 2017-3-10 14:43
应该是在 I2C 通讯的时间内,不要去主动进行任务切换,这时间太小,不要主动加大系统负担。
而被动被剥夺 ...

看了您推荐的链接,讲的非常清楚,多谢!  我这里提出的观点是用模拟方式相比硬件方式,对系统效率的影响不是很大,两者都要等待I2C从机将数据返回。虽然DMA不用傻等,但在等的时候也不好安排别的任务。不知您看法如何? 另外,如果STM32用系统, 您觉得最好的方式是哪种?  多谢!

出0入0汤圆

 楼主| 发表于 2017-3-11 16:52:24 | 显示全部楼层
laoshuhunya 发表于 2017-3-10 10:33
不管是硬件还是软件模拟,都可以中途切换任务。你可以把切换任务看成执行时间较长的中断,要注意的是 ...

多谢您的例子。EEPROM没有写坏吧? 还有,你们评估在使用、或不使用系统情况下,模拟与硬件方式,效率差别很大吗?

出0入0汤圆

 楼主| 发表于 2017-3-11 16:53:46 | 显示全部楼层
cpholr1 发表于 2017-3-10 13:52
网上找过几个STM32硬件iic的例程,不能正常运行,或者运行几下就挂了。

后来仔细分析协议,发现还是写例程 ...

你没有使用中断吧,一旦有中断,I2C就容易出问题了。

出0入0汤圆

发表于 2017-3-11 21:50:02 | 显示全部楼层
godsend 发表于 2017-3-11 15:38
我没有使用DMA中断,发送之前使用I2C_BUSY来检查总线

那调用I2C1_ReceiveData时,怎么判断什么时候接受完成了呢?

出0入0汤圆

发表于 2017-3-11 22:45:03 来自手机 | 显示全部楼层
stm32的iic不好使,atmel和nxp的倒是做的很好,所以stm32我都用模拟方式或者查询法,有什么错误直接忽略。

出0入0汤圆

发表于 2017-3-12 18:35:14 | 显示全部楼层
int 发表于 2017-3-11 21:50
那调用I2C1_ReceiveData时,怎么判断什么时候接受完成了呢?

这函数在这里并不是接收I2C数据的作用,而是读取一次清空RX_BUFF的内容,防止接收数据不更新。

出0入0汤圆

发表于 2017-3-12 20:13:50 | 显示全部楼层
godsend 发表于 2017-3-12 18:35
这函数在这里并不是接收I2C数据的作用,而是读取一次清空RX_BUFF的内容,防止接收数据不更新。 ...

哦哦这样

出0入8汤圆

发表于 2017-3-13 09:56:05 | 显示全部楼层
本帖最后由 security 于 2017-3-13 09:57 编辑
szszjdb 发表于 2017-3-11 16:45
看了您推荐的链接,讲的非常清楚,多谢!  我这里提出的观点是用模拟方式相比硬件方式,对系统效率的影响 ...


这要分场合:
  - 对于你的应用场景,整个软件逻辑流程,需要多次的 Master 和 Slave 的交互,一般这个软件逻辑流程都设计为同步阻塞模型,所以从这个角度来看, CPU 的利用率,没太大差异。恰恰这也是 I2C 总线使用最频繁的应用场景。
  - 如果对于批量数据传输的话,那么 DMA 的优势就体现出来了,这其中的道理,我想你应该知道。
我没用过 STM32,但从理论上讲,具备硬件模块的,我会优先选择硬件。

出0入0汤圆

发表于 2017-3-13 23:53:48 | 显示全部楼层
很有参考价值的帖子,有时间也试试硬件I2C

出0入0汤圆

发表于 2017-3-14 17:28:23 | 显示全部楼层
用CubeMX自动生成的I2C代码也是可以用的。中断会自动设在最高优先级。

出0入0汤圆

 楼主| 发表于 2017-3-16 10:11:24 | 显示全部楼层
security 发表于 2017-3-13 09:56
这要分场合:
  - 对于你的应用场景,整个软件逻辑流程,需要多次的 Master 和 Slave 的交互,一般这个 ...

您分析的很清楚,多谢! 可以说在很多情况下,模拟I2C是一个不错的选项。

出0入0汤圆

 楼主| 发表于 2017-3-16 10:20:57 | 显示全部楼层
Azuresky 发表于 2017-3-14 17:28
用CubeMX自动生成的I2C代码也是可以用的。中断会自动设在最高优先级。

这个最高优先级不太好吧,I2C不是高速设备,往往执行的慢,如果其他关键任务经常被其打断,效率不高吧。而且 I2C操作的程序逻辑通常还是要等待读、写完成,才能继续,中断并不能消除等待时间。当然这可以避免硬件出错,也是不错的选项。

出0入0汤圆

发表于 2017-3-16 17:25:34 | 显示全部楼层
szszjdb 发表于 2017-3-16 10:20
这个最高优先级不太好吧,I2C不是高速设备,往往执行的慢,如果其他关键任务经常被其打断,效率不高吧。 ...

你并没有理解DMA,还是先好好理解DMA,就知道你一直纠结的问题在哪了。

出0入0汤圆

发表于 2017-3-16 17:37:56 | 显示全部楼层
STM32 IIC用模拟的也方便

出0入0汤圆

发表于 2017-3-16 23:38:45 | 显示全部楼层
szszjdb 发表于 2017-3-16 10:20
这个最高优先级不太好吧,I2C不是高速设备,往往执行的慢,如果其他关键任务经常被其打断,效率不高吧。 ...

我有印象,I2C不稳定的官方解决办法就是将中断设置在最高级,否则就是等死。
我采集视频信号,I2C中断也是在最高级,没感觉到影响。其实是看到别人的不稳定问题以后,去看自己的源码,才发现中断是在最高级。
I2C中断响应时间和中断处理的时间都是可知的,你可以计算一下,无法接受就不能用硬件I2C,就去用软件模拟吧。

出0入0汤圆

 楼主| 发表于 2017-3-27 11:06:08 | 显示全部楼层
godsend 发表于 2017-3-16 17:25
你并没有理解DMA,还是先好好理解DMA,就知道你一直纠结的问题在哪了。

据我的理解,I2C的DMA中断主要是完成中断,当然此中断的时间很短,的确不会有很明显的系统开销。主要是涉及I2C操作的程序不好安排,大部分此类操作的前后顺序都有要求,启动DMA后,还是得用各种方式去等待操作从机完成,不知我理解是否正确。兄弟经验丰富,唯愿闻其详!

出0入0汤圆

 楼主| 发表于 2017-3-27 11:54:44 | 显示全部楼层
Azuresky 发表于 2017-3-16 23:38
我有印象,I2C不稳定的官方解决办法就是将中断设置在最高级,否则就是等死。
我采集视频信号,I2C中断也 ...

看来要用硬件I2C,只有如此了。只是我的I2C操作EEPROM, 批量写入后还要读回校验无误,才写下面内容,所以启动DMA后,还是要等DMA中断完成, 不知如何能巧妙安排程序,能够利用这段等待时间。时间其实不算短,一次操作有几个毫秒。希望听听你的建议,多谢!

出0入0汤圆

发表于 2017-3-28 08:53:31 | 显示全部楼层
szszjdb 发表于 2017-3-27 11:06
据我的理解,I2C的DMA中断主要是完成中断,当然此中断的时间很短,的确不会有很明显的系统开销。主要是涉 ...

只需要给个标志位去检测标志位即可(stop的时候),在dma发送时cpu可以去干别的事。为什么非要死等。

出0入0汤圆

发表于 2017-3-28 08:57:58 | 显示全部楼层
本帖最后由 godsend 于 2017-3-28 09:00 编辑
szszjdb 发表于 2017-3-27 11:06
据我的理解,I2C的DMA中断主要是完成中断,当然此中断的时间很短,的确不会有很明显的系统开销。主要是涉 ...


还有一点你要明确,启用的中断并不是DMA中断,是i2c的事件中断,在事件中断检测发生的事件再做出处理,当然不要忘记错误中断,处理好I2C总线错误可以避免死机事件发生。程序我上面已经给出了,你可以自己理解一下。

出0入0汤圆

 楼主| 发表于 2017-3-28 10:46:07 | 显示全部楼层
godsend 发表于 2017-3-28 08:57
还有一点你要明确,启用的中断并不是DMA中断,是i2c的事件中断,在事件中断检测发生的事件再做出处理,当 ...

多谢! DMA完成中断前,的确可以干别的事,但实际上因为程序顺序逻辑的问题,这个检测标志并进一步进行I2C操作的功能函数还并不好安排。比如发起DMA后,先执行别的函数,但执行到何处再进行DMA完成标志判断,并且还要继续I2C剩下的操作等等,在实际操作时并不好操作。所以包括ST官方很多例子,不限于I2C DMA, 在DMA完成前,也是仅仅原地延时查询完成标志,并没有干别的事。这是我在使用DMA时觉得比较棘手的地方。不知兄弟怎么考虑?

出0入0汤圆

发表于 2017-3-28 11:08:02 | 显示全部楼层
楼主可以用GD32的硬件IIC试试,哈哈

出0入0汤圆

发表于 2017-3-28 11:54:24 | 显示全部楼层
我之前用硬件的IIC试过,是可以的,只不过发送数据不能太快了。其实模拟的也可以,只是相对于会浪费一些时间。其实那么一点时间也干不了多大的事情;

出0入0汤圆

发表于 2017-3-28 16:41:35 | 显示全部楼层
szszjdb 发表于 2017-3-28 10:46
多谢! DMA完成中断前,的确可以干别的事,但实际上因为程序顺序逻辑的问题,这个检测标志并进一步进行I2 ...

上操作系统!

出0入0汤圆

发表于 2017-3-29 06:50:52 | 显示全部楼层
本帖最后由 Azuresky 于 2017-3-29 06:54 编辑
szszjdb 发表于 2017-3-27 11:54
看来要用硬件I2C,只有如此了。只是我的I2C操作EEPROM, 批量写入后还要读回校验无误,才写下面内容,所以 ...


看来,问题的关键不在于I2C通讯,而是你需要等待EEPROM做出反应。
如果其它任务不紧急,就等等吧。
如果和紧急任务同时执行,死等EEPROM肯定是不行的。一个简单的办法是使用操作系统,单独开个进程处理EEPROM的读写。

出0入8汤圆

发表于 2017-3-29 08:26:16 | 显示全部楼层
Azuresky 发表于 2017-3-29 06:50
看来,问题的关键不在于I2C通讯,而是你需要等待EEPROM做出反应。
如果其它任务不紧急,就等等吧。
如果 ...

楼主的意图,在楼主位写得很清楚,你们迟迟未抓住楼主的痛点。包括 godsend 也是一样。
我在 60 楼已经给楼主建议了。

其实,楼主也不用去纠结了。
结论就是:对于小批量数据交互,没太大差异,不管你用不用操作系统,因为那点时间,通常是短暂的,能利用的也是有限的。

出0入0汤圆

 楼主| 发表于 2017-3-29 11:44:31 | 显示全部楼层
非常感谢楼上众兄弟热情回复,你们的无私细致耐心,于我帮助良多!再次感谢!

出0入0汤圆

发表于 2017-3-30 10:50:26 | 显示全部楼层
一直用模拟io。

出0入0汤圆

发表于 2017-3-30 14:53:05 | 显示全部楼层
看硬件可靠性,stm32主要还是用模拟iic,不用花额外的时间在调试可靠性上,已证实可靠性的其它的MCU有用硬件iic的

出0入0汤圆

 楼主| 发表于 2017-3-30 15:52:38 | 显示全部楼层
smithding 发表于 2017-3-30 14:53
看硬件可靠性,stm32主要还是用模拟iic,不用花额外的时间在调试可靠性上,已证实可靠性的其它的MCU有用硬 ...

是的,用自己熟悉的方法最好,多谢!

出0入0汤圆

发表于 2017-5-16 21:30:11 | 显示全部楼层
s1j2h3 发表于 2017-3-3 09:30
其硬件应该是没问题的,网上很多人说有问题只不过是使用不当或对其理解不够 ...

我想也是,

出0入0汤圆

发表于 2017-5-24 13:15:15 | 显示全部楼层
mark......

出100入113汤圆

发表于 2017-5-24 20:04:18 | 显示全部楼层
STM32F091  使用IO模拟I2C。

出0入0汤圆

发表于 2017-5-25 09:22:15 | 显示全部楼层
用一个timer中断来执行每一步。就既可以模拟又不耗时间了。

出0入0汤圆

发表于 2017-5-25 15:53:35 | 显示全部楼层
liaoze22 发表于 2017-3-7 07:49
硬件IC2的确很头疼,我也为此调了很久,也纠结了很长一段时间,到底是软件模拟好呢,还是硬件I2C好呢,最后 ...

硬件I2C加DMA读写的话有用上中断了么,还有串口频繁读写也试了不影响硬件I2C?

出100入101汤圆

发表于 2017-5-25 16:31:37 | 显示全部楼层
软件模拟更可靠些

出0入0汤圆

发表于 2017-6-5 15:40:48 | 显示全部楼层
lzly0302 发表于 2017-5-25 15:53
硬件I2C加DMA读写的话有用上中断了么,还有串口频繁读写也试了不影响硬件I2C? ...

是需要中断的,优先级要设高一些,否则会死掉,串口读写基本不会影响,用不用DMA需要看使用场合

出0入0汤圆

发表于 2017-9-22 09:20:14 | 显示全部楼层
godsend 发表于 2017-3-10 08:58
模拟在操作I2C时cpu只能干等,中断+dma操作过程中,cpu少许干预,其他时间可以干别的事。你说哪个效率好 ...

加个定时器回调就好了。
小系统DMA没问题,如果大系统,多中断复杂系统,IIC照样挂(ST的),只能说应用场合的问题。

出0入0汤圆

发表于 2017-9-22 09:26:10 | 显示全部楼层
F1,F2,F4虽然开DMA加最高中断可以解决所有问题。 这个场景已经局限了很多应用了。 如果用F1,F2,F4系列,个人建议还是使用IO口模拟。另外加个小的OS或者自己做个软定时器一起协作,其实IIC并没有比硬件的差。至少我的系统DMA没有空给IIC,最高优先级也不允许,我没5分钟就要读写一次IIC, 如果用ST硬件的IIC,跑不了多久就挂掉了。改用模拟,加简单OS。完全OK。

出0入0汤圆

发表于 2017-9-22 21:10:53 | 显示全部楼层
只遇到 F1 有问题,容易挂,挂了只能下电再上电。

F2 没用过。

F0/F3/F4 虽然也不够好,但目前确实没有遇到容易卡住的情况。

出0入0汤圆

发表于 2017-9-22 21:14:35 | 显示全部楼层
IIC 的速度不快不慢的, 还是觉得配合硬件逻辑实现的状态机比较高效.

出0入18汤圆

发表于 2017-9-22 21:37:27 | 显示全部楼层
IIC  速度  400K 一般 ,假如算 一个字节 10个 clk,40K字节,大多数外设 除非是eeprom  一般都不需要读40字节,所以一般来讲 IIc 读一次就 1ms,即便你读取 100字节 也才 2ms,慢点也就 5ms 吧,没什么必要搞成 高级中断,DMA,之类的,跑 OS也不需要 2-3ms 切换任务吧,实时性要求这么高难道造  航天设备,所以  软件模拟 简单方便可靠。不折腾,IO布局布线随便,折腾起来累呀!

出0入0汤圆

发表于 2019-4-9 19:43:38 | 显示全部楼层
godsend 发表于 2017-3-12 18:35
这函数在这里并不是接收I2C数据的作用,而是读取一次清空RX_BUFF的内容,防止接收数据不更新。 ...

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

本版积分规则

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

GMT+8, 2024-4-23 17:09

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

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