2018-05-25 07:51 AM
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 compresoruint8_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 voyGen.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 ##stm32l0Solved! Go to Solution.
2018-05-26 07:58 AM
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
2018-05-25 01:32 PM
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);
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?
2018-05-26 07:58 AM
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
2018-05-26 07:00 PM
I thought that the compilers had auto 32 bit alignment on all declarations...
Clive ?
2018-05-26 08:27 PM
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.