有人使用STM32G431芯片做DAC应用,具体来说就是通过DMA将数据从指定内存传送给DAC数据寄存器,并由定时器触发DAC转换。他发现总是没法实现该功能。这里简单介绍下相关实现过程,并做些提醒。这里要演示的基本功能就是让DAM将内存数据周期性传输给DAC数据寄存器,最后输出一路正弦波。
" T% I" ^0 f* o$ E, ]* o2 B* u" {2 i0 K% I5 y8 Z3 w
结合STM32G4芯片的特性,这里可以有两种实现方式。
2 V4 _% a: \7 \4 V/ J
5 o0 @9 a5 H$ W H; K2 f% S+ k9 \/ l0 G$ u- f* @3 f
第一种,DMA的请求来自于DAC1的转换事件。第二种,DMA的请求来自于定时器事件,下面用到的是定时器的更新事件。这里就两种实现方式的配置及相关用户代码简单介绍下,以供参考。
/ q4 U4 k Y% H6 O# v/ y' B5 q$ r5 D5 e, R! K
我们先看第一种方式,即DMA请求来自DAC转换事件,定时器3触发DAC的转换。, T, J& h, @# G6 R5 X# }. G
使用CubeMx进行配置,主要配置如下:; {0 l. n$ \6 Q# b* ^' Q w! M, Q
5 b" Z9 @9 d5 ^% X
7 ?: G8 g. c4 R( C) [0 t
5 |* S' Z5 t* q, b- q8 }- c完成配置后生成初始代码,再添加下面代码即可验证测试:% b- B9 b9 G2 s. ?. y, ?
- #define Tpai (2*3.14159)
4 u3 b, v) J! ^9 s! {* F - uint32_t PData[200],Dac_data;% E: d+ h0 T! ~; [( D0 [
- uint16_t i;
, `6 h4 a: p. Z& ~) j - for (i=0;i<200;i++)
0 E1 A% c" F) j; E8 Y - {
; p o& |! I2 R- t - PData[i]=& D; x+ ?9 T- y' v& b2 ^9 t
- (uint32_t ) (2000*(sin((Tpai/200)*i))+2000);9 `) a3 T% p3 M
- } //prepare data for DAC
+ a: m" ^/ l. H; F - HAL_DAC_Start_DMA(&hdac1,DAC_CHANNEL_1,(uint32_t*)&PData[0], 200,DAC_ALIGN_12B_R);
$ B$ B8 Y% K/ \; h* E& P - __HAL_TIM_ENABLE(&htim3);
复制代码 上面配置的DMA传输方向是从内存到外设,目的和源的访问宽度都是32位WORD. 当然也可以是内存访问宽度为16位的半字,外设访问宽度为32位字。即DMA的配置像下面这样也是可以的。( c% n X( T1 d# \' b3 b
- ~8 C6 w, e6 r; ]% X
其它配置不动,代码稍微改动和整理下即可。参见下面代码:
% c( @( z. O" u$ R/ q3 L9 b P" x0 R: `0 d2 u/ ]2 k
- Uint16_t PData[200],Dac_data;
: L- \9 c8 w/ x" y7 B, g
~0 {# Y1 x6 e+ K9 k1 U- #define Tpai (2*3.14159)
F, X$ I* j" A. A( I4 I) v& i
% v/ u7 p, m7 u( K- uint16_t i;1 K& D! ]( K6 _; [
- & ~% Q, M! Y$ l2 ?: G
- for (i=0;i<200;i++)
5 p5 a0 w/ C' W+ ` w& F - ! s- { S6 X& C/ Y! N) d
- {% ~0 O0 U- z- M9 d' l
- d# ]6 ~, I% d* W: ~
- PData[i]=
2 m5 d9 u3 O+ S# k2 {! G
. j) a) G! u& k- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);, v) \' j3 z! s$ M' D& P0 i
- - c! T9 _2 G2 o3 ]) `
- } //prepare data for DAC
! P2 \7 `& e4 {3 Z - + A$ ]' I! X E4 ?1 G' }, Y' R
- SET_BIT(hdac1.Instance->CR,DAC_CR_DMAEN1);1 S# t, q/ x. B) g8 h
- 0 n5 W* c# R$ w* L0 S) }
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
8 N' n1 s' u& Y
2 {- _# E2 u9 k7 I! [. ~' c, {- HAL_Delay(2);# C+ _( _& j0 k T4 I4 ?0 {
- 9 C3 P0 X5 i8 T; q7 @( q
- HAL_DMA_Start_IT(&hdma_dac1_ch1,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);
' I2 R) l& f+ U/ x5 h1 f
1 g2 s: _2 @2 o9 C* [- __HAL_TIM_ENABLE(&htim3);
复制代码 第一种方式就介绍到这里,再来看看第二种实现方式,即TIMER更新事件作为DMA请求源,同时作为DAC转换触发源。 M, U& ~& G. f
2 R* U) q7 k7 o; Q
基于CubeMx配置,主要调整下DMA配置,其它配置基本不动。
7 i" V" V2 k9 w8 g, ]1 I! I* [" i0 d: h
主要是DMA请求事件变了,其它配置跟第一种模式基本一样。, \7 c2 O d1 H6 U9 h4 B
4 ~ `) u2 b5 f5 r! t8 O
" B2 P c+ r9 l* @配置完成后生成初始化代码,再添加下面用户代码:
) t2 g3 [; J0 ^4 r4 z2 s
3 F2 ]* n' I% B- #define Tpai (2*3.14159)
( }- b5 S; G' g) }$ F; G" g3 @ - 1 I3 F7 b* R- d1 P5 n' \
- uint16_t PData[200], Dac_data;# G* a, E* B& Q/ \9 I8 w( W
- , ?# O+ v! v" ?: M# l' f2 }
- uint16_t i;, C9 y/ E* u2 Y4 W: m. H2 |
- 3 R0 T9 x: r0 e
- / K. X) O/ k( A0 ]# q- X5 m Y$ ?
- J9 u @0 {" q5 E. x A
- for (i=0;i<200;i++)1 h1 c; y/ j& I8 E0 x1 U3 f
- $ |2 m# b i8 I$ V
- {
% l6 h" w! P+ k7 y: a" P& R/ q- `$ H - 6 K3 I- Q. k2 U
- PData[i]=
; k' R8 C1 C9 Q" _: H/ w
/ h+ L8 k$ S/ `) |. U" a0 V$ M- (uint16_t )(2000*(sin((Tpai/200)*i))+2000);/ ~/ f& ?+ Z1 Q H! `% E( m
- # X" y# U2 `, \" O4 n' E3 z" A
- }" r) ]8 Q4 E/ Z
; u) O+ L/ x- F+ e# O" z) g" M- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );
! L8 R9 k; k) q+ C2 S( f. w/ f3 n, j, M
# V( n# q# v# a3 y# G- HAL_Delay(3);
1 O) {( f& D( y m) {
" |7 e( i1 u0 h( j- 3 Z* @8 `8 j% q
- " ]9 u. h4 n+ J+ ?- \ s. @
- HAL_DMA_Start(&hdma_tim3_up,(uint32_t)&PData[0],(uint32_t)&DAC1->DHR12R1, 200);: D1 d. v4 h4 ]( e* B r* F q8 @
- 8 b' _+ S) Z9 p% u: o/ a0 O
- : l6 ~+ l* {$ V, Y$ C0 @: q
& i$ V6 M1 l5 T% b0 z- __HAL_TIM_ENABLE_DMA(&htim3, TIM_DMA_UPDATE);
) d, A1 M" P/ E6 z5 Q8 n1 o4 q - 8 b( r* h/ U4 q: p2 i7 j- \, X; ~
- __HAL_TIM_ENABLE(&htim3);
复制代码 ) n9 @& \1 e* L- B3 Y& I" Y5 D
这里要提醒一点,G4系列的DAC1的数据保持寄存器可以一次放2个通道的数据,在使用DMA传输时,即使只用到1个通道,DMA对它的访问也要遵循WORD对齐,不然你可能会遇到麻烦。
* m# s J7 J. y# H# V" m! S6 E
$ p3 i$ f6 [6 c8 L) S+ R如果说,我们不使用DMA做数据传输,只是手动给DAC喂数据,那如何实现上述效果呢?这时我们可以使用软件触发DAC的传输,手动给DAC的数据保持寄存器赋值,参考配置及实现代码如下:( H0 @& v' ?5 ~
3 _. w/ S* D( S$ V. q& [
0 G1 Z- x' k! \$ L- M# p( G相关用户代码如下:
' g" E- A3 k+ {/ j0 ?0 T. t; }4 M" m/ m5 C; ^# G
- #define Tpai (2*3.14159)
& v0 W) B$ F' S/ c- T3 n
9 e9 ]8 k. ]2 t% g' s4 M- uint16_t PData[200],Dac_data;
7 r5 C( O" x2 m* R - , B2 z& [, j" C+ f! t
- uint16_t i;2 s- b# H6 E# i5 ^" h
- 5 n, F- w4 D4 o' M3 r0 B
- W' j: d0 j( \
, F" d* z3 F0 E- P/ ^/ Z X2 ?( t9 \- for (i=0;i<200;i++)
, a/ @5 @( S0 ~! } - 5 z! j- }8 p- k* }
- {
2 A M7 t# R! x9 Y
0 \- v, R% i; \- PData[i]=# m' _% [ G8 e" ]* U2 w5 T% x
$ O) _( r+ m1 i4 W% m9 e. s# L) d! [$ M- (uint16_t)(2000*(sin((Tpai/200)*i))+2000);
5 x& g' M6 b( K; l) d3 T' m& `
# T# ]3 `- ]* q3 o1 o, I- }
0 h8 G5 z! {# w# A8 ?+ ~ - 6 g L9 U9 r$ h [
- HAL_DAC_Start(&hdac1,DAC_CHANNEL_1 );9 w x+ M* ~/ g9 b5 t* n
, v! y2 o+ h1 o' h% c5 z- HAL_Delay(3);' u! p. U5 j9 R9 R7 Q# M' A# h1 R
5 J7 R; E3 s% t) @+ k-
9 b: Q* h5 z1 `/ S J7 n/ L
3 j5 v g; O2 m" q3 o* u2 G. z: r- while(1)1 _$ v" s: [6 |0 @/ Q, j
& p2 D" O: J/ D* E, p3 z% Q1 S# I- {
& Z9 U: E/ d6 z+ J ]# ?0 n# Q
/ A o2 }& ? c0 K X4 ?- for(i=0;i<200;i++)
( j' D. u+ m- E0 X. K& r& I4 B+ C
5 F' n3 X* D3 X y+ F3 H- {( d( G* H) C) @4 A9 A/ F0 K" }
: N" C# }+ k5 H( I4 Z2 `6 \- DAC1->DHR12R1= PData[i]; 7 {+ v6 ]# A8 g( k
- 7 d7 V- M- [3 E: b
- SET_BIT(hdac1.Instance->SWTRIGR,DAC_SWTRIGR_SWTRIG1);8 V' z6 C, p. }5 P
- : B T& o4 z4 H' ^' {) K
- HAL_Delay(5);
* A3 a" ?( P: g% e: |4 Y - : ]* u( v6 G9 N$ V" s C
- Dac_data = DAC1->DOR1;//for debug" m( C W8 G! ~$ C$ @# W/ k
- % S) [/ ^( O# q" F z/ s* G& q
- } ' M0 _6 p" n* ~4 d( B" l- r
- $ p- U: M/ n" X. y' p! G/ ^! p
- }
复制代码 总之,不论使用哪种方式,都可以实现我们所期望的结果,即输出如下正弦波。$ o2 X9 O3 \; Q+ a, t3 `8 [
3 b3 `) l- E( c+ P" n好,关于STM32G4的DAC应用就简单介绍到这里,STM32G4系列的模拟外设丰富而强大,此处算是抛砖引玉。这里做些分享也是为了让其他有需要的人少走弯路,加速开发进程。【文中代码都很简短、直观,就没做注释说明了。还有个客观原因就是在微信公众号里排版也很费劲,字符不太好对齐。若有需要交流的可以后台留言。】
5 n' `* J' t( { K8 D, w% h0 e
% {6 Y( I! \! P+ e2 F/ v9 t5 V9 f0 d
$ s1 _; J* C! G2 k9 S2 S0 A: B |