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

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

[复制链接]
STMCU小助手 发布时间:2022-1-22 21:58
STM32 ADC多通道转换
# u# W' d5 R7 R描述:用ADC连续采集11路模拟信号,并由DMA传输到内存。ADC配置为扫描并且连续转换模式,ADC的时钟配置为12MHZ。在每次转换结束后,由DMA循环将转换的数据传输到内存中。ADC可以连续采集N次求平均值。最后通过串口传输出最后转换的结果。: Y/ o8 Q* }+ G3 v/ C0 S5 S
程序如下:. o6 [* a& ~; S8 d1 O
#i nclude "stm32f10x.h" //这个头文件包括STM32F10x所有外围寄存器、位、内存映射的定义
$ O/ {) _! @7 Y( k6 x- v#i nclude "eval.h" //头文件(包括串口、按键、LED的函数声明)
7 q" E+ s, N6 ?7 k  Q#i nclude "SysTickDelay.h"$ E0 J+ v" t1 d
#i nclude "UART_INTERFACE.h"; G6 \1 d! C$ ]+ D3 }# s1 Z
#i nclude <stdio.h>
& m# L/ P6 S# C. }% o7 R
. \! R. T2 S9 o: J* G$ i8 J( R#define N 50 //每通道采50次5 e4 G9 ?* g# N6 ?# G* }1 W
#define M 12 //为12个通道
: i- p4 o) P! ]5 @5 F" U7 ?; D8 z4 C& p* R
vu16 AD_Value[N][M]; //用来存放ADC转换结果,也是DMA的目标地址7 n1 k# X1 ]* s8 j
vu16 After_filter[M]; //用来存放求平均值之后的结果
4 S& P; `% F8 p- N! a: o$ ?' ^int i;1 g3 R! j* z% e6 U# d4 K2 x

& @2 A) O9 n, b% a# I- Q$ _
& |* w' i) s0 j/ Hvoid GPIO_Configuration(void). p; R# C( Q, a( ^1 [- E; C- ?
{
( Y( y! [" `, qGPIO_InitTypeDef GPIO_InitStructure;, K, e8 b- J6 r" _

% l# I# W# p6 KGPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;7 Z, }; k) ?2 M+ o
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //因为USART1管脚是以复用的形式接到GPIO口上的,所以使用复用推挽式输出# _% ?. J$ |& l7 `2 x/ o& [4 d
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
7 f2 a7 ]- e! n  @GPIO_Init(GPIOA, &GPIO_InitStructure);9 W( s1 S3 E  b' W' A

: y3 A9 u0 u) T. h: `* u; W% h
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
( a6 O; X( b# \6 iGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;3 M0 a. E$ B/ d7 _& S* ~
GPIO_Init(GPIOA, &GPIO_InitStructure);0 T0 F0 S6 |" u: b
/ b( m" a: `+ ~. \
1 a& s! i. e0 z) x- k8 N

