1.概念
在linux内核内部开发者设计了内核定时器,它的工作原理和硬件定时器一样,使用它不需要关注底层的实现,只需要分配定时器对象,初始化对象,启用定时器即可,当定时时间到达之后执行定时器的处理函数。
2.Linux内核定时器的频率
linux内核定时器的频率在内核的顶层目录下中.config文件指定,
指定的选项:CONFIG_HZ = 100
相当于内核节拍数数值变化1,需要10ms。(ubuntu数值增加1的时间是1ms)
3.jiffies变量
从内核启动开始,jiffies的数值就从0开始不停的增加,它记录的是内核的节拍数,增加的频率是由CONFIG_HZ指定
4.内核定时器的API
#include<linux/timer.h>
//定时器对象结构体
struct timer_list {
struct hlist_node entry;//构成链表
unsigned long expires;//设置的时间阈值 jiffies+CONFIG_HZ (定时1s)
void (*function)(struct timer_list *);//定时器处理函数的函数指针
u32 flags;//缺省,填0
};
1. 分配一个定时器对象
struct timer_list mytimer;
2.定时器对象的初始化
//定义一个定时器处理函数
void timer_function(struct timer_list *timer)
{
}
//设置时间阈值
mytimer.expires=jiffies+HZ;
//调用定时器对象初始化函数
timer_setup(&mytimer,timer_function,0);
3.将定时器对象注册进内核并启用定时器
void add_timer(struct timer_list *timer)
//通过这个函数只能启用一次定时器,当时间到了执行定时器处理函数
//再次启用定时器不能通过这个函数,否则内核会出现崩溃的现象
4.再次启用定时器
int mod_timer(struct timer_list *timer, unsigned long expires)
5.注销定时器对象
int del_timer(struct timer_list *timer)
5.实例
#include <linux/init.h>
#include <linux/module.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include<linux/timer.h>
/* myleds{
// &gpioe表示引用gpioe控制器,10表示管脚是gpioe10 0表示默认状态
led1= <&gpioe 10 0>;
led2= <&gpiof 10 0>;
led3= <&gpioe 8 0>;
};
};*/
struct device_node *node;
struct gpio_desc *desc;
//给定时器对象分配空间
struct timer_list mytimer;
//定时器处理函数
void timer_function(struct timer_list *timer)
{
gpiod_set_value(desc,!gpiod_get_value(desc));//让灯对应的引脚状态值取反
//再次启用定时器
mod_timer(timer,jiffies+HZ);
}
static int __init mycdev_init(void)
{
//通过名字获取设备树节点信息
node=of_find_node_by_name(NULL,"myleds");
if(node==NULL)
{
printk("通过路径解析设备树节点信息失败\n");
return -ENODATA;
}
printk("通过路径解析设备树节点信息成功\n");
//获取gpio编号
desc=gpiod_get_from_of_node(node,"led1",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(desc))
{
printk("申请gpio编号失败\n");
return PTR_ERR(desc);
}
printk("申请gpio编号成功\n");
//定时器对象的初始化
mytimer.expires=jiffies+HZ;//定时1s
timer_setup(&mytimer,timer_function,0);
//将定时器对象添加到内核并且启用定时器
add_timer(&mytimer);
return 0;
}
static void __exit mycdev_exit(void)
{
//将定时器对象注销
del_timer(&mytimer);
//灭灯
gpiod_set_value(desc,0);
//释放gpio编号
gpiod_put(desc);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
Linux内核中断子系统
1.Linux内核中断架构图
2.中断子系统相关API
1.解析设备树节点
通过路径、名字或者compatible都可以
2.从解析后的设备树节点中解析出软中断号
unsigned int irq_of_parse_and_map(struct device_node *dev,
int index)
功能:解析设备树节点得到软中断号
参数:
dev:设备树节点结构体指针
index:索引
返回值:成功返回中断号,失败返回0
3.注册要使用的中断
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
功能:注册中断
参数:
irq:软中断号
handler:中断处理函数函数指针 //typedef irqreturn_t (*irq_handler_t)(int, void *);
irqreturn_t irq_handler(int irqno,void *dev)
{
//中断处理函数
// 返回值:
//IRQ_NONE:表示不是这个设备中断或者没有被处理
//IRQ_HANDLED:表示中断成功被处理
}
flags:中断触发方式
IRQF_TRIGGER_RISING :上升沿触发
RQF_TRIGGER_FALLING :下降沿触发
IRQF_TRIGGER_HIGH :高电平触发
IRQF_TRIGGER_LOW :低电平触发
IRQF_SHARED :共享中断
name:注册中断时填写的名字
dev:传递给中断处理函数的参数
返回值:成功返回0,失败返回错误码
4.注销中断
void *free_irq(unsigned int irq, void *dev_id)
参数:
irq:软中断号
dev_id:向中断处理函数传递的参数
返回值:返回设备名
3.中断子系统设备树
3.1按键中断的硬件连接图