本章介绍STM32的DAC功能。本章利用按键(或者USMART)控制STM32内部DAC模块的通道1来输出模拟电压,通过ADC1的通道1采集DAC的输出电压,在LCD模块上面显示ADC获取到的电压值以及DAC的设定输出电压值等信息。# `! R6 e- y# S4 I8 w4 x
/ W1 F) w, V4 p5 U
DAC本身是输出,但是为什么端口要设置为模拟输入模式呢?
1 M9 O. g1 S; e8 F因为一旦使能DACx通道之后,相应的GPIO引脚(PA4或者PA5)会自动与DAC的模拟输出相连,设置为输入,是为了避免额外的干扰。0 {2 d G1 r0 n
, l2 n, O: h- I- {! Q- Z
* {( J8 L2 L6 z5 L
+ t: j0 f, D# c# o0 r- X/ O
3 z: a/ Y9 Y& B+ o' P6 N
# { T6 }- j' Z% M. f) z6 { j0 r+ t' p5 Z4 G8 A
7 o5 F: _$ u2 [2 h
# l& O/ t" w9 T, K
Z8 p; G1 k2 O/ L' n0 C+ M
/ T0 u+ L* ~9 b d% ?' m L0 C2 a) O$ A& u& s4 f
3 I+ \. c9 ?0 [0 S: P4 M. K
2 x# ^ K) K+ L6 P; C G0 M1 a0 P
硬件设计: [5 M4 Z0 ~3 T/ {5 N' H
" @: m' O$ T4 e
& W6 A1 a) H' o) U4 O5 @
% Y$ h# f1 T# O, g* k4 Lmain函数, h% T0 }8 w( ]$ _2 E
- #include "led.h"
3 i, }: N1 f G9 `/ @ - #include "delay.h"7 U2 m3 Z7 _% S
- #include "key.h"/ D/ o/ E! ?% w/ b+ g/ B
- #include "sys.h"
! k' A' K8 D6 s4 q - #include "lcd.h"9 L9 f- {' c" X- X: ?
- #include "usart.h"
* e2 k- t" T' s- s0 ]2 \# I - #include "dac.h"! B" f( C5 Y r- L5 M
- #include "adc.h"# ]7 J7 s) ~1 E! M
- #include "usmart.h"( {# B+ M3 ]% t( ~( @7 j
* |1 _0 w" z) Z1 u* f4 S3 B& J- int main(void)& s1 I) b, _* a; [2 z# x7 Z2 T
- { ; N+ {: H1 x7 b% B9 H( A" V; }2 E
- u16 adcx;
# i4 Y# | f9 P2 E - float temp;
7 M7 y8 o8 Q3 C v8 L - u8 t=0; 9 s- x# U# R* j$ H/ w
- u16 dacval=0;
/ @/ L. g9 Y, f4 f3 q: K - u8 key;: G( {5 ~$ k0 O: b) L; u
-
5 {% e. Z9 \# |) T; ^% ~ G - delay_init(); //延时函数初始化
6 v4 {& P5 Z! N. E7 |( E- b - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
; q0 H. S5 P3 @* u( @8 f - uart_init(115200); //串口初始化为115200; d9 ]- Y L% C6 Y
- KEY_Init(); //初始化按键程序! |8 l! i9 O- ~% }$ i8 w
- LED_Init(); //LED端口初始化4 S, e) c0 |/ ~2 O4 x+ E" ?
- LCD_Init(); //LCD初始化
) y6 m; `7 a8 Z - usmart_dev.init(72); //初始化USMART 0 j0 ~* l3 z* `
- Adc_Init(); //ADC初始化
. o* }" f5 R$ n7 Q+ x$ j6 C6 n! H2 d - Dac1_Init(); //DAC初始化# C7 E9 R% M+ }5 p! q
- " _" T( s* l1 ~! c9 |0 T Y r
- POINT_COLOR=RED;//设置字体为红色
9 {' x4 k- r$ v1 h - LCD_ShowString(60,50,200,16,16,"WarShip STM32");
, E3 W2 D- F, y2 d - LCD_ShowString(60,70,200,16,16,"DAC TEST");
8 q% `& c1 L' ^* y - LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
' p8 ]! ?0 I; m( T5 X- t1 t/ b - LCD_ShowString(60,110,200,16,16,"2015/1/15"); 9 t2 b `: s6 I( v: X
- LCD_ShowString(60,130,200,16,16,"WK_UP:+ KEY1:-");
5 T5 }8 F8 A6 ^/ f - //显示提示信息
( H7 ^' v0 [! U3 K- P - POINT_COLOR=BLUE;//设置字体为蓝色
; M$ m0 t6 P* ?5 T% K. r - LCD_ShowString(60,150,200,16,16,"DAC VAL:");
5 p& _/ i; X5 R4 H0 C: r - LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V"); 9 l. ^* z& L# ~- p1 m9 N
- LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");& x5 w: B1 Y" ?0 N$ ^
-
( ^1 C0 E9 e, J) Z - DAC_SetChannel1Data(DAC_Align_12b_R, 0);//初始值为0
" Z3 d" t! t3 k1 R4 ?* ~" y; a5 R -
6 w( I( m/ R. ^8 i' A7 y& e+ _ - while(1)
2 o0 s( A& A! @- S- T - {. s1 V* Q' m/ ]1 I$ g0 `) M B2 b
- t++;9 @* Z$ u3 ^$ o& Y' {/ _
- key=KEY_Scan(0); 7 _# z; A! F: Z: n5 S% \7 J
- if(key==WKUP_PRES)
, D8 u2 E3 o+ \. |3 q: l( s - {
; W8 [" R) o5 L" z/ i/ c, @8 v - if(dacval<4000)dacval+=200;% N1 j2 c7 \/ E; D
- DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值
5 q% I* U6 a1 r; p2 Q$ M! ` L - }else if(key==KEY1_PRES)
7 ^ c8 F1 r7 X5 `8 Y - {
& P4 ~ P K" ]; b0 u! g0 {0 u! } - if(dacval>200)dacval-=200;
9 b# u. O4 x; a, o3 r/ F1 m T - else dacval=0;
. P. q4 \ }) c - DAC_SetChannel1Data(DAC_Align_12b_R, dacval);//设置DAC值
, h r4 G P5 m! F1 T0 j, O% ~ - }
& L# \6 W* \. j! R# H, C - + @: [1 X- F0 n! z) x# ]9 o
- if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1按下了,或者定时时间到了
& Z e5 |2 g+ ]8 d: S C: U1 E - {
1 T: X8 z' }, _' ^2 w- B0 Q9 v - //获取DAC数据6 U/ `6 T1 A& R& d2 U3 i- b) g J
- adcx=DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置DAC的值
" s' N( @1 u. W U& f$ B - LCD_ShowxNum(124,150,adcx,4,16,0); //显示DAC寄存器值2 f' O6 J# M% @. V. ]4 }' M0 H
- temp=(float)adcx*(3.3/4096); //得到DAC电压值
]- b! L1 L/ y% z$ l5 P1 G0 v' [ - adcx=temp;
5 V" z- [( L3 S- E - LCD_ShowxNum(124,170,temp,1,16,0); //显示电压值整数部分
3 r0 `* M+ n2 p - temp-=adcx;
' [/ V6 P2 _ E+ A" @5 z1 ?( O - temp*=1000;4 {; O6 c0 M; x4 y
- LCD_ShowxNum(140,170,temp,3,16,0X80); //显示电压值的小数部分
c& A; }5 x5 w2 A8 \7 F# S6 v - ( W, H) J: [0 t
- //获取ADC数据
2 Z' G I' B( Z1 I/ _9 J! G& a; p - adcx=Get_Adc_Average(ADC_Channel_1,10); //得到ADC转换值 & R$ U2 r& Q+ X, ?5 c( r( F0 c; k
- temp=(float)adcx*(3.3/4096); //得到ADC电压值5 P0 ^" a$ l S, x
- adcx=temp;9 k' W$ x& V- f1 `; C# _# @) N
- LCD_ShowxNum(124,190,temp,1,16,0); //显示电压值整数部分
4 o( h, ~( B9 d2 L$ ] - temp-=adcx;0 y4 l2 T& \1 |, R9 M7 Z
- temp*=1000;' k; ~$ W k6 C5 h. R# f
- LCD_ShowxNum(140,190,temp,3,16,0X80); //显示电压值的小数部分
1 K0 `9 J( G% B# L2 B3 b7 {1 ? - LED0=!LED0;
% L$ s) x0 w4 {0 Y: P - t=0;3 H4 g/ U x) ~- V0 i0 }
- }
9 ^, i8 J' F T. q8 U' x - delay_ms(10);
7 k( Q- O6 Z3 w# g! P, _5 g8 P - }
) o% H, S Q( M: E - }
复制代码
. V6 Q* w- v6 p/ d+ z5 M: g' [9 g7 udac.c函数. z+ Q7 R: y" v7 t: O! p
- #include "dac.h" o/ O0 x* v& s
: @( v0 @4 E( p7 r) W- I- //DAC通道1输出初始化
; o/ n5 }, d9 P, c/ L5 I- Z, ` - void Dac1_Init(void)' M4 K3 i/ ?! |" X
- {
& Z3 U7 T- j: P, K - GPIO_InitTypeDef GPIO_InitStructure;
: r' e+ @$ @1 Z# ^ W0 U - DAC_InitTypeDef DAC_InitType;5 H7 a: ~+ i: e" B/ A
' j% c# O, F" Y' \3 K- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟
( A4 n/ n0 v7 x - RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟
( [% H! I, J c* b1 | - 4 s- L; _1 x8 x @
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //端口配置- t# @2 S" Y. h' B( H3 q
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
: z- T% S. j0 R* U" i( L9 q9 X$ i - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;6 i7 ?' V! |- _- u
- GPIO_Init(GPIOA, &GPIO_InitStructure);0 m6 f, n W5 v; U( t
- GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4 输出高* X2 I6 [3 n3 g" }. E
-
! b% r. u" _: g; h: A9 v; g - DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0
! I6 J ?2 g' O' ^# ?8 R) z - DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生$ [" G7 k Y( y
- DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置+ y# t& X( y4 s8 H% f, J
- DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1
# C1 `" I" R8 K' X* w# R - DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1
1 q9 @/ ]' ] B. c; j8 U& ~0 [
* L; A z' s! p! K. i/ m( F, |- DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1
; ~# Q* g: |# F& B9 I7 i, `* S% q3 W - 6 y( h1 X8 r, G7 u6 S5 J
- DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值! W) W. s. t8 b, |
- }: w. l, }1 i' ^9 F2 \7 k* {
- A) ]. D i: Y, c! Z c
- //设置通道1输出电压
% V2 H- M0 K: _1 g% l - //vol:0~3300,代表0~3.3V
' B) d- E0 G0 {0 `3 T - void Dac1_Set_Vol(u16 vol)/ x, m. R+ b" R& r) N4 h
- {0 Z8 y# O: a; g- o
- float temp=vol;
: q, d. w* F; ]% V! o6 L% \ - temp/=1000;9 |" e5 @, ~7 L" _
- temp=temp*4096/3.3;/ Y6 `0 ^3 k: N( Z. r2 ^
- DAC_SetChannel1Data(DAC_Align_12b_R,temp);//12位右对齐数据格式设置DAC值
; e7 s" V8 Y# J2 V7 c3 T - }
5 |5 H1 q) A/ l2 D
复制代码
- N2 N* n+ B& W( S. Oadc.c函数
' Y+ H, c, ^9 A% p: k( t: [- #include "adc.h"
, C8 w- [" c8 h- B6 n R - #include "delay.h"
: t. s) N6 Q/ y+ C( y -
0 f9 B; C7 [( H6 A: t - //初始化ADC
! l9 E$ Q( s" _! ~ - //这里我们仅以规则通道为例
! K% B A% D/ O5 @( z7 R3 u0 F' ? - //我们默认将开启通道0~3 4 Z" ]9 O- `2 |. T1 H
- void Adc_Init(void): a3 q& d& h! x8 K. F7 ?$ {
- {
( l8 x$ z/ s _0 W. D) z - ADC_InitTypeDef ADC_InitStructure; % f U* _7 s; Y4 t! o' S
- GPIO_InitTypeDef GPIO_InitStructure;! T5 o, V# r _- J! b# I) M6 ~& B0 l
6 K8 l! m( o6 q8 `4 L( y- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟& m7 o; {& B4 b" ?
- " t( J, n: ?& G- u& X3 @( M7 ~2 l
- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
% h* P/ D4 y, |( ~ - # ]! \7 r5 }0 k& W/ r6 l) [
- //PA1 作为模拟通道输入引脚
% p, {- X# n( X& p5 D! U; m* {- e - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;1 C( \8 Y7 I% d6 t( w- m. Q
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
2 ]5 {& N' d" Y4 o0 s - GPIO_Init(GPIOA, &GPIO_InitStructure);
7 ^1 L9 _, `, o/ C+ ] - . ^2 p8 c& \% ?" w9 L
- ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
0 Q& x6 r7 A* M+ \0 \ |4 @/ V - 7 N. A/ _# p6 J3 h: P( R; E
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式) M$ Q1 u+ Y* `; F; {- r
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式# @! f8 l& u/ u2 ]5 h
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式: d6 Z- W( v5 D
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动1 b0 g& ?# \8 E! r5 Y* [% C5 V" D3 a
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐. p/ ]& c1 S! ^* b) n
- ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目+ M6 P7 f/ v) P. t
- ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
; c8 y% c$ e( F4 _6 x0 R
& s. z* K/ W7 E- U w- I* v- ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
6 _% g6 r" G7 K -
9 o/ U" r* m5 @6 x - ADC_ResetCalibration(ADC1); //使能复位校准
6 U, }' g) b/ z8 g8 y# c1 @ -
9 q4 v4 }9 l- f% t; M - while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
# G* \" C2 H4 x: B+ O' B) U7 @ - 5 D9 v' N. m* u: v
- ADC_StartCalibration(ADC1); //开启AD校准
2 O) a. }& a' V4 e. N$ ?' _- z U - 3 e4 n4 u" L8 Z* n" x1 ?; Z
- while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
, y5 x) i- `) |$ `3 t1 W - }
7 r9 t+ k7 h# g2 z) S& q
' l( D/ w" h% B- - a7 ^8 X* @) \4 l
- //获得ADC值
1 \8 G1 Y0 h/ p$ p& B7 i - //ch:通道值 0~39 T' D) }% C- `
- u16 Get_Adc(u8 ch)
: o3 g. A. c; W$ \ I* |9 {& X - {7 I. D- L6 b8 `. t0 k# r
- //设置指定ADC的规则组通道,一个序列,采样时间# ]) D! n7 g5 p# z* s0 K2 t' ^) \
- ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
% a$ ]3 f! O) t, }
) j( [& [7 {! v$ x& l- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
( D$ \3 P) h+ c, u - 4 b& z* r8 C2 I+ [: r
- while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束; g# K4 Z! s& G8 e1 s2 }2 N6 T2 H& a2 j' \
$ D" V& t% `/ y; i" b- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
2 |5 b$ K: d9 O( P4 W - }1 q4 j! k" D4 }. Z# w' t
- % q7 j' U# l$ S0 Q. I+ l
- u16 Get_Adc_Average(u8 ch,u8 times)# J4 E# @0 c9 ~
- {6 c. w0 a g/ g4 M$ W
- u32 temp_val=0;
8 t1 j& u4 ~6 W5 G% ~# R2 L - u8 t;
5 p, K7 L& S! s - for(t=0;t<times;t++)
5 j, ]9 J I+ q( x6 ?! F1 {' } - {9 @0 L" L( s7 B( w# l W- F6 q
- temp_val+=Get_Adc(ch);# S& n8 c2 n$ _) ^/ D8 m
- delay_ms(5);
/ Z2 K; v8 v4 A& d! D: B - }
t# a+ Y4 {' \- f- B, D - return temp_val/times;
2 L( h& K m& o- Q - }
1 {' T- f& f" W. m
复制代码
$ ^& |; u7 ], ?$ H& o/ x从 main 函数代码可以看出,按键设置输出电压的时候,每次都是以 0.161V 递增或递减的,而通过 USMART 调用 Dac1_Set_Vol 函数,则可以实现任意电平输出控制(当然得在 DAC 可控范围内)。
( o: N: `8 z7 Z) X. [3 X: v1 p
- l6 \) G/ ^% N$ T# B: X; V4 [- `/ b1 }! x4 u9 T
|