有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。
- }& n" V# \, u$ J3 j1 H c
- I+ j+ }8 ^( S4 V% i! H
# t, d/ d8 D$ r% a! i. u- g
- R, l- d5 Z- \4 o: [, H9 u! H! O$ |" Z
结合STM32G4芯片的特性,这里可以有两种实现方式。. v& Z5 s' Y" K3 a6 \1 `9 o9 Z3 G0 D
2 {" \/ i6 q( z
第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。& Q8 X& @! i2 J# x0 G
$ @5 J7 B9 t1 h2 E. W8 C& E' T* l我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。
0 S2 G8 A% c+ s) B$ \! j
# D% v% C5 z: [" U6 v9 t1 l2 A' \使用CubeMx进行配置,主要配置如下:
# c# x: Q6 \3 H$ p' V
; e1 w/ N" g$ M9 @5 U `: q
( f5 |; w* v1 ~$ H+ P9 I# o* E' C4 D1 _1 r j9 A
6 Q) y7 s. [2 d7 q+ Q# z( Y* {$ s* T9 G7 j5 H
( w4 q7 T2 l: `, ?0 b! B/ _) n8 v o% Y3 T2 n' A! F, H
完成配置后生成初始代码,再添加下面代码即可验证测试: t! q( d- i2 k$ v0 z
: I7 X! B) M/ x2 M
- #define Tpai (2*3.14159)9 \% O; @/ `( C$ p* N
- ' [4 x! S. e( T
- uint32_t PData[200],Dac_data;3 p. x7 ^" f, s& |5 ~
/ U/ L& Q+ E% H% N- uint16_t i;. m. s6 L& }2 ^# F
- 9 f7 [9 R% z" i2 h( ?/ L8 u
- for (i=0;i<200;i++)1 e t2 n) S/ T
- 8 G5 h: T# c/ S3 C; k7 m0 O$ n# ~) u
- {undefined
: i! C; S5 k+ |7 e. ^
, x4 f7 t6 G1 W% z: Y- ?- V- PData<i>=</i>
1 O, f& P# P2 D# Y! b - $ d0 h' ?& p7 h* D
- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);6 A( M6 l" y! f( y# d
: B- i9 s) N0 E1 Q- m# C! ~- } //prepare data for DAC, _% ?9 W, J' _0 a. e# W$ H
- 4 u! a1 ]: W( H6 l% Q5 K) I- h
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);
. Q4 c4 W+ |# I+ a: D - 6 n% |, H2 `$ L5 B" u9 T
- __HAL_TIM_ENABLE(&htim3);
复制代码 0 z L+ Q; Y; L$ @; U {2 h2 |
; C0 G; |( ], X& v b6 h$ _9 R" f: }
上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。
% u% q3 @0 N% N3 a, T
3 L. N/ k6 p* M( `5 c3 }- c6 \
) D0 k2 {. B" Q% u1 B+ t* Y
' r: f: O1 x; e% E$ ]5 R其它配置不动,代码稍微改动和整理下即可。参见下面代码:
4 V1 N4 G- {5 K0 M/ o# O6 f6 \9 ]" x2 I F) @
- Uint16_t PData[200],Dac_data;: l& s# g9 R0 H/ o+ B) K% m, C
- ; m1 g* A% d' W+ E/ ]
- #define Tpai (2*3.14159)
0 t0 {. |( E- ^' ^. D' L0 }
" k" o) e9 I: \# F, l- uint16_t i;
" j/ P: o4 v8 M
) j- }0 p o) K- for (i=0;i<200;i++)0 \4 c9 r# o% k; ]1 a/ ?+ O
( ^6 O% r6 h- F# z. M; @; y- {undefined
$ J+ ?! q% L' j9 e5 |( s - " q3 Z9 f( [# @7 W
- PData=6 [3 ^2 Z8 l" F2 I
- ; u9 \+ `2 O. m. m3 _
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
# T, z/ k9 _$ ?" w
# C7 @% {' F( D' h x7 m, X- } //prepare data for DAC r2 G( s! I& m
+ o" E+ b4 K( P J' y2 [9 h- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);
4 b8 [/ ]" D0 I7 N) [ g - ! f1 B- |1 B) a4 }% X- m$ @
- 2 x( d# h, _' y8 h f
1 l( w# t. F U, p3 T- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
9 S) B- N$ b2 b6 m - 9 C+ f0 E+ S8 ?# H8 L# f" `/ P% F: R- Y
- HAL_Delay(2);
6 @4 w# A5 W5 e; U
2 @- b* J/ X. K2 r8 x6 R( g& q5 W
# z0 {5 E/ U9 y* b- M
6 O5 k/ G) j! b( T- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);1 U/ X. n5 s! ]5 ?$ H- p
- + J: e# S. }2 I7 v. B* Y
- + z) J+ V0 t, P" u$ z1 ^
- % l$ `4 C1 `1 o$ U) P; C
- __HAL_TIM_ENABLE(&htim3);
复制代码 - R! Y/ j4 H5 S. e& z1 Y
- h. H" W8 c$ f
E9 c, D2 P/ O$ I" P6 b& C5 F# e* X
第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。
/ E1 g1 `7 q, u7 ]+ O) w+ e- P' c' y/ {! S2 r; j
基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。. }1 Q8 A; k: [" j% E @' T' }
) x- x, S7 F2 U: N
* U' q. k0 ^4 n7 \
1 p0 i. ~' g/ G$ m- g) e6 L! @9 W* q6 m1 P6 n J" }
主要是DMA请求事件变了,其它配置跟第一种模式基本一样。2 v" E3 u; G0 S0 L
; Q3 ?" k1 ^( J2 r
配置完成后生成初始化代码,再添加下面用户代码:9 |2 @3 Y2 U9 | z3 J! Z
3 n* Q3 g) I" l! x
- #define Tpai (2*3.14159)
9 h; { O5 o7 H) ?
5 S4 { ?! L( n- uint16_t PData[200], Dac_data;
: ^, N6 i$ z+ A' o6 K4 l7 c7 ]
" C" r \0 T; U9 v; n- q" R- uint16_t i;- w9 {6 l0 a9 N/ w; ?( d
' j' o& V: H7 j- for (i=0;i<200;i++)7 S2 P/ |# H7 N
" R$ s+ z- m }- E |- {undefined
# `2 h" n. C" U, _1 f9 W
/ d6 q5 m- W9 ~" ]8 e( ?: V; D- PData=. {2 g8 k6 h% f* |5 ~
. f1 N' H2 l$ [& I0 v5 _6 X; C- B- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);& O P$ o( r4 r, l- i1 a f
- : A' n% Y: ]9 i w! f' x) e: S
- }3 P8 {$ ~" D, P8 ~: [. L
- e6 c s" \" T& }* K# d' ^
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
8 i i* g. f: A2 X4 h" W" s/ T& J - $ c% X' `. u( J y, W
- HAL_Delay(3);
" ^2 z1 L8 H$ Y+ M1 X, n
3 Y- ^; m E, h7 g: F2 e4 E- * y- g/ k0 f9 B9 a
- / S9 O7 g V! C e# m/ Q8 Q
- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
1 D4 _6 C( P2 g S
( D6 ]4 {% S. V S& \$ P ^' F- ! s+ t; N. j! F) j/ j/ ?0 z
- # m0 ]" u5 a6 b: \3 k" v* G1 Q- i6 p
- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);9 E4 D2 q* N8 m; a4 X
9 Y8 a. b- T/ |8 C; |* F. D- __HAL_TIM_ENABLE(&htim3);
复制代码
; H, h" q1 l8 H# t/ y% ^" T$ O5 n- x4 S( K& R$ k; {" L" x
这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。
0 e6 v1 Z' m- C; u; V* T' f. u: l( q$ [ K
如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:
; ]# m0 m q0 U; {- f: t: b) O. h3 U& v- p
& u5 o5 y- T& S* p f( u
" B1 A |) E& B( R
' I$ o8 E1 d! C5 a$ t' G相关用户代码如下:
# R6 T5 O+ ]: t6 P* q+ C; T, `5 \ G4 Y9 ?3 f& J
- #define Tpai (2*3.14159)
% N1 p9 p4 S- K3 I0 b- C3 ?* { - ( e" G0 L, g6 ~: }
- uint16_t PData[200],Dac_data;* s* A# F( J% m3 |- c# O
- ; A) x, y _0 F
- uint16_t i; X/ ^% u5 |# \3 w" h' z
- 8 R( M( W; l7 S. |4 _* o
- for (i=0;i<200;i++)
- W- M6 M7 r0 B) ?% ^% c - 2 o7 G: g$ m, A& l
- {undefined
8 P" U& K1 [; e - ( m5 O# i! ~- c0 `# H
- PData<span style="font-style: italic;"><span style="font-style: normal;">=6 @" h" B! U7 Z! Z
- d; X t8 r8 T* T: o& ~4 L- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);0 D8 ^$ _# S* \
E8 {: F1 A t% z% C; X5 m6 W" X- }
' f* D4 s- z1 b
# |8 X& e$ r' P% e/ d3 \3 U- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
V- {7 k6 E# q
: |( D ? E3 v- HAL_Delay(3);
5 b/ Z* @+ Y% E6 Z
! a5 _+ _# K) L) o8 Z
6 V4 E9 q+ k! C. R
) d0 F1 H" N& z+ e- while(1)4 N! ]* v' e* G# o$ A2 S4 W
$ M& h( i0 {# n# c% _5 ?" B- {undefined' P1 V! l8 j3 @5 f
- 7 s+ F3 M2 c' A
- for(i=0;i<200;i++)7 c5 E- J4 \3 ?" {' E
- % s) } B n3 c% c; t
- {undefined& ^! c% X9 D* [- q6 i' X- W5 E1 u @
- & ^ @6 x" T) n5 U4 Y( W, E
- DAC1->DHR12R1= PData</span><span style="font-style: normal;">; ' s: @% \0 z. L t
- ! C: y2 Z3 O2 a" J. x
- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);
* I4 N6 P' f# \9 u: z5 \0 V7 y4 G
+ @: [' l4 |* ~- HAL_Delay(5);
) \; C5 x. d) Z5 t: y
7 [5 m* p. H" X. T9 t
: |, j+ t( S. ^9 `( K- . j6 |9 z) C1 k4 y; Y4 O7 M+ q) `
- Dac_data = DAC1->DOR1;//for debug8 k" U$ R/ y( G1 K4 n8 E8 m4 X
) U, V! u- |2 F- ! ?9 Y" p1 g% J6 H
- E/ O. a; @3 T7 W' [+ r, k
- } + J y( Z' `, e- w3 k
- % @6 O, I2 F- n" h) y2 f
- }
# M1 ^) b* R8 b; [* n - </span></span>
复制代码 0 ]! ^' I7 w K0 T" e3 e
总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。. h; Q7 z2 t! N( r: D; H' H
& l* T% r1 q" d ^
r( R6 B& c9 L; p
8 h& `/ [ |- s3 z% ]好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】$ X* C8 a" y2 k( ^
# C$ _4 c: D$ A# z7 W
+ \1 Q6 K9 M, ~" y2 [ |