Linux系统驱动(十三)Linux内核定时器

文章目录

  • 一、内核定时器原理
  • 二、定时器API
  • 三、使用定时器让LED灯闪烁
  • 四、使用定时器对按键进行消抖

一、内核定时器原理

内核当前时间通过jiffies获取,它是内核时钟节拍数,在linux内核启动的时候,jiffies开始(按照一定频率)增加。在驱动中可以直接使用jiffies获取当前时间。

定时器每增加1走的时间由频率决定,定时器的频率可以通过make menuconfig进行选配,选配后的结果在.config文件中保存,选项是CONFIG_HZ。

linux-5.10.61内核CONFIG_HZ=100,定时器每增加1走10ms。
ubuntu的内核CONFIG_HZ=250,定时器每增加1走4ms。

二、定时器API

1.分配对象struct timer_list {struct hlist_node entry; //构成链表成员unsigned long expires; //定时器到期时间void (*function)(struct timer_list *);//定时器处理函数(定时时间到执行的函数)u32	flags; //填写为0};struct timer_list mytimer;
2.对象初始化mytimer.expires = jiffies+HZ; //定时1s钟 timer_setup(&mytimer, 定时器处理函数, 0); 
3.启动定时器void add_timer(struct timer_list *timer)//功能:启动定时器,这个定时器只会执行一次(add_timer只能调用一次,多次调用内核会崩溃)int mod_timer(struct timer_list *timer, unsigned long expires)//功能:再次启动定时器
4.删除定时器int del_timer(struct timer_list *timer)

三、使用定时器让LED灯闪烁

#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h> //设备树文件相关头文件
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include "mynode.h"const char *led[3]={"led1","led2","led3"};
int core_gpiono[3];
int expend_gpiono[3];
struct cdev *led_cdev;
struct class *led_class;
struct device *led_device;
int major = 0; //主设备号
int minor = 0;
dev_t led_dev_num;struct timer_list mytimer;//定时器int my_led_open(struct inode *inode, struct file *file){return 0;
}
int my_led_close(struct inode *inode, struct file *file){return 0;
}
long myled_ioctl(struct file *file, unsigned int cmd, unsigned long arg){return 0;
}const struct file_operations ledfops={.open=my_led_open,.release=my_led_close,.unlocked_ioctl=myled_ioctl,
};void timer_handler(struct timer_list* timer){int i;for(i=0;i<3;i++){gpio_set_value(expend_gpiono[i],!gpio_get_value(expend_gpiono[i]));}mod_timer(&mytimer,jiffies+HZ);
}static int __init mynode_init(void){struct device_node *core_node,*expend_node;int i,ret;//分配对象led_cdev = cdev_alloc();if(NULL == led_cdev){ //成功返回结构体指针,失败返回NULLpr_err("cdv_err error");return -ENOMEM;}//初始化对象:部分成员初始化cdev_init(led_cdev,&ledfops);//申请设备号:如果major为0,则动态申请,否则就静态指定if(major > 0){register_chrdev_region(MKDEV(major,minor),1,"mynode");}else if(major == 0){alloc_chrdev_region(&led_dev_num,0,1,"mynode"); major=MAJOR(led_dev_num);minor=MINOR(led_dev_num);}//注册cdev_add(led_cdev,MKDEV(major,minor),1); //自动创建设备节点led_class=class_create(THIS_MODULE,"mynode");led_device = device_create(led_class,NULL,MKDEV(major,minor),NULL,"mynode");/***gpio***///1. 获取节点//core节点core_node = of_find_node_by_path("/myleds/core_leds");if(NULL == core_node){pr_err("of_find_node_by_path error");return -EINVAL;}//expend节点expend_node = of_find_node_by_path("/myleds/expend_leds");if(NULL == expend_node){pr_err("of_find_node_by_path error");return -EINVAL;}//2.获取gpio号for(i=0;i<3;i++){//corecore_gpiono[i] = of_get_named_gpio(core_node,led[i],0);if(core_gpiono[i] < 0){pr_err("of_get_named_gpio error");return core_gpiono[i];}//expendexpend_gpiono[i] = of_get_named_gpio(expend_node,led[i],0);if(expend_gpiono[i] < 0){pr_err("of_get_named_gpio error");return expend_gpiono[i];}}//3. 申请gpiofor(i=0;i<3;i++){ret=gpio_request(core_gpiono[i],NULL);if(ret){pr_err("gpio_request error");for(i--;i>0;i--){gpio_free(core_gpiono[i]);}return ret;}}for(i=0;i<3;i++){ret=gpio_request(expend_gpiono[i],NULL);if(ret){pr_err("gpio_request error");for(i--;i>0;i--){gpio_free(expend_gpiono[i]);}for(i=0;i<3;i++){gpio_free(core_gpiono[i]);}return ret;}}//4.设置方向为输出for(i=0;i<3;i++){ret = gpio_direction_output(core_gpiono[i], 0);if (ret) {pr_err("gpio_direction_output error\n");goto err;}ret = gpio_direction_output(expend_gpiono[i], 0);if (ret) {pr_err("gpio_direction_output error\n");goto err;}}/***定时器****///2.定时器对象初始化mytimer.expires = jiffies+HZ; //定时1s钟 timer_setup(&mytimer, timer_handler, 0); //3.启动定时器add_timer(&mytimer);return 0;
err:for(i=0;i<3;i++){gpio_free(core_gpiono[i]);gpio_free(expend_gpiono[i]);}return ret;
}
static void __exit mynode_exit(void){int i;//删除定时器del_timer(&mytimer);for(i=0;i<3;i++){gpio_free(core_gpiono[i]);gpio_free(expend_gpiono[i]);}device_destroy(led_class, MKDEV(major, minor));class_destroy(led_class);cdev_del(led_cdev);unregister_chrdev_region(MKDEV(major, minor), 1);kfree(led_cdev);
}module_init(mynode_init);
module_exit(mynode_exit);
MODULE_LICENSE("GPL");

