国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

linux驅(qū)動開發(fā) - 08_內(nèi)核定時器

這篇具有很好參考價值的文章主要介紹了linux驅(qū)動開發(fā) - 08_內(nèi)核定時器。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。


鏈接: C/C++Linux服務(wù)器開發(fā)/后臺架構(gòu)師【零聲教育】-學習視頻教程-騰訊課堂

1 Linux 時間管理和內(nèi)核定時器簡介

1.1 內(nèi)核時間管理簡介

  • Linux 內(nèi)核中有大量的函數(shù)需要時間管理,比如周期性的調(diào)度程序、延時程序、對于驅(qū)動編寫者來說最常用的定時器。

  • 硬件定時器提供時鐘源,時鐘源的頻率可以設(shè)置, 設(shè)置好以后就周期性的產(chǎn)生定時中斷,系統(tǒng)使用定時中斷來計時。中斷周期性產(chǎn)生的頻率就是系統(tǒng)頻率,也叫做節(jié)拍率(tick rate)(叫系統(tǒng)頻率),比如 1000Hz, 100Hz 等等說的就是系統(tǒng)節(jié)拍率。系統(tǒng)節(jié)拍率是可以設(shè)置的,單位是 Hz

在編譯 Linux 內(nèi)核的時候可以通過圖形化界面設(shè)置系統(tǒng)節(jié)拍率,按照如下路徑打開配置界面:

make menuconfig

-> Kernel Features
	-> Timer frequency (<choice> [=y])

選中“Timer frequency”

linux驅(qū)動開發(fā) - 08_內(nèi)核定時器

可以看出,可選的系統(tǒng)節(jié)拍率為 100Hz、 200Hz、 250Hz、 300Hz、 500Hz 和 1000Hz,默認情況下選擇 100Hz。設(shè)置好以后打開 Linux 內(nèi)核源碼根目錄下的.config 文件,在此文件中

linux驅(qū)動開發(fā) - 08_內(nèi)核定時器
CONFIG_HZ 為 100, Linux 內(nèi)核會使用 CONFIG_HZ 來設(shè)置自己的系統(tǒng)時鐘。

打開文件 include/asm-generic/param.h,有如下內(nèi)容:

6 # undef HZ
7 # define HZ CONFIG_HZ
8 # define USER_HZ 100
9 # define CLOCKS_PER_SEC (USER_HZ)

第 7 行定義了一個宏 HZ,宏 HZ 就是 CONFIG_HZ,因此 HZ=100,編寫 Linux驅(qū)動的時候會用到 HZ,因為 HZ 表示一秒的節(jié)拍數(shù),也就是頻率。

  • 系統(tǒng)節(jié)拍率默認為 100Hz 的時候都會有疑問,怎么這么小? 100Hz 是可選的節(jié)拍率里面最小的。為什么不選擇大一點的呢?這里就引出了一個問題:高節(jié)拍率和低節(jié)拍率的優(yōu)缺點:

①、高節(jié)拍率會提高系統(tǒng)時間精度,如果采用 100Hz 的節(jié)拍率,時間精度就是 10ms,采用1000Hz 的話時間精度就是 1ms,精度提高了 10 倍。高精度時鐘的好處有很多,對于那些對時間要求嚴格的函數(shù)來說,能夠以更高的精度運行,時間測量也更加準確。
②、高節(jié)拍率會導致中斷的產(chǎn)生更加頻繁,頻繁的中斷會加劇系統(tǒng)的負擔, 1000Hz 和 100Hz的系統(tǒng)節(jié)拍率相比,系統(tǒng)要花費 10 倍的“精力”去處理中斷。中斷服務(wù)函數(shù)占用處理器的時間增加,但是現(xiàn)在的處理器性能都很強大,所以采用 1000Hz 的系統(tǒng)節(jié)拍率并不會增加太大的負載壓力。根據(jù)自己的實際情況,選擇合適的系統(tǒng)節(jié)拍率,本教程我們?nèi)坎捎媚J的 100Hz 系統(tǒng)節(jié)拍率。

  • Linux 內(nèi)核使用全局變量 jiffies 來記錄系統(tǒng)從啟動以來的系統(tǒng)節(jié)拍數(shù),系統(tǒng)啟動的時候會將 jiffies 初始化為 0, jiffies 定義在文件 include/linux/jiffies.h 中,定義如下:
