你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32学习笔记1(ADC多通道采样)

[复制链接]
STMCU小助手 发布时间:2022-1-22 21:58
STM32 ADC多通道转换
1 ^1 `& X2 G0 }1 O1 I描述:用ADC连续采集11路模拟信号,并由DMA传输到内存。ADC配置为扫描并且连续转换模式,ADC的时钟配置为12MHZ。在每次转换结束后,由DMA循环将转换的数据传输到内存中。ADC可以连续采集N次求平均值。最后通过串口传输出最后转换的结果。
* c7 H6 U  W$ z: j# g程序如下:
3 B) T4 Q& {; B: c: b0 d0 V9 G#i nclude "stm32f10x.h" //这个头文件包括STM32F10x所有外围寄存器、位、内存映射的定义
$ q" N' F1 V3 K% y9 y#i nclude "eval.h" //头文件(包括串口、按键、LED的函数声明)
7 j1 S; ?1 R# L  B#i nclude "SysTickDelay.h"
: W  K+ F2 S7 E8 t  t! L& R#i nclude "UART_INTERFACE.h"
) L/ \! v+ Y: c( s#i nclude <stdio.h>6 H- F  L9 }" X7 ?0 Y; O( J
7 n# u( n2 M( c  ]  g6 M+ ]0 q/ J/ D! T
#define N 50 //每通道采50次
: d8 k, P, G6 J' B& b/ w5 C8 W#define M 12 //为12个通道; e, ?& O: E5 x4 q. e* x
8 O. o; @( Q& [" G, ^
vu16 AD_Value[N][M]; //用来存放ADC转换结果,也是DMA的目标地址4 C/ X% k: o3 ^- \3 ~; G9 j) F
vu16 After_filter[M]; //用来存放求平均值之后的结果7 {( j* x' S7 y3 a' P- `" L* \/ ?
int i;$ T' j7 L* _( {3 k
) n9 N" q+ r3 n* ^; C: [  p0 y4 n

8 k1 c0 p) {8 Z- ivoid GPIO_Configuration(void)
8 N8 C: E2 `" ~9 I) O  Q/ w{
" l' p' }# C0 dGPIO_InitTypeDef GPIO_InitStructure;( v, |% t5 T; R
5 F; u- [" C9 B: }
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  r' b- K8 p4 t$ w' oGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //因为USART1管脚是以复用的形式接到GPIO口上的,所以使用复用推挽式输出
' G& n- \) t& I3 hGPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
# h. F. w5 o, ~5 m1 B1 h: SGPIO_Init(GPIOA, &GPIO_InitStructure);
; J$ U3 L. b( F3 w; ^5 C6 ~5 s$ ~: F; u/ P$ @  s
+ Z7 d5 n1 {  O" _' `. ~2 @0 M
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;; X- @, X% V* f) s$ I' y
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
: v9 h1 V# `5 F5 q, ~% s* e* aGPIO_Init(GPIOA, &GPIO_InitStructure);. |1 K7 s+ U, J7 s

