本章介绍STM32的DAC功能。本章利用按键(或者USMART)控制STM32内部DAC模块的通道1来输出模拟电压,通过ADC1的通道1采集DAC的输出电压,在LCD模块上面显示ADC获取到的电压值以及DAC的设定输出电压值等信息。
3 b$ L, Q' _$ \0 f) e9 k# F0 o" S, i
s3 j4 P1 b; a, W( K, F% X0 x3 c5 HDAC本身是输出,但是为什么端口要设置为模拟输入模式呢?
% ^6 E# k- c8 K* |1 R因为一旦使能DACx通道之后,相应的GPIO引脚(PA4或者PA5)会自动与DAC的模拟输出相连,设置为输入,是为了避免额外的干扰。& T2 l. Y8 \' j1 Q @( ~
: L' y+ q J6 j' w* ]: T! x1 }
$ w+ i( v' }' R( G; n1 @
% g% H2 x' O1 Q9 R# y
: s ~0 i! K) ]+ F9 e; [. [7 P6 w1 Q" X
3 j( f6 K- A8 L+ a1 h! @( u
* F$ s$ ^+ j' o3 y
8 ^, O: ]1 @* e5 |) e! b- z5 x# o# u: L' B2 c. S+ k5 u- m
3 V5 Y+ R8 p2 z( F4 l( I6 ~- V6 p F+ G3 x. Y
. b( _ j5 x* c1 S) E
; s. W( [" U4 B2 V
# @5 `+ ]/ p- o
硬件设计
, v6 }; ?) j8 W9 H3 x9 g" z, ?! [' g; x" T2 q
+ }8 d ^: U) i1 l" _# H1 m1 F
8 y1 v; l! s5 ?' R/ _* P0 v; ?main函数
, o6 t+ Y, O4 I- #include "led.h"' {4 u% s, D' V$ n! }" Y w
- #include "delay.h") W" ?( U4 R& z( d
- #include "key.h"
s7 H; L4 f5 M, ?/ J - #include "sys.h"* c8 h, a! C# n1 Q; d* V
- #include "lcd.h"2 d( `; j+ R. k; b
- #include "usart.h" ; k+ B( T8 t+ @) d7 W; G
- #include "dac.h"
: a% m3 r* V! _4 o. X6 t% f - #include "adc.h"
* L1 m% [) u/ r) b, u3 g - #include "usmart.h"
- n+ ]1 ]2 I: ~' S+ f
- {% h1 C Y% p0 T! R7 Q; J- int main(void)
- A" n" c; }& x - {
9 S0 @1 `: ?5 E( ~% `$ ?% \ q - u16 adcx;
' ?, q5 H+ s% ~9 ~2 W" P1 o - float temp;% r7 t. h1 v- M" ]. I
- u8 t=0;
! d A. @; N$ A) X - u16 dacval=0; Z; w, y# m9 t
- u8 key;' A6 T. e E+ i2 j. ]" ]# y
-
7 P; ?* C) q8 V+ T5 W - delay_init(); //延时函数初始化
% v/ r8 _: K6 P4 T$ [, l& X - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级) [7 C0 B$ Z" p0 @1 E- O4 u
- uart_init(115200); //串口初始化为1152009 O+ Q7 J$ a2 O, k F
- KEY_Init(); //初始化按键程序% D2 [' _. E$ |6 o: R4 }: ^: ^) F
- LED_Init(); //LED端口初始化4 X7 Q0 L% _1 Q/ Z
- LCD_Init(); //LCD初始化) S, q- l% i; \. t$ W( p/ Y) Y
- usmart_dev.init(72); //初始化USMART
3 w* c0 X3 G6 k+ S - Adc_Init(); //ADC初始化
0 g8 x+ K8 [# {9 a1 ?0 l, X7 H - Dac1_Init(); //DAC初始化! ]/ i: p/ X7 f) {4 n1 {4 `- W" M
# j5 ]) Y9 Q! _; B9 S" E% t+ |, N: m- POINT_COLOR=RED;//设置字体为红色 * V, y" c. x& }2 e& r
- LCD_ShowString(60,50,200,16,16,"WarShip STM32"); ) q7 m/ o9 `) r/ S- B
- LCD_ShowString(60,70,200,16,16,"DAC TEST"); 2 E' j5 r) W' {4 A
- LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
: t" ~( V, t- W& N - LCD_ShowString(60,110,200,16,16,"2015/1/15");
; s1 e) ?4 l' I+ N" c - LCD_ShowString(60,130,200,16,16,"WK_UP:+ KEY1:-"); 7 O' J$ p5 a8 t P$ p; o7 I
- //显示提示信息
( a J# ~( ]7 A- b) j1 e9 I - POINT_COLOR=BLUE;//设置字体为蓝色
. v0 u% [( z; J1 X2 g9 V - LCD_ShowString(60,150,200,16,16,"DAC VAL:");
. D; x, g/ N( _4 t - LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V"); ( a, z: L# L0 }: f- H
- LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");
+ a; @( x- S4 w5 j2 e: m( f0 S7 H - $ Q2 i1 s% M8 ^0 y% [! o) _" ?, s
- DAC_SetChannel1Data(DAC_Align_12b_R, 0);//初始值为0 , I1 Z! @8 K* Y
-
& c9 i7 v) h: Y2 G; e0 I - while(1)5 D( d/ A2 _1 x4 Y5 |, K- ~
- {
, @' t$ E1 p( u - t++;
* o3 L4 _/ K7 P: n - key=KEY_Scan(0);
9 l E' O& S, N: W$ [& _ - if(key==WKUP_PRES)" V, Y( c/ ^/ P ~! P+ I
- { & E5 s0 \* E9 {4 w
- if(dacval<4000)dacval+=200; c4 o" m& |# U* D
- DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值 8 T% i5 {! Z3 r
- }else if(key==KEY1_PRES) 5 X* t& V. u/ ~- c
- {1 X/ A2 q) g: o# a* K3 G" c4 x
- if(dacval>200)dacval-=200;
. T6 r8 {# Z6 f5 O; l - else dacval=0;7 H) Z( L: N C
- DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值
9 K4 a v$ {, X - }
. J* g! I6 G/ U" ^0 {' | - 9 c' a+ I' Z. Z9 I7 ?# ^" N. r
- if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1按下了,或者定时时间到了
! D8 l/ h8 U2 o9 _5 C1 L - {
* j- T' n0 |6 b8 h - //获取DAC数据
: ]3 V& T9 t+ Q- F - adcx=DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置DAC的值
6 X# c4 ?8 D. @- h/ @, _5 ^ - LCD_ShowxNum(124,150,adcx,4,16,0); //显示DAC寄存器值
4 `/ `! y: m, R2 B( _6 S - temp=(float)adcx*(3.3/4096); //得到DAC电压值
8 ?( {5 |3 `1 H$ X" x- S - adcx=temp;3 A9 b6 ~) ~1 U" c% |/ J- E3 g
- LCD_ShowxNum(124,170,temp,1,16,0); //显示电压值整数部分
" a' r( U4 L) @& R( Q' k& }/ ^ - temp-=adcx;; V1 ^# f/ L" z8 j! s9 l0 K5 z5 a6 E
- temp*=1000;- B' k3 P8 |3 a7 h
- LCD_ShowxNum(140,170,temp,3,16,0X80); //显示电压值的小数部分4 a: j' s8 I1 P3 Y# o' P" h" f
- & z- I; x4 f1 D {: }
- //获取ADC数据
) v" d) J! {' ?3 S1 z+ o - adcx=Get_Adc_Average(ADC_Channel_1,10); //得到ADC转换值 7 n1 {. m1 R; w
- temp=(float)adcx*(3.3/4096); //得到ADC电压值
* J, ?" C- x! U4 r8 y+ a - adcx=temp;# m: m# ?8 I: x, Z
- LCD_ShowxNum(124,190,temp,1,16,0); //显示电压值整数部分
) W7 e) P' B ~+ B# b$ }& [ - temp-=adcx;
0 Q, l' \" T+ Z6 Q+ z - temp*=1000;
) E! w2 ^& i$ I7 D- u* E - LCD_ShowxNum(140,190,temp,3,16,0X80); //显示电压值的小数部分
6 l' ?( ^. Q% K; k - LED0=!LED0; ; U2 o5 O# o* J) D- N% Y8 t
- t=0;9 \0 k# [0 d2 ^" p/ W
- }
: z) P9 s3 Z9 e- e7 `' Y7 n% Y" g - delay_ms(10);
/ |, X( h+ \, G' T% X - }
" s6 s2 R+ z# C# U0 ] - }
复制代码
: O, h0 c- K. Y1 C! p1 R! [. P2 J0 @dac.c函数
+ ^5 T: @+ T7 I1 A4 L. v/ W- #include "dac.h"7 ~" L s3 z- M% G; w
- & F6 S1 n% Z0 Y* G
- //DAC通道1输出初始化
% [! h5 t8 P" A - void Dac1_Init(void)
) F; v/ S5 u7 @9 |) s - {
+ R! K, c; s" g% D5 w3 E - GPIO_InitTypeDef GPIO_InitStructure;
" Y, B' q# E: x6 b" Q' O - DAC_InitTypeDef DAC_InitType;
9 X0 [0 L1 t2 _9 w* [& j+ q - 2 y/ L$ P# ?: }3 w9 L8 `
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟; B7 n8 ~4 s z
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟 9 { z3 X3 }, Q+ [: q0 }; `
5 v6 F+ p0 ~7 S: \- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //端口配置
3 o& Y0 b% ]: @$ C' I; l - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入1 u5 D, y& d! s
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
j! A, i! N8 e8 `6 w4 W1 |8 @( r - GPIO_Init(GPIOA, &GPIO_InitStructure);
2 ]4 b, F" g! O* F" u9 f+ @0 E - GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4 输出高2 i2 ] ~2 I: A
- . w. Q+ A4 G. X( D
- DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0
# P; j/ i Z" k0 Z ^2 q - DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生4 {* V1 k8 y1 b6 [7 l0 [# f
- DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置4 w) M9 _) w( E- T& H6 K3 G
- DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1# V- b5 M0 y. O# n3 {0 n
- DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1: J5 \, p+ ?0 |$ x
, O0 B+ j, l! }+ w' ^7 g4 y- DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1% z& k8 a3 n: }
- # n; I6 \* r. e- j3 ^
- DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值
& d( I) T V# x' K# u - }
) F R E3 o* _$ o( t* ^ - 5 z5 R- I1 d" Z1 n2 s2 u
- //设置通道1输出电压
* J0 R0 J7 C( r# Z( C* A" v - //vol:0~3300,代表0~3.3V
: f4 Y( O5 a; c - void Dac1_Set_Vol(u16 vol)
4 j7 l9 e8 W9 `) a - {8 a- c: E9 G& j4 @5 ^
- float temp=vol;/ P1 B# j4 f) r. y) |& j( }6 ?
- temp/=1000;% q$ l4 B* n6 v v0 ~
- temp=temp*4096/3.3;$ o" @3 b" h. k4 k5 G
- DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式设置DAC值
3 t V$ `- t6 M) } - }) p( E9 `- A+ p/ Y
复制代码
+ E6 J. J& F- A) }" R; [adc.c函数
2 q+ g. N3 o" @' q x- N. {- #include "adc.h"0 @; P4 v+ r' n5 W1 A# R1 T
- #include "delay.h"# v! j' S6 A3 _
- " B) Y2 c D) i; h& b& {
- //初始化ADC
+ U, s4 @# U. V: }) a' x( t/ z - //这里我们仅以规则通道为例
. q7 k' b. A& Z$ W - //我们默认将开启通道0~3 5 O/ c% M- J8 @
- void Adc_Init(void)( ~6 D4 Q5 E% T3 g
- {
; \) s8 z0 p* U9 l. O I8 y7 z( Y - ADC_InitTypeDef ADC_InitStructure; & h E, @& w4 k! G6 w; W1 M- w. P
- GPIO_InitTypeDef GPIO_InitStructure;6 I2 R) N$ q) d& q6 S
- & Z" j1 j F" C! X7 C
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟% ^6 w: p7 H: R" i$ {2 n7 @0 K
5 m" y9 I# F3 t, F- u" Q- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M) n, I5 M9 h) S% M
- , t" x. B% u. h: C" S( _9 A
- //PA1 作为模拟通道输入引脚 ' [1 F) c3 L6 r% f# A7 [5 m: [% X
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;2 V# W4 @3 O( B0 k
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
) I1 O: W- x! |7 e) L- G - GPIO_Init(GPIOA, &GPIO_InitStructure); ) _$ @# p+ B: l/ a7 H/ w) y2 f
0 f$ W$ t6 T( L5 w/ U8 P! w- ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
' }# O3 h# s* F t6 v3 x0 c
* t5 a2 _& |+ k1 `- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式) I6 A- U$ a2 \: Q& ?
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
6 Z, C1 a/ K/ a4 Q& B5 T - ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式* D3 T/ H+ _" t4 k, u; }
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
) m) B9 R) b" j/ S) _5 l' H - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
1 q+ s% b a# E+ Q5 j8 ~7 L - ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目# e( b4 b' |* t( n( `
- ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 + t9 f, e/ I/ i7 }0 A8 d
- ) H! ?5 g0 U( r9 f, @
- ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1: L( d u0 |: Z' T
-
7 N$ X+ z6 d7 { - ADC_ResetCalibration(ADC1); //使能复位校准
- ^7 o6 X; ]" S0 |2 z F - , z6 t# ^' H2 c2 o5 |
- while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
) u3 R7 I2 y1 U! P -
9 O; O7 T' g- A' |; @% S" c7 X - ADC_StartCalibration(ADC1); //开启AD校准
0 {: u- G( e8 _- ?' ^2 Y
! y* W; m- r6 d5 s3 J$ \8 i- while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
% `: J! N0 k8 k( |: S0 T3 T- l - }
1 f/ l0 X# _$ I& q5 J5 g0 \+ ~$ m - 1 ~- E9 ?' V: N z7 r
- . ?* j# Q3 a! O( H4 j9 j0 o, T* ?
- //获得ADC值7 H( ^8 j! _! d" R
- //ch:通道值 0~3- D) V5 g7 D; ]$ y$ ?& q6 ~
- u16 Get_Adc(u8 ch) % V4 h: P" h9 ^; Y
- {
' K9 X! R7 Y+ e( P3 {: _0 @0 G. y - //设置指定ADC的规则组通道,一个序列,采样时间
* L7 t) Y* Z9 E) {& Z - ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
; T, e7 v* Q3 ^( x m# H! h
* ~/ n. a! @# L; I: a0 a- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
3 e' f, x% j( B, m2 m; U0 Q: b' { -
& h* p! k& Y* |0 L - while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
3 {) _! S6 I' [+ k4 I1 ^: [. v - ; \* x* H D; D$ \2 F6 A
- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果! G8 s% r) Y* w; z+ E! I2 e
- }
/ `) y9 w0 U+ e& w* {! S - 2 i: s' M s, X( f6 T7 w& K
- u16 Get_Adc_Average(u8 ch,u8 times)- u, _9 t( L! S, c7 f2 K
- {, P$ N4 ?) {2 J. N! d/ c
- u32 temp_val=0;
9 o0 C+ j' c7 f" R4 o7 f - u8 t;% X6 }) l0 J& a5 n: B+ V
- for(t=0;t<times;t++)$ I, r: g/ S6 R; u
- {
( ~0 q1 P$ M, e" c0 Z) x - temp_val+=Get_Adc(ch);
- c# G% a- i' m, A4 \4 P - delay_ms(5);! ~! ?5 z1 U N0 L
- }/ {% @8 t$ a0 a, B
- return temp_val/times;
4 K1 \5 D8 s; r7 o0 G5 H - }
4 b5 O1 j& F0 n
复制代码 l! T2 t4 B# s# m' j) q( ?
从 main 函数代码可以看出,按键设置输出电压的时候,每次都是以 0.161V 递增或递减的,而通过 USMART 调用 Dac1_Set_Vol 函数,则可以实现任意电平输出控制(当然得在 DAC 可控范围内)。8 P- B% F% \( F- U6 T- e0 R
7 `# {. X' q# a) a/ K( G- T
. Y# F. |$ a1 R; h% F, q |