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

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

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

0 d) R; O7 P; u$ W# hDAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。4 k6 t4 ?  X. H0 \0 n/ z  g8 E4 I$ ^

8 ^6 ]# r; M+ ?* n: a, IDAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。
  X9 M3 M" g, _7 ?# W
$ t% e, c3 }% H, f
微信图片_20240529172405.png
: w3 ^0 U7 G! a8 _
. M1 Y: i: }3 q
STM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。9 G+ B& r' j5 T$ u! \& B, [
$ E+ A; o6 k% y& ?' A/ c1 u! g( e5 M" O
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。
7 d" S" s  s3 i
" m9 P: \; V1 u( Q$ U, w) |+ R
CubeMX配置
$ f: u4 V. s8 G# a# F8 l$ O6 t3 U) p; t$ h' X% \- g: y' D5 J
微信图片_20240529172401.png
9 l" w! H7 w! B* |' W1 Y2 T
$ ^; f8 ]$ n- i/ Y, z4 l
在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。9 N$ `- ^0 ~4 q$ U  e7 G. ^

' m8 Y4 o3 _- ]5 u" q0 b 微信图片_20240529172356.png
4 z& A4 T# K. r5 d( r: s( H+ T; E
! F& L" Z% p& b* N% e# B
OutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。2 p+ U' ^1 _/ N( O$ |9 R
; a* W6 r, o9 `5 D  @
如果选择触发的话是从缓存区写入数据。0 Y3 }' Z9 j3 \1 l9 y) a% c1 Y
$ T9 J, X. q" K! y/ h  g1 j4 L: S
微信图片_20240529172353.png 2 z$ e. Y: q! R+ g( m+ r4 a7 U
$ D8 F1 G  n$ H6 `
这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。
$ P* z: G, j: d: M; k  p3 r! s
; @: D9 x- D: O5 e0 @! ~: d
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。6 ?. V6 `1 F* \0 j/ J  U- ^9 G

) @+ a" ]. i+ {) U! y
STM32的DMA采样+FFT时域分析(STM32F407)
* A8 h2 b, v. U4 k
! I2 @" j3 w/ |% A3 Z% @# {, e
但是相比于之前的,需要更改一些设置,首先是DMA的设置
+ h# p1 J9 v0 G5 u9 |
- [9 n; V( _2 m7 Z. C" @ 微信图片_20240529172349.png
3 J9 C8 P1 A- G! l% }& v* M: e
1 c$ B. _0 L2 ]) `( U
这里设置单词请求,而不是循环模式防止数据跑飞掉。
" S0 _  ]( f; v0 G+ z5 ?9 o( x, r6 c8 w9 Y4 a( `6 w0 `' H9 j

9 N) m( @1 |; H1 J( a  a+ w* G
  1.   MX_GPIO_Init();4 H; C. r) k4 k) {- Z$ u
  2.   MX_DMA_Init();
    / R( _: R3 s: @4 Z! m* S
  3.   MX_ADC1_Init();
    $ m% ]  H% A, c3 z4 ?2 ]& P
  4.   MX_DAC_Init();
    ! ]5 q4 b6 E. t
  5.   MX_USART1_UART_Init();# y+ F4 K9 R. ~5 l$ P' r4 |
  6.   MX_TIM1_Init();' P/ w; \. I. r" B' D$ c4 b; ~5 j
  7.   MX_TIM2_Init();
    / {, x& M) _  |3 d; m
  8.   /* USER CODE BEGIN 2 */. }  Q. t2 ]' @# m/ \) X9 `
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    & l$ j3 y4 @& k1 q. H: E
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    9 y9 b4 x5 D4 t6 |3 v
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    ( A, L* M% l/ X' x
  12.   /* USER CODE END 2 */: |  s8 A1 v$ L3 C- F

  13. $ F& l' Q% g, c4 r3 F: C
  14.   /* Infinite loop */) \, s' @2 i8 {
  15.   /* USER CODE BEGIN WHILE */2 h( N& x! r3 i. L
  16.   while (1)
    / S, A; j6 Y, \+ y" c$ s
  17.   {4 c! D6 Z% e: Z$ H5 }
  18.     /* USER CODE END WHILE */1 Q  R+ i' O( j3 Y

  19. 7 V0 x0 w& l0 v
  20.     /* USER CODE BEGIN 3 */
    8 f' r4 H7 P+ r; b4 P
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))$ n3 V, n1 ^4 K. M
  22.     {
      @$ S$ p4 W  O- Q! |% Z
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC9 R6 g- t; E* C0 z
  24. 2 C6 @# ^/ U" W
  25.       HAL_Delay(20);: N; ~8 D! h3 S/ [; J
  26.       0 i& E  [: `& h1 z! p" I# [
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));, H: `$ N9 e. [" D4 t( H
  28.       HAL_Delay(1000);
    , \. p, f3 A# w2 |; e
  29.       for(int i = 0;i<ADLenth;i++)0 b& ]( z5 P* c. p) R
  30.       {+ G& e3 u$ A, d( S1 b, |8 ^
  31.         printf("A:%d\r\n",AD_Value[i]);$ w! p' V1 I, Z$ _# P  ~5 q& Z4 U
  32.       }& E6 o$ J5 P+ w  @; T
  33.       
    & d) G1 y& M' Y+ Z8 R+ |# M
  34.       
    / p& _+ Y5 V# _- x
  35.     }" a, ?2 F7 c, {9 \  F' ]
  36.   }
    * E) _3 b- S1 P3 g9 S
  37.   /* USER CODE END 3 */( k; g, e/ i+ }  K9 M
  38. }
