cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L4 - ADC Module using the LL

Flavio Barbarossa
Associate
Posted on June 27, 2018 at 10:18

Hi guys,

I'm tryingto use the ADC module of STM32L4 using the Low Level Library.

We created a simple project (I have a nucleo board). I used the STM32Cube Mx to generate the Setup Code (the project file is attached) and I saw a few exampletouse the ADC Module by LL.

I've setup the ADC Module in very simple mode: trigger by software and interrupt disabled.

After the STM32Cube code, I enable the ADC Module with the following code:

// ENABLE TIMER2 (SERVICE TIMER)
LL_TIM_EnableIT_UPDATE(TIM2);
LL_TIM_EnableCounter(TIM2);
// Start Internal Regulator
LL_ADC_DisableDeepPowerDown(ADC1);
LL_ADC_EnableInternalRegulator(ADC1);
// Wait the Internal Regulator Startup
Wait_Time = 50;
while( Wait_Time );
// ADC CAlibration
LL_ADC_StartCalibration(ADC1, LL_ADC_DIFFERENTIAL_ENDED);
// Wait ADC Calibration
while ( LL_ADC_IsCalibrationOnGoing(ADC1) );
Wait_Time = 50;
while( Wait_Time );
// Enable ADC
LL_ADC_Enable(ADC1);
// Wait to Start ADC
while ( LL_ADC_IsActiveFlag_ADRDY(ADC1) == 0 );
if( LL_ADC_IsActiveFlag_EOC(ADC1) ) LL_ADC_ClearFlag_EOC(ADC1);
// Enable Timer6 and Interrupt
LL_TIM_EnableIT_UPDATE(TIM6);
LL_TIM_EnableCounter(TIM6);�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Wait_Time variable is decremented by TIMER2 interrupt (1ms), so the 'sleep time' is around 50ms. SysClock is 4Mhz and ADC Clock is 32 Mhz.

In the while loop, the start conversion is managed by the TIMER6 interrupt (500ms), using the following code:

while (1)
{
 if( flag_timer6 )
 {
 LL_ADC_REG_StartConversion(ADC1);
 while(LL_ADC_IsActiveFlag_EOC(ADC1) == 0);
 if( LL_ADC_IsActiveFlag_EOC(ADC1) ) LL_ADC_ClearFlag_EOC(ADC1);
 Buffer_ADC = LL_ADC_REG_ReadConversionData12(ADC1);
 LL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
 flag_timer6 = 0;
 }
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

The following code works only 'one time', after the first conversion the ADC Module go in loop mode (I noted thatLL_ADC_REG_IsConversionOngoing(ADC1) return always true after the first conversion).

If I add theLL_ADC_REG_StopConversion(ADC1) in the code, the ADC Module 'seems' to work correctly

while (1)
{
 if( flag_timer6 )
 {
 LL_ADC_REG_StartConversion(ADC1);
 while(LL_ADC_IsActiveFlag_EOC(ADC1) == 0);
 if( LL_ADC_IsActiveFlag_EOC(ADC1) ) LL_ADC_ClearFlag_EOC(ADC1);
 Buffer_ADC = LL_ADC_REG_ReadConversionData12(ADC1);
 LL_ADC_REG_StopConversion(ADC1);
 LL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
 flag_timer6 = 0;
 }
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

It is correct procedure to start conversion of ADC ?

In the examples that I've read the StopConversion should not berequired.

Thanks for your support

#stm32l4-adc #ll
2 REPLIES 2
Henrik Bjorkman
Associate
Posted on June 29, 2018 at 19:07

I am trying to do something similar. As test input I use the internal temp sensor. I only check the ADRDY.

First initialize ADC1:

RCC->

AHB2ENR

|=

RCC_AHB2ENR_ADCEN_Msk

|

RCC_AHB2ENR_GPIOCEN_Msk;

sysSleepMs

(

10

);

ADC1->

CR

=

0x00000000

;

ADC1->

CFGR

=

0x80000000

;

LL_ADC_DisableDeepPowerDown

(ADC1);

sysSleepMs

(

100

);

LL_ADC_EnableInternalRegulator

(ADC1);

sysSleepMs

(

100

);

ADC1_COMMON->

CCR

|=

(

3

<<

ADC_CCR_CKMODE_Pos);

sysSleepMs

(

100

);

LL_ADC_StartCalibration

(ADC1, LL_ADC_SINGLE_ENDED);

while

(

LL_ADC_IsCalibrationOnGoing

(ADC1)) {}

sysSleepMs

(

100

);

ADC1->

CFGR

|=

ADC_CFGR_DISCEN_Msk

|

ADC_CFGR_ALIGN_Msk;

ADC1_COMMON->

CCR

|=

(

4

<<

ADC_CCR_PRESC_Pos);

ADC1->

SQR1

=

(

17

<<

ADC_SQR1_SQ1_Pos);

ADC1_COMMON->

CCR

|=

ADC_CCR_TSEN_Msk;

sysSleepMs

(

100

);

ADC1->

ISR

|=

ADC_ISR_ADRDY_Msk;

LL_ADC_Enable

(ADC1);

while

(ADC1->

ISR

&

ADC_ISR_ADRDY_Msk

==

0

) {}

ADC1->

ISR

|=

ADC_ISR_ADRDY_Msk;

sysSleepMs

(

100

);

Then later start a conversion:

LL_ADC_REG_StartConversion

(ADC1);

Then inside a loop (once per second):

if

(

ADC1->

ISR

&

ADC_ISR_ADRDY_Msk

)

{

usartPrintUInt32

(DEV_USART2,

ADC1->

DR

);

LL_ADC_REG_StartConversion

(ADC1);

}

I do get some numbers, and those changes if I blow hot air on the Nucleo with a hair dryer. Still a bit unsure though. I have not used the STM32 before.

/H

Flavio Barbarossa
Associate
Posted on July 04, 2018 at 11:26

Hi Henrik;

Thanks for your precious reply.

I tried with your manual setup, and the system works correctly. So, I used your code tocheck the ADC register values difference between your 'manual' setup and the code generated by the STMCube.

I noted a bug on STMCube code, although it's not activated any DMA channel the code generated by STMCube is the following:

 ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE;
 ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_DISABLE;
 ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
 ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE;
 ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_LIMITED;
 ADC_REG_InitStruct.Overrun = LL_ADC_REG_OVR_DATA_PRESERVED;
 LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

I changed theLL_ADC_REG_DMA_TRANSFER_LIMITED toLL_ADC_REG_DMA_TRANSFER_NONEand theADC module works fine without I have to force the stop conversion (with the code that I posted originally).

Which explains why the the ADC module was gotten into aninfinite loop, because it was waiting theDMA transfer.

Thanks for you support.