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

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

[复制链接]
攻城狮Melo 发布时间:2024-5-29 17:24
DAC,即Digital to Analog Convertor,是数字到模拟转换器,也称为D/A转换器。其核心部分由R-2R电阻网络(也称倒T型电阻网络)、模拟开关和运算放大器组成。它可以将二进制码或BCD码表示的数字量转换为与其成正比的模拟量输出。4 O/ I; ?, G8 ~
- b  E" ]# h* t) S1 ~
DAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
  H; A" o2 M  B
$ ?! H3 o' |7 v2 L! f
DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。
4 g* [" Y& q" i% l$ }! M% `% c; ~" Y# x- O0 S5 p, ^
微信图片_20240529172405.png # q2 @+ A6 o! q+ x( b1 b; L' U

+ `, }* ]1 i) \  k1 eSTM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。3 }1 W9 M1 {: Y, g; @9 [
$ o: T- Z+ b* M7 k
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。
( e$ e6 l' [. c2 B% v4 j1 u( W$ C  J& l3 [2 Q
CubeMX配置
* d4 r; L3 @& V" E0 u9 D6 z& v8 M
. i" j4 g' h8 N 微信图片_20240529172401.png
8 [* L5 Q% U- s, r! h
1 }$ Y8 Z, c, d8 R
在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。
- b! Y9 A7 D+ X, I
& o2 Z4 D8 Z! S' }" t) @6 U) ` 微信图片_20240529172356.png 7 G; V  N  l. \
+ |5 x; w/ M7 K, I
OutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。
# `; S& Z7 C! `- f% b) N8 t) v9 T% D+ c# q% i. _
如果选择触发的话是从缓存区写入数据。% g0 d! z# G( e6 F* w
$ i# E( g$ O# F) m" t
微信图片_20240529172353.png 8 C6 K/ c3 I  {
2 n1 f8 M! O: f' M
这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。
7 j1 z& t" E. K; `
# N  n* `# O  n. r9 A/ D7 w
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。
7 g' E0 A( J2 E  y% t1 }, h, Y6 s* H4 l3 J
STM32的DMA采样+FFT时域分析(STM32F407)
; V: H' X0 _7 z7 y3 w  \; r2 E! y* e, E6 t# f/ E
但是相比于之前的,需要更改一些设置,首先是DMA的设置
  g1 `3 }7 N* [. V; }8 D4 R6 @! X/ n5 P2 s1 U1 D
微信图片_20240529172349.png
! I$ g. g  {, y& ?! Q. Z7 r% s
/ f) M8 e8 G2 [* K" X- }# J4 p
这里设置单词请求,而不是循环模式防止数据跑飞掉。) Y  s& ?; g; ]; y& X8 a

1 V( B6 Z& @  k) `' t

; X, h! {5 y; e# a
  1.   MX_GPIO_Init();
    ! ~/ t0 [0 a3 f
  2.   MX_DMA_Init();# m* _& Q3 w" d
  3.   MX_ADC1_Init();
    % O& `" _& @4 I
  4.   MX_DAC_Init();
    2 \  `+ R) Q3 C
  5.   MX_USART1_UART_Init();5 D/ Z' T; J6 U: M# S" ^/ |8 I4 t
  6.   MX_TIM1_Init();( K6 B( Q# ?' O+ E$ f( P
  7.   MX_TIM2_Init();2 ?' \3 Z5 m+ O, i0 k/ Q' ~
  8.   /* USER CODE BEGIN 2 */
    - `4 t1 T! i! i
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样 2 c* K' w6 t- @, ]6 I5 T
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    # C0 f: b# w+ E* @# e
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);7 W# C- J2 [" T* j. U% n1 \. V
  12.   /* USER CODE END 2 */
    9 r5 \1 e( r) [7 m* |( o
  13.   y# e5 C, W$ N+ h; E
  14.   /* Infinite loop */5 {  b" w3 n# G8 c9 x) f
  15.   /* USER CODE BEGIN WHILE */6 H  N  q+ H) W, V
  16.   while (1)
    , b8 J$ {/ X0 e5 V
  17.   {
    ( T! l. j5 A# S5 s& d1 K+ R
  18.     /* USER CODE END WHILE */, g* |9 |) V9 y4 I1 ^0 w
  19. , N  s6 Y9 i( B) M$ }
  20.     /* USER CODE BEGIN 3 */
    ' r* x6 c. [/ J3 ~& I3 j/ l* ]% f$ E, D
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))5 E& l& f" c, o  N6 T' Q
  22.     {$ b( b& ?5 K+ ^! E
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC" J" F8 Z$ J$ f# W3 H3 \" `, _$ J
  24. - B5 J1 m0 E: X% N) A0 l
  25.       HAL_Delay(20);( p) d8 F6 u8 E# g) o$ v
  26.       
    8 ~  d! ~: l  v+ y3 A
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));
    5 }$ _" C$ R/ ]3 L- s" Z
  28.       HAL_Delay(1000);
    " ], L( f4 Z$ O% d3 x
  29.       for(int i = 0;i<ADLenth;i++)
    : P$ o5 B: ?4 B* `/ T
  30.       {
    / X! m! t8 }& b! h# W- Z
  31.         printf("A:%d\r\n",AD_Value[i]);
    5 r  r& {; o' v
  32.       }( m( u* L0 ~, o
  33.         \  u0 H1 f; t
  34.       : V: I$ p/ n, L, |$ H0 h" O
  35.     }
    7 o+ ]5 ^7 w. E5 d: {+ C# W
  36.   }
      w( i$ b; o$ e8 c
  37.   /* USER CODE END 3 */
    ) @: p8 j4 Q" b5 G$ E
  38. }
复制代码
+ v) h6 x  q6 ~4 w' n2 O& }% e
2 S+ D$ p/ I) L/ V$ o. f
我们编写一段代码。
4 l2 D% {: l. U- U: e  F8 m7 {. _1 i9 |4 Z. c4 T! o: Q3 @) n' M
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。
5 K# R; j3 H, F+ ^* U/ M5 t, f8 J$ ?( C5 }4 |
微信图片_20240529172345.png 1 k  V$ W1 A" U* k9 f
4 \" R1 e5 P. ]# a2 c. @/ n# z
可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。) {3 T$ h9 ~1 _5 d+ U3 B6 C
0 _3 C  P% i& t4 l2 L
三角波发生器0 G- X0 ~8 d/ U

1 }- u1 }% z7 i2 X& p/ T8 r* ~
微信图片_20240529172342.png
2 e4 r, J8 d4 y+ w- y
6 W% O  h, c; T# K0 c. _
在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。2 w( m6 [; _3 a& N9 O- S; ~
/ W9 p3 q% n. i% M3 I( }( P
微信图片_20240529172337.png
* v) g* B) z6 J
2 d) V) n/ B7 p5 G6 l* A
之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。; }, X* T% Y, s; A/ B0 {1 R& X$ y8 R
1 I1 Z8 F/ d9 Z1 N1 R8 B7 P( H
微信图片_20240529172334.png
, p; H" y3 F, d0 ]- f/ e
5 Z- e7 `7 c  g. `
定时器设置好时间之后,设置触发事件。% u! }: b- I6 m. c. T6 A9 v

* C1 s( N" T$ z- l8 ]' u4 o 微信图片_20240529172331.png & G+ u/ a0 ^6 ?' e# f" L9 V8 A
( a  I  d) j. V  w: q
芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:
5 {9 J9 X2 S; l. G/ E
* S9 d8 P( }" {4 j" y3 ], r
定时器频率/分频系数+1/Period/三角波最大值/2
2 e& Q/ w& g* g5 R6 x1 D3 e' ^5 }+ j' R/ U
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    6 K- y5 W8 l: \+ y0 ~5 o* A
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出  I7 {* @/ P1 Z& t. l& R
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    8 ~- j( Q& z" c9 ]
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);) J; Q+ B3 P  A8 P
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码
6 v4 V0 M5 C/ A1 x) }. A
微信图片_20240529172327.png
$ i8 {9 j4 Q. e) x

$ e/ m. A0 v3 c/ p正弦波发生器: Z* t- Q9 L9 I" a2 U. O
) R9 ?  r5 j" }' F- X
微信图片_20240529172323.png
2 p$ ^: v! a7 j; @8 U+ e% h) C9 e

1 M# E! T- P5 v5 e; V8 m首先是定时器触发(方便控制频率)。但是关闭波形发生器。; Y, I/ z0 Z" F8 n: r
# F- B, j& T. [: z6 L0 `9 O
微信图片_20240529172320.png
( E% E: l) e: \

/ m6 |3 Y; @4 t" ~添加DMA,模式选择循环模式。
. H  `. L" ]4 l3 H# i+ f, i8 \4 s9 o! |2 t4 x1 E# `& g  E
还有改变一下定时器的频率!
' j, L: S% t, p: A" F  F6 ~6 w# u  h" ?1 g% X) }/ N
其他几乎不做改变。0 ~! W( C, v5 h

& d6 R9 L4 D8 S  k
0 H8 z( k, z8 \5 z, k
  1.   #define POINTS 256                         ! v  R2 b$ l9 p% E6 R7 t% ~: B& H
  2.   #define MIN_VALUE 50                         . o5 d2 b2 B3 B' p7 y: ]( o
  3.   #define MAX_VALUE 650                        
    9 r8 n! H6 i1 K" L. f+ t$ Z
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  ; l0 w" ^; k! I- K
  5.   #define OFFSET 50  
    2 `# n  m  m) [& `
  6.   #define M_PI  3.14159265
    . x. d. u. x2 C
  7.   
    6 k# J" b$ j" }: v
  8.   uint16_t SinWaveInt[POINTS];  t2 L- V" V1 `3 b+ R0 n) j
  9.   int SinWave[POINTS]; # M7 @& c) u# D( y$ X
  10.   void SinInit(void) ; B5 V; h( Z$ U9 z# w0 V; N' [: L
  11. {  - K, M( }: |6 |
  12.      for (int i = 0; i < POINTS; i++)
    : t& K5 E% `. H' C) w. I7 T
  13.       {  0 K, [: y7 A1 g+ }
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI;
    ' A- w7 S6 |2 j* u/ J! b5 B& P
  15.       double sin_value = sin(x);  // 计算正弦值  
    ) f2 i7 C; |$ r, |9 x7 N
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    ; D2 Y' ^* M6 z
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  $ b6 z6 P, ~& I
  18.     }( m2 u" {! A% v7 F) L9 }
  19.   }
复制代码
7 ]# G; \7 @' p4 I
计算一个正弦表。4 @# h% ~! g. s  p9 b. y& }: y  i

& ?" y1 U1 \2 ?
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。
4 v7 V, q$ g* Q  z- a8 [/ \) @7 ]
7 s0 Y0 ?7 T# D; E; E* U. j 微信图片_20240529172317.png # B1 ^% d) _' }& b! P& g
  }, q" Y" K3 h2 ?
测试一下正弦表,输出的是正弦信号。
8 o$ K6 j. }* ~& b6 C& K9 w  S2 Y/ }+ f3 ]
我们之后将正弦信号表导入DMA中。# N1 L" E" a' v" I

2 K: k8 ?8 y7 e+ Y
  1.   SinInit();
    1 d8 L& |, v! p- E2 Q
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码

1 |  P8 D0 E! y* w6 y8 C正弦表初始化,之后启动DMA传输,导入正弦信号。& I2 R" X) L& F8 e! y
. w! `! D- \# ?) A0 s
微信图片_20240529172311.png
2 X7 k, I3 s3 z, F' V1 F$ F; m
! Q# j8 X$ r# o5 o1 X; B  b: n. {
测试正弦信号成功。
( `- u# ?- M* ~. R& H
; L' d4 L( s- A2 M. ~9 \3 t0 d
FM调制
  1.   #define POINTS 1024                         , h& g7 `4 u. ]
  2.   #define MIN_VALUE 50                        
    8 o- i( K! w# M' e- Q% L
  3.   #define MAX_VALUE 650                        
    " Y( Z  y( h/ A) M* u2 q5 s
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    / G) z& e7 c7 z1 o7 R% k$ b$ Z+ W  E
  5.   #define OFFSET 50  
    5 k* X1 d3 y" ~, R
  6.   #define M_PI  3.14159265% [4 g. Q9 z+ I! z- i, H
  7.   : }9 l3 B5 ]9 r/ o6 Y) P4 ?! N, E- _8 L" e
  8.   uint16_t SinWaveInt[POINTS];+ Q. X& d+ C2 v7 g  [' q  P
  9.   int SinWave[POINTS]; ; V' |2 s2 b  f6 z) V$ q3 L
  10.   void SinInit(void)
    1 I- G* ~) K5 d
  11. {  
    - J: N  L1 }, U; c
  12.      for (int i = 0; i < POINTS; i++)
    6 I6 ]5 Y% `/ B
  13.       {  
    # Y6 m$ ^5 i& n  o7 m& F! o' Q
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI;
    / K5 k/ w4 x/ q* S8 T* ^3 _
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  
    7 y/ u  D5 p, d0 f' I; V" T
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); ; ^- H0 [- t' C1 S' O8 W
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    , N, Y" a7 ^$ A# f4 l
  18.     }
    # E" A$ V; B' r; v+ D2 R9 R
  19.   }
复制代码

# [" C* I% y- [  E5 ~) l1 |* x# ~7 m. c9 F+ j5 ~+ E
拓宽点数,加上载波,即可构成FM调制信号。& ^. W1 M, @2 u- X; L

6 ]' Z- a! N& g 微信图片_20240529172305.png , d  r$ @$ }; W0 M1 F& D

4 `. z& i8 ^% I+ x) |. C; @$ k
8 J, ]* W9 V7 E' t; N2 t6 T4 k! c转载自:电路小白
: ^- Z1 M# }6 n1 `0 f3 e. L如有侵权请联系删除! W# E& @- W- [& w. V5 N

/ ~, _% H" E# L( X6 ^
# e% y3 _, ?5 T/ q. d- K( X8 ~! [. T! o2 U% R- l$ k4 c
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管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版