四、使用定时器对按键进行消抖

在这里插入图片描述

#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h> //设备树文件相关头文件
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/of_gpio.h>
// mykeys{
// 	interrupt-parent = <&gpiof>;
// 	interrupt = <7 0>,<8 0>,<9 0>;
// };struct device_node *key_node;
unsigned int key_gpiono[3];struct timer_list mytimer;//定时器
//7 8 9------2 3 1
unsigned int irqno[3]={0};void timer_handler(struct timer_list* timer){int i;for(i=0;i<3;i++){if(!gpio_get_value(key_gpiono[i])){switch(i){case 0:printk("key1 down ......");break;case 1:printk("key2 down ......");break;case 2:printk("key3 down ......");break;}}}
}irqreturn_t irq_handler(int irq, void *dev){mod_timer(&mytimer,jiffies+1);return IRQ_HANDLED;
}static int __init mynode_init(void){int i;/***GPIO***///1. 获取节点key_node = of_find_node_by_path("/mykeys");if(NULL == key_node){pr_err("of_find_node_by_path error");return -EINVAL;}printk("of_find_node_by_path success\n");//2.获取gpio号for(i=0;i<3;i++){//corekey_gpiono[i] = of_get_named_gpio(key_node,"keys",i);if(key_gpiono[i] < 0){pr_err("of_get_named_gpio error");return key_gpiono[i];}}printk("of_get_named_gpio success\n");//3. 申请gpio,是为了防止竞态/***中断***///1. 获取节点key_node = of_find_node_by_name(NULL,"mykeys");if(NULL == key_node){pr_err("of_find_node_by_name error");return -EINVAL;}//2.获取中断号for(i=0;i<3;i++){irqno[i] = irq_of_parse_and_map(key_node,i);if (irqno[i] == 0) {pr_err("irq_of_parse_and_map error\n");return -EAGAIN;}}//3.注册中断号for(i=0;i<3;i++){request_irq(irqno[i],irq_handler,IRQF_TRIGGER_FALLING,"my_IRQ_test",(void *)irqno[i]);}/***定时器****///2.定时器对象初始化mytimer.expires = jiffies+1; //定时10ms timer_setup(&mytimer, timer_handler, 0); //3.启动定时器add_timer(&mytimer);return 0;
}
static void __exit mynode_exit(void){int i=0;//注销中断号for(i=0;i<3;i++){free_irq(irqno[i],(void *)irqno[i]);}
}module_init(mynode_init);
module_exit(mynode_exit);
MODULE_LICENSE("GPL");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397100.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】顺序结构实现:特殊完全二叉树(堆)+堆排序

