STM32 的 DAC 模块(数字/模拟转换模块)是 12 位数字输入,电压输出型的DAC。+ q' x. v$ m' S+ p* r
DAC 可以配置为 8 位或 12 位模式,也可以与 DMA 控制器配合使用。
0 A$ }* \" d# b( @: u* T# F0 hDAC工作在 12 位模式时,数据可以设置成左对齐或右对齐。
# i; w X7 \ |; ^$ e1 }2 _3 P& hDAC 模块有 2 个输出通道,每个通道都有单独的转换器。8 g; E7 i5 ^8 u
在双DAC 模式下,2 个通道可以独立地进行转换,也可以同时进行转换并同步地更新 2 个通道的输出。
8 ^ F. ]( t+ i4 K3 RDAC 可以通过引脚输入参考电压 VREF+以获得更精确的转换结果。' Y9 Z& c, C) p. m& K3 Y) }
0 x m& G1 f2 g1 }/ x$ M: M! C+ a! Z" L7 a8 x# Z9 X
STM32 的 DAC 模块主要特点有:
% j# f( x7 Z; M* d. V s
0 a3 M- c$ u, b' `
- m1 r) W2 p$ f( @① 2 个 DAC 转换器:每个转换器对应 1 个输出通道 ! _- M8 x- u8 \- B$ G; D* z. Z a
② 8 位或者 12 位单调输出 ; x7 m# M& L2 \) c! i
③ 12 位模式下数据左对齐或者右对齐 , h1 z+ g' O. b" t3 A. {, I8 T( S
④ 同步更新功能
+ K+ m1 g+ k; C, W- B, `⑤ 噪声波形生成 ! E6 F+ s" D+ \& R
⑥ 三角波形生成 % u: C5 U8 b, G6 e3 a8 Y
⑦ 双 DAC 通道同时或者分别转换
3 {, G& J/ {' n& ?/ t% a+ `⑧ 每个通道都有 DMA 功能 % k6 g; y+ l; Y( u
使用库函数的方法来设置 DAC 模块的通道 1 来输出模拟电压,其详细设置步骤如下:
" H: ]$ S7 j9 R# P& Y: }: h6 y: H* n3 S" k. g6 f1 a
' M; a9 F5 t S/ t+ `& U( }' J t7 Z/ g1 Y1)开启 PA 口时钟,设置 PA4 为模拟输入。
- S. p! p9 b2 M& {$ ?. Z: A5 A+ f. [. j- U6 }
% G R+ I/ @+ `- a/ `& G' Y6 WSTM32F103ZET6 的 DAC 通道 1 在 PA4 上,所以,我们先要使能 PORTA 的时钟,然后设置 PA4 为模拟输入。DAC 本身是输出,但是为什么端口要设置为模拟输入模式呢?因为一但使能 DACx 通道之后,相应的 GPIO 引脚(PA4 或者 PA5)会自动与 DAC 的模拟输出相连,设置为输入,是为了避免额外的干扰。
# t, l) Z; k( c8 W% O& ?! Y$ ~. W1 J4 S t
4 v. T( `# t. F$ d2 G( K使能 GPIOA 时钟:- m; m4 g' d, u( D( g5 Q3 y
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );
0 u! c7 d* w4 c5 r# \/ X) S - //使能 PORTA 时钟
复制代码 设置 PA1 为模拟输入只需要设置初始化参数即可:: I$ Q5 M/ D. S6 t3 X
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
j1 ^7 _, R( z* G - //模拟输入
复制代码 2)使能 DAC1 时钟。
5 T; j& [% c# b) J) _8 U+ i同其他外设一样,要想使用,必须先开启相应的时钟。STM32 的 DAC 模块时钟是由 APB1提供的,所以我们调用函数 RCC_APB1PeriphClockCmd()设置 DAC 模块的时钟使能。
# E! t; Y! T' @" n+ n# I/ l- RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE );
+ S* v) U: z8 p8 H0 i- O - //使能 DAC 通道时钟
复制代码 3)初始化 DAC,设置 DAC 的工作模式。1 A. O4 e5 X- m9 z% R& L- I# ]
该部分设置全部通过 DAC_CR 设置实现,包括:DAC 通道 1 使能、DAC 通道 1 输出缓存关闭、不使用触发、不使用波形发生器等设置。这里 DMA 初始化是通过函数 DAC_Init 完成的:9 g. a" Z2 u8 \* L9 D# l% e
4 N4 V3 N3 V0 R1 j
( ?# w; j) y/ t4 K
- void DAC_Init(uint32_t DAC_Channel, DAC_InitTypeDef* DAC_InitStruct)
复制代码 参数设置结构体类型 DAC_InitTypeDef 的定义:/ G! d4 r0 V% c( g) ^8 h" `$ A2 r/ s! c
- typedef struct
9 V/ s( S% P; d6 D5 E( I p - {0 O0 L: N( i5 J
- uint32_t DAC_Trigger;
1 K- `6 w- l1 D$ h4 ?5 u, W1 g - //设置是否使用触发功能
. U& @, L. b3 O) H0 U' D7 y - + B' m9 {2 w, f; s
- uint32_t DAC_WaveGeneration; ( ?$ ^, z8 J# F8 m" o
- //设置是否使用波形发生0 M" t0 J9 V5 O- Z8 i
- + u. X( u0 R6 H6 ^" _
- uint32_t DAC_LFSRUnmask_TriangleAmplitude;
0 b2 Y5 K B9 w6 k - //设置屏蔽/幅值选择器,这个变量只在使用波形发生器的时候才有用. ?' \" a, j& a& }! |# Y7 I
2 ]" H$ Y) K2 C3 M! k$ `, x- uint32_t DAC_OutputBuffer; ! C s8 r8 V; l$ o" |5 J2 I
- //设置输出缓存控制位
]5 g: e# P- B3 o# ^2 n - }
& _0 d1 D/ B( h1 o5 C+ e+ r' S - DAC_InitTypeDef;
复制代码 实例代码:/ D/ j9 Z& l' l+ }# n: }2 ^
3 X) W" a; X5 \/ ~5 S: D* Y- DAC_InitTypeDef DAC_InitType;, V0 x, t% f% a8 b* O+ P8 R
- DAC_InitType.DAC_Trigger = DAC_Trigger_None; b1 A1 ?5 B; `7 a
- //不使用触发功能 TEN1=0
' n7 F$ i" F- N - C0 `& {- x, H* {/ x/ |
- DAC_InitType.DAC_WaveGeneration = DAC_WaveGeneration_None;
; y5 ]) h3 E) k - //不使用波形发生
/ N8 h$ y5 }/ `& c5 t& m - ) t z, w% [6 Y* B, N& Z* H- B6 h
- DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
6 H- @3 _. E! e' i* y' w - DAC_InitType.DAC_OutputBuffer = DAC_OutputBuffer_Disable ;
6 K) O: @- E/ M y7 F; Y' V1 q - //DAC1 输出缓存关闭 " x9 t+ y. l" L! u4 z% a$ t# g- o
. l5 ?2 V5 Y" q: K8 I9 d1 @- DAC_Init(DAC_Channel_1,&DAC_InitType); 8 Z) _% E1 G& I2 c
- //初始化 DAC 通道 1
复制代码 4)使能 DAC 转换通道
: H: S& K8 b2 C, |- y4 _初始化 DAC 之后,理所当然要使能 DAC 转换通道,库函数方法是:
; s) k: a+ T3 {! |/ f
1 q# t7 ~9 N4 N9 ~: v- DAC_Cmd(DAC_Channel_1, ENABLE);$ s) Y9 U0 C7 p) b, S' Z( H2 ~3 X* L
- //使能 DAC1
复制代码 5)设置 DAC 的输出值。! V0 T( c2 V# h! I% z8 X* E
通过前面 4 个步骤的设置,DAC 就可以开始工作了,我们使用 12 位右对齐数据格式,所以我们通过设置 DHR12R1,就可以在 DAC 输出引脚(PA4)得到不同的电压值了。库函数的函数是: E) D) Z" V; r0 _0 h2 O" K s
- C. p9 X X9 W0 Y I: T( {
- DAC_SetChannel1Data(DAC_Align_12b_R, 0);
复制代码 第一个参数设置对齐方式,可以为 12 位右对齐 DAC_Align_12b_R,12 位左对齐DAC_Align_12b_L 以及 8 位右对齐 DAC_Align_8b_R 方式。第二个参数就是 DAC 的输入值了,这个很好理解,初始化设置为 0。1 K6 k) Q$ } f% ~/ x7 L
这里,还可以读出 DAC 的数值,函数是:
# m' g) B. k2 p, j% u: B& r! I
7 ]. ^. R) k& e+ q9 [- DAC_GetDataOutputValue(DAC_Channel_1);% O( N2 |1 ?* e+ ?- u& w1 S" u# X
复制代码 以下为代码:' O0 n1 a: D$ E7 [ _3 y) X
- 9 q3 \5 ]! O9 S* P7 S* W# W; r, ~8 q
- //DAC通道1输出初始化/ B- j0 \* P& {( k* y
- void Dac1_Init(void)( I* X0 L' c+ V) B
- {
& n$ h0 i x# N3 m8 @2 w - GPIO_InitTypeDef GPIO_InitStructure;
( S6 e) ]( l; K0 g - DAC_InitTypeDef DAC_InitType;6 Z; t! u& D$ ?* C, H ]
-
2 H' e* F& ?4 O: T - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );, V+ O$ U% c; R' b) C
- //使能PORTA通道时钟 A6 ^4 M L/ E* F/ h; J+ c
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE );
$ s; ~$ P6 |+ L3 j - //使能DAC通道时钟2 `, \8 j6 p4 a, B" h
- 4 e" h! _+ P5 T& Z& G1 @
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
. \. [% K5 W8 @8 U* N, ]- [4 X - // 端口配置
: T9 t7 \. y1 K* O w8 }5 Q1 t& d - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;: n/ r; J/ o \! {. ~; u& Y, p& C# M
- //模拟输入
3 t: ^5 g) E+ e5 q( _ - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;. | l- o# X' p2 C# A& V
- GPIO_Init(GPIOA, &GPIO_InitStructure);
& D: m0 M9 m+ y3 Y5 \& P - GPIO_SetBits(GPIOA,GPIO_Pin_4);- W+ {: ~! z, z, a/ P- j; i
- //PA.4 输出高
- F# N; G% @. n -
& y7 j2 O4 p1 @6 e( v- b( ~; R5 w - DAC_InitType.DAC_Trigger=DAC_Trigger_None;: i6 c* v* [4 a. D5 N
- //不使用触发功能 TEN1=0
0 w# ]8 Q4 ~" Y; @& L% Q - DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;
8 v$ @7 F" }$ ^9 ]/ N% ` - //不使用波形发生
8 ]" n. l$ m+ L. l# V - DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;
0 F3 W+ Z& K2 }0 H9 b( F8 S0 s# R - //屏蔽、幅值设置" R8 O4 x1 |' Q$ r. L
- DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ;
' {2 v9 M2 Y+ o! u/ M9 I0 Z - //DAC1输出缓存关闭 BOFF1=1, a% w+ G# ?# j8 y
- DAC_Init(DAC_Channel_1,&DAC_InitType);
7 O& ~( s4 E% G- w6 i; d, S - //初始化DAC通道18 _ {* f. t5 ?. k! M2 h( y( @, t1 h
- 7 s' J8 ^) L. u7 v7 c9 M: f- x
- DAC_Cmd(DAC_Channel_1, ENABLE); 3 S$ i2 u) w7 M6 p1 G9 M I) U
- //使能DAC1& T! i* \/ \8 Q6 d# e
- DAC_SetChannel1Data(DAC_Align_12b_R, 0);1 h& J8 O" N' \, m' y% |; ?" a$ q
- //12位右对齐数据格式设置DAC值; D) W9 O R% [: Z# l5 ]$ s
- }& ~( _# \; A7 J/ ?( `4 T
- //设置通道1输出电压# `( R2 S+ @) b8 A5 N* F# b
- //vol:0~3300,代表0~3.3V
* c' I0 S! H' [9 c; p - void Dac1_Set_Vol(u16 vol)1 S7 X, K7 n; x& l$ u
- {" [, r# h- e/ V8 ~6 }
- float temp=vol;5 n( w/ x( e, j! l) o
- temp/=1000;" W+ O7 n9 k0 d2 Q# ?6 [
- temp=temp*4096/3.3;
2 Y/ W- }1 }- L9 Z }1 J& q1 e - DAC_SetChannel1Data(DAC_Align_12b_R,temp);" M! W) F8 }9 f5 O
- //12位右对齐数据格式设置DAC值
* Z; K! y8 j( Z' U* d' V5 C' w% I - }
复制代码 在使用的过程中,只需要调用 DAC_SetChannel1Data(DAC_Align_12b_R,temp);该函数就可以随意设定需要输出的电压值。
$ m0 J, `+ H# b1 V! g |