2024-01-18 07:37 AM - edited 2024-01-18 07:38 AM
I have a setup which reads data from 4 step-motors and some sensors over ADC1(CH0, CH1, CH2, CH3), and ADC2(CH0, CH1).
The system executes continuous ADC conversions in DMA Mode in the following way:
- In the while-loop in the main function, it reads the actual position of the motors and the data of the sensors.
- The TIM3, which is configured to execute in an interval of 20ms, is responsible for controlling the motors.
The issue that I am facing is that both ADCs stop reading a few minutes after starting, and no data in the register DR is available, it just freezes, first one ADC then the other one, sometimes ADC1 freezes first, other ADC2.
I have checked both ADCs Overflow flags & DMA Transfer error flags, but they indicate no error. I thought some race conditions could be happening due to TIM3, so I have commented out its initialization, but even though the same issue persisted.
I am using the STM32F303VET
Microsoft Visual Studio Professional 2019 - Version 16.11.30
VisualGDB Version 5.6R9
The initialization codes are:
ADC1
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
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 = 9;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure the ADC multi-mode
*/
multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_4CYCLES_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
The configuration for other Channels and ADC2 is the same
For DMA1:
hdma_adc1.Instance = DMA1_Channel1;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);
__HAL_RCC_DMA1_CLK_ENABLE();
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
DMA2
hdma_adc2.Instance = DMA2_Channel1;
hdma_adc2.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc2.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc2.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc2.Init.Mode = DMA_CIRCULAR;
hdma_adc2.Init.Priority = DMA_PRIORITY_HIGH;
if (HAL_DMA_Init(&hdma_adc2) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc2);
__HAL_RCC_DMA2_CLK_ENABLE();
HAL_NVIC_SetPriority(DMA2_Channel1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(DMA2_Channel1_IRQn);
TIM3
static const uint32_t TIM3_PRIORITY = 2;
static const uint32_t PRS_TIM3 = 19507 - 1;
static const uint8_t ARR_TIM3 = 2 - 1;
void InitializeTimerTim3()
{
// Enable TIM3 clock
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
// Configure TIM3
TIM3->PSC = PRS_TIM3; // Prescaler
TIM3->CR1 &= ~TIM_CR1_DIR; // Counter direction up
TIM3->ARR = ARR_TIM3; // Autoreload Register
TIM3->CR1 |= TIM_CR1_CKD_0; // Clock division: division by 1
TIM3->RCR = 0; // Repetition counter register
TIM3->CR1 |= TIM_CR1_CEN; // Enable TIM3
}
void EnableTimerInterruptTim3()
{
// Configure NVIC for TIM3 interrupt
HAL_NVIC_SetPriority(TIM3_IRQn, TIM3_PRIORITY, 0); // Set priority
HAL_NVIC_EnableIRQ(TIM3_IRQn); // Enable TIM3 interrupt
TIM3->DIER |= TIM_DIER_UIE; // Enable update interrupt
}
And the function in the main-loop to read from the ADC: adcMcu_executeDma()
void adcMcu_executeDma(adcMcu_DmaInterface_t *adc, uint64_t time)
{
if (adc->state == adc_wait && adc->nextUpdateTime <= time)
{
startDmaInput(adc, time);
}
if (adc->state == adc_finish)
{
uint64_t nextTime = UINT64_MAX;
for (uint8_t i = 0; i < adc->numberOfChannels; ++i)
{
calcInput(adc, i, time);
if (nextTime > adc->channels[i].nextUpdateTime_ms)
{
nextTime = adc->channels[i].nextUpdateTime_ms;
}
}
adc->nextUpdateTime = nextTime;
adc->state = adc_wait;
}
}
void startDmaInput(adcMcu_DmaInterface_t *adc, uint64_t time)
{
HAL_ADC_Start_DMA(adc->hadc, (uint32_t*)adc->rawValueMatrix, adc->numberOfChannels * adc->numberOfValues);
adc->state = adc_working;
}
I have run out of ideas about what might be causing it.
Any help is highly appreciated.
Solved! Go to Solution.
2024-01-22 06:41 AM
That doesn't explain why you need to call it more than once per DMA stream. Why call HAL_ADC_Stop_DMA ever if your intention is to convert ADC data continuously?
I suspect there is a program logic bug, but I don't think it's in the code you've shown.
2024-01-18 07:52 AM
Consider starting DMA only once in circular mode and using the half- and full-complete interrupts to process converted data. Although it seems like you're in circular mode, so why is DMA being started multiple times?
> sConfig.SamplingTime = ADC_SAMPLETIME_4CYCLES_5;
Consider increasing this. Could be swamping the system with data.
The bug is probably within other code, or within the order in which things get executed. The code logic used to call adcMcu_executeDma and set adc->state == adc_finish would be informative here.
2024-01-22 12:06 AM
Hello @TDK , thank you for your prompt response.
So, I am starting the DMA multiple times because I am using the DMA1 CH1 for the ADC1 and DMA2 CH1 for the ADC2. As there are many devices I choose to make use of two DMAs
These functions were written in my first comment, they are executing in the while-loop in the main() function:
adc_execute( ) → adcMcu_executeDmaInput() →
→ if (adc->state == adc_wait) → startDmaInput() → HAL_ADC_Start_DMA() → adc->state = adc_working
→ if (adc->state == adc_finish) → calcInput() → adc->state = adc_wait
This is in the interrupt when the ADC conversion is completed:
HAL_ADC_ConvCpltCallback() → adcMcu_dmaInterrupt() → HAL_ADC_Stop_DMA() → adc->state = adc_finish;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if (hadc == &hadc1)
{
adcMcu_dmaInterrupt(&adc1_Interface);
}
else if (hadc == &hadc2)
{
adcMcu_dmaInterrupt(&adc2_Interface);
}
}
void adcMcu_dmaInterrupt(adcMcu_DmaInterface_t *adc)
{
HAL_ADC_Stop_DMA(adc->hadc);
adc->state = adc_finish;
}
2024-01-22 06:41 AM
That doesn't explain why you need to call it more than once per DMA stream. Why call HAL_ADC_Stop_DMA ever if your intention is to convert ADC data continuously?
I suspect there is a program logic bug, but I don't think it's in the code you've shown.
2024-01-23 01:42 AM
@TDK The problem was exactly in the continuous calling of the DMA_Start and Stop, somehow it was messing up with all ADCs readings.
So I have commented out the HAL_ADC_Stop_DMA and called the HAL_ADC_Start_DMA() only once before the while-loop.