2023-02-27 02:28 AM
I am trying to read data using ADC1 continuously and copy it into a buffer via DMA.
However, the data is only read and copied once (seemingly correctly), and afterwards nothing happens anymore (data in buffer does not change, ADC1_IRQHandler() is not called anymore).
If I set a breakpoint somewhere in the program, the value of the ADC_CFGR register equals 2147495944, i. e. the DMNGT bits are both 0. Shouldn't they be both 1, as I enabled DMA Circular Mode?
As you can see in the code, the MX_DMA_Init() function is called before HAL_ADC_Init(), which was seemingly a bug in earlier versions of STM32CubeIDE (see this thread: How to enable ADC continuous mode with DMA? (st.com)).
I already tried setting the DMNGT bits manually and also tried setting the ADSTART bit manually in the ADC1_IRQHandler() on EOC interrupt, but both did not work.
I am using an STM32MP153AAB3 on a custom board.
The relevant parts of code look like this (I left out unrelated things, timer inits etc. for the sake of clarity):
main.cpp:
/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
IPCC_HandleTypeDef hipcc;
static MyAdc myAdc(ADC1, ADC_CHANNEL_12, &hadc1);
static MyMeasurement myMeasurement{&myAdc};
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_IPCC_Init(void);
/*
...
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
if(IS_ENGINEERING_BOOT_MODE())
{
/* Configure the system clock */
SystemClock_Config();
}
else
{
/* IPCC initialisation */
MX_IPCC_Init();
}
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
MX_DMA_Init();
myMeasurement.init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSIDivValue = RCC_HSI_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_NONE;
RCC_OscInitStruct.PLL4.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL4.PLLSource = RCC_PLL4SOURCE_HSI;
RCC_OscInitStruct.PLL4.PLLM = 4;
RCC_OscInitStruct.PLL4.PLLN = 25;
RCC_OscInitStruct.PLL4.PLLP = 2;
RCC_OscInitStruct.PLL4.PLLQ = 2;
RCC_OscInitStruct.PLL4.PLLR = 4;
RCC_OscInitStruct.PLL4.PLLRGE = RCC_PLL4IFRANGE_1;
RCC_OscInitStruct.PLL4.PLLFRACV = 0;
RCC_OscInitStruct.PLL4.PLLMODE = RCC_PLL_INTEGER;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** RCC Clock Config
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_ACLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_PCLK3|RCC_CLOCKTYPE_PCLK4
|RCC_CLOCKTYPE_PCLK5;
RCC_ClkInitStruct.AXISSInit.AXI_Clock = RCC_AXISSOURCE_HSI;
RCC_ClkInitStruct.AXISSInit.AXI_Div = RCC_AXI_DIV1;
RCC_ClkInitStruct.MCUInit.MCU_Clock = RCC_MCUSSOURCE_HSI;
RCC_ClkInitStruct.MCUInit.MCU_Div = RCC_MCU_DIV1;
RCC_ClkInitStruct.APB4_Div = RCC_APB4_DIV1;
RCC_ClkInitStruct.APB5_Div = RCC_APB5_DIV1;
RCC_ClkInitStruct.APB1_Div = RCC_APB1_DIV1;
RCC_ClkInitStruct.APB2_Div = RCC_APB2_DIV1;
RCC_ClkInitStruct.APB3_Div = RCC_APB3_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct) != HAL_OK)
{
Error_Handler();
}
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMAMUX_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
/* DMA interrupt init */
/* DMA2_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}
MyMeasurement.cpp:
MyMeasurement::MyMeasurement(MyAdc* myAdc)
{
this->myAdc= myAdc;
}
void MyMeasurement::init()
{
myAdc->Init();
myAdc->Start();
}
MyAdc.cpp:
MyAdc::MyAdc(ADC_TypeDef* adc, uint32_t adcChannel, ADC_HandleTypeDef* adcHandle)
{
this->adcChannel = adcChannel;
this->adcHandle = adcHandle;
/** Common config
*/
adcHandle->Instance = adc;
adcHandle->Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
adcHandle->Init.Resolution = ADC_RESOLUTION_12B;
adcHandle->Init.ScanConvMode = ADC_SCAN_DISABLE;
adcHandle->Init.EOCSelection = ADC_EOC_SEQ_CONV;
adcHandle->Init.LowPowerAutoWait = DISABLE;
adcHandle->Init.ContinuousConvMode = ENABLE;
adcHandle->Init.NbrOfConversion = 1;
adcHandle->Init.DiscontinuousConvMode = DISABLE;
adcHandle->Init.NbrOfDiscConversion = 1;
adcHandle->Init.ExternalTrigConv = ADC_SOFTWARE_START;
adcHandle->Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
adcHandle->Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
adcHandle->Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
adcHandle->Init.OversamplingMode = DISABLE;
}
void MyAdc::Init(void)
{
ASSERT(HAL_ADC_DeInit(adcHandle) == HAL_OK);
ASSERT(HAL_ADC_Init(adcHandle) == HAL_OK);
/** Configure the ADC multi-mode
*/
ADC_MultiModeTypeDef multimode = {0};
multimode.Mode = ADC_MODE_INDEPENDENT; // multi-mode disabled
ASSERT(HAL_ADCEx_MultiModeConfigChannel(adcHandle, &multimode) == HAL_OK);
/** Configure Regular Channel
*/
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = adcChannel;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_387CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
HAL_ADC_ConfigChannel(adcHandle, &sConfig);
/* Run the ADC calibration in single-ended mode */
ASSERT(HAL_ADCEx_Calibration_Start(adcHandle, ADC_CALIB_OFFSET_LINEARITY, ADC_SINGLE_ENDED) == HAL_OK);
// enable End of Conversion interrupt (call ADC1_IRQHandler at the end of each conversion)
SET_BIT(adcHandle->Instance->IER, ADC_IER_EOCIE);
}
void MyAdc::Start()
{
ASSERT(HAL_ADC_Start_DMA(adcHandle, (uint32_t*) &samplingData[0], BUFFER_SIZE) == HAL_OK);
}