STMCU小助手
发布时间:2022-1-22 21:58
|
STM32 ADC多通道转换 描述:用ADC连续采集11路模拟信号,并由DMA传输到内存。ADC配置为扫描并且连续转换模式,ADC的时钟配置为12MHZ。在每次转换结束后,由DMA循环将转换的数据传输到内存中。ADC可以连续采集N次求平均值。最后通过串口传输出最后转换的结果。# S3 K7 N# i. j' [2 z7 B% P2 V 程序如下:! @9 q6 t+ C' V6 d #i nclude "stm32f10x.h" //这个头文件包括STM32F10x所有外围寄存器、位、内存映射的定义 #i nclude "eval.h" //头文件(包括串口、按键、LED的函数声明) #i nclude "SysTickDelay.h"6 x2 I7 M. N& s; g2 Q8 D3 ^. W3 V/ x #i nclude "UART_INTERFACE.h"! ~( k6 a5 w# ]; x9 V. l, ^ #i nclude <stdio.h>$ @ ]+ D6 o! ? 6 h5 i0 h- u* D0 n! Y* ^' Q& `* J #define N 50 //每通道采50次" }+ b7 r/ P3 O" J# C$ y- F$ X #define M 12 //为12个通道) A/ b& i8 b7 L9 i ; ~! e# x: l& D vu16 AD_Value[N][M]; //用来存放ADC转换结果,也是DMA的目标地址 vu16 After_filter[M]; //用来存放求平均值之后的结果) V' k" `# h# P7 d6 O int i; & l1 l- g' m! s- _ void GPIO_Configuration(void)6 Q9 C$ N3 t) A. x- D {: U& g- j9 G/ n0 ?" t& l" A. M* e, G GPIO_InitTypeDef GPIO_InitStructure;) B7 q, w5 P0 t: A3 I * ^3 P( }; C- r9 `( g/ u GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //因为USART1管脚是以复用的形式接到GPIO口上的,所以使用复用推挽式输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;* P$ \8 |9 @: L+ R2 W* q GPIO_Init(GPIOA, &GPIO_InitStructure);) p2 K) \6 G, d1 \$ G , D7 S9 C; \# e8 U, ? GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure);% ~. G/ Z: g! ~; g P" _$ x - g1 k/ n+ G+ Z$ h' v% w ! K, B" e9 L/ D( E$ Q) 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; //模拟输入引脚 GPIO_Init(GPIOA, &GPIO_InitStructure);' v* k" g X F6 f, _% u: D+ V //PB0/1 作为模拟通道输入引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚! t; [5 X- `8 f: s, H- ^% t GPIO_Init(GPIOB, &GPIO_InitStructure); 2 l. N4 i7 S+ r0 l/ ?9 D& O //PC0/1/2/3/4/5 作为模拟通道输入引脚5 c* ^8 Q9 d7 u/ J9 v8 C4 B! ^- ] GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;2 r8 E$ R. D2 d1 w _. [ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚 GPIO_Init(GPIOC, &GPIO_InitStructure); }8 ]. L; C# O# l }/ e# Z& s9 A2 J" o 8 h) a) I/ O) ~6 Y' J ]( ~7 e void RCC_Configuration(void)& P$ E2 L9 B1 s; d# | { ErrorStatus HSEStartUpStatus; RCC_DeInit(); //RCC 系统复位 RCC_HSEConfig(RCC_HSE_ON); //开启HSE7 n' d5 v: c. B8 f* f) M HSEStartUpStatus = RCC_WaitForHSEStartUp(); //等待HSE准备好 if(HSEStartUpStatus == SUCCESS) {; P/ ~ [8 t3 H, y& u FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //Enable Prefetch Buffer FLASH_SetLatency(FLASH_Latency_2); //Set 2 Latency cycles2 I( g/ _) c3 w. u( K# b RCC_HCLKConfig(RCC_SYSCLK_Div1); //AHB clock = SYSCLK; R- N, e! g0 \# b' L RCC_PCLK2Config(RCC_HCLK_Div1); //APB2 clock = HCLK, _$ m3 h/ N M3 Y- C; x: a# r# X RCC_PCLK1Config(RCC_HCLK_Div2); //APB1 clock = HCLK/2 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_6); //PLLCLK = 12MHz * 6 = 72 MHz; f, `( Z. J3 S# t9 K. d6 e RCC_PLLCmd(ENABLE); //Enable PLL, }. d) m! d& t) x: { while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //Wait till PLL is ready! D2 ]% x- j+ C& r# y5 t# q& f F 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$ q0 Z5 V8 N: L+ W5 X8 o RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC |RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO |RCC_APB2Periph_USART1, ENABLE ); //使能ADC1通道时钟,各个管脚时钟1 ?( j# `* C( \ J# J3 E H RCC_ADCCLKConfig(RCC_PCLK2_Div6); //72M/6=12,ADC最大时间不能超过14M RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输: s! g& w5 A2 \0 V4 q: k* y 5 ?+ {. ?- |* q* ~4 Z } }$ \) P7 o; N& U * X0 q; M8 k0 z& a" X void ADC1_Configuration(void) { ADC_InitTypeDef ADC_InitStructure;# T2 I1 l a& J- O: G. e' A ADC_DeInit(ADC1); //将外设 ADC1 的全部寄存器重设为缺省值( Y1 s' A" t# Z ( s2 g9 a- d2 r1 r/ E4 ]* Q5 G ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式 ADC_InitStructure.ADC_ScanConvMode =ENABLE; //模数转换工作在扫描模式 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在连续转换模式 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发转换关闭' J% s4 ~- h" \% m/ ?* t( S ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐0 u4 U; x a% R ADC_InitStructure.ADC_NbrOfChannel = M; //顺序进行规则转换的ADC通道的数目 ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 # t- P8 R; `8 ~ 2 z7 H' x. e4 E //设置指定ADC的规则组通道,设置它们的转化顺序和采样时间, x3 s$ _; w5 [) g: m$ O/ l //ADC1,ADC通道x,规则采样顺序值为y,采样时间为239.5周期9 @) S' O; B& _; F; h 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 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 5, ADC_SampleTime_239Cycles5 );0 q! d! G8 @1 V1 ? ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 6, ADC_SampleTime_239Cycles5 );& x1 _. B8 _: u" I ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 7, ADC_SampleTime_239Cycles5 );! _- E7 d; o+ Q- x# w3 V1 F7 O5 | ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 8, ADC_SampleTime_239Cycles5 );9 c8 k; p' s: \+ j( R ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 9, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 10, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 11, ADC_SampleTime_239Cycles5 );0 {9 J* D/ l2 x7 r0 r ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 12, ADC_SampleTime_239Cycles5 );9 \- T* ^9 H/ y/ P3 ? // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)5 z6 F% J$ m& O X0 m9 p* t# P" E2 b ADC_DMACmd(ADC1, ENABLE);* S( M" P9 u. R+ W- Y ) }( I* D3 p. f5 V ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1, l H! {; ~8 I; }( S6 e# n9 h 4 V$ d8 x3 R3 H5 L+ S! z ADC_ResetCalibration(ADC1); //复位指定的ADC1的校准寄存器 " c- f( p" Y) L# Y' g% s9 v8 w while(ADC_GetResetCalibrationStatus(ADC1)); //获取ADC1复位校准寄存器的状态,设置状态则等待: B6 r7 o; I& h/ T) [1 `6 m& g ADC_StartCalibration(ADC1); //开始指定ADC1的校准状态 ) B' `; ?9 O* i5 C9 R b: G8 d while(ADC_GetCalibrationStatus(ADC1)); //获取指定ADC1的校准程序,设置状态则等待# I0 o- t% G" L. ? . r& w2 b4 `# g1 k0 t; H" d* W }# Y( P3 Q; D [9 a, R6 x8 V. ~ / @( O( ?& S8 s8 ^ K8 ` void DMA_Configuration(void)9 e, \, ~3 \ ], f. b {$ l# Q* c: v1 ^- P! D1 v DMA_InitTypeDef DMA_InitStructure;% V- o |- x$ }9 M; S# V* B DMA_DeInit(DMA1_Channel1); //将DMA的通道1寄存器重设为缺省值5 f1 y8 F: Z) i1 Y9 U6 P1 L: N DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; //DMA外设ADC基地址# t: N% X! Z) _7 w; n$ y" T) N DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value; //DMA内存基地址& j" U( `! @: @: }9 U4 \ DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //内存作为数据传输的目的地 DMA_InitStructure.DMA_BufferSize = N*M; //DMA通道的DMA缓存的大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增$ j0 ^' q, w5 a. b# c" v3 D" X7 o DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位3 B' `# X* d! N' s+ l5 f DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式4 o+ K& [! p+ g DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级, }; L8 x9 a8 K% q9 m DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输 DMA_Init(DMA1_Channel1, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道! F; n& g5 D2 H }+ X ) `$ [+ o1 {9 m }( o- V Z( C6 R 0 f/ @" L: n$ [! _& X4 z: P3 d //配置所有外设* _4 x4 b3 C, s5 L. Y/ g void Init_All_Periph(void)+ E8 H9 [# S: z' r4 W {. Z% c; m: \: }1 F 4 s, W# f# K" y8 r5 n RCC_Configuration();3 A; C6 m0 Y/ r% d GPIO_Configuration(); ADC1_Configuration(); % g0 D4 S$ h# F5 p( @' @( b; v1 G H DMA_Configuration();0 e8 y8 G) [( X; T2 {2 ?, R, L & M' O4 Z T# y$ E4 M1 a# l //USART1_Configuration(); USART_Configuration(9600);' H6 G3 g" X$ f1 H1 x5 ]- a( [ $ g# R) u1 Q9 `7 ^ % s( M& Y, B) s } ! o' [- y+ x- J% A 9 S+ _0 b7 f+ N u16 GetVolt(u16 advalue) z. E) B1 H9 y6 [% p 6 M3 I8 z; D- {4 V6 L7 _ { return (u16)(advalue * 330 / 4096); //求的结果扩大了100倍,方便下面求出小数 } 9 E) t/ v, `) v2 U6 d" ]- E" e + l) t4 {1 ~6 @3 _3 y5 e: _% B Q) {/ T" C! O3 i. p1 d void filter(void) { int sum = 0;, @% X+ c( w$ s6 y u8 count;9 v+ C7 F g: M for(i=0;i<12;i++) { for ( count=0;count<N;count++) { 0 K: f8 `- z: } sum += AD_Value[count]; a" X. M# [$ m/ M( p }! }. C9 e; D" d+ Y* }# ]6 s6 l4 h After_filter=sum/N; sum=0;2 M7 I) G2 l7 ^, Z" x }( N! D/ _# J0 U }) \ z* c* r% h2 n , k, Z: e z7 n" G- E0 ]% L- i1 E( z( v , i4 r4 I! D+ ]3 q8 s5 B2 O( I " V# U! s& S1 z6 j9 h7 U/ P int main(void) { u16 value[M]; init_All_Periph(); SysTick_Initaize();( X+ Z; o m8 F( C8 { ADC_SoftwareStartConvCmd(ADC1, ENABLE);9 ]# w4 S4 S0 M! U3 |/ U6 O DMA_Cmd(DMA1_Channel1, ENABLE); //启动DMA通道 while(1)! W- \5 I( g5 ]9 S7 L# A { while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);//等待传输完成否则第一位数据容易丢失0 D% X+ D1 C* i' j# w filter();. J- B6 `, N& U3 w' ]0 E% Y3 N for(i=0;i<12;i++)+ `6 t6 c& V* D, n8 R& N { value= GetVolt(After_filter);- S$ r0 J4 y% ? printf("value[%d]:\t%d.%dv\n",i,value/100,value0) ; delay_ms(100);! ^* g+ `# F2 R' d: i } }. s1 c5 Y# E, O$ r1 c) f 9 J. ?4 e* |% ~; {+ B' n2 y2 } } 总结 该程序中的两个宏定义,M和N,分别代表有多少个通道,每个通道转换多少次,可以修改其值。& p! ]$ Q$ ^+ d& y 曾出现的问题:配置时钟时要知道外部晶振是多少,以便准确配置时钟。将转换值由二进制转换为十进制时,要先扩大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库
微信公众号
手机版