Wednesday, April 10, 2013

Raspberry Pi gpio interrupts in kernel space

The goal

Configure gpio pins on Raspberry Pi to trigger an interrupt.

Usability

Big: Interfacing devices, for example ov7670 8-bit camera. Connecting own devices to gpio pins, testing data transfer protocols, etc.


Code


Hardware


Description


Linux kernel api provides access to gpio pins, you can configure it as input/output, set/read value and attach current pin to an interrupt.

Most information about gpio in kernel space you can find here.

Kernel compilation and module compilation, here.

Linux kernel programming guide will be helpful too.

Fire interrupt flags are:

IRQF_TRIGGER_RISING
IRQF_TRIGGER_FALLING
IRQF_TRIGGER_HIGH
IRQF_TRIGGER_LOW

As far now, I could trigger interrupt only in FALLING or RISING (or both) modes, when I try configure interrupt as LOW or HIGH error is generated.

Use case


I assume that you have already compiled m_test.ko and sent it to Raspberry pi.

Follow this steps, to do first gpio interrupt test.

Connect via ssh to your Raspberry Pi on two sessions.

On first session, run command:
pi@raspberrypi ~ $ tail -f /var/log/messages
On second one, check registered interrupts:
 pi@raspberrypi ~ $ cat /proc/interrupts
           CPU0       
  3:      58033   ARMCTRL  BCM2708 Timer Tick
 32:     749423   ARMCTRL  dwc_otg, dwc_otg_pcd, dwc_otg_hcd:usb1
 52:        325   ARMCTRL  BCM2708 GPIO catchall handler
 65:       5141   ARMCTRL  ARM Mailbox IRQ
 66:          1   ARMCTRL  VCHIQ doorbell
 75:          1   ARMCTRL
 77:       7303   ARMCTRL  bcm2708_sdhci (dma)
 79:          0   ARMCTRL  bcm2708_i2c.0, bcm2708_i2c.1
 80:          0   ARMCTRL  bcm2708_spi.0
 83:         19   ARMCTRL  uart-pl011
 84:      13684   ARMCTRL  mmc0
FIQ:              usb_fiq
Err:          0
Now, insert our kernel module:
pi@raspberrypi ~/test $ ls
m_test.ko
pi@raspberrypi ~/test $ sudo insmod m_test.ko
On first console, you can see:
Apr 10 21:04:34 raspberrypi kernel: [ 2645.083228] Hello !
Apr 10 21:04:34 raspberrypi kernel: [ 2645.083262] Mapped int 187
Now, between gpio_17 (P5 header, pin 11) and 3.3V put 4.7k resistor. Wait a little bit, disconnect from 3.3V and put it between gpio_17 and ground.

On first console you can see:
Apr 10 21:07:17 raspberrypi kernel: [ 2808.374082] Interrupt [187] for device some_device was triggered !.
Apr 10 21:07:17 raspberrypi kernel: [ 2808.384400] Interrupt [187] for device some_device was triggered !.
Apr 10 21:07:17 raspberrypi kernel: [ 2808.394015] Interrupt [187] for device some_device was triggered !.
Apr 10 21:07:17 raspberrypi kernel: [ 2808.394055] Interrupt [187] for device some_device was triggered !.
Apr 10 21:07:17 raspberrypi kernel: [ 2808.404416] Interrupt [187] for device some_device was triggered !.
Apr 10 21:07:17 raspberrypi kernel: [ 2808.414070] Interrupt [187] for device some_device was triggered !.
Apr 10 21:07:17 raspberrypi kernel: [ 2808.424419] Interrupt [187] for device some_device was triggered !.
It's so many, because pin is very sensitive, in fact, when you only insert in gpio_17 a couple of wire held in hand, the interrupts will be generated too.

Now when module is loaded, check once again registered interrupts:
 pi@raspberrypi ~ $ cat /proc/interrupts
           CPU0     
  3:      65849   ARMCTRL  BCM2708 Timer Tick
 32:     867027   ARMCTRL  dwc_otg, dwc_otg_pcd, dwc_otg_hcd:usb1
 52:        412   ARMCTRL  BCM2708 GPIO catchall handler
 65:       5945   ARMCTRL  ARM Mailbox IRQ
 66:          1   ARMCTRL  VCHIQ doorbell
 75:          1   ARMCTRL
 77:       7472   ARMCTRL  bcm2708_sdhci (dma)
 79:          0   ARMCTRL  bcm2708_i2c.0, bcm2708_i2c.
 80:          0   ARMCTRL  bcm2708_spi.0
 83:         19   ARMCTRL  uart-pl011
 84:      14550   ARMCTRL  mmc
