學(xué)習(xí)過 UCOS 或 FreeRTOS 的同學(xué)應(yīng)該知道,UCOS 或 FreeRTOS 是需要一個(gè)硬件定時(shí)器提供系統(tǒng)時(shí)鐘,一般使用 Systick 作為系統(tǒng)時(shí)鐘源。
同理,Linux 要運(yùn)行,也是需要一個(gè)系統(tǒng)時(shí)鐘的,至于這個(gè)系統(tǒng)時(shí)鐘是由哪個(gè)定時(shí)器提供的,有興趣的讀者可以去研究一下 Linux 內(nèi)核。不過對(duì)于Linux 驅(qū)動(dòng)編寫者來說,不需要深入研究這些具體的實(shí)現(xiàn),只需要掌握相應(yīng)的 API 函數(shù)即可。
Linux 內(nèi)核中有大量的函數(shù)需要時(shí)間管理,比如周期性的調(diào)度程序、延時(shí)程序、對(duì)于我們驅(qū)動(dòng)編寫者來說最常用的定時(shí)器。硬件定時(shí)器提供時(shí)鐘源,時(shí)鐘源的頻率可以設(shè)置, 設(shè)置好以后就周期性地產(chǎn)生定時(shí)中斷,系統(tǒng)使用定時(shí)中斷來計(jì)時(shí)。
中斷周期性產(chǎn)生的頻率就是系統(tǒng)頻率,也叫做節(jié)拍率(tick rate)(有的資料也叫系統(tǒng)頻率),比如 1000Hz,100Hz 等等說的就是系統(tǒng)節(jié)拍率。
系統(tǒng)節(jié)拍率是可以設(shè)置的,單位是 Hz,在編譯 Linux 內(nèi)核的時(shí)候可以通過圖形化界面設(shè)置系統(tǒng)節(jié)拍率,按照如下路徑打開配置界面:
->KernelFeatures ->Timerfrequency([=y])
效果圖:
在內(nèi)核文件路徑通過輸入:make menuconfig指令打開圖形化配置界面!
可以在.config文件中找到對(duì)應(yīng)的配置,在內(nèi)核文件路徑通過輸入:gedit .config指令打開:
CONFIG_HZ 為 100, Linux 內(nèi)核會(huì)使用 CONFIG_HZ 來設(shè)置自己的系統(tǒng)時(shí)鐘。打開文件 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ū)動(dòng)的時(shí)候會(huì)常常用到 HZ,因?yàn)?HZ 表示一秒的節(jié)拍數(shù),也就是頻率。
高節(jié)拍率會(huì)提高系統(tǒng)時(shí)間精度,如果采用 100Hz 的節(jié)拍率,時(shí)間精度就是 10ms,采用1000Hz 的話時(shí)間精度就是 1ms,精度提高了 10 倍。
高精度時(shí)鐘能夠以更高的精度運(yùn)行,時(shí)間測(cè)量也更加準(zhǔn)確。高節(jié)拍率會(huì)導(dǎo)致中斷的產(chǎn)生更加頻繁,頻繁的中斷會(huì)加劇系統(tǒng)的負(fù)擔(dān), 1000Hz 和 100Hz的系統(tǒng)節(jié)拍率相比,系統(tǒng)要花費(fèi) 10 倍的“精力”去處理中斷。
中斷服務(wù)函數(shù)占用處理器的時(shí)間增加,但是現(xiàn)在的處理器性能都很強(qiáng)大,所以采用 1000Hz 的系統(tǒng)節(jié)拍率并不會(huì)增加太大的負(fù)載壓力。
根據(jù)自己的實(shí)際情況,選擇合適的系統(tǒng)節(jié)拍率,本教程全部采用默認(rèn)的 100Hz 系統(tǒng)節(jié)拍率。
Linux 內(nèi)核使用全局變量 jiffies 來記錄系統(tǒng)從啟動(dòng)以來的系統(tǒng)節(jié)拍數(shù),系統(tǒng)啟動(dòng)的時(shí)候會(huì)將 jiffies 初始化為 0,jiffies 定義在文件 include/linux/jiffies.h 中,定義如下:
jiffies_64 和 jiffies 其實(shí)是同一個(gè)東西, jiffies_64 用于 64 位系統(tǒng),而 jiffies 用于 32 位系統(tǒng)。當(dāng)訪問 jiffies 的時(shí)候其實(shí)訪問的是 jiffies_64 的低 32 位,使用 get_jiffies_64 這個(gè)函數(shù)可以獲取 jiffies_64 的值。
在 32 位的系統(tǒng)上讀取 jiffies 的值,在 64 位的系統(tǒng)上 jiffes 和 jiffies_64表示同一個(gè)變量,因此也可以直接讀取 jiffies 的值。所以不管是 32 位的系統(tǒng)還是 64 位系統(tǒng),都可以使用 jiffies。
|繞回
前面說了 HZ 表示每秒的節(jié)拍數(shù),jiffies 表示系統(tǒng)運(yùn)行的 jiffies 節(jié)拍數(shù),所以 jiffies/HZ 就是系統(tǒng)運(yùn)行時(shí)間,單位為秒。
不管是 32 位還是 64 位的 jiffies,都有溢出的風(fēng)險(xiǎn),溢出以后會(huì)重新從 0 開始計(jì)數(shù),相當(dāng)于繞回來了,因此有些資料也將這個(gè)現(xiàn)象也叫做繞回。
假如 HZ 為最大值 1000 的時(shí)候,32 位的 jiffies 只需要 49.7 天就發(fā)生了繞回,對(duì)于 64 位的 jiffies 來說大概需要5.8 億年才能繞回,因此 jiffies_64 的繞回忽略不計(jì)。處理 32 位 jiffies 的繞回顯得尤為重要,Linux 內(nèi)核提供了如表 50.1.1.1 所示的幾個(gè) API 函數(shù)來處理繞回。
可以在這個(gè)文件中找到定義:
如果 unkown 超過 known 的話,time_after 函數(shù)返回真,否則返回假。如果 unkown 沒有超過 known 的話 time_before 函數(shù)返回真,否則返回假。time_after_eq 函數(shù)和 time_after 函數(shù)類似,只是多了判斷等于這個(gè)條件。同理,time_before_eq 函數(shù)和 time_before 函數(shù)也類似。比如要判斷某段代碼執(zhí)行時(shí)間有沒有超時(shí),此時(shí)就可以使用如下所示代碼:
unsignedlongtimeout; timeout=jiffies+(2*HZ);/*超時(shí)的時(shí)間點(diǎn)*/ /************************************* 具體的代碼 ************************************/ /*判斷有沒有超時(shí)*/ if(time_before(jiffies,timeout)) { /*超時(shí)未發(fā)生*/ } else { /*超時(shí)發(fā)生*/ }
timeout 就是超時(shí)時(shí)間點(diǎn),比如我們要判斷代碼執(zhí)行時(shí)間是不是超過了 2 秒,那么超時(shí)時(shí)間點(diǎn)就是 jiffies+(2*HZ),如果 jiffies 大于 timeout 那就表示超時(shí)了,否則就是沒有超時(shí)。
為了方便開發(fā),Linux 內(nèi)核提供了幾個(gè) jiffies 和 ms、us、ns 之間的轉(zhuǎn)換函數(shù):
| 定時(shí)器
定時(shí)器是一個(gè)很常用的功能,需要周期性處理的工作都要用到定時(shí)器。定時(shí)器大體分兩類,一個(gè)是硬件定時(shí)器,一個(gè)是軟件定時(shí)器,而軟件定時(shí)器需要硬件定時(shí)器做基礎(chǔ),通過軟件的方式使用無限拓展(理論上)的軟件定時(shí)器,使用了操作系統(tǒng)后,往往是使用軟件定時(shí)器,可以不需要再對(duì)硬件定時(shí)器進(jìn)行初始化配置。
Linux 內(nèi)核定時(shí)器使用很簡(jiǎn)單,只需要提供超時(shí)時(shí)間(相當(dāng)于定時(shí)值)和定時(shí)處理函數(shù)即可,當(dāng)超時(shí)時(shí)間到了以后設(shè)置的定時(shí)處理函數(shù)就會(huì)執(zhí)行,和我們使用硬件定時(shí)器的套路一樣,只是使用內(nèi)核定時(shí)器不需要做一大堆的寄存器初始化工作。
在使用內(nèi)核定時(shí)器的時(shí)候要注意一點(diǎn),內(nèi)核定時(shí)器并不是周期性運(yùn)行的,超時(shí)以后就會(huì)自動(dòng)關(guān)閉,因此如果想要實(shí)現(xiàn)周期性定時(shí),那么就需要在定時(shí)處理函數(shù)中重新開啟定時(shí)器。Linux 內(nèi)核使用 timer_list 結(jié)構(gòu)體表示內(nèi)核定時(shí)器,timer_list 定義在文件include/linux/timer.h 中,定義如下(省略掉條件編譯):
structtimer_list{ structlist_headentry; unsignedlongexpires; structtvec_base*base;/*定時(shí)器超時(shí)時(shí)間,單位是節(jié)拍數(shù)*/ void(*function)(unsignedlong);/*定時(shí)處理函數(shù)*/ unsignedlongdata;/*要傳遞給function函數(shù)的參數(shù)*/ intslack; };
要使用內(nèi)核定時(shí)器首先要先定義一個(gè) timer_list 變量,表示定時(shí)器,tiemr_list 結(jié)構(gòu)體的expires 成員變量表示超時(shí)時(shí)間,單位為節(jié)拍數(shù)。比如現(xiàn)在需要定義一個(gè)周期為 2 秒的定時(shí)器,那么這個(gè)定時(shí)器的超時(shí)時(shí)間就是 jiffies+(2*HZ),因此 expires=jiffies+(2*HZ)。function 就是定時(shí)器超時(shí)以后的定時(shí)處理函數(shù),要做的工作或處理就放到這個(gè)函數(shù)里面,需要根據(jù)需求編寫這個(gè)定時(shí)處理函數(shù)。
API函數(shù)
init_timer 函數(shù)
init_timer 函數(shù)負(fù)責(zé)初始化 timer_list 類型變量,當(dāng)我們定義了一個(gè) timer_list 變量以后一定要先用 init_timer 初始化一下。init_timer 函數(shù)原型如下:
/* timer:要初始化定時(shí)器。 返回值:沒有返回值。 */ voidinit_timer(structtimer_list*timer)
add_timer 函數(shù)
add_timer 函數(shù)用于向 Linux 內(nèi)核注冊(cè)定時(shí)器,使用 add_timer 函數(shù)向內(nèi)核注冊(cè)定時(shí)器以后,定時(shí)器就會(huì)開始運(yùn)行,函數(shù)原型如下:
/* timer:要注冊(cè)的定時(shí)器。 返回值:沒有返回值。 */ voidadd_timer(structtimer_list*timer)
del_timer 函數(shù)
del_timer 函數(shù)用于刪除一個(gè)定時(shí)器,不管定時(shí)器有沒有被激活,都可以使用此函數(shù)刪除。在多處理器系統(tǒng)上,定時(shí)器可能會(huì)在其他的處理器上運(yùn)行,因此在調(diào)用 del_timer 函數(shù)刪除定時(shí)器之前要先等待其他處理器的定時(shí)處理器函數(shù)退出。del_timer 函數(shù)原型如下:
/* timer:要?jiǎng)h除的定時(shí)器。 返回值:0,定時(shí)器還沒被激活;1,定時(shí)器已經(jīng)激活 */ intdel_timer(structtimer_list*timer)
del_timer_sync 函數(shù)
del_timer_sync 函數(shù)是 del_timer 函數(shù)的同步版,會(huì)等待其他處理器使用完定時(shí)器再刪除,del_timer_sync 不能使用在中斷上下文中。del_timer_sync 函數(shù)原型如下所示:
/* timer:要?jiǎng)h除的定時(shí)器。 返回值:0,定時(shí)器還沒被激活;1,定時(shí)器已經(jīng)激活。 */ intdel_timer_sync(structtimer_list*timer)
mod_timer 函數(shù)
mod_timer 函數(shù)用于修改定時(shí)值,如果定時(shí)器還沒有激活的話,mod_timer 函數(shù)會(huì)激活定時(shí)器!函數(shù)原型如下:
/* timer:要修改超時(shí)時(shí)間(定時(shí)值)的定時(shí)器。 expires:修改后的超時(shí)時(shí)間。 返回值:0,調(diào)用 mod_timer 函數(shù)前定時(shí)器未被激活;1,調(diào)用 mod_timer 函數(shù)前定時(shí)器已被激活。 */ intmod_timer(structtimer_list*timer,unsignedlongexpires)
內(nèi)核定時(shí)器一般的使用流程如下所示:
structtimer_listtimer;/*定義定時(shí)器*/ /*定時(shí)器回調(diào)函數(shù)*/ voidfunction(unsignedlongarg) { /* *定時(shí)器處理代碼 */ /*如果需要定時(shí)器周期性運(yùn)行的話就使用mod_timer *函數(shù)重新設(shè)置超時(shí)值并且啟動(dòng)定時(shí)器。 */ mod_timer(&dev->timertest,jiffies+msecs_to_jiffies(2000)); } /*初始化函數(shù)*/ voidinit(void) { init_timer(&timer);/*初始化定時(shí)器*/ timer.function=function;/*設(shè)置定時(shí)處理函數(shù)*/ timer.expires=jffies+msecs_to_jiffies(2000);/*超時(shí)時(shí)間2秒*/ timer.data=(unsignedlong)&dev;/*將設(shè)備結(jié)構(gòu)體作為參數(shù)*/ add_timer(&timer);/*啟動(dòng)定時(shí)器*/ } /*退出函數(shù)*/ voidexit(void) { del_timer(&timer);/*刪除定時(shí)器*/ /*或者使用*/ del_timer_sync(&timer); }
Linux 內(nèi)核短延時(shí)函數(shù)
有時(shí)候需要在內(nèi)核中實(shí)現(xiàn)短延時(shí),尤其是在 Linux 驅(qū)動(dòng)中。Linux 內(nèi)核提供了毫秒、微秒和納秒延時(shí)函數(shù):
| LED閃爍
通過設(shè)置一個(gè)定時(shí)器來實(shí)現(xiàn)周期性的閃爍 LED 燈,通過這個(gè)案例來學(xué)習(xí)定時(shí)器的基本使用,這個(gè)實(shí)驗(yàn)不需要看應(yīng)用層。
簡(jiǎn)單使用型:
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include //#include //#include #defineCHRDEVBASE_CNT1/*設(shè)備號(hào)個(gè)數(shù)*/ #defineCHRDEVBASE_NAME"chrdevbase"/*名字*/ /*chrdevbase設(shè)備結(jié)構(gòu)體*/ structnewchr_dev{ dev_tdevid;/*設(shè)備號(hào)*/ structcdevcdev;/*cdev*/ structclass*class;/*類*/ structdevice*device;/*設(shè)備*/ intmajor;/*主設(shè)備號(hào)*/ intminor;/*次設(shè)備號(hào)*/ structdevice_node*nd;/*設(shè)備節(jié)點(diǎn)*/ intled_gpio;/*led所使用的GPIO編號(hào)*/ structtimer_listtimer;/*定義一個(gè)定時(shí)器*/ }; structnewchr_devchrdevbase;/*自定義字符設(shè)備*/ /* *@description:LED硬件初始化 *@param:無 *@return:無 */ staticintled_hal_init(void) { intret=0; /*設(shè)置LED所使用的GPIO*/ /* 1、獲取設(shè)備節(jié)點(diǎn):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編號(hào)*/ chrdevbase.led_gpio=of_get_named_gpio(chrdevbase.nd,"led-gpio",0); if(chrdevbase.led_gpio0)?{ ????????printk("can't?get?led-gpio"); ????????return?-EINVAL; ????} ????printk("led-gpio?num?=?%d ",?chrdevbase.led_gpio); ????/*?3、設(shè)置?GPIO1_IO03?為輸出,并且輸出高電平,默認(rèn)關(guān)閉?LED?燈?*/ ????ret?=?gpio_direction_output(chrdevbase.led_gpio,?1); ????if(ret?0)?{ ????????printk("can't?set?gpio! "); ????} ????return?0; } /* ?*?@description????????:?打開設(shè)備 ?*?@param?-?inode?????:?傳遞給驅(qū)動(dòng)的inode ?*?@param?-?filp?????:?設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量 ?*???????????????????????一般在open的時(shí)候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。 ?*?@return?????????????:?0?成功;其他?失敗 ?*/ static?int?chrdevbase_open(struct?inode?*inode,?struct?file?*filp) { ????printk("[BSP]chrdevbase?open! "); ????filp->private_data=&chrdevbase;/*設(shè)置私有數(shù)據(jù)*/ return0; } /* *@description:從設(shè)備讀取數(shù)據(jù) *@param-filp:要打開的設(shè)備文件(文件描述符) *@param-buf:返回給用戶空間的數(shù)據(jù)緩沖區(qū) *@param-cnt:要讀取的數(shù)據(jù)長(zhǎng)度 *@param-offt:相對(duì)于文件首地址的偏移 *@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ù)長(zhǎng)度 *@param-offt:相對(duì)于文件首地址的偏移 *@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, }; /*定時(shí)器回調(diào)函數(shù)*/ voidtimer_function(unsignedlongarg) { structnewchr_dev*dev=(structnewchr_dev*)arg; staticintsta=1; sta=!sta;/*每次都取反,實(shí)現(xiàn)LED燈反轉(zhuǎn)*/ gpio_set_value(dev->led_gpio,sta); /*重啟定時(shí)器*/ mod_timer(&dev->timer,jiffies+msecs_to_jiffies(500)); } /* *@description:驅(qū)動(dòng)入口函數(shù) *@param:無 *@return:0成功;其他失敗 */ staticint__initchrdevbase_init(void) { /*初始化硬件*/ led_hal_init(); /*注冊(cè)字符設(shè)備驅(qū)動(dòng)*/ /*1、創(chuàng)建設(shè)備號(hào)*/ if(chrdevbase.major){/*定義了設(shè)備號(hào)*/ chrdevbase.devid=MKDEV(chrdevbase.major,0); register_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT,CHRDEVBASE_NAME); }else{/*沒有定義設(shè)備號(hào)*/ alloc_chrdev_region(&chrdevbase.devid,0,CHRDEVBASE_CNT,CHRDEVBASE_NAME);/*申請(qǐng)?jiān)O(shè)備號(hào)*/ chrdevbase.major=MAJOR(chrdevbase.devid);/*獲取主設(shè)備號(hào)*/ chrdevbase.minor=MINOR(chrdevbase.devid);/*獲取次設(shè)備號(hào)*/ } printk("newcheledmajor=%d,minor=%d ",chrdevbase.major,chrdevbase.minor); /*2、初始化cdev*/ chrdevbase.cdev.owner=THIS_MODULE; cdev_init(&chrdevbase.cdev,&chrdevbase_fops); /*3、添加一個(gè)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ū)動(dòng)出口函數(shù) *@param:無 *@return:無 */ staticvoid__exitchrdevbase_exit(void) { gpio_set_value(chrdevbase.led_gpio,1);/*卸載驅(qū)動(dòng)的時(shí)候關(guān)閉LED*/ del_timer_sync(&chrdevbase.timer);/*刪除timer*/ /*注銷字符設(shè)備*/ cdev_del(&chrdevbase.cdev);/*刪除cdev*/ unregister_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT);/*注銷設(shè)備號(hào)*/ device_destroy(chrdevbase.class,chrdevbase.devid);/*銷毀設(shè)備*/ class_destroy(chrdevbase.class);/*銷毀類*/ printk("[BSP]chrdevbaseexit! "); } /* *將上面兩個(gè)函數(shù)指定為驅(qū)動(dòng)的入口和出口函數(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è)備號(hào)個(gè)數(shù)*/ #defineCHRDEVBASE_NAME"chrdevbase"/*名字*/ /*chrdevbase設(shè)備結(jié)構(gòu)體*/ structnewchr_dev{ .... structtimer_listtimer;/*定義一個(gè)定時(shí)器*/ }; structnewchr_devchrdevbase;/*自定義字符設(shè)備*/ /* *@description:LED硬件初始化 *@param:無 *@return:無 */ staticintled_hal_init(void) { ...... return0; } /* *@description:打開設(shè)備 *@param-inode:傳遞給驅(qū)動(dòng)的inode *@param-filp:設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量 *一般在open的時(shí)候?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ù)長(zhǎng)度 *@param-offt:相對(duì)于文件首地址的偏移 *@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ù)長(zhǎng)度 *@param-offt:相對(duì)于文件首地址的偏移 *@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, }; /*定時(shí)器回調(diào)函數(shù)*/ voidtimer_function(unsignedlongarg) { structnewchr_dev*dev=(structnewchr_dev*)arg; staticintsta=1; sta=!sta;/*每次都取反,實(shí)現(xiàn)LED燈反轉(zhuǎn)*/ gpio_set_value(dev->led_gpio,sta); /*重啟定時(shí)器*/ mod_timer(&dev->timer,jiffies+msecs_to_jiffies(500)); } /* *@description:驅(qū)動(dòng)入口函數(shù) *@param:無 *@return:0成功;其他失敗 */ staticint__initchrdevbase_init(void) { /*初始化硬件*/ led_hal_init(); /*注冊(cè)字符設(shè)備驅(qū)動(dòng)*/ /*1、創(chuàng)建設(shè)備號(hào)*/ ...... /*2、初始化cdev*/ ...... /*3、添加一個(gè)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ū)動(dòng)出口函數(shù) *@param:無 *@return:無 */ staticvoid__exitchrdevbase_exit(void) { gpio_set_value(chrdevbase.led_gpio,1);/*卸載驅(qū)動(dòng)的時(shí)候關(guān)閉LED*/ del_timer_sync(&chrdevbase.timer);/*刪除timer*/ /*注銷字符設(shè)備*/ cdev_del(&chrdevbase.cdev);/*刪除cdev*/ unregister_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT);/*注銷設(shè)備號(hào)*/ device_destroy(chrdevbase.class,chrdevbase.devid);/*銷毀設(shè)備*/ class_destroy(chrdevbase.class);/*銷毀類*/ printk("[BSP]chrdevbaseexit! "); } /* *將上面兩個(gè)函數(shù)指定為驅(qū)動(dòng)的入口和出口函數(shù) */ module_init(chrdevbase_init); module_exit(chrdevbase_exit); /* *LICENSE和作者信息 */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");
| ioctl
上邊那個(gè)定時(shí)器案例是固定周期, 可用借助ioctl來動(dòng)態(tài)修改定時(shí)器周期!
驅(qū)動(dòng):
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #defineCHRDEVBASE_CNT1/*設(shè)備號(hào)個(gè)數(shù)*/ #defineCHRDEVBASE_NAME"chrdevbase"/*名字*/ #defineCLOSE_CMD(_IO(0xEF,0x01))/*關(guān)閉定時(shí)器*/ #defineOPEN_CMD(_IO(0xEF,0x02))/*打開定時(shí)器*/ #defineSETPERIOD_CMD(_IO(0xEF,0x03))/*設(shè)置定時(shí)器周期命令*/ #defineLEDON1/*開燈*/ #defineLEDOFF0/*關(guān)燈*/ /*chrdevbase設(shè)備結(jié)構(gòu)體*/ structnewchr_dev{ dev_tdevid;/*設(shè)備號(hào)*/ structcdevcdev;/*cdev*/ structclass*class;/*類*/ structdevice*device;/*設(shè)備*/ intmajor;/*主設(shè)備號(hào)*/ intminor;/*次設(shè)備號(hào)*/ structdevice_node*nd;/*設(shè)備節(jié)點(diǎn)*/ intled_gpio;/*led所使用的GPIO編號(hào)*/ inttimeperiod;/*定時(shí)周期(ms)*/ structtimer_listtimer;/*定義一個(gè)定時(shí)器*/ }; structnewchr_devchrdevbase;/*自定義字符設(shè)備*/ /* *@description:LED硬件初始化 *@param:無 *@return:無 */ staticintled_hal_init(void) { intret=0; /*設(shè)置LED所使用的GPIO*/ /*1、獲取設(shè)備節(jié)點(diǎn):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編號(hào)*/ chrdevbase.led_gpio=of_get_named_gpio(chrdevbase.nd,"led-gpio",0); if(chrdevbase.led_gpio0)?{ ????????printk("can't?get?led-gpio"); ????????return?-EINVAL; ????} ????printk("led-gpio?num?=?%d ",?chrdevbase.led_gpio); ????/*?3、設(shè)置?GPIO1_IO03?為輸出,并且輸出高電平,默認(rèn)關(guān)閉?LED?燈?*/ ????ret?=?gpio_direction_output(chrdevbase.led_gpio,?1); ????if(ret?0)?{ ????????printk("can't?set?gpio! "); ????} ????return?0; } /* ?*?@description????????:?打開設(shè)備 ?*?@param?-?inode?????:?傳遞給驅(qū)動(dòng)的inode ?*?@param?-?filp?????:?設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量 ?*???????????????????????一般在open的時(shí)候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。 ?*?@return?????????????:?0?成功;其他?失敗 ?*/ static?int?chrdevbase_open(struct?inode?*inode,?struct?file?*filp) { ????int?ret?=?0; ????printk("[BSP]chrdevbase?open! "); ????filp->private_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)閉定時(shí)器*/ //等待其他處理器使用完定時(shí)器再刪除 del_timer_sync(&dev->timer); break; caseOPEN_CMD:/*打開定時(shí)器*/ timerperiod=dev->timeperiod; mod_timer(&dev->timer,jiffies+msecs_to_jiffies(timerperiod)); break; caseSETPERIOD_CMD:/*設(shè)置定時(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, }; /*定時(shí)器回調(diào)函數(shù)*/ voidtimer_function(unsignedlongarg) { structnewchr_dev*dev=(structnewchr_dev*)arg; staticintsta=1; sta=!sta;/*每次都取反,實(shí)現(xiàn)LED燈反轉(zhuǎn)*/ gpio_set_value(dev->led_gpio,sta); /*重啟定時(shí)器*/ mod_timer(&dev->timer,jiffies+msecs_to_jiffies(dev->timeperiod)); } /* *@description:驅(qū)動(dòng)入口函數(shù) *@param:無 *@return:0成功;其他失敗 */ staticint__initchrdevbase_init(void) { /*注冊(cè)字符設(shè)備驅(qū)動(dòng)*/ /*1、創(chuàng)建設(shè)備號(hào)*/ if(chrdevbase.major){/*定義了設(shè)備號(hào)*/ chrdevbase.devid=MKDEV(chrdevbase.major,0); register_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT,CHRDEVBASE_NAME); }else{/*沒有定義設(shè)備號(hào)*/ alloc_chrdev_region(&chrdevbase.devid,0,CHRDEVBASE_CNT,CHRDEVBASE_NAME);/*申請(qǐng)?jiān)O(shè)備號(hào)*/ chrdevbase.major=MAJOR(chrdevbase.devid);/*獲取主設(shè)備號(hào)*/ chrdevbase.minor=MINOR(chrdevbase.devid);/*獲取次設(shè)備號(hào)*/ } printk("newcheledmajor=%d,minor=%d ",chrdevbase.major,chrdevbase.minor); /*2、初始化cdev*/ chrdevbase.cdev.owner=THIS_MODULE; cdev_init(&chrdevbase.cdev,&chrdevbase_fops); /*3、添加一個(gè)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ū)動(dòng)出口函數(shù) *@param:無 *@return:無 */ staticvoid__exitchrdevbase_exit(void) { gpio_set_value(chrdevbase.led_gpio,1);/*卸載驅(qū)動(dòng)的時(shí)候關(guān)閉LED*/ del_timer_sync(&chrdevbase.timer);/*刪除timer*/ /*注銷字符設(shè)備*/ cdev_del(&chrdevbase.cdev);/*刪除cdev*/ unregister_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT);/*注銷設(shè)備號(hào)*/ device_destroy(chrdevbase.class,chrdevbase.devid);/*銷毀設(shè)備*/ class_destroy(chrdevbase.class);/*銷毀類*/ printk("[BSP]chrdevbaseexit! "); } /* *將上面兩個(gè)函數(shù)指定為驅(qū)動(dòng)的入口和出口函數(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)閉定時(shí)器*/ #defineOPEN_CMD(_IO(0XEF,0x2))/*打開定時(shí)器*/ #defineSETPERIOD_CMD(_IO(0XEF,0x3))/*設(shè)置定時(shí)器周期命令*/ /* *@description:main主程序 *@param-argc:argv數(shù)組元素個(gè)數(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ū)動(dòng)文件*/ fd=open(filename,O_RDWR); if(fd0){ ????????printf("[APP]Can't?open?file?%s ",?filename); ????????return?-1; ????} ????while?(1)?{ ????????printf("Input?CMD:"); ????????ret?=?scanf("%d",?&cmd); ????????if?(ret?!=?1)?{?/*?參數(shù)輸入錯(cuò)誤?*/ ????????????return?1;?/*?防止卡死?*/ ????????} ????????if(cmd?==?1)?/*?關(guān)閉?LED?燈?*/ ????????????cmd?=?CLOSE_CMD; ????????else?if(cmd?==?2)?/*?打開?LED?燈?*/ ????????????cmd?=?OPEN_CMD; ????????else?if(cmd?==?3)?{ ????????????cmd?=?SETPERIOD_CMD;?/*?設(shè)置周期值?*/ ????????????printf("Input?Timer?Period:"); ????????????ret?=?scanf("%d",?&arg); ????????????if?(ret?!=?1)?{?/*?參數(shù)輸入錯(cuò)誤?*/ ????????????????return?1;?/*?防止卡死?*/ ????????????} ????????} ????????ioctl(fd,?cmd,?arg);?/*?控制定時(shí)器的打開和關(guān)閉?*/ ????} ????close(fd); ????return?0; }
使用:
上文就是簡(jiǎn)單介紹了一下定時(shí)器, 簡(jiǎn)單使用了一下定時(shí)器, 后邊根據(jù)各自需求進(jìn)一步深入學(xué)習(xí).
審核編輯:劉清
-
定時(shí)器
+關(guān)注
關(guān)注
23文章
3250瀏覽量
114859 -
FreeRTOS
+關(guān)注
關(guān)注
12文章
484瀏覽量
62195 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
316瀏覽量
21653 -
時(shí)鐘源
+關(guān)注
關(guān)注
0文章
93瀏覽量
15975 -
定時(shí)中斷
+關(guān)注
關(guān)注
0文章
19瀏覽量
8576
原文標(biāo)題:i.MX6ULL|時(shí)間管理和內(nèi)核定時(shí)器
文章出處:【微信號(hào):玩轉(zhuǎn)單片機(jī),微信公眾號(hào):玩轉(zhuǎn)單片機(jī)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論