有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。# K) D- j1 B4 m) q9 c
0 ?% G! L1 @7 P, h$ a$ m7 t& x& U' L
9 Q4 |; e$ B/ a8 u, c1 ^
* y1 h2 k$ N. L% v! l& N结合STM32G4芯片的特性,这里可以有两种实现方式。8 I2 v# @* v- A
, p/ I; u4 }0 X; P4 D" ]- d: F9 h1 l
第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。+ {! y# L" W9 b- P
$ ~: Z& d2 l. a6 L2 i7 R, t
我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。$ ?) R* T, |5 Y1 V) ^9 @! x
$ ?- H: y; k! d$ ^+ _
使用CubeMx进行配置,主要配置如下:
/ a5 F+ L) b' {8 j! j
, {! R+ p" n: s# p. s3 \9 ?
% _* k* j; j2 y& F2 ^( q4 a3 D+ B" e- [5 F2 N7 ]% y
& r4 t$ H% ?$ d/ s* ^" ]! y0 v. S2 W+ b! m* H. D; m' i! P& J
; U6 k# |# F' z0 z) \
( }2 k( R3 a) P6 L0 X7 f$ o1 R( S完成配置后生成初始代码,再添加下面代码即可验证测试:: A# z# l: Q, l
4 J+ ?! I2 _) H+ z( ]$ R( ]' W% f- #define Tpai (2*3.14159)( Q- f1 E* t6 n4 Y& u
B2 b3 `& l" U+ n5 I% @- uint32_t PData[200],Dac_data;
) g. f' X* U' _
6 G% V' n$ J. q. }5 x. S/ K- uint16_t i;
^# k' K8 Q( G- Z2 ^3 O9 Q8 r, { e
6 |+ i3 A) a* Z$ S- for (i=0;i<200;i++)
; B- `; `. y P( } - ' h4 m O; V, r- |
- {undefined9 X4 @+ a1 A! e+ F! \
9 T" d3 c" v, D; q- PData<i>=</i>& S ]/ {, h2 g2 T. _
4 z8 \9 g4 ^0 ]- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);; K* T2 {: [6 u! ?, ~
- * Q4 M$ u" s" a3 L- i# n
- } //prepare data for DAC
* G& \- c0 I- Y/ x. v
; u9 {* i% B2 i$ h- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);
J. \1 N, t/ w% F1 F - $ M9 S# n) q y, H
- __HAL_TIM_ENABLE(&htim3);
复制代码
5 ]' N' P6 J i- r& I D. ]' ?% s+ Y8 ]# Z9 b
. p3 m9 `1 f7 r% Y上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。
+ H& m6 l# Q7 l Y/ H7 q- w$ h2 A! M/ m9 B
3 ]8 J: |( j" w' f8 i1 t
$ ?4 V( e7 `( Z7 u( v7 n; S3 p3 D其它配置不动,代码稍微改动和整理下即可。参见下面代码:
: F# N- O% z8 Q! _. x: _
s1 V y) T1 V$ q- Uint16_t PData[200],Dac_data;
! e( Q8 D; F) O2 ^6 x" H - * A% l$ r8 l" c, o! U8 {
- #define Tpai (2*3.14159)" |3 g" E7 G( {1 L( L _
- / a0 E* y/ X& F+ h7 ]
- uint16_t i;0 Y# p0 c% a0 ]$ N- U1 e
: S+ d7 f" a$ F7 @% z- for (i=0;i<200;i++)4 o: J) v- e, F- p! x5 s
0 ^6 G. U9 V, A4 D7 p) N" _- {undefined# q Q- n( _& ]( S q1 p( } |+ O
/ J! n h" z9 z7 J$ @+ p/ e- PData=" q* L8 o9 `1 t3 _- O9 j
: u8 F e7 @9 A& v7 z- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);7 H" {& E$ y7 \: }. V. R
- * N6 C# X, C5 P
- } //prepare data for DAC
h+ @* x: n0 |" T2 O% B
8 G0 J: S6 ^4 }( H M( e5 N J8 e" L- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);8 [3 @, K* j3 O9 f& o: _3 ^
- ; ~8 x2 z! ^+ u/ y4 Z! y1 ?' y4 X
- }; |9 G/ [; H, `$ N/ P4 U6 |- - i' B% r. g0 Y3 Z. B
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
) Z& a7 W$ k& h' d. v. \1 _ - # P2 C1 Z1 U1 j- E7 b- X& {% z
- HAL_Delay(2);
& P1 c& W* e. B - ) u( q$ G0 V8 \9 [9 \# Z
- " t& T7 y6 c0 z# V5 j- R, Q
* X# [ [3 B2 ~% m- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);2 Z5 g. d0 c6 a7 ]% q& y, B7 r
7 R' y Z7 {1 n7 ^8 V, U, \
3 C; _. S1 r5 ?+ H' ?# L; G; V- 1 s J* u8 `, {4 d0 o* s
- __HAL_TIM_ENABLE(&htim3);
复制代码 ( X: X& ]6 P0 f! _
* n' j& D1 E# g& Z6 x8 @; H( z& S9 O3 y5 N
" F! j; F* s" Z/ |' o. L" k
第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。/ t* \: C; Y9 P( x7 v! ^
( H% o2 Q t. [1 I- V
基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。
8 G) G, O4 L& H) B) o
! i8 u h% E/ E/ p, k0 h: |4 w6 @/ |; j
, [, P, A6 s* l% |
( P- B( x+ P; G; J" ?0 E
主要是DMA请求事件变了,其它配置跟第一种模式基本一样。
/ f1 R9 w/ b' o( L
" M3 Q- B% t0 K# g0 Q W; S: {配置完成后生成初始化代码,再添加下面用户代码:
7 M& j- N+ I- R# M% ~7 X1 v5 H7 g* F' L) k. m+ B
- #define Tpai (2*3.14159)) |& t. i! ?! s [- P
2 ~/ X8 U" n$ Z# h- uint16_t PData[200], Dac_data;
9 O" d) Y" _% b5 _ - # f$ ?+ a# b3 V6 I" g
- uint16_t i;
. {6 s5 s2 F: k, z! W7 \) U+ D" k3 z - + |' i- O8 O2 J! @
- for (i=0;i<200;i++)
1 _8 p3 |7 q" A3 K) I9 |. h8 E! Q
" ^, ^0 [5 f5 r) E- {undefined
4 |( O5 N* @) G% m7 X, x$ ?/ X - ( M1 ^( r+ ~' b6 i( Z
- PData=9 Y1 }& ?6 n+ H V. n
- $ r4 l# a5 E$ F8 k
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);5 c8 ~% D( S) P, p
0 N8 \, u6 n) Z$ f- }
! e7 H. N. t# d& X% I - - A& E5 h5 g8 `
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 ); @8 u: q& T5 t3 u+ b4 {' _
- & K6 m& R% p4 R
- HAL_Delay(3);
. H' ^0 V; k+ M' |8 b$ d! M/ c5 E
j* x9 z; b9 R8 N4 a9 z
2 a) E, x2 \' p7 L8 c! Z6 k- ( l/ y, i" M2 x# H; S
- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
% J; I- w3 x; ~- ~
1 }; S- D5 C# S! m- - d& O5 o& s" e& G) ^
& |+ @8 Y$ i3 r& I1 e) g) m' J- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);
8 }6 Q: z/ ^4 e) @& l
, } \( x6 W3 n6 A3 e- e$ _3 u- __HAL_TIM_ENABLE(&htim3);
复制代码
* y) D0 P& e5 x* G* S7 C4 @( I% o. A4 o3 o
这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。# {5 X& |; W n
& J8 Z4 D" p, I) q如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:
4 h' v9 o1 a+ l" G" [0 Q+ f% _4 `
1 [! ? u3 _ s- Y* {: H: u' E6 E1 J
( y0 c% b" F k5 w% a
, n; O$ B2 m# D相关用户代码如下:
' j2 A7 m* C" B7 B% n$ k' B5 T! a+ R7 ?, `
- #define Tpai (2*3.14159)
$ p* i0 J+ L; C: p7 J% _; C9 J - ) s z- W' K( Q9 a- V; |+ K0 C/ w
- uint16_t PData[200],Dac_data; K+ A: A! d8 T% g! R. u H
- , @8 H7 D! U& Z+ X2 c% q8 ]7 A
- uint16_t i;
' ?) Y% V, i2 H9 u. J2 u @
+ H3 e a* Y- r4 {+ n( l& S- for (i=0;i<200;i++)- w1 }2 T3 p3 l
( ?3 @1 l0 z( ^% o$ W- {undefined0 q+ l. _1 h: G( D$ T
, h' j1 }* q4 Z& c, J! `- PData<span style="font-style: italic;"><span style="font-style: normal;">=
8 J, l: c; N i, l - 1 S# R6 |6 ]/ h
- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);( E6 o. S% j5 r( u! F: F
- / T/ Z6 m0 a3 H) C5 m
- }
* n' e' O7 R' t
, c( b/ k0 o) b& u' c7 o* ~- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
7 k; `# s( L6 e6 {* k1 w ? - - E7 `7 M; ]) R& s( `0 n
- HAL_Delay(3);6 k! ]% ~- T5 I2 f0 Q
0 n1 }, u7 m. Q( \8 @- `* _$ f" l9 y3 r* e1 y
* y/ Y* `7 g4 a- while(1)
! S: w5 g" }& [7 N - , e& F3 E4 V0 E9 o2 N4 k7 ^- T
- {undefined6 z+ p5 r; k' T$ ^ `
5 v/ f& R# |1 E0 W- for(i=0;i<200;i++)
2 |4 p& J* V" Z0 M" k6 N5 h7 c# |
8 j3 O2 J( N, P6 X; Z3 }5 F0 z- {undefined
5 I7 A1 T/ `& v$ n' I9 |; V R4 ` - , T* Y- _' T/ v- b5 P
- DAC1->DHR12R1= PData</span><span style="font-style: normal;">; ?; f$ S; _; C, L# x0 {3 Z; E0 I
; x) ^' Q/ I( U" w- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);
% t) I3 D9 m; {: P) h
" ?+ M1 M5 ~& b- R- HAL_Delay(5);
2 w$ p+ b2 ?' y* M2 g
: {4 u4 C3 A' b: `4 \7 O0 t- % b% R7 H& ^0 j2 M/ n
6 D5 [- B% ^$ [- Dac_data = DAC1->DOR1;//for debug
8 a! d8 `8 S( g+ h
- w6 d( ^0 S/ [- # `# n( T3 X- q% ]
- : t. J9 c1 t: h' U6 x T
- }
! Q& Q5 A l/ v2 s* B6 K - . G& V4 R3 M- a* w
- }; }- w5 G# l4 N+ r: _5 m
- </span></span>
复制代码 & K. a. ~/ C% i' ^# M) S6 E$ F
总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。
' q5 S# C6 j4 z- @( O' K+ G/ T. \3 j, ^3 U. E) J% i8 |+ _
' G; W2 E( o1 T1 O z( T$ h
. I& N" v/ |/ O6 D: F. n4 G# c1 |1 _
好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】
' a9 t3 q0 c& _% X% u
2 ~9 ~9 _" g6 e {9 Z' L; w+ e/ Q; B& g9 v0 j
|