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

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

[复制链接]
攻城狮Melo 发布时间:2024-5-29 17:24
DAC,即Digital to Analog Convertor,是数字到模拟转换器,也称为D/A转换器。其核心部分由R-2R电阻网络(也称倒T型电阻网络)、模拟开关和运算放大器组成。它可以将二进制码或BCD码表示的数字量转换为与其成正比的模拟量输出。. v2 q4 A( Y" a( b
9 X  n# z. l9 d" F9 }1 W
DAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
- Z1 l' n1 G, F
- Q/ e: u( J  U, ^, f( d
DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。. k5 `* i2 [% `7 z/ }, b3 n: \

; Q# E1 l+ G/ ^( d' A$ z1 C3 j
微信图片_20240529172405.png - x$ Q( W4 i0 k* l
* b( V+ u- ]7 a, `  k) j
STM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。
2 ]5 C" m2 o2 k. ^/ g, J
: R* L( g/ r. X. I- ]0 T) Q+ B/ D8 {
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。/ u) I# W% _5 j
1 R. _. s6 `" Y# [
CubeMX配置
0 X* `* B2 F) M4 A* H) I* {+ Z9 ~' z1 X0 U4 I" H
微信图片_20240529172401.png / e- V( Y7 p2 e3 A3 N! J, u& Y

& }) E0 i3 I, G2 B在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。
1 j9 C+ r. S4 n7 V- a: u2 }, ?, e* w! r& B" l% l- K
微信图片_20240529172356.png
" d3 D; g5 c) {$ @; C
) u  K' |; K# O$ {* E
OutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。
3 A1 X: w0 b: R0 g& `0 g" [7 y1 R& Z! ~8 _8 Y: c
如果选择触发的话是从缓存区写入数据。
% S% q& U' `% L% a6 J. e
2 x* a7 O# k3 L 微信图片_20240529172353.png
& c) m8 X; @" P2 e) x
$ x7 G% P- ~) R0 h' }0 ?: C
这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。0 {6 j6 q! z# T: ~2 A3 V3 e0 M
  {* v8 k! a8 U0 f& o
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。3 V- {/ l9 i% B: m- H5 W1 F) [
; r7 L; C% y  f" x2 E3 A! R/ [
STM32的DMA采样+FFT时域分析(STM32F407)6 t: L1 K2 I6 W/ b) u* C8 D. J

0 C# Z. S, p" c/ ^5 U; s" P
但是相比于之前的,需要更改一些设置,首先是DMA的设置& W0 r1 g( {" Q8 }. u$ ?

) Q; v2 y' T4 Y* O 微信图片_20240529172349.png
4 B/ G0 s6 }" l- l- ^* k

/ k# Y! y  z/ z/ P% W" d! C, @# J' L8 g这里设置单词请求,而不是循环模式防止数据跑飞掉。# r) Z- _2 l0 I. D

  g( G8 K: |5 i( a4 L1 E; i% u
  W* h5 Z0 g9 C/ _7 ~, w
  1.   MX_GPIO_Init();' z+ @' ]) y: d+ x: T% B
  2.   MX_DMA_Init();
    * h. t. t, ]$ ^$ O3 B
  3.   MX_ADC1_Init();: U: S- m- z& R# S3 W
  4.   MX_DAC_Init();
    ) V; ~# }8 R+ r
  5.   MX_USART1_UART_Init();
    - F! |* a) |/ V1 F1 G8 o; J
  6.   MX_TIM1_Init();4 Z! h/ f4 {# q/ c: I3 e5 z
  7.   MX_TIM2_Init();5 V# I) b: Q$ @5 h- @  k
  8.   /* USER CODE BEGIN 2 */
    # Z6 b2 j$ t* i) u5 s: q! R: X% [
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    " a/ B9 ~0 r$ J- b% b; ~: F
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    : |" i$ p' D0 E
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);6 `* Q  t$ }' @7 D2 @4 `
  12.   /* USER CODE END 2 */* C# e7 [; c$ R) K: y! H7 Y$ h6 w
  13. # Q. k0 Q1 Q& K: o- u3 J
  14.   /* Infinite loop */
    ' L9 B! N% ~6 O% u- f+ O" G
  15.   /* USER CODE BEGIN WHILE */
    " u8 l0 m5 I' P/ ^0 z9 l- o1 v
  16.   while (1)! G: |% r- R! A3 A: S) p
  17.   {
    ( c6 Z8 t% \; o; u6 s# r
  18.     /* USER CODE END WHILE */0 C, t! v# P$ P* p  {) O9 Z. ~

  19. 8 J6 H6 }* B) b& H  A
  20.     /* USER CODE BEGIN 3 */( T+ H3 ~* R' ~' H3 `
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))4 i+ `* {. z7 r/ P+ l+ N
  22.     {
    6 O- c6 Q) R+ {8 U. n+ E
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC/ N) L7 N' |8 r$ e6 f8 y

  24. * A5 n" E' s" S( x
  25.       HAL_Delay(20);1 C! Q7 ~( {2 G) A- H3 Y1 K
  26.       . w; R2 c" z$ o
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));
    6 v" z+ _6 r) X5 ^2 k
  28.       HAL_Delay(1000);; q$ G; Z5 }4 I& R3 b
  29.       for(int i = 0;i<ADLenth;i++)0 p. [7 ^7 O! p
  30.       {
    8 L* w. c% s9 S% s2 `# I
  31.         printf("A:%d\r\n",AD_Value[i]);, C! C0 z: I1 u5 r
  32.       }; d  D9 Z- }- u# V
  33.       
    8 X" S$ j3 E- F+ v
  34.       : H" w+ F3 @) J& `9 K" Q" O
  35.     }* q( W3 K4 k" S! m6 F1 i9 f
  36.   }* n: o# l+ j9 P* {. H, n4 b
  37.   /* USER CODE END 3 */1 h7 i* X- Y. }+ x9 s
  38. }
