有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。( _- T* }3 I J5 D
# ]# ?! W% i+ K- C d
8 K' S- ~! i' C
; f. O. t0 k: b2 E# T1 u. W, R2 N* x& |8 K {) w
结合STM32G4芯片的特性,这里可以有两种实现方式。
O6 J. ]) }& \$ B; t" h
( F' l( M5 ]' m" p第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。
1 R& T# y3 @ O! g. l4 R
' L! L* U! h6 [, w( j* Z% b我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。
$ P G0 M7 K3 ^" \; X5 D
, m G% G( m4 M/ V- ~" j使用CubeMx进行配置,主要配置如下:) m! U, p0 J; k9 ^2 r- F1 {2 d& z8 B
6 Q: d5 W9 d: p2 w8 H$ M! ^8 V5 P# G- p4 F( ?4 H- Y0 N! W
3 B2 K4 S) o3 F8 `: Z: S j1 P: Y& @; l; j a
" l/ L; L3 f7 }
$ M- V# h2 x5 H* U w' v; p' q" Z& B# v# z. w3 b1 U' P2 `& S" c
完成配置后生成初始代码,再添加下面代码即可验证测试:$ O. s% _( ?( ?! {: K/ j5 E w
& \# t% O$ r3 D' w4 T# X; U2 Y
- #define Tpai (2*3.14159)+ [, X" z, g# }" c" w
- 6 m4 n; v1 i4 C+ C, E
- uint32_t PData[200],Dac_data;
) t4 c6 S, N) ~ L; r6 D, [ - % X# @' W9 T6 T' `+ c
- uint16_t i;
8 {+ ]2 ?6 c( C h- u9 y( F1 F: N - 9 y- e' f+ o+ d
- for (i=0;i<200;i++)
0 D/ y6 [, |% Y! O - ( @4 p( Q: z/ E3 o Q
- {undefined
2 h2 c- }, R1 Q+ o8 P I' K* m7 U: v
( C7 c8 p' | T6 O* c! }- PData<i>=</i>
D: G* m* x9 r$ e" V% w - 2 X% _8 B- f8 `7 l5 G' g9 @; v
- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);
9 f" j5 y! h* J$ L - * P! V3 n& [( B2 i1 x
- } //prepare data for DAC5 U9 ]: W. Q. u2 a, J
) x( A4 r2 M- N5 x- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);
5 {5 A+ o) Q- L7 @% N - ' H6 J6 Q* r7 ?; i' }1 t6 g
- __HAL_TIM_ENABLE(&htim3);
复制代码
3 S0 O r& [ {& K* _9 Q7 B* c2 R) _7 f2 _
* l( b1 _. h6 x S1 W上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。
0 U$ U( L4 [2 X5 |
! M6 A1 h+ U: o' b M4 w: K K+ ^$ q9 u5 }" j$ l2 f
X! P7 `6 F# i# y' T: r
其它配置不动,代码稍微改动和整理下即可。参见下面代码:) z0 x9 g4 h* q1 U3 [+ S+ O
. d9 @& {, n0 E1 y) o
- Uint16_t PData[200],Dac_data;& W7 e8 D+ Q6 @
- ! B4 ^2 d5 B2 I' o3 l5 T
- #define Tpai (2*3.14159)7 D3 h% G$ g$ A0 V
- " B8 K) W) A, c: V& b a( y
- uint16_t i;
4 {7 T' a( X) S& @
( W1 T4 J0 C2 Y* s( n$ Z+ c) z5 h( U- for (i=0;i<200;i++)9 V0 x* H5 r& f6 [# ^0 P _ \
- 5 Y6 Y3 g$ P8 e }
- {undefined
1 u2 _! w! t6 ]. u h. Y# B8 Q
5 ^& H# ^3 L2 \. s- PData=
4 @: m! y, p. U' ^
q1 }- H+ d% s9 d% I- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);; ~* h; G( q* f$ [$ o0 v) g$ M) ^
- # u8 n$ X1 D% k1 H8 E
- } //prepare data for DAC: x: x( F6 c* u# E
: J! `( M7 d2 k, A- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);
- p3 N& j' d! O; ?" h' S* S6 E: E/ B0 j1 L - 1 U3 p7 L( U2 U8 _0 M- v0 n; V
8 g% ^/ \3 O2 L) `' {9 K3 N
( N# q D" c7 j* y4 s' q3 s- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );4 I5 ]. ?' R; ]! s
- ! a+ h8 ]; D* X. ^, w9 `# y
- HAL_Delay(2);1 ], c0 X4 Y* t% K
- ) W& Z; }. Q: o R+ @ @& j
- ' k1 y3 V5 w! P9 x' M# Q
o" {% S: J( N/ n% s% y6 \6 a- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);# G8 c% Z9 u" I% @1 u! ]- ]/ [* U3 j
- 8 ~) _# s u; F: e) P
- . D0 V" _- j, [$ Q, V
3 U, e ?* O- W7 u, l" X9 u- __HAL_TIM_ENABLE(&htim3);
复制代码
w- {! k+ k1 a9 M9 M
& k f o0 b3 }0 m% |9 u, g% B- I% j* h. e3 ]6 L. L: Y
: F: F9 D' b. N4 f9 y+ K* f第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。
, q6 p1 X( L& G9 ]
6 k9 M/ |, K) j& T* y1 `$ O基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。* W$ V$ A6 J+ P- F% S% N i, [
7 l# m1 P; T6 f6 H2 _" Q7 l+ I
# x! n; t1 {7 ? J, I' F' u& A1 M
5 L; l6 r6 Z) a& q- {
" S. y5 v1 z( H+ K
主要是DMA请求事件变了,其它配置跟第一种模式基本一样。# X2 N% N) z' N- O
4 w/ l% u5 {* l4 c+ p3 w配置完成后生成初始化代码,再添加下面用户代码:9 x1 q2 h% w6 v/ K. B" k" L0 _( p
7 `/ Y3 ?* x, r" d# w- #define Tpai (2*3.14159)" Q# [, z* s. i4 K
( M" J: C8 c& Y% Y- uint16_t PData[200], Dac_data;. ^- S& v/ Y ]% h5 H$ z5 z/ [
' I* Q' u+ I/ U2 E7 T- uint16_t i;8 S3 w2 H d4 N. k9 |" y8 N
- , o5 A E% M: ^9 ~. X4 D2 L. r
- for (i=0;i<200;i++)
1 |* }( c0 H5 j4 t9 h. | - + _8 _1 p2 k; _7 M& j
- {undefined
; X9 |$ P3 e( |# Z' x9 Y% m( R
8 {# v6 `! u7 [9 a% Z% I$ Q/ W- PData=
2 k- j! h3 H( W2 |5 { - 4 q) E( O7 S( ]6 k* o
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);& r1 ?$ n) z8 X9 Q; j+ L
- . K+ ^! |8 ^# i. E& o* s
- }
8 |4 O3 B' `$ T6 V - 5 [$ X* w8 |7 w' M, A& P
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );8 _ q% S J9 [* H9 h7 C; J
- , n; a3 |- J: Q+ ]. U, ]5 T* _* M
- HAL_Delay(3);
+ H. W" ?* k. a( L7 m
- Y7 z: J: A/ @8 i5 G
8 V) t% ~( _0 Y/ J8 J4 E! K
" j9 ] V" w6 T9 ]3 ~- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);- k+ G1 P( n6 a5 m1 F% Q5 o
1 g6 Y6 _8 k( ^* k! H2 A8 e& T0 {/ L- 3 r u; L1 T/ A: }8 S
- + J3 q4 N$ o t+ a
- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);
8 m9 j: F, a# f$ \2 V+ ?) i( C
* l& F5 _5 C+ y/ [- g! z4 X- __HAL_TIM_ENABLE(&htim3);
复制代码
5 L% B3 i3 K' X2 T8 z2 p' p# G. _0 p
这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。- x0 r1 t \ J& `/ ^" S
+ q' k" N. D/ D9 w& T% _
如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:0 [5 I% S g/ J
6 U1 [1 b1 D7 O; o* D0 s3 }8 T( a# a- |. H& J, N; n7 L8 c3 A
9 ?* r8 |" d) T
/ o( ]7 \& }. a/ W! I相关用户代码如下: 9 C/ I; W0 _5 ~ F% ^9 G8 G
2 W, E p/ e& u3 B+ a. k6 s- #define Tpai (2*3.14159)8 k$ t* W0 c1 e8 a* x4 ^
, w8 Z# P# O- i0 R9 L- uint16_t PData[200],Dac_data;6 z3 t# \8 ?/ m7 I
! {0 Y2 b8 k6 D- uint16_t i;: z4 D) `( g# l y
- 3 U1 y- P3 R. U# t: K
- for (i=0;i<200;i++)& B( `+ x( G8 a) t) m5 l
6 [6 k4 Z) o" Z9 k+ L( e* K) Z- {undefined% F3 |& q5 j7 |4 E( l
- * W/ n8 s; y8 J: p& T8 j! X o
- PData<span style="font-style: italic;"><span style="font-style: normal;">=
( ?' k+ w$ z5 D! c- Y$ k$ l* c
8 r% Q! S/ f! _- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);2 i" U: F5 L# U6 L
+ O( ?3 N4 e4 C. B- }$ A4 r* y) K1 j; a" O! E
. Y; y4 B8 ?8 Z) e1 r- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );% w- c. D0 \0 D" ~ f0 a6 L# C6 Y
3 L6 h: R$ C# z- HAL_Delay(3);
; ^$ ?1 S h" p+ T' p
% e+ F1 T$ i" [. ~. o- w0 Q3 A
) `& I0 a4 b! A% g- a, Q
6 |3 f9 `( V+ a1 o% g* V- while(1)
) X0 Q- Z: g! C9 N4 j/ |% D% a
* y% M5 S2 ^4 _2 r3 e2 g5 t- {undefined
$ T: y! |- l. z5 U2 B - - N' b1 E! H& y
- for(i=0;i<200;i++)# {0 g" M0 ?2 [2 [3 e: \3 e
- ; d3 P. C) L H* G, Y6 K( c6 p1 r: U
- {undefined! D$ k/ }. D8 g, j
2 Z* W- j% G7 u- DAC1->DHR12R1= PData</span><span style="font-style: normal;">;
& F. i) ~3 D: w: Y) c - ' H$ G6 Y2 _& E: U4 t
- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);4 y* w# C$ K. |8 N( [
; l% t, ?/ q. T6 c- HAL_Delay(5);+ h6 r, F0 @- M k; [9 u% ^4 s
- 3 f/ V$ B& E* d# f
- 9 Y6 |' u6 n/ T; z/ H" n( R
- 5 h' d; V* v* S$ j9 k3 Q9 o
- Dac_data = DAC1->DOR1;//for debug9 x3 a5 L; t1 M; g3 p. S
$ V$ ~8 b# y, ?9 M3 {6 N
4 B( Y9 I/ z9 A% f' z5 A- ; P8 A4 M* s4 D- F
- } , Q+ }8 t: R O3 f/ t0 p9 K, Z4 h3 C
- 9 m& r% V2 G6 t1 G9 l" I; c
- }
/ V* J# u% w# v% \% |5 X$ ^ - </span></span>
复制代码
; ~$ [% d7 \$ \0 P总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。" t9 W1 _2 F( H! c0 i3 r. j
; c3 F+ p% o6 n
* c. q2 P% ] X
1 r' [ j% p+ ]4 Z& @ f4 b* u, @好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】
5 r2 i y7 g J2 i5 `0 N0 S. R- f0 n6 w: t, x9 X' @6 b
, p% L2 V3 T% H% F u. y2 m" u
|