STM32f103 系列有 3 个 ADC,精度为 12 位,每个 ADC 最多有 16 个外部通道。其中ADC1 和 ADC2 都有 16 个外部通道, ADC3 根据 CPU 引脚的不同通道数也不同,一般都有8 个外部通道。
        STM32-ADC的功能框图

STM32CubeMX ADC DMA自动读取_学习

1.输入通道:

        

STM32CubeMX ADC DMA自动读取_数据_02

        STM32 的 ADC 多达 18 个通道,其中外部的 16 个通道就是框图中的 ADCx_IN0、ADCx_IN1...ADCx_IN5。其中ADC1/2/3还有内部通道: ADC1的通道16连接到了芯片内部的温度传感器,Vrefint 连接到了通道 17。

 Vrefint:内部参照电压 

        规则通道:普通的ADC输入通道

        注入通道:转换优先级比规则通道高,即在规则通道转换过程中注入通道申请转换,那么就先完成注入通道转换,再回来接着规则通道的转换。

2.转换顺序:

        规则通道:

                 规则序列寄存器有 3 个,分别为 SQR3、 SQR2、 SQR1。

STM32CubeMX ADC DMA自动读取_单片机_03

        如果通道 1 想第 8 个转换,则 SQ8[4:0]写 1即可;如果通道 1 想第 10 个转换,则 SQ10[4:0]写 1即可。

具体使用多少个通道,最多 16 个通道

       注入通道:

                 注入序列寄存器 JSQR 只有一个,最多支持 4 个通道,具体多少个由 JSQR 的 JL[2:0]决定。

STM32CubeMX ADC DMA自动读取_stm32_04

        当JL[1:0] = 4时,如果通道 1 想第 2 个转换,则 JSQ2[4:0]写 1即可;
        当JL[1:0] < 4时,如果JL[1:0] = 3,转换顺序为:JSQ2[4:0]、JSQ3[4:0]、JSQ4[4:0]。当JL[1:0] = 2时,转换顺序为:JSQ3[4:0]、JSQ4[4:0]。如果JL[1:0] = 3,转换顺序为:JSQ4[4:0]。

 3.触发源

        软件触发转换:由ADC 控制寄存器 2: ADC_CR2 的 ADON 这个位来控制,写 1 的时候开始转换,写 0 的时候停止转换。

        外部触发转换:包括内部定时器触发和外部 IO 触发,如ADC的功能框图所示。

                ADC 控制寄存器 2:ADC_CR2 的(规则通道)EXTSEL[2:0]和(注入通道)JEXTSEL[2:0]位来控制触发源的选择。触发源是否要激活,则由ADC 控制寄存器 2:ADC_CR2 的(规则通道) EXTTRIG 和 (注入通道)JEXTTRIG 这两位来激活。

4.转换时间

每个通道可以分别用不同的时间采样。其中采样周期最小是 1.5 个。

        ADC 的转换时间跟 ADC 的输入时钟和采样时间有关,公式为: Tconv = 采样时间 +12.5 个周期。

5.数据寄存器

        规则通道:

存在问题:数据覆盖,解决办法:利用DMA将数据搬运走) 

                ADC 规则组数据寄存器 ADC_DR 只有一个,是一个 32 位的寄存器,低 16 位在单 ADC时使用,高 16 位是在 ADC1 中双模式下保存 ADC2 转换的规则数据。
        注入通道:

                数据放在 JDRx。

                ADC 注入组最多有 4 个通道,刚好注入数据寄存器也有 4 个,每个通道对应着自己的寄存器,不会跟规则寄存器那样产生数据覆盖的问题。

6.中断

        中断分为三种:规则通道转换结束中断,注入转换通道转换结束中断,模拟看门狗中断。

7.程序

        首先是GPIO初始化及ADC初始化

/*****************************以ADC2 通道1为例******************************/

