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

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

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

! r: o: D: q( [' I7 hDAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。/ p; N' m+ V# ^) J" P7 _
7 g) p- Z# x5 p5 l# [" E3 ^9 T
微信图片_20240529172405.png # H9 w( B6 M. e- ?; g

5 Z( z5 x5 L3 U' v% `& w9 QSTM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。
, X7 L( A' j# K3 E' T
' @' M+ M" d* R/ F# K5 |; p8 P
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。: o" j1 Z- @# }) T: N  j  {
) C3 x9 f& m9 e" L
CubeMX配置
; n$ ^1 d+ ^. L8 ?% C1 s0 A0 _3 @( r  j& d3 K) q) `/ K
微信图片_20240529172401.png : G8 ^0 w5 r  Y& o! I

" C' Y% W! w0 w在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。
8 `5 z, o/ a" B9 [/ H& m* f8 Z7 d; ^
0 S) o) F6 j& R! Y3 T" M2 ~ 微信图片_20240529172356.png ( }. G4 y: w0 j' M: M
# I4 n. \, w$ @, y" @5 U
OutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。
$ F1 v& p$ q! j* U5 w5 d
9 ]5 i3 J6 d7 w
如果选择触发的话是从缓存区写入数据。
0 l* @: b$ k2 }9 J) v+ q& O% }+ Y$ W3 h* N4 e
微信图片_20240529172353.png
% T4 w4 F% s5 p8 F( J3 F

6 h: E% i2 v& T; K/ K3 ~, d  i这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。) r1 h- `" {$ X0 N; G

3 ~# b& M3 B! B6 |
这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。
1 J2 O: c0 u& `1 [+ R0 ^, I! M2 g  R+ n% B$ o) b' }
STM32的DMA采样+FFT时域分析(STM32F407)
1 C5 q6 y& s) Y* M, a4 M# x
  J% T6 p# |7 \2 C8 p4 ^0 `/ i% n# H
但是相比于之前的,需要更改一些设置,首先是DMA的设置
' s0 ^0 ^& d! k% e2 s
/ F# a5 F* P0 I4 C) e: l# m/ S 微信图片_20240529172349.png
1 a7 w0 x- a0 ]: h

9 a& I! k$ F1 a% v8 g* W这里设置单词请求,而不是循环模式防止数据跑飞掉。
" W/ a  k9 g6 I4 g* c- M+ {# F2 k0 a; P- _% o2 p5 |0 U) K9 W2 f

0 j$ E3 q/ r' F, q, D( a
  1.   MX_GPIO_Init();
    ! ?2 a* Q+ m* H; @' ]
  2.   MX_DMA_Init();
    5 D) A9 D1 d6 z; K7 n& t
  3.   MX_ADC1_Init();
    7 W5 s8 {0 J" K
  4.   MX_DAC_Init();' C* X/ f% j1 o" e4 |' \2 O
  5.   MX_USART1_UART_Init();: b" V& M' v  k: e+ T) g
  6.   MX_TIM1_Init();
    + _9 z2 `8 ^6 j# c8 y
  7.   MX_TIM2_Init();& j4 q$ ]  t! Y) O$ \3 F
  8.   /* USER CODE BEGIN 2 */
    / F, D( _! _8 q& f8 V8 z
  9.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样 % ^, V4 ?: {4 S# p
  10.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
      P7 h; F8 s+ Y
  11.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);. D2 L! R! f, M4 M5 y
  12.   /* USER CODE END 2 */
    4 ^+ z( Y! h  r
  13. 5 G( D, E9 I: g, M' H% _7 w( t. S
  14.   /* Infinite loop */
    0 D- d8 L+ p: S/ W& V+ p7 L
  15.   /* USER CODE BEGIN WHILE */
    & C# m7 ^* G; k- ~( U! M" s
  16.   while (1)
      d; s2 l' x, \( w* T. k; O
  17.   {
    " J- C3 D& b2 R9 M& H( q) y' p* I
  18.     /* USER CODE END WHILE */" @9 V% j8 Q4 A

  19. 3 F+ v' [/ I# ~3 W: u7 t. u
  20.     /* USER CODE BEGIN 3 */
    ( ]5 l1 W# d" y) F2 N
  21.     if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))
    , O0 i& \" x. s6 }
  22.     {
    7 E6 B5 _; O* `
  23.       HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    ; `  p0 g# I8 D
  24. - C3 M% z2 g+ t4 @
  25.       HAL_Delay(20);
    & ?% A+ d+ K( q3 Y% S& e
  26.       
    ; Y4 U0 `( k: I
  27.       while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));
    ( {# D& L; f0 N) S1 |: N/ U8 C- a4 J
  28.       HAL_Delay(1000);6 \2 E  W7 t4 K2 t7 n! O) b
  29.       for(int i = 0;i<ADLenth;i++)
    ' M/ H7 c/ h7 z6 [3 W5 Z: w
  30.       {
    9 |; {, ?6 B3 I& _$ E3 }
  31.         printf("A:%d\r\n",AD_Value[i]);, H; q! T4 \8 J
  32.       }$ ~! \. z; S3 K3 Y. C, ^5 V
  33.       " j2 D4 S$ I/ R; h5 ]
  34.       
    0 X0 c1 z: ~  K6 O( q3 b
  35.     }6 w0 Q3 S8 G' P, Z4 n- F
  36.   }
    : N9 W* H3 v& p" ?
  37.   /* USER CODE END 3 */; W$ E3 t# {. r5 }' ]; l! l+ ^- P
  38. }