复制代码
: `% f% P: e1 W$ e+ `

; W( h$ q& T5 F0 ?% I我们编写一段代码。! a2 m. B" h7 d, U

7 Y$ I0 ^8 Z# H; j0 L; r
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。1 f# @4 U8 {' S0 c9 G3 N6 ~4 B1 Q

+ G% `; O0 {$ d" j 微信图片_20240529172345.png 8 x$ F& g9 ]- Q

3 a' y) a4 ~9 A! Y) i可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。
6 @; v7 O( e6 p  b! c# U; I! l
- c3 B7 ]2 L. p8 a  E( m7 c# Z
三角波发生器/ f$ Y- ?* ], `6 T

1 M/ f) o) p8 x
微信图片_20240529172342.png
. n" d0 E* i  }  _6 {! ?% Y6 ]

# Q9 ]5 F& d+ v1 P: D6 E8 k6 S在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。
6 T) Z+ h- C% X* ]- C1 _  Z: ?1 O5 u* c0 g  B, Z
微信图片_20240529172337.png
/ a: i. U" N( w

+ ]* B( f4 t# p  F) B7 \之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。
: O4 J" U% Y9 i# [9 k7 Q5 n' u. N5 M0 v% {) L% ?" M" e; U
微信图片_20240529172334.png
" L; ?& N8 g- R  s' y( d4 B

+ K' U  v) P9 g5 j3 O9 h( J定时器设置好时间之后,设置触发事件。
' B2 O/ e+ N% ~/ H8 d* k: y6 O# Q1 T. j3 W" {) \# h5 B
微信图片_20240529172331.png
1 b0 h- L/ ]+ y" [! A

# e5 Z3 r1 y9 x3 o* q6 E芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:
/ p- x; ?  M. _2 l0 K2 J6 l9 B$ f, ~9 [; Y, x6 t( \
定时器频率/分频系数+1/Period/三角波最大值/2# j- w# W* p  B, \: `; Q
6 u# ^, J: v/ [
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    7 ]$ |1 }% O5 H
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出" _+ \: v; D; B/ n; n+ b3 t
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    % d' ~: N7 y. w* Y
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);4 }7 }* K1 C8 e( _* W, i
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码

2 y- U/ s7 Q; B* f( q/ \+ [# J 微信图片_20240529172327.png 1 N2 E+ b2 U. Y

, m  F4 U) c' r3 W正弦波发生器1 x, \. Z4 S" d: e( M
: ^' [1 E! z; }! R/ B& M  H2 v
微信图片_20240529172323.png
- z) P5 O" E3 n: z; p$ z
+ }; i. b& H. s# l! K! }  c: Y
首先是定时器触发(方便控制频率)。但是关闭波形发生器。! G* E- J! P: h: G
& b5 ~/ e$ a& C9 G% h
微信图片_20240529172320.png
( w; t" ?- x2 Y4 ^9 |

2 m  U5 y# p. F添加DMA,模式选择循环模式。
% n; y% V( i, J- E
+ Q, a# ]1 V  h% M
还有改变一下定时器的频率!/ s3 x, C% s6 V* R9 M, t$ ^
2 ~$ g1 G3 T) T- M* h! M$ v
其他几乎不做改变。
# G8 }, k/ c% I; ~8 ?- D% ?
! O+ d& `( ^" ~6 L. R+ b; e, ?

0 G* t1 C$ L! L8 Q, w% l* H
  1.   #define POINTS 256                        
    7 Z- C: V! L2 T, c; g
  2.   #define MIN_VALUE 50                        
    , q' ^, h3 M2 }) Z  U
  3.   #define MAX_VALUE 650                         + N* b7 K2 v, `3 M( D/ _3 S3 k9 u+ F
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    5 F9 c7 a4 ]6 W- t1 B* C! P1 z
  5.   #define OFFSET 50  
    ' Q" C% H2 r, o9 E, m; V! k) ^
  6.   #define M_PI  3.14159265
    / d* U( {) f, P8 ]8 x- w
  7.   
    " J. a3 |9 c# r, O7 y
  8.   uint16_t SinWaveInt[POINTS];1 m6 A- r5 s* k8 s% t
  9.   int SinWave[POINTS]; - A' V$ Z: r' b3 J" j9 s+ }
  10.   void SinInit(void) ' I' f. M( D6 t) X! n: r7 e2 M9 }
  11. {  5 p1 m; N7 I9 U% s
  12.      for (int i = 0; i < POINTS; i++) ' m, V" T; l3 w$ o$ `7 B' n8 X: i
  13.       {  # u, U- ~% S0 C0 b4 T9 W, [, ?( U: G" l
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI; - l+ ?+ X  V1 \/ I
  15.       double sin_value = sin(x);  // 计算正弦值  4 R# U$ ^8 d8 s# Z
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    2 {% b/ o- _) c7 K4 {9 }/ D% O
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    " I* c, e* M- z1 t: v
  18.     }- y4 @; U5 Y6 f; y/ x+ w. g6 r
  19.   }
