本章介绍STM32的DAC功能。本章利用按键(或者USMART)控制STM32内部DAC模块的通道1来输出模拟电压,通过ADC1的通道1采集DAC的输出电压,在LCD模块上面显示ADC获取到的电压值以及DAC的设定输出电压值等信息。
3 a/ E4 ~# q( t" t! f' p4 u) h& O; z8 j5 I+ k
DAC本身是输出,但是为什么端口要设置为模拟输入模式呢?
1 Z* p5 _% H& k$ s8 x. K+ ?) ?6 ^因为一旦使能DACx通道之后,相应的GPIO引脚(PA4或者PA5)会自动与DAC的模拟输出相连,设置为输入,是为了避免额外的干扰。
/ ^) \5 c9 H# U! V: M: O& @& t( V3 Y9 |6 m
1 R3 J0 Y Y# k) R* {+ i
) I4 R/ p1 `' ]2 e3 s
, m$ u* W' g: s! a" d
* U7 ?1 S5 C* y, Q
" R K/ |/ l2 `. h* H4 W% l; s" B
# ^0 Z3 t2 y4 O/ b4 B2 c' U3 C, Y; r5 | J
* A- l) ~- ~8 H$ ]3 `4 P5 e- m7 f E- c- N
. A4 L% Y6 P! ?! d1 }0 y# a) ~
5 z7 C) ]& V$ O6 r4 B1 |
2 B7 ]8 H& ? j3 S+ [
硬件设计2 r8 N# y( O. s ?: f
: |4 d8 v" _7 A! ?$ e5 J9 v
3 s3 D4 G* h5 |; o+ V
, p1 ?* |# A3 `% Q; t+ x" mmain函数7 k H, p/ C& B% Q
- #include "led.h"4 t# a s3 f& g
- #include "delay.h"
7 ~$ L8 j1 a7 c9 l - #include "key.h"
" g6 r% x9 D: [7 J - #include "sys.h"
# l0 h' Q1 m. I- d0 }! t - #include "lcd.h"
4 g3 |5 o+ c) N1 c) F - #include "usart.h"
, S3 B* U' s3 ~# `! A; D3 Q, u - #include "dac.h"0 p3 g5 }( s# \! W6 P9 s: g3 I- C
- #include "adc.h"
3 d. e8 O2 @) p, N, J( m - #include "usmart.h"* |! |4 O% E0 p* C" v
- + Z) F% G7 s* \3 M
- int main(void)! w5 X* d2 N: ^2 @1 N
- { 3 T8 O% n/ d U" y! F$ Z- d, c; S
- u16 adcx;3 v1 d1 p) D# J
- float temp;
' W8 Z% F" e" x" _ - u8 t=0;
5 T( h8 x& O! t+ J! L+ i+ L7 I/ Z* Z - u16 dacval=0;! |7 k6 N- n/ s) }9 q" X( B2 r) c
- u8 key;- y" n4 K! G' D: p K, }6 D: \- p
-
5 B. S9 J8 G3 s7 l - delay_init(); //延时函数初始化 & I- V$ ^0 O# B( y8 o5 x
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
- [% V- A0 o0 w3 S' E0 ^+ a) i- ? - uart_init(115200); //串口初始化为115200% D0 N4 }: r. Z& m- n
- KEY_Init(); //初始化按键程序6 j) V) a. ], y* h0 M
- LED_Init(); //LED端口初始化
: _! X/ a, J0 c2 h' c; x9 o/ D8 P - LCD_Init(); //LCD初始化8 _- I& Y+ S* X- g7 a u
- usmart_dev.init(72); //初始化USMART 6 J. ~4 C' F. y# }1 n; O
- Adc_Init(); //ADC初始化
4 k7 p' F; L( x" r5 ` - Dac1_Init(); //DAC初始化* X% A; E( ~5 @& p' ?! T" I) m
- 1 o& O1 t& b& |2 M% U0 W
- POINT_COLOR=RED;//设置字体为红色 # ]3 ~( e4 Z& D% A
- LCD_ShowString(60,50,200,16,16,"WarShip STM32");
8 c& G- F. D* C0 @. z3 u4 y4 D0 g - LCD_ShowString(60,70,200,16,16,"DAC TEST"); 4 F) T8 Y0 B+ D* L$ B- Z
- LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
" ^0 H( G, w$ ?( d - LCD_ShowString(60,110,200,16,16,"2015/1/15");
, C' N7 b3 [+ A1 T! ^ - LCD_ShowString(60,130,200,16,16,"WK_UP:+ KEY1:-"); & Y* V1 Q$ B0 N' x H
- //显示提示信息 # M' x. p4 i: U
- POINT_COLOR=BLUE;//设置字体为蓝色
' \5 t% L3 n4 o - LCD_ShowString(60,150,200,16,16,"DAC VAL:"); - E* V( d0 M4 K$ E; W
- LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V");
: I" Q# s* x3 S% s4 d9 e7 a p6 U - LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");
% g: Z" T2 ?0 j. H! q -
# V3 j. f- ?! m$ o) ]" O - DAC_SetChannel1Data(DAC_Align_12b_R, 0);//初始值为0 3 |, U! g+ d1 k% R& s$ u
- ( z2 H/ n) S6 l6 Q# C# n: ?
- while(1)
" p1 _9 |# U# I: J5 B2 v - {( d' e* f# E# W& E8 J4 i
- t++;
6 n+ r) ^" S" ]3 D - key=KEY_Scan(0);
5 N& i* w- i( Y$ ^$ T! L - if(key==WKUP_PRES)
H' y9 ]% c: x+ H. N' B - {
) w+ W' v/ ^+ U D' { - if(dacval<4000)dacval+=200;
# X* ?7 r; c- J6 ?+ s2 ]0 X( I - DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值 : U3 a& r3 l {+ p! m7 ]7 W
- }else if(key==KEY1_PRES)
/ ~" g5 t1 M; I6 d - {
) a* a; {) M( h- g - if(dacval>200)dacval-=200;
( R) g" [7 [9 M4 K - else dacval=0;
# s' G5 O* [4 V6 L. s g. g - DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值; O1 A& e. U- V( U
- } - l( \, ^& K, v! I6 Y
- " ~1 m, }0 H1 N9 Y) C( F- f3 {
- if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1按下了,或者定时时间到了
# u( {# S( c. e - { $ {$ P9 A$ ^ I- R2 H# {
- //获取DAC数据1 I* t9 L7 @0 e5 r E( N+ w9 l
- adcx=DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置DAC的值
, e- K8 O9 @7 N$ _5 k: X! }6 { - LCD_ShowxNum(124,150,adcx,4,16,0); //显示DAC寄存器值: U$ u( o9 ]3 r
- temp=(float)adcx*(3.3/4096); //得到DAC电压值
0 _. a5 e0 N! X$ m - adcx=temp;
6 { C; S6 c/ K1 {( l6 N - LCD_ShowxNum(124,170,temp,1,16,0); //显示电压值整数部分
m( {& }% G$ F$ g* S x1 v - temp-=adcx;7 f9 E6 m1 ]3 S& T5 w& y: c) G
- temp*=1000;
* H8 z3 X9 S6 V; ^; H* { - LCD_ShowxNum(140,170,temp,3,16,0X80); //显示电压值的小数部分
* X, U0 V( p: s4 O0 x$ f - ) b; Q+ @$ s8 w2 z2 ?
- //获取ADC数据
1 ?& M# I/ H3 f' S; t - adcx=Get_Adc_Average(ADC_Channel_1,10); //得到ADC转换值
& G: I% p" Y4 r4 a( O% a ^ - temp=(float)adcx*(3.3/4096); //得到ADC电压值
; U! v8 L9 }2 M, x; T - adcx=temp;' k' O7 j- a+ @, b- V6 x5 }6 }
- LCD_ShowxNum(124,190,temp,1,16,0); //显示电压值整数部分+ z% l; {9 Q, i$ B# j1 t
- temp-=adcx;
2 k2 |# r# s v _: y; R - temp*=1000;
0 x6 F5 B: `. U) d1 ^% w( D% y - LCD_ShowxNum(140,190,temp,3,16,0X80); //显示电压值的小数部分
2 E, q$ V( q8 M! Q, f5 Y1 L - LED0=!LED0;
7 G* _ T2 Z8 V4 N8 m* j - t=0;. ^0 \+ K) J" l( [+ I' L) k* o8 X
- }
$ q( n0 c# A2 D7 j$ ~ - delay_ms(10); + x" A- e% R$ x) v) H- o
- }4 J% Q% D6 B. i; f9 G; {% \# q
- }
复制代码 + }. e* F2 w! l; r/ n
dac.c函数; z+ R/ {* ^4 M5 Q5 k, ]" H
- #include "dac.h"9 l! _0 _! i( ~6 b3 W7 a) k
- ( Q' l8 I& w4 g' e- ~8 }
- //DAC通道1输出初始化
+ \2 w* [1 Y' E& v( d - void Dac1_Init(void)6 S9 I! w! s1 K' @$ o
- { 5 Y* n# u v3 l/ k4 B
- GPIO_InitTypeDef GPIO_InitStructure;/ ^5 ?$ [, ^# B
- DAC_InitTypeDef DAC_InitType;
I( I+ O# x7 y2 G - ) ]0 X7 W$ w2 s' K( L+ u
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟
' n$ q6 S2 b$ c3 k - RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟
, |0 B5 z6 B x( ~ - 9 c5 q$ @: X7 f y: m; m
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //端口配置
* `1 Z( t9 Q C - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
" W; L. e* m+ G8 f7 x0 S - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
* ^5 \5 k( _5 |6 f' o- y! A - GPIO_Init(GPIOA, &GPIO_InitStructure);# E E& ~+ f4 z# `
- GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4 输出高
$ m) M4 R: Y x -
: U3 g6 ?/ A7 E# i3 C - DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0) u+ j' E: {" R( v9 G* @$ s* {- y
- DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生
4 T4 h/ y6 f& h# H - DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
5 l# Z4 T! I1 _( P9 l @% ]$ D - DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1
9 x H0 M5 K) Y5 I; O - DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1+ ^1 m) t+ n5 h* L' Q4 t. @
- G; z# A$ n0 N0 D! n- DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC17 D m$ g5 m. o( n2 n% t
1 H( m5 X" X. i7 i. a- DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值) D- ^; y4 c. z* m" I+ _
- }) |( Q6 d9 b5 u1 U+ j7 G) v5 } j
/ Y I3 ]: a( d8 ^- //设置通道1输出电压! D c s: f, n7 N" p# n
- //vol:0~3300,代表0~3.3V" a. }1 C9 D% p( K5 k, }$ |
- void Dac1_Set_Vol(u16 vol)8 J- e5 @1 I7 {3 P' d
- {
4 j& C% H1 \) ~7 O* z - float temp=vol;0 r1 V! L9 \% O0 E8 H: s' P
- temp/=1000;9 }# a9 S* a% `0 r
- temp=temp*4096/3.3;
7 A/ u! m% _* r5 @) o - DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式设置DAC值6 @ `( D" e* U6 e3 l% o
- }
; H& a" o5 w; |+ i: I- I0 @
复制代码 - M" S+ m0 J5 ` n" r: B0 }8 k
adc.c函数
7 K9 Y% u5 n. @& |- #include "adc.h"
; w; G O, k2 c4 ]' Q/ P. k( b - #include "delay.h") W3 j3 K# D7 d, a2 D9 |& W
- " t# X2 S2 i) j0 n/ ?
- //初始化ADC
0 c: D% n7 x4 R& f - //这里我们仅以规则通道为例, B& h) V2 X3 F, H: d/ L% h
- //我们默认将开启通道0~3 % k- [; n0 o0 z# ^- S0 m
- void Adc_Init(void)
8 N2 o6 o3 _8 J# @ - { ; _$ J' Z( ~# W
- ADC_InitTypeDef ADC_InitStructure;
" u4 |, C& ]! V+ _ - GPIO_InitTypeDef GPIO_InitStructure;% G. R3 M0 `3 |+ T
: F& C# w( c8 }- Q5 v1 K7 D( x- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟
1 v0 a. v8 `# f& H
1 D/ S! ~4 @+ v& Z& N- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M6 e7 s5 j, h8 H# p4 n7 }/ D0 S
- , J, w/ L M' ^! s& a- y% [1 n/ q
- //PA1 作为模拟通道输入引脚 1 t+ g# H( V% D6 p. R/ [
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
) `+ r/ J3 K3 X, a6 B! s( e - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
" g. q) \) x) ~% C' c/ ~ - GPIO_Init(GPIOA, &GPIO_InitStructure);
7 t) t2 j$ W3 P/ F - 4 s, }3 J: X8 b8 P. x, X
- ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值" c# x/ p ^9 J
7 ?; I; d% }- T9 _- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式9 x0 Q! Z# T3 F3 O8 t/ o
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式$ X* ~7 d6 Y6 D1 y
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
9 F/ Q' M+ _6 b; i8 Y - ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
. }, h: I3 F8 T - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
5 g! z o' S% E9 ^8 _ - ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目5 d; Q7 @% g8 d: ~0 V9 m3 y& O
- ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
9 I6 n" r' h2 \/ Q' S7 ~
+ v f* r* \+ T- ADC_Cmd(ADC1, ENABLE); //使能指定的ADC13 b3 w! u- E) F4 @( |7 x9 V' K
-
1 i0 z: g5 |* w - ADC_ResetCalibration(ADC1); //使能复位校准
4 R9 j9 O' B2 _ -
# x% B5 q9 P' h+ t# O - while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束$ U# H0 f( Q. |* k% Q
- . K. O2 N- ?; y' Y# T+ F/ p- o
- ADC_StartCalibration(ADC1); //开启AD校准
! `( W& Y% u' h' y" X# u! r S" ]. F - : j1 H7 t' f9 N1 s9 U0 [* o
- while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
+ u, P" M' A+ r7 v - }
* P: M: N# k) A6 R - - Y) {) f( a- _' h& e9 Z
# t- S: H+ S* z! C9 b0 Q3 ?- //获得ADC值
# g {! c* I! J ^7 O - //ch:通道值 0~39 c. T H5 j0 n Q
- u16 Get_Adc(u8 ch)
# o7 q8 H+ _, J0 e+ f - {! t2 I" Q6 c4 F" k8 R
- //设置指定ADC的规则组通道,一个序列,采样时间
0 q; L% ~+ H7 S1 t# i - ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期 $ B3 A3 j0 c! U* E, c+ p! W
- 5 u9 Y/ H R' O" s5 C% B3 p
- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
4 @) _+ R( w6 ] e) n -
2 b9 U3 w" q2 Z1 L7 _( Q - while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束/ K7 s& h$ H5 Z5 e* ~7 i$ j
3 v* z% p7 B; g; P6 s- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果7 B& V) K/ z" A2 t
- }% a" g7 q7 }9 P/ T
0 U) f) a8 W( n$ Z/ ]% L- u16 Get_Adc_Average(u8 ch,u8 times)
3 U( I# J0 c. _ K7 n' [& z - {
( W5 Q- j( @& ] \* ? - u32 temp_val=0;! Q6 |6 K2 Q0 o: N# w6 y, ~
- u8 t;4 [' f9 D' V& o% a4 c
- for(t=0;t<times;t++)7 ? T% R v8 r7 C
- {1 [1 R W0 t% }$ v
- temp_val+=Get_Adc(ch);
0 n; x4 m# r$ t1 |" o' n6 {* H - delay_ms(5);
\* R1 H: `: r - }/ r$ l: c2 i+ s! P* Q
- return temp_val/times;5 G: S" G3 Z9 I* \; L7 U. W) v7 E8 V
- }
+ c# ~5 Z! }( L4 z2 O
复制代码 }: R, `0 A5 o6 D6 i+ }+ n/ S+ f4 q
从 main 函数代码可以看出,按键设置输出电压的时候,每次都是以 0.161V 递增或递减的,而通过 USMART 调用 Dac1_Set_Vol 函数,则可以实现任意电平输出控制(当然得在 DAC 可控范围内)。
, X3 r$ P) n r4 P; d7 Z" ]+ F$ f( Z; R" d5 p; x7 l2 d- p# f
l0 @3 \0 x: ~& z6 H4 Q3 b/ I- \8 Y4 P |