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

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

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

( a0 k! K+ L3 k  e8 {) j. IDAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
( C0 V$ D1 E9 G6 m+ c, G0 r
6 @- t4 a. B7 q4 W8 U6 ?% V$ r' h
DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。' ]1 m7 ?! J0 s) C0 Z% K

: k3 L' O, ~; m# y1 H3 _! N5 \$ \
微信图片_20240529172405.png 4 @. Q! f" n5 N$ W0 p

! Y/ D: }6 n7 A& q5 a4 hSTM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。
$ |$ r- E7 H: \" [6 U# q1 I$ }" c8 I
8 B4 D7 p/ \$ t
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。
! O& {6 Q, E; U( T+ m
/ V: J6 y0 v7 E* ]& d
CubeMX配置
, ?0 [7 _( @& Y8 r; g! L, `; D+ |1 A5 ^- z6 w- J% S
微信图片_20240529172401.png 6 H# X6 Q7 ?4 q: w  t. n# d5 J

, m' o/ q5 J9 T2 L在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。
) [/ U# c+ c% P0 h( j( R8 o) H4 Y( E$ Y' ?$ u
微信图片_20240529172356.png
4 O8 [/ u4 I/ \7 E: h7 q
/ \, @3 u5 Y( i% O$ N0 u& C
OutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。- Y7 s+ r3 h( c0 B5 z4 w
4 e' s4 t8 G+ {, k5 U
如果选择触发的话是从缓存区写入数据。
' u6 I3 t- ~% M$ H+ d
9 m8 m. q" y8 i3 s% {8 H 微信图片_20240529172353.png
9 a; r; O/ L# T3 I

; s0 y) }5 Q! K; ?9 V5 g这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。
! H3 l# X* C! O9 @1 O  F5 T2 ]' F. u8 n- z6 z; @( s' p& n
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。
( I# W  d$ G: L! q# G/ _
$ ?0 u# Z$ o2 P) S- U& D% ]6 j
STM32的DMA采样+FFT时域分析(STM32F407)& v+ N" H8 e1 U9 u* U/ q

3 F0 a0 `8 Q2 d2 M
但是相比于之前的,需要更改一些设置,首先是DMA的设置
% N! H) O& k( T" L
: o4 G7 L1 Q2 O; n 微信图片_20240529172349.png 5 s, t- o# e7 A, j9 L9 |: ?0 a
3 m; E" s9 ^  u( Q3 P' U2 J
这里设置单词请求,而不是循环模式防止数据跑飞掉。
$ {( e6 j6 ]9 q5 P) r, @5 G' _
" G+ k0 z! ~% W3 p
$ V7 ~$ _8 Z9 b8 b7 k: a
  1.   MX_GPIO_Init();
    ! r$ Q* c& G  g8 \9 z
  2.   MX_DMA_Init();0 T1 ?: b" v" B, \1 L
  3.   MX_ADC1_Init();
    2 ^* E7 x! R( K# p2 j9 Q
  4.   MX_DAC_Init();: y, z: q6 \; E5 I
  5.   MX_USART1_UART_Init();' l5 Y  X! P+ S2 c
  6.   MX_TIM1_Init();8 S9 R- h# e& |- j" d# t1 v
  7.   MX_TIM2_Init();
    $ m/ M9 g* ~+ z# E
  8.   /* USER CODE BEGIN 2 */
    % U) R, O/ j  d7 Y$ p1 y- A
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
      P6 {& y4 D/ x# _
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    7 n+ l+ \0 R: k( y. u0 Z! M: }3 T
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    4 d" N9 A! V% F3 D$ z2 T( ~
  12.   /* USER CODE END 2 */
    3 s0 Q, m/ F1 @6 w9 T9 m$ B
  13. 9 c" b1 @% c% M9 B- P% U/ C" D
  14.   /* Infinite loop */8 D& H+ D3 K) S9 }, b
  15.   /* USER CODE BEGIN WHILE */' [  Q9 }/ z# R. E* `+ e9 H, h( Q
  16.   while (1)
    5 ^% U! k; }" g. U8 @
  17.   {
    3 W3 v, c6 x4 x- _7 ~! O7 P
  18.     /* USER CODE END WHILE */1 o, r; X8 l/ V& M
  19. . s4 [4 Q1 P$ F/ Z
  20.     /* USER CODE BEGIN 3 */. ^1 B- K. `. b7 u: `
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))) O9 m3 [3 D  ~: Y) L
  22.     {' m9 M7 M1 }3 W7 C" v! [- N) H. w9 q7 w
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    5 A3 f, G* M; O

  24. 1 I* ?/ ~/ k" L( ?1 u
  25.       HAL_Delay(20);
    0 F) v. [! t$ l5 o' k
  26.       
    / q* t6 a( A( V
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));8 [$ a0 E+ Z/ t+ x( W2 m2 X$ I
  28.       HAL_Delay(1000);' g6 M5 o# P, q' n0 y! W9 {) i
  29.       for(int i = 0;i<ADLenth;i++)
    , M! E# G5 J( K5 t
  30.       {
    " {' x- e3 a- |$ v. Q
  31.         printf("A:%d\r\n",AD_Value[i]);
    9 `1 Z% g/ E* f/ I: R4 n
  32.       }
    ( J+ {; R  W' ^5 `, p4 y: y; {
  33.       8 q$ P: H) P2 s7 T1 c/ T
  34.       5 Z' Y' b9 A0 d! h( s6 T& X' s6 l
  35.     }; f% }2 |& \  W1 T" B  o8 d, k
  36.   }2 {: Y) \! L) L( z
  37.   /* USER CODE END 3 */
    ! F& ^' B' X# i2 T" G
  38. }
复制代码

* o- ?5 T- l/ f& L' w0 Z0 m9 ]. d( P2 z! m+ g: l4 ^4 P
我们编写一段代码。
: U+ s4 x/ F3 I* D+ r& _5 \
9 \2 k* A* f2 ~0 O- r4 k4 J
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。& ^  P* Z) A. Z) r/ @3 {1 h

9 v( |  d, N2 }1 X- x1 e* ? 微信图片_20240529172345.png * [- P* T. K7 i" n  K6 k5 H8 o
$ J8 I7 X9 i: m& u
可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。
) x; p5 D% T+ t2 u; I
0 I4 D& [  S  G6 Y6 C0 w4 Z8 y
三角波发生器
/ d, U! ~% h! j# Q& Z; o" _1 V) d$ M4 K
微信图片_20240529172342.png
! r7 u9 S' z( b! I( U: `5 K5 Z0 f

/ z  [  n8 }8 t6 T+ w9 K$ k4 P在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。. w; ?4 k4 y' T# ^

; H" X0 a& Y! c$ d; V& V8 f 微信图片_20240529172337.png ) C" ^0 O! S7 ^2 d/ v% }, J; Y. K8 r

3 y/ L) \% K/ Q8 n之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。# y' Y8 M. N9 I2 l6 V- t; }

