cancel
Showing results for 
Search instead for 
Did you mean: 

Copied project from TrueSTUDIO to STM32CubeIDE, and changed to C++, now my callback functions never run?

NNagy.1
Senior

I had a working ADC/DAC C project generated from CubeMX to TrueSTUDIO, but decided I wanted to make the project C++ instead. From what I remember from trying it before, setting up C++ in TrueSTUDIO can be a hassle.

So I decided to download/install CubeIDE and then used it to open the .ioc file. I set it up to be a C++ project. Then once everything was loaded in the Project Explorer, all I did was re-name the main.c file to main.cpp, and then copy-pasted the code from the TrueSTUDIO main file.

I built the project and ran it, and everything built successfully, but I stopped receiving any audio out. I went into Debugger mode in CubeIDE, and it looks like the DMA callback functions are no longer getting called.

This is my code for main() and the ADC DMA callbacks:

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc3;
DMA_HandleTypeDef hdma_adc3;
 
DAC_HandleTypeDef hdac;
DMA_HandleTypeDef hdma_dac1;
 
TIM_HandleTypeDef htim6;
 
/* USER CODE BEGIN PV */
#define BUFFER_LEN 256
#define DATA_SIZE BUFFER_LEN>>1
uint16_t adcBuffer[BUFFER_LEN] = {0};
uint16_t dacBuffer[BUFFER_LEN] = {0};
static volatile uint16_t * inBufPtr;
static volatile uint16_t * outBufPtr;
volatile uint8_t processReady = 0;
/* 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_ADC3_Init(void);
static void MX_DAC_Init(void);
static void MX_TIM6_Init(void);
/* USER CODE BEGIN PFP */
void processDSP();
/* USER CODE END PFP */
 
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
 
void processDSP() {
	for (int i = 0; i < DATA_SIZE; i++) {
		outBufPtr[i] = inBufPtr[i];
	}
	processReady = 0;
}
/* USER CODE END 0 */
 
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* 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_ADC3_Init();
  MX_DAC_Init();
  MX_TIM6_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim6);
  HAL_ADC_Start_DMA(&hadc3, (uint32_t*)&adcBuffer, BUFFER_LEN);
  HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)&dacBuffer, BUFFER_LEN, DAC_ALIGN_12B_R);
  /* USER CODE END 2 */
 
 
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	if(processReady) {
		processDSP();
	}
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
 
/* ... */
 
/* USER CODE BEGIN 4 */
extern void TIM6_IRQHandler() {
	HAL_TIM_IRQHandler(&htim6);
}
 
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef * htim) {
	HAL_GPIO_TogglePin(Timer_GPIO_GPIO_Port, Timer_GPIO_Pin);
}
 
 
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc) {
	// when ADC is processing first half of buffer, DAC is processing second half and vice versa
	inBufPtr = &adcBuffer[0];
	outBufPtr = &dacBuffer[DATA_SIZE];//BUFFER_LEN>>1];
	processReady = 1;
}
 
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
	// copy second halves of buffers
	inBufPtr = &adcBuffer[DATA_SIZE];//BUFFER_LEN>>1];
	outBufPtr = &dacBuffer[0];
	processReady = 1;
}

Like I said, I had no build errors when building the project in C++, but is there a nuance that I missed by copying my C code into the project?

7 REPLIES 7
TDK
Guru

Your callback functions need to have C linkage since they are in the vector table as such.

extern "C" {
 
void TIM6_IRQHandler() {
  ...
}
 
}

You only showed this one in your code but if you have DMA stream callbacks, those need the same thing as well.

If you feel a post has answered your question, please click "Accept as Solution".

I tried that but it didn't solve the issue. Do the other functions (MX_---_Init() functions or SystemClockConfig()) require C linkage? Do the HAL driver C files and other C files in the project need to be changed to C++ files and wrapped with extern "C"?

Generally, only the ISR functions.

Maybe try to get it working in C first before you rename so you can isolate the issue.

Debug the code, see if it's stuck in the default handler somewhere. It should be, if your handler is not getting called. If it's happily running in the main loop, your interrupts are not firing, as the default handler doesn't clear any flags.

If you feel a post has answered your question, please click "Accept as Solution".

So I tried re-creating the project and made it a C project instead of C++ to try and keep everything the same as the TrueSTUDIO project, and again, the ADC/DAC run when I run from TrueSTUDIO but not from CubeIDE.

I put a breakpoint in Error_Handler() but it never is called, and the debugger seems to signal that it's in (main) and Running. So I guess the issue is the interrupts not firing, like you said? Should I be manually clearing flags somewhere?

So I went over to the stm32f7xx_it.c file:

/**
  * @brief This function handles DMA1 stream5 global interrupt.
  */
void DMA1_Stream5_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream5_IRQn 0 */
 
  /* USER CODE END DMA1_Stream5_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_dac1);
  /* USER CODE BEGIN DMA1_Stream5_IRQn 1 */
 
  /* USER CODE END DMA1_Stream5_IRQn 1 */
}
 
/**
  * @brief This function handles TIM6 global interrupt, DAC1 and DAC2 underrun error interrupts.
  */
void TIM6_DAC_IRQHandler(void)
{
  /* USER CODE BEGIN TIM6_DAC_IRQn 0 */
 
  /* USER CODE END TIM6_DAC_IRQn 0 */
  HAL_DAC_IRQHandler(&hdac);
  HAL_TIM_IRQHandler(&htim6);
  /* USER CODE BEGIN TIM6_DAC_IRQn 1 */
 
  /* USER CODE END TIM6_DAC_IRQn 1 */
}
 
/**
  * @brief This function handles DMA2 stream0 global interrupt.
  */
void DMA2_Stream0_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream0_IRQn 0 */
 
  /* USER CODE END DMA2_Stream0_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_adc3);
  /* USER CODE BEGIN DMA2_Stream0_IRQn 1 */
 
  /* USER CODE END DMA2_Stream0_IRQn 1 */
}

I tried setting breakpoints in the DMA_IRQHandler() calls and then have just been running/pausing the debugger, and it looks like the program keeps hitting the void TIM6_DAC_IRQHandler() function, but never seems to enter the other two.

I'm still not very well-versed with HAL libraries - is this a problem with interrupt priority? I have both DMA stream interrupts set to high priority.

Also, should there be a call to something like HAL_ADC_IRQHandler() inside TIM6_DAC_IRQHandler()? Both the ADC and DAC are set based on TIM6 trigger out events so it's weird to me that I'm only seeing a HAL_DAC_IRQHandler inside the function.

Okay, I kind of solved it: I had TIM6's global interrupt enabled so I could toggle a GPIO pin and verify that the period/prescaler for the timer were set correctly. Once I disabled this global interrupt (so only the DMA stream handlers are in stm32f7xx_it.c), the callback functions started getting called again.

These are all questions which are not specific to STM32CubeIDE, but also apply equally to TrueSTUDIO.

I don't see any issues in the limited code you've presented. It's hard to debug through a keyhole. Consider showing the entire project as I assume it has changed since your original post. Do a diff on code between both projects to see what is different.

Ensure MX_DMA_Init is called before the relevant MX_*_Init functions which use DMA.

If you feel a post has answered your question, please click "Accept as Solution".