STM32-ADC模数转换内容概要: STM32-ADC模数转换概述 STM32-单通道采集实例 STM32-多通道采集实例 ( ?4 w1 \; l7 L
STM32-ADC模数转换概述
. j- y, Y D* o& `% w# Z) y2 _5 j内容概要: ADC简介 STM32F0-ADC时钟 STM32F0-ADC转化模式 STM32F0-ADC转化时间 STM32F0-ADC模拟看门狗 ( Y; D" Q- H7 P9 I' J: q) E
# F* b/ O' k1 O9 b, }# D5 o B5 q' i
ADC简介: ADC的作用:采集传感器的数据,测量输入电压,检查电池电量剩余,监测温湿度等。
* C* l- J) H# g4 l1 a8 }) z K x
ADC的性能指标: 量程:能测量的电压范围 分辨率:ADC的分辨率通常以输出二进制数的位数表示,位数越多,分辨率越高,一般来说分辨率越高,转化时间越长。 转化时间:模拟输入电压在允许的最大变化范围内,从转换开始到获得稳定的数字量输出所需要的时间称为转换时间 + b0 a G1 L6 ]) _- J7 B$ q
0 k6 K! M+ v7 w6 i% M# Q& m
STM32F0-ADC特性: 8 @# t! g# m: x/ q! d( N
9 @4 d/ n0 o( ~/ E! y0 c& @) x
12位精度下转换速度可高达1MHz
: l f" q% v+ Y, i- {
可配置的转换精度:6位,8位,10位,12位 转换电压范围:0 ~ 3.6V,V SSA ~ V DDA 供电范围:2.4V ~ 3.6V 19个转换通道: 16个外部通道、 3个内部通道 # S; E1 A* F' U2 }: k
采样时间可配置
' ~9 N' q+ e& I8 Q l" M+ ^0 G% v
ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中 1 ] v% d& V( @% ~4 ^/ c7 P6 W g
STM32F0-ADC时钟:
2 q# _( C7 a! K# Q1 h8 K
APB时钟的2或4分频,最高14MHz 优点:不会有时钟域之间的同步带来的抖动,触发事件和转换的起始时刻之间的延迟是确定 的,从 而保证转换之间的时间间隔是固定的 缺点: ADC的转换时间和系统时钟频率相关,受系统频率的影响较大 3 i: ]. r$ P% ?2 U# x# \1 t
2 k/ b4 d3 e4 C* U# c/ P6 q
片上14MHZ HSI RC振荡器 优点:无论MCU的运行频率,都可以保证最高的ADC工作频率可以使用自动节电模式(自动开启或关闭14MHz的内部振荡器) 缺点:触发信号的同步会带来抖动,触发事件和转换的起始时刻之间的延迟不确定
( M; m6 L' \; ]" o" r1 a
# ~( E2 L2 |0 B& O" n- Z8 z
# n, \1 h! H% _1 t
STM32F0-ADC通道的选择:
9 l7 t- P3 f" z6 J% }4 n7 z' W$ R9 A; r# P6 S# t, m& Z( C
19路复用通道: ●16 个从 GPIO 引脚引入的模拟输入 (ADC_IN0...ADC_IN15) ●3 个内部模拟输入 ( 温度传感、内部参考电压、 VBAT 通道 ) ADC 可以转换一个单一通道或自动扫描一个序列通道。被转换的通道序列必须在通道选择寄存器 ADC_CHSELR 中编程选择:每个模拟输入通道有专门的一位选择位 (CHSEL0...CHSEL18).
* E: e: E; f2 z- V, i6 w
0 `4 e/ ^: g8 _- d; G; n
STM32F0-ADC转化模式:
2 a: Z4 K0 B7 f' [% Z# H' P5 k4 j2 q
" d! U$ z' D/ f9 Y/ C1 w- a
注: ADC 通知应用每次转换结束 (EOC) 事件 ADC 通知应用每次序列转换结束 (EOS) 事件。 这些标志位都是在ADC 中断和状态寄存器(ADC_ISR)中 ADC_CFGR1可配置COUNT位 。 每次有一个通道在转化结束之后( (EOC) 事件),必须先读取出数据寄存器中采集的数据,然后才能采集下一个通道。
& @% ?1 y6 e) U1 D" y5 Q7 L+ c
" v+ Q8 l. C6 i. e) C
5 ]" c# F/ U5 \* T
: N8 z% s# D- Z: i/ W1 U6 g3 K
~) W% X- x" P/ f
STM32F0-ADC转化时间: 可编程采样时间 (SMP): T Sampling 可配置: SMP[2:0]@ADC_SMPR 需要和外部电路的输入阻抗匹配,采样时间适用于所有通道
9 ~ _/ k5 `9 P7 ^; H
转化的时间: T conversion 取决于转换精度: RES[1:0]@ADC_CFGR1 % S' z6 q, l8 C0 x3 U6 C4 ^" e
+ n! i8 h7 Y; d
( J& |) @% w4 T! y$ N( ?, C
每个通道总的转换时间等于: T Sampling + T conversion (采样时间 + 转化时间) 转换时间快速预览表:不需要高转换精度的应用,可以通过降低精确度来提高转换速度 假设ADC模块工作在14MHz的最高工作频率下 / D0 t; `3 I) w2 H5 L. t5 T' b9 a
9 C/ u5 ?* }4 }# w2 L7 g7 C
! ^/ P7 W3 Q0 l3 C5 ]
* O; a/ V6 [) m" R9 jSTM32F0-ADC触发方式: 软件触发:软件设置ADC_CR的ADSTART=1 时,触发选择有效。 外部事件触发:外部事件 ( 例如:定时器TRGO、输入引脚 ) 触发,可以设置触发源以及触发极性
& U* c2 v9 v1 x; k/ _' @
. i0 P( J$ u6 U$ `
. k' O) D Z' o7 {& S0 a0 m
1 l J! r, |1 y' J9 c0 o
: z: t7 }7 [3 r0 V( `* }; i2 [/ e" \7 j V& q' Y
STM32F0-ADC模拟看门狗: : a% o3 _, ?* B( G* ^
# N% {9 i# y) O; T. S! R- u
检测待转换的模拟电压:(简单的来说就是检测到电压值不在预设的范围之内,则产生中断,在中断中设置报警等处理措施) 电压超出检测范围就置位AWD@ADC_ISR,并条件性地产生中断 检测范围由上下门限寄存器指定、 12位的ADC_HTR和ADC_LTR有效值 4 `$ E" x0 U8 Q0 N9 z
模拟看门狗的使能控制: AWDEN@ADC_CFGR1 检测所有通道还是单个通道由AWDSEL@ADC_CFGR1决定 检测哪个单个通道由AWDCH[4:0]@ADC_CFGR1决定 $ l; k* V: \" c+ k: C1 R9 j7 V
) T) v7 t& [2 W# n' _
STM32-单通道采集实例9 _$ W; X9 U9 u7 [+ j, |' f7 p7 o
实验要求:利用ADC采集光照传感器的数据,并在中断中获取采集的结果
, L1 O3 Q' r, x( O4 @+ \4 J
7 ^6 u( h6 H$ q" o9 C5 ?5 j
* C5 y1 ^3 G' U a$ W$ h4 C7 n0 {# K/ n6 r! s0 k0 L7 n
注:光敏电阻光强越强则阻值越小 + b. b' y' y. r2 h+ E' K8 v ^" g
过程如下: \; s: ^3 s0 d# z4 y/ Y6 X& K L1 _
1 I' U: N% N$ k5 s: {
' `: g4 V# U' M b. h, P5 B8 l" V7 L o& b3 a- h( s
M2 d) b+ j* I* A
8 Z, G4 v2 S+ N8 |+ ~
$ N; M+ A- L0 e! U; O- E" w
$ v, A) w* ~3 Q l! @( R$ E; g
( g( i5 T- \( N% @! |' j) {2 G5 D$ N3 q7 D2 A' f T% q
( I! r% Q L& |8 E* U$ I
. q! E1 e: U" {" R% {
" T: V+ M$ l, T/ i7 @0 I/ p
f- `4 q* j! l b% w
& p: L9 g: s: C$ w; c2 h* Z
& V4 p3 S( C$ D4 U: l8 z7 a) p
- t( t1 v8 c( G# c
" L% [/ @ H* x$ c+ I/ E: n) F- N! K+ f
2 P, {) o- ?2 x9 ^ Z
main.c中启动ADC并使能中断
- Z' h" v( {. e# X
3 J0 h$ A" Z" e! D1 ^
HAL_ADC_Start_IT(&hadc);//启动ADC转化并使能中断追加到回调函数(可以在向量表中开始追加,也可以在中断程序文件中(.._it.c表示的就是中断程序文件)追加) main.c中重新编写fputc函数,adc.c中重新编写回调函数:
" m) ^/ V# i# G' X F9 `1 T
- - r7 U8 f1 Q9 \
- int fputc(int ch,FILE *f){ / b: ?% x" p+ F" L& B
- while((USART1->ISR&(1<<7)) == 0); . z8 k6 R6 o) [' C, Q) I5 b
- USART1->TDR=(uint8_t)ch;. K0 c1 }+ O% e# i
- return ch;
; r, Z. S8 ]' G6 v* E7 h8 v' g& M; \& u - }
复制代码- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)3 I' x: R6 f1 O, w
- {
$ F X( l7 ^3 ]8 o Q4 E, e2 Y9 ]+ v - uint32_t light_value;
% u% ~& }7 M: A# ^ - light_value = HAL_ADC_GetValue(hadc); //获取ADC中数据寄存器采集的值9 q. f O2 {" I6 [; ]+ n3 l
- printf("light_value = %d\n",light_value);
9 J4 W3 H4 V) P - }
复制代码 2 b% E; W; K7 S$ @! W, I) Y3 [
测试结果: - ?( h, c- ~ ]: h+ C$ R7 Y9 Q
/ T# d7 l& K- y5 n* b. T
+ O1 P- k4 c* r- I3 ^
7 b! g2 F9 |* F. G6 {) \1 O7 FSTM32-多通道采集实例实验要求:利用ADC采集按键以及光照传感器的数据,并在按键中断处理程序中打印采集的结果 原理图分析:
5 Q4 I, V# M! e U. c
# m' I8 N" I5 G2 n# \
实验过程:
| W$ D1 d2 x5 g' }3 X
$ b: E1 @" Z% G
& M9 D6 @% f8 F& i
" V+ R# j9 Q0 s" I: Q7 E5 z
! B$ Z$ i8 h+ X; n S% Y0 X+ P8 B
& T. w: Q' c) x& p S) x) B9 c配置ADC功能,因为是由按键中断进行采集,所以配置可以随便选择
, Z' M' e+ ^+ \: m! n打开按键中断:
4 ^! ~) h* n/ ~$ ~
- W3 e! \ x- o, P导出工程:
3 U& z4 k1 C) |9 w3 B N1 ^5 D ^0 Z- G
; Z1 n4 n k* @$ r
( m* `& R) A2 I: i; E3 ~. Q4 L4 ^6 j9 I
追加到按键中断回调函数
0 |- D2 [3 Q# s4 |1 S t
$ D' L: ]5 p" L! r' e
2 k, }) \" u ?, V( L
gpio.c中重新编写fputc 和 回调函数,添加必要的头文件: 4 R( w! n8 l* R6 o
1 Z7 V) z; y* K) T; c! U) R0 F9 Q" a) K
6 c" @# h* B7 M7 y/ g
- #include "adc.h"
$ _1 W# M# h9 |" L - #include "usart.h"2 P+ e) m# Y8 O& n$ v w$ h. y
-
+ U% R* H9 l" w% }! t - int fputc(int ch,FILE *f){
" J8 e4 _% ~0 y: R; _7 K - while((USART1->ISR&(1<<7)) == 0); 7 L$ a: D0 P d0 a
- USART1->TDR=(uint8_t)ch;
$ g0 @0 e" q! m P2 y4 t - return ch;3 d; ^/ F! i& U; B
- }
' o# i8 f8 D& y$ u/ S/ x( ] - $ n* P- @" B" B, a
- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
5 P: f1 Y8 o% [; s( h - {
+ Y, _" Y4 c, F! O - uint32_t temp = 0;
5 [9 H; M( x9 H' U% `& w7 ^ - if(GPIO_Pin == GPIO_PIN_8)
- N" {3 w* @* u+ j+ D$ W) V - {7 t. u$ \- Z; `2 k
- HAL_ADC_Start(&hadc); //启动ADC4 j+ o( R" n! J
- while(!(hadc.Instance->ISR & (1<<2)));//如果ADC的状态寄存器中的EOC置位,则表示当前通道转化结束( f4 w5 P8 o' M0 m& c
- temp = HAL_ADC_GetValue(&hadc);7 E, L7 D5 Q. c, @( x
- printf("key adc value = %d\n", temp);
3 j9 I, b5 q/ Y6 o+ h8 f - while(!(hadc.Instance->ISR & (1<<2)));//如果ADC的状态寄存器中的EOC置位,则表示当前通道转化结束
1 X U, V' U( ?8 X - temp = HAL_ADC_GetValue(&hadc);
4 s1 p& N2 D9 v$ \9 E$ X! h1 K1 y - printf("light adc value = %d\n", temp);) u4 @1 ?$ K$ b5 ?
- HAL_ADC_Stop(&hadc); //关闭ADC
q+ @& s9 `! V: K - }
3 C; t g5 k0 o7 z3 d - }
复制代码 0 z; r7 h# |# c* @4 T
0 ~2 K1 j8 ^6 W8 O" b" [
2 T8 I) x6 R4 V1 {6 `/ s, R6 [% Q# q8 s5 a) g1 |+ `
测试结果:
3 y9 e3 Z3 k3 _. Y% |: B
8 O5 g: j; D& y
利用中断和ADC识别五向键,即五向键任一按键拨动时能通过串口打印出是那个按键* x6 ]3 ]& r d T# v! u
9 d! c, l0 n# I/ J9 G
) |- j' _! k$ U. w
Q! N+ W9 K5 l; E6 f, I3 ?$ {
" x5 D/ M4 p+ l! q; _- V6 O
& ~. s9 ?. H4 ?" U- T8 ^
; h5 }8 Y' u1 ~* A! E0 q2 h6 i# \
9 c3 s n/ n# [3 F* w1 g1 B& V' B1 _1 p9 v
6 y6 X2 _$ w# l1 | K% A; h
# s G9 N6 s/ T9 Y: C) y& |+ ]
1 ]: j3 A' |! M
% m# |# L; j3 D4 E/ H
; M' ~! U+ Z# q+ q3 R4 w7 g8 { b- + l9 j: Y) A0 W4 J! E3 f n
- #include "adc.h"; M0 m5 w% c1 Q# }( L& }
- #include "usart.h"
' S8 a. P8 y% D -
. p: w$ P1 C4 A! m3 b# V - int fputc(int ch,FILE *f){ ( F/ {# f5 @: m7 J6 t
- while((USART1->ISR&(1<<7)) == 0);
0 T3 v2 C. F7 r" ] - USART1->TDR=(uint8_t)ch;8 y; b6 Q( \1 s9 r- F
- return ch;/ T" d/ [$ E# i: i7 I
- }
3 b7 Q- c1 Y$ z$ k" y7 @$ ^ - 1 S6 a# S+ i* O9 j) p- f8 j4 @
- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)# A/ `4 r% S+ g3 K$ B; H: o
- {, p9 G# z. ^ g3 [
- if(GPIO_Pin == GPIO_PIN_8)/ U2 m5 v& N- O+ d* i, I# m
- {
. j$ N' ~4 `% q8 {% R - uint32_t data = 0;. l$ K7 }/ i8 E
- HAL_ADC_Start(&hadc);. F6 G0 `2 B2 n- z2 ^& F; }- f9 e
- while(!(hadc.Instance->ISR & (1<<2)));/ ?7 z/ l' ]- e
- data = HAL_ADC_GetValue(&hadc);
% s4 i" `7 N9 [( H, G6 E% Q+ q - printf("key value = %d ", data);5 {; h' d) w; i U1 G
- if(data > 0 && data < 500)
- ] H* l( h. a; x0 S - {
8 [ z9 O M u( `( ?/ G - printf("下边的按键被按下\n");1 ?' w$ l/ n' p$ F, o! Q* u4 t
- }
8 B0 t7 d2 C2 P! m) E - else if(data < 1600): ?0 @1 v: m, I7 ~2 M3 F' J# r
- {7 s$ q3 k2 ^- n/ T$ b0 _, ?0 t0 b5 e9 ~
- printf("左边的按键被按下\n");
/ [ q2 {7 q1 } - }! j+ {+ b9 T/ w6 K# o
- else if(data < 2200)
! p( W* o. }8 W" ~! a" ]" M8 H - {0 Z, F& [: c* s- _: w! j# u0 x2 ]
- printf("上边的按键被按下\n");
. T6 V) |) J+ \: ]6 u w [; u - } A; b( {0 m/ N
- else if(data < 2700)1 _$ g6 h% |9 S0 T
- {3 ~1 ~, d1 I/ w8 {+ u
- printf("中间的按键被按下\n");- X0 N$ ~. n( N" F( Q
- }2 D8 g2 r; y, k# K1 X3 e- N+ Q6 F1 d
- else if(data < 3100)/ j/ R) I4 W$ L" t
- {2 D% Q- C& y: E& s3 C& y
- printf("右边的按键被按下\n");
+ \! C+ s. l, o2 w% T - }
4 I' z, Y+ J9 J: j - HAL_ADC_Stop(&hadc);9 d2 b3 g8 W( H, c# v
- }
( ~+ @9 Q1 [( ~7 L4 _5 U - }
复制代码 ' v' e6 ]3 _5 L$ H0 K/ E
实验结果: & K/ o. W2 S* G0 P/ f9 T! _& r* ]
4 _9 ~ ]! Q! T: q2 {
; J& l ^4 v L& M6 k |