Raspberry PIのGPIOのデバイスドライバを作成します。「bcm2835 ライブラリによるスイッチ入力とLEDの点滅 」で使用したタクトスイッチを使って割り込みを発生させます。

GPIOデバイスドライバ

GPIOポートに接続されたタクトスイッチから接点信号を入力するため、「Raspberry PIのGPIOのデバイスドライバ – Open 」で作成したデバイスドライバーのプログラムを基に次の修正を行います。

ヘッダファイルの追加

Linux用デバイスドライバの割り込みヘッダを追加します。

#include <linux/interrupt.h>

GPIOピンデータストラクチャ

割り込み処理を行うために、次の変数を追加します。

  • irq_perm: GPIOピンの割り込み許可フラグ
  • irq_flag: 割り込み立ち上がり/立ち下り信号フラグ
struct raspi_gpio_dev {
	struct cdev cdev;
	struct gpio pin;
	enum state state;
	enum direction dir;
	bool irq_perm;
	unsigned long irq_flag;
	unsigned int irq_counter;
	spinlock_t lock;
};

割り込みハンドラ関数irq_handler

割り込みが発生すると実行される割り込みハンドラ関数irq_handlerを作成します。割り込みハンドラは、次のような形式で作成されます。

irqreturn_t irq_handler(int irq, void *dev)
  • irq: request_irq() で指定した割り込み番号
  • dev: request_irq() で指定したデバイス
  • 結果: irqreturn_t型で戻され、次の値を取る。
    • IRQ_NONE:割り込み処理異常
    • IRQ_HANDLED:割り込み処理正常

作成した割り込みハンドラを次に示します。

static irqreturn_t irq_handler(int irq, void *arg) {
	unsigned long flags;
	unsigned int interrupt_time = millis();
	if (interrupt_time - last_interrupt_time < 200) {
		printk(KERN_NOTICE "Ignored Interrupt [%d]\n", irq);
		return IRQ_HANDLED;
	}
	last_interrupt_time = interrupt_time;
	local_irq_save(flags);     /* 割り込み禁止 */
	printk(KERN_NOTICE "Interrupt [%d] was triggered\n", irq);
	local_irq_restore(flags);  /* 割り込み許可 (save の時の状態にもどる) */
	return IRQ_HANDLED;
}

GPIOのオープンデバイスドライバ関数raspi_gpio_open

request_irq() を用いて割り込みハンドラを登録します。

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
            const char *name, void *dev)
    irq
    割り込み番号
    handler
    割り込みハンドラ
    flags
    後述
    name
    名前。この名前は/proc/interrupts や /proc/irq に表示される
    dev
    デバイス。割り込みが複数のデバイスで共有される時に、どのデバイスかを区別するために使われる。割り込みハンドラが呼ばれると、この値が引数で渡される
    結果
    成功すると 0を返し、失敗すると 0 以外を返す。たとえば、-EBUSY だと割り込み番号が使われている
    <flagsの例>

    IRQF_DISABLED
    割り込みハンドラの実行中、「全ての」割り込みを禁止する。このフラグがセットされていなければ、自分自身だけ禁止され、他の割り込みは許可された状態で動作する。
    RQF_SAMPLE_RANDOM
    乱数生成器のために割り込みを利用してもよい。
    IRQF_TIMER
    システム・タイマのために利用する。
    IRQF_SHARED
    複数のデバイスで同じ割り込み番号を共有できる。


割り込みのために追加したオープンデバイスドライバ関数を次に示します。