void ADC2_Channel1_GPIO_Config(){  
     GPIO_InitTypeDef  GPIO_InitStructure;
 	
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能PA端口时钟
	
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;		

    /*************************ADC采集需使用模拟输入模式********/		
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; 		 //模拟输入

     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
     GPIO_Init(GPIOA, &GPIO_InitStructure);					 //根据设定参数初始化GPIOA.1
}

void ADC2_Channel1_Config{
    ADC_InitTypeDef ADC_InitStructure;	

	// 打开ADC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE );
	
	/******************ADC模式配置***************************/
	// 只使用一个ADC,属于独立模式
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	
	// 禁止扫描模式,多通道才要,单通道不需要
	ADC_InitStructure.ADC_ScanConvMode = DISABLE ; 

	// 连续转换模式(不使能则为单次转换模式,一次转换完成后ADC停止)
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;


    /*******************ADC转换触发事件
    *TIM1_CC1事件 
    *TIM1_CC2事件 
    *TIM1_CC3事件 
    *TIM2_CC2事件 
    *TIM3_TRGO事件 
    *TIM4_CC4事件 
    EXTI线11/TIM8_TRGO事件
    ********************/

	// 不用外部触发转换,软件开启即可    
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;

	// 转换结果右对齐
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	
	// 转换通道1个(所开启的ADC通道数量)
	ADC_InitStructure.ADC_NbrOfChannel = 1;	
		
	// 初始化ADC2
	ADC_Init(ADC2, &ADC_InitStructure);
	
	// 配置ADC时钟为PCLK2的8分频,即9MHz
	RCC_ADCCLKConfig(RCC_PCLK2_Div8); 
	
	// 配置 ADC 通道转换顺序和采样时间
	ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, 
	                         ADC_SampleTime_55Cycles5);
	
	// ADC 转换结束产生中断,在中断服务程序中读取转换值
    //也可采用扫描的方式,具体参照正点原子的ADC例程
	ADC_ITConfig(ADC2, ADC_IT_EOC, ENABLE);
	
	// 开启ADC ,并开始转换
	ADC_Cmd(ADC2, ENABLE);

}

   ADC在开始精确转换前需要一个稳定时间tSTAB。在开始ADC转换和14个时钟周期后, EOC标志被设置, 16位ADC数据寄存器包含转换的结果。

STM32CubeMX ADC DMA自动读取_数据_05

STM32CubeMX ADC DMA自动读取_学习_06

void ADC2_Channel1_Calibration(){
 
   ADC_ResetCalibration(ADC2);	//使能复位校准  
	 
	while(ADC_GetResetCalibrationStatus(ADC2));	//等待复位校准结束
	
	ADC_StartCalibration(ADC2);	 //开启AD校准
 
	while(ADC_GetCalibrationStatus(ADC2));	 //等待校准结束

    // 校准结束后开始转换,由于没有采用外部触发,所以使用软件触发ADC转换 
	ADC_SoftwareStartConvCmd(ADCx, ENABLE);
}

        以上完成后就可在中断中读取AD值,以野火例程为例,中断统一定义在stm32f10x_it.c中

        

ADC2_Channel1_Init(){
    ADC2_Channel1_GPIO_Config();
    ADC2_Channel1_Config();
    ADC2_Channel1_Calibration();
}
extern __IO uint16_t ADC_ConvertedValue;


补充:core_cm3.c中
        #define     __IO    volatile                 

/***********************************************************************************
	1) 中断服务程序中修改的供其它程序检测的变量需要加 volatile;
	2) 多任务环境下各任务间共享的标志应该加 volatile;
	3) 存储器映射的硬件寄存器通常也要加 volatile 说明,因为每次对它的读写都可能由不同意义;
************************************************************************************/

void ADC1_2_IRQHandler(void)
{	
	if (ADC_GetITStatus(ADC2,ADC_IT_EOC) == SET) 
	{
		// 读取ADC的转换值
		ADC_ConvertedValue = ADC_GetConversionValue(ADC2);
	}
	ADC_ClearITPendingBit(ADC2,ADC_IT_EOC);
}

最后在主函数对读取的AD值进行处理即可。