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

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

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

  q4 `1 L+ E2 L( nDAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。  ]# F# a% w+ m; X9 x" s
1 |. c2 A2 o# J
微信图片_20240529172405.png
1 ?9 g" P" ?; Z

( _' \, Y4 i% p, KSTM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。
- G! o5 K8 f- D( {, O. A
$ M7 q3 H9 h; ?% z: d( |
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。/ s2 a" U5 W! |+ [( I+ a
" K! B! `, W) v6 ?5 \, G
CubeMX配置/ o3 q- ~" l& ]1 U# f, e

, {2 s  o! \  W! p 微信图片_20240529172401.png * ~& a$ I3 V/ d" d& ~. _& D- t. |

! z# G* W6 I( M# X" N' _; L  Y# W在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。
6 D+ w8 o0 A; Q- ?+ J" `  D# `5 t3 L# [2 {
微信图片_20240529172356.png
" P# X7 n4 ?# P' S5 {

, K& |, m, J; G0 D5 h' POutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。! p2 ]- g" p& g: Z. S, L5 A

) A0 u& Q1 T% _, o: V4 b# g  T/ o
如果选择触发的话是从缓存区写入数据。8 \/ C5 y8 a( m; p0 k
, [3 M: O: e2 n8 c  V
微信图片_20240529172353.png
9 H+ [9 `8 Q' [
" |% T" Q- c) y7 ]$ t9 J
这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。
, M, N8 g$ Q" m8 G/ D$ L! i- U. c' ~! e
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。
7 f: T, y% @( v+ E' N9 v$ O' R; m, j2 Y7 f/ m3 B( D  Z
STM32的DMA采样+FFT时域分析(STM32F407)
, F- c6 a: s9 A- ~+ u) @9 L& X4 A0 H. i8 l# p+ @1 O. S6 |5 Q
但是相比于之前的,需要更改一些设置,首先是DMA的设置% ?' Z( ^+ F2 K+ A( p
" y+ \$ ^1 C! N) H$ f
微信图片_20240529172349.png
2 \& r& j. p0 T& x2 w5 Z0 B

, T7 ?8 \3 V0 Q: Y. d, t这里设置单词请求,而不是循环模式防止数据跑飞掉。
* x5 @' ?; i( i
% m3 D% B/ J. n$ `
2 a/ b& O" L" E6 G5 e( W) A# n/ m6 b' z7 ^
  1.   MX_GPIO_Init();% i, p& L6 F+ r" [- f3 R& b' U
  2.   MX_DMA_Init();7 ~2 L  S0 C# C5 S
  3.   MX_ADC1_Init();# S, }3 A1 g# u& r. m$ A- X" c
  4.   MX_DAC_Init();  @$ Q- w; w7 U4 M7 |1 y0 Z% j
  5.   MX_USART1_UART_Init();- J3 }; \' v3 N* i
  6.   MX_TIM1_Init();
    , |- z2 q/ v' ]9 }
  7.   MX_TIM2_Init();+ v# a5 l& j4 H8 \5 m: _
  8.   /* USER CODE BEGIN 2 */+ J' o  F/ ]9 h8 T3 R# K, B( j
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    - K4 \& `0 r1 u1 r1 O9 t/ M1 P
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC& L# K$ x: a' c) E6 ~
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    + t  Q' U6 o1 R% X0 O
  12.   /* USER CODE END 2 */! \+ {  w- V/ i, a' o
  13. 5 b+ `- F' d6 L/ X
  14.   /* Infinite loop */1 }  h; {  l6 D
  15.   /* USER CODE BEGIN WHILE */  s% w4 ^$ U  A6 c) S7 G
  16.   while (1)3 ]" k" T) x* [" R7 y# B
  17.   {
    - `# _  e( D1 P! \7 @
  18.     /* USER CODE END WHILE */* }7 Z& ]8 T3 v6 I$ X2 |7 S4 q

  19. & i! x. r' i9 X. [# B
  20.     /* USER CODE BEGIN 3 */- g8 b, [3 N. R
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))
    1 [5 ]9 `1 ~$ d3 s! d
  22.     {! H) E$ a* |* T
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC1 R' ^1 M9 u. k

  24. 6 d* s% Z# L  y
  25.       HAL_Delay(20);
    ; m) }/ Y" X( {3 U
  26.       ( w) j8 X$ ^8 k4 c: w: y, Y
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));
    1 \7 v8 t1 {( d# I* ]
  28.       HAL_Delay(1000);5 t, b, L* i2 C, n. U5 d
  29.       for(int i = 0;i<ADLenth;i++)
    ! z8 ~$ p$ d8 G% M
  30.       {
    8 s1 j% N8 y" U4 \, n8 n9 Y7 M& s8 a
  31.         printf("A:%d\r\n",AD_Value[i]);
    0 L  T) B, B! R0 A
  32.       }4 |" O( i/ k% O/ n
  33.       * P& {9 U/ V8 k1 t6 ?
  34.       
    " p0 g0 n1 U9 B+ g, }( P
  35.     }
    * b$ X' s" O; s; }! I- M! G/ R1 c
  36.   }
    * Q& K, I: w% T3 H/ p8 R! N
  37.   /* USER CODE END 3 */
    - t* C. b% {% e& A; P0 F
  38. }
