STM32 ADC —测试实验

STM32 ADC —测试实验

环境

IDE: keil MDK
mcu: stm32f407vet6

采样通道:
PC0 —> ADC1_IN10
PC1 —> ADC1_IN11

cubeMX配置

在这里插入图片描述

  • 红框中的内容会根据不同的实验通过代码进行不同的修改。
void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */
  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = DISABLE; //ENABLE;
  hadc1.Init.ContinuousConvMode = DISABLE; //ENABLE; //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 = 2;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  */
  sConfig.Channel = ADC_CHANNEL_10;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_56CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
  */
  sConfig.Channel = ADC_CHANNEL_11;
  sConfig.Rank = 2;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

实验一:单次转换模式+禁止扫描模式

配置修改

将ADC初始化函数中的对应内容改为如下配置

hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;

实验代码

int main() {
	// ... 略
	while(1) {
		HAL_ADC_Start(&hadc1);
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[0] = HAL_ADC_GetValue(&hadc1);
    	HAL_Delay(100); // 可有可无
   		HAL_ADC_Start(&hadc1); //也可注释掉
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[1] = HAL_ADC_GetValue(&hadc1);
    	HAL_Delay(100);
	}
}

实验结果

两次采样的结果都是一样的,说明两次获取到的结果实际为一个通道的值
在这里插入图片描述
实际测试中并未触发溢出错误,说明没有数据丢失的情况
在这里插入图片描述

实验总结

虽然配置了多个通道,但禁止扫描模式,相当于只配置了第一个通道。因而每次采样都是第一个通道的值。单次转换模式,每次转换完配置的这个通道后便会停止,因而不会触发OVR错误。

实验二:连续转换模式+禁止扫描模式

配置修改

将ADC初始化函数中的对应内容改为如下配置

hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;

实验代码1

因为是连续采样模式只用启动一次转换即可

int main() {
	// ... 略
	HAL_ADC_Start(&hadc1);
	while(1) {
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[0] = HAL_ADC_GetValue(&hadc1);
    	//HAL_Delay(100); // 不能有延时
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[1] = HAL_ADC_GetValue(&hadc1);
    	//HAL_Delay(100);
	}
}

在这里插入图片描述
在这里插入图片描述
连续采样模式下只需启动一次转换即可。循环中不能有延时等待,这样不会触发溢出错误。两次采样通道的值一样,说明实际只转换了第一个通道。

实验代码2

int main() {
	// ... 略
	HAL_ADC_Start(&hadc1);
	while(1) {
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[0] = HAL_ADC_GetValue(&hadc1);
    	//HAL_Delay(100); 
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[1] = HAL_ADC_GetValue(&hadc1);
    	HAL_Delay(100);
	}
}

在这里插入图片描述
在这里插入图片描述
变量的值已经不更新变化了,因为触发了ADC溢出错误。如果想恢复的话需要清楚OVR标志位并重新启动采样或者直接调用HAL_ADC_Start()函数也可以恢复。

实验总结

连续采样模式下,如果不及时将数据寄存器中的数据读取出来,便会触发溢出错误

实验代码3:

int main() {
	// ... 略
	while(1) {
		HAL_ADC_Start(&hadc1);
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[0] = HAL_ADC_GetValue(&hadc1);
    	HAL_Delay(100);
	}
}

在这里插入图片描述
在这里插入图片描述
这种情况下虽然也会触发OVR溢出错误,但HAL_ADC_Start()函数会一直清除错误并重新启动采样,因而可以一直获取到第一个通道的数据。

实验三:单次转换模式+扫描模式

配置修改

将ADC初始化函数中的对应内容改为如下配置

hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = DISABLE;

实验代码1

int main() {
	// ... 略
	while(1) {
		HAL_ADC_Start(&hadc1);
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[0] = HAL_ADC_GetValue(&hadc1);
    	// HAL_Delay(100); // 可以有延时也可以没延时
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[1] = HAL_ADC_GetValue(&hadc1);
    	HAL_Delay(100);
	}
}

在这里插入图片描述
在这里插入图片描述
可以正确获取到两个通道的数据,也不会触发OVR错误。
单次扫描模式执行完一次序列中所有通道的采样便会停止。两个转换通道之间不建议添加延时函数,因为扫描模式下会一次性将所有通道的值转换完。但对于本例子而言只有两个通道,中间添加延时也不会出现溢出错误的情景,因为不涉及到数据丢失的情况。

实验代码2

