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 だと割り込み番号が使われている
- IRQF_DISABLED
- 割り込みハンドラの実行中、「全ての」割り込みを禁止する。このフラグがセットされていなければ、自分自身だけ禁止され、他の割り込みは許可された状態で動作する。
- RQF_SAMPLE_RANDOM
- 乱数生成器のために割り込みを利用してもよい。
- IRQF_TIMER
- システム・タイマのために利用する。
- IRQF_SHARED
- 複数のデバイスで同じ割り込み番号を共有できる。
- <flagsの例>
割り込みのために追加したオープンデバイスドライバ関数を次に示します。
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