2020-08-28 12:50 PM
I'm following this tutorial on DMA (https://www.youtube.com/watch?v=EsZLgqhqfO0) which at the end shows how to set up the DMA peripheral to continuously perform ADC conversions and store the results in a circular buffer.
I am using an STM32F051C6 (different to the one in the tutorial) on a custom development board from my University. I have set up the ADC and DMA using the device configuration tool. I am using PA6 which has a pot connected to it, I have tested this in single shot mode to confirm it works.
For some reason the callbacks are not being called and it doesn't seem like the DMA is actually operating (Even though when I call 'HAL_ADC_Start_DMA' it returns 'HAL_OK').
I am new to the HAL library and I'm sure I'm probably missing something small but critical. Any pointers would be greatly appreciated. I apologize if this has been posted in the wrong place.
----------------------------------------------------- EDIT-----------------------------------------------------
I dug into the registers to get some screenshots. If I toggle the enable bit in the DMA_CCR1 register, the DMA seems to transfer an arbitrary number ~(10-100) of ADC reads but then freezes up again... It turns out that if I keep toggling the enable bit, it eventually gets to the halfway mark and the interrupt triggers. So the issue is not the interrupts triggering, but that the DMA isn't 'running'. (This behaviour occurs if I toggle other DMA registers, but does not occur if I toggle values in other registers)
This leads me to believe its a clock issue, but I think the clocks are all correctly setup.
I can use the debugger to read the ADC reads in the buffer array and the ADC does read valid results when it 'works' (that is when I'm toggling a bit in a DMA register as I mentioned above)
Here are the screenshots for DMA, ADC and RCC registers:
----------------------------------------------------- /EDIT-----------------------------------------------------
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdint.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define ADC_BUF_LEN 1024
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc;
DMA_HandleTypeDef hdma_adc;
/* USER CODE BEGIN PV */
uint16_t adc_buf[ADC_BUF_LEN];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
//uint16_t raw;
//int voltage;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC_Init();
/* USER CODE BEGIN 2 */
HAL_StatusTypeDef status;
status = HAL_ADC_Start_DMA(&hadc, (uint32_t*)adc_buf, ADC_BUF_LEN);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.HSI14CalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief ADC Initialization Function
* @param None
* @retval None
*/
static void MX_ADC_Init(void)
{
/* USER CODE BEGIN ADC_Init 0 */
/* USER CODE END ADC_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC_Init 1 */
/* USER CODE END ADC_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.ContinuousConvMode = ENABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = ENABLE;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_6;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC_Init 2 */
/* USER CODE END ADC_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
}
/* USER CODE BEGIN 4 */
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc){
int i = adc_buf[0];
int breakpointhere; //This is here so I can place a break point should the callback execute
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){
int i = adc_buf[1023];
int breakpointhere;
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
Solved! Go to Solution.
2020-09-01 11:07 AM
So I think I've figured it out.
The issue was my clock configuration. I was using 8MHz to clock the DMA peripheral, while using the asynchronous internal 14MHz clock source for the ADC. Hence why the ADC kept getting over-run and why the DMA ultimately kept freezing up.
Thanks everyone for the help.
2020-08-28 01:52 PM
Perhaps there is some plumbing in the MSP file, don't see it associating the DMA with ADC here
Perhaps review a HAL example, or two,
STM32Cube_FW_F0_V1.11.0\Projects\STM32F0308-Discovery\Examples\ADC\ADC_DMA_Transfer\Src\stm32f0xx_hal_msp.c
Check you have interrupt code in stm32f0xx_it.c
/**
* @brief This function handles DMA1_Channel1_IRQHandler interrupt request.
* @param None
* @retval None
*/
void DMA1_Channel1_IRQHandler(void)
{
HAL_DMA_IRQHandler(AdcHandle.DMA_Handle);
}
2020-08-28 03:14 PM
Read out and check/post the ADC and DMA registers content.
JW
2020-08-30 11:16 AM
Hi JW,
I have edited my post to include those. Interestingly it appears the issue is not that the callbacks don't fire, but rather that the DMA doesn't seem to be running (DMA_CNDTR1 register remains constant).
2020-08-30 11:20 AM
Hi there,
Thanks for the pointers!
I had a dig, but before I got too deep, experimenting showed me that the issue is actually occurring at a higher level. The DMA doesn't appear to be running, indicated by DMA_CNDTR1 register remaining constant. (My posted has been edited to explain how I determined this)
2020-08-31 03:33 AM
So I have narrowed the problem further.
It seems that the ADC is being over-run.If I change the ADC clock source to 'synchronous clock mode' the code works fine, but if I use asynchronous there ADC is overrun and the DMA freezes up.
I'm not sure why this is happening? At least the code does work now... but I am curious as to why the asynchronous mode causes problems.
2020-08-31 02:05 PM
I'm lazy to decipher the registers, and don't know your RCC/clocks settings anyway. So, what's your ADC sample rate in both cases, and compare that to system/APB frequencies.
Also, is there any other DMA or any code accessing peripherals on the same bus as ADC is?
JW
2020-09-01 11:07 AM
So I think I've figured it out.
The issue was my clock configuration. I was using 8MHz to clock the DMA peripheral, while using the asynchronous internal 14MHz clock source for the ADC. Hence why the ADC kept getting over-run and why the DMA ultimately kept freezing up.
Thanks everyone for the help.
2020-09-01 11:09 AM
Thanks, turns out I was clocking the DMA at 8MHz while the ADC was operating on 14MHz.