【STM32F042】ADC卡死,ADC一直为0,ADC配置的坑

1.问题描述

最近在调试电机驱动,刚好写到电压采集这一部分,板子的主控是STM32F040K6T6.用到了ADC,使用非DMA方式。刚开始读取到的值全部是0,然后程序卡死,我在使用万用表测IO口电压后,排除了硬件问题。网上找了一堆资料,得到的解决方案有这么几种:

1.说是官方的库函数ADC_ChannelConfig()有问题,需要把“|”符号去掉的,代码如下,修改之后,依旧卡死

void ADC_ChannelConfig(ADC_TypeDef* ADCx, uint32_t ADC_Channel, uint32_t ADC_SampleTime)
{
  uint32_t tmpreg = 0;

  /* Check the parameters */
  assert_param(IS_ADC_ALL_PERIPH(ADCx));
  assert_param(IS_ADC_CHANNEL(ADC_Channel));
  assert_param(IS_ADC_SAMPLE_TIME(ADC_SampleTime));

  /* Configure the ADC Channel */
  ADCx->CHSELR |= (uint32_t)ADC_Channel;  //把这里的或运算符去掉

  /* Clear the Sampling time Selection bits */
  tmpreg &= ~ADC_SMPR1_SMPR;

  /* Set the ADC Sampling Time register */
  tmpreg |= (uint32_t)ADC_SampleTime;

  /* Configure the ADC Sample time register */
  ADCx->SMPR = tmpreg ;
}

2. 在配置中加入:ADC_InitStructure.ADC_ExternalTrigConv =ADC_ExternalTrigConv_T1_TRGO;这一句,代码如下,测试没用

  ADC_StructInit(&ADC_InitStructure);
  ADC_DeInit(ADC1); //复位并初始化
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;           
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;    
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;       
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_TRGO; //据说加入这句
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;                                                          
  ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward;    

3.在while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_ADRDY));这一句后面加入标志位清除函数,如下,测试之后,对我没用

  
 while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_ADRDY));  //启动就绪标志
 ADC_ClearFlag(ADC1, ADC_FLAG_EOC); //!!!加入这一句

4.有博主在重新命名了GPIO 结构体的名字后解决问题,如下面的代码,我尝试了一下,也没有解决。

void  Adc_Init(void)
{ 	
	ADC_InitTypeDef ADC_InitStructure; 
    //GPIO_InitTypeDef GPIO_InitStructure; //原来在其他地方多次使用GPIO_InitStructure
	GPIO_InitTypeDef GPIO_ADC;  //将GPIO_InitStructure改为GPIO_ADC
}

 2.解决办法

我这里两种情况都出现了。卡死是首要问题,先解决卡死问题,上会卡死的代码:

//配置部分
void  Adc_Init(void)
{ 	
 ADC_InitTypeDef ADC_InitStructure; 
 GPIO_InitTypeDef GPIO_ADC;

  /*ADC以及GPIO时钟使能*/
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA|RCC_AHBPeriph_GPIOB,ENABLE);//GPIO
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE );	  //使能ADC1通道时钟

  RCC_ADCCLKConfig(RCC_ADCCLK_PCLK_Div4);   
  
  /*PA5 作为模拟通道输入引脚*/                  
  GPIO_ADC.GPIO_Pin = GPIO_Pin_5; //PA5
  GPIO_ADC.GPIO_Mode = GPIO_Mode_AN;		//模拟输入引脚
 //PIO_ADC.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_ADC.GPIO_PuPd = GPIO_PuPd_NOPULL; //无上下拉
  GPIO_Init(GPIOA, &GPIO_ADC);	 
  
  /*ADC配置*/
  ADC_StructInit(&ADC_InitStructure);
  ADC_DeInit(ADC1); //复位并初始化
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位分辨率            
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;    //连续转换模式
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //外部触发无     
  //ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_TRGO; //???
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   //右端对齐                                                       
  ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward;    //浏览模式
  
  ADC_Init(ADC1,&ADC_InitStructure); //初始化ADC
  
  ADC_GetCalibrationFactor(ADC1); //校验
  ADC_Cmd(ADC1,ENABLE); //使能
  //ADC通道配置
 ADC_ChannelConfig(ADC1,ADC_Channel_5,ADC_SampleTime_55_5Cycles); //IN5

 /*程序卡在了这里*/
 while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_ADRDY));  //启动就绪标志
 ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
}