76 extern u64 __jiffy_data jiffies_64;
77 extern unsigned long volatile __jiffy_data jiffies;

jiffies_64 和 jiffies 其實是同一個東西, jiffies_64 用于 64 位系統(tǒng),而 jiffies 用于 32 位系統(tǒng)。為了兼容不同的硬件, jiffies 其實就是 jiffies_64 的低 32 位, jiffies_64 和 jiffies 的結(jié)構(gòu)如圖

linux驅(qū)動開發(fā) - 08_內(nèi)核定時器

訪問的是 jiffies_64 的低 32 位,使用 get_jiffies_64 這個函數(shù)可以獲取 jiffies_64 的值。在 32 位的系統(tǒng)上讀取 jiffies 的值,在 64 位的系統(tǒng)上 jiffes 和 jiffies_64表示同一個變量,因此也可以直接讀取 jiffies 的值。所以不管是 32 位的系統(tǒng)還是 64 位系統(tǒng),都可以使用 jiffies。

  • HZ 表示每秒的節(jié)拍數(shù), jiffies 表示系統(tǒng)運行的 jiffies 節(jié)拍數(shù),所以 jiffies/HZ 就是系統(tǒng)運行時間,單位為秒。

不管是 32 位還是 64 位的 jiffies,都有溢出的風險,溢出以后會重新從 0 開始計數(shù),相當于繞回來了,將這個現(xiàn)象也叫做繞回。 假如 HZ 為最大值 1000 的時候, 32 位的 jiffies 只需要 49.7 天就發(fā)生了繞回,對于 64 位的 jiffies 來說大概需要5.8 億年才能繞回

處理 32 位 jiffies 的繞回顯得尤為重要,Linux 內(nèi)核提供了幾個 API 函數(shù)來處理繞回

函數(shù) 描述
time_after(unkown, known) unkown 通常為 jiffies, known 通常是需要對比的值。
time_before(unkown, known)
time_after_eq(unkown, known)
time_before_eq(unkown, known)
  • 如果 unkown 超過 known 的話, time_after 函數(shù)返回真 ,否則返回假。
  • 果 unkown 沒有超過 known 的話 time_before 函數(shù)返回真,否則返回假。
  • time_after_eq 函數(shù)和 time_after 函數(shù)類似,只是多了判斷等于這個條件。同理, time_before_eq 函數(shù)和 time_before 函數(shù)也類似。

要判斷某段代碼執(zhí)行時間有沒有超時,此時就可以使用如下所示代碼:

1 unsigned long timeout;
2 timeout = jiffies + (2 * HZ); /* 超時的時間點 */
3 
4	/*************************************
5 	具體的代碼
6 	************************************/
7 
8	/* 判斷有沒有超時 */
9 	if(time_before(jiffies, timeout)) {
10 		/* 超時未發(fā)生 */
11 	} else {
12 		/* 超時發(fā)生 */
13 	}

timeout 就是超時時間點,比如我們要判斷代碼執(zhí)行時間是不是超過了 2 秒,那么超時時間點就是 jiffies+(2*HZ),如果 jiffies 大于 timeout 那就表示超時了,否則就是沒有超時。

通過函數(shù) time_before 來判斷 jiffies 是否小于 timeout,如果小于的話就表示沒有超時。

為了方便開發(fā), Linux 內(nèi)核提供了幾個 jiffies 和 ms、 us、 ns 之間的轉(zhuǎn)換函數(shù)

函數(shù) 描述
int jiffies_to_msecs(const unsigned long j) 將 jiffies 類型的參數(shù) j 分別轉(zhuǎn)換為對應(yīng)的毫秒、 微秒、納秒。
int jiffies_to_usecs(const unsigned long j)
u64 jiffies_to_nsecs(const unsigned long j)
long msecs_to_jiffies(const unsigned int m) 將毫秒、微秒、納秒轉(zhuǎn)換為 jiffies 類型。
long usecs_to_jiffies(const unsigned int u)
unsigned long nsecs_to_jiffies(u64 n)

