为何实现不了定时器DMA Burst传输?

2023-11-14

07d1d5bc9f667090e73f7492060a7973.png

有人使用STM32F4系列开发产品,程序运行过程中需要不时地对外输出一串驱动脉冲,并要求这几串脉冲的频率可变、占空比固定。他想到使用基于STM32定时器的DMA BURST传输。具体点说,他期望不时地通过TIM3的CH1输出一串频率可变、占空比固定的脉冲然后停下来。这个思路在原理上是没问题的,可是他在测试过程中发现怎么也折腾不出预期的效果。

他目前使用的芯片是STM32F401,虽有点老旧,但我查看了手册,确认该芯片的TIM3是支持基于TIMER事件实现TIME寄存器与内存间的DMA BURST传输的。即每TIMER事件可以申请多个DMA请求从而实现定时器寄存器与内存间的批量数据传输。要知某个STM32 TIMER是否支持上述功能,只需查看STM32参考手册的相关定时器的寄存器中有无TIMx_DCR和TIMx_DMAR寄存器的介绍。

889e0e649e2e845c756e7ee44da8bbd5.png

既然支持,为什么实现不了呢?关于这个功能我们还需要注意几点:

1、所选择的TIMER必须支持基于定时器事件的DMA BURST传输功能。

2、触发事件必须是来自参与DMA传输的定时器事件,不能是别的定时器事件。比方说你想实现TIM1的寄存器与内存间的DMA BURST传输,触发事件不能是来自TIM2、TIM3这些非TIM1的事件。

3、定时器DMA Burst传输时,用来被BURST访问的定时器寄存器应该是同一定时器的而且是地址连续的寄存器,不可跳跃访问。比方说像下面某TIMER的4个比较寄存器物理地址是安排在一起的,而CH2恰好因为某些原因没有被用上。

e6152632919a28c9216c1e5c289f5558.png

如果你仅对CH1/CH3/CH4个通道的比较寄存器的值做BURST访问,此时尽管CH2没有被用上,BURST访问的传输个数应该是4而不是3。

4、STM32 HAL库里的例程或函数重点在演示相应的功能或特性,但它不能包罗万象或保证适用于任何场景。有些时候我们可能需要在现有库函数的前提下适当地做些调整来满足需求。

结合这几点,我们一起排查下。第1点、第2点已经核对过了,没问题。看看第3点,即设置BURST传输个数。下图是F40x系列TIM2~TIM5的内存地址映射图。

18c898c543d462fd9d55227514392690.png

现在使用者真正需要用到的寄存器只有TIM3->ARR和TIM3->CCR1两个寄存器,但二者中间还有个预留空位【其它高级定时器的RCR寄存器的位置】也必须算进来,即这里BURST传输个数应该是3而不是2

再看看上面提到的第4点要注意的地方。这点我就不过多解释了,ST提供的HAL库例程及相关函数只能实现1次BURST传输的功能,如果要实现多次BURST传输就得在其代码基础上做些调整,更多细节可以参考我之前分享的那篇《STM32定时器BURST传输介绍及示例》。不过,在那篇的演示例程里我使用的是DMA Circular模式,现在的用户则要使用DMA Normal模式。采用Normal模式和采用Circular模式基于现有HAL库函数组织代码还略有差异,若没处理好这点小差异,可能让你完全出不来想要的结果。

下面我使用STM32G4芯片的TIM3-CH1演示上面用户的功能。每次输出5个脉冲,3次输出为一个循环,同一循环中的3次输出的频率各不相同,占空比一样。【为什么没用STM32F401芯片,是因为此时手上没有带该芯片的开发板。不过演示功能的配置及代码基本一样。】

我使用CubeMx对TIM3进行配置,参考配置如下:

fa6397f86310bcc1a5bd7e81f8ab10ce.png

开启TIM3基于更新事件的DMA传输功能并做相关配置。

c3425ffd395b0ac07ac26a6846b563ee.png

配置完成,生成初始化工程,添加用户代码。

我准备了3串脉冲的ARR/CCR1的值,分别以数组PulseData1[]、PulseData2[]、PulseData3[]来存放,占空比都设置为50%。显然数组行中间的0值用于前面提到过的预留字,此处无实际作用。另外,每个数组中的第6行数据其实是关闭当前通道PWM输出,具体应用时注意结合所选择的PWM模式及极性选择。

