STM32-ADC模数转换内容概要: STM32-ADC模数转换概述 STM32-单通道采集实例 STM32-多通道采集实例
) x2 z1 Y1 |& g! B& G STM32-ADC模数转换概述# s7 Y* _5 H- R; Q$ \
内容概要: ADC简介 STM32F0-ADC时钟 STM32F0-ADC转化模式 STM32F0-ADC转化时间 STM32F0-ADC模拟看门狗
0 L5 r, P7 j, c: i! J; D8 R* l& C
# y2 I4 _! K$ H8 r4 a) n
ADC简介: ADC的作用:采集传感器的数据,测量输入电压,检查电池电量剩余,监测温湿度等。 0 A: K1 V- l% n! ^% u) x4 ?
ADC的性能指标: 量程:能测量的电压范围 分辨率:ADC的分辨率通常以输出二进制数的位数表示,位数越多,分辨率越高,一般来说分辨率越高,转化时间越长。 转化时间:模拟输入电压在允许的最大变化范围内,从转换开始到获得稳定的数字量输出所需要的时间称为转换时间 ' Q5 W! S, T, [* H
* U' k: ~8 j: J5 l2 Q8 H2 T
STM32F0-ADC特性: + k% [, A+ X$ [6 q
+ t' r' n3 g6 [' ^# |1 {0 r5 K12位精度下转换速度可高达1MHz
! X$ |# ~7 l0 O( i/ r4 z# E2 b
可配置的转换精度:6位,8位,10位,12位 转换电压范围:0 ~ 3.6V,V SSA ~ V DDA 供电范围:2.4V ~ 3.6V 19个转换通道: 16个外部通道、 3个内部通道 $ W7 c4 P; O* X. c" \
采样时间可配置 ) K T/ ~4 n" l; \
ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中 , X& K! u; C8 q& t( y% m
STM32F0-ADC时钟:
$ Q0 \3 S$ S1 Z8 S( l1 c
APB时钟的2或4分频,最高14MHz 优点:不会有时钟域之间的同步带来的抖动,触发事件和转换的起始时刻之间的延迟是确定 的,从 而保证转换之间的时间间隔是固定的 缺点: ADC的转换时间和系统时钟频率相关,受系统频率的影响较大 # U8 `9 F* n$ D' y. u: w9 Z& k" |7 X
$ O- }# u9 C, o0 l: N( r d
片上14MHZ HSI RC振荡器 优点:无论MCU的运行频率,都可以保证最高的ADC工作频率可以使用自动节电模式(自动开启或关闭14MHz的内部振荡器) 缺点:触发信号的同步会带来抖动,触发事件和转换的起始时刻之间的延迟不确定 + F' g' R) F! M5 D+ `' c
4 X5 J3 O+ h5 H( _6 h- l, _
1 q( ^% x9 b4 Q0 H# n9 z
STM32F0-ADC通道的选择:
0 i' K. D+ x& @6 w
' v4 z. I v% F) |. U19路复用通道: ●16 个从 GPIO 引脚引入的模拟输入 (ADC_IN0...ADC_IN15) ●3 个内部模拟输入 ( 温度传感、内部参考电压、 VBAT 通道 ) ADC 可以转换一个单一通道或自动扫描一个序列通道。被转换的通道序列必须在通道选择寄存器 ADC_CHSELR 中编程选择:每个模拟输入通道有专门的一位选择位 (CHSEL0...CHSEL18).
1 l% I7 q$ r' f
+ ]+ Z s) y! W( c' q8 A
STM32F0-ADC转化模式:
3 a% u/ j ^" n" E: B% _5 W& k
& r& A- R, b/ y4 b4 p4 x0 }7 B
注: ADC 通知应用每次转换结束 (EOC) 事件 ADC 通知应用每次序列转换结束 (EOS) 事件。 这些标志位都是在ADC 中断和状态寄存器(ADC_ISR)中 ADC_CFGR1可配置COUNT位 。 每次有一个通道在转化结束之后( (EOC) 事件),必须先读取出数据寄存器中采集的数据,然后才能采集下一个通道。
$ f% v/ Z3 H7 j- F) U. b, r7 W
9 w: o1 S {* I4 s, d
9 O% s3 j; ^# V7 e6 S
" y- s7 Y9 i# _% l
6 q( `4 F3 D' }; I
STM32F0-ADC转化时间: 可编程采样时间 (SMP): T Sampling 可配置: SMP[2:0]@ADC_SMPR 需要和外部电路的输入阻抗匹配,采样时间适用于所有通道
7 ~; H& O8 E% R% B/ V; l3 A( T: O* ?, a
转化的时间: T conversion 取决于转换精度: RES[1:0]@ADC_CFGR1 4 \! R8 ?: d {7 K9 f3 w. e" k
% x( T4 r' v9 Q" c+ ~3 M: W
8 v& y# ~/ s5 \" b
每个通道总的转换时间等于: T Sampling + T conversion (采样时间 + 转化时间) 转换时间快速预览表:不需要高转换精度的应用,可以通过降低精确度来提高转换速度 假设ADC模块工作在14MHz的最高工作频率下
( c2 x! y; }& e2 R; C, ^# Q
- ]3 Y4 s5 V9 t5 e0 O7 `& u$ G
! ~9 Z7 V; z) p, ]# G3 r! X
0 I1 ]2 G' ?* [( h2 u) \! ZSTM32F0-ADC触发方式: 软件触发:软件设置ADC_CR的ADSTART=1 时,触发选择有效。 外部事件触发:外部事件 ( 例如:定时器TRGO、输入引脚 ) 触发,可以设置触发源以及触发极性 6 o# V" p1 G& I2 H- e6 f
) V+ r+ [! \6 [
# ?* W# \0 @' \* D n$ q
) c8 }% x6 F9 ?3 Y
3 q. r8 g. Z2 c! ~! k! |6 E1 e
/ ?. t1 b! f0 A( E% `/ K6 ]STM32F0-ADC模拟看门狗: 7 Y8 ~$ {# Z) j) E' ?
}1 j5 K4 V8 s' a! s7 W1 b
检测待转换的模拟电压:(简单的来说就是检测到电压值不在预设的范围之内,则产生中断,在中断中设置报警等处理措施) 电压超出检测范围就置位AWD@ADC_ISR,并条件性地产生中断 检测范围由上下门限寄存器指定、 12位的ADC_HTR和ADC_LTR有效值
+ A+ h! v7 A b. n, u! ~+ Y
模拟看门狗的使能控制: AWDEN@ADC_CFGR1 检测所有通道还是单个通道由AWDSEL@ADC_CFGR1决定 检测哪个单个通道由AWDCH[4:0]@ADC_CFGR1决定
- ^7 E: m# s- s& w
4 O# z" a7 b0 f2 v6 S STM32-单通道采集实例$ {2 J W: l3 d! y& |: _
实验要求:利用ADC采集光照传感器的数据,并在中断中获取采集的结果 9 n! a3 W1 `& g9 d2 \8 ?
- n5 I) p5 {8 X
: Z% I8 L! ]; @- |7 D- C, F
+ G% N% |" ~- ?& I# [7 ~- i注:光敏电阻光强越强则阻值越小
8 ~' p/ m! r) \
过程如下: 6 Q! g! d* J/ v) ?
4 B5 i; z+ C5 k: K0 v
% B+ {' w" o! `1 K7 z: a) e! G
, [: u+ K7 t0 X. q' b" ^+ i% I
- q' Z- d$ P7 P5 }0 s- M
, z5 R: X0 u" }1 D9 z
; }3 r' J( M+ }& P# l1 ]) B
+ n. [+ d- ~% \- d/ w4 V% A
$ c% ]" m4 {) t
& Z+ ^$ a1 o& s: N$ O
+ R' ]* f$ y* B/ i* Z5 b/ V3 p* q( Z3 h
9 _! v5 g# [- X1 H/ M. r% v* i$ ]/ I4 v4 i
. A& ~0 ~ C2 a: a7 c
( H5 X1 x7 `5 }
5 j# P* t' |/ U n5 W
4 ~# E7 m, U+ H% z" G; p, u9 d5 I, ~4 y! i6 ^! _
main.c中启动ADC并使能中断
9 L# u. S2 U: ?% S- B0 _9 F+ P: q0 ^
HAL_ADC_Start_IT(&hadc);//启动ADC转化并使能中断追加到回调函数(可以在向量表中开始追加,也可以在中断程序文件中(.._it.c表示的就是中断程序文件)追加) 

main.c中重新编写fputc函数,adc.c中重新编写回调函数: 

' f+ S- l& s* p. X- c) F' G7 Q2 v
' n# j# S, G( Z( A0 b3 R- int fputc(int ch,FILE *f){
- q4 e- ^+ v) N6 t9 o - while((USART1->ISR&(1<<7)) == 0);
5 n. a- s( X% L7 c9 u - USART1->TDR=(uint8_t)ch;
6 d1 y( o$ T [6 T9 |; L/ x- [. F - return ch;, ^5 P+ {- j2 Z9 n0 k h
- }
复制代码- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
1 w( K {) F a% e - {3 h6 m. Z4 j. u5 Z. T
- uint32_t light_value;1 Z4 P8 W. t7 l" c3 e: [" {
- light_value = HAL_ADC_GetValue(hadc); //获取ADC中数据寄存器采集的值+ ~3 X7 e! k, Z; o6 E. ~
- printf("light_value = %d\n",light_value);. B1 d0 G. \. C7 v6 m w
- }
复制代码 8 A$ L# B2 m$ ]# L
测试结果: 7 y* q, a- f: g" c+ G5 X
9 B9 k" ~8 f7 k" v+ e7 v
# a' H$ T) ~2 g& u4 v
+ f4 j2 H$ {0 p: @5 gSTM32-多通道采集实例实验要求:利用ADC采集按键以及光照传感器的数据,并在按键中断处理程序中打印采集的结果 原理图分析:
P5 C1 F! b5 Q# g; A# L ~
& h. Q" g8 }$ j4 U1 w' S实验过程:
2 ^8 R* k) `1 A8 o f
: a U3 _4 U* B4 \- f8 Q
: I1 n7 p1 W4 W* M
$ p, h6 c- P3 M: u& O
, |. o7 l5 s$ n* R8 i8 ^9 [
) w9 x# ]8 E1 I. e4 Q& I
配置ADC功能,因为是由按键中断进行采集,所以配置可以随便选择
: Y) {' |4 G- R! _- i
打开按键中断:
8 I" U; \8 Z8 Z! f
3 i( }# L+ X7 k: B( j. U
导出工程:
" H1 H, R* B- q6 c6 |* `& Y
, q4 y1 b6 y, d' ~3 I
' Q+ ~' L% W% I+ E8 L! r
+ x( d" z5 A' k- |# ]追加到按键中断回调函数
3 x1 M6 X' p9 J0 ~$ o1 n, _% |
$ A" k* J4 r2 [, N2 T7 w
% o0 ?! P; N4 O6 P' `& Ogpio.c中重新编写fputc 和 回调函数,添加必要的头文件: 0 w! E7 `7 w4 S4 n. |. \' X) h5 X- ]" o
" [' K0 s5 l- ]% @, y% t8 c
( {+ C3 H; R9 q: u- #include "adc.h"
0 G7 L/ \4 M9 U3 {5 C/ C5 C - #include "usart.h", i4 r p4 B l! j& N! L* A! b& \/ z+ b
- - O# T9 C4 R1 V* t
- int fputc(int ch,FILE *f){
7 D$ \# x2 g( x4 T - while((USART1->ISR&(1<<7)) == 0);
2 t- o' Y/ F; l4 y5 B - USART1->TDR=(uint8_t)ch;
D u' y( G' Y% M4 E$ t - return ch;
& R- i; X2 h6 R1 q/ b3 B - } 3 ]% y! J4 H: d! t+ h& j0 U" F
-
0 n/ \- J9 d% v6 c% i: C - void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)0 R7 |$ @+ _0 E K4 M% p
- {; c2 i+ Z2 M+ u2 F9 C, r( ~( C
- uint32_t temp = 0;
; u" J& @( W: i( T3 P - if(GPIO_Pin == GPIO_PIN_8)
* Q5 B% D) q( L+ U) d- W - {
4 Q* H% j V6 N! O2 D/ L- P; \( F - HAL_ADC_Start(&hadc); //启动ADC
. N% X, X- z$ h s- D- ~# F - while(!(hadc.Instance->ISR & (1<<2)));//如果ADC的状态寄存器中的EOC置位,则表示当前通道转化结束
% ?7 I- W/ w8 r4 r3 g - temp = HAL_ADC_GetValue(&hadc);
- }7 e' ]; q- s6 p/ O# J - printf("key adc value = %d\n", temp);
% @: F4 k9 Z" `' t* h - while(!(hadc.Instance->ISR & (1<<2)));//如果ADC的状态寄存器中的EOC置位,则表示当前通道转化结束- c5 ^: r6 N1 Y* I& V( ` R
- temp = HAL_ADC_GetValue(&hadc);
$ a9 h4 r& }/ [9 D. U" ], U - printf("light adc value = %d\n", temp);
1 o3 v2 t1 q7 m A$ P - HAL_ADC_Stop(&hadc); //关闭ADC, \; o5 R: v% b
- }
' `8 s* T% i# h, ]% Z3 D, `6 _2 { - }
复制代码
5 S" r \! o) [" \6 H% h/ ?0 k
+ y k( t0 p- b& o; t1 I" K) {/ v7 m$ G( h2 t \: p% S
6 c! ~/ D) }- B+ H测试结果: 
@" y, C! e0 H" m* l/ W
5 a- w8 Q, L3 x, ^$ b5 p4 c 利用中断和ADC识别五向键,即五向键任一按键拨动时能通过串口打印出是那个按键& l( \4 ~5 u$ ^5 D3 V
4 [1 [7 p1 G' x# N8 O8 a+ y
5 T7 l1 E8 X* _: y
: i4 L! J* h. I' P7 [
x% v c, {( M0 B; p
+ t7 O1 ]9 ^* a0 A b
$ d% R P1 G+ g" N1 }
0 Z' D f# q* e9 o6 V& @( L. M4 A% F6 w; P! |! v6 X
; I6 |+ P/ f. X( ?4 u' A
7 _- y# I$ M7 K C, g% `
" U8 K w! k% {5 u# L+ [
1 j3 I" h/ C+ \% \8 J( U* i" E
6 a: r# ^! u* \$ j F( O- - r0 |/ h% l/ ]" @
- #include "adc.h"% O! z0 I% y+ w
- #include "usart.h"4 u6 F6 K, J2 j3 R% p. w( ^
- ; @) j0 j" m1 X3 q- w& ]% y* y
- int fputc(int ch,FILE *f){ 7 I$ ^0 t7 I" Q z2 |% g! Q; u1 \6 _
- while((USART1->ISR&(1<<7)) == 0); 5 i- _3 m: L& \6 a' w6 a
- USART1->TDR=(uint8_t)ch;
0 W3 Q. h) z4 E" b0 n: o* q8 K) k - return ch;
E! Z4 f' X2 }, | - }# A: ^1 D' Z* F, q% [8 b
-
" p( j0 L! p, V6 d. \2 a0 w - void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
/ {- m8 S" F) |$ V5 o/ w/ S - {3 ^( J" Y) D5 s& ?5 m
- if(GPIO_Pin == GPIO_PIN_8)
! b$ u# {- h& u$ g! u4 g- \ - {
& G V, W" N; {+ X# T2 R - uint32_t data = 0;& o7 H0 i, E9 k* V/ C' v u$ _% |
- HAL_ADC_Start(&hadc);+ K' e5 q5 L8 x5 Q% Y+ x7 g7 ?+ E1 C
- while(!(hadc.Instance->ISR & (1<<2)));
6 ?, u' }2 a; R4 [% U0 K( n- o - data = HAL_ADC_GetValue(&hadc);8 t7 s" Y3 u9 V |
- printf("key value = %d ", data); L, \5 l9 K6 [( a& n
- if(data > 0 && data < 500)" |0 M: H) C( L$ O0 w* }4 T' L9 @& r
- {
! K6 @: W1 \1 \* ^5 a& _ - printf("下边的按键被按下\n");/ z9 `" ]) v; x& A- c/ x
- }
3 i9 c7 E6 t3 z$ {$ N. i/ o2 p, ` - else if(data < 1600)
- Y# O3 H8 x$ b% ~9 P, q - {4 F8 O# H$ `' }) B \+ X; Q2 R2 `
- printf("左边的按键被按下\n");2 B) B( L( X. L$ T
- }
* d$ a; _2 Y6 q- d7 C; \. e - else if(data < 2200)
0 q2 Y9 r* n& n! o' V4 \+ B - {
, G, ]* N, F, y8 r1 y - printf("上边的按键被按下\n");7 v; s" k7 f# }4 [
- }$ s, d. e+ ]4 p; t( M# d
- else if(data < 2700)6 |4 |2 Z" }! Y, v. k, r3 q+ o
- {, V- M" t1 T% D1 f' s
- printf("中间的按键被按下\n");+ x+ D2 s0 n2 {8 m% M
- }
8 V, c! G0 v% F7 L2 H) x - else if(data < 3100)
1 J, Y/ i; m9 Z5 U' _% I2 A - {. ]9 r3 h4 `( u
- printf("右边的按键被按下\n");/ ?- o" g# N! t
- }4 f$ `! T- o- [
- HAL_ADC_Stop(&hadc);( x) H3 a' g" i: C) y
- }
" S3 W [+ Q; Q3 e9 ? - }
复制代码 / I( } Y5 N2 h6 ?% Q$ |
实验结果:
$ R' H$ V) \: m. H2 n
4 Q. [$ k7 e1 P! W
: N7 w6 F! ?* T4 ?, q |