有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。
! Y" X3 W \9 k, j0 J- ?; z1 o
9 H6 s. d" P, k2 W7 q' s
3 J/ F% _9 u, W. ^; N/ o- |' n5 m" [' b% V: i% V+ r/ o
" V. @# M2 h6 @% U6 A5 G结合STM32G4芯片的特性,这里可以有两种实现方式。
3 V/ F/ ]0 e% j/ x: Y( k$ h2 K
8 u" a0 [4 U* ]/ E# M第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。
2 P; d6 y* `, Z/ S. q7 T1 X
$ k$ [* f6 M8 `( z我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。
" Q0 ^1 W7 \. D: ^* H. V! K( Y( Z% k H: F" O) U- Y
使用CubeMx进行配置,主要配置如下:3 f3 \# E9 F4 ~" f5 x0 u) Q9 [
7 k* i0 h/ I3 C8 a. e
- l9 G- N5 F/ y' _- H" U
8 J& t2 H( B- S# w2 m1 V4 I1 M. Z4 J* t1 Y y+ x8 f% x- C
4 k2 ]1 f5 K* V6 R
: M1 F. Y8 E9 S. B' W
4 D T# s# X `5 s, J完成配置后生成初始代码,再添加下面代码即可验证测试:% m! g# a1 S, n7 M
2 d# r( u8 S& ~. t/ a2 G- #define Tpai (2*3.14159)1 v8 M% N* _% {
* h# K* \6 n8 {3 x/ m7 T6 z, q- uint32_t PData[200],Dac_data;4 c: z- L8 f3 |& I, V! I& d
# p( g7 k G0 g9 k; K- uint16_t i;- j" D: k( Z/ ]4 X3 u3 w
- 9 S+ N. e M; s$ P
- for (i=0;i<200;i++)
+ }% ]4 h3 Z$ O9 m# b- e. u! c: J4 J - ' }1 E# e& H4 @, V! g' s
- {undefined5 B( O& n9 z: X: a6 w( P8 ^8 u
- : R) ^, ^' G6 p
- PData<i>=</i>2 U/ e7 k& k. O3 o& O! c, W; ^# n
; W3 T: h* I4 v5 t2 w j- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);: [4 d' I- c5 ^9 ?/ Q+ n4 Q
i0 u% e$ ^# q5 O( ^# s- D, n- } //prepare data for DAC* S9 }" P% e3 O
- 7 B0 [4 O% ~; ]: {3 n5 r
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);
8 m% ~0 U$ r% H Z, w2 J - + D4 _) d/ w& g5 o
- __HAL_TIM_ENABLE(&htim3);
复制代码 # y o1 M9 p; m
! ], o+ g& v' f8 C
8 w g, b6 @7 U) y6 M2 [上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。& C5 o% }4 t- s8 b; d# q
! h% G5 a# ]: x' W* J; |' I' a/ U. _
! f( X4 w0 i7 e2 f' I, N5 ]7 S$ t* U8 _0 e5 u) m8 v- j; U
其它配置不动,代码稍微改动和整理下即可。参见下面代码:& r' `# p G6 q6 P q/ X: D
" U- w3 `! l, J7 s# O W" |7 U$ ?- Uint16_t PData[200],Dac_data;6 ?# s! e- D7 l) _' u
0 n( a4 t$ q+ X8 u+ r- A8 W* z- #define Tpai (2*3.14159)
: c4 s7 a/ x) z- T1 ~
& F, v4 ?2 V7 Z: f- uint16_t i;
6 e8 U) f" f, [ - # h. T0 m# x" W8 T5 |" {7 H9 n
- for (i=0;i<200;i++)
1 f: d8 J; O+ w - : L9 N! [0 a @% D1 L9 [2 z
- {undefined
" |* T: N- i8 q& U' D2 ^4 z
1 v# A8 s% T* T5 @6 M- PData=. x) R8 ?7 G" t3 S: @
- 4 `) r i6 f/ S7 | O& N
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
/ C) X2 @& g/ C2 ~) u, a - % b# e. x8 \: U9 V8 N5 h, S1 c- F
- } //prepare data for DAC
6 N }; t( I( [+ o- a& o
: @& A" C" T) z2 i- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);" i2 O0 r' a: v* d
- 2 P) X6 K+ R# K
2 z, `4 o1 v+ v q4 ~
! Q) H( o+ \' Z/ e' i5 {7 N9 h- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
; z$ b( H W% o; O
$ m) r; ?/ q9 M; a5 u- HAL_Delay(2);% i3 Q: ?: c! \; i: k# n1 _" I0 k
- g, X' l& t* q0 J5 m' F" K3 f s6 Q
5 v) ~. Q4 y. p# P/ t% e5 J: x
6 v4 B/ i; y1 i2 t% H* m- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);! K2 _, P' F! J2 @! @9 l! K
: }3 M; v% L& m' f5 B) ~& U
3 [$ b% {5 T, R! J' p% a- . A: r6 c2 ~) D) o1 k3 D Z
- __HAL_TIM_ENABLE(&htim3);
复制代码
: n, s' \0 x- r$ A& y7 |2 x/ j% n7 Q# t- ]5 N9 c
& ^; D+ d+ e( i" t' r
% s1 M8 m& p* g& O! w) h
第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。3 H% \# y0 M0 [' `
7 ?) t' c5 A! d9 b7 [
基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。; X6 A. d2 x) |1 s
: J. c' A, j" c% N
* L- n+ z# a/ R3 }) G! q* ~0 \) h, {" c" G# q: U1 k/ `; ]
- F: Y* [- K7 T$ V" D& I6 g
主要是DMA请求事件变了,其它配置跟第一种模式基本一样。
$ I! L' h' s, f( ]
' m9 a7 p) m# H7 o) Z配置完成后生成初始化代码,再添加下面用户代码:6 t9 w& x3 j0 K$ K
$ ]: c8 Z' C2 _- p. N3 q- #define Tpai (2*3.14159)& Z( p1 {4 ~* Q4 q. R7 q
8 `: v( ]- q6 g( C# ]' T, w- uint16_t PData[200], Dac_data;
, g+ u& w" M7 c, e, e' V
) O/ V# K& f8 W1 R- uint16_t i;" F9 V* w1 Q [2 M5 n
* L" b" v$ ^6 A5 G2 W- for (i=0;i<200;i++)6 Q& w. V3 B3 y, A
9 ?$ T6 P! X4 D* d5 t- {undefined
$ k1 E# y. n/ T# j
6 o) @/ w- ?) L+ ^7 m, r; U+ w- PData=
# G. q3 ?0 |) d& x( V' ~
' I7 _( \ _/ D3 D* ^( Y& s$ g9 L5 [% ^- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);, i1 y D% G& O, B* ~8 b1 ^
' `/ Y' T2 b9 U0 V- ~/ h- }
; d! A; F: K& C' o4 n) d3 c% q
' R% b. e" X+ j' I- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );, o6 k- ]& a) }1 T, v
- 9 I' a7 S) s. Q' m" q
- HAL_Delay(3);
7 d* m' ]3 \6 _: E8 d g - 7 g$ q' u( T+ l1 F+ \8 X
- 3 A1 A' z7 Z6 |$ T5 m" b
# q8 w; T: D s; i V1 ?9 `- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
2 ] a! r- c5 t! R# }# \; e' G - ]4 z' T/ ? L/ C5 `. a
0 O& z% E, e- k5 q
# ]# h- W m; a- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);7 C2 h! R, I+ \+ g
! t3 e$ K, i4 c% [/ \) A3 L. k- __HAL_TIM_ENABLE(&htim3);
复制代码 1 q6 ^* e; s5 Y) `! @* D! W
, b7 e* W8 L5 l8 m: h这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。
1 n5 b+ n* d; C- `) Q0 ?/ j
, U5 l, z5 x: t& U& P4 V/ g0 N9 w如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:
, m# i: H& \9 Z: d1 O5 ^3 @
7 P1 s0 z. R2 z$ H4 p: J4 b/ k6 {8 l8 B$ o7 x
3 |) u1 j% Q- |/ f& Q2 q3 }. y) ^! G: T7 Z+ g
相关用户代码如下:
, f( E# k0 E$ M" q$ \! W, {; {* j3 k+ ~$ H& [5 w
- #define Tpai (2*3.14159)3 J5 a1 V# I( T
- k h& z9 z2 N; R' ]- uint16_t PData[200],Dac_data;1 X+ y6 {) S. {+ }& n
- 7 g$ P5 a& `( K2 I6 A
- uint16_t i;
# V+ G+ a( A' R7 P- i/ [& i3 E5 m$ u
1 V' P# n5 J" e- u1 o! l; w- for (i=0;i<200;i++)2 }( F5 k' }. I3 E( k3 t1 ]
" e0 u- L8 q5 X" c& O$ f- {undefined
5 @ E ]3 T0 I/ j+ l8 L2 u - & n+ F# a2 J X* y
- PData<span style="font-style: italic;"><span style="font-style: normal;">=1 R+ V9 W+ t E9 o2 u
( f- D/ b$ p' @# ?- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);
: V& E. q1 F3 e. i
) U8 g$ F* t4 X6 ~- }
1 H& e- Q$ L' W6 X5 } - ; L7 x& ^; G& B7 E7 A1 }1 T/ l7 ]
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );& l1 [- N/ S9 v( Y* ~' e
4 s8 e, d7 n+ Z4 P$ a- HAL_Delay(3);
/ i7 | R& a% W& x% [
# Z" W% |& n( U o* [
) c" U% Y8 k* J4 A5 x% o* R
- K: K& Q- f# ^ X+ Y b- while(1)+ F% S+ t# |% q, J5 k1 i) z* a
4 l3 p, u% A4 u/ _7 T3 e- {undefined: }2 v# t' j! @
4 C& a' Q1 r% r: Y- for(i=0;i<200;i++)3 f# \. N& {# c5 x* y' v+ M
3 W9 I; F' k# y0 M/ I n( L+ U- {undefined, T9 m$ S( C% f
: ^* \: D6 _& U x9 O+ L0 A- DAC1->DHR12R1= PData</span><span style="font-style: normal;">;
7 G3 c$ o. F# Z- H; a. q% j d7 V$ W - ) U' j$ R, V$ l7 R3 u
- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);
7 y3 Q1 V9 @& z) p9 g - : W' n- F: ?. M( P
- HAL_Delay(5);5 O( J9 i4 L% r+ w- n
- ' t1 b# D" f8 `- q' h2 y# J C
- 2 E9 }4 D1 i0 ^$ U
. b+ |/ K! f9 h- Dac_data = DAC1->DOR1;//for debug5 g6 o' p; j/ M' |
- 9 r4 q& B+ V6 ]5 U% K5 m1 W! S
- + I" Q# \7 ]! C; l9 R/ A- N+ n1 Q
" _" v' J- c3 t" t* a- } g! |8 C! `0 @3 q- V- {
6 e5 C" C) K/ y0 j1 a6 d- }! b4 s2 u2 J g, Z
- </span></span>
复制代码 ' w# j( e+ Y" I5 L& P' n: _
总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。
7 b+ W1 E6 S1 H X: b& c r3 S4 r: p/ \% V. D F9 K+ t
6 M" Q2 {! F, [3 a+ v% X6 S
8 _9 v0 i) `. `+ h% o好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】* s2 D* Y* p7 g
3 k8 a5 B$ I1 Y
* C, p4 f t4 n1 @- R- f A |