本章介绍STM32的DAC功能。本章利用按键(或者USMART)控制STM32内部DAC模块的通道1来输出模拟电压,通过ADC1的通道1采集DAC的输出电压,在LCD模块上面显示ADC获取到的电压值以及DAC的设定输出电压值等信息。
# `" |; n: G9 ~
+ l( w/ \$ s1 a8 }+ w* ~* kDAC本身是输出,但是为什么端口要设置为模拟输入模式呢?
! `$ b! ^. @* D# N( m% H9 v因为一旦使能DACx通道之后,相应的GPIO引脚(PA4或者PA5)会自动与DAC的模拟输出相连,设置为输入,是为了避免额外的干扰。
- c( H: O0 ^3 P, C4 w2 T2 \! Q% R- d. y* ?5 z
/ O5 U* t& k1 j, Y& C; w7 {7 x8 j$ u: S( \
0 Y: O" R3 @# H5 r5 L; L
/ ?* @! u. m3 A. X( X% S% [
0 a2 n! V2 R1 G4 D* r
/ P8 w1 }5 {8 q7 R5 S2 u- V0 u7 J/ O3 y3 N/ N9 T, I
/ ]" t. @) ?! ?4 Q; N' p& `& `# g" R. Y y' @2 Z* i
& h3 C) a& H. D' H
' f2 ~5 w& L# d+ v6 s4 k
4 B) M# A/ R& N- T6 o1 D6 s0 O- S硬件设计
. n" x% V( H" c% c
2 S D( x9 ]; q1 `$ G: u3 q4 U+ ]: v& }" |
) D6 E1 t* |" d
main函数% P/ M9 e' M! r6 S- b
- #include "led.h"
5 U3 L1 h$ Q( _1 O4 g - #include "delay.h"
: O: [) S% C$ O. ?1 K, a7 [) @- u - #include "key.h"
6 g" x4 C' G% r; B - #include "sys.h", K4 F- l3 v, _# r4 `# [
- #include "lcd.h"
# _: N+ v8 F7 Q& F' o0 Q - #include "usart.h"
+ @- a3 r& G; ~/ @" U; S/ o+ Z - #include "dac.h"
6 @# r" w; w M; u% a - #include "adc.h" R" ?$ W) d& K! @/ j5 d7 Y
- #include "usmart.h"
8 l3 W7 h, D/ ~6 C2 E
4 O0 |2 h% R% r9 L) h. P6 s4 a- int main(void)
# H2 }( S$ D" W2 {, x - {
2 w7 a8 |3 F! H - u16 adcx;( L# W6 H$ C9 m5 a
- float temp;
( Z. h$ m" V `. W* L - u8 t=0;
: E# ~$ n0 q( _: J2 [# o' o - u16 dacval=0;
0 |" b% [* O9 D$ X' z - u8 key;$ r) l) x; n, c
- ! R: ~ i; n, t4 Z- ^ z/ C
- delay_init(); //延时函数初始化
% i8 j7 N' j0 U8 p; |; K - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
$ m0 \: `% u/ s {6 P - uart_init(115200); //串口初始化为1152005 i2 b6 ]& K: h$ O5 H/ |$ l. Y! u. r
- KEY_Init(); //初始化按键程序& K# u0 o5 R6 b
- LED_Init(); //LED端口初始化# Q" {+ A. B- r; U# m) D' n
- LCD_Init(); //LCD初始化. @' v1 k3 P# a1 [. }; B. Z6 ~ W% `
- usmart_dev.init(72); //初始化USMART
( }: ]7 z8 A0 F- D' Q - Adc_Init(); //ADC初始化
' Z3 f6 N% i: I - Dac1_Init(); //DAC初始化
, E3 F: N4 w- t3 E - 7 O% t$ q8 n' r1 j& B' `% r+ k
- POINT_COLOR=RED;//设置字体为红色 6 Q+ \ t# y4 c$ { p! A9 x& S% X
- LCD_ShowString(60,50,200,16,16,"WarShip STM32");
5 E" E* T" f" ^. Z8 [2 x: g - LCD_ShowString(60,70,200,16,16,"DAC TEST");
8 e7 v/ c: I- u4 C! u# z) @3 t - LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
7 w; u- w% ^! R7 X: G0 n - LCD_ShowString(60,110,200,16,16,"2015/1/15"); % H1 O/ L6 t j8 M4 g
- LCD_ShowString(60,130,200,16,16,"WK_UP:+ KEY1:-");
6 k& y3 u7 m/ J2 V0 y; W - //显示提示信息 & B+ B2 d ]. y
- POINT_COLOR=BLUE;//设置字体为蓝色
1 ?0 G( q. ~+ B8 I+ z8 V - LCD_ShowString(60,150,200,16,16,"DAC VAL:"); 2 K& n( Z4 _9 X, Z2 L
- LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V"); ' T; Y. V& T1 ~+ b! c# ^6 p
- LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");- h* l- T+ l+ J* i& p0 @
- # i. r8 n. W5 e+ h( x
- DAC_SetChannel1Data(DAC_Align_12b_R, 0);//初始值为0
! T4 p7 J2 I4 b# h, ]$ y6 p& j, m - ; l+ k/ Z0 e$ s( i J
- while(1)9 _ U' N% ?! F$ A4 e; [9 \2 \; W% `
- {' m- z8 E4 K: }' c- ?, q
- t++;
2 L, i/ ^- ^0 L- N9 D - key=KEY_Scan(0);
' y2 ]+ W0 t& [2 R: z - if(key==WKUP_PRES)
0 P7 |3 F6 d, d- e - {
9 {& U2 K7 A7 H' \7 V3 y - if(dacval<4000)dacval+=200;- N: J+ P' D% q9 h& U
- DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值 / N4 z$ h8 {' X9 Q; r' U: W8 C8 i
- }else if(key==KEY1_PRES) 6 d. F) i. G- {) S8 I9 D, B1 [
- {5 l9 L$ ?/ ]/ W& e
- if(dacval>200)dacval-=200;, g' v9 y. g" n% I1 ]/ D
- else dacval=0;4 @* b5 C8 z: c, a" Y0 E9 g
- DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值* L/ q+ V6 E* }( i7 i
- } + v0 n& ^8 g/ A9 O9 A0 M
-
; b+ [5 S m9 G - if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1按下了,或者定时时间到了0 u8 ]' h0 W# h) }4 N- ~
- {
2 R# k" c& T. i - //获取DAC数据( F C/ q! B4 x- i% b( ^
- adcx=DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置DAC的值$ {% v1 x! v. z
- LCD_ShowxNum(124,150,adcx,4,16,0); //显示DAC寄存器值
) H1 q$ P, n. c2 }* D5 j; f - temp=(float)adcx*(3.3/4096); //得到DAC电压值
5 U4 ]6 v- ~0 {' e8 F - adcx=temp;
- B% e4 S' g# a4 Y2 c - LCD_ShowxNum(124,170,temp,1,16,0); //显示电压值整数部分
6 x, A$ o1 T% G4 f% ^# d! y - temp-=adcx;5 o* S' y2 O: u* e1 g( N- b3 Q
- temp*=1000;
! M, B' b& ^8 x% ^' s% S/ e: p - LCD_ShowxNum(140,170,temp,3,16,0X80); //显示电压值的小数部分4 d3 {5 U% g9 `; V$ z0 ?
-
$ b O3 F$ ?# j- p$ `+ X% Y- u - //获取ADC数据8 g6 T- N' o8 o
- adcx=Get_Adc_Average(ADC_Channel_1,10); //得到ADC转换值 ) t5 r' Q8 D2 ~2 t$ g2 S9 T# i8 [
- temp=(float)adcx*(3.3/4096); //得到ADC电压值
; {' j+ s* Z3 Q7 j' r) v% ] - adcx=temp;/ z) m3 k9 Z' |
- LCD_ShowxNum(124,190,temp,1,16,0); //显示电压值整数部分
$ Q l4 ^! Q- r7 u$ } - temp-=adcx;) @7 E3 N( X( E% R' d5 e8 c1 g- E
- temp*=1000;7 F8 |* B% Y, h4 x& G4 @
- LCD_ShowxNum(140,190,temp,3,16,0X80); //显示电压值的小数部分
* a6 t# w1 b$ l1 ~: o2 Y - LED0=!LED0;
3 Q' y! L7 y3 t - t=0;
* j( v$ P3 G; c2 G a# o - }
1 P* s! ^/ s6 P- Z* a! S- @ - delay_ms(10); 2 C9 O1 `0 N- l h4 y
- }
6 p+ M. b9 t# d1 e' Z - }
复制代码 1 C1 I- |# t: Z$ B: Z% ~5 s
dac.c函数
6 d% z3 i$ u2 H8 b- #include "dac.h"
. Q) y1 T; U( _2 _1 i2 G2 f9 M6 J! f: Y' K
1 K, u9 a5 I9 c- //DAC通道1输出初始化% ~( R% E! Q: c, c3 U' z
- void Dac1_Init(void)
0 j* ^7 w) U" ?( K - { 8 Z9 o- f- J3 ]9 B
- GPIO_InitTypeDef GPIO_InitStructure;
* H( H, W! G3 V& a+ P* V - DAC_InitTypeDef DAC_InitType;
+ @2 T2 Q' o6 N# L6 N, u& k1 S0 t - : I( X: z) k% ?, f. a
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟
' W; q+ k% d5 \+ B5 @' Z - RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟
& H2 N: _0 {, ^- ?( I: M
. Z$ B0 I3 T4 A/ C2 w9 ]! X- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //端口配置' c% m! v4 V% j; G6 ]
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入; o( K/ Q8 j) l* g, Y
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;9 X4 r7 d: m; a, E4 M0 I3 g
- GPIO_Init(GPIOA, &GPIO_InitStructure);
" H3 B$ U; j) X2 z" ?; h - GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4 输出高: c: G; _8 \3 C
-
* J- ^% {( L8 D5 d' z5 _$ _ - DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=05 e; P8 s1 I' a) V, {& r0 V" L4 b
- DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生
" j$ z2 I1 @0 A) f% Y3 P9 l7 p; P% I - DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
" V7 ?5 l# R _; m$ o! Y/ S - DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1! U# k0 d7 k T/ F: }
- DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1! a+ U9 d \7 ?1 s) U S
- ! M; K0 \( G1 e7 z! E
- DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1
& J3 a: y8 u$ S8 p' d+ h4 w
x& X( z- K5 h- DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值( s) H( x+ a; z5 L$ b
- }
* ?" g9 i5 r1 h5 u U - 3 d2 C- I; [# Y# _! y
- //设置通道1输出电压. V* c* U0 @8 q B; T$ {
- //vol:0~3300,代表0~3.3V
l- ~$ V' _2 j6 r( q$ b - void Dac1_Set_Vol(u16 vol)
3 n4 s* f j% Q - {1 F, j4 N5 L" Z5 z1 J
- float temp=vol;/ K# s0 [$ Q6 b( I
- temp/=1000;* O) ^& x1 i% }6 X2 k
- temp=temp*4096/3.3;
, P7 Z7 P) ~. a: S - DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式设置DAC值# m3 C8 w9 c0 Q1 {- K
- }
; t% o# G5 R, a. H2 q3 G
复制代码 # A- S& y; N/ q( s$ A: _
adc.c函数
" R( O/ C7 S. l4 R" O- #include "adc.h"( s0 I0 E9 ], Y- c3 c, W! ~
- #include "delay.h"
$ _& J- e7 k# c - 8 c% b* m m( X6 W9 Z/ W0 H4 N; ?
- //初始化ADC8 k- O6 x! B8 f4 |
- //这里我们仅以规则通道为例9 S. P: k8 H& i7 M) ~
- //我们默认将开启通道0~3 6 R' B' X: W+ w7 G. e; H
- void Adc_Init(void)
, E4 O+ p! B0 a# f# C$ H; e - {
* r+ J8 w; s9 X! u+ e0 l - ADC_InitTypeDef ADC_InitStructure;
/ A* b+ w2 A, y" J/ O" b) ^" V - GPIO_InitTypeDef GPIO_InitStructure;# {6 u) G4 S3 b( y: r* K
- F. T2 Y& T- l* G0 f+ _( d- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟
1 J5 x% u$ B9 U' q - 2 ]+ n7 I" Y! q/ \! V
- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M1 Z3 x. n) [, f
+ \5 C" P- M4 ^- //PA1 作为模拟通道输入引脚
, y, _$ F; |& |! }9 [ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
( [' f6 P+ K. o9 p* V# Z* E - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚) @ w1 F2 U+ E) j0 p
- GPIO_Init(GPIOA, &GPIO_InitStructure);
* q T/ f- Z* j9 ~
6 \, l! Z* o9 b( u6 y& i- ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
4 L# }! a7 F1 d( i/ Z' o - . ^0 \% ~7 i: ^: F
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
4 _6 c# e+ P* o9 K" Z& c; v* B6 T* \ - ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式& D. u |; C9 m' V+ c l6 K& v2 i* K
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式5 J {$ W$ b$ g$ k+ b
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
, k& y# D- U8 z6 [; N+ w - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐3 M; }8 u. V* q
- ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
) B1 O6 e+ r- M - ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 # ~9 o; y0 ^# @
- ( T, z9 A# H# Z k% p9 r4 m
- ADC_Cmd(ADC1, ENABLE); //使能指定的ADC17 C8 C% M) ]) J% i. {
-
! q% \% C) ]+ i0 g3 w$ T8 T) M - ADC_ResetCalibration(ADC1); //使能复位校准 - E2 T, W! f2 m1 Y2 d
- # I6 B3 B3 \+ ~2 a- x$ A5 ?. ?
- while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
: |7 a5 [! O) N+ Z% [ - + ~0 l+ q+ D6 {3 @. z! S8 h: {
- ADC_StartCalibration(ADC1); //开启AD校准. p5 H/ [7 q" [' J3 J% N" e
8 d/ s1 ^0 T! R- while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束- L& Q) x! S) S2 [
- }
0 q8 t: R) } q. }. |9 o5 S
3 \$ a8 v3 ]8 r! A% C- ( d( l) m4 t" L
- //获得ADC值; |3 d, i4 h4 x w& a9 P! N! c# Z" ]
- //ch:通道值 0~3( Z6 g2 g1 L# O: z9 q: t& g+ _
- u16 Get_Adc(u8 ch) $ X# i5 K, ~) G8 e
- {
% ~$ u% \1 N% j - //设置指定ADC的规则组通道,一个序列,采样时间1 M: I( L0 Y2 S) H
- ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
- E& A/ e- B; g. K' T* |2 \+ O - ( `3 q) S) \0 }& ~, D: V6 |* b. l
- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能 & r# b2 I3 M: K- X( D u, M
- 2 d6 R1 a( s+ F1 Z7 }4 U
- while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束. ]9 Y9 D( W/ `2 c
% }: }( y; |% G$ a: F: S8 S& A, e7 U- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果1 Y f: X7 ]/ u: Q8 I3 l
- }0 u( x. l- f1 i/ {- ^& n; }
- r: U, b4 h8 {: z. G- u16 Get_Adc_Average(u8 ch,u8 times)* J9 V3 j) C1 r2 _
- {" Q5 V, _+ M7 V
- u32 temp_val=0;
# ~ O2 ~7 E; I& l$ e# k, F - u8 t;5 l3 x' t6 A1 G- M+ z
- for(t=0;t<times;t++)9 f# p0 ^$ C+ l- T" J
- {8 X$ u7 a9 Z$ {4 i, w
- temp_val+=Get_Adc(ch);- I: c% \- ]* r
- delay_ms(5); f2 h0 i2 z# }. t
- }
. _2 V6 g. \2 B) Q3 o; j! N - return temp_val/times;1 I- `4 \# o) m: U: I$ {2 R
- }
; ]4 d* ^3 ~, y; r
复制代码
7 y' n) @* z0 x3 t8 V* f8 V* H从 main 函数代码可以看出,按键设置输出电压的时候,每次都是以 0.161V 递增或递减的,而通过 USMART 调用 Dac1_Set_Vol 函数,则可以实现任意电平输出控制(当然得在 DAC 可控范围内)。
1 h5 l. U1 v, u1 `! v
0 |' ~ l! G0 v; T. s7 [
. r8 H" j! }) \. L' r& V |