当前位置: 首页> 文旅> 美景 > 网络营销工具及其特点_湖北网站建设企业_免费软文推广平台都有哪些_郑州计算机培训机构哪个最好

网络营销工具及其特点_湖北网站建设企业_免费软文推广平台都有哪些_郑州计算机培训机构哪个最好

时间:2025/8/12 0:26:40来源:https://blog.csdn.net/Zephyrus_2023/article/details/145755245 浏览次数:0次
网络营销工具及其特点_湖北网站建设企业_免费软文推广平台都有哪些_郑州计算机培训机构哪个最好

简介

中断这块是个重点,结合了很多之前已经讨论过的知识点,个人觉得是一个大的应用,这里把逻辑和代码都捋一下,留个记录。

驱动框架

框架部分还是Linux底层驱动中的字符设备框架,然后在此基础上添加中断部分,在中断中为了消抖,使用了定时器。同时,为了保护数据,使用了原子操作。下面就是具体实现。代码部分参考了原子的教程,特此注明一下。

字符设备驱动

字符设备驱动框架就是一入一出,非常的简单:

module_init(imx6uirq_init);
module_exit(imx6uirq_exit);

传入的参数就是个函数,框架内这样:

static int __init imx6uirq_init(void);
static void __exit imx6uirq_exit(void);

注意分别有关键字**__init以及__exit**修饰。之后开始写这入口和出口函数。

驱动入口

驱动入口就是

static int __init imx6uirq_init(void);

的实现。

框架中,驱动入口需要做5个事情,分别是

  1. 注册字符设备驱动;
  2. 初始化cdev
  3. 添加cdev
  4. 创建类
  5. 创建设备

当然,在框架中,我们需要一个结构体来描述字符设备的属性,所以框架实际还需要包含字符设备使用的结构体:

sturct imx6uirq_dev{dev_t devid;        //设备号int  major;         //主设备号,devid的高12位int minor;          //次设备号,devid的低20位struct cdev cdev;   //字符设备结构体struct class* class; //类结构体struct device* device; //设备结构体struct device_node* nd; //节点结构体
};

入口函数要做的事情,就是初始化这个结构体里的每个成员变量,让设备可以使用。之后就是一步一步实现。

注册字符设备驱动

直接上代码:

    if(imx6uirq.major){imx6uirq.devid = MKDEV(imx6uirq.major, 0);ret = register_chrdev_region(imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);} else {ret = alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);imx6uirq.major = MAJOR(imx6uirq.devid);imx6uirq.minor = MINOR(imx6uirq.devid);}if (ret < 0){goto fail_devid;}

这里处理了一下给定设备号和未给定设备号的情况。其实主设备号和次设备号可以不用处理,之前说过主设备号是devid的高12位,次设备号是低20位。一旦通过alloc_chrdev_region注册了设备号,majorminor都被隐形的确定了。这里这么写是为了分析。实际工作中不需要。

初始化cdev

这个实际就是初始化字符设备结构体成员cdev的过程。注意cdev本身也是个结构体。代码很简单,有接口函数,调用一下就可以了。

    imx6uirq.cdev.owner = THIS_MODULE;cdev_init(&imx6uirq.cdev, &imx6uirq_fops);
添加cdev

初始化完成后,需要把cdev添加到系统中,也是使用API

    ret = cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);if(ret < 0){goto fail_cdevadd;}
添加类

所谓的类,实际是内核中的一个概念,它所扮演的角色是设备的大管家,位置在内核中。class可以包含多个具体设备,因此就是设备类。

    imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);if(IS_ERR(imx6uirq.class)){ret =  PTR_ERR(imx6uirq.class);goto fail_class;}

需要访问设备的时候,必须指定类

添加设备

device用于表示一个具体的设备结构体,通常就是个硬件设备。需要创建一个具体的设备时,必须先创建好对应的类。

    imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, IMX6UIRQ_NAME);if(IS_ERR(imx6uirq.device)){ret = PTR_ERR(imx6uirq.device);goto fail_device;}

至此,一个简单的字符设备框架的入口就完成了。

字符设备出口

出口,就是实现

static void __exit imx6uirq_exit(void);

依据入口中各个模块的初始化和添加顺序,反过来就可以:

static void __exit imx6uirq_exit(void)
{device_destroy(imx6uirq.class, imx6uirq.devid);class_destroy(imx6uirq.class);cdev_del(&imx6uirq.cdev);unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);
}

中断部分

裸机里处理中断是这样:

  1. 使能中断,初始化对应的寄存器
  2. 注册中断服务函数,也就是向irqtable数值的指定标号处吸入中断服务函数
  3. 中断发生后进入IRQ中断服务函数,然后执行

设备树中断信息节点

