STMCU小助手
发布时间:2022-1-22 21:58
|
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"* z# Z( A+ d# d8 {8 Y* ?9 A #i nclude "UART_INTERFACE.h" #i nclude <stdio.h> " \( i+ a+ {1 H% |. Y9 p$ ], d; ~: { #define N 50 //每通道采50次 #define M 12 //为12个通道* h4 R, D$ w' G vu16 AD_Value[N][M]; //用来存放ADC转换结果,也是DMA的目标地址 vu16 After_filter[M]; //用来存放求平均值之后的结果, t6 r5 Z" s6 L* k. v int i; 8 T( Z# E8 c7 K6 d% v- n 7 a0 k1 j' h4 R6 o! h void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure;# j5 i2 W5 b) S+ j" d5 a ( u9 v1 Y Y& m+ O! t& u N 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); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;' G( C4 W4 B V3 S( F3 { GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;4 ~# C/ u6 \. T+ ? GPIO_Init(GPIOA, &GPIO_InitStructure);) C# G' N4 |# `. J & M& b2 G5 V! J ( A. i" q. \5 @% E9 h : J- I" |- h6 m //PA0/1/2 作为模拟通道输入引脚. ~" F) [ t: v/ Y 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); ! U l. t& H5 C9 M- c, W //PB0/1 作为模拟通道输入引脚; E4 @9 Z2 c4 b! u# \ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚/ a0 q7 Q( @: {8 A) j GPIO_Init(GPIOB, &GPIO_InitStructure);' B/ p' T; o. i/ E $ m4 |( P; Z: \3 B //PC0/1/2/3/4/5 作为模拟通道输入引脚1 `+ ]! Q7 u3 r+ K' B- j GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;1 u0 h/ o5 Y& B GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚8 `! Q8 k* c" T4 [ GPIO_Init(GPIOC, &GPIO_InitStructure);& N; b+ P! M8 i' _) Z3 ] }- i# i/ Z+ m" j4 |5 _ }' x1 h4 z6 I6 o void RCC_Configuration(void); H5 X/ ^7 G9 R8 \3 w) Y( u { ?/ n9 A* p& N) Z ErrorStatus HSEStartUpStatus; RCC_DeInit(); //RCC 系统复位 RCC_HSEConfig(RCC_HSE_ON); //开启HSE% f4 ~! Z* E( a$ d/ ] HSEStartUpStatus = RCC_WaitForHSEStartUp(); //等待HSE准备好 if(HSEStartUpStatus == SUCCESS)5 @9 d+ v5 l% q {5 h( l; Y( Q! R. z/ G; w0 o FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //Enable Prefetch Buffer3 ]1 ? R. a; G) j FLASH_SetLatency(FLASH_Latency_2); //Set 2 Latency cycles1 E/ z- L# d9 d: F RCC_HCLKConfig(RCC_SYSCLK_Div1); //AHB clock = SYSCLK2 z) \9 z) I. X( M- @1 q { RCC_PCLK2Config(RCC_HCLK_Div1); //APB2 clock = HCLK- H9 y! ]' V3 A6 P6 C, i' J RCC_PCLK1Config(RCC_HCLK_Div2); //APB1 clock = HCLK/2 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_6); //PLLCLK = 12MHz * 6 = 72 MHz0 H7 J! L6 `3 J# z$ |: h RCC_PLLCmd(ENABLE); //Enable PLL 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+ Q! Q" V2 J. M& m RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB) n F$ P" ^4 `% x! \/ O! _4 I | RCC_APB2Periph_GPIOC |RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO |RCC_APB2Periph_USART1, ENABLE ); //使能ADC1通道时钟,各个管脚时钟 * N* @% W6 b0 }9 W4 a. p6 f1 l& ^4 { RCC_ADCCLKConfig(RCC_PCLK2_Div6); //72M/6=12,ADC最大时间不能超过14M RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输3 Z! m/ Z6 K7 F2 v } } 7 r4 e1 ^( \/ e1 K6 w& d7 P3 \% u- G- [ 9 F1 W2 ?5 E) |7 T& {" ^ void ADC1_Configuration(void) { ADC_InitTypeDef ADC_InitStructure; ! x6 }7 r; c/ G1 w( ?7 j ADC_DeInit(ADC1); //将外设 ADC1 的全部寄存器重设为缺省值 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式6 w) d9 x5 D) s0 {1 o ADC_InitStructure.ADC_ScanConvMode =ENABLE; //模数转换工作在扫描模式 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在连续转换模式8 p5 k4 |& |1 K8 E5 I ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发转换关闭 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐 ADC_InitStructure.ADC_NbrOfChannel = M; //顺序进行规则转换的ADC通道的数目 ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器5 X; T9 r, i* E / `- o- D# ^% k/ ^6 \9 r% Z/ a# f ) O* K/ ]. Q" E8 Z0 u //设置指定ADC的规则组通道,设置它们的转化顺序和采样时间6 w0 I2 u7 t7 |# F0 I: c- Y/ W //ADC1,ADC通道x,规则采样顺序值为y,采样时间为239.5周期 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );; l" M1 h) _2 w6 h) [% f ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5 ); C) F1 M5 K4 j: L2 Y ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5 );( w6 f5 {4 F- |- |4 N8 x8 q* b) n ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 5, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 6, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 7, ADC_SampleTime_239Cycles5 );- s2 I/ ], `7 x: o% _ ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 8, ADC_SampleTime_239Cycles5 );* k9 [* F5 {3 w ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 9, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 10, ADC_SampleTime_239Cycles5 );1 ?1 a- n0 S2 [* z/ p+ S- Q5 ~ ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 11, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 12, ADC_SampleTime_239Cycles5 ); 1 m, c& B. p! h // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数) ADC_DMACmd(ADC1, ENABLE); 3 U9 C- D; E2 k3 c# n. x ADC_Cmd(ADC1, ENABLE); //使能指定的ADC14 Z/ M' ^1 s5 m! h* \- ]" n ADC_ResetCalibration(ADC1); //复位指定的ADC1的校准寄存器; I- J0 G1 S) q- x, j: B/ _# A) y while(ADC_GetResetCalibrationStatus(ADC1)); //获取ADC1复位校准寄存器的状态,设置状态则等待 4 t! n& K. P/ G ADC_StartCalibration(ADC1); //开始指定ADC1的校准状态, @1 N% L1 @4 M \2 Q while(ADC_GetCalibrationStatus(ADC1)); //获取指定ADC1的校准程序,设置状态则等待 ; `/ U1 I; { g- s O& N$ ^$ T, g+ j } ) X6 o3 R+ m/ \- V$ b7 E void DMA_Configuration(void) {; v D. e2 I! z* P) @" A/ ` DMA_InitTypeDef DMA_InitStructure;/ p0 O" B4 Y( |' c$ R* Q DMA_DeInit(DMA1_Channel1); //将DMA的通道1寄存器重设为缺省值 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; //DMA外设ADC基地址9 G R+ ^. P8 Z2 x2 j( _ C DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value; //DMA内存基地址$ G: o) P2 Q; ^/ @3 S4 M6 k2 R" | DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //内存作为数据传输的目的地 DMA_InitStructure.DMA_BufferSize = N*M; //DMA通道的DMA缓存的大小3 z. v2 r! |! \) R% J( A$ e0 }& T DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增6 ^9 v+ p1 u1 J( v3 w DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式% u& b9 C2 B: m# | DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级! ]7 g+ ^8 N* K2 L+ {4 v DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输 DMA_Init(DMA1_Channel1, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道 , s0 s" B" k$ n, o2 j }7 L9 }/ G1 J2 M: n: K" W! U //配置所有外设 void Init_All_Periph(void) { + T$ U: T4 l5 ]) W RCC_Configuration(); GPIO_Configuration();+ H0 m1 ~4 }; b1 I" g" k ADC1_Configuration(); 0 }. \: _! q8 }3 g! p DMA_Configuration(); 5 a* B+ O0 y6 @. K. d" P //USART1_Configuration(); USART_Configuration(9600);1 K, Z. J/ z% ]0 A, H# J; i / _9 s- P: _2 Q }7 Q, G1 `1 n5 R" S2 r" y. |8 |; l / w/ q3 |: U0 T! S$ U u16 GetVolt(u16 advalue)! K- e0 a2 n: D: _/ v- [ 8 o1 t/ c, ?8 G# |: w { ( S2 }2 Q6 b7 A8 A( w( Q' {, ] return (u16)(advalue * 330 / 4096); //求的结果扩大了100倍,方便下面求出小数 8 ?5 \3 B+ {$ p, ] g) u9 e }, c3 Y/ d4 C: a6 W. V/ G: e! b ! m+ @& H' k1 u# n ' z6 l, ~! j: f# l: Q 0 m+ \9 t% D+ a# _; r: K # x8 t3 ]/ ^+ r. H* z void filter(void) {4 o* B9 u9 f+ b Y( `6 H9 \9 Y int sum = 0;! ?" N' B* m& h1 O U f% M u8 count; for(i=0;i<12;i++) { for ( count=0;count<N;count++)4 @) U( Q3 {/ r3 Y' `( q* Y! L / A$ Q6 r# n$ A! d$ T; r {4 e. V! ]4 N: J2 S! z# e+ Y # W8 B g# b4 n1 N sum += AD_Value[count];5 A# v& e+ r$ e, g% C" ^2 Q7 f }0 }8 R) @. K* Z4 V2 P: y After_filter=sum/N;7 m! ~ g4 V0 K o" T : o n3 e/ ^0 G; _ sum=0; }: A5 @+ { l( e' I) J: ] }$ e# K7 m7 _! Q6 |6 G ! \- Y1 a( d* `8 H0 ] 8 p3 I u) {$ Y; O int main(void)' t0 a5 c2 m& d T" e5 } { u16 value[M];+ X# `% J" s5 T3 O$ r init_All_Periph();7 p) q2 v, D4 j7 H4 |) t SysTick_Initaize();! N! u0 i$ ^8 S/ m) @% n ADC_SoftwareStartConvCmd(ADC1, ENABLE);+ z: c9 a" X$ |: L DMA_Cmd(DMA1_Channel1, ENABLE); //启动DMA通道2 g8 q2 k8 |( T$ G while(1) { while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);//等待传输完成否则第一位数据容易丢失/ u" N' T* d5 P) ?* Z+ O( n+ y filter(); for(i=0;i<12;i++) { value= GetVolt(After_filter); ~2 H' P2 K' O& h0 S; r $ @: A7 f" [2 y% q2 V+ e. n/ r! H printf("value[%d]:\t%d.%dv\n",i,value/100,value0) ;- G0 h2 a1 ~& [/ C2 K delay_ms(100);4 A6 j1 |# j, j" b* T }7 ^9 O7 P& v9 J0 \ } % C0 t5 d% e; f% M& p* J4 d8 J0 K } 总结 该程序中的两个宏定义,M和N,分别代表有多少个通道,每个通道转换多少次,可以修改其值。 曾出现的问题:配置时钟时要知道外部晶振是多少,以便准确配置时钟。将转换值由二进制转换为十进制时,要先扩大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库
微信公众号
手机版