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

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

[复制链接]
攻城狮Melo 发布时间:2024-5-29 17:24
DAC,即Digital to Analog Convertor,是数字到模拟转换器,也称为D/A转换器。其核心部分由R-2R电阻网络(也称倒T型电阻网络)、模拟开关和运算放大器组成。它可以将二进制码或BCD码表示的数字量转换为与其成正比的模拟量输出。: r- w9 ]' D, `8 t

, P# k# V* N6 q; R+ \7 d- VDAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
1 y; K5 t2 o. Z2 ]

9 }7 S  {( Z& N  O( `; v6 Z% z$ T4 c8 yDAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。4 O$ n* K3 [& b* P' U8 [( I2 h4 h
& C& u. Z8 G8 F4 |. y/ K4 {
微信图片_20240529172405.png
- h( T# {+ ]9 |
' G( D/ p. f4 w1 W. D
STM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。
* k( V: E1 b$ g# N
+ A$ Q( ^% q! |5 J
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。
& ~. z1 y& ]# P3 k/ a
% m- u5 Y- r% H# T" v
CubeMX配置$ L, A2 E+ R7 H5 \1 u  }1 N& N! }% p
- n' H: _5 C+ z1 e# Z! F* J
微信图片_20240529172401.png ( y' ], H! ]- K, O7 u7 L
( b3 @. W7 k6 f' ~1 `. p
在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。
9 h0 z4 P- A; o7 x0 m$ Z7 O
4 B3 A: {; w/ n- P2 j% E 微信图片_20240529172356.png
# l1 V! r! w' S: O3 R

5 |% `* m- J; B; F& x7 _OutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。
9 h( Z0 J' Q2 H' E# S2 n! q8 x) J7 f/ X; J8 x% y. z% W8 b$ a
如果选择触发的话是从缓存区写入数据。
! [' R9 j6 r4 O' C. q" y5 O
; Y0 l" i( V/ G4 m7 o9 N 微信图片_20240529172353.png : ?' h' Q2 R* {" W, {6 f( s

9 k- F2 Y1 I/ i2 E: u  y# c这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。7 k9 {4 V) E8 n+ u9 S0 U: N9 M
4 A, Z8 ]  F2 y6 D8 l
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。
  ]0 c5 U/ q( s. [; P0 T( G
/ T& ]0 R9 t$ a0 L
STM32的DMA采样+FFT时域分析(STM32F407)
+ ]9 y  N+ S8 x
8 T1 `0 O" G  g1 W( x1 E) g6 [4 E
但是相比于之前的,需要更改一些设置,首先是DMA的设置
! P6 h/ k9 B2 w' S9 [% V+ B6 n0 ~8 {8 o
微信图片_20240529172349.png 3 ^  ?. Y& v2 Z  f
) [5 g3 G' E) t# {
这里设置单词请求,而不是循环模式防止数据跑飞掉。8 n6 B6 u! w4 q- n* f& L
8 l9 T7 C3 b; E

$ e( G- Q* A0 V# [3 S; V2 h6 \
  1.   MX_GPIO_Init();
    4 V2 o3 |8 v, ]3 R5 _
  2.   MX_DMA_Init();8 H4 ^2 Y/ l' ~- S0 C
  3.   MX_ADC1_Init();
    ; E) p$ r+ E4 M' j/ e
  4.   MX_DAC_Init();& Z! F9 L) P3 L, D- ?
  5.   MX_USART1_UART_Init();
    & O6 U- B! i1 X4 T9 m, g  O$ ^; n4 F! O9 g
  6.   MX_TIM1_Init();) I" k# u1 z; s7 B
  7.   MX_TIM2_Init();
    ! Z6 g$ c; N. ]5 Q/ |
  8.   /* USER CODE BEGIN 2 */
    + X2 z) y# S! b6 }
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样 ) E( d7 M0 e9 y, ~  W& C; P
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC& d- S+ s* R, h, l- E! D1 {$ v
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);5 A9 }/ a1 t0 y2 F. w' g, H
  12.   /* USER CODE END 2 */
    7 L+ G2 Y' F' |5 n, D5 @) Z- U" S
  13. . r) w9 F* x7 y5 q1 ]8 o
  14.   /* Infinite loop */2 e5 t' r2 R$ i2 M! @9 C' Z- s9 s
  15.   /* USER CODE BEGIN WHILE */9 H3 }3 A+ B$ `! Z$ h% J3 q6 a
  16.   while (1)
    ( L- [- m9 G8 {& @# b( k
  17.   {
    - J# C( _% L+ k5 q8 |' H& r
  18.     /* USER CODE END WHILE */9 V9 {/ X# F! D. A+ q2 _
  19. 1 J* g6 T) e$ u! P- v8 t- g
  20.     /* USER CODE BEGIN 3 */3 h# a7 E9 L# T1 B" ]- N' t9 V
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)), ]$ H. E- a) f) S( r6 n  y/ ^
  22.     {
    : I1 e; q  o8 ~" u% h' i( @
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC- J4 q3 o/ I3 s2 }5 _  M

  24. ' v6 m2 s% c1 m- R& Z! o" H0 \% t
  25.       HAL_Delay(20);0 C3 y( p6 |& _! O9 n. N
  26.       
    - ^; d/ D4 Z  M$ T( |; }+ q
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));6 x- a, j( k2 C- b
  28.       HAL_Delay(1000);
    " [# @( `- m1 X- T: A! N
  29.       for(int i = 0;i<ADLenth;i++); W/ c2 K. `& _. ~/ e( u
  30.       {
    8 ?% O5 X. P! }  l4 ^4 `* Y
  31.         printf("A:%d\r\n",AD_Value[i]);
    % R% U( o& e3 R7 @5 I% o* N
  32.       }# `" P' o* Z* J( Q4 O7 E/ x
  33.       9 Y0 ?5 b# o$ t2 j
  34.       
    7 E! f: a! t$ ^* c
  35.     }
    ( K2 ?! s( p7 B1 Y5 T) L# z$ q
  36.   }
      h" \: j6 b7 c5 j6 h+ j- F8 w
  37.   /* USER CODE END 3 */$ |6 F( |4 U8 W( ?' l. \
  38. }
复制代码
: L  `  F) J- s3 z5 p

* s" K& `5 Q4 L/ v  I我们编写一段代码。4 O- I4 W# N$ A$ z% v: w% u

6 D, E# F5 d5 c! z( `" _4 ~
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。
* c) c& y1 x0 h& L! I  Z* J) ^# |
7 Y. {, B+ N  S% w* Z, ^; B2 y# E 微信图片_20240529172345.png / J, ?$ D. V4 G( d6 S1 G# R

9 N; a7 e) ~$ F; e可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。
- N% m# ?9 |; u- P, N* E; Y) T. c$ r' {
三角波发生器
8 F9 E9 C  @5 n: C3 N5 i
5 ]5 {' o6 V0 z% ~
微信图片_20240529172342.png
+ w3 K7 p7 N$ S  d# C3 y6 i
5 ?5 Q% y# V" ]' G! X3 Z
在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。$ Z: V) I; w7 H' I3 D
* r3 i& ?; s2 R9 }9 Z
微信图片_20240529172337.png
% a9 b6 J4 l; k$ O! `) L" p( f

$ s/ v: ]+ Q1 [+ F$ h, [之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。  H0 v7 a. q$ l# a+ C! |+ p' d
8 e8 S+ ~6 L6 U$ j
微信图片_20240529172334.png + o& q: a8 _6 L" X

  s9 s" E- ]* x- a定时器设置好时间之后,设置触发事件。! J& h8 a# l% g

) }1 s7 [6 v: z* d$ w* Z. F4 g  } 微信图片_20240529172331.png ) ?* @! w7 a9 O$ K

: x3 l: e6 g' J# l& ^( j8 Y3 @芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:* T/ @3 ~( X( c8 @; j# y
- Z  R) p. ?5 z2 U1 q
定时器频率/分频系数+1/Period/三角波最大值/2
5 |; W/ h9 m0 P4 E  u# w. T3 C$ f# o* g; V& W' O& s
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    7 Z  F6 r8 y2 f* v$ {* K% _
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出+ A  ?% X8 @+ g( I- M( ~
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    # a7 {$ U8 F* |* A$ [* o6 m+ D) m. H6 k7 U
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
      L8 d( D2 L, z
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码

' N7 r8 g7 H. X% s! U6 l 微信图片_20240529172327.png
+ n5 |+ i. r& b! n
8 n/ P- H. w9 f2 z( w
正弦波发生器
, P4 H1 d5 g( Y3 T& N$ m3 K; M# [4 \; w, q$ C0 g: {
微信图片_20240529172323.png 5 g) J! Q/ V/ w4 G1 D0 I

+ K& N; u8 s0 k- A4 K首先是定时器触发(方便控制频率)。但是关闭波形发生器。9 D9 }1 B4 y- j5 c0 \
  K8 \$ Q- J6 U! y5 P
微信图片_20240529172320.png ! ~4 a& e( S& N2 a3 ?' Z- p
, L$ [! M' ^( r* e+ Q1 f
添加DMA,模式选择循环模式。
- a& Y4 h9 N8 [
( K2 ]9 O' h; q0 S
还有改变一下定时器的频率!
. G8 T& P+ V" c$ y4 a8 G
' H' z' S$ i( b" y
其他几乎不做改变。/ k6 f$ a. u- B, X- u# w

0 a/ F8 O  r8 C2 \! D

; D5 }( T; U( ?) B" z) R  w
  1.   #define POINTS 256                         " I4 t; d( H6 T$ X! J% _$ x9 |# f
  2.   #define MIN_VALUE 50                         8 ]) [6 W" _! ~3 u
  3.   #define MAX_VALUE 650                         8 O) D& k( [  c. m& e& {
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    6 p3 p4 }! G" w! f" }
  5.   #define OFFSET 50  & y. V- B7 J; p$ m, v# l6 D
  6.   #define M_PI  3.14159265
    ) L) A' P& z9 e0 B& \
  7.   % v6 e! P- Y2 q6 w7 R* X* ~
  8.   uint16_t SinWaveInt[POINTS];+ F! C; L+ @6 |1 X5 q
  9.   int SinWave[POINTS];   W8 n- K4 ^5 J" d7 e: i" S' ~6 l& v
  10.   void SinInit(void)
    ' ^* D, A! k0 Q- d. J* R
  11. {  ' m# Q  v! d; L* _# z
  12.      for (int i = 0; i < POINTS; i++) 7 Y0 q2 Z# J: N
  13.       {  
    7 A& D: ~( w! F9 {" u! V, y  b$ O
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI;
    ) Q! |8 N! x9 I4 P
  15.       double sin_value = sin(x);  // 计算正弦值  
    ! Z5 \, C  c6 h0 n& @9 n7 `1 K/ k
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    1 H* C5 o8 t5 a9 c+ L$ C
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    ' Y& I/ l! r1 m5 W
  18.     }
    1 `+ w& j5 a% j7 F$ f- S3 ~! C
  19.   }
复制代码

/ k  e9 j4 x( w  g计算一个正弦表。7 R& Y3 R  f5 B6 |/ n: \

7 v5 d' i, f" {7 f5 N. Y
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。) q; B7 V: l8 B0 _- x

: F* M; W+ ~5 S- ~1 ^ 微信图片_20240529172317.png
. |( l3 [" t0 |6 e

) |- n! q& `; b  _' Y7 h测试一下正弦表,输出的是正弦信号。
, v  n, z) [$ t* k
7 S! l/ I7 m% g! l1 ~
我们之后将正弦信号表导入DMA中。
/ ~/ o# M4 W6 ]$ K5 e4 Y5 r0 {6 P2 `' @$ e
  1.   SinInit();6 e/ B% t6 j* f8 ?2 b* d$ H
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码

5 [6 S) j6 j, R5 I正弦表初始化,之后启动DMA传输,导入正弦信号。
9 z% {( L% N% m& H! ~, B1 t+ E. I
/ l& i$ C- |" b" T6 X; R$ @ 微信图片_20240529172311.png . R  S3 g! s( p. z0 W7 u$ N

: n% x5 s& G4 g8 u测试正弦信号成功。
1 e. m" u7 g, l* ^
; N8 |& H" G  \
FM调制
  1.   #define POINTS 1024                         - t' d% ^/ E, N% E! \( J
  2.   #define MIN_VALUE 50                         % h  N4 U4 M& A) v6 ~" }
  3.   #define MAX_VALUE 650                        
    0 g- `0 I  Q- m! q' ]; ~1 D
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  ' l% V! V; ^* T) E- e
  5.   #define OFFSET 50  3 G  d# N) |& Q) P& K5 q7 @
  6.   #define M_PI  3.141592653 l9 i1 E8 E( k6 o5 }0 J
  7.   : i, V0 m9 L, R6 T6 u* G2 i
  8.   uint16_t SinWaveInt[POINTS];& p/ q# R- u. a4 _
  9.   int SinWave[POINTS];
    % {# ~& j8 F0 C9 I. ~: Y4 K
  10.   void SinInit(void) & Y6 y" f- f5 _
  11. {  
    4 X! `) a( R- `) D2 U& O
  12.      for (int i = 0; i < POINTS; i++)
    % w/ g1 b7 ?- \, `. G) ^
  13.       {  
    - c. A  ?# K2 J# `3 }
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI;
    0 n+ Z0 @; T2 h8 H
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  6 ^$ X( c* M- a8 B+ h7 F; L5 j
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    4 k' T( Z0 p# S6 A& }
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  / ?$ i% R+ [' J  M5 K8 m
  18.     }
    ! w4 W5 u- R$ P, B
  19.   }
复制代码
6 }* X. f# K: z* B
7 @6 g2 }  N0 z% u1 d1 V" F
拓宽点数,加上载波,即可构成FM调制信号。
1 C4 T6 ], j3 E( u/ W$ A5 _( C, {( }- d# l5 n, R) u
微信图片_20240529172305.png
& d- _6 V+ Y2 c% [5 l' y2 \! ]
0 c; I2 y& k# i5 _6 w! o# C' b; r6 `# [( a/ p
转载自:电路小白+ e! U8 i) }  }, y6 F5 Q
如有侵权请联系删除
! A  K! r. z! }7 W; y0 K- v0 h% M$ z
8 V4 F6 g  v' f, N
: f) S6 |2 R& Y/ 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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版