复制代码

) H, m! l3 _/ y) [% @1 T$ G$ X! e$ A' \/ }6 x/ u% K$ D, c; n& X
我们编写一段代码。
/ s2 U4 s: S5 s, Z2 W
$ X6 }6 C7 f/ b3 g
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。+ ^( A! k' c" Y  H$ s; f

/ Z! h7 _$ ]; p6 l9 E" P( M2 P; v6 ] 微信图片_20240529172345.png 9 J& G, m3 h3 v3 i2 z4 G
9 v# D, e9 \# p' d. b" w
可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。
! V$ n6 C7 O+ {) X4 `1 v: i  M. {, W' s7 S$ k/ ?# U. R
三角波发生器2 r4 H9 s% z: B" s! y* _
$ ?- J& q) t0 z6 S! j# T0 @
微信图片_20240529172342.png
8 v3 s5 B! _" H5 E' Q0 ^
" L3 I; `+ u5 q# x
在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。
3 h: ~# J& ~( l$ l; O# Z2 ~1 f+ o3 X* ]& W8 X2 b' o
微信图片_20240529172337.png
; k& J! _; ]. w* `" P( }* f% \: n
. ]+ ]4 {; a5 K+ p2 _
之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。
/ Y) ]5 o6 P$ Z2 g$ x/ g4 I2 j1 f( ?2 a3 v7 ^
微信图片_20240529172334.png # d: V- q) {1 b" }$ y
  _: g. ~: f6 S) F6 [+ ~, Z
定时器设置好时间之后,设置触发事件。
) D$ C; U: V5 a+ e& V$ ~/ p* s& N
0 p9 b( C# M" H% {7 f  p! d 微信图片_20240529172331.png
4 g7 C! x" E7 s

' _% B* ?2 ~) m2 q7 \/ ?6 u芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:
" N% z1 K* A/ {) E5 {
# m4 _, D& l; G  Y# A6 F- ^' S# M
定时器频率/分频系数+1/Period/三角波最大值/21 H/ G- h& [$ K

- ~" Q5 @% Y+ G+ {; z7 C0 x
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样" ?# I3 S1 d- s) r) ?
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出
    5 l+ a" m) i% Y- V2 z# k# O
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC* T+ \; b. N! G7 {/ L1 f
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);3 J( S; P8 e& @2 F. m
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码
  p0 l# Q9 d* U) ], p! ^3 S, {9 c7 A
微信图片_20240529172327.png
# R* J) @# D# D5 J* q
" s3 N1 h5 |7 A. G& H0 P
正弦波发生器
7 m: r* R& s- T* G0 H
8 R9 g+ ?- W7 t) X6 \$ Y& M
微信图片_20240529172323.png ! m, x+ L5 M. F3 u
, |$ S! Z2 W$ N' U0 |: ~6 e
首先是定时器触发(方便控制频率)。但是关闭波形发生器。
! w; e' a& X' J) `% ~# @6 s6 \6 |+ P& Y4 Y9 Z
微信图片_20240529172320.png / i8 x. E# C9 T  F+ w8 D; `3 B( v
$ k" m& B+ }$ ?8 z( z, k- ^: p
添加DMA,模式选择循环模式。
8 Y: M7 j- c# D/ w9 H! {
3 _) s: [8 p8 x/ m! r
还有改变一下定时器的频率!  ?5 ?2 \5 ?! }# U$ n
- V  Y: x6 v, E
其他几乎不做改变。2 I# `: i* @8 E# e% L
) F  P6 c) L; S. Z; h. y5 ?/ G6 D

