cubemx配置如下,单通道采样,独立模式,连续转换
错误案例:
在main函数while循环之前用HAL_ADC_Start_IT(&hadc2);函数打开了adc2的接收中断,
在中断回调函数中
产生的错误结果:程序在这里堵死,调试模式中无法继续向下执行
调试模式下运行
adcvlue的值会实时反馈
adc的isr寄存器的值在0x0000000F与0x0000001F之间跳变,程序无法进行while循环
程序卡死在中断回调函数里面
查找说明手册
此位为数据丢失位,原因目前暂且未知,后面分析hal库函数的源码
先将代码修改成如此,
通过自定义状态标志位在while循环中来循环开启,以此跳出被卡死的中断回调。这样修改之后可以进行adc中断回调读取的正常流程了,但是原理部分仍未解决。下面来根据说明书分析hal库源码,以此视图找出根本错误原因。
首先
罪魁祸首HAL_ADC_Start_IT
由于这里使用的是连续转换模式,让我们先看一下连续转换模式究竟是什么
下面是源码
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef *hadc)
{
HAL_StatusTypeDef tmp_hal_status;
#if defined(ADC_MULTIMODE_SUPPORT)
const ADC_TypeDef *tmpADC_Master;
uint32_t tmp_multimode_config = LL_ADC_GetMultimode(__LL_ADC_COMMON_INSTANCE(hadc->Instance));
#endif
/* Check the parameters */
assert_param(IS_ADC_ALL_INSTANCE(hadc->Instance));
/* Perform ADC enable and conversion start if no conversion is on going */
if (LL_ADC_REG_IsConversionOngoing(hadc->Instance) == 0UL)
{
/* Process locked */
__HAL_LOCK(hadc);
/* Enable the ADC peripheral */
tmp_hal_status = ADC_Enable(hadc);
/* Start conversion if ADC is effectively enabled */
if (tmp_hal_status == HAL_OK)
{
/* Set ADC state */
/* - Clear state bitfield related to regular group conversion results */
/* - Set state bitfield related to regular operation */
ADC_STATE_CLR_SET(hadc->State,
HAL_ADC_STATE_READY | HAL_ADC_STATE_REG_EOC | HAL_ADC_STATE_REG_OVR | HAL_ADC_STATE_REG_EOSMP,
HAL_ADC_STATE_REG_BUSY);
#if defined(ADC_MULTIMODE_SUPPORT)
/* Reset HAL_ADC_STATE_MULTIMODE_SLAVE bit
- if ADC instance is master or if multimode feature is not available
- if multimode setting is disabled (ADC instance slave in independent mode) */
if ((__LL_ADC_MULTI_INSTANCE_MASTER(hadc->Instance) == hadc->Instance)
|| (tmp_multimode_config == LL_ADC_MULTI_INDEPENDENT)
)
{
CLEAR_BIT(hadc->State, HAL_ADC_STATE_MULTIMODE_SLAVE);
}
#endif
/* Set ADC error code */
/* Check if a conversion is on going on ADC group injected */
if ((hadc->State & HAL_ADC_STATE_INJ_BUSY) != 0UL)
{
/* Reset ADC error code fields related to regular conversions only */
CLEAR_BIT(hadc->ErrorCode, (HAL_ADC_ERROR_OVR | HAL_ADC_ERROR_DMA));
}
else
{
/* Reset all ADC error code fields */
ADC_CLEAR_ERRORCODE(hadc);
}
/* Clear ADC group regular conversion flag and overrun flag */
/* (To ensure of no unknown state from potential previous ADC operations) */
__HAL_ADC_CLEAR_FLAG(hadc, (ADC_FLAG_EOC | ADC_FLAG_EOS | ADC_FLAG_OVR));
/* Process unlocked */
/* Unlock before starting ADC conversions: in case of potential */
/* interruption, to let the process to ADC IRQ Handler. */
__HAL_UNLOCK(hadc);
/* Disable all interruptions before enabling the desired ones */
__HAL_ADC_DISABLE_IT(hadc, (ADC_IT_EOC | ADC_IT_EOS | ADC_IT_OVR));
/* Enable ADC end of conversion interrupt */
switch (hadc->Init.EOCSelection)
{
case ADC_EOC_SEQ_CONV:
__HAL_ADC_ENABLE_IT(hadc, ADC_IT_EOS);
break;
/* case ADC_EOC_SINGLE_CONV */
default:
__HAL_ADC_ENABLE_IT(hadc, ADC_IT_EOC);
break;
}
/* Enable ADC overrun interrupt */
/* If hadc->Init.Overrun is set to ADC_OVR_DATA_PRESERVED, only then is
ADC_IT_OVR enabled; otherwise data overwrite is considered as normal
behavior and no CPU time is lost for a non-processed interruption */
if (hadc->Init.Overrun == ADC_OVR_DATA_PRESERVED)
{
__HAL_ADC_ENABLE_IT(hadc, ADC_IT_OVR);
}
/* Enable conversion of regular group. */
/* If software start has been selected, conversion starts immediately. */
/* If external trigger has been selected, conversion will start at next */
/* trigger event. */
/* Case of multimode enabled (when multimode feature is available): */
/* - if ADC is slave and dual regular conversions are enabled, ADC is */
/* enabled only (conversion is not started), */
/* - if ADC is master, ADC is enabled and conversion is started. */
#if defined(ADC_MULTIMODE_SUPPORT)
if ((__LL_ADC_MULTI_INSTANCE_MASTER(hadc->Instance) == hadc->Instance)
|| (tmp_multimode_config == LL_ADC_MULTI_INDEPENDENT)
|| (tmp_multimode_config == LL_ADC_MULTI_DUAL_INJ_SIMULT)
|| (tmp_multimode_config == LL_ADC_MULTI_DUAL_INJ_ALTERN)
)
{
/* ADC instance is not a multimode slave instance with multimode regular conversions enabled */
if (READ_BIT(hadc->Instance->CFGR, ADC_CFGR_JAUTO) != 0UL)
{
ADC_STATE_CLR_SET(hadc->State, HAL_ADC_STATE_INJ_EOC, HAL_ADC_STATE_INJ_BUSY);
/* Enable as well injected interruptions in case
HAL_ADCEx_InjectedStart_IT() has not been called beforehand. This
allows to start regular and injected conversions when JAUTO is
set with a single call to HAL_ADC_Start_IT() */
switch (hadc->Init.EOCSelection)
{
case ADC_EOC_SEQ_CONV:
__HAL_ADC_DISABLE_IT(hadc, ADC_IT_JEOC);
__HAL_ADC_ENABLE_IT(hadc, ADC_IT_JEOS);
break;
/* case ADC_EOC_SINGLE_CONV */
default:
__HAL_ADC_DISABLE_IT(hadc, ADC_IT_JEOS);
__HAL_ADC_ENABLE_IT(hadc, ADC_IT_JEOC);
break;
}
}
/* Start ADC group regular conversion */
LL_ADC_REG_StartConversion(hadc->Instance);
}
else
{
/* ADC instance is a multimode slave instance with multimode regular conversions enabled */
SET_BIT(hadc->State, HAL_ADC_STATE_MULTIMODE_SLAVE);
/* if Master ADC JAUTO bit is set, Slave injected interruptions
are enabled nevertheless (for same reason as above) */
tmpADC_Master = __LL_ADC_MULTI_INSTANCE_MASTER(hadc->Instance);
if (READ_BIT(tmpADC_Master->CFGR, ADC_CFGR_JAUTO) != 0UL)
{
/* First, update Slave State in setting HAL_ADC_STATE_INJ_BUSY bit
and in resetting HAL_ADC_STATE_INJ_EOC bit */
ADC_STATE_CLR_SET(hadc->State, HAL_ADC_STATE_INJ_EOC, HAL_ADC_STATE_INJ_BUSY);
/* Next, set Slave injected interruptions */
switch (hadc->Init.EOCSelection)
{
case ADC_EOC_SEQ_CONV:
__HAL_ADC_DISABLE_IT(hadc, ADC_IT_JEOC);
__HAL_ADC_ENABLE_IT(hadc, ADC_IT_JEOS);
break;
/* case ADC_EOC_SINGLE_CONV */
default:
__HAL_ADC_DISABLE_IT(hadc, ADC_IT_JEOS);
__HAL_ADC_ENABLE_IT(hadc, ADC_IT_JEOC);
break;
}
}
}
#else
/* ADC instance is not a multimode slave instance with multimode regular conversions enabled */
if (READ_BIT(hadc->Instance->CFGR, ADC_CFGR_JAUTO) != 0UL)
{
ADC_STATE_CLR_SET(hadc->State, HAL_ADC_STATE_INJ_EOC, HAL_ADC_STATE_INJ_BUSY);
/* Enable as well injected interruptions in case
HAL_ADCEx_InjectedStart_IT() has not been called beforehand. This
allows to start regular and injected conversions when JAUTO is
set with a single call to HAL_ADC_Start_IT() */
switch (hadc->Init.EOCSelection)
{
case ADC_EOC_SEQ_CONV:
__HAL_ADC_DISABLE_IT(hadc, ADC_IT_JEOC);
__HAL_ADC_ENABLE_IT(hadc, ADC_IT_JEOS);
break;
/* case ADC_EOC_SINGLE_CONV */
default:
__HAL_ADC_DISABLE_IT(hadc, ADC_IT_JEOS);
__HAL_ADC_ENABLE_IT(hadc, ADC_IT_JEOC);
break;
}
}
/* Start ADC group regular conversion */
LL_ADC_REG_StartConversion(hadc->Instance);
#endif
}
else
{
/* Process unlocked */
__HAL_UNLOCK(hadc);
}
}
else
{
tmp_hal_status = HAL_BUSY;
}
/* Return function status */
return tmp_hal_status;
}
这个函数有很多对于hal库状态的维护,我们不用全看,它开启了哪些中断
eos中断或者eoc中断,而导致我们程序卡死的是ovr中断(前面提到的isr寄存器跳变)
这就清楚了,我们的adc采样过载了。
那么,下一个问题,有什么原因会导致adc采样过载呢
网上很少找到资源,来问问gpt
都是围绕着几点,采样过快或者超出了电压
那来让我们延长采样的时间吧
最后,采样可以正常进行。读取正常
得到结论:当我们在main函数调用hal_adc_start_it后,在adc的中断回调中直接读取就可以,hal库的处理流程并不会帮我们关闭这个中断,而过载会导致ovr标志位一直被置1,这样就会一直进入中断,导致程序卡死
大家要合理选取采样时间哦