一、概念
内核定时器是内核用来控制在未来某个时间点(基于jiffies当前时间)调度执行某个函数的一种机制,被调度的函数是异步执行的,类似于一种“软件中断”,而且是处于非进程的上下文中,所以调度函数需要遵循以下规则:
1) 没有 current 指针、不允许访问用户空间。因为没有进程上下文,相关代码和被中断的进程没有任何联系。
2) 不能执行休眠(或可能引起休眠的函数)和调度。
3) 任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件。
内核定时器的调度函数运行过一次后就不会再被运行了(相当于自动注销),但可以通过在被调度的函数中重新调度自己来周期运行。
内核定时器的数据结构
struct timer_list {
struct list_head entry;
unsigned long expires; //表示期望定时器执行的 jiffies 值,到达该 jiffies 值时,将调用 function 函数,并传递 data 作为参数
void (*function)(unsigned long);
unsigned long data;
struct tvec_base *base;
/* ... */
};
二、按键 + 内核定时器 去抖 代码下载点击打开链接
1、定义struct timer_list buttons_timer; 2、初始化init_timer(&buttons_timer); 3、注册定时器add_timer(&buttons_timer); 4、启动定时器mod_timer(&buttons_timer, jiffies + (HZ / 10)); /*延时 1s/10=100ms */ 5、注销定时器del_timer(&buttons_timer); |
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GPH0CON 0xE0200C00
#define GPH0DAT 0xE0200C04
#define DEVICE_NAME "tqkey"
#define LEDCON 0xE0200060
#define LEDDAT 0xE0200064
volatile unsigned int *led_config;
volatile unsigned int *led_data;
volatile unsigned int *key_data;
//1、定义工作
struct work_struct *work;
//(定时器)1、定义定时器结构体
struct timer_list buttons_timer;
void work_func(struct work_struct *work)
{
/*启动定时器*/ /*延时 1s/10=100ms */
mod_timer(&buttons_timer, jiffies + (HZ / 10));
}
//(定时器)5、函数
static void buttons_timer_function(unsigned long data)
{
unsigned int key_val;
key_val = readw(key_data) & 0x1; //GPH0_0引角
if(key_val == 0) //按键按下为低电平
{
volatile unsigned short data;
data = readw(led_data);
if(data == 0)
{
data = 0xFF;
}
else
{
data = 0;
}
writel(data, led_data);
}
}
void timer_init(void)
{
//(定时器)2、初始化
init_timer(&buttons_timer);
buttons_timer.function = &buttons_timer_function;
//(定时器)3、向内核注册定时器
add_timer(&buttons_timer);
}
static irqreturn_t key_int(int irq, void *dev_id)
{
//3、提交下半部至内核默认工作队列keventd_wq
schedule_work(work);
return 0;
}
void key_hw_init(void)
{
volatile unsigned short data;
volatile unsigned int *gpio_config;
gpio_config = (volatile unsigned int *)ioremap(GPH0CON, 4);
data = readw(gpio_config); //读出原有寄存器中的值
data &= ~0x0F;
data |= 0x0F;
writew(data, gpio_config);
printk("key_hw_init!\n");
led_config = (volatile unsigned int *)ioremap(LEDCON, 4); //物理地址转换为虚拟地址
writel(0x00011000, led_config);
led_data = (volatile unsigned int *)ioremap(LEDDAT, 4);
writel(0xFF, led_data);
key_data = (volatile unsigned int *)ioremap(GPH0DAT, 4);
}
/*static long key_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return -EINVAL;
}*/
static int key_open(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations key_fops =
{
.owner = THIS_MODULE,
//.unlocked_ioctl = key_ioctl,
.open = key_open,
.release = NULL,
};
struct miscdevice key_miscdev =
{
.minor = 200,
.name = DEVICE_NAME,
.fops = &key_fops,
};
//注册函数
static int __init button_init(void)
{
int ret = 0;
misc_register(&key_miscdev);
//注册中断处理程序
ret = request_irq(IRQ_EINT0, key_int, IRQF_TRIGGER_FALLING, DEVICE_NAME, 0);
//硬件初始化
key_hw_init();
//2、工作初始化
work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
//(注:GFP_KERNEL是内核内存分配时最常用的,无内存可用时可引起休眠)
INIT_WORK(work, work_func); //创建工作,关联工作函数
//内核定时器初始化
timer_init();
return 0;
}
//注销函数
static void __exit button_exit(void)
{
misc_deregister(&key_miscdev);
//注销中断程序
free_irq(IRQ_EINT0, 0);
//注销定时器
del_timer(&buttons_timer);
}
module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jerry.Gou");
MODULE_DESCRIPTION("TQ210 button driver");
多按键中断
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GPH0CON 0xE0200C00
#define GPH0DAT 0xE0200C04
#define DEVICE_NAME "tqkey"
#define LEDCON 0xE0200060
#define LEDDAT 0xE0200064
volatile unsigned int *led_config;
volatile unsigned int *led_data;
volatile unsigned int *key_data;
//1、定义工作
struct work_struct *work;
//(定时器)1、定义定时器结构体
struct timer_list buttons_timer;
void work_func(struct work_struct *work)
{
/*启动定时器*/ /*延时 1s/10=100ms */
mod_timer(&buttons_timer, jiffies + (HZ / 10));
}
//(定时器)5、函数
static void buttons_timer_function(unsigned long data)
{
unsigned int key_val;
volatile unsigned short leddata;
key_val = readw(key_data) & 0x1; //GPH0_0 Key_1引角
if(key_val == 0) //按键按下为低电平
{
leddata = 0xFF; //点亮LED
writel(leddata, led_data);
}
key_val = readw(key_data) & 0x02; //GPH0_0 Key_2引角
if(key_val == 0) //按键按下为低电平
{
leddata = 0x00; //熄灭LED
writel(leddata, led_data);
}
}
void timer_init(void)
{
//(定时器)2、初始化
init_timer(&buttons_timer);
buttons_timer.function = &buttons_timer_function;
//(定时器)3、向内核注册定时器
add_timer(&buttons_timer);
}
static irqreturn_t key_int(int irq, void *dev_id)
{
//3、提交下半部至内核默认工作队列keventd_wq
schedule_work(work);
return 0;
}
void key_hw_init(void)
{
volatile unsigned short data;
volatile unsigned int *gpio_config;
gpio_config = (volatile unsigned int *)ioremap(GPH0CON, 4);
data = readw(gpio_config); //读出原有寄存器中的值
data &= ~0xFF;
data |= 0xFF;
writew(data, gpio_config);
printk("key_hw_init!\n");
led_config = (volatile unsigned int *)ioremap(LEDCON, 4); //物理地址转换为虚拟地址
writel(0x00011000, led_config);
led_data = (volatile unsigned int *)ioremap(LEDDAT, 4);
writel(0xFF, led_data);
key_data = (volatile unsigned int *)ioremap(GPH0DAT, 4);
}
/*static long key_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return -EINVAL;
}*/
static int key_open(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations key_fops =
{
.owner = THIS_MODULE,
//.unlocked_ioctl = key_ioctl,
.open = key_open,
.release = NULL,
};
struct miscdevice key_miscdev =
{
.minor = 200,
.name = DEVICE_NAME,
.fops = &key_fops,
};
//注册函数
static int __init button_init(void)
{
int ret = 0;
misc_register(&key_miscdev);
//注册中断处理程序
ret = request_irq(IRQ_EINT0, key_int, IRQF_TRIGGER_FALLING, DEVICE_NAME, 0);
ret = request_irq(IRQ_EINT1, key_int, IRQF_TRIGGER_FALLING, DEVICE_NAME, 0);
//硬件初始化
key_hw_init();
//2、工作初始化
work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
//(注:GFP_KERNEL是内核内存分配时最常用的,无内存可用时可引起休眠)
INIT_WORK(work, work_func); //创建工作,关联工作函数
//内核定时器初始化
timer_init();
return 0;
}
//注销函数
static void __exit button_exit(void)
{
misc_deregister(&key_miscdev);
//注销中断程序
free_irq(IRQ_EINT0, 0);
}
module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jerry.Gou");
MODULE_DESCRIPTION("TQ210 button driver");
实现一个定时器
/* 实现每隔一秒向内核log中打印一条信息 */
/*__func__ 当前所在函数名 __TIME__ 源文件编译时间,格式微“hh:mm:ss”*/
#include
#include
#include
#include
#include
static struct timer_list tm;
struct timeval oldtv;
void callback(unsigned long arg)
{
struct timeval tv;
char *strp = (char*)arg;
printk("%s: %lu, %s\n", __func__, jiffies, strp);
do_gettimeofday(&tv);
printk("%s: %ld, %ld\n", __func__,
tv.tv_sec - oldtv.tv_sec, //与上次中断间隔 s
tv.tv_usec- oldtv.tv_usec); //与上次中断间隔 ms
oldtv = tv;
tm.expires = jiffies+1*HZ;
add_timer(&tm); //重新开始计时
}
static int __init demo_init(void)
{
printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
init_timer(&tm); //初始化内核定时器
do_gettimeofday(&oldtv); //获取当前时间
tm.function= callback; //指定定时时间到后的回调函数
tm.data = (unsigned long)"hello world"; //回调函数的参数
tm.expires = jiffies+1*HZ; //定时时间
add_timer(&tm); //注册定时器
return 0;
}
static void __exit demo_exit(void)
{
printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
del_timer(&tm); //注销定时器
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jerry.Gou");
MODULE_DESCRIPTION("TQ210 button driver");