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

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

[复制链接]
攻城狮Melo 发布时间:2024-5-29 17:24
DAC,即Digital to Analog Convertor,是数字到模拟转换器,也称为D/A转换器。其核心部分由R-2R电阻网络(也称倒T型电阻网络)、模拟开关和运算放大器组成。它可以将二进制码或BCD码表示的数字量转换为与其成正比的模拟量输出。) H. O$ O, d$ x  J+ `1 t# H
( \" o2 z) Z: P: R+ J0 F
DAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。7 t; e! E+ I7 I( B% t  t% X

1 I( `2 k# X0 bDAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。, i9 u# }# P5 N$ }3 t
0 O( J, a: K( ?! V5 V
微信图片_20240529172405.png
  D" D/ Q! k4 u/ b' Q% P+ c
0 o3 @' e" ?& }7 T8 r/ y
STM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。. l$ S" O2 A: V

4 }# G1 T) I7 k
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。
7 Y- A) m: h: h2 f$ e% D7 S
: s2 x3 ?5 J7 M9 n! F
CubeMX配置
7 @# F$ g& D* u  e! c- l: n3 m, L5 u6 G* b  {+ T' c" w
微信图片_20240529172401.png 3 `. c4 A2 F% V) I* M4 R2 ~) h

  [* Z* O: _3 F- s" Q, W1 I在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。
* s& w; k0 s6 Y, O7 C! C5 K+ J+ g% p, m
微信图片_20240529172356.png
* R$ T. C' ^5 C; l/ a' ~7 n

4 K, l5 v7 e- [2 ]$ YOutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。, o: ?1 A( O1 W( I4 r

8 v* ~  ]1 K2 n8 z
如果选择触发的话是从缓存区写入数据。
: e, f/ |" p$ F& ]) O) i5 U  e* K" c# S* E
微信图片_20240529172353.png
/ y+ Z  ~! u7 n% Z( b, x

9 {9 c+ s1 s% p+ a0 I, g  |: o这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。. B. ~( m/ K* L% _- P) Z9 n  p7 f

, M& I9 C9 C. I7 \& \2 `
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。
  t+ B: l% z( S% ~
8 z4 E+ U# [( {9 `: v. a( J: L
STM32的DMA采样+FFT时域分析(STM32F407)% P* _* @5 D" y; ~8 ?, j+ ?* n

/ `, Y( c5 Q5 V' ]
但是相比于之前的,需要更改一些设置,首先是DMA的设置1 v$ A7 n, v, a2 p5 n# a6 N) [
+ L0 ^3 v4 w5 V9 V( m$ ~6 ^* a
微信图片_20240529172349.png
6 y9 b: r9 G# q0 o8 I% b

; l) N7 W1 }# X1 s. Q这里设置单词请求,而不是循环模式防止数据跑飞掉。! g8 A. D1 t7 ^1 V3 X! s* B/ |5 Q

0 J2 ^/ V" L: F/ A
) f0 _, r6 N& ~/ P3 U" [  b  Z8 }. J
  1.   MX_GPIO_Init();/ a8 w! v4 l0 p0 T: W$ Z
  2.   MX_DMA_Init();
    ' F; S) `9 R+ `4 K0 ^: }5 E, g
  3.   MX_ADC1_Init();
    : ]0 I9 k8 c2 l4 }( b
  4.   MX_DAC_Init();
    , ]8 _$ x) p& M
  5.   MX_USART1_UART_Init();
    & X! p' S, e; w6 j4 J8 t
  6.   MX_TIM1_Init();
    1 Z, {/ H! j' s! P
  7.   MX_TIM2_Init();
    , m7 p7 ~, W+ S  [! J8 p" |8 n
  8.   /* USER CODE BEGIN 2 */( A# ~, O' ?7 J" _
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样 8 `: @0 T& x) Y' D1 p' U
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    $ r+ U3 G1 Z4 {
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);7 Z5 p. u, A0 T/ F* y: E0 Q- S% o
  12.   /* USER CODE END 2 */1 @. R# i; o0 H
  13. : }/ R4 h" @3 ^9 [: y  I( f
  14.   /* Infinite loop */4 p7 e! l4 j: q- ?
  15.   /* USER CODE BEGIN WHILE */
    ) n$ `; T: i' @8 S  U
  16.   while (1)4 X& f0 s; r- w" t4 C
  17.   {0 V6 {1 ]; A/ \8 P- c7 D
  18.     /* USER CODE END WHILE */
    ' P) a$ {! z$ k& _  |( T* N

  19. ; b% e" a- e* w- L* ^6 B6 P
  20.     /* USER CODE BEGIN 3 */
    2 X; n9 o$ Q) f: a4 W. Z
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)): _  J* L5 R, J6 o: l. ^
  22.     {
    + b% S- M0 [, E
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC5 l$ ~0 F9 G/ i4 z
  24. 0 T9 W! p* Q6 S  n8 s5 Y" \7 S
  25.       HAL_Delay(20);
    5 X( M5 R  ]4 p) g! e/ D+ x! o
  26.       
    + \. C  y+ [% [, t2 w
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));, {9 l- j, G  ~
  28.       HAL_Delay(1000);( L3 m8 U7 u/ n- B
  29.       for(int i = 0;i<ADLenth;i++)
    ( Y# ~) s5 Y6 f) k" |
  30.       {2 R! p# _! G: Y9 K; V
  31.         printf("A:%d\r\n",AD_Value[i]);
    , z# o+ x9 J8 r  J# a
  32.       }
    ) e/ l3 Z2 G# m% C% i4 `/ |3 H
  33.       
    9 e) o. D; S! V7 {" @
  34.       
    & U9 n4 H! K. u; P* A
  35.     }
    ; F$ y# Q! X- o! s% }; [6 v
  36.   }
    ' f: E0 N- P+ s' P
  37.   /* USER CODE END 3 */
    4 a. e7 F: |! O6 o3 r
  38. }
