1. 程式人生 > >LINUX下ADC按鍵驅動程式

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定義一個名字為

adc_key_time的定時器。

<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");

相關推薦

LINUXADC按鍵驅動程式

ADC按鍵驅動 Adc鍵盤原理圖如下,將串聯電阻之間分別用按鍵引出來與地相連,當按鍵按下時端電壓會發生改變。基本思想是在ADC驅動基礎上,對取樣電壓進行判斷,檢測是哪一個按鍵按下。 1.      ADC驅動分析 在init()函式中,首先獲取adc的時鐘,並用clk_e

LinuxPCI裝置驅動程式開發基本框架

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)        向客戶