分析流程:初始化时钟(OK)->GPIO配置(OK)->ADC配置(OK)->ADC启动(?)->ADC转换(卡死) ,问题出在ADC启动之后,转换并未开始,通过打印调试(记住keil不支持F0系列的Debug),发现程序卡死在while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_ADRDY));找到数据手册ADC配置一节,有几个细节引起了我的注意。

【STM32F042】ADC卡死,ADC一直为0,ADC配置的坑_第1张图片

请看这句: 对 于 以 下 的 这 些 位 ADC_IER、ADC_CFGRi、ADC_SMPR、ADC_TR、ADC_CHSELR 和 ADC_CCR 寄存器,软件必须在 ADC 开启 (ADEN = 1) 且无转换期间 (ADSTART = 0) 的情况 下才能进行改写。我们来看下ADC_CHSELR这个寄存器,它是选择ADC通道的,他的意思是,我们只能在开启ADC后再选择通道,列个表说明一下这几个寄存器的名字。所以问题就出在这里:我们必须先配置,启动ADC,再操作这些寄存器,不然就会使ADC进入未知状态。

【STM32F042】ADC卡死,ADC一直为0,ADC配置的坑_第2张图片

综上,第一个问题解决,正确代码思路如下: 

Adc_Init();//配置并启动ADC
Get_Value();//转换
void  Adc_Init(void)
{ 	
	ADC_InitTypeDef ADC_InitStructure; 
	GPIO_InitTypeDef GPIO_ADC;

  /*ADC以及GPIO时钟使能*/
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA|RCC_AHBPeriph_GPIOB,ENABLE);//GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE );	  //使能ADC1通道时钟

	RCC_ADCCLKConfig(RCC_ADCCLK_PCLK_Div4);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
  
  /*PA4,5 作为模拟通道输入引脚*/                  
  GPIO_ADC.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_4; //PA4与PA5
  GPIO_ADC.GPIO_Mode = GPIO_Mode_AN;		//模拟输入引脚
  GPIO_ADC.GPIO_PuPd = GPIO_PuPd_NOPULL; //无上下拉
  GPIO_Init(GPIOA, &GPIO_ADC);	 
  
  /*PB0 作为模拟通道输入引脚*/                  
  GPIO_ADC.GPIO_Pin = GPIO_Pin_0; //PA4与PA5
  GPIO_ADC.GPIO_Mode = GPIO_Mode_AN;		//模拟输入引脚
  GPIO_ADC.GPIO_PuPd = GPIO_PuPd_NOPULL; //无上下拉
  GPIO_Init(GPIOB, &GPIO_ADC);	
  
  
  /*ADC配置*/
  ADC_StructInit(&ADC_InitStructure);
  ADC_DeInit(ADC1); //复位并初始化
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位分辨率            
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;    //连续转换模式
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;     //外部触发无     
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   //右端对齐                                                       
  ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward;    //浏览模式
  
  ADC_Init(ADC1,&ADC_InitStructure); //初始化ADC
  
  ADC_GetCalibrationFactor(ADC1); //校验
  ADC_VrefintCmd(ENABLE);
  ADC_Cmd(ADC1,ENABLE); //使能
}	
float Get_Value(void)   
{

  u32 Value= 0;
  u8 index;
  ADC_ChannelConfig(ADC1,ADC_Channel_5,ADC_SampleTime_55_5Cycles); //选择通道
  ADC_StartOfConversion(ADC1);        //启动转换,注意不是启动ADC,而是启动ADC转换
    
  for(index = 0;index<8;index++)  //均值滤波
  {
    while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) == RESET);
    ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
    Get_Value+=  ADC_GetConversionValue(ADC1);
  }
  Result_Voltage = Result_Voltage>>3; //求平均值
  return (float)((Get_Value/4096.00)*3.3);  //计算电压值
}


 

关于第二个问题,黑脸,忘记在4096后面加小数点了,4096是整型,我的的ADC为分子,就会得到小数,刚好运气不好,小数为0.34---,于是计算机取整,默认它就是0,后面的我就不用讲了。粗心害人呀。

 return (float)(((Result_Voltage/4096)*3.3*(39+2.2))/2.2);  //错误
 return (float)(((Result_Voltage/4096.00)*3.3*(39+2.2))/2.2);  //正确

 

 

你可能感兴趣的:(STM32单片机)