1.2 內(nèi)核定時器簡介

  • 定時器是一個很常用的功能,需要周期性處理的工作都要用到定時器。

  • Linux 內(nèi)核定時器使用很簡單,只需要提供超時時間(相當于定時值)和定時處理函數(shù)即可,當超時時間到了以后設(shè)置的定時處理函數(shù)就會執(zhí)行 ,使用內(nèi)核定時器不需要做一大堆的寄存器初始化工作。

  • 要注意一點,內(nèi)核定時器并不是周期性運行的,超時以后就會自動關(guān)閉,因此如果想要實現(xiàn)周期性定時,那么就需要在定時處理函數(shù)中重新開啟定時器。

Linux 內(nèi)核使用 timer_list 結(jié)構(gòu)體表示內(nèi)核定時器, timer_list 定義在文件nclude/linux/timer.h 中,定義如下

struct timer_list {
    struct list_head entry;
    unsigned long expires; 				/* 定時器超時時間,單位是節(jié)拍數(shù) */
    struct tvec_base *base;
    
    void (*function)(unsigned long); 	/* 定時處理函數(shù) */
    unsigned long data; 				/* 要傳遞給 function 函數(shù)的參數(shù) */
    
    int slack;
};
  • 要使用內(nèi)核定時器首先要先定義一個 timer_list 變量,表示定時器, tiemr_list 結(jié)構(gòu)體的expires 成員變量表示超時時間,單位為節(jié)拍數(shù)。

  • 比如需要定義一個周期為 2 秒的定時器,那么這個定時器的超時時間就是 jiffies+(2HZ),因此 expires=jiffies+(2HZ)。

  • function 就是定時器超時以后的定時處理函數(shù),要做的工作就放到這個函數(shù)里面,需要編寫這個定時處理函數(shù)。

需要通過一系列的 API 函數(shù)來初始化此定時器,這些函數(shù)如下:

1、init_timer 函數(shù)

init_timer 函數(shù)負責初始化 timer_list 類型變量,當定義了一個 timer_list 變量以后一定要先用 init_timer 初始化一下。 init_timer 函數(shù)原型如下:

void init_timer(struct timer_list *timer)
函數(shù)參數(shù)和返回值含義如下:
timer:要初始化定時器。
返回值: 沒有返回值。
2、add_timer 函數(shù)

add_timer 函數(shù)用于向 Linux 內(nèi)核注冊定時器,使用 add_timer 函數(shù)向內(nèi)核注冊定時器以后,定時器就會開始運行,函數(shù)原型如下:

void add_timer(struct timer_list *timer)
函數(shù)參數(shù)和返回值含義如下:
timer:要注冊的定時器。
返回值: 沒有返回值。  
3、del_timer 函數(shù)

del_timer 函數(shù)用于刪除一個定時器,不管定時器有沒有被激活,都可以使用此函數(shù)刪除。在多處理器系統(tǒng)上,定時器可能會在其他的處理器上運行,因此在調(diào)用 del_timer 函數(shù)刪除定時器之前要先等待其他處理器的定時處理器函數(shù)退出。 del_timer 函數(shù)原型如下:

int del_timer(struct timer_list * timer)
函數(shù)參數(shù)和返回值含義如下:
timer:要刪除的定時器。
返回值: 0,定時器還沒被激活; 1,定時器已經(jīng)激活。  
4、del_timer_sync 函數(shù)

del_timer_sync 函數(shù)是 del_timer 函數(shù)的同步版,會等待其他處理器使用完定時器再刪除,del_timer_sync 不能使用在中斷上下文中。

del_timer_sync 函數(shù)原型如下所示:

int del_timer_sync(struct timer_list *timer)
函數(shù)參數(shù)和返回值含義如下:
timer:要刪除的定時器。
返回值: 0,定時器還沒被激活; 1,定時器已經(jīng)激活。
5、mod_timer 函數(shù)

mod_timer 函數(shù)用于修改定時值,如果定時器還沒有激活的話, mod_timer 函數(shù)會激活定時器!函數(shù)原型如下:

int mod_timer(struct timer_list *timer, unsigned long expires)
函數(shù)參數(shù)和返回值含義如下:
timer:要修改超時時間(定時值)的定時器。
expires:修改后的超時時間。
返回值: 0,調(diào)用 mod_timer 函數(shù)前定時器未被激活; 1,調(diào)用 mod_timer 函數(shù)前定時器已被激活  
  • 內(nèi)核定時器一般的使用流程
struct timer_list timer; /* 定義定時器 */

