0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

i.MX6ULL時間管理和內(nèi)核定時器深入研究

玩轉(zhuǎn)單片機 ? 來源:玩轉(zhuǎn)單片機 ? 2023-11-04 10:17 ? 次閱讀

學(xué)習(xí)過 UCOSFreeRTOS 的同學(xué)應(yīng)該知道,UCOS 或 FreeRTOS 是需要一個硬件定時器提供系統(tǒng)時鐘,一般使用 Systick 作為系統(tǒng)時鐘源。

同理,Linux 要運行,也是需要一個系統(tǒng)時鐘的,至于這個系統(tǒng)時鐘是由哪個定時器提供的,有興趣的讀者可以去研究一下 Linux 內(nèi)核。不過對于Linux 驅(qū)動編寫者來說,不需要深入研究這些具體的實現(xiàn),只需要掌握相應(yīng)的 API 函數(shù)即可。

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é)拍率,按照如下路徑打開配置界面:

->KernelFeatures
->Timerfrequency([=y])

效果圖:

0ff22374-7aa6-11ee-939d-92fbcf53809c.png

在內(nèi)核文件路徑通過輸入:make menuconfig指令打開圖形化配置界面!

100beb38-7aa6-11ee-939d-92fbcf53809c.png

可以在.config文件中找到對應(yīng)的配置,在內(nèi)核文件路徑通過輸入:gedit .config指令打開:

10184338-7aa6-11ee-939d-92fbcf53809c.png

CONFIG_HZ 為 100, Linux 內(nèi)核會使用 CONFIG_HZ 來設(shè)置自己的系統(tǒng)時鐘。打開文件 include/asm-generic/param.h,有如下內(nèi)容:

#ifndef__ASM_GENERIC_PARAM_H
#define__ASM_GENERIC_PARAM_H

#include

#undefHZ
#defineHZCONFIG_HZ/*Internalkerneltimerfrequency*/
#defineUSER_HZ100/*someuserinterfacesare*/
#defineCLOCKS_PER_SEC(USER_HZ)/*in"ticks"liketimes()*/
#endif/*__ASM_GENERIC_PARAM_H*/

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

高節(jié)拍率會提高系統(tǒng)時間精度,如果采用 100Hz 的節(jié)拍率,時間精度就是 10ms,采用1000Hz 的話時間精度就是 1ms,精度提高了 10 倍。

高精度時鐘能夠以更高的精度運行,時間測量也更加準(zhǔn)確。高節(jié)拍率會導(dǎo)致中斷的產(chǎn)生更加頻繁,頻繁的中斷會加劇系統(tǒng)的負(fù)擔(dān), 1000Hz 和 100Hz的系統(tǒng)節(jié)拍率相比,系統(tǒng)要花費 10 倍的“精力”去處理中斷。

中斷服務(wù)函數(shù)占用處理器的時間增加,但是現(xiàn)在的處理器性能都很強大,所以采用 1000Hz 的系統(tǒng)節(jié)拍率并不會增加太大的負(fù)載壓力。

根據(jù)自己的實際情況,選擇合適的系統(tǒng)節(jié)拍率,本教程全部采用默認(rèn)的 100Hz 系統(tǒng)節(jié)拍率。

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

1035f522-7aa6-11ee-939d-92fbcf53809c.png

jiffies_64 和 jiffies 其實是同一個東西, jiffies_64 用于 64 位系統(tǒng),而 jiffies 用于 32 位系統(tǒng)。當(dāng)訪問 jiffies 的時候其實訪問的是 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,都有溢出的風(fēng)險,溢出以后會重新從 0 開始計數(shù),相當(dāng)于繞回來了,因此有些資料也將這個現(xiàn)象也叫做繞回。

假如 HZ 為最大值 1000 的時候,32 位的 jiffies 只需要 49.7 天就發(fā)生了繞回,對于 64 位的 jiffies 來說大概需要5.8 億年才能繞回,因此 jiffies_64 的繞回忽略不計。處理 32 位 jiffies 的繞回顯得尤為重要,Linux 內(nèi)核提供了如表 50.1.1.1 所示的幾個 API 函數(shù)來處理繞回。

1058b134-7aa6-11ee-939d-92fbcf53809c.png

可以在這個文件中找到定義:

1073b13c-7aa6-11ee-939d-92fbcf53809c.png

如果 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í)行時間有沒有超時,此時就可以使用如下所示代碼:

