Loading...
 
Skip to main content

System Workbench for STM32


ADC with DMA on the STM32F769I Discovery

Hello guys,

I want to share my experience with using HAL Examples (STM32CubeF7 Firmware Package). I have STM32F769I Discovery board
In my program I needed UART and ADC. I implemented UART6 in interrupt mode (Rx, Tx). After many problems with receiving variable length data it was settled. Then I wanted to use analog to digital conversion of one channel on one analog pin. I used ADC1, ADC_CHANNEL_6, ADC_SAMPLETIME_56CYCLES, continous conversion mode. MCU is on max. frequency 216 MHz, ADC has PRESCALER_PCLK_DIV8.

For ADC I was inspired with example project file ADC_TemperatureSensor, it uses DMA2_Stream0, DMA_CHANNEL_0, circular mode. Interruption is done with DMA2_Stream0_IRQn. (I found that this example program does not crash when I remove MPU_Config().)
I copied configuration of the ADC (changed channel and little things) and add it to my program with the UART communication. First I started without MPU_Config(); and CPU_CACHE_Enable(); and after build and run, it ended in the HardFault (HardFault_Handler). It crashed during conversion process HAL_ADC_Start_DMA(&AdcHandle, (uint32_t*)&ConvertedValue, 1); I was not able to find the cause for a couple of days. AN4031 (about DMA) did not help me. After a lot of searching I found AN4667. Part of the setting of the MPU (default) was

MPU_InitStruct.BaseAddress = 0x20010000;
MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;

According to AN4667 (later I found it also in the reference manual RM0410, first it was not so obvious) I found the base address for the MPU is maybe wrong. I changed the address to
MPU_InitStruct.BaseAddress = 0x20020000;

and now the program is running. For the STM32F76xxx/STM32F77xxx DTCM is from 0x2000 0000 to 0x2001 FFFF and SRAM1 is from 0x20020000 to 0x2007 BFFF.

Still I don't understand when it is needed to use CPU_CACHE_Enable() or MPU_Config(). I read MPU should be used when 2 masters can be in the conflict on the bus or in the RAM. Can someone explain to me why I need to use MPU and use it for SRAM1 region for example?

vt23

Hello,

problem was not solved as I thought previously.
I've added only pin PA6 (as analog) configuration (GPIOA init) to the "main" or to the ADC_Config (doesn't matter where) and the HardFault error occured again at the same place - possible step debugging into HAL_DMA_Start_IT(), not out of this. I was not able to get rid of it. To change MPU_InitStruct.BaseAddress is not solution or I don't know what parametres. Different sizes of MPU_InitStruct.Size also do not change the behaviour of the program. HardFault_Handler is called again and again. It was a try and an error.
A few days later I found that before the Hard Fault parameters of the functions were OK, but after the Hard Fault came I was able to find that parametres (arguments) of the functions

HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)

usage: HAL_DMA_Start_IT(hadc->DMA_Handle, (uint32_t)&hadc->Instance->DR, (uint32_t)pData, Length)
DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength)
where pData is destination buffer address or DstAddress is &ConvertedValue,
DataLength or Length is 1UL (1 word)

were changed or mixed (if the debugger was not lying).
So when the Hard Fault occured the debugger showed SrcAddress was &ConvertedValue, DataLength was DMA2 peripheral address (0x40026400) and DstAddress was 1. It was strange to me. But was not able to find the cause.

