-1

NOTE: In this context, the term "ISR" is not really addressing ISR vectors. There are a lot of tricks in the drivers. One of them is handling with the interrupts generated by the radio tranceiver chip. SX1276_LoRaRadio driver has a thread that checks the actual interrupt source. This is because all these things are working on an rtos (mbed, based on RTX5) and the rtos does not allow direct access to the MCU interrupt vectors. So once PIN_X of the mcu is triggered, the thread reads the radio chip registers for the actual interrupt source.

I am developing for an ARM MCU. There is a radio tranceiver chip (sx1276) on my board. The radio chip driver requires pointers to my own ISR member functions. Thus, I have to pass the pointers of the ISR member functions to another member object (a struct that keeps the pointers) of the same class. The detailed explanation is below.

  • A low level radio driver handles with the device registers (SX1276_LoRaRadio.cpp). This is provided by the manufacturer and developed exactly for the framework I am using (mbed os). This inherits from LoRaRadio
  • An API developed by the framwork developer (arm) provides a higher level pure virtual class for abstraction (LoRaRadio.h).
  • My own radio comm stack exploits the other two in the application layer (ProtoTelecom.hpp and ProtoTelecom.cpp).

The radio has some interrupt pins which are triggered in the occurance of some events. But i can use only one of them due to how the manufacturer designed the driver. This is explained at the very beginning. The events are members of a struct:

// This is in LoRaRadio.h
typedef struct radio_events {
    /**
     * Callback when Transmission is done.
     */
    mbed::Callback<void()> tx_done;

    
    <other mbed::Callback type members>
} radio_events_t;

My ProtoTelecom.hpp looks like this:

class ProtoTelecom: public manageCommands {
public:
    explicit ProtoTelecom(LoRaRadio *Radio, tcm_Manager *tcm);
    static void OnTxDone(void);
    void nonstaticTxDone(void);

    <other corresponding ISR member functions>
private:
    static radio_events_t RadioEvents;

    <other private stuff here>
};

Then in ProtoTelecom.cpp I assign the pointers of the corresponding static member functions to the RadioEvents members:

radio_events_t ProtoTelecom::RadioEvents = {
            .tx_done = ProtoTelecom::OnTxDone,
            
             <other pointer assignments>
};

ProtoTelecom::ProtoTelecom(LoRaRadio *Radio, c_TC_Manager *tcm) : manageCommands()
{
    <other stuff goes here>

    Radio->init_radio(&RadioEvents);
}

My problem is I have to have multiple instances of ProtoRadio. However, the radio isr functions must be static because passing member function pointers inside the class itself is not allowed in c++. This simply prevents me to initialize another hw because both execute the same isr functions. So how can I pass the pointer of a non static member function to another non static member object inside the class itself? Something like this:

ProtoTelecom::ProtoTelecom(LoRaRadio *Radio, c_TC_Manager *tcm) : manageCommands()
{
    <other stuff goes here>
    RadioEvents.tx_done = nonstaticTxDone;
    Radio->init_radio(&RadioEvents);
}

