Wednesday, May 1, 2013

Raspberry Pi interrupt to signal conversion, kernel to user space.

The goal

Make a gpio interrupt in kernel space, send signal to any program.

Usability

Educational. Hardware ultra-kill button.

Code


Description


With this code, you can connect 'some thing' to gpio pin which gives an impulse (button, clock, whatever), and High-to-Low on this gpio pin will send signal to any pid on your system.

For example, you can send SIGKILL(9) to some pid, by pressing button connected to gpio pin.

Kernel module


m_gpioSignal has 2 parameters:
gpio_pin  // default value is 17
and
signal_to_pid  // default value is 34
If you want to change this, load module with parameters:
pi@raspberrypi ~/progs/cpp/gpioSignal $ sudo insmod m_gpioSignal.ko gpio_pin=27 signal_to_pid=35
After module is load, special device is registred in '/dev' directory. You must create it from root by command displayed in /var/log/messages.
pi@raspberrypi ~/progs/cpp/gpioSignal $ sudo insmod m_gpioSignal.ko
pi@raspberrypi ~/progs/cpp/gpioSignal $ tail -n 2 /var/log/messages 
May  1 20:07:25 raspberrypi kernel: [ 2198.765275] Info: signal_to_pid is 34, gpio_pin is 17
May  1 20:07:25 raspberrypi kernel: [ 2198.765303] 'mknod /dev/m_gpioSignal c 248 0'
pi@raspberrypi ~/progs/cpp/gpioSignal $
pi@raspberrypi ~/progs/cpp/gpioSignal $ sudo mknod /dev/m_gpioSignal c 248 0
And enable write by others.
pi@raspberrypi ~/progs/cpp/gpioSignal $ sudo chmod o+w /dev/m_gpioSignal
To set pid to which signal will be send, just write it to device. Now you can do it as a regular user.
echo "2117" > /dev/m_gioSignal
On kernel messages in /var/log/messages you will see:
May  1 20:12:43 raspberrypi kernel: [ 2516.988983] send_to_pid set: 2117
After this, interrupt flag is enabled and if High-to-Low occurs on gpio17, signal 34 will be send to pid 2117, and interrupt flag will be disabled until pid will be again written to device.
May  1 20:14:19 raspberrypi kernel: [ 2613.210739] Signal 34 was send to pid 2117

User space application


User space program is very simple, it send his own pid to /dev/m_gpioSignal device, and then attach own function to signal 34.

When signal arrives (High-to-Low on gpio pin 17), function is executed and pid is written to device once again (to enable interrupt).

Disadvantages


Interrupt in kernel is active all time. By writing and executing interrupt we only enable/disable own flag so you must be sure, that your trigger (clock, button etc.) is not generating noise. If you use a button gpio pin should be pulled-up to 3.3V from raspberry pi.

Performance test

 

I did small performance test, to check how much time last from falling edge on GPIO pin to user space function call. User space application was extened and now after every signal sets GPIO pin 22 to HIGH (bcm2835_gpio_write) for about 10us, so I can measure time between falling edge on GPIO pin 17 and HIGH state on GPIO pin 22.

Atmega32, connected to Raspberry, triggers an interrupt on GPIO pin 17 by generating falling edge condition HIGH->LOW on pin PD5.

Test conditions:
  • Raspberry Pi is under load of:
    • 7 ssh client sessions
    • transmission-daemon (idle)
    • mpd (playing audio stream from http)
    • two while(true); do true; done; loops
    • vim x 3
    • tail -f /var/log/messages
    • htop
    • our user space application

  •  Atmega32:
    • PD5 pin is connected to Raspberry's GPIO pin 17
    • every 250us flips PD5 state, HIGH, LOW in endless loop 

  •  Oscilloscope connected to Raspberry:
    • First channel is connected to GPIO pin 17
    • Second channel is connected to GPIO pin 22

On several trials, average time was close to 130us, even just before freeze - I've generated to much load on other sessions. We could assume, that heavy load has less impact in our test. But remember, that we've generated only CPU load, IO (SD card) was untouched.

However 5% on 100 samples was completely out of timing, 460us, 690us even 1.8ms.

Average time from falling edge on gpio pin to user space function call.

I do not know how to assess the reliability of this test, so let's treat this only as an approximation.

User space function body had bcm2835_gpio_write at first line, if we put something more before it, the INT->SIGNAL delay will be much bigger.

