有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。5 |' Z& ~+ Y O+ b, [
$ P* i5 F8 v: z* U3 y( f' z; H7 _, R+ y
/ I( | k: v3 K" c, m5 J- ?* s
- I- _4 ^ x) M' J! v [
结合STM32G4芯片的特性,这里可以有两种实现方式。& F& K) Q: ~1 [
x Q' f6 O) q- t/ x# U# I
第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。
; f, P3 ~! S& S' x4 {9 q& P
7 v+ b1 V6 D; d4 p我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。
: J( F2 `$ @5 {1 c; v9 a" J
' D# o9 G2 L6 q% B6 @2 p使用CubeMx进行配置,主要配置如下:1 l# W4 L) y q% }' n
- t1 z0 @& G$ Q$ U, m6 z6 H
$ u8 d" w. }: e& h. ]
" V# Z/ x: P, G
4 B: Q! w9 N% \( t$ {) t& \0 f4 f6 H( i$ P; Q. a
" ^' h! H' w1 p- U6 d! R+ J
% i$ D. \+ P) {& B1 r+ K2 n完成配置后生成初始代码,再添加下面代码即可验证测试:8 X! m9 U9 P( x& ^( e9 p8 j
, Y# B! R" x& m: N4 e" I' @! h- #define Tpai (2*3.14159)( `, K8 H" N- ~
- C5 E. u/ Z z3 u0 f( L% I
- uint32_t PData[200],Dac_data;
, Z, ?& y7 K1 I! V7 M - $ H, q4 B' @' c% _, Q- Q/ x
- uint16_t i;
7 [7 q" D/ \' s; S
) [: {8 }5 l# e) y- for (i=0;i<200;i++)) A' S3 Z5 _& g
- 8 g/ C7 Y& _% E3 x1 u' n
- {undefined
9 N* C P* P- n5 a
8 @0 ~+ }, g- J2 M0 M- PData<i>=</i>0 z* p; \* I j) o6 c8 {
+ F" X5 y. t2 w- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);6 i2 o* c A. h" S9 p
- 1 l5 L/ ?% O8 T* c. w& B2 T
- } //prepare data for DAC) z1 x; h7 C* k& m
4 w4 ~/ ?+ i: K- N2 }- Z9 g- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);
* e* C% t- v8 N1 u! R
6 c7 b- c) X& m/ n2 Q- __HAL_TIM_ENABLE(&htim3);
复制代码
8 \7 b4 Y* p& L i: @9 q* m
5 W8 E% P+ X" M% T$ I* h3 l" O6 t, J. A! u# ?
上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。
: c0 B8 s' w; [9 V
. f7 ^! \& v! R& z- n' l( o
, [* v) @" o+ s) w1 M$ Y$ T! }3 Q, J0 ^4 b: T
其它配置不动,代码稍微改动和整理下即可。参见下面代码:" ^/ G; O7 r- [! H% Z
# _4 l. S' n( {" [- Uint16_t PData[200],Dac_data;
! b& X5 T" j' E H& l - 3 u( b7 R/ @! S( h. d. b
- #define Tpai (2*3.14159)
( v4 C) v, } m; w - + ]5 A$ @0 U4 l( z" P3 {
- uint16_t i;! L1 G, T3 Z# j3 Q7 [' {4 L
- 5 d3 b* ~' [0 K& V/ H
- for (i=0;i<200;i++)! G% X9 c. N- ?/ I, U: l0 w! X# O! T
- 4 [+ U9 ^6 a+ f/ u
- {undefined: ]+ B/ n) S9 D- \- Y% K8 S/ U
- 3 g3 d; L! `+ X$ v# B9 j& { f% b
- PData=1 m, h) }: p' B+ X7 E
- , \( x+ k# n! r4 |4 n( B
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);4 x! ~' G, P. s
- , R8 S9 u3 Z$ e
- } //prepare data for DAC
" ]3 Y! w& G! \$ y! T* ? - 5 g4 M1 U: L, C5 }$ v
- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);
1 B1 a6 U8 a+ k5 A - 6 ^3 v( V: N, C# e3 B" T
" j9 [! v% G. x8 G9 G, H- 9 c6 w2 E" a, J
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );1 q3 [9 o% |8 J2 J
- . o3 G" d" h2 a
- HAL_Delay(2);- q: J2 u) A; j& l
( o" o9 M/ Q6 R0 L* n; O; L
. ]& L% V1 [1 m- ! x g. M) r/ U2 d k
- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
1 x9 T1 \- U, T - 8 r$ Q6 d% {9 v5 f; w
- A7 A8 Y! n! F" Y9 X4 h: D
- ) p9 A6 O9 r+ B3 b/ @/ V
- __HAL_TIM_ENABLE(&htim3);
复制代码 9 q- z4 |$ T7 [. v$ ~" k
/ T* _1 c3 b& I* p/ \; ~8 z8 s/ k+ U* \& E. e) z6 r5 `" q
0 |, A R, h; I
第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。$ }+ x3 A6 I& U! j7 U
# @+ Z5 F- O! @3 v- X基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。
+ s0 B Z2 a% R" f/ O+ i% R$ h
+ V( ~8 N2 D0 i/ n x. d, j
" e- M% @! b N1 q! k
6 c$ [1 i# S% H% @5 `! C/ B
v! b$ F+ y7 B5 h! U主要是DMA请求事件变了,其它配置跟第一种模式基本一样。; o' x/ z h) W7 F& O; I
/ c6 a( Z `# {- S4 Z' F: C% M
配置完成后生成初始化代码,再添加下面用户代码: d' \( u' x) P# v& R# z" e
" \" W* u5 ^& }; ]; U6 q- #define Tpai (2*3.14159)! O! B0 J3 L7 [# t* L1 X y
- " \% z. N0 C; S& o( t
- uint16_t PData[200], Dac_data;
1 p, J! R/ c* l1 v: p; N2 ]" i - ) U4 E( o# c. b; q. P
- uint16_t i;
7 E$ I/ }# @1 H7 G }; n - 6 O: E( v/ }, u& p! j- [' A
- for (i=0;i<200;i++); o- O% Y# x7 i) n& Y U) i. w
% z3 u, ?( B9 e0 A+ v- t; i4 X- {undefined
6 d4 s" _3 p- B+ t" m& X. ? - 3 G* g, e6 p2 |# K" [! z/ o
- PData=6 x' e# T I I
4 R2 k0 w( t% f+ R- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);6 x3 K. D9 x3 h: }( ~6 ^- N
) E8 l* k/ I0 ] \) ~# _2 ?- }* L3 r7 ^" ?( d9 s% f. c0 f4 y* ^
$ M9 C9 c; r% o. u- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
; J/ O* }- _6 a: l/ q- {5 ^ - g i4 T0 E! s. f5 `1 X
- HAL_Delay(3);
7 U# {/ o( ^4 H0 C6 d4 p
# [* R e, N, W/ k0 a: [2 P
6 p( D- m% u! z0 |7 [( ~+ ]- $ r' c8 E7 b1 D' v& k q/ i u8 Z
- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
+ j2 A8 M0 G( \0 ^
1 ^$ F8 ]3 E# g7 z
& Q- l' w( m1 @( ~2 c- % q& |% r" }' Q% g/ {4 z
- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);
# e6 g) j- x! p4 C, {2 g
3 ~7 Y/ J' x0 X) l$ q9 h9 I- __HAL_TIM_ENABLE(&htim3);
复制代码 9 y8 N9 [6 T* s" b8 q/ s
& u+ N8 w) J' Q" G+ N' l
这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。& m7 c* n2 s+ z' s* \% l
! ^! G/ c7 i# z% H Q* J如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:9 w8 w# c, c$ [9 G- y& U
2 m. u8 @& d. R# t6 Y* }( A1 @& Q5 G: m# M+ F( l7 C% B5 ?! h
6 r4 m% e2 D' E0 I$ Z& x1 ~! G
& M: s G1 P h1 f相关用户代码如下: % F" t. e/ Y4 A# t* g; p
. m! z6 T+ w( M8 D0 T- #define Tpai (2*3.14159)
' s# l) Z, y1 `% o9 |+ t - 0 n- y, H" l! g# O- L$ f- b2 {- W
- uint16_t PData[200],Dac_data;
4 C& t* @$ X+ H7 ^! Y: k: c/ x - " {+ u. |: c4 l$ G5 l
- uint16_t i;; p* G* }1 A) a9 F3 B
- ' Y: y2 d0 O1 }" P
- for (i=0;i<200;i++)
* h& f) L0 M9 s - 5 R7 M x( d- K& u: G
- {undefined
) V7 F$ n- ~$ M8 V2 R- j, ~0 O
4 x# f5 b5 W- _8 o9 `/ f- PData<span style="font-style: italic;"><span style="font-style: normal;">=
! ^4 Q' p6 G5 p( p - " \+ Q4 q2 J v
- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);
: t8 A. K7 ^1 ^( ]+ A: Z# L - 7 P. c8 o8 k6 ?, b, c4 |. B
- }0 U& U9 `# A( N! N+ C8 y
; _& d1 g9 t6 T. n9 m5 s- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );& v& A0 m, M! a7 A
- 8 ^; J, }6 A' l- ]
- HAL_Delay(3);) Q- y* t) D* @. Z I
( B( o* P b+ i/ J: Q3 i* W
7 k8 ]$ i# U5 g) d- a
" }( ?- H" T9 d3 C& v- while(1)
8 t X6 d- r9 v
- Y' R1 K) u' @- {undefined8 n9 Y9 t) y2 r
- ; w3 } ~2 N/ ?3 S
- for(i=0;i<200;i++)
" l; w# R4 ?2 H. c6 L2 a - 7 v, r+ K i2 e& I) v1 n/ p1 M& b+ u
- {undefined
4 c' I$ B2 @( O+ ^' X) K
! a, Q% I8 p; w' y- DAC1->DHR12R1= PData</span><span style="font-style: normal;">;
0 K/ o" O9 D; v1 h4 `+ C - 0 T* |7 R" A$ I: e* l4 ]
- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);' W1 h$ ~8 K8 M# e
- * S" A0 `* k' M, o3 n
- HAL_Delay(5);0 S5 W8 R! j' L( w, E, y
- - ` T/ H% K" t' C% ^$ z
- 4 H2 f% J9 N2 ^& p" k0 t
- ]# N4 U4 ?+ h. `4 n4 L
- Dac_data = DAC1->DOR1;//for debug
5 d% ]5 v, v9 C6 C' x - 3 q `$ A" l" b" t9 t/ ?+ B8 b5 {
3 H$ S, i4 ^; ]0 `% E- ' t2 D6 q+ S" r/ [# s
- } y6 q# @4 b4 j8 i
- ' u) ^3 D3 j3 h5 X
- }/ \( T @7 }! K9 f. f* n4 x# i. m9 ~
- </span></span>
复制代码
/ w& A7 E6 E& O: ~: e5 g总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。2 c3 O8 [/ f9 f; E4 T1 p* K
" L" @5 I3 c1 U) x N( I0 r- }
1 J* {2 u; r5 ?' {/ h U9 ~. X) y$ U
好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】. B; o) u9 [+ }
/ s6 k3 h8 g2 [+ ~, E& V1 j8 E& C; E2 [7 X ?8 w
|