RK3568驱动指南|第四篇-高级字符设备进阶-第30章 定时器实验

瑞芯微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])

img

​ 从上图可以看出可选的系统节拍率为 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文件目录下,如下图()所示:

img

然后使用命令“make”进行驱动的编译,编译完成如下图(图30-5)所示:

img

编译完生成 timer_mod.ko目标文件,如下图(图 30-6)所示:

img

至此驱动模块就编译成功了,下面交叉编译应用程序。

30.3.2 运行测试

开发板启动之后,使用以下命令进行驱动模块的加载,如下图(图 30-7)所示:

insmod timer_mod.ko

img

可以看到驱动加载之后,每隔五秒钟会打印“this is function test”相关打印,证明编写的驱动程序没有问题,最后使用以下命令卸载相应的驱动,如下图(图30-8)所示:

rmmod timer_mod.ko

img

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值