十二、【内核时钟】linux内核时钟
一、什么是内核时钟
操作系统的正常工作,需要硬件提供一下系统时钟,系统利用该时钟进行轮转调度、sleep....,这个时钟信号就叫内核时钟(系统节拍、滴答时钟)。系统节拍(内核时钟)频率越高,所能识别的时间刻度越精细,实时性好,但系统负担加重
内核时钟的设置,要结合处理器的性能
二、内核时钟(HZ)如何设置
make menuconfig--->System Type
三、jiffies
此变量是用于记录从内核启动到当前时刻,经历了多少个系统节拍。
jiffies在内核启动时初始化为0,然后随着系统节拍的道理,每一节拍+1.
四、内核中时间相关的函数
ssleep、msleep这两个函数会导致调度器启动,调度其他内核线程
ndelay 、udelay这两个不休眠,忙等延时。
实际应用中,忙等延时与休眠时结合使用。例如,红外接收器9ms和4.5ms的引导码检测,可以使用休眠延时;超声波模块,20us的触发信号,可以用到udelay
五、内核定时器
1、内核定时器结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | struct timer_list { /* * All fields that change during normal runtime grouped to the * same cacheline */ struct list_head entry; unsigned long expires; //定时时长 struct tvec_base *base; void (*function)(unsigned long ); //超时时执行的函数 unsigned long data; //传给定时器处理函数的参数 int slack; #ifdef CONFIG_TIMER_STATS int start_pid; void *start_site; char start_comm[16]; #endif #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif }; |
2、定时器的初始化
(1)定义一个按键定时器
1 | struct timer_list key_timer; |
(2) 初始化
1 2 | #define init_timer(timer)\ init_timer_key((timer), NULL, NULL) |
例如 :
1 2 3 4 | init_timer(&key_timer); //完成其它成员的初始化,但是核心的expires 、function、data由使用者初始化 key_timer.expires = key_timer.function = xxx_func; key_timer.data = |
3、启动内核定时器
(1)add_timer :启动定时器
1 | void add_timer( struct timer_list *timer) |
(2)mod_timer:修改定时器的超时时间,并启动定时器
1 | int mod_timer( struct timer_list *timer, unsigned long expires) |
mod_timer()函数= del_timer(timer); timer->expires = expires; add_timer(timer);
4、删除内核定时器
一般用在驱动卸载exit函数中。
1 | int del_timer( struct timer_list *timer) |
六、使用内核定时器进行按键消抖
如图所示,是按键按下的电平信号的,按键按下之前是高电平,按下时,电平被拉低,但是在按下时,会有抖动,如上图;在释放按键时,同时也会有抖动。所以我们要使用内核定时器进行按键消抖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/ioport.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/types.h> #include <linux/gpio.h> #include <cfg_type.h> #include <linux/interrupt.h> #include <linux/miscdevice.h> #include <linux/delay.h> #include <linux/wait.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/timer.h> struct key_gpio_t{ unsigned int gpionum; unsigned int irq; char irqname[20]; unsigned char keyvalue; }; static DECLARE_WAIT_QUEUE_HEAD(key_wq) ; static bool flag = false ; static struct key_gpio_t key_gpio[]= { {PAD_GPIO_A+28,IRQ_GPIO_A_START+28, "KEY2_GPIOA28" ,2}, {PAD_GPIO_B+30,IRQ_GPIO_B_START+30, "KEY3_GPIOB30" ,3}, {PAD_GPIO_B+31,IRQ_GPIO_B_START+31, "KEY4_GPIOB31" ,4}, {PAD_GPIO_B+9,IRQ_GPIO_B_START+9, "KEY6_GPIOB9" ,6}, }; static char keyvalue = 0; static struct timer_list key_timer; /* //用tasklet实现下半部 static void key_tasklet_func(unsigned long data)//下半部 { //做一些对时间要求不严格的工作 printk(KERN_INFO"key_tasklet_func\n"); flag = true; //设置flag为true wake_up_interruptible(&key_wq); //按键按下时,唤醒等待队列 } static DECLARE_TASKLET(key_tasklet, key_tasklet_func, 0); */ //用工作队列实现下半部 static void key_work_func( struct work_struct *work) { printk(KERN_INFO "key_work_func\n" ); flag = true ; //设置flag为true wake_up_interruptible(&key_wq); //按键按下时,唤醒等待队列 } static DECLARE_WORK(key_work, key_work_func); static ssize_t gec6818_key_read( struct file *filp, char __user * buf, size_t size, loff_t *oft) { int ret; wait_event_interruptible(key_wq, flag); flag = false ; //唤醒一次队列后要复位flag的值 if (size !=1) { return -EINVAL; } ret = copy_to_user(buf, &keyvalue, sizeof (keyvalue)); if (ret != 0) { return (size -ret); } keyvalue=0; return size; } static irqreturn_t gec6818_key_handler( int irq, void * dev) { struct key_gpio_t keytmp=*( struct key_gpio_t *)dev; keyvalue =keytmp.keyvalue; key_timer.data=keytmp.gpionum; mod_timer(&key_timer, jiffies+(HZ/1000)*20); //20ms消抖,20ms后执行超时处理函数使用工作队列唤醒等待队列read键值。 return IRQ_HANDLED; } struct file_operations key_misc_fops= { .read = gec6818_key_read, }; static struct miscdevice key_misc={ .minor = MISC_DYNAMIC_MINOR, .name = "key_misc" , .fops = &key_misc_fops, }; static void key_timer_func(unsigned long arg) { if (!gpio_get_value(arg)) //当检测到低电平的时候调度工作队列,唤醒等待队列,read keyvalue,释放按键高电平时不唤醒,我们读的时按键按下,按键释放时不读 { schedule_work(&key_work); } } static int __init gec6818_key_init( void ) { int ret,i; printk(KERN_INFO "gec6818_key_init\n" ); ret = misc_register(&key_misc); if (ret < 0) { printk(KERN_INFO "key misc register fail.\n" ); goto misc_register_err; } for (i=0;i<4;i++) { ret = request_irq(key_gpio[i].irq, gec6818_key_handler,IRQF_TRIGGER_FALLING,key_gpio[i].irqname,( void *)&key_gpio[i]); if (ret < 0) { printk(KERN_INFO "request_irq fail.\n" ); goto irq_request_err; } } init_timer(&key_timer); key_timer.function = key_timer_func; return 0; irq_request_err: while (i--) { free_irq(key_gpio[i].irq,NULL); } misc_register_err: return 0; } static void __exit gec6818_key_exit( void ) { int i; del_timer(&key_timer); printk(KERN_INFO "gec6818_key_exit\n" ); misc_deregister(&key_misc); for (i=0;i<4;i++) { free_irq(key_gpio[i].irq,( void *)&key_gpio[i]); } } module_init(gec6818_key_init); module_exit(gec6818_key_exit); MODULE_LICENSE( "GPL" ); |
main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <sys/ioctl.h> int main() { int fd,ret; char keyvalue=0; fd = open( "/dev/key_misc" ,O_RDWR); if (fd<0) { perror ( "open key_misc error!" ); } while (1) { ret=read(fd,&keyvalue,1); if (ret !=1) { perror ( "read error" ); continue ; } printf ( "keyvalue=key%d\n" ,keyvalue); } close(fd); } |
分类:
linux驱动
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2017-12-25 python中常用模块
2017-12-25 Python os.removedirs() 和shutil.rmtree() 用于删除文件夹
2017-12-25 Python os.remove() 删除文件
2017-12-25 Python os.chdir() 方法
2017-12-25 Python os.access() 方法
2017-12-25 Python replace()方法
2017-12-25 python中的os.listdir()函数