有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。
7 M0 G R3 _8 b) t2 U4 D( z+ x# m2 @5 Z" m. [4 C
- ?- p) ]9 P6 y9 K
# Q7 y* I- R: a$ o
' J' o% L* X( e, }
结合STM32G4芯片的特性,这里可以有两种实现方式。
4 }4 \4 o( a. t! Q0 l3 q8 T# b1 b) y7 M1 E* i) W$ Q
第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。: j. M: A: I& q$ y& I) f
3 O5 G& j9 J7 k e% j- p6 e
我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。( c. d1 U7 j; k' @* A/ N. c1 @
! c8 l. C) Y* `6 e/ e. o% h+ k
使用CubeMx进行配置,主要配置如下:: O) p' s3 T" i2 L$ ]
: e7 T1 A: m4 Z" b, R8 k9 v9 L( ?& S% s' C# N7 ^$ q5 {, v0 Z
P) o' W0 I. S/ H* ^# c; X" l
. [0 I w7 {6 e( T6 E8 z: _0 H! N- f$ M: ~3 `) t
( I& `0 {3 ]* `' J
" E* n, h" p3 y. w/ U% C6 l1 p完成配置后生成初始代码,再添加下面代码即可验证测试:& r# u) t; P/ q; G: B
/ A; q) u; r* \
- #define Tpai (2*3.14159)
3 Q- l0 N% U8 @ - : J! p% A% O+ `5 z* u- q+ ~" y
- uint32_t PData[200],Dac_data;
# Y6 E# g: O+ q/ t3 M0 o; Z1 \' ` - / z! q8 ^: P. s" l2 k: y4 P
- uint16_t i;
+ C; N# T2 e* X& Q8 o& b- Q2 t5 d8 b0 I
1 g/ y8 @& @2 K: T' Y- for (i=0;i<200;i++)* u8 n) h) V6 F5 P4 `8 M- G
2 S; z9 F: G5 n3 G: B4 t9 H1 u- {undefined
! X( l" }7 j5 o y, Z' Q - # ?( c1 L6 Q+ Q
- PData<i>=</i>
: T( P+ E; o1 Z& K0 T. N
( B. q) d, f$ P8 R2 N- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);; A; u2 G* M& D2 D i5 c' z
- 7 R" _: l3 q/ p7 F5 s
- } //prepare data for DAC# f" P; F9 s1 ~& {6 h' X
- ; [, c' A8 O. n9 P [
- HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);' L+ L: |# G" {1 u
- 9 K, H9 k2 \) |( T/ H
- __HAL_TIM_ENABLE(&htim3);
复制代码 6 ? ?2 e$ \+ [( i1 i2 l n
0 x" }/ x( v1 D/ b. S
4 B3 A# R( |4 f. r' ^上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。) d) J+ P) v" V% b# v5 k5 z/ J7 s" d' R
u' p% ^* ?6 z: y
2 x2 O3 s4 y' M3 A$ E. s% h5 f6 x: i6 K' Z6 w4 v' h/ Y* D$ V+ Z
其它配置不动,代码稍微改动和整理下即可。参见下面代码:
+ V. J/ V3 ]. r5 G2 ?& {' }" u8 P) F- t' k9 g- j t
- Uint16_t PData[200],Dac_data;
5 w$ d$ K/ L0 x% { - " q) X- E6 r' l9 r. a# w) J
- #define Tpai (2*3.14159)( y/ N, l9 d8 C$ a( R
- " Q* E: c9 k4 @2 N/ P
- uint16_t i;
7 p/ F; R# u/ J% d - & b1 R3 n! \% t& e8 v
- for (i=0;i<200;i++)
6 Q, L3 z* g. F- C3 C - / A2 ?/ n9 w% I0 x* N( |% c
- {undefined3 `: ~& f2 m8 y9 }: \; z6 q. U% K
- # d8 \% H3 h: I
- PData=8 c' ^9 c) k8 V: m$ ?8 q( \( o
- & w, N! z9 w0 e6 ^* H& s
- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);
* _. J! @3 k* j. |# S4 M
. m) e: C) ]/ w1 i- } //prepare data for DAC
6 N2 B; i6 }* u" p1 l - $ n5 X& Y0 ~0 t2 D
- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);$ c3 |( Z8 T8 [" Y9 ]2 z2 Z5 P, e; v
& b# L, `: P3 U# a9 t- R5 z
2 H5 I9 a: ?' q# x! F8 ~
3 N+ N8 h- m9 o( ]0 d* [! o- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
, I q, Q" }3 H0 g0 b+ y& } - & [) w# W% O9 L1 C! C! P
- HAL_Delay(2);7 z. _6 D" p% [# E5 H
: y7 }! c. Q) @5 F1 I- ) u$ f. y ?* |- H; A! x q
) Z& D0 s1 ^' O9 R% R' b% I- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);" J9 Y: r2 J3 U9 O9 l2 r! j
0 i1 F5 s! o/ W/ T& a* }
0 ]3 ]/ o5 H! ~8 M# v" b- 6 P" I7 H' \% x" A
- __HAL_TIM_ENABLE(&htim3);
复制代码 ' E' Y6 R( k4 N' a; ]( A
9 b: X1 M7 q5 r/ t" E
8 r S y2 L; }7 v7 L f
$ ~: w0 Q( R3 q* i第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。
) p7 ^6 o! `6 M0 C! O- z, t" ~0 ^3 r2 B2 U9 r) @7 h( V
基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。7 ?* U/ L+ T7 i
5 T; ~" W( f' i9 ^9 g! [ G0 R# n& y1 |# ]
& d& W! \! t2 B* O' _
; e, \* p1 X4 V- d' x7 p0 M! t主要是DMA请求事件变了,其它配置跟第一种模式基本一样。
# W( b& M4 }5 p7 Z
1 v, F: [' x; m& n6 h+ m配置完成后生成初始化代码,再添加下面用户代码:+ j3 x% u" F8 x" o* p. B1 B
! x4 l4 d& r; T5 _ E- #define Tpai (2*3.14159)
) W+ x' H# Y7 F
* C; P* t$ y! D0 s- uint16_t PData[200], Dac_data;
9 B/ v# n3 z) F; D$ { - 0 ?1 m( d6 H- w" _: v2 U% H
- uint16_t i;. ^4 Z: Q4 u# u, f# K8 t
, k3 S g. z# X9 |- for (i=0;i<200;i++)
/ \) e# K# H9 a, ? P' e5 m
, @' T: b! k$ r3 _; i1 m, Q- {undefined
/ Y& a9 ]* o/ a0 m: E- S9 n - 2 t- v* a; J# I" o
- PData=! V+ y% b: Q$ [# G
" O! W4 R/ F# b3 t2 U$ }- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);7 E! H; l5 j/ h- J4 o
- + L7 N- [. L0 ^3 F" r
- }- R- ^: m$ e- S2 F0 ^7 w$ {
- A' @5 u% S( S4 ~- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );* w4 ~4 p h Z4 Y* K- j m! b
. P% G, G( n8 ?5 P# ]- HAL_Delay(3);
+ W+ l( P e& i3 y
6 g# Q4 y. b/ u% B# S- 8 v& }6 C# V, F' O" w6 |
2 Z- ^# o2 e6 B. r9 f' |8 {- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
# r# T2 U# X, ]1 H/ i) A x! s - 2 R* }$ q. l2 k9 @: `$ t0 M
- 5 A. i' p$ l3 j2 Y) |
0 c% `9 O: d; I% J' s- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);- A4 [3 \2 j. q) b/ K% t
' T; R. y N) ?* E* ~% O( }- __HAL_TIM_ENABLE(&htim3);
复制代码 & e, {$ W% p6 J3 g
2 Q( G6 @' ?: o! @ m7 Z这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。
4 I' y* r. M9 @
6 D" L' g4 v h; h) ~1 m如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下: Z% V1 T; F- A% A' E8 t Z
+ |, G2 z# I3 p; ]- `* h
# v/ _, f& D, q. u( I
% ?! `3 I# F7 W- @! J/ x
! s4 J2 c. d# |5 l9 b. P相关用户代码如下: . Z* `4 Z6 ]! ]3 Z( I. [9 J9 }
' N2 Q+ }5 E& y Z9 D. K/ K
- #define Tpai (2*3.14159)
% e% e {$ m) z# M" j. c - # {! k0 O% w& N
- uint16_t PData[200],Dac_data;% M2 k1 ^8 j1 X/ v( f9 ~
" Q$ q9 ?* r# {7 h& H- uint16_t i;
) O8 ?( X; b$ \, g5 _
1 R9 I! } H1 O% J0 g- for (i=0;i<200;i++)
- Y- I7 e8 g5 S U X$ x) } - 2 c ^- k$ Q* d: Q+ E* v1 S
- {undefined4 C# u4 n1 S2 l' F/ `; o
/ t; x- O5 n% S @5 C- PData<span style="font-style: italic;"><span style="font-style: normal;">=, f p, }2 Z) e* D' I
0 |2 u* I9 B# c6 n6 M- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);
1 ]0 \6 U* g% ?" w: v9 K - - |( X$ p# G( Z* X! P8 \
- }+ b' T, S! ` [ W+ D) t/ u
* b- q% g% `: V: K, x4 S- M- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
' ?* `4 C3 S% J6 L5 Z
3 I% ]* s% g& n3 ~/ R- HAL_Delay(3);$ T9 C8 N$ a" R: Q) @5 M
- # M F: p1 M( S) {5 E, [
& K9 C7 K5 W8 e: x0 {- H' }9 e2 B2 B
) n+ ~( R, q+ d" N9 b. X% z5 l- while(1)* ^+ M+ O7 @( Y$ l# A: c
! |. b0 M. z1 I8 k C( a7 m- {undefined0 C1 r8 J6 x4 D* s, v! h6 I
- $ X: }6 h; _. ]1 {3 \3 q
- for(i=0;i<200;i++)$ F. L& {6 c: n; p0 c( w
5 P! Z/ y5 c; k; d& i" M( W- {undefined
3 J3 W9 o0 [- }$ k& [ - 1 C2 u4 L4 t! U# ?! O" Y6 I+ @
- DAC1->DHR12R1= PData</span><span style="font-style: normal;">; z4 f8 z" i' U& B% A7 Y. N9 p
- , \) [" r* n" Y1 W; F
- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);# ?% d, w0 l$ {% A. X, o0 d
9 C+ |+ X5 y$ f: `% S- HAL_Delay(5);% E3 t! n0 g: r; V% @: G" ?! Q8 ~
- + r Q# K x" ^4 y" ^
; z6 ~4 ] z, \( J* H
& f8 ~" v* o2 ^4 f- Dac_data = DAC1->DOR1;//for debug
/ s" T i, c2 r9 {" u) _' Q/ R
4 k( l: C; z4 U$ x4 Y1 J- % d/ r- x: {; q% Q# s0 \$ V) M4 L0 H |+ V
- ' j* k( Y) R5 ?2 ~% ]/ T5 \! ?. }
- } 3 K# S$ j7 \. c6 ?
- 9 k0 T: s9 i6 N! ^% M
- }
4 B/ K a! |- Q2 t( V+ D$ T - </span></span>
复制代码
$ R: M" }( v8 e0 K5 c& Y( u总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。- `8 p1 o. _6 D8 A! _ v
1 b6 K* v8 y# o" B6 V2 ~$ n" {# q
' A9 s5 s1 e0 Z$ E2 n6 r: ]7 W& y5 a: o; K y9 o2 i
好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】( E& O, s% ]1 x3 [. N
% s2 w7 K. Y- l/ H/ [+ ?
3 Q8 E3 T, l3 p( d7 _; i |