折腾了两天ADC多通道采样,采样的结果都很乱,完全不是预期值。在amobbs求助也没有找到结果。于是决定从简单开始,一步步折腾着破ADC。

【ADC试验1实验说明】

            1、这个实验仅仅是初始化一个ADC,对其输入进行采样。

            2、使用STM32F4的ADC1进行采样,采样值不输出之在编译器里边观察。

            3、使用ST外设库进行实验

            4、本实验只为采集到数据,采样周期、采样间隔设置为最大。

【ADC试验1实验结果】

             成功采集到了ADC1,通道1引脚PA1上的输入。数据稳定不跳变。

【ADC试验1实验步骤】

1、首先怀疑是工程中使用的USART、EXTI什么的影响了ADC的。重建工程,加入ST外设库,添加引用位置。这一步就不说了。

2、开启GPIOA、ADC时钟。因为使用ADC1的通道1,对应的PA1引脚作为输入。

ADC挂接在APB2时钟上,GPIOA挂接在AHB1时钟上。所以要开启这两个时钟。

代码如下:

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);

3、输入引脚配置

          输入端口PA,引脚1.模拟输入,引脚时钟100M

//PA1 PA2 PA3,模拟输入
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
 GPIO_InitStructure.GPIO_Pin = GPIO_PinSource1 | GPIO_PinSource2 | GPIO_PinSource3;
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
 GPIO_Init(GPIOA,&GPIO_InitStructure);

4、ADC通用的初始化

        这是F4系列新出来的东西,与F1不同。这里通过库函数配置一个新增的寄存器ADC_CCR,这个配置将影响片上所有的ADC。

//ADC通用配置
 ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;  //不用DMA
 ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;  //独立采样
 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;  //速度尽可能低
 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
 ADC_CommonInit(&ADC_CommonInitStructure);

5、ADC1初始化

        之后对单独的ADC1进行配置,设定一些参数。最后的ADC_RegularChannelConfig完成通道选择和扫描顺序设置

主要参数:连续采样/数据右对齐/不用外部触发/采样通道数1/12位精度
//ADC1配置
 ADC_InitStructyre.ADC_ContinuousConvMode = ENABLE;
 ADC_InitStructyre.ADC_DataAlign = ADC_DataAlign_Right;
 ADC_InitStructyre.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None;
 ADC_InitStructyre.ADC_NbrOfConversion = 1;  //通道数
 ADC_InitStructyre.ADC_Resolution = ADC_Resolution_12b;
 ADC_InitStructyre.ADC_ScanConvMode = ENABLE;
 ADC_Init(ADC1,&ADC_InitStructyre);
   
 ADC_Cmd(ADC1,ENABLE);
 ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_144Cycles);

6、手动开始ADC采样,由于配置为连续扫描所有只需要开启一次即可:

     使用函数ADC_SoftwareStartConv(ADC1);完成

7、获取ADC采样数据:

    可以通过读ADC的DR或者使用库函数ADC_GetConversionValue来完成。我使用的是库函数。

adcvalue1 = ADC_GetConversionValue(ADC1);
adcvota = adcvalue1 *3300 / 0xfff;

8、最后加上串口功能,让数据回显

while(1)
 {
    adcvalue1 = ADC_GetConversionValue(ADC1);
    adcvota = adcvalue1 *3300 / 0xfff;
   
   for(i = 0;i<10000;i++)
   {
       sum += adcvota;
     if(i ==9999)
     {
        avgvota = sum/10000;
        sum = 0;
       printf("avg vota is: %d\r\n",avgvota);
     }
   }
 }

9、完整的代码:

#include <stm32f4xx.h>
 #include "stm32f4xx_rcc.h"
 #include "stm32f4xx_gpio.h"
 #include "stm32f4xx_adc.h"
 #include "stm32f4xx_conf.h"#include "usart.h"
#include <stdio.h>
NVIC_InitTypeDef NVIC_InitStructure;
 GPIO_InitTypeDef GPIO_InitStructure;
 ADC_CommonInitTypeDef ADC_CommonInitStructure;
 ADC_InitTypeDef ADC_InitStructyre;int adcvalue1,adcvalue2,adcvalue3;
 int adcvota ;int i,sum,avgvota;
