请选择 进入手机版 | 继续访问电脑版

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

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

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

- v! S0 ?8 M; P7 o7 \2 PDAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
( V* W* P, L3 `7 ~! `8 j$ s+ k
& m) t1 @* U( Z7 A8 `* R
DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。6 N2 K8 E5 D  K) z, X
1 S0 u! o/ w' m. N3 p
微信图片_20240529172405.png
! P' t$ M8 J( `$ U8 m1 Q& P# e
7 v: V5 \0 K$ Y  h2 C+ ^5 {1 Y
STM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。# O0 S3 j! }1 o* p

* A+ x- \4 U+ ]' h4 b
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。/ q: w0 c) O! J1 k, L/ [

, N" \/ w1 w/ D( J  y' X
CubeMX配置! @, u- d; H$ t# H$ R
* R; a# C. s5 p8 P% ~, o) S( {
微信图片_20240529172401.png
' @% Z& f' d" A) P9 s1 I

7 j9 y) D- y2 K; K5 S4 N$ ?在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。) H+ W6 i+ U1 D6 K

& q/ \. I7 m6 m! [" C 微信图片_20240529172356.png
# O3 S" U  F, w" _! }. ~

: x; Z. k  `2 _! COutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。9 a8 E3 a) N3 D" R) B8 H

; b& V& l" q- B
如果选择触发的话是从缓存区写入数据。
6 C) W6 l% Z  j
0 f+ \! E# _9 e5 K# D 微信图片_20240529172353.png
( K3 q5 s* Y3 b

0 k2 e, q- `' |这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。
) z! O* ?" X  d, R: y
+ O, s2 `/ |6 \- p* W# W
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。% ?" ~! |1 e( O% Q
2 r8 [' I4 A7 @5 z" T; G9 U: a3 f
STM32的DMA采样+FFT时域分析(STM32F407)
! |3 {5 m  e0 T' T- b5 H2 o
4 n8 d; b3 R! R6 R) w0 Z+ b& z% l* D& S+ A
但是相比于之前的,需要更改一些设置,首先是DMA的设置
3 m) `6 f+ [- `$ M: D
" p! t% S* K1 T9 r# } 微信图片_20240529172349.png + ^/ H! m& V! j4 ]! s% w' S8 e) q
8 i7 e! c) }; W
这里设置单词请求,而不是循环模式防止数据跑飞掉。
8 T) ~5 j4 \4 l- A& z3 R- i& {, V" K, f0 `( A0 m. I5 S# B! u1 ~
5 i$ t6 m3 Z8 R& C7 p
  1.   MX_GPIO_Init();
    5 x( |; v5 t# O# D$ q
  2.   MX_DMA_Init();
    7 a$ |4 V! k8 L  m2 J$ I
  3.   MX_ADC1_Init();" x4 M7 l% u: M3 l' J
  4.   MX_DAC_Init();
    , A* B7 ]4 E- d" p; W6 Y2 ]" _
  5.   MX_USART1_UART_Init();
    , v6 k* Z2 E5 U) r) K
  6.   MX_TIM1_Init();' q- p7 b- F2 Q( o" Q6 F
  7.   MX_TIM2_Init();
    . R- o+ u. d2 k
  8.   /* USER CODE BEGIN 2 */5 r7 G7 Y7 z/ q# H4 {' A0 B
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样 . O: ~0 B! p0 `2 Q
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC, b3 F8 M$ }) }
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    ; Q# @, \9 d) ^0 R1 |$ {
  12.   /* USER CODE END 2 */
    # k) B' z# z/ @3 J. i; g( e
  13. ( Y4 O% D  ?- ]! ?( B! ]! m5 P) K
  14.   /* Infinite loop */
    ! x8 g, c1 v2 p3 {  X$ w3 J
  15.   /* USER CODE BEGIN WHILE */! |# _# }; `' C2 K4 z: _5 M: ~
  16.   while (1)
    : S8 U5 Q8 O( S) U8 }$ A
  17.   {
    : j& H5 W7 O4 h, O) Y
  18.     /* USER CODE END WHILE */9 N- I: r, L+ l$ k% w
  19. 8 K& f( e6 Q2 I. g- }
  20.     /* USER CODE BEGIN 3 */
    : p5 C# l8 S) l7 C$ c; T
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))  {% \$ D& K% i; l
  22.     {; T. }: C0 R( Y$ s% [! L, {
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    ) X8 L+ z- F, q1 ], ?1 n; _

  24. 4 A& B& p$ E- J
  25.       HAL_Delay(20);  y" G( A. l) _7 f
  26.       
    " |7 B1 b: F+ s9 j7 T  T7 |
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));
    9 ^7 n% S2 O) g. q, u! S3 V* v
  28.       HAL_Delay(1000);  h7 g* K8 H4 A  Q" C, H: T( }% C8 }
  29.       for(int i = 0;i<ADLenth;i++)# u) H: t. I9 H5 n+ s
  30.       {2 |3 {, R# Z% O) K
  31.         printf("A:%d\r\n",AD_Value[i]);
    4 w& F3 y/ n: T+ c7 b. l0 C; b
  32.       }
    . p7 X+ z6 I. k6 Y1 Q+ S- K
  33.       
    0 t/ \8 Z. a) n$ ^- L8 ~  \$ d
  34.       
    3 T& s* A! u7 B, q# J9 a2 J( S7 w- ?
  35.     }9 W$ _, }6 z  ^: w% v  G7 C. T: ?- \
  36.   }( {3 W' Y: R9 {% ]
  37.   /* USER CODE END 3 */
    + C7 J; i9 C" |0 \5 T0 q0 C
  38. }
复制代码
/ C8 {/ _# _2 m1 y7 H
& d2 i& x3 {! ~
我们编写一段代码。
! q1 }/ P5 ^2 y8 A- R$ o3 c. ]
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。
5 a1 l/ a+ [0 b8 l: j. U# I+ F5 U/ v( i2 R& H( P/ G# V
微信图片_20240529172345.png
- n' f/ R2 x2 w3 b3 m( T9 ~+ ~
7 F1 ?5 i4 S3 g( Y0 _& g, p6 |
可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。
1 o6 d$ p3 [- Z: O) R% n- H6 F+ ~+ q5 o! K: {; M# R
三角波发生器1 g5 }' n% l& G' H, T

- t: V, i* F6 d0 G2 h8 L
微信图片_20240529172342.png
- f  g0 H! N" {2 C0 G$ J- C) \
- r) C5 q/ b# x5 M
在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。8 M% x2 W- W+ E8 C7 e
, E- U& n, @* r  T5 G8 {* g" |1 D( n
微信图片_20240529172337.png
7 j' o# \  c- z7 y, @4 G& s8 s
) ~- l5 L+ Z. R1 j
之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。
+ L* z0 h+ j# @9 }9 Z; I. x
2 K( S! P  Z/ _6 ^ 微信图片_20240529172334.png 7 r& l  c5 l2 x% \% I: T

2 ~; W$ e" W. K4 n" w定时器设置好时间之后,设置触发事件。1 i" ^6 k1 r, j) b/ w

# B6 @8 u- ?. V 微信图片_20240529172331.png
/ {9 J* P$ C8 o" z& B

: b# w8 Y; N! V: B3 ~芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:
! k4 b+ D! y4 J! N9 G0 g# C
6 `$ M% n  k5 N$ u" J% G( ~
定时器频率/分频系数+1/Period/三角波最大值/2
; u1 X: y# {7 y" d. a/ `4 g- h* s
8 [" |+ \6 U% h, d: ^9 I; N+ f, q
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    1 ^" V+ r8 p: f' \3 d3 m- `9 v& n
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出
    4 o2 ^: i5 F1 X  c' Y
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    " f  |6 |5 O$ r7 c; g$ K
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);' H2 F  `# O8 R; x, w
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码
% o" j/ ^3 ?- C) V* t
微信图片_20240529172327.png
! h# I4 f* C' |) N8 X/ D

* z# v+ x6 ^$ a% O3 _5 D% A  H正弦波发生器
4 j( @, ^% r! r, f5 T) `( J" A. r( N
4 Q2 b) c, F& w- \. n
微信图片_20240529172323.png
, O% X% W$ p. t, r8 m8 w6 p3 ^

' y- ^' o. x) E- J' Y首先是定时器触发(方便控制频率)。但是关闭波形发生器。9 m' D! I# Q3 I+ s8 G) {
& J7 e' W8 F8 I0 f
微信图片_20240529172320.png
9 N& r: D0 A! D0 [1 V' m
5 Y% T8 @6 @% M
添加DMA,模式选择循环模式。4 a0 H! |6 r) E+ i2 W7 ]3 b6 P
* {8 \! U& X& @
还有改变一下定时器的频率!& g! A  s4 O5 {( [( Y- U$ U2 t
0 N* i4 d+ i1 [0 C: W, p# _5 K
其他几乎不做改变。" q  |0 h& {9 e

6 ]" U; Q* y- y

" y% w& Q8 ~- @' W. X. p$ d- \
  1.   #define POINTS 256                        
    " Z7 V8 m3 ?3 `" s. I6 J5 Z8 S
  2.   #define MIN_VALUE 50                         5 M  X, Y! {5 ^/ J1 L/ }
  3.   #define MAX_VALUE 650                        
    9 S; Y, Z- d$ a' ^; N
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    ) q9 @/ L5 j! v& ]
  5.   #define OFFSET 50  
    & x* E# d3 ^. U/ o- U0 n
  6.   #define M_PI  3.14159265
    # o1 P& E2 w2 K, w9 y7 [
  7.   
    8 t3 X* N/ y5 _; C3 d) v
  8.   uint16_t SinWaveInt[POINTS];! m( d  m4 ]$ C& T4 x- l
  9.   int SinWave[POINTS];
    # Z# B( j* |- ~5 ~6 `& |& c
  10.   void SinInit(void)
    3 ?, q6 e7 E6 t9 ~9 K5 T) o
  11. {  0 H: M7 c, b/ p; w  ^) {3 o
  12.      for (int i = 0; i < POINTS; i++)
    2 A2 z5 E8 D. @7 ?; h) G
  13.       {  5 G6 P* A+ x0 a' k
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI;
    ) J/ h# e  d$ B# P7 l8 C/ [
  15.       double sin_value = sin(x);  // 计算正弦值  
    ( R1 Q; E) P6 `; H, i
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); 3 n6 W& B+ t2 j0 t; R
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    5 C" `# M9 @3 |) i
  18.     }7 g% \/ N! n2 q) c
  19.   }
