本章介绍STM32的DAC功能。本章利用按键(或者USMART)控制STM32内部DAC模块的通道1来输出模拟电压,通过ADC1的通道1采集DAC的输出电压,在LCD模块上面显示ADC获取到的电压值以及DAC的设定输出电压值等信息。: x* g8 ]% }, \ p1 ?8 |% a7 L
) \3 }' A* b- Q* J( {4 U
DAC本身是输出,但是为什么端口要设置为模拟输入模式呢?8 v# M; Q$ W& N+ ?& s
因为一旦使能DACx通道之后,相应的GPIO引脚(PA4或者PA5)会自动与DAC的模拟输出相连,设置为输入,是为了避免额外的干扰。/ U G& L% [; a% M
; ~* y. z; M9 ^3 G$ Z6 l( y- Z
4 z% N( j3 ]+ a7 d$ r2 Q+ I9 }8 j' c/ l$ a% ~8 Z9 _. V" \8 N+ g
" x+ c. D/ u, ?* a
& t e2 v; W- M- x( Y- A; Q3 R9 I7 S. D; e- r7 E& `+ z- j
. ~! Y1 }% K* B n- H/ O% L# H/ a& \
5 `- q [: U7 |6 n6 {
. A0 M! c( `1 o
% ~8 I/ V+ X8 H- Q; |
& Z. y0 s) @3 x; O) _2 y; B, G) J, {" J
硬件设计 _8 A$ O2 j# j
, o- Y8 y9 h' n+ B; O8 c5 R# i/ b) T& }! ]0 d
5 F' V7 j1 A$ ^! }; C( ?# k
main函数6 S( n1 x. A/ y$ _4 L3 b6 k5 d
- #include "led.h"
2 |3 z! e* d7 B2 ?! j: m: x, \" \ - #include "delay.h"
$ d# o+ ]' `6 Q - #include "key.h"6 u9 f3 V% O# n( F0 h
- #include "sys.h"
/ @) F0 o; h/ x9 t - #include "lcd.h"$ S; S1 h1 s- }# w# A) Y* R' N
- #include "usart.h" ! W* h9 q, W6 l
- #include "dac.h") h, k& b+ `. K: K/ N0 G9 s
- #include "adc.h"
7 w" L, Z* f% k" K3 ]" |( m - #include "usmart.h"
$ }- \+ l. I. v, Q( S# U) q1 t: L) u
) Z: Y& g/ u. s4 J: n( n( X- int main(void)) `( b# a2 N3 f3 }
- { " R% N2 L8 Q% S/ N: C
- u16 adcx;
- Z4 C- I2 y2 y/ E# g+ L5 x - float temp;8 @! G' C# Q/ ?; B! b5 p
- u8 t=0; ! { E; m% a( J$ R* } T/ o
- u16 dacval=0;9 r3 S: s w+ M. o% ^
- u8 key;0 [$ R5 @3 i% U V# C5 Y
- & l. c0 C# ~ J- ?& f
- delay_init(); //延时函数初始化 / O1 Q/ U& {/ @6 {+ [
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级5 J2 C# u7 M6 _) ?) p4 X
- uart_init(115200); //串口初始化为115200
; ^: N! p$ @5 L. A - KEY_Init(); //初始化按键程序
) Z* i, Q1 L2 O- p9 F0 `7 p( B - LED_Init(); //LED端口初始化
2 V- ~! c, ^% B7 ^' n B0 a$ i+ p - LCD_Init(); //LCD初始化
7 F7 T5 R. @( ^- k/ n3 G+ M - usmart_dev.init(72); //初始化USMART 2 T& Q" s9 ^- S6 A4 r. s
- Adc_Init(); //ADC初始化) [- T% e- E$ o7 S7 K" n
- Dac1_Init(); //DAC初始化0 U1 c0 G) h7 t$ v
- , u4 t- [ k ^9 I' y7 p
- POINT_COLOR=RED;//设置字体为红色
/ ` R. B4 [3 `2 K: s - LCD_ShowString(60,50,200,16,16,"WarShip STM32");
8 i$ e, S0 e3 {7 K2 @& H7 N$ | - LCD_ShowString(60,70,200,16,16,"DAC TEST"); * \8 F f( N: z8 @: e
- LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");: v# H6 ]( k' A
- LCD_ShowString(60,110,200,16,16,"2015/1/15");
1 D$ Z: w: P8 R - LCD_ShowString(60,130,200,16,16,"WK_UP:+ KEY1:-");
2 \5 j8 K$ ]. o! G - //显示提示信息 ' O# n6 [! G1 w
- POINT_COLOR=BLUE;//设置字体为蓝色2 W& ]! O5 K! i
- LCD_ShowString(60,150,200,16,16,"DAC VAL:");
$ ~) t, T# X0 X2 a! R - LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V");
5 {" D, ^/ l+ d8 j( X' R - LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");* P1 E& g/ b0 I
- ( M; ^& W: S' S
- DAC_SetChannel1Data(DAC_Align_12b_R, 0);//初始值为0 2 a" [/ V* m! ` y) M8 V; a
- ( t7 I* a6 Y: e. F/ g# z
- while(1)
- Y4 D3 ^, ?1 l2 u @' R7 J - {' m# P1 x7 ]1 q1 E# S7 |
- t++;
5 L$ y$ v( G- s; h: j - key=KEY_Scan(0);
' |" C6 b2 t: ` - if(key==WKUP_PRES)" r3 W( [/ ]+ v7 w+ v) J3 u- @3 Z! m
- { - h4 z3 y, G! _
- if(dacval<4000)dacval+=200;
9 r$ `1 T, }1 t8 u# k6 Y - DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值
2 b$ L. h+ Q. d2 w! }& \/ g3 B - }else if(key==KEY1_PRES) 7 |% Z8 x0 V9 L" e" K6 Q
- {
- ?9 m! q; V1 s6 z% B) V% o - if(dacval>200)dacval-=200;
6 u/ G D! C; w6 d - else dacval=0;
T2 _5 G9 M" k# e/ U; c - DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值- M. S* o P1 C
- }
* `: T0 w8 d# u) J3 | - : ^& w: h4 }7 H4 I0 o/ R3 y8 W4 ^6 K
- if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1按下了,或者定时时间到了
: P X% R, Y9 p - { 8 L/ ~$ t) ^; D2 e3 r; K5 `9 ?
- //获取DAC数据# f% U. ]# p# O( |; N
- adcx=DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置DAC的值
$ C& L; D- f5 ]: v - LCD_ShowxNum(124,150,adcx,4,16,0); //显示DAC寄存器值$ _4 i& s5 V, x2 f, Z" {; f& Y
- temp=(float)adcx*(3.3/4096); //得到DAC电压值4 |( F9 W+ v8 @: n, @9 \
- adcx=temp;
: o/ {& \& B7 O: D1 J0 K - LCD_ShowxNum(124,170,temp,1,16,0); //显示电压值整数部分
: W2 K( z2 ]" w" I - temp-=adcx;
t, T1 |. d% _% c" e6 J1 i9 r - temp*=1000;4 k, h$ S0 n; {9 K9 l6 w- T) f9 D8 ~
- LCD_ShowxNum(140,170,temp,3,16,0X80); //显示电压值的小数部分
3 |% [) t/ _! q) E! m* E -
% U, P4 I0 w0 T+ K& f - //获取ADC数据7 J! n g4 j2 e5 H* `6 ~
- adcx=Get_Adc_Average(ADC_Channel_1,10); //得到ADC转换值
# D) Y. ?( Y) k - temp=(float)adcx*(3.3/4096); //得到ADC电压值
+ q2 J8 f2 o' }: \1 g* R1 k% E& U - adcx=temp;
, w- i- L. G4 c3 g - LCD_ShowxNum(124,190,temp,1,16,0); //显示电压值整数部分$ E f$ p' e! R2 }* P
- temp-=adcx;
C) z9 L& Y# V+ V' \ - temp*=1000;# j: ]) r: J% Z* U
- LCD_ShowxNum(140,190,temp,3,16,0X80); //显示电压值的小数部分* i! X6 r& H; C# z* ^
- LED0=!LED0; & i }, r- r L' T- H- r
- t=0;2 X, [" `/ o( u5 o( _0 S6 a. q
- } 3 A3 m o# U' W9 T4 M
- delay_ms(10);
9 U6 H; t* P/ r4 D% n/ | - }) E+ s. s; Q. X; V# o( G
- }
复制代码
: }& f' o6 l4 Udac.c函数
_6 }9 x& S7 Y1 L; b& W- #include "dac.h"- }" g' R- x7 J4 d# `9 j% \) F( L$ W
1 Z* e7 F/ h( V u7 G2 a; R- //DAC通道1输出初始化/ i- C. V( h3 s" |. w* Q6 Q
- void Dac1_Init(void)2 d0 J* O2 Z/ W2 ?, K; Q
- { ! B# m9 O' V! b r& P
- GPIO_InitTypeDef GPIO_InitStructure;! H- _3 l0 _6 w) W
- DAC_InitTypeDef DAC_InitType;9 {# s+ t# ^( A2 ~$ e2 {% q) Y4 R5 V
- 6 G" ~* ~ A: n/ `
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟* y3 |+ {9 J- n) Q6 p
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟
- S0 r& q; B, J/ y! x# S3 z - ( J2 t- o% a6 q) p: G [
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //端口配置3 ^: v4 s' v/ K- m9 x
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
' K, e% p2 t$ z# t% X# o0 J+ Z - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;: B3 K* `. E! A
- GPIO_Init(GPIOA, &GPIO_InitStructure);
( u; E1 s& F3 b$ ~: K - GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4 输出高
: k Q; }. S4 b7 }* k8 G -
+ u$ |$ `* V* Z/ W- R - DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0
6 ]1 x" G0 T7 F0 p: { e% [ - DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生: ^) i* H0 v9 k2 E) p% L9 J( e
- DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
* n- _5 Q+ e# E- k5 j5 d# t7 I( y - DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1; m, _" k2 h! ]! e8 z# D1 n* k
- DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1
( k9 a8 e s0 m- n$ n2 ~ - : X& i! M8 s9 M# R9 f
- DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1* H( L7 ^6 m: U8 \5 K
- . D1 d- b" w' x$ V
- DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值$ ]) O8 E9 q7 g+ T9 P3 ^) F0 I& ]" ]9 b
- }/ P5 S8 g- S2 |8 z2 S& U
7 ]5 p4 d% l; b: N% x! a; Z( J! c* K- //设置通道1输出电压' \3 B( e0 D7 C" `
- //vol:0~3300,代表0~3.3V
3 W; h6 Z* q& H. s! C+ `# [) L6 ] - void Dac1_Set_Vol(u16 vol)
6 h" A$ p) Z/ I4 t - {
+ W+ J' |! Y7 {! I - float temp=vol;, J4 q2 L8 |% }0 @
- temp/=1000;
/ w" [- j% g( B' Y4 _8 j7 q - temp=temp*4096/3.3;
* h/ A$ {# e; F - DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式设置DAC值; W5 \7 A n0 p- q' E
- }7 h! M5 X9 @% B0 s/ ^0 l
复制代码 4 M- y7 B3 B5 m' Q3 b4 g
adc.c函数9 Q: u$ \- b" [0 k$ b
- #include "adc.h"( r, V A7 S8 }$ e; K0 ~
- #include "delay.h"3 W4 B. ~7 D1 m* Y8 C+ o% A
-
# y2 _# W5 r& J - //初始化ADC
) K7 t7 i1 L: D - //这里我们仅以规则通道为例
, {( X0 F: D/ F2 O7 C* } - //我们默认将开启通道0~3 $ \+ X9 D z* _% V4 A8 y$ m
- void Adc_Init(void)+ U, E8 N3 ]: G8 y6 o) f! y' Q
- { + Z( D: g- G- r8 X c$ o
- ADC_InitTypeDef ADC_InitStructure; h, V' p! I! Q8 H, W
- GPIO_InitTypeDef GPIO_InitStructure;
- [) `7 p- z! B/ c$ E7 S
6 g# ?2 f2 l9 s4 y- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟
& a1 e8 X4 y9 U3 N6 l
3 n+ T/ M0 z6 G9 v) y. a) }. S- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M) S: K) b0 ?6 e, ]/ V3 L
- 0 M" i4 N" P# X/ W
- //PA1 作为模拟通道输入引脚 ! h& b; P( N- T U5 Y+ I$ g( ~
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
" b9 H9 M0 x- o, k7 n - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
# r/ D/ Y& `4 } x) W - GPIO_Init(GPIOA, &GPIO_InitStructure);
7 h0 y) L( p5 M
, _$ P: x1 g: ^: D& V- ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
5 I% z4 L1 |1 i8 \2 @8 U - * i# \$ g) h5 k# q4 x
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式3 v" W1 R% s0 n. |3 [
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式+ } [/ y! y" H+ X0 x E) C4 M
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
/ d% ?( ]& k& `5 o" z1 C - ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
) q/ J x& t! H: x) G2 g - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
3 ?% U( d" f& g8 R+ h# J# B% R - ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
! y' c1 |# D) w) _# [ - ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 . Y+ j& x8 P# O3 O. g) ~% E& S5 I
- " R7 H% F! A' h4 }, s+ \5 Q+ U/ B( V" o
- ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
% C2 ~: Q v* r( T9 k -
; _! v' T1 l8 I; _8 g - ADC_ResetCalibration(ADC1); //使能复位校准
/ ?# t0 P4 x0 g1 w0 O - 1 ^! @- I8 \. U. T+ L3 u
- while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
2 B# T: Z c }/ W7 n - & @8 p/ b4 z) A5 f7 f7 \6 v7 p
- ADC_StartCalibration(ADC1); //开启AD校准
& n/ n( S% E( Y5 f- e8 ?/ ~* g - $ @5 _& g5 ~2 L2 Y2 ?
- while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束7 _. n9 P9 T$ Y3 O0 U9 n1 |
- } # R1 D3 {: [; `/ g
- " B0 t' q* \* _. U
- ( |" O$ [- r0 f4 g4 @9 F
- //获得ADC值
- f+ g9 t9 B5 Z% q' B/ S - //ch:通道值 0~3- l1 T& S( [& C9 E; B8 R, A
- u16 Get_Adc(u8 ch) ' S( l+ t1 U) O; K0 l
- {
6 Z, j% p' u: T6 o2 ?0 W9 ?$ }0 S - //设置指定ADC的规则组通道,一个序列,采样时间& r' `% f* N- G% b( |
- ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期 & r. j6 ?; @( b0 K6 I' W( Q% `
; \ n+ J! o. W: H- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
4 q6 m9 s2 X8 I( f6 o% w - * u/ a/ D- l2 m' K" U2 k/ k
- while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
8 a# W9 ]0 r% u" ?' i - 2 R% V" ^8 E. a- E4 B) Z- j6 Q+ d
- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
5 @* U3 O1 ~2 m4 d - }$ v2 K/ r% B; \- G" ]6 `
- 9 v8 G9 ^1 Y8 D; [
- u16 Get_Adc_Average(u8 ch,u8 times)
9 m6 \* W. h- x" ?1 D: N - {
: q7 j% v; w& L% S - u32 temp_val=0;9 e/ m' N' B9 w9 w
- u8 t;
! u' {" p- A& o# m* x5 M - for(t=0;t<times;t++)
8 `2 g% K$ {+ i9 i - {- N6 k7 l! p/ u1 P- v& H+ }
- temp_val+=Get_Adc(ch);* |! d& n: I/ w: a; i- H
- delay_ms(5);
_, u" k" w1 V$ S" h2 ~9 a - }2 i" t# |1 F% i& L) B
- return temp_val/times;/ S. f# C: ]" W. U: {7 r) S- P
- } / v4 c' `5 n' I# s: V w
复制代码
0 G$ X. W+ y0 C/ d Y4 [7 G) w从 main 函数代码可以看出,按键设置输出电压的时候,每次都是以 0.161V 递增或递减的,而通过 USMART 调用 Dac1_Set_Vol 函数,则可以实现任意电平输出控制(当然得在 DAC 可控范围内)。2 R3 x- `1 c0 D" C* `
4 h# b: T. ] M2 X
. ?3 V5 h, N( V2 e5 Z1 W |