有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。
% _3 f$ c) ~; K# P9 @: {. _4 u5 `9 i* }; g4 k
6 q$ f1 a: |* a' n
* x; [" n: b) F# L9 C; w" s, e
3 t* p. \1 _' J' ] g7 F2 a结合STM32G4芯片的特性,这里可以有两种实现方式。9 f" Z( l4 a, f- g8 I6 K2 R
1 h3 ]# W/ j4 g; y& n
第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。+ H# O9 f7 x9 j. L- b6 g+ ~: l. p8 i
# Y4 [- t7 D# {4 q
我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。
) |) s0 w( z4 f& f- @' Q
, g8 ^. Z9 i* E1 {+ H+ H1 |使用CubeMx进行配置,主要配置如下:
; }3 G) x% U2 ]8 R( b2 R# S; P# k# D; H3 g; ? o
7 L# R8 n( E( a4 k3 G
! J2 Y1 Y( ?2 C% y! v+ B
# p6 _; i9 L) C/ N5 \
; l" X( j- Z6 f. d, {& R0 N
# ?/ v0 @* b7 a) v# w5 J
& T- j& N) k% K完成配置后生成初始代码,再添加下面代码即可验证测试:/ i4 ^6 H3 o( B5 w3 v0 ]
/ j2 L) Z' \# ]
- #define Tpai (2*3.14159)
; h/ E; b: {5 ^5 ~. Z+ o8 }* G/ W. o - / j% o; @/ ~( u# X
- uint32_t PData[200],Dac_data;2 ^- @4 f3 T) P# b/ L
- ) K) K* ?6 o8 u9 t
- uint16_t i;
! @3 K4 h& n# U M# @( {6 k - 5 g! b3 J- C+ I/ e. @. H& E$ i; q
- for (i=0;i<200;i++)
a% W6 B% C& E; Z4 k - ^( Z5 c8 t) M W0 q; g7 K
- {undefined, K& [& V4 o: X( c( ~) |
/ ^' f: K2 c' x! D8 N- PData<i>=</i>
# W; d% J2 d- I$ T& \
! [: O3 ? }/ w- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);. [7 m1 R3 b& Z a4 `( Q8 j
" |3 p5 F& U, u A# V* Z- } //prepare data for DAC, Z2 [+ z% O5 J; T4 K# e
) m# u: O0 s7 a" ^7 u- M8 F- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);& j9 w9 ]* K' W2 q
% v6 O3 B; v7 k5 g1 c: S- __HAL_TIM_ENABLE(&htim3);
复制代码
; z. U! ?4 h& q/ k ^3 U! `
% ^# a8 }, L5 U8 Y1 d9 y- @4 |) p5 Q# l
上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。
8 y, Y) e+ H; [# @% c' L- m; [2 ]* e. _& g; n7 L$ t8 N
) j# \2 ? R# O- D
' ?1 Z! C" d* p" J* j& N其它配置不动,代码稍微改动和整理下即可。参见下面代码:
5 P! ~5 z, a# ?( C* w8 V6 T& r5 z I1 Z3 z, }* p
- Uint16_t PData[200],Dac_data;
. A+ i! @; X" ~$ z - - z( a6 S% n' E0 r4 p
- #define Tpai (2*3.14159)
' j# {4 |& k/ {: M/ P! W7 B - " A* G/ @, t `; C9 I
- uint16_t i;' G# P! A5 q7 I* j B% C+ L6 T
- 1 O9 ?. K' X3 f- K
- for (i=0;i<200;i++)
& N& ]# P6 V, h8 R# P* ^7 L - , C0 P# G/ {4 k5 e' }) z- B: i
- {undefined
7 u( O2 R- f$ u4 I( o7 K - % e; V X- m* i \8 g& W+ A
- PData=# D) R ]: @# v$ [7 L$ e8 R
- ) T* K9 x4 K: L5 U f
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);# D+ s' ]1 p% ?+ o
$ w. z. K. Z2 ]5 J. p8 G7 E- } //prepare data for DAC; w- Q; f/ |) L, z9 \4 l
- ! ~: ]% O( _; N# x
- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);
9 T5 |: f7 V m7 M - 6 Y' b+ x- L( B* ?2 ~
5 l* X4 L* N7 @* I& _, K8 [8 j
( ?: ?/ e9 `" m4 @ }- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );/ h) f; C9 O: p$ y% e! k
( A( T7 h! y; _- HAL_Delay(2);
" x! k+ E5 a8 Q2 B- i
& f4 _: ~& X2 f5 E A+ Y- ; [4 V- l3 F' A) C6 i
- % D c3 T$ o4 s
- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);4 {, s# d- F) ]+ Q8 B
. K5 s, G# y! \, m- / O& y0 B1 K. s) V9 C; g7 |3 a& c
P* t9 e6 v l( }9 K. F' ^: i- __HAL_TIM_ENABLE(&htim3);
复制代码
1 R7 g4 n+ \" P4 i1 p) T1 a k' t1 O! `1 `6 q3 l8 B' M
) _% [, {3 O/ b: m5 B3 n! V9 Q, t. t& ~. t, }
第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。2 a3 `9 Q; g+ d& b8 r1 j0 E& J# [9 L- Q/ J
$ y2 n! t0 d5 L a7 Y% A基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。
3 O- i" y6 z, w9 M' T0 x5 x1 b. R" p: [2 ]( \+ `
8 X* @) |5 F5 o2 v4 B: X" d% x. K( e( h* B5 `6 z( ~( R9 r
# w7 |5 V+ n: C% F4 B主要是DMA请求事件变了,其它配置跟第一种模式基本一样。
# R& o* J8 j- U" q& {; I: O4 k9 c
5 b; W2 v1 N( w. D# |/ D配置完成后生成初始化代码,再添加下面用户代码: `/ b$ |- d' R$ c* X7 s8 k( f
u! i' z& c4 ~
- #define Tpai (2*3.14159); }' B# }* e* d' {/ @6 M* Y# `* L( O
- ' }; y( j, q1 c$ E$ e7 ~7 X! u
- uint16_t PData[200], Dac_data;
. B% U5 `7 d; Q2 \, ^& A2 A; D* k - 0 Q) q3 \. w. E7 p& d
- uint16_t i;6 F% K Q* O. P/ r
( o% T# }4 Q3 J- v8 d- L$ \ T- for (i=0;i<200;i++)
5 f7 r' J$ }, ^! S" o3 q - ' D- b# x6 `3 U( `/ @: |# ]
- {undefined
- L1 g$ a3 X; M8 \
* V r% ]+ _" E8 g- PData=3 m6 c+ }2 L1 \
) g' E& p1 m+ [; E- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);0 \2 V8 `# F1 ^3 m
, V& Z: R# @6 v' o- }7 q3 z' C- i* K. s& W8 B' ]* E9 a
$ s; w6 p( ?9 w- E. m- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
2 K4 c( F; V; `( R3 @. m - " ?: ` [2 p3 @
- HAL_Delay(3);
4 _2 M9 s; A5 k4 n5 [
1 A! r7 ]8 V8 d
/ `- Y5 j0 K0 ^/ O% X6 s8 ]- U/ t# Q" f* Y; |5 w* q# Q6 Y
- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
9 ^( t2 y+ x9 J* V - " b& o! N/ z* D6 j+ e9 x1 ?
- - s1 K7 @+ Z% Y0 T/ e: Y* m5 P
- 7 O6 w! ]- n/ B6 n1 k4 D
- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);
; x3 x) p* M. j$ F* P- |
0 I& x8 q/ i9 A3 V! R- __HAL_TIM_ENABLE(&htim3);
复制代码
( B* N- {2 C. Y# ^$ p" ?! f% n1 G/ k/ k8 Q. J: e: m
这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。 v4 |0 i1 x5 t7 m
9 s4 h% C" {' Q, r4 }) o1 h
如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:3 W- q3 M6 i5 a! h% }2 G/ {/ [
; ^1 R# f% |% `, R# o- K `( y
0 E0 ?1 n) H5 q1 @/ I' Q, |( |! A/ e8 q. U" j
& ]4 ]# {! l7 r/ ?
相关用户代码如下:
O1 ]+ d( D9 _3 ] R6 i3 F4 X5 [/ Q4 h4 P) x
- #define Tpai (2*3.14159)- M V: t" e/ h' T7 B# c( h& \! \
- 2 T2 I2 o0 A# r; ^( s' v, N; Y
- uint16_t PData[200],Dac_data;
4 M0 I" _+ t; j' p$ X
5 ~+ n% ^( N$ ~& T2 `/ ~- uint16_t i;' O1 C' J3 e5 S, |0 }
3 h D& M, I6 `9 S% ?/ l: G7 x- for (i=0;i<200;i++)# b, f- t! [1 |( E4 ?0 h0 R
) p% N$ v2 V3 ~! y2 B. ]- {undefined; ~" z) ` D) S5 V# U9 c( ~
- # q' Q( q+ V7 P V1 Y1 p8 R
- PData<span style="font-style: italic;"><span style="font-style: normal;">=4 T1 D+ ]; z, e1 r# w) A8 D
- ; a3 K8 q i5 e7 \9 ~# G* h1 W
- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);: p7 a( t8 |1 T% H0 g7 P
- 9 S5 u' p9 Z& Y7 ?
- }* [$ _6 f- e; L" a. E7 j0 h
- 7 j3 _) I3 @9 s V" N- R3 S3 l7 x
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
* G* s% d& Q1 k. L$ s
1 @; r) H. B8 e+ z7 R- HAL_Delay(3);$ |! n1 H0 L6 \% v h2 i- i5 B# Z
6 _5 L! D; g/ m, o2 k$ ^ J
8 v+ i3 |3 C }9 u7 O( m
- d9 J {) @# f. r- M( }8 y9 m- while(1) F; p% P. S+ {& J( j0 }
5 J6 B) N& ]6 \% R. C5 w3 D$ Z! \# w- {undefined) x. m& X0 \9 Z! c: N8 q; [: J
, A2 v& K8 R1 L) P6 l- for(i=0;i<200;i++)
+ o" R6 b* P! e! Z
4 }. ]! h0 A7 L8 K' g! k6 _$ ^5 E- {undefined
5 J4 B: @& w& j( g" N' x - $ F, a( W. U) q- X4 ?: o
- DAC1->DHR12R1= PData</span><span style="font-style: normal;">;
K3 k! f. p! Q
' D: c1 P2 Y: X5 O- R* M- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);
: R. f& C: c( a7 a. u
- E# l) L/ W& ? ~6 |( i9 ~' [- HAL_Delay(5);, u5 J4 J1 F: j' m: K5 K! X
! E% T9 q+ r1 o+ I) l% i8 ~- 7 f5 |/ I" C! x: c$ a
* L! G7 E& X6 u: C- Dac_data = DAC1->DOR1;//for debug7 s- c$ C2 ]3 o0 x( k
- . S$ t3 ~5 p) H4 y
+ u5 [9 f3 K q* [, S, Z- G- 2 u/ |5 |; C8 B( _0 u
- } : V! u0 C, r" h
- 1 ]( T* J' H3 ]5 k( v+ @2 A' M
- }3 ]8 H! N' T! N- A' w
- </span></span>
复制代码 8 \. L4 Z+ @- r- K( Q8 d
总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。6 P% i/ |# O5 {! L, s
# Z5 m j8 L$ L
5 y# j* P3 a( B' g0 v8 u, n, z" o2 y4 Q
) @8 M! e9 `% f, k" P
好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】
9 Y+ B! M. r$ J; g( {0 `: O3 G) W5 U5 D3 w2 t: j
% B" L P/ O- r3 A; p5 B7 e D0 b
|