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

基于STM32 DAC+TIMER+DMA经验分享

[复制链接]
攻城狮Melo 发布时间:2023-7-11 22:39
微信图片_20230711223554.png
9 `2 O$ P$ W- z3 c; r
现在有如下图所示的这样一个需求,希望使用STM32芯片来实现。
/ C; _, w7 m0 a/ W" L  P# f9 Z
横轴表示时间,纵轴表示电压【3.3v为限】,不同时刻的电压输出不一样、持续时间也不尽相同。
% S: ~1 D& f  A
微信图片_20230711223550.png
$ A, E9 x, o/ I, O" O7 \! E
此问题源于某高校STM32学习时的习题,这里拿出来一起交流探讨下。方法不是唯一的,尤其基于不同STM32系列。这里尽量使用通用、常规的方法,算是抛砖引玉。

& Z/ j! S5 A# ], a8 h) Z1 R, s
显然,我们可以考虑使用STM32的DAC加TIMER以及片内其它资源加以实现。

3 ?# F% z9 M) K  @% t, O
对于这个实现我们可以分两种方式完成,每一种方式同时也体现不同难度。
( q5 S3 A1 I' A' W/ l
我们可以考虑下面两种应用情形:

  o3 H9 B% d& J; R8 I
第一种方式:MCU除了做这一件事外,还做点别的,比方做按键响应、ADC采样这些,整体上没有太复杂的功能和要求。【中断方式】

  `0 j: S. p$ L9 T
第二种方式:MCU的主要工作是别的而不再是这个输出了,要求该输出自启动后不再需要CPU的参与,即由相应外设自行完成。【DMA方式】

( s$ A4 l2 L: q# ?+ _& \
对于第一种实现方式,我们可以用个TIMER作为时基,每到适当的计时点就通过TIMER中断及时修改DAC的输出值而改变输出电压。至于对DAC输出寄存器赋值,可以直接在定时器中断里操作,也可以先在定时器中断里设置标志位后在主循环里实现修改,可以灵活决定。显然,这样操作也不会影响其它按键处理、ADC处理等。该方式的实现就介绍到这里,重点聊聊第二种方式。
* z" h+ a2 w7 d. x3 D
对于第二种方式,显然不能使用中断,这里就得DMA出场了。因为人家要求该输出自启动后不再让CPU参与。这里有两个量都是变的,DAC的输出值在变,不同DAC输出所持续的时间也在变。这两个变量都需要DMA帮忙完成,显然DAC的输出需要使用TIMER事件来触发DMA,这里使用更新事件比较合理。那么,TIMER自身的数据更新又如何实现呢?我们可以考虑使用TIMER的比较事件来触发另外的DMA请求以更新自己。
# u/ w2 g& Q" v' v6 i
下面我使用STM32F4系列芯片的TIM1及DAC来实现第二种需求。【当然,使用STM32其它系列,比如G4,H5,H7,U5等都可以】
2 l, t2 i+ l% T
TIM1的更新事件触发DMA,修改DAC的输出寄存器的值以改变输出。另外,选择TIM1通道1的比较事件触发DMA【哪个通道比较事件不重要,能触发DMA即可】,使用TIMER DMA Burst传输同时修改TIM1的ARR,RCR,CCR1三个寄存器的值,此处RCR始终用0值。因为这里要修改CCR1的值,RCR夹在ARR和CCR1寄存器中间,做Burst传输时RCR必须每次被使用。【这里CCR1的值其实也可以固定不变。我是每次取ARR的中间值作为CCR1的值,不是必须的。主要是考虑到有些应用场合可能需要动态修改CCR值,在此特意拓展下TIMER Burst传输的应用介绍。】

5 V# J' X, |. ^5 D
下面是关于TIM1时基参数的初始配置,其中ARR和CCR1值我是随便设定的,算是个过渡值,目的就是产生更新事件和比较事件,之后都会按照代码中预定的数据运行。

- ^) ^9 j) u' @9 h: D
: E0 O2 h% ?6 P; `. u: i3 M" I" [
微信图片_20230711223616.png

4 @% c, X# x/ \& b3 X7 a) F
下面是有关TIM1的基于更新事件和通道1比较事件的DMA配置。
7 y0 K) P* e" O# w. K( N
微信图片_20230711223613.png

+ A  }4 Q1 k6 P
下面截图是关于DAC的CubeMx配置,比较简单,开启其输出功能即可。

( w4 l' p- p* F$ V9 O
微信图片_20230711223610.png
4 X1 W! J% i1 M2 v) P0 I
下面截图里的数组DacOutData[10]存放不同时刻DAC输出所对应的数据。数组PulseData3[30]存放10次DMA Burst 传输用到的数据。显然这两个数组数据在使用时间上要匹配,否则输出波形对不了。
- z# `, J1 \5 T4 {: t
微信图片_20230711223602.png
6 E6 B' }0 J" A# I$ v1 E2 [
下面是具体的用户代码,使用CubeMx进行配置和STM32 HAL库函数,以源码形式放在下面,供有需要的参考、使用。
  1. HAL_DMA_Start_IT(&hdma_tim1_up, (uint32_t) DacOutData, \
    - O. j6 M6 \6 n) `: [  l/ p6 k; p
  2. (uint32_t)&hdac.Instance->DHR12R1, 10);
    2 d% r" m7 K/ q! H9 h- D

  3. ' a/ H+ V9 m) X+ B( Y: H- \- k) v! H. N
  4.   __HAL_TIM_CLEAR_FLAG(&htim1,TIM_FLAG_UPDATE);
    9 G! ?# _: z+ ?/ \; Z4 d+ T
  5.   __HAL_TIM_CLEAR_FLAG(&htim1,TIM_DMA_CC1);4 ^: r) |6 ^/ e3 D4 l
  6. - I8 q+ N6 M& Q5 V9 _) k
  7.   HAL_TIM_DMABurst_MultiWriteStart(&htim1, TIM_DMABASE_ARR, \
    - D, W' h/ q/ L
  8.   TIM_DMA_CC1,(uint32_t *)PulseData3,   \9 S0 X7 Q- _6 O+ X! D2 O
  9.   TIM_DMABURSTLENGTH_3TRANSFERS,10*3);
    + X3 G1 Y& c' v2 ~
  10. " I' v7 a. C" a+ E7 i- n; R- Y
  11.   __HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_UPDATE);
    ' L6 k* Q8 `: P- o  G) b4 g

  12. 0 P6 S  \( @' X3 S+ Y- e
  13.   HAL_DAC_Start(&hdac,  DAC_CHANNEL_1);4 w, A) N( u/ I0 M. R" i  @
  14. ! P" g6 ]: P7 r& g1 f7 [
  15. __HAL_TIM_ENABLE(&htim1);
复制代码

2 N# t  v- _) d; h
下面黑底黄线图是基于上面配置及代码的最终实现截图。跟最初的需求曲线进行比对,不难发现是一致的。
7 ^& i9 R3 ]/ r6 ^; M' i! B
微信图片_20230711223559.png

, e9 k5 I( G2 u! [8 }" `
5 o" Z" p% {$ c4 ~6 y
OK,今天的分享就到这里,是些比较基础的东西。只有掌握最基础的,才会有最灵活的发挥。

( k" ^8 }  i) |
转载自: 茶话MCU

) @# R  A1 I7 h8 ^8 N
如有侵权请联系删除

. n4 \( ]1 ~/ `, ]. c6 Y2 x* n/ ^

. f6 ^0 m2 Q8 H+ m8 L  N  s
111.png
赞 收藏 评论0 发布时间:2023-7-11 22:39

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版