有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。1 n+ V9 l! n7 @
( s% y+ R% j% i& t
* V3 S! @4 Z5 |' @: f+ H" z6 {3 x8 ` w) _: h
# t" t- y4 I& N. O4 n结合STM32G4芯片的特性,这里可以有两种实现方式。, s7 W5 q* x0 q6 T+ ~, B% ~
5 E% ~2 E* P- A. u2 v第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。
7 i- L" |' m- E$ ~
" c" l9 _& s. d/ x9 k. U, y我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。
. Y+ o! N. d3 G
( p! p1 d9 f/ H* U/ S, i& R使用CubeMx进行配置,主要配置如下:
7 y8 w2 Q$ u$ a0 W5 A' p( s3 {
& Z7 G, _8 n, w7 K1 Z* e" u
7 p4 M: E% B) e* u/ h
2 k/ @( A) Y2 h
: g& y) G8 M% b# f7 |1 s- w* ^1 Z8 P& c# u& w9 z) l0 c
9 o' h# C. L: j* p) d
. k, P& f/ E3 Z+ G完成配置后生成初始代码,再添加下面代码即可验证测试:
* D* W" O6 g% z! B* c
: x0 o5 O2 g, n+ @% U5 B8 }- #define Tpai (2*3.14159)1 O. x8 C' t, K( L/ R" t- U
- ( u0 E# D% n/ p
- uint32_t PData[200],Dac_data;, j) \/ \( d. |# S6 Y6 n$ Q- m
- 8 n; f) K! C/ ]8 H2 x7 H# T* s
- uint16_t i;, m0 B, R9 r5 @7 s/ t( o c
- ( y- p' W. q1 m& l
- for (i=0;i<200;i++)" h* F4 M" |8 i& a) T( o
) g9 c8 J. r( Q! w0 ]7 w, V- {undefined
# n/ n- g4 j4 s7 a I6 v2 t. c - * i. o& ^3 P/ X" P7 g' @' I
- PData<i>=</i>0 S( ~8 i0 ^$ s, E2 G( B
- & B x+ `% N" E% a
- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);1 F0 m( n6 J: R* n1 U
- ) q! X5 z1 M% E
- } //prepare data for DAC8 @0 N( u! G: t
- ; S- o& b R9 B3 a3 |: F0 D
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);$ A4 L) D* \/ ^1 O" ^0 }
- 7 m/ v3 I7 ^! H- t* q/ A
- __HAL_TIM_ENABLE(&htim3);
复制代码 " N7 k+ N4 M* |$ n" n, J, ~
" J; G; c. @* u3 n, F9 T* J/ V4 Q. \: `. a
上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。
! i3 W5 R Q+ K
. p- [$ y/ _1 a z
- Y& N3 e5 V# G6 |) s$ y! G
* @: i# l0 ~: b i其它配置不动,代码稍微改动和整理下即可。参见下面代码:% u5 z8 L, C0 P. W0 m# L
2 T8 c# t' J8 a3 i) [8 V- S) q- Uint16_t PData[200],Dac_data;
8 P7 S7 n) N6 X- Y& j - . z6 W" ?3 R; X: n- A* u
- #define Tpai (2*3.14159)# q3 H# M, r6 s- u8 g
- - R$ n; V# G: B
- uint16_t i;
, Y2 n3 w: n$ ~" B3 E - 7 B$ V: U S+ E* \ R# U" z
- for (i=0;i<200;i++) z# \* Z3 G3 P0 h
- 0 C' ^# U& J+ j' ~
- {undefined- D0 V$ [, `# D. i# i
( M. j# r5 q) v8 [ ?( p6 R, I- PData=
9 v* X$ @5 Z) O7 I+ D
$ [4 X6 w! {6 x, @ e" _2 D, S) I! _- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);9 W* [+ [0 Q& p" @' Y" P
, M5 \4 t" S4 [' U- } //prepare data for DAC1 [4 E' O- ]; e9 P& x4 M
5 v- A# U" d# x8 w8 ?" E- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);' y5 w8 Z( s1 }2 l. y, \! A" o! S
0 U) _+ H8 W4 y! K$ I8 O/ O0 ?- D2 w7 K6 ?8 o3 k
- K. q. N4 T2 X+ O' F- M
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );/ ]" D( [$ M1 `( E; F1 o1 L) f
- % y% u% J% j& t$ N G. r& t
- HAL_Delay(2);3 q" i/ H; H7 }" q" U3 S$ i! ~
- / z9 e4 ]1 R4 A& M: V4 B; p( R
: P/ ? z/ [* C c- ( {7 Z$ c. X Z* `
- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);' S, ?6 q. m& o& K
; C5 {' h# P, Y4 |* L
s! _0 B* c$ C& a3 H5 ?4 U: k; s3 j. s
8 o/ X$ l3 {1 n- __HAL_TIM_ENABLE(&htim3);
复制代码
# S" [4 g5 f4 X# w5 O! j; J+ H7 ` N4 x/ d1 P/ p" C: o5 z5 J2 t# s
) R0 ^/ O, |$ h# U1 B( v* a
- Y L* C# n- K" [; ?2 E第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。
' _ i1 j+ F$ x) h2 ^/ c! Y6 ?, S$ p3 R" c, \- ?
基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。
& n. }% [( u9 h& I6 `( I! X$ d# T5 q! l0 k1 n. a
% p1 B U% {% L6 ?
% C# v3 _* U" v% @$ R2 k9 j% f, I
R- b* `! F0 R5 g主要是DMA请求事件变了,其它配置跟第一种模式基本一样。 e) A0 P3 H* b2 `! E* h! v% y
- i4 E p+ k' g' n8 ?
配置完成后生成初始化代码,再添加下面用户代码:
. O+ d0 |3 k, c# O" X- J. J; f8 y6 P% i! Z- q5 j7 y* l% J: F
- #define Tpai (2*3.14159)
1 Q5 I9 U7 h7 x6 d
9 P# f: w; P4 Z& Y: j- s8 H- uint16_t PData[200], Dac_data; ?0 M! y* A3 W9 e6 J1 ]
- $ r8 x8 i" ~$ e& a% @# H3 ?& y# l
- uint16_t i;% o$ ~$ K/ r4 `$ t# V$ F* g
- " B/ n% \8 F. i
- for (i=0;i<200;i++)9 P# Z! J& a' M* R1 Q2 T4 [
- ( Y6 R0 u/ M- N P; T# a
- {undefined
3 ]. U; d# }5 q& U4 } r0 }0 ?6 ?/ ?/ ^
0 Z3 R C/ L) a8 ^ i7 |- N- PData=" ^) V, @' H8 Q) d" n4 {
3 \) p' T# } H! u- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
0 H' k( h3 L4 o
+ J0 v2 C6 {" G7 I- }3 G1 _6 a y/ y! I6 C
6 ]7 Y0 e: a: P" P- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );( Z2 V3 I3 A+ ~, t( Q' e8 U8 d" [
- 9 S4 C, C* M' [! ]/ z7 K. x* f
- HAL_Delay(3);0 a5 x8 p: x) m0 U2 n' c
+ z: I$ [: ^0 }0 ~' B- 2 V3 s9 V3 q7 r
- $ j7 U, A: [ _' d0 R
- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);& e+ n/ L* d# ]$ @
6 x) r I6 u7 j5 q2 d- L8 {) Y) u `) |
7 U1 z% v, `- {* l
# `4 O/ P5 _4 a4 w0 e5 s8 l- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);! v# Y" ]8 D; t; K, h* C x
- ) S7 T9 n! N% v5 j7 Z3 X0 h9 S
- __HAL_TIM_ENABLE(&htim3);
复制代码 4 H! w, ], B, H$ x
2 J, K& V7 d7 i3 n+ q7 X这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。5 a" H7 ~4 x6 A" I, l7 q1 I
% g5 w. S) |; {% o
如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:7 b, y; M% {" [- O' z
6 p- J0 G+ E9 s& n* u
- m. K, e6 n: Z7 W( {) C
3 X' i5 f. }$ ?5 t7 r* R! k
* r4 e, z! a# `+ \8 e相关用户代码如下: 9 Z3 v8 g) _3 T
. S* a1 f% l. _
- #define Tpai (2*3.14159)
7 F+ w# v8 B) i
4 b0 `5 X' L4 J. I% S4 f- uint16_t PData[200],Dac_data;* W5 Y; u' c& q3 v" n
- / q' w. z( t- w S/ _4 P$ }) M# t/ r
- uint16_t i;2 a; @0 e0 t# m0 u
- 1 o5 r b4 u0 H( A( q5 Z
- for (i=0;i<200;i++)
, D- `6 g) Z% j
1 L1 Z$ y3 Y. ^& r' [0 {- {undefined4 ]6 }/ w8 ?& V- u
- ' p' Q1 w% P+ [) L4 a" k
- PData<span style="font-style: italic;"><span style="font-style: normal;">=
0 Q# ~( B/ L( T3 ]% _' p5 y
3 O6 Y% _- `/ ]- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);
- @0 q, u2 \0 T( n* J
7 M" `/ c' q3 A3 R6 s4 j- }* q# r: C- F% ]8 B
- . W( l- g1 {: ^. V
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 ); @3 P* b. D5 u
- ' N% _+ m, Y% d
- HAL_Delay(3); j8 j; P9 N9 s- U
3 ~- X; g. F# X1 W h: K7 y& ]! \
) u! g1 [! W) t* e/ m- 9 w. |0 t3 |/ O3 W) J V
- while(1). ^- h+ i1 i# v$ b
" |7 d* y8 |5 w# x! Q7 m4 _- {undefined
* n [ k9 x4 L& A- D7 t - 4 B3 R' v: {; \9 O3 D; x& i" N
- for(i=0;i<200;i++)6 Y \& Z6 ^& I* E
' M" {- P) e. X+ K0 o- {undefined: g+ Q) {6 e* w* n( H+ m
- ' c# a* v; ]- m7 W( Q
- DAC1->DHR12R1= PData</span><span style="font-style: normal;">; 4 K2 M/ ?& J- G5 X7 z2 S Z+ P
3 R) K ^% j& J2 H# S- t( I. \! e) O! g- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);% v2 D9 ^$ {* E, a
- ' y6 R7 t( @5 ]7 u$ I S
- HAL_Delay(5);5 F* W5 c3 x* y" R" _
- ; z* F' @5 U0 R8 z* l7 U
- 6 d; N* f0 a J9 ~
9 ]3 k$ d6 k, L* @- Dac_data = DAC1->DOR1;//for debug c0 S3 K: Z! K" k8 e( _
- ) {! e, q* [+ p" i/ n! z, b# u8 Q1 f
7 W4 r4 v- s. u' Y
9 J; I0 {3 [2 Q1 J1 v' e6 \- } 4 F( I3 Q- C* G: `1 ~3 v/ t
- : k/ l2 R0 H0 P9 G( Q; ^' l' n, {$ W
- }# t( a2 i/ V2 I! x1 s' a, L
- </span></span>
复制代码
3 b3 E5 N0 L* ~% N9 A1 Z总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。
9 ]1 c8 ]$ y% Z# Q
! R3 _; ?5 {( Z3 N0 ^& W, a6 @1 T
4 {8 U$ s# K9 K. d! \7 X
( ]6 ~( f# Q7 p好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】1 U" U! v4 F3 c
. W( \3 Z Z* u5 z
, l/ }) t1 f; }8 A/ T+ A |