unsignedlongtimeout;
timeout=jiffies+(2*HZ);/*超時的時間點*/

/*************************************
具體的代碼
************************************/

/*判斷有沒有超時*/
if(time_before(jiffies,timeout))
{
/*超時未發(fā)生*/
}
else
{
/*超時發(fā)生*/
}

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

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

1097d378-7aa6-11ee-939d-92fbcf53809c.png

| 定時器

定時器是一個很常用的功能,需要周期性處理的工作都要用到定時器。定時器大體分兩類,一個是硬件定時器,一個是軟件定時器,而軟件定時器需要硬件定時器做基礎(chǔ),通過軟件的方式使用無限拓展(理論上)的軟件定時器,使用了操作系統(tǒng)后,往往是使用軟件定時器,可以不需要再對硬件定時器進(jìn)行初始化配置。

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

在使用內(nèi)核定時器的時候要注意一點,內(nèi)核定時器并不是周期性運行的,超時以后就會自動關(guān)閉,因此如果想要實現(xiàn)周期性定時,那么就需要在定時處理函數(shù)中重新開啟定時器。Linux 內(nèi)核使用 timer_list 結(jié)構(gòu)體表示內(nèi)核定時器,timer_list 定義在文件include/linux/timer.h 中,定義如下(省略掉條件編譯):

structtimer_list{
structlist_headentry;
unsignedlongexpires;
structtvec_base*base;/*定時器超時時間,單位是節(jié)拍數(shù)*/
void(*function)(unsignedlong);/*定時處理函數(shù)*/
unsignedlongdata;/*要傳遞給function函數(shù)的參數(shù)*/
intslack;
};

要使用內(nèi)核定時器首先要先定義一個 timer_list 變量,表示定時器,tiemr_list 結(jié)構(gòu)體的expires 成員變量表示超時時間,單位為節(jié)拍數(shù)。比如現(xiàn)在需要定義一個周期為 2 秒的定時器,那么這個定時器的超時時間就是 jiffies+(2*HZ),因此 expires=jiffies+(2*HZ)。function 就是定時器超時以后的定時處理函數(shù),要做的工作或處理就放到這個函數(shù)里面,需要根據(jù)需求編寫這個定時處理函數(shù)。

API函數(shù)

init_timer 函數(shù)

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

/*
timer:要初始化定時器。
返回值:沒有返回值。
*/
voidinit_timer(structtimer_list*timer)

add_timer 函數(shù)

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

/*
timer:要注冊的定時器。
返回值:沒有返回值。
*/
voidadd_timer(structtimer_list*timer)

del_timer 函數(shù)

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

/*
timer:要刪除的定時器。
返回值:0,定時器還沒被激活;1,定時器已經(jīng)激活
*/
intdel_timer(structtimer_list*timer)

del_timer_sync 函數(shù)

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

/*
timer:要刪除的定時器。
返回值:0,定時器還沒被激活;1,定時器已經(jīng)激活。
*/
intdel_timer_sync(structtimer_list*timer)

mod_timer 函數(shù)

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

/*
timer:要修改超時時間(定時值)的定時器。
expires:修改后的超時時間。
返回值:0,調(diào)用 mod_timer 函數(shù)前定時器未被激活;1,調(diào)用 mod_timer 函數(shù)前定時器已被激活。
*/
intmod_timer(structtimer_list*timer,unsignedlongexpires)

內(nèi)核定時器一般的使用流程如下所示:

structtimer_listtimer;/*定義定時器*/

/*定時器回調(diào)函數(shù)*/
voidfunction(unsignedlongarg)
{
/*
*定時器處理代碼
*/

/*如果需要定時器周期性運行的話就使用mod_timer
*函數(shù)重新設(shè)置超時值并且啟動定時器。
*/

mod_timer(&dev->timertest,jiffies+msecs_to_jiffies(2000));
}

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

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

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

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

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

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

10a6185c-7aa6-11ee-939d-92fbcf53809c.png

| LED閃爍

通過設(shè)置一個定時器來實現(xiàn)周期性的閃爍 LED 燈,通過這個案例來學(xué)習(xí)定時器的基本使用,這個實驗不需要看應(yīng)用層。

簡單使用型:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
//#include
//#include

#defineCHRDEVBASE_CNT1/*設(shè)備號個數(shù)*/
#defineCHRDEVBASE_NAME"chrdevbase"/*名字*/

