FlexCAN_T4 C++ help with code structure because of generics

Curt Welch

Member
I'm a long-time C programmer, but I'm only familiar with the basic features of C++. Generics (templates) bit me here with FlexCAN when I tried to change which CAN bus was being used.

We have a PH_motor class that implements motor control, mapping motor commands to CAN bus commands. It currently consists of around 2,000 lines of code.

The usage of the class was to create a CAN object as usual and pass it to the motor control class like this:

C++:
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can;
PH_motor MyMotor(Can, 25, 1); // PH25 motor with ID 1

But then I needed to use CAN3 on one of our new projects and realized the issue that templates creates with this.

I could not just change the CAN1 to CAN3 on the FlexCAN object because our Ph_motor class had multiple places where the type of CAN1 had to be hard-coded as part of the FlexCAN type.

This motor wrapper class couldn't support any CAN bus other than the one for which it was hard-coded.

The only solution I have found so far is to turn the entire 2000 lines of our PhMotor class into a template class. So the usage becomes:
C++:
FlexCAN_T4<CAN3, RX_SIZE_256, TX_SIZE_16> Can3;
PH_motor<CAN3> MyMotor(Can3, 25, 1); // PH25 motor with ID 1

This is not bad, but it's awkward. Now I have 2000 lines of C++ code in a header file as a template that can't be compiled and linked. It just bothers my old school aesthetics but creates no real operational issue.

But then I had constants in the PH_motor class, which now must all include the CAN bus definition in my higher level code:

C++:
MyMotor.check_state(PH_motor<CAN3>::MODE_VEL);
    if (MyMotor.state != PH_motor<CAN3>::State::READY) {
        ...

So, I suddenly have to hard-code these CAN3 references in many places where they don't belong.

I see one path to fixing this by never referencing the Class constants and using instance methods, such as:
C++:
MyMotor.check_state(MyMotor.MODE_VEL);
    if (MyMotor.is_ready()) {
        ...

All this mess arises because the CAN bus definition is a compile-time type definition rather than a run-time variable that can be passed and referenced.

I assume the reason for doing this was to optimize the CAN driver code, but I never realized that the use of templates in C++ would then propagate its approach, making all code above it using the lower level have to be templates.

If one Teeny project uses all three CAN buses then it also means three versions of all the Ph_motor code gets compiled even though there litterly is no difference in the code.

So my question is, are there any other ways to structure the code of my PH_motor class other than what I did by turning the class into a template so I could pass the CAN bus definition through it to the FlexCAN_T4 class? Is there some standard C++ way to deal with this I just haven't found yet?

Part of the issue is that the PH_motor class has an instance variable that points to the FlexCAN object. But the type of this pointer needs to be different depending on what CAN bus is used. Typing it a void* might bypas the checking and work? But that feels wrong.

I've also seen that by subclassing, some of this is solved. Create a base class with all the code that is not dependent, and a template subclass that implements just the code that is dependent on talking to FlexCAN. That could be done. But that also just adds odd complexity to work around what the language doesn't let me do directly. I'm undecided if I like that or not.

So just hoping others might have dealt with this with the FlexCAN package and had some ideas to share....
 
Yes, I have run into that. My source is at this address: https://github.com/collin80/GEVCU7

Note that there is a CANHandler class that handles CAN functionality.

The basic way I did it was so:

I created the three instances of the FlexCAN class, one for each of the three buses on the hardware. Then I had routines that could set which bus a given device should use. The device would then call a normal object (no templating) that would check which bus we said to use. From there you just call one of your three bus instances. When receiving, you have the three instances and code that checks messages at each bus and forwards them to the proper classes that are interested.

So, a device driver for something like a motor controller might have:
Code:
can_handler->setBus(1);
<create a frame to send>
can_handler->sendFrame(myFrame);

The sendFrame function is not generic but can easily send on any bus with something as simple as:

Code:
switch (assignedbus)
{
case 0:
   myCan0Instance.write(myFrame);
   break;
case 1:
   myCan1Instance.write(myFrame);
   break;
case 2:
   myCan2Instance.write(myFrame);
   break;
}

The actual FlexCAN objects are set statically / globally;

Code:
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can0; //Reg CAN or SWCAN depending on mode
FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_16> Can1; //Isolated CAN
FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_16> Can2; //Only CAN-FD capable output

It's complicated, yes, but it's possible without any generic classes at all, save for the existing FlexCAN generics that you can instantiate once for each bus then call into from non-generic classes.
 
It's complicated, yes, but it's possible without any generic classes at all, save for the existing FlexCAN generics that you can instantiate once for each bus then call into from non-generic classes.
Thanks for the feedback!

Yes, I was considering that path as well. We utilize only a few of the features in FlexCAN. I would have to check, but I think we are only sending and receiving messages. (and the basic set baud rate and init). We are not using mailboxes or call-backs. So, all I would need to do is duplicate those lowest-level calls to send and receive with a switch statement.

Your void CanHandler::setup() is funny. All the copy and paste you had to do to get around using templates.

So I learned by digging deeper that not only do we have a motor class that wraps the CAN bus, we have other classes that wrap the motor class, and we have a lot of code that passes the motor objects around that need to be typed as PH_motor &motor that would have to change to hard-coded PH_motor<CAN2> &motor or else make it all genetics. It's a mess how you end up doing something that seems simple, like generics at the low level, and it forces you to turn everything above it into generics as well. Well, now I know!

I'm going to try experimenting with sub-classes and see what the code looks like that way first, then I'll try your low-level switch technique.

Thanks for letting me know I wasn't crazy, and that others had seen this and didn't find a clever, straightforward answer that I didn't understand.
 
The below code is how I ended up structuring a solution to my issue.

The problem comes from a need ot use different CAN buses with a shared code that you don't want to hard code the can bus into the library. The use of templates in FlexCAN_T4 creates this issue.

The solution is to put all the code that doesn't access the FlexCAN_T4 object in a base class that is not a template, but then create a subclass that is a template to deal with the FlexCAN_T4 access. The subclasses will be duplicated as needed by the compiler to compile different subclasses for each CAN bus, but the base class will be compiled and linked as only one set of code in the final object.

In the example code below, the base class is MotorWrapper and the derived subclass is MotorWrapperCan.

So to define new objects of this type, the subclass must be used, and it's a template that understands the type of the FlexCAN object passed:


C++:
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can1;

MotorWrapperCan<CAN1> Motor1(Can1, 1); // Motor 1 on can bus 1 with id 1.

But C++ can figure out the CAN1 part and allow you to leave it off as:

C++:
MotorWrapperCan Motor1(Can1, 1); // Motor 1 on can bus 1 with id 1.

After the object is created, you can refer to as type MotorWrapper, which is not a template class and runs into no type complexity.

Example code to show this sort of solution:

C++:
// main.c

#include "MotorWrapper.h"

FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can1;
FlexCAN_T4<CAN3, RX_SIZE_256, TX_SIZE_16> Can3;

MotorWrapperCan Motor1(Can1, 1); // Motor 1 on can bus 1 with id 1.
MotorWrapperCan Motor2(Can3, 2); // Motor 2 on can bus 3 with id 2.

void do_motor(MotorWrapper &motor);

void main()
{
    Can1.begin();
    Can1.setBaudRate(1000000);

    Can3.begin();
    Can3.setBaudRate(1000000);

    do_motor(Motor1);
    do_motor(Motor2);
}

// Example of how this MotorWrapper class is passed as normal without templates
void do_motor(MotorWrapper &motor)
{
    motor.do_stuff_here();
}

C++:
// MotorWrapper.h

#ifndef MotorWrapper_h
#define MotorWrapper_h

#include "Arduino.h"
#include <FlexCAN_T4.h>

class MotorWrapper
{
public:
    uint8_t _node_id;
    virtual int can_read(CAN_message_t &msg);
    virtual int can_write(CAN_message_t &msg);

    void do_stuff_here(); // defined in MotorWrapaper.cpp

    // lots of code here to implement logic of this object
};

template <CAN_DEV_TABLE can_bus>
class MotorWrapperCan : public MotorWrapper {

    FlexCAN_T4<can_bus, RX_SIZE_256, TX_SIZE_16> *_can;

public:
    MotorWrapperCan(FlexCAN_T4<can_bus, RX_SIZE_256, TX_SIZE_16> &can_ref, uint8_t node_id)
    {
        _can = &can_ref;
        _node_id = node_id;
    }

    int can_read(CAN_message_t &msg) {
        return _can->read(msg);
    }

    int can_write(CAN_message_t &msg) {
        return _can->write(msg);
    }
};

#endif
C++:
// MotorWrapper.cpp

#include "MotorWrapper.h"

void MotorWrapper::do_stuff_here() {
    // DO all the logic in functions in this class which them call
    // the virtual functions to do the CAN I/O.
    CAN_message_t msg;
    // put stuff in msg...
    can_write(msg);
}
 
Back
Top