
STM32 ADC多通道转换 描述:用ADC连续采集11路模拟信号,并由DMA传输到内存。ADC配置为扫描并且连续转换模式,ADC的时钟配置为12MHZ。在每次转换结束后,由DMA循环将转换的数据传输到内存中。ADC可以连续采集N次求平均值。最后通过串口传输出最后转换的结果。 程序如下: #i nclude "stm32f10x.h" //这个头文件包括STM32F10x所有外围寄存器、位、内存映射的定义 #i nclude "eval.h" //头文件(包括串口、按键、LED的函数声明) #i nclude "SysTickDelay.h" #i nclude "UART_INTERFACE.h" #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次 #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 void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure;( v, |% t5 T; R 5 F; u- [" C9 B: } GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //因为USART1管脚是以复用的形式接到GPIO口上的,所以使用复用推挽式输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); 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; GPIO_Init(GPIOA, &GPIO_InitStructure);. |1 K7 s+ U, J7 s //PA0/1/2 作为模拟通道输入引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0| GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚 _( x0 G& |% O# g* \ GPIO_Init(GPIOA, &GPIO_InitStructure);5 i* k, s2 `2 p( ?& k //PB0/1 作为模拟通道输入引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚) P8 L* V( n" M+ T; |3 y6 {/ D GPIO_Init(GPIOB, &GPIO_InitStructure); 2 B& V( I8 T& `2 |/ u# ~7 x //PC0/1/2/3/4/5 作为模拟通道输入引脚 GPIO_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; //模拟输入引脚 GPIO_Init(GPIOC, &GPIO_InitStructure); } } ( 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; ' w" t: P; y. [8 n q RCC_DeInit(); //RCC 系统复位* I6 N+ G8 D& {) d RCC_HSEConfig(RCC_HSE_ON); //开启HSE HSEStartUpStatus = RCC_WaitForHSEStartUp(); //等待HSE准备好 if(HSEStartUpStatus == SUCCESS) {% {( D7 c, w, G) a3 K! O FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //Enable Prefetch Buffer FLASH_SetLatency(FLASH_Latency_2); //Set 2 Latency cycles RCC_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 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //Select PLL as system clock source while(RCC_GetSYSCLKSource() != 0x08); //Wait till PLL is used as system clock source & 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 RCC_ADCCLKConfig(RCC_PCLK2_Div6); //72M/6=12,ADC最大时间不能超过14M RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输* B/ q8 x3 q# Z- j9 }/ n4 ^& | }5 }- q" W' d+ B6 d) N } 0 ~; o! O/ g8 k" V1 s void ADC1_Configuration(void): p1 Y" g" w1 [. ?2 {2 o! }) Z6 s {6 Y, m8 ?+ N( U# C2 m ADC_InitTypeDef ADC_InitStructure; ADC_DeInit(ADC1); //将外设 ADC1 的全部寄存器重设为缺省值 * d0 a4 G2 ]) \& |" _) s ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式 ADC_InitStructure.ADC_ScanConvMode =ENABLE; //模数转换工作在扫描模式+ ^" S8 Y F: ` ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在连续转换模式 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发转换关闭5 N( I" G/ h) S$ h, P6 T ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐 ADC_InitStructure.ADC_NbrOfChannel = M; //顺序进行规则转换的ADC通道的数目( J! o$ O$ B$ }& S ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 3 V+ C+ |+ d3 p: c+ N e. d) v //设置指定ADC的规则组通道,设置它们的转化顺序和采样时间 //ADC1,ADC通道x,规则采样顺序值为y,采样时间为239.5周期 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5 ); 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 ); 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 ); ADC_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 ); // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数) ADC_DMACmd(ADC1, ENABLE);" ~" O+ S* p) P% r4 }: ^: i0 N $ B+ @' Z& n" Y- }% C ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1 ADC_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 ADC_StartCalibration(ADC1); //开始指定ADC1的校准状态, i% U6 d7 z c+ B0 b . ^& z3 O w; H. k# H% r, t# P while(ADC_GetCalibrationStatus(ADC1)); //获取指定ADC1的校准程序,设置状态则等待 8 j( R2 J0 ?: I: o/ @- w 1 b* W4 @; g( a, r2 Q" E! b6 M } 3 c% n! T; k5 G t; Q& a+ A0 \" i void DMA_Configuration(void) {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内存基地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //内存作为数据传输的目的地" W, S; J, u2 n. e5 i DMA_InitStructure.DMA_BufferSize = N*M; //DMA通道的DMA缓存的大小 DMA_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位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输 DMA_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 //配置所有外设 void Init_All_Periph(void)* A; z4 R/ M5 @4 M. s2 i5 a { * s- L+ D* p; j/ @( F/ o, T RCC_Configuration(); GPIO_Configuration(); * k0 t% ^. T& c! x( u8 \* | ADC1_Configuration();: p3 V4 ~' q6 E3 v9 @" v8 T @ DMA_Configuration(); //USART1_Configuration(); USART_Configuration(9600);2 F+ R: y0 T; P! M0 {2 S* T. T: | + H' [1 o4 t0 t' \, @/ d }' R, U6 p% V' _6 a u16 GetVolt(u16 advalue) - y# l9 h) E8 u7 Q& \ J0 z7 n { 2 @! 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 void filter(void)2 M, W0 {1 {1 C3 Y {6 R0 U9 O1 {% ]0 d! C$ w& y$ p int sum = 0; u8 count;3 K# k4 _4 O" {( g for(i=0;i<12;i++)- w3 {" v1 j6 L! ? { ! T- u1 x2 U% a) L2 | for ( count=0;count<N;count++) {8 [& N2 g) }* W0 n# i sum += 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; 9 M9 k+ f7 R i) w sum=0;$ }6 A: f' G+ u, G }; m h3 Y3 U0 |. ^& q3 ? } ; r; r; \# X/ R 4 w4 V6 N9 t' u int main(void) { % D; X- l# t& B* i2 D& ?- z u16 value[M];8 x; }5 C2 L6 S. L1 \ init_All_Periph();* v: K/ K3 ]/ i7 [. G* _+ x9 R SysTick_Initaize();# S1 Q+ y h! L& h; E7 v. W ; ` {* 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(); for(i=0;i<12;i++) { value= GetVolt(After_filter); + \ 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); } } } 总结0 Z7 v* U2 I9 S: h( ^, Z 该程序中的两个宏定义,M和N,分别代表有多少个通道,每个通道转换多少次,可以修改其值。 曾出现的问题:配置时钟时要知道外部晶振是多少,以便准确配置时钟。将转换值由二进制转换为十进制时,要先扩大100倍,方便显示小数。最后串口输出时在 printf语句之前加这句代码,防止输出的第一位数据丢失:while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); |