复制代码
( q& z$ F. W" H2 E0 i; X# l
计算一个正弦表。
3 {/ Q' q* G3 @. N- ?9 y  ?
+ c+ s1 I& J; B) j8 Q4 Y: T% R
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。9 D! W0 z3 g) v  q

! K% F' F; o* a$ A& Y! y9 I& y! I 微信图片_20240529172317.png ; W- Q' X; U, u5 m
! Y4 J4 H( f: i$ T$ Y
测试一下正弦表,输出的是正弦信号。
1 R8 q" r" _8 J+ N* g8 t, W: s" N: x$ X0 l
我们之后将正弦信号表导入DMA中。" D- e" x0 Q2 J, ?' a* {# \

# S3 ~5 S' z% g/ S5 h/ `$ `) F9 b
  1.   SinInit();6 a0 B& ]3 _1 e/ a; p; L* e
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码

  u) ~( ]( ~9 |3 Z3 t正弦表初始化,之后启动DMA传输,导入正弦信号。
9 ?) [2 h( \0 _$ U* i% h% g6 [4 f
6 h0 ^  g3 }0 a- N1 q' D 微信图片_20240529172311.png
+ Z7 L5 `3 p; u& A, v- V4 |4 p! s; v
' O- }" Y! {7 l- F5 d
测试正弦信号成功。
1 p" H" S( I+ A2 B; [* ?( X
* X6 }* Q6 D: K- \5 K9 t
FM调制
  1.   #define POINTS 1024                         1 x6 m" p' W5 o9 W- H" h  t- @  K* j
  2.   #define MIN_VALUE 50                        
    + V% X% s  w0 ^5 i) o$ Q# \/ y
  3.   #define MAX_VALUE 650                         ) x0 ^, t) Z' b) Z! f
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  / A3 K1 Y& b7 r8 a8 [7 @/ y. G
  5.   #define OFFSET 50  
    ; O0 Z3 Z& H% d( h) f
  6.   #define M_PI  3.14159265- ?4 J7 o. o# w* ]8 I2 M
  7.   / Q( N, U/ O4 W
  8.   uint16_t SinWaveInt[POINTS];  L& c) m. c. _: T# g+ x5 E
  9.   int SinWave[POINTS]; & L) p9 E5 b- q% b  }, {  d- @
  10.   void SinInit(void)
    3 Y7 l# A: ]: u  j( Q/ x; e+ W9 \
  11. {  
    . i( y% z  V/ {. e  y; m* c6 |( X5 P
  12.      for (int i = 0; i < POINTS; i++) 7 Y" y1 s0 ?; J1 }  R1 `
  13.       {  
    - Z1 j5 Q, P0 K- b: G, Q/ b" D
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI; % p+ ~& U& q3 Z7 g# `
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  
    2 A0 u. H" w. Z9 |+ @% C* ^
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); 0 y' ^6 C" Y2 J
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];    |1 T* z) |4 a4 t9 W
  18.     }+ M: D( l$ t2 r; u
  19.   }
复制代码
5 R5 b+ ^0 f7 `( b
4 I/ I7 _, z5 T
拓宽点数,加上载波,即可构成FM调制信号。5 ^- L# F1 b) Q9 _7 V( W! e. [

8 E  L2 [2 C) z 微信图片_20240529172305.png
, y7 o" q' \$ u! L4 s2 f* h) G6 s

+ F4 d& V8 ^: y( G2 E2 J, Z转载自:电路小白) F/ D- P+ X; C$ e( I9 G- N0 J# j7 h
如有侵权请联系删除
  J" T; L1 X- @' M# Q
" |3 Q2 R% @7 [. M8 H- X4 z2 x
& |" J3 k9 o  ~. I! n: Y. G) E" m: Z! O
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 手机版