I describe how to achieve latencies less than 3 microseconds 99+% of the time, with a worst-case latency of 41 microseconds. This is on a Raspberry Pi 3B+ running 2018-06-27-raspbian-stretch-lite, without any kernel drivers or other exotica. Note that on single-core models the results will be much worse. While this is not "hard" real time, it is sufficient for many applications. My applications are for a scientific instrument and involve sampling hardware at 1,000 Hz and writing each result to a socket.
By "latency" I mean the time difference between the time that "something" happens, and the time the program knows that it happened. Here "something" could be an edge on a GPIO, a time-delay ends, a specific time of day, etc. As best I can tell, large latencies are due to an interrupt causing the kernel to schedule some other task. Because of that, worst-case latencies do not add, and during one loop the code can call several routines that have latencies, but it essentially never happens that more than one of them gets a large latency. The RPi has a hardware counter that increments at 1 MHz, so measuring latency with 1 microsecond (us) resolution is easy.
The idea is to dedicate one core on a RPi 3B+ to the real-time process, and then write it as a simple loop doing whatever it needs to do:
Code: Select all
initialize
for(;;) {
wait for connection to the socket
for(;;) {
wait for GPIO edge indicating ADC data are ready
read ADC channels
fprintf(socket, ...)
}
close socket
}
How to do it
- add this argument to /boot/cmdline.txt:
isolcpus=3
This prevents Linux from scheduling processes on core 3. But interrupts still happen on it, and there is an essential kernel task running on it. Still, it is a good start, but not at all sufficient. - Set the process's CPU affinity to 3. See attached code.
- Disable turbo mode. Otherwise the OS will change the CPU clock frequency, and that screws up the SPI clock and the overall timing. See attached code.
- Set the process to real-time FIFO scheduling, with maximum priority. See attached code.
- Permit real-time processes to use 100% of the CPU. Without this you'll get 50 millisecond delays once per second. See attached code.
Also attached is latency.cc to measure the latencies. Note that to measure the GPIO latency it needs GPIO22 connected to GPIO23. Here is a screenshot of an 18-hour run on a Raspberry Pi 3B+: The line
0us: 0 76961522 31486427 9058 ...
Means that no sample had a 0-microsecond latency, 76961522 samples had a 1-microsecond latency, 31486427 samples had a 2-microsecond latency, etc. See the comments in the code for an explanation of what is measured.