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

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

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

5 M, x: G- Y9 x9 p0 WDAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
" Z4 p( X# ?+ L7 T7 m6 p8 b- f
% a; p( l( Q# n! X* x/ A5 Z  t  h
DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。
" i" E% E( S9 C  w/ y. ?
8 r- Q, c" e, Z
微信图片_20240529172405.png ( {3 e% q+ G% v( P; Q
, K* `& M; ^! Q7 W9 V# _: r; I
STM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。
% C3 O, s2 E& Q& B: Z4 s  b+ C/ m4 \4 B9 S
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。
* F: g. {$ [3 e& d  h' B( r: c* d9 F1 Y/ Z8 I
CubeMX配置6 b5 _; k. l4 r% A# }

7 w2 x9 c* o9 a- x8 L9 W. n 微信图片_20240529172401.png
' e& o" h# r4 G- B0 k

! t) l: }, N( k' d. s在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。- c0 h+ j1 U" @( G# x4 X- i. F6 O/ B
+ g" P. Q$ G! S" T6 W
微信图片_20240529172356.png - f' L/ a; p" \8 T

, G8 x7 a/ {2 s8 ^' g$ lOutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。
! E, e: n. d; }; A. `2 z
  N+ a5 ~) N" e* z8 j5 x) R
如果选择触发的话是从缓存区写入数据。
& A) V7 \2 s( h0 L
$ ]6 j( h/ p6 w1 v4 G9 A5 c' N+ Z 微信图片_20240529172353.png & I9 e6 N. s+ [1 e. V

) J2 H! K3 h" C这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。6 @! H- F, F6 }& Z! p# r5 N
* A$ U$ G9 k- O5 G/ {- c  y
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。- h9 x: |) j# i6 h, c! y8 P; t
. ~1 t; |! M4 S0 Q% i
STM32的DMA采样+FFT时域分析(STM32F407)( _* O5 y1 k: V

, M7 _2 J) ]; o4 k, F
但是相比于之前的,需要更改一些设置,首先是DMA的设置  |- S. Q' @  s, s9 `; `5 h
% {- O6 D% [+ b, y7 U) ~/ O' U
微信图片_20240529172349.png
# @  u# S6 K  e4 u( O8 k/ n$ e( b* L
& q% D; A# N: h0 V! `2 @: k
这里设置单词请求,而不是循环模式防止数据跑飞掉。: S, N3 @! ?' x# R$ a: D3 o- h: p
$ U2 s2 C4 k5 h: c0 I2 _: i
; X5 g* v, _. b! O7 T( ?
  1.   MX_GPIO_Init();
    - N2 K$ `( y+ ^3 p+ h" {
  2.   MX_DMA_Init();0 L3 Y4 ^8 l4 D8 V! Z( F: M
  3.   MX_ADC1_Init();6 W+ ?% Q& ]1 N" T+ j& h
  4.   MX_DAC_Init();' r: u  B4 O2 ~& z* J9 ~0 X( f/ I
  5.   MX_USART1_UART_Init();
    * F+ c& N8 E* j& A. s
  6.   MX_TIM1_Init();
    9 J; D- H9 r% @- v6 e/ R
  7.   MX_TIM2_Init();
    1 O5 o& u1 r, K) P* m
  8.   /* USER CODE BEGIN 2 */& f# N3 j4 _' F7 w7 S  K( Z4 }
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样 / u4 ]# _$ a9 N# W. [7 b  A0 P3 E
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC* J9 F( E  E8 [: H* r
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    " z: G7 R% u! E. t& P: n! x
  12.   /* USER CODE END 2 */0 C( {7 i0 P* f/ i
  13. 2 T, u& ]: J0 I+ V
  14.   /* Infinite loop */
    ; t8 r& x9 x0 i" @) r! J
  15.   /* USER CODE BEGIN WHILE */1 J0 p' q& o& Z
  16.   while (1)
    , m8 Y8 y! t- C0 O9 v, n% _2 D# @
  17.   {
    6 N0 [( k5 r2 ?% S3 [' }
  18.     /* USER CODE END WHILE */
    , d% c. L4 ]: x/ v
  19. 1 s6 ~/ J6 A, k2 {
  20.     /* USER CODE BEGIN 3 */
    - z( t: _! T; C7 `% P$ U0 Q
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))! e7 I, g; t" D# m4 `- ~" P# {
  22.     {
    ( Y: U! `7 i; F3 [# @7 j( t
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    ; v: Y7 w6 j1 [, }& r! V3 Z3 E
  24. : D* |9 W- I7 m8 H7 P5 e
  25.       HAL_Delay(20);9 L$ J0 b; U* Y3 C
  26.       
    3 b  F" j, Q$ n& ]9 A; x$ `
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));
    5 o9 B+ q- r, F" ?
  28.       HAL_Delay(1000);+ ]  o; u! [0 c" A5 H
  29.       for(int i = 0;i<ADLenth;i++)
    0 a9 W  u' M' e% R
  30.       {) ?. Y% r- v( R) w5 `" M+ l
  31.         printf("A:%d\r\n",AD_Value[i]);4 @2 K9 d& G$ V
  32.       }; _3 ?( ~9 Y! _2 @8 q" C  Z
  33.       
    / z3 h- {& }" g" e
  34.       3 g5 p3 f, S" W" X
  35.     }
    5 Q& U8 K" r, y6 Z: E. ]
  36.   }
    . a- w( z, y5 U
  37.   /* USER CODE END 3 */
    ; L+ \1 o% X2 u& h) W3 ]
  38. }
复制代码

8 S" @% [$ d& d# z  B0 f
% ]$ f# `2 w' C, \2 o9 N) f; m6 z. _我们编写一段代码。
. ~1 X% e7 u7 B( G, F# N8 }9 b6 V. m/ W
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。( p( Q- u8 {/ }5 |5 S$ S$ m* R: a

& D' [0 `; u& N- i2 W- P" a7 m 微信图片_20240529172345.png + p$ I& h" A- x7 A

4 ^$ A( O- }3 h& D可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。5 J4 y) }" ]- C! J
& \! e8 ~7 T2 D) X" ?/ G
三角波发生器3 ^7 Q3 Z1 y* l' K
0 {, X* |2 }: W1 y# l6 T1 f8 o
微信图片_20240529172342.png 3 D. L" g. h5 Q8 K7 U

- w' e7 ~. A4 N3 P, X在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。
+ V; e6 h$ j4 _; _
1 Q* J1 _4 X; P! q 微信图片_20240529172337.png
4 f# o3 A8 X3 H1 J# c" g+ B/ |

1 m6 b, y; s: v  L5 B6 s  r1 l之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。
4 V1 \. q3 o& R8 c1 f4 k! w1 X; k1 H+ W8 F9 L8 Z/ C; a
微信图片_20240529172334.png
& e9 T# g9 D. X4 R3 n4 t% b
% k2 v* i- W: E- y; F8 Z
定时器设置好时间之后,设置触发事件。% n5 G# e# T* V2 O
4 D6 g& N  _& a5 j% q% m' `
微信图片_20240529172331.png 6 N' T) }# a" N+ N* `

. w1 A, u( v5 P' h0 s芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:
& ~& h; ~9 T9 _7 A
! ?# W" P  }* X$ K; H
定时器频率/分频系数+1/Period/三角波最大值/23 ^% n! Y( s& M$ g

& W  f: w6 `( t( h; l
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样( K# b3 ?; H" L  e& B
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出4 O+ V7 Y. L5 e5 i
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC; k1 v' L! Z5 g/ s7 |/ W
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);: v, l3 z) L' Y
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码

3 \! X  ]$ [6 ~3 S 微信图片_20240529172327.png 9 N3 z2 `5 |6 g( Z
& z: ?9 @; m% y# ?! `, z3 v' W
正弦波发生器# k& O0 Y* L" k- l, ?

# w  y4 l/ p; W! P. ^
微信图片_20240529172323.png + u- M: d) Z( s+ B- s$ L

# g( |2 {3 ]* l4 R, \; z首先是定时器触发(方便控制频率)。但是关闭波形发生器。
2 h3 x& d7 z7 S7 ^) {
; }) F! n& u' r) S 微信图片_20240529172320.png
+ [2 h* V$ z% Z% q3 V" u( Z9 n0 O3 M
# R% {, G: |. w) ]
添加DMA,模式选择循环模式。% U# u0 d& }* N/ ?& Q& W! W

8 _8 o4 |& B5 g1 H0 H; X3 \, C
还有改变一下定时器的频率!; E0 e: M& m) [9 {2 y

1 u: C  r7 i" i
其他几乎不做改变。9 i+ j" B" B+ x: ^0 G8 O0 e8 f

# `. ]$ o& |6 L, I' C

4 n) S. d: D$ J% q# A! d
  1.   #define POINTS 256                        
    ; s: K0 }: H! x  f
  2.   #define MIN_VALUE 50                        
    2 x5 J0 T) w. g- `  D4 e
  3.   #define MAX_VALUE 650                        
    % ^9 P$ K$ Z% _+ o+ v  B2 J
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    6 y+ R+ d- R# P1 j0 i
  5.   #define OFFSET 50  
    * R4 }) p0 T. H4 q& E
  6.   #define M_PI  3.14159265( k3 Z) T3 P. E# S" |2 l5 C
  7.   + {5 X9 R4 d: i' N
  8.   uint16_t SinWaveInt[POINTS];$ G4 @( x6 e" F) ~% }3 p
  9.   int SinWave[POINTS]; 7 n8 q1 w- z, B" s% `" {
  10.   void SinInit(void) : M1 |& K3 g/ R. X# U% Y& H* M% ^
  11. {  & o2 y1 c$ S; }" u" s
  12.      for (int i = 0; i < POINTS; i++)
    1 L0 V' T- ]$ \; ?5 N$ Z! L& C7 \! Y
  13.       {  0 |4 r) {; u7 o
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI;
    ' K5 t6 x, ~' y( z- }$ a
  15.       double sin_value = sin(x);  // 计算正弦值  
    4 q' u6 y( c3 G% v: d
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);   n3 _; A8 e$ l& o4 @
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    4 d; }5 G, ^6 e3 J9 K
  18.     }& s/ D  M9 p0 [7 F+ o
  19.   }
复制代码

. ?. L9 G/ K8 _/ R5 M% L计算一个正弦表。0 o" {- w+ W# q7 p# E. ~) w

6 K% j% _$ v, K/ [3 w1 e$ E1 W0 i) _
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。+ Q5 K& F$ G. l, j. `& j

& D- Q( ^+ |& z! ?) | 微信图片_20240529172317.png
7 f% |8 m7 v; [
6 k) ~" Z+ c4 S" l7 [
测试一下正弦表,输出的是正弦信号。. c/ q1 b4 e  s
1 l  g/ U% X) Z1 ]  c
我们之后将正弦信号表导入DMA中。
! b( P& C# l. |2 E3 O" u# `$ {: O- e5 F
  1.   SinInit();  W1 U8 l3 F3 K
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码

# N6 ?, P9 J2 v$ G. Q& [8 F9 K0 b正弦表初始化,之后启动DMA传输,导入正弦信号。! `$ ?' N: J7 m4 l. B' R- p

$ H! F+ {0 }$ x; [4 E* Y  S& C! m 微信图片_20240529172311.png $ V* Y9 f5 n4 L
% \9 z7 L) B/ R9 C  E, x% z
测试正弦信号成功。
# L! ~, K* G' W" o1 f3 _, v# R% V; V* F
FM调制
  1.   #define POINTS 1024                         # b% z! q9 z. \& u) L. W
  2.   #define MIN_VALUE 50                        
    ! [3 e0 U8 H& Z7 b( H/ K- d- U3 f
  3.   #define MAX_VALUE 650                         ) D# [6 {( G" w5 Q. }+ i
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    & H( F2 |7 b3 q# ]9 ]' |0 `
  5.   #define OFFSET 50  9 G. @* V! G  s
  6.   #define M_PI  3.14159265
    5 P7 }+ `- x7 o5 Y
  7.   
    7 {8 b. T6 b# \
  8.   uint16_t SinWaveInt[POINTS];
    + Z6 u6 _; Z3 A, E  L# {2 S2 b8 M
  9.   int SinWave[POINTS];
    2 L( B6 A4 O: |6 E
  10.   void SinInit(void) 1 o1 T1 q% D+ p) A0 P
  11. {  
    $ s" l% s. K, ~3 w1 T9 T
  12.      for (int i = 0; i < POINTS; i++)
      r) A* g- A/ D% n, D9 j
  13.       {  
    8 I; r* @* X/ V" t
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI;
    9 y+ k0 h0 a) r' J* @
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  
    / D6 N1 f: w- |! K/ r
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); 1 D6 M5 y0 Y9 Y' b& i  x
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  3 Y& R0 d" K/ K" t
  18.     }
    . p8 [8 P9 l4 X- x! c3 |  e
  19.   }
复制代码
+ @; Q2 ~4 r, b  }& N

: N( |$ R" s/ p拓宽点数,加上载波,即可构成FM调制信号。9 M8 R0 h9 i8 a# L

8 j* y0 N! l! W$ \7 R 微信图片_20240529172305.png
( D/ a9 r! v. Q4 j' O/ E
1 k! n1 O0 f/ _2 l
1 R) T& n9 n" H  u转载自:电路小白" s! I$ u9 J+ {
如有侵权请联系删除
4 g3 {0 a, R6 y) M
9 z6 J/ @1 D" s0 t
% L0 }( I1 v- ?1 _
, U. h, c# C% p- c$ V7 \
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 手机版