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