【LINUX驱动】标准字符类设备学习(使用IMX283平台完成按键中断控制LED灯)

(一)背景介绍

自从开始的一段时间学习4412以后,就没有怎么碰过LINUX驱动了,主要是太过繁琐,想要有所突破难度很大,所以我又转而去学习了JAVA,Android,还有流媒体的相关知识。 但是LINUX驱动始终是避不开的东西。最近要做的项目一定需要自己写一些驱动,只能回头再学习一下了。
之前学习的时候,对于驱动是没什么概念的只知道套用那些格式,什么platform_driver ,platform_devices,misc_device,搞得我真的有点懵,但是过了大半年以后我发现,自己又有了一些新的领悟。这一次,我将使用致远科技的IMX283开发板去实现通过4个按键分别控制4个LED灯的目标。

(二)电路以及原理

在 AP-283Demo 板上有 5 个按键:KEY1 ~ KEY5,其电路原理图如图 5.6 所示。每个按键都连接到一个 GPIO。当按键被按下时,对应 GPIO 被拉低;当按键松开后,对应 GPIO恢复高电平。

在这里插入图片描述在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

通过以上的电路图我们可以得到我们需要操控的引脚 。

//LED:3.26(1) ,3.22(2) ,3.20(3), 2.7(4)
//KEY:2.6(5) ,2.5(4) ,2.4(3),1.18(2),1.17(1)  

(三)LINUX中的中断知识点

(1) 申请和释放 中断

中断是一个处理器的稀缺资源,在系统中非常重要,通过中断能够及时高效的响应外部
事件,提高系统的响应能力,增加系统吞吐量。在驱动中使用中断,其实比较简单,先申请中断号,并注册一个中断中断处理程序,在中断程序实现对外部事件的处理。
1. 申请中断
通过 request_irq()可以申请中断号,并同时安装中断处理程序。request_irq()在
</linux/interrupt.h>中声明,函数原型如下

static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);

irq
是要申请的硬件中断号。
handler
是指实际中断处理程序的函数指针。只要系统接收到中断,系统调用这个函数。
flags
设置与中断有关的一些选项。比较重要的有 SA_INTERRUPT,标明中断处理程序是快速处理程序(设置 SA_INTERRUPT)还是慢速处理程序(不设置 SA_INTERRUPT)。快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽。还有一个 SA_SHIRQ 属性,设置了以后运行多个设备共享中断,处理程序之间通过 dev 来进行区分。如果中断由某个处理程序独占,则 dev 可以设置为 NULL。
*name
传递给 request_irq 的字符串,用来在/proc/interrupts 显示中断的拥有者。使用 cat 命令查看。
*dev
在中断共享时会用到。一般设置为这个设备的 device 结构本身或者 NULL。中断处理程
序可以用 dev 找到相应的控制这个中断的设备。 在没有强制使用共享方式时,dev 可以被设置为 NULL,不过,将它指向设备的数据结构是比较好的方法。函数会将 dev 原封不动的传递给中断处理程序,因而可以很方便的用于向中断传递额外数据。

(2)释放中断

中断时系统的稀缺资源,一旦不再使用,最好将中断号释放。释放中断通过 free_irq()实现。与 request_irq()一样,free_ire()函数在</linux/ interrupt.h>中声明,其函数原型如下:
void free_irq(unsigned int irq, void *dev_id)
第一个参数是将要释放的 irq 中断号。
第二个参数标志设备。如果中断是该设备独占的,这里设置为 NULL;如果是共享中断,
需要设置为中断处理程序指针

(3)设置触发条件

中断需要设置触发条件,如上升沿中断或者下降沿中断等。Linux 提设置触发条件的接
口函数为 irq_set_irq_type(),在<linux/irq.h>定义,函数原型为:
extern int irq_set_irq_type(unsigned int irq, unsigned int type);
irq 为中断号,type 为终端类型。在<linux/irq.h>中定义了如下中断类型:

IRQ_TYPE_NONE = 0x00000000,
IRQ_TYPE_EDGE_RISING = 0x00000001,
IRQ_TYPE_EDGE_FALLING = 0x00000002,
IRQ_TYPE_EDGE_BOTH = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING),
IRQ_TYPE_LEVEL_HIGH = 0x00000004,
IRQ_TYPE_LEVEL_LOW = 0x00000008,
IRQ_TYPE_LEVEL_MASK = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH),
IRQ_TYPE_SENSE_MASK = 0x0000000f,
IRQ_TYPE_PROBE = 0x00000010,