9 H) U0 M% l& O% b' s6 D- E 微信图片_20240529172334.png
4 n4 A6 Z4 e1 R

6 i* u! P# \' a2 K3 S6 b# R定时器设置好时间之后,设置触发事件。
6 g  N$ M& S  m/ o3 m# H" X$ i9 t$ ^9 R; l/ u2 n) j9 ~2 l
微信图片_20240529172331.png 9 ?4 F. n& b( Z9 D& ]% o

7 a2 t% p1 G7 `% _芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:4 Q5 _# W3 X7 N! W' [4 |
8 x& o4 K* Q1 Y, B4 D
定时器频率/分频系数+1/Period/三角波最大值/2" n1 k; T2 P/ ]" `1 n& ~# d

8 r8 h+ U8 q( K, h
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样; D7 S% R6 v1 f
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出% y( W5 S* K' B% k, o7 a
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    1 k/ b+ x6 s6 V/ ?6 [& X$ R4 l6 k
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    4 o( _4 E7 C6 H) a5 B0 i
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码

. |% M) S+ j7 A5 K9 G 微信图片_20240529172327.png
, ^7 I% Z3 J9 a% ?: q

: j" A8 |1 \. o4 h正弦波发生器! s! |$ J  F/ H; b' Z
3 |  B4 Q+ C' |5 T% H/ |
微信图片_20240529172323.png
+ ~  k) F/ {1 X, ~; l$ ?% h0 t

) R, d8 b: d/ u. v首先是定时器触发(方便控制频率)。但是关闭波形发生器。
8 z$ T$ Q6 N% Z- d7 D
( t5 D( }) P* D. h; }( B 微信图片_20240529172320.png
/ j6 d) a2 r! b  [% j
' f1 L! u- f, h$ E- ?
添加DMA,模式选择循环模式。
3 F- c& y) W# B% Y( T
  e. I" i7 O. J
