有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。
) I' U% a& E: l4 o& b; D% b/ k4 G3 x( w. k
6 `: s3 {6 U' z& F0 r* b& r
' E0 p6 N5 g7 ?, g, M6 [5 x8 `7 t A' l5 m) l
结合STM32G4芯片的特性,这里可以有两种实现方式。5 Z! p) t5 K, ~7 F1 N6 z" E
/ c3 ~- @7 c2 h6 v' O1 u' h& l" L5 k
第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。
# Z. f& Y9 g; O% {; i
. C. R) E) I! x, e3 z4 B8 X我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。
) A' M7 u1 X4 h" S3 D
% V+ y( R: P# `" b7 ` t使用CubeMx进行配置,主要配置如下:
' e1 U& x: E+ }5 C6 K0 N0 \2 {; x! u* H2 P3 [* U
( X; ]( U9 M4 L
. @) S8 K6 G# `" j4 Y/ H
7 ?- t8 u- F) w: t# G* G6 X1 k: i
5 N) c, ^" K0 A7 k$ y; r" f7 k
- }0 f7 I1 n5 Z2 D% \
% O0 H; B" d& c* M完成配置后生成初始代码,再添加下面代码即可验证测试:: {0 F( f$ j" ~
, ?$ p6 ~6 s/ a t# \" u5 g+ S
- #define Tpai (2*3.14159)
4 ~# l8 ~* c$ K- Q: I: Z7 L8 l M( \ - & p j! P$ \% L1 P5 X3 K) x! s
- uint32_t PData[200],Dac_data;
* M$ ~/ [$ o+ O' w3 y, a- ^" _
" }4 i- S* A- _& s- uint16_t i;
! }" j& v- K$ A6 K& ^) p9 W - 7 ]/ A" ]5 Q! E
- for (i=0;i<200;i++)3 X, s, |5 m! [& X
1 H9 k7 a/ c9 x9 H2 B4 E, d- {undefined
6 h+ E4 f4 {* |/ B - 9 ~+ s5 x- j1 h* |3 O
- PData<i>=</i> A: M; Z7 s o+ W8 p9 A8 ]* z
$ O& d- C* X% Y, C- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);
) r$ }' C1 v' {% }* c5 Q - " d8 b& i& ]8 _1 X w
- } //prepare data for DAC
- }. i% ~; |8 v! w* p& B
0 _! O- O! @6 h( Y# |- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);1 {& t- ^% y. c3 O+ Q4 x
- 9 O3 b) W! Y% n
- __HAL_TIM_ENABLE(&htim3);
复制代码
) V7 j' p- c/ ]2 Z6 [5 L5 I- e) R- @8 I
: q( H$ ]; j. H. @上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。
+ o% n8 S2 n5 C& y- N; g" n, @" B% V( Y5 n: J8 O) d8 a" \; x, Q
" V9 |5 G t A/ a8 K9 h
& n2 y% y" F6 R, B( v r8 O7 c1 P其它配置不动,代码稍微改动和整理下即可。参见下面代码:
" e9 Z: W, u& m
: |6 e6 p8 C; L Q8 z% Z- Uint16_t PData[200],Dac_data;2 W3 @8 E& f7 ~2 [4 `
- h) R- |; u/ [% J1 Z* J- #define Tpai (2*3.14159)
3 J* V* c9 d! v - ( @! q) t8 o" m0 g6 t
- uint16_t i;
$ `; W+ b7 |6 L( h3 M5 `* g/ |
1 M- a: s* B# L' _- k- for (i=0;i<200;i++): p6 w- v: t4 |: |$ I5 I) ?' D; d
/ ~+ \8 ~3 }! M* |! Q6 t& k- {undefined
1 X3 \9 g0 e1 ]7 D1 O - / J+ a5 D- H, r/ J$ }6 Y. S" k
- PData= w& W6 O3 a7 h6 g
& d* G5 V& e2 x$ q+ ~ Z- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
& _4 A1 a% L; p3 i - 6 A9 m9 M" i+ c- v* j% H" [5 {
- } //prepare data for DAC
/ ~! S$ _5 d/ w3 A, l. l
M4 K' r) h2 u- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);: B& N. `0 C% V! `5 z) A0 w2 y
g) ^, h& Y/ f2 V" y
4 Q- Q" {8 d; f+ \7 I4 M- : t: x3 ^9 p. q# ?6 V
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
+ ?* I- ^. i( A
e$ |/ ^) g. r* r! O% K ?. U, j- HAL_Delay(2);
- p1 O+ l9 J$ Y: I; y" C1 ^0 L' D' O - 1 p. {( I9 [$ E. h1 M8 A5 p! Z
# ^ u( G' _0 |! D8 t+ B$ z- % v8 d8 M, L6 ~* }% X! }
- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);% \6 G- ` @+ V' I* Q! [0 q
/ i* O, ^# B4 F& B4 W- 0 L: @; F4 ?/ E. ?$ o
5 G4 c# o9 N8 p4 A- __HAL_TIM_ENABLE(&htim3);
复制代码 , u: c- Q: P+ B0 h$ h# R8 T
8 G ]! Y9 R; K/ v( L/ C c' q
8 _; W, ?; b W5 ~, e6 T, ^
1 N7 A0 g4 p& o7 @& S! E* {
第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。9 d9 y' K1 n5 W# C* L
( @4 l; ]; C0 a* L* X# P, K
基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。
0 u) W2 z* Y, I# F8 C! e
. l$ V4 G' s3 Y2 f% j
0 p5 w- a6 q, H5 p) ?9 K- }, U7 i5 L; g8 m" m
; G- h9 @6 {# m* Z2 A
主要是DMA请求事件变了,其它配置跟第一种模式基本一样。2 K0 l: a) p! a8 o n# s0 p( K
[2 g$ H, m w配置完成后生成初始化代码,再添加下面用户代码:
* n% @% q& w n# _$ H9 C3 |" c3 G N7 S! G8 _0 w
- #define Tpai (2*3.14159)1 X7 [, j) A# Z. w# q& N
' B1 i+ T8 {! Q4 Y3 E- uint16_t PData[200], Dac_data;6 P. G( P- V+ Y2 N( |5 ^/ x
, w3 C. N1 c `0 f( s$ \7 c- uint16_t i;
! C2 ?& T$ Z; D2 e; z0 e( r7 D - 8 M' ?/ F0 }! m1 y
- for (i=0;i<200;i++)8 f% `3 U0 D6 q, B4 p
- $ K9 }8 M: g+ q* Q
- {undefined
7 {) ?: O2 }- n, B8 \' u8 b - ! R) B* l8 P/ j Z8 p
- PData=
$ R( N! s( @. n6 L- j2 R - : u. s$ W* U% @! w5 l& H2 k) b
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
' V0 L* m* y+ B& p* x1 n# @
8 X" z& c& M4 g6 n$ X- }1 A) F5 |% _! ]2 T2 p4 p9 K) M
- " @3 m. ~% ?; Z! w q& L6 I
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );% b0 |) e4 Y! ^: \) A0 k; I
- + w9 N) K; I( X' L, P
- HAL_Delay(3);
2 U( T. h) L* q- S+ G - % a) ?# A6 N; T% {4 t
. F( J5 Y7 M9 J4 A- Y( f# W6 P( N- a' E/ B
- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);, a" ?/ u6 n. _
, w& u" W% k8 J- {" ]
' H/ l6 t1 b& [9 s, f% d5 t
- W9 d8 s* w# ~( d# }- u8 r, ?- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);: T; w: ~3 p6 c, B* v9 G
- 7 F o3 ]: _8 m8 g1 c4 \ L) U
- __HAL_TIM_ENABLE(&htim3);
复制代码 $ I* X" d G r
6 g- S1 g2 f' L; O, ^
这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。
' z2 s) K3 D' {8 I: d+ W0 Q7 c% X9 `! @
如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:
! w6 W- {* F; S8 ^3 z
/ O+ I6 b# S+ [2 o' f
7 Q: V9 n2 z8 `+ a* r% `& y* u% b& u# E- e
/ u1 r3 \, H' L0 S/ h* R# j; V& n
相关用户代码如下: c* b! @3 ^& o* b1 r' O% O
: E4 m1 y8 j, m% E' Z- #define Tpai (2*3.14159)
# V' W( u; O$ I7 d& K# D( X% n# Z
* h$ ] @# \: V h' z; x- uint16_t PData[200],Dac_data;
! c- h7 r. j( U0 E9 r - L9 V4 i# j3 l
- uint16_t i;
0 h- S8 ~3 t( _8 o: M6 L - ( ~0 I/ M' t- W
- for (i=0;i<200;i++)- w7 | x" K& a* m
$ T- W2 [' K2 h- {undefined: g. j; H+ N5 b, x9 @
- 5 a& w3 \' P2 M; j$ W
- PData<span style="font-style: italic;"><span style="font-style: normal;">=& R+ J' l3 z: ]; D
/ z; m' m5 U: [# _) s9 { ?& t- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);
. W$ Q& y" [- F; B4 t - # } I& u: B: w) |7 e$ ~7 }9 A, w7 Q
- }0 f9 i5 H: a$ p) y( f) G& e/ b, v
# U. H7 V" n8 Z4 W% i; K! x; i- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );7 K# ~. I/ i5 Y. v3 }
- ! w3 M# @: { l% L/ g9 h* N0 \
- HAL_Delay(3);1 |# Q/ r8 }. P, b \; l$ Z7 H
4 O& \ V' ]( ~
9 q" Y( [: C' i
3 |% w' N+ _9 y2 m% b6 g M- while(1)
; w; \ e* }" ?* w8 {
6 A; F' L' e9 y" y- {undefined
# N3 r G: ]9 t# h1 i! T - 0 P, W# ^9 ^; ?5 v
- for(i=0;i<200;i++)/ `3 v) g% r- a" X
6 s% o6 @8 p0 `- {undefined! q, z8 |' X6 m6 f; j/ _) z' u
4 d' c0 h/ v- n N* ]8 w! k+ B- DAC1->DHR12R1= PData</span><span style="font-style: normal;">;
- ]6 |; [% V( e$ ^* A7 K - 6 r+ W5 r. ^: t; I7 p+ M+ y
- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);
8 ~$ l1 @, V3 m/ `5 I - D) G# |- }: Y7 s6 p- h
- HAL_Delay(5);/ \ h: Y0 p5 j- R) U, F& l
! ]8 |3 G% n. ?8 L! Q/ _ S
' \- V5 ^" r% `* c+ e+ [5 M
& M+ N( t2 F, |6 h) N7 p- Dac_data = DAC1->DOR1;//for debug
7 m, |1 Z4 q! |0 y' z. e5 t+ w
+ d) y7 I- G o) W2 ?6 P- & E; \" s( A, L
- # P" P( h! Y. b0 w+ F' S* T
- } 1 X8 _" L3 x: Q
3 s$ G, P3 g. M7 N0 K& ?$ T- }( f$ g, Q9 o, d& R/ p
- </span></span>
复制代码 $ i3 G( k8 _8 G8 v, @) N' c
总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。0 @5 ^+ ]) A! g
" M+ ?& `6 M& W% M6 Q- y
4 n0 T6 H* w, h. o! S" V4 S' r$ h" R* a
好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】0 N) G$ ], [* E
! [1 m8 @3 s+ n' @& r- j# _4 O5 a' n
2 ~1 X% g) J5 L( S/ F2 V |