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

基于STM32利用ADC+DMA采样显示经验分享

[复制链接]
攻城狮Melo 发布时间:2024-5-29 17:24
DAC,即Digital to Analog Convertor,是数字到模拟转换器,也称为D/A转换器。其核心部分由R-2R电阻网络(也称倒T型电阻网络)、模拟开关和运算放大器组成。它可以将二进制码或BCD码表示的数字量转换为与其成正比的模拟量输出。. n4 K% b% c1 P! ^2 i' [! r
% S5 P! P  l' c
DAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
6 a) u4 ]4 n% t; `

8 ^* z0 y+ ^  }: Q" ~8 ?DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。
' @; `& S$ ~% M* k( j
% G% F* h5 ?7 l5 N. N( m8 p
微信图片_20240529172405.png & U7 S1 ?3 T! ?2 {
3 F6 i1 h+ O: b4 z$ V+ z
STM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。
! ~( Z( ]; x: Y; {' S/ U
9 y# A: z  Y" d8 B' {- D5 f
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。) ?! f3 P$ a' V: _/ W% i; m, Q

7 L. L& _2 a! C1 B9 n
CubeMX配置
0 m% h9 U6 \  i' Q6 j. {* c1 P: b) w4 N  {9 l* N# ^
微信图片_20240529172401.png
2 U7 L! C& p: \) k

- R% j5 C7 u% Z' ~9 {1 G3 t在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。
) z% Z5 V) I. `) m# O5 K' v5 L3 P
微信图片_20240529172356.png : C9 p* L8 z# f( i+ j
, c( L; [* W* Q8 f. u
OutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。: U+ A& H3 a! h7 N( ^7 {+ @

4 r* g+ g; g; X& i1 @$ G
如果选择触发的话是从缓存区写入数据。! a4 v2 Y# g% }$ G8 l* c. \
1 a5 q$ W! R. f- y/ f
微信图片_20240529172353.png
+ Z, L# w) d2 D" I6 `

; g, k# [: o- T4 T1 A0 I* i; y3 u( \! _这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。
2 ], B+ a4 O6 v5 X, Y/ R  O2 W$ J5 C
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。
$ [9 m* Z- W- V0 W, r: d
0 C2 f/ r7 T5 q+ W+ N, J
STM32的DMA采样+FFT时域分析(STM32F407); Q: ?2 ]$ i; P/ J5 Z/ ^' @
$ c3 `- ]: x7 z  H5 y
但是相比于之前的,需要更改一些设置,首先是DMA的设置
+ M5 s) N9 a. D, j& _+ Z/ A" r; `! A  b* k
微信图片_20240529172349.png / N4 O3 o0 j' G2 _3 Z

7 @1 G/ P  V0 {7 ]这里设置单词请求,而不是循环模式防止数据跑飞掉。8 X4 M+ J6 O% A

3 L8 L7 j: n  _; V+ Z, B' L' \7 r

$ Y6 R/ h, V6 Z4 l1 m/ `4 N2 E
  1.   MX_GPIO_Init();7 r: n. e( p1 h/ c: I) X) a" c
  2.   MX_DMA_Init();
    % L$ P3 c5 A8 a. U3 Z' ]
  3.   MX_ADC1_Init();7 ?/ ]# M6 X& J) ~  u% }
  4.   MX_DAC_Init();
    ; b" g* b# e! M) o( I0 h3 j
  5.   MX_USART1_UART_Init();
    5 G/ K- t: F  G
  6.   MX_TIM1_Init();
    ) f" k9 k5 Y) x! h8 {- u2 ]
  7.   MX_TIM2_Init();8 B( u6 c5 e3 G9 [. I" u
  8.   /* USER CODE BEGIN 2 */& ~; x! h$ ^! y% v; I3 s- Y
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    # i1 P& S) G; O4 S1 D
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC# @3 W# y$ t7 p
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    1 t+ X4 l6 T6 C; S2 F8 |
  12.   /* USER CODE END 2 */
    7 \; ]3 o* t# e6 J8 _8 X5 n% l

  13. / j+ n7 |8 ?7 c1 u$ L
  14.   /* Infinite loop */
    , Q! Z: F4 }3 P1 ^
  15.   /* USER CODE BEGIN WHILE */
    . q8 _% T/ p: h3 r2 A* _
  16.   while (1)
    6 h) a0 [% P4 {/ U! y9 |
  17.   {! q$ P+ l' U5 }/ M
  18.     /* USER CODE END WHILE */; }; C7 V" q/ h
  19. & z% W& G( K0 n0 R' f+ L
  20.     /* USER CODE BEGIN 3 */
    , {# R% r; j+ e( O: x; z+ g4 e+ {5 u
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))' p, _, H5 K8 v  W
  22.     {9 [: A! y0 M8 P9 s$ n
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
      D: y2 D2 t2 {5 z5 i2 P1 j' j
  24.   ^* P9 W4 l0 G5 A/ y
  25.       HAL_Delay(20);
    / s0 I- B! ~9 A. @% Z
  26.       
    : v9 A  Q0 c$ {: Y
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));& a6 t- P& m0 U+ \8 l( I
  28.       HAL_Delay(1000);
    ( U0 Y5 M; d) q* w, C7 R
  29.       for(int i = 0;i<ADLenth;i++)
    $ m! ^$ W( f' e' G3 D# _- c, a& m
  30.       {- b6 W; Q# u. ~2 K# \$ o
  31.         printf("A:%d\r\n",AD_Value[i]);2 O( D4 C4 q1 U
  32.       }
    # g2 {! [9 N1 E# g
  33.       ( s6 c6 N+ q" i* j5 I# k% n* Z9 l
  34.       
    7 i2 L* i; P! _) i
  35.     }
    , W: m6 ~1 s+ Z
  36.   }( w1 z2 E0 v' e
  37.   /* USER CODE END 3 */% C$ r+ N% z9 D. l3 S) R# k3 K
  38. }
复制代码

9 U: R1 ~% ?1 J2 Y/ V/ H; Y8 [% h/ }8 O9 }1 F3 h& Q+ ~3 D
我们编写一段代码。3 k& O0 _7 E4 S6 R  x8 Q4 S! t

* U  l% I3 R4 [8 _7 p/ H, U
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。
* j" T4 S' e5 C* F) w
0 t. C; T4 X- t) _8 t# {6 w. I, Q 微信图片_20240529172345.png $ |9 {0 P/ o0 T8 v

* ?, k* y* K9 k* \4 S& c+ R  Y可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。
* l7 g" ~* V+ I
) v$ U/ e" d1 j6 X/ V6 @; W
三角波发生器6 r5 l) `% H, W" h