int main() {
	// ... 略
	while(1) {
		HAL_ADC_Start(&hadc1);
    	//HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[0] = HAL_ADC_GetValue(&hadc1);
    	// HAL_Delay(100); // 可以有延时也可以没延时
    	//HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[1] = HAL_ADC_GetValue(&hadc1);
    	HAL_Delay(100);
	}
}

在这里插入图片描述
在这里插入图片描述
这种情况下出现了数据丢失因而会产生数据溢出的情况,两次获取到的结果一样是因为获取到了通道一的数据后没有清除EOC标志位,因而第二次读取到的结果还是第一通道的值(HAL_ADC_PollForConversion()函数中会清除EOC标志位)。

实验代码3

int main() {
	// ... 略
	while(1) {
		HAL_ADC_Start(&hadc1);
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[0] = HAL_ADC_GetValue(&hadc1);
    	HAL_Delay(100); // 可有可无
    	HAL_ADC_Start(&hadc1);
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[1] = HAL_ADC_GetValue(&hadc1);
    	HAL_Delay(100);
	}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
状态寄存器中的值一直在有溢出错误和无溢出之间跳变,说明出现了数据丢失的情况。且第二次启动ADC采样获取到的结果,恰巧也是第二个通道的数据。这说明在第一次启动ADC采样时正常会将两个通道的值都转换完成,第二个通道的值我们没有获取。再次启动ADC采样后,出现了溢出错误,读取到ADC数据依旧是第一次启动ADC采样后第二个通道转换完的数据(可以在单步调试过程中验证),下一次循环回到第一次启动ADC采样时又会清除OVR错误重新开启采样。

实验四:连续转换模式+扫描模式

配置修改

将ADC初始化函数中的对应内容改为如下配置

hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;

实验代码1

int main() {
	// ... 略
	HAL_ADC_Start(&hadc1);
	while(1) {
		//HAL_ADC_Start(&hadc1); //在这个位置开启也不会出错
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[0] = HAL_ADC_GetValue(&hadc1);
    	//HAL_Delay(100); // 
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[1] = HAL_ADC_GetValue(&hadc1);
    	//HAL_Delay(100);
	}
}

这里注意while循环中不能有延时,否则便会触发OVR错误。

  • ADC启动函数不放在循环内,且while循环内有延时的话便会出现ADC通道数据不会更新的情况,因为出错后没有恢复动作。

实验代码2

int main() {
	// ... 略
	while(1) {
		HAL_ADC_Start(&hadc1);
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[0] = HAL_ADC_GetValue(&hadc1);
    	HAL_Delay(100); // 
    	HAL_ADC_Start(&hadc1);
    	HAL_ADC_PollForConversion(&hadc1, 100);
    	adc_value[1] = HAL_ADC_GetValue(&hadc1);
    	HAL_Delay(100);
	}
}

这种情况可以分别正确获取到两个通道的数据,但会出现OVR错误。这种方式是比较容易出现的错误写法。

总结

上面的测试实验我们的主要目的是为了真正的区分清楚单次转换模式、连续转换模式、扫描模式之间的区别和联系,大多数写法在实际使用中并不推荐。

溢出错误

上面的实验中经常会出现溢出错误,那究竟什么情况下产生的这个溢出错误呢:启动ADC后ADC进行转换,转换完成后将EOC标志位置1,并将转换结果放到ADC数据寄存器。这算是完成了一次转换过程,再进行下次ADC转换,并转换完成前没有将EOC标志位清零,便会出现溢出错误。基于此再看上面的实验结果便会更容易理解了。
在这里插入图片描述

再看单次转换模式、连续转换模式、扫描模式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面是参考手册中对3种模式的描述,结合之前的实验来说总结如下:

  1. 采样单通道的情况下要禁止扫描模式,多通道的情况下要使用扫描模式。
  2. 单次转换模式可以根据需求在需要的地方进行一次转换(开启了扫描模式的话是序列中的每个通道都转换一次)
  3. 连续转换模式一般都用来配合DMA使用,否则很容易出现数据溢出的情况

例如:采样CH1一个通道

  • 单次转换模式:启动->CH1->结束
  • 连续转换模式:启动->CH1->CH1…

例如:采样CH1、 CH1、 CH3三个通道

  • 单次转换模式+扫描模式:启动->CH1->CH2->CH3->结束
  • 连续转换模式+扫描模式:启动->CH1->CH2->CH3->CH1->CH2…
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值