手把手教你寫Linux設(shè)備驅(qū)動(dòng)---定時(shí)器(一)(基于友善之臂4412開發(fā)板)
這個(gè)專題我們來說下Linux中的定時(shí)器。
在Linux內(nèi)核中,有這樣的一個(gè)定時(shí)器,叫做內(nèi)核定時(shí)器,內(nèi)核定時(shí)器用于控制某個(gè)函數(shù),也就是定時(shí)器將要處理的函數(shù)在未來的某個(gè)特定的時(shí)間內(nèi)執(zhí)行。內(nèi)核定時(shí)器注冊(cè)的處理函數(shù)只執(zhí)行一次,即不是循環(huán)執(zhí)行的。
如果對(duì)延遲的精度要求不高的話,最簡(jiǎn)單的實(shí)現(xiàn)方法如下---忙等待:
Unsigned long j = jiffies + jit_delay * HZ;
While(jiffies < j)
{
……
}
下面來說下具體的參數(shù)代表的含義:
jiffies:全局變量,用來記錄自系統(tǒng)啟動(dòng)以來產(chǎn)生的節(jié)拍總數(shù)。啟動(dòng)時(shí)內(nèi)核將該變量初始化為0;
此后每次時(shí)鐘中斷處理程序增加該變量的值。每一秒鐘中斷次數(shù)HZ,jiffies一秒內(nèi)增加HZ。系統(tǒng)運(yùn)行時(shí)間 = jiffie/HZ.
jiffies用途:計(jì)算流逝時(shí)間和時(shí)間管理
jiffies內(nèi)部表示:
extern u64 jiffies_64;
extern unsigned long volatilejiffies; //位長(zhǎng)更系統(tǒng)有關(guān)32/64---->
|
|
32位:497天后溢出
64位:……
在定時(shí)器中有這樣一個(gè)概念,度量時(shí)間差:
時(shí)鐘中斷由系統(tǒng)的定時(shí)硬件以周期性的時(shí)間間隔產(chǎn)生,這個(gè)間隔說白了其實(shí)就是頻率由內(nèi)核根據(jù)HZ來確定,HZ是一個(gè)與體系結(jié)構(gòu)無(wú)關(guān)的常數(shù),可以配置為(50-1200),在X86平臺(tái),它的值被默認(rèn)為1000 ;
定時(shí)器在內(nèi)核中相關(guān)的頭文件以及數(shù)據(jù)結(jié)構(gòu)如下:
#include <linux/timer.h> /*timer*/
#include <asm/uaccess.h> /*jiffies*/
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
//定時(shí)器可以作為鏈表的一個(gè)節(jié)點(diǎn)
struct list_head entry;
//定時(shí)值基于jiffies
unsigned long expires;
//定時(shí)器內(nèi)部值
struct tvec_base *base;
//定時(shí)器處理函數(shù)
void (*function)(unsigned long);
//定時(shí)器處理函數(shù)參數(shù)
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
};
定時(shí)器最基本的使用方法可以使用下面這兩個(gè)個(gè)內(nèi)核提供的宏:
//初始化定時(shí)器
#define init_timer(timer)\
init_timer_key((timer), NULL, NULL)
//注冊(cè)一個(gè)定時(shí)器
#define setup_timer(timer, fn, data)\
setup_timer_key((timer), NULL, NULL, (fn), (data))
還有以下兩個(gè)函數(shù):
添加一個(gè)定時(shí)器
void add_timer(struct timer_list *timer)
刪除一個(gè)定時(shí)器
int del_timer(struct timer_list *timer)
那么寫一個(gè)定時(shí)器的具體步驟是什么?
1、初始化內(nèi)核定時(shí)器
2、設(shè)置定時(shí)器執(zhí)行函數(shù)的參數(shù)(可有可無(wú))
3、設(shè)置定時(shí)時(shí)間
4、設(shè)置定時(shí)器函數(shù)
5、啟動(dòng)定時(shí)器
接下來,我們結(jié)合一個(gè)簡(jiǎn)單的驅(qū)動(dòng)來了解這個(gè)過程,這個(gè)驅(qū)動(dòng)非常簡(jiǎn)單,就是開機(jī)后,5s鐘后,開發(fā)板上的蜂鳴器就會(huì)每隔1s鐘交替響。
先來看看開發(fā)板的蜂鳴器的原理圖:
(1)蜂鳴器接口位于電路板的底板,看電路圖可知道是高電平有效。
(2)相對(duì)應(yīng)的找到核心板的接口。由此可知,我們的蜂鳴器是GPD0_0
接下來找數(shù)據(jù)手冊(cè),找到對(duì)應(yīng)的寄存器,然后配置它就可以了。
2、查數(shù)據(jù)手冊(cè),找到相關(guān)的寄存器,并配置
(1)找到GPD0CON,地址是0x114000A0,我們需要配置GPD0CON(0)為輸出狀態(tài)。也就是寫0x1這個(gè)值到這個(gè)寄存器。
(2)找到GPD0DAT這個(gè)寄存器,用于配置蜂鳴器的高低電平,物理地址是0x114000A4,剛好與上一個(gè)差4個(gè)字節(jié)的偏移
我們只要對(duì)這個(gè)寄存器寫1和寫0,那么蜂鳴器就可以叫起來了,哈哈。是不是很簡(jiǎn)單?
整個(gè)簡(jiǎn)單的驅(qū)動(dòng)代碼如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/timer.h> /*timer*/
#include <asm/uaccess.h> /*jiffies*/
#include <linux/delay.h>
//設(shè)備名稱
#define DEVICE_NAME "Bell"
//設(shè)備GPIO引腳
#define BUZZER_GPIO EXYNOS4_GPD0(0)
//定義一個(gè)定時(shí)器鏈表
struct timer_list timer;
static void Bell_init()
{
//1、請(qǐng)求gpio,相當(dāng)于注冊(cè)gpio
gpio_request(BUZZER_GPIO,DEVICE_NAME);
//2、調(diào)用板級(jí)驅(qū)動(dòng)的函數(shù),將gpio配置成輸出狀態(tài)
s3c_gpio_cfgpin(BUZZER_GPIO, S3C_GPIO_OUTPUT);
//3、設(shè)置gpio為0,表示低電平,蜂鳴器高電平就會(huì)響
gpio_set_value(BUZZER_GPIO,0);
}
void timer_function(unsigned long value)
{
while(value)
{
//設(shè)置gpio為1,表示高電平,蜂鳴器高電平就會(huì)響
gpio_set_value(BUZZER_GPIO,1);
printk("BUZZER ON\n");
mdelay(1000);
//設(shè)置gpio為0,表示低電平,蜂鳴器高電平就會(huì)響
gpio_set_value(BUZZER_GPIO,0);
printk("BUZZER OFF\n");
mdelay(1000);
}
}
static int __init tiny4412_Bell_init(void)
{
//bell init
Bell_init();
//初始化內(nèi)核定時(shí)器
init_timer(&timer);
//給執(zhí)行的函數(shù)傳參
timer.data= 1;
//當(dāng)前jiffies的值加上5秒鐘之后
timer.expires= jiffies + (5 * HZ);
//如果超時(shí)了就執(zhí)行這個(gè)函數(shù)
timer.function= timer_function;
//啟動(dòng)定時(shí)器
add_timer(&timer);
return 0 ;
}
static void __exit tiny4412_Bell_exit(void)
{
//釋放gpio
gpio_free(BUZZER_GPIO);
//刪除注冊(cè)的定時(shí)器
del_timer(&timer);
}
module_init(tiny4412_Bell_init);
module_exit(tiny4412_Bell_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YYX");
MODULE_DESCRIPTION("Exynos4 BELL Driver");
接下來,開啟我們開發(fā)板串口,觀察運(yùn)行結(jié)果:
果然,定時(shí)器在開發(fā)板啟動(dòng)后的若干時(shí)間后,就周而復(fù)始的去打開和關(guān)閉我們板子上的蜂鳴器了。
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!