蓝桥杯嵌入式之ADC讲解

ADC模块是将模拟量转换成数字量,也是多数芯片最常用到的模块。相对于DAC来说,ADC是比较常考的。接下来简单介绍蓝桥杯嵌入式开发板上的ADC模块的使用方法。

ADC的电路连接

蓝桥杯嵌入式的ADC是通过一个电位器的扭转来改变滑动变阻器的阻值,从而测得滑动变阻器的电压。通过PB0的输入得到模拟量,在芯片内部进行ADC的转换。可以将准换后的值显示到LCD液晶显示屏上。

ADC的初始化

ADC的初始化需要用到stm32f10x_adc.c和stm32f10x_adc.h文件。

ADC的初始化函数为

/**
  * @说明     ADC时钟源及工作模式配置
  * @参数     none
  * @返回值   None
  */
void ADC_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  ADC_InitTypeDef ADC_InitStructure;
  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
  //PB0-ADC channel 8
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  
  // ADC1 工作模式配置
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  //单次转换
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 1;
  ADC_Init(ADC1, &ADC_InitStructure);

  ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_13Cycles5);

  ADC_Cmd(ADC1, ENABLE);   
  ADC_ResetCalibration(ADC1);
  /* Check the end of ADC1 reset calibration register */
  while(ADC_GetResetCalibrationStatus(ADC1));
  ADC_StartCalibration(ADC1);
  /* Check the end of ADC1 calibration */
  while(ADC_GetCalibrationStatus(ADC1));
}

首先是端口的时钟使能。可以用FCT117E嵌入式竞赛板V1.1\CT117E嵌入式竞赛板\STM32_MCU\stm32f10x_stdperiph_lib\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\ADC\ADC1_DMA中的main.c文件进行修改。源代码是

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);

可以将RCC_APB2Periph_GPIOC替换成RCC_APB2Periph_GPIOB。

其次是端口模式的设置。同上的文件,源代码是

/**
  * @brief  Configures the different GPIO ports.
  * @param  None
  * @retval None
  */
void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  /* Configure PC.04 (ADC Channel14) as analog input -------------------------*/
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(GPIOC, &GPIO_InitStructure);
}

只需将相应的I/O进行修改即可。

最后是ADC工作模式的配置。同上的文件,源代码是

ADC_InitTypeDef ADC_InitStructure;
/* ADC1 configuration ------------------------------------------------------*/
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_InitStructure.ADC_ScanConvMode = ENABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 1;
  ADC_Init(ADC1, &ADC_InitStructure);

  /* ADC1 regular channel14 configuration */ 
  ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1, ADC_SampleTime_55Cycles5);

  /* Enable ADC1 DMA */
  ADC_DMACmd(ADC1, ENABLE);
  
  /* Enable ADC1 */
  ADC_Cmd(ADC1, ENABLE);

  /* Enable ADC1 reset calibration register */   
  ADC_ResetCalibration(ADC1);
  /* Check the end of ADC1 reset calibration register */
  while(ADC_GetResetCalibrationStatus(ADC1));

  /* Start ADC1 calibration */
  ADC_StartCalibration(ADC1);
  /* Check the end of ADC1 calibration */
  while(ADC_GetCalibrationStatus(ADC1));
     
  /* Start ADC1 Software Conversion */ 
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);

需要将结构体ADC_InitStructure的元素ADC_ScanConvMode、ADC_ContinuousConvMode 的值变为DISABLE。有兴趣的可以查一查这两个值的作用。如果不改变的话,对ADC的采集影响不大。

其次是改变ADC的通道和采样周期。例程中选择的是通道8,采样周期为ADC_SampleTime_13Cycles5。即将ADC_Channel_14替换成ADC_Channel_8,将ADC_SampleTime_55Cycles5替换成ADC_SampleTime_13Cycles5即可。

因为没有使用到DMA,所以对于DMA的使能函数也就不需要了。即可以删除ADC_DMACmd(ADC1, ENABLE);。ADC_SoftwareStartConvCmd(ADC1, ENABLE);是软件打开ADC的工作,在初始化函数中可以不需要该函数。

ADC的读取和转换

ADC的读取和转换代码为

/**
  * @说明     读取ADC转换结果
  * @参数     none
  * @返回值   ADC_VALUE:ADC转换结果
  */

