cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L0 ADC two channel polling

ortegagonzalez
Associate II
Posted on November 06, 2016 at 21:04

Hi, 

I am coding an application for STM32L053R8 using the HAL functions. Right now I need to read continuously two analog values via ADC. Because there is no need of high frequency readings, I want to try doing it via polling instead of using the DMA right away.

The problem is I don't know how to differentiate from which channel the value at the ADC data register comes from. Because I've set the EOCSelection to Single Conversion, my idea was to assign the value to one channel when only EOC is active and assign it to the other one when both EOC and EOS are active, depending on each channel's rank on the regular group. However, the HAL's function PollForConversion clears both EOC and EOS, so I have no way of knowing their value when the data is avalaible. 

If I was using STM32F4, I would just use the Injected Channel sequence, but I believe (please correct me if I'm wrong) they are not available in STM32L0. 

Any ideas on how to do this? 

Thank you very much.

#stm32l0 #gpio #no-hablo-hal #adc_channel #adc
5 REPLIES 5
Posted on November 06, 2016 at 22:59

Typically DMA is used as it is more efficient, especially for low power applications, the CPU burns a lot of power grinding in loops.

You can use DMA in circular, not continous mode, trigger it and use the DMA TC as an EOC for channels. Your channels are sequenced correctly in your array.

You could use two ADC.

Not sufficiently familiar with L0+HAL, but most STM32 require a tear down of the ADC to change regular channels.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
ortegagonzalez
Associate II
Posted on November 07, 2016 at 22:21

Thank you for the answer, Clive. Unfortunately, I believe there is just one ADC in thismcu.

I've followed your suggestion and used DMA, as there is just one ADC available in STM32L053R8. However, I'm finding it really difficult to set up something with the limited information the reference manual, the HAL driverguideand the examples provide. In fact, the constant names provided in the examples are not accepted in my main file. Tomorrow I'll have access to the Nucleo board and I'll update with the results. What I've done so far is this:

/*-----DMA-----*/
DMA_HandleTypeDef DmaHandle;
static const uint32_t aSRC_Buffer[1] = {/*ADC_DR address = ?};*/};
static uint32_t aDST_Buffer[2];
/*-----ADC-----*/
/* ADC handle declaration */
ADC_HandleTypeDef AdcHandle;
/* ADC channel configuration structure declaration */
ADC_ChannelConfTypeDef sConfigADC;
/* Converted value declaration */
uint32_t aResultDMA;
int main(void){
/*-----DMA_Initialization-----*/
/*## -1- Enable DMA1 clock #################################################*/
__HAL_RCC_DMA1_CLK_ENABLE();
/*## -2- Select the DMA functional Parameters ###############################*/
DmaHandle.Init.Direction = DMA_PERIPH_TO_MEMORY; /* P2M transfer mode */
DmaHandle.Init.PeriphInc = DMA_PINC_DISABLE; /* Peripheral increment mode Disable */
DmaHandle.Init.MemInc = DMA_MINC_ENABLE; /* Memory increment mode Enable */
DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; /* Peripheral data alignment : Word */
DmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; /* memory data alignment : Word */
DmaHandle.Init.Mode = DMA_CIRCULAR; /* Circular DMA mode */
DmaHandle.Init.Priority = DMA_PRIORITY_HIGH; /* priority level : high */
DmaHandle.Init.Request = DMA_REQUEST_1;
/*## -3- Select the DMA instance to be used for the transfer : DMA1_Channel1 #*/
DmaHandle.Instance = DMA1_Channel1;
/*## -4- Select Callbacks functions called after Transfer complete and Transfer error */
DmaHandle.XferCpltCallback = TransferComplete;
DmaHandle.XferErrorCallback = TransferError;
/*## -5- Initialize the DMA stream ##########################################*/
if(HAL_DMA_Init(&DmaHandle) != HAL_OK)
{
/* Initialization Error */
Error_Handler();
}
/*## -6- Configure NVIC for DMA transfer complete/error interrupts ##########*/
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
/* Enable the DMA STREAM global Interrupt */
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
/*## -7- Start the DMA transfer using the interrupt mode ####################*/
/* Configure the source, destination and buffer size DMA fields and Start DMA Stream transfer */
/* Enable All the DMA interrupts */
if(HAL_DMA_Start_IT(&DmaHandle, (uint32_t)&aSRC_Buffer, (uint32_t)&aDST_Buffer, 2) != HAL_OK)
{
/* Transfer Error */
Error_Handler();
}
/*-----ADC_Initialization-----*/
/* ### - 1 - Initialize ADC peripheral #################################### */
AdcHandle.Instance = ADC1;
AdcHandle.Init.OversamplingMode = DISABLE;
AdcHandle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
AdcHandle.Init.LowPowerAutoPowerOff = DISABLE;
AdcHandle.Init.LowPowerFrequencyMode = ENABLE;
AdcHandle.Init.LowPowerAutoWait = DISABLE;
AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;
AdcHandle.Init.SamplingTime = ADC_SAMPLETIME_7CYCLES_5;
AdcHandle.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
AdcHandle.Init.ContinuousConvMode = DISABLE;
AdcHandle.Init.DiscontinuousConvMode = DISABLE;
AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
AdcHandle.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
AdcHandle.Init.DMAContinuousRequests = ENABLE;
/* Initialize ADC peripheral according to the passed parameters */
if (HAL_ADC_Init(&AdcHandle) != HAL_OK)
{
Error_Handler();
}
/* ### - 2 - Start calibration ############################################ */
if (HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_SINGLE_ENDED) != HAL_OK)
{
Error_Handler();
}
/* ### - 3 - Channel configuration ######################################## */
/* Select Channel 0 to be converted */
sConfigADC.Channel = ADC_CHANNEL_0;
if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfigADC) != HAL_OK)
{
Error_Handler();
}
/* ### - 4 - Start conversion in DMA mode ################################# */
if (HAL_ADC_Start_DMA(&AdcHandle, &aResultDMA, 1) != HAL_OK)
{
Error_Handler();
}
while(1){
}
}
/**
* @brief DMA Transfer complete callback
* @note This function is executed when the transfer complete interrupt
* is generated
* @retval None
*/
static void TransferComplete(DMA_HandleTypeDef *DmaHandle)
{
(DMA1->IFCR)|=DMA_IFCR_CTCIF1; //Clear complete flag;
if(HAL_DMA_Start_IT(&DmaHandle, (uint32_t)&aSRC_Buffer, (uint32_t)&aDST_Buffer, 2) != HAL_OK)
{
/* Transfer Error */
Error_Handler();
}
}
/**
* @brief DMA Transfer error callback
* @note This function is executed when the transfer error interrupt
* is generated during DMA transfer
* @retval None
*/
static void TransferError(DMA_HandleTypeDef *DmaHandle)
{
//Handle error
}

