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

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

[复制链接]
攻城狮Melo 发布时间:2024-5-29 17:24
DAC,即Digital to Analog Convertor,是数字到模拟转换器,也称为D/A转换器。其核心部分由R-2R电阻网络(也称倒T型电阻网络)、模拟开关和运算放大器组成。它可以将二进制码或BCD码表示的数字量转换为与其成正比的模拟量输出。
% w/ N; d: a- l- u# m
: u2 U" T" ^4 f. jDAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。& e. v1 Z' S+ F- h7 V

6 ~, T# m( \& V  {& f  Y: P0 @DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。0 G- X( Q7 ^; q
; i! L2 r  c, d) a+ e, H  D
微信图片_20240529172405.png
) H. }7 z4 O+ @, L$ X

% M/ e5 a. ]6 I. x9 f) aSTM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。, j: \) W8 m. F% [3 y. ], h

* X4 O- F1 s; T& _& w5 e. g
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。' o) a+ a0 f3 O, j) s7 t2 q3 h9 k

5 I% o# H  X1 `: Z: J' }4 K# E
CubeMX配置0 o, W, V# W8 S  n7 a8 R$ d
: H0 r  Q8 [  ?2 E8 ?
微信图片_20240529172401.png
$ N5 B% G2 j( }: u5 s

1 y0 F: M9 ]( x* z* p0 k在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。
4 B  S' u3 R* h1 X! m
- s- u, ]1 c) Y* R) i 微信图片_20240529172356.png , ^7 T0 t( s% b: {4 p
0 G. Z6 W1 q. g7 P8 `* g# E, R% U! f
OutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。  |+ Q8 ]4 F1 x$ B' N( J

' {) L# D/ t* l$ Y$ I5 I
如果选择触发的话是从缓存区写入数据。9 A; _7 z1 }  a6 [& a5 y
$ W. o& O0 {+ p0 v% q
微信图片_20240529172353.png
' A5 C& h0 a/ L' D4 d; L
; w: Y' c% Q* L3 t
这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。
# O4 ?3 h3 {5 \" [4 E, n2 x8 T) P% ^# j* V
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。) r# X+ z1 F4 D7 U# @+ k

8 a: |8 V# S% u! I
STM32的DMA采样+FFT时域分析(STM32F407), f. K  X; _2 k& n/ A0 b% B( E

: t& q7 g+ Z" I  y# v; F
但是相比于之前的,需要更改一些设置,首先是DMA的设置* u0 j0 n) S8 i7 o. \0 u
& l2 {1 _. m8 ]# U+ U/ W+ a& q
微信图片_20240529172349.png
8 G- N/ g% L2 e
% h. ~! |# X7 ^& \& Z
这里设置单词请求,而不是循环模式防止数据跑飞掉。9 r6 D+ M# }% [: t

$ x, V' ?' c/ \5 E

5 Z1 T! Q5 L1 \( j8 ?; l( P
  1.   MX_GPIO_Init();
    8 ~# l, I0 o5 m  ?$ A9 L# L8 q% Y
  2.   MX_DMA_Init();3 j3 B# B. g: @  q; w1 T; Q4 Q6 {% Y
  3.   MX_ADC1_Init();
    ; G8 N# x; b" O9 L+ R( k
  4.   MX_DAC_Init();& N! t% u2 U9 b# E/ M- Z
  5.   MX_USART1_UART_Init();2 w+ G2 f2 S8 r6 S$ P$ L
  6.   MX_TIM1_Init();. A' b+ C$ N5 g
  7.   MX_TIM2_Init();4 l4 Z$ y: R+ N' p$ l
  8.   /* USER CODE BEGIN 2 */
    & [8 r, `! F" R
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
      {  L6 P' s+ b. F' O
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    6 N4 A9 w- `% v4 }3 C, J+ d$ k
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    5 {7 q* k7 }! a  ^" D, V
  12.   /* USER CODE END 2 */4 ?5 V+ ~' j% v3 }! d* y& n7 f7 k
  13. 9 Z- E/ @5 w6 H2 [9 O
  14.   /* Infinite loop */! G& V" r+ G0 x3 ]0 y
  15.   /* USER CODE BEGIN WHILE */
    ! G  E6 m* ^5 D1 {, Q
  16.   while (1)# M, f1 Q# V7 G) u, ?' R
  17.   {; h3 ?: {  `( S6 P5 z! }
  18.     /* USER CODE END WHILE */
    3 x( Y1 a; c" h, l
  19. * s2 Q1 L0 ^9 R- }9 E& T& h
  20.     /* USER CODE BEGIN 3 */
    ! F( K0 j3 x" S
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))7 f* K. G2 u. z5 a/ Z
  22.     {' [* S6 R# |4 R( w% v, Y  Y
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC/ a, T  p" _5 L" \+ H7 s
  24. " V  D4 V( b% B2 i0 Q
  25.       HAL_Delay(20);
    5 C" M. D& h7 ^
  26.       . `" y! R  w5 S9 F; B6 E* s1 U
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));
    . z4 Q9 M4 ~. C6 a* \. I) N
  28.       HAL_Delay(1000);8 v! H/ ]5 o- d8 o9 m' a1 [
  29.       for(int i = 0;i<ADLenth;i++)) S# @9 }6 p; ?2 }" a
  30.       {% S+ g- |% D3 H3 K" r
  31.         printf("A:%d\r\n",AD_Value[i]);4 r0 D' m0 v. \
  32.       }) M8 s0 A/ f0 U
  33.       
    . |5 v- d: M: S) C+ Y2 R* O
  34.       1 p1 S# a! a0 X6 |# W
  35.     }
    " s" R$ x* m. Q- p
  36.   }6 d2 G, ^& R6 k8 @0 x. ?
  37.   /* USER CODE END 3 */
    : \& v8 B* V9 y8 ]
  38. }
复制代码

; v  A7 Q* F& h, J6 f4 y  u1 `2 |/ n) i8 \
我们编写一段代码。0 X2 e& Y, F8 D' _4 G0 j3 c

1 Y' F4 |4 x( ?* i- k
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。; G& O3 x/ y7 C; ?7 ^
: F" N6 |4 d3 |$ P. E
微信图片_20240529172345.png   b/ ^9 m# L' E' l+ V
. L- `2 y& {- \  M
可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。& _9 F$ m( X( p. `; i
8 k2 {4 H- ~+ d$ d# v! ~  O
三角波发生器* t  k- h" l! a) @. p# y2 [& A

3 ^* U% {- g" K  D  V
微信图片_20240529172342.png 3 u9 y1 e4 ^4 ~; [2 ^
" Q3 w' D0 N  [( |
在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。
, ~) J4 [2 \7 r/ A
3 z  Y! V% C9 t1 b 微信图片_20240529172337.png
0 Y3 e% C, J; D, R+ W0 Q6 i

: B! q' C- n0 k$ Y6 Y. J+ L% {% k之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。
: G; v& }6 v" K$ d
- v4 P4 S4 b: q1 J( J5 v  h3 i 微信图片_20240529172334.png
$ ~( v& t$ o* K8 W! }1 \
. V; o$ z; {" h4 l; b
定时器设置好时间之后,设置触发事件。  m9 K7 J) h7 a4 e3 v7 b
& P' j3 q5 P" w; z" |2 r0 {
微信图片_20240529172331.png , J2 [' ^4 g% f" S

+ {2 Y0 ?- Z- b7 i芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:
) u$ q- a# S2 g" h3 l% p
8 H( ~6 o, Z( A- V+ X
定时器频率/分频系数+1/Period/三角波最大值/2
3 @' Y: R7 ^% `. `& E& X8 V* s* n9 F, J5 j
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样, |. w! w) I3 u) S3 e7 Z6 t, n' N" j
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出
    ! K/ X, v: ?" v5 R; q' l
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
      N4 e, i. G% D
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    + `6 g  N2 Y+ j( J& G1 Y2 H# m+ n
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码
) A9 W! O. y. l0 f- ?, K, ~
微信图片_20240529172327.png
' U" J, Y$ R. v" `7 M2 a7 k( S

# X' p$ Z5 J5 q* c( K' `正弦波发生器$ x$ [: Z- R! c0 u$ ^
  w1 H4 @% f0 {/ G% [% A+ Z
微信图片_20240529172323.png
4 b( {# ]! N) z9 I

  T3 a2 `3 y8 \! h首先是定时器触发(方便控制频率)。但是关闭波形发生器。
2 u; A3 x% V2 ?: Q6 h
% b9 M0 m5 u- F" y8 E% I3 o 微信图片_20240529172320.png 9 |  Q3 X# X/ @* M3 ?# h
1 z& W( P) [1 l6 s* }! W5 F1 \2 ~
添加DMA,模式选择循环模式。
0 m. o. T: f/ F  h8 |  W0 b4 m8 c
还有改变一下定时器的频率!7 e  T7 C8 I2 g

7 S* l2 J: ?$ K5 E! D
其他几乎不做改变。0 O+ A- a% I6 f8 i$ l* t
  v8 r% U. P' j2 C& s& z
% D1 a+ I8 v$ G% p- [- O9 t
  1.   #define POINTS 256                         2 Y' P" k8 d  X1 @& V! F9 }
  2.   #define MIN_VALUE 50                         % {5 E1 N( y+ g8 A- e" H' b, s4 _
  3.   #define MAX_VALUE 650                         1 U+ O" D) j  q5 }
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    5 p& I  F! n" x4 S1 v! W; R% T$ F. R2 ~
  5.   #define OFFSET 50  9 M8 D. H. z  D/ m) z, C
  6.   #define M_PI  3.14159265
    0 u5 a+ Z) S/ W! t
  7.   0 {3 z5 e( o* T  n, y! U' Z5 s, n4 h( U
  8.   uint16_t SinWaveInt[POINTS];
    # |4 E; x) x! ?1 |( ~
  9.   int SinWave[POINTS];
    ! g0 V  S, O5 f8 O, ~9 W
  10.   void SinInit(void)
    " R6 o% M# b6 D# w$ e
  11. {  
    5 n3 b. \' p. o$ W# d% M
  12.      for (int i = 0; i < POINTS; i++) % i9 V: U+ T) H% Q& r" z' {/ E' x
  13.       {  
    & }& M: y0 {7 [( R1 T) C- K3 A
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI;
    ' p  D% i/ ^+ e# W8 I. X
  15.       double sin_value = sin(x);  // 计算正弦值  ' D6 ]  Y, H0 R/ j9 o/ }! ~
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    0 v: {- N& `$ F% G
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  : J6 o- v; p) ^( t* Y5 G# p
  18.     }
    " A: m1 d- U7 @
  19.   }
复制代码

$ Z  Q8 N9 R$ O/ Y. I计算一个正弦表。
* k+ e5 |9 U+ g$ ?# y! B( H# D2 P" ^/ D0 _( h2 l, R) N  x1 l
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。
9 N/ ]" i- A. r; A9 o4 y; \. e# A1 e8 z8 V1 t) z, P
微信图片_20240529172317.png 4 |1 r9 i' C+ E# k% v: o
( V  G4 v6 b4 G. }0 @4 t
测试一下正弦表,输出的是正弦信号。& B6 r0 Z) O1 N" s# _
" }# n' m, I9 N1 E1 C3 @
我们之后将正弦信号表导入DMA中。! L9 ]6 p# P5 a+ Q: k

" F: |' D6 d* l; ?$ p+ S' D
  1.   SinInit();
    5 T$ T5 Q, G& x+ z( y
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码

2 c7 ]7 o" b  H" T% Y! {1 P3 G+ |正弦表初始化,之后启动DMA传输,导入正弦信号。1 {- d* C# K9 t+ ^1 ~: h
, [- g/ W# P9 H  I; s. J8 U' V
微信图片_20240529172311.png
' _" I' x3 ]# Q1 G& Q  k) w; o: O
* u; R0 k' Q7 }
测试正弦信号成功。
4 r) a6 T. Y, ?- p" X) G
9 d+ V2 U9 _: b$ G+ h
FM调制
  1.   #define POINTS 1024                        
    5 {' t5 o) h9 t; v0 S) S1 r5 m3 e, j
  2.   #define MIN_VALUE 50                         . q. P( T. q& P7 [" g% ?5 d1 ]
  3.   #define MAX_VALUE 650                         5 }6 v- X9 J* O7 O% H; b
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  2 x$ A0 r. F) U9 |' U& F
  5.   #define OFFSET 50  
    + q# ?5 e" M0 x( H, L& w6 h
  6.   #define M_PI  3.14159265$ d9 M5 `4 I. C; {
  7.   
    + {8 }8 S6 ]% J4 O& }; e
  8.   uint16_t SinWaveInt[POINTS];
    , P. b  w9 `, |* @( X9 b+ \+ W
  9.   int SinWave[POINTS]; " H) t& ^- ^( S5 T
  10.   void SinInit(void) " J9 w% u4 A* v0 `  f
  11. {  
    6 ~* l9 Y( x) F/ [5 X
  12.      for (int i = 0; i < POINTS; i++)
    . Z9 b( [7 {: t4 c# s
  13.       {  
    # z4 C% |* \" [2 k2 ^' w% N
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI;
    # f0 ?4 r/ _7 n2 C; q
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  5 N2 L/ W" q2 O" O: Y+ Y
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); 9 B1 p# i* p# f8 r# ~9 `5 w7 a
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  ! b# l: W# D! K' H
  18.     }& ?9 P) g9 k0 [6 y. K& z3 a
  19.   }
复制代码

0 E% X3 s" m2 A' S; f0 H( u$ `
$ X0 k; }$ s4 c0 a! Q拓宽点数,加上载波,即可构成FM调制信号。. T4 @8 r1 _. q" [) w+ Y

5 n2 k% V9 J0 R) j 微信图片_20240529172305.png & A$ P+ D1 x: T% U0 q

' A3 R+ S6 M5 [! [8 U+ X6 w, T4 p; S/ u9 [1 O% z; Z3 u
转载自:电路小白% |1 x4 D* ~" f- p
如有侵权请联系删除
# q* A3 }% t( A$ `- u+ h: O4 s! M6 A" R  ]3 M

1 {" ^* F/ k* b8 u, ?' r
1 D8 t: @8 k" S
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 手机版