/* 定時器回調(diào)函數(shù) */
void function(unsigned long arg
{
    /*
    * 定時器處理代碼
    */

    /* 如果需要定時器周期性運行的話就使用 mod_timer
    * 函數(shù)重新設(shè)置超時值并且啟動定時器。
    */
    mod_timer(&dev->timertest, jiffies + msecs_to_jiffies(2000));
}

/* 初始化函數(shù) */
void init(void)
{
    init_timer(&timer); /* 初始化定時器 */

    timer.function = function; /* 設(shè)置定時處理函數(shù) */
    timer.expires=jffies + msecs_to_jiffies(2000);/* 超時時間 2 秒 */
    timer.data = (unsigned long)&dev; /* 將設(shè)備結(jié)構(gòu)體作為參數(shù) */

    add_timer(&timer); /* 啟動定時器 */
}

/* 退出函數(shù) */
void exit(void)
{
    del_timer(&timer); /* 刪除定時器 */
    /* 或者使用 */
    del_timer_sync(&timer);
}

1.3 Linux 內(nèi)核短延時函數(shù)

需要在內(nèi)核中實現(xiàn)短延時,尤其是在 Linux 驅(qū)動中。 Linux 內(nèi)核提供了毫秒、微秒和納秒延時函數(shù)

函數(shù) 描述
void ndelay(unsigned long nsecs) 納秒、微秒和毫秒延時函數(shù)。
void udelay(unsigned long usecs)
void mdelay(unsigned long mseces)

2 實驗程序編寫

使用內(nèi)核定時器周期性的點亮和熄滅開發(fā)板上的 LED 燈, LED 燈的閃爍周期由內(nèi)核定時器來設(shè)置,測試應(yīng)用程序可以控制內(nèi)核定時器周期。

2.1 定時器驅(qū)動程序編寫

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/timer.h>
#include <linux/jiffies.h>

#define TIMER_CNT        1
#define TIMER_NAME       "timer"

#define CLOSE_CMD         _IO(0XEF, 1)          //關(guān)閉命令
#define OPEN_CMD          _IO(0XEF, 2)          //打開命令
#define SETPERIOD_CMD     _IOW(0XEF, 3, int)    //設(shè)置周期

/* timer設(shè)備結(jié)構(gòu)體 */
struct timer_dev{
    dev_t devid;                /* 設(shè)備號 	 */
    struct cdev cdev;           /* cdev 	*/
    struct class* class;        /* 類 		*/
    struct device* device;      /* 設(shè)備 	 */
    int major;                  /* 主設(shè)備號	  */
    int minor;                  /* 次設(shè)備號   */
    struct device_node* nd;     /* 設(shè)備節(jié)點 */
    int led_gpio;               /* led所使用的GPIO編號		*/
    struct timer_list timer;    /* 定時器 */
    int timeperiod;             /*  定時周期ms */
};

struct timer_dev timerdev;     /* led設(shè)備 */

/*
 * @description		: 打開設(shè)備
 * @param - inode 	: 傳遞給驅(qū)動的inode
 * @param - filp 	: 設(shè)備文件,file結(jié)構(gòu)體有個叫做private_data的成員變量
 * 					  一般在open的時候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。
 * @return 			: 0 成功;其他 失敗
 */
static int timer_open(struct inode* inode, struct file* filp)
{
    filp->private_data = &timerdev;
    return 0;
}

/*
 * @description		: 關(guān)閉/釋放設(shè)備
 * @param - filp 	: 要關(guān)閉的設(shè)備文件(文件描述符)
 * @return 			: 0 成功;其他 失敗
 */
static int timer_release(struct inode* inode, struct file* filp)
{
    return 0;
}

static long timer_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)
{
    int ret = 0;
    int value = 0;
    struct timer_dev* dev = filp->private_data;

    switch (cmd) {
	case CLOSE_CMD:
        del_timer_sync(&dev->timer);
		break;
	case OPEN_CMD:
        mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
		break;
	case SETPERIOD_CMD:
        ret = copy_from_user(&value, (int *)arg, sizeof(int));
        if(ret < 0) {
            return -EFAULT;
        }

        dev->timeperiod = value;
        mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
		break;
	}

    return ret;
}



/* 設(shè)備操作函數(shù) */
static struct file_operations timerdev_fops = {
    .owner = THIS_MODULE,
	.open = timer_open,
    .unlocked_ioctl = timer_ioctl,
	.release = 	timer_release
};