187:        412      GPIO  Some gpio pin description
FIQ:              usb_fiq
Err:          0
There is one more row in registered interrupts:
187:        412      GPIO  Some gpio pin description
Now unload module:
pi@raspberrypi ~ $ sudo rmmod m_test
Watch first console:
Apr 10 21:10:27 raspberrypi kernel: [ 2998.254507] Goodbye
And once again registered interrupts:
pi@raspberrypi ~ $ cat /proc/interrupts
           CPU0       
  3:      67321   ARMCTRL  BCM2708 Timer Tick
 32:     890623   ARMCTRL  dwc_otg, dwc_otg_pcd, dwc_otg_hcd:usb1
 52:        412   ARMCTRL  BCM2708 GPIO catchall handler
 65:       6107   ARMCTRL  ARM Mailbox IRQ
 66:          1   ARMCTRL  VCHIQ doorbell
 75:          1   ARMCTRL
 77:       7491   ARMCTRL  bcm2708_sdhci (dma)
 79:          0   ARMCTRL  bcm2708_i2c.0, bcm2708_i2c.1
 80:          0   ARMCTRL  bcm2708_spi.0
 83:         19   ARMCTRL  uart-pl011
 84:      14729   ARMCTRL  mmc0
187:        412      GPIO
FIQ:              usb_fiq
Err:          0
412 is number which describes, how many times interrupt was triggered.

11 comments:

  1. Hi, thanks for the tutorial. How would you implement the debounce feature via software? Thanks in advance.

    ReplyDelete
    Replies
    1. http://raspberrypi.stackexchange.com/questions/8544/gpio-interrupt-debounce/8556#8556

      Delete
    2. Hi, thanks for above link. Sorry, I was on vacation so I could not respond to your question, I'm glad that you figured out sw debounce. Personally I would go into hw and use something with Schmitt trigger. Often I use CD4093 which is a quad 2-input NAND gate with schmitt trigger. This gate + couple of resistors and condensators and you could simply move your problem from software to hardware. I'm building rfid reader on this gate (only) and it's quite fun :).

      Delete
  2. Hi ,
    you are putting printk while interupt are disabled. printk can block. It's not a good practice.

    // disable hard interrupts (remember them in flag 'flags')
    local_irq_save(flags);

    printk(KERN_NOTICE "Interrupt [%d] for device %s was triggered !.\n",
    irq, (char *) dev_id);

    // restore hard interrupts
    local_irq_restore(flags);

    ReplyDelete
    Replies
    1. Hi,
      First of all, I want to thank you for good comment. You're absolutely true about printk and any other function which would run on disabled interrupts, we should be careful doing that. I'll add your comment to source code.
      In my example, I was triggering interrupt by noise, rather than "nice" input like clock etc. so, output on console was ugly. so that's why I've used printk in disabled interrupt block code.

      Delete
  3. Thanks for sharing this. How can I use the interrupt(s) it in my own (C-)application?

    ReplyDelete
    Replies
    1. Please write some more details. On what platform, your application will run. Atmel ? - no problem. Regular PC/Raspberry PI -> I doubt, that using interrupts here (application) is a good thing. Please give some more info, some example etc. ... maybe you'll end with using signals, not interrupts, etc.

      Delete
  4. Hi, Is it possible to do the same thing on Beaglebone black ?

    ReplyDelete
    Replies
    1. Hi, of course. Check this link: http://processors.wiki.ti.com/index.php/Linux_PSP_GPIO_Driver_Guide

      You just need to know how irq to gpio mapping looks like.
      -- snippet --
      In this case Bank-1[0..31] maps to Linux GPIO numbers 32..63, Bank-2[0..31] to Linux GPIO numbers 64..95 etc.
      -- snippet --

      Basically function gpio_to_irq( ); does all dirty work for you.

      Delete