I’ve almost always been told this is too hard a problem to solve, so why even try. I never really know how to respond to this reaction. Usually I respond to this type of quip with one of two assumptions: they’re a lazy person, I don’t know what I don’t know.
When this problem was first presented at work I was leaning in the direction of simply writing a realtime kernel to run, instead of the linux kernel, which would handle all the required hardware for the project. My will rapidly deflated following this option. I rather quickly realized the Pi isn’t quite like any other Single Board Computer (SBC) I had used up to this point.
One major difference on a pi from nearly all other platforms come from the fact that the main processor is one of the last components to be initialized. On the Pi the graphics “co” processor (GP) is the first peripheral to startup which loads external code and executes it. The graphics processor identifies which version of the Pi it is and selects the corresponding kernel binary from the /boot partition. Then after configuring the main processor the GP loads the kernel image into ram and starts the main CPU core at the entry point for the ELF binary it just copied into ram.
The problem wasn’t actually getting the system to take my custom kernel and load that. The problem arose when I needed to access the Broadcom SoC’s peripherals such as the GPIO ports. Another major consideration that led to abandoning this idea came when I realized we would be required to interface with a mobile app using with bluetooth or wifi. I wasn’t about to handcraft, or even borrow, a full TCP/IP stack, a full wifi driver, and a robust webserver all of which would have to run on a single core without real process management.
In the end I essentially stumbled upon a couple articles from several years ago (50 or so) involving dedicating entire cpu cores to a single process, or even a single thread in some cases when realtime, or “near-realtime” processing is required. Using a combination of SCHED_FIFO scheduling and sched_setaffinity cpu affinity options I was able to dedicate one of the cores of the pi to the main GPIO thread under linux. This also required a change to the cmdline.txt file for isocpu config option (I think this stands for isolate cpu from scheduler) being set to reserve a couple cores for my realtime threads.
After doing both these steps I had one of my threads running as the only runnable thread for core 2. This thread manages the GPIO calls because reading them out regularly is essential for the correct debouncing of input signal there are no other threads in the system with a higher priority. The data is read into a buffer for later processing. The processing thread was spun-up using this same method on another core to handle the debouncing of the stored data. The final thread wasn’t run with such a high priority but it was run on a specific core. This means the linux scheduler can pre-empt, or pause my execution at any time. This final thread was responsible for making all the decisions based on the current system state.
This setup resulted in acceptable responsiveness for the application. What this wasn’t though was true realtime. I saw timing jitter on the order of tens of milli-seconds or around 10%. This wasn’t a massive deal for out usecase. If it had been I believe the issue lies with the library we used to manage the GPIO interface. If I were to do this over again I would manage the GPIO myself as I missed on my initial investigation the fact that the library does sampling at a configurable rate on its own. I probably didn’t need to duplicate that effort.