下面是参考用户代码。代码在手机模式下可左右滑动。

/* USER CODE BEGIN PD */
#define ARR1 (10000U)
#define ARR2 (20000U)
#define ARR3 (40000U)
#define Count (18)      //3 * 6


uint16_t PulseData1[]={
 ARR1,0,ARR1*0.5,
 ARR1,0,ARR1*0.5,
 ARR1,0,ARR1*0.5,
 ARR1,0,ARR1*0.5,
 ARR1,0,ARR1*0.5,//
 ARR1,0,0};




uint16_t PulseData2[]={
ARR2,0,ARR2*0.5,
ARR2,0,ARR2*0.5,
ARR2,0,ARR2*0.5,
ARR2,0,ARR2*0.5,
ARR2,0,ARR2*0.5,//
ARR2,0,0,
};


uint16_t PulseData3[]={
ARR3,0,ARR3*0.5,
ARR3,0,ARR3*0.5,
ARR3,0,ARR3*0.5,
ARR3,0,ARR3*0.5,
ARR3,0,ARR3*0.5,//5
ARR3,0,0,
};


/* USER CODE END PD */

主循环测试代码如下:

int main(void)
{
/* USER CODE BEGIN 1 */


/* USER CODE END 1 */


/* MCU Configuration--------------------------------------------------------*/


/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();


/* USER CODE BEGIN Init */


/* USER CODE END Init */


/* Configure the system clock */
  SystemClock_Config();


/* USER CODE BEGIN SysInit */


/* USER CODE END SysInit */


/* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM3_Init();
/* USER CODE BEGIN 2 */
    __HAL_TIM_CLEAR_FLAG(&htim3,TIM_FLAG_UPDATE);
  TIM_CCxChannelCmd(TIM3,TIM_CHANNEL_1,TIM_CCx_ENABLE);
/* USER CODE END 2 */


/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
  {
/* USER CODE END WHILE */


/* USER CODE BEGIN 3 */
       htim3.DMABurstState = HAL_DMA_BURST_STATE_READY;
       hdma_tim3_up.State = HAL_DMA_STATE_READY ;


       __HAL_TIM_CLEAR_FLAG(&htim3,TIM_FLAG_UPDATE);


      HAL_TIM_DMABurst_MultiWriteStart(&htim3, TIM_DMABASE_ARR,TIM_DMA_UPDATE,(uint32_t *)PulseData1,\
                                       TIM_DMABURSTLENGTH_3TRANSFERS,Count);
      TIM3->EGR = TIM_EGR_UG;
      __HAL_TIM_ENABLE(&htim3);


      HAL_Delay(150);  //Prepared for the next 5 Pulses
      __HAL_TIM_DISABLE(&htim3);
//  HAL_DMA_Abort(&hdma_tim3_up);


      htim3.DMABurstState = HAL_DMA_BURST_STATE_READY;
      hdma_tim3_up.State = HAL_DMA_STATE_READY ;


      __HAL_TIM_CLEAR_FLAG(&htim3,TIM_FLAG_UPDATE);


       HAL_TIM_DMABurst_MultiWriteStart(&htim3, TIM_DMABASE_ARR,TIM_DMA_UPDATE,(uint32_t *)PulseData2,\
                                        TIM_DMABURSTLENGTH_3TRANSFERS,Count);
      TIM3->EGR = TIM_EGR_UG;
      __HAL_TIM_ENABLE(&htim3);


      HAL_Delay(150);//Prepared for the next 5 Pulses
      __HAL_TIM_DISABLE(&htim3);
//   HAL_DMA_Abort(&hdma_tim3_up);


      htim3.DMABurstState = HAL_DMA_BURST_STATE_READY;
      hdma_tim3_up.State = HAL_DMA_STATE_READY ;


       __HAL_TIM_CLEAR_FLAG(&htim3,TIM_FLAG_UPDATE);


       HAL_TIM_DMABurst_MultiWriteStart(&htim3, TIM_DMABASE_ARR,TIM_DMA_UPDATE,(uint32_t *)PulseData3,
                                        TIM_DMABURSTLENGTH_3TRANSFERS,Count);
      TIM3->EGR = TIM_EGR_UG;
      __HAL_TIM_ENABLE(&htim3);


      HAL_Delay(150);//Prepared for the next 5 Pulses
      __HAL_TIM_DISABLE(&htim3);
//   HAL_DMA_Abort(&hdma_tim3_up);


  }
/* USER CODE END 3 */
}

