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

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

[复制链接]
攻城狮Melo 发布时间:2024-5-29 17:24
DAC,即Digital to Analog Convertor,是数字到模拟转换器,也称为D/A转换器。其核心部分由R-2R电阻网络(也称倒T型电阻网络)、模拟开关和运算放大器组成。它可以将二进制码或BCD码表示的数字量转换为与其成正比的模拟量输出。
7 F6 K+ \4 `9 Y& Y; y4 Z# M0 Z& w' ^8 E  H8 F% b
DAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
. V* r) Y4 [3 ?" b
3 B$ E6 @  p+ N) c" ~/ m
DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。
3 F8 A4 W9 `9 j7 ~% Q' K
4 |. J% f* i6 k% w
微信图片_20240529172405.png 1 E# {" n# y9 L
: {! v  D  ]6 M7 c3 Y& }  a
STM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。
- f) T# X& J- M* t4 Z( K- _( p6 @6 Z9 `/ O$ v
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。
. k, F0 [. _) z, B: G$ R. H3 Y; c1 F5 K% e# l
CubeMX配置
) d# C5 G. w  c. X/ ?
) b2 U1 S+ R: e" b! y, S 微信图片_20240529172401.png
) U. J5 A  y/ N5 u9 E
; o% o2 T. v+ m: D- f
在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。7 U. B1 Z' p! f+ g! r" I
+ t! ^+ u' _# F. J4 L2 |& X& l& m
微信图片_20240529172356.png 0 s9 I" x2 t! h* ^* S( G# K" o

# v6 |$ a# E. w% \  O0 xOutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。9 ?- O- e( i3 f# E. a/ _
+ E% b" ?+ W& ~$ b+ Q: `
如果选择触发的话是从缓存区写入数据。
  N/ i5 l5 i0 N% Q/ m. y- W" ]1 z, |5 S# o3 h9 c: h
微信图片_20240529172353.png
. O/ M4 s  ?5 W! u, Q* J3 a$ {

8 ]2 W; i* x6 e$ q: n5 u这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。+ w: V4 Q3 ^& }2 V9 @

9 I6 y$ {3 H2 h# M
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。
' B, O+ N8 A2 c( X% o" b0 V2 y2 x! {& J2 X
STM32的DMA采样+FFT时域分析(STM32F407)
9 b/ R& g7 V+ H0 H$ ~- t  m: R
- h8 f4 f  J- m( l  W0 n9 z8 h
但是相比于之前的,需要更改一些设置,首先是DMA的设置2 b# _6 o' j; T' @

" y3 K' b7 c! r8 c 微信图片_20240529172349.png ( U0 l% [" D% _
: D: S- w2 y; n1 |6 D2 _% h
这里设置单词请求,而不是循环模式防止数据跑飞掉。
% c" |0 U6 _+ }0 f$ Y5 u. p1 f$ M5 u% [6 w' V  }

  O& Q& K) F6 C; ^$ r5 |
  1.   MX_GPIO_Init();
    5 U) u$ V# L, k! I6 S1 B; E: z$ T
  2.   MX_DMA_Init();
    6 j5 H3 g1 \$ ]) l: ^9 K7 ]. g0 \
  3.   MX_ADC1_Init();7 R8 A& p: z7 `6 {/ d3 n
  4.   MX_DAC_Init();
    9 F' K& ^- y, C/ U; s* o. l
  5.   MX_USART1_UART_Init();
    6 L; J3 R$ K! u3 I9 s0 D8 l
  6.   MX_TIM1_Init();
    4 J/ N' R) W. X8 x5 [! D- n1 _0 s
  7.   MX_TIM2_Init();3 C% a5 Z! A4 e1 C
  8.   /* USER CODE BEGIN 2 */) k: V3 n# n- B& I$ ]+ [2 [
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    2 S- Y( h, P6 S4 Q& J9 ~9 A
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC3 e( h/ ~3 C' ^4 F
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    # |8 I+ M5 h+ x* |% |) p6 @
  12.   /* USER CODE END 2 */8 |7 U; B; P3 l

  13. $ r/ Y! R+ m/ M0 p' n( @4 K: Y
  14.   /* Infinite loop */
    9 a# K; m8 c- r. v1 e$ d; v
  15.   /* USER CODE BEGIN WHILE */
    7 r3 ]7 z; t# Z) X: F
  16.   while (1): V" g  J/ h! Y; {7 ]
  17.   {
    7 I- u5 U6 v6 f/ F
  18.     /* USER CODE END WHILE */# z; M" K" L2 O# f( ~) Q

  19. 1 T9 b% @, [9 J+ m$ [; s3 b
  20.     /* USER CODE BEGIN 3 */
    ( b2 H; w5 \3 V( N5 e
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))
    ! J$ t  c4 d! _) P# |
  22.     {# M3 T. m$ _. y0 F: Q3 T- z
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC6 n" `. M. M( @* A

  24. 5 ]9 Y" c0 m* Y- n) O
  25.       HAL_Delay(20);
    ( h; r& F1 O  ^/ c5 e5 _( L. K
  26.       
    , G9 E) G$ J3 f3 ~9 x9 J4 D4 M, r8 g
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));
    9 E  c# [; W% U: b
  28.       HAL_Delay(1000);" S7 l% `1 w$ b
  29.       for(int i = 0;i<ADLenth;i++)
    ) M: p" H2 e  H
  30.       {
    1 j& _/ w8 }1 z+ A
  31.         printf("A:%d\r\n",AD_Value[i]);8 X/ M7 O. A; p" G/ [! @
  32.       }: U1 M: s" I% ]% b
  33.       9 c% D# @0 ~4 v- c! R! O$ [+ t
  34.       
    " `- n6 D6 v4 V3 F5 R, M, F3 V( e
  35.     }
    # b9 y( {$ R! N
  36.   }5 r6 w2 h% N7 O1 O5 I" O
  37.   /* USER CODE END 3 */
    & q  M2 I6 L& n6 Y( B5 E% y
  38. }
