LINUX下ADC按鍵驅動程式
ADC按鍵驅動
Adc鍵盤原理圖如下,將串聯電阻之間分別用按鍵引出來與地相連,當按鍵按下時端電壓會發生改變。基本思想是在ADC驅動基礎上,對取樣電壓進行判斷,檢測是哪一個按鍵按下。
1. ADC驅動分析
在init()函式中,首先獲取adc的時鐘,並用clk_enable進行使能,然後使用ioremap將ADC暫存器地址對映到kernel中(核心中對ADC只能使用虛擬地址進行訪問),之後呼叫probe()函式完成定時器的初始化和ADC暫存器的初始化。
/////////////////////////////////////////////////////////////////////////////////////////////////
核心定時器使用步驟:
<1>使用static struct timer_list adc_key_timer定義一個名字為
<2>adc_key_time.function =adc_key_timer_fun 定義一個定時器中斷服務函式。
<3>add_time(&adc_key_timer)將定時器在kernel 中註冊
<4>mod_timer(&adc_key_timer,jiffies+HZ)最後啟用定時器開始計時,時間由jiffies+HZ設定。
除錯方法:
<1>暫存器初始化一般放在probe函式中,可以使用prink來反映暫存器(特別是中斷)是否配置成功
<2>在單板下使用如下命令:
cd /proc/gcore
echo RD fe005400 7 > regs ///fe005400為暫存器地址 7為讀取暫存器個數
來讀取暫存器內是否被設定成功
/////////////////////////////////////////////////////////////////////////////////////////////////
接下來使用request_irq函式完成ADC中斷的註冊,需要注意的是在該函式中,ADC的中斷號需要加一,並且中斷標誌位設定成為IRQF_SHARED。在初始化最後部分使用misc_register將ADC當做misc裝置完成註冊。
2. 在file_operations結構體中主要定義了三個函式,其中adc_key_open是完成裝置開啟、adc_key_release是裝置關閉,adc_key_read()是完成裝置的讀取。在adc_key_read()函式中,首先定義一個全域性變數ev_adc作為按鍵按下的標誌位,當按鍵沒有按下時,使用wait_enent_interruptible進入等待佇列,當按鍵按下觸發中斷後,使用wake_up_interruptible喚醒等待佇列,然後在read函式中將按鍵標誌位ev_adc清零,使用copy_to_user()函式將kernel中的資料傳遞到應用層,供使用者使用。然後使用mod_timer()觸發定時器,跳入定時器中斷服務程式中去。
///////////////////////////////////////////////////////////////////////////////////////////////
在Linux驅動程式中,可以使用等待佇列(waitqueue)來實現阻塞程序的喚醒
等待佇列的使用步驟:
<1>DECLARE_WAIT_QUEUE_HEAD(my_queue)定義並初始化列頭"
<2> DECLARE_WAITQUEUE(name,tsk);義並初始化一個名為name的等待佇列(在本程式中並沒有使用到這部分)
<3>新增/移除等待佇列(也沒有用到這部分)
add_wait_queue()用於將等待佇列wait新增到等待佇列頭q指向的等待佇列連結串列中,而remove_wait_queue()用於將等待佇列wait從附屬的等待佇列頭q指向的等待佇列連結串列中移除。
<4>等待事件wait_event_interruptible(queue, condition);將程序掛起。
<5>wake_up_interruptible(&queue),將queue佇列中的程序喚醒,前提條件是必須保證
cndition為真,才能使用wake_up_interruptible
///////////////////////////////////////////////////////////////////////////////////////////////
3. ADC中斷處理函式
該部分是adc按鍵與一般adc驅動程式區別最大的部分,在該部分中斷中,主要使用ADC高閾值中斷和低閾值中斷來檢查按鍵的鬆開和按下,此外對採集的電壓會進行一個判斷,確定是哪一個按鍵被按下。
高閾值中斷和低閾值中斷的切換:在ADC中斷中只有一箇中斷號,因此只存在一箇中斷服務程式,因此對於高閾值中斷和低閾值中斷需要進行軟體的處理。首先將ADC設定為低閾值中斷,在按鍵按下後,ADC取樣電壓低於設定電壓,進入中斷服務程式,在中斷程式中首先判斷ADC中斷暫存器值,若為低閾值則在執行完按鍵按下處理過程後將ADC暫存器設定成為高閾值中斷。在鬆開按鍵後,當ADC取樣電壓高於設定的電壓,則進入中斷服務程式,在程式中顯示按鍵鬆開資訊後將ADC中斷暫存器設定為低閾值觸發,等待下一次按鍵的按下。
4. 原始碼如下:
/////////////////////////////////////////////////////////////////////////////////////////////////////
#include<linux/errno.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/clk.h>
#include<linux/input.h>
#include<linux/miscdevice.h>
#include<linux/timer.h>
#include<linux/sched.h>
#include<linux/interrupt.h>
#include<asm/io.h>
#include<asm/irq.h>
#include<asm/uaccess.h>
#include<linux/sched.h>
#include<linux/wait.h>
#include<linux/delay.h>
#define ADC_BASE_ADDR (0xfe005400)
#define DEVICE_NAME "adc_key"
#define REG_ADC_INTV 0x04
#define REG_ADC_CTRL 0x1c
#define REG_ADC_DIV 0x00
#define REG_ADC_DATA1 0x0c
#define REG_ADC_INTR1 0x24
#define IRQ_ADC 24
#define REG_ADC_STATUS 0x28
static void __iomem *adc_base;
static struct clk *adc_clk;
static int adc_data;
static volatile int ev_adc = 0;
static int adc_data;
static DECLARE_WAIT_QUEUE_HEAD(adc_key_waitq);
static struct timer_list adc_key_timer;
static struct botton{
int name;
intdata;
};
struct botton adc_button;
static int gcsoc030_adc_key_select(int val)
{
adc_button.data= val;
if(val>=0&&val<0x5){adc_button.name= 1;}
else if(val<0xff){adc_button.name = 2;}
else if(val<0x1ef){adc_button.name = 3;}
else if(val<0x3e0){adc_button.name = 4;}
return0;
}
static int gcsoc030_adc_key_release(structinode *inode , struct file *file)
{
return0;
}
static irqreturn_t adc_irq(int irq,void*dev_id)
{
u32val = readl(adc_base+REG_ADC_INTR1);
if(val== 0x83e00000)
{
mdelay(80);
iowrite32(1<<3,adc_base+REG_ADC_STATUS);
iowrite32(0,adc_base+REG_ADC_INTR1);
adc_data= readl(adc_base+REG_ADC_DATA1);
adc_button.data= adc_data;
iowrite32(0,adc_base+REG_ADC_CTRL);
gcsoc030_adc_key_select(adc_data);
ev_adc= 1;
wake_up_interruptible(&adc_key_waitq);
// printk("thebutton%d has been pressed ,the value is%d\n",adc_button.name,adc_button.data);
iowrite32(0x000083e0,adc_base+REG_ADC_INTR1);
}
elseif(val == 0x000083e0)
{
ev_adc= 0;
iowrite32(1<<2,adc_base+REG_ADC_STATUS);
iowrite32(0,adc_base+REG_ADC_INTR1);
iowrite32(0,adc_base+REG_ADC_CTRL);
printk("thebutton is released\n");
mod_timer(&adc_key_timer,jiffies+8*(HZ/100));
iowrite32(0x83e00000,adc_base+REG_ADC_INTR1);
}
returnIRQ_HANDLED;
}
static ssize_t gcsoc030_adc_key_read(structfile *file,char *buffer,size_t count,loff_t *ppos)
{
if(!ev_adc)
{
wait_event_interruptible(adc_key_waitq,ev_adc);
}
ev_adc= 0;
copy_to_user(buffer,(char*)&adc_button,sizeof(adc_button));
memset((char*)&adc_button,0,sizeof(adc_button));
mod_timer(&adc_key_timer,jiffies+HZ);
returnsizeof(adc_button);
}
static int gcsoc030_adc_key_open(structinode *inode,struct file *file)
{
return0;
}
static void adc_key_timer_fun(void)
{
iowrite32(0x61,adc_base+REG_ADC_CTRL);
}
static struct file_operations gcsoc030_adc_key_fops = {
.owner = THIS_MODULE,
.open = gcsoc030_adc_key_open,
.read = gcsoc030_adc_key_read,
.release = gcsoc030_adc_key_release,
};
static struct miscdevice adc_key_miscdev=
{
.minor= MISC_DYNAMIC_MINOR,
.name= DEVICE_NAME,
.fops=&gcsoc030_adc_key_fops,
};
static int adc_key_probe(void)
{
init_timer(&adc_key_timer);
adc_key_timer.function= adc_key_timer_fun;
add_timer(&adc_key_timer);
iowrite32(0x83e00000,adc_base+REG_ADC_INTR1);
iowrite32(0x100,adc_base+REG_ADC_DIV);
iowrite32(0x61,adc_base+REG_ADC_CTRL);
iowrite32(0x20,adc_base+REG_ADC_INTV);
printk("///////////////////////////////\n");
return0;
}
static int __initgcsoc030_adc_key_init(void)
{
int ret_irq,ret,flag;
adc_clk = clk_get(NULL,"adc");
if(!adc_clk)
{
printk(KERN_ERR"failedto find adc clock source\n");
return-ENOENT;
}
clk_enable(adc_clk);
adc_base= ioremap(ADC_BASE_ADDR, 0x30);
if(adc_base== NULL)
{
printk(KERN_ERR"failedto remap register block\n");
ret= -EINVAL;
gotoerr_noclk;
}
printk("ioremapsuccess base = %x\n", adc_base);
flag= adc_key_probe();
if(flag==0)
{
printk("interrupthas initied\n");
}
ret_irq= request_irq(IRQ_ADC + 1,adc_irq,IRQF_SHARED,DEVICE_NAME,1);
printk("///////////////////////////////\n");
printk("ret_irqis:%d\n",ret_irq);
if(ret_irq< 0)
{
printk(KERN_ERR"IRQ%d error%d\n",IRQ_ADC,ret);
return-EINVAL;
}
ret = misc_register(&adc_key_miscdev);
if(ret < 0){
printk(DEVICE_NAME "can'tregister major number\n");
goto err_nomap;
}
printk(DEVICE_NAME"initialiazed");
return0;
err_noclk:
clk_disable(adc_clk);
clk_put(adc_clk);
err_nomap:
iounmap(adc_base);
returnret;
}
static void __exitgcsoc030_adc_key_exit(void)
{
iounmap(adc_base);
if(adc_clk)
{
clk_disable(adc_clk);
clk_put(adc_clk);
adc_clk= NULL;
}
misc_deregister(&adc_key_miscdev);
}
module_init(gcsoc030_adc_key_init);
module_exit(gcsoc030_adc_key_exit)
MODULE_LICENSE("GPL");
相關推薦
LINUX下ADC按鍵驅動程式
ADC按鍵驅動 Adc鍵盤原理圖如下,將串聯電阻之間分別用按鍵引出來與地相連,當按鍵按下時端電壓會發生改變。基本思想是在ADC驅動基礎上,對取樣電壓進行判斷,檢測是哪一個按鍵按下。 1. ADC驅動分析 在init()函式中,首先獲取adc的時鐘,並用clk_e
Linux下PCI裝置驅動程式開發基本框架
PCI是一種廣泛採用的匯流排標準,它提供了許多優於其它匯流排標準(如EISA)的新特性,目前已經成為計算機系統中應用最為廣泛,並且最為通用的匯流排標準。Linux的核心能較好地支援PCI匯流排,本文以Intel 386體系結構為主,探討了在Linux下開發PCI裝置驅動程式的基本框架。 一、PCI匯流排
輸入子系統------鍵盤按鍵驅動程式 13.Linux鍵盤按鍵驅動 (詳解)
由上一節的輸入子系統的框架分析可知,其分三層:裝置驅動層,核心層,事件驅動層 我們在為某種裝置的編寫驅動層,只需要關心裝置驅動層,即如何驅動裝置並獲得硬體資料(如按下的按鍵資料),然後呼叫核心層提供的介面,核心層就會自動把資料提交給事件處理層。在輸入子系統中,事件驅動是標準的,適用於所有輸入類的。
嵌入式Linux裝置驅動開發之:按鍵驅動程式例項
11.6 按鍵驅動程式例項 11.6.1 按鍵工作原理 高電平和低電平相接怎麼會變成低電平呢 就像你把電源正極的負極相連一樣會把電壓拉低。大電流會從高電平引腳流向低電平引腳,把高電平引腳拉低。 LED和蜂鳴器是最簡單的GPIO的應用,都不需要任何外部
linux下網卡驅動安裝全過程
linux網卡驅動方法一,用RPM包安裝驅動程序方法:1.將驅動程序文件bcm5700-.src.rpm復制到一個臨時目錄中,並在此目錄中運行以下命令:rpm –ivh bcm5700-.src.rpm2.運行以下命令切換到驅動目錄中:cd /usr/src/redhat/SPECS/3.此目錄中會生成一個名
Linux下實現進度條程式. 通過makefile進行編譯. 建議自主完成一個彩色的進度條.
Linux下用C語言完成一個彩色進度條 1.建一個Makefile檔案 2.vim Makefile test:test.c
Linux下,為應用程式新增桌面圖示(ubuntu18.4)
一、桌面圖示位置 Lniux下桌面圖示儲存路徑為:/usr/share/applications 二、桌面圖示格式 所有桌面圖示格式均為desktop,即名為XXX.desktop 三、編輯內容(常用) // 檔案頭(必須) [Desktop Entry] /
linux下用gdb實現程式宕機時自動列印呼叫堆疊
linux下程式執行幾天莫名其妙宕機了,不能還原現場,找到宕機原因就很無語了。 一個解決辦法是使用core檔案,但是對於大型伺服器檔案,動輒幾百M的core檔案是在有點傷不起,於是想到程式宕機時自動列印呼叫堆疊。簡單實用。
4412下的pwm驅動程式-暫存器形式
這是一個操作暫存器的pwm驅動,關於pwm這一塊可以看晶片手冊,這些暫存器至於為什麼要這樣設定,手冊裡都有說明 #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h
除了vim, 還有哪些常用的牛逼的編輯器。 自行查詢資料, 調研除了gcc, 還有哪些常用的牛逼的編譯器,為什麼除錯的時候需要編譯選項中新增 -g在Linux下實現進度條程式
除了vim, 還有哪些常用的牛逼的編輯器, 並能夠橫向對比編輯器之間的區別和優缺點。 首先先有一個概念IDE(整合開發環境),厲害的有vs等等。vim以及Emacs就向著這個方向發展。為了實現其強大功能。vim有了多模式編輯有(normal,insert,vis
除了vim, 還有哪些常用的牛逼的編輯器,除了gcc, 還有哪些常用的牛逼的編譯器,為什麼除錯的時候需要編譯選項中新增 -g,調研readelf命令,Linux下實現進度條程式.
除了vim, 還有哪些常用的牛逼的編輯器 Brackets Brackets也是一款為Linux開發者設計的開原始碼編輯器,使用Brackets寫程式碼,你不會被任何事情所打斷。比如在寫HTML程式碼時,即便你沒有儲存程式碼也可以及時預覽你的Web頁面效果。你也可以使用T
第一個linux下的c語言程式
當然是列印hello world啦。 1.vim helloworld.c如下: #include <stdio.h> int main() { char *c; c = "hello world!"; printf("%s\n",c
Linux下檢視某一個程式執行所佔用的記憶體【轉】
第一種方式 top -p 程序號 [[email protected] micro-service]$ top -p 20490 top - 16:50:32 up 6 days, 2:18, 1 user, load average: 0.00,
linux下檢視某一程式所佔記憶體
原文:https://blog.csdn.net/rickiyeat/article/details/70230253 第一種方式 top -p 程序號 1 [[email protected] micro-service]$ top -p 20490 top
Linux下第一個C程式
今天開始在學習C的同時學習使用linux,通過雲伺服器來進行C開發。 接下來就是所有程式語言第一課:HelloWorld。 首先在Linux下建立新資料夾(總不能把所有學習過程的程式都堆在一個資料夾下吧
linux下後臺執行python程式並輸出到日誌檔案中。
現有"test_read_pic_and_predict.py"程式要在後臺部署,裡面有輸出內容 執行命令: nohup python -u test_read_pic_and_predict.py > test.log 2>&1 &
linux下用valgrind檢查程式記憶體洩漏
2010-12-27 http://abloz.com 2010.12.27 問題提出: 如果一個較複雜的程式,有記憶體洩漏,如何檢測? 在windows下,VC本身帶有記憶體洩漏的檢查,程式結束時輸出視窗會提示有多少memo
linux下如何執行c++程式(包括呼叫外部函式庫)
這個問題困擾了我好久,但在網路上搜索很久也沒有找到具體的,條理清晰的一篇。所以,自己動手豐衣足食(拖拖拖了一個月。。) 執行c++程式 事實證明gcc是理解不了c++的。。 用g++執行你的程式,比如:g++ main.cpp g++產生一個.out
.NET作品集:linux下的部落格程式
部落格程式架構 本部落格程式是博主11年的時候參考loachs小泥鰍部落格核心開發的.net跨平臺部落格cms,距今已有6年多了,個人部落格網站一直在用,雖然沒有wordpress那麼強大,但是當時在深究.net的同時,自己能寫一個部落格程式,並且基於獨立Linux伺服器搭建一個自己的.net網站還是挺有意思
linux下的TCP小程式
服務端是用多執行緒寫的併發伺服器,比較簡單。 功能: 1. 服務端執行後,即監聽指定的埠。允許多個連線併發執行 2. 服務端接收到客戶端請求後,根據客戶端傳過來的指令完成特定任務: a) 向客戶端傳送服務端所在機器的當前時間 b) 向客戶