Admittedly I am new to C++ and am much more comfortable in ANSI C, so this might be a simple problem:
I have some C++ files for my application that are running on top of the CubeMX HAL from ST. I am running bare metal.
My project all compiles cleanly with no warnings or errors. When it tries to link I get an undefined symbol error. My suspicions are it has something to do with name mangling from C++, but I'm not certain. I am using the extern "C" {} around the c++ header files to accommodate the mixed c/c++
Below is the definition of the functions (overloaded), the complete include file, and a snippet from the usage that is throwing the linkage error.
linking... DiscBoard-Interm\DiscBoard-Interm.axf: Error: L6218E: Undefined symbol GPIO::RWGpio(GPIO::Pin_e) (referred from max31856.o). DiscBoard-Interm\DiscBoard-Interm.axf: Error: L6218E: Undefined symbol GPIO::RWGpio(GPIO::Pin_e, GPIO::AbstractState_e) (referred from max31856.o). Not enough information to list image symbols. Finished: 1 information, 0 warning and 2 error messages.
/** @brief Read/Write GPIO This function will take the parameters and determine if the request is a read or write request. The index will be the particular IO to use, and the data passed is the set value. If there is no set value passed in, the function will return the existing value. If a value is passed, it will set the value and return. Global variables none */ GPIO::AbstractState_e RWPGpio(GPIO::Pin_e PinIdx) { GPIO_PinState State; GPIO::AbstractState_e retval; if (GpioCfgTable[PinIdx].Idx == PinIdx) //make sure table is consistent with request { State = HAL_GPIO_ReadPin(GpioCfgTable[PinIdx].Port,GpioCfgTable[PinIdx].Pin); if (GpioCfgTable[PinIdx].ActiveState == GPIO_PIN_SET) { //normal positive polarity so do nothing retval = (GPIO::AbstractState_e)State; } else { //we need to invert on/off retval = (GPIO::AbstractState_e)((State + 1)>>1); } } else { //inconsistent def, throw an error and do nothing } return retval; } /** @brief Read/Write GPIO This function will take the parameters and determine if the request is a read or write request. The index will be the particular IO to use, and the data passed is the set value. If there is no set value passed in, the function will return the existing value. If a value is passed, it will set the value and return. Global variables none */ void RWGpio(GPIO::Pin_e PinIdx, GPIO::AbstractState_e State) { GPIO_PinState PinState; if (GpioCfgTable[PinIdx].ActiveState == GPIO_PIN_SET) { //normal positive polarity so do nothing PinState = (GPIO_PinState)State; } else { //we need to invert on/off PinState = (GPIO_PinState)((State + 1)>>1); } if (GpioCfgTable[PinIdx].Idx == PinIdx) //make sure table is consistent with request { HAL_GPIO_WritePin(GpioCfgTable[PinIdx].Port,GpioCfgTable[PinIdx].Pin,PinState); } else { //inconsistent def, throw an error and do nothing } //return state; }
#ifndef GPIO_H #define GPIO_H #ifdef __cplusplus extern "C" { #endif #include "stm32f4xx_hal.h" // Keil::Device:STM32Cube HAL:Common #include <stdint.h> #include "stm32f4xx_hal_gpio.h" //GPIO defs struct GPIOConfig { uint8_t Idx; GPIO_TypeDef* Port; uint16_t Pin; GPIO_PinState ActiveState; }; class GPIO { public: enum AbstractState_e { OFF=0, ON }; enum Pin_e { UC_KICK_WDOG = 0, CONTACT_QUALITY_MON, CH4_TEMP_FAULT, CH4_RF_ENABLE, CH4_CAL_RELAY, FAN_TEMP_ALERT, CH4_RETURN_RELAY, UC_WKUP, CH4_RF_VOLTAGE_MON, CH4_RF_CURRENT_MON, CH3_RF_VOLTAGE_MON, CH3_RF_CURRENT_MON, CH2_RF_V_MON, CH2_RF_I_MON, CH1_RF_V_MON, CH1_RF_I_MON, CH4_TEMP_DATA_READY, CH4_DAC_SPI3_CS, CH4_TEMP_SPI6_CS, CH3_TEMP_FAULT, CH3_RF_ENABLE, CH3_CAL_RELAY, CH3_RETURN_RELAY, CH3_TEMP_DATA_READY, CH3_DAC_SPI3_CS, CH3_TEMP_SPI6_CS, CH2_TEMP_FAULT, CH2_RF_ENABLE, CH2_CAL_RELAY, CH2_RETURN_RELAY, CH2_TEMP_DATA_READY, CH2_DAC_SPI3_CS, CH2_TEMP_SPI6_CS, CH1_RF_ENABLE, CH1_TEMP_FAULT, CH1_CAL_RELAY, CH1_RETURN_RELAY, CH1_TEMP_DATA_READY, CH1_DAC_SPI3_CS, CH1_TEMP_SPI6_CS, STIM_NEG, STIM_POS, STIM_SELECT, DEBUG_OUTPUT_PC7, DEBUG_OUTPUT_PC8, DEBUG_INPUT_PC9, FAN_FLT_FULL_SPEED, OSC_460KHZ_ENABLE, FAN_OVERTEMP, H_LOCKOUT_RF_MONITOR, L_STANDBY_ACTIVE, L_UC_MASTER_DISABLE_RF, L_WDOG_TIMER_EXPIRED, UC_WDOG_REG_DAT, UC_WDOG_REG_CLK, LED3_PE0, LED4_PE1, MAX_PIN_CNT }; private: //TODO Come back and figure out how to make this a private const table //and where does it live in memory //static const GPIO_Config GpioCfgTable[]; public: GPIO_PinState RWGpio(Pin_e PinIdx); void RWGpio(Pin_e PinIdx,AbstractState_e State); }; //end class #ifdef __cplusplus } #endif #endif //GPIO_H
usage:
Thermocouple::TempTenths Thermocouple::ReadThermocouple (uint8_t Tidx) { uint8_t spiCommand[3]; uint8_t spiResp[3]; //HAL_StatusTypeDef retStatus; GPIO_PinState State; GPIO rwGPIO; TempTenths temperature; State = rwGPIO.RWGpio(rwGPIO.CH2_TEMP_DATA_READY); if (State == GPIO_PIN_RESET) { //Read the temperature spiCommand[0] = 0x0c; rwGPIO.RWGpio(rwGPIO.CH2_DAC_SPI3_CS,rwGPIO.ON); //Transmitting 2 more bytes than configured. Doesn't really matter, but will be garbage. HAL_SPI_TransmitReceive(&hspi6, (uint8_t*)&spiCommand,(uint8_t*)&spiResp,sizeof(spiCommand),1000); rwGPIO.RWGpio(rwGPIO.CH2_DAC_SPI3_CS,rwGPIO.OFF); } //TODO check this response for accuracy temperature = (spiResp[0]<<8) & spiResp[1]; return temperature; }
Additional info:
I am using
IDE-Version: µVision V5.23.0.0 Copyright (C) 2017 ARM Ltd and ARM Germany GmbH. All rights reserved.
Tool Version Numbers: Toolchain: MDK-ARM Standard Cortex-M only Version: 5.23 Toolchain Path: C:\Keil_v5\ARM\ARMCC\Bin C Compiler: Armcc.exe V5.06 update 4 (build 422) Assembler: Armasm.exe V5.06 update 4 (build 422) Linker/Locator: ArmLink.exe V5.06 update 4 (build 422)
Also the typo in the first function declaration is not real Cut and paste error. There is no "P" in the middle of the function name.