说明:驱动基于STm32G031K6测试,其他型号需自行做改动。
* _+ d* y; x# c3 A+ \* L5 X! ^9 z( |2 w2 U% q% M
ADC的初始化:
( v0 l, J' P0 m& S8 ~/ ~& g: t7 b! [% l9 f; Y. a6 A* f2 U
- #define ADCIO1_IN_CHANNEL LL_ADC_CHANNEL_5- P/ S2 S& d8 O: Z9 v4 R6 L7 R
- #define ADCIO2_IN_CHANNEL LL_ADC_CHANNEL_7* o$ O: P, I7 O; d% U* I$ Z( |, W9 b
- uint8_t STM32LLADC1Init(void)! I. n0 J5 h2 z+ [1 j
- {% S- o. E2 D( y3 C
- LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
0 H( j- z3 N( S" \. w - LL_ADC_InitTypeDef ADC_InitStruct = {0};
& A f0 J* r2 U! j' H& ? - ! o V3 k" S1 {4 J+ A
- LL_GPIO_InitTypeDef GPIO_InitStruct = {0};+ k8 N' I4 {! D6 f& J
- ! x3 S- b* \2 `" S: |
- /* Peripheral clock enable */
) U! H) G" Z6 I2 z! R* G' s - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC); //使能ADC时钟& I! ]) R8 B4 t& `/ U; Y( Q; d
- 1 \* o7 v5 S" v. {0 s* @# n- O( D
- LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA); //谁能响应ADCioGpio时钟
. W! c" e8 l+ e1 Y9 _) \ - /**ADC1 GPIO Configuration 8 o8 n. N7 t6 }1 i
- PA5 ------> ADC1_IN59 {9 I) _6 L3 L" y$ k; F! f# i
- PA7 ------> ADC1_IN7 " e' G* _# Z* z3 q, V
- */2 Y1 l/ \) i; `9 E7 o6 u" I1 ?
- GPIO_InitStruct.Pin = gpioADCIO1_IN_PIN; //ADC检测IO初始化: W6 w8 U& z- |5 C3 s
- GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;4 ^0 D2 q& X8 c
- GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
4 o w* `2 Z# k- v - LL_GPIO_Init(GPIOA, &GPIO_InitStruct);, \5 A& a) N5 \1 O
* b+ N- I4 N% o7 h6 D: K, K% _- GPIO_InitStruct.Pin = gpioADCIO2_IN_PIN;- |1 q& w1 G9 S% X/ W7 G
- GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
$ v* q' C+ `; P1 g - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
# `' ?; a# K, G% B& Z! U - LL_GPIO_Init(GPIOA, &GPIO_InitStruct);* l2 d7 H9 f! I% `
" v; S' m0 h+ U4 d0 J0 |- V9 g3 [
; ?+ j8 w) Y4 X/ L- /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) % e5 L2 m5 H& @
- */
. D1 _1 r. F9 `, B - ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE; //ADC定时转化触发条件来源,可以为软件触发或硬件(外部中断、定时器)触发。
! ^3 B e3 c/ Z* N" \+ d4 y - ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_2RANKS; //ADC顺序采集通道的个数,根据自己需要的通道个数设置! d0 \6 e% l$ j
- ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE; //
( R1 x0 n' p; K5 a+ ^: O4 G - ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE; //采集方式,单次采集还是连续采集$ J! \2 r H6 b+ n- d/ e
- ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED; //使能DMA,并使用无限传输,如果DMA保存方式为循环覆盖的话才可以使用无限传输。$ |& v) S# w# C }6 B. r5 B
- ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN; //采集的数据循环覆盖模式
; P2 ~( F4 @/ G) O. J8 W8 I - LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);9 C$ Z$ f) H8 W
- LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE); //失能过采样0 x# C& L, I6 L, f% {
- LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH); //采样时钟使用高频模式
. X7 }& n u8 b- z( h$ X - LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_CONFIGURABLE); //adc通道和硬件adc in是否强制对应,因为adc的io使用时不是连续的,所以这里使用可自定义配置
" G% K4 C) {% {5 F1 F - LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_160CYCLES_5); //设置通道共用取样时间,根据需要自行选择: b, ~, U' b8 n4 d C6 C
- LL_ADC_DisableIT_EOC(ADC1); //禁用通道采样结束中断: V' r& H0 Y) q$ A& q. P
- LL_ADC_DisableIT_EOS(ADC1); //禁用序列采样结束中断,因为使用的是单次软件触发所以这里关闭这些中断
' k$ ~! k4 N% b; \6 d - ADC_InitStruct.Clock = LL_ADC_CLOCK_SYNC_PCLK_DIV2; //选择采样时钟来源
7 m8 Q* y& H4 ` - ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B; //采样分辨率' S) w' U# \( y; }6 g
- ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT; //采样数据对齐方式,右对齐高位补0,左对齐低位补0.& L$ B$ A. s" O3 l
- ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE; //低功耗模式,使用DMA的话无法使用,这里关闭
( _1 A7 R0 c( P+ |4 _7 |; }& D& d - LL_ADC_Init(ADC1, &ADC_InitStruct);
5 I7 j3 ?5 t6 \) F5 U - /** Configure Regular Channel 9 C# U' Z; I5 j; u
- */
9 |$ Y: i& Q q. x+ k0 i - //ADC_RegularChannelConfig
9 H c3 Y+ J4 a8 } - LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, ADCIO1_IN_CHANNEL); //将硬件通道ADCIO1_IN_CHANNEL映射到ADC通道14 C: p* `$ t; L6 l; w/ x
- LL_ADC_SetChannelSamplingTime(ADC1, ADCIO1_IN_CHANNEL, LL_ADC_SAMPLINGTIME_COMMON_1); //设置通道采样时间! ]' t5 r" i3 R2 t- x* g6 ]
- LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_2, ADCIO2_IN_CHANNEL); //将硬件通道ADCIO1_IN_CHANNEL映射到ADC通道24 K7 D, d5 w! e# r
- LL_ADC_SetChannelSamplingTime(ADC1, ADCIO2_IN_CHANNEL, LL_ADC_SAMPLINGTIME_COMMON_1); //设置通道采样时间
' J3 i* d; [2 v i% I! E2 U
* `* i& l2 B% n) ~! x% E- /*采样数据转换时需要参考电压,参考电压可能是变化的,所以也有可能需要采集*/9 v9 l& j+ t b. U
- //LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_4, LL_ADC_CHANNEL_VREFINT);
# w0 _5 r8 m! d) ? - //LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_VREFINT, LL_ADC_SAMPLINGTIME_COMMON_1);
+ J9 I4 R; l$ ]# @" f - return 1;; v6 M2 n' q6 \% B1 K- K- X7 }6 p
- }
复制代码 : q R$ A. g! b* K2 |
激活并校准ADC:( G3 c! O/ j5 V- P$ I. k! k
9 b. q9 l, Y' |; `- uint8_t STM32ActivateADC1(void)* B2 u/ W' G h1 W+ y( F. G" X
- {& w) G1 {7 J* G8 H# g9 d6 u. }
- __IO uint32_t wait_loop_index = 0U;5 b; Q- m" V5 F% T( S1 a8 T
- __IO uint32_t backup_setting_adc_dma_transfer = 0U;
+ C* A I3 q' v# z) E/ X7 I - uint32_t Timeout = 0U; /* Variable used for timeout management */
- S. l s3 B3 o3 }: A - /*## Operation on ADC hierarchical scope: ADC instance #####################*/5 r% U7 A' a" N* _0 K0 r
- 3 g. x V& T1 _' v
- /* Note: Hardware constraint (refer to description of the functions */* R4 ~; \/ t; t' j2 \
- /* below): */
* k: V( d9 y. q - /* On this STM32 serie, setting of these features is conditioned to */& \& t3 _; |, c* f' z
- /* ADC state: */& e* o* h) S7 j! R
- /* ADC must be disabled. */
( L* g0 t1 G* V3 K1 G$ z d, n; i a - /* Note: In this example, all these checks are not necessary but are */* n. N8 n1 ^3 N. y; @' m
- /* implemented anyway to show the best practice usages */
- @# U1 G- w- y. c. b - /* corresponding to reference manual procedure. */" C; k* @) l( r$ o
- /* Software can be optimized by removing some of these checks, if */
* f+ a4 C7 R: a: L1 j/ F - /* they are not relevant considering previous settings and actions */
/ h- ]! \6 c, m6 a/ T: v - /* in user application. */, c$ ?5 _1 v. D4 J1 {/ ]2 `4 u& \
- if (LL_ADC_IsEnabled(ADC1) == 0)
. J" K- j/ A# ]5 t9 O - {1 ?- A. e3 e) p5 }9 ]: v
- /* Enable ADC internal voltage regulator */
3 ?; A7 I% e& M6 W3 y - LL_ADC_EnableInternalRegulator(ADC1);
) z7 r Q1 h- N. L6 u! W - + f0 N. ~& Q8 h9 J
- /* Delay for ADC internal voltage regulator stabilization. */+ G: G. ^- }9 e5 w
- /* Compute number of CPU cycles to wait for, from delay in us. */1 m: [# C+ F9 h4 F8 I2 m
- /* Note: Variable divided by 2 to compensate partially */
, ]. Y; N- T3 o3 D0 L1 G. m - /* CPU processing cycles (depends on compilation optimization). */
( H1 Z" L8 S- |9 s9 ] - /* Note: If system core clock frequency is below 200kHz, wait time */. ]6 I' t7 b& r% k8 } f0 n
- /* is only a few CPU processing cycles. */( g1 b: }8 f5 R( E
- wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);" t& G( K }( Z9 y+ a5 o
- while(wait_loop_index != 0) T8 o% p4 L8 w f% q) M
- {8 W, r( x' P0 i" p4 G k
- wait_loop_index--;
: m3 F: R! \) s% e0 m- g( J9 P* h2 Q - }- [1 d: m2 ?9 i+ K0 J6 s5 P
- ) G& |3 } Z* P3 z+ Z2 E; }6 Y/ |
- /* Disable ADC DMA transfer request during calibration */2 N" H }6 o, h, e6 X) }0 Y4 p' z) l
- /* Note: Specificity of this STM32 serie: Calibration factor is */
5 q. k2 ^7 x; x' c8 P6 z - /* available in data register and also transfered by DMA. */
! w, K' y& k' l6 B- g8 Z- H- ^ - /* To not insert ADC calibration factor among ADC conversion data */
7 v$ S7 Z/ A' M: A7 f& c - /* in DMA destination address, DMA transfer must be disabled during */
4 R4 J. @7 f v- P* I" n7 K, s3 O+ j - /* calibration. */
, X- R% Q1 {7 i; Q* e5 M - backup_setting_adc_dma_transfer = LL_ADC_REG_GetDMATransfer(ADC1);
, V( |8 w x7 S W3 x9 x& z - LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_NONE);/ ]$ M7 M1 ^7 k0 I
- 5 g' j. |9 ` a3 d
- /* Run ADC self calibration */: c* V. K2 }2 R9 B
- LL_ADC_StartCalibration(ADC1);
8 o' j; f1 V% ~, \- u
9 B) i' l4 Y) f2 J- /* Poll for ADC effectively calibrated */, ?0 ^1 N7 X, I7 ]# ]* r
- Timeout = ADC_CALIBRATION_TIMEOUT_MS;
; U! c; a$ X7 B! a - 1 a9 Z3 d' g2 B" r* u: r' b+ U
- while (LL_ADC_IsCalibrationOnGoing(ADC1) != 0): B3 y w& Q$ C# W0 |- w8 B
- {# W2 {6 e, B& a7 E. P" ?
- /* Check Systick counter flag to decrement the time-out value */
8 q: d8 L8 V m. q. X - if (LL_SYSTICK_IsActiveCounterFlag())
# b# M j& O8 B2 ?% k5 O9 {% @ - {; F5 c6 d7 v" ]; J8 k, U" w
- if(Timeout-- == 0)9 n) b6 a* {* Y3 w* ~1 `: T* K
- {' A4 I/ W r$ ^# g, W9 W' |1 ^
- return 0;
# f* A4 e- D: k) c( Y1 L- c% {( o - }: s1 A1 O3 t6 O& v- D- k. l
- }
1 v1 E4 O, i# ` - }! N$ `% S, q1 O' G6 [9 P' d
- [+ \& l0 v8 [* v
- /* Restore ADC DMA transfer request after calibration */2 J9 g' y+ V- H5 g6 s3 o# ]
- LL_ADC_REG_SetDMATransfer(ADC1, backup_setting_adc_dma_transfer);
3 q' A* o; i7 A
. s$ r4 H, O- j- /* Delay between ADC end of calibration and ADC enable. */6 u5 \0 a& W- \1 }" k
- /* Note: Variable divided by 2 to compensate partially */; y7 T) {/ e' q
- /* CPU processing cycles (depends on compilation optimization). */5 }9 p9 V. z! P" e4 Z7 ]* L5 V
- wait_loop_index = (ADC_DELAY_CALIB_ENABLE_CPU_CYCLES >> 1);
0 i5 P+ g9 R0 @+ \5 N - while(wait_loop_index != 0)" @( y9 h5 e. P+ J" T
- {% `# b/ ]/ t9 N M! `9 N
- wait_loop_index--;* U! @# S5 ?9 F0 E& {& [/ l
- }( c1 O) s2 ?2 \6 G9 d
) Y- A$ n' n0 r0 w5 l- /* Enable ADC */
0 q* L$ f6 c1 s3 i - LL_ADC_Enable(ADC1);7 n) b! X, E& h: Z! U1 q
- . c: Q9 q$ R/ Z
- /* Poll for ADC ready to convert */
) Y" V4 `& ?0 q O# V* x; V - Timeout = ADC_ENABLE_TIMEOUT_MS;
( g/ p) C( T L8 C - & ^. U x% v: N3 z) ?% G, A3 g
- while (LL_ADC_IsActiveFlag_ADRDY(ADC1) == 0)$ C; ?) d6 W" N+ t; A; g% N
- {7 y B# D& t& W; v" J; K% u% |! w- G
- /* Check Systick counter flag to decrement the time-out value */
2 w: A* P) d0 [ - if (LL_SYSTICK_IsActiveCounterFlag())! S/ X$ x) v$ d6 z8 L
- {/ _ v: p* ]8 O k: |, e
- if(Timeout-- == 0)/ V/ V( ]( \) t% L. p3 Z4 E
- {
! r1 e3 s$ R/ U7 t7 B) ` - return 0;
& \" \# i7 }( I# u, d - }
2 N/ @# G: w3 ]3 Q - }" M: o, n# E' A) i2 O& q1 w
- }
[8 I, u3 f( ~2 g( N- l- w6 \ - /* Note: ADC flag ADRDY is not cleared here to be able to check ADC *// p. |/ \ O8 a8 }
- /* status afterwards. */. F# m$ i" t# W) U* Q
- /* This flag should be cleared at ADC Deactivation, before a new */! j# Y" ^7 I/ O1 G4 H% _
- /* ADC activation, using function "LL_ADC_ClearFlag_ADRDY()". */
/ L1 _3 O3 D1 P7 d - }
* z6 D* I( ], l% g( T% g% n! R - return 1;
, v, O* p: X- P4 R6 x, v; _ - }
复制代码
7 K5 w; p7 E8 J: L6 c因为ADC配置的是软件触发的单次采样,所以还需要有手动开始和结束采样的函数。/ b9 g3 o) Q+ K1 R. ~
& c. H# d! n% a+ d% C g! s4 W5 L# V
使能ADC采样:* e- N$ b6 u# [# C2 L# E
% O4 q0 ^6 z3 Q6 o* U8 j: \9 ]2 a- uint8_t STM32ADC1SampleEnable(void)$ ~9 Y1 x2 e* Y
- {, {( a: T- T/ k; @! c+ G& { ]
- uint32_t Timeout = ADC_ENABLE_TIMEOUT_MS; 7 J6 r2 O" a4 t: X9 M. K
- LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_4); //先使能dma再使能adc,以防采样数据丢失
" O0 R* j# _5 V- p O; [7 F - LL_ADC_Enable(ADC1);
2 _* W( B! L. y& b9 e! k# p8 {7 f - Timeout = ADC_ENABLE_TIMEOUT_MS;. x/ l- J8 K9 |& G1 R _4 O# w- |9 r
- while(LL_ADC_IsActiveFlag_ADRDY(ADC1) != SET)
' m+ ?2 i+ D) H: M$ v - {
z+ ]0 S5 b- N/ {0 x - if(Timeout-- == 0)
9 C; V, t" J' q( f; a V+ H7 z - {! X6 `. A( `! C' v1 w
- LOG(1,"STM32ADC1SampleEnable ERROR!!!");! ~# |3 F$ n& x; R/ Q5 w
- return 0;: O( y/ i# H; @: Y2 q* |
- }1 w7 R6 M- x6 ] G, D7 F( \
- }
1 q6 E; E% P9 ] - LL_ADC_REG_StartConversion(ADC1);) ^2 Q3 d3 X' E) v2 ?3 k4 X5 I
- return 1;
: r: Y! _+ E e5 Q* _; Y$ D" r - }
复制代码 - I+ j" X4 l; \1 t1 u
失能ADC采样:
- W7 ?: h! N7 b. e+ ^6 `% l
5 y9 {0 f4 ^$ N7 V! l4 b4 K0 ^- uint8_t STM32ADC1SampleDisable(void)4 V1 E F8 K3 A
- {
5 k: o6 N* O' D: T% x" a l% C - uint32_t Timeout = ADC_DISABLE_TIMEOUT_MS; : d* z1 P6 f( q0 E! H! p* t
- LL_ADC_REG_StopConversion(ADC1);2 F! X, g" K8 B, e
- while(LL_ADC_REG_IsConversionOngoing(ADC1) != 0)( m: y3 y) U" n6 C8 T
- {
4 K9 B5 f1 X; M& b. I - if(Timeout-- == 0)
( D* }4 [. ]8 K: x7 e: @% O - {# Q+ \4 |* P& U- \/ \6 N4 U
- LOG(1,"STM32ADC1SampleDisenable ERROR!!!");/ @& m7 C' [1 j1 l
- return 0;
) ?" V/ U; O" w$ [- B0 z3 P. { - }
V+ S2 C' n, x$ r, e4 J$ [3 }( P' i - }! S$ E4 i$ q. u) V4 F. C
- LL_ADC_Disable(ADC1); //先关闭adc再断开dma,以防采样数据丢失8 E- u9 C V# Z$ F: b/ T {& S
- LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_4);
# \5 G. { ?' @# r3 \+ i - return 1;
! c- V2 X, z0 r - }
复制代码
! a* \# f: S2 ?3 j0 \进行ADC数据采集流程(采用多次采样求平均值的方式):
/ K. I9 y+ i9 O+ _- T
1 z/ s3 e% C7 }& O7 C1、当需要开始采样前,调用一次STM32ADC1SampleEnable()函数;
4 l% p# k& }1 P+ j7 u Z
0 |* o' P& c9 }2 {8 q! ^% ]: s0 r2、在一个定时函数内调用LL_ADC_REG_StartConversion(ADC1)进行数据的转换(因为使用的是单次采样,所以每次进入定时函数时都需要调用),取出DMA数据进行累加;. l; J* e; O2 H
" ^. C& @9 h. P" h3、达到需要的采样次数之后,对得到的数据进行运算,并调用STM32ADC1SampleDisable()函数关闭ADC通道。
% X9 q$ `5 N! v+ M; n* D7 A) B* y
& h! D& ^0 J! R" \1 N. s, Y6 N以上方式适用于需要间隔一段时间获取一次ADC数据时使用,例如需要每隔1分钟获取电池电压时,可以进行1分钟倒计时,然后连续进行十次间隔10ms的ADC采样求平均值。/ w: F) {4 v2 v$ {& x0 B j
4 x h4 o% P/ _% P% @
* R9 T) X; x$ g# R3 n& _* o |