3 ^' p( X  ?) v2 G1 o! n, P//PA0/1/2 作为模拟通道输入引脚( y0 e- }4 k- q  M& N
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0| GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
+ P& p7 M0 i! O3 `: s+ IGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
, i. p9 Q5 I: u" aGPIO_Init(GPIOA, &GPIO_InitStructure);/ d+ Q* |8 t/ X! p% o( c  Z
( ^2 u- x9 G+ F3 x9 i" z: [! E0 y
//PB0/1 作为模拟通道输入引脚* X5 P" g# A. }, s  \2 b; g
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
* @* n0 g/ A' v4 h+ _GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
# R" N  M3 j' _. G8 }4 E& h8 TGPIO_Init(GPIOB, &GPIO_InitStructure);
$ @% O5 q7 @! ?) F0 ]- F$ {0 [; [8 m  a
//PC0/1/2/3/4/5 作为模拟通道输入引脚* _* A9 v# I8 U: r- X  {
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;
( |- z! N, j) ^  L) kGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚; M# C! G9 ^% V) J  q7 h
GPIO_Init(GPIOC, &GPIO_InitStructure);
! X# z! v% d7 E) g& Y. @' U}6 a& d: C1 I/ Q8 o$ T; h

- m; P- i) @2 r}0 e5 L$ b( u# G0 L) _! y* e

1 K+ H; a5 x5 c1 o& U# ]& T! o, R' h; Q. |6 ~! ?
void RCC_Configuration(void)
5 M7 ], _9 S& o5 P{
' `+ z8 @# v' E; o  RErrorStatus HSEStartUpStatus;
! c( T$ H, ^6 S1 A: y/ Y( O0 P* E
% y) Y0 }1 u/ H( VRCC_DeInit(); //RCC 系统复位
: H* @9 n4 }- z, a- A2 D" lRCC_HSEConfig(RCC_HSE_ON); //开启HSE: T( A8 f* y7 }0 K, F7 H: O' @
HSEStartUpStatus = RCC_WaitForHSEStartUp(); //等待HSE准备好/ t1 e3 ]" f: ^+ p$ Y
if(HSEStartUpStatus == SUCCESS)( j3 G5 X2 N) _3 R% m. _
{
, H9 V3 Q+ u/ y+ gFLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //Enable Prefetch Buffer
6 d8 w1 b% {9 k) k( u3 H' }FLASH_SetLatency(FLASH_Latency_2); //Set 2 Latency cycles- G# B8 V7 i9 F; n( V: J
RCC_HCLKConfig(RCC_SYSCLK_Div1); //AHB clock = SYSCLK% ^/ k. U" ]5 J- G; S
RCC_PCLK2Config(RCC_HCLK_Div1); //APB2 clock = HCLK/ j+ g: i- {# g/ _  d$ Z
RCC_PCLK1Config(RCC_HCLK_Div2); //APB1 clock = HCLK/2; V* ^6 G2 |0 o4 j- E( n7 V  [
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_6); //PLLCLK = 12MHz * 6 = 72 MHz( V) I, H6 m( A, Y7 c- [
RCC_PLLCmd(ENABLE); //Enable PLL
9 O, _2 N; j  C7 ?/ Bwhile(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //Wait till PLL is ready5 w3 s/ D& o; w3 @! s+ A1 t
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //Select PLL as system clock source
0 l3 `: l/ t- c( owhile(RCC_GetSYSCLKSource() != 0x08); //Wait till PLL is used as system clock source: j* L! f8 \% d  S1 @
. G7 m1 W5 g; q! a" R; E
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB
; f, q) _/ Q" y| RCC_APB2Periph_GPIOC |RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO |RCC_APB2Periph_USART1, ENABLE ); //使能ADC1通道时钟,各个管脚时钟4 d$ A& k; S, K! J
. h* L- Y5 Y2 I) I1 o
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //72M/6=12,ADC最大时间不能超过14M
$ d  A+ m$ Y' x  U2 g" e, Z  J' ERCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
) j' _. S# {% l7 v: J' w! ^7 P* j
; r. w" r8 P$ w5 ]# D/ Q+ ?}# c. Y; e2 O' b% _# R% d! o3 {
}$ m. }3 o& v/ W2 x" F$ N* D  S
$ ^, o* Y/ @# \8 d& }( J6 X1 l
4 H1 M$ c' h5 ?) j  Z1 f4 w2 Q$ m- a
void ADC1_Configuration(void)
% B" }) G  M  F) a{. i5 E. O; {9 t" S( O6 I3 K  F
ADC_InitTypeDef ADC_InitStructure;
- `: }9 O5 |0 Y& B2 f/ ]# I+ p$ B4 z6 R4 k2 }) ^) E
ADC_DeInit(ADC1); //将外设 ADC1 的全部寄存器重设为缺省值- `- G, A7 c0 n* o, V
( y- D- b& x' u

( X$ O$ l9 X2 K0 IADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式3 F# G7 G3 n  R; ?6 u6 `, |
ADC_InitStructure.ADC_ScanConvMode =ENABLE; //模数转换工作在扫描模式# G6 t) K$ u/ b7 C+ N
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在连续转换模式
) \4 r: Q4 G, {ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发转换关闭" M; M4 @+ }+ x3 @! N8 n! E" [
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
" x5 a6 y6 I! q2 y7 xADC_InitStructure.ADC_NbrOfChannel = M; //顺序进行规则转换的ADC通道的数目
5 H0 ^, l9 N( ~" W" N+ e6 tADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
# I. `& ~  G, T4 Q: M
8 P. h: m% M3 O. i9 s
& y* m; M" @# p//设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
  Q& M9 N+ h( }//ADC1,ADC通道x,规则采样顺序值为y,采样时间为239.5周期  x, r! g6 H9 g, Y
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );: o+ O/ o( h7 W7 f* m8 A2 f
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 );
6 j: V$ d# ^6 \4 q% |+ n* g" d; ?ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5 );
, @5 A% x8 K9 d9 X- S# LADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5 );/ ^) W. b; c8 ]
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 5, ADC_SampleTime_239Cycles5 );1 b- F$ b0 J& w" d
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 6, ADC_SampleTime_239Cycles5 );
# _+ V# `9 O; u& WADC_RegularChannelConfig(ADC1, ADC_Channel_10, 7, ADC_SampleTime_239Cycles5 );
% x7 C4 d5 u; C+ wADC_RegularChannelConfig(ADC1, ADC_Channel_11, 8, ADC_SampleTime_239Cycles5 );1 g: [- v3 W. f$ F# y+ w
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 9, ADC_SampleTime_239Cycles5 );( p5 O- \. n' y* Q$ a7 `, w
ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 10, ADC_SampleTime_239Cycles5 );
* I& k3 j& X/ s/ ?; {& {ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 11, ADC_SampleTime_239Cycles5 );7 X) ]& g7 |3 h4 c# v' v
ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 12, ADC_SampleTime_239Cycles5 );% E& B/ ?& Y* j$ e- b0 ?3 Q
0 r, D1 X. Q) k) z; ?
// 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
7 q2 j0 H% S1 E' k7 C8 H" r6 r: OADC_DMACmd(ADC1, ENABLE);$ X. B( H; b* I. y* U
+ J, S6 y7 Q1 L/ o5 i

