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

【经验分享】STM32使用DAC+DMA+TIM生成10KHz正弦波

[复制链接]
STMCU小助手 发布时间:2022-4-8 23:30
前言3 y8 Y5 H6 z/ p3 a3 ?
正弦波曲线的函数公式是:y=sin(x)
! i2 t( A, C6 S# n
; |) i- B  x- U8 F2 z" hy的范围区间是[-1:1]
1 h1 I6 c; W( C! y$ i% l; P, d) P- z% u
x的取值范围是任意实数# {6 k" I& ^2 H: o
9 Y7 s5 E2 Q% N! I. C: J
周期为2π
! j7 f3 P9 A% {4 c- X* z, ~; ?8 J4 n: f. Q: i& v# D3 z$ w  H# I
如下图所示的蓝色函数曲线:
% P; O3 q5 a0 Q. G, _! ?2 z' U/ I! e8 F  ]0 s* k0 J5 H& u
R)J9`VX8$`U][LTT)@@Y%R1.png
5 V2 a; P5 k  k% `9 F. u' {$ w+ h+ j( A5 a1 C( i+ t) f
使用DAC生成正弦波比较方便的方法是预先生成一个正弦波的数据点表,为了能够快速设置到DAC上所有会使用到DMA,然后通过定时器控制DAC的出样频率就达到了生成正弦波的效果。
7 w# X1 w4 E. o- v) {
( V7 {1 J# _$ x4 D( \, A9 J那么这个正弦波数据点表是怎么生成的呢?下面就来讲解一下。1 K" [" g' O# v2 Z5 `: f( c3 A+ A; p

9 L2 v* j2 t5 B. ], M* J5 @$ x* o将这个y=sin(x)函数映射成我们现在的这个正弦波,那么y就是代表的电压,x代表的周期。# |, f6 Y# T/ I$ T% V' V
/ q* x+ r) Q8 F9 C/ p1 D
由于y=sin(x)的值范围在[-1:1]之间,DAC设置的时候不存在负数,所以就需要加1让公式生成的值都在正数范围内,公式就变成了y=six(x) + 1,现在值范围就成了[0:2],但是这样最高能表示到2V,而DAC是能输出到3.3V的,也就是说y=six(x) + 1输出2V的时候代表3.3V,所以就需要对y=six(x) + 1进行扩大,比值关系就是3.3V:2V,所以公式又变成了y=(sin(x) + 1)*(3.3/2),这样值范围就变为了[0:3.3],然后再将电压转为DAC数值就可以了。
8 D% a: v+ u0 r" t  f( k+ a6 }, }+ R# L, a1 N  t+ e, e) J
因为正弦波每周期的波形都是一样的,所以我们生成一个周期的数据表即可。
3 V% U$ i) Z8 x, l+ \. s5 ]5 ^( y3 h6 g% Q1 m0 c
经试验所得,一个周期内满足32个点就能近似逼近正弦波的效果,这里为了波形更好看,我选择了100个点。
+ Z3 x  g- S  [$ K. [
( y1 w& s$ [4 N9 S( `) s0 I周期为2π,一共100个点,那么每两个点的间距就是2π/100。
9 i3 ]+ f7 Q* J+ u6 M/ a: l; v7 g# `+ Z6 T; O: z
  1. /**
    * ~5 @+ z$ o; O) y+ T" ]9 o
  2. * 生成正弦波数据点函数" B* ~/ k4 A. L
  3. * @param NPoints       一个周期内的点数
    ( \+ `4 R- ?& ?9 p2 _! W2 \! y
  4. * @param VMaxRange     输出的电压最大值,取值范围0~3.3V
    + R) [2 V5 j. |! J5 `
  5. * @param SineWaveTable 存放生成的数据点
    5 m) G! @) k/ v
  6. */
    $ F% ?0 L0 m. G" x3 E" D
  7. void SineWaveGen(uint32_t NPoints, float VMaxRange, uint16_t* SineWaveTable)
    ) r$ j+ V8 d% N
  8. {# J* o4 L: v7 C6 n3 O
  9. #ifndef PI" e" D) i% S+ l, h
  10. #define PI 3.14159265358979323846
    1 ~6 [' n2 E# o% a$ r, }
  11. #endif
    ! b- U% y3 d. @2 J

  12. ( f* n1 M7 z: y6 ?
  13.         int    i       = 0;/ h2 }3 B3 ?/ @: ]  H" Y
  14.         double radian  = 0;  // 弧度3 B( A1 d& ^/ ?8 C! \4 a: U7 [
  15.         double setup   = 0;  // 弧度和弧度之间的大小( g' L. i' I6 O% D
  16.         double voltage = 0;  // 输出电压0 S6 m9 A' W. d2 O
  17. ( g; q. @% \- }
  18.         setup = (2 * PI) / NPoints;  // 两点之间的间距; D2 |/ n6 h" T- S4 P2 k0 `1 f

  19. ! g2 B  M' m6 {) @
  20.         while (i < NPoints)
    ! I$ a5 N' J5 o9 ^
  21.         {% M+ g1 t7 ^- z* Z. i! d7 U- P
  22.                 voltage = VMaxRange / 2.0 * (sin(radian) + 1.0);              // 计算电压
    2 O$ A: i! _1 N
  23.                 SineWaveTable<i> = (uint16_t)(voltage * 4096 / 3.3);          // 电压转为DAC数值% Q/ k% l; H7 n6 h
  24.                 radian += setup;                                              // 下一个点的弧度0 d: d0 @( r# Q1 S1 W8 T; o
  25.                 i++;
    - K5 d  {. U2 t0 @# u
  26.         }0 v  j/ x8 o3 F# R1 i
  27. }</i>
复制代码

, Z% Z$ J- p8 V0 T5 i工程配置
8 d$ j9 Z# K1 G  K1 y时钟配置到72M主频:

0 {& j/ ~. V9 Q2 F) v# i' t; Y. [2 ]% _- t; l' V: }% R
R07[V]RE()9QNS)VMMRMT5N.png # k# }, S( L* S# \7 t6 G

5 `# N. b0 d' Y  _+ U' Z+ B4 @: K; o配置DAC
9 I( ^8 C; g9 m% j' a0 l3 ~8 I$ a& f* o0 I: U3 N
4N$$YM_S{LT`{NM3X06BV~F.png 8 x) R- b  }+ n& L% @
, w( H# N: P8 l( [+ n
说明:

1 N- N' I, j9 A( v" B/ \
6 o* B+ P( T" w) V( ?Output Buffer:输出缓存7 s- Y$ P# B5 t# ^" O2 J
8 ~- Z- Q" H4 Y% Z/ T
DAC 集成了 2 个输出缓存,可以用来减少输出阻抗,无需外部运放即可直接驱动外部负载。每个 DAC 通道输出缓存可以通过设置 DAC_CR 寄存器的 BOFFx 位来使能或者关闭。如果带载能力还不行,后面就接一个电压跟随器,选择运放一定要选择电流大的型号。
$ J1 s& I) ~, v使能输出缓冲后,DAC 输出的最小电压为 0.2V,最大电压为 VREF±0.2,而未使能输出缓冲则输出可达到0V。4 [4 M7 n5 o5 f2 f0 G: x
+ y3 M5 M* C# C5 t3 }* T) q
Tigger:触发方式
) N3 o! U5 ?: r2 w' ?1 p/ Y0 I, T9 \. \, _8 ^! L
选择DAC的触发方式,可以选择为定时器触发、外部中断触发和软件触发。这里我选择了定时器6来触发DAC,因为通过设置定时器的频率就可以很方便的控制DAC输出的正弦波频率。
+ I- W" ^9 K( K& h% Z5 J& p
( D( o+ P+ q$ M* uWave generation mode:波形发生器: M$ d& Z; m7 w# a5 x
; V8 O% ^3 o' {0 o+ `/ ?0 x
我这里没有使用。
% x* m8 u: B" l
* z  G- p+ O. S# b; R- Z配置DMA8 ?; j8 T. K1 R! w; K
' K) R* q0 Y1 e+ g" C2 [3 r
7(B[K5{_`T[NN4$~D%]CI@H.png
5 x' P: B+ T7 j( j# L! \) \7 e! |! F
配置定时器

* E% W) I. [5 \# b: C2 a- U. A
0 R; S+ u/ U5 J( X# d, }$ m NY~O}[$M10@`_~6ONL_()}J.png + K3 r' [& X9 M; i% b6 G+ O9 R

& a/ M* \' B( V1 o$ d% H. a* Y8 i8 V, f: |定时器6的时钟主频为72MHz,我这里没有分频,那么把重载值设置为72,这样就得到了72M/72=1MHz的触发频率。
# c) ~6 k/ ?0 R: _$ _3 X
* R  H% r; S! `2 o7 j/ {上面说到过我的设置是一个周期内100个点,定时器触发频率为1MHz,触发一百次才能完成一个周期的波形,所以生成的波形频率就是1MHz/100个点=10KHz。
2 Q& ?; L; g# {0 N& R" K) d8 ~6 h4 I3 `% p3 M' s2 S$ {& L
最后启动定时器和DMA传输即可:
  1.         HAL_TIM_Base_Start(&htim6);
      m) Y' @& c2 @3 V* b8 c5 S9 }
  2.         HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_2, (uint32_t *)SineWaveTable, POINTS, DAC_ALIGN_12B_R);
复制代码

# B7 `0 k& E+ B0 J2 U0 a. r 7$R4%{_PB9Y5J[N94TB8}JO.png + U' _$ a* B: o6 `! f

4 R& T3 X2 _4 J- ~; l9 K生成的波形用示波器查看如下:
( D) a, M' b2 L" _! Y( p
1 y! v. t5 u$ k7 J0 ]: G9 f" s _QX6MBWHTEUHCZQB0%[RXM5.png
3 r+ `7 m$ f2 t7 |( g
; N' t- E. y" h: h) h( g( w
赞 收藏 评论0 发布时间:2022-4-8 23:30

举报

0个回答

所属标签

相似技术帖

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