cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L071 ADC + DMA + Multichannels Problems

Palacios.Bruno
Associate III
Posted on May 25, 2018 at 16:51

Hi all,

I am working with a custum made PCB which has a STM32L071 micro and I am facing some troubles to read multiple adc inputs via DMA (because HAL IT dondoesn't work because ADC interrupts faster than ISR can excecute).

The think is the output from the DMA is beyond 4096 values. I'd tried a lot of combinations in config without luck. I want to use HSE with 8MHz crystal PLL to 32MHz.

General struct

&sharppragma pack(push, 1)

typedef struct {

uint16_t PresionAire;

uint16_t SensorHumo;

uint16_t CAL_IS1;

uint16_t CAL_IS2;

uint16_t CAL_HUMO;

uint16_t CAL_AIRE;

uint16_t I_Extractor;

uint16_t I_Compresor;

} Sensores;

typedef struct {

uint16_t Systick;

uint16_t EstadoEntradas;

uint8_t ExtractorEncendido;

uint8_t CompresorEncendido;

uint16_t CompresorHabilitado; // Variable de tiempo para apagar el compresor

uint8_t Error;

uint16_t ConversorADC[8];

Sensores Analog;

} General;

&sharppragma pack(pop)

__IO General Gen;

int main(void){

memset((General *) &Gen, 0x00, sizeof(Gen));

HAL_Init();

Setup();

while (1){

LecturaADCs((General *) &Gen);

}

}

void Setup(void){

ConfiguroOscilador();

HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

SysTick_Config(SystemCoreClock/1000);

ConfiguroIOs();

ConfiguroADC();

}

void ConfiguroOscilador(void){

RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

RCC_OscInitTypeDef RCC_OscInitStruct = {0};

/* -2- Enable HSE Oscillator, select it as PLL source and finally activate the PLL */

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;

RCC_OscInitStruct.HSEState = RCC_HSE_ON;

RCC_OscInitStruct.LSEState = RCC_LSE_OFF;

RCC_OscInitStruct.HSIState = RCC_HSI_OFF;

RCC_OscInitStruct.LSIState = RCC_LSI_OFF;

RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;

RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;

RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL8;

RCC_OscInitStruct.PLL.PLLDIV = RCC_PLL_DIV2;

if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK){

while(1);

}

/* -3- Select the PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */

RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);

RCC_ClkInitStruct.SYSCLKSource = RCC_CFGR_SW_PLL;

RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK){

/* Initialization Error */

while(1);

}

__HAL_RCC_GPIOA_CLK_ENABLE();

__HAL_RCC_GPIOB_CLK_ENABLE();

__HAL_RCC_GPIOC_CLK_ENABLE();

__HAL_RCC_ADC1_CLK_ENABLE();

}

void ConfiguroIOs(void){

GPIO_InitTypeDef GPIO_InitStruct;

__HAL_RCC_ADC1_CLK_ENABLE();

//********* GPIOA

GPIO_InitStruct.Pin = PIN_SENSOR_PRESION | PIN_SENSOR_HUMO | PIN_CAL_IS1 | PIN_CAL_IS2 | PIN_CAL_HUMO | PIN_CAL_AIRE;

GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

GPIO_InitStruct.Pin = PIN_I_EXTRACTOR | PIN_I_COMPRESOR;

GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc){

static DMA_HandleTypeDef DmaHandle;

__HAL_RCC_DMA1_CLK_ENABLE();

DmaHandle.Instance = DMA1_Channel1;

DmaHandle.Init.Direction = DMA_PERIPH_TO_MEMORY;

DmaHandle.Init.PeriphInc = DMA_PINC_DISABLE;

DmaHandle.Init.MemInc = DMA_MINC_ENABLE;

DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

DmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

DmaHandle.Init.Mode = DMA_NORMAL;

DmaHandle.Init.Priority = DMA_PRIORITY_VERY_HIGH;

DmaHandle.Init.Request = DMA_REQUEST_0;

HAL_DMA_Init(&DmaHandle);

/* Associate the DMA handle */

__HAL_LINKDMA(&AdcHandle, DMA_Handle, DmaHandle);

/* NVIC configuration for DMA Input data interrupt */

HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);

HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

}

