There are many good MBED examples. It's a popular platform, supported by many manufacturers.
Here's my story on how to port one of those excellent MBED example program to another platform.
The program I want to port is the SemTech LoRa Ping-Pong example.
The goal is to get a reusable C++ library that can be used on micro controllers, Raspberry Pi, BeagleBone etc...
I need to find what part of the code is dependent on MBED and factor that out.
The factored-out part is the one that will have to be implemented for the target non-MBED platform.
If I get this working, in the end I have to provide an abstract C++ library that only depends on standard libraries, and a document that specs, for the missing parts, what extra functionality is needed.
I expect that the missing (to be ported) parts are GIO, SPI and UART (optional ).
As test bed, I'm using a Texas Instruments Hercules micro controller. It could be any platform that supports SPI and GPIO.
I selected this one because I have two identical boards and my example is a radio Send + Receive scenario (two SemTech radios pinging and ponging each other).
There are several ways to replace one utility/service library by another. In the case where there is documentation of dependencies, one can start from that. In the case where the dependencies are to be detected from sources, there are also a few options. You can walk through the code and flag all constructs that are dependent on the library you want to replace. Another option is to hide that library from the build process and let the build log point out what's broken. That's the approach I'm using here. |
Task 1: Break the Dependency with MBED
The easiest way to find dependencies is to cut away the MBED sources and libraries. Then use the tool chain build errors to point at the gaps. Tools are more efficient than a full manual gap analysis in many cases.
You can start from an empty project for your target platform.
For a Pi, that could be a "Hello, world!" Linux project in Eclipse. For my platform, I used a minimal Hercules project.
As long as you have a project that successfully runs on your target (it does not have to do anything functional), you are good to go.
If you do that, you have broken the MBED link. You now have a non-MBED project as starting point.
Take care that you don't have any build errors or warnings left. We'll use the build error log in the next step to help replace the functions provided by MBED.
Task 2: Integrate the MBED Example Code in your Project
This is the step where you copy the sources (h and cpp) from the MBED example to your project.
Usually, the library code can just be copied and pasted into your project.
You'll have to do some manual work to merge your project's main file with the main from the MBED example.
Take care that you take over all includes, move all declarations above your project's main() declaration, and add the example's main() functionality to that of your project.
Save everything. Then, just for kicks, build the example. You'll have many errors.
Task 3: Collect Unresolved MBED Dependencies
For all the errors that you have now during compilation, you'll have to provide a replacement.
My approach is to create a honeypot to put all the unresolved issues. I use a header file called missing.h for that.
First, I replace any occurance of mbed.h with missing.h in the project.
Then I compile again.
I then look at the errors in the build log and look for the ones that occur a lot.
I investigate the top one and try to find out what the dependency is.
A common miss is the digital i/o api from MBED.
What I then do, is look up the declaration of the missing dependency online (example: DigitalInOut).
I then create a minimal replacement of the declaration, without implementation, in the missing.h file.
Example:
class DigitalIn { public: DigitalIn(PinName pin) {} }; class DigitalOut { public: DigitalOut(PinName pin) {} DigitalOut &operator= (int value) {} DigitalOut &operator= (DigitalOut &rhs) {} }; class DigitalInOut { public: DigitalInOut(PinName pin) {} DigitalInOut &operator= (int value) {} DigitalInOut &operator= (DigitalInOut &rhs) {} operator int() {} void input() {} void output() {} };
You'll see that my classes are not complete. I only put those dependencies that break the build. And they have an empty implementation.
But those are the indicators of what functionality is missing.
I perform this exercise iterative.
- find a build error
- implement an empty implementation in missing.h
- check that the build error is resolved
- rinse and repeat
The number of comilation errors has to decrease whle doing that exercise. Repeat until no compilation errors (ignore link issues)
In my case, at the end of the exercise, I end with this missing.h file:
/* * missing.h * * Created on: 9 nov. 2019 * Author: jancu */ #ifndef CPP_MISSING_H_ #define CPP_MISSING_H_ #include "HL_sys_common.h" #include <stdio.h> #include <string.h> // define all MBED APIs, classes and defines used in the example typedef enum { PIN_INPUT, PIN_OUTPUT } PinDirection; typedef enum { PA_0 = 0x00, PA_1 = 0x01, PA_2 = 0x02, PA_3 = 0x03, PA_4 = 0x04, PA_5 = 0x05, PA_6 = 0x06, PA_7 = 0x07, PA_8 = 0x08, PA_9 = 0x09, PA_10 = 0x0A, PA_11 = 0x0B, PA_12 = 0x0C, PA_13 = 0x0D, PA_14 = 0x0E, PA_15 = 0x0F, PB_0 = 0x10, PB_1 = 0x11, PB_2 = 0x12, PB_3 = 0x13, PB_4 = 0x14, PB_5 = 0x15, PB_6 = 0x16, PB_7 = 0x17, PB_8 = 0x18, PB_9 = 0x19, PB_10 = 0x1A, PB_12 = 0x1C, PB_13 = 0x1D, PB_14 = 0x1E, PB_15 = 0x1F, PC_0 = 0x20, PC_1 = 0x21, PC_2 = 0x22, PC_3 = 0x23, PC_4 = 0x24, PC_5 = 0x25, PC_6 = 0x26, PC_7 = 0x27, PC_8 = 0x28, PC_9 = 0x29, PC_10 = 0x2A, PC_11 = 0x2B, PC_12 = 0x2C, PC_13 = 0x2D, PC_14 = 0x2E, PC_15 = 0x2F, PD_2 = 0x32, PH_0 = 0x70, PH_1 = 0x71, A0 = PA_0, A3 = PB_0, A4 = PC_1, D1 = PA_2, D2 = PA_10, D3 = PB_3, D4 = PB_5, D5 = PB_4, D6 = PB_10, D7 = PA_8, D8 = PA_9, D9 = PC_7, D10 = PB_6, D11 = PA_7, D12 = PA_6, D13 = PA_5, D14 = PB_9, D15 = PB_8, // Generic signals namings LED1 = 0x05, LED2 = 0x05, // Not connected NC = (int)0xFFFFFFFF } PinName; class SPI { public: SPI(PinName mosi, PinName miso, PinName sclk, PinName ssel = NC) {} virtual int write(int value){return 0;} void format(int bits, int mode = 0) {} }; class DigitalIn { public: DigitalIn(PinName pin) {} }; class DigitalOut { public: DigitalOut(PinName pin) {} DigitalOut &operator= (int value) {} DigitalOut &operator= (DigitalOut &rhs) {} }; class DigitalInOut { public: DigitalInOut(PinName pin) {} DigitalInOut &operator= (int value) {} DigitalInOut &operator= (DigitalInOut &rhs) {} operator int() {} void input() {} void output() {} }; template <typename F> class Callback; class InterruptIn { public: InterruptIn(PinName pin) {} void rise(void) {} }; class Timeout{ protected: virtual void handler() {} public: void detach() {} }; // placeholder for all calls used in the MBED example that don't have an implementation static inline void debug(const char *format, ...) {} static inline void debug_if(int condition, const char *format, ...) {} void wait_ms( int ) {} void wait( int ) {} double ceil(double) {return 0.0;} double floor(double) {return 0.0;} double rint(double) {return 0.0;} #endif /* CPP_MISSING_H_ */
Some constructs are cumbersome to resolve, e.g.: the MBED Callback template construct.
In those cases, where it takes longer to do this exercise than it would be to build a final solution, I comment out the failing code and put a // todo: comment in the source.
Eclipse and other editors have a Task view that shows the things that have to be resolved in source code.
Work Items at This Stage
At this point, where there are 0 compilation errors left, we have a list of manageable work items.
- All code in missing.h. Each of those will need a MBED-independent implementation.
There's no need to implement it identical to MBED. But it's a good starting point. I think that the MBED object oriented abstractions are neat. Better than what I usually come up with. - The tasks in the Todo window. hose are all pieces of code you commented out but are needed for a working design.
This is a good time to try and dig into those constructs, get a better understanding and create a solution that works on your non-MBED target platform.
- All code in missing.h. Each of those will need a MBED-independent implementation.
Next steps for this blog: Resolve these gaps for my project and document them.
Because the Hercules projects are typically C, and the MBED libs are C++, I also had to provide a bridge between the two. A non-intrusive one.
Here's the write-down of that Hercules Safety Microcontroller: Use C++ with HALCoGen C Projects..
Top Comments