上海优质网站seo有哪些,wordpress点击阅读全部,产品品牌推广公司,wordpress英文模板下载瑞芯微RK3568芯片是一款定位中高端的通用型SOC#xff0c;采用22nm制程工艺#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU#xff0c;可用于轻量级人工…瑞芯微RK3568芯片是一款定位中高端的通用型SOC采用22nm制程工艺搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。
【公众号】迅为电子
【粉丝群】824412014加群获取驱动文档例程
【视频观看】嵌入式学习之Linux驱动第五期_中断_全新升级_基于RK3568
【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板 第41章 中断下文tasklet实验
在上一个章节中我们申请GPIO中断使用的是request_irq,但是request_irq绑定的中断服务程序指的是中断上文。在之前的中断视频中讲解了中断分为俩个部分——中断上文和中断下文。本章节我们来学习中断下文的一种实现方式——tasklet。
41.1 什么是tasklet
在Linux内核中tasklet是一种特殊的软中断机制被广泛用于处理中断下文相关的任务。它是一种常见且有效的方法在多核处理系统上可以避免并发问题。Tasklet绑定的函数在同一时间只能在一个CPU上运行因此不会出现并发冲突。然而需要注意的是tasklet绑定的函数中不能调用可能导致休眠的函数否则可能引起内核异常。
在Linux内核中tasklet结构体的定义位于include/linux/interrupt.h头文件中。其原型如下
struct tasklet_struct {struct tasklet_struct *next;unsigned long state;atomic_t count;void (*func)(unsigned long);unsigned long data;
};
typedef struct tasklet_struct tasklet_t;
tasklet_struct结构体包含以下成员
next:指向下一个tasklet的指针用于形成链表结构以便内核中可以同时管理多个tasklet。state:表示tasklet的当前状态。count:用于引用计数用于确保tasklet在多个地方调度或取消调度时的正确处理。func:指向tasklet绑定的函数的指针该函数将在tasklet执行时被调用。data:传递给tasklet绑定函数的参数
此外为了方便还定义了tasklet_t类型作为struct tasklet_struct的别名。这样我们可以使用tasklet_t来声明tasklet变量而不是直接使用struct tasklet_struct。
41.2 tasklet相关接口函数
41.2.1 静态初始化函数
在Linux内核中有一个用于静态初始化tasklet的宏函数DECLARE_TASKLET。这个宏函数可以帮助我们更方便地进行tasklet的静态初始化。
宏函数的原型如下
#define DECLARE_TASKLET(name,func,data) \
struct tasklet_struct name { NULL,0,ATOMIC_INIT(0),func,data}
其中name是tasklet的名称func是tasklet的处理函数data是传递给处理函数的参数。
初始化状态为使能状态。
如果tasklet初始化函数为非使能状态使用以下宏定义
#define DECLARE_TASKLET_DISABLED(name,func,data) \
struct tasklet_struct name { NULL,0,ATOMIC_INIT(1),func,data}
其中name是tasklet的名称func是tasklet的处理函数data是传递给处理函数的参数。
初始化状态为非使能状态。
下面是一个示例展示了如何使用DECLARE_TASKLET宏函数进行tasklet的静态初始化
#include linux/interrupt.h// 定义tasklet处理函数
void my_tasklet_handler(unsigned long data)
{// Tasklet处理逻辑// ...
}// 静态初始化tasklet
DECLARE_TASKLET(my_tasklet, my_tasklet_handler, 0);
// 驱动程序的其他代码
在上述示例中my_tasklet是tasklet的名称my_tasklet_handler是tasklet的处理函数0是传递给处理函数的参数。但是需要注意的是使用DECLARE_TASKLET静态初始化的tasklet无法在运行时动态销毁因此在不需要tasklet时应该避免使用此方法。如果需要在运行时销毁tasklet应使用tasklet_init和tasklet_kill函数进行动态初始化和销毁接下来我们来学习动态初始化函数。
41.2.2 动态初始化函数 在Linux内核中可以使用tasklet_init函数对tasklet进行动态初始化。该函数原型为
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);
其中t是指向tasklet结构体的指针func是tasklet的处理函数data是传递给处理函数的参数
以下是一个示例tasklet_init函数进行动态初始化如下所示
#include linux/interrupt.h// 定义tasklet处理函数
void my_tasklet_handler(unsigned long data)
{// Tasklet处理逻辑// ...
}// 声明tasklet结构体
static struct tasklet_struct my_tasklet;// 初始化tasklet
tasklet_init(my_tasklet, my_tasklet_handler, 0);
// 驱动程序的其他代码
在示例中我们首先定义了my_tasklet_handler作为tasklet的处理函数。然后声明了一个名为my_tasklet的tasklet结构体。接下来通过调用tasklet_init函数进行动态初始化。
通过使用tasklet_init函数我们可以在运行时动态创建和初始化tasklet。这样我们可以根据需要灵活地管理和控制tasklet的生命周期。在不再需要tasklet时可以使用tasklet_kill函数进行销毁以释放相关资源。
41.2.3 关闭函数
在Linux内核中可以使用tasklet_disabled函数来关闭一个已经初始化的tasklet。该函数的原型如下
void tasklet_disable(struct tasklet_struct *t);
其中t是指向tasklet结构体的指针。
以下是一个示例使用tasklet_disable函数来关闭tasklet。
#include linux/interrupt.h// 定义tasklet处理函数
void my_tasklet_handler(unsigned long data)
{// Tasklet处理逻辑// ...
}
// 声明tasklet结构体
static struct tasklet_struct my_tasklet;
// 初始化tasklet
tasklet_init(my_tasklet, my_tasklet_handler, 0);
// 关闭tasklet
tasklet_disable(my_tasklet);
// 驱动程序的其他代码
在上述示例中我们首先定义了my_tasklet_handler作为tasklet的处理函数。然后声明了一个名为my_tasklet的tasklet结构体并使用tasklet_init函数对其进行初始化。最后通过调用tasklet_disable函数我们关闭了my_tasklet。
关闭tasklet后即使调用tasklet_schedule函数触发tasklettasklet的处理函数也不会再被执行。这可以用于临时暂停或停止tasklet的执行直到再次启用通过调用tasklet_enable函数。
需要注意的是关闭tasklet并不会销毁tasklet结构体因此可以随时通过调用tasklet_enable函数重新启用tasklet或者调用tasklet_kill函数来销毁tasklet。
41.2.4 使能函数
在Linux内核中可以使用tasklet_enable函数来使能启用一个已经初始化的tasklet。该函数的原型如下
void tasklet_disable(struct tasklet_struct *t);
其中t是指向tasklet结构体的指针。
以下是一个示例展示如何使用tasklet_enable函数来使能tasklet
#include linux/interrupt.h
// 定义tasklet处理函数
void my_tasklet_handler(unsigned long data)
{// Tasklet处理逻辑// ...
}// 声明tasklet结构体
static struct tasklet_struct my_tasklet;// 初始化tasklet
tasklet_init(my_tasklet, my_tasklet_handler, 0);// 使能tasklet
tasklet_enable(my_tasklet);
// 驱动程序的其他代码
在上述示例中我们首先定义了my_tasklet_handler作为tasklet的处理函数。然后声明了一个名为my_tasklet的tasklet结构体并使用tasklet_init函数对其进行初始化。最后通过调用tasklet_enable函数我们使能启用了my_tasklet。
使能tasklet后如果调用tasklet_schedule函数触发tasklet则tasklet的处理函数将会被执行。这样tasklet将开始按计划执行其处理逻辑。
需要注意的是使能tasklet并不会自动触发tasklet的执行而是通过调用tasklet_schedule函数来触发。同时可以使用tasklet_disable函数来临时暂停或停止tasklet的执行。如果需要永久停止tasklet的执行并释放相关资源则应调用tasklet_kill函数来销毁tasklet。 41.2.5 调度函数
在Linux内核中可以使用tasklet_schedule函数来调度触发一个已经初始化的tasklet执行。该函数的原型如下
void tasklet_schedule(struct tasklet_struct *t);
其中t是指向tasklet结构体的指针。
以下是一个示例展示如何使用tasklet_schedule函数来调度tasklet执行
#include linux/interrupt.h
// 定义tasklet处理函数
void my_tasklet_handler(unsigned long data)
{ // Tasklet处理逻辑// ...
}// 声明tasklet结构体
static struct tasklet_struct my_tasklet;// 初始化tasklet
tasklet_init(my_tasklet, my_tasklet_handler, 0);
// 调度tasklet执行
tasklet_schedule(my_tasklet);
// 驱动程序的其他代码
在上述示例中我们首先定义了my_tasklet_handler作为tasklet的处理函数。然后声明了一个名为my_tasklet的tasklet结构体并使用tasklet_init函数对其进行初始化。最后通过调用tasklet_schedule函数我们调度触发了my_tasklet的执行。
需要注意的是调度tasklet只是将tasklet标记为需要执行并不会立即执行tasklet的处理函数。实际的执行时间取决于内核的调度和处理机制。
41.2.6 销毁函数
在Linux内核中可以使用tasklet_kill函数来销毁一个已经初始化的tasklet释放相关资源。该函数的原型如下
void tasklet_kill(struct tasklet_struct *t);
其中t是指向tasklet结构体的指针。
以下是一个示例展示如何使用tasklet_kill函数来销毁tasklet
#include linux/interrupt.h// 定义tasklet处理函数
void my_tasklet_handler(unsigned long data)
{ // Tasklet处理逻辑// ...
}// 声明tasklet结构体
static struct tasklet_struct my_tasklet;// 初始化tasklet
tasklet_init(my_tasklet, my_tasklet_handler, 0);
tasklet_disable(my_tasklet);// 销毁tasklet
tasklet_kill(my_tasklet);// 驱动程序的其他代码
在上述示例中我们首先定义了my_tasklet_handler作为tasklet的处理函数。然后声明了一个名为my_tasklet的tasklet结构体并使用tasklet_init函数对其进行初始化。最后通过调用tasklet_kill函数我们销毁了my_tasklet。
调用tasklet_kill函数会释放tasklet所占用的资源并将tasklet标记为无效。因此销毁后的tasklet不能再被使用。
需要注意的是在销毁tasklet之前应该确保该tasklet已经被停止通过调用tasklet_disable函数。否则销毁一个正在执行的tasklet可能导致内核崩溃或其他错误。
一旦销毁了tasklet如果需要再次使用tasklet需要重新进行初始化通过调用tasklet_init函数。在下一小节中我们将使用上述tasklet函数相关接口函数进行相应的实验。
41.3 实验程序的编写
41.3.1 驱动程序编写
本实验对应的网盘路径为iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\32_tasklet\module。
本实验将实现注册显示屏触摸中断每按当触摸LCD显示屏就会触发中断服务函数在中断服务函数中调度中断下文tasklet处理函数打印“This id test_interrupt”和“data is 1”。 在驱动程序中的模块初始化函数中我们将GPIO转换为中断号并使用request_irq函数请求中断然后对tasklet进行初始化。在中断处理函数中我们调度tasklet执行使得当中断触发时tasklet会被调度执行。在模块退出函数中我们释放中断资源并使能tasklet销毁tasklet。
编写完成的interrupt.c代码如下所示添加的代码已加粗表示。
#include linux/module.h
#include linux/init.h
#include linux/interrupt.h
#include linux/gpio.h
// #include linux/delay.hint irq;
struct tasklet_struct mytasklet;// 定义tasklet处理函数
void mytasklet_func(unsigned long data)
{printk(data is %ld\n, data);// msleep(3000);
}// 中断处理函数
irqreturn_t test_interrupt(int irq, void *args)
{printk(This id test_interrupt\n);tasklet_schedule(mytasklet); // 调度tasklet执行return IRQ_RETVAL(IRQ_HANDLED);
}
// 模块初始化函数
static int interrupt_irq_init(void)
{int ret;irq gpio_to_irq(101); // 将GPIO转换为中断号printk(irq is %d\n, irq);// 请求中断ret request_irq(irq, test_interrupt, IRQF_TRIGGER_RISING, test, NULL);if (ret 0){printk(request_irq is error\n);return -1;}// 初始化tasklettasklet_init(mytasklet, mytasklet_func, 1);return 0;
}
// 模块退出函数
static void interrupt_irq_exit(void)
{free_irq(irq, NULL);tasklet_enable(mytasklet); // 使能tasklet可选tasklet_kill(mytasklet); // 销毁taskletprintk(bye bye\n);
}module_init(interrupt_irq_init); // 指定模块的初始化函数
module_exit(interrupt_irq_exit); // 指定模块的退出函数MODULE_LICENSE(GPL); // 模块使用的许可证
MODULE_AUTHOR(topeet); // 模块的作者
41.4 运行测试
41.4.1 编译驱动程序
在上一小节中的interrupt.c代码同一目录下创建 Makefile 文件Makefile 文件内容如下所示对于Makefile的内容注释已在上图添加保存退出之后来到存放interrupt.c和Makefile文件目录下如下图图41-1所示 图 41-1
然后使用命令“make”进行驱动的编译编译完成如下图图41-2所示 图 41-2
编译完生成interrupt.ko目标文件如下图图41-3所示 至此驱动模块就编译成功了。
41.4.2 运行测试
开发板启动之后使用以下命令进行驱动模块的加载如下图图 41-4所示 insmod interrupt.ko 图 41-4
看到驱动加载之后可以看到申请的中断号113被打印了出来然后用手触摸连接的LVDS 7寸屏幕触发中断服务程序打印如下图41-5所示 图 41-5
在上图中可以看到打印中断处理函数中添加的打印“This is test_interrupt”和tasklet处理函数中添加的打印“data is 1”说明成功执行了中断下文tasklet处理函数。
最后可以使用以下命令进行驱动的卸载如下图图图 41-6所示 rmmod interrupt 之前的理论章节我们强调说tasklet函数中不能调用休眠的函数在此我们在上述驱动实验的基础上实验一下驱动文件中添加休眠函数如下图 41-7所示 图 41-7
同理进行编译驱动模块卸载掉之前的驱动模块后加载新编译的驱动模块如下图图 41-8所示 图 41-8
然后用手触摸连接的LVDS 7寸屏幕打印如下图41-9所示内核会崩溃。 图 41-9
至此中断下文tasklet实验就完成了。