4 J3 ?3 [; O7 t0 X% s
  1.   #define POINTS 256                         6 x1 ~% [+ J$ ~9 K; Y, G$ F  r
  2.   #define MIN_VALUE 50                        
    + ]1 ~6 P; O+ o# L
  3.   #define MAX_VALUE 650                        
    2 _; c6 d1 M: P: `& S& d: G
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    0 m4 Q$ N& x8 W
  5.   #define OFFSET 50  
    ; n+ _+ p+ Q& S5 C6 E/ d8 P
  6.   #define M_PI  3.14159265
    ) Z7 h, C& o: K3 }
  7.   ) Y/ K: e4 S: M/ m7 n- _& j% [
  8.   uint16_t SinWaveInt[POINTS];
    # H8 A: x/ D7 m3 ?
  9.   int SinWave[POINTS];
    ) W) v. B) O! h6 I
  10.   void SinInit(void)
    1 f; F9 o2 N! s: ], V$ s! `
  11. {  2 |0 P5 g4 x# ?: y
  12.      for (int i = 0; i < POINTS; i++) $ f6 G) E7 a0 m$ b) E0 Y4 A9 A$ \' F* O
  13.       {  & @0 M3 {. a0 b/ x( M, p
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI; 9 E# z8 D8 U+ A* J" a. a
  15.       double sin_value = sin(x);  // 计算正弦值  
    ! d, [: M2 r- Y- b# ]' ?8 f5 X8 I
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    / m: q6 @# a7 b5 N9 m) {
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    ; [/ \9 k- Q8 b1 {
  18.     }
    5 i5 w2 ?, f+ E- K! D! h
  19.   }
复制代码

  T7 N8 \/ k3 r: `' }计算一个正弦表。: q% H5 j5 N" Q9 d' s: e
: q* D8 l  P$ u/ w' T  Y
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。
* m3 g) s: D2 O9 q- E# U
/ ~" v7 T, E$ B  N# l 微信图片_20240529172317.png ; u, j3 d" K. B
+ ], X) A8 x7 J
测试一下正弦表,输出的是正弦信号。
1 x, J5 T! L6 D$ R) F- i2 S
8 l& u  A6 s5 e' F4 J! I1 M4 d
我们之后将正弦信号表导入DMA中。- _3 k- N2 E6 P+ o6 U
* w. x+ ^/ a% ?  r
  1.   SinInit();
    ; I5 N/ H3 Z  \+ ^" Q7 P7 h
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码
' C+ \8 P& k: V5 T0 Z8 w
正弦表初始化,之后启动DMA传输,导入正弦信号。# l% E2 Z+ K8 Z; H0 [2 p
& _/ s: Z& t  U3 [3 E* k9 V. ^
微信图片_20240529172311.png   U7 r: t; T& k9 ]  z
  z! b# ?3 B6 Z# h
测试正弦信号成功。
8 R" G7 h3 M* R2 N& E! X) n, N- c0 \4 m+ Z9 I! A( ]0 V
FM调制
  1.   #define POINTS 1024                        
    5 Z% H: Q9 G3 p! ]9 p* ]6 |3 M
  2.   #define MIN_VALUE 50                        
    * F, K. u" [; q9 H
  3.   #define MAX_VALUE 650                        
    # N! T: x1 T1 }/ q
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  ) @: ]9 v1 f/ R+ w4 R* c
  5.   #define OFFSET 50  
    - ], Z8 l/ L& y( R4 b+ ~9 v" |
  6.   #define M_PI  3.14159265
    6 e% ^. s3 g" O; U. l3 l
  7.   
    9 s% z6 a! U, R; r3 x7 A! Q7 A( }
  8.   uint16_t SinWaveInt[POINTS];/ I6 [7 h9 T7 L2 I& f6 O
  9.   int SinWave[POINTS]; " {1 S& g, [0 Q
  10.   void SinInit(void)
    9 y9 V1 l3 |1 P% x2 |
  11. {  
      V9 e. G0 W3 W8 U4 H& h  s
  12.      for (int i = 0; i < POINTS; i++) ( j  _! ~7 \: c* g7 S; P
  13.       {  0 A. H2 C$ a1 D! h6 ?  H5 ~
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI; 7 x) _4 J" U8 G
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  / _" _3 f1 S4 Z4 O+ h$ {
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    5 ]  X* v6 h2 L* L% d
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    ' ]( A  ~: ^3 Z" x4 H
  18.     }
    " ]+ }9 ]1 S; W; q- b/ I
  19.   }
复制代码
) M4 [  r; V: m' f

; G! ~/ e( w3 O拓宽点数,加上载波,即可构成FM调制信号。5 b5 M! p' G+ W! u

& r, W8 Y  ?+ k- u! B/ }  R/ l 微信图片_20240529172305.png 8 t& S; H1 _- `7 H6 p+ t2 U( p
& M; T0 n+ l/ K

6 K+ V% W2 l9 c( I& @; X, B- z转载自:电路小白
; r3 m$ g0 S5 J9 j1 E如有侵权请联系删除
9 P! H* B( W3 o3 z* V6 G# X) i4 p) C% K, O7 w* }
% O7 V. Q: Z( ?. K% W/ h7 `
0 L# N+ i' T, \, \
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 手机版