Linux中,需要把中断先添加进设备树,然后再编写中断驱动。所以在实际写驱动之前,需要先在设备树内添加对应的中断。例如,需要使用按键KEY0的中断,需要添加:

key{#address-cells = <1>;#size-cells = <1>;compatible = "Femtomes-key";pinctrl-names = "default";pinctrl-0 = <&pinctrl_key>;key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>;interrupt-parent = <&gpio1>;interrupts = <18 IRQ_TYPE_EDGE_BOTH>;status = "okay"
};

感兴趣的可以自行查阅中断控制器对应的设备树(intc节点)。

中断添加和处理

以下的代码均是需要添加在上面的字符设备驱动框架中的。在添加设备完成后,需要初始化IO。初始化IO的过程比较复杂,写成独立的函数,然后在驱动入口调用即可。
在做处理之前,首先添加一下需要用到的宏定义:

#define KEY_NUM			1
#define KEY0VALUE		0x01
#define INVAKEY			0xFF

然后在字符设备结构体中添加:

struct irq_keydesc irqkey[KEY_NUM];
struct timer_list timer;
atomic_t keyvalue;
atomic_t releasekey;

由于可能会有多个按键,每个按键也可能需要不同的irq_keydesc来描述:

struct irq_keydesc{int gpio;		//IO编号int irqnum;		//中断号unsigned char value;	//键值char name[10];	//名称irqreturn_t(*handler)(int,void*);	//中断处理函数
}

这里注意,irq_keydesc是作为imx6uirq_dev的成员变量存在的!

添加到入口的代码:

ret = keyio_init(&imx6uirq);

函数实现:

static int keyio_init(struct imx6uirq_dev* dev)
{int ret = 0;int i = 0;/*1. 按键初始化 */dev->nd = of_find_node_by_path("/key");if(dev->nd == NULL){ret = -EINVAL;goto fail_nd;}for(i = 0; i < KEY_NUM; i++){dev->irqkey[i].gpio = of_get_named_gpio(dev->nd, "key-gpios", i); if(dev->irqkey[i].gpio < 0){printk(" Can't get key%d\r\n",i);}}for(i = 0; i < KEY_NUM ; i++){memset(dev->irqkey[i].name, 0, sizeof(dev->irqkey[i].name));sprintf(dev->irqkey[i].name, "KEY%d", i);gpio_request(imx6uirq.irqkey[i].gpio, dev->irqkey[i].name);gpio_direction_input(dev->irqkey[i].gpio);dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio); /* 获取中断号 *///dev->irqkey[i].irqnum = irq_of_parse_and_map(dev->nd, i); /* 通用获取函数 */}dev->irqkey[0].handler = key0_handler;dev->irqkey[0].value   = KEY0VALUE;/*2. 按键中断初始化 */for(i = 0; i< KEY_NUM; i++){ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,dev->irqkey[i].name, &imx6uirq);if(ret){printk("irq %d request error!\r\n", dev->irqkey[i].irqnum);goto fail_irq;}}/* 3. 初始化定时器 */init_timer(&imx6uirq.timer);imx6uirq.timer.function = timer_func;return 0;fail_irq:for(i = 0; i < KEY_NUM ;i++){gpio_free(imx6uirq.irqkey[i].gpio);}
fail_nd:return ret;
}

keyio_init中调用的key0_handler,也就是中断服务函数:

static irqreturn_t key0_handler(int irq, void* dev_id)
{
#if 0int val = 0;struct imx6uirq_dev *dev = dev_id;val = gpio_get_value(dev->irqkey[0].gpio);if(val == 0){           /* 按下 */printk("KEY0 push!\r\n");} else if(val == 1){    /**/printk("KEY0 release!\r\n");}
#endifstruct imx6uirq_dev *dev = dev_id;dev->timer.data = (volatile unsigned long)dev_id;mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));return IRQ_HANDLED;
}

keyio_init中调用的timer_func,也就是定时器服务函数:

static void timer_func(unsigned long arg)
{int val = 0;struct imx6uirq_dev* dev = (struct imx6uirq_dev*)arg;val = gpio_get_value(dev->irqkey[0].gpio);if(val == 0){           /* 按下 */atomic_set(&dev->keyvalue, dev->irqkey[0].value);printk("KEY0 push!\r\n");} else if(val == 1){    /* 释放 */atomic_set(&dev->keyvalue, 0x80 | (dev->irqkey[0].value));atomic_set(&dev->releasekey, 1);printk("KEY0 release!\r\n");}
}

这里简单描述一下逻辑。我们要实现定时器消抖,那么在按下按键的时候,定时器开始工作,延迟一段时间后(代码中是10ms)读取键值,如果还是按下,那么就认为是一次有效的按压。

关键字:网络营销工具及其特点_湖北网站建设企业_免费软文推广平台都有哪些_郑州计算机培训机构哪个最好

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: