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

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

[复制链接]
攻城狮Melo 发布时间:2024-5-29 17:24
DAC,即Digital to Analog Convertor,是数字到模拟转换器,也称为D/A转换器。其核心部分由R-2R电阻网络(也称倒T型电阻网络)、模拟开关和运算放大器组成。它可以将二进制码或BCD码表示的数字量转换为与其成正比的模拟量输出。- n6 o5 d! q' S3 t. D
  e' a3 [* F5 k* V( D
DAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
' z% w9 f! [) }0 p' h( j( l, o! l
% a5 ^; |' C5 u  z/ F; t, [% _7 {
DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。5 l2 R+ g( I) u
' {' j. ~/ k5 Y: L1 X2 K
微信图片_20240529172405.png " r( X9 b9 V% n! j
: x& P; ~# U* I+ ]- l
STM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。
3 ?& U7 d$ [" C6 N# f3 i9 y9 F" T
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。
5 w" H: C. Z& x$ ^2 S9 U. u8 T2 V* Q4 m# N; G( z5 U! }2 h* d  F
CubeMX配置
" \+ K" ]8 [( y5 |" X6 J
  Q* ~, k+ a3 C& h 微信图片_20240529172401.png
( ]6 B' ]( }, L% [; T
. @! N9 z* q5 w3 @6 X
在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。( J  f" ]( n) V+ z6 ?; `' C
1 Q- P! I; ?- b6 Y* O( w  _- d$ |3 m
微信图片_20240529172356.png
9 B( G9 _, G5 i6 }
5 s6 k& n* F$ s6 y6 u9 Z
OutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。" h8 Y/ u! D, _* b" P  u* @* |3 ?
; @' t' h1 Q$ t2 P" p# V, m/ T2 p2 P
如果选择触发的话是从缓存区写入数据。
# Y. u: R& s; k& G  B# v8 ^0 p  L9 V" L3 y4 n# I" o; ~
微信图片_20240529172353.png
8 j  a' _: ^' W/ e$ L7 w& @- L- T

$ I6 [# n. K4 e: u$ x4 Z. Z2 v这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。  m. {, R( v" V, A4 i. Q: I
' o6 m8 s3 i/ t0 B7 y& w
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。: `# r) Z' B7 e, |! A5 c) S- N

& p- E' Z  r* V2 L# }6 u1 Q4 q
STM32的DMA采样+FFT时域分析(STM32F407)
  l. x4 R) {5 S# D3 L
' k8 b* I8 r0 w' e% n8 ^
但是相比于之前的,需要更改一些设置,首先是DMA的设置7 |9 V! P7 W5 Q( n* V
' X( I+ h$ q6 r7 r
微信图片_20240529172349.png
5 U4 S2 e, h: W5 i
* J) T- A, i, G; ?. Z4 U3 A1 r
这里设置单词请求,而不是循环模式防止数据跑飞掉。6 w0 S9 _' M* Y) D9 M% l
; a: m( X: [7 z7 V; M. _+ R
3 |! g& ?0 @0 Z$ i' p0 V! _: |
  1.   MX_GPIO_Init();
    . D5 ]9 B8 x0 d0 @- {9 o
  2.   MX_DMA_Init();
    / U) A) J! M+ `! j$ ^9 D, M
  3.   MX_ADC1_Init();
    % V" W, V$ M. T
  4.   MX_DAC_Init();
    4 w+ y/ O, _4 ?+ B5 f( G
  5.   MX_USART1_UART_Init();
    9 c+ ^* t5 y' f' Y9 c  a) `
  6.   MX_TIM1_Init();& t% z' k7 U' _# o% O7 Z
  7.   MX_TIM2_Init();
    2 G6 f$ N- [: ^7 g
  8.   /* USER CODE BEGIN 2 */  S  M( p5 Q- i4 d6 ]1 W7 R( m
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    ; B' @- Y" |" E; A4 u& y! o
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC0 P5 G: }) n) Y9 I: k2 b8 {6 J
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);  T5 K$ s+ z5 Z+ w. R5 \' z
  12.   /* USER CODE END 2 */3 Q% O0 E" B3 G3 e! ^$ x" R

  13. 6 z' M, f# X8 m# X& B' R$ G5 Y; O
  14.   /* Infinite loop */0 T, Z. N( V' ]" n+ m, ?
  15.   /* USER CODE BEGIN WHILE */
    ' B& ~1 I! ~) X, l7 a
  16.   while (1)
    . I4 T$ ~! p& O' O/ p
  17.   {6 s- W! f0 Q# z5 ?! |5 F
  18.     /* USER CODE END WHILE */$ Z/ t& b0 N# \7 d$ x/ i
  19. : }: F0 Q! v: X% s, o% C
  20.     /* USER CODE BEGIN 3 */
    ( D  m5 x7 s  C) ^- f0 s- f, d
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))
    3 ~6 c7 e! ^+ x2 `9 t# [
  22.     {
    % O" J( g) }9 ?7 E0 ~/ E; U: h  h
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC- r6 p1 f' T5 R" R+ j1 a
  24. - i$ {- [0 G5 Q# z+ f% B
  25.       HAL_Delay(20);
    7 @$ U: u5 w: T0 R- y- ^/ w
  26.       8 Y" l, R6 O$ w, ?
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));$ {( W6 |7 t% A9 s& u
  28.       HAL_Delay(1000);0 U$ l3 M5 ]# Y9 W* X
  29.       for(int i = 0;i<ADLenth;i++)' R4 c5 [# Z; U1 w7 F/ S$ T) ~+ r
  30.       {
    1 C' z# a2 ?+ X
  31.         printf("A:%d\r\n",AD_Value[i]);% Q0 @& c  |& P9 i" `2 j
  32.       }
    ; S$ E$ S* ?# S/ z( {# n
  33.       + V6 G. b- N# j  x1 G8 h4 `: [
  34.       
    7 ]0 W& U$ m% ?) l: X2 X: m
  35.     }' l- n2 {' q0 A+ C1 Y0 k
  36.   }  ?/ f' T+ X, Z$ _5 W
  37.   /* USER CODE END 3 */& O( U5 t3 e4 |
  38. }
复制代码

6 i" R- H) {- @# S: P8 y  ?& b' n3 _" f  Z1 R
我们编写一段代码。  o/ @) q( D$ [: s' j% @$ \, U  c

. n* `4 Y% w1 M6 S* E  a
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。
( R% i9 z" M- o; A+ V  w" Z- s- T
( m' w; m6 v6 u# E& u8 E# ]* H 微信图片_20240529172345.png
1 I5 \  z% p! e0 e9 B. C1 [4 B

1 W& `" D$ b% {6 p5 M9 f可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。4 F2 P$ l; Y% T/ Z. T
& ?1 L  L, \; I) `* |
三角波发生器, Y  f, }- f3 |3 x/ [
7 P) a. A+ l  T! i
微信图片_20240529172342.png ) |1 B; _2 u3 m% Q1 h

" W1 i- s% u/ ?2 n" M& P在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。
7 s: s5 K  t: w/ k$ L! D2 e- B  F% N9 W2 y0 }8 E" Y) v
微信图片_20240529172337.png & T% {1 I; X+ D
: \) H! V: D: h
之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。9 H; K* j% I9 \- s: Y4 n
9 y! a/ T( M% E) ], [
微信图片_20240529172334.png ' |5 q6 S$ j& _6 j+ q& C% z' p

+ M- @: ]& }3 A定时器设置好时间之后,设置触发事件。. s9 A) j7 A$ E$ t" W
0 _7 O. C+ ?3 _' H
微信图片_20240529172331.png + D! A" _1 p- E: {
; z- l; y+ B* D. E, D
芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:
% v7 x$ C( E' E4 F" U
7 k0 Y( n* D* Q2 u# d: U3 x
定时器频率/分频系数+1/Period/三角波最大值/2
2 S5 D" ?( @, ~- h5 j( z0 K) G2 F: ^( ]
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样. D+ n: x& Z1 W1 G% l
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出
    1 f( M* H7 B5 P( K
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC% U, m  X4 E6 y/ {, ]8 X) V
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    / k7 v' j2 M8 V/ p
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码

. a+ K7 q- l" ?+ G$ {9 ` 微信图片_20240529172327.png
5 [: X, d* d; L: t& G! H
2 s! d5 I5 Q$ Q5 v0 c" R3 q$ ~0 s
正弦波发生器
9 z" q% X4 o8 B8 J3 y
7 W1 F: _$ {! k! O# L( L, {7 S+ E3 W
微信图片_20240529172323.png
* N/ C7 H7 o6 a2 G! k$ w
. V: J, V/ K1 G, N+ h6 R
首先是定时器触发(方便控制频率)。但是关闭波形发生器。
6 X8 ?0 J, Y/ y0 ]
4 A, F9 j6 k+ I1 h5 U& A 微信图片_20240529172320.png
; x( j+ a. v2 _9 V. Q
4 X* j4 H' T& w$ M5 H0 |1 Q
添加DMA,模式选择循环模式。0 ]4 g5 `2 I* V$ y

; O, ^  y# E$ P- e
还有改变一下定时器的频率!
$ f' M1 [5 w4 u. c; o/ b; K4 G+ H& A
其他几乎不做改变。$ E- r0 P3 T0 b5 [& \5 O$ {
  F7 U8 @' F  [8 N2 |  D
+ T2 Z6 u6 e- ~
  1.   #define POINTS 256                        
    9 l& J+ u7 F: H
  2.   #define MIN_VALUE 50                        
    6 v# r  B# ^% H4 I6 d
  3.   #define MAX_VALUE 650                         $ h$ C7 \' n, o; a
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  % ]6 _. P/ g$ U6 {. k  x- a
  5.   #define OFFSET 50  " x% J( {: I  B" @7 {% ?1 b/ ~' _
  6.   #define M_PI  3.14159265
    1 A8 W( U4 }8 I2 A4 F2 O8 ?
  7.   , V/ b2 Q, p! J$ X, \
  8.   uint16_t SinWaveInt[POINTS];
    6 C5 y% f# n; c  l8 A9 T
  9.   int SinWave[POINTS];
    7 Z3 {- G* Y0 `- J( B6 _0 @; W
  10.   void SinInit(void)
    8 R3 i  m5 c: s
  11. {  & j$ P" D  v8 G& m. }% h
  12.      for (int i = 0; i < POINTS; i++)
    , i! I8 W; c, L: Y& j
  13.       {  
    . a( h. O+ ?4 w% C" Q3 Z/ [; N
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI; ( E* J- K" X  X" d& L9 C
  15.       double sin_value = sin(x);  // 计算正弦值  ' F6 u. _& p5 }# W
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
      R7 t' Z! q* N# @8 @
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  3 d& }6 Y1 N, P1 |$ L( s+ O) g
  18.     }% T2 X' c- N! l4 ?0 t3 ^' r( m! G
  19.   }
复制代码

- S4 q, N6 o- Y( {" ]6 d) D计算一个正弦表。$ u( C0 O% h) c* o& G+ M

, }5 v7 b; Q; X# h+ a  ]  o
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。" c7 |2 v, G0 Z4 i; Z7 ]

% V' z- V6 H* g; |' u 微信图片_20240529172317.png
1 R" ^1 S; n( A+ i
5 ?: Q% b# R0 ?' f
测试一下正弦表,输出的是正弦信号。, B2 c. i9 x* i' _
. G9 @  t9 J  n# v% U
我们之后将正弦信号表导入DMA中。
% z# t4 r1 _, t. P3 p- p# ]0 O# a! _; X, @  k# s( ]
  1.   SinInit();$ h) x7 G4 t; w- n3 ]' H9 v
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码
: V) E9 D1 m4 D+ F$ i9 r7 k
正弦表初始化,之后启动DMA传输,导入正弦信号。. F. R: L! \  s1 S
4 n( ]+ S* d# V- a' D
微信图片_20240529172311.png 4 v4 e8 l* m* S. k2 y* r

* Z' w9 w! n! z6 D( `测试正弦信号成功。
; j# a6 j$ V8 C  h9 Q5 L
+ x' @3 A4 _6 S0 h* |, t: Z
FM调制
  1.   #define POINTS 1024                        
    9 H2 m$ H- C- J2 U3 y1 [- ?2 _
  2.   #define MIN_VALUE 50                         2 a" z& ?1 K# D
  3.   #define MAX_VALUE 650                        
    & n2 G9 C4 K7 I& f6 w3 i% p  ?
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  5 u  w$ {3 G' n% O3 D/ p
  5.   #define OFFSET 50  & w  ]( j) ^( K
  6.   #define M_PI  3.14159265
      G6 W1 P/ g; `* N
  7.   ! j3 `7 _! k% x! N: l. ~! H0 p
  8.   uint16_t SinWaveInt[POINTS];
      ~+ @/ j% p5 x/ F6 u7 e1 z3 n
  9.   int SinWave[POINTS]; . s2 x0 E' T; [: q9 B" {
  10.   void SinInit(void)
    : r, g4 z4 e* \: o( U# t; T
  11. {  : a3 i2 ~( y  p/ D- g2 l
  12.      for (int i = 0; i < POINTS; i++)
      T9 X9 f+ Q! n9 E) O
  13.       {  
    7 J3 c! g2 h& A- H
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI; . [0 W9 ]+ r5 s
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  
    # M) J$ Y/ k& \+ C0 c
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    $ `5 A+ W* s! N$ Q4 ^1 g# v
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    8 h  Z' H9 k' t& z8 u
  18.     }$ R1 ?* a/ {, ~  x0 z
  19.   }
复制代码

' H6 Y7 e8 t1 b7 z# o% y1 ?% ~5 r! B
: [& [* N8 |! q7 v拓宽点数,加上载波,即可构成FM调制信号。
' P3 e& c: [4 \4 Z" m" [5 n6 b; x) J/ g; z7 s
微信图片_20240529172305.png 7 b$ l3 e4 [0 `

3 Z! n( h2 `5 I: Z; F# R1 O3 x$ s: s3 N
转载自:电路小白
/ H! D: n2 W3 N( _( m3 Z1 w' I如有侵权请联系删除
" {4 C; ^" I, p1 f, o" \6 w. s2 m! t2 l+ n

% p3 k* S7 [  L3 B& Q
( i& _& X7 G) x
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 手机版