. k* Q: H1 e# i' L
微信图片_20240529172342.png : q2 L+ {# b- H  W" h  F
* l% I# @0 v, H; a- c# ^
在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。. _: v! z) l: w

/ {. t4 `, T. h7 Y! j! r 微信图片_20240529172337.png ; W  c& A8 }; C7 R2 Y% I
! k0 ^9 o4 X( B4 Z8 L# V2 P
之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。3 a0 F/ H" T8 d% n, C
! a" `& L) _* g; P7 W+ Z2 X2 ^) \
微信图片_20240529172334.png & V/ l2 }* J& ?& r* r

# O' `& l/ |/ {) ?: z8 Z* o' H8 j; |定时器设置好时间之后,设置触发事件。
: G* T* g; M- |3 x" y" X3 Q1 t. I- o& K- O0 s8 u
微信图片_20240529172331.png ' u1 ]6 v, l+ K  A! K

* P( @+ G& \* G: G芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:& Y- P- |) h0 a2 c  E- ?

) E$ T: A. V$ L8 w+ J; _
定时器频率/分频系数+1/Period/三角波最大值/2! Y% ^: E1 m) @$ R& p) Y

6 U  N. h& c2 C, I( S( |4 i
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    ; u' D  ^3 {5 {1 D& b% v1 q$ ~5 i
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出& @* H" Z- r  h+ i! q$ {
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC' O- @2 z( s4 ^
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);) i' h  k, h& y1 x2 M
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码
. ^: h. \8 `# W6 e
微信图片_20240529172327.png " F% f5 J# D' t2 A( ?( Q! \4 K! `
) W2 k* ~7 G+ G. o% p) {
正弦波发生器; q( Y$ m( g* g0 x, k5 T8 I  d! Z# g

; A0 d2 D/ f4 ]- S; C8 X# o7 ~! J
微信图片_20240529172323.png : D* Z; I% K) Q& A* e, k8 h) o1 J
9 w* M. G: X. m9 ]1 b: o
首先是定时器触发(方便控制频率)。但是关闭波形发生器。
: C$ u' n5 b$ {+ N* t% c: ?
" b  G7 c/ W: A3 H7 j1 Y3 D 微信图片_20240529172320.png # R% S/ r2 L' h4 D% N2 q