By the way, how can I assign the ADC Data Register as the source address? Am I going in the right direction? Do you happen to know of a certain example that could help me? (my searches have not been very successful) Also, I'll have to enable interruptions for UART as well, to read user input while the ADC keeps reading (a thread would be created), will using the DMA with interruptions interfere in some way? Thank you again.
ortegagonzalez
Associate II
Posted on November 09, 2016 at 14:58

Hi, I just wanted to say that I solved it, in case it's useful to someone.

In the end, I tried a different approach than DMA as I was getting quite tangled up in it. The solution was to use the ADC in single conversion mode with the LowPowerAutoWait setting, that ensures no conversions start before others end, in order to make override unlikely. That way, I simply start the used one HAL_ADC_PollForConversion(...) and a read to ADC-DR two times (one for each channel), and the mixing up problem is gone.

In the future, when the UART simultaneous communication and other parts of the application are complete, I will try again to use DMA as I'm sure it is more efficient, as Clive pointed out. If I manage to do so, I'll post an update.

Thanks again!
antonius
Senior

Can I see the code for pollconversion ?

I have the same case

I'm using channel0 and channel3

within this variable :

ADC_raw = HAL_ADC_GetValue(hadc); 

How can I separate channel0 and channel3 ?

sConfig.Rank   = 1;

sConfig.Channel = ADC_CHANNEL_0;

then

HAL_ADC_ConfigChannel(&hadc, &sConfig)

then

ADC_raw = HAL_ADC_GetValue(hadc); 

??

Any clues ? thanks

FMuel.2
Associate

Dear @ortegagonzalez​ and @antonius​ - could you please provide a solution/code which solved your problem in case you still have it? I am currently with the same problem and I guess your code would be really helpful.

Best regards!