本章介绍STM32的DAC功能。本章利用按键(或者USMART)控制STM32内部DAC模块的通道1来输出模拟电压,通过ADC1的通道1采集DAC的输出电压,在LCD模块上面显示ADC获取到的电压值以及DAC的设定输出电压值等信息。
4 ~) S5 ?/ `4 \9 X; `. j+ I# f- _$ f2 X8 _7 `, t+ a# `
DAC本身是输出,但是为什么端口要设置为模拟输入模式呢?
3 C9 l1 L9 Y* v- C. g! n1 a% k因为一旦使能DACx通道之后,相应的GPIO引脚(PA4或者PA5)会自动与DAC的模拟输出相连,设置为输入,是为了避免额外的干扰。
4 [& w2 X* J3 E0 `2 J5 {) G
0 W" P* O4 ^7 `8 N. b ]0 A# v; B9 x! L7 g
6 g/ N8 ?, o' R1 ~" A4 ^" E
( f& z: ]0 ?; I0 R' B/ X, |0 a3 w8 U- ?5 j6 _! u9 J. G5 z5 u
- v) H" c0 Q8 e y
$ e8 K8 ]& s4 Q3 [/ Q! [* K, v' I" S# O' a$ E! k8 L
& n# x6 m. ]+ q5 _5 H) h! a* a8 E: t
+ P- p' f- u2 K2 X5 O% ^
% S& ~. T, v' P$ V7 c2 ~, |2 Y) R
a: [5 h, q- V硬件设计
3 f4 D; [4 b0 u8 [* X; x# Q" c( y0 R
* L- q$ O8 M4 n f4 P; Z6 W: U# u: Z9 s- p
main函数( U% T c" r7 `* J2 W# J1 C
- #include "led.h"
+ n6 m; y6 `% G- X: v- K. z - #include "delay.h"
0 l* |+ q! ^" m' z" w$ o - #include "key.h", D |8 D. V% ?8 L! p
- #include "sys.h"
6 e( ]7 ^# v! U! N. C - #include "lcd.h"; N3 J$ b$ V( x
- #include "usart.h"
* d( |# [* O7 F. `8 X8 w/ r - #include "dac.h": M9 |: A" Y% A$ O8 V0 d* m
- #include "adc.h"
% D+ ]! `/ `" A- E - #include "usmart.h"% Z7 n) M: k U3 q* n* Q; m
* X5 c' I+ g2 X0 I5 s7 w- int main(void)
1 m" Q+ e- ~' X/ T - { + w# o Z2 E: _$ S6 {# x( e
- u16 adcx;& S" D& A% \! M* I6 s5 z2 u
- float temp;2 H3 k8 T+ R, c9 ?6 t% b' W
- u8 t=0; $ W, o( x9 S0 \" H- D$ n
- u16 dacval=0;
; T4 b9 G5 P. t5 f1 i3 T+ i0 P - u8 key;8 y' k" A6 k1 G3 `$ t, E
-
& l+ V$ C w$ j- Y" x5 ` - delay_init(); //延时函数初始化
' i: f, O' U! y! t' {9 I - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级. N! t$ I. N' B
- uart_init(115200); //串口初始化为115200# Z% ~% |% B* t* V& d F
- KEY_Init(); //初始化按键程序* b5 F; _* l$ C* T7 X3 V
- LED_Init(); //LED端口初始化
) Y5 F# z; l/ n# G0 @0 Y. d - LCD_Init(); //LCD初始化
6 i) u5 F1 M: m% G - usmart_dev.init(72); //初始化USMART , H5 W" w+ L9 S
- Adc_Init(); //ADC初始化
# [0 l. _' ?4 V9 E+ z/ J5 r" G - Dac1_Init(); //DAC初始化7 S7 x' M3 u1 H. Y+ d
' ` ?- i; R7 D" r5 d7 \- POINT_COLOR=RED;//设置字体为红色
6 V! k* ~8 H: x4 X) X6 J - LCD_ShowString(60,50,200,16,16,"WarShip STM32");
1 _) B4 Y$ f9 j& S& u - LCD_ShowString(60,70,200,16,16,"DAC TEST"); " r3 P: n5 Z: M" R, X) L
- LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");9 U8 H2 e. ~6 L1 C4 ?
- LCD_ShowString(60,110,200,16,16,"2015/1/15"); , p( c# y1 d$ a- g5 t
- LCD_ShowString(60,130,200,16,16,"WK_UP:+ KEY1:-"); $ |7 f2 H+ w, r% ]. k& n# @+ Q6 J
- //显示提示信息 6 Q8 ^9 W$ [# o1 P4 k
- POINT_COLOR=BLUE;//设置字体为蓝色. L3 T4 R* {/ Q' H, r
- LCD_ShowString(60,150,200,16,16,"DAC VAL:");
f4 i: f4 z' v3 v. @. b$ @) v - LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V");
/ @! j2 Z2 A R, G& h& f$ E - LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");
4 X9 S% ]" [0 `' ^! R- W% z3 e - / C0 f7 p- k4 h5 k0 P
- DAC_SetChannel1Data(DAC_Align_12b_R, 0);//初始值为0 * ]( W1 M3 Q2 I- a, o
-
6 Y4 b% [! ?7 V6 b+ d - while(1)
7 c. u- ?/ P2 x$ m - {
* t/ Z- V+ Y# q5 d! S3 Q( M2 q2 g - t++;& u7 z& S, F! J6 m0 k/ N
- key=KEY_Scan(0);
1 `+ V& ]$ t( E8 L' I- g - if(key==WKUP_PRES)
/ p# W, v/ L# Q* G - {
! O6 c: x1 ?% m0 B - if(dacval<4000)dacval+=200;8 L( ~1 L& }* v' ]1 k# `( r& o7 H
- DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值 1 n$ k. Q) W$ h, K) a: I: m
- }else if(key==KEY1_PRES)
8 i8 d4 C* `1 _/ s" @3 P/ R - {" A& u6 Z+ o) V3 j5 U# f
- if(dacval>200)dacval-=200;/ ` k7 O* E U" O5 w
- else dacval=0;
6 |1 B3 i! e6 n6 Y - DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值/ B, J4 |* k4 T& Z
- }
+ ?; N5 T9 J9 ? -
0 p+ P2 V8 k' X5 G& C - if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1按下了,或者定时时间到了- [" C* C0 p+ H E
- { ( |+ m; Q4 B3 w: S i% x/ R! t
- //获取DAC数据, o, G ]0 o5 y
- adcx=DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置DAC的值
/ n. R1 t1 g: ?* e - LCD_ShowxNum(124,150,adcx,4,16,0); //显示DAC寄存器值, }3 ?7 d+ I) n; x9 N5 ]
- temp=(float)adcx*(3.3/4096); //得到DAC电压值
; |# w9 T) _4 a) m4 C M/ s" \ - adcx=temp;# j" m6 o m1 e
- LCD_ShowxNum(124,170,temp,1,16,0); //显示电压值整数部分4 ~/ ]5 s! f8 g/ v3 X
- temp-=adcx;# n) K, B5 F2 ^* y* h8 ?
- temp*=1000;0 g- G/ A& e" |
- LCD_ShowxNum(140,170,temp,3,16,0X80); //显示电压值的小数部分8 s4 n% N0 y# I$ g' L0 I) {
-
0 z/ q) Z% e/ W7 [& H1 M - //获取ADC数据+ v' _9 M- i m" K' u! }8 H4 i. ~
- adcx=Get_Adc_Average(ADC_Channel_1,10); //得到ADC转换值
& R/ m& i+ M* r8 v" s' L - temp=(float)adcx*(3.3/4096); //得到ADC电压值. A2 y) ]6 N. ^- n& [( P8 `' H2 A
- adcx=temp;
2 U) s( p' {# e. g0 X - LCD_ShowxNum(124,190,temp,1,16,0); //显示电压值整数部分4 M* [' n9 _& H7 _% O8 B3 N
- temp-=adcx;
3 n9 f: |# Z' I/ Q2 L - temp*=1000;
7 F! m& d. d/ Q: D$ M n - LCD_ShowxNum(140,190,temp,3,16,0X80); //显示电压值的小数部分
6 Q# X/ h, o, p) Z, Y& o9 m1 Y - LED0=!LED0;
2 l8 w' @. y# z$ Q7 e - t=0;5 k1 V' \& S4 F! @3 A2 l) J0 D
- } % {- d5 e+ E0 _! v( P: ?1 v
- delay_ms(10); " T4 L- u& I% B) \. Q2 I
- }8 q8 X. a2 t9 N
- }
复制代码 9 U* u1 s1 B6 M" v0 d
dac.c函数9 @( S& d/ D; i% T3 `
- #include "dac.h"
0 D; g% o T" e, L# W) I - 0 x9 T% F# w: c: B. d
- //DAC通道1输出初始化" g8 V( v: Q& I1 G; g, }
- void Dac1_Init(void)1 e, j) h; q8 v t: {/ |# Y
- {
: V* t# e! M2 i2 P9 T - GPIO_InitTypeDef GPIO_InitStructure;& ]! C9 Y2 ~# H' Z4 ~( t+ f
- DAC_InitTypeDef DAC_InitType;% j# o( o# T, F- O
- ! S4 F& [$ c |" H, v
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟7 C4 M0 U. f# F, t2 Y# c
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟
& l! O3 `' P6 o( \4 r ~. s$ p - " K0 k( u6 Z1 U
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //端口配置
4 I3 y: w, `) ^3 O2 o1 B; C - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
+ n! @) q D8 s( a4 ] - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;4 ~6 p$ g% Q0 C; g4 \( N a! l# M
- GPIO_Init(GPIOA, &GPIO_InitStructure);
& c* b5 N0 j# s. ~8 ]+ i - GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4 输出高$ u1 m0 V) g3 L5 d
-
: z$ d1 O1 u* v# ]- ]" h( f - DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0# Y+ P) m7 Q- v% J0 e+ H5 V
- DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生
( S1 z' W3 w6 @ - DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置 z; H* J5 g) R/ }. O! g! Y9 B- j
- DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1
; {" {: R$ k: u - DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1$ c: U& j' r# `5 X
- ! ^' b# k. B4 o% p# y7 A
- DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1
* E2 K, B9 v' v5 \ - 4 ~) e+ w5 O' x0 R3 ^$ o: Z
- DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值1 I6 e( }# J6 m; U- J% i
- }3 A; Y. s3 y* N, Q$ _
- 3 r; A7 a5 ~* _& a
- //设置通道1输出电压
/ `* J# K$ [) i M - //vol:0~3300,代表0~3.3V
% K% f: E7 W3 _ - void Dac1_Set_Vol(u16 vol)4 C1 m4 @6 A1 ?% X$ N
- {+ n7 H% W6 \. y e
- float temp=vol;" ], J9 ]$ ?# @: j& w7 J: d
- temp/=1000;
) E) f' [4 t( d+ a; \ - temp=temp*4096/3.3;
. G1 H* ]0 \% q& D4 L! y7 J - DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式设置DAC值
d, A3 K5 v; A( m - }
% e$ O& B! {% u( c' Q
复制代码 : c/ y. c7 H- V6 i3 B+ R6 w: x( W
adc.c函数
4 X! h9 l, X# {0 w3 N- #include "adc.h"
' b( C) S8 v5 q( Q8 N! V - #include "delay.h"# C b7 U. m8 B7 p, R, G( I
- , ^- I- h5 Q& B2 _% x$ t5 S' E. R
- //初始化ADC- R( C# U# |* x7 [) M$ }4 U
- //这里我们仅以规则通道为例, g5 h# Y2 D# k
- //我们默认将开启通道0~3 2 F6 `6 V8 s5 r. a, V+ t0 }9 e
- void Adc_Init(void): A# B$ E7 f3 n0 I. j. `
- { % B0 u& g4 }/ G3 F% J5 w; v
- ADC_InitTypeDef ADC_InitStructure;
" d6 J* O( T5 E: S: i+ k - GPIO_InitTypeDef GPIO_InitStructure;
6 D( z6 O( l% h, F8 K: p: j+ x
# C l! {3 P; o) t0 {% |- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟( l: t" ?8 c# ~7 k
* J9 S/ R( b- z% j: _- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M, ^2 t. s2 X/ P6 {1 Y! p: g
: z2 p" D4 k* z9 y- d: |- //PA1 作为模拟通道输入引脚 , J% D, O+ f+ ^: A% u1 C
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;1 N+ {/ c( O" m- k, \( @* d6 i
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
9 g( l8 N0 e$ L, i7 t9 z - GPIO_Init(GPIOA, &GPIO_InitStructure); 9 N C5 l4 J3 G9 _* [9 U' Q# \% r
- ' W- D2 c7 x5 h- n2 ]4 J W
- ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
+ ?" a1 j3 k) e4 w9 _
/ I# B4 d9 Z* \. [/ O3 ?- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
2 E4 K$ ?( F8 W - ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
1 s' o6 x& a1 ~$ P - ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
2 a! `' T5 e, a! X: h4 C' H - ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
, G2 J- u* l! z/ e# D% b5 e& W8 ? - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
5 \ M V; C: a& Q. O - ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目7 H- n0 F$ c9 n, I8 W
- ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 - l+ m7 P* ~3 a- f; B: ^+ Q: Y2 M
9 p. P6 X8 e1 _' C# @' w- ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
4 |% C# O. Q9 H* ]$ m( G - h$ ]3 A8 y, g+ N$ `7 }
- ADC_ResetCalibration(ADC1); //使能复位校准
; H5 V e* P+ T# R4 Q -
+ w# j( ?1 v9 {( F1 y' X5 { - while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束: U# M; }% H5 I ]" D
- 5 T& h* W f: s
- ADC_StartCalibration(ADC1); //开启AD校准
1 \. O& {1 y( u& c - 5 `1 a. ]" e' y
- while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束% A8 m$ H! Q: v; ]
- }
: H: g$ l2 e1 g T+ k( A5 D - 8 r0 u1 T$ Q7 F6 \6 b& T) k, } B
- . j8 a* d0 j$ R- g0 F' L
- //获得ADC值
) P8 A$ M3 w% a - //ch:通道值 0~3
9 \- Y3 W2 }0 p' S - u16 Get_Adc(u8 ch)
! |3 h2 l* Z9 I( j% [5 J8 h - {
9 C: q$ F( _1 J& w - //设置指定ADC的规则组通道,一个序列,采样时间
3 e3 g& D9 T m - ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
+ _/ j$ x6 M4 L1 B/ i& V$ ?/ j - 0 n& W! b# E7 V3 y
- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
& L, l' Y$ w2 h6 r: i2 W5 e -
& b& t$ h) f) I" Z - while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
5 X+ L9 ^( b) ]* f7 V( v - 1 D) z% d9 i4 y+ {5 K
- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
5 T2 ^3 t7 }& [2 W) ` - }
, b# z }: f+ g2 V( f
: j: E# a& J# v& ^- u16 Get_Adc_Average(u8 ch,u8 times)
2 W. S0 a! Y0 W' q6 B m; h4 T. n - {3 h4 E" D; R6 @' m! ?" l
- u32 temp_val=0;
" Q9 j8 L8 W% J1 K/ d - u8 t;
; F) V: i* y; l$ F9 Q+ o - for(t=0;t<times;t++)
4 N4 d* G f3 h9 D9 s - {" N w! P L2 `6 ?2 X8 y
- temp_val+=Get_Adc(ch);
: {& ]1 x: [( c! O! N - delay_ms(5);' j, E8 @0 P! {7 r1 I. X2 ]0 e
- }
# r2 `" h6 X' t4 q8 @& D; @ - return temp_val/times;: U% K. ~7 i5 Q4 j
- }
; v( D! V7 \$ j/ ^
复制代码
; n% G3 X+ M- `( ~6 B& V' a' N5 t从 main 函数代码可以看出,按键设置输出电压的时候,每次都是以 0.161V 递增或递减的,而通过 USMART 调用 Dac1_Set_Vol 函数,则可以实现任意电平输出控制(当然得在 DAC 可控范围内)。
( H. O) H& I& z5 }" W8 a Y% x( a8 O; G- R4 M/ S
, g. {* v B: O) J2 g" I
|