本章介绍STM32的DAC功能。本章利用按键(或者USMART)控制STM32内部DAC模块的通道1来输出模拟电压,通过ADC1的通道1采集DAC的输出电压,在LCD模块上面显示ADC获取到的电压值以及DAC的设定输出电压值等信息。% D( y. }( N4 }* k) U( p) k
g2 b/ y3 y5 Z( K: X& {
DAC本身是输出,但是为什么端口要设置为模拟输入模式呢?
" \- p3 P0 R# C' { ?2 D v$ |因为一旦使能DACx通道之后,相应的GPIO引脚(PA4或者PA5)会自动与DAC的模拟输出相连,设置为输入,是为了避免额外的干扰。, }5 R9 l! U, Y% @8 M, r
+ i1 x( o8 c6 p) t0 T* j7 M& x a
$ l/ i3 B3 N' s( T
6 N! ]+ H! ^9 O/ h
& C0 k1 p7 f6 |5 H( b1 s) ?
) C7 @9 O% @3 E7 y( G6 c. [/ N
9 w2 _) ` e6 h& h/ Z6 b
5 |8 ?1 s- ^5 [; O/ n( p2 l0 j m; \9 D* Z. I
: r3 ~% U3 f! ~# j" E7 \# z) u' W( N# L P) v0 a N
1 M0 X' b/ r, Y$ r0 h
7 r- m2 p. c o. u( L! v3 I7 R* I# n9 c( v7 U7 Z* X1 u7 i
硬件设计# Q7 k, U& X5 S" U* G
$ D( a. I! m; T/ | }
# Q; U( ~ Q7 O% V0 O! |; y
- [0 s, d m: qmain函数# a3 x" F% `7 i6 n5 ^" [& S8 H
- #include "led.h"! r+ _) F. U* T
- #include "delay.h"& c) i; [8 E& d- q9 j
- #include "key.h"
* N* V) }7 _* s P; Y; x - #include "sys.h"
1 H! P ?& q# O3 u j9 X - #include "lcd.h"
# B- M$ x+ z: E G/ K; Z7 e" p - #include "usart.h" ( o- H$ `% @: E& q2 D
- #include "dac.h"6 W0 O" Z. Z5 l9 W
- #include "adc.h"/ y3 ]4 @, z z4 R5 y9 E- e
- #include "usmart.h"
* J$ n" d& `+ w- Y z
% m9 V6 x! M+ a- int main(void)0 A8 P8 u( E/ y+ Q: p: v) E
- {
7 L6 _6 O; X; H1 o! _+ }) [. { - u16 adcx;6 y0 m ^/ b% s( D% I0 v
- float temp;9 d: ^ U+ `6 b# M2 \3 E
- u8 t=0;
/ v. e9 j# _0 [ - u16 dacval=0;+ I. j7 x2 v! Y1 r; L
- u8 key;7 \- z+ F" H' ]% [. @
- 1 M; N( {$ x5 h- y9 y' C9 s5 {- ] k
- delay_init(); //延时函数初始化
( r y a. q2 W - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
0 C- a, v; O# T% E% Z5 v - uart_init(115200); //串口初始化为1152008 k. b% D0 T' s! K$ v
- KEY_Init(); //初始化按键程序
' w1 F7 H5 J- E* R9 n0 h' c - LED_Init(); //LED端口初始化
4 b0 D! @+ c+ e - LCD_Init(); //LCD初始化
" q' C/ q# M4 E1 J7 a& Z# _% j6 w2 } - usmart_dev.init(72); //初始化USMART
$ j) U j* z: k' Y/ E# Q1 J- B - Adc_Init(); //ADC初始化
& B! _: t! n8 R: w9 K i2 G { - Dac1_Init(); //DAC初始化
' P& M, Y2 r! ?5 r8 l, _ y& Z - 0 m# ]3 T: C: i
- POINT_COLOR=RED;//设置字体为红色 4 h$ e4 S/ N1 K0 u5 \. Z0 l/ k) A" E
- LCD_ShowString(60,50,200,16,16,"WarShip STM32"); " S- N5 V# Y2 y4 k# S$ g5 B4 g0 ~; M
- LCD_ShowString(60,70,200,16,16,"DAC TEST");
: ^8 K& F% R: M$ p - LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");/ d4 T! r$ \, o2 G$ O5 m/ o, B( ^* v' L
- LCD_ShowString(60,110,200,16,16,"2015/1/15"); y' W+ |/ _/ j8 R
- LCD_ShowString(60,130,200,16,16,"WK_UP:+ KEY1:-"); " w3 T# w. {( l$ c: H" M
- //显示提示信息
; Z# a C; n6 o3 _$ s - POINT_COLOR=BLUE;//设置字体为蓝色0 C" | W* `8 F, e/ x" w5 S
- LCD_ShowString(60,150,200,16,16,"DAC VAL:"); ' ?# ]5 z4 _) t3 b2 m: `: ~* l. \
- LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V");
* l( u4 `7 B3 q0 @- l# o - LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");
+ Q* l' s; f7 W - ' f! n; s6 ]* H7 `9 u5 U
- DAC_SetChannel1Data(DAC_Align_12b_R, 0);//初始值为0 3 P% b( m; B+ C
-
' u" ~) {' e! J N# v+ }' A! m0 ~& \ - while(1)
: }0 b' [( @: h/ _ - {& }8 v2 B \- Y
- t++;# x8 @( M, E* n, n7 w7 l
- key=KEY_Scan(0); / V/ E1 B5 q* G4 a( j6 c
- if(key==WKUP_PRES)6 d7 Q: m6 S+ n3 h$ [# z2 m
- {
( O* J" ]2 F6 z4 R$ Z - if(dacval<4000)dacval+=200;# \/ o" I. w9 N- x
- DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值
- Z) f: h* P6 D: {- J' o' z - }else if(key==KEY1_PRES) - r- x% ^; a! E. V' H
- {2 K. o2 B f7 P* C N- X
- if(dacval>200)dacval-=200;
8 d8 L8 y& `4 F2 [8 X - else dacval=0;" ]4 u0 Y! P. G4 m
- DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值
5 L2 x$ q B, B! v4 u - } + t2 ~1 b3 n+ t; ]
- # [' S6 P$ _% {# G; [1 K
- if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1按下了,或者定时时间到了% G9 }6 o' i* X
- { $ t( V. y, V" {% Q4 h i- y
- //获取DAC数据5 X; ^. o& n$ p: y! q
- adcx=DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置DAC的值
! b! f4 j$ l9 }1 l( m# H - LCD_ShowxNum(124,150,adcx,4,16,0); //显示DAC寄存器值
& B# H# h) e% ?* g' c: P+ l; U# ? - temp=(float)adcx*(3.3/4096); //得到DAC电压值0 W% B. z$ r7 @0 J
- adcx=temp;7 F5 f. j, p% R D8 d$ u
- LCD_ShowxNum(124,170,temp,1,16,0); //显示电压值整数部分5 R1 i3 D# R& h2 G! j f, T
- temp-=adcx;0 S2 ~5 G' i1 O. x
- temp*=1000;
- v3 p% c9 s( l) ~ - LCD_ShowxNum(140,170,temp,3,16,0X80); //显示电压值的小数部分3 Y+ ~3 M7 c' W; b# s, m
-
' ?: W6 B( e( p# p4 v# l - //获取ADC数据
Z/ p8 P) q/ j+ g! `. ^( k, E - adcx=Get_Adc_Average(ADC_Channel_1,10); //得到ADC转换值
* _4 C* k" |! X6 H8 \) R - temp=(float)adcx*(3.3/4096); //得到ADC电压值
* C6 A# E1 G" E# _ - adcx=temp;
' x& z0 ~' A! U: \1 {2 @ - LCD_ShowxNum(124,190,temp,1,16,0); //显示电压值整数部分
- q1 z" B0 |% ~" r! D+ c( z - temp-=adcx;
! }+ I8 e! G9 j" w - temp*=1000;3 Y( E l j9 c5 ?' {
- LCD_ShowxNum(140,190,temp,3,16,0X80); //显示电压值的小数部分
' y4 _+ v. s. M - LED0=!LED0;
~1 m+ E- ~ |9 H; d) U( H - t=0;
) ~: c4 \/ S$ q+ w( s4 v/ Z4 E - }
) ~9 {6 D) }8 H7 _ - delay_ms(10);
$ s& T% `$ Y9 J9 M - }
5 P6 h# t1 q' M0 Y - }
复制代码 ; r" f+ W4 N) [9 C
dac.c函数
7 ^. W6 F: ?3 y- #include "dac.h"0 t# }# O- k0 C
- , z; ?: S) m: x
- //DAC通道1输出初始化
$ u- B1 u: r* g, S! \; u - void Dac1_Init(void)
+ T% W6 }3 ~" G4 j+ [ - { ( I! }6 ]1 w" v1 s% Y5 C
- GPIO_InitTypeDef GPIO_InitStructure;
. T8 ]' m4 f& R" }7 s6 _ - DAC_InitTypeDef DAC_InitType;
0 U2 {6 @9 U. R. ^) I0 b
3 T5 `# J. Y9 C3 I! m0 V- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟
' w, Z8 K$ Z9 J - RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟
, |) h3 j; c- C2 G- `) f- | - & c! _( @% s* n \
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //端口配置5 y" C! M1 g" h/ c" _' }5 a7 c
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入/ w- h: Q7 l" c a/ \
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
2 Q- m8 G3 j1 `3 C5 }% h - GPIO_Init(GPIOA, &GPIO_InitStructure);
' }; j0 o3 W6 y+ G - GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4 输出高/ g: X8 E4 f% X9 V: G# A0 l
- 6 t7 T9 K3 e0 [& |, \
- DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0" E( i, g) |+ I6 J
- DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生: l! R$ i! r/ Y' s% H4 M
- DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
$ A! r+ k. G/ {$ s - DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=15 T) {! g4 z( o* Y4 E
- DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1
' u q" F) m3 m9 f; L1 p$ a
3 Z s3 H7 l+ U) l- DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC12 \3 E6 u1 _! p' o; J
- 1 u/ Q9 U; C5 Y5 {8 z4 ?! w
- DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值
# P2 U! n7 Y- r; ^ - }
, G c' @5 k. a) v6 C- Y/ ?, r% X - ! E5 v; R5 w1 e Q
- //设置通道1输出电压
) H. [: B4 `' C# i( }( w - //vol:0~3300,代表0~3.3V+ ?* K2 [+ S# m8 m& u9 ^
- void Dac1_Set_Vol(u16 vol)
' P. @) A; X. t& C0 |9 T# @ - {; m7 Z+ \! S5 u4 I6 s& L, A, U) p
- float temp=vol;" ` ~6 K8 j L6 R A) b0 V
- temp/=1000;9 t; Z& T" o" k* o$ D
- temp=temp*4096/3.3;
6 o$ u4 E1 g( G5 S5 `5 V; I - DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式设置DAC值4 ^: e, E0 s0 z: ~3 ^
- }4 b2 K1 {1 }# O2 b: H5 p
复制代码 r- t- \0 U' Z. s' M+ P5 z
adc.c函数
) x) @0 m9 v. y0 W- #include "adc.h"
% f3 O# }! Q5 d: J" v$ O/ h - #include "delay.h"% R1 K1 S: J K
-
- C8 k, n/ Z( a# b" R& [5 X - //初始化ADC9 r. b$ \1 K# G$ r8 v9 S" T
- //这里我们仅以规则通道为例& u% w* G2 U# U. O4 e5 a
- //我们默认将开启通道0~3 & A5 i) P$ |. x3 y4 X$ N7 i
- void Adc_Init(void)
$ G5 }/ B( K+ P0 k: A - {
+ M8 t! o# l' ?5 n4 A+ x2 ^ - ADC_InitTypeDef ADC_InitStructure; 5 {' K0 p# ~* F/ a p
- GPIO_InitTypeDef GPIO_InitStructure;
4 M. k: H/ d A3 }1 L% f# h/ { - t* n! Q3 m$ d
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟
8 U* T' a6 E) o9 B2 y6 F
$ j. W6 @$ k: J$ R% j2 N! I- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M; p* W! S+ m6 E; z! G# n0 ^! S' z
- 8 J1 h# V7 U" q) g3 i! j
- //PA1 作为模拟通道输入引脚 + X- t# l5 V" C2 n) H/ K
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
8 g) G& w$ M( H2 }" W6 X - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
& H2 }: u) j' v$ J# ?& n; C' V+ F - GPIO_Init(GPIOA, &GPIO_InitStructure);
' K; d4 D' _$ C& @ q0 e0 H& }
! F* f& |* d! w" W4 |! t3 P1 n( a- ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
# Q1 e* P V; i) ~- k - $ t, R0 F/ g+ V o6 u) a( [5 D
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式4 J9 d L; d4 z' s1 @& R
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式8 A* ~, h. F( k7 @! g$ x4 D Z
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
' f- X/ Z/ m3 ~( ^6 C& B1 t - ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
5 h3 \* E1 W. r - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐; V5 ]# U" d$ W+ b5 R5 L
- ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目* l+ V. P* T. ^7 X H
- ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 . Z4 P% h" f4 r+ M" H- o8 L
- / }7 l# Z, t C: ?
- ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
/ e0 w* W: H8 g% B6 S - ' Y4 {/ s6 i w$ L7 B2 }
- ADC_ResetCalibration(ADC1); //使能复位校准
, f- M7 c A8 n3 k - 5 @/ Z) P2 ]( O6 A: |
- while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
/ g, u3 A! y8 K t -
; w# v' d8 @) p9 N, k - ADC_StartCalibration(ADC1); //开启AD校准- q. [( g9 S% q( [
- " j$ q3 e' A& ?( f# \6 u3 }* |
- while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
* h& ]" S& \# P' A - } ! B, y$ I Y* H
6 A/ t( I" _( E. b0 T- ( O8 x6 _. p4 R# q J0 H8 b
- //获得ADC值) S3 s F. M. ?: V* X! V3 V8 y
- //ch:通道值 0~3
6 o" I; [8 ]$ \ - u16 Get_Adc(u8 ch)
% ]* _! W9 r9 `1 t( j, H7 J) Z' V4 Y - {& T8 M' v% ` w6 e/ S8 \
- //设置指定ADC的规则组通道,一个序列,采样时间
# z$ M3 X9 L$ X" j - ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期 , X5 t2 n" P( o1 p2 B+ f
/ p% B" S+ L9 q1 D- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能 2 u7 L! P( W: H) T
- ' m$ T3 F8 w; r" K/ C/ C
- while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束2 }/ c( q& |1 a, E* G
- * u4 d M2 Z2 `0 m/ M+ f; e
- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
( C' ]% Y! R; Z0 k; @+ ~) h/ v' h - }+ H- S. g- Z d8 t, S. k8 d
- ; G# a- k* I2 q9 C! W
- u16 Get_Adc_Average(u8 ch,u8 times)
* c; ~% H9 c6 H0 p& v - {
6 z) I" ?! H3 O+ p) f& W - u32 temp_val=0;
* T; v/ v. B4 z, L) H" F+ h3 R+ o - u8 t;2 d$ _( i$ Y4 w& B
- for(t=0;t<times;t++)
% u5 ]- H9 @/ V) g - {. D0 z6 D; X7 s
- temp_val+=Get_Adc(ch);1 W- A, x% y9 y* D8 X- v1 t
- delay_ms(5);4 u$ Y" i0 m0 @6 v$ [% R
- }
3 @6 V. L5 h8 s ?5 t( W2 r - return temp_val/times; m8 E9 Z4 w- E w+ H' T( c* V
- } * _3 X l# G+ Y$ L* k4 ~0 V; b7 s
复制代码
6 g6 D. r1 I7 n3 L# S( k: G从 main 函数代码可以看出,按键设置输出电压的时候,每次都是以 0.161V 递增或递减的,而通过 USMART 调用 Dac1_Set_Vol 函数,则可以实现任意电平输出控制(当然得在 DAC 可控范围内)。
6 r) e! @( p9 v
3 i7 T* y; l7 J$ j9 X0 h |& m& F+ |' `- q% q C
|