/*chrdevbase設(shè)備結(jié)構(gòu)體*/
structnewchr_dev{
dev_tdevid;/*設(shè)備號*/
structcdevcdev;/*cdev*/
structclass*class;/*類*/
structdevice*device;/*設(shè)備*/
intmajor;/*主設(shè)備號*/
intminor;/*次設(shè)備號*/
structdevice_node*nd;/*設(shè)備節(jié)點*/
intled_gpio;/*led所使用的GPIO編號*/

structtimer_listtimer;/*定義一個定時器*/
};

structnewchr_devchrdevbase;/*自定義字符設(shè)備*/

/*
*@description:LED硬件初始化
*@param:無
*@return:無
*/
staticintled_hal_init(void)
{
intret=0;

/*設(shè)置LED所使用的GPIO*/
/* 1、獲取設(shè)備節(jié)點:gpioled */
chrdevbase.nd=of_find_node_by_path("/gpioled");
if(chrdevbase.nd==NULL){
printk("chrdevbasenodecantnotfound!
");
return-EINVAL;
}else{
printk("chrdevbasenodehasbeenfound!
");
}

/*2、獲取設(shè)備樹中的gpio屬性,得到LED所使用的LED編號*/
chrdevbase.led_gpio=of_get_named_gpio(chrdevbase.nd,"led-gpio",0);
if(chrdevbase.led_gpioprivate_data=&chrdevbase;/*設(shè)置私有數(shù)據(jù)*/
return0;
}

/*
*@description:從設(shè)備讀取數(shù)據(jù)
*@param-filp:要打開的設(shè)備文件(文件描述符)
*@param-buf:返回給用戶空間的數(shù)據(jù)緩沖區(qū)
*@param-cnt:要讀取的數(shù)據(jù)長度
*@param-offt:相對于文件首地址的偏移
*@return:讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗
*/
staticssize_tchrdevbase_read(structfile*filp,char__user*buf,size_tcnt,loff_t*offt)
{
printk("chrdevbaseread!
");
return0;
}

/*
*@description:向設(shè)備寫數(shù)據(jù)
*@param-filp:設(shè)備文件,表示打開的文件描述符
*@param-buf:要寫給設(shè)備寫入的數(shù)據(jù)
*@param-cnt:要寫入的數(shù)據(jù)長度
*@param-offt:相對于文件首地址的偏移
*@return:寫入的字節(jié)數(shù),如果為負(fù)值,表示寫入失敗
*/
staticssize_tchrdevbase_write(structfile*filp,constchar__user*buf,size_tcnt,loff_t*offt)
{
printk("chrdevbasewrite!
");
return0;
}

/*
*@description:關(guān)閉/釋放設(shè)備
*@param-filp:要關(guān)閉的設(shè)備文件(文件描述符)
*@return:0成功;其他失敗
*/
staticintchrdevbase_release(structinode*inode,structfile*filp)
{
printk("[BSP]release!
");
return0;
}

/*
*設(shè)備操作函數(shù)結(jié)構(gòu)體
*/
staticstructfile_operationschrdevbase_fops={
.owner=THIS_MODULE,
.open=chrdevbase_open,
.read=chrdevbase_read,
.write=chrdevbase_write,
.release=chrdevbase_release,
};

/*定時器回調(diào)函數(shù)*/
voidtimer_function(unsignedlongarg)
{
structnewchr_dev*dev=(structnewchr_dev*)arg;
staticintsta=1;

sta=!sta;/*每次都取反,實現(xiàn)LED燈反轉(zhuǎn)*/
gpio_set_value(dev->led_gpio,sta);

/*重啟定時器*/
mod_timer(&dev->timer,jiffies+msecs_to_jiffies(500));
}

/*
*@description:驅(qū)動入口函數(shù)
*@param:無
*@return:0成功;其他失敗
*/
staticint__initchrdevbase_init(void)
{
/*初始化硬件*/
led_hal_init();

/*注冊字符設(shè)備驅(qū)動*/
/*1、創(chuàng)建設(shè)備號*/
if(chrdevbase.major){/*定義了設(shè)備號*/
chrdevbase.devid=MKDEV(chrdevbase.major,0);
register_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT,CHRDEVBASE_NAME);
}else{/*沒有定義設(shè)備號*/
alloc_chrdev_region(&chrdevbase.devid,0,CHRDEVBASE_CNT,CHRDEVBASE_NAME);/*申請設(shè)備號*/
chrdevbase.major=MAJOR(chrdevbase.devid);/*獲取主設(shè)備號*/
chrdevbase.minor=MINOR(chrdevbase.devid);/*獲取次設(shè)備號*/
}
printk("newcheledmajor=%d,minor=%d
",chrdevbase.major,chrdevbase.minor);

/*2、初始化cdev*/
chrdevbase.cdev.owner=THIS_MODULE;
cdev_init(&chrdevbase.cdev,&chrdevbase_fops);

/*3、添加一個cdev*/
cdev_add(&chrdevbase.cdev,chrdevbase.devid,CHRDEVBASE_CNT);

/*4、創(chuàng)建類*/
chrdevbase.class=class_create(THIS_MODULE,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.class)){
returnPTR_ERR(chrdevbase.class);
}

/*5、創(chuàng)建設(shè)備*/
chrdevbase.device=device_create(chrdevbase.class,NULL,chrdevbase.devid,NULL,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.device)){
returnPTR_ERR(chrdevbase.device);
}

/*6、初始化timer*/
init_timer(&chrdevbase.timer);
chrdevbase.timer.function=timer_function;
chrdevbase.timer.expires=jiffies+msecs_to_jiffies(500);
chrdevbase.timer.data=(unsignedlong)&chrdevbase;
add_timer(&chrdevbase.timer);

return0;
}

/*
*@description:驅(qū)動出口函數(shù)
*@param:無
*@return:無
*/
staticvoid__exitchrdevbase_exit(void)
{
gpio_set_value(chrdevbase.led_gpio,1);/*卸載驅(qū)動的時候關(guān)閉LED*/
del_timer_sync(&chrdevbase.timer);/*刪除timer*/

/*注銷字符設(shè)備*/
cdev_del(&chrdevbase.cdev);/*刪除cdev*/
unregister_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT);/*注銷設(shè)備號*/

device_destroy(chrdevbase.class,chrdevbase.devid);/*銷毀設(shè)備*/
class_destroy(chrdevbase.class);/*銷毀類*/

printk("[BSP]chrdevbaseexit!
");
}

/*
*將上面兩個函數(shù)指定為驅(qū)動的入口和出口函數(shù)
*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);

/*
*LICENSE和作者信息
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

套路分析:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#defineCHRDEVBASE_CNT1/*設(shè)備號個數(shù)*/
#defineCHRDEVBASE_NAME"chrdevbase"/*名字*/

/*chrdevbase設(shè)備結(jié)構(gòu)體*/
structnewchr_dev{
....
structtimer_listtimer;/*定義一個定時器*/
};

structnewchr_devchrdevbase;/*自定義字符設(shè)備*/

/*
*@description:LED硬件初始化
*@param:無
*@return:無
*/
staticintled_hal_init(void)
{
......
return0;
}

/*
*@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成功;其他失敗
*/
staticintchrdevbase_open(structinode*inode,structfile*filp)
{
......
return0;
}

/*
*@description:從設(shè)備讀取數(shù)據(jù)
*@param-filp:要打開的設(shè)備文件(文件描述符)
*@param-buf:返回給用戶空間的數(shù)據(jù)緩沖區(qū)
*@param-cnt:要讀取的數(shù)據(jù)長度
*@param-offt:相對于文件首地址的偏移
*@return:讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗
*/
staticssize_tchrdevbase_read(structfile*filp,char__user*buf,size_tcnt,loff_t*offt)
{
......
return0;
}

/*
*@description:向設(shè)備寫數(shù)據(jù)
*@param-filp:設(shè)備文件,表示打開的文件描述符
*@param-buf:要寫給設(shè)備寫入的數(shù)據(jù)
*@param-cnt:要寫入的數(shù)據(jù)長度
*@param-offt:相對于文件首地址的偏移
*@return:寫入的字節(jié)數(shù),如果為負(fù)值,表示寫入失敗
*/
staticssize_tchrdevbase_write(structfile*filp,constchar__user*buf,size_tcnt,loff_t*offt)
{
......
return0;
}

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

/*
*設(shè)備操作函數(shù)結(jié)構(gòu)體
*/
staticstructfile_operationschrdevbase_fops={
.owner=THIS_MODULE,
.open=chrdevbase_open,
.read=chrdevbase_read,
.write=chrdevbase_write,
.release=chrdevbase_release,
};

/*定時器回調(diào)函數(shù)*/
voidtimer_function(unsignedlongarg)
{
structnewchr_dev*dev=(structnewchr_dev*)arg;
staticintsta=1;

sta=!sta;/*每次都取反,實現(xiàn)LED燈反轉(zhuǎn)*/
gpio_set_value(dev->led_gpio,sta);

/*重啟定時器*/
mod_timer(&dev->timer,jiffies+msecs_to_jiffies(500));
}

/*
*@description:驅(qū)動入口函數(shù)
*@param:無
*@return:0成功;其他失敗
*/
staticint__initchrdevbase_init(void)
{
/*初始化硬件*/
led_hal_init();

/*注冊字符設(shè)備驅(qū)動*/
/*1、創(chuàng)建設(shè)備號*/
......

/*2、初始化cdev*/
......

/*3、添加一個cdev*/
......

/*4、創(chuàng)建類*/
......

/*5、創(chuàng)建設(shè)備*/
......

/*6、初始化timer*/
init_timer(&chrdevbase.timer);
chrdevbase.timer.function=timer_function;
chrdevbase.timer.expires=jiffies+msecs_to_jiffies(500);
chrdevbase.timer.data=(unsignedlong)&chrdevbase;
add_timer(&chrdevbase.timer);

return0;
}

/*
*@description:驅(qū)動出口函數(shù)
*@param:無
*@return:無
*/
staticvoid__exitchrdevbase_exit(void)
{
gpio_set_value(chrdevbase.led_gpio,1);/*卸載驅(qū)動的時候關(guān)閉LED*/
del_timer_sync(&chrdevbase.timer);/*刪除timer*/

/*注銷字符設(shè)備*/
cdev_del(&chrdevbase.cdev);/*刪除cdev*/
unregister_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT);/*注銷設(shè)備號*/

device_destroy(chrdevbase.class,chrdevbase.devid);/*銷毀設(shè)備*/
class_destroy(chrdevbase.class);/*銷毀類*/

printk("[BSP]chrdevbaseexit!
");
}

/*
*將上面兩個函數(shù)指定為驅(qū)動的入口和出口函數(shù)
*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);

/*
*LICENSE和作者信息
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

| ioctl

上邊那個定時器案例是固定周期, 可用借助ioctl來動態(tài)修改定時器周期!

驅(qū)動:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#defineCHRDEVBASE_CNT1/*設(shè)備號個數(shù)*/
#defineCHRDEVBASE_NAME"chrdevbase"/*名字*/

#defineCLOSE_CMD(_IO(0xEF,0x01))/*關(guān)閉定時器*/
#defineOPEN_CMD(_IO(0xEF,0x02))/*打開定時器*/
#defineSETPERIOD_CMD(_IO(0xEF,0x03))/*設(shè)置定時器周期命令*/

#defineLEDON1/*開燈*/
#defineLEDOFF0/*關(guān)燈*/

/*chrdevbase設(shè)備結(jié)構(gòu)體*/
structnewchr_dev{
dev_tdevid;/*設(shè)備號*/
structcdevcdev;/*cdev*/
structclass*class;/*類*/
structdevice*device;/*設(shè)備*/
intmajor;/*主設(shè)備號*/
intminor;/*次設(shè)備號*/
structdevice_node*nd;/*設(shè)備節(jié)點*/
intled_gpio;/*led所使用的GPIO編號*/

inttimeperiod;/*定時周期(ms)*/
structtimer_listtimer;/*定義一個定時器*/
};

structnewchr_devchrdevbase;/*自定義字符設(shè)備*/

/*
*@description:LED硬件初始化
*@param:無
*@return:無
*/
staticintled_hal_init(void)
{
intret=0;

/*設(shè)置LED所使用的GPIO*/
/*1、獲取設(shè)備節(jié)點:gpioled*/
chrdevbase.nd=of_find_node_by_path("/gpioled");
if(chrdevbase.nd==NULL){
printk("chrdevbasenodecantnotfound!
");
return-EINVAL;
}else{
printk("chrdevbasenodehasbeenfound!
");
}

/*2、獲取設(shè)備樹中的gpio屬性,得到LED所使用的LED編號*/
chrdevbase.led_gpio=of_get_named_gpio(chrdevbase.nd,"led-gpio",0);
if(chrdevbase.led_gpioprivate_data=&chrdevbase;/*設(shè)置私有數(shù)據(jù)*/
chrdevbase.timeperiod=1000;/*默認(rèn)周期為1s*/
ret=led_hal_init();/*初始化LEDIO*/
return0;
}

/*
*@description:ioctl函數(shù),
*@param–filp:要打開的設(shè)備文件(文件描述符)
*@param-cmd:應(yīng)用程序發(fā)送過來的命令
*@param-arg:參數(shù)
*@return:0成功;其他失敗
*/
staticlongtimer_unlocked_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg)
{
structnewchr_dev*dev=(structnewchr_dev*)filp->private_data;
inttimerperiod;

switch(cmd){
caseCLOSE_CMD:/*關(guān)閉定時器*/
//等待其他處理器使用完定時器再刪除
del_timer_sync(&dev->timer);
break;
caseOPEN_CMD:/*打開定時器*/
timerperiod=dev->timeperiod;
mod_timer(&dev->timer,jiffies+msecs_to_jiffies(timerperiod));
break;
caseSETPERIOD_CMD:/*設(shè)置定時器周期*/
dev->timeperiod=arg;
mod_timer(&dev->timer,jiffies+msecs_to_jiffies(arg));
break;
default:
break;
}
return0;
}
/*
*設(shè)備操作函數(shù)結(jié)構(gòu)體
*/
staticstructfile_operationschrdevbase_fops={
.owner=THIS_MODULE,
.open=chrdevbase_open,
.unlocked_ioctl=timer_unlocked_ioctl,
};

/*定時器回調(diào)函數(shù)*/
voidtimer_function(unsignedlongarg)
{
structnewchr_dev*dev=(structnewchr_dev*)arg;
staticintsta=1;

sta=!sta;/*每次都取反,實現(xiàn)LED燈反轉(zhuǎn)*/
gpio_set_value(dev->led_gpio,sta);

/*重啟定時器*/
mod_timer(&dev->timer,jiffies+msecs_to_jiffies(dev->timeperiod));
}

/*
*@description:驅(qū)動入口函數(shù)
*@param:無
*@return:0成功;其他失敗
*/
staticint__initchrdevbase_init(void)
{
/*注冊字符設(shè)備驅(qū)動*/
/*1、創(chuàng)建設(shè)備號*/
if(chrdevbase.major){/*定義了設(shè)備號*/
chrdevbase.devid=MKDEV(chrdevbase.major,0);
register_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT,CHRDEVBASE_NAME);
}else{/*沒有定義設(shè)備號*/
alloc_chrdev_region(&chrdevbase.devid,0,CHRDEVBASE_CNT,CHRDEVBASE_NAME);/*申請設(shè)備號*/
chrdevbase.major=MAJOR(chrdevbase.devid);/*獲取主設(shè)備號*/
chrdevbase.minor=MINOR(chrdevbase.devid);/*獲取次設(shè)備號*/
}
printk("newcheledmajor=%d,minor=%d
",chrdevbase.major,chrdevbase.minor);

/*2、初始化cdev*/
chrdevbase.cdev.owner=THIS_MODULE;
cdev_init(&chrdevbase.cdev,&chrdevbase_fops);

/*3、添加一個cdev*/
cdev_add(&chrdevbase.cdev,chrdevbase.devid,CHRDEVBASE_CNT);

/*4、創(chuàng)建類*/
chrdevbase.class=class_create(THIS_MODULE,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.class)){
returnPTR_ERR(chrdevbase.class);
}

/*5、創(chuàng)建設(shè)備*/
chrdevbase.device=device_create(chrdevbase.class,NULL,chrdevbase.devid,NULL,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.device)){
returnPTR_ERR(chrdevbase.device);
}

/*6、初始化timer*/
init_timer(&chrdevbase.timer);
chrdevbase.timer.function=timer_function;
chrdevbase.timer.expires=jiffies+msecs_to_jiffies(500);
chrdevbase.timer.data=(unsignedlong)&chrdevbase;
//add_timer(&chrdevbase.timer);

return0;
}

/*
*@description:驅(qū)動出口函數(shù)
*@param:無
*@return:無
*/
staticvoid__exitchrdevbase_exit(void)
{
gpio_set_value(chrdevbase.led_gpio,1);/*卸載驅(qū)動的時候關(guān)閉LED*/
del_timer_sync(&chrdevbase.timer);/*刪除timer*/

/*注銷字符設(shè)備*/
cdev_del(&chrdevbase.cdev);/*刪除cdev*/
unregister_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT);/*注銷設(shè)備號*/

device_destroy(chrdevbase.class,chrdevbase.devid);/*銷毀設(shè)備*/
class_destroy(chrdevbase.class);/*銷毀類*/

printk("[BSP]chrdevbaseexit!
");
}

/*
*將上面兩個函數(shù)指定為驅(qū)動的入口和出口函數(shù)
*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);

/*
*LICENSE和作者信息
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

應(yīng)用:

#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"sys/stat.h"
#include"fcntl.h"
#include"stdlib.h"
#include"string.h"
#include"linux/ioctl.h"

/*命令值*/
#defineCLOSE_CMD(_IO(0XEF,0x1))/*關(guān)閉定時器*/
#defineOPEN_CMD(_IO(0XEF,0x2))/*打開定時器*/
#defineSETPERIOD_CMD(_IO(0XEF,0x3))/*設(shè)置定時器周期命令*/

/*
*@description:main主程序
*@param-argc:argv數(shù)組元素個數(shù)
*@param-argv:具體參數(shù)
*@return:0成功;其他失敗
*/
intmain(intargc,char*argv[])
{
intfd,ret;
char*filename;
unsignedintcmd;
unsignedintarg;
charwritebuf[100];
unsignedcharstr[100];

if(argc!=2){
printf("[APP]ErrorUsage!
");
return-1;
}

filename=argv[1];

/*打開驅(qū)動文件*/
fd=open(filename,O_RDWR);
if(fd

使用:

10b6b360-7aa6-11ee-939d-92fbcf53809c.png

上文就是簡單介紹了一下定時器, 簡單使用了一下定時器, 后邊根據(jù)各自需求進(jìn)一步深入學(xué)習(xí).







審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 定時器
    +關(guān)注

    關(guān)注

    23

    文章

    3228

    瀏覽量

    114149
  • FreeRTOS
    +關(guān)注

    關(guān)注

    12

    文章

    483

    瀏覽量

    61852
  • LINUX內(nèi)核
    +關(guān)注

    關(guān)注

    1

    文章

    315

    瀏覽量

    21580
  • 時鐘源
    +關(guān)注

    關(guān)注

    0

    文章

    92

    瀏覽量

    15910
  • 定時中斷
    +關(guān)注

    關(guān)注

    0

    文章

    19

    瀏覽量

    8540

原文標(biāo)題:i.MX6ULL|時間管理和內(nèi)核定時器

文章出處:【微信號:玩轉(zhuǎn)單片機,微信公眾號:玩轉(zhuǎn)單片機】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

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

    內(nèi)核定時器內(nèi)核用來控制在未來某個時間點(基于jiffies(節(jié)拍總數(shù)))調(diào)度執(zhí)行某個函數(shù)的一種機制,相關(guān)函數(shù)位于 和 kernel/timer.c 文件
    的頭像 發(fā)表于 09-17 15:06 ?1396次閱讀

    移植NXP官方linux 5.4內(nèi)核i.MX6ULL開發(fā)板

    本文描述移植NXP官方 linux 5.4 內(nèi)核i.MX6ULL開發(fā)板。
    發(fā)表于 12-19 11:10 ?1966次閱讀

    i.MX6UL/i.MX6ULL開發(fā)常見問題】單獨編譯內(nèi)核,uboot生成很多文件,具體用哪一個?

    i.MX6UL/i.MX6ULL開發(fā)常見問題》基于米爾電子 i.MX6UL/i.MX6ULL產(chǎn)品(V.10)2.3單獨編譯內(nèi)核,uboot
    發(fā)表于 07-01 17:50

    I.MX6ULL終結(jié)者開發(fā)板裸機仿真jlink調(diào)試

    I.MX6ULL‘終結(jié)者’開發(fā)板預(yù)留了JTAG仿真接口,并給出了開發(fā)文檔,可以實現(xiàn)在JLINK仿真條件下的單步跟蹤、斷點調(diào)試等功能,使得開發(fā)研究i.MX6ULL處理
    發(fā)表于 07-07 10:56

    i.MX6ULL開發(fā)板硬件資源

    迅為i.MX6ULL 終結(jié)者開發(fā)板硬件資源非常豐富,幾乎將 i.MX6ULL 芯片的所有資源都擴展引出到底板上了,底板提供了豐富的外設(shè)接口,開發(fā)板的尺寸是 190mm*125mm,充分考慮了人性化設(shè)計,整體顯得十分大。
    發(fā)表于 12-29 06:18

    i.MX6ULL核心板資源

    路多功能音頻通道多路 SPI、IIC、定時器、PWM、DMA、RTC、看門狗等常用外設(shè)規(guī)格參數(shù):核心板配置CPU 型號 NXP i.MX6ULL(MCIMX6Y2C),ARM Cortex A7
    發(fā)表于 07-12 17:50

    Linux內(nèi)核i.mx6ull中的編譯運行

    Linux內(nèi)核i.mx6ull的編譯運行編譯Linux Kernel需要使用lzop庫,所以需要安裝,否則編譯內(nèi)核會失?。。。udo apt-get install lzop一、Linux
    發(fā)表于 11-05 07:14

    初識 i.MX6ULL 寄存

    裸機開發(fā)_L1_匯編LED實驗0. 本節(jié)目標(biāo)1. 硬件層電路2. 初識 i.MX6ULL 寄存2.1 i.MX6ULL 時鐘控制寄存2.2 i.
    發(fā)表于 12-20 07:13

    ARM裸機篇之i.MX6ULL處理資料分享

    1、i.MX6ULL處理啟動過程i.MX6ULL是NXP基于ARM Cortex-A7內(nèi)核的單核處理家族,主頻可以高900MHz。
    發(fā)表于 04-14 16:42

    飛凌i.MX6ULL開發(fā)板的評測,再次進(jìn)階擁有更高的性價比

    MCIMX6Y2開發(fā)設(shè)計,采用先進(jìn)的ARMCortex-A7內(nèi)核,運行速度高達(dá)800MHz。i.MX6ULL應(yīng)用處理包括一個集成的電源管理
    發(fā)表于 10-27 11:55 ?1421次閱讀
    飛凌<b class='flag-5'>i.MX6ULL</b>開發(fā)板的評測,再次進(jìn)階擁有更高的性價比

    基于NXP i.MX6ULL處理的FETMX6ULL-C核心板

    “性價比高,功能接口豐富,資料齊全,穩(wěn)定性強”這是許多用戶對飛凌FETMX6ULL-S核心板的評價。作為NXP公司一顆經(jīng)典的MPU,i.MX6ULL的市場認(rèn)可度無需多言。而作為NXP公司的金牌
    發(fā)表于 04-11 15:05 ?1104次閱讀
    基于NXP <b class='flag-5'>i.MX6ULL</b>處理<b class='flag-5'>器</b>的FETMX<b class='flag-5'>6ULL</b>-C核心板

    基于i.MX6ULL應(yīng)用處理的FETMX6ULL-C核心板

    NXP i.MX6ULL擴展了i.MX6系列,它是一個高性能、超高效、低成本處理子系列,采用先進(jìn)的ARM Cortex-A7內(nèi)核,運行速度高達(dá)800MHz。
    發(fā)表于 04-29 14:37 ?1298次閱讀
    基于<b class='flag-5'>i.MX6ULL</b>應(yīng)用處理<b class='flag-5'>器</b>的FETMX<b class='flag-5'>6ULL</b>-C核心板

    Linux內(nèi)核定時器

    在Linux內(nèi)核中,也可以通過定時器來完成定時功能。但和單片機不同的是,Linux內(nèi)核定時器是一種基于未來時間點的計時方式,它以當(dāng)前時刻為啟
    的頭像 發(fā)表于 09-22 08:56 ?1790次閱讀
    Linux<b class='flag-5'>內(nèi)核定時器</b>

    【北京迅為】i.MX6ULL開發(fā)板移植 Debian 文件系統(tǒng)

    【北京迅為】i.MX6ULL開發(fā)板移植 Debian 文件系統(tǒng)
    的頭像 發(fā)表于 02-10 15:34 ?1069次閱讀
    【北京迅為】<b class='flag-5'>i.MX6ULL</b>開發(fā)板移植 Debian 文件系統(tǒng)

    基于i.MX6ULL的掉電檢測設(shè)計與軟件測試

    基于i.MX6ULL的掉電檢測設(shè)計與軟件測試基于i.MX6ULL平臺設(shè)計實現(xiàn)掉電檢測功能,首先選擇一路IO,利用IO電平變化觸發(fā)中斷,在編寫驅(qū)動時捕獲該路GPIO的中斷,然后在中斷響應(yīng)函數(shù)中發(fā)
    的頭像 發(fā)表于 11-09 10:40 ?750次閱讀
    基于<b class='flag-5'>i.MX6ULL</b>的掉電檢測設(shè)計與軟件測試