复制代码

& T5 ^: ?5 k( r( ~5 `
( ]1 ^0 m4 O- m4 W/ L我们编写一段代码。
. B  c; J( _  g' p5 y" ^
7 Z7 r& o. b$ f! @$ Z7 v3 x
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。
4 h: R. c3 \2 E' g7 a4 D0 S; A  b
微信图片_20240529172345.png
% {% {/ {' w" }

* `+ ]" k  V! N0 i/ E可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。
4 d* N( q$ v7 ~2 K; q: j
4 v3 _, g  U; ^6 b7 ?# c
三角波发生器
9 X) Y5 H, C1 d/ M! o) F
. q# f) p. j7 U4 P5 W+ P
微信图片_20240529172342.png 2 o  R# [* ~4 V$ m+ `, F  ]+ C: p6 R

3 _: e# `1 k( `在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。7 F; l7 D2 R- C  E# X' Z( ?
# W& U' m( ~7 S0 k5 V
微信图片_20240529172337.png ) J9 v1 Y6 \- M) B, Y

8 h" M; H% G& r0 }& M! Z之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。
& z% E" \" l: _5 u0 P! ?7 ]' `  I5 j
微信图片_20240529172334.png
0 k, }2 Q  H2 A7 m; K; y# K2 q/ V

9 B/ G" o, U- |* g% ?0 Y( }定时器设置好时间之后,设置触发事件。. I- ^7 F' N, P5 U, I
$ m& C6 r8 x5 Z! ]( ]1 d
微信图片_20240529172331.png 6 g5 s5 F$ `  e  Z5 @1 m: s5 l
0 M! P" Z0 D- r3 ?3 V$ @6 \8 N
芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:
& K* k9 K4 |0 W4 j% _$ ], A" E; m' p
定时器频率/分频系数+1/Period/三角波最大值/22 L: j+ @5 G6 G, d: X
4 l# ^9 Y0 [/ \2 E* m' w, P
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样, H; L1 c8 T' `* B; t9 Q
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出( J6 j% C- M- @" x% z' z- T
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    ' U4 Z5 ?. n! X( k2 a" B
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    : F1 q5 P- q. A0 @: T5 B/ z
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码

3 i: n1 w" f4 R" o5 _ 微信图片_20240529172327.png
0 f5 r: I4 v: R: ~8 n4 g$ a% M
4 W0 z7 v  m# s7 x3 j7 K' o
正弦波发生器! v6 N, Q( k$ F
0 E* r' |' L$ S1 Y, u* n' C
微信图片_20240529172323.png
4 M/ |0 a8 h7 f7 W1 _

/ k2 p1 H/ E- S4 |. S2 C首先是定时器触发(方便控制频率)。但是关闭波形发生器。7 L3 `/ L$ b; ~+ ]
+ }: d: |* p3 k4 c9 w7 H4 s
微信图片_20240529172320.png 4 X* q: {* j9 C! b- \4 B% l+ _
# Q* P+ [, W+ B8 Z2 A; m
添加DMA,模式选择循环模式。
& o& f& t* x8 I$ {- v* {1 W6 X  k
: p1 T4 X1 g- [8 A- B
还有改变一下定时器的频率!
6 q0 O0 s5 J. F- F, ?/ a; J) C$ ~5 n* y$ e0 v1 E$ o
其他几乎不做改变。- N0 Z! Y0 }% Q0 L
& C  c5 H9 H6 v) `/ O
2 Y* B1 ?( X2 D2 Q- V  h0 M
  1.   #define POINTS 256                        
    / O1 R. W( x! C$ X8 o
  2.   #define MIN_VALUE 50                         $ L" J$ A9 r) w
  3.   #define MAX_VALUE 650                         " p# D' R) U  B0 ?; \  s
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  5 K: c8 w1 i" o
  5.   #define OFFSET 50  
    6 G/ M+ t/ [$ {( r0 P8 m/ F
  6.   #define M_PI  3.14159265
    ; y* X$ l2 G; ~( x( o9 y
  7.   + t1 `6 {; g/ L0 ^  E
  8.   uint16_t SinWaveInt[POINTS];
    ; a- Q4 Y) R" F1 j& r% I2 c
  9.   int SinWave[POINTS];
    ' n, s# }7 R+ C) e# D+ I
  10.   void SinInit(void)
    ( k# @; L. z3 J5 W2 [- M; q
  11. {  
    # t$ U, J3 f9 I+ C) P# W/ f
  12.      for (int i = 0; i < POINTS; i++) : O. O& b4 I( l9 L: W" _
  13.       {  : s! [4 [1 b! F/ B% Q+ [
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI; ! p7 A% ^4 R+ X5 `
  15.       double sin_value = sin(x);  // 计算正弦值  ! O( i% M, q% V
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); * [1 E( D  z) \( e/ l* h' H
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  $ Y9 T6 q8 P2 E4 O
  18.     }6 K: ]6 R' T( k6 k' v
  19.   }
复制代码

9 z) l: ?, \9 D  _9 A3 }* w+ y计算一个正弦表。0 O& m4 x/ T$ Z3 r

6 O# w1 T/ X0 {1 S
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。
! N. X' l* Z# Q1 s8 H% V6 n
6 C3 i5 d+ J+ o9 _. `9 S 微信图片_20240529172317.png 1 K* }$ c. j& d& @+ s& b: Z
' W  p; _/ Z5 V$ |- E- F# [9 \
测试一下正弦表,输出的是正弦信号。
6 j4 k8 S7 L4 L2 E. g  l- ?  M6 C8 l  f3 \2 e8 L' `3 ^& i
我们之后将正弦信号表导入DMA中。
3 Y: J! @. Y0 ~4 t$ n* c9 e# h7 ?/ n7 C6 z. I, P
  1.   SinInit();1 k5 o. c! X7 `( E
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码

8 E7 {9 q3 D* W+ K) _- p正弦表初始化,之后启动DMA传输,导入正弦信号。
7 f% q% Q9 i$ w( S0 H
. f* ^6 W2 F- C2 }# B! D& R' B- Y 微信图片_20240529172311.png $ X* [% F6 l: l. q7 p1 Z  G

( ?# W+ q( {! M# X; P' N测试正弦信号成功。7 C' j* a- ?$ t, ~+ S

# c: M( x* e# L9 T1 q9 H
FM调制
  1.   #define POINTS 1024                         6 f% c2 X4 O( z. l9 }5 m
  2.   #define MIN_VALUE 50                         - x2 r, s4 ~4 z1 F# \! l1 n, n2 K
  3.   #define MAX_VALUE 650                         # D: m$ C/ ~" G. l0 k$ O, x0 j
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  
    3 d9 ~6 r1 A$ w! g2 u9 C+ z. Z
  5.   #define OFFSET 50  
    6 x5 o) }$ {: X& w: c4 C
  6.   #define M_PI  3.141592655 G; T" {- E) c- s; O% d  R$ H% t7 `
  7.   
    ! y% I7 ]+ C6 l0 N" u2 k1 r+ `
  8.   uint16_t SinWaveInt[POINTS];
    1 [% K& X5 p6 Z1 A+ [
  9.   int SinWave[POINTS];
    5 W2 J& _& ]+ A. h  Q
  10.   void SinInit(void) 6 }. i, W$ ^% d# M) T3 K% e
  11. {  
    ! ~' B4 w& R( ~6 L& }- t8 `
  12.      for (int i = 0; i < POINTS; i++)
    5 f! {3 p* O$ E' |, G4 ]) E
  13.       {  . R  K3 n: G3 Y/ }% Q% e. _5 e
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI;
    ! _! [9 h' i. W
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  
    6 t& c: Y- J- i* I7 @
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
    2 O1 h, j* i" H3 [7 g1 m. X
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  ; {: N, j# ~# M  ?
  18.     }/ J; Q: l$ W% A3 V, @; {
  19.   }
复制代码
& z" I; Y" I2 ~) o* k
0 _1 X/ J8 p3 {% l: ]: L
拓宽点数,加上载波,即可构成FM调制信号。
( R; r" |4 C' R- \
, h  E1 @. ]$ ?  f2 {' C 微信图片_20240529172305.png
6 }, v; q- g( h. L0 k, X$ J( ?) t5 k
# n) @' [% v9 R. a0 M
转载自:电路小白; d, n! M" o) T$ f8 M" g0 L7 X% l
如有侵权请联系删除
) Y: k; N7 R" k* h: H$ z1 L6 W3 c
) m5 [& s* P- z3 k; T
; X4 p$ S6 Y' s9 c) n0 K; t+ v8 N- a. w  u' J$ Z/ H- ?, ~
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管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版