Linux下的中断介绍及其应用实验——按键响应

前言

本文的主要内容是介绍中断及其相关函数,再通过一个实例,即按键响应实验来帮助理解中断相关的知识,按键响应实验的内容是按键按下时在开发板上进行打印,每按一次,打印一次。


一、中断相关的概念

中断:CPU在正常运行期间,由外部或者内部引起的事件,让CPU停下当前正在运行的程序,转而去执行触发其他中断所对应的程序,这就是中断。
中断上下文:中断的存在可以极大的提高CPU的运行效率,但是中断会打断内核进程中正常的调度和运行,所以为了保证系统的实时性,中断服务程序必须足够简短,但实际应用中某些中断必须要处理大量的事物,如果这些都在中断服务中完成,则会严重降低中断的实时性,基于这个原因,Linux系统提出把中断服务程序分为两个部分,即中断上文和中断下文。
中断上文:完成尽可能少且急的任务,其显著特点是响应速度快。
中断下文:处理中断中剩余的比较耗时的任务,该中断可以被新的中断再次打断。
Linux下中断不可以进行嵌套,因为容易进入死循环。


二、中断相关的函数

1.获取中断号相关函数

获取中断号相关的函数有irq_of_parse_and_mapgpio_to_irq,其介绍分别如下。

1>.irq_of_parse_and_map

该函数定义在:/linux-4.1.15/include/linux/of_irq.h文件中。

extern unsigned int irq_of_parse_and_map(struct device_node *node, int index);

参数介绍:
node:设备节点。
index:索引号,interrupts可能包含多条中断信息,通过索引号就可以指定要获取的信息。
返回值:中断号。
irq_of_parse_and_map函数可以从interrupts属性中提取到对应的设备号。

2>.gpio_to_irq

该函数定义在:/linux-4.1.15/include/linux/gpio.h文件中。

static inline int gpio_to_irq(unsigned int gpio)
{
    return __gpio_to_irq(gpio);
}

参数介绍:
gpio:要获取的gpio编号。
返回值:gpio对应的中断号。
以上两个函数在调用时使用一个即可,区别在于irq_of_parse_and_map函数要在设备树中添加interrupts属性,而函数gpio_to_irq可以不用。

test_key{
    compatible = "led_keys";
    pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_led_keys>;
    key-gpio = <&gpio2 18 GPIO_ACTIVE_LOW>;
                
    interrupt-parent = <&gpio2>;  //使用irq_of_parse_and_map函数获取gpio编号时需要添加
    interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
};

2.申请中断函数