static int raspi_gpio_open (struct inode *inode, struct file *filp) {
	struct raspi_gpio_dev *raspi_gpio_devp;
	unsigned int gpio;
	int err, irq;
	unsigned long flags;
     ・
     ・
	/* interrupt */
	if ((raspi_gpio_devp->irq_perm == true) &&(raspi_gpio_devp->dir == in)) {
		if ((raspi_gpio_devp->irq_counter++ == 0)) {
			irq = gpio_to_irq(gpio);
			if (raspi_gpio_devp->irq_flag == IRQF_TRIGGER_RISING) {
				spin_lock_irqsave(&raspi_gpio_devp->lock, flags);
				err = request_irq ( irq,
					irq_handler,
					IRQF_SHARED | IRQF_TRIGGER_RISING,
					INTERRUPT_DEVICE_NAME,
					raspi_gpio_devp);
				printk(KERN_INFO "interrupt requested\n");
				spin_unlock_irqrestore(&raspi_gpio_devp->lock, flags);
			} else {
				spin_lock_irqsave(&raspi_gpio_devp->lock, flags);
				err = request_irq ( irq,
					irq_handler,
					IRQF_SHARED | IRQF_TRIGGER_FALLING,
					INTERRUPT_DEVICE_NAME,
					raspi_gpio_devp);
				printk(KERN_INFO "interrupt requested\n");
				spin_unlock_irqrestore(&raspi_gpio_devp->lock, flags);
			}
			if (err != 0) {
				printk(KERN_ERR "unable to claim irq: %d, error %d\n", irq, err);
				return err;
			}
		}
	}
     ・
     ・

GPIOの書き込みデバイスドライバ関数raspi_gpio_write

書き込みデバイスドライバ関数では、割り込み信号の立ち上がり時/もしくは立下り時に割り込みを発生するかを指定します。
パラメータにより、”rising”の場合は立ち上がり時に割り込みを発生するように、GPIOピンデータストラクチャのirq_flagにIRQF_TRIGGER_RISINGを設定し、”falling”の場合は立ち下がり時に割り込みを発生するように、GPIOピンデータストラクチャのirq_flagにIRQF_TRIGGER_FALLINGを設定します。
また、割り込みを禁止する場合は、パラメータにより、”disable-irq”が指定されます。この場合、GPIOピンデータストラクチャのirq_permにfalseを設定します。
割り込みのために追加した書き込みデバイスドライバ関数を次に示します。

static ssize_t raspi_gpio_write ( struct file *filp,
	const char *buf,
	size_t count,
	loff_t *f_pos)
{
     ・
     ・
	if ( (strcmp(kbuf, "rising") == 0) ||(strcmp(kbuf, "falling") == 0)) {
		spin_lock_irqsave(&raspi_gpio_devp->lock, flags);
		gpio_direction_input(gpio);
		raspi_gpio_devp->dir = in;
		raspi_gpio_devp->irq_perm = true;
		if (strcmp(kbuf, "rising") == 0)
			raspi_gpio_devp->irq_flag = IRQF_TRIGGER_RISING;
		else raspi_gpio_devp->irq_flag = IRQF_TRIGGER_FALLING;
		spin_unlock_irqrestore(&raspi_gpio_devp->lock, flags);
	} else if (strcmp(kbuf, "disable-irq") == 0){
		spin_lock_irqsave(&raspi_gpio_devp->lock, flags);
		raspi_gpio_devp->irq_perm = false;
		spin_unlock_irqrestore(&raspi_gpio_devp->lock, flags);
	} else {
     ・
     ・

割り込みの発生の確認

デバイス「raspiGpio0」に対して、それぞれechoコマンドで書き込み、catコマンドで読み込み、実行毎にデバイスドライバから出力されたシステムログをdmesgコマンドで確認します。

次のコマンドで信号の立ち上がり時に割り込みを発生するように登録します。

# echo rising > /dev/raspiGpio0

システムログにより立ち上がり時に割り込みを発生するように登録されたシステムログが確認できます。
# dmesg
     ・
     ・
[ 3546.617913] start gpio_init
[ 3546.626827] RaspberryPi GPIO driver initialized
[ 3656.715714] GPIOstart open
[ 3656.715755] GPIO[0] opened
[ 3656.715930] Request from user: rising

次のコマンドを実行し、タクトスイッチを押します。

# cat /dev/raspiGpio0

タクトスイッチを押すと割り込みハンドラのシステムログ「Interrupt [394] was triggered」が表示されます。「394」は、割り込みハンドラ登録時に指定した割り込み番号を示します。
# dmesg
     ・
     ・
[ 3758.354822] GPIOstart open
[ 3758.354865] GPIO[0] opened
[ 3758.354941] Interrupt [394] was triggered
[ 3758.355291] interrupt requested

次のコマンドにより割り込みを禁止します。

# echo disable-irq > /dev/raspiGpio0

割り込みが禁止されたシステムログが表示されます。
# dmesg
     ・
     ・
[ 3886.315148] GPIOstart open
[ 3886.315187] GPIO[0] opened
[ 3886.316781] Request from user: disable-irq

次のコマンドを実行し、タクトスイッチを押します。

# cat /dev/raspiGpio0

タクトスイッチを押しても割り込みハンドラのシステムログが表示されません。つまり割り込みが禁止されています。
# dmesg
     ・
     ・
[ 3927.270408] GPIOstart open
[ 3927.270449] GPIO[0] opened

/proc/interrupts は、割り込みの回数を保持しています。catコマンドにより割り込み回数を表示させます。タクトスイッチにより割り込みを発生させると、「gpio interrupt」として表示されました。

# cat /proc/interrupts
           CPU0
  3:      19502   ARMCTRL   3  BCM2708 Timer Tick
 16:          0   ARMCTRL  16  bcm2708_fb dma
 32:     154568   ARMCTRL  32  dwc_otg, dwc_otg_pcd, dwc_otg_hcd:usb1
 49:          1   ARMCTRL  49  20200000.gpio:bank0
 50:          0   ARMCTRL  50  20200000.gpio:bank1
 65:         10   ARMCTRL  65  ARM Mailbox IRQ
 66:          2   ARMCTRL  66  VCHIQ doorbell
 75:          1   ARMCTRL  75
 80:          0   ARMCTRL  80  20204000.spi
 83:          6   ARMCTRL  83  uart-pl011
 84:      52256   ARMCTRL  84  mmc0
394:          1  pinctrl-bcm2835   0  gpio interrupt
FIQ:              usb_fiq
Err:          0