+ {4 ]1 ^, k6 h% k9 y& f! \3 E添加DMA,模式选择循环模式。
& ^$ X3 K* m& _7 h  N
' e' ~  m" o/ X$ R8 P+ W- K/ x" q) B
还有改变一下定时器的频率!
3 Y0 T) [. M, ?: O: x
  W- {. W$ m% ^
其他几乎不做改变。" W& k1 F5 q" _3 S

1 ?9 `" q# o! d# j
1 |% y% ~6 p% T; c; x8 x
  1.   #define POINTS 256                        
      }8 q( p) _$ w/ I  y
  2.   #define MIN_VALUE 50                        
    0 A- H: B/ _2 K+ a+ p1 J
  3.   #define MAX_VALUE 650                        
    ; Q' X3 M) v; a, _  \
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  " H+ t6 G4 g) F
  5.   #define OFFSET 50  ) j' h+ S. ~! k7 F, T. R; [' w3 {* B
  6.   #define M_PI  3.14159265: k1 b0 f" ^  [2 b
  7.   $ e, Q4 @  Z% l( h
  8.   uint16_t SinWaveInt[POINTS];
    / _9 O  l- p9 r. W# [" p
  9.   int SinWave[POINTS];
    ; N" r2 W, P) E5 [
  10.   void SinInit(void) ) y* C) O/ y9 ]
  11. {  
    4 H& b3 q' X% f4 s6 W- L) a5 z; q4 }5 q
  12.      for (int i = 0; i < POINTS; i++)
    7 h# G( e0 V9 G
  13.       {  
    ' q, v, w5 e/ v: T. x# X3 Z1 {" T
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI; + ?0 ~: \8 W: I, `, p6 i: u0 \
  15.       double sin_value = sin(x);  // 计算正弦值  
    + X" O% |7 a; L" Q
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    : q" u' P" m& J; k
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    " q+ ]# M# k# O* D# C0 c. I
  18.     }! j) o! K8 l3 }) [
  19.   }
复制代码

3 ?) E' l3 n$ O3 _. U- A4 _8 S计算一个正弦表。- v7 O: ~! j! t

- Z5 C3 c4 W0 d( h
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。
0 o! f$ w* [+ b7 }9 ]0 @# J4 Y$ u' U4 y% o+ U
微信图片_20240529172317.png / O- K  h* f) ^4 w" u1 g+ r0 c

3 f: b4 p2 x$ ~' Q$ k& a- M/ Q# D: w测试一下正弦表,输出的是正弦信号。
: ]7 x0 S1 k' C+ X# Q7 F! I  l. {3 N5 z1 O0 i% W
我们之后将正弦信号表导入DMA中。
+ }8 O  p( O0 b. j+ B4 \' o3 f7 \, O2 _
  1.   SinInit();
    , T. g6 Q6 _0 F$ F0 q# f
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码
8 x7 w6 r/ R% ~
正弦表初始化,之后启动DMA传输,导入正弦信号。. V- r7 X+ F  S- m, w* H4 H. `

( K2 s5 d  S0 V6 G1 Y2 R4 y 微信图片_20240529172311.png 1 u% k/ \8 k  C8 {& H
8 q6 Q2 O) ]3 H$ w$ d
测试正弦信号成功。
  c4 _( m. K; G& k3 S* c% I2 \4 M0 w2 M
FM调制
  1.   #define POINTS 1024                        
    . b; _* R9 j" j& T. o! g
  2.   #define MIN_VALUE 50                        
    6 S4 \; z  Y7 w/ `2 P, V. O
  3.   #define MAX_VALUE 650                        
    0 x; F: M/ Q: q4 ?
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    - _- n$ F) T- k7 O) T4 i
  5.   #define OFFSET 50  % |& f  l1 n0 N
  6.   #define M_PI  3.14159265: C2 f: U) S' Z/ r. a& P* y
  7.   
    . A4 [4 W  r" O' q
  8.   uint16_t SinWaveInt[POINTS];
    $ u% m# J8 k: d: [$ r' z* S  x
  9.   int SinWave[POINTS];
    % K: ]* f( e5 e/ r& h) ]2 @
  10.   void SinInit(void)
    7 f. b( y  S; \7 M
  11. {    r; K$ c/ S) h. p' q
  12.      for (int i = 0; i < POINTS; i++)
    ) |9 Y* ~0 K5 K" r% B* r7 L/ ], o
  13.       {  
    4 w5 R$ R# A# f8 U: N
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI;
    ) h9 U2 u4 t# V1 u8 A% |
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  
    % l, s) @! I! ], e, G: X
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); ) _' `  ~) g0 U- ~
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    & Y1 f4 ]0 f5 ~4 c* S7 o, k
  18.     }
    3 B8 q  M+ g$ S5 }. s( r/ d; a
  19.   }
复制代码
3 g. a' N0 p4 j2 x1 @1 T$ c
0 R$ g- ^' X) E3 h; e. k# ?
拓宽点数,加上载波,即可构成FM调制信号。3 g( J3 k2 f1 q* C% l, E2 t
3 O: y: Q8 B& ?5 _. Z  h1 _
微信图片_20240529172305.png
6 {: U; k8 [4 c, w3 t( S
' F! B$ Q1 q" l8 ]6 G% ^
% m- @4 }$ S7 f7 [) `; ~) _5 G转载自:电路小白& d  r& S& P7 \5 s+ ]" n( q
如有侵权请联系删除
- T% K4 N( C& M. M9 u: _  B" |/ G( c( b+ F
# T6 C2 C3 Q# P) o+ y6 m

' \4 E! z/ y2 h  d# d$ }
1 收藏 1 评论2 发布时间:2024-5-29 17:24

举报

2个回答
STMWoodData 回答时间:2024-5-29 22:06:30

资料不错,值得参考学习。

喻皓文 回答时间:2024-7-12 14:10:53

dac+dma?

所属标签

相似分享

官网相关资源

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