STM32L0开发系列——01 ADC采集
( g) E9 S1 @$ J, Y) M+ s. w7 Z前言
. `$ G+ ~( @4 t; k. ^7 ~使用芯片:STM32L051C8T6+ a+ i! i) `& ?1 b
软件平台:KEIL V5、STM32CubeL0
0 r2 `/ w& z" y3 ^库函数:HAL) I& Z6 ~1 P3 S8 h) M
- r1 ~4 }3 m+ x: S5 ?/ {! f# G7 T
一、原理图
: I* C! w. {: g, G本文介绍在STM32上使用ADC1的第1通道,对电池电量进行采集。
! e" g6 g5 M5 o6 ]/ @% s2 Q; j! U3 s$ t! d1 R9 C# n, j
* \ C+ q- ]& \* ]% M9 Q, `# w
7 u$ e) n9 C, X% M二、ADC通道与引脚对应关系
8 M3 C% w, M8 }# \ T4 r# p8 o5 g# U' J/ ^
7 p: E3 _4 B0 [) k0 B& I5 h" ?
* u2 H7 }6 u# {# r6 |' @
8 M8 |( n: y/ h5 l) u8 F% V
三、ADC相关
1 Q% m9 k: P1 Z& c1、 ADC 可由 APB 时钟或 HSI16 时钟提供。
3 E8 h& x/ B- o6 l2、 ADC 转换时间: 12 位分辨率对应的转换时间为 0.87 µs (1.14 MHz), 10 位分辨率
# q0 J3 t: `, z$ b* f2 d对应的转换时间为 0.81 µs,若降低分辨率,可进一步缩短转换时间。
" {7 j2 Z$ D, I( u$ X5 V/ O0 D6 [1 B% h
四、实验步骤
8 _' u9 y6 E- h1、系统时钟配置
4 a* \6 h" G' F6 P, n& u: U$ ~) C5 [6 q: W9 A
- //****************************************************************************** 5 @+ E; ~$ }2 h# l: z+ Z- x
- //name: SystemClock_Config 5 d1 }6 K5 I1 R- I6 Y7 k2 `
- //introduce: 系统时钟配置 # Q' T+ u% e3 p) i j
- //parameter: none
$ p0 ]" u; T; C3 g1 V - //return: none
7 S* |% w9 Y& C9 ~ - : h$ o% n9 \0 E
- //changetime: 2019.05.21
' p8 ~0 q3 w/ S! _0 o - //******************************************************************************; h" B& _) @; i; G# d- Y
- void SystemClock_Config(void)6 f8 }! H9 D5 j6 k
- {; Y' S" H5 I% D/ \
+ d( p1 _9 b% X4 L- RCC_ClkInitTypeDef RCC_ClkInitStruct;
; a( V# c4 F9 B# e( i - RCC_PeriphCLKInitTypeDef PeriphClkInit;3 S ?. |/ C+ y" C8 c3 x7 p
- RCC_OscInitTypeDef RCC_OscInitStruct;
' ]9 C) f, s; e2 Z2 Y - 9 f# n) E# y8 ]. \7 y; B
- __PWR_CLK_ENABLE();& t5 w7 w! N2 ]2 m- D9 S+ i
- 3 R5 P* q/ p9 n1 L& p) L7 X
- __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
5 a0 w0 T. b+ _$ f6 T- u% s
: C( E4 i Q3 ~* F, f- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE;
8 k1 L" s4 G x4 C4 W N6 J
5 z! _! K) Q" b$ [' O8 f3 T* l% j# p- RCC_OscInitStruct.HSEState = RCC_HSE_ON;" l8 n; x2 ]$ ^- m3 f
- RCC_OscInitStruct.HSIState = RCC_HSI_ON; //ADC的时钟源7 W$ J I/ R$ N# g
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;3 K; C1 w6 T Z; C8 T# o+ ]
- RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;3 C7 ]. R( y$ j! s4 }6 R- b# x
- RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_8;
* w! \2 C9 q, C - RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_2;
; a, l$ N) I* [3 v0 S, s u% ~ - HAL_RCC_OscConfig(&RCC_OscInitStruct);4 d1 h5 J8 I2 a* Z3 P
% B8 S" z$ |3 E$ g- RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK9 N8 S, ?8 f. ~3 {1 h, k% v! Z) D
- |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;# S( Q% H# _3 F2 {5 u* w5 G
- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;: Q2 |" ^6 A' w% g
- RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
' z2 ~4 [5 m+ i7 w - RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;$ c- P' [2 c8 y* [. y
- RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;! } X1 \; \2 I
- HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);( K9 d$ {/ v2 v2 `8 h1 v
4 F+ \" h' P2 R6 K- PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
; d) u# g5 h5 {" m8 P) u - PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
! i5 D& |) U: e - HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);. S- ]5 e9 ?! a: |3 y* }3 P
. P6 }5 p" w2 t0 S7 K& z- __SYSCFG_CLK_ENABLE();
3 |) Z. ]: q* j& z; S: Z8 ~. U% S - 5 L) O- }! ]" U
- }
复制代码
; A5 w2 n$ \; R* }* E8 h注意:一开始程序中没有配置HSI时钟,采集不到电压
2 }1 I1 ^( U5 n9 q; ^, d# D; B3 R7 J5 b( _; \
2、ADC文件2 _! B0 [6 M2 y& |, b* `. G7 ~5 f; M
1 `2 P9 `/ V* M- Z9 |
- #include "main.h"1 p9 v$ V& j4 I- u" x4 G
- & f5 d$ ?& o" { F7 k
- ADC_HandleTypeDef hadc;2 G9 Z3 J* j% f% a
- GPIO_InitTypeDef GPIO_InitStruct;
7 c. W, Z$ L4 A \; a; o; m - ADC_ChannelConfTypeDef sConfig;! J m, I. f- m& ^2 J
6 A7 y6 m" O8 m- j. A; J
" @' S5 g0 T5 c6 H3 D2 G- //******************************************************************************
7 ~! D Z" n: I' B& C$ A$ S3 y0 U6 a - //name: ADC_Init " s: W: C. H1 P. n
- //introduce: ADC初始化
+ M- ?' \$ R% Z+ [0 c" j2 P - //parameter: none
9 X3 ]' y ~4 r: N - //return: none / U! v- U1 F+ Y; o
0 t, y8 \ O' }& w( s, U- //changetime: 2019.05.21
" C8 B2 `. Z# G9 p/ |, t4 \+ s - //******************************************************************************+ P) T5 t* N8 n+ Z9 D+ ~9 R! C
- void ADC_Init(void)& M& m/ q# e. ^1 m M
- {7 k" {3 f* h9 B# F
- uint32_t Calibration=0;* P0 \# Y$ e# ] p8 s+ U
-
) ^1 r# U2 i% l0 V1 ^ - __HAL_RCC_ADC1_CLK_ENABLE();
0 v' }0 q' ^8 n; d* B% @* [5 o9 A - __HAL_RCC_GPIOB_CLK_ENABLE();( P9 O$ f9 X- m
-
1 U$ l3 h0 D. t+ y# T2 k - GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;% j; M% n8 {. T( d- t' P
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;; O& p D; g. W) [2 E, u
- GPIO_InitStruct.Pull = GPIO_NOPULL;7 _& v/ X4 u- V6 u3 v- {! b% R
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
. K5 |( W3 V; c: _' F* S- X - % o; b" ?- I* h% g
- hadc.Instance = ADC1;
+ j; n& A4 j7 U* l3 ^- I: ] - hadc.Init.OversamplingMode = DISABLE;
: y% M0 @: v! w! f - hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
0 A. |( U& G5 g2 p9 K9 i2 b$ R - hadc.Init.Resolution = ADC_RESOLUTION_12B;
. R5 [7 T C9 w" y4 s, x - hadc.Init.SamplingTime = ADC_SAMPLETIME_79CYCLES_5;
, _: Y, |( X/ T! E( p - hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;. A2 u' l" |/ A- n+ X' T$ ~
- hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;6 |) x: t8 G5 I2 P( x; a6 J
- hadc.Init.ContinuousConvMode = DISABLE;% @0 w9 D5 L' E: m
- hadc.Init.DiscontinuousConvMode = DISABLE;
& n/ C$ Q4 ]! ? - hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;& ?6 a* ^4 i( O" g1 G6 a8 E' p
- hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;- q$ J" i& [" z% |0 j! t
- hadc.Init.DMAContinuousRequests = DISABLE;' G$ g) C5 h0 I, h
- hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;8 l4 }) S, r* r2 S4 @7 X
- hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
5 d3 B' A6 U0 d1 e T" b- a; ^% ^ - hadc.Init.LowPowerAutoWait = DISABLE;
7 r9 C* e' M$ z" O6 |/ ?: R; Q2 r - hadc.Init.LowPowerFrequencyMode = DISABLE;! I/ @3 V3 H# X4 R. J( t# i& e
- hadc.Init.LowPowerAutoPowerOff = DISABLE;
; c; f" q+ M- w6 g - HAL_ADC_Init(&hadc) ;3 B: ], G9 z* q/ Q# K# R4 _
- 0 [; J7 t; w, P7 N4 F# |
- //启动ADC校验功能
c6 r( }) J+ W! `$ B - HAL_ADCEx_Calibration_Start(&hadc,ADC_SINGLE_ENDED);. H* B, R6 J- E( G1 H+ m) |( w. ~$ }
- //获取校验值
' K' x5 v2 Q5 K - Calibration = HAL_ADC_GetValue(&hadc);5 ]2 D- o; Z2 x! p* A! I* L
- 5 t/ R; o4 M( v6 V* {5 Y J
- // 清除之前通道
3 D$ k3 }/ E' t2 C - sConfig.Channel = ADC_CHANNEL_1|ADC_CHANNEL_2|ADC_CHANNEL_3;* d2 W0 ~5 w( q( Y: E G
- sConfig.Rank = ADC_RANK_NONE; // 清除通道属性
/ p; [/ A; A z. B - HAL_ADC_ConfigChannel(&hadc, &sConfig);4 L8 q5 `4 N6 J& o: E- k: v
- : T. O9 U+ a- u3 c3 w
- }! O8 c$ Z0 G# j
0 n; h+ @. Z$ L9 a- //****************************************************************************** ( m" m- e: l+ s3 v T& F
- //name: GET_ADC 0 Z+ G/ t" c2 s( E
- //introduce: 单通道采集ADC的值 & F8 e( ^7 F# _& e
- //parameter: CH:ADC采集通道
2 V( x8 u7 C" i# Z, m9 S9 ? - //return: ADC采集值
' N4 d3 Q4 X( i* h+ j! C* W
) K U0 ^$ U$ G* K- //changetime: 2019.05.21 # @1 k- K9 ]2 K* B
- //******************************************************************************
% L. O; V0 \( ^3 _ - uint32_t GET_ADC(uint32_t CH)
; H! ~# b2 o9 V - {) I. }" @+ I+ W1 z7 ~
- uint32_t adc_conv_var;
3 h* ^1 C0 E, S" V2 | - sConfig.Channel = CH;
/ j1 q. ]9 j9 x) W% {" D6 R& Z/ W - sConfig.Rank = ADC_RANK_CHANNEL_NUMBER; // 设置通道3 K9 e: s; s5 z. B9 C! \
- HAL_ADC_ConfigChannel(&hadc, &sConfig);% }6 |; D: C/ R2 n+ v* @9 {
- 9 m' z6 {+ |! ?; u+ b W
- // 启动转换+ h% o, m2 P& D! W9 [9 |3 R
- HAL_ADC_Start(&hadc);
& P+ o/ }& E: ], P% E- _3 d - 4 K/ x! ~0 ?+ B2 {/ d" {
- // 等待转换结束
8 ~; Q {/ ?( p# ]+ E7 Y: t" ^5 \ - HAL_ADC_PollForConversion(&hadc,20);// 超时20ms% Y! B2 b7 N) ^8 O5 }( r( Z9 A% ?
-
+ |8 Z6 ?; v z7 f/ W - // 读取结果' A( o& \ ~- |
- adc_conv_var = HAL_ADC_GetValue(&hadc);# w( w. u/ T0 b& n' C5 g7 v: Q
- ' |* l( q$ r' ^. F) P5 m
- // 清除通道
" ^" t8 W4 F, o0 A) j - sConfig.Rank = ADC_RANK_NONE; // 清除通道+ L: o- W M9 P4 W. U& [
- HAL_ADC_ConfigChannel(&hadc, &sConfig);
. B0 y$ @3 }4 m$ Y' {4 o - : c- q! P/ p! Z9 x
- return adc_conv_var;3 b* L7 T9 V# Z9 n$ R
- }; z/ [( K7 ~1 p6 m# q$ @# Z
- - J s5 [0 J, H2 n+ d) d
- //******************************************************************************
. M+ R1 K9 w$ ]; V7 Z - //name: Get_Adc_Average $ z" v8 `% p% c8 ~8 `: a/ k4 D/ Z
- //introduce: 多次采集求平均值
; j' L( D! ] Y4 Z5 h) w - //parameter: CH:ADC采集通道 ( s K( |' a2 v+ ~
- //return: ADC采集值 ( }; I: V! k% H# G
- 5 S5 u% N; N) Z, W+ n+ T6 C. J, F* ]
- //changetime: 2019.05.21
1 D4 g( S- e T7 k, c - //******************************************************************************! p. h2 h7 V5 \$ t
0 I0 w2 {4 c+ F1 U( ?8 f$ W- void Get_Adc_Average(uint32_t *ch,uint32_t *adcx,uint8_t times)
: m' K" T+ \( H8 ] n - {, r" S l! {9 i5 ~/ j
- uint32_t temp_val[9]={0};
3 j4 E6 }8 j: K, X4 v. \. H3 E - uint8_t t,i;! @/ \/ I/ c D0 k- _
- for(t=0;t<times;t++)
# D1 n1 o& j. g) b7 T/ d+ V - {6 O: ^& c. x0 I0 l, b; A/ A
- for(i=0;i<9;i++)7 ~* h, ^; x9 j4 d; J2 t0 E! Y
- {
! o Z% e- `! R - temp_val<i style="font-style: italic;">+=GET_ADC(ch);# u) H) k1 L( a4 e
- </i> HAL_Delay(5);9 z# s8 \+ W. Y
- }
6 i* A; I: [4 N: F' q - 3 |) g" T" Y8 m9 A5 Q5 k3 [# e
- }
" D" }( a. ~* v M2 I. T - $ g0 D5 z) C8 B# }4 E
- for(i=0;i<9;i++)
/ c3 p' ?* ^3 J; q* I+ T8 e0 o' D - {/ Y' b0 f) [9 v3 {5 E; }" g* U' k/ y
- adcx = temp_val/times;% ?4 X; E4 Z( Z7 J4 ^- q. d
- } A% ^6 U, E! _
- 5 v0 U; |% W# }0 ?' a0 [* B: B
- } 3 c( |, m: `' E& c& Q
复制代码 ! ~. I, X9 v' r( A8 P4 }9 n/ Y
3、main中测试
/ S" u b+ I8 Z: o- while(1)3 g- R" D. B4 n4 P" j: n; n
- {% K. T' w5 |# _4 J$ F0 P! W% j
- BATTER_VALUE = GET_ADC(ADC_CHANNEL_1);
% l8 w0 Q2 ?; m0 _' P$ S - temp=(float)BATTER_VALUE*(3.3/4096)*2; ; Q2 X0 I; T9 E
- printf("temp=%0.2f\r\n",temp);4 L1 s* N" l4 z
- / c4 s6 R+ Q+ Z
- HAL_Delay(100);+ H$ J6 u1 N5 g9 G; O9 m* h
- }
复制代码 ! j7 O4 f) Y" F) q2 H3 w! E7 E
3 o% b r5 }! G+ ]0 C0 n四、实验结果
, `9 y1 q9 A% f6 p! i" x
8 \& s( D8 x# n由于ADC的精度是12bit(4096)、参考电压为3.3V,因此实际读出的电量值为BATTER_VALUE3.3/40962.
. J) d& J! [) x1 V5 d/ v试验成功
1 u/ F0 Z2 _6 W$ M {/ \2 U, ]- _2 d/ A+ u
' H: u4 V% I4 ~4 X( I
' }. P- \: E8 ?8 H8 z5 @# A+ q' I' s |