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
( {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
' 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
- 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
& 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
# @ 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( ?
- MX_GPIO_Init();
- N2 K$ `( y+ ^3 p+ h" { - MX_DMA_Init();0 L3 Y4 ^8 l4 D8 V! Z( F: M
- MX_ADC1_Init();6 W+ ?% Q& ]1 N" T+ j& h
- MX_DAC_Init();' r: u B4 O2 ~& z* J9 ~0 X( f/ I
- MX_USART1_UART_Init();
* F+ c& N8 E* j& A. s - MX_TIM1_Init();
9 J; D- H9 r% @- v6 e/ R - MX_TIM2_Init();
1 O5 o& u1 r, K) P* m - /* USER CODE BEGIN 2 */& f# N3 j4 _' F7 w7 S K( Z4 }
- HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); //用来触发adc采样 / u4 ]# _$ a9 N# W. [7 b A0 P3 E
- HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC* J9 F( E E8 [: H* r
- HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
" z: G7 R% u! E. t& P: n! x - /* USER CODE END 2 */0 C( {7 i0 P* f/ i
- 2 T, u& ]: J0 I+ V
- /* Infinite loop */
; t8 r& x9 x0 i" @) r! J - /* USER CODE BEGIN WHILE */1 J0 p' q& o& Z
- while (1)
, m8 Y8 y! t- C0 O9 v, n% _2 D# @ - {
6 N0 [( k5 r2 ?% S3 [' } - /* USER CODE END WHILE */
, d% c. L4 ]: x/ v - 1 s6 ~/ J6 A, k2 {
- /* USER CODE BEGIN 3 */
- z( t: _! T; C7 `% P$ U0 Q - if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))! e7 I, g; t" D# m4 `- ~" P# {
- {
( Y: U! `7 i; F3 [# @7 j( t - HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
; v: Y7 w6 j1 [, }& r! V3 Z3 E - : D* |9 W- I7 m8 H7 P5 e
- HAL_Delay(20);9 L$ J0 b; U* Y3 C
-
3 b F" j, Q$ n& ]9 A; x$ ` - while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));
5 o9 B+ q- r, F" ? - HAL_Delay(1000);+ ] o; u! [0 c" A5 H
- for(int i = 0;i<ADLenth;i++)
0 a9 W u' M' e% R - {) ?. Y% r- v( R) w5 `" M+ l
- printf("A:%d\r\n",AD_Value[i]);4 @2 K9 d& G$ V
- }; _3 ?( ~9 Y! _2 @8 q" C Z
-
/ z3 h- {& }" g" e - 3 g5 p3 f, S" W" X
- }
5 Q& U8 K" r, y6 Z: E. ] - }
. a- w( z, y5 U - /* USER CODE END 3 */
; L+ \1 o% X2 u& h) W3 ] - }
复制代码
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
+ 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
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
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
& 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' `
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- HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); //用来触发adc采样( K# b3 ?; H" L e& B
- HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //用来触发DAC输出4 O+ V7 Y. L5 e5 i
- HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC; k1 v' L! Z5 g/ s7 |/ W
- HAL_DAC_Start(&hdac,DAC_CHANNEL_1);: v, l3 z) L' Y
- HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码
3 \! X ]$ [6 ~3 S
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. ^
+ 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
+ [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- #define POINTS 256
; s: K0 }: H! x f - #define MIN_VALUE 50
2 x5 J0 T) w. g- ` D4 e - #define MAX_VALUE 650
% ^9 P$ K$ Z% _+ o+ v B2 J - #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)
6 y+ R+ d- R# P1 j0 i - #define OFFSET 50
* R4 }) p0 T. H4 q& E - #define M_PI 3.14159265( k3 Z) T3 P. E# S" |2 l5 C
- + {5 X9 R4 d: i' N
- uint16_t SinWaveInt[POINTS];$ G4 @( x6 e" F) ~% }3 p
- int SinWave[POINTS]; 7 n8 q1 w- z, B" s% `" {
- void SinInit(void) : M1 |& K3 g/ R. X# U% Y& H* M% ^
- { & o2 y1 c$ S; }" u" s
- for (int i = 0; i < POINTS; i++)
1 L0 V' T- ]$ \; ?5 N$ Z! L& C7 \! Y - { 0 |4 r) {; u7 o
- double x = ((double)i / (POINTS - 1)) * 2 * M_PI;
' K5 t6 x, ~' y( z- }$ a - double sin_value = sin(x); // 计算正弦值
4 q' u6 y( c3 G% v: d - SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); n3 _; A8 e$ l& o4 @
- SinWaveInt[i] = (uint16_t)SinWave[i];
4 d; }5 G, ^6 e3 J9 K - }& s/ D M9 p0 [7 F+ o
- }
复制代码
. ?. 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! ?) |
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
- SinInit(); W1 U8 l3 F3 K
- 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
$ 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调制- #define POINTS 1024 # b% z! q9 z. \& u) L. W
- #define MIN_VALUE 50
! [3 e0 U8 H& Z7 b( H/ K- d- U3 f - #define MAX_VALUE 650 ) D# [6 {( G" w5 Q. }+ i
- #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)
& H( F2 |7 b3 q# ]9 ]' |0 ` - #define OFFSET 50 9 G. @* V! G s
- #define M_PI 3.14159265
5 P7 }+ `- x7 o5 Y -
7 {8 b. T6 b# \ - uint16_t SinWaveInt[POINTS];
+ Z6 u6 _; Z3 A, E L# {2 S2 b8 M - int SinWave[POINTS];
2 L( B6 A4 O: |6 E - void SinInit(void) 1 o1 T1 q% D+ p) A0 P
- {
$ s" l% s. K, ~3 w1 T9 T - for (int i = 0; i < POINTS; i++)
r) A* g- A/ D% n, D9 j - {
8 I; r* @* X/ V" t - double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI;
9 y+ k0 h0 a) r' J* @ - double sin_val = sin(x)*sin(x/4); // 计算正弦值
/ D6 N1 f: w- |! K/ r - SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); 1 D6 M5 y0 Y9 Y' b& i x
- SinWaveInt[i] = (uint16_t)SinWave[i]; 3 Y& R0 d" K/ K" t
- }
. p8 [8 P9 l4 X- x! c3 | e - }
复制代码 + @; 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
( 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 \ |
资料不错,值得参考学习。
dac+dma?