/* 定時器處理函數(shù) */
static void timer_func(unsigned long arg)
{
    struct timer_dev *dev = (struct timer_dev*)arg;
    static int sta = 1;

    sta = !sta;
    gpio_set_value(dev->led_gpio, sta);

    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
}


/*初始化LED燈*/
int led_init(struct timer_dev* dev)
{
    int ret = 0;

    /* 1,獲取設(shè)備節(jié)點 */
    dev->nd = of_find_node_by_path("/gpioled");
    if(dev->nd == NULL) {
        ret = -EINVAL;
        goto fail_fd;
    }

    /* 2, 獲取LED所對應(yīng)的GPIO */
    dev->led_gpio = of_get_named_gpio(dev->nd, "led-gpio", 0);
    if(dev->led_gpio < 0) {
        printk("can't find led gpio\r\n");
        ret = -EINVAL;
        goto fail_gpio;
    }
    printk("led gpio num = %d\r\n", dev->led_gpio);

    /* 3,申請IO 
    ret = gpio_request(dev->led_gpio, "led-gpio");
	if (ret) {
		printk("Failed to request the led gpio\r\n");
		ret = -EINVAL;
        goto faile_request;
	}*/

    /* 4,使用IO,設(shè)置為輸出 */
    ret = gpio_direction_output(dev->led_gpio, 1);  /* 設(shè)置輸出,默認關(guān)燈 */
    if(ret < 0) {
       ret = -EINVAL;
       goto fail_gpioset;
   }

fail_gpioset:
    gpio_free(dev->led_gpio);
//faile_request:
fail_gpio:
fail_fd:
    return ret;
}

/*
 * @description	: 驅(qū)動出口函數(shù)
 * @param 		: 無
 * @return 		: 無
 */
static int __init timer_init(void)
{
    int ret = 0;

    /* 注冊字符設(shè)備驅(qū)動 */
	/* 1、創(chuàng)建設(shè)備號 */
	if (timerdev.major) {		/*  定義了設(shè)備號 */
		timerdev.devid = MKDEV(timerdev.major, 0);
		register_chrdev_region(timerdev.devid, TIMER_CNT, TIMER_NAME);
	} else {						/* 沒有定義設(shè)備號 */
		alloc_chrdev_region(&timerdev.devid, 0, TIMER_CNT, TIMER_NAME);	/* 申請設(shè)備號 */
		timerdev.major = MAJOR(timerdev.devid);	/* 獲取分配號的主設(shè)備號 */
		timerdev.minor = MINOR(timerdev.devid);	/* 獲取分配號的次設(shè)備號 */
	}
    if(ret < 0){
        goto fail_devid;
    }
	printk("timerdev major=%d,minor=%d\r\n",timerdev.major, timerdev.minor);	
	
	/* 2、初始化cdev */
	timerdev.cdev.owner = THIS_MODULE;
	cdev_init(&timerdev.cdev, &timerdev_fops);
	
	/* 3、添加一個cdev */
	cdev_add(&timerdev.cdev, timerdev.devid, TIMER_CNT);
    if (ret)
		goto fail_cdevadd;

	/* 4、創(chuàng)建類 */
	timerdev.class = class_create(THIS_MODULE, TIMER_NAME);
	if (IS_ERR(timerdev.class)) {
		return PTR_ERR(timerdev.class);
        goto fail_class;
	}

	/* 5、創(chuàng)建設(shè)備 */
	timerdev.device = device_create(timerdev.class, NULL, timerdev.devid, NULL, TIMER_NAME);
	if (IS_ERR(timerdev.device)) {
		return PTR_ERR(timerdev.device);
        goto fail_device;
	}

    /* 5、初始化LED燈 */
    led_init(&timerdev);
    if(ret < 0) {
        goto fail_ledinit;
    }

    /* 7,初始化定時器 */
    init_timer(&timerdev.timer);                    /* 初始化定時器 */

    timerdev.timeperiod = 500;                      /* 設(shè)置定時周期 */
    timerdev.timer.function = timer_func;;          /* 設(shè)置定時處理函數(shù) */
    timerdev.timer.expires = jiffies + msecs_to_jiffies(timerdev.timeperiod);   /* 超時時間 500 毫秒 */
    timerdev.timer.data = (unsigned long)&timerdev;  /* 將設(shè)備結(jié)構(gòu)體作為參數(shù) */                                        

    add_timer(&timerdev.timer);                              /* 啟動定時器 */

    return 0;

fail_ledinit:
fail_device:
    class_destroy(timerdev.class);
fail_class:
    cdev_del(&timerdev.cdev);
fail_cdevadd:
    unregister_chrdev_region(timerdev.devid, TIMER_CNT);
fail_devid:
    return ret;
}

