有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。) B! K2 F: _( j2 A# d6 P
3 t5 ^. }8 ^! C- O& {, f
4 r* {( L; [- ^0 I$ ^7 a) ?; b9 L+ |# ~8 K# P, g4 P7 F# l
~& {( K# { g结合STM32G4芯片的特性,这里可以有两种实现方式。& K4 w/ b+ ^' g3 J# X/ G) p, Z! D- ~6 `- y
[& t& |+ b; B第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。
& ^4 M6 x+ i1 Q# o- E, |5 V" P: V- _1 n
我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。% ]6 S& S2 J) j
: q/ [: {. ?6 B$ P9 B; Y) F, D
使用CubeMx进行配置,主要配置如下:
3 j, ?- z* c( q
9 C- T4 b5 S: p ~2 c- a$ {0 R" o4 Z2 A6 v$ X# d* j
* H7 ?2 J& G; y6 X/ }% H+ z5 U
8 L5 ]3 x, {! e
1 |4 a+ N: O# _) ?9 x2 O) L( j$ k
# C$ B* E$ {; n e- s% n$ X4 q0 q" t# |% M+ M% v* t4 {+ O
完成配置后生成初始代码,再添加下面代码即可验证测试:4 \7 E4 A: N, s3 X5 P
1 D @8 W5 P: D1 i$ }7 R' m
- #define Tpai (2*3.14159)
5 Z1 L9 O- E# t* T1 ~ - + p) t6 K* g/ C
- uint32_t PData[200],Dac_data;
) ]( [; \4 O/ M% p7 m4 `0 W - 9 q# j$ `3 ^' l! o
- uint16_t i; Q; X9 [, \/ w9 e% E0 H
( X) U( G! x1 |, v/ F2 J- for (i=0;i<200;i++)
" J3 ?5 w% S' W* p% N: P
# |# }9 B7 B! L3 e# r( V- {undefined+ S- N* T( ~/ c% B8 g) M
- 0 k" \, ]% W c5 j3 [
- PData<i>=</i>
' t! R, s- ~2 k - & |% v; w2 H+ n, r) V
- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);
- e \2 Q Q- L! l - " i6 `( V- x" \! H9 L. E
- } //prepare data for DAC
6 B9 \# Q/ n, h7 | - % C; L4 D) t* c
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);6 M; G1 g, Q2 [3 v1 r
& ]! B7 ]5 k2 ~2 ?- __HAL_TIM_ENABLE(&htim3);
复制代码 ) g8 T: }4 @ O3 H+ n
2 K; ~+ D) p" X, M+ N2 j2 f; r
: V2 C5 A5 m. r9 Z9 m$ S
上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。# c5 ^. P5 ` h# Y8 Z1 t
2 q+ C* L6 I) g' M/ s( ~" }! ]; P
; \0 g( a3 U4 p
其它配置不动,代码稍微改动和整理下即可。参见下面代码:0 A' }5 H8 r- {! W2 M8 c, J
; k, D& U# M$ ~8 k% D) }
- Uint16_t PData[200],Dac_data;
) i; p$ e6 G! T A" }8 A
$ ~6 D1 i; i) H- #define Tpai (2*3.14159)" i, H; B2 {+ h; m3 N
- ) ?, g) x- X7 s0 o' h' G
- uint16_t i;% y) U, g' z9 i8 F% k+ @9 y
. Y' v, v/ x3 U6 _- for (i=0;i<200;i++)
; `: W( m9 d1 A3 j0 W - 2 T6 |" K& p* |+ w _
- {undefined% p0 P" [- K/ F1 v3 A# [* V
4 S) e* P3 z+ I& K- PData=
0 e t! L* f/ _- n0 t - 0 A8 G- W6 W1 M0 v
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
' G) w# `* | b" d2 W' \ - * P- m& T" v. o2 c; x+ M' N
- } //prepare data for DAC" n1 ?( o( V0 V4 Z3 |% E5 `
% V' X. \0 d/ b1 @5 D# s- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);
( S( q% h8 S5 J. l$ J! g
. _5 r3 U2 ^9 [; L2 w
" K3 J% y* Z4 i7 V8 d6 p- # ~3 S+ |/ H' E" `3 x
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );8 L0 K9 G! H& J; M7 V
( Y B5 T% \# m8 c- HAL_Delay(2);
1 Q: }! G+ b) b' w' ] - / D* T4 y8 l4 h! b
- ! l E3 a" l* H! [
- 7 R( o6 W0 {/ V7 Q! S6 ~6 w
- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
3 O, }' p+ k* E0 ~. q; f
( P. i/ T, v# P2 i9 f- 8 E- z2 a, k# |/ L/ `/ ^
- # F' v0 q$ R1 g. D3 z) D3 a8 o
- __HAL_TIM_ENABLE(&htim3);
复制代码 9 m; r' M8 C: S6 Q4 M4 u) U. h
8 B: ]% m+ }7 O+ y) m" Q, X
/ u' }6 `8 w* v, |: l0 i' P( L$ }8 [8 N
第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。; ]' s8 w I* M
% x" o) g& J1 d% b) Z7 g. ~基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。
- p* z2 Z5 Y5 b1 A: |* r$ v G( s/ \+ v9 A3 V! [$ B. Q# V
3 n6 ~- W7 L/ g% Y2 I+ Y
$ Z$ j* q& b% ?+ K* G
& R0 k1 @, m E) v2 ~主要是DMA请求事件变了,其它配置跟第一种模式基本一样。
* \0 A$ S) B1 S, F& N; e# F( H5 D
, d; H( ~5 j- Q5 r& Q p; r$ V配置完成后生成初始化代码,再添加下面用户代码:6 r' U Y3 u/ k7 ?
" U4 b- t( @* A+ R( O- #define Tpai (2*3.14159)4 y5 l' D- A% W5 A$ `
/ U) F* d3 X& Y E3 |- uint16_t PData[200], Dac_data;6 \5 A3 e) U1 i, E& _+ \3 t! ~) o1 ]- Y
- ! B/ b# q# t! E0 W$ o) w/ @
- uint16_t i;
* y) ^# v) \+ [7 V1 b - 3 g9 R8 n+ \ Z0 y
- for (i=0;i<200;i++)
+ i! q1 x* Z0 i6 R1 u9 a6 u% c. U
# @- t. Z$ m: I& E: Y% ~' ]- {undefined* a: i$ w+ E* H& o0 n
- 6 G3 o' k' v% ^1 \, _
- PData=
, b4 ~, i+ I$ n - `( h. h) H* Z1 |/ ?+ ~; v! X
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);2 D% n+ e- m% F9 x9 }
$ I: A8 z& k3 V: }1 J1 G8 R- }1 R/ P' n) M! F% d! y
- 1 k# g3 t. u1 `. e+ q ` v
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );/ q7 n9 E+ W* Q$ Z
- 4 I7 `* x# l/ \: z% P5 B
- HAL_Delay(3);
5 z% c: R# R: j3 l3 \+ K: B2 K
$ d4 B. J* i& j7 [
8 }/ w: O) E) R7 n: ^& V7 H( X$ J- 5 r) V) p9 h- a* B5 P
- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);+ g% y X( ]. s: j$ p
- , H# t& u: H% d1 N# C2 p
7 B0 V& v; N2 B& o5 u. c& L6 j. n3 l- 9 b+ @* j; J: Z, e/ Z& \9 u
- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);( b$ g% O7 `) M3 v# s& z( o, s
- - o' v) V2 B+ [" P
- __HAL_TIM_ENABLE(&htim3);
复制代码
9 L% y* |) l; H! i
) D" y w: j" q这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。
* z% o) c2 d6 c& G; ^, O) e$ |0 Y2 P
% Y9 T ~& x2 P3 G, P如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:7 s2 h, C+ |& z' i* e9 M7 J+ |
# V& B5 z! _9 ]" n9 U5 v8 U
' ~6 Y) Q& v A5 Z- p# e" U
3 k) H/ \! W4 w
: {" f& n# w; ]; K. ]相关用户代码如下:
: b( d# H- {6 p1 F/ B# C3 x4 M
0 O/ ~4 P, }: h; [/ m$ O- #define Tpai (2*3.14159) m* @: S2 U5 ~& `- f' Q( {; M
1 p# e7 R2 F* ~- uint16_t PData[200],Dac_data;; \6 g* o0 L3 M) ]' |, c
' z$ k( W( ~4 M4 a- uint16_t i;
/ h) D1 |, N$ P% o - 7 p4 a1 x* u. @* Q8 B
- for (i=0;i<200;i++)
" {! I/ P% b, |% p( g - 3 A% j/ o6 a6 @* x% z) s& ~
- {undefined) [7 {* a4 @- |+ D5 _7 j
* I+ A- ]: W0 \5 K5 q- PData<span style="font-style: italic;"><span style="font-style: normal;">=- e' H+ I0 a. o! t/ F' K. k
9 W4 U! t6 A) o4 x* W- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);
- t# w8 v9 f) \3 @: t) ]
+ A; J7 u) S& d- }
?4 k( Y& p x4 o# z; | - $ M. p$ d) L6 p
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
" E B. ~# h8 R% O6 ~, `5 c) { - + W! |3 V. t3 `- l7 f- `, @( z( }" `
- HAL_Delay(3);
$ `; A( ~5 ]$ Q9 X; ~# D - ! e5 i) D6 ]0 ]" u1 `
- 9 l/ K8 h" x& @+ F; N
- ( p2 Z/ Z4 g2 {) V2 A
- while(1)
, b1 ~$ m8 n) x* `3 O, ^ [- Q6 E
9 `. x6 e% a' r( ^) B$ b- p% v( p( E7 d0 t- {undefined
3 @2 S8 s0 ^' Y H( r1 W - 1 U1 I" Y0 u) O* K' g
- for(i=0;i<200;i++): |0 \7 a _4 h3 j9 \. P
- 2 u) `0 v) R) ?" F' d) u
- {undefined
9 s( w4 q' k% T5 s9 K
1 K& ~5 s% }; Q; Q. J- DAC1->DHR12R1= PData</span><span style="font-style: normal;">; ' H, a9 M3 S' d8 G, n2 l9 _: i
9 R5 J" }4 Z9 i8 p' u* J- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);* P6 B7 n+ u7 `
- - D; `: C4 g5 W7 u
- HAL_Delay(5);* y1 z/ R; }' s' V
* t8 h2 R8 K! w X* C- * }$ m# A6 l% @5 j; r" i
6 z0 d, f! C6 @; W% }- Dac_data = DAC1->DOR1;//for debug
! L! ]6 F6 n0 M
5 z" N5 l& W' `* N8 Y) b# R- : s, ], @8 ]0 ^2 i/ |
- 6 E! S4 J# o. ]% R
- } # o B( H0 r8 S# y- T
- - c( z$ a. f( h2 L$ y
- }) \+ j/ C; F' d; `
- </span></span>
复制代码 + [# }5 M) @9 [( |( }2 n
总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。
& i) y, q& t5 ^' G m( I7 q
9 e) Q& {5 c0 a% B! A* @& Q* p3 K4 k) Q8 h Y+ `' p, u- V
0 I% h7 W: y& v好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】! g2 G% l; o. P& V
! `0 ]' W+ P, r$ l+ x( A! ?% j
' O% z+ y, \" E. R3 f- G% } |