void ConfiguroADC(void){

AdcHandle.Instance = ADC1;

if (HAL_ADC_DeInit(&AdcHandle) != HAL_OK){

HAL_Delay(100);

}

AdcHandle.Init.OversamplingMode = DISABLE;

AdcHandle.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;

AdcHandle.Init.LowPowerAutoPowerOff = DISABLE;

AdcHandle.Init.LowPowerFrequencyMode = DISABLE;

AdcHandle.Init.LowPowerAutoWait = DISABLE;

AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;

AdcHandle.Init.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;

AdcHandle.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;

AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;

AdcHandle.Init.ContinuousConvMode = ENABLE;

AdcHandle.Init.DiscontinuousConvMode = DISABLE;

AdcHandle.Init.ExternalTrigConv = ADC_SOFTWARE_START;

AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;

AdcHandle.Init.EOCSelection = ADC_EOC_SINGLE_SEQ_CONV;

AdcHandle.Init.DMAContinuousRequests = ENABLE;

if (HAL_ADC_Init(&AdcHandle) != HAL_OK){

HAL_Delay(100);

}

if (HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_SINGLE_ENDED) != HAL_OK){

HAL_Delay(100);

}

sConfig.Channel = (ADC_CHANNEL_2 | ADC_CHANNEL_3 | ADC_CHANNEL_4 | ADC_CHANNEL_5 | ADC_CHANNEL_6 | ADC_CHANNEL_7 | ADC_CHANNEL_8 | ADC_CHANNEL_9);

if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK){

HAL_Delay(100);

}

if(HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *) Gen.ConversorADC, 8) != HAL_OK){

HAL_Delay(100);

}

}

void LecturaADCs(General *Genb){

if (!(Gen.ConversorADC[0] & 0x8000)) // Si el bit mas significativo esta en 1 hay dato

return; // Sino me voy

Gen.ConversorADC[0] &= 0x7FFF; // Borro flag de conversion disponible

memcpy(&Gen.Analog, Gen.ConversorADC, sizeof(Sensores));

memset(Gen.ConversorADC, 0x00, sizeof(Gen.ConversorADC));

if(HAL_ADC_Start_DMA(&AdcHandle, (uint32_t *) Gen.ConversorADC, 8) != HAL_OK){

HAL_Delay(100);

NVIC_SystemReset();

}

}

void DMA1_Channel1_IRQHandler(void){

HAL_DMA_IRQHandler(AdcHandle.DMA_Handle);

}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle){

HAL_ADC_Stop_DMA(AdcHandle);

Gen.ConversorADC[0] |= 0x8000;

}

The values given by the DMA are out of ranges and oscillates on each conversion.

Hope someone can help me.

Best regards,

Bruno

#stm-mcu #stm32l0-adc-dma #stm32l071 #stm-32lo ##stm32l0
1 ACCEPTED SOLUTION

Accepted Solutions
Palacios.Bruno
Associate III
Posted on May 26, 2018 at 16:58

Solved! It was an alineation problem. I've use #pragma pack(push, 1) pop for align Sensores struct but I also included General struct so ADC buffer got missaligned.

Hope it helps someone.

Best regards,

Bruno

View solution in original post

4 REPLIES 4
T J
Lead
Posted on May 25, 2018 at 22:32

I used a timer to trigger the DMA cycle.

 start_Timer15();
 init_mS_Wait_Timer(); //testuSwait();
 ADC_RowCounter = 0;
 Ave_ADC_ChannelCounter = 0;
 ADC_ChannelsCounter = ADC_ChannelCount; // 15 ADC channels
 HAL_ADC_Start_DMA(&hadc, (uint32_t *)ADC_DMABuffer, ADC_ChannelCount); 
 

void start_Timer15(void) {
 HAL_TIMEx_OCN_Start(&htim15, TIM_CHANNEL_1);
 HAL_TIM_Base_Start(&htim15);
}