还有改变一下定时器的频率!4 ~7 J6 Y$ D+ m: ~* o0 G6 W! M( R
/ ]) Z: o4 `2 t/ a6 D
其他几乎不做改变。' N5 B% e( Z$ x1 a/ k8 G9 ^
5 e# v5 ]. G, {+ c: j* o) d! V- b
6 V( W; a. e& R& }
  1.   #define POINTS 256                        
    4 J% n. D) y+ G0 Z+ @
  2.   #define MIN_VALUE 50                        
    & x7 N9 U( V" d  x
  3.   #define MAX_VALUE 650                         . ?# s% [8 F: q  M1 \7 }! V
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  % y' X* r4 G* ~. C5 u
  5.   #define OFFSET 50  
      B  H4 j7 O: P: W% b- N$ S$ @
  6.   #define M_PI  3.141592656 L. S7 w5 S, Y
  7.   
      s8 p! \# q5 T
  8.   uint16_t SinWaveInt[POINTS];
    / e8 W: S* _/ [; P2 O. {5 ^: W
  9.   int SinWave[POINTS];
    5 g/ Q8 c1 ~+ U- S2 t5 o
  10.   void SinInit(void) # I+ a  M" s* I4 W1 \9 m# h. f9 n
  11. {  
    & G4 r2 X5 z5 r$ n. p
  12.      for (int i = 0; i < POINTS; i++) + f! H8 N# p; f% x5 l/ X
  13.       {  
    / W; m+ E& f3 r2 a% H/ P
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI; 9 }7 ~. l( a. L" V
  15.       double sin_value = sin(x);  // 计算正弦值  ! l8 n0 V6 Q( O) z+ ^
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);   g; ^5 A. f4 l4 n0 b( i
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    / r: r8 r! e- [, B( L! S. p; d
  18.     }$ F4 O" F2 w* a% v. M; u4 f
  19.   }
复制代码

' S- ]( o* X8 y: j# u# m计算一个正弦表。: S; [& _5 t8 _' ^1 W
. U5 z2 X' k# V. @/ F  \
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。
9 |& c1 J- P/ f5 g, e, ?! q/ b. u% w& `' g  P) K& P( I
微信图片_20240529172317.png $ d1 ]$ S3 c: ]0 M! B

& L6 D" j: W1 h& E, u测试一下正弦表,输出的是正弦信号。- s  A0 b( X4 O% M( p' c0 \) @
- h% P1 p: i0 K/ b& W  {
我们之后将正弦信号表导入DMA中。
. Q0 `! z4 k4 E( ]4 ~6 e9 a/ }- W3 y$ K9 X6 @/ F
  1.   SinInit();
    : O$ X3 }4 j% M
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码
5 j( n: s! P9 B! C8 t. P
正弦表初始化,之后启动DMA传输,导入正弦信号。' H+ N, c3 c6 I- D1 O, I
4 c4 d2 B) q3 A1 D/ R3 \4 b" O5 k
微信图片_20240529172311.png
2 }5 P& e; |- J: j
! J" ~; z5 M) O% q
测试正弦信号成功。
3 E, ^" M" ~5 s$ s
8 G8 a6 f' i+ I3 P, U+ h
FM调制
  1.   #define POINTS 1024                         : [( n% M; O' z& j/ E
  2.   #define MIN_VALUE 50                         9 Q$ i7 F1 P$ m- T1 r+ R/ U) m4 m
  3.   #define MAX_VALUE 650                         4 f3 g& Y* j* S8 P' H
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)    ]7 x, m( W; T0 l1 n
  5.   #define OFFSET 50  
    : ^! w" F( g' @. V& h' o
  6.   #define M_PI  3.14159265" W+ p" Z) L# G+ }( [/ b
  7.   & l* X. F) G) p1 G( k6 O  y
  8.   uint16_t SinWaveInt[POINTS];
    - b9 q: Q3 @1 F# ?+ k* V
  9.   int SinWave[POINTS]; ' Q% C; K8 i$ X) a8 k. [4 J- ?# T
  10.   void SinInit(void)
    / C/ o* u! A, v, U: v' P
  11. {  " ^0 U( S6 [( r7 z' H: W, l
  12.      for (int i = 0; i < POINTS; i++) 8 q; S# O- l6 }! v3 q; R" I
  13.       {  
    , j3 U' ]6 e% x0 P
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI; ! B( j. c4 k7 _: d8 ?% n/ Q* W
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  
    , B, \( b! O3 o* s# W8 m, c0 K
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); 8 ?! F/ |0 q6 `2 Q6 Z
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
      e9 e  K8 l/ U. L2 Y
  18.     }9 H6 t7 ^: v( P) H; D& v
  19.   }
复制代码
% C$ V- g' M8 {8 b+ w- F) t

. i# F% j7 M6 }9 x( ]' _# q1 L拓宽点数,加上载波,即可构成FM调制信号。" S- e. T8 q+ v4 q+ Y4 m
, M6 \/ ?$ |8 M
微信图片_20240529172305.png
4 O; O' `5 D& F" W5 s4 N3 E0 N6 @6 K7 w  d

. N* X6 M  y* [# D7 L( s8 z转载自:电路小白
' X. B* G0 b+ Y如有侵权请联系删除: [5 q$ G: V8 G0 w/ W/ e4 N3 @

3 T  B/ p$ l7 I& W( p* Q+ r) R# Q- \: i; l
+ I$ r5 |1 v/ 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 手机版