zoukankan      html  css  js  c++  java
  • 十二、【内核时钟】linux内核时钟

    一、什么是内核时钟

       操作系统的正常工作,需要硬件提供一下系统时钟,系统利用该时钟进行轮转调度、sleep....,这个时钟信号就叫内核时钟(系统节拍、滴答时钟)。系统节拍(内核时钟)频率越高,所能识别的时间刻度越精细,实时性好,但系统负担加重

    内核时钟的设置,要结合处理器的性能

    二、内核时钟(HZ)如何设置

    make menuconfig--->System Type

     

     

     三、jiffies

      此变量是用于记录从内核启动到当前时刻,经历了多少个系统节拍。

    jiffies在内核启动时初始化为0,然后随着系统节拍的道理,每一节拍+1.

    四、内核中时间相关的函数

    ssleep、msleep这两个函数会导致调度器启动,调度其他内核线程

    ndelay 、udelay这两个不休眠,忙等延时。

    实际应用中,忙等延时与休眠时结合使用。例如,红外接收器9ms和4.5ms的引导码检测,可以使用休眠延时;超声波模块,20us的触发信号,可以用到udelay

    五、内核定时器

    1、内核定时器结构体

    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)定义一个按键定时器
    struct timer_list key_timer;
    
    (2) 初始化
    #define init_timer(timer)\
    	init_timer_key((timer), NULL, NULL) 

    例如 :

    init_timer(&key_timer);//完成其它成员的初始化,但是核心的expires 、function、data由使用者初始化
    key_timer.expires = 
    key_timer.function = xxx_func;
    key_timer.data = 
    
    3、启动内核定时器

    (1)add_timer :启动定时器

    void add_timer(struct timer_list *timer)
    

    (2)mod_timer:修改定时器的超时时间,并启动定时器

    int mod_timer(struct timer_list *timer, unsigned long expires) 

     mod_timer()函数=    del_timer(timer); timer->expires = expires; add_timer(timer); 

    4、删除内核定时器

    一般用在驱动卸载exit函数中。

    int del_timer(struct timer_list *timer) 

    六、使用内核定时器进行按键消抖

     

    如图所示,是按键按下的电平信号的,按键按下之前是高电平,按下时,电平被拉低,但是在按下时,会有抖动,如上图;在释放按键时,同时也会有抖动。所以我们要使用内核定时器进行按键消抖。

    #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

    #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);
    }
    

      

     

     

      

      

      

      

     

  • 相关阅读:
    left、pixelLeft、posLeft的区别
    mysql null 值查询问题
    把php.exe加入系统环境变量-使用命令行可快速执行PHP命令
    《软件测试》
    《软件实现》
    《面向对象设计》
    《面向对象分析》
    《面向对象基础》
    《软件工程中的形式化方法》
    《需求工程》
  • 原文地址:https://www.cnblogs.com/yuanqiangfei/p/15730181.html
Copyright © 2011-2022 走看看