通常情况下,一般采用边沿触发和电平触发类型,具体如何设置,还需与实际硬件匹配。

(4)中断服务程序

中断处理程序返回值 irqreturn_t,接受两个参数:中断号 irq 和 dev_id, dev_id 就是
request_irq 时传递给系统的参数 dev:
typedef irqreturn_t (*irq_handler_t)(int irq, void *dev_id);
中断处理完毕,通常返回 IRQ_HANDLED。通常,一个中断处理程序如程序清单 2.24
所示。

(四)代码思路

  1. 在init 函数中首先初始化硬件

  2. 获取设备号

  3. 分配cdev结构,初始化结构,添加到系统

  4. 创建class 设备类

  5. 创建设备结点

  6. 完成cdev 结构体中的文件操作

  7. 完成中断服务函数

  8. 完成exit函数

(五)代码

//LED:3.26(1) ,3.22(2) ,3.20(3), 2.7(4)
//KEY:2.6(5) ,2.5(4) ,2.4(3),1.18(2),1.17(1)  
//I need to use the key to control the led. Every time I press the key ,the Led will change
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>

#define DEVICE_NAME  "key_ctl_led"

struct KEY_INFO{
    int id;
    char* describe;
}KEY_GPIO[]= {
              {1*32+17,"KEY_GPIO1"},
              {1*32+18,"KEY_GPIO2"},
              {2*32+4 ,"KEY_GPIO3"},    
              {2*32+5 ,"KEY_GPIO4"},
              {2*32+6 ,"KEY_GPIO5"},
            };

struct LED_INFO{
    int id;
    char* describe;
}LED_GPIO[]= {
              {3*32+26 ,"LED_GPIO1"},
              {3*32+22 ,"LED_GPIO2"},
              {3*32+20 ,"LED_GPIO3"},
              {2*32+7  ,"LED_GPIO4"},
            };

#define KEY_GPIO_IRQ(x)  gpio_to_irq(KEY_GPIO[x].id)


const char irq_type[5]={
    IRQ_TYPE_EDGE_RISING,
    IRQ_TYPE_EDGE_FALLING,
    IRQ_TYPE_EDGE_BOTH,
    IRQ_TYPE_LEVEL_HIGH,
    IRQ_TYPE_LEVEL_LOW
};


static irqreturn_t key_irq1_handler(unsigned int irq,void *dev_id)
{
    printk(KERN_INFO DEVICE_NAME" pressed!\n");
    int data= gpio_get_value(LED_GPIO[0].id);
    
    gpio_direction_output(LED_GPIO[0].id,!data);
    module_put(THIS_MODULE);
    return 0;
}

static irqreturn_t key_irq2_handler(unsigned int irq,void *dev_id)
{
    printk(KERN_INFO DEVICE_NAME" pressed!\n");
    int data= gpio_get_value(LED_GPIO[1].id);
    
    gpio_direction_output(LED_GPIO[1].id,!data);
    module_put(THIS_MODULE);
    return 0;
}

static irqreturn_t key_irq3_handler(unsigned int irq,void *dev_id)
{
    printk(KERN_INFO DEVICE_NAME" pressed!\n");
    int data= gpio_get_value(LED_GPIO[2].id);
    
    gpio_direction_output(LED_GPIO[2].id,!data);
    module_put(THIS_MODULE);
    return 0;
}

static irqreturn_t key_irq4_handler(unsigned int irq,void *dev_id)
{
    printk(KERN_INFO DEVICE_NAME" pressed!\n");
    int data= gpio_get_value(LED_GPIO[3].id);
    
    gpio_direction_output(LED_GPIO[3].id,!data);
    module_put(THIS_MODULE);
    return 0;
}

static irqreturn_t key_irq5_handler(unsigned int irq,void *dev_id)
{
    printk(KERN_INFO DEVICE_NAME" pressed!\n");
    int data= gpio_get_value(LED_GPIO[3].id);
    gpio_direction_output(LED_GPIO[3].id,!data);
    module_put(THIS_MODULE);
    return 0;
}

void led_init()
{
    int i=0;
    for(i=0;i<4;i++){
    gpio_free(LED_GPIO[i].id);
    gpio_request(LED_GPIO[i].id, LED_GPIO[i].describe);
    gpio_direction_output(LED_GPIO[i].id, 0);
    }
}

