As we saw in part 13, a device driver gives us possibilities we do not have in user space. We have more reliable timing and we cannot be suspended while we are communicating with external hardware.
But the problem up to now is the busy waiting we have to do to detect changes on GPIO pins. In this part we will use hardware interrupts to let the CPU tell us when a GPIO pin has changed. This frees us from doing busy waiting and enables the CPU to do other things while we wait for changes.
To simplify things, we simply detect the press of a button. This can later be extended to more advanced scenarios. The current setup uses GPIO27 for reading the button input (low for not pressed, high for pressed) and GPIO17 for output. When the button is pressed, the hardware interrupt simply toggles GPIO17 to follow GPIO27. With an oscilloscope we can verify and measure the time lag.
The code of this part now uses the GPIO library of the linux kernel. I first tried activating the GPIO event detection directly (GPIO registers GPEDS, GPREN, GPFEN, GPHEN, GPLEN). This caused the operating system to completly lock up as soon as the button was pressed. I had to remove the power cord to get the Rapsberry Pi to reset. To make sure this was no issue specificly with Raspbian, I tested the same code with an ArchLinux installation on the same Raspberry Pi with the same result. But at least ArchLinux showed some useful error message when locking up:
kernel: sdhost-bcm2835 3f202000.mmc: timeout waiting for hardware interrupt.
So after this it was clear that the linux kernel had some interrupt handling already set up for GPIO which was not configured correctly when we manipulated the GPIO registers directly. After this I decided to go for the kernel library which did work well.
When registering an interrupt service routine with "request_irq", there are parameters like "IRQF_TRIGGER_RISING" and "IRQF_TRIGGER_FALLING". This does not correlate to the GPIO pin rising or falling. Instead it is the hardware interrupt line rising or falling. And we need both parameters to have a reliable interrupt handler which can switch the second GPIO pin to the value of the first.
With my oscilloscope I measured the delay between button press and the second GPIO pin going high:
The upper yellow line shows the button closing the circuit and bringing the input pin high. The lower cyan line shows the output pin being set high by the interrupt handler detecting the change on the input pin. We can see that the output pin lags about 10 microseconds.
Again the upper yellow line shows the button. But this time the button is released resulting in the input pin being brought low. The lower cyan line shows the output pin being brought low by the hardware interrupt handler. Again it is about 10 microseconds behind.
At last I replaced the manual button with a wave generated by an Arduino Uno. The upper yellow line is the input tiggered by the Arduino as fast as possible. The lower cyan line shows the output pin following the input pin in close proximity. The lag even decresed to about 2 microseconds. This shows that the hardware interrupt service routine can easily follow data frequencies with a width around 8 microseconds.
For source code look for the following files and directories:
kernel-space\example02-button
README.md