有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。
" A" \! z+ s2 p/ B+ [2 e+ l3 l p5 Z% ~
a5 s2 \9 y r2 j6 W3 W W ^' k
5 V. _4 L" _( Y, @# q6 l1 e5 J0 d" l1 D
结合STM32G4芯片的特性,这里可以有两种实现方式。5 E8 e: A8 v8 [, p5 m/ T
; E; u" i+ p" c$ n/ L5 W1 x第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。7 F- H8 V; B q6 m1 N/ A
3 g' r; [" J! E! s3 d6 [3 Z3 p
我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。" k7 t1 D( r# f1 Z6 L2 q
$ O o: r. U2 A3 d. g
使用CubeMx进行配置,主要配置如下:
8 s" X* A: ~- s# N5 v/ ^+ R5 A6 s0 h7 X8 Z& f( h1 `1 y: Y1 R& w
: L1 u& b9 a t
9 C, i. `8 B0 ]9 Z6 L# U) x7 j3 Y4 p) i$ }9 v" I$ r& T
9 K" y, U6 c+ h! X8 _. ^/ l& W; H. K" _9 z; J! ]
4 r& u% d5 G. E3 f
完成配置后生成初始代码,再添加下面代码即可验证测试:( D* I: k9 u' L, w& {9 C
/ W D5 }) L% |
- #define Tpai (2*3.14159)$ e3 Z3 S" C" x% q
- * E& f; z7 ~8 }& R4 W7 t9 D
- uint32_t PData[200],Dac_data;4 E9 `$ m6 i# j+ G5 v
- . i& [$ \' c! d3 t$ v4 g6 @ l
- uint16_t i;! m& k$ H% }3 Q$ k# X
" m0 {% i) i. J$ h- for (i=0;i<200;i++)
9 k. a* q8 ~9 p0 v- G - . B) U! @: q9 L6 G5 X
- {undefined2 i# h/ V% R# o7 A$ b p: b9 A
/ Y3 z# m! E! ~" q8 M% X- PData<i>=</i>
o8 G# c! [4 p, y+ s0 Q& W& c4 n
* A5 ^0 J3 X+ O* P- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);* l$ @! B3 ^ `. _- Z) c, Y
- , l! I: {9 w# ^4 |8 Q( X
- } //prepare data for DAC
+ {6 F& E* h4 m+ Z1 g5 H
* n; y) N8 b( W; f8 I0 O- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);2 }: j5 M9 s& P0 r; e
- : m3 O4 \4 J$ r. `* Z: |% V, S! I
- __HAL_TIM_ENABLE(&htim3);
复制代码
, M( k2 H% k3 G- o% T( L" s% b" I H. ^$ D% x$ N6 k
2 _. h4 O4 o0 @- S4 r$ n
上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。
7 n7 X4 @* i/ L4 F) A2 `9 N1 h# o! G, e4 ^# p+ a3 @# F/ P$ {
! a& {9 G" G$ a# s+ D0 L& N9 E, I8 _' s" S
其它配置不动,代码稍微改动和整理下即可。参见下面代码:# z, Z) _+ E. o% }# ], R
J8 Y& q* y9 r1 h1 f- Uint16_t PData[200],Dac_data;: s8 {1 {- S. Y
- 8 U3 [& V9 T1 F6 e. N; b V8 w& a
- #define Tpai (2*3.14159)- `. e- o# x& d T) t# ? x |8 W
- 9 l7 D% F/ d' m9 K+ f
- uint16_t i;/ |* `1 j+ z" G. U0 N& k
- 0 C+ g: |6 z9 u8 r; B
- for (i=0;i<200;i++)! }/ P) N6 D% B- u N
1 g# U2 A5 l' l! K0 O- {undefined. n, e& y; c7 X; t, O
- - ?/ m& F7 N; x2 N' e. C
- PData=0 M' O; s/ q( y2 W7 |0 q
" }2 `+ ^& k" A: y5 X! J: [- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
1 H0 F) \& B/ e. W - [5 l7 n4 A7 f
- } //prepare data for DAC
7 s" m1 b; q9 u: z" |& ` - " m& |* V& ^2 K
- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);+ f3 A. l' |) }/ M4 g
- % V) u+ H1 ~: \7 e, Q% K# K! c
- 8 f) O1 G0 v) W/ a1 J% x( ]
- & P6 F2 B( i. W" A; t# e
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
: u% v; _3 S- O' T6 Q - 6 F# T* c: I6 A! j
- HAL_Delay(2);% \8 c- Z+ P( d9 h) ]6 U. `' ^
- * X5 b$ K5 H. l6 p! w
- ) m) R$ m# x G
. Y# X4 a4 q8 W! Z$ O- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);0 K8 `0 @8 j- r9 L, ]
- ' X( e. S+ ]5 x! M) q$ j
) @4 A3 b% s# Z2 F
% D& u8 F- I" W+ X A- __HAL_TIM_ENABLE(&htim3);
复制代码 / L: W% s+ c( h/ y" g- f) z
. J7 b0 W/ q; s* S& Y
v, y- l }9 A8 i, x5 |7 W) g
' ^5 Z. ]: \, J. _第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。+ C: l$ U) @# Y2 N& h, o: q
3 W# Y* |/ z1 x9 C; ?
基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。/ x! V8 X2 e; b A
g- K: J& t3 B
6 D* `- L# s: p4 B
$ ?$ D0 G" G5 L% E' B, E5 E) p h E" p( I% m3 ]$ V6 J1 W6 ~2 e
主要是DMA请求事件变了,其它配置跟第一种模式基本一样。
1 G# q" M- D) T3 C
3 \0 t' J$ P; B" D# m9 k9 C9 s: C配置完成后生成初始化代码,再添加下面用户代码:# e: \5 W6 ^3 I" q1 T
3 \' O+ q! B4 A1 Q# K! F; F
- #define Tpai (2*3.14159) C7 m# ^ {' k# C1 \3 P1 a
- 2 P* \4 [2 |7 I3 K& U: Q: t/ v
- uint16_t PData[200], Dac_data;; h2 e# e1 \7 v% t& }; Q
+ _* q) A7 L5 ^- uint16_t i;
! f. C: Q% k( c+ q% ~4 a" i - " ~( w& N. m, e/ Q
- for (i=0;i<200;i++)
2 N# D5 {& H% ^6 E; O2 J1 I - L) O. o* q7 a
- {undefined J6 R* |9 h/ b( P; Q8 D+ z
/ P6 v$ K9 j$ W0 S- PData=: I1 G+ R4 F& G( u0 |
; _4 P4 p+ s- T7 t- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
- g2 L# R* u4 {. Z. P4 {4 V - ( D3 A3 y5 o! _2 J9 x7 z
- }
! |& ]- p! N$ W - 5 j- z" M4 F) `! V
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
6 l+ e# l( f; [& ?5 c: W - . a0 a6 p4 D+ \) r; L" o
- HAL_Delay(3);
' B3 E$ v5 I* G8 i" d
$ ], T) r& H; ?; v+ k/ A
( i" j- f3 m* ^1 A# [* t9 u- . ^! r6 B2 L# o' C" }
- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200); [# f+ B2 N3 I' N4 J( [$ \
, w8 w: ]$ I; e' B* ?
7 \5 r" M5 ?3 w4 S. e+ E* i+ P
4 |$ O- S2 C! t5 U3 j- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);
9 Y k* Q* v7 ~9 ?+ V# n - " T1 K0 {! b7 |7 `* t4 m
- __HAL_TIM_ENABLE(&htim3);
复制代码
0 i. K9 Y9 M" I' y4 c* a0 h5 K/ k/ }8 T6 K5 e5 |/ k/ m
这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。
2 X# t' n" h) I% ~# c6 |
0 ~' o, G+ M) B) p4 a如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:! V& K8 m c5 B7 y$ H2 n# J
; l9 r% B+ ]! E
9 r/ P( C8 ]8 \0 E% [1 F5 V9 S" a/ a
: n t$ d E9 e7 g! [; {
相关用户代码如下: ' B7 B% S2 K; E0 |/ a7 C \0 J1 ]
3 j% d3 |9 E5 w8 F5 O, p
- #define Tpai (2*3.14159)9 C! P. A. G. [- j% I/ B$ {
$ Q* j8 {! }! w s- uint16_t PData[200],Dac_data;
0 Y+ Z/ A: z2 v - ! e6 ~/ K) ]' p3 S
- uint16_t i;. S1 v' p; ?9 q, B
- , \% L. S# q, ^7 T' ?. A9 n5 q
- for (i=0;i<200;i++)- }8 R* I0 J7 d X9 W# e
% O8 ~' `" J$ u- {undefined% y+ ^+ B! z# }3 u" t
* O6 z% f* q w6 y- PData<span style="font-style: italic;"><span style="font-style: normal;">=
: w* O% n7 h* m( V: a; Y
1 v3 R u6 V6 B- a" D( c- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);0 p; Q+ E; w" o; f- I3 K
8 t# y Y! M' n* p9 v- |# a- }3 M/ Q, m, P3 H }4 P
- % [: \* ~& Z# ? J$ E
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );2 b$ E3 B ]1 x
. e0 w+ _* u4 G) p2 T9 J- HAL_Delay(3);0 }) `( V1 S; U
- ) o X. j5 l! I8 a9 [ A0 p# F! f
% {9 y! p6 {- m! }- ( i4 r! ]. |5 V) Y$ M9 @
- while(1)
9 W3 k8 e3 x6 z& ~
_- B! Z2 i. d( W- {undefined5 h' Q1 [0 D7 e3 @' A5 j
$ ^9 Y1 c6 G3 M1 ]# {0 ~- j: ~- for(i=0;i<200;i++)% g) L6 W, X6 [% B
- / D9 O3 s7 B3 \( u# N* E
- {undefined7 H. B* G( E8 ]* c. f: G' g3 V8 }
- # {$ l" f6 Q) a6 g( R P ]& W
- DAC1->DHR12R1= PData</span><span style="font-style: normal;">; : }8 ^9 f5 E9 u
2 A6 G8 M) Z# O( _. g- X: O- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);
' r2 M1 {& E( [5 l: O
( l( }/ I/ b" M- d C# T* R: ?) o4 S- HAL_Delay(5);
0 j, y0 q _ i5 t- l, j5 A0 u - . d* F4 d& e6 a
7 ?9 p/ P! S7 b3 ~" g4 A0 Q9 |
% [/ I: K$ w0 S- q I. J5 @* L6 F- Dac_data = DAC1->DOR1;//for debug
5 l' F) G# q. v* f1 ?* \ - " U7 h3 {2 |* r+ z
# Q! ^; K! E0 {" {$ a% [- a$ [8 _4 S" I- * W n7 W# X$ {6 h1 U/ o$ ]/ s0 A
- }
% ]( ~& K& k4 _1 J
$ h2 [. u' K" U# T6 @- }
. c: @# d* s) ^7 \1 {: @ - </span></span>
复制代码 : u* ]$ d% a" m: a- p, m
总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。7 ^- n- S4 C2 X' j
9 \' H# j- }4 }, y
: i& w+ R# C1 I) G. O' [8 i; y7 {) {4 ~! i! \% ^
好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】
' X4 `* c: j# J- v# g
; v& t+ ?! a5 P7 E, p
+ f7 ^( D( I7 B |