有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。
7 y, o# q6 D l3 |, A$ O+ _" N6 B! o* }
) q6 j7 t1 w9 G- x; y* ^ I2 d8 `
5 Y' M9 t5 u: _ Z9 n5 U! w; G6 T/ E
结合STM32G4芯片的特性,这里可以有两种实现方式。$ C4 l4 l k; ^0 n2 A
9 p3 C! m0 f8 \* @; @' l" a
第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。
% }$ I+ s2 c2 H6 q. `, k, x
7 O0 u! m: T- ~我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。
2 ]4 }' N' x0 W( V: Z+ W% \- x0 i% q
使用CubeMx进行配置,主要配置如下:/ b) |& L; P& V* M8 Z; Q! D. ^0 m# {
e% n8 d, X) Y1 u4 [: m
5 O- M1 d5 h3 t/ b3 W% L) b
& h5 v1 V! [: q) H
/ N: ?$ Y5 U5 z
2 ^; R! x! I n* m" v; |: c8 w
! x9 k7 B9 S* I9 d8 S4 o
4 ~" O, F! O' P- ?9 ?; R完成配置后生成初始代码,再添加下面代码即可验证测试:
- R/ @$ a- D/ k1 `+ X9 s8 |/ u; O N/ t$ g; f
- #define Tpai (2*3.14159)
6 Y1 R1 w% I$ C9 v6 t - 2 k2 R8 B: L" Z
- uint32_t PData[200],Dac_data;
: f4 w( U6 S+ O e, H
) ?" \2 |4 `8 E, L2 i" T- uint16_t i;- L( ?& x4 a; B, j
2 V% g; c% N' ?2 F8 K- for (i=0;i<200;i++)! n+ o% q/ b$ U6 V# W: f
- $ d# l3 M4 e* P
- {undefined
5 _% l' v" t/ n8 a' U1 {. _
! ?7 @1 k4 f+ E; y4 ^+ M5 Z- PData<i>=</i>) W$ ~7 \$ F0 }: C' g
. b* [ K$ _- a& m; R/ Z: `- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);, X& n9 f/ d/ i0 I& X
* I! S; l h6 E6 N5 U- } //prepare data for DAC
% J( D, K0 y# u. t, m9 t1 a - 9 O" q5 p9 h* T* i$ x2 Z
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);8 q9 a O% F. R( ~* T: E$ e5 E+ I6 b
- ) R- Z9 k3 n/ X
- __HAL_TIM_ENABLE(&htim3);
复制代码
0 l: A2 { f: W ^
) ^% }3 k3 l, G3 [2 @6 ]- A5 ]( R# K- B+ |
上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。) b& K1 }: O9 ]: h9 i; J
; c1 [$ C/ C2 R! G K) K) x
2 r* R0 V( r; a& t8 t, ~) b, b. q" E, y5 K1 t
其它配置不动,代码稍微改动和整理下即可。参见下面代码:
5 s1 |( Q9 D8 r* b5 E! B
% N- v i0 s) w5 _( y' J- Uint16_t PData[200],Dac_data;
7 Q0 Y5 w% }5 G" d# j2 B" e1 z. @9 Q - * ~1 Q- c( A( U, s
- #define Tpai (2*3.14159)
5 K, a; y+ ~. p l
% I+ d, K. D8 y$ g: z6 i- uint16_t i;
* ~% r' z! ^9 T6 y2 m6 W$ @1 Q
+ e" T+ u4 a2 X& _. I- for (i=0;i<200;i++)0 K8 v" ?' i8 S! q, _5 [2 R, }( s1 G
$ |8 L, Q3 |- c8 z( f# n8 B- {undefined
: } S S% C4 A1 l
7 X2 L% E5 a- M- PData=
& c3 [/ j# \& D1 J5 u/ A" |2 y
9 [! t* |+ c" p4 E; O7 d4 a6 z- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
5 x. E2 U! ^" w- T0 a - . y* `# E9 A+ C6 M( @
- } //prepare data for DAC8 H N' c2 C* U3 ~$ Q
- " ^* M/ T: X- y0 V3 @8 J
- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);
2 I/ z5 D( S! d
- w1 U' _2 [) |8 v4 U) }$ g
. U& m, Y, C6 @/ m8 V- a9 ~, V9 s6 V: g) b: q* b
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );* Y7 p$ Z4 I+ r+ k- ?
- , t7 D6 W3 V5 ^* _% B: t7 t) H
- HAL_Delay(2);
6 @! `- w0 L7 {: Z
# T5 @3 m9 u9 q4 |2 x q
$ w- M) {" r4 ^3 ]; O- ) h# }& d2 ]+ L/ h
- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
4 F" {1 S3 ^; C' K( q7 I - ) t0 Z" W( U4 \8 Q2 {4 i
* X/ A" N( g( p2 E1 N
* e! J0 p c* M C/ P0 ~; o; I- __HAL_TIM_ENABLE(&htim3);
复制代码
$ _2 c3 y0 D- H$ C1 ^# D
4 \+ ^2 |+ [, y& g. j) K5 V$ I4 i* u) l
- w& G/ f q0 H* ^, _1 B7 g0 ~' _
( J! G0 x2 Q5 ?第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。/ {3 c1 Y2 K8 p) l4 L$ b3 `
4 U* e1 A/ F1 y6 e# j9 f
基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。
2 \3 L" p" k0 v7 Z) t8 q* [
$ ]/ u* E ]$ a4 u( h2 {, ]7 e
& z& W3 r* Q5 j1 `/ [ e! n
/ V+ u4 x4 z; s$ e7 }0 R
0 L0 g/ _4 |" T' H: P6 n& P) d主要是DMA请求事件变了,其它配置跟第一种模式基本一样。
, z& `6 @- p! F4 I- P+ j& Y, Q# }7 A% K! x h
配置完成后生成初始化代码,再添加下面用户代码:
3 \: @, W& L, P9 p% [
0 M: i- x. K" A- y7 _, V- #define Tpai (2*3.14159)6 k/ _/ p: n( {& L
- 0 k c! q+ B3 o; O1 m
- uint16_t PData[200], Dac_data;* y! \' R7 x6 Q9 e' U+ D
- : E' V0 c# w+ B0 u( Y1 F
- uint16_t i;4 U( k7 T$ F; m
- 1 V z) h) K+ \1 Y' |" t! L
- for (i=0;i<200;i++)
( y5 Y: b* T; C3 v/ [9 ?1 j - / ~6 z7 x5 f) W
- {undefined- z, |3 Y/ ?/ s' |+ {
( i3 z) I) t: e; J- B( i: N8 B' V# D- PData=
1 [- j& z' d2 ]9 _, a+ o
( M1 h+ J' ^/ q9 \& _7 ]- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
* j3 l6 @4 S8 U9 N7 o
7 K, U7 o. |( G% T8 ]& n- }
! h2 I0 f# D7 @ J# h
2 w8 K: l( T- M& T: X" F) K8 O' t- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );5 Q( }, F' U' U
- f7 y) @2 `% B: z- w7 x
- HAL_Delay(3);
; _ d Z W- a! ? - , N5 v4 |. ?5 Q- l" ?8 J# u: o
2 h. c5 u% K) f: n; b: a
. j* @" }2 A E- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);% e* q/ H) ^: r" _: S( m
, w" Z$ h& ], Y- d" |5 U- 3 }" r/ s5 B( l9 z5 E: Q) q/ ]# B
- + S( w- | ]! u
- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);
$ q9 P2 @* r! T
2 M! s: T8 E0 K6 \; _; ]- __HAL_TIM_ENABLE(&htim3);
复制代码 3 \1 ^. ?: w" o
; J1 r6 b& o1 m& a3 [7 K. q% ~5 x; e这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。
5 H2 R6 x& R. U: a% K; `' z& F9 \; u3 g7 E8 `
如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:
2 e' W- v- O# a# L5 j
o6 O, a1 b% @, G2 G
" h' A4 Q: I) {/ ]" K+ m! S7 g0 O9 P& i$ V- ]; ?7 w
% g( [0 P' [0 V
相关用户代码如下:
- N0 q* W# C1 Z/ Z& [4 b: U% u+ a/ _! f/ r$ C
- #define Tpai (2*3.14159)
& G7 _6 S! l, R* V! z - 4 y3 h$ b# |! o. ]& ?7 _4 r6 {
- uint16_t PData[200],Dac_data;
) G R/ f' i8 N - ) j3 L, d$ V H5 w# u0 B+ t7 \/ }
- uint16_t i;6 S* F$ ~! { w p9 y1 f
- 9 v5 X5 G( x' W; G# I1 V% W: t. W
- for (i=0;i<200;i++) [# j8 \" Z# M# X4 e
- 9 G2 U/ H2 D2 U+ J/ a. U" J9 c: f
- {undefined9 Z# J2 n( `3 n5 q5 z' b7 m/ P' S
/ t) q7 I3 s5 f9 Z2 U3 s- Q# K- PData<span style="font-style: italic;"><span style="font-style: normal;">=" w8 `- T) T0 X
( o) n' Q Y R% i: Z. P- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);" A/ ?+ y; d% r
- " }* {4 Y: s' O1 S
- }
! w( e A4 `2 x) Z7 h, L G
0 z; }5 l* d' |6 S' v& F+ N% I- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );$ `2 B. B% G5 Z A1 z. h
- 6 q+ z4 {7 B8 `% B3 H* N# M
- HAL_Delay(3);0 b. A% Q) x0 m
3 F p: c1 g R; w$ O- + C& z" Y' k* t! s2 |
- . {3 ~5 F7 v# r3 ?
- while(1)
: \7 I- Y9 J; Q' r0 I4 y - 6 [0 b: E) V v
- {undefined, L8 i; |. J7 b& L- M
6 Y! l+ c( s0 Z" P$ ^0 A+ g2 q- for(i=0;i<200;i++)
0 A J' M6 ^/ E
, L( F, |6 O' ?- {undefined4 ]0 N5 p6 P9 o) l! i! K& a$ X
- : ?8 D% w( d; _) X2 f
- DAC1->DHR12R1= PData</span><span style="font-style: normal;">; 4 B$ F" s+ ^3 [! l6 F! x# b
- % T% g- U3 l2 S' b+ h. W1 K, J
- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);6 E4 x3 g6 y: O) w+ A
- 3 @# x1 y5 ]/ Q$ a6 Q
- HAL_Delay(5);
! O, m' g9 D: [2 q+ ?8 E
1 J3 m0 U5 E. h- `7 l# g; y
, M' j3 u% a7 S1 ^8 L! V) S- 2 S; m% {; [) k0 P
- Dac_data = DAC1->DOR1;//for debug. s1 S% f, ~4 \4 I
- . T/ H; M* j/ W/ a
7 z, C, D# r/ Z1 Y+ _
* u- @5 A3 \" c# f4 A/ a8 q) u- } 5 Q3 Y1 h) b) [: G: H/ p
+ ?* e- `/ ?* Z& G' _) b- }
) ]5 F. Q+ W" _$ B, F; a1 o! e% h- g - </span></span>
复制代码 0 m0 W1 \1 _: `* U
总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。
2 b$ \ {! j% i& i# n# x
; y; t) H8 n/ I! a* K Q
" h6 D7 i" Z& _& k( W3 ], h5 T2 j
+ b1 ^% L9 v9 {8 \# U8 a- k: r1 T好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】
2 A; u- W; m I8 C8 q
2 f0 d# p5 u a7 \
5 L. p3 U6 J" f |