Pulsar #6
Hello everybody, today I wanted to talk a little bit about the software module interface system and some of the problems I'm trying to figure out. In the end I will also give some quick updates about the hardware.
Software Updates
When I started writing the code to interface with the SX1262 LoRa chips I soon realized there's a big problem to address. Since the entire system is centered around the idea of a completely modular radio platform, I need to have all core radio functionality work with any radio module. I cannot integrate the specific functions for the SX1262 in the PulsarOS base structure. For this reason I started to look for solutions that allowed me to develop a basic core functionality and a separate "layer", specific to the installed modules.
What I ended up with is a variable set of "interfaces" that can translate a certain "core API" function call into all the specific instructions for the specific chip that the interface is created for. To give an example, if the core system receives from a user the request to send a message, it calls a certain "send(message)" function linked to the specific MODx interface. That function will then have implemented all the steps necessary to allow the SX1262 to send the message, like communicating the packet size, preparing the TX buffer, etc..
How would this be implemented practically in C99 in an embedded environment? Since I need the interfaces to be modular, I need them to be clearly separated from the rest of the code with a standard solution for the two parts of the code to communicate with each other, and I need anybody to be able to write a new interface and install it into the firmware.
The first part is sort of easi-er, I moved the code specific to the SX1262 into its own folder and started working on a C99 interface, which would translate to a structure of functions that each opposing part can call. One problem is certainly how many functions and what functions should be available to allow the core to access most of each RF chip potential and retain a future-proof design. Using too many functions too specific to one chip could undermine the development of other interfaces for different chips, on the other hand too few functions could leave much of the performance on the table. In the end I think that starting from fewer, better organized functions is the best option, especially because adding more functions later down the line could be implemented in a way that older interfaces can still operate, just without the added functionality.
The second part is a little more complicated. The problem here is deciding how easy it should be for somebody to integrate a new interface in the system. Since the core system needs to call functions implemented in an "external" part of the code, letting the core know the exact address at which to find these functions is necessary and not simple. The first option is compiling all the interfaces a user wants in the firmware, passing to the linker, during compilation, all the addresses of the available interfaces. This is the simpler and safes way to handle this. If a user wants to develop its own interface, or choose exactly what interfaces to have available on its own Pulsar, he/she just needs to recompile the firmware with the specific interfaces in it. There are low risks of the program counter going to some random place and triggering a hard-fault and there are no particular linker scripts to design and maintain. But this method requires the user to know how to compile software. The second option is to have an external memory with the modules compiled in blobs that the core system can load on request. The blobs are generated with the first bytes exposing the specifically structured C99 structure. This way the core system can look for the blobs inside a generic file system (maybe a removable microSD with the module's blobs inside it, downloaded from a computer), load the blobs in memory and call the interface functions by accessing the initial bytes of the blob which will contain the functions addresses relative to the start of the blob in a specific order. This way requires specific linker scripts to build the interfaces blobs (with instructions for alignment of the exposed structure) and can lead to easier hard-faults with the program counter moving to the wrong address. On the other hand, this option doesn't require every user to know how to compile anything, in fact the user can just download the blobs of the modules he/she needs and load them into the microSD.
In the end I'm not yet sure of how to proceed, in the meantime I am moving forward with the development with a structure more towards the first option but that can still be adapted to the second option. If you have ideas about this or want to give your opinion, please do not hesitate to contact me via GitHub.
Hardware Updates
Thanks to the fact that PCBWay is a sponsor of this project I'm working on the new rev2.x of the CLU and PSU. After testing the rev1.x I've compiled a list of upgrades I want to do to the design before finalizing the standards.
For the PSU:
- The total absorbable power is bumped to 140W, at 5A@28V from USB-C PD
- The 5V rail should get an increase in max current from 3A to ~6A
- A new 32V rail is introduced to allow high-power radio modules to pull more power from the DCDC
- The PCB size is increased to match the CLU
- The vertical connectors are swapped for RA connectors
- Added GPIO control over each rail activation
For the CLU:
- Added on-board memory (FRAM)
- Added GPIO expander
- Added SWO for better debugging
- Swapped the JDY-23 chip for a nRF52840 powered module
- The vertical connectors are swapped for RA connectors
- Possibility of upgrade to differential signal lines for the MODx cables (still to be decided)
Work on these hardware upgrades is still very early-on so I'll update you all in the near future.