复制代码

8 m; W; i+ A1 @0 N+ k; A0 [
4 W, ?' R& j7 x( [1 t我们编写一段代码。4 G$ z9 b1 M9 r# M7 R7 {  A% X; i
. g; d! L+ T* o* W3 f( g8 O6 ~1 S
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。
  Y$ z- E7 h2 f& ~& ]/ L1 F9 e: i" k( B; X7 X$ P3 j  _: }; Z8 w
微信图片_20240529172345.png / a" J% s8 }  s; g5 j  _0 {$ g! U% o
( e, `: S8 E; L
可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。
6 M- u$ }3 j$ l/ D! S! j! m' f8 E7 T7 J
( r" a. Y! s; K/ |/ r, S
三角波发生器; s; O2 E1 s. _. i+ T) I
9 w$ `' G) }8 i$ f) s
微信图片_20240529172342.png
1 n% V/ q8 }- \% T

& O, \( H& P0 M8 q/ `, s% g在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。
  S8 a: P0 h/ q, }+ Z
4 B0 E- K9 c0 R! \" J 微信图片_20240529172337.png ' E! V9 e+ J/ c' G9 [/ `

) E9 d  M2 H; N/ g" \之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。- K, m2 F' w5 [

- G3 \" }7 d6 P 微信图片_20240529172334.png
8 C" z1 |& H, b+ @1 r
6 t; E" O; s2 E' W
定时器设置好时间之后,设置触发事件。8 D0 b6 g. f+ {& O/ r' U
- j8 \3 _7 _- D# }1 a
微信图片_20240529172331.png
; x" T0 \; P, F. M$ k+ b

9 o" q/ m1 ^4 \2 F9 [! Y3 |芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:2 `1 G7 D# c6 h. L. K8 x8 @
5 p3 L* L/ w' {
定时器频率/分频系数+1/Period/三角波最大值/2
; Q; j+ H; F5 b; n8 ?% Z: T& X; M
) r0 n0 V% D  F6 M2 C4 {
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样
    % J$ ?, a: s0 J! {- B
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出
    2 f- A2 M# P) a+ u) r* p5 s
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC+ m3 n4 J! K: m( ]2 I7 G
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    - Q, T) e6 ?$ s4 P: X2 F
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码
6 P" j" P& [) l+ s
微信图片_20240529172327.png 7 e; C6 r% e1 M! n

' W2 p: e- U& o) F正弦波发生器! W5 T9 K# v- J" i. K
7 b6 y0 Y+ t- e; j; u+ F
微信图片_20240529172323.png / v+ z: q: Y, Y6 g0 g# T

( o; W2 V# j7 T! J首先是定时器触发(方便控制频率)。但是关闭波形发生器。7 L# @& X( l- v$ B6 e5 s& l
2 t+ P. o9 H8 E
微信图片_20240529172320.png
/ H5 o' k: J: s+ I. i$ U
( D5 x6 T' y7 V, o, k
添加DMA,模式选择循环模式。; {3 {6 w% L+ k. k

/ K: \* V# _8 b$ N4 y
还有改变一下定时器的频率!) j0 S# W0 L  N1 G: E
1 q7 [- l, R6 H, m8 ^* {
其他几乎不做改变。2 U# E2 Y9 N; Z4 X  s

9 G% \0 t5 x" m, Y

* ]+ U5 c6 a4 o
  1.   #define POINTS 256                         6 F6 F# b8 n, ?' V( K
  2.   #define MIN_VALUE 50                         / X. e; z8 c5 a2 ?" n! z
  3.   #define MAX_VALUE 650                         1 S3 }  `+ C$ C. Y( J5 o
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  % N0 M! A' R6 ^1 w+ q7 d+ Q
  5.   #define OFFSET 50  5 H( Y$ [# a. A8 q
  6.   #define M_PI  3.14159265* {7 s5 O5 j# u- e3 C- D: ?
  7.   . L) u: c* v# `$ E" X
  8.   uint16_t SinWaveInt[POINTS];
    4 R# Z' g9 g0 M6 q) x
  9.   int SinWave[POINTS];
    + J/ u. a$ [; A" e
  10.   void SinInit(void)
    ; b: Y- U" Q8 J
  11. {  
    2 W+ R0 X/ {5 l1 c& e. `
  12.      for (int i = 0; i < POINTS; i++) 9 Z) X; c7 e  \, F/ _( _
  13.       {  
    4 [' w6 \/ G5 K4 i& r9 A1 X
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI; 0 C( p( N, _# T% h
  15.       double sin_value = sin(x);  // 计算正弦值  7 v6 |8 Z5 H7 y% d: n6 b
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); 1 ^% p8 C1 H# o
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  
    ' _2 H) |; q; D3 ~$ E( A6 J
  18.     }8 C$ E9 D& e: [* g/ W$ Z% L
  19.   }
复制代码
$ r+ K" M7 `# i/ Y
计算一个正弦表。; Q9 h+ W7 R& G; }& Z+ F

5 f7 a7 G- Q9 n: p3 m% i- q6 k
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。9 b/ \& \/ ~6 U4 p' }6 r$ w
. d! M# p3 }/ s" Z& M8 s" B7 A
微信图片_20240529172317.png : b7 a4 y& y, ~2 |

( I7 v. k  W- |1 J测试一下正弦表,输出的是正弦信号。$ s: g1 ]* x, J9 k7 R. n: ?

! R; V( @8 q) E/ J4 v6 D7 k
我们之后将正弦信号表导入DMA中。3 q+ m& f8 H0 \7 z+ U

/ D* {4 K; S. I. p  E3 J& H
  1.   SinInit();
      E; E5 O( j# d! U8 e" s$ O
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码

# u* t2 H' i9 e5 v8 x正弦表初始化,之后启动DMA传输,导入正弦信号。
. [7 e6 D8 i' \2 ?4 l
$ r4 N5 L; D4 V0 h1 \. ]( Y$ y 微信图片_20240529172311.png : V+ a! P4 o9 c# p5 O+ r
$ g" C2 Y( t  W  a2 y( b, @7 [
测试正弦信号成功。( A6 a' R: e4 G# Z
2 g# T0 H& L; ~. d; R8 s
FM调制
  1.   #define POINTS 1024                        
    ; [5 ~0 U- e! |  Q+ `, ~5 L' s
  2.   #define MIN_VALUE 50                        
    " I. U+ p' k  j* ^8 o; ^% ]7 o! p
  3.   #define MAX_VALUE 650                        
    # C; s! [. f% f; i# `& X; N
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  - w; d  x) P5 S: w2 H  ^& S
  5.   #define OFFSET 50  . R2 F$ E7 b! s! l/ P( d" j
  6.   #define M_PI  3.14159265
    + H; a6 n9 T; j7 P$ f, O  ?
  7.   ) c8 _8 V  f) |
  8.   uint16_t SinWaveInt[POINTS];2 }3 z8 d" k& x1 y1 b# w" R
  9.   int SinWave[POINTS]; 2 z0 J. j% s' j, l
  10.   void SinInit(void) 3 ?' g' |/ }4 n
  11. {  
    8 P$ \) G6 U" b" n% k
  12.      for (int i = 0; i < POINTS; i++) 0 c: J2 P0 y# x( R: x
  13.       {  3 ^. O0 I; x/ {- W3 q. G/ ?8 W
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI;
    & h, v$ I7 A  O/ l
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  ) V, j- ?" Q8 O6 Z- L- b: g
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); 5 u! n2 g3 A3 {8 u* Y0 M( g1 M
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  7 J& e8 c2 c3 j" U3 i
  18.     }
    " n* C3 Y/ d5 D# D4 d4 X$ `
  19.   }
复制代码

; M/ t, S, D! X( }4 ]. f$ j! i* F. k" c+ c' ]% I+ `
拓宽点数,加上载波,即可构成FM调制信号。
  m$ g. e4 G+ J( c9 |! \1 ]5 c, \2 Q9 L5 E- y3 Y
微信图片_20240529172305.png
3 c5 p7 A' ]8 Z# M' \8 ?  z5 ]/ R+ {* ^$ U! v4 ?3 c0 ^+ r% M1 Y, g
8 c& ?) f. u% S, x
转载自:电路小白8 u3 t2 `' v; P* Z
如有侵权请联系删除. `& m- f! v( L0 s1 U
+ p( s  C6 v4 o
4 D; K3 i4 M, Y7 O
! d4 y: b/ {7 c# @1 X; N9 y( b& w9 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管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版