实验十一 ADC—电压采集

一.实验目标

  1. 通过编程STM32实现使用内部ADC采集电压;

  2. 熟悉内部ADC使用。

二.知识储备及设计思路

ADC是模数转换器的简称,是将模拟信号转化为数字信号的重要器件。其具体流程可简单概括为:取样、保持、量化、编码。ADC的实现结构有许多种,如逐次逼近型,流水线型等。单片机内ADC大多数是逐次逼近型。STM32F407IGT6
有3 个ADC,每个ADC 有12 位、10 位、8 位和6 位可选,每个ADC 有16
个外部通道。另外还有两个内部ADC 源和VBAT 通道挂在ADC1 上。ADC
具有独立模式、双重模式和三重模式,对于不同AD
转换要求几乎都有合适的模式可选。ADC功能非常强大,具体的我们在功能框图中分析每个部分的功能。

1. 电压输入范围

ADC 输入范围为:VREF- ≤ VIN ≤ VREF+。由VREF-、VREF+ 、VDDA 、VSSA、这四个外部

引脚决定。

我们在设计原理图的时候一般把VSSA 和VREF-接地,把VREF+和VDDA 接3V3,得到ADC
的输入电压范围为:0~3.3V。

如果我们想让输入的电压范围变宽,去到可以测试负电压或者更高的正电压,我们可以在外部加一个电压调理电路,把需要转换的电压抬升或者降压到0~3.3V,这样ADC
就可以测量了。

2. 输入通道

我们确定好ADC 输入电压之后,那么电压怎么输入到ADC?这里我们引入通道的概念,STM32
的ADC 多达19 个通道,其中外部的16
个通道就是框图中的ADCx_IN0、ADCx_IN1…ADCx_IN5。这16 个通道对应着不同的IO
口,具体是哪一个IO 口可以从手册查询到。其中ADC1/2/3 还有内部通道: ADC1
的通道ADC1_IN16 连接到内部的VSS,通道ADC1_IN17 连接到了内部参考电压VREFINT
连接,通道ADC1_IN18 连接到了芯片内部的温度传感器或者备用电源VBAT。ADC2 和ADC3
的通道16、17、18 全部连接到了内部的VSS。

开发板上的ADC具有如下特性:

数据手册中还提供了ADC的电路框图,有助于进行程序设计:

三.引脚说明与硬件连接

根据开发板原理图(如图1),ADC的输入端口为PC2,故只需将外部直流电压接在PC2处,即可对外部直流源进行采样。这里是ADC1、ADC2、ADC3公用的引脚,开发板上J2提供了引出接口,使用杜邦线即可与外界信号进行连接。

图1 ADC硬件连接

ADC配置过程中较为重要的寄存器如下:

状态寄存器:

状态寄存器描述了ADC转换过程中的多种状态,在使用过程中读取这些状态即可获知ADC处于何种工作模式下,通过库函数也能获得这些状态。

控制寄存器:

控制寄存器负责对ADC的工作模式进行配置,如进行使能、进行DMA或中断配置等,具体内容可以参考手册相关部分。上述寄存器在使用过程中较为常用,其他寄存器内容可以在参考手册中查阅。

在实际HAL库开发时,对寄存器的操作可以通过结构体的赋值来完成。配置需要的结构体如下:

其中比较重要的是instance成员,他代表了寄存器基地址,对该成员的操作即可以操作控制寄存器,从而对工作模式进行配置。具体可以参考工程代码。

四.程序整体框图和设计

本次程序框图比较简单,只用到了ADC模块。整体思路是:对ADC进行初始化,对结构体进行配置,调用库函数读取ADC码值,经过适当转换得到直流电压值。

部分程序源码如下:

1
ADC_HandleTypeDef hadc1; void MX_ADC1_Init(void)//CubeMX配置的初始化函数 {   ADC_ChannelConfTypeDef sConfig = {0};   hadc1.Instance = ADC1;  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;  hadc1.Init.Resolution = ADC_RESOLUTION_12B;  hadc1.Init.ScanConvMode = DISABLE;  hadc1.Init.ContinuousConvMode = DISABLE;  hadc1.Init.DiscontinuousConvMode = DISABLE;  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;  hadc1.Init.NbrOfConversion = 1;  hadc1.Init.DMAContinuousRequests = DISABLE;  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;   if (HAL_ADC_Init(&hadc1) != HAL_OK)  {  Error_Handler();  }  sConfig.Channel = ADC_CHANNEL_12;//配置为通道12  sConfig.Rank = 1;//第一个序列  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;//采样时间
1
2
3
4
| if (HAL_ADC_ConfigChannel(&hadc1, \&sConfig) != HAL_OK)  {  Error_Handler();  } } |
| ------------------------------------------------------------ |


这一部分代码是MX生成的初始化代码,对ADC的工作模式进行了配置,具体过程见第六部分。同时定义了转换时的速率和使用的通道。此时ADC已经配置完毕,可以进行数据的采集了。

1
2
3
4
| void HAL_ADC_MspInit(ADC_HandleTypeDef\* hadc) {//打开GPIOC时钟和ADC时钟  GPIO_InitTypeDef GPIO_Initure;  \__HAL_RCC_ADC1_CLK_ENABLE(); //在stm32f4xx_hal_msp.c中配置也可以  \__HAL_RCC_GPIOC_CLK_ENABLE();     GPIO_Initure.Pin=GPIO_PIN_2;   GPIO_Initure.Mode=GPIO_MODE_ANALOG;  GPIO_Initure.Pull=GPIO_NOPULL;  HAL_GPIO_Init(GPIOC,&GPIO_Initure);//配置PC2为模拟模式 } |
| ------------------------------------------------------------ |


这一部分对时钟和端口进行了初始化,主要是端口工作模式和GPIO以及ADC的时钟。此外,注意在stm32f4xx_hal_conf.c文件里面打开ADC的声明。

1
2
3
4
| uint16_t Get_Ave_Value(uint16_t times){//输出times采样的平均值  int i;  uint16_t temp;  for(i=0;i\<times;i++){  HAL_ADC_Start(&hadc1);//每次都要先开始才能取值  temp += HAL_ADC_GetValue(&hadc1);  delay_ms(5);  }  return temp/times;//平均 } |
| ------------------------------------------------------------ |


这一部分开始处理采集的数据,由于噪声等影响,我们可以多次采样取平均值,得到一个相对稳定的码值。类似于DAC,我们可以通过码值转换出此时的直流电压值。

ADC的主要代码介绍完毕,具体可以参见工程文件。

五.实验结果

实验过程中,可以实时地获得PC2管脚此时的输入电压值,并将码率和电压显示在LCD上,如图2所示。

图2 实验结果

六.STM32CubeMX配置关键硬件截图

图3 CubeMX配置截图

CubeMX的配置比较简单,只需打开ADC的相应通道,即可生成代码,如图3。