今日為了調試ARM闆上的GPIO引腳中斷效果,以便在後續項目使用ARM與ZLG7290按鍵LED中斷晶片連接配接中随意選擇空閑的GPIO引腳來作為ZLG7290的中斷信号線,特意編寫了一個小的Linux GPIO中斷驅動程式下載下傳到開發闆上做實驗。經驗證,這種軟體中斷方式也還差強人意。下面貼出自己編寫的不成熟的代碼,見笑(<-_->)。
實驗的硬體電路為ARM GPIO的PB17連接配接一個共陰LED,PB18與PB19連接配接,PB18由中斷驅動設定為低電平觸發,PB19由GPIO驅動程式控制,上層應用程式通過驅動控制PB19高低電平變化,進而引發PB18發生中斷,中斷程式中控制PB17的LED亮和滅。
Linux中斷驅動部分:
/*
* PB18_IRQTest.c
* This is a test program for sam9260, using PB19(J5_18 pin) input a signal to PB18(J5_16 pin),
* PB18 receive this signal as IRQ and make the LED linking on PB17((J5_14 pin)) turn on or turn off
*
* @Author: Cun Tian Rui
* @Date :March.18.2011
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <asm/arch/hardware.h>
#include <asm/arch/gpio.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/arch/board.h>
#include <linux/cdev.h>
#include <asm/arch/gpio.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/arch/at91_pio.h>
#include <asm/arch/at91_aic.h>
#include <asm/arch/at91_pmc.h>
void led_on()
{
at91_set_gpio_output(AT91_PIN_PB17,1);
}
void led_off()
{
at91_set_gpio_output(AT91_PIN_PB17 ,0);
}
struct light_dev *light_devp;
int light_major = 200;
struct light_dev
{
struct cdev cdev;
unsigned char value;
};
MODULE_AUTHOR("Cun Tian Rui");
MODULE_LICENSE("Dual BSD/GPL");
static void io_init(void)
{
at91_set_gpio_input(AT91_PIN_PB18, 1);
at91_set_deglitch(AT91_PIN_PB18, 1);
at91_sys_write(1 + PIO_IDR, 1<<18);
at91_sys_write(1 + PIO_IER, (~(1<<18)));
at91_sys_write(AT91_PMC_PCER, 1 << 3);
}
struct gpio_irq_desc
{
int irq;
unsigned long flags;
char *name;
};
static struct gpio_irq_desc PB18_IRQ={AT91_PIN_PB18,AT91_AIC_SRCTYPE_LOW,"PB18"};
static irqreturn_t PB18_intHandle(int irq, void *dev_id)
{
led_on();
return IRQ_RETVAL(IRQ_HANDLED);
}
int light_open(struct inode *inode,struct file *filp)
{
int err;
struct light_dev *dev;
dev = container_of(inode->i_cdev,struct light_dev,cdev);
filp->private_data = dev;
io_init();
err = request_irq(PB18_IRQ.irq,PB18_intHandle,PB18_IRQ.flags,PB18_IRQ.name,(void*)0);
if(err)
{
free_irq(PB18_IRQ.irq,(void*)0);
return -EBUSY;
}
return 0;
}
int light_release(struct inode *inode,struct file *filp)
{
free_irq(PB18_IRQ.irq,(void*)0);
return 0;
}
// ioctl
int light_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,
unsigned long arg)
{
struct light_dev *dev = filp->private_data;
switch(cmd)
{
case 0:
at91_set_gpio_output(AT91_PIN_PB19,0);
break;
case 1:
at91_set_gpio_output(AT91_PIN_PB19,1);
led_off();
break;
default:
return -ENOTTY;
// break;
}
return 0;
}
struct file_operations light_fops =
{
.owner = THIS_MODULE,
.ioctl = light_ioctl,
.open = light_open,
.release = light_release,
};
static void light_setup_cdev(struct light_dev *dev,int index)
{
int err,devno = MKDEV(light_major,index);
cdev_init(&dev->cdev,&light_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &light_fops;
err = cdev_add(&dev->cdev,devno,1);
if(err)
{
printk(KERN_NOTICE "Error %d adding LED%d",err,index);
}
}
int light_init(void)
{
int result;
dev_t dev = MKDEV(light_major,0);
if(light_major)
{
result = register_chrdev_region(dev,1,"PB18_IRQTest");
}
if(result < 0)
{
return result;
}
light_devp = kmalloc(sizeof(struct light_dev),GFP_KERNEL);
if(!light_devp)
{
result = - ENOMEM;
goto fail_malloc;
}
memset(light_devp,0,sizeof(struct light_dev));
light_setup_cdev(light_devp,0);
return 0;
fail_malloc:unregister_chrdev_region(dev,light_devp);
return result;
}
void light_cleanup(void)
{
cdev_del(&light_devp->cdev);
kfree(light_devp);
unregister_chrdev_region(MKDEV(light_major,0),1);
}
module_init(light_init);
module_exit(light_cleanup);
Linux上層應用程式:
#include <stdio.h>
//#include <conio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/ioctl.h>
#include <signal.h>
int main(int argc ,char *argv[])
{
int fd;
char input;
fd = open("/dev/PB18_IRQTest",O_RDWR);
if(fd < 0)
{
perror("open PB18_IRQTest device");
return 0;
}
while(1)
{
printf("input 0 to trigger int/n");
scanf("%c",&input);
switch(input)
{
case '0':
ioctl(fd,0,0);
printf("/n");
break;
case '1':
ioctl(fd,1,0);
printf("/n");
break;
default:
printf("/n");
break;
}
}
return 0;
}
由上面的代碼可以看出,Linux核心在中斷程式處理方面已經做了很多抽象,對于驅動程式編寫者隻需要按照核心中斷構架去實作一定的控制函數就可以,以後有時間會專門撰文剖析還原Linux核心裡那些對中斷實作的抽象。