本章介绍STM32的DAC功能。本章利用按键(或者USMART)控制STM32内部DAC模块的通道1来输出模拟电压,通过ADC1的通道1采集DAC的输出电压,在LCD模块上面显示ADC获取到的电压值以及DAC的设定输出电压值等信息。
D3 K5 x/ d: @0 x. X
7 X' r$ G6 L5 d3 t. v& m" n) p ZDAC本身是输出,但是为什么端口要设置为模拟输入模式呢?7 R) b' O, {* M& t3 Y/ N
因为一旦使能DACx通道之后,相应的GPIO引脚(PA4或者PA5)会自动与DAC的模拟输出相连,设置为输入,是为了避免额外的干扰。
6 z# c/ Z8 K9 q1 K) ^0 O0 Q5 k* G: K2 | N+ t/ Q
1 B' B" s7 c4 x, j3 w" {8 I
; U5 ~& I$ @' b7 q) c5 {; z: F, u5 h7 J& E0 O c
* K) V- ~8 I! w0 u. y
1 n+ W. @! `5 M- [. e
9 u# I% g% Z1 Y8 k' [0 e' m/ C) q0 V
# L M8 e' \. W; S5 L' G) U8 K% b+ h7 g& H1 ~
, _$ i N, m0 q1 J" V( Q
; v/ W9 S; s9 J* ~9 P6 R1 z3 ?" b
0 _! B/ A6 [8 G1 Y6 `5 X0 c! k/ U H硬件设计- U3 I0 v& W; K, s: B
* q7 j7 d& ` ^- O" Y/ J7 A. ]5 z/ W5 l
q& g+ l0 h8 h* Z8 h
main函数( Q( R4 q8 \' E9 a- ~1 ~+ t& D, S
- #include "led.h"
- W6 m4 \# t8 E* h9 [' }- P6 I - #include "delay.h"
5 {, }; B4 ^9 {/ k - #include "key.h"5 S( B3 @" E6 |/ K( Q
- #include "sys.h"
1 \ ~6 T3 C, `- J' t ~8 \, J - #include "lcd.h"
9 `, z1 }1 ]. p* u* u - #include "usart.h" + U Z P3 z6 O2 O
- #include "dac.h"' Q0 J- D" R2 r+ G5 f' h
- #include "adc.h"
5 ~% Y O7 q6 i7 q - #include "usmart.h"! o# R# N: x6 q) m
- 3 {8 J+ A K/ H( W2 \
- int main(void)# ^/ n6 H! o5 {
- {
/ m% |# S! x6 Q9 x1 t2 ]- h, M - u16 adcx;2 Q( s' T: k! I+ A
- float temp;
b( S- M8 t6 s, x( N! X - u8 t=0;
+ e$ H4 F3 U3 P - u16 dacval=0;
S0 E2 o' x* H3 \/ Y6 t - u8 key;
; }" J3 R. \/ l% z: I* K. u -
* U2 I& Q( S# Z |5 [; b5 f! m; b: [ - delay_init(); //延时函数初始化
3 l# y) {6 ~6 q( y - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级% u/ P, i- z: v
- uart_init(115200); //串口初始化为115200
. @& p- F; W: B) d: H; T0 a - KEY_Init(); //初始化按键程序
; l9 H" |4 k2 A1 Z: i5 M - LED_Init(); //LED端口初始化
* T) s9 Y8 N5 E" `, c0 @% i - LCD_Init(); //LCD初始化5 x4 w: v* @* q$ o+ x8 I
- usmart_dev.init(72); //初始化USMART * _ |4 k& R7 v6 c% J% A
- Adc_Init(); //ADC初始化
1 j/ e' P# t. K7 \ - Dac1_Init(); //DAC初始化
7 r$ h/ g! [) `7 u! A( @ - 7 y; j9 l* U* Z6 K# _$ s4 k, M$ m
- POINT_COLOR=RED;//设置字体为红色
$ G& W9 X- `/ w. Q- A - LCD_ShowString(60,50,200,16,16,"WarShip STM32");
9 j0 D$ v; ^$ o# \ - LCD_ShowString(60,70,200,16,16,"DAC TEST");
. h' g- m+ m; L+ J% R1 r; k" P6 w - LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
! v$ N) ^' ?$ B/ m, w2 @1 a - LCD_ShowString(60,110,200,16,16,"2015/1/15");
2 i6 O- w, c) ]7 I- K - LCD_ShowString(60,130,200,16,16,"WK_UP:+ KEY1:-"); q2 q3 x7 _, j) v- T+ B
- //显示提示信息
2 G, j' [! G* ? - POINT_COLOR=BLUE;//设置字体为蓝色
2 l( F# H( D) g" n - LCD_ShowString(60,150,200,16,16,"DAC VAL:");
6 |# D! K, k9 A8 [; c2 m' m - LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V");
+ r6 p: @( S6 h - LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");0 G3 i) U" R; `% W4 h6 w5 P
- ! |5 r( V/ X6 J
- DAC_SetChannel1Data(DAC_Align_12b_R, 0);//初始值为0 ! u' f( G! F% P( b* n
-
1 v" b+ x6 y9 r! a6 x, K" F - while(1)
9 R, s. [, _, {: y9 z( L7 s- C - {5 \1 A2 V% o; f* J: y2 w2 X: Z
- t++;
4 D4 i$ a/ W5 P1 X& s" ]6 w - key=KEY_Scan(0);
' h& x. b N$ [$ b2 T2 R - if(key==WKUP_PRES)( e. v" f! O0 Q+ P2 f# H
- { ) `! F6 N1 B% Q+ w: b4 V7 g- P$ H
- if(dacval<4000)dacval+=200;
, U5 y* s" v4 M- _" @$ B - DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值 $ _" I [/ O5 @5 p. w( F) C4 D, R
- }else if(key==KEY1_PRES) 5 d0 [0 w# e- @* J# i0 j8 d
- {" k6 W' Q: n) o, K* F) Z% D
- if(dacval>200)dacval-=200; T, w, ]0 B( p4 w
- else dacval=0;
# o0 P+ ]. B/ s5 q8 L: D' H2 g& ? - DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值% f$ t; x6 J$ U% l' G
- } t3 w) U Y' }, e" O
-
+ c; L. O, s" S( {/ ~ - if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1按下了,或者定时时间到了) B: J6 Q) ?. L# U4 F
- {
7 |9 w* _* s6 j1 G7 V4 M1 a1 N# L - //获取DAC数据9 {. J7 j2 A3 M" ]' L+ j
- adcx=DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置DAC的值
9 W! q8 H" p3 ~3 ~ K9 C - LCD_ShowxNum(124,150,adcx,4,16,0); //显示DAC寄存器值
) D* Q$ s+ d8 @* o8 ~ - temp=(float)adcx*(3.3/4096); //得到DAC电压值0 G8 S$ ^8 i E
- adcx=temp;
& ^0 o3 f' C( f8 I+ N) Q& s1 o - LCD_ShowxNum(124,170,temp,1,16,0); //显示电压值整数部分8 s9 f) ^' R4 O6 C6 w4 q) o
- temp-=adcx;
4 A$ n- I. m2 H- F- G p7 _6 Y. A - temp*=1000;
( p3 Y. F3 ^+ L3 ?) g - LCD_ShowxNum(140,170,temp,3,16,0X80); //显示电压值的小数部分* @8 v" R3 N1 F, H5 ?; }
- ' Z( d) [+ @2 k: g- y2 H& ]
- //获取ADC数据3 F1 @! z! V* `0 f
- adcx=Get_Adc_Average(ADC_Channel_1,10); //得到ADC转换值 * _; e/ F/ |% u. Y2 h+ Q2 j( s3 @
- temp=(float)adcx*(3.3/4096); //得到ADC电压值
& P( H6 g6 R% K: y+ b+ `9 R9 A. h - adcx=temp;
6 E: n* D. ]3 c% Y# Y2 c - LCD_ShowxNum(124,190,temp,1,16,0); //显示电压值整数部分
; K" M% S g( ^ - temp-=adcx;0 m7 e1 M5 _3 ^2 n
- temp*=1000;
6 z2 M4 C O/ d( g - LCD_ShowxNum(140,190,temp,3,16,0X80); //显示电压值的小数部分
1 i5 x: t1 c3 f7 q0 @ - LED0=!LED0;
. h% t" h, c1 K4 P n# `0 m/ U - t=0;
" }' t0 X! e4 \5 q* @ - } $ \, S8 h! k3 i9 Z
- delay_ms(10);
3 [% \3 w4 w. W' l6 v - }
. f. n7 K5 y& |7 q4 z; `, c. N - }
复制代码 : L6 c( B7 L4 ~ ~' T, o3 b; G
dac.c函数
- Q$ T, n, y2 W8 d1 |% ~) @* m' |, u- #include "dac.h"4 b8 X6 v) ~% o, R: W* f
- 8 J* z6 N5 g4 E% |& U3 Q7 T0 j$ o0 F
- //DAC通道1输出初始化
9 e+ o! g8 i6 U. w0 H: {. L - void Dac1_Init(void)
& A8 i+ S' l6 N# x - { ( R& l/ R' V1 M1 j! S# ]9 _
- GPIO_InitTypeDef GPIO_InitStructure;
' p2 v; f7 }* i3 n L9 ? - DAC_InitTypeDef DAC_InitType;
* V% _+ W, U$ U+ u
( p- M2 E+ }# F- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟; w8 C# R. z9 n) m
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟 ! |! E; E( }8 w; P4 m( U
- 4 O' t/ t* r* h- I; x, `
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //端口配置
/ H/ `7 o% I' | v" u3 V( C - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
~2 d+ A6 P2 h& b* j+ H: t6 P - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;4 G9 V, L; R, Y; U
- GPIO_Init(GPIOA, &GPIO_InitStructure);
" k& q5 O; K2 _# E J. z6 P( M5 }1 R! O - GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4 输出高, R/ W! B @ g" Y& K5 x
-
% F" K: ^' g$ [ - DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=09 w& U& I$ V) O4 `5 f+ v
- DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生. B# P% c. J g. Z
- DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
, e9 L/ g2 d+ u; w' t - DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1
4 _! f$ U" x6 a- a% [ - DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道11 @8 O+ h4 W9 h: p5 s# X
- 7 X+ S9 D6 z% X, a% p% ~' Z
- DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1
' x) b+ {3 w4 G+ s1 M
9 \& B4 y/ r) M' {( c. \$ g6 |- DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值
3 U7 f: u7 |2 k2 a3 [0 \& U - }" X' C5 {+ G m- N3 ?: E
( v; _& \$ X! ?$ r; H5 s1 p- //设置通道1输出电压% O4 k( d9 z- v% y7 e
- //vol:0~3300,代表0~3.3V3 }& t1 Y6 }8 \) `
- void Dac1_Set_Vol(u16 vol)9 A7 J$ Q( {* n% ^% B+ F
- {9 P; q$ U# y* `/ S, E) T( h2 h# ^) v
- float temp=vol;
% F: X4 ^ J4 r8 v! I6 B2 l$ b - temp/=1000;( M- V1 a3 w. f' e$ f/ U7 ~
- temp=temp*4096/3.3;
4 U7 A3 n; H! M) j% W, O - DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式设置DAC值, l& S- l& G: x, T* g8 y
- }
( z! z& n( R. Z; i- \4 n2 }! N+ I
复制代码
3 y) M4 H* ~; }! g- y& P* m% dadc.c函数# |1 f9 @6 z" V" a* X3 C$ P
- #include "adc.h"1 g: D2 M0 i2 \6 o0 B
- #include "delay.h"7 e6 m, n; R$ [0 w& q
- ) J9 S, N, ^8 q. Q5 C
- //初始化ADC9 N3 {0 s( K0 G# ~, H- o
- //这里我们仅以规则通道为例1 Z2 ? X' e! m2 ?8 ~
- //我们默认将开启通道0~3 * K' @0 P- d+ U% ?4 M
- void Adc_Init(void)% O( t! ~$ A* {: m8 n
- { ! S0 @$ m% M/ ]; d9 F# s
- ADC_InitTypeDef ADC_InitStructure;
0 b! O8 q8 r5 _' `$ W/ c. y - GPIO_InitTypeDef GPIO_InitStructure;
+ Y* L* _1 s- U" T8 J/ S n0 m
`4 [0 b( T# a) i& C! J- Y+ \- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟
. u4 f. C3 ^4 }6 B. F. d - # d8 ~" I6 W0 V9 O
- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
2 F& m, I3 J" E
0 N0 ~9 E9 R6 C) |% |- //PA1 作为模拟通道输入引脚 - y- W& U* s9 x* r7 V
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;9 J" W( w/ x% b) r( _, n6 {
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
8 ?* z( R3 I' n: ? C- l0 T) O - GPIO_Init(GPIOA, &GPIO_InitStructure); 3 l/ P+ N8 H. w" X% I- S
- \& b1 L, l5 ?2 |8 r# O- ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值, q% d2 L: f4 A. a9 m1 G; y
0 p* ?2 J5 E5 s" o9 C4 M. Q- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式( y* H, a2 z! L/ p" N: e- w
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式, G7 v( H; ^. n1 {! i1 m
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式" Z4 z' S; E2 \+ F7 {3 o
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
) ^3 e, c" M# p0 V/ V - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐) _# w6 k( i: i: j% Y4 Q% ^7 q
- ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
) I: o" h% `8 O: a6 i - ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
% Z- k+ X' ?& l - d1 u) C! o: q O
- ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1% B4 u4 G i4 B) a O
-
+ d# y+ q5 U& O- c2 T - ADC_ResetCalibration(ADC1); //使能复位校准 + Z3 j8 D. E2 H7 C5 |
- 0 w t p' P8 \" U9 Q5 ~( f& n5 ?
- while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束: f( p- d/ ]7 Q. b; |( t
-
" M$ L' l; p2 o - ADC_StartCalibration(ADC1); //开启AD校准$ t" v3 w7 D& o+ P
- 8 r' r. E* V+ K. a5 N
- while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
" y* N$ R3 i# t! c7 J, h7 P' { - }
; s2 ]4 h! [3 P2 S; ? - ! |% m, O, q6 _* M. P0 C2 t- [
- % L. p. @! Z3 g! \: r/ @: }8 w; B
- //获得ADC值
# x8 w2 F2 T( f" R7 D$ Q. F2 u - //ch:通道值 0~3
) Y$ a; _8 ~% C1 l$ k& b - u16 Get_Adc(u8 ch)
7 E; n9 q: k" d4 C& g - {
& X& x' O$ C; c* e6 i - //设置指定ADC的规则组通道,一个序列,采样时间8 }, F& h- M; G
- ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
- u9 u ]1 b% j1 K& z, J; @ - # M0 `) @6 ~" k
- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
* `) v* J/ n' V6 a- G -
( b: _; v( Y3 [$ u - while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束8 |- l+ k0 x0 t T7 n" O* w) J
- ; ^) z0 @$ J7 u0 Z1 N* R
- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果3 s n0 S$ F9 ^
- }% S, S3 y0 I7 p2 U" _8 B% g
- w* D" G' |& y6 e. y
- u16 Get_Adc_Average(u8 ch,u8 times)
1 w; j* j6 j- k% y/ | - {
: d& j5 Q3 B7 K1 D8 o! V( W7 o - u32 temp_val=0;; e( q2 `- S- B2 t% H0 h
- u8 t;
5 D+ v; w d/ P9 D. M - for(t=0;t<times;t++)
/ m; D- c. R1 h+ P1 ^ - {
" `& g* i/ g( Q5 l1 J: J( b( Z - temp_val+=Get_Adc(ch);- U$ h* y! L" h+ r- t- b% X
- delay_ms(5);- L6 P; _/ w8 \) V) m
- }" X0 X9 r# r" D6 O
- return temp_val/times;
6 d% m3 [2 E9 @- f3 ?! v - }
4 b2 [+ P" \2 F9 e1 |8 z
复制代码 : H7 K4 M1 W, [1 C
从 main 函数代码可以看出,按键设置输出电压的时候,每次都是以 0.161V 递增或递减的,而通过 USMART 调用 Dac1_Set_Vol 函数,则可以实现任意电平输出控制(当然得在 DAC 可控范围内)。
, u! r% @2 U- c u0 n9 @' e8 }% v2 ?* v' N2 b
1 O ^3 t h4 _. q3 P- G |