$ p; T+ F$ V, ^3 V
$ X, B1 q/ Q8 Q2 B6 D
' l; I3 {! T4 r+ K- r1 {1 h//PA0/1/2 作为模拟通道输入引脚
3 b8 t% `7 q- {5 }# VGPIO_InitStructure.GPIO_Pin = GPIO_Pin_0| GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
0 l& A+ A. l+ XGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚  _( x0 G& |% O# g* \
GPIO_Init(GPIOA, &GPIO_InitStructure);5 i* k, s2 `2 p( ?& k

3 J3 A; S2 }5 i) {1 [  `: Y& c/ p7 G//PB0/1 作为模拟通道输入引脚
+ X, W$ H' f" W/ }* V1 E) NGPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
% R! A* {/ A/ v1 ]! O9 yGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚) P8 L* V( n" M+ T; |3 y6 {/ D
GPIO_Init(GPIOB, &GPIO_InitStructure);
  b. c) P( q4 J8 m# S2 B& V( I8 T& `2 |/ u# ~7 x
//PC0/1/2/3/4/5 作为模拟通道输入引脚
) y4 S9 _& b/ l/ Z, WGPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;+ O* @: g% U+ A( A0 G. q( u0 }2 }$ w
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
  d) D* F9 n* ?2 [GPIO_Init(GPIOC, &GPIO_InitStructure);
, l- P+ o' e. o1 h}
; }0 r# R1 S* }% f1 S9 K5 z
4 j) X. Z; q4 u}
! S. ^* j' B- }! I3 V
) [, d9 o$ x0 O: b( m1 E6 F7 J& ^! y; a( l  K
void RCC_Configuration(void); q' z+ F& n- P
{! @4 }/ i( s& y1 ~- s6 m! x
ErrorStatus HSEStartUpStatus;
( ~- u3 I  @1 v& Q, s9 B2 K' w" t: P; y. [8 n  q
RCC_DeInit(); //RCC 系统复位* I6 N+ G8 D& {) d
RCC_HSEConfig(RCC_HSE_ON); //开启HSE
# m3 m5 a, d% D4 b0 {4 x$ b( UHSEStartUpStatus = RCC_WaitForHSEStartUp(); //等待HSE准备好
  @  s2 e2 m" J- |) H- Rif(HSEStartUpStatus == SUCCESS)
7 y7 A; @6 J( V; A$ Z3 U{% {( D7 c, w, G) a3 K! O
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //Enable Prefetch Buffer
. Y: b' X, M7 G0 e) K/ |5 \8 RFLASH_SetLatency(FLASH_Latency_2); //Set 2 Latency cycles
+ b+ f) _  M6 B* G5 t9 `5 d- D1 @- zRCC_HCLKConfig(RCC_SYSCLK_Div1); //AHB clock = SYSCLK! i% \; O2 j. I/ N
RCC_PCLK2Config(RCC_HCLK_Div1); //APB2 clock = HCLK6 i7 X7 W0 `3 V, M
RCC_PCLK1Config(RCC_HCLK_Div2); //APB1 clock = HCLK/2& u  {0 d% ?1 A6 R! R7 H+ k. r
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_6); //PLLCLK = 12MHz * 6 = 72 MHz! m! ?$ I& \9 m6 q/ u
RCC_PLLCmd(ENABLE); //Enable PLL, E2 v- ~2 Y) x0 {
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //Wait till PLL is ready
% F( r2 y1 u" z: tRCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //Select PLL as system clock source
* H- ]' k: q+ O3 Y# R6 ?2 j* {9 twhile(RCC_GetSYSCLKSource() != 0x08); //Wait till PLL is used as system clock source
& r9 ?3 f: {! @) R& G7 f& O. F7 {' B1 w3 V
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB4 W+ C2 @' b# b# f4 i
| RCC_APB2Periph_GPIOC |RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO |RCC_APB2Periph_USART1, ENABLE ); //使能ADC1通道时钟,各个管脚时钟. i/ c1 _7 ]/ z) e/ M8 R

+ F$ k/ K$ C  D- BRCC_ADCCLKConfig(RCC_PCLK2_Div6); //72M/6=12,ADC最大时间不能超过14M
; M! M3 e. h% q# ^RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输* B/ q8 x3 q# Z- j9 }/ n4 ^& |

* }% c! {/ j' J}5 }- q" W' d+ B6 d) N
}
  o' D- @0 v  q5 e; g# W' e8 a% t. W0 ~; o! O/ g8 k" V1 s

7 p* @) ], h5 |0 u" g/ M# }* [6 Yvoid ADC1_Configuration(void): p1 Y" g" w1 [. ?2 {2 o! }) Z6 s
{6 Y, m8 ?+ N( U# C2 m
ADC_InitTypeDef ADC_InitStructure;
* H" I) L4 r$ o; F. C/ A9 c
0 D. g8 ^: g: Q9 ^2 p6 ^0 z' p( lADC_DeInit(ADC1); //将外设 ADC1 的全部寄存器重设为缺省值
2 O9 p) a% N3 n- m* d0 a4 G2 ]) \& |" _) s

) h: I; l+ z0 q% lADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
1 F2 p/ ~2 w, g6 j5 f+ n- @# R+ zADC_InitStructure.ADC_ScanConvMode =ENABLE; //模数转换工作在扫描模式+ ^" S8 Y  F: `
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在连续转换模式
" p  l& L$ l0 G. \* I* kADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发转换关闭5 N( I" G/ h) S$ h, P6 T
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
4 ?2 k. w) a# B5 u  V4 F1 P, AADC_InitStructure.ADC_NbrOfChannel = M; //顺序进行规则转换的ADC通道的数目( J! o$ O$ B$ }& S
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
4 {$ _8 r7 D- G$ V' z* p! }5 P
6 h5 x  g  Q) y# Y# ?; Q4 z" P3 V+ C+ |+ d3 p: c+ N  e. d) v
//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
8 V" x, o6 M9 n! M//ADC1,ADC通道x,规则采样顺序值为y,采样时间为239.5周期
4 B$ h! E3 l$ v/ CADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );
; K  B, T: m  e: w1 YADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 );
6 w; h) X' a0 G2 G; g, ?$ JADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5 );
' O! i+ o% @% ?+ B. J' \ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5 );5 ^( X: h, T, u- x" v
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 5, ADC_SampleTime_239Cycles5 );3 U* A9 m8 `% ^* d
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 6, ADC_SampleTime_239Cycles5 );1 w8 w4 j, d1 S$ g+ d* X. @% H: J- j# v0 _
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 7, ADC_SampleTime_239Cycles5 );
9 |9 B- Y" L) W" _8 X4 J/ [) [ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 8, ADC_SampleTime_239Cycles5 );: O9 W3 S; D, F# c3 y
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 9, ADC_SampleTime_239Cycles5 );
% E/ I1 Q# ~% N0 o2 L9 xADC_RegularChannelConfig(ADC1, ADC_Channel_13, 10, ADC_SampleTime_239Cycles5 );4 H( J  f' H/ n( i- N6 h( U! o
ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 11, ADC_SampleTime_239Cycles5 );* O1 B& w7 }# T) f$ l
ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 12, ADC_SampleTime_239Cycles5 );
% K4 f. d  U7 |
) P; k2 C0 I6 c2 y// 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
# y) x* F8 ^7 T; }1 i! @3 \ADC_DMACmd(ADC1, ENABLE);" ~" O+ S* p) P% r4 }: ^: i0 N

+ _# R' A) h5 l) g1 C8 K" o$ B+ @' Z& n" Y- }% C
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
1 x  R4 X. j: U% Q, q. P
  t& i$ h7 v9 E. }7 f  k; TADC_ResetCalibration(ADC1); //复位指定的ADC1的校准寄存器# ~% d) K. ~* C- T
+ y# O. d. w) f
while(ADC_GetResetCalibrationStatus(ADC1)); //获取ADC1复位校准寄存器的状态,设置状态则等待5 F/ d8 d2 K  F* K
0 s1 y/ ?; S7 i' }- Y5 b

* V5 u0 v& U5 Z7 {( IADC_StartCalibration(ADC1); //开始指定ADC1的校准状态, i% U6 d7 z  c+ B0 b
. ^& z3 O  w; H. k# H% r, t# P
while(ADC_GetCalibrationStatus(ADC1)); //获取指定ADC1的校准程序,设置状态则等待
9 Z) V1 V9 _! _! k  P7 f8 j( R2 J0 ?: I: o/ @- w
1 b* W4 @; g( a, r2 Q" E! b6 M
}
( ~# c3 L2 D" b$ }" ]6 G% A
2 w/ ~( E2 `7 k' O6 W5 P3 c% n! T; k5 G  t; Q& a+ A0 \" i
void DMA_Configuration(void)
. c9 g7 j' e( T, `$ s{3 m% G) `) a. e1 M  L2 J$ u
! ~1 \! h. Z; S! k7 o
DMA_InitTypeDef DMA_InitStructure;- U/ L  S8 a  }/ \# }* r7 V! i
DMA_DeInit(DMA1_Channel1); //将DMA的通道1寄存器重设为缺省值, q! p! z5 V# r6 V! l1 E
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; //DMA外设ADC基地址3 c% Q# K( Z2 o$ k
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value; //DMA内存基地址
+ N8 h2 P) \" b3 ]" ?2 QDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //内存作为数据传输的目的地" W, S; J, u2 n. e5 i
DMA_InitStructure.DMA_BufferSize = N*M; //DMA通道的DMA缓存的大小
' M. T* r  w- x0 B6 z. qDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变# k! ?8 k2 a. W5 A* }# \% U
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增1 F+ \' J5 D8 @$ x
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
2 `# ?* z; a7 z6 w6 |+ ZDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
: U  |2 q5 g' O+ e! H" o5 h. {DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式
, `1 |7 \% F3 P/ C7 O5 sDMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级
" x! J- [7 y( ?" |4 j! [$ b/ S! gDMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
& J" T( l; B  J" q2 d( o& NDMA_Init(DMA1_Channel1, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道9 O8 f, q( Y1 k# T% W% c5 n
9 ?( f: d3 c* }1 J
}  `1 u& M& i. Y4 [* v
! R( s# z4 P7 t: z$ r
( K8 ?+ p3 N8 ?6 P
//配置所有外设
, t* _7 W/ N' E6 Evoid Init_All_Periph(void)* A; z4 R/ M5 @4 M. s2 i5 a
{
/ R" o3 |; f, t* s- L+ D* p; j/ @( F/ o, T
RCC_Configuration();
; n5 x' [, Z6 f3 q- l) H( I# X
$ z& j: G' c6 b  P$ A5 PGPIO_Configuration();
9 N- {- g, Y+ A* k0 t% ^. T& c! x( u8 \* |
ADC1_Configuration();: p3 V4 ~' q6 E3 v9 @" v8 T  @

4 G8 }, j& u+ f$ P8 ~  iDMA_Configuration();
0 V: T- ~" t9 Q$ v5 Q, n
, T0 I2 ?; d- N; B8 T: U) C3 D6 h//USART1_Configuration();
0 o& D$ C5 P$ x, KUSART_Configuration(9600);2 F+ R: y0 T; P! M0 {2 S* T. T: |

9 W1 o0 Q6 M' J, T4 L+ H' [1 o4 t0 t' \, @/ d
}' R, U6 p% V' _6 a

5 q/ b) C+ z4 D2 v" z' a6 V
" f; O0 y: |1 a5 V' C; f6 \
: J  @" ~+ T1 ]. s2 K& Y, Lu16 GetVolt(u16 advalue)
& q: [+ `' z' k* v* u- y# l9 h) E8 u7 Q& \  J0 z7 n
{
- n) ]' C+ Y( ?# c6 x2 @! C% }% W8 j& x( C6 p$ W7 f  l
return (u16)(advalue * 330 / 4096); //求的结果扩大了100倍,方便下面求出小数. R, j4 R8 I) G0 x1 A2 I0 x( r; U
" j# s6 b) O. F4 {  {6 _# G
}' G% d( j& i% ^  M7 s9 @5 R& d# n

1 q& h) F- I& p' A9 r1 L( `' R. ~
0 F2 h4 X  J; \1 ?0 o
* V- I9 e4 C1 V* s
# j" _/ p2 y: e4 Ivoid filter(void)2 M, W0 {1 {1 C3 Y
{6 R0 U9 O1 {% ]0 d! C$ w& y$ p
int sum = 0;
7 N! D, [( I% Q/ h0 o, q: T0 l8 {% s2 yu8 count;3 K# k4 _4 O" {( g
for(i=0;i<12;i++)- w3 {" v1 j6 L! ?

3 ]* `0 o# n7 ?2 E: P8 i/ z{
6 S& e0 v7 b" E0 A( K# ^! T- u1 x2 U% a) L2 |
for ( count=0;count<N;count++)
. o' K/ \2 z. V" b+ d
( X# _4 \1 k' h. l3 X{8 [& N2 g) }* W0 n# i

% l- o4 g6 X+ x( X- }: S0 ^7 |: psum += AD_Value[count];! z6 r" d9 d$ S: N
# P3 Y) D. X$ _: r
}4 D3 b8 N1 q* I; ~
9 l4 I9 D/ m" Z0 k4 B
After_filter=sum/N;
2 u9 V+ a* U8 a8 Q9 M9 k+ f7 R  i) w
sum=0;$ }6 A: f' G+ u, G
}; m  h3 Y3 U0 |. ^& q3 ?

1 B: |7 q. I6 Z}
1 Q) M8 z# }$ X3 A+ P! `
9 F( R. J5 F! y+ D6 `
- E" R$ Y- f0 }/ n: m; r; r; \# X/ R
4 w4 V6 N9 t' u
int main(void)
+ x- l9 S& R% T: T* o6 D4 L6 [{
6 E( q; }5 r, `9 C% R8 z9 T' I1 @% D; X- l# t& B* i2 D& ?- z
u16 value[M];8 x; }5 C2 L6 S. L1 \

' j, e- O. V) M, b6 X/ Pinit_All_Periph();* v: K/ K3 ]/ i7 [. G* _+ x9 R
SysTick_Initaize();# S1 Q+ y  h! L& h; E7 v. W

3 p  m5 C2 P' U2 V7 @; `  {* k! D) ?5 H1 t5 h
ADC_SoftwareStartConvCmd(ADC1, ENABLE);/ T% J# L% K  z6 t+ h
DMA_Cmd(DMA1_Channel1, ENABLE); //启动DMA通道+ L) r3 b8 O. B8 b8 b
while(1)& H" j! i8 b; \  V% b7 L: m
{2 n0 i, ]. z( d! a% r! i
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);//等待传输完成否则第一位数据容易丢失: A3 U9 W$ ]' C7 s. E7 {
( N/ Z  J( o3 R" \: w0 R3 k
filter();
% R8 M5 W' ^1 `5 O$ U/ Q9 ]" R( Mfor(i=0;i<12;i++)
) u7 f/ u9 Y4 i( h! b{
- Q, \4 ~1 @/ z6 n3 }" c6 tvalue= GetVolt(After_filter);
6 O  w( q2 n) H3 ~1 P( ]  H/ W2 X+ \  R2 `7 ^4 {  ^7 L& {" Y
printf("value[%d]:\t%d.%dv\n",i,value/100,value0) ;: C& L  i9 Y4 p% T5 Q9 ~  f
delay_ms(100);
% ?7 W  X* k- n! f8 W}
! U) s/ y* `) {1 \: _( u6 t# Z}
/ P* n% b( \6 z* A% C" J
- R& `! L( H3 p& ]}
9 l/ N$ Q8 t# Z) k$ p9 ^" v( S总结0 Z7 v* U2 I9 S: h( ^, Z
该程序中的两个宏定义,M和N,分别代表有多少个通道,每个通道转换多少次,可以修改其值。
9 L7 t$ Q+ t- U  n曾出现的问题:配置时钟时要知道外部晶振是多少,以便准确配置时钟。将转换值由二进制转换为十进制时,要先扩大100倍,方便显示小数。最后串口输出时在 printf语句之前加这句代码,防止输出的第一位数据丢失:while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
收藏 评论0 发布时间:2022-1-22 21:58

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版