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

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

[复制链接]
攻城狮Melo 发布时间:2024-5-29 17:24
DAC,即Digital to Analog Convertor,是数字到模拟转换器,也称为D/A转换器。其核心部分由R-2R电阻网络(也称倒T型电阻网络)、模拟开关和运算放大器组成。它可以将二进制码或BCD码表示的数字量转换为与其成正比的模拟量输出。
% p7 \5 t% k+ ~( X
. A3 y! d2 D& A! F/ YDAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
( _8 L2 x3 E9 d3 m# p: R9 ]' X
: ^( c3 ~6 L) a) s' b3 R
DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。2 ?  D" W+ ?$ P% v% i
3 v( I+ o4 B1 p" s& V0 U7 X
微信图片_20240529172405.png 7 @8 K" x: c8 a* B2 U4 F; k  q

  i2 {9 _3 P. |) z* \3 kSTM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。
+ g: j- `# H7 J  l
6 Y5 f8 j1 E, S% u$ X4 C
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。7 Z9 O+ \# }& o/ }& X. @& p) [& Q
* z* M$ B& m4 O, s1 h! ~% ^$ m
CubeMX配置
2 d8 W( ?: E9 L  M8 K
! M8 h" F3 Q/ S# _& |9 E 微信图片_20240529172401.png
0 ~2 n! h6 @! p0 t
. y! y  X1 N, o7 ?( i+ x
在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。& C( Z* J& A6 H) S4 s8 J( {% Z

4 `' Z  T* ]- N 微信图片_20240529172356.png
9 ~4 H$ e- W8 v. r9 R  r0 Z
8 R4 r+ P& {9 X6 e
OutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。+ Z. [# x, [! F  \( s

+ t; F. s* q5 O9 X( G- F% }2 v' ?" J
如果选择触发的话是从缓存区写入数据。
+ [9 q* X4 E9 o  v( e3 a. y$ _5 h, y( z' d
微信图片_20240529172353.png 7 I) L9 E' H' ~* t6 Z0 a
+ V  r+ W- P5 D/ n3 ?8 G# }
这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。9 q) _+ D2 L% M# e7 T

: c! y3 i/ x8 ?; \+ ~
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。8 C& v# @4 v  V0 G& n! Q
% R! d, K3 D( Q0 G- Y$ w, K
STM32的DMA采样+FFT时域分析(STM32F407)
! z" D* y; X* U; E1 r# X6 o4 R
( C. C- q  D! W( w# S
但是相比于之前的,需要更改一些设置,首先是DMA的设置+ l, y+ a2 _& _3 f0 @4 M$ W

9 q6 h* Z( U- M& n 微信图片_20240529172349.png
+ Z: W6 T: ]9 ]% b, ~
/ m) U8 V9 G0 R- k8 z* Z
这里设置单词请求,而不是循环模式防止数据跑飞掉。
. _( Y: _( h9 u- ^6 `9 }. \
3 F) Y8 \# h' }

% u1 x. ?' g: |, d9 k/ n9 q
  1.   MX_GPIO_Init();6 o/ M' S8 S. ?+ x/ ?
  2.   MX_DMA_Init();
    ! [; q* g: M# c* Z8 u" `7 D
  3.   MX_ADC1_Init();
    * @0 M" }& R" {2 M% Z+ ~2 ^+ N
  4.   MX_DAC_Init();
      t  m$ H: l6 |6 J$ b
  5.   MX_USART1_UART_Init();% w9 ^' S5 Q: Y/ a+ K) U; C1 @; w
  6.   MX_TIM1_Init();
    4 ]7 i8 n9 p3 q8 t
  7.   MX_TIM2_Init();- N2 v" Q; e; T+ m) }" P  K1 K' O
  8.   /* USER CODE BEGIN 2 */
    # F: |* v/ y( k9 o
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    + |& ?6 _( u8 \3 b! d% i1 M% _' n
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC3 L3 b, b* o. w! o5 `
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);( A' Q' C4 d5 r4 c. u  `/ y
  12.   /* USER CODE END 2 */
    * o- u' P( G  N& D

  13. * S: C: s, [6 I3 J
  14.   /* Infinite loop */1 v, ^! l6 ^0 B/ R2 s) h
  15.   /* USER CODE BEGIN WHILE */; C6 \( u# H3 @8 h2 _
  16.   while (1)
    ; `5 L% k/ v) m8 E. p
  17.   {9 z+ b/ M% R9 a7 p
  18.     /* USER CODE END WHILE */7 n3 @) y( e6 W7 J8 P7 w

  19. 2 D( p1 X& r2 `% |1 C
  20.     /* USER CODE BEGIN 3 */- q5 C% V+ k9 i
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))
    ( I' j1 q+ c* f
  22.     {% g% n( y2 |* T( o. z$ p
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    0 ~$ [* a/ E# o6 M

  24. $ H9 I7 p6 J( b
  25.       HAL_Delay(20);3 z$ u8 T' i0 s& [
  26.       6 l1 F2 X1 c  J7 V' e" d0 O
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));
    4 y" i, J9 n0 f& w9 R9 J
  28.       HAL_Delay(1000);1 o$ j0 j$ E1 t& d+ J; L8 ?
  29.       for(int i = 0;i<ADLenth;i++)6 J7 |: P$ t8 F4 T  C7 Q6 W- ^
  30.       {' N& L0 i7 U+ [: Q+ e" b
  31.         printf("A:%d\r\n",AD_Value[i]);
    * b% Y5 Z; Q( C7 {0 P3 \' W8 }% T' A
  32.       }
    * J9 ?4 x& j& @2 q3 N
  33.       
    5 d$ Z0 D; U! g3 w! k& p
  34.       
    3 Q; V+ j, z% W8 p2 ?. [/ L
  35.     }
    4 \- `# O$ x5 H
  36.   }
    ' C" N! V- k3 S" I" W4 m
  37.   /* USER CODE END 3 */" b. s3 K% E; z
  38. }
复制代码

6 ~; c- n( r8 j
5 D3 Q# S: L6 p% o! O0 z2 o; A" z我们编写一段代码。
! c% X9 Q/ ~" w5 I/ ?
1 _7 D6 T0 a" D% D
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。% @" x. D) M' |- k$ a0 F
% ?" G4 t+ W" A/ f! H
微信图片_20240529172345.png
. p( w: T/ W' E4 y  A

# i- e* i! k1 V; n# M6 l可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。' ]: b8 i+ s& f

# n6 L9 T; ]7 J! V- J1 ^
三角波发生器; K3 E3 i4 n2 y! C$ B" c8 z

+ r7 D4 K$ j9 k( m2 F: p: j) v
微信图片_20240529172342.png
9 h! m5 }3 t7 Y2 l5 e; W( i: V
9 V, T+ @/ t: T. z' |( m4 ^
在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。. {% S" [& w! y" h( J( o
2 M3 S  `7 c6 J" Q# N
微信图片_20240529172337.png - H* E3 N$ z+ g6 o; `2 m
; [" m: q4 E& s- t3 c
之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。
4 r) P# @) B- Z- D) m
) J( A1 d8 G( q& ^' n 微信图片_20240529172334.png 3 ~3 t* R) _4 u' p7 o
3 @  r. n% F& e- ?
定时器设置好时间之后,设置触发事件。
5 n. M( z7 [7 G' N  E, `* N2 R3 z/ n; }" |
微信图片_20240529172331.png
3 ]! m/ p! U: l, W; c

. s/ z9 E; K* c% [+ ?+ X/ X) H( e3 G芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:' B  k' d# f9 g* }0 {( H

0 f4 u: \- E% }& q
定时器频率/分频系数+1/Period/三角波最大值/2
3 D! \: f' u% _) S( ?% L1 C
" g; w+ |9 f% r9 B6 s; W
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    8 b1 s7 Q) }! d2 T' R6 r( m% i9 K
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出3 s7 ~5 ]4 k6 S) R
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    " O0 _! j) X9 j) i1 P9 B$ ]8 Y
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    . \6 J3 C/ K& j
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码
' t5 l; V9 ~# R) @( {& s, s. q
微信图片_20240529172327.png 9 y" ^+ }. B) J- g( D! b: u
4 p+ z3 |" E6 O* m! o2 R* Z$ y
正弦波发生器
% n$ g% I" Q; W3 o
# ]5 T/ L) h# D. }" `8 @) E7 M8 T
微信图片_20240529172323.png
# V2 Q' @( b( N/ b1 F

3 Z% @5 A$ S' m! K7 y0 \% p首先是定时器触发(方便控制频率)。但是关闭波形发生器。% K  v! [$ L  ]

2 e! Y3 E; g; W# } 微信图片_20240529172320.png ; I/ \: H' o7 E: k' {  T
( u8 x! D* z% ~8 d
添加DMA,模式选择循环模式。
/ \' J3 v+ @7 Z; D9 I
! R, i! Z& o  @. i# Y( S
还有改变一下定时器的频率!
1 s1 q4 f  K- R3 `+ o5 ]$ I: D0 b# i4 T
其他几乎不做改变。# N6 Z6 N6 W; W3 d
, O- U$ V; ?3 w/ m5 O! l, p/ V

5 W  m& C& Q7 K' @2 J9 [
  1.   #define POINTS 256                         $ u5 M8 C& K  L/ g' c, }7 d3 s
  2.   #define MIN_VALUE 50                         2 M. F7 M$ W, w* E& }3 l2 ~1 W- E; r9 W
  3.   #define MAX_VALUE 650                        
    9 t2 Q3 u) |* [/ a) K
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  " E& i) s- `; }+ ]$ O* B$ `
  5.   #define OFFSET 50  " ]+ \3 v9 U9 O' d
  6.   #define M_PI  3.14159265
    ; M' }% ~: s5 r
  7.   
    * P2 M/ O/ H9 W& c7 E
  8.   uint16_t SinWaveInt[POINTS];# D5 z0 j3 Q0 J" V; t6 o
  9.   int SinWave[POINTS]; 3 Y/ t' H8 ^2 ~$ m+ l" ^5 @* @6 {
  10.   void SinInit(void)   C- ?' o: ~" s6 a. U8 ^8 ^
  11. {  
    1 B# j6 j& L. W+ C5 {( L0 w
  12.      for (int i = 0; i < POINTS; i++) " M: p5 Y% m" w& Q8 B
  13.       {  + F0 X2 t9 `4 X( H6 h
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI; ; M8 |4 `: D( M; b
  15.       double sin_value = sin(x);  // 计算正弦值  ' y* \" \) L4 E6 a8 I0 A; q
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    , A% x. t4 ^: P& G4 F5 b: R: e5 g
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    7 t) r7 O! J8 v3 Z
  18.     }
    9 o& A8 @4 Z! d
  19.   }
复制代码

; t5 w5 i+ P1 p2 t计算一个正弦表。
! A/ N! F3 s7 z! s, a! I
2 v9 Z  u9 t7 P9 l" I; h
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。
  V7 Z9 p6 g& b/ N* B0 U+ C4 B3 X3 m+ H1 N- V& K
微信图片_20240529172317.png
( m. _+ X: d4 @5 H  Q* Q. f

3 z1 G% L  B  B2 }+ c测试一下正弦表,输出的是正弦信号。1 `" O8 {, W' y
1 x* q' \: D. z4 X1 I7 e7 f9 j( x
我们之后将正弦信号表导入DMA中。0 D6 ]" R6 k( l3 K

( N9 U* }& ~" X7 S: r5 J) _5 \
  1.   SinInit();7 w; ]# l( x6 }( N( K
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码

8 j( A6 @( m" |" ~正弦表初始化,之后启动DMA传输,导入正弦信号。
' B  I6 s7 |6 S$ p% T7 X4 Z; w5 U& r
: p! g( N: H6 ^4 Z& U 微信图片_20240529172311.png 8 O- V! j# ]& M1 T( s

1 W: K" x* G7 p+ |$ w$ }( t测试正弦信号成功。
) H. x/ j; A- m, e! q+ q8 j' o3 l2 l- {3 X
FM调制
  1.   #define POINTS 1024                         0 b8 p' X& e; ~1 k4 s: p8 [& [
  2.   #define MIN_VALUE 50                        
    ; d; I- `3 e% G: _3 v! i
  3.   #define MAX_VALUE 650                         8 X# q; ~  v& B1 F$ ?: z+ v- ?# S5 j. d
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    6 B4 {9 Q# P' Q3 c. t: i$ c
  5.   #define OFFSET 50  + h  C" I( P$ Y3 d4 @
  6.   #define M_PI  3.14159265
    / V! L* R; c+ j. t2 l- Q
  7.   
    7 Y6 Z* E  u" \  v! G; }* m
  8.   uint16_t SinWaveInt[POINTS];
    # I! a# V0 y$ H5 v3 \/ u( Z$ h
  9.   int SinWave[POINTS]; 8 ^: t: m' Z4 b" D" z9 I" n& g
  10.   void SinInit(void)
    ; N3 k3 |! g0 e3 z) r+ J
  11. {  
    % q2 @+ N) \* F4 `0 X
  12.      for (int i = 0; i < POINTS; i++) 6 O1 D, k! P* m! R' I1 S0 s/ F9 ^
  13.       {  2 [# U( e0 ?  ~" z4 L; l
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI;
    1 l. e7 Z, X* a( d% B# _% z! P& l
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  
    ; v* N; y7 ?! e* L* q$ r, O* ?
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    + [( e5 G) k5 D8 T" V' z
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  9 R* D% J6 n  T( y7 E$ C
  18.     }
    0 K/ l5 X$ b- M0 }& J: M
  19.   }
复制代码
) u( l0 Z: f( D1 c% g

. |9 H0 i) l' ]2 U2 t拓宽点数,加上载波,即可构成FM调制信号。
$ j- a6 M1 `7 X4 `8 U% L# T: o' @/ o, {5 r8 H$ Z# m9 R8 L
微信图片_20240529172305.png
7 O9 @% m  y, }8 T$ k" {( n
9 L, N3 H! _) j9 k9 B
. ^8 I* ^4 W- u' ?" }7 Y转载自:电路小白
+ [* @1 i" n7 P4 q1 A+ Z' x如有侵权请联系删除: y  j& p0 m& ~9 N# S4 v

4 p. Z7 p5 Z1 J9 W. @# }" g2 f/ s8 u
$ A. E; U0 N) U- n
) W0 k; S& {2 J
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 手机版