STM32-ADC Detailed

About a .ADC

3 STM32f103 series ADC, 12-bit precision, each ADC up to 16 external channels. Wherein ADC1 and ADC2 are 16 external channels, ADC3 generally have eight external channels, each channel A / D converter can be a single, continuous, or intermittent scan execution, the results of the ADC conversion can be aligned in the left or right storage 16 bit data register. ADC'sThe input clock may not exceed 14MHz, which clock frequency is produced by dividing PCLK2

Two .ADC functional block diagram to explain

Learning development board STM32 peripherals must first understand the functional block diagram of its peripheral, as follows:
Here Insert Picture Description
a functional block diagram can be divided into seven parts, explain one by one the following:

1. The input voltage range

Voltage range of the ADC is measured VREF- the VIN ≤ ≤ VREF + , VREF- and the ground VSSA, then the VREF + and VDDA 3V3, to give the ADC input voltage range: 0 ~ 3.3V.

2. Input channel

ADC input signal through a channel is achieved, Channel input signal to the microcontroller, the microcontroller after conversion, outputs an analog signal to a digital signal. The STM32 ADC with 18 channels, whereinExternal channels 16 have been marked in the block diagramAs follows:
Here Insert Picture Description
the 16 channels corresponding to different IO port, in addition ADC1 / 2/3 as well as the internal passage: ADC1 passage 16 is connected to the internal chip temperature sensor, Vrefint connected to the passage 17. ADC2 analog channels 16 and 17 are connected to the interior of the VSS.
All channels of the ADC is shown:
Here Insert Picture Description
the outer channels 16 into the conversion time andRule channel and injection channel, wherein the channel has a rule up to 16, up to four injection channel (using seemingly small injection channel),下面简单介绍一下俩种通道:
规则通道
规则通道顾名思义就是,最平常的通道、也是最常用的通道,平时的ADC转换都是用规则通道实现的。
注入通道
注入通道是相对于规则通道的,注入通道可以在规则通道转换时,强行插入转换,相当于一个“中断通道”吧。当有注入通道需要转换时,规则通道的转换会停止,优先执行注入通道的转换,当注入通道的转换执行完毕后,再回到之前规则通道进行转换。

3.转换顺序

知道了ADC的转换通道后,如果ADC只使用一个通道来转换,那就很简单,但如果是使用多个通道进行转换就涉及到一个先后顺序了,毕竟规则转换通道只有一个数据寄存器。多个通道的使用顺序分为俩种情况:规则通道的转换顺序和注入通道的转换顺序。

规则通道转换顺序
规则通道中的转换顺序由三个寄存器控制:SQR1、SQR2、SQR3,它们都是32位寄存器。SQR寄存器控制着转换通道的数目和转换顺序,只要在对应的寄存器位SQx中写入相应的通道,这个通道就是第x个转换。具体的对应关系如下:
Here Insert Picture Description
通过SQR1寄存器就能了解其转换顺序在寄存器上的实现了:
Here Insert Picture Description

注入通道转换顺序
和规则通道转换顺序的控制一样,注入通道的转换也是通过注入寄存器来控制,只不过只有一个JSQR寄存器来控制,控制关系如下:
Here Insert Picture Description
需要注意的是,只有当JL=4的时候,注入通道的转换顺序才会按照JSQ1、JSQ2、JSQ3、JSQ4的顺序执行。当JL<4时,注入通道的转换顺序恰恰相反,也就是执行顺序为:JSQ4、JSQ3、JSQ2、JSQ1
配置转换顺序的函数如示:

/**
  * @brief  Configures for the selected ADC regular channel its corresponding
  *         rank in the sequencer and its sample time.
  * @param  ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
  * @param  ADC_Channel: the ADC channel to configure. 
  *   This parameter can be one of the following values:
  *     @arg ADC_Channel_0: ADC Channel0 selected
  *     @arg ADC_Channel_1: ADC Channel1 selected
  *     @arg ADC_Channel_2: ADC Channel2 selected
  *     @arg ADC_Channel_3: ADC Channel3 selected
  *     @arg ADC_Channel_4: ADC Channel4 selected
  *     @arg ADC_Channel_5: ADC Channel5 selected
  *     @arg ADC_Channel_6: ADC Channel6 selected
  *     @arg ADC_Channel_7: ADC Channel7 selected
  *     @arg ADC_Channel_8: ADC Channel8 selected
  *     @arg ADC_Channel_9: ADC Channel9 selected
  *     @arg ADC_Channel_10: ADC Channel10 selected
  *     @arg ADC_Channel_11: ADC Channel11 selected
  *     @arg ADC_Channel_12: ADC Channel12 selected
  *     @arg ADC_Channel_13: ADC Channel13 selected
  *     @arg ADC_Channel_14: ADC Channel14 selected
  *     @arg ADC_Channel_15: ADC Channel15 selected
  *     @arg ADC_Channel_16: ADC Channel16 selected
  *     @arg ADC_Channel_17: ADC Channel17 selected
  * @param  Rank: The rank in the regular group sequencer. This parameter must be between 1 to 16.
  * @param  ADC_SampleTime: The sample time value to be set for the selected channel. 
  *   This parameter can be one of the following values:
  *     @arg ADC_SampleTime_1Cycles5: Sample time equal to 1.5 cycles
  *     @arg ADC_SampleTime_7Cycles5: Sample time equal to 7.5 cycles
  *     @arg ADC_SampleTime_13Cycles5: Sample time equal to 13.5 cycles
  *     @arg ADC_SampleTime_28Cycles5: Sample time equal to 28.5 cycles	
  *     @arg ADC_SampleTime_41Cycles5: Sample time equal to 41.5 cycles	
  *     @arg ADC_SampleTime_55Cycles5: Sample time equal to 55.5 cycles	
  *     @arg ADC_SampleTime_71Cycles5: Sample time equal to 71.5 cycles	
  *     @arg ADC_SampleTime_239Cycles5: Sample time equal to 239.5 cycles	
  * @retval None
  */
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)
{
  函数内容略;
}

4.触发源

ADC转换的输入、通道、转换顺序都已经说明了,但ADC转换是怎么触发的呢?就像通信协议一样,都要规定一个起始信号才能传输信息,ADC也需要一个触发信号来实行模/数转换。
其一就是通过直接配置寄存器触发,通过配置控制寄存器CR2的ADON位,写1时开始转换,写0时停止转换。在程序运行过程中只要调用库函数,将CR2寄存器的ADON位置1就可以进行转换,比较好理解。
另外,还可以通过内部定时器或者外部IO触发转换,也就是说可以利用内部时钟让ADC进行周期性的转换,也可以利用外部IO使ADC在需要时转换,具体的触发由控制寄存器CR2决定。
ADC_CR2寄存器的详情如下:
Here Insert Picture Description
Here Insert Picture Description
Here Insert Picture Description

5.转换时间

还有一点,就是转换时间的问题,ADC的每一次信号转换都要时间,这个时间就是转换时间,转换时间由输入时钟和采样周期来决定
输入时钟
由于ADC在STM32中是挂载在APB2总线上的,所以ADC得时钟是由PCLK2(72MHz)经过分频得到的,分频因子由 RCC 时钟配置寄存器RCC_CFGR 的位 15:14 ADCPRE[1:0]设置,可以是 2/4/6/8 分频,一般配置分频因子为8,即8分频得到ADC的输入时钟频率为9MHz
采样周期
采样周期是确立在输入时钟上的,配置采样周期可以确定使用多少个ADC时钟周期来对电压进行采样,采样的周期数可通过 ADC采样时间寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0]位设置,ADC_SMPR2 控制的是通道 0~9, ADC_SMPR1 控制的是通道 10~17。每个通道可以配置不同的采样周期,但最小的采样周期是1.5个周期,也就是说如果想最快时间采样就设置采样周期为1.5.
转换时间
转换时间=采样时间+12.5个周期
12.5个周期是固定的,一般我们设置 PCLK2=72M,经过 ADC 预分频器能分频到最大的时钟只能是 12M,采样周期设置为 1.5 个周期,算出最短的转换时间为 1.17us。

6.数据寄存器

转换完成后的数据就存放在数据寄存器中,但数据的存放也分为规则通道转换数据和注入通道转换数据的。
规则数据寄存器
规则数据寄存器负责存放规则通道转换的数据,通过32位寄存器ADC_DR来存放。
Here Insert Picture Description
当使用ADC独立模式(也就是只使用一个ADC,可以使用多个通道)时,数据存放在低16位中,当使用ADC多模式时高16位存放ADC2的数据。需要注意的是ADC转换的精度是12位,而寄存器中有16个位来存放数据,所以要规定数据存放是左对齐还是右对齐

当使用多个通道转换数据时,会产生多个转换数据,然鹅数据寄存器只有一个,多个数据存放在一个寄存器中会覆盖数据导致ADC转换错误,所以我们经常在一个通道转换完成之后就立刻将数据取出来,方便下一个数据存放。一般开启DMA模式将转换的数据,传输在一个数组中,程序对数组读操作就可以得到转换的结果。
DMA的使用之前在一篇博客中介绍过:DMA介绍

注入数据寄存器
注入通道转换的数据寄存器有4个,由于注入通道最多有4个,所以注入通道转换的数据都有固定的存放位置,不会跟规则寄存器那样产生数据覆盖的问题。 ADC_JDRx 是 32 位的,低 16 位有效,高 16 位保留,数据同样分为左对齐和右对齐,具体是以哪一种方式存放,由ADC_CR2 的 11 位 ALIGN 设置。
Here Insert Picture Description

