LINUX下ADC按键驱动程序

来源:互联网 发布:淘宝联盟分享赚任务 编辑:程序博客网 时间:2024/06/04 18:11

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

0 0
原创粉丝点击