编译、除错后,运行程序可以看到我所期望的结果。即我每隔一会儿就发出5个脉冲,3次为1个循环。测试代码都放在这里,供参考使用。这里不逐句解释了,具体使用时结合库代码来研究即可。

f33002e04eee1a2dda3b029642f4cfdb.png

最后,补充一下有关上篇《利用非对称PWM模式体验编码器功能》文中3个变量如何使用ARM MDK IDE自带逻辑分析仪的配置,我把配置截图放于此供有需要的人参考。

这里的CNT_value连续记录TIM2计数器的值,这里为Analog量。

Level_PA8记录GPIOA_PIN8的电平情况,1或0两个值之一,为Bit量。

Level_PA9跟Level_PA8是完全相同的数据类型,显示的是GPIOA_PIN9的电平。显然,逻辑分析仪配置里关于Level_PA9的显示算式的屏蔽数应该是0x00000200,右移位为9。我是在SYSTICK的毫秒中断里读取GPIOA->IDR的值即管脚电平到变量Level_PA8和Level_PA9的。【下图中Core Clock填写芯片支持的最高主频】

855b6033b7601dbbd791dd7afb5494a6.png

4bfe908dfa1dc871e90d8466b90d8b90.png

******************************

往期话题阅读链接【点击即可阅读】:

1、利用非对称PWM模式体验编码器功能

2、STM32定时器BURST传输介绍及示例

3、巧用STM32片内RTC亚秒特性之示例

4、为何STM32H7的ADC数据不变?

5、两份基于STM32 FDCAN开发的资料

25cc6fc00678c22021a23ef034ed7eb9.png

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为何实现不了定时器DMA Burst传输? 的相关文章

  • 进厂拧了4年螺丝,零基础自学python月入过万,一切都来之不易

    前言 种子放在水泥板上就会被晒死 放在水里就会被淹死 但是放在肥沃的土壤里 就会生根发芽 选择可以决定命运 环境可以造就人生 今天给大家分享一位从工厂到程序员小哥的逆袭之路 就是这样一个在朋友眼里的满是羡慕的高薪工作 或许只有只有我自己 才
  • 在 Mac 上升级 pip

    遇到的问题 终端安装包时 会有以下提示 pip install upgrade pip WARNING You are using pip version 20 2 3 however version 21 0 1 is available