7.中断

Here Insert Picture Description
从框图中可以知道数据转换完成之后可以产生中断,有三种情况:
规则通道转换完成中断
规则通道数据转换完成之后,可以产生一个中断,可以在中断函数中读取规则数据寄存器的值。这也是单通道时读取数据的一种方法。
注入通道转换完成中断
注入通道数据转换完成之后,可以产生一个中断,并且也可以在中断中读取注入数据寄存器的值,达到读取数据的作用。
模拟看门狗事件
当输入的模拟量(电压)不再阈值范围内就会产生看门狗事件,就是用来监视输入的模拟量是否正常
以上中断的配置都由ADC_SR寄存器决定:
Here Insert Picture Description

当然,在转换完成之后也可以产生DMA请求,从而将转换好的数据从数据寄存器中读取到内存中。

8.电压转换

要知道,转换后的数据是一个12位的二进制数,我们需要把这个二进制数代表的模拟量(电压)用数字表示出来。比如测量的电压范围是0~3.3V,转换后的二进制数是x,因为12位ADC在转换时将电压的范围大小(也就是3.3)分为4096(2^12)份,所以转换后的二进制数x代表的真实电压的计算方法就是:
y=3.3* x / 4096

三.初始化结构体

每个外设的核心就是其对应的初始化结构体了,ADC的初始化结构体如下:

typedef struct
 {
 uint32_t ADC_Mode; // ADC 工作模式选择
 FunctionalState ADC_ScanConvMode; // ADC 扫描(多通道)或者单次(单通道)模式选择 
 FunctionalState ADC_ContinuousConvMode; // ADC 单次转换或者连续转换选择
 uint32_t ADC_ExternalTrigConv; // ADC 转换触发信号选择
 uint32_t ADC_DataAlign; // ADC 数据寄存器对齐格式
 uint8_t ADC_NbrOfChannel; // ADC 采集通道数
 } ADC_InitTypeDef;

通过配置初始化结构体来设置ADC的相关信息。

四.单通道电压采集

用这个程序来简单熟练一下ADC的单通道电压采集吧,程序使用了ADC1的通道11,对应的IO口是PC^1,因为博主的开发板上PC ^1引脚没有任何复用,使用中断,在中断中读取转换的电压。

1.头文件

In order to improve fileportability, Header file defines a number related to the amount of the ADC and interrupt transplant program at the timeOnly need to change the definition to the header file

#ifndef __ADC_H
#define __ADC_H

#include "stm32f10x.h"

/* 采用ADC1的通道11  引脚为PC^1 模式必须是模拟输入*/
#define ADC_GPIO_RCC     RCC_APB2Periph_GPIOC
#define ADC_GPIO_PORT    GPIOC
#define ADC_GPIO_PIN     GPIO_Pin_1
#define ADC_GPIO_MODE    GPIO_Mode_AIN  

/* 配置与中断有关的信息 */
#define ADC_IRQn         ADC1_2_IRQn
#define ADC_RCC          RCC_APB2Periph_ADC1


/* 配置ADC初始化结构体的宏定义 */
#define ADCx                          ADC1
#define ADCx_ContinuousConvMode       ENABLE                 		 //连续转换模式
#define ADCx_DataAlign                ADC_DataAlign_Right    		 //转换结果右对齐
#define ADCx_ExternalTrigConv         ADC_ExternalTrigConv_None      //不使用外部触发转换,采用软件触发
#define ADCx_Mode                     ADC_Mode_Independent   		 //只使用一个ADC,独立模式
#define ADCx_NbrOfChannel             1                     		 //一个转换通道
#define ADCx_ScanConvMode             DISABLE                		 //禁止扫描模式,多通道时使用

/* 通道信息和采样周期 */
#define ADC_Channel                   ADC_Channel_11
#define ADC_SampleTime                ADC_SampleTime_55Cycles5


/* 函数声明 */
void ADC_COnfig(void);
void ADC_NVIC_Config(void);
void ADC_GPIO_Config(void);
void ADCx_Init(void);

#endif  /* __ADC_H */


2. Pin Configuration Functions

First configure the GPIO pin, the pin after the analog signal is transmitted through the GPIO development board, it is noted that the pin pattern must be aAnalog Input

void ADC_GPIO_Config(void)
{
	GPIO_InitTypeDef   GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(ADC_GPIO_RCC,  ENABLE);
	
	GPIO_InitStruct.GPIO_Pin = ADC_GPIO_PIN ;
	GPIO_InitStruct.GPIO_Mode = ADC_GPIO_MODE ;
	
	GPIO_Init(ADC_GPIO_PORT , &GPIO_InitStruct);
}

