大概6-7年前,在网上看到过一篇用STM32F1的DMA控制GPIO输出高速数字波形的帖子。觉得很有意思,就自己试了试:控制GPIO输出波形翻转的速度最高只能达到3-4MHz,且容易受到STM32F1的APB2总线其他设备读写的影响,输出的方波不稳定。由于问题较多,对高速实时性提升不大,感觉基本不实用,就没有再进一步研究。
前几天在研究STM32F4和STM32F1的区别时,发现STM32F4进行了两项升级:1、把GPIO连接从APB2总线改到AHB总线上,极大的改善了GPIO输入、输出的实时性和时序控制能力。2、增加DMA stream的概念(参见拙作 https://www.cnblogs.com/helesheng/p/18167026 ),使得DMA传输请求的来源更明确。
DMA控制器的上述升级,使得STM32F1上比较“鸡肋”的DMA高速并行同步传输能力得到了较大提升 ,具备了一定的实用性。我尝试用STM32F407VE的DMA2在TIM1的触发配合下,实现了对并行接口的流水线型ADC的控制和读取。理论读取速度可达40MSPS以上,实测读取速度可超过AD9200E的理论上限——20MSPS。设计思路、程序和电路如下,供大家参考和指正。
以下原创内容欢迎网友转载,但请注明出处: https://www.cnblogs.com/helesheng
一、DMA控制高速同步并行数据传输的原理分析
本文基于DMA的GPIO高速并行读写程序,主要利用了STM32F4系列DMA的以下关键特性:
1)STM32的DMA传输可分为外设和内存之间,以及内存之间两种传输模式。这两种模式的最重要区别是: 外设和内存之前的DMA传输必须由其他外部 请求 信号触发;而内存和内存之间的DMA传输则不会等待其他触发,它会在上一次传输进行完之后自动进行下一次传输 。 在STM32的嵌入式系统中,除去少数只追求传输带宽的纯数据传输任务以外,大部分与硬件相关的DMA传输,还要要求在恰当的时刻完成传输。以本文要完成的高速A/D数据读取为例,读写数据的时刻必须收到采样间隔的严格控制。也就要求由定时器TIM来实现采样间隔的定时,即 由TIM产生DMA传输的请求信号,而数据则通过DMA在GPIO 数据寄存器 和内存之间进行传输 。
2)外设和内存之间的DMA传输的请求信号,可以是本次DMA传输的源或目的的外设,也可以是其他外设。 STM32F4的DMA传输请求信号由DMA的流(stream)和请求信号(或称通道channel)共同决定;但传输源和目的外设种类却由源和目的地址决定 。 二者不能混为一谈。
3)STM32F4的 两个DMA控制器的两个端口被指定为特定的数据源 。具体连接关系如下图所示。以GPIO为例,STM32F4把它连接到AHB1总线上,由下图可知连接到AHB1外设的只有蓝色和黑色两种颜色的线,也就意味着,左侧的两个DMA控制器中只有连接了蓝色和黑色的 DMA2 可以实现与GPIO之间的DMA传输。
图1 STM32F4系列两个DMA控制器端口(源和目标)连接的外设/存储器种类
通过查询STM32F4的数据手册中的DMA请求信号表可知,可用于请求DMA2的定时器只有TIM1和TIM8两个高级定时器。我选择了TIM1的更新事件TIM1_UP来请求DMA传输。
图2 STMF4系列DMA2控制器的外设传输请求表
另外,控制高速ADC还要求单次DMA传输耗时要小于采样间隔,而 STM32F4把GPIO连接到AHB1总线的意义也就在于此——相比之前将GPIO连接到APB2总线的STM32F1系列,STM32F4将能够更快速的对GPIO进行读写 ,从而提高与所控制ADC的数据读取速率。
最后,流水线型ADC还需要一个采样同步时钟;由于数据读取也是在该时钟的同步下进行,自然只能由TIM1时基部分同时产生该时钟。一种合理的解决办法是用TIM1的输出比较(OC)功能电路来产生。这也意味着该时钟只能由TIM1的某个通道(CHx)产生,从而只能在某些管脚上输出,这一点必须在硬件设计时加以注意。
二、高速并行接口ADC读取的程序和电路设计
1、硬件设计
下图是我采用的ADI公司的标称转换速率为20MSPS的流水线型A/D转换器——AD9200E(10bits分辨率)的工作时序图。
图3 AD9200的读取时序
可以发现,数据的更新发生在 上升沿 后25ns左右。 如果输出比较OC电路采用时基计数的前段输出高电平,比较翻转的后输出低电平的模式,就会使得输出PWM信号的上升沿发生在TIM1更新时。而如前所述STM32F4的DMA2数据传输则发生在TIM1更新事件后,这就有由于高速数字电路的竞争与冒险造成读取时序不收敛。
我曾在某论坛看到过有人对AD9226做类似尝试,仅在16MSPS以上时就出现采样点读取错误的问题(https://blog.csdn.net/cusichidouren/article/details/126002742),我猜测就是由于这个原因。 合理的解决方案其实也不复杂:对OC电路输出的PWM信号反相,使其在下降沿时触发DMA2传输请求。幸运的是STM32的OC输出支持负逻辑的PWM输出,不需要附加进一步的门电路。具体配置代码请参见软件设计部分。
具体GPIO选择方面,我用了PE0~16号端口来实现对AD9200的控制和读取。其中,PE口中PE11管脚可以配置为TIM1的通道2(CH2),可以作为AD9200的转换时钟(INPUT CLOCK)。而AD9200的10根数据线则用PE0~9负责读取,低位对齐的做法也有利于后续的数据读取和整理。AD9200的钳位控制(CLAMP)、溢出指示(OTR)、低功耗待机(STBY)等管脚则连接到PE口的其他管脚。原理图太简单,这里就不贴出来了,放一张实物图。 注意:AD9200的模拟驱动应使用一个高压摆率的宽带运放,我使用了低成本的AD8052。
图4 实验系统实物图
2、软件设计
正如本文前面“原理分析”介绍的,DMA传输的通道、流、数据源/目标、传输请求信号如下图所示。
图5 DMA工作原理示意图
3、遇到的几个“坑”
尽管STM32F4是非常成熟的MCU产品线,但本文所述的“基于DMA的高速并行GPIO读写”并不是常见的功能,因此在调试过程中我还是遇到并克服了一些问题。个人觉得是芯片本身以及标准外设库的一些小问题造成的,但也可能是由于我才疏学浅、考虑不周的原因。罗列与此,供大家参考和指正。
1)官方提供的标准外设库高速外部晶振频率不匹配问题
我使用了ST官方提供的标准外设库作为开发平台,其中配置的高速外部晶振HSE的频率(HSE_VALUE)为25MHz。而我实际使用的晶振为8MHz,这除了导致UART通信的波特率不准之外,更重要的是还会导致TIM1输出比较OC电路输出的时钟频率不对。在固件库stm32f4xx.h中搜索宏“HSE_VALUE”,将其改为8000000即可解决问题。
2)PA8管脚复用为TIM1_CH1输出比较功能时无输出的问题
我最初进行硬件设计时,曾想用管脚PA8复用的TIM1_CH1功能输出AD9200所需的转换时钟。但折腾了很长时间都无法让PA8管脚输出所需时钟(PWM)信号,在网上搜索后发现问题是STM32芯片的一个“顽疾”( www.openedv.com/posts/list/49738.htm )——只要使能复用在PA8、PA8、PA10等管脚上的USART1功能,就会导致PA8上的TIM1_CH1无法输出PWM信号。修补这个BUG也不困难:我改成使用TIM1_CH2(在PE11管脚上)输出PWM波,就很好的解决了这个问题。
三、测试结果
下图所示的是VOFA+软件显示的采集信号
图6 AD9200采集的信号波形(采样率为21MS,输入正弦信号为1MHz,采样长度为512点)
相关问题分析如下:
四、DMA控制高速并行ADC/DAC的弊端和问题
1、采样触发信号问题 及其解决办法
1) 用STM32这样的MCU代替FPGA来控制高速ADC,最大的问题在于MCU软件的实时性远远赶不上硬件控制的FPGA。例如,前面提供的主程序代码中,用检测按键的方式触发DMA实现采样。显然无论是检测按键的程序的时间精度还是程序调用外设库启动DMA传输的时间精度都远远低于ADC采样的100ns数量级的时间精度。致使采样触发信号的实时性只能满足对“时间平稳信号”分析的需要,无法达到对非平稳信号进行时域分析的需求。
2)另外,从图6中可以发现信号刚开始的一段信号是混乱的,造成混乱的原因有二:
其一:在高速传输条件下, 在传输刚开始的一段时间 S TM32的DMA控制器无法及时的响应传输请求 ,从而造成DMA只能在TIM1的采样请求已经发出一段时间后才读取ADC的输出数据,结果自然不正确。
其二:流水线型ADC的数据传输和采样值之间存在延迟。从图3给出的时序图也可以发现,当前读取的数据是四个时钟之前“潜伏”在ADC的流水线中的,从而造成了缓冲区中开始一段的信号错误。
以上两个原因,都可以通过丢掉缓冲器中开始的一段数据的方法掩盖,但这无疑也是对采样触发信号的实时性的进一步降低。
2、TIM1输出采样时钟抖动问题及其解决办法
下图是用20G采样率的示波器DSOX6004A采集到的TIM1_CH2(PE11)输出的采样时钟信号。
图7 TIM1的PWM功能产生的采样时钟的孔径抖动(触发后100us处)
为了测试采样时钟信号的孔径抖动情况,我将观察窗对准采样触发后100us的地方(触发-采样延迟如图中红色圈内数据所示),可以发现该处时钟上升沿的抖动达到了5ns左右(图中示波器横轴每个为5ns,如图中黄色圈所示)。这表明TIM1的OC模块产生的采样时钟孔径抖动品质较差,大大降低了采样信号的信噪比。
为解决这个问题,可以使用片外模拟锁相环PLL输出的时钟信号作为ADC的采样时钟。至于STM32F407的TIM1_CH2则由输出比较模式(OC)变为输入捕获模式(IC),由外部锁相环产生的时钟信号作为TIM1_CH2的捕获(IC)对象。 下图是我使用单独的模拟锁相环PLL芯片Si5351产生相同的21MHz信号,同样在采样触发后100us的地方观察时钟抖动情况。可以明显的看到锁相环芯片产生的时钟的孔径抖动性能明显由于定时器输出比较模式输出的时钟。
图8 模拟锁相环芯片Si5351产生的采样时钟的孔径抖动(触发后100us处)
3、同步传输速率被限制在10MSPS左右
从本质上讲,DMA是与CPU内核共享片上的总线资源的,当使用DMA高速传输并行数据时必然挤占CPU读取指令和数据的总线时间。如果DMA传输的是SRAM中的数据,某一笔传输由于总线被占用而延期并不会影响传输整体的正确性。但对于ADC和DAC这样的高速数据传输,某一笔数据的延迟就有可能造成采样的错误。
经过测试,我整体的感觉是: 即使把DMA传输数据的优先级设为非常高(DMA_Priority_VeryHigh),且在采集期间不执行中断服务(ISR)等可能打断DMA的程序,当把DMA同步传输速率提升到10MSPS以上就很难保证每一笔传输的可靠实时性了 。当然在极限情况下,同步传输速率是可以达到40MSPS的,但不建议大家在产品中使用。
4、 单端数字信号,抗干扰能力弱于差分数据线
当数字信号在PCB上传输的速率达到10MSPS以上时,STM32中使用的单端3.3V CMOS在很多情况下就有可能出现传输错误,一般的解决方案是使用LVDS等差分传输标准。但STM32F4系列中没有类似硬件配置,导致同步传输速率达到10MSPS以上时系统的抗干扰能力和传输正确率都会有所下降。