10 comments:

  1. Can you specify the latency from the GPIO pin raising level till the call of the user mode function?

    I'm looking for the FASTEST solution (PPS Input) to attach an GPS clock for timing purposes. But have no idea what to can achieve with PI and Interrupt handling. Not that Topic, but can i also send a udp packet from within the Interrupt kernel function?

    Thanks in advance!

    ReplyDelete
    Replies
    1. Hi,

      If you have very fast PPS input, interrupts and raspberry may not be a good idea. Remember that interrupts are handled by kernel, and kernel is doing some other 'stuff' like IO, memory operations, switching task between CPU etc. So sometimes under heavy CPU load, interrupt can be fired and signal will be send (to userspace program) , but userspace application could wait for CPU time, e.g. when you run something like while(true); in bash / C etc, and "be late" to handle it in reasonable time.

      I would rather connect this PPS to microcontroler, where runs only "your stuff", and sends results by i2c / spi / uart.

      But it all depends how fast your PPS is, do you have any specification of max frequency, kilo/mega Hz ?

      UDP packet from kernel: check 'netpoll_send_udp' function and simillar.

      I will try to check GPIO -> userspace latency, at weekend, but I don't know if I'll have time.

      Delete
  2. Thanks a lot - the "Interrupt" issue was also my concerns, because my application is really simple, but the PI is so versatile that (perhaps) it would need the lowest amount on time to implement this.

    I have a GPS board with NMEA via Serial and an ultra precise 1HZ PPS Output. I want utilize both Inputs and want to send an UDP packet with the current time to my PC at every PPS Signal (1 per second). So it's all about latency for this single Interrupt.

    I've searched at other microcontrollers, but havent found one with 1. approbbiate IP Stack and 2. easy development Environment (like arduino).

    I've also looked for a solution to swap Linux and get the things done at the RISC Level, but found nothing (because i don't need an OS). But perhaps if i stripe down all linux deamons, i can get a reliable solution with the PI. Latancies of 1-3ms are acceptable for this kind of task doing it with the PI.

    ReplyDelete
    Replies
    1. Ok, 1Hz is no problem, so we have to focus just on those 1-3ms. And if it's 1Hz, this could be implemented in kernel (just like you wrote earlier). I'll check timing and sending udp packets from kernel, late Sunday or Monday I should have something for you. Did you consider to sync time localy GPIO (PPS) -> Raspberry PI and treat it like time server ? Then configure your PC to sync time e.g. every 5 seconds. And one more thing, 1-3 ms is time from PPS input to UDP packet send, or to recive and process on you PC ?

      Delete
    2. It would be great, if you could pointing me in the right direction. I very appreciate your contribution. i am afraid that sync the PI time in the kernel mode will add a lot more amount of work to me. For time sync i've found a NTP server doing that. But instead polling an PI NTP server from PC i thought it would be better if the PI sends a 1Hz UDP packet to the PC. This packet could have some useful data (PI timeofday or similar). The 1-3ms should cover the whole transfer. I assume that the UDP transfer himself should be in under 1ms. I can also measure UDP the roundtrip time from PI-PC-PI to get some offset to subtract that time from the "time to set". I'm looking forward into this setup, because handling the Interrupt AND sending a small UDP packet in kernel mode should be the fastes solution available on an Raspberry.

      Delete
    3. Hi,

      I've did some test and updated this post. I think, you could end with user space function triggered by an interrupt. I'll do some more test in this week, mostly with sending UDP packets, so we'll see how long does the whole process.

      If it's gonna be too long, I will implement sending UDP packets in kernel.

      Regards.

      Delete
    4. Excellent test and results, thanks a lot! For sure these timings are sufficent for my needs - even in user space. I'm afraid sending UDP packets from kernel are too complicated for me. But the difference against user space UDP is interesting. I think that the difference strongly depends from the scheduling and interrupt algo.

      Delete
  3. Me again! Do you have an recommended read about programming PI at such low level? I've found nothing at amazon. Programming linux with C is also thin covered at Amazon. :0(

    ReplyDelete
    Replies
    1. If you know how to programm in C, you know how to program in Raspberry Pi (linux) :), low level programming is just reading/writing to different data structures/registers, and that's all. On my other post I recommend to read this: http://www.tldp.org/LDP/lkmpg/2.6/html/lkmpg.html .

      Programming microcontrollers is very good lesson for later kernel-fun, but not in IDE such as Arduino, where you have plenty of libs. IMHO the most effective way is the hardest: download datasheet of your atmega328 (arduino), and read some C examples (they are in datasheet) and this: http://www.nongnu.org/avr-libc/user-manual/modules.html (there are some samples too), and try to fit in your 2Kb SRAM :)

      Look here: http://morethanuser.blogspot.com/2012/10/rs232-stepper-motor-control-attiny2313_22.html and at source code: http://morethanuser.blogspot.com/2012/10/rs232-stepper-motor-control-attiny2313.html , this is simple stepper motor control over rs232, look at main.c and motor.c. I drive stepper motor by writing half byte (4 bits) to PORTB register, which is represented in hardware by 8 pins (1 pin = 1 bit, whole port = one byte) on microcontroller. So you can see it's 'regular' programing, but on different data structures.

      You can also write regular (not low level) programs in C using many pointers in code (get familiar with them, pointers arithmetic etc.), dynamic memory allocation and Valgrind to check if your program have leaks.

      Microcontroller, Raspberry Pi and your PC are only hardware which are controlled by some program, there is a lot of similar things in those architectures.

      Delete
    2. I've looked into the links and this is godd stuff. The kernel development pages are excellent - Need some time to study it. My roots are down to assembler programming with a 80C535, so i can follow your intentions entirely. The point is that i want to avoid an additonal development tool chain to get the things done with other micro Controller. But during seeking for an solution i've read a lot over "today's" uC Solutions like arduino, Beagle, st32 and so on. It was a lot of fun how easy it is today. If i have some spare time i will look into these topic again. Currently my focus is on PI, because there are some projects waiting and i want to learn more about Linux programming.

      Delete