In the main.c I have ADC configuration (I don't use _msp config file), now.

/*Global variables in the main.c file*/
ADC_HandleTypeDef AdcHandle;
volatile uint32_t ConvertedValue = 0UL;
/*func. prototype*/
static void ADC_Config(void);
...
... int main(void) {...}
...
static void ADC_Config(void)
{
static ADC_ChannelConfTypeDef sConfig;
static DMA_HandleTypeDef hdma_adc;
GPIO_InitTypeDef gpio_adc_struct;

gpio_adc_struct.Pin = GPIO_PIN_6;
gpio_adc_struct.Mode = GPIO_MODE_ANALOG;

HAL_GPIO_Init(GPIOA, &gpio_adc_struct);

/* Configure the ADC peripheral */
AdcHandle.Instance = ADC1;

AdcHandle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV8;
AdcHandle.Init.Resolution = ADC_RESOLUTION_12B;
AdcHandle.Init.ScanConvMode = DISABLE; /* Sequencer disabled (ADC conversion on only 1 channel: channel set on rank 1) */
AdcHandle.Init.ContinuousConvMode = ENABLE; /* Continuous mode enabled to have continuous conversion */
AdcHandle.Init.DiscontinuousConvMode = DISABLE; /* Parameter discarded because sequencer is disabled */
AdcHandle.Init.NbrOfDiscConversion = 0;
AdcHandle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; /* Conversion start NOT trigged by an external event */
AdcHandle.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T1_CC1;
AdcHandle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
AdcHandle.Init.NbrOfConversion = 1;
AdcHandle.Init.DMAContinuousRequests = ENABLE;
AdcHandle.Init.EOCSelection = DISABLE;

/*##-1- Enable peripherals and GPIO Clocks #################################*/
/* ADC1 Periph clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();

/* Enable DMA2 clock */
__HAL_RCC_DMA2_CLK_ENABLE();

/*##-2- Configure the DMA streams ##########################################*/
/* Set the parameters to be configured */
hdma_adc.Instance = DMA2_Stream0; // DMA2_Stream4

hdma_adc.Init.Channel = DMA_CHANNEL_0;
hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc.Init.MemInc = DMA_MINC_DISABLE;
hdma_adc.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc.Init.Mode = DMA_CIRCULAR;
hdma_adc.Init.Priority = DMA_PRIORITY_HIGH;
hdma_adc.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
hdma_adc.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
hdma_adc.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_adc.Init.PeriphBurst = DMA_PBURST_SINGLE;

HAL_DMA_Init(&hdma_adc);

/* Associate the initialized DMA handle to the ADC handle */
__HAL_LINKDMA(&AdcHandle, DMA_Handle, hdma_adc); //(&AdcHandle)->DMA_Handle = &(hdma_adc)

/*##-3- Configure the NVIC for DMA #########################################*/
/* NVIC configuration for DMA transfer complete interrupt */
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

// HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 0, 0);
// HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);

if (HAL_ADC_Init(&AdcHandle) != HAL_OK)
{
/* ADC initialization Error */
Error_Handler();
}

/* Configure ADC Temperature Sensor Channel */
sConfig.Channel = ADC_CHANNEL_6; //CN14_A0=ADC1_IN6=PA6 //ADC_CHANNEL_TEMPSENSOR = ADC_CHANNEL_18;
//CN14_A1=ADC_IN4=PA4
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_112CYCLES; //ADC_SAMPLETIME_56CYCLES;
sConfig.Offset = 0;

if (HAL_ADC_ConfigChannel(&AdcHandle, &sConfig) != HAL_OK)
{
/* Channel Configuration Error */
Error_Handler();
}
}

Result:
If ADC_ChannelConfTypeDef sConfig declaration is used, Hard Fault occurs.
If DMA_HandleTypeDef hdma_adc decalration is used, ConvertedValue is still 0 (and program runs without any other faults.)

If I use these declarations with the static keyword, the program runs and ADC converts without faults.
Then I found that DMA_Handle was placed in the DTCM RAM region (0x2000XXXX) in the working code. In the defective code the DMA_Handle was placed at the end of the SRAM1 (0x2007fef8). However I don't know why this should be the problem to have the variable at the end of the SRAM.

I will be happy if somebody has similar experience or can give me a comment why I should use static keyword in the config function (generally) or if it is needed to define static functions in the main.c. (However void SystemClock_Config(void) is defined in the main.c without static keyword. In all examples.)

vt23