int main()
 {
   USART_Config();
   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
   //PA1 PA2 PA3,模拟输入
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
 //   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
   GPIO_InitStructure.GPIO_Pin = GPIO_PinSource1 | GPIO_PinSource2 | GPIO_PinSource3;
   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
   GPIO_Init(GPIOA,&GPIO_InitStructure);
   //ADC通用配置
   ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;  //不用DMA
   ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;  //独立采样
   ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;  //速度尽可能低
   ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
   ADC_CommonInit(&ADC_CommonInitStructure);
   
   //ADC1配置
   ADC_InitStructyre.ADC_ContinuousConvMode = ENABLE;
   ADC_InitStructyre.ADC_DataAlign = ADC_DataAlign_Right;
   ADC_InitStructyre.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None;
   ADC_InitStructyre.ADC_NbrOfConversion = 1;
   ADC_InitStructyre.ADC_Resolution = ADC_Resolution_12b;
   ADC_InitStructyre.ADC_ScanConvMode = ENABLE;
   ADC_Init(ADC1,&ADC_InitStructyre);
     
   ADC_Cmd(ADC1,ENABLE);
   ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_144Cycles);
   
   ADC_SoftwareStartConv(ADC1);
   
   sum = 0;
   while(1)
   {
      adcvalue1 = ADC_GetConversionValue(ADC1);
      adcvota = adcvalue1 *3300 / 0xfff;
     
     for(i = 0;i<10000;i++)
     {
         sum += adcvota;
       if(i ==9999)
       {
          avgvota = sum/10000;
          sum = 0;
         printf("avg vota is: %d\r\n",avgvota);
       }
     }
   }
 }
   
   //重定义printf
 int fputc(int ch,FILE *f)
 {
   //检查发送寄存器为空之后再往里边放数据 
   while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
   {}
   //USART发送一个字符
   USART_SendData(USART1, (uint8_t) ch);
   return ch;
 }1:混合同步注入+快速交替模式 
    #define ADC_Mode_InjecSimult_SlowInterl ((uint32_t)0x00040000) 0100:混合同步注入+慢速交替模式 
    #define ADC_Mode_InjecSimult ((uint32_t)0x00050000) 0101:注入同步模式 
    #define ADC_Mode_RegSimult ((uint32_t)0x00060000) 0110:规则同步模式 
    #define ADC_Mode_FastInterl ((uint32_t)0x00070000) 0111:快速交替模式 
    #define ADC_Mode_SlowInterl ((uint32_t)0x00080000) 1000:慢速交替模式 
    #define ADC_Mode_AlterTrig ((uint32_t)0x00090000) 1001:交替触发模式 
    */ 
    ADC_InitStructure.ADC_ScanConvMode = ENABLE; 
    /* ADC_ScanConvMode在stm32f10x_adc.h中定义如下: 
    FunctionalState ADC_ScanConvMode; 
    这个参数用来指定转换是扫描(多通道模式)还是单个转换(单通道模式),该参数可以被设置为DISABLE或者ENABLE。 
    在数据手册中,SCAN位是这样描述的:扫描模式 
    该位由软件设置和清除,用于开启或关闭扫描模式。在扫描模式中,由ADC_SQRx或ADC_JSQRx寄存器选中的通道被转换。 
    0:关闭扫描模式 
    1:使用扫描模式 
    注:如果分别设置了EOCIE或JEOCIE位,只在最后一个通道转换完毕才会产生EOC或JEOC中断。 
    这样,如果一次需要对多个通道进行转换,这位就必须设置为ENABLE。 
    */ 
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; 
    /* FunctionalState ADC_ContinuousConvMode; 
    这个参数用来指定转换是连续进行还是单次进行,它可以设置为ENABLE或者DISABLE。 
    这两个参数中出现了FunctionalState数据类型,那么它是什么呢,顺滕摸瓜,可以看到它的的定义如下: 
    typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState; 
    因此,它相当于是一个位变量,我的理解,DISPABLE=0这个没有问题,ENABLE=!DISABLE是否应该确切的是1??否则下面的设置就会有问题。 
    用这两个符号来对寄存器中的位进行设置的话,还需要提供位置信息,如下面的代码所示: 
    tmpreg1 |= (uint32_t)(ADC_InitStruct-》ADC_DataAlign | ADC_InitStruct-》ADC_ExternalTrigConv | 
    ((uint32_t)ADC_InitStruct-》ADC_ContinuousConvMode 《《 1)); 
    这个《《1就是位置信息,CONT是CON2寄存器的位1 
    这样,我们看STM32的库又能多看懂一点了。 
    用于设定CON2的CONT位(位1):是否连续转换 
    该位由软件设置和清除。如果设置了此位,则转换将连续进行直到该位被清除。 
    0:单次转换模式 1:连续转换模式 
    */ 
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; 
    /* uint32_t ADC_ExternalTrigConv; 
    定义如何来触发AD转换,一共有8个可选项,以下给出两个来解释一下: 
    #define ADC_ExternalTrigConv_T1_CC3 ((uint32_t)0x00040000) 
    将0x00040000写成二进制,就是: 
    0000 0000 0000 0100 0000 0000 0000 0000 
    对照下面的说明,不难看出,第19:17位是 010,即定时器1的CC3事件触发。 
    #define ADC_ExternalTrigConv_None ((uint32_t)0x000E0000) 
    将0x000E0000写成二进制,就是: 
    0000 0000 0000 1110 0000 0000 0000 0000 
    对照下面的说明,是SWSTART方式,即用软件标志来启动转换。 
    关于EXTSEL[2:0]的说明: 
    位19:17 EXTSEL[2:0]:选择启动规则通道组转换的外部事件 
    这些位选择用于启动规则通道组转换的外部事件 
    ADC1和ADC2的触发配置如下 
    000:定时器1的CC1事件 100:定时器3的TRGO事件