有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。 q" b! Z% A# |
( Z+ M: I" S) }# B s
' W: u8 p9 g7 E/ e/ d; B
$ `( F3 z/ V4 O. _* O: V6 Q: o! j2 o: _) C0 N6 Z& V0 e$ _
结合STM32G4芯片的特性,这里可以有两种实现方式。
5 Z; }9 A9 T p, @) X# l( n" }* p8 Q$ a- [0 l! ^, l7 i
第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。$ C& H; y5 w6 \9 L/ i2 y
) C5 F3 N/ U8 ~我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。
7 k( w/ U9 }1 |' ~' k+ J' U. h; {! b6 c
使用CubeMx进行配置,主要配置如下:5 u$ Y5 E0 z8 Q: K
; H9 M3 ^) a! k8 ?
% J \; R& r% l& H+ Q4 q8 ^& p2 | ?- k. j" Q& D- _+ ]
; A: ? P2 w: Q1 K% ^6 }
: l. c- b! z8 T% [* r
; R' M" {& `1 L$ L# L1 ~4 Y. h- C
完成配置后生成初始代码,再添加下面代码即可验证测试:; @/ ?! g/ O$ v7 a/ E q
- F: }- |; z7 H1 `) `0 N% ~% c1 M
- #define Tpai (2*3.14159)
' F3 W+ d. C% n2 t - 5 U9 q; n. R5 n/ D* a
- uint32_t PData[200],Dac_data;
/ _% W) y7 y& I4 ^ - @. q, G' T; ^0 B' L
- uint16_t i;1 v1 {- z' N* L! I9 \* z
- * v2 z8 ], ~$ M0 [! [9 z
- for (i=0;i<200;i++)
1 m" J' u, f) y. o c+ K) J% G
. x! ^9 S' X2 g: w" Q: O- {undefined/ c7 M' l: W1 G/ O8 `* l' k8 O
- ! A# w- h/ C% N, K
- PData<i>=</i>
/ ?: D* e) x* O1 A ?0 Y
% C. G6 J- f6 r2 C5 _* x# Y- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);/ {2 ~: s, ?, s/ x' S4 E
- d0 d1 G7 j! w" d( E- } //prepare data for DAC
8 q+ ?$ h* ~' T" I4 j) a+ w/ _7 d - 5 B6 f5 w9 G% U3 A* \: J
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);2 w; d5 z- f2 @' S$ ]& D9 j& o
- , U6 @3 a9 d( ]
- __HAL_TIM_ENABLE(&htim3);
复制代码 8 K6 `2 F* @$ [$ n6 H- d: f
( F+ c J- `. p; t
( P7 ^8 @& ~- L3 |
上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。
" F! u0 A/ ^0 o v
6 z/ |' `) i' D, y8 s
- M* P, m( E, a, S( I `- Y# y! U" B) }; E8 ?$ B+ c
其它配置不动,代码稍微改动和整理下即可。参见下面代码:
4 T. _" K, r1 i0 V- X
" d4 h7 H: A' a+ @. K' b2 V- Uint16_t PData[200],Dac_data;
6 O3 j' ]6 @/ q/ k# C
; @) K" ?3 e; Z+ A5 U1 h) h( S- #define Tpai (2*3.14159)
8 Q* w- `0 {- [% q7 A - ( z& Y9 x$ m( Q' V, P; r
- uint16_t i;7 g3 |+ _( M' l e( D
7 l7 ?; v0 h, \. V9 c2 e- for (i=0;i<200;i++)
# L. M2 }- `4 i$ l - 2 s- O+ n. G, n; ~8 F: Y7 \
- {undefined
U6 }7 M6 [9 P5 q, [1 ^ - p3 y4 \' q7 u4 [! o
- PData=& r8 s. [) P( C; ]9 B5 V. r0 I* p/ Y
- & B, J7 z2 B9 {# a" M0 }
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);1 o2 ~& _1 C; X6 M/ M6 m5 v5 a
* n" Y u- z7 i0 ?5 X- } //prepare data for DAC2 \( f1 `/ }9 Y) c2 H
" f/ A" X* I0 E2 R$ x' a- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);
1 I# n+ x0 L3 G" l; K
0 G# U3 X8 ]; X& y! z% \- ]: e
* M9 k0 b( Y" u0 ]; {( l
. w8 k& m# i, x- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
* Q+ {6 G0 G+ K; K6 S [
+ b2 ~. D) {: W# y' U* h! h8 R- HAL_Delay(2);
4 C A4 O5 x. |
' L6 U2 n6 `2 w/ }& ?7 o. n
0 U. S" p0 j1 G. u
' A, w* [) O# a- m- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
0 \. F$ u* K+ c* b* [9 }) ?8 } - t4 E" G# o! N" u6 Y
- : K' s. n8 a4 L, H6 r8 J6 N4 G
- 9 H: }) L) W$ W& G" F
- __HAL_TIM_ENABLE(&htim3);
复制代码 8 T' s* T7 `# j" ~
, K6 s9 l |! Y4 U
6 ?8 X3 J7 K3 e0 g, e" H
9 ~1 \! u; {/ C; k" q# l8 q b+ f: J第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。3 x3 R- ^& @$ _( p0 N. n& x U
$ u3 X/ m. _! I' E. m
基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。
0 u1 O6 m* h& O6 f3 {+ C5 s1 m
" L- K/ J6 B# D! @ {+ J; a. I" C* k1 S" H) y( g$ T1 [# o
/ g' K n' U/ ]1 }' p) X
' V9 j& \0 J* J9 b5 z+ U1 Y4 e0 _ w主要是DMA请求事件变了,其它配置跟第一种模式基本一样。 s2 g) b, k! ]. _2 f) o5 T8 c6 X
, l/ Z1 w3 e; t8 x }) U6 L4 K# x% v
配置完成后生成初始化代码,再添加下面用户代码:" f5 p" R8 ]2 {) F( [) G
2 \+ `4 h* P+ m% _/ E4 T# C
- #define Tpai (2*3.14159)+ v4 r, v' B# Y
+ V1 K$ D8 m* N7 H3 U1 ^- uint16_t PData[200], Dac_data;
" g, I, |& E, v( m - / @0 q5 Y/ p3 ]4 N" q. X6 K
- uint16_t i;- o( m6 P& i0 L& H1 O
- ' u: k1 O b! b2 [
- for (i=0;i<200;i++). x8 x" f. {; \3 x
- 0 R0 D! W8 z! A2 g+ Z4 f
- {undefined
4 l h' Z9 U# ]4 g
' c$ }0 s: R' p, D0 _, z- PData=( ]( k2 E: v. u0 w
- + x# |* I" Y% H
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
1 v Z6 Q, f) v4 k( r - $ d: H% }' Y: M* E$ v
- }" e7 V8 _6 \8 ~( d+ X
- ( r" x# b+ E4 m- a& i
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
# r; S9 S0 }- W N5 X - , p) S% e, _1 n7 x! |
- HAL_Delay(3);! [# T9 b6 P# m0 f m
( g" \" }+ h0 s2 c7 U8 c' f2 r- : ?/ ?( c' `3 _* I
- ' ?+ D) k/ I( j; ~/ S. T% O- J; ^
- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
+ j7 R4 @3 @! l4 P, v) I; X- O - " b& x1 @7 y- |5 O
8 x8 j: Q/ O# e8 {6 r* H2 i$ S- " x3 C! k( W; n6 h2 o6 t
- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);
* @. B5 X7 _: y; q0 N7 `: w. V4 ~ - 2 L' N% ?4 \& z T& A/ z
- __HAL_TIM_ENABLE(&htim3);
复制代码 * y; v D7 @5 B' D- T
C3 A% A' R( z8 w7 ~5 Y
这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。& R2 F! v/ P6 I6 W* L: j/ l1 M
8 S( @) d7 o2 @/ A9 v
如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:
" D7 ~" _4 G0 {( S" V% x# x; W9 Q% ?' _) z5 Z' M0 z% ]
8 P6 N" e t3 x* w1 C
" c2 o! z" G/ Z8 {) W
; N4 D1 \# K2 G c, _ T相关用户代码如下: 8 _$ U' y2 h3 o0 Q( h0 n) L
0 _. `# ]( c* s' v! `) \1 w- b- #define Tpai (2*3.14159): Y1 v6 |$ L: M0 M. N) a! X
- . ?2 z) m. [* N: `
- uint16_t PData[200],Dac_data;' J. t" @+ V4 X1 a0 f
- , p0 _% p# Q1 C& g# N
- uint16_t i;" M+ E. ]2 B4 c5 S9 e$ i& c
) z+ O" ^1 D4 x# i! U9 D- for (i=0;i<200;i++)/ x% O0 x7 g8 z& h: F
/ x3 K5 b! N6 K) I: [ v/ K3 i- {undefined
8 R# m& |2 U- v% F& C3 l7 v/ i
* G: u w/ _: l& R* T- PData<span style="font-style: italic;"><span style="font-style: normal;">=
, G+ V7 C5 b% ]# i1 T - ( i/ X% p! h ^& i
- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);! r0 P$ J5 M' ~; L$ `+ H9 t& k( g1 r
6 r! r4 N! |- f! ?# S) A% H- }* M' O' j0 U8 a" x' c- \. E
- $ x; F3 B4 t% _. ?! m( x2 o4 A+ o; [/ M
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );+ P* v3 C2 [' q# V( q
8 Q) Y0 `7 X$ l. d0 H% r9 I! }3 a( h1 X- HAL_Delay(3);
/ j; s9 v' j# [4 |9 `( d {3 t2 O4 H
! X8 Z( c- l2 }" o. p8 b- 9 k$ |7 t9 q# w& @9 v
- , X& J4 s! p4 {- V( E+ ^% W+ K6 p
- while(1)* u* V8 J4 L& m( ^9 t9 l
- - P8 j& [ H" U9 k1 U* o
- {undefined$ I. }6 ]7 R# O7 e3 y* v, K! \
- : M# ~5 c% k! v7 B" c* s0 o( j* b$ P, x5 m
- for(i=0;i<200;i++)
# H6 A4 ^- m' |" k5 P' h - ; N% I6 k d5 L( I) t$ B
- {undefined
% ^" v; P$ A' {# D. O4 ^, v
9 K. i& Z' z3 ~+ E" P" D- r* l- DAC1->DHR12R1= PData</span><span style="font-style: normal;">;
1 L; U- v- q- x! G" x - @) \% m$ Z: X
- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);- ?2 E3 f M6 I, d
& O m% u5 p u; T1 m- HAL_Delay(5);) c, N, h; k& T+ u# h1 u
- 1 \! Y- \& M9 f" k- g( j& R8 @
- ( k- s" T6 H9 c# \
- ! |; V* v ^+ O' T# u! p' Y9 l3 U
- Dac_data = DAC1->DOR1;//for debug! z5 A+ x) m. \
- " d7 P- } Q. z" O7 s; m
: I* }$ ]6 n0 h9 D/ a7 U8 u% l- 4 E/ b. V) ~8 c3 R! t9 x: F/ v
- }
/ M8 @$ u9 z9 d - 3 _ V- L7 K' u1 A: e
- }
. E# I8 y e2 j7 S% T6 M: j - </span></span>
复制代码
: Z# R% D6 g) p总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。' g0 i# V6 T' J# n5 U/ p0 G
& V. T9 ^* Y" b3 u: K( `2 s! @1 R' T" O; g* G
. b" H8 B/ P5 [, @: c' R' L5 q
好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】
2 n( Q+ b4 k% k7 @; G+ F
* B, r; r5 S# G) T7 b
5 H V& G; Z4 }* I |