typedef enum {
 Rs8_Ad1,
 Rs5_Ad5,
 Rs1_Ad6,
 Rs2_Ad7,
 Rs3_Ad8,
 Rs4_Ad9,
 CurrentSense_Ad13,
 Rs6_Ad14,
 Rs7_Ad15,
 VTemp_Ad16,
 VRef_Ad17,
 VBat_Ad18,
 ADC_ChannelCount,
} ADCresultsAveColumn_t;
uint16_t ADC_DMABuffer[ADC_ChannelCount]; // 18channels 12bit words 
uint16_t ADC_Channel_AVE[ADC_ChannelCount];
uint16_t HAL_ADC_Channel_Buffer[ADC_RowCount][ADC_ChannelCount]; // 16 to sum to calc averages for each channel

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
 ADC_ChannelsCounter = ADC_ChannelCount;
 HAL_ADC_CompleteCycle = true; // signal foreground to process 
 // copy 15 results away now
 for(int i = 0 ; i < ADC_ChannelCount ; i++) {
 //here add 2nd dimension to the array
 HAL_ADC_Channel_Buffer[ADC_RowCounter][i] = ADC_DMABuffer[i]; // AVEColumnSum += HAL_ADC_Channel_Buffer [Ave_ADC_RowCounter][Ave_ADC_ChannelCounter];
 }
 ADC_RowCounter++; // for next time through the loop
 if(ADC_RowCounter >= ADC_RowCount) ADC_RowCounter = 0;
}
void Ave_next_ADC_Column(void) {
 uint32_t AVEColumnSum = 0;
 for (int Ave_ADC_RowCounter = 0; Ave_ADC_RowCounter < ADC_RowCount; Ave_ADC_RowCounter++)
 AVEColumnSum += HAL_ADC_Channel_Buffer[Ave_ADC_RowCounter][Ave_ADC_ChannelCounter];

 ADC_Channel_AVE[Ave_ADC_ChannelCounter] = AVEColumnSum; // we take an extra bit of resolution here..... we should divide by 
 Ave_ADC_ChannelCounter++; // for next time, its the next column
 if(Ave_ADC_ChannelCounter >= ADC_ChannelCount) { 
 Ave_ADC_ChannelCounter = 0; // for next time, its the next column
 AllADC_AveragesUpdatedFlag = true; // All averages updated
 }
}