复制代码
4 y6 }% z' m* z# X6 ~6 Z/ ^

! f2 F3 P7 }7 E我们编写一段代码。
# ]7 x2 d: F2 ^; H  M5 Z7 j  l4 v1 D1 B; h9 p
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。
  W' Y2 h  c0 b( G- P( e( t2 z, H
微信图片_20240529172345.png   H3 n( G6 A& l! a3 d; z+ R
7 u1 m5 G$ s; V9 m6 T& e; W3 d
可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。
, ]0 q+ D9 B0 F# T3 i
2 F3 p- v; G, x
三角波发生器
/ q; s' h0 G4 {6 Z0 C; V) w
$ h3 O- }! X' W- K; o
微信图片_20240529172342.png ! ?$ B: \! ~/ O- q2 M. L' u3 b
. b) s* P3 G( {
在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。
1 t7 n! {. \- a+ B9 l
, `5 V! f! a; [6 J; \: B5 { 微信图片_20240529172337.png ) \- M: Q4 n- M* V+ V  T
4 u# S7 n% x- W4 w4 z( `! y# n2 m
之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。
. \& q) M% @' x; p7 Q3 F9 H8 x; V7 W/ z- w
微信图片_20240529172334.png
/ I* v6 g) n( K) S/ A0 \
) ]" e, B. O* e9 C% d  n' w5 [
定时器设置好时间之后,设置触发事件。
, r3 @( C9 q2 d2 ~% R) b4 r" e& m" B5 f) q* v
微信图片_20240529172331.png : e- T$ p* ]; H3 I6 ?7 R

# l, x, {2 H- k0 m4 Z. o% |5 `芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:
; `9 D$ N7 J1 P& F+ W, t7 @
  y& e4 ?2 I5 ?5 O) n
定时器频率/分频系数+1/Period/三角波最大值/2
3 [- Y- [7 t* f9 c7 [3 o! j" @+ O. E/ z' Q4 i0 ~* S% ~8 n7 _
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    3 h1 f) L- u4 x
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出7 e% k- P0 r/ @  K
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC; b* v; E* C% ]9 P
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    ' u- X& a6 s; u; j5 \) x8 i
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码

; H" R$ S1 {8 R. e( ]" c4 |3 S& z# w+ b 微信图片_20240529172327.png
; T% X! U1 w0 a3 t+ T3 ^$ f; |+ ~

# ^! U3 \9 B+ J7 W正弦波发生器  q: M. N; I* N$ W; h1 b' j* X, l6 L: t/ }9 }
9 J8 }7 t2 v4 i- r
微信图片_20240529172323.png - X. ~& g. o7 J7 x3 S
; V0 P" A* {: [. m% F" X
首先是定时器触发(方便控制频率)。但是关闭波形发生器。
+ K9 V) {' c1 P5 Z8 a
# q- g( f. l& g1 i4 J 微信图片_20240529172320.png
4 V: n6 F! z% J$ K% N, C/ K
( S, a, h! i( s/ M/ D8 l( Y
添加DMA,模式选择循环模式。, T% _6 v9 s6 p/ i# `" ~8 k
0 U! N0 \& k0 h
还有改变一下定时器的频率!1 k. L2 H6 v' f2 O" z# [/ s