复制代码

( r0 w0 @! b  ]* I
" \8 K6 K: x# m6 H0 G7 `5 T" h  ]0 [我们编写一段代码。
% p. g; o0 D# I9 w
( w8 l( `7 L+ F+ a# s
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。* e. ^+ y2 @! [8 U. X' F% b
- u8 r1 [) {) t; K" n9 R2 u
微信图片_20240529172345.png
3 M# R! @* w/ `% i/ q& s
5 o) k* ~% _" ?1 ?8 b7 j% j* {+ O
可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。8 t5 g/ p( ^& r4 m3 E
0 q' K; _8 A  w& |" @0 W" x
三角波发生器9 I# y. @0 f0 p+ Z9 P3 G

# o& C2 G# h% B6 `/ ~
微信图片_20240529172342.png ; G7 F" m) K* e( _! _- u7 ?
) Y4 f: |' Y. Y, x3 w, E0 E
在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。9 `% c' j! m) B

- e; v5 z/ x% B" p 微信图片_20240529172337.png
+ {0 R! x& A! a
& U' z* ], x* \6 d' `
之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。* h  \$ f# y8 k; j7 Z4 U

$ ~) T9 _; U8 i' q 微信图片_20240529172334.png
  M* l$ t! ^4 F5 q' a, n

9 d' d0 `1 W* y8 z* [定时器设置好时间之后,设置触发事件。
+ ]: f5 e6 ?* \; F4 {
; ~! Z3 _: Y# e) P/ {& `- ^4 h 微信图片_20240529172331.png
6 {; ^; j* B: b; o# M2 t: m, h$ @

- J% h; f# q! o% l7 ~芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:$ `3 ]1 _! X# D7 F5 m$ J* o2 p7 ]
4 t; r0 I# J1 o
定时器频率/分频系数+1/Period/三角波最大值/2
; \( \& D* G: C/ D' T3 v$ L) ?6 I, F" D/ T
  1.   HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);        //用来触发adc采样' W8 M) D9 x9 H
  2.   HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);        //用来触发DAC输出
    9 C8 K. g' T* I& o; Z) B
  3.   HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
    3 p4 `5 l/ L2 W  d1 m5 R
  4.   HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
    & e7 g: N) _: }
  5.   HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码

# d! K$ [2 I0 d/ _, R( }) [2 G 微信图片_20240529172327.png * S& M# c1 [' [
8 z1 I% L2 j7 G/ r+ J- a0 H
正弦波发生器
8 J: q: r5 V1 p) W' ?' @
) _8 E. s) j5 r2 v
微信图片_20240529172323.png . a5 j6 M& h! c/ l2 [1 \

+ U; g# q! M/ C+ b. j8 ]首先是定时器触发(方便控制频率)。但是关闭波形发生器。$ L+ D: w* [, N; U7 D) s

2 w9 |4 [8 {' h. U 微信图片_20240529172320.png
( Z% D# m! g- |4 r$ O+ J
: i! x0 C3 z) E7 K
添加DMA,模式选择循环模式。
5 _0 I' p; _; f6 a3 W/ E; m; }) P/ r* e4 c
还有改变一下定时器的频率!
- C  p6 k; p2 g+ e$ d! b+ W4 T' F9 b7 a- X% P- g# M; H
其他几乎不做改变。; u' X4 e, M1 @% n* f& V2 z! `

6 v7 k8 ]( ]: `; V

; T6 j4 x3 u. Z7 W+ t" v; c1 h# \
  1.   #define POINTS 256                        
    , A6 S$ ?. n7 C) O
  2.   #define MIN_VALUE 50                        
    6 ^& ?; J% f- _# e# {3 \& X9 C
  3.   #define MAX_VALUE 650                         ) n8 U; B( k/ h8 @4 Y( p
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  ; v& ^5 Z6 @' y! ^) b
  5.   #define OFFSET 50  
    " w  W5 A$ `/ X7 @; N& X8 Q( `
  6.   #define M_PI  3.14159265* S; r. p: J5 V% E! J) j0 G. p
  7.   / ?6 ^9 M! a/ s! i  [$ J) S- h
  8.   uint16_t SinWaveInt[POINTS];' Q! y( k) r5 Z& `/ [2 V: `
  9.   int SinWave[POINTS]; ' w# U9 x( x. n3 J" g7 T
  10.   void SinInit(void)
    ) Y2 L9 y* K) y  c8 ^5 d- n
  11. {  ' K: W: q8 B/ l% @* g
  12.      for (int i = 0; i < POINTS; i++) 0 R3 j0 D% c4 q* b
  13.       {  
    1 L- p4 N& D- o( z1 [
  14.       double x = ((double)i / (POINTS - 1)) * 2 * M_PI; , n8 A6 l8 q; z7 k4 B
  15.       double sin_value = sin(x);  // 计算正弦值  $ o' f; E* d. s5 V
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);   v/ w+ \4 Z1 J2 Q- n$ @# m, `
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  , L% C4 {4 K2 @/ r
  18.     }4 l( [7 Y4 ^7 A/ @" q
  19.   }
复制代码
$ B$ H; L7 ~% ^& Z
计算一个正弦表。2 M  K+ a5 k* `# T1 a: Z+ W

; B4 I# v7 w5 P2 p( W- w/ h; F- f
这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。5 i+ H% m, T  O6 \) f" j- [. \
& I5 G: ~7 U2 G+ K
微信图片_20240529172317.png
% ?7 K2 D) t: M. X' F( |

5 `) d* x% d1 o& m! [测试一下正弦表,输出的是正弦信号。
# N# ~2 N1 m" j: u# X
3 C5 ?. a) o) D( h# [6 M
我们之后将正弦信号表导入DMA中。' t- q: z. L! o( U3 S$ q5 a

" w4 x2 v& A( a  Y; Y
  1.   SinInit();5 G( Y# X% [6 ?! A6 u8 A% Q3 B+ S- I
  2.   HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码

4 O4 @) S; P2 s3 V正弦表初始化,之后启动DMA传输,导入正弦信号。
/ r+ N) R9 b7 ?+ B8 g
9 I2 W' E- c9 ~6 U& D 微信图片_20240529172311.png
, ?4 G) R4 v3 P, F$ }, T3 ?

