瑞芯微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主板
第30章 定时器实验
在Linux内核中很多函数是基于定时器进行驱动的,所以时间管理在内核中占有非常重要的地位。本小节将对Linux中的时间管理相关知识进行学习.
30.1 Linux定期器
硬件为内核提供了一个系统定时器来计算流逝的时间(即基于未来时间点的计时方式,以当前时刻为计时开始的起点,以未来的某一时刻为计时的终点),内核只有在系统定时器的帮助下才能计算和管理时间,但是内核定时器的精度并不高,所以不能作为高精度定时器使用。并且内核定时器的运行没有周期性,到达计时终点后会自动关闭。如果要实现周期性定时,就要在定时处理函数中重新开启定时器。
Linux内核中使用timer_list 结构体表示内核定时器,该结构体定义在“内核源码/include/linux/timer.h”文件中,具体内容如下所示:
struct timer_list {
struct hlist_node entry;
unsigned long expires;/* 定时器超时时间,单位是节拍数 */
void (*function)(struct timer_list *);/* 定时处理函数 */
u32 flags;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
};
使用以下宏对timer_list结构体进行定义,_name为定义的结构体名称,_function为定时处理函数,该宏同样定义在文件 “内核源码/include/linux/timer.h”文件中,如下所示:
#define DEFINE_TIMER(_name, _function) \
struct timer_list _name = \
__TIMER_INITIALIZER(_function, 0)
例如可以使用以下代码对定时器和相应的定时处理函数进行定义
DEFINE_TIMER(timer_test,function_test);//定义一个定时器
定时器定义完成之后还需要通过一系列的 API 函数来初始化此定时器,部分函数说明如下(表30-1)所示:
函数 | 作用 |
---|---|
void add_timer(struct timer_list *timer) | 向 Linux 内核注册定时器,使用 add_timer 函数向内核注册定时器以后,定时器就会开始运行 |
int del_timer(struct timer_list * timer) | 删除一个定时器 |
int mod_timer(struct timer_list *timer,unsigned long expires) | 修改定时值,如果定时器还没有激活的话,mod_timer 函数会激活定时器 |
在使用add_timer()函数向 Linux 内核注册定时器之前,还需要设置定时时间,定时时间由timer_list结构体中的expires参数所确定,单位为节拍数,可以通过图形化界面设置系统节拍的频率,具体路径如下图(图 30-2)所示:
-> Kernel Features
-> Timer frequency ( [=y])
从上图可以看出可选的系统节拍率为 100Hz、250Hz、300Hz和 1000Hz,默认情况下选择 300Hz。
通过全局变量jiffies来记录自系统启动以来产生节拍的总数。启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值,一秒内jiffes增加的值为设置的系统节拍数,该变量定义在”内核源码/include/linux/jiffies.h”文件中(timer.h文件中已经包含,不需要重复引用),具体定义如下:
extern u64 __cacheline_aligned_in_smp jiffies_64;
extern unsigned long volatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies;
其中jiffies_64用于64位系统,而jiffies用于 32 位系统。为了方便开发,Linux 内核还提供了几个jiffies和ms、us、ns之间的转换函数,如下(表 30-2)所示:
函数 | 作用 |
---|---|
int jiffies_to_msecs(const unsigned long j) | 将 jiffies 类型的参数 j 分别转换为对应的毫秒 |
int jiffies_to_usecs(const unsigned long j) | 将 jiffies 类型的参数 j 分别转换为对应的微秒 |
u64 jiffies_to_nsecs(const unsigned long j) | 将 jiffies 类型的参数 j 分别转换为对应的纳秒 |
long msecs_to_jiffies(const unsigned int m) | 将毫秒转换为 jiffies 类型 |
long usecs_to_jiffies(const unsigned int u) | 将微秒转换为 jiffies 类型 |
unsigned long nsecs_to_jiffies(u64 n) | 将纳秒转换为 jiffies 类型 |
例如可以使用以下命令进行3秒钟的定时:
timer_test.expires = jiffies_64 +msecs_to_jiffies(3000)
至此关于Linux定时器相关的知识就讲解完成了,在下个小节中将进行相应的实验。
30.2 实验程序编写
本实验对应的网盘路径为:iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\24\module。
本实验将实现五秒钟的计时,五秒钟之后将打印“this is function test”相关字符,为了实现循环打印还需要在定时处理函数中使用mod_timer函数重新设置定时时间。
编写好的驱动程序timer_mod.c如下所示:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
static void function_test(struct timer_list *t);//定义function_test定时功能函数
DEFINE_TIMER(timer_test,function_test);//定义一个定时器
static void function_test(struct timer_list *t)
{
printk("this is function test \n");
mod_timer(&timer_test,jiffies_64 + msecs_to_jiffies(5000));//使用mod_timer函数将定时时间设置为五秒后
}
static int __init timer_mod_init(void) //驱动入口函数
{
timer_test.expires = jiffies_64 + msecs_to_jiffies(5000);//将定时时间设置为五秒后
add_timer(&timer_test);//添加一个定时器
return 0;
}
static void __exit timer_mod_exit(void) //驱动出口函数
{
del_timer(&timer_test);//删除一个定时器
printk("module exit \n");
}
module_init(timer_mod_init);
module_exit(timer_mod_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("topeet");
30.3 运行测试
30.3.1 编译驱动程序
在上一小节中的timer_mod.c代码同一目录下创建 Makefile 文件,Makefile 文件内容如下所示:
export ARCH=arm64#设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
obj-m += timer_mod.o #此处要和你的驱动源文件同名
KDIR :=/home/topeet/Linux/linux_sdk/kernel #这里是你的内核目录
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules #make操作
clean:
make -C $(KDIR) M=$(PWD) clean #make clean操作
对于Makefile的内容注释已在上图添加,保存退出之后,来到存放timer_mod.c和Makefile文件目录下,如下图()所示:
然后使用命令“make”进行驱动的编译,编译完成如下图(图30-5)所示:
编译完生成 timer_mod.ko目标文件,如下图(图 30-6)所示:
至此驱动模块就编译成功了,下面交叉编译应用程序。
30.3.2 运行测试
开发板启动之后,使用以下命令进行驱动模块的加载,如下图(图 30-7)所示:
insmod timer_mod.ko
可以看到驱动加载之后,每隔五秒钟会打印“this is function test”相关打印,证明编写的驱动程序没有问题,最后使用以下命令卸载相应的驱动,如下图(图30-8)所示:
rmmod timer_mod.ko