关于STM32的I2C硬件DMA实现
关于STM32的I2C硬件DMA实现
网上看到很多说STM32的I2C很难用,但我觉得还是理解上的问题,STM32的I2C确实很复杂,但只要基础牢靠,并没有想象中的那么困难。
那么就先从基础说起,只说关键点,不涉及代码。
首先说I2C这个协议:协议包括START、ACK、NACK、STOP。尽管协议中规定START必须,其他几个非必须,但实际上其他三个仍旧非常重要。
主发从收:主 START -> 主发地址 -> 从 ACK -> (主发数据 -> 从 ACK (循环)) -> 主 STOP 或 主 START 启动下一次传输
这一过程中,主控SCL线,从只在ACK时控SDA线,其他时刻主控SDA线。
主收从发:主 START -> 从发地址 -> 主 ACK -> (从发数据 -> 主ACK (循环)) -> 接受至最后一个字节时,主 NACK -> 主 STOP 或 主 START 启动下一次传输
在这一过程中,主 START、主 ACK 时,主控总线;从发ACK时,主控SCL线,从控SDA线;在主接受数据时,虽然由主设备产生时钟,但从设备在数据未准备好时,拉低SCL线,这样主设备可知从设备未发送数据,从设备在数据准备好,可以发送的时候,停止拉低SCL线,这时候才开始真正的数据传输,换句话说,虽然时钟是由住设备产生,但在总线上未必就有时钟存在,这期间可以看做是从设备在控总线;当发送到最后一字节的时候,主设备发送NACK,从设备接受后,放弃对总线的控制。
STOP在单主环境下非必要,但在多主环境就非常必要,主控总线的设备发送STOP后,通知总线其他设备总线已经闲置。
以前的老器件很容易导致总线死锁,但现在的产品很多都带有超时机制,所以总线被锁的情况基本不怎么存在了。
下面要说的是STM32的寄存器,状态寄存器有两个,事件、错误状态一堆,看起来确实都算是有用,但实际使用的时候未必都要用到,还是要看情况,那么状态寄存器的清除就是个问题,有两个方法,一个是PE位禁止,不过除非在通讯结束,否则会扰乱总线上的电平,后果未知,争取的方法是:对于普通事件,先读SR1,再读SR2,如果是错误,那么就要再增加一个将SR1写 0 。想简单的话,那就用一个32位无符号整形,先读SR1,然后或上SR2左移16,再将SR1写 0,最后用这个变量和ST公司提供那个库中的状态比对就行了。
STM32的I2C和其他模块有些不同,其他模块完全可以交给DMA控制器,但I2C不行,必须结合中断或者IO方式,不建议IO方式,得等,万一出点岔子,被狗咬就麻烦了,所以最佳方式是结合中断。
主发时:PE位使能,PE位必须先使能,否则你操作不了其他位,然后使能ACK位,ITEVTEN位,DMA位,使能START位(这几个位可以同时置),然后进入事件中断,判断 I2C_EVENT_MASTER_MODE_SELECT ,将从地址写入 DR 寄存器,这里需要注意一点,就是从设备应答后,如果主设备不读状态寄存器,那么主设备就不会继续发送时钟来传输数据!这时候就体现出使用中断方式的另外一个好处,每次进中断的时候状态寄存器都要被读一下,不符合处理条件的你可以不管,但模块操作可以正确进行下去。数据开始传输时,控制就基本完全交给DMA控制器了,这时候一般也不会有什么状态中断产生,当然也不是绝对没有,有可能会有错误中断,也可能会由于MCU过忙产生事件中断,但这个事件一般影响不大,出错的时候你可能要处理一下。当数据传输完成后,会产生一个 I2C_EVENT_MASTER_BYTE_TRANSMITTED,注意这个不是只在数据传输完成才有!如果MCU过忙,DMA在I2C传输完上一个数据时,没能将下一个数据送到I2C,也会产生,这个事件只代表I2C位移寄存器内的数据被传完,而DR寄存器又没有被写入新的数据!所以,在这个状态产生的时候,要判断一下DMA的CNDTR寄存器,这是个递减的,如果是 0 ,那么就代表完成,可以去掉I2C的ACK位,使能STOP;或者是START进入下一轮数据传输。当然你不管也行,单主控下这不是必须的。
主收时:前面和主发时一样,但有一点要特别注意,那就是主控寄存器的LAST位,这个我在ST的库中没找到设置的函数,也可能是我没看仔细,反正我都是直接寄存器操作,不用库,除非是库中一些现成的状态可以用一下。这个位很重要,如果你只是一轮DMA传输,那么这个必须被置位,因为传输到最后一个字节的时候,主控需要发出NACK而不是ACK来通知从设备释放对总线的控制!LAST位就是做这个用的。主收的时候,传输完成就不是依靠I2C的事件中断来判断了,这个要通过DMA的IT_TC来完成,DMA中断产生后,做一下结束处理工作,最后别忘了清DMA的中断标志,不然会死循环在里面。
从发和从收这次就先不写了,相对简单一些,而且我感觉用的一般也不多吧,等有时间下次再写,另外再说一下,采用这种DMA+中断的方式,可以不去处理错误,操作开始的时候置一个标志,结束的时候清标注,在主程序中判断,如果超过一定时间标志还在那,那么就要考虑重置I2C了,一方面是错误状态太多……我真的判断不过来,也可能我比较懒吧,都给统一处理了。还有一个建议就是尽量采用STM32的硬件位域操作,因为一方面你有些操作要在主程序里,一些操作要在中断里,通常的读再写可能会导致错乱,位域操作就不会,即使不错乱,如果总线上产生错误,那么在操作某些位的时候会卡死在那,位域操作也不会卡死。
附一个位域操作宏
#define BITBAND_ADDRESS(x) (((x) & 0xF0000000) + 0x02000000 + (((x) & 0xFFFFF) << 5))
#define BITBAND(x,bit) (*(volatile uint32_t*)(BITBAND_ADDRESS((uint32_t)&(x)) + ((bit) << 2)))
使用 BITBAND(x,bit),x 代表 寄存器,bit 代表是操作哪个位。单独的一个位会被展开成一个32位整形,当然,你只能写 1 或 0
关于STM32的I2C硬件DMA实现的更多相关文章
- STM32的I2C特性及架构
软件模拟协议:使用CPU直接控制通讯引脚(GPIO)的电平,产生出符合通讯协议标准的逻辑. 硬件实现协议:由STM32的I2C片上外设专门负责实现I2C通讯协议,只要配置好该外设,它就会自动根据协议要 ...
- STM32的I2C框图详解及通讯过程
STM32 的I2C 特性及架构 如果我们直接控制STM32 的两个GPIO 引脚,分别用作SCL 及SDA,按照上述信号的时序要求,直接像控制LED 灯那样控制引脚的输出(若是接收数据时则读取SDA ...
- STM32基础分析——USART的DMA模式
有关USART的DMA传输模式,其基本的概念和配置,网上有很多博客和教程都有,这里不再赘述,只是记录一下比较容易忽视而造成调试不通的问题. 1. 串口发送和接收分属两个DMA通道 一般方式操作串口时, ...
- STM32(11)——DMA
简介: DMA:Direct Memory Access,直接存储器访问.DMA传输数据从一个地址空间复制到另外一个地址空间.当CPU初始化这个传输动作,传输动作本身就是DMA控制器来实现和完成.典型 ...
- stm32串口USART 硬件流控 --学习笔记
流控的概念源于 RS232 这个标准,在 RS232 标准里面包含了串口.流控的定义.大家一定了解,RS232 中的"RS"是Recommend Standard 的缩写,即&qu ...
- I2C硬件与模拟的区别
硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,因而效率要远高于软件模拟的I2C:一般也较为稳定,但是程序较为繁琐. 硬件(固件)I2C是直接调用内部寄存器进行 ...
- (三)stm32之串口通信DMA传输完成中断
一.DMA功能简介 首先唠叨一下DMA的基本概念,DMA的出现大大减轻了CPU的工作量.在硬件系统中,主要由CPU(内核).外设.内存(SRAM).总线等结构组成,数据经常要在内存和外设之间,外设和外 ...
- STM32学习日志--使用DMA功能自动更新PWM的输出
/******************************************************************************* 编译环境: EWARM V5.30 硬 ...
- STM32 ADC多通道转换DMA模式与非DMA模式两种方法(HAL库)
一.非DMA模式(转) 说明:这个是自己刚做的时候百度出来的,不是我自己做出来的,因为感觉有用就保存下来做学习用,原文链接:https://blog.csdn.net/qq_24815615/arti ...
随机推荐
- 淘宝客类别id大全
汽车/用品/配件/改装 例 [ID:26] 家居饰品 例 [ID:50020808] 特色手工艺 例 [ID:50020857] 景点门票/度假线路/旅游服务 例 [ID:50025707] 男装 例 ...
- WebForm——浅拷贝与深拷贝
注:本文整理来自连接 https://www.cnblogs.com/echolun/p/7889848.html ,感谢博主的分享 总结: 1.浅拷贝:只拷贝变量的名,而不拷贝变量的值——常为引用类 ...
- Vue代码分割懒加载的实现方法
什么是懒加载 懒加载也叫延迟加载,即在需要的时候进行加载,随用随载. 为什么需要懒加载 在单页应用中,如果没有应用懒加载,运用webpack打包后的文件将会异常的大,造成进入首页时,需要加载的内容过多 ...
- 使用JQuery获取被选中的checkbox的value值
上网查了一下,感觉一些人回答得真的是不知所云,要么代码不够简便.或者是有些想装逼成分等. 以下为使用JQuery获取input checkbox被选中的值代码: <html> & ...
- 小菜鸟之java基础
1. javac 命令的作用: javac 编译器解析 Java 源代码,并生成字节码文件的过程 2. java为什么可以跨平台: ava有虚拟机(JVM),JAVA程序不是直接在电脑上运行的,是在虚 ...
- Photon Server 实现注册与登录(二) --- 服务端代码整理
一.有的代码前端和后端都会用到.比如一些请求的Code.使用需要新建项目存放公共代码. 新建项目Common存放公共代码: EventCode :存放服务端自动发送信息给客户端的code Operat ...
- k8s-日志收集架构
日志收集 Kubernetes 集群中监控系统的搭建,除了对集群的监控报警之外,还有一项运维工作是非常重要的,那就是日志的收集. 介绍 应用程序和系统日志可以帮助我们了解集群内部的运行情况,日志对于我 ...
- Payload 实现分离免杀
众所周知,目前的杀毒软件的杀毒原理主要有三种方式,一种基于特征,一种基于行为,一种基于云查杀,其中云查杀的一些特点基本上也可以概括为特征码查杀,不管是哪一种杀毒软件,都会检查PE文件头,尤其是当后门程 ...
- 怎样获取所有style节点
通过 document.styleSheets 获取所有的样式表节点. document.styleSheets instanceof StyleSheetList; // true 注意: 1. 返 ...
- js date对象传参获取特定日期的时间戳
当我们想要通过js获取某一特定时间的时间戳时,会通过给date对象传参再通过getTime函数来获取,传递的参数格式也有不同形式.有些时候,可能会因为自己传入参数的格式不正确而导致date对象inva ...