" \, Q$ Q( E& i2 X' f. y( M测试正弦信号成功。3 z# G* S' j1 w. Z0 Q  M

2 {; J5 x) Y2 _
FM调制
  1.   #define POINTS 1024                         : Y. A; Y" E- u; U; q- `8 u6 e" y
  2.   #define MIN_VALUE 50                        
    8 v4 L# u& w; @4 R. B' }7 @
  3.   #define MAX_VALUE 650                        
    $ Z* B4 m- i, Q- c1 k
  4.   #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)  / x* u! k& v: I8 M& Z5 l' P
  5.   #define OFFSET 50  / `; P* n& g3 _8 \
  6.   #define M_PI  3.14159265
    7 T' _) H4 q' S# a# h
  7.   
    + l5 ?7 C# P. B3 K
  8.   uint16_t SinWaveInt[POINTS];
    : u4 p- p9 f# m- x/ v
  9.   int SinWave[POINTS]; " l, J0 P& U0 m; `
  10.   void SinInit(void)
    ) _4 c1 f0 |. `! B! J4 }0 k, Q
  11. {  0 ~! w; y% r0 J& \7 {- ^
  12.      for (int i = 0; i < POINTS; i++) 3 ^0 N3 H1 Y6 B( f. F, Q, L6 @
  13.       {  
    7 V5 z- C$ d( Z8 m9 s
  14.       double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI; 6 G% O5 h! g; F4 W* p- z4 T
  15.       double sin_val = sin(x)*sin(x/4);  // 计算正弦值  
    ! N! q- E) S6 r4 e" @+ K# Q+ c
  16.       SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); - T# X' R/ l) V
  17.       SinWaveInt[i] = (uint16_t)SinWave[i];  * A, R2 V, V6 ]: k7 ^
  18.     }  i/ o6 W/ k, f4 V  v
  19.   }
复制代码
2 |3 ]4 C+ d& x+ n9 w" d
7 m6 L' I6 N7 e) B: p4 `
拓宽点数,加上载波,即可构成FM调制信号。
2 F) B2 \3 N3 }" J; ]5 ]6 N5 \7 x
/ T! ]! D5 M) G4 N# q 微信图片_20240529172305.png
6 P3 g0 U& |6 ]9 b! T/ [, \4 z1 T, W. U

0 y! `5 |; M* V' D9 H转载自:电路小白
9 F$ P; U+ I* |9 p如有侵权请联系删除
+ S& J% [- h; b) t7 a! |! v3 c! E3 c& |# q0 l% j
6 r! E5 W! s% I3 H  z/ h
/ u7 L* e9 O+ x& T8 y" X
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管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版