STM32L0开发系列——01 ADC采集2 C& g# ~3 Y: y' Y2 {0 {3 `- z8 g( O
前言% z+ f7 `9 F* |; u1 Q% z
使用芯片:STM32L051C8T6/ i1 @# T) }7 I# u0 F
软件平台:KEIL V5、STM32CubeL0' g1 F! g' T3 P% h" u* w* B
库函数:HAL
; e. h& I% x+ o/ j T" _7 X) l3 s$ E5 q' d. ]
一、原理图5 Z0 M* ]* \) A1 G
本文介绍在STM32上使用ADC1的第1通道,对电池电量进行采集。
& W" i$ l l! h/ R$ K
% r7 p% q l/ E- \. o
; x* |) c- Y/ k1 u) e0 s% ^) O# `/ N1 H! W9 g' U h0 {
二、ADC通道与引脚对应关系
# Y8 T; A$ N; i7 @4 C% h% y$ i& E; K1 C3 Q% _
8 L* g7 h1 _0 o
% P# s) f' W1 c
; r( i0 _; T) t. B7 P: E* }三、ADC相关
( M9 j2 e4 c1 k# I* k1、 ADC 可由 APB 时钟或 HSI16 时钟提供。
7 R& [8 K$ v+ j' R, B: `2 k2、 ADC 转换时间: 12 位分辨率对应的转换时间为 0.87 µs (1.14 MHz), 10 位分辨率1 D9 x7 u, @1 n+ \" a4 M
对应的转换时间为 0.81 µs,若降低分辨率,可进一步缩短转换时间。
" D' e% C% ^! q; o5 w v: ^4 f( ?) W- N+ p
四、实验步骤9 F* {, @+ t+ b- c& q
1、系统时钟配置3 Z2 `- z, w2 i* M( P& O
& f5 i0 R' a* E0 P* R
- //****************************************************************************** 0 @: u9 u& B# b* J% H
- //name: SystemClock_Config
/ ^) s4 b1 ?! m7 G1 x# W# b - //introduce: 系统时钟配置
1 R5 W$ Y7 G, T. ~+ j8 q: T - //parameter: none
, j3 {1 U, w' |: m, y$ J3 [" L - //return: none 5 `3 F0 ^& u/ S1 f; ~% L( ^% d6 U) O
) i0 T T# X( r. V4 C- //changetime: 2019.05.21 9 F5 |- W6 `9 o2 a3 C# T8 b4 c
- //******************************************************************************4 e: h: M) C! Z, ~& ]% w" ~
- void SystemClock_Config(void)0 k/ O' H9 I0 F% L8 z" G3 f
- {/ B( E6 J, W- W/ ~
- / Z N) {( i: d3 X4 U
- RCC_ClkInitTypeDef RCC_ClkInitStruct;
5 U# \, W! P& h: ]. q. \ - RCC_PeriphCLKInitTypeDef PeriphClkInit;
$ n x- i: b: o& w) ` - RCC_OscInitTypeDef RCC_OscInitStruct;
) `/ a) c9 I1 @" }
/ ~ x6 |/ K0 S n) `! ^5 J: ?- __PWR_CLK_ENABLE();
1 E9 Z/ _1 `4 T - 0 T \5 P' ^; E) V! o% G
- __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);% N( M( W$ ]8 L) O
' h" q% }, @. t2 _+ l2 @3 N w, B- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE;
, O4 X; M0 [+ s5 |9 N5 x- P
6 ^7 C# \6 c: p% h- RCC_OscInitStruct.HSEState = RCC_HSE_ON;
/ |& Z. w' V5 E X4 G; Y c% }- _9 z& F - RCC_OscInitStruct.HSIState = RCC_HSI_ON; //ADC的时钟源# e& o& F! u+ L, B( B ?) s
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
% {0 F9 e- @' ~( e - RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
) p2 E8 i4 X2 k' r0 o - RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_8;9 B" X. Q5 T- [4 Q' {' E7 u) M! }
- RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_2;
2 U9 }6 A6 k- t: y: a - HAL_RCC_OscConfig(&RCC_OscInitStruct);
. R% B; O9 i8 r8 m - + L1 d& b E$ v4 e' v1 v4 o5 g/ |
- RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK+ v J: }* S, i, \0 h
- |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
& F8 K3 Q/ X: k, U0 y3 u8 P - RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
2 h6 _/ D) O: g6 f - RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;. Q6 X) R2 p) s% m
- RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;+ c# d. Z" [. h( p
- RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;: W, U* ]3 Y: `( U5 E
- HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);9 @1 {' y: J" K% |( c, s
7 M& k7 Z0 }7 G6 M5 {' V- PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;* ?6 I0 A9 ]) r4 k
- PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;$ U' Y0 h0 U+ x' K- t4 Q0 y
- HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit); A# K! c3 G4 s& x
% v( H) ^: K) |+ v( h r6 j* t- __SYSCFG_CLK_ENABLE();
$ j. B3 ]7 H0 N/ Q' ^8 D
2 _+ O+ G4 U& I- }
复制代码 * J4 Z- A/ \9 m3 M9 ?
注意:一开始程序中没有配置HSI时钟,采集不到电压
* c+ ]4 v+ z5 Q0 ^6 M& G" n1 i+ X8 N
2、ADC文件: K0 A7 j' f A& d. P2 C4 [8 z
8 J _0 |+ Y3 X `- I
- #include "main.h"1 Z& D H' G% z* e @+ [
- . O* V' T; T/ p2 j9 M
- ADC_HandleTypeDef hadc;/ I4 G3 g6 E9 X2 C9 b' R
- GPIO_InitTypeDef GPIO_InitStruct;# c, V/ |& k0 ~, r7 a
- ADC_ChannelConfTypeDef sConfig;
+ O2 H9 d. Q8 j$ \- d1 a - * p) I5 F4 U1 w2 `
, Q1 ^0 ?( d" l+ M0 W3 U7 ?% o, |) ~- //****************************************************************************** $ ~, C5 P$ p. T2 A* P* @ \, j
- //name: ADC_Init
. j* ~7 U5 ]$ J) G6 K - //introduce: ADC初始化 / S$ X, f7 \+ y; |% Q0 N
- //parameter: none / ]: v4 S) D) e
- //return: none
% u3 A9 O5 p7 u* q$ P5 u: u# U# J2 V7 n
- _" R: K$ S, a- //changetime: 2019.05.21
3 ~/ n6 m7 p$ [/ {8 r - //******************************************************************************. z! K4 @# O3 |) k8 f% y
- void ADC_Init(void)
8 z: P6 N! p3 @ - {4 x0 t! ]" s. M7 q1 ]! @
- uint32_t Calibration=0;* S3 y* d5 m1 Y, K' S
-
: Z4 ~8 A# h9 H0 W - __HAL_RCC_ADC1_CLK_ENABLE();
7 _1 \/ X! R# b/ y) O, G - __HAL_RCC_GPIOB_CLK_ENABLE();+ V/ ^7 q6 F- S+ @/ `0 {
-
% `% a9 k ^ E3 h5 ]1 J! I% M - GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3; \% r8 N; |# s( Y) T) a# _) @% `) J
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
" U/ F1 f8 \8 b% o" ? - GPIO_InitStruct.Pull = GPIO_NOPULL;5 g4 K" e+ d# ]) N: a1 L
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
( E' \" P4 A' l5 j/ e3 Y -
$ V' u/ t% a" `4 P0 q* p# T - hadc.Instance = ADC1;
3 \, c1 p. X$ Q) h+ N/ Z. u" W - hadc.Init.OversamplingMode = DISABLE;
* E3 x2 H% |, z, C( s1 L) h& U - hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
" O; n* d7 ~& h - hadc.Init.Resolution = ADC_RESOLUTION_12B;
) n; ^: W% n( W. b" S9 { - hadc.Init.SamplingTime = ADC_SAMPLETIME_79CYCLES_5; ?4 t R# F& G( T/ m) G* S9 h
- hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
& j. u# U" @* ?- ?2 ?6 q- ?9 _ - hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
5 r D0 x* K( Z% L% b - hadc.Init.ContinuousConvMode = DISABLE;
- V9 l- ?' a/ g9 T4 _0 z9 j4 i - hadc.Init.DiscontinuousConvMode = DISABLE;
& K! C2 Q; s9 Y! m) B ^( {( v - hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
9 N& A2 d& Q* S& a - hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;8 ]1 ~+ W+ G% P, t! J
- hadc.Init.DMAContinuousRequests = DISABLE;
8 h' c+ r( P- q( q, p# ^0 n" B - hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
1 N. k N# |: r* N0 M - hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
) _, p, q, V4 w - hadc.Init.LowPowerAutoWait = DISABLE;) H7 \8 A0 C; ?, e E) P
- hadc.Init.LowPowerFrequencyMode = DISABLE;
/ t Z9 w# u+ b4 { - hadc.Init.LowPowerAutoPowerOff = DISABLE;
: t# H/ T* a0 B, J0 s - HAL_ADC_Init(&hadc) ;/ g, ~, ?9 F8 Y. x9 V- r1 U+ ~; V
-
% @) q3 ~5 M/ v$ |$ Z/ a - //启动ADC校验功能
3 i, s$ A0 J5 ]* h/ t - HAL_ADCEx_Calibration_Start(&hadc,ADC_SINGLE_ENDED);
+ v" F! `$ ~3 e7 Y, E - //获取校验值' R& Y9 [0 D0 B- q$ X- s$ U# X
- Calibration = HAL_ADC_GetValue(&hadc);/ M: z. n# K6 b
- / T* |- Z# d+ i7 ^$ t k2 T5 E( U
- // 清除之前通道
3 _" j! _; E- u0 ]# I$ o+ O - sConfig.Channel = ADC_CHANNEL_1|ADC_CHANNEL_2|ADC_CHANNEL_3;
' T! V' L, s0 p* K4 ?4 D - sConfig.Rank = ADC_RANK_NONE; // 清除通道属性
! u& P& H1 C4 ]( c - HAL_ADC_ConfigChannel(&hadc, &sConfig);
4 z' m* K' C- [9 @% ]6 m1 e
0 g. ?% R$ r! ^9 F: V+ ~: t- }( g6 V8 Q9 ]4 p" b
- 2 V% Y" g8 S m& K
- //******************************************************************************
$ W; S" r4 ~, c* q - //name: GET_ADC
! ^9 k. w7 h- |: K8 I$ M - //introduce: 单通道采集ADC的值 ) e i2 o; [# e# ^; D/ t
- //parameter: CH:ADC采集通道
4 f6 c; Z5 ~& H8 i" n9 Y5 D8 [( T - //return: ADC采集值
& C1 ~. Y( Y& I6 y, a - 6 V8 f/ s! Z* Q7 G9 S. A
- //changetime: 2019.05.21
: q8 S, t+ p% }3 M+ I - //******************************************************************************% b! L9 ?5 k5 F
- uint32_t GET_ADC(uint32_t CH)
; J( g" k# U1 _& k. K+ `) p - {
$ B6 V. e, Y4 F; V8 W - uint32_t adc_conv_var;0 N* R+ F0 d8 v; g4 N
- sConfig.Channel = CH;# d( l0 ?( R8 P( l4 c; ]
- sConfig.Rank = ADC_RANK_CHANNEL_NUMBER; // 设置通道
1 X8 |& W# M4 F5 j: e* `& v- d% h6 o - HAL_ADC_ConfigChannel(&hadc, &sConfig);! V: T% h; k6 B+ O7 H
- ' Y( Y3 \8 m. P4 l
- // 启动转换
4 j6 q+ r F0 _! ?9 {. A: e% Z - HAL_ADC_Start(&hadc);
5 A- a! F; x# Y+ f - : A% p r( V( n5 U8 ?
- // 等待转换结束
! @5 l$ @4 n! [& Q7 [" B4 s x. { - HAL_ADC_PollForConversion(&hadc,20);// 超时20ms4 k" @* U# w, G, J! v
- ' b; i3 u B/ R1 Z5 n
- // 读取结果
0 P1 T$ e' y. k3 [, B - adc_conv_var = HAL_ADC_GetValue(&hadc);; N2 n- X# _$ S1 w
- 4 Y$ B$ N' {8 f; U$ Q7 m
- // 清除通道
" C6 M' Z- A: t: |! S3 P+ { - sConfig.Rank = ADC_RANK_NONE; // 清除通道( s% _% d0 A) O+ E9 J' j: U0 \ ^
- HAL_ADC_ConfigChannel(&hadc, &sConfig);9 {7 d2 J6 \$ l* q- ]0 {
-
: W" @5 |. @( N' K - return adc_conv_var;# U4 @2 s! l f( x6 [
- }
. J/ Z9 [& }5 F8 I$ i8 J% h' F
( ^6 x. k: r; }- //****************************************************************************** & T3 w! t1 J4 r/ H
- //name: Get_Adc_Average
( n- ? n& B. B3 Y$ g/ r) O+ B - //introduce: 多次采集求平均值 0 {" J! p% w1 L$ z2 v
- //parameter: CH:ADC采集通道
2 X; ]9 H! n' {& Q* f7 c1 y) Y - //return: ADC采集值 % M" \1 z; d `
- ( T' i8 O+ z/ S6 I7 p0 j
- //changetime: 2019.05.21 % e9 o5 j- b7 y% j: l [; }
- //******************************************************************************
/ ]. K0 _9 D* a8 A7 g - V2 R$ x0 b( `- R' U9 P! k
- void Get_Adc_Average(uint32_t *ch,uint32_t *adcx,uint8_t times)
6 C( s; F4 z& `1 j; s" \ - {
) @ \7 J) F: e! Q - uint32_t temp_val[9]={0};/ \, C/ J" U. j
- uint8_t t,i;
! d, K, u' I9 p, r2 L7 b - for(t=0;t<times;t++)
; {7 n; d& n. J# N8 D% l3 K6 h - {3 ?" V9 w5 L, S4 \' k
- for(i=0;i<9;i++)4 P( `! @- K i& O6 l9 r, Y0 c9 q
- {
; P! a3 h( X. l8 R, R - temp_val<i style="font-style: italic;">+=GET_ADC(ch);
: U2 s( R. v& v$ P - </i> HAL_Delay(5);
# k1 h) y1 n9 B1 z/ J - }
# K V) @0 t: F -
* J0 O: ?8 U5 n3 t - }
. E- \# v5 w9 b& U5 N. L1 {% U - 1 z1 _/ A, J K4 E8 {
- for(i=0;i<9;i++)
! ~4 F, T$ X1 r: d - {
) N3 ?* Y# S3 T5 D - adcx = temp_val/times;3 G X, ?7 ~* a: L+ M9 I. V2 a
- }
6 J5 n: g4 D& K8 b+ m! N0 }" z
5 d( H( W1 m3 t- } * |8 f/ |9 T. F# _7 s
复制代码 , q! n* x2 E$ e/ e; @" k
3、main中测试
+ V' L, ?' w% p% @/ y8 l- while(1)
6 M* D ~1 o B( N6 r - {
4 A+ p0 i# o" F0 ] - BATTER_VALUE = GET_ADC(ADC_CHANNEL_1);
, F U1 x! ~# T, F4 v - temp=(float)BATTER_VALUE*(3.3/4096)*2;
8 z& ~& _( b+ W9 \ - printf("temp=%0.2f\r\n",temp);
& b! b& @. {/ O( O/ R, i -
" _5 Q7 G M1 n) l, X- r - HAL_Delay(100);
1 e' o+ a- j+ p, _5 Z - }
复制代码 , ?# t8 ^, o m8 w% m! F; J
, _1 k% T' R4 L) U! ]1 ~
四、实验结果1 H9 N+ _, \; b1 ?
, h2 d9 w% w& F+ r* {由于ADC的精度是12bit(4096)、参考电压为3.3V,因此实际读出的电量值为BATTER_VALUE3.3/40962.2 R- m) |, y% ]+ I W
试验成功" w# I8 t* E6 k2 d$ I- _% P- M
k! G5 L: [" F) X% ^$ N3 z9 `! Q8 Q, {2 ]
7 r( p3 l% I6 U) g! F- D
|