I've ported the MBED DigitalOut class to the Hercules controller that I'll be using.
It could be any controller or SBC, but having one example makes it tangible.
I've try to do this with as limited as possible impact to the SemTech library. That is my main goal.
If you are looking for performant code or the best possible choice from language purity perspective, you may want to click away.
I'm using 64 bit integers on a 32 platform and more of that stuff .
Not all the things that I have to port may be applicable to your situation.
The pin versus pin/port dilemma
MBED uses these constructs to use an output pin:
Constructor:
DigitalIn::DigitalIn(PinName pin)
Declaration:
typedef enum { PA_0 = 0x00, // ... PC_9 = 0x29, // ... // Generic signals namings LED1 = PC_9, // ... // Not connected NC = (int)0xFFFFFFFF } PinName; DigitalOut led( LED1 );
Assign a value:
led = 0;
Toggle:
led = !led;
These are the great wonders of C++. I like them a lot. They use operator overrides to make a led behave like a boolean.
The part that does not, by defaut, fit my microcontroller is that it has a Port + Pin. So the PinName paradigm does not fit automatically on my controller.
An example of a port is GPIO port A, on address 0xFFF7BC34U
#define gioPORTA ((gioPORT_t *)0xFFF7BC34U)
Whenever I want to use a pin on that port, I have to tell the controller what port I'm using.
I could solve this in a neat way:
- create a class or structure that holds both Port and Pin.
- create an enumerator type for the pins I'm using.
- create an collection of these structures, each structure indexed or keyed by the enumerator value, so that, in the implemntation, I can retrieve the corresponding Port and Pin.
What I did id somewhat sneakier, and controversial on a 32-bit microcontroller.
I defined the PinName type as a 32 bit unsigned integer.
Then I assigned the most significant 32 bits as location for the port. The lower 32 for the pin.
The example below shows the type definition, and assigning LED 1 to gioPortA, pin 1:
typedef uint64_t PinName; const PinName LED1 = 0xFFF7BC3400000001;
In the constructor of the DigitalOut class, I split them up in two data members.
Declaration:
Class DigitalOut { public: DigitalOut(PinName pin); // ... protected: uint32_t pin; uint32_t port; };
Implementation:
DigitalOut::DigitalOut(PinName pin) { // hercules specific= The first 32 bits are the port address, lower 32 bits the pin this->pin = pin & 0xffffffff; // the lower 32 bits are the pinnummer this->port = ((pin & 0Xffffffff00000000) >> 32); // if your controller uses APIs to set the pin direction, you'd do that here. // I'm using the Hercules HALCoGen utility to set pin directions, so they are in the right state // when the pin will be used first. }
I can now use the GPIO API of my microcontroller and send the pins and ports without further analysis.
Here is how my DigitalOut class members looks like at the end of the constructor execution (for LED1, where the port is 0xFFF7BC34 and the pin 1 is 1).
at the time this variable declaration is executed, just after parsing the port and pin from PinName...
DigitalOut led( LED1 );
How would you do this, if you also had the constraint to make your design fully backward compatible with MBED?
Top Comments