二叉树 一.二叉树的顺序结构二.堆的概念及结构三.堆的实现1.堆的结构2.堆的初始化、销毁、打印、判空3.堆中的值交换4.堆顶元素5.堆向上调整算法&#xff1a;实现小堆的插入6.堆向下调整算法&#xff1a;实现小堆的删除7.堆的创建1.堆向上调整算法&#xff1a;建堆建堆的时间复…

CentOS 安装Redis

在 CentOS 安装 Redis 操作系统&#xff1a;centos-7.9.2009-Core 1. 更新系统 首先&#xff0c;确保你的系统是最新的&#xff1a; sudo yum update -y2. 安装 EPEL 仓库 Redis 可能不在默认的 CentOS 仓库中&#xff0c;因此你需要安装 EPEL&#xff08;Extra Packages f…

TCP详解及其在音视频传输中的应用

传输控制协议&#xff08;TCP&#xff0c;Transmission Control Protocol&#xff09;是互联网协议栈中至关重要的传输层协议。它提供了可靠、面向连接的数据传输服务&#xff0c;广泛应用于各种网络应用中。对于音视频传输&#xff0c;虽然TCP协议并不是最常用的传输协议&…

LVS实验——部署DR模式集群

目录 一、实验环境 二、配置 1、LVS 2、router 3、client 4、RS 三、配置策略 四、测试 1.Director服务器采用双IP桥接网络&#xff0c;一个是VPP&#xff0c;一个DIP 2.Web服务器采用和DIP相同的网段和Director连接 3.每个Web服务器配置VIP 4.每个web服务器可以出外网…

《Advanced RAG》-11-RAG查询分类和细化

总结 文章介绍了两种高级的检索增强生成&#xff08;RAG&#xff09;技术&#xff1a;自适应 RAG 和 RQ-RAG&#xff0c;以及它们在问题复杂性学习和查询细化方面的应用和优势&#xff0c;以及如何通过小型模型的训练来提高这些技术的性能。 摘要 传统 RAG 技术虽然能够减少大型…

「MyBatis」数据库相关操作2

&#x1f387;个人主页 &#x1f387;所属专栏&#xff1a;Spring &#x1f387;欢迎点赞收藏加关注哦&#xff01; #{} 和 ${} 我们前面都是采用 #{} 对参数进行赋值&#xff0c;实际上也可以用 ${} 客户端发送⼀条 SQL 给服务器后&#xff0c;大致流程如下&#xff1a; 1.…

51单片机之动态数码管显示

一、硬件介绍 LED数码管是一种由多个发光二极管&#xff08;LED&#xff09;封装在一起&#xff0c;形成“8”字型的显示器件。它广泛用于仪表、时钟、车站、家电等场合&#xff0c;用于显示数字、字母或符号。 通过控制点亮a b c d e f g dp来显示数字&#xff0c;本实验开发板…

前端八股文笔记【三】

JavaScript 基础题型 1.JS的基本数据类型有哪些 基本数据类型&#xff1a;String&#xff0c;Number&#xff0c;Boolean&#xff0c;Nndefined&#xff0c;NULL&#xff0c;Symbol&#xff0c;Bigint 引用数据类型&#xff1a;object NaN是一个数值类型&#xff0c;但不是…

十三、代理模式

文章目录 1 基本介绍2 案例2.1 Sortable 接口2.2 BubbleSort 类2.3 SortTimer 类2.4 Client 类2.5 Client 类的运行结果2.6 总结 3 各角色之间的关系3.1 角色3.1.1 Subject ( 主体 )3.1.2 RealObject ( 目标对象 )3.1.3 Proxy ( 代理 )3.1.4 Client ( 客户端 ) 3.2 类图 4 动态…

