DAC,即Digital to Analog Convertor,是数字到模拟转换器,也称为D/A转换器。其核心部分由R-2R电阻网络(也称倒T型电阻网络)、模拟开关和运算放大器组成。它可以将二进制码或BCD码表示的数字量转换为与其成正比的模拟量输出。4 O/ I; ?, G8 ~
- b E" ]# h* t) S1 ~
DAC的工作原理主要包括数字信号采样、量化、编码和模拟信号输出几个步骤。首先,将连续变化的模拟信号在一定的时间间隔内进行离散取样,即数字信号采样。接着,对采样后的数字信号进行量化,将其转换为离散的数值。然后,通过编码将量化后的数值进行转换,以便DAC能够识别和处理。最后,DAC将这些数字信号转换为模拟信号输出。
H; A" o2 M B$ ?! H3 o' |7 v2 L! f
DAC是数字系统和模拟系统之间的桥梁,具有广泛的应用领域。在音频处理中,DAC被用来将数字音频信号转换为模拟音频信号,以驱动扬声器和耳机,其性能对音频质量有着决定性的影响。在通信系统中,DAC用于将数字信号转换为模拟信号,以实现信号调制和解调。此外,在仪器仪表领域,DAC也被广泛应用于各种测量和控制设备中。
4 g* [" Y& q" i% l$ }! M% `% c; ~" Y# x- O0 S5 p, ^
# q2 @+ A6 o! q+ x( b1 b; L' U
+ `, }* ]1 i) \ k1 eSTM32中的DAC(视芯片而定例如F407ZGT6是2个DAC通道,有些芯片不支持DAC功能)支持12模式的数据输入,可以双通道同时转换。3 }1 W9 M1 {: Y, g; @9 [
$ o: T- Z+ b* M7 k
本期我们将介绍如何使用STM32F407和CubeMX利用HAL库实现DAC的输出。(本来是想使用C8T6的,结果突然想起来C8T6)没有DAC。
( e$ e6 l' [. c2 B% v4 j1 u( W$ C J& l3 [2 Q
CubeMX配置
* d4 r; L3 @& V" E0 u9 D6 z& v8 M
. i" j4 g' h8 N
8 [* L5 Q% U- s, r! h1 }$ Y8 Z, c, d8 R
在DAC通道中开启DAC,在F407ZGT6中DAC1对应PA4,DAC2对应PA5。
- b! Y9 A7 D+ X, I
& o2 Z4 D8 Z! S' }" t) @6 U) `
7 G; V N l. \
+ |5 x; w/ M7 K, I
OutputBuffer这里设置DAC的输出缓存使能,Trigger是DAC是触发方式,这里我们选择不触发(手动写入)。
# `; S& Z7 C! `- f% b) N8 t) v9 T% D+ c# q% i. _
如果选择触发的话是从缓存区写入数据。% g0 d! z# G( e6 F* w
$ i# E( g$ O# F) m" t
8 C6 K/ c3 I {
2 n1 f8 M! O: f' M
这里顺带加一路ADC采样,因为手上没有示波器,所以使用ADC来进行查看。
7 j1 z& t" E. K; `
# N n* `# O n. r9 A/ D7 w这里使用DMA进行采样,具体可以参考公众号之前的关于DMA+ADC采样的内容。
7 g' E0 A( J2 E y% t1 }, h, Y6 s* H4 l3 J
STM32的DMA采样+FFT时域分析(STM32F407)
; V: H' X0 _7 z7 y3 w \; r2 E! y* e, E6 t# f/ E
但是相比于之前的,需要更改一些设置,首先是DMA的设置
g1 `3 }7 N* [. V; }8 D4 R6 @! X/ n5 P2 s1 U1 D
! I$ g. g {, y& ?! Q. Z7 r% s/ f) M8 e8 G2 [* K" X- }# J4 p
这里设置单词请求,而不是循环模式防止数据跑飞掉。) Y s& ?; g; ]; y& X8 a
1 V( B6 Z& @ k) `' t
; X, h! {5 y; e# a- MX_GPIO_Init();
! ~/ t0 [0 a3 f - MX_DMA_Init();# m* _& Q3 w" d
- MX_ADC1_Init();
% O& `" _& @4 I - MX_DAC_Init();
2 \ `+ R) Q3 C - MX_USART1_UART_Init();5 D/ Z' T; J6 U: M# S" ^/ |8 I4 t
- MX_TIM1_Init();( K6 B( Q# ?' O+ E$ f( P
- MX_TIM2_Init();2 ?' \3 Z5 m+ O, i0 k/ Q' ~
- /* USER CODE BEGIN 2 */
- `4 t1 T! i! i - HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); //用来触发adc采样 2 c* K' w6 t- @, ]6 I5 T
- HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
# C0 f: b# w+ E* @# e - HAL_DAC_Start(&hdac,DAC_CHANNEL_1);7 W# C- J2 [" T* j. U% n1 \. V
- /* USER CODE END 2 */
9 r5 \1 e( r) [7 m* |( o - y# e5 C, W$ N+ h; E
- /* Infinite loop */5 { b" w3 n# G8 c9 x) f
- /* USER CODE BEGIN WHILE */6 H N q+ H) W, V
- while (1)
, b8 J$ {/ X0 e5 V - {
( T! l. j5 A# S5 s& d1 K+ R - /* USER CODE END WHILE */, g* |9 |) V9 y4 I1 ^0 w
- , N s6 Y9 i( B) M$ }
- /* USER CODE BEGIN 3 */
' r* x6 c. [/ J3 ~& I3 j/ l* ]% f$ E, D - if(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0))5 E& l& f" c, o N6 T' Q
- {$ b( b& ?5 K+ ^! E
- HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC" J" F8 Z$ J$ f# W3 H3 \" `, _$ J
- - B5 J1 m0 E: X% N) A0 l
- HAL_Delay(20);( p) d8 F6 u8 E# g) o$ v
-
8 ~ d! ~: l v+ y3 A - while(!HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0));
5 }$ _" C$ R/ ]3 L- s" Z - HAL_Delay(1000);
" ], L( f4 Z$ O% d3 x - for(int i = 0;i<ADLenth;i++)
: P$ o5 B: ?4 B* `/ T - {
/ X! m! t8 }& b! h# W- Z - printf("A:%d\r\n",AD_Value[i]);
5 r r& {; o' v - }( m( u* L0 ~, o
- \ u0 H1 f; t
- : V: I$ p/ n, L, |$ H0 h" O
- }
7 o+ ]5 ^7 w. E5 d: {+ C# W - }
w( i$ b; o$ e8 c - /* USER CODE END 3 */
) @: p8 j4 Q" b5 G$ E - }
复制代码 + v) h6 x q6 ~4 w' n2 O& }% e
2 S+ D$ p/ I) L/ V$ o. f
我们编写一段代码。
4 l2 D% {: l. U- U: e F8 m7 {. _1 i9 |4 Z. c4 T! o: Q3 @) n' M
按下按键的时候设置DAC的值,开启DMA传输,再加上ADC采样,之后输出结果。
5 K# R; j3 H, F+ ^* U/ M5 t, f8 J$ ?( C5 }4 |
1 k V$ W1 A" U* k9 f
4 \" R1 e5 P. ]# a2 c. @/ n# z
可以看到,我们按下按键之后,ADC的值会呈阶梯状上升。) {3 T$ h9 ~1 _5 d+ U3 B6 C
0 _3 C P% i& t4 l2 L
三角波发生器0 G- X0 ~8 d/ U
1 }- u1 }% z7 i2 X& p/ T8 r* ~
2 e4 r, J8 d4 y+ w- y6 W% O h, c; T# K0 c. _
在DAC配置中打开波形发生器,触发方式选择定时器2触发,三角波最大振幅511。2 w( m6 [; _3 a& N9 O- S; ~
/ W9 p3 q% n. i% M3 I( }( P
* v) g* B) z6 J2 d) V) n/ B7 p5 G6 l* A
之后开启定时器2,由于ADC的采样率是1000HZ,因此根据奈奎斯特采样定律,信号的最大频率不能超过500HZ,因此我们使用100HZ的三角波。; }, X* T% Y, s; A/ B0 {1 R& X$ y8 R
1 I1 Z8 F/ d9 Z1 N1 R8 B7 P( H
, p; H" y3 F, d0 ]- f/ e5 Z- e7 `7 c g. `
定时器设置好时间之后,设置触发事件。% u! }: b- I6 m. c. T6 A9 v
* C1 s( N" T$ z- l8 ]' u4 o
& G+ u/ a0 ^6 ?' e# f" L9 V8 A
( a I d) j. V w: q
芯片手册中简单的介绍了一下如何计算三角波的频率。当触发信号发生后,内部计数器的值就会+1,所以频率可以用如下公式计算:
5 {9 J9 X2 S; l. G/ E
* S9 d8 P( }" {4 j" y3 ], r定时器频率/分频系数+1/Period/三角波最大值/2
2 e& Q/ w& g* g5 R6 x1 D3 e' ^5 }+ j' R/ U
- HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); //用来触发adc采样
6 K- y5 W8 l: \+ y0 ~5 o* A - HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //用来触发DAC输出 I7 {* @/ P1 Z& t. l& R
- HAL_ADC_Start_DMA(&hadc1, (uint32_t *)AD_Value, ADLenth);//开启ADC
8 ~- j( Q& z" c9 ] - HAL_DAC_Start(&hdac,DAC_CHANNEL_1);) J; Q+ B3 P A8 P
- HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_12B_L,0);//设置直流信号
复制代码 6 v4 V0 M5 C/ A1 x) }. A
$ i8 {9 j4 Q. e) x
$ e/ m. A0 v3 c/ p正弦波发生器: Z* t- Q9 L9 I" a2 U. O
) R9 ? r5 j" }' F- X
2 p$ ^: v! a7 j; @8 U+ e% h) C9 e
1 M# E! T- P5 v5 e; V8 m首先是定时器触发(方便控制频率)。但是关闭波形发生器。; Y, I/ z0 Z" F8 n: r
# F- B, j& T. [: z6 L0 `9 O
( E% E: l) e: \
/ m6 |3 Y; @4 t" ~添加DMA,模式选择循环模式。
. H `. L" ]4 l3 H# i+ f, i8 \4 s9 o! |2 t4 x1 E# `& g E
还有改变一下定时器的频率!
' j, L: S% t, p: A" F F6 ~6 w# u h" ?1 g% X) }/ N
其他几乎不做改变。0 ~! W( C, v5 h
& d6 R9 L4 D8 S k0 H8 z( k, z8 \5 z, k
- #define POINTS 256 ! v R2 b$ l9 p% E6 R7 t% ~: B& H
- #define MIN_VALUE 50 . o5 d2 b2 B3 B' p7 y: ]( o
- #define MAX_VALUE 650
9 r8 n! H6 i1 K" L. f+ t$ Z - #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0) ; l0 w" ^; k! I- K
- #define OFFSET 50
2 `# n m m) [& ` - #define M_PI 3.14159265
. x. d. u. x2 C -
6 k# J" b$ j" }: v - uint16_t SinWaveInt[POINTS]; t2 L- V" V1 `3 b+ R0 n) j
- int SinWave[POINTS]; # M7 @& c) u# D( y$ X
- void SinInit(void) ; B5 V; h( Z$ U9 z# w0 V; N' [: L
- { - K, M( }: |6 |
- for (int i = 0; i < POINTS; i++)
: t& K5 E% `. H' C) w. I7 T - { 0 K, [: y7 A1 g+ }
- double x = ((double)i / (POINTS - 1)) * 2 * M_PI;
' A- w7 S6 |2 j* u/ J! b5 B& P - double sin_value = sin(x); // 计算正弦值
) f2 i7 C; |$ r, |9 x7 N - SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET);
; D2 Y' ^* M6 z - SinWaveInt[i] = (uint16_t)SinWave[i]; $ b6 z6 P, ~& I
- }( m2 u" {! A% v7 F) L9 }
- }
复制代码 7 ]# G; \7 @' p4 I
计算一个正弦表。4 @# h% ~! g. s p9 b. y& }: y i
& ?" y1 U1 \2 ?这里的Points决定了分辨率,结合定时器触发频率决定了正弦波的信号。
4 v7 V, q$ g* Q z- a8 [/ \) @7 ]
7 s0 Y0 ?7 T# D; E; E* U. j
# B1 ^% d) _' }& b! P& g
}, q" Y" K3 h2 ?
测试一下正弦表,输出的是正弦信号。
8 o$ K6 j. }* ~& b6 C& K9 w S2 Y/ }+ f3 ]
我们之后将正弦信号表导入DMA中。# N1 L" E" a' v" I
2 K: k8 ?8 y7 e+ Y- SinInit();
1 d8 L& |, v! p- E2 Q - HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)SinWave, POINTS, DAC_ALIGN_12B_R);
复制代码
1 | P8 D0 E! y* w6 y8 C正弦表初始化,之后启动DMA传输,导入正弦信号。& I2 R" X) L& F8 e! y
. w! `! D- \# ?) A0 s
2 X7 k, I3 s3 z, F' V1 F$ F; m! Q# j8 X$ r# o5 o1 X; B b: n. {
测试正弦信号成功。
( `- u# ?- M* ~. R& H
; L' d4 L( s- A2 M. ~9 \3 t0 dFM调制- #define POINTS 1024 , h& g7 `4 u. ]
- #define MIN_VALUE 50
8 o- i( K! w# M' e- Q% L - #define MAX_VALUE 650
" Y( Z y( h/ A) M* u2 q5 s - #define SCALE ((MAX_VALUE - MIN_VALUE) / 2.0)
/ G) z& e7 c7 z1 o7 R% k$ b$ Z+ W E - #define OFFSET 50
5 k* X1 d3 y" ~, R - #define M_PI 3.14159265% [4 g. Q9 z+ I! z- i, H
- : }9 l3 B5 ]9 r/ o6 Y) P4 ?! N, E- _8 L" e
- uint16_t SinWaveInt[POINTS];+ Q. X& d+ C2 v7 g [' q P
- int SinWave[POINTS]; ; V' |2 s2 b f6 z) V$ q3 L
- void SinInit(void)
1 I- G* ~) K5 d - {
- J: N L1 }, U; c - for (int i = 0; i < POINTS; i++)
6 I6 ]5 Y% `/ B - {
# Y6 m$ ^5 i& n o7 m& F! o' Q - double x = ((double)i / (POINTS/4 - 1)) * 2 * M_PI;
/ K5 k/ w4 x/ q* S8 T* ^3 _ - double sin_val = sin(x)*sin(x/4); // 计算正弦值
7 y/ u D5 p, d0 f' I; V" T - SinWave[i] = (int)((sin_value + 1) * SCALE + OFFSET); ; ^- H0 [- t' C1 S' O8 W
- SinWaveInt[i] = (uint16_t)SinWave[i];
, N, Y" a7 ^$ A# f4 l - }
# E" A$ V; B' r; v+ D2 R9 R - }
复制代码
# [" C* I% y- [ E5 ~) l1 |* x# ~7 m. c9 F+ j5 ~+ E
拓宽点数,加上载波,即可构成FM调制信号。& ^. W1 M, @2 u- X; L
6 ]' Z- a! N& g
, d r$ @$ }; W0 M1 F& D
4 `. z& i8 ^% I+ x) |. C; @$ k
8 J, ]* W9 V7 E' t; N2 t6 T4 k! c转载自:电路小白
: ^- Z1 M# }6 n1 `0 f3 e. L如有侵权请联系删除! W# E& @- W- [& w. V5 N
/ ~, _% H" E# L( X6 ^
# e% y3 _, ?5 T/ q. d- K( X8 ~! [. T! o2 U% R- l$ k4 c
|
资料不错,值得参考学习。
dac+dma?