申请中断的函数为request_irq,该函数定义在:/linux-4.1.15/include/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)
{
    return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

参数介绍:
irq:要申请的中断号。
handler:中断处理函数,当中断发生时,执行该函数。
flags:中断标志。
name:中断名字,设置后在/proc/interrupts目录下可以查看。
dev:如果flags设置为IRQF_SHARED,dev用来区分不同的中断,一般情况下,dev设置为设备结构体,它会传给中断处理函数irq_handler_t第二个参数,本例中置为NULL。
返回值:申请成功返回0,申请失败返回负值,返回**-EBUSY**表示中断已经被申请。
flags的相关标志介绍如下表。
在这里插入图片描述

3.中断释放函数

中断使用完成以后要释放掉,如果中断是非共享的,那么释放函数会删除掉中断处理函数并且禁止中断,共享中断只有在释放最后中断处理函数时才会被禁止。
中断释放函数为free_irq,该函数定义在:/linux-4.1.15/include/linux/interrupt.h文件中。

extern void free_irq(unsigned int, void *dev);

参数介绍:
int:要释放的中断号。
dev:如果flags设置为IRQF_SHARED,dev用来区分不同的中断。
返回值:无。


三、修改设备树

由于我的开发板上没有多余的按键,因此需要通过gpio口外接一个按键开关,这个按键对应的gpio要写在设备树里面。
首先查找原理图文件或者数据手册,选定一个gpio口来外接开关,我这里选择EIM A20口。
在这里插入图片描述
然后在文件/linux-4.1.15/arch/arm/boot/dts/imx6dl-pinfunc.h中按CTRL+F查找A20,选择含有gpio的那一个即可。
在这里插入图片描述
然后在你开发板对应的设备树文件中添加如下代码。

test_key{
    compatible = "led_keys";
    pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_led_keys>;
    key-gpio = <&gpio2 18 GPIO_ACTIVE_LOW>;
                
    interrupt-parent = <&gpio2>;   //使用irq_of_parse_and_map函数获取gpio编号时需要添加
    interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
};

添加位置为。
在这里插入图片描述
添加按键开关gpio口的代码。

pinctrl_led_keys: led_keys{        //开发板上A20口对应的按键开关
      fsl,pins = <
          MX6QDL_PAD_EIM_A20__GPIO2_IO18     0x80000000
       >;
};

添加位置为。
在这里插入图片描述
其他位置不再做改动,然后编译设备树文件,将其复制到tftp文件夹中待开发板启动时使用。
这部分的内容可以参见:Linux下通过tftp烧写设备树文件并启动开发板


四、代码文件

该部分的代码以Linux中设备树下platform总线的应用为基础。

1.interrupt.c文件

#include <linux/init.h> 
#include <linux/module.h> 
#include <linux/of.h> 
#include <linux/of_address.h> 
#include <linux/platform_device.h> 
#include <linux/gpio.h> 
#include <linux/of_gpio.h> 
#include <linux/interrupt.h> 
#include <linux/of_irq.h> 

struct device_node *test_device_node;
struct property *test_node_property;

int gpio_num;
int irq;

irq_handler_t test_key(int irq, void *args)
{
	printk("test_key trigger off!\n"); //按按键打印一句话
	return IRQ_HANDLED;
}
int dts_probe(struct platform_device *pdev)
{
	int ret = 0;
	printk("dts_probe matching ok!\n");

	/*查找节点*/
	test_device_node = of_find_node_by_path("/test_key");  //在设备树节点中查找test_key这个节点
	if(test_device_node == NULL){
		printk("of_find_node_by_path is error!\n");
		return -1;
	}

	/*获得gpio编号*/
	gpio_num = of_get_named_gpio(test_device_node,"key-gpio",0);
	if(gpio_num < 0){
		printk("of_get_named_gpio is error!\n");
		return -1;
	}

	/*设置gpio的方向*/
	gpio_direction_input(gpio_num); //输入
	
	/*获得gpio中断号*/
	//irq = gpio_to_irq(gpio_num); 
	irq = irq_of_parse_and_map(test_device_node,0); //与上面这句代码的作用相同
	printk("irq is %d\n", irq);

	/*申请中断*/
	ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING, "test_key", NULL); 
	//IRQF_TRIGGER_RISING为上升沿触发,定义在/linux-4.1.15/include/linux/interrupt.h中
	if(ret < 0){
		printk("request_irq is error!\n");
		return -1;
	}
	return 0;
}

int dts_remove(struct platform_device *pdev)
{
	printk("dts_remove!\n");
	return 0;
}

const struct platform_device_id dts_idtable = {
	.name = "dts_test1"  //匹配优先级 第二
};

const struct of_device_id of_match_table_test[] = {
	{.compatible = "led_keys"},   //匹配优先级 第一
	{}
};

struct platform_driver dts_device = {
	.probe = dts_probe,
	.remove = dts_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "dts_test2", //匹配优先级 第三
		.of_match_table = of_match_table_test
	},
	.id_table = &dts_idtable
};