/*
 * @description	: 驅(qū)動出口函數(shù)
 * @param 		: 無
 * @return 		: 無
 */
static void __exit timer_exit(void)
{
    /* 關(guān)燈 */
    gpio_set_value(timerdev.led_gpio, 1);

    /*  刪除定時器 */
    del_timer(&timerdev.timer);

    /* 注銷字符設(shè)備驅(qū)動 */
    cdev_del(&timerdev.cdev);
    unregister_chrdev_region(timerdev.devid, TIMER_CNT);

    device_destroy(timerdev.class, timerdev.devid);
    class_destroy(timerdev.class);

    /* 釋放IO */
    //gpio_free(timerdev.led_gpio);
}

module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kaka");
  • 函數(shù) timer_ioctl,對應(yīng)應(yīng)用程序的 ioctl 函數(shù),應(yīng)用程序調(diào)用 ioctl函數(shù)向驅(qū)動發(fā)送控制信息,此函數(shù)響應(yīng)并執(zhí)行。此函數(shù)有三個參數(shù): filp, cmd 和 arg,其中 filp是對應(yīng)的設(shè)備文件, cmd 是應(yīng)用程序發(fā)送過來的命令信息, arg 是應(yīng)用程序發(fā)送過來的參數(shù).
  • 共有三種命令 CLOSE_CMD, OPEN_CMD 和 SETPERIOD_CMD,這三個命令分別為關(guān)閉定時器、打開定時器、設(shè)置定時周期。這三個命令的左右如下:
    • CLOSE_CMD: 關(guān)閉定時器命令, 調(diào)用 del_timer_sync 函數(shù)關(guān)閉定時器。
    • OPEN_CMD:打開定時器命令,調(diào)用 mod_timer 函數(shù)打開定時器,定時周期為 timerdev 的timeperiod 成員變量,定時周期默認是 1 秒。
    • SETPERIOD_CMD:設(shè)置定時器周期命令,參數(shù) arg 就是新的定時周期,設(shè)置 timerdev 的timeperiod 成員變量為 arg 所表示定時周期指。并且使用 mod_timer 重新打開定時器,使定時器以新的周期運行。

2.2 編寫測試 APP

測試 APP 要實現(xiàn)的內(nèi)容如下:
①、運行 APP 以后提示我們輸入要測試的命令,輸入 1 表示關(guān)閉定時器、輸入 2 表示打開定時器,輸入 3 設(shè)置定時器周期。
②、如果要設(shè)置定時器周期的話,需要讓用戶輸入要設(shè)置的周期值,單位為毫秒。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
/*
 *argc:應(yīng)用程序參數(shù)個數(shù)
 *argv[]:具體的參數(shù)內(nèi)容,字符串形式 
 *./timerAPP  <filename>  
 * ./timerAPP /dev/timer
 */

#define CLOSE_CMD           _IO(0XEF, 1)           //關(guān)閉命令               
#define OPEN_CMD            _IO(0XEF, 2)          //打開命令             
#define SETPERIOD_CMD       _IOW(0xEF, 3, int)    //設(shè)置周期

int main(int argc, char *argv[])
{
    int fd, ret;
    char *filename;
    unsigned char databuf[1];
    unsigned int cmd;
    unsigned int arg;
    unsigned char str[100];

    if(argc != 2) {
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename, O_RDWR);
    if(fd < 0) {
        printf("file %s open failed!\r\n", filename);
        return -1;
    }

    /* 循環(huán)讀取 */
    while(1) {
        printf("Input CMD:");
        ret = scanf("%d", &cmd);
        if(ret !=1 ) {
            gets(str);  /* 防止卡死 */
        }

        if(cmd == 1) {          /* 關(guān)閉 */
            ioctl(fd, CLOSE_CMD, &arg);  
        } else if(cmd == 2) {   /* 打開 */
            ioctl(fd, OPEN_CMD, &arg); 
        } else if(cmd == 3) {    /* 設(shè)置周期 */
            printf("Input Timer period:");
            ret = scanf("%d", &arg);
            if(ret !=1 ) {
                gets(str);
            }
            ioctl(fd, SETPERIOD_CMD, &arg); 
        }
    }

    close(fd);

    return 0;
}

