cancel
Showing results for 
Search instead for 
Did you mean: 

How can I call STM HAL UART code in multiple files?

magene
Senior II

I have a file in my VisualGDB project that has all the HAL code necessary to talk to a GPS receiver through a UART. I can #include this GPS file in one of the other files in my project and the code in that file can talk to the GPS just fine. If I #include the GPS file in a second file in my project, all the IRQ Handlers throw multiple definition errors like this:

Error  multiple definition of `USART1_IRQHandler'

Shouldn't the #pragma once directive prevent this? Seems like there must be a way to make the UART code visible in multiple files in my project that I haven't figured out yet? I've been looking for an example project or library that uses the STM HAL stuff in a project that's broken up into multiple files but haven't been successful yet. At some point, I realize I'm going to have to dump the STM HAL code and do it myself but I'm hoping to delay that as along as possible.

The first part of the GPS file is the variables and HAL specific IRQ handlers. The second part is a GPS class the encapsulates the GPS functionality that I'd like to use elsewhere in my project. I don't think this problem is related to the GPS class but if I have to dump the GPS class and do it a non-C++ way, that will be fine. But I haven't figured out how to do that either. Here's the GPS code:

#pragma once
#include <stm32h7xx_hal.h>
#include <stm32h7xx_hal_rcc.h>
 
#include <memory.h>
#include <array>
 
using namespace std;
 
static UART_HandleTypeDef hUART1 = UART_HandleTypeDef();
static DMA_HandleTypeDef hdma_tx;
static DMA_HandleTypeDef hdma_rx;
 
static uint8_t msgBuffer[128];
static uint8_t msgBufferIndex = 0;
static bool msgReady = false;
static uint8_t rxBuffer[16];
 
extern "C" void USART1_IRQHandler()
{
	HAL_UART_IRQHandler(&hUART1);
}
 
extern "C" void DMA1_Stream1_IRQHandler()
{
	HAL_DMA_IRQHandler(&hdma_tx);
}
 
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_RESET);
}
 
extern "C" void DMA1_Stream0_IRQHandler()
{
	HAL_DMA_IRQHandler(&hdma_rx);
}
 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	HAL_UART_Receive_IT(&hUART1, rxBuffer, 1);
	msgBuffer[msgBufferIndex] = rxBuffer[0];
	msgBufferIndex++;
	
	if (rxBuffer[0] == 10)
		msgReady = true;
}
 
class GPSHack
{
public:
	GPSHack()
	{}
	
	void initGPS_UART()
	{
		__USART1_CLK_ENABLE();
		__GPIOB_CLK_ENABLE();
	
		GPIO_InitTypeDef uart1GPIO_InitStructure;
 
		uart1GPIO_InitStructure.Pin = GPIO_PIN_6;
		uart1GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
		uart1GPIO_InitStructure.Alternate = GPIO_AF7_USART1;
		uart1GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
		uart1GPIO_InitStructure.Pull = GPIO_NOPULL;
		HAL_GPIO_Init(GPIOB, &uart1GPIO_InitStructure);
	
		uart1GPIO_InitStructure.Pin = GPIO_PIN_7;
		uart1GPIO_InitStructure.Mode = GPIO_MODE_AF_OD;
		HAL_GPIO_Init(GPIOB, &uart1GPIO_InitStructure);
 
		hUART1.Instance = USART1;
		hUART1.Init.BaudRate = 9600;
		hUART1.Init.WordLength = UART_WORDLENGTH_8B;
		hUART1.Init.StopBits = UART_STOPBITS_1;
		hUART1.Init.Parity = UART_PARITY_NONE;
		hUART1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
		hUART1.Init.Mode = UART_MODE_TX_RX;
 
		if (HAL_UART_Init(&hUART1) != HAL_OK)
			asm("bkpt 255");
	
		NVIC_EnableIRQ(USART1_IRQn);
		
		//HAL_RCC_DMA1_CLK_ENABLE		//For some reason vgdb can't find this macro
		do 
		{
			__IO uint32_t tmpreg; 
			SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_DMA1EN); 
			/* Delay after an RCC peripheral clock enabling */ 
			tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_DMA1EN); 
			UNUSED(tmpreg); 
		} while (0) ;
		
		hdma_tx.Instance                 = DMA1_Stream1;
		hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;
		hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;
		hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;
		hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
		hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
		hdma_tx.Init.Mode                = DMA_NORMAL;
		hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;
		hdma_tx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
		hdma_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
		hdma_tx.Init.MemBurst            = DMA_MBURST_INC4;
		hdma_tx.Init.PeriphBurst         = DMA_PBURST_INC4;
		hdma_tx.Init.Request             = DMA_REQUEST_USART1_TX;
 
		HAL_DMA_Init(&hdma_tx);
		__HAL_LINKDMA(&hUART1, hdmatx, hdma_tx);
		
		/* Configure the DMA handler for reception process */
		hdma_rx.Instance                 = DMA1_Stream0;
		hdma_rx.Init.Direction           = DMA_PERIPH_TO_MEMORY;
		hdma_rx.Init.PeriphInc           = DMA_PINC_DISABLE;
		hdma_rx.Init.MemInc              = DMA_MINC_ENABLE;
		hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
		hdma_rx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
		hdma_rx.Init.Mode                = DMA_NORMAL;
		hdma_rx.Init.Priority            = DMA_PRIORITY_HIGH;
		hdma_rx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
		hdma_rx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
		hdma_rx.Init.MemBurst            = DMA_MBURST_INC4;
		hdma_rx.Init.PeriphBurst         = DMA_PBURST_INC4;
		hdma_rx.Init.Request             = DMA_REQUEST_USART1_RX;
 
		HAL_DMA_Init(&hdma_rx);
		__HAL_LINKDMA(&hUART1, hdmarx, hdma_rx);
		
		/* NVIC configuration for DMA transfer complete interrupt (USART1_RX) */
		HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 1);
		HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
		
		/* NVIC configuration for DMA transfer complete interrupt (USART1_TX) */
		HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
		HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
	}
 
 
	int8_t getTime(uint8_t msg[])
	{
		const char pubx04CString[] = "$PUBX,04*37\r\n";
		std::array<uint8_t, sizeof(pubx04CString) - 1> pubx04Cmd = { };
		std::copy(std::begin(pubx04CString), std::end(pubx04CString)-1, pubx04Cmd.begin());
		
		if(rxtxInProgress)
		{
			if (msgReady)
			{
				rxtxInProgress = false;
				for (int i = 0; i < 128; i++)
					msg[i] = msgBuffer[i];
				
				return 1;
			}
			else
			{
				return 0;
			}
		}
		else
		{
			memset(rxBuffer, 0, sizeof(rxBuffer));
			memset(msgBuffer, 0, sizeof(msgBuffer));
			msgReady = false;
			msgBufferIndex = 0;
 
			HAL_UART_Receive_IT(&hUART1, rxBuffer, 1);
			HAL_UART_Transmit(&hUART1, pubx04Cmd.data(), pubx04Cmd.size(), 2000);
			
			rxtxInProgress = true;
			
			return 0;
		}
	}
	
	uint8_t parsePUBX04(GPS::GPSFix& currentFix)
	{
		uint16_t year;
		
		year = (currentFix.msg[23] - 48) * 10 + (currentFix.msg[24] - 48) + 2000;
		if (year > 2018)
		{
			currentFix.TOF.year = year;
			currentFix.TOF.hour = (currentFix.msg[9] - 48) * 10 + (currentFix.msg[10] - 48);
			currentFix.TOF.minute = (currentFix.msg[11] - 48) * 10 + (currentFix.msg[12] - 48);
			currentFix.TOF.second = (currentFix.msg[13] - 48) * 10 + (currentFix.msg[14] - 48);
 
			currentFix.TOF.date = (currentFix.msg[19] - 48) * 10 + (currentFix.msg[20] - 48);
			currentFix.TOF.month = (currentFix.msg[21] - 48) * 10 + (currentFix.msg[22] - 48);
 
			return 1;
		}
		else
			return -1;		
		
	}
	
private:
	static bool rxtxInProgress;
};
 
bool GPSHack::rxtxInProgress = false;

6 REPLIES 6
TDK
Guru

There's probably another definition of USART1_IRQHandler in your code somewhere. CubeMX will generate one and put it in the hal_*_it.c file. The linker error message should provide more info on where it's first defined.

To use a variable in multiple source files, it need to be declared in a common header, or, declared in one source file and declared as "extern" in the other one you want to use it in.

Using HAL functions in multiple source files shouldn't be an issue as long as you're including the headers, which you are.

You should not use #pragma once in source files, only headers. But that's not an issue here.

Would be better to post the full error message you're getting.

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

Header files which are to be #included must not contain function/object/variable *definitions*, only declarations.

JW

magene
Senior II

@TDK - Here's the full error

0693W00000JPcrJQAT.png 

I started this project with the STM32 example for the STM32H7B3 Discovery board but I'm trying to make it work on the STM32H7A3 Nucleo board. My understanding is the B3 is identical to the A3 but has a encryption module so it doesn't seem like that's part of the problem.

/* Started with 
  * @file    FatFs/FatFs_uSD_DMA_Standalone/Core/Src/main.c
  * @author  MCD Application Team
  * @brief   Main program body
  *          This sample code shows how to use FatFs with uSD card drive.
  */

USART1_IRQHandler is not defined or declared in stm32h7xx_it.c. I actually can't find the declaration by searching for USART1_IRQHandler. This subroutine is the only other place I find a reference to USART1_IRQHandler but I don't really understand what it's doing.

void * g_pfnVectors[0xab] __attribute__ ((section (".isr_vector"), used)) = 
{
	&_estack,
	&Reset_Handler,
	...
	&USART1_IRQHandler,
	&USART2_IRQHandler,
	&USART3_IRQHandler,
	...
	&OTFDEC2_IRQHandler,
	&GFXMMU_IRQHandler,
	&BDMA1_IRQHandler,
};

USART1_IRQHandler is defined in startup_STM32H7b3xxq.c as weak, naked. If I don't define my own USART1_IRQHandler, my program will compile and run but jump here and tell me I need to define my own which is what I'd expect.

void __attribute__ ((weak, naked)) USART1_IRQHandler() 
{
	//If you hit the breakpoint below, one of the interrupts was unhandled in your code. 
	//Define the following function in your code to handle it:
	//	extern "C" void USART1_IRQHandler();
	__asm("bkpt 255");
	__asm("bx lr");
}

@Community member​ That's what I usually do but I haven't figured out how to build a file that I can #include with all the IRQHandlers and the code that calls them except by putting them all in one file.

Every time I try to do anything significantly different than a STM example, I run into problems trying to figure out what STM is doing under the hood with their HAL which seems incredibly complicated. I'd sure like to find an example project or library or whatever that shows how to use the STM HAL for something more generally useful than the simple examples STM provides.

And thanks as usual for the help

TDK
Guru

> GPSHack.hpp

Ah, so it's a header file. As JW says, you can't have definitions in a header if that file is included from multiple source files. Split it up into a header with declarations and a source file with definitions.

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

OK, I'll give that a try again. Many thanks for the help.

magene
Senior II

That worked! @TDK​  and @Community member​ , I'll be happy to buy each of you the drink of your choice the next time you're in the Monterey Bay, California area. Or several drinks. Many, many thanks.