有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。
/ ^% X4 [+ A' G3 ]
2 r; A5 C* y& L9 Y) F, ~
, w4 A( a" M) L' z, h' ^9 c2 y5 u9 [4 \4 D: O& u; X
8 A# {' Z" E" V' F& R结合STM32G4芯片的特性,这里可以有两种实现方式。
* `! D" G$ [7 k8 z" _ W: E0 T) Z8 Y# f9 {, I, c
第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。
& t8 n* u7 G6 R' F
, [, I5 y3 K( K我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。4 A6 G- n6 P, [
1 k+ ] I( |- w) R3 f" z: _使用CubeMx进行配置,主要配置如下:5 s+ K5 ^" w9 U+ S/ w
8 w) M4 i5 Y: ~4 X# @; i2 Z
2 h& N1 L9 l. E% e' P
' r+ |+ A8 p! ^8 _5 p
, x$ P4 D, B# w& c6 B8 O0 h
" k" { F+ W7 {6 ?( E9 w0 f/ k; p$ n6 |. h
k9 A5 @- k2 a9 n9 c
完成配置后生成初始代码,再添加下面代码即可验证测试:
3 K( ]& p& g8 ~% e; V$ l7 ?; K. \; i6 w# f9 X$ e
- #define Tpai (2*3.14159). ^6 u$ ~' u" U& j( } P! z
; d# ` A3 R' p5 q- uint32_t PData[200],Dac_data;
- u3 t0 |! G. K1 h! W" m. [& t
+ b s& |+ R. I+ b- j& R- uint16_t i;
; T8 P* i* G# d7 m0 z9 l - 9 D C# i: m% t$ n, v3 i3 Z+ u
- for (i=0;i<200;i++)
& \( F5 S, C7 k+ ?6 H" ^ - 5 @9 T* \4 l' {* {/ x7 p
- {undefined( u5 T( K H D* R
) v# U3 J, \) n: n6 o3 x& e- PData<i>=</i>
, I) ?0 @% g3 G9 a' E5 x2 Q - $ b3 Y) h& s) H1 O% H: v& f
- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);0 L% z! O8 O# Z" A% F8 |
- : {: c& J# P4 \% q
- } //prepare data for DAC, H$ g) O% t8 g& m8 l! L2 F6 p
- 5 W" q- Q" W2 K" B( j6 s1 e
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);
8 g5 S/ \4 \, A. x6 K4 `. i6 D
) p$ I2 w: @: e8 H2 `% q- `- __HAL_TIM_ENABLE(&htim3);
复制代码
$ l4 i1 _: g7 z0 @. i \3 x# W6 r7 X; Q
8 w8 d5 u! z, g5 o上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。
+ D! M! o- Q7 _8 E# ^& U# _& ~- _: V9 R7 Y9 Z3 l% y, L
1 c3 |% }: l2 z) {) `
: k" T; ]- Q9 p! Y2 @9 z其它配置不动,代码稍微改动和整理下即可。参见下面代码:' E3 C8 ^3 L" h# W7 S
0 y& {, s% R+ F! \* A( A- Uint16_t PData[200],Dac_data;, e! d7 @5 H& y
- 3 y, X% t4 m7 V- N, ?. p1 W7 Y
- #define Tpai (2*3.14159)+ Y+ v2 E' d/ q1 Q5 I; {7 k" `
- ; V3 d: N/ N( n& P4 u0 W) y
- uint16_t i;
9 c$ J0 I7 _ R4 i' ]# \3 y - 2 T% j3 n$ w' J2 j6 p# \
- for (i=0;i<200;i++)1 h: a0 O9 ?6 q6 R* U
- 7 }$ H+ t3 R/ y6 J: C( @
- {undefined6 ]4 K# ]5 O0 k. {
- % u) I4 W( h$ \0 r! K7 U
- PData=6 U3 O) `" Q$ h* W- u3 K
- - m6 h6 l0 W, u) `* g! y3 u) B
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);* Y3 @. X' Q/ Z% m9 N
- ( M4 I, U* r6 a% X
- } //prepare data for DAC* F( ^2 \) l9 x; z
; T7 I$ _/ T; Q, V3 v- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);3 M! }8 b6 m; B% j, p; D
( S: M& Z* t! u c
. B" a, ^7 \5 E5 J- ; U2 F Q* o4 b% ^ ?
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
; `0 m$ N; t; l" I& W0 `4 e0 S - & m$ Z6 {5 A( T% q/ q( F1 r' Q) p; A
- HAL_Delay(2);5 V- v2 m7 {6 Q# {: E) x, m
- & }: l$ h9 y- N3 k0 K) j* }1 E
- - d. X! \$ i. @0 ~
- - n$ e% I! {# Q( k5 T1 E. W6 w* s" Z
- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);. U' k# k( a9 @+ J* C
/ J3 c7 b ]; r/ H0 i
% X1 K4 |$ _3 Y7 ^
5 R8 s9 ^/ G$ c. {$ W' i- __HAL_TIM_ENABLE(&htim3);
复制代码 & \' b8 n3 t) g S0 M# b2 D
1 F1 P0 u: A8 U) l
& q' p# Z& h* }& m- d. T1 D
$ R! z' M2 B. }1 |3 }) T第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。! a) V5 g, A6 `: y; m3 D8 S
" {$ c' L& F- j9 S t
基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。8 C5 i- J. p6 t- @
: }% O1 M$ w r- V) `3 y. t" y( x' a5 L. `( f
7 Y. W# Q+ s( {4 m: d2 \* o$ S/ p- J
& J$ x Y N) G0 ^: j( V: P主要是DMA请求事件变了,其它配置跟第一种模式基本一样。 V. L1 |$ Q! Y
* M7 Y& W/ W' ~4 n: l% ?% J配置完成后生成初始化代码,再添加下面用户代码:
1 F# W9 Z( {6 t/ C4 ~
: v0 ]3 v& ~2 K0 d- #define Tpai (2*3.14159)
' Q, W6 l; t2 G" m
+ S6 Z4 J+ ~' B+ R- uint16_t PData[200], Dac_data;
* i$ g/ d! `; f. M - " f- R3 @2 T* k+ c! O
- uint16_t i;, M3 Z) K& B4 O+ n% n; X. J
) ~! t: B1 Z* l7 M- for (i=0;i<200;i++)8 [! E% i$ t/ h9 I* W
- & _0 ~9 f3 U: {, ]3 X4 X3 d
- {undefined- w' i/ }* J4 ], {
- $ y# ^! a# s& q% G) i
- PData=$ q2 z$ K- z- y/ U q
- : i3 B& o- a# z+ O3 o5 @! F
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
. Q( g; z" s- o& w4 ?9 P - " a/ h0 a. Y) W0 @' x7 ~
- }6 j" K' y2 H2 f$ K2 B
- : w6 F4 P+ E$ M6 ?) \2 ?- T+ |
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
6 [7 J8 ]1 @/ [% z9 p - % _. r' Z7 J8 M2 X
- HAL_Delay(3);1 _6 a/ j# O4 I
, T1 v. M: f' ]! P* l* S, ^: ^' K- / U: D; Y0 E9 l0 q8 K9 A
- 6 G0 o. d v4 K! \9 q
- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
7 U% w; ~2 w# j3 s- a
! L3 Q& j# ~+ F, [, ^ Y) h! ~
8 o+ E. N' [. B
* t" G- c l' Q' u9 t' q! j- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);1 K* P# c @% k* k% J' H
# y; u7 n& x( f$ w- __HAL_TIM_ENABLE(&htim3);
复制代码 2 N/ U. _" G6 ^9 ]
2 X, }- C& T. b& u7 G, q3 w这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。
) @. V- |- U1 G% p2 i' D4 s+ _: n
如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:* l. Y' I( P4 l6 A& G7 |
5 N; d8 n' K% ~ M8 A; J- H$ U
( ~$ Z" M8 Z; }
( G! j7 p1 V$ B" y% F* R9 ~2 R! k( e2 k
相关用户代码如下:
2 o# m) a2 S. L( ?
" X. p+ A1 F$ }( X- #define Tpai (2*3.14159)
+ a3 A' Q9 F4 ]4 c: Q2 @2 R$ _. r - ! A7 b1 M7 g: k3 k% i
- uint16_t PData[200],Dac_data;
/ f) J1 Y) W9 j) q) Y
( F, v" A: N8 z9 ~7 Z4 q7 y- uint16_t i;7 L% M6 j5 u+ N: c0 |
2 Y8 q5 C! B- q7 l U# O- for (i=0;i<200;i++)5 B; m# K, \: r0 w
3 W2 \7 M- Z) s* E- {undefined( s$ j, A7 B: F1 c0 m5 x
" s/ n, Y5 T3 I7 l; `- PData<span style="font-style: italic;"><span style="font-style: normal;">=
$ `! d8 R5 m( T, d; f1 _& l# Z
0 G# k7 C3 D7 l" i- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);
8 ?/ `8 r6 ~" [) F- R- @7 n1 i
B3 V6 R7 N0 a, c; B- }" K+ y0 [! _0 `5 B& V) Y6 X3 P
- 4 c; A* ^; W' B/ V0 _# q6 x9 `4 F( ?
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );, J. x `0 G+ {
, Y9 s, `. `' V) P( @) E) I- HAL_Delay(3);! x7 r. v0 M9 a6 `9 _$ M
- ; I0 S2 J/ [5 c# B5 F" i' v
( d3 |: Z- s _4 B8 O! _" ^7 G- # g$ e* q& F! P; ]8 N
- while(1), o) ]2 a8 p& b) |
# b% z- @+ z" h* y- {undefined
. ^3 I0 v) f9 ], \8 _* `0 o - ) R% z8 v& P" B3 ?
- for(i=0;i<200;i++)
8 }& J5 L0 M5 W9 c( X2 M - 7 _0 @0 M% Q' B; J( J/ ?9 G
- {undefined) V) j& ^: B# W1 f+ S3 z
- + H9 M7 P( s+ Q$ G
- DAC1->DHR12R1= PData</span><span style="font-style: normal;">;
- Y2 [7 z; |+ P" m/ n: W) b
* y& a( H; Z$ L: _- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);( ?* e% ?/ M& @: h2 j1 x3 k
- : k3 D6 W: e$ J( M" W# Y1 P
- HAL_Delay(5);- ]% y7 H8 Q4 X$ V& \2 N# B! p
6 Q) \! I5 K$ L- & N3 @& t: ?( y" F! M# R9 y
- , o& [# }/ _- W' m% o6 I
- Dac_data = DAC1->DOR1;//for debug( c' D p# P+ ~+ n% d! f
/ ]' m4 S1 _ s) b# n+ `
9 ~: T! n! A" r; A8 t- ! \+ p1 u9 a1 ]) m5 G, e
- } 6 a* ]( _' u) N; g+ i
- " X1 a, @( {$ f* G, _* B3 U+ _
- }$ ]$ I- O% C" D% Q; C
- </span></span>
复制代码
- R7 f, |) b: ]# P" P总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。/ W/ O* b/ m& I1 k& D0 ~, |7 a
[6 I0 Q K0 q I: ]; K% l3 o' |; S! Z
! q& l0 R: v5 P1 k; j
! L' p2 z9 G& z( p2 q% Q好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】
$ K; v% e2 U9 E) Z) p5 v+ j0 i" c. N6 e
U3 Y' K$ s; c Q
|