3 編譯驅(qū)動程序和測試 APP

3.1 編譯驅(qū)動程序

編寫 Makefile 文件

KERNELDIR := /home/kaka/linux/IMX6ULL/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek

CURRENT_PATH := $(shell pwd)

obj-m := timer.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
	arm-linux-gnueabihf-gcc timerAPP.c -o timerAPP

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

輸入如下命令編譯出驅(qū)動模塊文件:

make -j32

編譯成功以后就會生成一個名為“timer.ko ”的驅(qū)動模塊文件。

3.2 編譯測試 APP

arm-linux-gnueabihf-gcc timerAPP.c -o timerAPP

4 運行測試

將編譯出來的led.ko和ledApp這兩個文件拷貝到rootfs/lib/modules/4.1.15目錄中 ,輸入如下命令加載 timer.ko 驅(qū)動模塊:

depmod //第一次加載驅(qū)動的時候需要運行此命令
modprobe timer.ko //加載驅(qū)動

驅(qū)動加載成功以后會在終端中輸出一些信息

/lib/modules/4.1.15 # modprobe timer.ko
timerdev major=249,minor=0
led gpio num = 3

驅(qū)動加載成功以后如下命令來測試:

/timerApp /dev/timer

輸入上述命令以后終端提示輸入命令

/lib/modules/4.1.15 # ./timerAPP /dev/timer
Input CMD:

輸入“2”,打開定時器,此時 LED 燈就會以默認的 1 秒周期開始閃爍。在輸入“3”來設(shè)置定時周期,根據(jù)提示輸入要設(shè)置的周期值

Input CMD:3
Input Timer period:1000

輸入“1000”,表示設(shè)置定時器周期值為 1000ms,設(shè)置好以后 LED 燈就會以 1000ms 為間隔,開始閃爍。最后可以通過輸入“1”來關(guān)閉定時器,如果要卸載驅(qū)動的話輸入如下命令即可:文章來源地址http://www.zghlxwxcb.cn/news/detail-433087.html

rmmod timer.ko

