c++ – How do I access a member function in an object declared in a base class from a derived class?

I have a base class called CTD that instantiates a singleton real time clock (RTC) object. The RTC is a singleton because it’s an embedded system with only 1 RTC and several other classes need to get time data from the RTC. I have a derived singleton class called CTD1 that inherits CTD. Again, CTD1 is a singleton because there is only 1 and several other classes need to get data from CTD1. I’d like to be able to call member functions in the RTC class, like getTimestamp(), from the derived CTD1 class but they aren’t visible. How can I do this?

Here’s the relevant part of the base CTD class that instantiates the real time clock class.

class CTD
{
public:
    CTD(string id)
    {
    }
    
    const string ctdID;

    struct CTDSample
    {
        double pressure_dBar;
        double tempDegC;
        double salinityPPS;
        uint64_t sysTickCount;
        uint8_t msg[256];
    };


protected:
    mcuRealTimeClock mcuRTC = mcuRealTimeClock::getInstance();

};

mcuRealTimeClock has a public getTimestamp() method I’d like to call in the derived CTD1 class.

mcuRealTimeClock::CPFTimestamp mcuRealTimeClock::getTimestamp()
{
    RTC_DateTypeDef dateNow;
    RTC_TimeTypeDef timeNow;
    uint16_t milliSeconds = 0;
    char cTimestamp[timestampSize] = "";
    CPFTimestamp timestamp;
    
    if (timeIsSet)
    {
        HAL_RTC_GetTime(&RTCHandle, &timeNow, RTC_FORMAT_BIN);
        HAL_RTC_GetDate(&RTCHandle, &dateNow, RTC_FORMAT_BIN);
        //TODO need to check this calculation
        milliSeconds = (int)(1000.0 * (float)(timeNow.SecondFraction - timeNow.SubSeconds) / (float)(timeNow.SecondFraction + 1.0));
    
        snprintf((char*)timestamp.data(),
            32,
            "%02d-%02d-%02dT%02d:%02d:%02d.%03d", 
            dateNow.Year + 2000,
            dateNow.Month,
            dateNow.Date,
            timeNow.Hours,
            timeNow.Minutes, 
            timeNow.Seconds,
            milliSeconds);
    }
    else
    {
        snprintf((char*)timestamp.data(), 32, "Time Is Not Set");
    }
    return timestamp;
}

Here’s the relevant part of the derived class

#include "CTD1.hpp"

static UART_HandleTypeDef ctd1UART = UART_HandleTypeDef();
static DMA_HandleTypeDef hdma_tx;
static DMA_HandleTypeDef hdma_rx;

static uint8_t ctd1MsgBuffer[128];
static uint8_t ctd1MsgBufferIndex = 0;
static bool ctd1MsgReady = false;
static uint8_t ctd1RXBuffer[16];

const uint32_t qDepth = 8;
const uint32_t qWidth = 256;
static FIFO ctd1FIFO(qDepth, qWidth, "ctd1Q");

static TIM_HandleTypeDef hTimer2Instance = { .Instance = TIM2 };

extern "C" void TIM2_IRQHandler()
{
    HAL_TIM_IRQHandler(&hTimer2Instance);
}
 
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim == &hTimer2Instance)
        cout << "CTD Timer Callback time: " <<  mcuRTC.getTimestamp() << endl;
}

void CTD1::initTimer()
{
    __TIM2_CLK_ENABLE();
    hTimer2Instance.Init.Prescaler = 280000;
    hTimer2Instance.Init.CounterMode = TIM_COUNTERMODE_UP;
    hTimer2Instance.Init.Period = 5000;
    hTimer2Instance.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    hTimer2Instance.Init.RepetitionCounter = 0;
    HAL_TIM_Base_Init(&hTimer2Instance);
    
    HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);
}

void CTD1::startTimer()
{
    HAL_TIM_Base_Start_IT(&hTimer2Instance);
}

void CTD1::stopTimer()
{
    HAL_TIM_Base_Stop_IT(&hTimer2Instance);
}

void CTD1::setTimerPeriod(uint32_t period)
{
    hTimer2Instance.Init.Period = period;
    HAL_TIM_Base_Init(&hTimer2Instance);
}

and here’s the header file.

and here’s the header file

#pragma once
#include <stm32h7xx_hal.h>
#include <stm32h7xx_hal_rcc.h>
#include <stm32h7xx_hal_tim.h>

#include <memory.h>
#include <stdio.h>
#include <string>
#include <iostream>
#include <sstream>

#include "main.hpp"
#include "CTD.hpp"

using namespace std;

//Implemented as a singleton per http://laristra.github.io/flecsi/src/developer-guide/patterns/meyers_singleton.html
//Had to remove destructors
//There may be better implementations like https://stackoverflow.com/questions/1008019/c-singleton-design-pattern/1008289#1008289