float Read_ADC(void)
{
  float ADC_VALUE;
  
  ADC_SoftwareStartConvCmd(ADC1,ENABLE);
  Delay_Ms(5);
  ADC_VALUE = ADC_GetConversionValue(ADC1)*3.30/0xfff;
  
  return ADC_VALUE;
}

函数ADC_SoftwareStartConvCmd(ADC1,ENABLE);是软件打开ADC的工作,可以在初始化函数中复制。函数是得到ADC转换后的数字值,定义在stm32f10x_adc.c中的第708行,声明在stm32f10x_adc.h中的第444行。其参数是对ADC的选择,在这里为ADC1。因为ADC的值是实型,所以应用实型变量保存转换后的ADC的值。转换公式是需要自己记忆的,比赛时没有明确的提供。

ADC数字值的显示

例程中ADC的显示代码为

  //200毫秒扫描一次ADC
    if(ADC_Flag){
      ADC_Flag = 0;
      adc_temp = Read_ADC();
      sprintf((char *)string,"%s%.3f","ADC Value:",adc_temp);
      LCD_DisplayStringLine(Line7,string);
    }

每200毫秒进行ADC的读取。变量ADC_Flag是ADC读取标志。stm32f10x_it.c文件中对SysTick中断函数的改变成

void SysTick_Handler(void)
{
  TimingDelay--;
  if(++Ms == 200){
    Ms = 0;
    ADC_Flag = 1;
  }
}

文件开头添加

extern uint8_t ADC_Flag;
uint8_t Ms = 0;

即当变量Ms加到200时表示200毫秒达到,将Ms赋值成0,等待下一个200毫秒的到来。并将采集标志量ADC_Flag置1。采集标志量在main.c文件中的定义为

uint8_t ADC_Flag;

ADC温度采集初始化讲解

该芯片内部有对温度进行采集和转换的电路,所以可以直接利用该ADC得到周围环境的温度,也是基础板唯一一个可以或得温度的模块。但是由于温度是比较难以控制的,所以省赛或国赛一般不会考。但是也不能完全不知道。

因为ADC温度采集的所有工作都是在内部完成,所以不需要任何的I/O口。初始化代码为

/**
  * @说明     ADC时钟源及工作模式配置
  * @参数     none
  * @返回值   None
  */
void ADC_Config(void)
{
  ADC_InitTypeDef ADC_InitStructure;
  
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
//   RCC_ADCCLKConfig(RCC_PCLK2_Div4); 
  
  // ADC1 工作模式配置
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  //单次转换
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 1;
  ADC_Init(ADC1, &ADC_InitStructure);

  ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_239Cycles5);    
  ADC_TempSensorVrefintCmd(ENABLE);  //使能温度传感器
  
  ADC_Cmd(ADC1, ENABLE);   
  ADC_ResetCalibration(ADC1);
  /* Check the end of ADC1 reset calibration register */
  while(ADC_GetResetCalibrationStatus(ADC1));
  ADC_StartCalibration(ADC1);
  /* Check the end of ADC1 calibration */
  while(ADC_GetCalibrationStatus(ADC1));
}

与普通ADC初始化相比,温度的初始化没有对I/O口时钟和模式选择的设置;也是利用ADC1,所以不能与普通的ADC转换同时使用;采集温度的ADC通道为16,即ADC_Channel_16,采集周期为ADC_SampleTime_239Cycles5。

ADC温度采集读取讲解

与普通ADC采集相比,只是增加了一行公式。其代码为

/**
  * @说明     通过ADC计算温度
  * @参数     none
  * @返回值   tempeeature:温度
  */

float Read_Temperature(void)
{
  uint16_t tempdata;
  float temperature;
  float volt;
  
//  Current Temperature = (V25-Vsensor)/AVG_SLOP + 25
  
  ADC_SoftwareStartConvCmd(ADC1,ENABLE);
//   Delay_Ms(5);
  tempdata = ADC_GetConversionValue(ADC1);
  volt = (tempdata*3.3)/0xfff;
  
  temperature = (1430 - 1000*volt)/4.3 + 25;
  return temperature;
}

增加的公式为。同样在比赛的时候没有明确的提示,需要记忆。

ADC温度采集后显示讲解

ADC温度采集后的显示与普通ADC采集后的显示一样,只是显示的变量不一样,即要显示读取函数Read_Temperature返回的值temperature。

原文发布于微信公众号 - IT界的小白帽(l1084504793)

原文发表时间:2019-05-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券