本章介绍STM32的DAC功能。本章利用按键(或者USMART)控制STM32内部DAC模块的通道1来输出模拟电压,通过ADC1的通道1采集DAC的输出电压,在LCD模块上面显示ADC获取到的电压值以及DAC的设定输出电压值等信息。
6 K# e1 W; J( s8 w, m/ h, u! A7 V0 h
DAC本身是输出,但是为什么端口要设置为模拟输入模式呢?
3 M$ s+ F0 Z; B( K因为一旦使能DACx通道之后,相应的GPIO引脚(PA4或者PA5)会自动与DAC的模拟输出相连,设置为输入,是为了避免额外的干扰。
3 Q5 f% g9 U) U6 M) p
; F8 f! P+ \7 r
1 k+ ]$ e C# I, `5 h. Z& S2 g6 N/ C
" ^ ~0 ~& W# v6 K( n
; O v" N* W. _' h1 x
* Q) V7 E+ t8 k C
# R; I. m6 W, B4 F( x- @5 m# f) k2 G6 i# j8 ~! ^4 S
1 j& ^( p; A8 p- O+ v$ Y7 |0 _% F$ ^9 \$ Q) a' U
5 v6 M+ {( t) Z% I! {0 t j0 `2 H; V. N' c5 }
* n( H3 B8 q# v$ N3 I1 G- u% C! r硬件设计
) T# N9 j0 l1 z# A) e) {0 S, R6 K- x% o/ N
- A6 y, i$ y/ p7 q O
, ~% ~6 R( C) y" `( d. p& m: m
main函数& | N i1 g8 b2 i: J7 N/ C
- #include "led.h"
: e- A# A/ K. }, E - #include "delay.h"
2 @4 d6 N& Z# v* ? }, d/ w k - #include "key.h"
& {5 G3 @2 b3 s! } - #include "sys.h"& s/ A! [; @7 b( o
- #include "lcd.h"
" Z8 s# I: Q" R7 [ - #include "usart.h" ) r8 i: C1 R4 ]$ [
- #include "dac.h"
. ?8 P! T0 s3 _: }, J k - #include "adc.h"! I i* c3 @' }* X# U. x
- #include "usmart.h"
. X' Q) z0 `7 q7 I8 J1 ~8 e8 r7 l - - T# K" U: N$ G/ n; l4 ~# m( \
- int main(void)
; h4 F4 w$ V$ y( z1 C( p - { ! `0 {6 \1 H0 S' P8 ?
- u16 adcx;6 u* ?, w8 f8 T5 O5 y
- float temp; G" c; x& X; i! V8 G4 G( J) R
- u8 t=0;
& R& I; g, u& J' @ H7 b' V - u16 dacval=0;+ ]% O; u0 U2 F U- v
- u8 key;
+ a! t4 S/ T9 j8 w! A5 T' N3 L - 7 I7 H- c7 x% m% w2 s. Y
- delay_init(); //延时函数初始化
/ q7 g$ O, }* Z. G! C( B" C ] - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
5 [2 V$ Z+ S: j! G( _* }- S# s - uart_init(115200); //串口初始化为115200/ B5 j6 K9 B# [+ c% ^/ @* F7 D
- KEY_Init(); //初始化按键程序" `; ~+ u# b( P& O$ F
- LED_Init(); //LED端口初始化 a" j+ \. T" k5 x2 v
- LCD_Init(); //LCD初始化6 {7 L$ i6 j0 _: [ L
- usmart_dev.init(72); //初始化USMART
; _) {# M! H# ?7 h7 T - Adc_Init(); //ADC初始化
4 L6 n; {' R/ T - Dac1_Init(); //DAC初始化! ^/ V3 @, P2 _+ x
- 7 f5 B) s8 R% t- ]. ~& Q: B# Y
- POINT_COLOR=RED;//设置字体为红色 c; D, f8 w6 A
- LCD_ShowString(60,50,200,16,16,"WarShip STM32");
f. P$ z) P0 W3 Z - LCD_ShowString(60,70,200,16,16,"DAC TEST"); ( R4 R" z: o" t& X1 R
- LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
# U, a& L( w' F$ Q% f8 V4 m V7 j - LCD_ShowString(60,110,200,16,16,"2015/1/15");
C! h' p4 D- w- j' A - LCD_ShowString(60,130,200,16,16,"WK_UP:+ KEY1:-"); % x: U+ g# X! \" [# m2 B; s0 j2 |
- //显示提示信息
3 }/ k" p% h! v: ?* `# H- P - POINT_COLOR=BLUE;//设置字体为蓝色; ^9 ?/ P& D# o6 l$ L
- LCD_ShowString(60,150,200,16,16,"DAC VAL:");
9 e: s8 g4 _5 E0 L - LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V"); ; R# Y. G* F5 y4 u/ w( ~ a* Z! J/ z
- LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");
# @3 T+ r) }( t _7 d -
% {( M9 `4 }% x5 o0 ]& Q% w+ { - DAC_SetChannel1Data(DAC_Align_12b_R, 0);//初始值为0
% R) [0 a2 x, Z; i - $ `9 \6 u& ~! B U4 `
- while(1)2 [! U7 Y1 z' u+ k
- {7 s. D. ?6 v3 u! o6 t7 h- o
- t++;! R5 k, L) u1 l6 _5 H
- key=KEY_Scan(0); ! W% g/ K; y, K2 z' L' d) `! D6 }
- if(key==WKUP_PRES)+ V! _# x4 |. j! K% T6 w3 n7 R0 O
- {
% w8 t+ s$ [- Y$ D- R, [! C8 _ - if(dacval<4000)dacval+=200;
- b/ E9 E; m1 |$ ]' x5 u - DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值
3 l2 b3 {) c! u0 ^1 z0 n) a* R - }else if(key==KEY1_PRES) % n9 O) `# S3 F _ X! O" K
- {
1 ?. q* D, f- ^ - if(dacval>200)dacval-=200; ]( V* w# M; a8 [3 A2 P* a7 p
- else dacval=0;
$ [- l/ Z- n" y - DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值/ R* l& O0 l1 v" ]) d
- }
6 w7 d9 w p! n1 c. I2 M5 h/ Q# G2 ^ -
2 P& `, m: _( J* k6 } - if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1按下了,或者定时时间到了
9 o7 l9 A' b0 B) z - { ) A% A6 W. ?3 [& P$ q2 l
- //获取DAC数据7 L4 h% n; A& m, r. U& t0 _6 o
- adcx=DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置DAC的值% c/ M! D h& K7 l8 R6 {; q
- LCD_ShowxNum(124,150,adcx,4,16,0); //显示DAC寄存器值, q$ y' i1 ]; F! r
- temp=(float)adcx*(3.3/4096); //得到DAC电压值7 m3 A! p8 s. H9 @2 b
- adcx=temp;5 h6 Q: q. c# Z4 a
- LCD_ShowxNum(124,170,temp,1,16,0); //显示电压值整数部分$ o0 @ K$ ]: j+ m/ l4 A5 q
- temp-=adcx;
9 t$ O8 h9 J( G: d2 U6 T - temp*=1000;2 |2 {& b1 `& h' o
- LCD_ShowxNum(140,170,temp,3,16,0X80); //显示电压值的小数部分# ]# j; U @& _5 b# I4 [5 g
-
' |# S% t$ i/ w& s/ C" g0 \ - //获取ADC数据
) g1 u0 P2 i8 N - adcx=Get_Adc_Average(ADC_Channel_1,10); //得到ADC转换值 $ R; Q/ Z; Q) v: i+ g
- temp=(float)adcx*(3.3/4096); //得到ADC电压值3 [, x$ Y2 a; R3 z3 ]" f/ G
- adcx=temp;0 e: x/ \# A: k9 H# J
- LCD_ShowxNum(124,190,temp,1,16,0); //显示电压值整数部分
. E' q" w& J6 Q. b6 U8 H/ a8 R; Y - temp-=adcx;3 m5 x* H! E: {8 y" a3 L
- temp*=1000;
8 V! q2 O2 Y' Y% M1 e - LCD_ShowxNum(140,190,temp,3,16,0X80); //显示电压值的小数部分
0 h' _2 s2 z0 W: B; W2 p - LED0=!LED0;
+ b" c u6 G! P$ t - t=0;
5 Z J" [# f, ~' q - }
4 _8 O) b1 p8 g6 S - delay_ms(10); 0 b& |0 o/ Z( @6 a3 i- T6 c5 e# f
- }( S* |% a2 E; L3 M
- }
复制代码
. `' P/ g" o- c- adac.c函数6 v7 ^9 H* Y" d/ @
- #include "dac.h"
2 W# o1 @3 V% b9 R$ H& j - 9 }2 d4 X" N/ W7 B5 h7 G! J9 T& t e ?
- //DAC通道1输出初始化
% a7 |, ?7 i1 m) E/ m B - void Dac1_Init(void)
$ Q( ~1 q* } B - {
6 f6 ]4 V7 k5 m( }; v1 } f4 B5 e - GPIO_InitTypeDef GPIO_InitStructure;
2 z Q3 f) v2 j - DAC_InitTypeDef DAC_InitType;
- h* V9 I4 F* T1 L, ]/ I9 } - ' E" a& s K) B' o8 w0 A
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟
5 K/ O9 e _8 B$ K( T: d - RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟 8 F- L$ E0 u- O1 \' j
$ n5 _. {, E+ U- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //端口配置1 X# b3 M+ x8 q) [% c
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入" Y2 a$ S, Z6 y; Q
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;# H7 F1 a6 l3 K7 P
- GPIO_Init(GPIOA, &GPIO_InitStructure);
# w6 n1 k2 Z( K$ ^4 Z - GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4 输出高) Q! g4 R6 s0 ^ G/ X/ \+ ~
- : J! w; A. j1 S- |* h
- DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0) g/ ^7 w' U3 \: q6 N- c
- DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生 ^3 `7 [9 B) U% y/ D5 o
- DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
* o0 M5 y& I7 _& r/ ]% m/ _ - DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=13 @! t* H% }0 V* `1 l; K
- DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1
; L5 Y( w% w; u n' m+ M0 c
# a* T- O1 e2 C [- DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1- B7 f( R) C6 @1 s
: b! W" k! Q( E( D4 J* e8 {- DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值 H7 a5 b) x% ?
- }
/ `& Z8 q# o6 H5 z# G2 y' @
& g/ R ^* u$ q5 M \& L- //设置通道1输出电压; i. H7 l p/ _2 U) a: m! k* ]# ]
- //vol:0~3300,代表0~3.3V2 f ?- u0 N( ]! G
- void Dac1_Set_Vol(u16 vol)3 T4 k% m' K* I" k# V6 ?" x; C
- {
/ D+ r" G* d F* q9 z7 ^) i - float temp=vol;
* W/ K5 {/ S0 a3 a6 `1 T - temp/=1000;
6 R; ^/ `: [, g. R! h - temp=temp*4096/3.3;. @/ V) E( \9 X1 e2 ?- F' @, J
- DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式设置DAC值; `2 i" P) B3 Q0 d. v
- }
- A. Q& [( Z/ a7 y
复制代码
: V+ r5 ^' S L2 W. @8 Eadc.c函数
6 }6 s. o2 f) R3 Z/ w- #include "adc.h"
" b$ J; u0 G0 a$ _8 j - #include "delay.h"
T# s9 s/ F& `* Z - 9 {. |* A9 j3 U, T" e4 |; ~1 E
- //初始化ADC$ t6 \5 q' D6 T. Z
- //这里我们仅以规则通道为例
F! f- F0 J7 M, U - //我们默认将开启通道0~3
; H' Q! d! w6 l( J$ V- S: g) }: B - void Adc_Init(void)
3 K" i( A+ D3 r0 p% q# t" \ - { 3 A, j9 C/ z( C" l
- ADC_InitTypeDef ADC_InitStructure; : C% }: G) B% T# x, ]; b; M. r! m4 _7 z
- GPIO_InitTypeDef GPIO_InitStructure;
- G$ I7 S; z6 l7 E$ |& v
8 [- N7 k" J! X- p- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟% a$ J* W' S2 E9 T" ^. Y$ Z( W4 r9 {
5 r& K$ T6 `& O. e. A1 Y- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
1 G! i8 }- Z+ R8 Z$ t' X g4 H
) c3 L' g+ i" t6 r G: r- //PA1 作为模拟通道输入引脚 5 \5 u$ y5 U" v5 I% Y/ k
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
( R* }8 r' v: j3 w0 O4 q x4 N - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
$ q v( F8 @: c9 P* T- { - GPIO_Init(GPIOA, &GPIO_InitStructure); . B( V! t% O! u9 c( R5 K7 d& u
6 h; ]" ^; r0 b- ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值" v3 V$ Q% |+ M# D
. l' l3 ?% v+ h4 `- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
1 V) s3 ~1 f: b5 d - ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
- F! r% S" V5 [- K - ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式6 b7 {' E' P& J: `- f# ]% A$ {
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动: P0 v) T6 s1 B4 R
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐7 k, W, w' ?7 D5 v
- ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目) w; }8 _4 U9 h( A, i" L0 |* \
- ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 / x* ?" g& C8 W* `. \& N
. ~! s! x: n. {" n. v9 c9 R- ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1$ z1 l" L, C7 h3 v
- # e. r7 W. {, B# T/ U- F
- ADC_ResetCalibration(ADC1); //使能复位校准 ' E6 b. h; b1 y2 w5 F
-
4 @8 Q3 Z* G& c3 m - while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
' s0 K' c- h$ Y% U- V2 I9 t -
# b( x# w6 A* {0 B5 h - ADC_StartCalibration(ADC1); //开启AD校准7 w( Q5 r; d+ g( |) |9 h
. j7 Y5 H' |6 t `- while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束) `/ u7 f- \: @& T
- }
: B+ b* k, F9 d. n# C$ Q
4 o( n+ U) u! ~- " f& n5 [# r$ T/ H+ i" j
- //获得ADC值$ V4 |, [9 U& \5 S7 e% l. h
- //ch:通道值 0~3
6 S7 l' @- ]) x - u16 Get_Adc(u8 ch)
9 S/ q* x# F9 q: r9 I" A* P7 d. y5 W - {
6 W7 N$ V, `( j0 ]# j( C9 p - //设置指定ADC的规则组通道,一个序列,采样时间
8 U" c6 Y$ J& ]6 v: y" B' | - ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期 4 X# A6 s! Y% C0 s* b8 {* S
" c7 s0 |# ? V9 H: x* Q! J% @- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能 / s9 y% j3 R3 ^" {( k
- 3 d. I2 B+ c7 G! G( `
- while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
# z- W/ z# F* Z - 2 ^' y# W; [2 U m! y4 S
- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果" f/ v7 `! w* w8 r7 p( G( @
- }
5 f0 j- @& G* I/ v1 i - ) k7 x4 Z8 e% n$ L6 N
- u16 Get_Adc_Average(u8 ch,u8 times)
8 k B( M" g6 T4 ^: b& N - {, o, F k6 N/ Z7 Y0 w I2 f% F( Y
- u32 temp_val=0;) v+ s% J8 s" W- G3 B2 ^
- u8 t;0 D7 x8 ~6 m$ B$ x" ?+ K
- for(t=0;t<times;t++)) P8 k1 {7 f, ?8 T
- {7 }9 _6 J% }. }# f# }# Y/ D
- temp_val+=Get_Adc(ch);. D$ n. H' [( k8 E) j, L' S
- delay_ms(5);
1 m" H h" R" O4 r+ ]; @ - }9 T$ S k- T. i. J+ X/ q( S5 p+ t
- return temp_val/times;
' @ x2 d! J$ N8 \6 r9 M9 d - }
4 \ F4 o/ K" L: ~5 J
复制代码 ) [% P0 n; s/ A5 A7 M& p3 @
从 main 函数代码可以看出,按键设置输出电压的时候,每次都是以 0.161V 递增或递减的,而通过 USMART 调用 Dac1_Set_Vol 函数,则可以实现任意电平输出控制(当然得在 DAC 可控范围内)。, Q+ J9 J. C! k( L0 u5 ]0 k+ P, s
% I) m4 B0 l0 {' }8 d S" g' ?. [
j3 B4 h8 b/ u* s v |