2
votes

I am attempting to use the STM32F411RE DMA with ADC to obtain 2048 samples each for 3 input channels / pins. The A0, A1 and A2 pins are connected to a sensor for which image processing (Fourier Transform) is to be used.

I thus use the DMA / ADC to fill the adc_buffer[6144] with all the 2048 samples from the 3 pins, using an interrupt to find when this buffer is full. When the buffer is full the data should be converted to voltages and put into the correct bin (data goes x, y, z, x, y, z).

Whilst I have all of that working for some reason my DMA circular buffer does not fill up with data. In a previous program this worked fine but I can now no longer make this work: adc_buffer remains 0 throughout program run and as such the ISR HAL_ADC_ConvCpltCallback is never triggered.


#include "main.h"
#include "arm_const_structs.h"
#include "core_cm4.h"
#include "math.h"
#include "arm_math.h"
#include <stdio.h>
#include <stdbool.h>
#include "main.h"

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
UART_HandleTypeDef huart2;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);
static void MX_USART2_UART_Init(void);

float adc_buffer[6144] = {0}; // 2048 samples * 3 channels

/*
 * Interrupt called when the input buffer adc_buffer is full.
 * TO DO: Add   void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)  for double buffering, when conversion is half complete also run Fourier transform
 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC1)  //  if(hadc->Instance == ADC1 && !conversionPaused)
    {
        conversionPaused = 1;   // Temporarily disable the conversion process
        for (int samples= 0; samples < fftLen; samples++)   // Allocates data from the 1 ADC buffer into the 3 separate axis buffers
        {
            Xin[samples] = ((3.3-0)/4096) * adc_buffer[0+3*samples];    // Allocate samples to X array, whilst transforming them into voltage: every 3rd value starting at 0 (0, 3, 6, 9)
            Yin[samples] = ((3.3-0)/4096) * adc_buffer[1+3*samples];    // Allocate samples to X array, whilst transforming them into voltage: every 3rd value starting at 1 (1, 4, 7, 10)
            Zin[samples] = ((3.3-0)/4096) * adc_buffer[2+3*samples];    // Allocate samples to X array, whilst transforming them into voltage: every 3rd value starting at 2 (2, 5, 8, 11)
        }
    }
}

int main(void)
{

    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_ADC1_Init();
    MX_USART2_UART_Init();

    HAL_ADC_Start_IT(&hadc1);
    HAL_ADC_Start_DMA(&hadc1, adc_buffer, bufferLen);   // Maybe DMA and ADC IT are mutually exclusive?

    while (1)
    {
        // Signal processing on Xin, Yin, Zin
    }


/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 16;
  RCC_OscInitStruct.PLL.PLLN = 393;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV8;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV8;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief ADC1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};

  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV8;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = ENABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;           // Maybe this needs to be set to off for use with the circular buffer?
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 3;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.EOCSelection = DISABLE; //ADC_EOC_SINGLE_CONV
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_56CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 2;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = 3;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
}

/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA2_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

}

I suspect that the problem lies in the configuration of the ADC and DMA, on which I can find little literature. A number of examples 1 2 3 4 are available, but this offers little remedy as their code appears the same as mine.



As an edit to the response by Tarick Welling I do not seem to get the full package when generating my code in CubeMX. Particularly I am missing the full MX_DMA_Init() as mine only generates the following:

static void MX_DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA2_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}

However this is despite my settings in CubeMX being configured so that this should be generated, as per the image below:

CubeMX DMA generation

I am thoroughly confused as to why this code is not being generated in CubeMX for me, whilst for others it apparently is.

1

1 Answers

2
votes

MX_DMA_Init doesn't initialize hdma_adc1. So the DMA isn't able to be used.

hadc1.Init.DMAContinuousRequests = DISABLE; disables continuous use of the DMA so it will be a single shot transfer.

uint32_t DMAContinuousRequests; Specify whether the DMA requests are performed in one shot mode (DMA transfer stops when number of conversions is reached) or in continuous mode (DMA transfer unlimited, whatever number of conversions). This parameter can be set to ENABLE or DISABLE. Note: In continuous mode, DMA must be configured in circular mode. Otherwise an overrun will be triggered when DMA buffer maximum pointer is reached.

The ADC isn't initialized to use the DMA so it doesn't even know to use it.

typedef struct
{
  ADC_TypeDef                   *Instance;              /*!< Register base address */

  ADC_InitTypeDef               Init;                   /*!< ADC required parameters */

  DMA_HandleTypeDef             *DMA_Handle;            /*!< Pointer DMA Handler */

  HAL_LockTypeDef               Lock;                   /*!< ADC locking object */

  __IO uint32_t                 State;                  /*!< ADC communication state (bitmap of ADC states) */

  __IO uint32_t                 ErrorCode;              /*!< ADC Error code */
}ADC_HandleTypeDef;

DMA_Handle needs to be initialized for the ADC to use the DMA.

This can be set up inside CubeMX by adding a line to DMA Settings of your ADC configuration on the Configuration tab: image of a DMA stream added to the ADC Further settings can be set by selecting this DMA transfer and configuring its parameters in the bottom part of the dialog.

Please note that ST makes use of a form of delegated initialization. HAL_ADC_Init calls a user defined HAL_ADC_MspInit. This is defined inside stm32f4xx_hal_msp.c and should link the ADC to the DMA. It should contain a line similar to: __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);