in the foreground process:
 if (HAL_ADC_CompleteCycle) {
 HAL_ADC_CompleteCycle = false;
 ADCcompletedcounter++;
 Ave_next_ADC_Column();
 // putc1('#'); //recalibration complete, i think
 
 if(ReCalADCrequested) {
 ReCalADCrequested = false;
 
 /*
 //Prior to calibrate, we must stop the DMA conversion cycle which is endless.
 //in 'end of sequence' check flag ReCalADCrequested <- we are here now
 //stop the ADC/DMA cycle and wait for it to stop. <- we will do this, and flag the foreground to check when the DMA stops.
 //Now in the foreground
 //Now Calibrate ADC as described below;
 //Renable ADC/DMA Cycle
 */
 setCursor1(6, 90); 
 sprintf(string, ' ');
 puts1(string);
 setCursor1(5, 90);
 sprintf(string, 'Stopping DMA ');
 puts1(string); //recalibration in progress
 HAL_ADC_Stop_DMA(&hadc); // stop is instant
 ADC_DMA_Wait = true; // waiting to recalibrate the ADC due to variant PCB temperature affecting ADC accuracy
 ADChasBeenRecalibrated = false;
 
 // stop DMA cycle function
 // stop ADC
 // tell foreground to wait for DMA to start the ADC Calibration Sequence
 //or restart here after cal... HAL_ADC_Start_DMA (&hadc,(uint32_t *)ADC_DMABuffer,ADC_Channels); from main, adc-start
 }
 }
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?
Calibrate
void process_ADCcalibration(void) {
 //sprintf(string,'ADC Recal is Progressing

');
 //puts (string);

// check DMA and ADC have stopped;
/*
Calibration software procedure
1. Ensure that ADEN=0
2. Set ADCAL=1
3. Wait until ADCAL=0
4. The calibration factor can be read from bits 6:0 of ADC_DR.
ADC Calibration code example
// (1) Ensure that ADEN = 0 
// (2) Clear ADEN 
// (3) Launch the calibration by setting ADCAL
// (4) Wait until ADCAL=0
*/
if((ADC1->CR & ADC_CR_ADEN) != 0) // (1) 
 {
 ADC1->CR &= (uint32_t)(~ADC_CR_ADEN); // (2)
 }
 ADC1->CR |= ADC_CR_ADCAL; // (3)
 while((ADC1->CR & ADC_CR_ADCAL) != 0) // (4)
 {
 checkBackgroundServices();
 // For robust implementation, add here time-out management 
 }
 ADC_CalibrationFactor = (ADC1->DR); //& 0x7F);
 ADChasBeenRecalibrated = true;
 char string[32];
 setCursor1(5, 90); 
 sprintf(string, ' ');
 puts1(string);
 setCursor1(6, 90);
 sprintf(string, 'ADC Recal is Completed

');
 puts1(string);

 HAL_ADC_Start_DMA(&hadc, (uint32_t *)ADC_DMABuffer, ADC_ChannelCount); //from main, adc-start
 //sprintf(string,'ADC DMA cycle has restarted

');
 //puts (string);
 //sprintf(string,' ADC Recalibration Completed, Calfactor is %02X 

',ADC_CalibrationFactor );// 
 //puts (string);

}
void readProcessorTemperature(void) {
 //Temperature computation code example
 // Temperature sensor calibration value address 
#define TEMP110_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7C2)) // calibrated at 3.3V +-10mV @110C +/- 5C 
#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7B8)) // calibrated at 3.3V +-10mV @ 30C +/- 5C 
#define VREFINT_CAL ((uint16_t*) ((uint32_t) 0x1FFFF7BA)) // calibrated at 3.3V +-10mV @ 30C +/- 5C 
#define VDD_CALIB ((uint16_t) (3300))
#define VDD_APPLI ((uint16_t) (3295))
 int32_t AveTemperatureADC = ADC_Channel_AVE[VTemp_Ad16]; // 16x over sampled, needs /16
 //int32_t processorTemperature;
 //processorTemperature = (((int32_t) ADC_CalibrationFactor * VDD_APPLI / VDD_CALIB)- (int32_t) *TEMP30_CAL_ADDR );

// temp in C = (110-30)/(TS_CAL2-TXCAL1)*(TS_DATA-TS_CAL1) + 0
//
 double processorTemp; // the temperature in degree Celsius
 
 //processorTemp = (double) AveTemperatureADC * VDD_APPLI / VDD_CALIB / 16;
 processorTemp = (double) AveTemperatureADC / 16 - *TEMP30_CAL_ADDR;
 processorTemp *= 110 - 30;
 processorTemp /= ((double)*TEMP110_CAL_ADDR) - *TEMP30_CAL_ADDR;
 processorTemp += 30;
 processorTemperature = (int32_t)(processorTemp * 100);
 //sprintf(string,'AveTemperatureADC %04X, ADC_CalibrationFactor %04X, *TEMP30_CAL_ADDR %04X, processorTemperature %.2f, processorTemp %.3f 

',AveTemperatureADC,ADC_CalibrationFactor,(int32_t) *TEMP30_CAL_ADDR,((float)processorTemperature/100),processorTemp);
 //puts (string);
 Vdda = (double)16 * 3.3 * *VREFINT_CAL;
 Vdda /= ADC_Channel_AVE[VRef_Ad17];
 
 
 
 /*
 VRef_Ch17
 VDDA = 3.3 V x VREFINT_CAL / VREFINT_DATA
 VCHANNELx = VDDA / FULL_SCALE * ADC_DATA
 */
 
 char string[64];
 setCursor1(0, 90);
 sprintf(string, 'processorTemperature %.1f ', ((float)processorTemperature) / 100);
 puts1(string);
 sprintf(string, 'Vdda as %.3f 

', Vdda);
 puts1(string);
 
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Palacios.Bruno
Associate III
Posted on May 26, 2018 at 16:58

Solved! It was an alineation problem. I've use #pragma pack(push, 1) pop for align Sensores struct but I also included General struct so ADC buffer got missaligned.

Hope it helps someone.

Best regards,

Bruno

Posted on May 27, 2018 at 02:00

I thought that the compilers had auto 32 bit alignment on all declarations...

Clive ?

Posted on May 27, 2018 at 03:27

Well the default would be to pack byte and 16-bit words together with the best natural alignment possible. When it gets to a 32-bit word it will align that.

In this case the tight packing  has been selected, and there will be consequences.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..