复制代码
+ ^+ B5 H) q# o9 b: e" B
计算一个正弦表。
' {/ b$ {" m$ K/ L. ~* Z9 s7 C) [" T) g# N" r6 d& r
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。
5 z8 y" R  o% n6 K# b- Q0 Z, z& x( W: i: I% p7 z
微信图片_20240529172317.png
& d& i  w3 ^' B  ?9 C) R
/ z( H  ~8 H& \& |6 `
测试一下正弦表,输出的是正弦信号。
# X  x  }9 u  j0 ~. M; b
+ U4 v3 R: e& @2 v; k
我们之后将正弦信号表导入DMA中。4 V4 x4 j! h9 `2 P  l

, P% B3 |8 b$ F+ p$ w5 g; s2 E1 _
  1.   SinInit();
    9 g/ x4 J) `4 O' A3 U
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码
4 l0 d# W; I1 U
正弦表初始化,之后启动DMA传输,导入正弦信号。' }4 e+ {( ^3 s) _7 r5 F& i/ q+ n
8 w; k5 J6 Z/ L) A% D9 G% A
微信图片_20240529172311.png 5 R$ w: w& O/ c; F; j) e6 D; g+ }
( ]5 p7 W% c4 c3 d! ^8 l
测试正弦信号成功。: ]' I& y4 z" ?. L. H

9 Q  V/ l3 i  w8 G
FM调制
  1.   #define POINTS 1024                         + C4 P7 A+ p4 N
  2.   #define MIN_VALUE 50                         # M: y/ ?" F/ l0 z
  3.   #define MAX_VALUE 650                        
    * n: K' S9 n% F8 C; J% u/ x4 V
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  ! N) l% q, D% A1 ^/ \5 r9 W6 {1 }
  5.   #define OFFSET 50  
      o! V# c7 W6 B+ F
  6.   #define M_PI  3.14159265
    ( g; s) _& \$ H/ Y4 m
  7.   , a/ M& g" l% x& a
  8.   uint16_t SinWaveInt[POINTS];4 [( K! c; z( K7 ^/ p" e; Z
  9.   int SinWave[POINTS];
    $ Q! e' J: G4 {0 v4 P) q) A. e
  10.   void SinInit(void) " V+ a5 {* O( G: S/ T1 c- f
  11. {  * @2 l3 j3 k/ M# c" W
  12.      for (int i = 0; i < POINTS; i++) ; H5 Q1 ?: r  x& F3 w5 b0 ~5 G
  13.       {  
    ' i- f& g/ f1 W+ n" G
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI; 9 W; _$ n: x; ]) }0 v% t
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  
    : b5 b6 Q. G, X: R
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    8 W, f* F7 b6 [! ], Y
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    ! R+ ?3 k: a: E* g! n; ]
  18.     }0 x! @% x0 Q) D
  19.   }
复制代码

% g8 ?7 E7 B# P$ i
0 T) g! _  d+ F) Y1 s& T拓宽点数,加上载波,即可构成FM调制信号。: r$ [; {1 O6 X+ P# ^

, ^  y- M1 A# ~+ x* f+ n  g 微信图片_20240529172305.png
7 [1 p$ H/ q% y! q7 b# n, j( _) P. T/ p  N; N2 x# E" {6 p1 y4 _  T+ T8 U: G, w
- ]1 e9 b! v& Y/ n* F
转载自:电路小白$ M- w0 }. D  W+ W
如有侵权请联系删除: d# }7 M8 |4 v; g! w  j
' S, r* L5 v3 F+ r: \' J

  {$ ]" i, B0 u; p- j0 w3 H) G
赞 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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版