1. 程式人生 > 其它 >十一、【核心時鐘】linux核心時鐘

十一、【核心時鐘】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、啟動核心定時器
void add_timer(struct timer_list *timer)
int mod_timer(struct timer_list *timer, unsigned long expires)---修改定時值,啟動定時器

4、刪除核心定時器

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