static int dts_driver_init(void)
{
	int ret = 0;
	ret = platform_driver_register(&dts_device);
	if(ret < 0) {
		printk("platform_driver_register error!\n");
		return ret;
	}
	printk("platform_driver_register ok!\n");
	return 0;
}
static int dts_driver_exit(void)
{
	printk("dts_driver_exit!\n");
	free_irq(irq,NULL);
	platform_driver_unregister(&dts_device);
}
module_init(dts_driver_init);
module_exit(dts_driver_exit);
MODULE_LICENSE("GPL");

2.Makefile文件

obj-m += interrupt.o
KDIR:=/linux/linux-4.1.15
PWD?=$(shell pwd)
all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	make -C $(KDIR) M=$(PWD) clean

五、运行结果

1.四脚按键开关简介

关于四脚按键开关,我查了其数据手册,重要的部分如下图所示。
在这里插入图片描述
按照电路图表可知,1,2脚连通,3,4脚连通,对应的实物如下,圈起来的是连通的。
在这里插入图片描述
当然,实际使用时最好拿万用表测一下,调到蜂鸣器档位,用万用表的两个探头将开关的管脚两两测试一下,发处蜂鸣声的即为连通的,或者电阻值为0的也是连通的。
在实际焊接时,两个连通的选其一即可,一般情况下选择对角位置的两个脚使用。一个脚接A20对应的gpio口,另一脚根据自己开发板的情况接3.3V或者GND。

2.开发板运行结果

将驱动文件发送到开发板,然后加载驱动文件。
通过如下命令在中断文件下查看刚才加载驱动所生成的新中断是否已经出现。

cat /proc/interrupts

运行结果如下图。
在这里插入图片描述
可以看到,中断号为87,中断名为“test_key”的中断已经出现了。
接着来测试按键开关的响应结果,按键每按下一次,开发板上就打印一句话。
在这里插入图片描述
CTRL+C停止后,使用如下命令再看我们这个过程中按下按键的总次数。

cat /proc/irq/87/spurious

通过截图可以看到,打印的次数与count值相同。


总结

以上就是Linux下的中断介绍及其按键响应实验的所有内容了,Linux下中断的应用还是比较多的,应该通过这个简单的例子掌握其核心,这样在以后做中断有关的实验就游刃有余了!
本文参考视频:https://www.bilibili.com/video/BV1Vy4y1B7ta?p=35https://www.bilibili.com/video/BV1Vy4y1B7ta?p=36

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux下使用DC进行ASIC设计,一般需要以下几个步骤: 1. 安装DC:首先需要从Synopsys官网下载DC的安装包,然后按照安装指南进行安装。安装完成后需要设置环境变量,将DC的bin目录加入到系统的PATH中。 2. 创建设计库:使用DC需要先创建设计库,可以使用以下命令创建: ``` > dc_shell DC> create_library <library_name> -technology <tech_name> -vendor <vendor_name> ``` 其中,`<library_name>`为设计库名称,`<tech_name>`和`<vendor_name>`为芯片工艺和芯片厂商名称,可以根据需要进行修改。 3. 导入设计:将设计文件导入到设计库中,可以使用以下命令: ``` DC> read_file <verilog_file> ``` 其中,`<verilog_file>`为Verilog格式的设计文件路径。 4. 进行逻辑综合:使用以下命令进行逻辑综合: ``` DC> compile <top_module> ``` 其中,`<top_module>`为顶层模块的名称。 5. 进行后端物理设计:逻辑综合完成后,需要进行后端物理设计,包括布局、布线和时序优化等。可以使用DC的后端工具完成这些任务,例如Floorplan、Place-and-Route和PrimeTime等。 6. 生成最终版图:后端物理设计完成后,生成最终版图,可以使用以下命令: ``` DC> write -format verilog -hierarchy -output <output_file> ``` 其中,`<output_file>`为输出版图文件的路径。 以上是使用DC进行ASIC设计的基本步骤,具体操作需要根据实际情况进行调整和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

西岸贤

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

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

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

打赏作者

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

抵扣说明:

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

余额充值