I found this thread but the case is slightly different than mine and I could not manage to adapt it to my situation. The main theme of my question is the title but I know that what I am trying to achive is a bit strange and requires some tricks. So if there are other ways to achive my final goal that is also okay for me. Thanks.

  • 2
    Why do your functions have to be static? From a quick glance at `mbed::Callback` it seems like it should work with non-static member functions? – Alan Birtles Jul 15 '22 at 10:49
  • @AlanBirtles Because a computer can only have one interrupt per interrupt vector. So in case of C++ class you must make ISRs static or nothing will work. – Lundin Jul 15 '22 at 10:57
  • Overall, this is a good illustration why inheritance is often a clunky way to design programs, because at the point of the initial design, it's very hard to predict all future use-cases and the needs of inherited classes. You'll have to make an inherited class "singleton" then design a communication interface to that one from separate classes. (Or check if they have a C API then go with that and watch all your C++ created problems go away as if by magic...) – Lundin Jul 15 '22 at 10:59
  • 2
    TLDR. Based on a description this sounds like a simple question that can be explained in one paragraph, or less, with a [mre] that's no more than a dozen lines of code. Can you [edit] your question and rewrite it, accordingly? This will greatly improve your chances of getting a helpful answer. The best way to get a helpful answer on Stackoverflow is to make it as easy as possible for everyone to understand a question. I gave up only a few sentences into this narrative. – Sam Varshavchik Jul 15 '22 at 10:59
  • ^That. So in short, you want to pass non-static member functions as callbacks to an API that only accepts function pointers? – Passer By Jul 15 '22 at 11:06
  • @Lundin I don't think that restriction is relevant here, unless the loraradio class was very badly designed. If it accepts mbed::Callback, a generic wrapper for any callable, then it must have some internal mapping from static isrs to invoking the callbacks. I also don't see how this problem is caused by c++ - if I wanted to use multiple radios, but can only pass static function pointers that don't take a generic userdata argument, how would I solve this better in c? – Wutz Jul 15 '22 at 11:21
  • @Wutz "unless the loraradio class was very badly designed" That is sadly the norm, not the exception, when it comes to source code supplied by silicon vendors. Never make the mistake of assuming that the code is well-written, makes sense or was tested. – Lundin Jul 15 '22 at 11:23
  • 1
    @Lundin Fair. Would that mean the askers task of using multiple radios at once is just impossible to achieve with this class? Except maybe by implementing each callback twice, once for each instance? Also, there's a difference between "bad design is frequent, so even though the interface looks like it supports your use-case, it might not work" - which is reasonable - and what you said, which I read as "it's impossible, use something else". – Wutz Jul 15 '22 at 11:38
  • @SamVarshavchik I would prefer a minimal working example but the code is a FW. So it runs on an ARM device. I thought a pure c++ would not adress my actual problem. But yes. in short it is what Passer By says. I posted all these code pieces extracted from the api, driver etc. to make it clear what is the overal structure of the code. Since I am using the API it is not so easy explain the situation. – jonathan eslava Jul 15 '22 at 11:38
  • 1
    Have you tried just passing non-static functions yet? There is a callback constructor taking an object pointer + pointer-to-member which looks like it's exactly what you want. – Wutz Jul 15 '22 at 12:00
  • If the question is about some aspect of C++, the programming language, then "pure C++" will be the best way to explain it. It is unlikely that anyone on Stackoverflow will have much interest in someone's custom firewall code, it is unclear why anyone would be interested in learning how it works, either. What most people here are interested in, though, is discussing C++-related questions that are of benefit to a wide audience. Questions on Stackoverflow are designed to be of general knowledge, benefitting a general audience, and not anyone specifically. – Sam Varshavchik Jul 15 '22 at 12:27
  • @Wutz thank you for the suggestion. **newRadioEvents.tx_done = mbed::Callback(this, &FeesRadio::nonstaticTxDone);** in the constructor allows me to compile. I need a bit of time to complete the others and pass the reference of newRadioEvents to **Radio->init_radio()**. I am working on it. – jonathan eslava Jul 15 '22 at 12:37
  • @SamVarshavchik since I am using an API and the compiler is arm-none-eabi-gcc but not the gnu-gcc itself, I am not sure a code written for an x86/64 platform would well represent my problem. I cannot replicate the whole mbed::Callback here. The API and the driver are not my custom firewall code. Instead they are public and provided with BSD/apache license. The whole os, APIs, drivers etc. are written in c++ and mbed is fairly used in the embedded world. So, I believe this thread can help other people in the future. In any case I do not ignore your point. Thank you for the clarification. – jonathan eslava Jul 15 '22 at 13:02
  • C++ and its core concepts, like static and non-static class methods, virtual methods, etc, work exactly the same on any CPU or platform. Here's the title of this question: "Pass pointer of a non static member function to another non static member object". Which part of this has anything to do with any particular CPU? This has nothing to do, whatsoever, with any particular CPU, and the underlying question should be explainable in terms that are universal, and reference only core C++ concepts. – Sam Varshavchik Jul 15 '22 at 13:05
  • @SamVarshavchik it does, however, have something to do with mbed specifically. If op had broken down their question to pure c++ and asked about passing raw function pointers, the only answer they would have gotten would be "it's impossible". Mbed::Callback is very relevant to this question. Other than that I agree with you: a minimal example laying out the core issue with mbed types would have made for a much better question. But sometimes it's hard to know which parts of the problem are relevant if you're trying to ask a question. – Wutz Jul 15 '22 at 16:54

1 Answers1

0

As @Wutz stated in his third comment, the correct way is using mbed::Callback. Here is my new constructor:

ProtoTelecom::ProtoTelecom(LoRaRadio *Radio, c_TC_Manager *tcm) : manageCommands()
{
    this->Radio = Radio;
    this->tcm = tcm;

    //Inizialize the radio
    newRadioEvents.tx_done = mbed::Callback<void()>(this, &ProtoTelecom::nonstaticOnTxDone);
    newRadioEvents.tx_timeout = mbed::Callback<void()>(this, &ProtoTelecom::nonstaticOnTxTimeout);
    newRadioEvents.rx_done = mbed::Callback<void(const uint8_t *, uint16_t , int16_t , int8_t)>(this, &ProtoTelecom::nonstaticOnRxDone);
    newRadioEvents.rx_timeout = mbed::Callback<void()>(this, &ProtoTelecom::nonstaticOnRxTimeout);
    newRadioEvents.rx_error = mbed::Callback<void()>(this, &ProtoTelecom::nonstaticOnRxError);

    Radio->init_radio( &newRadioEvents );
    switchModem(radio_mode);
    setState(RADIO_RECEIVE);
}

The newRadioEvents is a non static instance of radio_events_t.