STM32F4 HAL ADC DMA 传输错误
STM32F4 HAL ADC DMA Transfer Error
我正在为一个项目使用 STM32F405OG,其中一项必要的功能是以 ~1 Hz 的频率监控 3 个模拟通道。我想要的实现是在扫描模式下开始对所有 3 个通道进行 ADC DMA 读取,并在 DMA 完成中断发生后稍后检索结果。
我正在使用 ADC1 并尝试了 DMA 通道 0 和 4,结果相同:在第一次调用 HAL_ADC_Start_DMA() 之后调用 HAL_ADC_ErrorCallback()。此时,ADC 句柄处于错误状态 (HAL_ADC_STATE_ERROR_DMA),错误代码为 0x04 (HAL_ADC_ERROR_DMA)。检查链接的 DMA 句柄会产生 HAL_DMA_ERROR_NO_XFER 的 DMA 错误代码,意思是 "Abort requested with no Xfer ongoing."
我完全不知道是什么原因造成的 - 我的代码应该与示例和 stm32f4xx_hal_adc.c 顶部的 "how to use this module" 注释一致。我在下面附上了我的代码。
ADC_HandleTypeDef ADC_hADC =
{
.Instance = ADC1,
.Init =
{
.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8,
.Resolution = ADC_RESOLUTION_12B,
.EOCSelection = ADC_EOC_SEQ_CONV, // EOC at end of sequence of channel conversions
.ScanConvMode = ENABLE,
.ContinuousConvMode = DISABLE,
.DiscontinuousConvMode = DISABLE,
.NbrOfDiscConversion = 0U,
.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE,
.ExternalTrigConv = ADC_SOFTWARE_START,
.DataAlign = ADC_DATAALIGN_RIGHT,
.NbrOfConversion = _NUM_ADC_CONV,
.DMAContinuousRequests = DISABLE
}
};
DMA_HandleTypeDef _hDmaAdc =
{
.Instance = DMA2_Stream0,
.Init =
{
.Channel = DMA_CHANNEL_0,
.Direction = DMA_PERIPH_TO_MEMORY,
.PeriphInc = DMA_PINC_DISABLE,
.MemInc = DMA_MINC_ENABLE,
.PeriphDataAlignment = DMA_PDATAALIGN_WORD,
.MemDataAlignment = DMA_MDATAALIGN_WORD,
.Mode = DMA_NORMAL,
.Priority = DMA_PRIORITY_HIGH,
.FIFOMode = DMA_FIFOMODE_DISABLE,
.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL,
.MemBurst = DMA_MBURST_SINGLE,
.PeriphBurst = DMA_PBURST_SINGLE
}
};
void HAL_ADC_MspInit(ADC_HandleTypeDef *h)
{
if (!h)
{
return;
}
else if (h->Instance == ADC1)
{
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
HAL_DMA_Init(&_hDmaAdc);
__HAL_LINKDMA(h, DMA_Handle, _hDmaAdc);
HAL_NVIC_SetPriority(ADC_IRQn, IT_PRIO_ADC, 0);
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, IT_PRIO_ADC, 0);
HAL_NVIC_EnableIRQ(ADC_IRQn);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}
}
uint32_t _meas[3];
ADC_ChannelConfTypeDef _chanCfg[3] =
{
// VIN_MON
{
.Channel = ADC_CHANNEL_1,
},
// VDD_MON
{
.Channel = ADC_CHANNEL_8,
},
// VDD2_MON
{
.Channel = ADC_CHANNEL_2,
}
};
Bool ADC_Init(void)
{
ADC_DeInit();
memset(_meas, 0, sizeof(_meas));
Bool status = (HAL_ADC_Init(&ADC_hADC) == HAL_OK);
if (status)
{
// Configure each ADC channel
for (uint32_t i = 0U; i < NELEM(_chanCfg); i++)
{
_chanCfg[i].Rank = (i + 1U);
_chanCfg[i].SamplingTime = ADC_SAMPLETIME_480CYCLES;
_chanCfg[i].Offset = 0U;
if (HAL_ADC_ConfigChannel(&ADC_hADC, &_chanCfg[i]) != HAL_OK)
{
status = FALSE;
break;
}
}
_state = ADC_STATE_READY;
}
if (!status)
{
ADC_DeInit();
}
return status;
}
Bool ADC_StartRead(void)
{
Bool status = TRUE;
status = (HAL_ADC_Start_DMA(&ADC_hADC, &_meas[0], 3) == HAL_OK);
return status;
}
在通过 ClockPrescaler 初始化结构字段减慢转换速度、增加 ADC 周期数并调用 HAL_ADC_Stop_DMA()(根据 stm32f4xx_hal_adc.c 中的文件头注释)后,一切正常。
请注意,在 DMA 传输完成 ISR 中调用 HAL_ADC_Stop_DMA() 也会导致上述错误情况,因此必须在调用 DMAXferCplt ISR 之后的某个时间调用该函数。
我正在为一个项目使用 STM32F405OG,其中一项必要的功能是以 ~1 Hz 的频率监控 3 个模拟通道。我想要的实现是在扫描模式下开始对所有 3 个通道进行 ADC DMA 读取,并在 DMA 完成中断发生后稍后检索结果。
我正在使用 ADC1 并尝试了 DMA 通道 0 和 4,结果相同:在第一次调用 HAL_ADC_Start_DMA() 之后调用 HAL_ADC_ErrorCallback()。此时,ADC 句柄处于错误状态 (HAL_ADC_STATE_ERROR_DMA),错误代码为 0x04 (HAL_ADC_ERROR_DMA)。检查链接的 DMA 句柄会产生 HAL_DMA_ERROR_NO_XFER 的 DMA 错误代码,意思是 "Abort requested with no Xfer ongoing."
我完全不知道是什么原因造成的 - 我的代码应该与示例和 stm32f4xx_hal_adc.c 顶部的 "how to use this module" 注释一致。我在下面附上了我的代码。
ADC_HandleTypeDef ADC_hADC =
{
.Instance = ADC1,
.Init =
{
.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8,
.Resolution = ADC_RESOLUTION_12B,
.EOCSelection = ADC_EOC_SEQ_CONV, // EOC at end of sequence of channel conversions
.ScanConvMode = ENABLE,
.ContinuousConvMode = DISABLE,
.DiscontinuousConvMode = DISABLE,
.NbrOfDiscConversion = 0U,
.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE,
.ExternalTrigConv = ADC_SOFTWARE_START,
.DataAlign = ADC_DATAALIGN_RIGHT,
.NbrOfConversion = _NUM_ADC_CONV,
.DMAContinuousRequests = DISABLE
}
};
DMA_HandleTypeDef _hDmaAdc =
{
.Instance = DMA2_Stream0,
.Init =
{
.Channel = DMA_CHANNEL_0,
.Direction = DMA_PERIPH_TO_MEMORY,
.PeriphInc = DMA_PINC_DISABLE,
.MemInc = DMA_MINC_ENABLE,
.PeriphDataAlignment = DMA_PDATAALIGN_WORD,
.MemDataAlignment = DMA_MDATAALIGN_WORD,
.Mode = DMA_NORMAL,
.Priority = DMA_PRIORITY_HIGH,
.FIFOMode = DMA_FIFOMODE_DISABLE,
.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL,
.MemBurst = DMA_MBURST_SINGLE,
.PeriphBurst = DMA_PBURST_SINGLE
}
};
void HAL_ADC_MspInit(ADC_HandleTypeDef *h)
{
if (!h)
{
return;
}
else if (h->Instance == ADC1)
{
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
HAL_DMA_Init(&_hDmaAdc);
__HAL_LINKDMA(h, DMA_Handle, _hDmaAdc);
HAL_NVIC_SetPriority(ADC_IRQn, IT_PRIO_ADC, 0);
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, IT_PRIO_ADC, 0);
HAL_NVIC_EnableIRQ(ADC_IRQn);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}
}
uint32_t _meas[3];
ADC_ChannelConfTypeDef _chanCfg[3] =
{
// VIN_MON
{
.Channel = ADC_CHANNEL_1,
},
// VDD_MON
{
.Channel = ADC_CHANNEL_8,
},
// VDD2_MON
{
.Channel = ADC_CHANNEL_2,
}
};
Bool ADC_Init(void)
{
ADC_DeInit();
memset(_meas, 0, sizeof(_meas));
Bool status = (HAL_ADC_Init(&ADC_hADC) == HAL_OK);
if (status)
{
// Configure each ADC channel
for (uint32_t i = 0U; i < NELEM(_chanCfg); i++)
{
_chanCfg[i].Rank = (i + 1U);
_chanCfg[i].SamplingTime = ADC_SAMPLETIME_480CYCLES;
_chanCfg[i].Offset = 0U;
if (HAL_ADC_ConfigChannel(&ADC_hADC, &_chanCfg[i]) != HAL_OK)
{
status = FALSE;
break;
}
}
_state = ADC_STATE_READY;
}
if (!status)
{
ADC_DeInit();
}
return status;
}
Bool ADC_StartRead(void)
{
Bool status = TRUE;
status = (HAL_ADC_Start_DMA(&ADC_hADC, &_meas[0], 3) == HAL_OK);
return status;
}
在通过 ClockPrescaler 初始化结构字段减慢转换速度、增加 ADC 周期数并调用 HAL_ADC_Stop_DMA()(根据 stm32f4xx_hal_adc.c 中的文件头注释)后,一切正常。
请注意,在 DMA 传输完成 ISR 中调用 HAL_ADC_Stop_DMA() 也会导致上述错误情况,因此必须在调用 DMAXferCplt ISR 之后的某个时间调用该函数。