你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

经验分享 | STM32H7 MDMA 与通用DMA的联动传输示例

[复制链接]
攻城狮Melo 发布时间:2026-4-21 14:15

有人咨询STM32H7系列的MDMA如何与通用DMA实现联动传输。我这里准备了一个简单的应用场景,演示一下实现过程。

测试过程中我使用STM32H743开发板,IDE是ARM KEIL。用TIMER触发ADC,ADC EOC事件触发通用DMA将结果搬运到AXI SRAM缓冲区。基于该DMA的传输完成事件,触发MDMA的启动。MDMA再将AXI SRAM的数据搬运到DTCM区的缓冲区,交由CPU做后期数据高速处理。这里重点演示DMA/MDMA的联动传输过程

演示过程中的数据搬运流程如下图示意:

image.png

我使用STM32CubeMx进行配置。TIMER的配置就不贴图了。ADC的配置如下,重点关注其DMA配置。

image.png

根据配置不难看出ADC申请的是通用DMA1的Stream 0,缩写为DMA1-S0。

当DMA1将16个ADC结果搬运到AXI SRAM后,产生DMA传输完成事件。该事件去触发MDMA,让MDMA接着将存放于AXI SRAM的ADC结果搬运到DTCM缓存区。

在下面测试代码里,ADC_Result数组用于存放ADC结果,MDMA将其搬运到DTCM域的InDTCM数组。

先选择Buffer Transfer触发模式。基于该模式,MDMA每收到1个触发事件就完成一批Buffer Transfer Length字节长度的数据搬运。这里要搬运16个字,即64字节的数据。详见下图MDMA配置。红色注释是我加上去的,用于解释和提醒。

image.png

相关的用户代码如下。我在AXI RAM和DTCM RAM区通过指定地址方式定义了两个数组。其中,TIMER用来触发ADC。下面的define宏只是为了简化字符串长度,没啥特别意思。【手机模式下,代码可以左右滑动】

uint32_t  ADC_Result[16] __attribute__((section(".ARM.__at_0x24000400"))) ;
uint32_t  InDTCM[16] __attribute__((section(".ARM.__at_0x20000000"))) ;
#define hmdmaCH0   hmdma_mdma_channel0_dma1_stream0_tc_0
HAL_ADCEx_Calibration_Start(&hadc3,ADC_CALIB_OFFSET_LINEARITY,ADC_SINGLE_ENDED);//ADC校准
HAL_MDMA_Start_IT(&hmdmaCH0, (uint32_t)&ADC_Result[0], (uint32_t)&InDTCM[0], 16*4, 1);//启动MDMA
HAL_ADC_Start_DMA(&hadc3, (uint32_t *)ADC_Result,  16);//启动ADC及DMA HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);//启动TIMER

运行后的结果如下,MDMA成功实现将AXI RAM的数据搬运到DTCM区域。 image.png

当然,我们也可以选择MDMA的Block Transfer触发模式。即MDMA每收到触发事件,就做1个块的数据搬运。ADC的DMA配置完全不动。下面是MDMA的相应配置,注意此时的配置跟上面配置的细小差异。

image.png

此时的Block Data Length对应16个字的数据,即64字节。至于那个Buffer Transfer Length,设置为源数据宽度的倍数即可,比方4或8都行。

用户代码跟上面的完全一样,运行后的测试结果也完全符合预期,就不重复贴图了。

现在我们对上面测试做些小变动后再行验证。

假设ADC结果基于半字传输和存储,还是传输16个数据后停止搬运并产生DMA传输完成事件触发MDMA。ADC的DMA配置调整如下,这次是半字到半字的方式。

image.png

而MDMA这边呢,将储存于AXI RAM的ADC结果也按半字方式提取并存放到DTCM区。我们先看看MDMA基于Buffer Transfer触发模式的配置与实现。见下图配置:

image.png

用户代码除了局部数据有微调外,其它跟前面一样。我只把有变动的代码贴过来,注意数据宽度定义和Buffer Transfer 长度的字节数的变化。

uint16_t ADC_Result[16] attribute((section(".ARM.__at_0x24000400"))) ;

uint16_t InDTCM[16] attribute((section(".ARM.__at_0x20000000"))) ;

HAL_MDMA_Start_IT(&hmdmaCH0,(uint32_t)&ADC_Result[0], (uint32_t)&InDTCM[0], 16*2, 1);

运行基于当前配置和调整过的测试代码,结果完全正常。见如下结果截图: image.png

保持ADC的配置及上面用户代码完全不变,若将MDMA的触发模式改成BLOCK 传输模式也是可以的。MDMA的参考配置如下,MDMA的源端和目的端数据宽度仍然都是16位。

image.png

基于上面配置的运行结果也完成正常。

在沿用目前ADC配置和用户执行代码的基础上,如果MDMA传输时,源端数据宽度是16位,而目的端的数据宽度按32位存放呢?

不妨还是以Block传输模式为例来演示这个场景。MDMA的参考配置如下:【注意:源端是16位,目的端是32位。还有注意Data Alignment那个选项】

image.png

用户代码稍作改动:【修改了InDTCM数组的数据宽度定义,其它不动】

uint16_t  ADC_Result[16] __attribute__((section(".ARM.__at_0x24000400"))) ;
uint32_t   InDTCM[16] __attribute__((section(".ARM.__at_0x20000000"))) ; #define hmdmaCH0   hmdma_mdma_channel0_dma1_stream0_tc_0
HAL_ADCEx_Calibration_Start(&hadc3,ADC_CALIB_OFFSET_LINEARITY,ADC_SINGLE_ENDED);
HAL_MDMA_Start_IT(&hmdmaCH0,(uint32_t)&ADC_Result[0], (uint32_t)&InDTCM[0], 16*2, 1); HAL_ADC_Start_DMA(&hadc3, (uint32_t *)ADC_Result,  16); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);

测试结果如下,MDMA将16位源数据搬到目的端后通过补0形成32位数据,当然结果是正确的。 image.png

在刚才测试的基础上,若希望源端数据按照目的端数据宽度以打包的方式进行传输是否可以呢?即把源端每两个16位数据组合为一个32位数据存放到目的端,这样的话,源端的16个16位数据变成目的端的8个32位数据。如下图结果所示: image.png

我们还是以BLOCK传输模式为例看看相关配置,此时只需在上面的MDMA配置的基础上稍作如下调整即可实现。见下图配置红色圆圈和方框圈出的地方,其它不动。

image.png

因此时代码方面完全没有改动,不难想象,最终目的端缓冲将多出8个空缺。

针对STM32H7系列MDMA与通用DMA的联动演示主要使用了MDMA的Buffer Transfer 和Block Transfer两种触发模式,也是两种基本的、常用的MDMA传输方式。还有另外两种模式,今天就不聊了。

就此打住,下次再聊~!

收藏 评论0 发布时间:2026-4-21 14:15

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版