7 R& A8 A7 m" Z5 f( o% D
其他几乎不做改变。
1 p+ t+ ^; t. ~( T- A& A0 ~
. ^( o5 O8 T5 |- g- p

+ Z7 a- t. [! W  Q: |& r# O& f# y5 Z
  1.   #define POINTS 256                        
    ; O6 V4 }' u8 _  k: |  f. t
  2.   #define MIN_VALUE 50                         / p( ?8 g# k7 x  I. u* _
  3.   #define MAX_VALUE 650                         - Q3 e) h( [2 X- I+ z* t9 a
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    ) }$ B2 q2 @/ x
  5.   #define OFFSET 50  
    ( z: T3 c' q* X+ e8 v
  6.   #define M_PI  3.14159265
    4 D+ ]' }5 o# l; m( V. B
  7.   
    $ a- b/ e- a0 t* \! G% W5 Y4 ~& m7 W
  8.   uint16_t SinWaveInt[POINTS];: X/ n* j' _2 i! j; ^
  9.   int SinWave[POINTS];
    1 k2 c7 E; u" ~
  10.   void SinInit(void)
    7 K5 Z7 t9 j% k" V' d/ r
  11. {  
    ; i# g  O/ j# [
  12.      for (int i = 0; i < POINTS; i++)
    + Q  G: ?6 q6 _+ Z' e! G, D7 e; h
  13.       {  
      ^3 Q$ n( E4 b# G  O
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI;
    2 e. p- O- j4 o7 u$ \+ n
  15.       double sin_value = sin(x);  // 计算正弦值  
    " e7 C; h: D! L
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    6 T- ?6 v9 A, w: v9 W
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    ' f; p  |4 s  l6 y  t
  18.     }
    " k1 a7 e& ~$ y) k) R% M. l
  19.   }
复制代码
! ]( t3 B4 w8 R
计算一个正弦表。0 g7 M3 I8 }/ Q, o- L3 M) J  h

6 H5 A9 k! d2 e$ [* J
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。0 s2 _  K. \$ {" s. ~9 B7 [
$ e$ W) ?$ Z5 L
微信图片_20240529172317.png 5 ~: I# t3 S- v/ `  v$ n3 h3 w
* Z3 w) H% Z% S* H% @
测试一下正弦表,输出的是正弦信号。
8 J+ D2 y! M# A$ P
, W+ L' I8 a0 B$ j! m) ]
我们之后将正弦信号表导入DMA中。9 B7 o" y% a! A" |, S- |

' T2 b8 z% O# k$ V/ v" L. ]
  1.   SinInit();" p( I- m) v  T5 p' W9 b% a( E7 l
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码

8 \1 Y* Z1 W$ }正弦表初始化,之后启动DMA传输,导入正弦信号。: f( a; P6 ]: X* P/ P, r( s* w! w
# q! Q8 E9 B$ N- P8 W& a5 i" n! d
微信图片_20240529172311.png
3 i9 d( |5 N' \& }) ?+ V( J$ L2 u
* x% w; P) C. g& A
测试正弦信号成功。* F9 m+ l* i  G# ]9 D' ~  S. g- {

- V( ^. ^6 }' `# X  r) F
FM调制
  1.   #define POINTS 1024                        
    ) K$ D' {) K, v: a( ~' T
  2.   #define MIN_VALUE 50                        
    0 h9 e6 S7 ~+ D# p
  3.   #define MAX_VALUE 650                        
    : q4 T5 P- C3 f: ^1 N- m
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    3 U% L; ~5 }6 U: O4 P0 W. d7 V
  5.   #define OFFSET 50  
    5 \7 |1 T, W) q
  6.   #define M_PI  3.14159265: j2 j# @# ?1 X1 D( v
  7.   
    * G4 C* m0 {+ w4 w7 D) S: q
  8.   uint16_t SinWaveInt[POINTS];
    9 o2 }1 b9 h; M% N' X
  9.   int SinWave[POINTS]; 4 e- B, {; S  ~
  10.   void SinInit(void) 1 \# A! P0 K$ f5 H+ s8 y) m
  11. {  ( p. \6 ]0 n$ N" A6 u# \
  12.      for (int i = 0; i < POINTS; i++)
      Q+ h4 i& M# i$ F1 U
  13.       {  ' E" m, U2 m  o. R: t
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI;
    ) F  t  g" W6 K! h! \, w" y
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  2 c+ N0 d, J) M
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    ; {2 `' B: O3 `; I2 x) r
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    4 [+ a5 D. K6 m* Q
  18.     }5 y% g5 ?" `% ^- z4 K( ~+ h
  19.   }
复制代码
: B8 f2 }9 o' N& S- j$ O+ d
+ s1 L9 K6 m$ |3 [/ s  f. }
拓宽点数,加上载波,即可构成FM调制信号。
' @) N7 i6 p# `. J/ o
+ G% t" p1 ^# K' z) l3 Y 微信图片_20240529172305.png
/ b' Q/ \, g* j" I
7 A: T! w  b" v$ P6 d
8 J$ F. ?8 s! y. S7 D转载自:电路小白) U6 U0 k+ o! Z8 i5 r
如有侵权请联系删除. o0 F' k% W7 s2 z

" e+ U; b3 m9 u3 I
( a4 ]1 Z: k7 y& `
# i( n5 n  f) G7 b! j7 M' M! 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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版