说明:驱动基于STm32G031K6测试,其他型号需自行做改动。
+ Y: ?, ^" t+ X& i; M; O4 i+ I# z8 [( e0 c4 z" L
ADC的初始化:
* m$ O/ n4 p" y
' }* c8 k) T; n/ v0 u) S- #define ADCIO1_IN_CHANNEL LL_ADC_CHANNEL_5/ w5 m# W" X; c# e8 Z' i7 k
- #define ADCIO2_IN_CHANNEL LL_ADC_CHANNEL_7
8 F* T1 p* e/ A& K. A - uint8_t STM32LLADC1Init(void)
0 m- z% i( U1 y: P - {0 c" V% i& j& z8 c6 g
- LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
2 f8 P- c5 ~ ^4 w - LL_ADC_InitTypeDef ADC_InitStruct = {0};$ I5 d9 |6 X2 \9 L
- - T$ y: ^ ?/ M' t% z
- LL_GPIO_InitTypeDef GPIO_InitStruct = {0};$ [: A, K9 N0 b. q& j: j. r( m
+ k6 [- X( E% G% p! N' s5 ]4 u5 f- /* Peripheral clock enable */% u: F* t/ e/ G- B T
- LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC); //使能ADC时钟
- k5 I$ f" k! u - 0 C0 l4 ~0 `8 u- R& Y9 ^6 Q
- LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA); //谁能响应ADCioGpio时钟8 B7 ]/ ? Y7 l+ o% B
- /**ADC1 GPIO Configuration ! t* w) T4 W6 A, P# j! v
- PA5 ------> ADC1_IN5( q+ Y Q) K1 M/ { _) ^; ]
- PA7 ------> ADC1_IN7 9 D5 F. a, p' a! H
- */
# B7 W I, e+ a - GPIO_InitStruct.Pin = gpioADCIO1_IN_PIN; //ADC检测IO初始化- r6 ]1 E$ K3 n$ D# o- g$ M) ^. B" S8 e
- GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
' N$ h2 _6 l: n: z/ F6 s! L- E - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;* f) V+ s' Q2 E$ s. l6 I
- LL_GPIO_Init(GPIOA, &GPIO_InitStruct);- s! o9 v+ f/ U, [8 v; w% L& L$ g& @) Q
- 3 L) p! T. X" D" i# ?3 C- A
- GPIO_InitStruct.Pin = gpioADCIO2_IN_PIN;
6 k6 ~) I- x0 U$ V! d/ c - GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
2 b$ L# X4 _/ Q7 h( ~- X Z - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;+ o/ T7 l5 r6 B" s C
- LL_GPIO_Init(GPIOA, &GPIO_InitStruct);# e _6 Z2 y$ A- ^7 e `
0 g- V5 n. V! p# `6 \- + R4 Q; k" G7 t8 F2 Y9 M% E
- /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
6 `0 F9 x3 @9 x) L - */
6 H% q. p/ G' K7 U" g/ G) Z( I d# ? - ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE; //ADC定时转化触发条件来源,可以为软件触发或硬件(外部中断、定时器)触发。1 }/ H8 f5 E0 i* v. }
- ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_2RANKS; //ADC顺序采集通道的个数,根据自己需要的通道个数设置
: M9 {6 I6 A$ u( u1 ^3 k - ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE; //7 r2 b" r: r& I2 o
- ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE; //采集方式,单次采集还是连续采集/ C& V- H% O9 o; z: q+ d, n; s% @
- ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_UNLIMITED; //使能DMA,并使用无限传输,如果DMA保存方式为循环覆盖的话才可以使用无限传输。. T ~+ T2 [( S
- ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_OVERWRITTEN; //采集的数据循环覆盖模式. s0 N+ n. z y6 \% Y0 ]/ h5 P
- LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);6 [) ^9 a- Z/ E) V
- LL_ADC_SetOverSamplingScope(ADC1, LL_ADC_OVS_DISABLE); //失能过采样
8 A2 M" n8 g2 Y% M! f - LL_ADC_SetTriggerFrequencyMode(ADC1, LL_ADC_CLOCK_FREQ_MODE_HIGH); //采样时钟使用高频模式' \0 c; ~1 r$ _1 Z0 f O% ? S S
- LL_ADC_REG_SetSequencerConfigurable(ADC1, LL_ADC_REG_SEQ_CONFIGURABLE); //adc通道和硬件adc in是否强制对应,因为adc的io使用时不是连续的,所以这里使用可自定义配置' ^ P6 W3 v9 x; S
- LL_ADC_SetSamplingTimeCommonChannels(ADC1, LL_ADC_SAMPLINGTIME_COMMON_1, LL_ADC_SAMPLINGTIME_160CYCLES_5); //设置通道共用取样时间,根据需要自行选择
3 r; I$ q" {# r* D - LL_ADC_DisableIT_EOC(ADC1); //禁用通道采样结束中断! E- M& x* R- D* N x/ _8 _; ?
- LL_ADC_DisableIT_EOS(ADC1); //禁用序列采样结束中断,因为使用的是单次软件触发所以这里关闭这些中断
* z' _/ g' e* f' p; K- V3 h - ADC_InitStruct.Clock = LL_ADC_CLOCK_SYNC_PCLK_DIV2; //选择采样时钟来源
* b; X( Y& x8 i7 C; k/ ~ - ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B; //采样分辨率
1 p" Q: E) x3 r) w7 b2 e+ ?) W - ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT; //采样数据对齐方式,右对齐高位补0,左对齐低位补0.
, v x; ~ O h: ]) X - ADC_InitStruct.LowPowerMode = LL_ADC_LP_MODE_NONE; //低功耗模式,使用DMA的话无法使用,这里关闭
' A" k, N- B& {# M - LL_ADC_Init(ADC1, &ADC_InitStruct);
$ R+ h: \& p# y& \, o; ]5 \( Z - /** Configure Regular Channel
G/ B4 z) h' [7 R$ T; _5 C - */
" k$ c. F5 V) o! k( a. X - //ADC_RegularChannelConfig
$ G" X3 n3 v& ^+ R4 O9 M4 {* b& W/ _0 M - LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, ADCIO1_IN_CHANNEL); //将硬件通道ADCIO1_IN_CHANNEL映射到ADC通道1+ I! u7 H. }: E& z8 Y6 H
- LL_ADC_SetChannelSamplingTime(ADC1, ADCIO1_IN_CHANNEL, LL_ADC_SAMPLINGTIME_COMMON_1); //设置通道采样时间" M. y7 B+ }2 O! B. k6 y* H0 G7 R
- LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_2, ADCIO2_IN_CHANNEL); //将硬件通道ADCIO1_IN_CHANNEL映射到ADC通道2
+ i# Y8 v7 F: P2 k3 I) k3 X, ^ - LL_ADC_SetChannelSamplingTime(ADC1, ADCIO2_IN_CHANNEL, LL_ADC_SAMPLINGTIME_COMMON_1); //设置通道采样时间
n! `$ ]3 C, F2 Y" ^8 ^: E - 4 R# M( O3 P. h5 |
- /*采样数据转换时需要参考电压,参考电压可能是变化的,所以也有可能需要采集*/& h. e2 g" u8 I% y$ H1 `! G( J
- //LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_4, LL_ADC_CHANNEL_VREFINT);
" C' z( A. G- C - //LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_VREFINT, LL_ADC_SAMPLINGTIME_COMMON_1);8 V; t7 f+ s! X$ ~, s
- return 1;3 N% x: |9 G6 |8 w
- }
复制代码
: e, e; z7 U! C; a激活并校准ADC:
7 v8 y( d+ _( l8 |9 d0 R/ X4 ?: _0 ?* s: [" S) E1 l6 [- B Q0 |
- uint8_t STM32ActivateADC1(void)9 `0 V v5 _, f3 f
- {
' ?+ k4 Q( H i) j. L - __IO uint32_t wait_loop_index = 0U;
; c2 X% i7 g$ V8 K - __IO uint32_t backup_setting_adc_dma_transfer = 0U;' K* B2 q- G# p' M( K# t
- uint32_t Timeout = 0U; /* Variable used for timeout management */. c9 x* }. m: v; T. W% }( h
- /*## Operation on ADC hierarchical scope: ADC instance #####################*/
8 }& {7 w, u+ i! h# |! I0 R! u - # d$ I+ v9 G3 {0 P- t) C
- /* Note: Hardware constraint (refer to description of the functions */0 t7 t$ ^' t& A9 h
- /* below): */: }# D, P0 r1 V* X- Z
- /* On this STM32 serie, setting of these features is conditioned to */
8 f0 T# C1 `# B4 S" q - /* ADC state: */
l3 [. E6 v- E! q/ H6 k - /* ADC must be disabled. */: J" E( a# v: }, I+ |
- /* Note: In this example, all these checks are not necessary but are */
7 s/ h7 Y) u3 @1 ]/ v5 I - /* implemented anyway to show the best practice usages */% b( ]: }/ O: g7 ^' |; P
- /* corresponding to reference manual procedure. */
6 n) P- \9 C' I6 J) K - /* Software can be optimized by removing some of these checks, if */
* Y9 e D, r" X8 E - /* they are not relevant considering previous settings and actions */9 T" X* V0 C! k) |! k7 P/ p, b
- /* in user application. */4 t0 T Q3 r9 Q# S9 i- i
- if (LL_ADC_IsEnabled(ADC1) == 0)$ O' \: {* T6 n: |1 }- X: c) N
- {2 e1 T1 u$ n6 ^$ |
- /* Enable ADC internal voltage regulator */
5 r& ?5 G/ z9 Z2 Z3 A4 o+ ] J - LL_ADC_EnableInternalRegulator(ADC1);
R0 r0 m1 K: s: i
1 s) j# E$ O& a0 d2 S5 G6 f) b- /* Delay for ADC internal voltage regulator stabilization. */( A* P' T1 A% j x
- /* Compute number of CPU cycles to wait for, from delay in us. */
" H' G7 `0 a1 N2 x - /* Note: Variable divided by 2 to compensate partially */+ \ y, w2 ~3 _: n8 l3 _: ]
- /* CPU processing cycles (depends on compilation optimization). */
5 c, a8 Q% y) l# J/ n! ^! h' F% @ - /* Note: If system core clock frequency is below 200kHz, wait time */ }: B+ Q( j$ v( V
- /* is only a few CPU processing cycles. */
; N/ ^$ d0 f: p/ u- b - wait_loop_index = ((LL_ADC_DELAY_INTERNAL_REGUL_STAB_US * (SystemCoreClock / (100000 * 2))) / 10);
% h/ X+ P8 m# z8 r4 A+ e. y* | - while(wait_loop_index != 0)
2 \$ v6 t% ^" u: z4 h) N - { ^. A& l0 t& S
- wait_loop_index--;0 v/ V2 d, M* W- b
- }
0 z" h1 T& A3 q: f9 d - 0 q% B& Y I6 \9 r/ A7 u
- /* Disable ADC DMA transfer request during calibration */
, `1 p' U. o* d6 t: j1 K - /* Note: Specificity of this STM32 serie: Calibration factor is */6 f# F3 }$ R' \$ o0 v& Z: f
- /* available in data register and also transfered by DMA. */
9 a5 W1 S5 \( K* q% Q2 L: R) _. t0 ? - /* To not insert ADC calibration factor among ADC conversion data */: u( N3 o t$ y; b1 K( y9 [$ \
- /* in DMA destination address, DMA transfer must be disabled during */8 K% }* A; ~1 s7 k$ U0 h
- /* calibration. */
- M2 P4 }0 Y! r0 y! Q - backup_setting_adc_dma_transfer = LL_ADC_REG_GetDMATransfer(ADC1);) k, h9 R. P; t" z
- LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_NONE);/ y6 x |; N5 u9 v, E* V) Z$ x; f# \3 J
# I$ k) y8 D% k& S" u3 k% G, x- /* Run ADC self calibration */9 S! E/ X3 `8 I, {1 O
- LL_ADC_StartCalibration(ADC1);9 Y. ~/ u1 Z) Q8 n& [* N: F
- , H- N% T) j) g% d
- /* Poll for ADC effectively calibrated */
y# L& u1 V! X! E/ c! n- Q+ ? - Timeout = ADC_CALIBRATION_TIMEOUT_MS;0 S$ u$ x! E% n# @ C
- ! a' ?+ g# Z2 r* Q; m+ K3 {
- while (LL_ADC_IsCalibrationOnGoing(ADC1) != 0)7 B/ w: @7 c/ H6 j* \* w1 f
- {
# |# V' U% }4 R: o; h# V& m - /* Check Systick counter flag to decrement the time-out value */" O8 _2 t O( ~8 n! v7 V8 f
- if (LL_SYSTICK_IsActiveCounterFlag())3 T0 Q* l8 a& L4 C) ^$ h2 f- m
- {: L' z- D8 a8 f3 M$ p! e
- if(Timeout-- == 0)+ o$ ]* W8 t# a' U
- {
* H! ]) i* v T3 ^% N0 `$ ? - return 0;
$ V/ T, }: r) F - }
) E8 ^' A4 p! ^" Z! S - }
0 t" I) U5 C: O4 V - }
9 r; H; T8 }+ g, A: \
3 r- H# c) N* d- /* Restore ADC DMA transfer request after calibration */
& r. K$ M. p- W& g - LL_ADC_REG_SetDMATransfer(ADC1, backup_setting_adc_dma_transfer);/ `7 w5 L$ c! a8 @; X7 h1 n
- . M% g; J3 ]3 ~4 r. L
- /* Delay between ADC end of calibration and ADC enable. */- v( S' @3 T5 E" j
- /* Note: Variable divided by 2 to compensate partially */" C0 {8 X( S! H' l, j/ a2 s
- /* CPU processing cycles (depends on compilation optimization). */
% k/ G% k# m. T1 _7 |. U; U# a - wait_loop_index = (ADC_DELAY_CALIB_ENABLE_CPU_CYCLES >> 1);3 h1 L# F0 U/ _1 @$ F4 n2 t! |
- while(wait_loop_index != 0)2 P( {' H+ U! Y. `2 G4 }
- {; s1 A' |. E* M( R
- wait_loop_index--;
, f5 h) h; W0 ~( S$ V - }% t5 c1 w/ S! t0 Z& j) O9 N
- + ^1 a. h8 t5 y/ n8 D+ _3 |" B% j/ x
- /* Enable ADC */
$ H4 U4 S: \4 a+ R, l - LL_ADC_Enable(ADC1);
6 d* Y6 W' Q' n' E. A
8 d- |! n! A3 V0 i, p t: P8 u, ^- /* Poll for ADC ready to convert */3 w( |" i, E% [3 p8 \! z8 s
- Timeout = ADC_ENABLE_TIMEOUT_MS;8 ~9 o* X( y2 l% r. H4 Q
- ; {8 A2 h3 l; C6 s, s, ]
- while (LL_ADC_IsActiveFlag_ADRDY(ADC1) == 0)% L8 N" }0 K6 e2 _+ F w( D
- {
3 I& Z3 m0 y5 o& O/ a - /* Check Systick counter flag to decrement the time-out value */
3 D2 h9 J1 @! |) P - if (LL_SYSTICK_IsActiveCounterFlag())1 W. f& ~, y1 H/ I0 n- q" g
- {
; l2 \/ s( }' z$ O! g% _% n - if(Timeout-- == 0)
+ \ x8 c" B) H$ c$ J3 f! d - {
; _, i; g9 X# T - return 0;
3 p( m) w2 r& [+ f. A - }2 [& l1 m5 P; ]4 r2 T! Q
- }
- o, {7 R8 t+ }% t1 y+ X9 ` - }% C7 d$ i, X. H% G B- }
- /* Note: ADC flag ADRDY is not cleared here to be able to check ADC */+ T/ c; v. E9 r/ @6 e1 u
- /* status afterwards. */
, C1 D1 g# Z- T8 }! E( ^ - /* This flag should be cleared at ADC Deactivation, before a new */+ {- P6 M% |6 k
- /* ADC activation, using function "LL_ADC_ClearFlag_ADRDY()". */
4 {6 v6 z0 @# @! c4 ?5 ?( h$ G - }/ Z7 q$ F4 S% _1 t# a" Q, V
- return 1;/ o- l5 h; [1 V3 h
- }
复制代码
4 ?1 z. _& X3 `$ |2 ~# i$ i因为ADC配置的是软件触发的单次采样,所以还需要有手动开始和结束采样的函数。
: U: w: {) B4 G" q2 e' ]' g7 w7 H+ u- g. d
使能ADC采样:" p0 A( P, v$ U2 E: O* z L) c
6 J) x: b2 M7 @$ x
- uint8_t STM32ADC1SampleEnable(void)
6 J9 k( @5 [0 y# `7 W - {) r `# g. z+ }
- uint32_t Timeout = ADC_ENABLE_TIMEOUT_MS;
% H; i: d! r7 ^9 N f - LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_4); //先使能dma再使能adc,以防采样数据丢失, k% h: S$ X8 h: k2 h. |9 ^
- LL_ADC_Enable(ADC1);
6 {; }6 _" ~- m" ^' i) m7 n - Timeout = ADC_ENABLE_TIMEOUT_MS;
6 {- \4 D M W$ `6 | - while(LL_ADC_IsActiveFlag_ADRDY(ADC1) != SET)5 N* k" X8 K( @/ @, u
- {
. e$ e& \: J8 |: z" A& M: P5 _% V - if(Timeout-- == 0)$ v. m2 u# h6 j3 c8 D& A
- {: q$ i+ |; I8 `/ D1 R
- LOG(1,"STM32ADC1SampleEnable ERROR!!!");
J" g3 o g- R5 y - return 0;6 O( `. f0 F! k
- }
! w" c7 Y4 Z& U - }" U) p! G9 _5 i) h7 I1 U
- LL_ADC_REG_StartConversion(ADC1);
! N+ q+ X- M8 J, o - return 1;3 s" M9 y7 \' k7 f' C, G
- }
复制代码 ( E" m" x7 Q$ ^4 X* W- l6 d
失能ADC采样:
]8 Q0 B5 R- K. c! L5 P
; `8 C% P9 Y$ c, Q( ^4 N/ J- uint8_t STM32ADC1SampleDisable(void)
7 X2 V# }$ Q0 w& h: W& i( H5 l - {
/ \5 p8 B+ f7 U - uint32_t Timeout = ADC_DISABLE_TIMEOUT_MS;
* `7 {, c5 w6 U) I# i - LL_ADC_REG_StopConversion(ADC1);8 W& J" j" ]- a( U3 n' p9 |
- while(LL_ADC_REG_IsConversionOngoing(ADC1) != 0)2 r# e/ L5 H6 K
- {3 q4 \4 {+ J5 i6 x. o
- if(Timeout-- == 0)& p! L3 o- z; C% f/ d; L
- {
6 E: s0 Q% ]1 J# D- Z - LOG(1,"STM32ADC1SampleDisenable ERROR!!!");- {2 U7 N0 E J
- return 0;2 m6 J5 E N5 b: U: h
- }7 \& p. `6 I2 V5 w; k
- }
: e/ ?0 K, B6 Z" w/ H! x! ^' ~ - LL_ADC_Disable(ADC1); //先关闭adc再断开dma,以防采样数据丢失; a9 R2 `/ m4 `7 g4 J3 L' e
- LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_4);; s( V0 C" F' A
- return 1;5 A& D2 m. f8 I& i
- }
复制代码
$ M- C0 N: _5 ^& H2 {进行ADC数据采集流程(采用多次采样求平均值的方式):/ S4 A/ \' @, a
, f: L# I2 d. e1、当需要开始采样前,调用一次STM32ADC1SampleEnable()函数;
) N3 c. H L& Z' Q. r5 b/ s, D( q8 [& {7 a
2、在一个定时函数内调用LL_ADC_REG_StartConversion(ADC1)进行数据的转换(因为使用的是单次采样,所以每次进入定时函数时都需要调用),取出DMA数据进行累加;
' x3 u7 _& j5 b' g- j- d S' b+ q; U) v0 n
3、达到需要的采样次数之后,对得到的数据进行运算,并调用STM32ADC1SampleDisable()函数关闭ADC通道。( Q1 D/ t4 P/ |) H
/ D- c/ G; U1 R9 x7 v2 ]" i/ U6 M以上方式适用于需要间隔一段时间获取一次ADC数据时使用,例如需要每隔1分钟获取电池电压时,可以进行1分钟倒计时,然后连续进行十次间隔10ms的ADC采样求平均值。2 s- h5 l0 P) F/ A
- `1 U+ D/ h# b/ t, C8 i3 a3 I9 {
$ }& p% n h' d( K
|