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

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

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

( S% t+ @1 X" A5 m# {$ @* XDAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
) v" O* m. H  s  x' v7 h& \

- a9 A" e- d4 E+ X) V) `DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。" m8 v' `- i; u+ y: t- q# f7 H

* _" f( U9 G4 c- `
微信图片_20240529172405.png ' M5 U! f+ ~  r$ l4 \0 O
2 T' v: p. v0 S/ z* B
STM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。
7 [, J2 A# a1 T+ G; p* C; K4 b4 N, a' k" R3 D0 e; `3 K
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。4 K6 s4 X6 Z  [1 g8 x1 a
: Q' U0 @6 o5 W1 n
CubeMX配置2 o) K7 k6 D1 c& Z/ e

% B+ B, |1 V; H/ l* Z% z' K' m8 E6 O 微信图片_20240529172401.png
+ l- i, g* Y' I3 G

8 y2 f  x$ w7 U$ _* ^- R在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。+ b! A. N! q; x, Z/ X

5 R9 W5 y7 z5 ~: \ 微信图片_20240529172356.png - Y+ v; P2 B- H+ D/ M9 i8 R% w$ z

3 G- M; z' K1 Z, j. \; o$ eOutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。2 h; F4 p9 @) v, h: M; _

' f; _9 J9 ]3 ^1 X6 b# w& m5 S
如果选择触发的话是从缓存区写入数据。: ]. R8 z3 v, I, F