Configuration pins that old routine: declare structure variables, to start the clock, a write structure, initialize GPIO

3.NVIC configuration function

Because we are using the interruption after the conversion is complete, the data is read in the interrupt function, so be sure to configure the priority interrupt function,This is because the program is only a break, so the priority configuration is more casual

void ADC_NVIC_Config(void)
{

	NVIC_InitTypeDef NVIC_InitStruct ;
	
	/* 配置中断优先级分组(设置抢占优先级和子优先级的分配),在函数在misc.c */
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1) ;
	
	/* 配置初始化结构体 在misc.h中 */
	/* 配置中断源 在stm32f10x.h中 */
	NVIC_InitStruct.NVIC_IRQChannel = ADC_IRQn ;
	/* 配置抢占优先级 */
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ;
	/* 配置子优先级 */
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1 ;
	/* 使能中断通道 */
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;
	/* 调用初始化函数 */
	NVIC_Init(&NVIC_InitStruct) ;
}

4.ADC configuration function

ADC ADC configuration function is the essence of the content contained in this function are:ADC configuration initialization structure, a clock divider configured, the configured channel conversion sequence, open converter interrupt performs the calibration, collection and other software to trigger the ADC.
Function has a detailed annotation:

void ADC_COnfig(void)
{
	ADC_InitTypeDef  ADC_InitStruct;
	RCC_APB2PeriphClockCmd(ADC_RCC,  ENABLE);
	
	/* 配置初始化结构体,详情见头文件 */
	ADC_InitStruct.ADC_ContinuousConvMode = ADCx_ContinuousConvMode  ;
	ADC_InitStruct.ADC_DataAlign = ADCx_DataAlign ;
	ADC_InitStruct.ADC_ExternalTrigConv = ADCx_ExternalTrigConv ;
	ADC_InitStruct.ADC_Mode = ADCx_Mode ;
	ADC_InitStruct.ADC_NbrOfChannel = ADCx_NbrOfChannel ;
	ADC_InitStruct.ADC_ScanConvMode = ADCx_ScanConvMode ;
	
	ADC_Init(ADCx, &ADC_InitStruct);
	
	/* 配置ADC时钟为8分频,即9M */
	RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	/* 配置ADC通道转换顺序和时间 */
	ADC_RegularChannelConfig(ADCx, ADC_Channel, 1, ADC_SampleTime );
	/* 配置为转换结束后产生中断 在中断中读取信息 */
	ADC_ITConfig(ADCx, ADC_IT_EOC,ENABLE);
	/* 开启ADC,进行转换 */
	ADC_Cmd(ADCx, ENABLE );
	/* 重置ADC校准 */
	ADC_ResetCalibration(ADCx);
	/* 等待初始化完成 */
	while(ADC_GetResetCalibrationStatus( ADCx))
		/* 开始校准 */
		ADC_StartCalibration(ADCx);
	/* 等待校准完成 */
	while (ADC_GetCalibrationStatus(ADCx));
	/* 软件触发ADC转换 */
	ADC_SoftwareStartConvCmd(ADCx, ENABLE);
	
}

5. Interrupt Function

Read data in the interrupt function, The data stored in the variable result, here using the keyword extern statement, on behalf of the variable result has been defined in other files, prior to introduction to the blog extern have extern keyword

extern uint16_t resurt;

void ADC1_2_IRQHandler(void)
{
    /* 判断产生中断请求 */
	while(ADC_GetITStatus(ADCx, ADC_IT_EOC) == SET)
		resurt=ADC_GetConversionValue(ADCx);
	/* 清除中断标志 */
	ADC_ClearITPendingBit(ADCx, ADC_IT_EOC);
}

6. The main function

The main function is responsible for receiving the converted values, and converts it to a voltage value, and then printed on a computer through the serial port, for debugging.
Variable result is a global variable in the main function, attentionThe final result should be converted to floating point

#include "stm32f10x.h"
#include "usart.h"
#include "adc.h"

uint16_t resurt;

void delay(void)
{
	uint16_t k=0xffff;
	while(k--);
}


int main(void)
{
	float voltage;
	/* 串口调试函数 */
	DEBUG_USART_Config();
	
	/* 与ADC相关的函数打包在此函数中 */
	ADCx_Init();
	while(1)
	{
	    /* 强制转换为浮点型 */
		voltage = (float) resurt/4096*3.3;
		printf("\n电压值为:%f\n",voltage);
		delay();
	}

}


Blog has not the right place, but also invite you pointed out, the next would be grateful!

Published 62 original articles · won praise 188 · views 10000 +

Guess you like

Origin blog.csdn.net/qq_43743762/article/details/100067558