有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。% O6 l" N: \. P6 A4 l( C! U
9 |3 y, y/ u; t+ C/ O+ h8 n4 f) z! @
+ @) w: f$ V9 e) |% `2 S6 e+ t
- i: A7 [& J/ b# [9 `结合STM32G4芯片的特性,这里可以有两种实现方式。" p8 j0 X7 I* o' O* S X$ U
$ Y* f/ I+ W1 k+ i) Z4 q) g7 d R% O
第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。% m$ o! C Q0 [$ x3 B, C
3 M# E: f0 v6 M% z+ Q我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。9 p9 X3 x$ [# E$ h/ N5 r2 B ?
9 i% r, e8 c" ?7 d
使用CubeMx进行配置,主要配置如下:
; U' K- ]) a# y, R, p( }% v6 l k/ R/ |0 k
6 w' d) z+ j! r
6 |7 U! a# E1 X9 m
# A' J/ o' |6 l- m2 h
J% Y9 j- e J" L7 A1 k6 `+ u- o# R' x5 G, C7 D: k$ j* ^' }4 o; N8 x
" R' B" d# e* Z( _- g
完成配置后生成初始代码,再添加下面代码即可验证测试:
2 i/ f N, J! G/ t N V
9 Q& G5 W+ W* @2 p1 @. ~6 P- #define Tpai (2*3.14159)4 p- ?1 Y2 X6 d# V5 r! n+ N
- 1 E4 I' ?7 B1 P5 e; D
- uint32_t PData[200],Dac_data;% s ^1 r! ~' |! T" R! K. {
- % V) N0 X4 B( [% Z% ^
- uint16_t i;3 E: G( _: ?( [9 u* q& d8 S2 u. x! c
- ! Y5 b; c+ H; \- c; Y4 ^, q$ i' V
- for (i=0;i<200;i++)0 i# `4 Z. |0 l" D; l% b3 d* _
- # e0 r% c. N) ~& z: ~: W
- {undefined+ O0 {" J4 O; [" K2 h
- # ?0 U9 I" W+ [, M+ n5 s+ g
- PData<i>=</i>
! G& u0 B( F# v1 u0 r7 V - 8 H- j9 N# |7 | Y1 x
- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);1 e: g1 _. Y) c) k& d! E
# q4 {3 p+ D' L. W. W- } //prepare data for DAC
7 ~* y5 M$ Q3 ]7 Q! o9 d4 f - % k* R& l* Z, S, D' C
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);
/ w$ o) X1 Z# E6 ` - 1 R: s8 z/ F; W
- __HAL_TIM_ENABLE(&htim3);
复制代码
. a$ x8 l, k$ X& }& h9 g- ~1 h2 V- ~
2 F& J# H Z. f) e; F: ^
上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。/ J; d+ n3 G6 ?) N/ @1 o
* u& ~" E( }8 @1 A
Z# S- a& {3 F! B2 C8 _ X5 l# \' G, F
其它配置不动,代码稍微改动和整理下即可。参见下面代码:
* ]1 h* w1 h8 R1 {/ t. n
+ `* j& q# i# Z- Uint16_t PData[200],Dac_data;( g5 f+ V. L* ~- T# k# y. M2 `3 i
- + u* V3 [0 f. U7 z9 h" ]( n
- #define Tpai (2*3.14159)0 M2 a2 \ i+ e8 O( O
) e9 ~1 v. U2 q5 Y8 T- uint16_t i;
, l2 u q, \; a8 [; U+ X
5 _% E) N5 B- ~) I$ T+ ]- for (i=0;i<200;i++)
2 B4 E2 i7 W# W0 V( F8 ~3 m - * \# z6 t9 g6 E5 f
- {undefined
# b6 c, q) H. o/ B; [: {5 ^ - " r k4 R$ B" q d/ E
- PData=3 q+ |! i& k7 j3 `
3 B( r& a/ Y, x, a2 {- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);- v$ G; A/ g4 n" O* R5 _5 T7 f4 ~0 ]
- $ Q& Z( i, v% k
- } //prepare data for DAC" D C( m$ d+ l# O9 H6 g
- 2 X2 s4 }5 \: t2 p& _( [; ]
- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);
# a$ V3 [8 `7 @: L9 G - 8 \4 k4 r$ W# e' T d9 Z% F
8 A# c1 F+ E1 h9 k' G T- % j/ W/ W; ^+ w9 [* S/ Y
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
8 \/ y3 V+ O5 ]9 n; O
6 Q7 I( y) I8 \! @- Z0 z- HAL_Delay(2);5 X$ p/ h6 H. C R- ]
- 6 Z/ B$ H, r4 a
- % m8 S h4 \0 r' G4 i) u* V" H7 a
- 0 B7 z0 ?( E: H
- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
; a! }5 a* p7 k/ r - 7 ~; W3 o w2 f- S6 L! R
7 M% e# m, Y6 B# x1 f/ ?- + N# W& D4 J" n+ z, h8 Y
- __HAL_TIM_ENABLE(&htim3);
复制代码 ; N# D7 E2 B% o/ |, d" r, r
4 {/ u5 f0 W4 ]+ e
6 t" o: {+ E* g U3 s3 R1 U2 T4 v* s" @$ |
第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。
% C# a, O: f8 |& X
6 U: i$ ^; d5 R. ]3 m% y基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。9 B8 U+ l) S; Q* J& g% u) y* m
# P* u) m0 a) t9 n4 L$ a/ U7 L8 T- T7 _
1 U) ~* p, F5 ` d1 Y
* D- ~& I/ ]( S9 g6 @主要是DMA请求事件变了,其它配置跟第一种模式基本一样。3 x( u* m: b {7 B E
5 Q' ]6 V5 {/ c0 j- x配置完成后生成初始化代码,再添加下面用户代码:
+ j3 Q8 Y9 a4 p$ ?9 d: N
0 q% L% t5 R( X ], \( t- g- #define Tpai (2*3.14159)% ~# O0 r% M( Y! d0 x7 C2 f3 e
2 }5 q3 u( v7 n0 G$ h1 f- uint16_t PData[200], Dac_data;2 E& ^$ C% C( h c1 l
- & w7 d/ j8 ? T0 n
- uint16_t i;
$ A. @9 b. i! W t* [5 n+ Q4 Y
6 R9 ]/ t- r; M7 q; W H- for (i=0;i<200;i++)
/ C3 O2 z: Y [" O. N8 X- Z# O
' C# r: q% u0 M- v- {undefined
# b( G+ f& G9 q# e5 N! Q - , y9 S7 ~ g& R5 G, W
- PData=
7 k( n0 E2 M; U2 C8 K - ' V9 f) r, P6 G$ d, K# y$ s
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
- b2 L# j6 Q5 r3 W; g - " L5 l1 F& y' S& k+ b+ r$ v
- }
. T& i' X4 d U' L3 T, B
3 F6 Y8 N7 ~! e: r# r- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );; Q8 F8 A# t& z0 ?# `% r7 G
0 c o( P; }7 @+ a- HAL_Delay(3);% ]6 l5 F4 }# \6 t
: g0 ^) g! @+ I" o0 [- r- % J/ @ K- i3 E0 @, E
- 1 F* u4 Z- e6 i: C/ E5 y8 z( p
- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
$ b7 t$ O* S7 f* U9 s; X: S6 A - . O- o3 w, R# e6 @. d
- 8 K# A H* H: j; t1 i
% L; i' Y) |" }' ]- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);
$ n. \8 L- c+ `6 G A1 T" E - 2 g3 C: H/ z, ]$ {
- __HAL_TIM_ENABLE(&htim3);
复制代码 / d$ ^) ?, ]+ r7 @* M. Y
: f% E# D9 M# V" v8 D2 L* D
这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。
" [' r6 m1 r' X! L2 {
" i% v$ x: e$ \& G3 Y如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:: i ]) b6 P8 d2 V3 m2 V. c
9 H0 R* j0 ^- [6 t6 O, I
. i$ H& _8 v Q/ F# z4 O8 S D( [6 b! ^
( Y: @+ `$ P( k6 y, s5 n相关用户代码如下: ! ]( z: T0 P# P# Y2 t
6 ]' J, q6 q$ j
- #define Tpai (2*3.14159) w1 X& Q6 m8 H0 e! ?6 j
. J* O" A2 V0 o. \) Z% a- uint16_t PData[200],Dac_data;) l$ ^& {! X& @2 R! R( ]. b
- _) F: U: V1 r
- uint16_t i;
1 i- W: |/ Q. [# @8 [" ^
* s: b1 O5 v) w7 V; w" }- for (i=0;i<200;i++)# `# Z8 S& l* v# z
k f7 m5 v- d$ X0 c9 J" R- {undefined/ |8 S0 g% W! L$ K, g
7 Y" E% F; V( |- z$ p" o0 G2 }. n- PData<span style="font-style: italic;"><span style="font-style: normal;">=
- Q) O# K' S( \- g" V" ^" Y9 m - / n) X+ ?( I8 m
- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);
6 ~8 t/ i z1 k, B% r
' j% W0 }& D9 n1 Y- }
7 c7 I# y/ B% V: H - " K% y7 `7 T2 n5 ^
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );9 b* E3 l3 `+ A1 G- t# i! P
- 8 D) O+ m, |$ g$ o4 I
- HAL_Delay(3);5 S# P( ~* F' ~! c' g4 g
- : u7 _& D) r# [: [$ z
- 0 P n6 @* Q: G" |0 i! V3 x
. L7 r/ K3 Y7 M+ D. s1 D- while(1), w& D: Z% c/ j
- + D# H" o+ L* I, t- ~3 S& I
- {undefined& N" S, ?/ N4 u; L$ X/ j' C& O6 [
- . a+ N3 u8 t! t( D7 z6 q
- for(i=0;i<200;i++)5 y7 l5 I/ |( H- t! ^
- D5 _1 _( S$ C' o- ^" G( a7 K- {undefined5 p) i- w! L3 R2 Q; O! Y5 ~# S
( D5 f9 w e+ n D# C4 D- DAC1->DHR12R1= PData</span><span style="font-style: normal;">; 9 }) o V+ q8 F' v) ^- t
- + O2 x5 l7 \' V
- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);/ d* S( V+ F; r
% c" u9 F+ ]& n1 t& A! m6 f' I$ r0 t/ Y- HAL_Delay(5);/ v, j6 e" P( \
- : d1 X* i4 O+ \* k# ]
3 W+ _) n5 B) \- j' x- ; @7 }( N3 m' Y# M" _
- Dac_data = DAC1->DOR1;//for debug$ {, b* K( f& }
( B5 X: `: u; S' s- , b4 s( M, E! S& b5 Y
- # z1 X+ Q: K5 _8 ?: g9 j+ C
- } 0 u+ H( f7 N/ ?& V
- 5 p& {* _8 o0 l; r, d# Q6 L# {
- }' V, H% H1 x# ]4 _8 b% ]4 q+ y, a
- </span></span>
复制代码
: j; ~- t; s" C" f& h总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。' u- R: g+ a% X' b( @$ W! Q- a
( |3 n I- r" | B8 @3 m) h
! e: n G$ z- L+ Z) t
7 S1 ?; Y; Z' P9 s9 Y好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】
* v3 [. D4 ~3 q1 X6 r. x2 a# w8 K6 x- P. Y
- i! N" b* V9 c9 R1 h |