Java网络编程、TCP、UDP、Socket通信---初识版

标题 InetAddress----IP地址端口号协议&#xff08;UDP/TCP&#xff09;JAVA操作-UDP一发一收模式多发多收 JAVA操作-TCP一发一收多发多收 实现群聊功能BS架构线程池优化 InetAddress----IP地址 端口号 协议&#xff08;UDP/TCP&#xff09; JAVA操作-UDP 一发一收模式 多发多收…

React 性能优化

使用 useMemo 缓存数据 &#xff08;类似 vue 的 computed&#xff09;使用 useCallback 缓存函数异步组件 ( lazy )路由懒加载( lazy )服务器渲染 SSR用 CSS 模拟 v-show 循环渲染添加 key使用 Fragment &#xff08;空标签&#xff09;减少层级 不在JSX 中定义函数&#xff0…

一篇教会搭建ELK日志分析平台

日志分析的概述 日志分析是运维工程师解决系统故障&#xff0c;发现问题的主要手段日志主要包括系统日志、应用程序日志和安全日志系统运维和开发人员可以通过日志了解服务器软硬件信息、检查配置过程中的错误及错误发生的原因经常分析日志可以了解服务器的负荷&#xff0c;性…

使用本地大模型从论文PDF中提取结构化信息

1 安装ollama 点击前往网站 https://ollama.com/ &#xff0c;下载ollama软件&#xff0c;支持win、Mac、linux 2 下载LLM ollama软件目前支持多种大模型&#xff0c; 如阿里的&#xff08;qwen、qwen2&#xff09;、meta的(llama3、llama3.1)&#xff0c; 读者根据自己电脑…

C语言:求最大数不用数组

&#xff08;1&#xff09;题目&#xff1a; 输入一批正数用空格隔开&#xff0c;个数不限&#xff0c;输入0时结束循环&#xff0c;并且输出这批整数的最大值。 &#xff08;2&#xff09;代码&#xff1a; #include "stdio.h" int main() {int max 0; // 假设输入…

Qt——多线程

一、QThread类 如果要设计多线程程序&#xff0c;一般是从QThread继承定义一个线程类&#xff0c;并重新定义QThread的虚函数 run() &#xff0c;在函数 run() 里处理线程的事件循环。 应用程序的线程称为主线程&#xff0c;创建的其他线程称为工作线程。主线程的 start() 函数…

计算机网络408考研 2014

1 计算机网络408考研2014年真题解析_哔哩哔哩_bilibili 1 111 1 11 1

MyBatis:Maven,Git,TortoiseGit,Gradle

1&#xff0c;Maven Maven是一个非常优秀的项目管理工具&#xff0c;采用一种“约定优于配置&#xff08;CoC&#xff09;”的策略来管理项目。使用Maven不仅可以把源代码构建成可发布的项目&#xff08;包括编译、打包、测试和分发&#xff09;&#xff0c;还可以生成报告、生…

短视频SDK,支持Flutter跨平台框架,加速产品上线进程

在数字内容爆炸式增长的今天&#xff0c;短视频已成为连接用户、传递情感、展现创意的重要桥梁。为助力开发者快速融入这股潮流&#xff0c;美摄科技匠心打造了一款专为Flutter框架优化的短视频SDK解决方案&#xff0c;旨在降低技术门槛&#xff0c;加速产品迭代&#xff0c;让…

主题与分区

主题和分区是Kafka的两个核心概念&#xff0c;分区的划分不仅为Kafka提供了可伸缩性、水平扩展的功能&#xff0c;还通过多副本机制来为Kafka提供数据冗余以提高数据可靠性。 主题创建 主题和分区都是提供给上层用户的抽象&#xff0c;而在副本层面或更加准确地说是Log层面&a…

Unity效果优化之抗锯齿

Unityde 基于HDRP渲染管线的抗锯齿处理的设置参考图&#xff1a; 前提&#xff1a;需要导入HDRP的插件包才行&#xff0c; 该参数设置能保证在PC版上抗锯齿效果非常好&#xff0c; 英文版&#xff1a;