随机推荐

  • C++基础知识(十四)--- vector容器

    目录 1 数据结构 线性连续空间 2 迭代器 随机迭代器 三 vector容器动态增长原理 四 vector 常用API 1 vector 构造函数 2 vector 赋值操作 3 vector 大小操作 swap 的使用 缩小容量 4 v
  • 如何创建指向目录的链接

    本文翻译自 How to create a link to a directory closed How to create a link xxx to home jake doc test 2000 something 如何创建到 hom
  • 史上最全

    点击下方卡片 关注 自动驾驶之心 公众号 ADAS巨卷干货 即可获取 点击进入 自动驾驶之心技术交流群 后台回复 BEV综述 获取论文 后台回复 ECCV2022 获取ECCV2022所有自动驾驶方向论文 1摘要 以视觉为中心的俯视图 BE
  • 【HBZ分享】Redis如何与Mysql做数据同步,有几种方法?

    1 Mysql查完数据 再同步写入到Redis中 缺点1 会对接口造成延迟 因为同步写入redis本身就有延迟 并且还要做重试 如果redis写入失败 还需要重试 那就更费时间了 缺点2 不解耦 如果redis崩了 那直接卡线程了 缺点3
  • Java:简述try-catch-finally中return返回

    Java 简述try catch finally中return返回 Java 详解Java中的异常 Error与Exception Java 简述Java中的自定义异常 Java 简述throw throws异常抛出 Java 简述try
  • 字符串:找第一个只出现一次的字符(python实现)

    题目描述 给定一个只包含小写字母的字符串 请你找到第一个仅出现一次的字符 输入 一个字符串 长度小于100000 输出 输出第一个仅出现一次的字符 若没有则输出no 样例输入 abcabd 样例输出 c 思路1 统计各个字符出现的次数 然后
  • js Base64加密

    base64加密开始 var keyStr ABCDEFGHIJKLMNOP QRSTUVWXYZabcdef ghijklmnopqrstuv wxyz0123456789 function encode64 input var outp
  • 基于springboot & vue-admin-template 框架下的前后端分离开发,《文件上传》业务逻辑与el-upload组件详解,静态路径与动态路径

    springBoot vue admin template框架下的文件传输 我想实现的内容 前端部分代码的实现 el upload的学习与拓展 文件的upload与download 文件的show 回显 后端部分代码的实现 upload接口
  • Mysql中IFNULL , ISNULL, NULLIF这三个函数的相爱相杀

    IFNULL expr1 expr2 用法 假如expr1不为NULL 则 IFNULL 的返回值为expr1 否则其返回值为 expr2 IFNULL 的返回值是数字或是字符串 具体情况取决于其所使用的语境 以实际例子查看 SELECT
  • Charles软件使用

    Charles是通过将自己设置成系统的网络访问代理服务器 使得所有的网络访问请求都通过它来完成 从而实现了网络封包的截取和分析 安装Charles 去 Charles 的官方网站 http www charlesproxy com 下载最新
  • linux qt 动态链接库 静态链接库 学习笔记

    转自 http hi baidu com codeworkman item fa434498290bd38e591461d6 hello h ifndef HELLO H define HELLO H extern C void hello
  • Python中的面向对象编程的一些基本概念总结

    一 一些专有词汇的定义 面对对象编程 OOP object oriented programming 是一种程序设计范型 同时也是一种程序开发的方法 实现OOP的程序希望能够在程序中包含各种独立而又相互调用的对象 没一个对象又都应该能够接受
  • 转载Faster-rcnn理解

    文章转自https blog csdn net Lin xiaoyi article details 78214874 仅供方便自己学习 如有侵权请联系删除 效果图 作者 提到目标检测 就不得不RBG大神 该大神在读博士的时候就因为dpm获
  • CSDN第一篇博客,找工作日记第一篇

    今天结束了UC公司的几轮面试 不确定能否拿到offer 但回顾近几天的校招情况 比起十一之前不顺利的过程来说的确让人欣慰了很多 最近考了很多公司的笔试 也面过4399 UC TP LINK等等 峰回路转地明天还要参加百度的面试 当然还有菲音
  • C#程序演示Console.Write()和Console.WriteLine()的示例

    Console Write and Console WriteLine methods are used to print the text values on the Console Console Write prints only t
  • Postgresql查询每组的前N条记录

    Postgresql以指定字段分组后 查询每组的前N条记录 主函数 ROW NUMBER OVER PARTITION BY 省份名称 地市名称 ORDER BY arpu desc dou DESC AS row id 在原有数据表的基础
  • ORA-01157报错"cannot identify/lock data file"

    sqlplus以管理员方式接入数据库 启动时出现报错 如下 gt sqlplus as sysdba SQL gt startup ORA 01157 cannot identify lock data file 8 see DBWR tr
  • 数据结构:数组模拟队列

    实现一个队列 队列初始为空 支持四种操作 push x 向队尾插入一个数 x pop 从队头弹出一个数 empty 判断队列是否为空 query 查询队头元素 数组模拟队列 队列 先进先出 include
  • mysql注入语句说明

    判断闭合id 1 页面正常 id 1 页面不正常 id 1 页面恢复正常说明闭合是 id 1 页面正常 id 1 页面不正常 id 1 页面还是不正常说明闭合不是 如果这时id 1 页面恢复正常 说明闭合是 id 1 and 1 1id 1
  • 为何实现不了定时器DMA Burst传输?

    有人使用STM32F4系列开发产品 程序运行过程中需要不时地对外输出一串驱动脉冲 并要求这几串脉冲的频率可变 占空比固定 他想到使用基于STM32定时器的DMA BURST传输 具体点说 他期望不时地通过TIM3的CH1输出一串频率可变 占