void key_init()
{
    int i=0;
    for(i=0;i<5;i++){
    gpio_free(KEY_GPIO[i].id);
    gpio_request(KEY_GPIO[i].id, KEY_GPIO[i].describe);
    gpio_direction_input(KEY_GPIO[i].id);
    }

        if(request_irq(KEY_GPIO_IRQ(0),key_irq1_handler,IRQF_DISABLED,"key_irq1",NULL))
      {
          printk(KERN_WARNING DEVICE_NAME":can't get irq1\n");
      }
        if(request_irq(KEY_GPIO_IRQ(1),key_irq2_handler,IRQF_DISABLED,"key_irq2",NULL))
      {
          printk(KERN_WARNING DEVICE_NAME":can't get irq2\n");
      }
        if(request_irq(KEY_GPIO_IRQ(2),key_irq3_handler,IRQF_DISABLED,"key_irq3",NULL))
      {
          printk(KERN_WARNING DEVICE_NAME":can't get irq3\n");
      }
        if(request_irq(KEY_GPIO_IRQ(3),key_irq4_handler,IRQF_DISABLED,"key_irq4",NULL))
      {
          printk(KERN_WARNING DEVICE_NAME":can't get irq4\n");
      }
        if(request_irq(KEY_GPIO_IRQ(4),key_irq5_handler,IRQF_DISABLED,"key_irq5",NULL))
      {
          printk(KERN_WARNING DEVICE_NAME":can't get irq5\n");
      }

      for(i=0;i<5;i++)
      {
          set_irq_type(KEY_GPIO_IRQ(i),irq_type[1]);
          disable_irq(KEY_GPIO_IRQ(i));
          enable_irq(KEY_GPIO_IRQ(i));
      }
}


static int key_led_open(struct inode *inode,struct file *file)
{
    try_module_get(THIS_MODULE);//count how many times the module has been opened
    printk(KERN_INFO DEVICE_NAME" opened!\n");
    return 0;
}

static int key_led_release(struct inode *inode,struct file *file)
{
    printk(KERN_INFO DEVICE_NAME" closed!\n");

    return 0;
}

struct cdev *keyirq;
static dev_t devno;
static struct class *key_irq_class;

struct file_operations key_irq_fops=
{
    .owner =THIS_MODULE,
    .open =key_led_open,
    .release=key_led_release,
};





static int __init key_led_init(void)
{
    int major,minor;

    int ret;

    led_init();
    key_init();

    ret =alloc_chrdev_region(&devno,minor,1,DEVICE_NAME);//get device id
    major=MAJOR(devno);
    if(ret<0)
    {
        printk(KERN_ERR"can not get id");
        return -1;
    }

    keyirq=cdev_alloc();
    if(keyirq!=NULL)
    {
        cdev_init(keyirq,&key_irq_fops);
        keyirq->owner=THIS_MODULE;
        if(cdev_add(keyirq,devno,1)!=0){
            printk(KERN_ERR "add error !\n");
            goto error;
        }
    }else{
        printk(KERN_ERR " alloc error !\n");
        return -1;
    }

    key_irq_class=class_create(THIS_MODULE,"key_irq_class");
    if(IS_ERR(key_irq_class))
    {
    
        printk(KERN_ERR "create class error ! \n");
        return -1;
    }

    device_create(key_irq_class,NULL,devno,NULL,DEVICE_NAME);
    return 0;

error:
    unregister_chrdev_region(devno,1);
    
    return ret;
}

static void __exit key_led_exit(void)
{
    int i=0;
    for(i=0;i<4;i++){
    gpio_free(LED_GPIO[i].id);
    }
    for(i=0;i<5;i++){
    gpio_free(KEY_GPIO[i].id);
    disable_irq(KEY_GPIO_IRQ(i));
    free_irq(KEY_GPIO_IRQ(i),NULL);
    }
    cdev_del(keyirq);
    unregister_chrdev_region(devno,1);
    device_destroy(key_irq_class,devno);
    class_destroy(key_irq_class);

}

module_init(key_led_init);
module_exit(key_led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("YangYue");


Makefile:

obj-m:=key_ctl_led.o
PWD:=$(shell pwd)
KDIR:=/home/swann/IMX_283A/linux-2.6.35.3
all:
	$(MAKE) -C $(KDIR) M=$(PWD) 
clean:
	rm -rf *.ko *.order *.symvers *.cmd *.o *.mod.c *.tmp_versions .*.cmd .tmp_versions

(六)效果图

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

与光同程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值