到了這里,關(guān)于linux驅(qū)動開發(fā) - 08_內(nèi)核定時器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 09_Linux內(nèi)核定時器

    09_Linux內(nèi)核定時器

    目錄 Linux時間管理和內(nèi)核定時器簡介 內(nèi)核定時器簡介 Linux內(nèi)核短延時函數(shù) 定時器驅(qū)動程序編寫 編寫測試APP 運行測試? ????????學習過UCOS或FreeRTOS的同學應(yīng)該知道, UCOS或FreeRTOS是需要一個硬件定時器提供系統(tǒng)時鐘, 一般使用Systick作為系統(tǒng)時鐘源。同理 , Linux要運行 , 也是需

    2024年02月13日
    瀏覽(16)
  • Linux內(nèi)核 -高精度定時器

    高精度定時器使用示例

    2024年01月19日
    瀏覽(90)
  • Linux 內(nèi)核定時器(高級字符設(shè)備五)

    ??在 Linux 內(nèi)核中很多函數(shù)是基于定時器進行驅(qū)動的,但是內(nèi)核定時器的精度并不高,所以不能作為高精度定時器使用。并且內(nèi)核定時器的運行沒有周期性,到達計時終點后會自動關(guān)閉。如果要實現(xiàn)周期性定時,就要在定時處理函數(shù)中重新開啟定時器。 ??Linux 內(nèi)核中使用

    2024年02月08日
    瀏覽(26)
  • STM32MP157驅(qū)動開發(fā)——按鍵驅(qū)動(定時器)

    STM32MP157驅(qū)動開發(fā)——按鍵驅(qū)動(定時器)

    定時器涉及函數(shù)參考內(nèi)核源碼:includelinuxtimer.h 給定時器的各個參數(shù)賦值: 設(shè)置定時器 :主要是初始化 timer_list 結(jié)構(gòu)體,設(shè)置其中的函數(shù)、參數(shù)。 a) 向內(nèi)核添加定時器。timer-expires 表示超時時間。 b) 當超時時間到達,內(nèi)核就會調(diào)用這個函數(shù):timer-function(timer-data)。 修改定時

    2024年02月15日
    瀏覽(23)
  • 【Orangepi Zero2 全志H616】驅(qū)動舵機控制 / Linux定時器(signal、setitimer)

    【Orangepi Zero2 全志H616】驅(qū)動舵機控制 / Linux定時器(signal、setitimer)

    一、SG90舵機開發(fā) 舵機基本介紹 二、Linux定時器 signal 函數(shù) setitimer 函數(shù)原型 signal、setitimer函數(shù)API調(diào)用 三、舵機 軟件PWM實現(xiàn) 如下圖所示,最便宜的舵機sg90,常用三根或者四根接線,黃色為PWM信號控制用處: 垃圾桶項目開蓋用、智能小車的全比例轉(zhuǎn)向、攝像頭云臺、機械臂等

    2024年02月05日
    瀏覽(26)
  • lv14 內(nèi)核定時器 11

    lv14 內(nèi)核定時器 11

    硬件有一個時鐘裝置,該裝置每隔一定時間發(fā)出一個時鐘中斷( 稱為一次時鐘嘀嗒-tick ),對應(yīng)的中斷處理程序就將 全局變量jiffies_64加1 jiffies_64 是一個全局64位整型, jiffies全局變量為其低32位的全局變量, 程序中一般用jiffies HZ:可配置的宏,表示1秒鐘產(chǎn)生的時鐘中斷次數(shù)

    2024年01月22日
    瀏覽(18)
  • 【N32L40X】學習筆記08-定時器的基本定時功能-超時功能

    該函數(shù)庫的目的就是在統(tǒng)一的地方配置,將配置的不同項放置在一個結(jié)構(gòu)體內(nèi)部 使用一個枚舉來定義一個的別名 該庫就是基本定時產(chǎn)生超時中斷 bsp_time_base.h

    2024年02月16日
    瀏覽(55)
  • RK3399平臺開發(fā)系列講解(基礎(chǔ)篇)Linux 傳統(tǒng)間隔定時器

    RK3399平臺開發(fā)系列講解(基礎(chǔ)篇)Linux 傳統(tǒng)間隔定時器

    ??返回專欄總目錄 沉淀、分享、成長,讓自己和他人都能有所收獲!?? ??本篇將詳細 介紹 Linux 傳統(tǒng)間隔定時器。 Linux 的傳統(tǒng)間隔定時器設(shè)置接口是 setitimer,它可以設(shè)定在未來某個時

    2023年04月11日
    瀏覽(23)
  • 嵌入式培訓機構(gòu)四個月實訓課程筆記(完整版)-Linux ARM驅(qū)動編程第二天-ARM中斷、定時器、看門狗(物聯(lián)技術(shù)666)

    嵌入式培訓機構(gòu)四個月實訓課程筆記(完整版)-Linux ARM驅(qū)動編程第二天-ARM中斷、定時器、看門狗(物聯(lián)技術(shù)666)

    鏈接:https://pan.baidu.com/s/1E4x2TX_9SYhxM9sWfnehMg?pwd=1688 提取碼:1688 上午:中斷 ????????? 呂峰老師 下午:定時器 教學內(nèi)容: 一、中斷 ARM 中斷分為二級,分為一級中斷和二級中斷,二級中斷為子中斷,對于 ARM 來說有 50 個中斷源, 其中有 32+ ( EINT23-4 ) 23-4+1-2=50 子中斷源

    2024年02月19日
    瀏覽(26)
  • STM32-HAL-定時器(無源蜂鳴器的驅(qū)動)

    STM32-HAL-定時器(無源蜂鳴器的驅(qū)動)

    有源蜂鳴器: 有源蜂鳴器內(nèi)部有一個發(fā)聲電路,也就是“源”(震蕩源,與無源蜂鳴器的區(qū)別),只要通電就可以響。 無源蜂鳴器: 無源蜂鳴器相當于揚聲器,無源蜂鳴器直接接直流電,只在剛接觸和離開的時候發(fā)聲(利用電磁感應(yīng)現(xiàn)象,通電、斷電時推動振膜發(fā)聲),一般

    2024年02月14日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包