由于项目上要用到,于是乎我要学习linux设备驱动的编写,开始的时候还比较清楚,能够对简单的GPIO控制操作实现出来,但是项目上要用到的是SPI和GPIO的输入中断来读取AD的电压值,然后就陷入到了一个庞大的设备代码阅读中去了,尤其是platform device的学习,到现在都还没有理清其中的关系,虽然搜索了很多网上的文章,但庆幸的是我有一种比着框框买鸭蛋的精神,我想要比着这些源码画一个出来。以前没有在LPC1768上使用过SPI,导致对SPI是一个完全陌生的状态,不清楚他的传输方式,这也是学习中的一个问题,也是一开始我的盲目无方向感的原因,因为这里的linux SPI设备驱动和SPI协议就是两个要学习的问题。
先来把“简单”的中断实现出来吧,其实中断处理并不简单,他是很多项目中必须要用到的东西,这里使用的S5PV210的GPH3(2)这一个GPIO来实现,查看芯片手册其对应的外部中断号为EINT26,所以在驱动中定义一个结构体来描述他如下:
struct s5pv210_gpio_key{
int pin;//引脚号
int eint;//外部中断号
int eintcfg;//外部中断使能
int inputcfg;//输入使能
};
struct s5pv210_gpio_key my_gpio_key={
.pin = S5PV210_GPH3(2),
.eintcfg = 0X0f<<4,
.inputcfg = 0<<4,
.eint = IRQ_EINT(26),
};
然后构建一个驱动的框架代码:
static int __init gpio_interrupt_init(void)
{
int err=0;
gpio_int_num = MKDEV(MY_MAJOR,MY_MINOR);
err = register_chrdev_region(gpio_int_num, 1, "GPH3_2_interrupt");
if(err < 0)
{
printk("register error, num: %d have been used!\n", gpio_int_num);
return err;
}
cdev_init(&my_cdev , &gpio_interrupt_ops);
my_cdev.owner = THIS_MODULE;
err = cdev_add(&my_cdev, gpio_int_num, 1);
if(err < 0)
{
printk("add my_cdev error!\n");
return err;
}
printk("init ok\n");
return 0;
}
static void __exit gpio_interrupt_exit(void)
{
cdev_del(&my_cdev);
unregister_chrdev_region(gpio_int_num, 1);
printk("exit ok\n");
}
module_init(gpio_interrupt_init);
module_exit(gpio_interrupt_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("galuo");
cdev_del函数来删除内核中的字符设备;使用unregister_chrdev_region来释放设备号。
定义file_operations结构体实例gpio_interrupt_ops,在用户空间操作的时候实现调用对应的功能,其定义如下:
struct file_operations gpio_interrupt_ops={
.open=gpio_interrupt_open,
.release=gpio_interrupt_close,
};
.open实现中断的请求,并且使能中断
.release实现中断的释放
int gpio_interrupt_open(struct inode *inode,struct file *file)
{
int error;
error = request_irq(my_gpio_key.eint, //中断号
gpio_keys_isr,//中断处理函数
IRQF_TRIGGER_FALLING ,//下降沿触发
"interrupt_test",
NULL);
if(error){
printk("request irq failed!\n");
return -1;
}
printk("hello irq\n");
return 0;
}
int gpio_interrupt_close(struct inode *inode,struct file *file)
{
free_irq(my_gpio_key.eint, NULL);
printk("good bye irq\n");
return 0;
}
在中断处理函数gpio_keys_isr中就可以实现中断的需求:
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
printk("this is interrupt function\n");
return IRQ_HANDLED;
}
这里中断处理函数的定义类型和返回值固定不变。