$ f; l7 |+ \3 ~ 微信图片_20240529172353.png 9 Y7 x1 Q0 y; j$ X
3 h7 [  L6 l7 J1 x" r) U1 A
这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。- B6 A' B& u1 g) a+ T! f0 E* K
8 P- s3 m5 V7 U# I: A9 ^
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。! F  |" C8 C1 i  X7 K* b; l& m. m$ @

/ ?, Q6 b9 N& `, ?, t
STM32的DMA采样+FFT时域分析(STM32F407)
" }, o, b$ v: W7 k, K% [2 Z5 E7 O% F5 L+ R! g5 Z3 J7 y
但是相比于之前的,需要更改一些设置,首先是DMA的设置
3 t! J' G' @0 }
) g+ [& s- v, j# {' v 微信图片_20240529172349.png
; u5 w) K# N- ?; y

, u' J0 f9 ?( s/ |! M这里设置单词请求,而不是循环模式防止数据跑飞掉。  W, S  ~6 H! I) i7 V6 B6 [- _6 J

3 {$ Y/ L8 n% G# R! m
/ m" E9 @( J$ V4 u
  1.   MX_GPIO_Init();
    - G0 D) n! d9 `8 O
  2.   MX_DMA_Init();
    ' O3 l1 L( ~; W, g$ P. z
  3.   MX_ADC1_Init();9 L9 o6 ~. q$ K2 j. U
  4.   MX_DAC_Init();: G. D2 O: k+ \5 [* k. f
  5.   MX_USART1_UART_Init();
    - {: ~6 \+ ~2 D/ r% ~) u6 B3 K! c
  6.   MX_TIM1_Init();1 }6 z9 \; A1 O+ \* O- T
  7.   MX_TIM2_Init();
    7 p$ H' e( E+ ]/ ?+ b& d
  8.   /* USER CODE BEGIN 2 */
    5 l3 j4 c) k- j
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    ) T' C' P+ U, P- Z% K7 o% U
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC1 z1 A+ f6 \) Q2 r
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);. Q; j* X/ m5 r" H2 _
  12.   /* USER CODE END 2 */
    9 ^: C/ g2 U  J& q$ X! n

  13. 7 f) f8 a, u! L5 ^# g) v
  14.   /* Infinite loop */
    ! s' c/ H1 L0 {) j# D4 X# V* `
  15.   /* USER CODE BEGIN WHILE */
    2 l! e2 m, A! n# g+ D5 T
  16.   while (1)
    % a/ w/ L  y# R; D8 h
  17.   {: y2 g' f* Y0 G6 Z% ^& }! z
  18.     /* USER CODE END WHILE */
    4 m% Y! _2 v% f3 f2 R7 V2 j
  19. ' \. H1 m3 U# R. U
  20.     /* USER CODE BEGIN 3 */& R; T5 D  B* {# _8 N* H  G' A
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))
    / Y0 r# l2 z: h! _: ?9 J
  22.     {
    & o9 l% ?# `7 b# H+ U
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC: X& w' D. e/ P* t* r, F: U& w
  24. 8 n- j. K0 O, C) b) ~, O+ I5 T
  25.       HAL_Delay(20);
    % v7 [+ g( y; \0 y
  26.       
    1 t5 T, ]3 f; ]6 p0 S9 _; w4 a; W
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));
    3 t' e8 n; V8 q/ H& s3 ~" R% T& r
  28.       HAL_Delay(1000);
    % J0 Y8 ]! f3 K+ [6 j3 w
  29.       for(int i = 0;i<ADLenth;i++)
    ; l) O2 B' b% o$ m: ?
  30.       {) r' q' c+ l" V- V( P. Q
  31.         printf("A:%d\r\n",AD_Value[i]);0 t7 ~+ f. R- y& a9 ?( t
  32.       }
    - k6 V! `3 u. m6 L6 o4 g
  33.       
    % r& x, `; R7 L0 D* U
  34.       
    + w  o  b! j1 `# ~0 s, N
  35.     }( T- z1 Y" M- x% @8 M) o
  36.   }3 O: J4 p$ c# [7 f+ q( ?- i, _; b
  37.   /* USER CODE END 3 */
    ; p6 A2 M& a* ]& d: P6 ^3 W! {# I
  38. }
复制代码

/ h; b+ B) Q8 ]8 E, _8 g8 a% U
5 a1 o0 s0 X+ M3 x9 g我们编写一段代码。
) l6 I3 u+ l- @" G1 W8 R+ y- M! ?4 N! o
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。! v3 P. p. H; f3 J# R
+ G, ]9 b2 o# X  R3 p& U8 o
微信图片_20240529172345.png % F$ w/ c; N- ~/ q3 \

  L: B; W6 H9 ~. u; N可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。" W8 H9 k  j% j. Y3 `8 i0 ?( L
; s, @# T  G- U9 u1 @4 s* ~3 t( G
三角波发生器( k9 d$ C" Y* W. Y4 |8 X
, h8 @" U) S1 H' l3 Z( x8 [
微信图片_20240529172342.png $ ]$ m  c0 |* T; ?- r7 H

! q- M5 W0 |" y5 k* m在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。0 J4 ^% Y  B5 D, _; C
  X8 |3 Y/ I+ Y6 G* x1 h
微信图片_20240529172337.png 2 \1 {8 |- n1 Q1 U

! ?0 ^' _$ G4 m1 k9 `之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。' z* q5 c3 T9 }. z- w6 n5 @

8 Q2 p9 r* D/ h& }8 { 微信图片_20240529172334.png 7 S2 z1 g+ F$ A
& u5 @" S+ P9 m% J
定时器设置好时间之后,设置触发事件。1 r: p& `, h) @; w; E7 \5 B4 n
( [/ Q3 m2 n* X' Y
微信图片_20240529172331.png ) N$ a8 L* y. n0 K- h

9 w3 w8 Y8 C/ W9 s: N: ~2 y* {芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:
# L- a6 N' S, A1 y1 D7 V- [
; d2 g& L8 F$ x9 a
定时器频率/分频系数+1/Period/三角波最大值/2
# z: D7 p# G: R4 v: h7 L  N7 n3 `& v6 F  O
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    ) V3 |% N: k* x  y. r
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出, j5 s: j6 p, z, ?# n5 A
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC: J+ S* Z0 L" I6 ?# n
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);) b) j4 O* I9 y8 ?4 m% W6 J
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码
- g( M3 e! f8 r' R$ i3 M
微信图片_20240529172327.png 8 w' k, m. j$ k$ D
0 h% I2 I% o: W7 Q2 {# M5 \
正弦波发生器
# R) v( }& I3 n# @3 @
; I, k+ H$ h9 Z0 \% q/ f4 ?. X
微信图片_20240529172323.png * z7 L; t. t$ I+ e

/ o" ^1 |; R2 ?* @首先是定时器触发(方便控制频率)。但是关闭波形发生器。
/ `7 w9 K3 C$ L
$ e% w7 y$ ]; a  M 微信图片_20240529172320.png
1 G$ [- s8 X3 M- `  z
" y  Y3 ]) x  j/ [5 \& @, Q
添加DMA,模式选择循环模式。& D  q/ n  }6 ?! O9 R6 s

7 z! v# m- a. G2 F0 S. |) N/ C
还有改变一下定时器的频率!
. O5 \& V/ M8 S# z! M
% V$ X5 q/ a* p$ A( M) e% W5 ~! Y! |
其他几乎不做改变。3 _0 W8 G# ^% a4 q2 d
" a" c  m$ R* g. N8 [, L5 b

" Z) n3 S( w" u% E4 z) _$ I  _
  1.   #define POINTS 256                        
    ) S! }0 c7 F7 Y; k7 n  T
  2.   #define MIN_VALUE 50                        
    6 Y7 p, \) T3 u& n9 m
  3.   #define MAX_VALUE 650                         ' L, `. }* m. k: B3 L
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    7 Y# s4 f. c+ W5 f: z) j, c
  5.   #define OFFSET 50  & a& P: W; [0 d+ a% V3 A
  6.   #define M_PI  3.141592652 @" b2 v0 d8 ^5 `; c& j- `: @
  7.   * i( k, e/ N) E' @( y7 F$ ~
  8.   uint16_t SinWaveInt[POINTS];1 V* F6 K" D7 E0 F9 ]; |
  9.   int SinWave[POINTS];
    # P( `7 b2 p& p9 z! ]
  10.   void SinInit(void)
    8 c! E2 F9 V* Y7 I9 X/ @- M
  11. {  $ @; v8 L/ n, d6 l# |6 Q6 S5 k
  12.      for (int i = 0; i < POINTS; i++) / m8 b$ l  a; \
  13.       {  9 \" @$ ?  A9 R" w% R/ u2 }" M
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI; 9 h% O  O5 w" l+ B# }
  15.       double sin_value = sin(x);  // 计算正弦值  ( D9 y0 g( m2 B
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    7 c) P3 d% _& P5 q9 T
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    & b* K- S5 \" V8 A
  18.     }% i, o+ N( D5 E; K4 ^( x
  19.   }
复制代码

# a* c" P, v  B# e" c/ Q9 V计算一个正弦表。
& o* ?# F; T7 X8 i  e
" W! s; U* K5 N- B/ c4 r* w
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。. Z$ E1 T5 ?  j- J: I7 E" `  W. [
2 s: C% a5 n; b8 `
微信图片_20240529172317.png ; T4 [  \' v3 S# h4 |* Z) R

% d2 O2 v) h1 V2 E5 V& E测试一下正弦表,输出的是正弦信号。7 o% T# o& B8 ^$ `! @0 p0 X6 v% v

5 S  i5 t- w5 T7 \- \8 l3 D5 \, O
我们之后将正弦信号表导入DMA中。
: `0 H6 w9 H& g# }) \( }
' \! C- ?7 D& B: L, [# H
  1.   SinInit();
    * Q, d% w$ t: v1 ^* l$ l/ J
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码

7 X- C7 C% m2 Y* H* P正弦表初始化,之后启动DMA传输,导入正弦信号。% O) e" U2 m# B' f! n/ G# G

% q$ j( @, W" m 微信图片_20240529172311.png 0 u- I/ j/ B9 p3 `' A
+ j" Y/ R' ?" t$ h
测试正弦信号成功。
. |; C& h8 a! v  J% K
0 z0 H( t  d2 A3 I" ?- F
FM调制
  1.   #define POINTS 1024                        
    0 }$ G, `9 X7 u9 T* t
  2.   #define MIN_VALUE 50                        
    ' I4 t4 h; Q  N
  3.   #define MAX_VALUE 650                        
    4 g- S3 C" O- G( c% K$ |
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  2 [# T; o% p! j% S7 l( Z8 A
  5.   #define OFFSET 50  & r# v3 t/ i  }& ^% H' U0 a
  6.   #define M_PI  3.14159265
    ; O# d) V$ Y' _& i7 |7 M5 L% [
  7.   
    : |7 ~7 _! c* D/ O
  8.   uint16_t SinWaveInt[POINTS];# J+ Q7 b  y2 ]9 L* e6 V
  9.   int SinWave[POINTS]; - P% z6 J; q  B
  10.   void SinInit(void)
    9 A1 c# J: v* v2 \; \$ D  _6 ?
  11. {  
    6 o& S& R" Y/ _7 ?1 G4 U/ n, b
  12.      for (int i = 0; i < POINTS; i++)
    5 j) w# i+ A, C
  13.       {  ' S+ T( w$ H5 B7 {& h5 w# M
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI; ' t: [/ v( d5 M) x9 H
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  9 H$ E6 Z1 \* B5 B
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    ; Y; L3 t. i+ \8 o
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  * k! D7 H0 y$ k: C7 i
  18.     }
    - w8 Q4 W2 n6 [& [9 m. D. ~
  19.   }
复制代码

% k: `; ?1 s% j$ n( }; `: z+ a& H& |% r% s4 |' z
拓宽点数,加上载波,即可构成FM调制信号。
: C$ v6 s1 X5 B' F+ h8 e
, {6 }0 z) f1 _# n7 z# |9 i; X 微信图片_20240529172305.png
" j, W3 a' i4 @  s
; e1 X' k0 r5 `/ }7 s+ d4 t7 C% u6 ]* t2 U& o% X3 j/ L; z
转载自:电路小白
+ {9 r2 j2 D' W- Q. h% L如有侵权请联系删除, C4 ?0 ?' {# R$ }

& L! M' L  l" }; s- w' T
0 ^  l  K) J* l/ X5 n4 {' g
# Q( u) c/ Y: q
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 手机版