Porting C code to Teensyduino for MCP2517FD CAN FD Project

Status
Not open for further replies.

skpang

Well-known member
What is the best way to port C code to Teensyduino ?

I want to use the Microchip MCP2517FD CAN FD controller with Teensy 3.2
The code is in C but Teensy wants C++.

I've tried adding:
#ifdef __cplusplus // Provide C++ Compatibility
extern "C" {
#endif

to the C code but when I want to access the SPI bus by adding SPI.begin() it won't compile.

Another method I've tried is to creating a class and copy each function into the class but it was a bit of a mess and won't compile.

The C code is at:
http://www.microchip.com/developmenttools/productdetails.aspx?partno=adm00576
It is the MCP2517FD canfdspi API (v1.0)

Any idea?
 
You can move the C code to its own file in the project directory by using a .c suffix instead of .cpp, However, I doubt the general Arduino headers will work in a C environment.

So, one method is to create a .h header that encapsulates the communication between the Arduino bits and the library. The Arduino side would do I/O, etc. It would call into the C functions for setup, and they in turn would call back to the Arduino libraries to do the actual I/O. It is s bit of a mess, but it might be simpler than just rewriting the whole function.
 
I looked at the Microchip code briefly.

Their .h files already have this:

Code:
#ifdef  __cplusplus
extern "C" {
#endif

First, I'd copy everything from mcp2517fd_api/firmware/src/system_config/mcp2517fd_evb_api/framework/driver/canfdspi and mcp2517fd_api/firmware/src/system_config/mcp2517fd_evb_api/framework/driver/spi into your sketch folder. Do this while Arduino is not running. I'd rename drv_spi.c to drv_spi.cpp. My personal preference would be the put all the files into the sketch folder, without any subdirectories, and then edit the #include lines as necessary to remove folder names. These massive folder hierarchies make sense for huge software projects, but they make everything a lot harder when you're just trying to get stuff done with a few files!

You CAN leave all the other files as .c type. Arduino does indeed support using C (not C++) files. Normally this isn't done, because it's simpler to just make everything .cpp. But if the .h files that define the C stuff have those 'extern "C"' sections, then you can use the C stuff from C++ code. Those just tell the C++ compiler to call those functions as C code, rather than C++.

Then run Arduino. The very first step will be to edit drv_spi.cpp. It only has 3 functions. You'll need to delete all their code. Because you renamed this one to .cpp, you'll be able to #include <SPI.h> and call the SPI library functions.

The Microchip code has these includes.

Code:
#include "peripheral/spi/plib_spi.h"            // SPI PLIB Header
#include "system_config.h"
#include "system_definitions.h"

I would just comment those out and click Verify in Arduino. You'll probably get tons of errors about undefined stuff. For each thing, I'd search all the .h files from Microchip's code. Hopefully most will be fairly simple, maybe just copy and paste the define into the code needing it.

If you've never done this sort of porting work, the massive number of compiler errors can feel overwhelming. The key is knowing the difference between intractable errors and "easy" errors which merely involve simple but tedious work to go find each thing and either adapt that header file or code, or if the file contains lots of other difficult stuff (a pretty common problem) just copy the small piece into one of the files you're using.
 
Thanks Michael and Paul for the advice.

I've changed the drv_spi.c to drv_spi.cpp and added the SPI.h and SPI.begin() to the init function and that part compiles ok.

Now I'm stuck on a enum problem. There is a function that I'm calling in the .ino file:

uint32_t selectedBitTime = CAN_500K_2M;
DRV_CANFDSPI_BitTimeConfigure(DRV_CANFDSPI_INDEX_0, selectedBitTime, CAN_SSP_MODE_AUTO, CAN_SYSCLK_40M);

but it is giving me this error:
invalid conversion from 'uint32_t {aka long unsigned int}' to 'CAN_BITTIME_SETUP' [-fpermissive]

the function has this prototype:
int8_t DRV_CANFDSPI_BitTimeConfigure(CANFDSPI_MODULE_ID index,
CAN_BITTIME_SETUP bitTime, CAN_SSP_MODE sspMode,
CAN_SYSCLK_SPEED clk);


The "selectedBitTime" variable is the uint32_t and 'CAN_BITTIME_SETUP' has this typedef enum:


Code:
typedef enum {
    CAN_500K_1M,    // 0x00
    CAN_500K_2M,    // 0x01
    CAN_500K_3M,
    CAN_500K_4M,
    CAN_500K_5M,    // 0x04
    CAN_500K_6M7,
    CAN_500K_8M,    // 0x06
    CAN_500K_10M,
    CAN_250K_500K,  // 0x08
    CAN_250K_833K,
    CAN_250K_1M,
    CAN_250K_1M5,
    CAN_250K_2M,
    CAN_250K_3M,
    CAN_250K_4M,
    CAN_1000K_4M,   // 0x0f
    CAN_1000K_8M,
    CAN_125K_500K   // 0x11
} CAN_BITTIME_SETUP;

Googling around and some has suggested I change the typedef to this:

Code:
typedef enum  CAN_BITTIME_SETUP{
    CAN_500K_1M,    // 0x00
    CAN_500K_2M,    // 0x01
    CAN_500K_3M,
    CAN_500K_4M,
    CAN_500K_5M,    // 0x04
    CAN_500K_6M7,
    CAN_500K_8M,    // 0x06
    CAN_500K_10M,
    CAN_250K_500K,  // 0x08
    CAN_250K_833K,
    CAN_250K_1M,
    CAN_250K_1M5,
    CAN_250K_2M,
    CAN_250K_3M,
    CAN_250K_4M,
    CAN_1000K_4M,   // 0x0f
    CAN_1000K_8M,
    CAN_125K_500K   // 0x11
};

But that just moves the error to somewhere else. Looks like there is some differences on enum between C and C++

What is the best way to solve this enum issue ?
 
It’s not the Enum’s fault. The enum just does enumerate values and assign them a type. Then you assign an enum type and value to an unsigned 32bit integer which the compiler casts without problem. But then, in the function call, you give the casted uint32_t as a parameter while that enum type is expected, and the compiler can not cast back.

Thus, I would not do this implicit uint32_t casting, but try to declare selectedBitTime with the correct type before the function call:
Code:
 CAN_BITTIME_SETUP selectedBitTime = CAN_500K_2M; 
DRV_CANFDSPI_BitTimeConfigure(...);
 
It’s not the Enum’s fault. The enum just does enumerate values and assign them a type. Then you assign an enum type and value to an unsigned 32bit integer which the compiler casts without problem. But then, in the function call, you give the casted uint32_t as a parameter while that enum type is expected, and the compiler can not cast back.

Thus, I would not do this implicit uint32_t casting, but try to declare selectedBitTime with the correct type before the function call:
Code:
 CAN_BITTIME_SETUP selectedBitTime = CAN_500K_2M; 
DRV_CANFDSPI_BitTimeConfigure(...);
Or alternatively, pass an int, and use const int or #define. You could also use an explicit cast of the enum to int (or vice versa).
 
CAN-FD Tested and Working on T3.2 and T3.5

Just for reference I used @skprang code on a T3.2 and a T3.5 using a MIKROE MCP2517FD Click board. The porting to the Teensy worked no issues (once I fixed my wiring to the boards :( ). I did order his boards but would take too long to get and I have no patience to wait.

Thanks for the conversion @skprang. Opens up a couple of new doors.
 
I might order a few too, looks like it could need a sister library like IFCT :) It all depends on my free time of course

Tony
 
Just for reference I used @skprang code on a T3.2 and a T3.5 using a MIKROE MCP2517FD Click board. The porting to the Teensy worked no issues (once I fixed my wiring to the boards :( ). I did order his boards but would take too long to get and I have no patience to wait.

Thanks for the conversion @skprang. Opens up a couple of new doors.

Good to hear you got it working.

For those who want to try Teensy with the MCP2517FD the code is in GitHub:

For Teensy 3.2
https://github.com/skpang/Teensy32_with_MCP2517FD_CAN_FD_demo

For Teensy 3.6
https://github.com/skpang/Teensy36_with_mcp2517FD_CAN_FD_demo
 
Status
Not open for further replies.
Back
Top