STMCU小助手
发布时间:2022-1-22 21:58
|
STM32 ADC多通道转换 描述:用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所有外围寄存器、位、内存映射的定义 #i nclude "eval.h" //头文件(包括串口、按键、LED的函数声明) #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> #define N 50 //每通道采50次5 e4 G9 ?* g# N6 ?# G* }1 W #define M 12 //为12个通道 " U7 ?; D8 z4 C& p* R vu16 AD_Value[N][M]; //用来存放ADC转换结果,也是DMA的目标地址7 n1 k# X1 ]* s8 j vu16 After_filter[M]; //用来存放求平均值之后的结果 int i;1 g3 R! j* z% e6 U# d4 K2 x void GPIO_Configuration(void). p; R# C( Q, a( ^1 [- E; C- ? { GPIO_InitTypeDef GPIO_InitStructure;, K, e8 b- J6 r" _ GPIO_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; GPIO_Init(GPIOA, &GPIO_InitStructure);9 W( s1 S3 E b' W' A . h: `* u; W% h GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_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 //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; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚 GPIO_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; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚 GPIO_Init(GPIOB, &GPIO_InitStructure); $ {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; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚; M# C! G9 ^% V) J q7 h GPIO_Init(GPIOC, &GPIO_InitStructure); }6 a& d: C1 I/ Q8 o$ T; h }0 e5 L$ b( u# G0 L) _! y* e # ]& T! o, R' h; Q. |6 ~! ? void RCC_Configuration(void) { ErrorStatus HSEStartUpStatus; RCC_DeInit(); //RCC 系统复位 RCC_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. _ { FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //Enable Prefetch Buffer 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 while(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 while(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 | 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 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输 }# 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) {. i5 E. O; {9 t" S( O6 I3 K F ADC_InitTypeDef ADC_InitStructure; / ]# I+ p$ B4 z6 R4 k2 }) ^) E ADC_DeInit(ADC1); //将外设 ADC1 的全部寄存器重设为缺省值- `- G, A7 c0 n* o, V ( y- D- b& x' u ADC_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; //模数转换工作在连续转换模式 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发转换关闭" M; M4 @+ }+ x3 @! N8 n! E" [ ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐 ADC_InitStructure.ADC_NbrOfChannel = M; //顺序进行规则转换的ADC通道的数目 ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 //设置指定ADC的规则组通道,设置它们的转化顺序和采样时间 //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 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5 ); ADC_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 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 7, ADC_SampleTime_239Cycles5 ); ADC_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 ); 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通道等参数) ADC_DMACmd(ADC1, ENABLE);$ X. B( H; b* I. y* U + J, S6 y7 Q1 L/ o5 i ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1 # ^; \- C5 h% _! I! R! \! ], K ADC_ResetCalibration(ADC1); //复位指定的ADC1的校准寄存器7 X1 o' n$ G. f- i8 C& w' R while(ADC_GetResetCalibrationStatus(ADC1)); //获取ADC1复位校准寄存器的状态,设置状态则等待' G# t7 N" ], U 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; ] void DMA_Configuration(void)% n; `) u. E+ _ j8 V L9 r { DMA_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基地址 DMA_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缓存的大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变 DMA_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; //工作在循环缓存模式 DMA_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 } //配置所有外设 void Init_All_Periph(void) {& @) S8 V, [" q/ u4 W1 C% x# S% O ( `5 B, j L0 e7 p RCC_Configuration();( k% G' {; _& n! }" q( n/ `' P GPIO_Configuration();# s- U# y8 d" Z# R7 {* H$ ^ ADC1_Configuration();( a# {: Q6 i. H& b n) ] y DMA_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); 2 t1 _ n/ B7 w7 L8 ?6 { O ( q" C* `8 d) W/ [ } 9 l; g- A4 Q! [" l 5 k, c& j. z1 Z3 D# J: Y* {; l u16 GetVolt(u16 advalue)* k L+ y. v+ `/ Z1 T2 u { ! ]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& ] 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) { int sum = 0;& n2 B; H6 k, }$ P) ~% J! K$ G u8 count; for(i=0;i<12;i++)1 Q @1 I# N# B! k [, w {; z0 a0 b4 R( ~; _$ A. A/ t for ( count=0;count<N;count++) - `* L; p/ s ~' k; E { sum += 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 sum=0; }0 N* \6 b& d. \% h* @3 p }( 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) {$ M1 `. D) }4 O* | 8 l5 G7 e$ `- i) S- V u16 value[M]; init_All_Periph(); SysTick_Initaize(); 6 V9 M( I8 }: Q1 T2 Q" F + N" f2 n5 N# ? ADC_SoftwareStartConvCmd(ADC1, ENABLE); DMA_Cmd(DMA1_Channel1, ENABLE); //启动DMA通道 while(1)9 \4 j2 `# A0 v l) f8 C { while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);//等待传输完成否则第一位数据容易丢失 filter(); for(i=0;i<12;i++) { value= GetVolt(After_filter); 7 e8 [' `4 ~ a' w- n. w4 u printf("value[%d]:\t%d.%dv\n",i,value/100,value0) ; delay_ms(100); } } } 总结 该程序中的两个宏定义,M和N,分别代表有多少个通道,每个通道转换多少次,可以修改其值。9 m1 y! Q3 U! Z; R 曾出现的问题:配置时钟时要知道外部晶振是多少,以便准确配置时钟。将转换值由二进制转换为十进制时,要先扩大100倍,方便显示小数。最后串口输出时在 printf语句之前加这句代码,防止输出的第一位数据丢失:while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); |
实战经验 | 基于STM32G0B1的全桥移相+同步整流PWM解决方案
DMA 原理从 0 到 1:为什么它能解放 CPU?一文讲透 DMA 的工作流程
【福利三:逢7发帖赢大礼】8、基于STM32G070板子的Ymodem串口协议IAP Bootloader开发
【福利三:逢7发帖赢大礼】7、基于STM32G070板子TF卡的FATFS移植
【福利三:逢7发帖赢大礼】6、基于STM32G070板子spiflash的FATFS移植
【福利三:逢7发帖赢大礼】5、基于STM32G070板子IRDA红外遥控接收解码
【福利三:逢7发帖赢大礼】4、基于STM32G070板子SPI flash 移植SFUD库
【福利三:逢7发帖赢大礼】基于STM32G070板子的uart shell移植设计
【福利三:逢7发帖赢大礼】3、基于STM32G070板子的YModem串口协议通信
【福利三:逢7发帖赢大礼】2、基于STM32G070板子的OLED移植U8G2库
微信公众号
手机版