使用中断子系统实现对LED灯的控制

中断顶半部:不允许耗时操作

代码流程:

        1、基于字符设备驱动的注册(手动/自动)

        2、基于设备树文件的自定义完成(myled, myirq)

        2、基于GPIO子系统实现led的点亮(流水/测试文件控制)

        3、中断子系统操作流程

                3.1根据路径解析设备树节点信息

                3.2根据设备树节点指针解析获得软中断号

                        API: irq_of_parse_and_map

                3.3自定义中断处理函数(其内部完成对led灯的亮灭操作)

                        API: irqreturn_t irq_handler(int irqno, void *dev)

                3.4注册要使用的中断号进入内核

                        API: request_irq

                3.5注销软中断号

                        API: free_irq

注:循环注册需使用到irq_request的最后一个参数(void *),且循环注册需要循环注销

代码示例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/poll.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>

#define MAX 3

struct device_node *node;
struct device_node *node2;
struct gpio_desc *desc1; 
struct gpio_desc *desc2; 
struct gpio_desc *desc3; 
unsigned int irqno[3];
char* irqname[3] = {"key1", "key2", "key3"};
//int gpiono;

int major;
char kbuf[128]={0};

//中断处理函数
irqreturn_t irq_handler(int irqno, void *dev)
{
    int i = (int *)dev;
    switch(i)
    {
        case 0:
            gpiod_set_value(desc1, 0);
            break;
        case 1:
            gpiod_set_value(desc2, 0);
            break;
        case 2:
            gpiod_set_value(desc3, 0);
            break;
    }
    
    return IRQ_HANDLED;//表示当前中断被处理了    
}

int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *off)
{
    int ret;
    if(size>sizeof(kbuf))       //size传过来的是sizeof(ubuf),如果ubuf>kbuf,那么实际长度为kbuf
        size = sizeof(kbuf);
    ret = copy_to_user(ubuf, kbuf, size);
    if(ret)
    {
        printk("copy to user failed...\n");
        return -EIO;
    }
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *off)
{
    int ret;
    if(size>sizeof(kbuf))
        size = sizeof(kbuf);
    ret = copy_from_user(kbuf, ubuf, size);
    if(ret)
    {
        printk("copy from user failed...\n");
        return -EIO;
    }

    switch(kbuf[0])
    {
        /*
        case '0':
            gpiod_set_value(desc1, 0);
            break;
        */
        case '1':
            gpiod_set_value(desc1, 1);
            break;
        /*
        case '2':
            gpiod_set_value(desc2, 0);
            break;
        */
        case '3':
            gpiod_set_value(desc2, 1);
            break;
        /*
        case '4':
            gpiod_set_value(desc3, 0);
            break;
        */
        case '5':
            gpiod_set_value(desc3, 1);
            break;
    }
    return 0;
}

int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

struct file_operations fops={
    .open = mycdev_open,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};

static int __init mycdev_init(void)
{
    int ret,i;
    major = register_chrdev(0, "myled", &fops);
    if(major<0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功,major=%d\n", major);
    
    //根据路径解析设备树节点信息
    node = of_find_node_by_path("/myleds");
    if(node == NULL)
    {
        printk("of_find_node_by_path failed...\n");
        return -1;
    }

    //根据路径解析设备树节点信息
    node2 = of_find_node_by_path("/myirq");
    if(node2 == NULL)
    {
        printk("of_find_node_by_path failed...\n");
        return -1;
    }

/*  
    //老版
    //根据设备树结构体信息解析GPIO编号
    gpiono = of_get_named_gpio(node, "led1", 0);
    if(gpiono < 0)
    {
        printk("of_get_named_gpio failed...\n");
        return -1;
    }

    //注册GPIO编号
    ret = gpio_request(gpiono, NULL);
    if(ret)
    {
        printk("gpio_request failed...\n");
        return -1;
    }

    //设置当前GPIO方向为输出
    gpio_direction_output(gpiono, 0);

    //点灯
    gpio_set_value(gpiono, 1);

*/
    
    //根据设备树节点信息解析出软中断号
    for(i=0;i<MAX;i++)
    {
        irqno[i]=irq_of_parse_and_map(node2,i);
        if(irqno[i]==0)
        {
            printk("irq_of_parse_and_map failed...\n");
            return -ENOENT;
        }

        //注册中断号进内核
        ret=request_irq(irqno[i],irq_handler,IRQF_TRIGGER_FALLING,irqname[i],(void *)i);
        if(ret)
        {
            printk("request_irq failed...\n");
            return ret;
        }
    }

    //新版
    //led1,从设备书节点中解析和申请gpio编号,并设置方向为输出(默认低电平LOW)
    desc1 = gpiod_get_from_of_node(node, "led1", 0, GPIOD_OUT_LOW, NULL);
    if(IS_ERR(desc1))
    {
        printk("#desc1 gpiod_get_from_of_node failed...\n");
        return PTR_ERR(desc1);
    }
    
    //led2,从设备书节点中解析和申请gpio编号,并设置方向为输出(默认低电平LOW)
    desc2 = gpiod_get_from_of_node(node, "led2", 0, GPIOD_OUT_LOW, NULL);
    if(IS_ERR(desc2))
    {
        printk("#desc2 gpiod_get_from_of_node failed...\n");
        return PTR_ERR(desc2);
    }
    
    //led3,从设备书节点中解析和申请gpio编号,并设置方向为输出(默认低电平LOW)
    desc3 = gpiod_get_from_of_node(node, "led3", 0, GPIOD_OUT_LOW, NULL);
    if(IS_ERR(desc3))
    {
        printk("#desc3 gpiod_get_from_of_node failed...\n");
        return PTR_ERR(desc3);
    }

    return 0;
}

static void __exit mycdev_exit(void)
{
    int i;
    //灭灯
    //gpio_set_value(gpiono, 0);
    gpiod_set_value(desc1, 0);
    gpiod_set_value(desc2, 0);
    gpiod_set_value(desc3, 0);

    //释放申请的GPIO编号
    //gpio_free(gpiono);
    gpiod_put(desc1);
    gpiod_put(desc2);
    gpiod_put(desc3);

    //注销中断
    for(i=0;i<MAX;i++)
    {
        free_irq(irqno[i], (void *)i);
    }

    unregister_chrdev(major, "myled");

}

module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值