class CTD1 : public CTD
{
public:
    static CTD1& getInstance()
    {
        static CTD1 instance;
        return instance;
    }
    void initCTD_UART();
    void initTimer();
    void startTimer();
    void stopTimer();
    void setTimerPeriod(uint32_t period);
    
    int8_t getPressure(CTDSample& ctdSample);
    int8_t parseFastPressure(CTDSample& currentsample); 
    int8_t simFastPressure(CTDSample& currentSample);
    int8_t simGetPressure(CTDSample& ctdSample);
    int8_t setCPMode();
    void defineCommands();
    const string getEngFileHeader();
    
private:
    CTD1()  : CTD("$gps1")
    {
        ctdID = "$ctd1";
        initTimer();
    }

    bool ctd1RXTXInProgress = false;
    
    string ctdID = "$ctd1";
    
};

But mcuRTC.getTimestamp() in the HALL_TIM_PeriodElapsedCallback throws

Severity Description Project File Line Error ‘mcuRTC’ was not declared in this scope CPF C:UsersgeneDocumentsCPFElecEngrcpfSTM32H7A3_SOMCodeCPF_H7A3CPFCTD1.cpp 28

Is there an acceptable way to do this in C++ or am I just barking up the wrong tree?

Thanks – Gene

PS here are the other header files

#pragma once

#include "stm32h7xx_hal.h"
#include "stm32h7b3i_discovery.h"
#include <stdio.h>
#include <array>
#include <string>
#include <iostream>

using namespace std;

#define RTC_CLOCK_SOURCE_LSI
#define RTC_ASYNCH_PREDIV    0x7F
#define RTC_SYNCH_PREDIV     0x0F9

static uint32_t RtcClockSource = RCC_RTCCLKSOURCE_LSI;

//Implemented as a singleton per http://laristra.github.io/flecsi/src/developer-guide/patterns/meyers_singleton.html
//Had to remove destructors
//There may be better implementations like https://stackoverflow.com/questions/1008019/c-singleton-design-pattern/1008289#1008289

class mcuRealTimeClock
{
private:
    mcuRealTimeClock();
    const static uint8_t timestampSize = 32;
    void errorHandler(void);    
    void configRTC(void);
    
    RTC_HandleTypeDef RTCHandle;

protected:
    struct DateTime
    {
        RTC_DateTypeDef date;
        RTC_TimeTypeDef time;
    };
    
public:
    static mcuRealTimeClock& getInstance()
    {
        static mcuRealTimeClock instance;
        return instance;
    }
    
    struct DateTimeStruct
    {
        uint16_t year;
        uint8_t month;
        uint8_t date;
        uint8_t hour;
        uint8_t minute;
        uint8_t second;
        uint16_t milliSecond;
        string monthMMM; 
    };

    const char* monthText[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

    void test();
    typedef std::array<char, timestampSize> CPFTimestamp;
    CPFTimestamp getTimestamp();
    void clearTimeStamp(DateTimeStruct &currentTimeStamp);
    void setTime(DateTimeStruct newTime);
    DateTimeStruct getDateTimeNow();
    
};

#pragma once

#include "stm32h7xx_hal.h"
#include "stm32h7b3i_discovery.h"
#include <stdio.h>
#include <array>
#include <string>
#include <iostream>

using namespace std;

#define RTC_CLOCK_SOURCE_LSI
#define RTC_ASYNCH_PREDIV    0x7F
#define RTC_SYNCH_PREDIV     0x0F9

static uint32_t RtcClockSource = RCC_RTCCLKSOURCE_LSI;

//Implemented as a singleton per http://laristra.github.io/flecsi/src/developer-guide/patterns/meyers_singleton.html
//Had to remove destructors
//There may be better implementations like https://stackoverflow.com/questions/1008019/c-singleton-design-pattern/1008289#1008289

class mcuRealTimeClock
{
private:
    mcuRealTimeClock();
    const static uint8_t timestampSize = 32;
    void errorHandler(void);    
    void configRTC(void);
    
    RTC_HandleTypeDef RTCHandle;

protected:
    struct DateTime
    {
        RTC_DateTypeDef date;
        RTC_TimeTypeDef time;
    };
    
public:
    static mcuRealTimeClock& getInstance()
    {
        static mcuRealTimeClock instance;
        return instance;
    }
    
    struct DateTimeStruct
    {
        uint16_t year;
        uint8_t month;
        uint8_t date;
        uint8_t hour;
        uint8_t minute;
        uint8_t second;
        uint16_t milliSecond;
        string monthMMM; 
    };

    const char* monthText[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

    void test();
    typedef std::array<char, timestampSize> CPFTimestamp;
    CPFTimestamp getTimestamp();
    void clearTimeStamp(DateTimeStruct &currentTimeStamp);
    void setTime(DateTimeStruct newTime);
    DateTimeStruct getDateTimeNow();
    
};

Leave a Comment