第6课.定时器
1.简介
内核通过定时器中断来跟踪时间流。
定时器中断的中断间隔由内核根据HZ的值设定,HZ是一个与体系结构有关的常数默认HZ值范围为50~1200,而对软件仿真器的HZ值是24.大多数平台每秒有100或1000次时钟中断。
每次当时钟中断发生时,内核内部计数器的值就增加一。这个计数器的值在系统引导时,被初始化为0,因此,它的值就是自上次操作系统引导以来的时钟滴答数。这个计数器是一个64位的变量(即使在32位架构上也是64位),称为"jiffies_64",但是,驱动程序开发者通常访问的是jiffies变量,它是unsigned long型的变量,要么和"jiffies_64"相同,要么仅仅是jiffes_64的低32位。通常首先使用jiffies,因为对它的访问很快,从而对64位jiffes_64值的访问并不需要在所有架构上都是原子的
2.使用jiffes计数器
该计数器和读取计数器的工具函数包含在<linux/jiffies.h>中,但是通常只需要包含<linux/sched.h>文件,后者会自动放入jiffies.h。还需要说明的是,jiffies和jiffies_64均应该被看成只读变量。包含param.h时,HZ宏始终被扩展为100。
3.常用函数
// timer_list的数据结构
struct timer_list timer;
// 初始化timer_list结构体
void init_timer(struct timer_list * timer)
// 注册定时器结构,以在当前CPU上运行
void add_timer(struct timer_list *timer)
// 修改一个已经调度的定时器结构的到期时间。它也可以替代add_timer函数使用
int mod_timer(struct timer_list *timer, unsigned long expires)
4.代码解析
Makefile
KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += buttons.o
buttons.c
static struct timer_list buttons_timer;
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
/* 10ms后启动定时器 */
irq_pd = (struct pin_desc *)dev_id;
mod_timer(&buttons_timer, jiffies+HZ/100);
return IRQ_RETVAL(IRQ_HANDLED);
}
static void buttons_timer_function(unsigned long data)
{
struct pin_desc * pindesc = irq_pd;
unsigned int pinval;
if (!pindesc)
return;
pinval = s3c2410_gpio_getpin(pindesc->pin);
if (pinval)
{
/* 松开 */
key_val = 0x80 | pindesc->key_val;
}
else
{
/* 按下 */
key_val = pindesc->key_val;
}
ev_press = 1; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
kill_fasync (&button_async, SIGIO, POLL_IN);
}
static int sixth_drv_init(void)
{
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
//buttons_timer.expires = 0;
add_timer(&buttons_timer);
major = register_chrdev(0, "sixth_drv", &sencod_drv_fops);
sixthdrv_class = class_create(THIS_MODULE, "sixth_drv");
sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */
// gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
// gpfdat = gpfcon + 1;
return 0;
}
解析: HZ:表示1s
HZ / 1000 = 1 / 100 = 10ms
buttons_test.c
int fd;
void my_signal_fun(int signum)
{
unsigned char key_val;
read(fd, &key_val, 1);
printf("key_val: 0x%x\n", key_val);
}
int main(int argc, char **argv)
{
unsigned char key_val;
int ret;
int Oflags;
//signal(SIGIO, my_signal_fun);
fd = open("/dev/buttons", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
return -1;
}
//fcntl(fd, F_SETOWN, getpid());
//Oflags = fcntl(fd, F_GETFL);
//fcntl(fd, F_SETFL, Oflags | FASYNC);
while (1)
{
ret = read(fd, &key_val, 1);
printf("key_val: 0x%x, ret = %d\n", key_val, ret);
//sleep(5);
}
return 0;
}