说明:驱动基于STm32G031K6测试,其他型号需自行做改动。6 E6 O3 d: j: n! T3 b% q- R
9 Z8 g4 z! u& `5 A: P
ADC的初始化:
9 G8 @4 W1 h: t9 Y; ^: j& f% G% S1 S+ d2 j2 z$ W0 V/ F
- #define ADCIO1_IN_CHANNEL LL_ADC_CHANNEL_5
A7 t5 D) u; `0 C/ m7 e! T0 T0 u' w - #define ADCIO2_IN_CHANNEL LL_ADC_CHANNEL_7. _+ S. v7 {6 F" S' B' z7 g
- uint8_t STM32LLADC1Init(void): V$ R6 U. p% w6 z* B/ [
- {
2 [9 D/ a: ^" h. {( n& ~ - LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};2 w$ S& h/ o2 L; I( u6 k" R
- LL_ADC_InitTypeDef ADC_InitStruct = {0};: u) g p% f( q/ z+ Q
- ; H! b- q8 ]# J! E4 v9 D
- LL_GPIO_InitTypeDef GPIO_InitStruct = {0};+ t( m& Y% p+ K5 ?3 b0 p
H2 j1 H3 Q0 R% k- /* Peripheral clock enable */
% l% k( ?+ a1 _' l- C, }/ p - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC); //使能ADC时钟
, ?- |! w: }' y8 @3 a0 _
+ P0 W$ ?: E" F5 O- LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA); //谁能响应ADCioGpio时钟
) e7 B% D& {' d2 [. L5 v8 t - /**ADC1 GPIO Configuration - M9 |' f( i# T I7 M J" g
- PA5 ------> ADC1_IN5) M& x: s& E+ R+ f: z5 X
- PA7 ------> ADC1_IN7
$ V |, O5 f/ D - */
/ n) G T& F/ F+ [0 W& \* F3 C - GPIO_InitStruct.Pin = gpioADCIO1_IN_PIN; //ADC检测IO初始化. e; _. h- ~: @' L U( O k
- GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
0 F3 E, \3 s# a. a9 V" Q* T- V3 d9 B: i - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;- `6 r: R8 ~" j( m- }
- LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
) J) f ^/ N+ D+ I) |4 k, Q' s
( o0 g. d/ |, b/ N/ |- k- GPIO_InitStruct.Pin = gpioADCIO2_IN_PIN; z/ j. k, A% l# c4 f- Y
- GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
- N2 Y6 ~: f4 m" _0 y - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;5 J% p( d2 G* F' E9 E( K$ e3 G& q
- LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
" I ~& @( r1 b' g0 \# e0 ]2 y( C - + F% Q, b. P: ~5 z$ M4 o
% O0 }* c7 Y, A8 a% ]# ]- /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) , k; W+ w# W2 o3 `0 R
- */
! R& F' s% T% i. ] - ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE; //ADC定时转化触发条件来源,可以为软件触发或硬件(外部中断、定时器)触发。& P; A' ]- f: w2 @: k8 p4 Z" W
- ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_2RANKS; //ADC顺序采集通道的个数,根据自己需要的通道个数设置, o6 F4 _1 ~& ~2 B
- ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE; //
, \! t8 V0 A, P& { D2 ?, q - ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE; //采集方式,单次采集还是连续采集* d% B5 w5 \: Y' u
- ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED; //使能DMA,并使用无限传输,如果DMA保存方式为循环覆盖的话才可以使用无限传输。7 D5 Y t- k# M% X7 B
- ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN; //采集的数据循环覆盖模式
& z L( B2 `9 n7 y7 y' C - LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);8 u# O1 F6 ? w
- LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE); //失能过采样' a6 M' r7 r% Z" H
- LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH); //采样时钟使用高频模式
+ D2 S* v# d+ P3 M9 k" R3 ~ - LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_CONFIGURABLE); //adc通道和硬件adc in是否强制对应,因为adc的io使用时不是连续的,所以这里使用可自定义配置: Y# p; x' @& F" k
- LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_160CYCLES_5); //设置通道共用取样时间,根据需要自行选择
# D* d* ^5 Z4 L& A8 q5 I - LL_ADC_DisableIT_EOC(ADC1); //禁用通道采样结束中断
$ G( z2 g3 J3 g+ N - LL_ADC_DisableIT_EOS(ADC1); //禁用序列采样结束中断,因为使用的是单次软件触发所以这里关闭这些中断# {! ~3 Y7 Q1 p7 C% Q3 L
- ADC_InitStruct.Clock = LL_ADC_CLOCK_SYNC_PCLK_DIV2; //选择采样时钟来源
% K1 w" k, b' Z, C) @8 b0 t - ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B; //采样分辨率
& j: H' m' | |3 @4 y# K; _ - ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT; //采样数据对齐方式,右对齐高位补0,左对齐低位补0.
( c, [8 V/ @2 i& c5 O - ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE; //低功耗模式,使用DMA的话无法使用,这里关闭& v4 i1 }5 X" T9 {& w& d
- LL_ADC_Init(ADC1, &ADC_InitStruct);
' o- X2 |: K( B& R8 i - /** Configure Regular Channel $ ^8 g; b8 C& _' M$ X
- */
% y, _, ?) s5 U' {9 t - //ADC_RegularChannelConfig
3 d' _" `* G0 Q n( g# ] - LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, ADCIO1_IN_CHANNEL); //将硬件通道ADCIO1_IN_CHANNEL映射到ADC通道1% E" X9 e0 Z* M$ i
- LL_ADC_SetChannelSamplingTime(ADC1, ADCIO1_IN_CHANNEL, LL_ADC_SAMPLINGTIME_COMMON_1); //设置通道采样时间
w0 R4 o! o6 A! Q- J - LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_2, ADCIO2_IN_CHANNEL); //将硬件通道ADCIO1_IN_CHANNEL映射到ADC通道2
% {9 x0 G/ o4 U - LL_ADC_SetChannelSamplingTime(ADC1, ADCIO2_IN_CHANNEL, LL_ADC_SAMPLINGTIME_COMMON_1); //设置通道采样时间
, H& x3 N, ~* t/ Z' n( L - . X: k- d* z/ T* V, {
- /*采样数据转换时需要参考电压,参考电压可能是变化的,所以也有可能需要采集*/# {. e' q# m4 l6 g* f
- //LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_4, LL_ADC_CHANNEL_VREFINT);" `6 e6 A. w- @& A" k
- //LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_VREFINT, LL_ADC_SAMPLINGTIME_COMMON_1);2 {, G& h, q, V
- return 1;
& p2 |! c. R( k7 ^7 g- P - }
复制代码
" n4 J! q) m7 E. T8 F1 k激活并校准ADC:; F' l7 h- E1 ] P
5 t! ^8 R2 |* |' Z0 _. A5 F; Y- uint8_t STM32ActivateADC1(void)
& t7 k1 d% [) C k$ U - {9 j5 T& m* Y& }! K
- __IO uint32_t wait_loop_index = 0U;, `" ]% T9 e0 |: P: ~( m8 ^
- __IO uint32_t backup_setting_adc_dma_transfer = 0U;
4 C1 R+ O# F/ }* u! | - uint32_t Timeout = 0U; /* Variable used for timeout management */
2 J4 {, _6 `, O5 s! f - /*## Operation on ADC hierarchical scope: ADC instance #####################*/
3 r7 l0 \4 s& o
, @4 n7 E' L- H! O4 d7 y5 `% w- /* Note: Hardware constraint (refer to description of the functions */6 n$ @( V3 \" f0 C C5 ~) `& z
- /* below): */
0 i) o# ]% g- c' ] {- E$ R - /* On this STM32 serie, setting of these features is conditioned to */
8 L( g: _: ]1 M" R - /* ADC state: */
* X; x5 n E8 N% o4 F9 U - /* ADC must be disabled. */7 y" o2 z7 a9 E4 V: e4 G
- /* Note: In this example, all these checks are not necessary but are */
6 X6 E) |0 ]0 y: h4 l$ M - /* implemented anyway to show the best practice usages */
; U: Y7 w$ [% R - /* corresponding to reference manual procedure. */
/ Q& e: x5 r9 g5 P* C" N% \; p - /* Software can be optimized by removing some of these checks, if */
- s$ i4 }- Y1 ^. q) ^- x u$ j" ]6 t - /* they are not relevant considering previous settings and actions */# h& @0 n0 v1 F
- /* in user application. */4 g3 \. r2 P r y6 J6 ?
- if (LL_ADC_IsEnabled(ADC1) == 0)7 p7 A: K1 R- @. x
- {8 }$ D6 D& N6 E( U% f1 ^( W
- /* Enable ADC internal voltage regulator */
" G" m8 e' H% G$ F4 _% Y: i, t - LL_ADC_EnableInternalRegulator(ADC1);- _& Y" |" J! h( T' N) M
0 [) Q: _: {) J- /* Delay for ADC internal voltage regulator stabilization. */- z3 N. S+ z- `5 Y, O
- /* Compute number of CPU cycles to wait for, from delay in us. */6 \/ p$ \8 [: B0 z- I
- /* Note: Variable divided by 2 to compensate partially */
( g$ D% X- D9 M - /* CPU processing cycles (depends on compilation optimization). */+ |2 Q% H, q! L; D0 t# s" ~
- /* Note: If system core clock frequency is below 200kHz, wait time */
( e$ t" o8 e. o8 H# @! p: z$ | - /* is only a few CPU processing cycles. */
, ~. f' s7 b4 j; |* B - wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
) ^6 f8 ?4 ^7 L - while(wait_loop_index != 0)
# x! W; O, _) `3 } ]3 n6 c - {
% o) O/ d) w% V" C e3 |# G - wait_loop_index--;7 o# B5 C7 W* r V9 j; Q" Z
- }
& @. |8 K) {, L8 a1 c* G
" }) _1 F6 @; a4 ^, e! _- /* Disable ADC DMA transfer request during calibration */
! ]: e6 J: c7 @- M: m) R - /* Note: Specificity of this STM32 serie: Calibration factor is */
- f7 H: A3 P; O1 u- q" m* @( m1 V - /* available in data register and also transfered by DMA. */
) w M2 [, v# x, u+ R2 g - /* To not insert ADC calibration factor among ADC conversion data */1 ?/ |6 N/ c6 V0 Z
- /* in DMA destination address, DMA transfer must be disabled during */+ _! [$ F% Q+ p: s U3 p
- /* calibration. */6 j1 w8 s% `' S& x6 W' V0 q: H
- backup_setting_adc_dma_transfer = LL_ADC_REG_GetDMATransfer(ADC1);
+ J/ M4 B5 s, D/ S8 L4 |* ]4 @ - LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_NONE);
$ v( c6 j% A5 ]7 c - 7 r3 ]! o! w: n$ t$ W4 w
- /* Run ADC self calibration */
! r$ Z i" d1 H2 s, G J. |; L4 n - LL_ADC_StartCalibration(ADC1);
" f% ^3 A8 U) i - 3 X' G `6 l& E. C6 N) ^
- /* Poll for ADC effectively calibrated */
$ i& D& s T$ l- t - Timeout = ADC_CALIBRATION_TIMEOUT_MS;
6 @5 ~% U( `5 X# A) n8 A* G: q. q - # [0 |/ Q2 j4 ?& K" f' j
- while (LL_ADC_IsCalibrationOnGoing(ADC1) != 0)/ @; ~2 c! X7 ^* r
- {
9 g: y- e$ Z/ s1 G - /* Check Systick counter flag to decrement the time-out value */% u- m( x2 G0 ?
- if (LL_SYSTICK_IsActiveCounterFlag())3 ]( z k. Y4 \9 Z: M0 L1 u& M
- {4 @$ z: j0 g% b5 S6 ?
- if(Timeout-- == 0)' W, T4 e( i* m* {( F: S. G
- {
. X+ t& ~/ I5 O9 J* l - return 0;: e( Y# ]! r2 o" ~) I4 H! W; r
- }* w2 {+ i) o f/ \0 Q2 k8 [( S5 r: X
- }. F0 n" @+ g2 Z/ L) }# }, s
- }
; L0 L C8 \. y& O7 y& G3 s% v! c
* b$ L' Q. X# L8 [8 J& _ O- /* Restore ADC DMA transfer request after calibration */; a5 s# V* p% i$ }/ l; s- [; M
- LL_ADC_REG_SetDMATransfer(ADC1, backup_setting_adc_dma_transfer);
: e8 G9 M, B3 L r8 R9 w- O+ q - # @; N! b- ~9 m
- /* Delay between ADC end of calibration and ADC enable. */
" C$ E8 p7 c7 g5 A0 c - /* Note: Variable divided by 2 to compensate partially */0 p+ T; e7 ^$ V: z1 W& U
- /* CPU processing cycles (depends on compilation optimization). */0 D6 V& J9 v6 k
- wait_loop_index = (ADC_DELAY_CALIB_ENABLE_CPU_CYCLES >> 1);' G7 o: f [! l/ Q8 S
- while(wait_loop_index != 0)
; x5 C( @. ^# e/ v) d8 i. X - {
* N7 ]/ z5 h4 U" `% J - wait_loop_index--;, ]4 `% W! O# `% u( f2 Y
- }+ T$ o1 i& P, n
, W# J/ n4 j$ J8 v% R' L. R0 U+ p- /* Enable ADC */
' K6 Q0 m, ~& h0 q - LL_ADC_Enable(ADC1);
# Y) l0 H! m% W" {) u1 |
0 \/ u5 @7 u9 {: E; x/ V. I- /* Poll for ADC ready to convert */1 N& d% |4 J6 A# f
- Timeout = ADC_ENABLE_TIMEOUT_MS;
. \# k/ a4 p! H& @# |/ n. N9 K
5 N- y( U4 G+ d+ ]6 T$ u3 f- while (LL_ADC_IsActiveFlag_ADRDY(ADC1) == 0)
9 o9 Y& f3 T% m: H! r; a5 y, D - {
2 A% n8 Z; U0 B" H3 u( _+ B/ O - /* Check Systick counter flag to decrement the time-out value */( V/ n, `, r u5 b. } w
- if (LL_SYSTICK_IsActiveCounterFlag())
& Z* _0 w+ n' y: t - {9 x s5 o& S% k' V
- if(Timeout-- == 0)( w T" Q8 O' d3 {9 z
- {# c) M$ _3 x8 P Y; |9 B! S
- return 0;% s a: s: H, `1 s6 k% q: A2 P
- }. L! ^$ L2 R+ H9 ]
- }
! X, F O4 E% Y/ M; c - }% g" t8 b5 d2 j+ Y- ^( W8 h
- /* Note: ADC flag ADRDY is not cleared here to be able to check ADC */
! i) a9 C) P; c - /* status afterwards. */
' P; B0 y7 L" b0 ?" U X - /* This flag should be cleared at ADC Deactivation, before a new */
4 j" d' ]+ L. U1 W - /* ADC activation, using function "LL_ADC_ClearFlag_ADRDY()". */: r a+ T ~9 y4 `
- }
+ L8 T, S; w- h% U5 [2 G - return 1;: X: ~, p2 H q
- }
复制代码 : L! t- i4 w8 ?" n: k$ @8 H
因为ADC配置的是软件触发的单次采样,所以还需要有手动开始和结束采样的函数。( A0 A) o! b, k3 q0 W
4 d9 H) i- i: ~1 {, N2 D. l8 a: o
使能ADC采样:
+ U2 ~0 g; W- Y& ~
5 u8 j+ J, e+ [: A- uint8_t STM32ADC1SampleEnable(void)$ E1 d7 ?5 C# H. u
- {
1 C( d- ^7 S7 N7 C - uint32_t Timeout = ADC_ENABLE_TIMEOUT_MS; 3 a) V; J$ Z# o( o& L0 Q, U& x
- LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_4); //先使能dma再使能adc,以防采样数据丢失; c7 G `; K. V; n+ S3 G& c/ l
- LL_ADC_Enable(ADC1);- U! z" |. N7 u
- Timeout = ADC_ENABLE_TIMEOUT_MS;2 T$ [! ]% F$ z" x: W1 K* v" n" D
- while(LL_ADC_IsActiveFlag_ADRDY(ADC1) != SET)! i; N* g0 B }7 `! X
- {
3 o3 u3 N9 h% z r - if(Timeout-- == 0)
) p( h. s- t ?$ y$ [1 | - {' B& z/ x/ h- ^( J; l
- LOG(1,"STM32ADC1SampleEnable ERROR!!!");
7 X8 F- p9 L( c% v% N } - return 0;: W B1 D4 s) N c( f' X9 a
- }
P7 G$ r$ l3 R( x - }6 k0 y9 l' a( w$ ?
- LL_ADC_REG_StartConversion(ADC1);
+ s/ Q: O5 I! H! \4 z/ t6 B - return 1;
6 t0 s0 E1 Y( P1 W - }
复制代码
( [! B( `' y: G& f2 h6 A' A失能ADC采样:
( U9 w! l/ Q) U2 Z
/ O) r. g3 ?4 o5 \" {- uint8_t STM32ADC1SampleDisable(void) }& [/ `7 `! ?" H- g" }* {9 z# p
- {- J4 V a; R( l
- uint32_t Timeout = ADC_DISABLE_TIMEOUT_MS; ; l M) a5 X9 R2 d/ y( Y- v
- LL_ADC_REG_StopConversion(ADC1);
% M! F: g8 q; G& V - while(LL_ADC_REG_IsConversionOngoing(ADC1) != 0)
# V- Q, X: L- @7 A+ G# c& p: x5 k - {
- y6 ?4 c( x7 U3 @( c - if(Timeout-- == 0)
4 N" b& e0 k" p" q/ F7 M: T - {
4 h7 A4 b2 S* T v - LOG(1,"STM32ADC1SampleDisenable ERROR!!!");
9 A! N; h* B' _, S - return 0;7 m% d0 z& E/ C8 H+ _
- }
u" ^7 K( @( c( M* r" U - }# }! V2 L6 c; h0 h% w( J- A7 _
- LL_ADC_Disable(ADC1); //先关闭adc再断开dma,以防采样数据丢失! L; u/ S' n0 e6 f3 `1 U: C& ^% e: Y
- LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_4);
' O* ^) { Q# F) u/ G0 c - return 1;1 F- ]% Y% y, s' c5 Y, x
- }
复制代码
. p" b. B F2 X1 C" ?" a进行ADC数据采集流程(采用多次采样求平均值的方式):* w- g6 [# s/ S' ?/ M
9 I9 N, e/ K" s( `
1、当需要开始采样前,调用一次STM32ADC1SampleEnable()函数; _0 ~% u0 |) O% k8 P$ `- [6 ^- |
# O! \1 t+ O1 b! N) j
2、在一个定时函数内调用LL_ADC_REG_StartConversion(ADC1)进行数据的转换(因为使用的是单次采样,所以每次进入定时函数时都需要调用),取出DMA数据进行累加;
8 P, [8 j% K( d- r& m2 W5 g$ \
0 p+ g5 Y' W& O/ E3、达到需要的采样次数之后,对得到的数据进行运算,并调用STM32ADC1SampleDisable()函数关闭ADC通道。
' @8 B/ l) G% K$ T6 A9 d7 ^/ P8 a& o( [7 ]5 q# |% @: V
以上方式适用于需要间隔一段时间获取一次ADC数据时使用,例如需要每隔1分钟获取电池电压时,可以进行1分钟倒计时,然后连续进行十次间隔10ms的ADC采样求平均值。) }/ \5 Z* G7 M7 K
0 s* i9 D& d! b# m3 ]! j
0 N/ w9 e7 }' n; m" C/ s) R! E( k, u |