|
非中断和非DMA方式可以用于特殊场景,比如其它外设占据DMA负担比较重而CPU又比较清闲的时候。 6 \0 \& L" K0 p' x, n1 b
1. STM32F429的AD转换最高12bit
; p2 Y$ J' c L: N( H$ n9 Q12位分辨率意味着我们采集电压的精度可以达到:Vref /4096。
2 ]1 k/ P. A2 p8 J2 A! W采集电压 = Vref * ADC_DR / 4096;
3 C4 n' H/ E! @/ W6 nVref:参考电压, H. v/ w& ?) V' q8 m
ADC_DR:读取到ADC数据寄存器的值
) r: v& r$ H9 Y# U一般使用3.3V为参考电压,则采集到的电压 = ADC_DR*3.3/4096 。
0 g w3 [; C. P6 ?" M/ D4 @- k5 b
# n+ \ l6 u' D+ p ]: p
2. STM32F4的ADC转换不需要做程序校准。
' u* d6 w2 \3 m7 s: {9 J) A这在HAL库里也可以看出来,官方没有HAL_ADCEx_Calibration_Start();这个校准函数。 : _8 a. X% ~5 y! `
: F8 }/ k; n0 p& S& Z% n: ]3. STM32F4的ADC时钟最大36M。
+ ]5 t6 w- P4 b( ^% j" r' xSTM32F1为14M,STM32F4快了2倍多。
1 A3 W+ }) G5 @1 d3 q
5 \/ `1 p6 ^0 s4. 两个以上通道必须开启scan扫描模式。 @$ L) L3 j0 J" M1 d6 t
ADC_ScanConvMode = ENABLE。
6 u0 D- i% J! n# u" `( q. {因为转换结果都在同一个寄存器里,所以单个通道转换完成后必须读走数据再启动转换下一个通道。
! @% |9 S; D4 w- X: Z
- m8 p7 L2 c4 B* S- J
5. 不使用DMA不需要开连续方式。
K% I' Z4 {* ^( w6 sADC_ContinuousConvMode = DISABLE。3 ~% B4 f/ X/ A: y& V' s8 d
连续转换ENABLE后,也就是只需要启动(触发)转换一次,后面就不用再次启动(触发)就可以连续工作了。
/ D7 `4 X% c! x; }0 K* m0 d& T单次转换DISABLE:也就是根据一次转换完后需要再次启动(触发)才能工作。 / B) _6 u; W0 X; h. f1 [# w% L6 ^
7 p5 g# U3 m1 J6. 触发方式一般选用软件触发。
9 f* { M8 b; Y" p9 h t7. 对齐方式一般选右对齐。
0 r' ^2 a9 {; Z# \2 A! P8. 通道数就是你要转换的通道数量
通道数要和后面的规格组相符合。 : J; X" P2 k$ k/ D
2 E3 [! z2 a& Y* a9. 设置规则组通道
5 z) o9 x( Y5 E% w9 Z下面例子是设置 channel5 和 channel8连个通道,开启触发AD转换后,通道5是首先转换的,转换完成后,再次触发转换,就会自动转换通道8。一轮完成后,不需要STOP ADC,可以直接再次触发转换,此时又回到了转换通道5,开始新的循环。 - /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.5 b6 R$ w' z* J, P1 d$ `+ A- W
- */) t& ~; [1 M, I. ]
- sConfig.Channel = ADC_CHANNEL_5;
% V8 N2 K# V- C0 S b - sConfig.Rank = 1;1 X a N5 Y3 g# r* f; A9 ]
- sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;2 k2 p$ t/ [. A8 Q& A9 o
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)' f7 m3 B# y% j0 W/ Z5 f
- {
# z4 ~ q, F4 S; w" G. Z2 D3 p - _Error_Handler(__FILE__, __LINE__);
! [8 R7 \/ n7 ], S) \7 e - }! p3 C( s; M) g F
- ( F' N: V4 `3 e3 |0 @( ]/ [
- /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
" k/ w" T; @* u, l8 h8 [2 d - */
' K6 t4 b- E0 A" B R - sConfig.Channel = ADC_CHANNEL_8;
. ^8 S/ k- o/ {, K9 c( U' q - sConfig.Rank = 2;
; v9 l' P: y2 L6 t1 Z2 V/ Y/ ?+ a - if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)1 y+ |: \/ C- X1 `5 @
- {
1 u" t! E+ Q3 g$ p8 R. J5 \ - _Error_Handler(__FILE__, __LINE__);8 f1 v1 B3 m( K3 @+ q
- }
复制代码
" `* D' M3 [6 D10. 规则组间断模式配置。& n; J8 h l9 y9 _! r! O5 H
如果不使用DMA,这里必须要配置为1,也就是说每触发一次转换一条通道。否则,结果寄存器里只会是本组最后一次转换的通道电压值。具体可以看下图:
+ ~1 u8 d6 _% _& o" A+ r1 i4 P
* b, C0 b" `3 q' X
. Q9 d1 J9 ]2 e. ^. b
, e$ R! n, n7 ?8 |/ ~' o5 b$ j# ^4 l9 Y
11. 其他ADC的基础配置见下面程序代码。
. |. i8 L! `" Q例子中单片机型号为STM32F429VIT6,代码全部经过实测,通过算法滤波,转换精度足够平常使用。而且有人提出多个通道之间会有干扰,此例未发现。选用的未PA5(ADC channel5)和PB0(ADC channel8)两个通道。
+ d3 A, R$ \* m
1 G8 a" p M1 x4 n! a
一、ADC配置 - /* ADC1 init function */
4 w* d/ d" @9 ~% S - void MX_ADC1_Init(void)/ R% [8 n4 \% I; v! j. ]
- {
: L4 p6 A% R$ n* W& x c; \/ | T P - ADC_ChannelConfTypeDef sConfig;
5 k9 f: j n, W6 e8 S - 0 ?( I' O5 F) G E! `
- /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)+ V( i u; o/ B. n; ~
- */
( W* w3 w d+ b1 B - hadc1.Instance = ADC1;
5 a, e9 ?% i+ X - hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
6 A. l) D- u0 i. V. Y - hadc1.Init.Resolution = ADC_RESOLUTION_12B;& m! L6 o2 G0 f+ f) l( l
- hadc1.Init.ScanConvMode = ENABLE;
. M' {! u$ e" ~ - hadc1.Init.ContinuousConvMode = DISABLE;3 S. I! r2 ]7 a) g8 N( X2 K
- hadc1.Init.DiscontinuousConvMode = ENABLE;
! [) g9 C5 B% V - hadc1.Init.NbrOfDiscConversion = 1;
; r3 u G5 b# c: k9 P; x - hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;9 K1 |- @) d7 n( N3 D" t
- hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;8 R- [; a ^$ G- m
- hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;, N, q+ ~1 d* A. q6 `
- hadc1.Init.NbrOfConversion = 2;
- d" ?7 n" `3 [2 k8 `7 t# ? - hadc1.Init.DMAContinuousRequests = DISABLE;
6 ^) q2 G. o: W& |) l, ]6 R - hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;/ U, v1 c- A) m/ ?
- if (HAL_ADC_Init(&hadc1) != HAL_OK)
|2 \- Y+ Q$ M" b9 v4 q# t - {3 ]4 n: ^# `" T0 i
- _Error_Handler(__FILE__, __LINE__);
6 ^+ @% r+ D, W4 v# z% t& v - }
# u1 f: Z N1 h" w9 l' V1 ]
: O% X! ^7 g8 u5 u5 j- W- /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time./ C8 O7 a0 x7 Z$ E. t
- */
2 Z* C) d4 _9 Z! _; t E - sConfig.Channel = ADC_CHANNEL_5;
3 c3 d7 W, s, W G; |' U - sConfig.Rank = 1;
& V& C/ q7 d* Z* t( ]0 ] - sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
6 D& S" T& J# U/ p/ F& H - if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
- m3 A8 _8 c, ?8 J& r: ^ - {: N5 P n$ s( ]/ q
- _Error_Handler(__FILE__, __LINE__);
9 i' Y6 C; N* |' C2 r - }
& u4 g7 m9 V( r! x - : i( t0 V! I, r J/ e
- /**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.7 `0 ?# w' e" G& B) L* d# B
- */
* u$ ]$ T2 C5 J- ?6 b/ {8 o- l) p) ]0 s - sConfig.Channel = ADC_CHANNEL_8;( N( _6 w3 f, H3 P
- sConfig.Rank = 2;
2 p! g) u( M" e$ u0 y3 B! P& u3 ` - if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)) Z0 s8 {# x+ T( Z
- {
& V) P6 c5 P, V+ g1 K - _Error_Handler(__FILE__, __LINE__);
, e$ X" i: P% v# a5 S7 a - }+ ]( o' i, P( H$ Y
/ y3 `5 J o9 c1 |- }
: N( q( _* h& {& y$ L
4 E# O2 @- k* c- void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
: W% @: n3 T. h - {1 r& N* B6 }" e) ]1 }7 _9 N1 G6 m
- & s! V+ U& N, O( B# Z; I
- GPIO_InitTypeDef GPIO_InitStruct;
. g8 _9 C, a, g) c% W2 X - if(adcHandle->Instance==ADC1)
5 ~' b; g0 [$ z' Z! M - {1 |0 A m1 X! o
- /* USER CODE BEGIN ADC1_MspInit 0 */
8 G* a+ T' Y' ~' i - 6 T3 G0 r3 [) _! w$ X
- /* USER CODE END ADC1_MspInit 0 */
; y4 p( d( ?' m( ~ - /* ADC1 clock enable */' z7 G' e% a: x
- __HAL_RCC_ADC1_CLK_ENABLE();
! V* G! c `- R% ]: m - + X7 l0 e# Q1 M' E/ u+ O, a1 f+ P
- /**ADC1 GPIO Configuration- i+ ]! n# L7 F5 s6 r1 @" T1 {3 h
- PA5 ------> ADC1_IN5
; Q9 b# h. V0 a$ f7 F - PB0 ------> ADC1_IN83 y4 [2 Q" L' I* V$ T4 t* |- r. x
- */
4 W# D2 y- N" h% f6 R - GPIO_InitStruct.Pin = GPIO_PIN_5;
: N# [$ I* n5 P. V( [& U' a( z - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;/ e6 i! Y: e8 s2 L1 a( T
- GPIO_InitStruct.Pull = GPIO_NOPULL;
* B) r! `! A. Q N, G" I - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
8 _( o% J( G. S
0 @9 t' g* N: k- GPIO_InitStruct.Pin = GPIO_PIN_0;
% d" T |; O' }# k9 h; O' r) W. r - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;1 D4 i( [$ A$ Z
- GPIO_InitStruct.Pull = GPIO_NOPULL;0 S) n. r; `( a6 o
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
* S* H) {2 P2 l( @+ J( D( @ - 8 X4 _4 t% l# R* X0 U
- /* USER CODE BEGIN ADC1_MspInit 1 */7 ^( w4 T3 s ^( W& T
! A6 x$ m- l2 R" N5 w. c$ r- /* USER CODE END ADC1_MspInit 1 */8 ?5 Y" X( ^& W2 {; t
- }
/ b d& ?0 j* S4 j - }
复制代码 7 e7 N; A0 c: Z& N7 Z4 w
二、滤波算法处理 - uint16_t adc_values_ch5[14] = {0};+ R0 |) h7 S6 x. i& w
- uint16_t adc_values_ch8[14] = {0};. ?$ m* }3 D* Z3 N& i
4 Z: F9 T% N6 i. @( e9 R1 H! T6 s- /*进行单次AD转换,50ms超时*/' a o+ X6 r/ G# F6 a/ ~
- uint32_t Perform_single_AD_conversion(void)
* ]$ a( K/ \" ? - {
% \$ [( T/ ~ n! ~) x' K& g - HAL_ADC_Start(&hadc1);
# N' z; I- k" h% X. _2 @ - HAL_ADC_PollForConversion(&hadc1, 50);! ^% V1 L& ]$ h5 j% y, ^
- uint8_t i=255;# U1 S( Q Q4 g& z' X
- while (!HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
) p% P1 N9 }3 z! D. c4 m2 E$ b - {
; b* L+ n/ D" {# X: X5 i4 q - i--;+ |$ \0 ^6 x- ?& t
- if (i ==0)5 Z. o+ G8 B( }- y( {
- {
1 ~3 L. d+ ~& ^ - break; //防止AD死循环
$ ? p2 y. o. O- [$ I& {' P* j - }
h* }' `! D( M: ~. A) v - }
$ ]* e8 C \$ s9 g - uint32_t ad_Temp = HAL_ADC_GetValue(&hadc1);
/ v$ Q8 E& [ _. X/ ]& ~- G - return ad_Temp;* u( h0 b& o& y6 a5 b: j1 [
- }, [% w5 i2 ?2 S. P
- ' ~* l* b0 T, B! A3 {- N( e7 S$ W# P
- /*对14次采样的电压按照从小到大进行排序*/: \ r. Y9 {# f+ b, Q9 ?1 N$ Y
- void Sort_ADC_Values(uint16_t *adc_nums)5 K+ K4 F9 a# c1 g- |! o
- {6 _3 _0 ]9 A8 s: y3 r
- int i, j, temp, isSorted;
7 O4 Y; \, K* k) _ - //优化算法:最多进行 n-1 轮比较5 `2 H& l0 j$ u% g
- for(i=0; i<14-1; i++)5 r4 L3 D' e% y0 p/ _0 @' l
- {& U3 t% M6 _! \+ D Z+ q& ?+ S
- isSorted = 1; //假设剩下的元素已经排序好了/ Y; U) f4 P- i' d8 W
- for(j=0; j<14-1-i; j++)' `7 Z; O1 {, b+ _
- {
: a( o) N$ W1 R2 L9 ` - if(adc_nums[j] > adc_nums[j+1])& O# ~- I p5 q5 n
- {
8 N- n( X8 t5 |1 f - temp = adc_nums[j]; x4 G+ m* Z) E+ U% Z
- adc_nums[j] = adc_nums[j+1];
7 ^: A# ]$ {3 v- P: p* A: x - adc_nums[j+1] = temp;+ n' U7 P0 R# Q. X+ D; g& H
- isSorted = 0; //一旦需要交换数组元素,就说明剩下的元素没有排序好3 ~3 N* }; c6 @" z. I) ^, w# g
- }3 h# z" x( n7 K3 \
- }
+ I, G# a# i5 I) f- n9 u - if(isSorted) break; //如果没有发生交换,说明剩下的元素已经排序好了, U, n f1 e% O: o9 d- p/ `# t$ H
- } * o2 F* O, B: V
- }
+ L( M- ^4 y2 O) K8 M( x - /*去掉2个最大值,2个最小值,剩余10个值求平均数*/, i8 S' ]7 K5 z
- uint16_t Find_Average_ADC_Values(uint16_t *adc_nums)- L+ N- \+ h" N0 w2 o& d9 f
- {4 X m0 x! U$ L h( w- \" l2 }
- uint16_t sum = 0;
8 @, R' N: H- A G2 S- d6 g x- b - for(uint8_t i=2; i<12; i++)0 I T7 _7 Z2 I$ a+ S' \
- {
: |; K: a5 c$ E- c - sum = sum + adc_nums[i];
" ^7 q6 z0 |3 B* V* u! X - }
, [3 d1 @ P; n - return sum/10;
+ |0 Y+ c$ w; b" Q: Q: T - }! E; h/ t! S. Q7 W' m
) x' T+ C: t6 W- /*软件滤波求出ADC转换平均值*/+ y7 S; Z$ `' }, k0 M) J! v
- uint16_t Software_filtering_ADC(uint16_t *adc_nums)
' |$ H0 u) Z3 o9 R5 t2 x' l - {' _" Y* z" z+ o P! e
- uint16_t adc_avg = 0;, }8 L1 l- B) [) D4 P% p4 |7 s
- Sort_ADC_Values(adc_nums);' T3 Z1 Q1 x: ~0 w X
- adc_avg = Find_Average_ADC_Values(adc_nums); b, I0 d# l8 ~
- return adc_avg; W3 O3 O0 X" n, _) j
- }( M' T! w6 {, h9 o6 U1 a7 [; {
- & ?+ i) ~) |! I3 X% M
- void Get_Two_Channel_ADC_Votage(void) : W* A3 C- f! {) t, f
- {
$ N% d0 M( M. `6 @, _' ` - float votage = 0.0;
5 _$ D+ q6 n. u. g7 j" R( } - for(uint8_t i=0; i<14; i++)
9 {' L0 j2 H6 I - {7 b: N; r6 y' x' k" C8 {, `
- adc_values_ch5[i] = Perform_single_AD_conversion();, A) T& q( G: u- C/ g7 K
- printf("\r\nadc_values_ch5[%d] = %d\r\n", i,adc_values_ch5[i]);
5 M8 H' [( j& ` - adc_values_ch8[i] = Perform_single_AD_conversion(); c! [9 m% A1 U& p1 g: a$ h
- printf("\r\nadc_values_ch8[%d] = %d\r\n", i,adc_values_ch8[i]);6 \- H. R/ z( B) e
- }
* `0 e1 y9 p: D m% ~6 M - HAL_ADC_Stop(&hadc1);; Z* W* q, W% B/ o8 j
- 7 q0 ]" y( Q0 G2 T! |' o
- votage = Software_filtering_ADC(adc_values_ch5)*3.3/4096;' d8 G5 b. v! V
- printf("\r\nPA5采集电压: %f \r\n",votage);
: D+ @+ ?, P! j+ O, Y - # l T. |4 i1 c7 Q# ? D' D) O3 t
- votage = Software_filtering_ADC(adc_values_ch8)*3.3/4096;* ]6 n. h% J4 a; d$ o( N
- printf("\r\nPB0采集电压: %f \r\n",votage); . g; @( [. i# Y$ ~# j! R
- }
1 D4 b) A" Z- O1 c
复制代码
! e& ^5 Z1 ?1 ^1 c三、打印输出结果
9 Z0 M0 }3 i7 C( C: x/ S在main函数的主循环while里调用Get_Two_Channel_ADC_Votage(),即可输出打印结果。 - int main(void)1 d2 z3 G2 o- V6 z/ o- j" H. D
- {
0 |7 k4 b& v# q6 M; m - /* USER CODE BEGIN 1 */
" J/ \6 ^, P" G! C
/ S5 S7 }+ [7 e, {1 t9 t) w- /* USER CODE END 1 */ j; q0 X6 a1 v1 Z/ s
: g6 g' H4 X2 z( ?& A/ Z- /* MCU Configuration----------------------------------------------------------*/( B% ]4 [: X* @! d8 |
7 r: q! ]- Z8 B& P- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */0 \, h* M1 ?; F" K2 a( X2 K+ r! w
- HAL_Init();2 W1 B$ f9 B9 }
- 3 m9 t. J3 a4 O9 e+ w$ }* S6 K$ ?
- /* USER CODE BEGIN Init */
. J$ H! P% x; l& t/ r4 w& B8 R - / c( s0 c3 p9 }8 G* W
- /* USER CODE END Init */" D6 }2 |6 {/ G2 L
; D4 w) i7 r% S$ C2 a* X$ \, `- /* Configure the system clock */
/ J! z# X$ B7 O1 B6 ]1 u( [( z, V - SystemClock_Config();
6 P& d- {/ |1 b+ ?( q
2 e# X# f& X# }2 ]1 }- /* USER CODE BEGIN SysInit */0 T+ T$ P1 K8 m, A2 x+ p" f4 c
- 0 I. m4 A: x$ k# U
- /* USER CODE END SysInit */$ B9 I! F1 r( L2 O+ d: b. D
, J: R5 I$ [3 A) S5 A% w+ B- /* Initialize all configured peripherals */
$ [+ E# i0 y) S! ] - MX_GPIO_Init();
! V) N+ t3 ^' v3 @8 {4 H3 x. L - MX_DMA_Init(); / R; ~3 [# }. I8 r$ {/ C$ p
- MX_IWDG_Init();/ @( o% o7 r8 h t
- MX_ADC1_Init();; `0 a: r3 T2 O0 M$ e3 ^, w/ k. p
- MX_USART1_UART_Init();7 `# S. ?7 B* o, }
- /* Infinite loop */$ `7 W2 S/ Q; O
- /* USER CODE BEGIN WHILE */
) C& P* E" p, c8 j) \ - while (1)
( ^4 l9 S/ v4 M) m$ } - {
, A, k s2 h) b, u, f - Get_Two_Channel_ADC_Votage(); 2 T8 d) E2 d( Z" J
- HAL_Delay(2000);
q; D9 J$ m/ X; `) _ |: k - /* USER CODE END WHILE */. G. u3 I- j+ P6 L; @
- ' _- p7 `+ I( ?9 Y# i
- /* USER CODE BEGIN 3 */
3 ?0 P0 ]$ p8 l6 @% s - HAL_IWDG_Refresh(&hiwdg); //看门狗喂狗2 `2 W$ `7 e4 D3 T: m: u
- }* Q5 Y. p* j* F6 j0 l y" o
- /* USER CODE END 3 */
5 d0 v5 \3 X% P. e - }
复制代码 $ s0 A' _2 j8 `% L; \) `
! K& P3 F+ y/ Q3 x) o; Z5 M
% A2 j5 T5 E: F: o$ l0 `0 c
|