: t3 Q. L2 m- s; p9 |. A$ D% h6 u8 uADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
" n" o; C7 w; G" \9 v# ^; \- C5 h% _! I! R! \! ], K
ADC_ResetCalibration(ADC1); //复位指定的ADC1的校准寄存器7 X1 o' n$ G. f- i8 C& w' R

5 h* U: [$ ~. \; T# ^4 hwhile(ADC_GetResetCalibrationStatus(ADC1)); //获取ADC1复位校准寄存器的状态,设置状态则等待' G# t7 N" ], U

& j. b3 j5 T0 C: M4 |1 j7 b2 M) {3 E8 ^  k0 H/ X: g' ]+ n" f) `
ADC_StartCalibration(ADC1); //开始指定ADC1的校准状态: C; n+ H/ O  ?9 K
" n9 c+ ^! z' C! z( k7 n
while(ADC_GetCalibrationStatus(ADC1)); //获取指定ADC1的校准程序,设置状态则等待1 P$ x* S4 w1 e5 z! h1 |* L; F: s9 G9 O
5 |* E, \) J7 p  P$ b
0 U7 j. H' ^8 _& s5 u& `
}8 [0 R& O4 d# Z& `! Y; ]

2 D/ T( N, y; A+ V" n
9 q+ ^& u- l7 r1 Pvoid DMA_Configuration(void)% n; `) u. E+ _  j8 V  L9 r
{
1 S0 Y0 _7 J+ [7 H: Q
1 v; o# a+ F1 L3 \$ mDMA_InitTypeDef DMA_InitStructure;* V' o% r. z1 P: ]
DMA_DeInit(DMA1_Channel1); //将DMA的通道1寄存器重设为缺省值* a1 t0 f. ^& ~1 X
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; //DMA外设ADC基地址
' [! E1 l- g  D6 s; U- rDMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value; //DMA内存基地址0 X, g. h( R' a; X9 [
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //内存作为数据传输的目的地6 b; `- C: a+ |0 k  s5 T7 o9 U
DMA_InitStructure.DMA_BufferSize = N*M; //DMA通道的DMA缓存的大小
; e* A4 V, L: l  v! G8 NDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
8 p( ?0 a8 Q% p- UDMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增4 q& {" [" H. W6 u. @
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位( Z3 q; I) M  S" C* h5 i
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位5 T, Z2 j( ~4 {: _8 k4 I
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式
. c% F! T+ L5 M. P; W; jDMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级+ C0 k% G3 _8 o. Z3 n) O
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输2 G" |1 t/ U, k7 ?3 |3 v
DMA_Init(DMA1_Channel1, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道4 z3 e! o3 m3 o# C  Z, G' G

) R4 |0 x% G* `' j) P: M5 l9 n}
/ J0 l, u0 c" n9 P) _
! _' Y  Z8 U0 p% Q
/ o; Y5 U  Q/ J! N! A/ O//配置所有外设
) ?2 B3 ?0 ]# U1 d1 l1 {void Init_All_Periph(void)
8 E; I- x' a( B5 [4 W{& @) S8 V, [" q/ u4 W1 C% x# S% O
( `5 B, j  L0 e7 p
RCC_Configuration();( k% G' {; _& n! }" q( n/ `' P

1 T3 t9 o5 M- C/ Q, u8 [GPIO_Configuration();# s- U# y8 d" Z# R7 {* H$ ^

: ^; }. J; ?8 QADC1_Configuration();( a# {: Q6 i. H& b  n) ]  y

! L% X2 d% U; oDMA_Configuration();+ n: e( Y( S5 r; H$ P6 }/ W; j
/ B# [0 @8 s6 E5 Z" a; \
//USART1_Configuration();5 a; S8 \" H' j7 ~, L" e
USART_Configuration(9600);
( {1 u8 a0 U. w1 z2 t1 _  n/ B7 w7 L8 ?6 {  O
( q" C* `8 d) W/ [
}
& n8 W, U$ ^$ ^" I) s3 {3 C9 l; g- A4 Q! [" l

1 J$ [( Z, ?& u5 k, c& j. z1 Z3 D# J: Y* {; l
u16 GetVolt(u16 advalue)* k  L+ y. v+ `/ Z1 T2 u

+ o1 R% j5 l& m7 K4 W{
$ ]/ S. n4 ]0 g, f! ]4 X  Q3 b$ K
return (u16)(advalue * 330 / 4096); //求的结果扩大了100倍,方便下面求出小数: L) B( w6 `9 h' ]4 Q+ {
1 }3 {: ~8 g$ S" R
}$ w3 D# Q/ a& ]

1 t. C) w; i3 A0 N0 |  d' U) N. P4 p2 v' x
) l. z/ o3 k* `. f+ _
* E5 b, x3 N+ w. U2 r2 F0 b* h
void filter(void)
5 f* v+ h+ U4 X{
4 f) y3 s0 y" Q% iint sum = 0;& n2 B; H6 k, }$ P) ~% J! K$ G
u8 count;
  G5 }0 v: }9 k, q' ]3 Z$ ^' Ffor(i=0;i<12;i++)1 Q  @1 I# N# B! k  [, w

% F/ Q& B+ e( H{; z0 a0 b4 R( ~; _$ A. A/ t

" e3 H4 @5 v# q3 ]7 h5 d. Nfor ( count=0;count<N;count++)
7 G" n- t8 d( n& [! D0 o) \, i4 v- `* L; p/ s  ~' k; E
{
# I+ J& Y- R9 h3 w+ S
. v7 ^0 I) s  b; M3 psum += AD_Value[count];5 C+ Q& e+ E3 i7 h( l: t5 y
1 n! v% ]) Q4 j9 T
}3 Z6 ]3 U" H; x, G& h
' z/ j5 Q7 g( q
After_filter=sum/N;3 |" C2 e' O( T

5 z; L  r0 [% Nsum=0;
, C* \# T" O, _% n* T8 S}0 N* \6 b& d. \% h* @3 p

" M5 V. B, M, q0 l}( A% X+ s# C- s
! ?) m9 d1 t& h0 y2 z0 c( R0 U* K& a
5 S; Y% e4 v/ _4 X' a- T
+ V; a2 S+ W- M3 s1 x- _3 {
/ z; S8 ], r! X  |% O
int main(void)
7 `' Q5 w, v( s: ~0 @{$ M1 `. D) }4 O* |
8 l5 G7 e$ `- i) S- V
u16 value[M];
4 H  g! X. p6 y9 \6 B3 W
2 k: P& w7 U" r7 S' T& `8 a4 pinit_All_Periph();
' I4 \4 I, O5 k' `SysTick_Initaize();
( i! c! J3 V, l% h9 q6 V9 M( I8 }: Q1 T2 Q" F
+ N" f2 n5 N# ?
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
& }5 J, ^6 n) v+ zDMA_Cmd(DMA1_Channel1, ENABLE); //启动DMA通道
& Y4 ]3 t$ R: Q- E! cwhile(1)9 \4 j2 `# A0 v  l) f8 C
{
& K  q# F9 I9 P: N7 e, Hwhile(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);//等待传输完成否则第一位数据容易丢失
/ m% g- x* @! g! X/ v$ j! \# \
* W' {& B) d* w6 mfilter();
, N" C8 D8 T, e5 J$ U6 v2 yfor(i=0;i<12;i++)
& A5 g1 g! A* @! O( q7 b7 U{
  M3 X) u1 }: c) N4 B7 c; x. Kvalue= GetVolt(After_filter);
2 u; C! I9 r3 L1 u: \4 b7 e8 [' `4 ~  a' w- n. w4 u
printf("value[%d]:\t%d.%dv\n",i,value/100,value0) ;
- r1 c/ R, p' n, i( Z1 odelay_ms(100);
: E3 _6 j4 g+ C% D- A# ^9 o}
# l+ z( P9 l  N8 F}
! J- E3 u0 L8 t; R
  i: d& K7 X" d7 B* }# B6 s}
9 c3 j1 G3 ?% C. h$ [7 {& V4 L5 p总结
+ @1 W  h( E5 G该程序中的两个宏定义,M和N,分别代表有多少个通道,每个通道转换多少次,可以修改其值。9 m1 y! Q3 U! Z; R
曾出现的问题:配置时钟时要知道外部晶振是多少,以便准确配置时钟。将转换值由二进制转换为十进制时,要先扩大100倍,方便显示小数。最后串口输出时在 printf语句之前加这句代码,防止输出的第一位数据丢失:while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
赞 收藏 评论0 发布时间:2022-1-22 21:58

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版