一、写ts驱动步骤(新手稍微看下即可,内容有点搞)
1、复制头文件;
2、写入口函数跟出口函数
3、分配一个input_dev结构体,
在头文件下插入:static struct input_dev *ts_dev;
在init中分配:ts_dev = input_allocate_device();
4、注册:在init中注册:input_register_device(ts_dev);
5、设置:
1、能产生哪类事件:
set_bit(EV_KEY, ts_dev->evbit);按键类事件
set_bit(EV_ABS, ts_dev->evbit);触摸屏事件
2、能产生这类事件的哪些事件:
set_bit(BTN_TOUCH, ts_dev->keybit);按键类里的触摸屏事件
input_set_abs_params(ts_dev, ABS_X, 0, 0xfff, 0, 0);
input_set_abs_params(ts_dev, ABS_Y, 0, 0xfff, 0, 0);
input_set_abs_params(ts_dev, ABS_PRESSURE, 0, 1, 0, 0);
***********************************************************************************
所有的触摸屏程序几乎都是上面的框架,可直接采用
***********************************************************************************
6、写硬件相关的操作:
1、使能时钟:
clk = clk_get(NULL, "adc"); //使能时钟
clk_enable(clk); /* PCLK_GATE[12]设为1 */
2、对各种寄存器进行设置
1、在init外定义一个结构体struct adc_regs {};把各种寄存器放进去,再定义一个指针
static struct adc_regs *adc_regs;
2、在init中对地址映射:adc_regs = ioremap(0x7E00B000, sizeof(struct adc_regs));
7、先只对三个寄存器进行设置:adc_regs->adccon = (1<<16) | (1<<14) | (65<<6);
adc_regs->adcdly = 0xffff; //给它最大的delay延迟时间。
adc_regs->adcclrintpndnup = 0;
8、定义第一个中断:
1、request_irq(IRQ_TC, ts_pen_down_up_isr, IRQF_SHARED, "pen_down_up", 1);
2、然后进入该函数,在该中断下写入:enter_wait_for_pen_down();
3、在init外定义这个函数,在里面定义寄存器adc_regs->adctsc = 0xd3;进入等待中断方式,检测按下;再定义一个函数static void enter_wait_for_pen_up(void),检测松开
4、写上面那个中断的中断函数:static irqreturn_t ts_pen_down_up_isr(int irq, void *dev_id);
5、在中断中判断是按下还是松开,然后对其分别进行操作:
1如果是按下的(down),进入enter_measure_xy_mode();(自动的XY坐标转化模式),再之后启动start_adc()这个函数,这就需要添加两个函数:static void enter_measure_xy_mode(void),static void start_adc(void)
6、start_adc转换不能瞬间完成,我们也不能死等,它完成后会产生一个中断,so在init中再添加一个中断:request_irq(IRQ_ADC, adc_isr, IRQF_SHARED, "adc", 1);写这个中断的中断函数
***************************************************************************************
优化措施
***************************************************************************************
9、在自动的xy坐标模式,在adc中断后,需要几毫秒时间去判断下是up还是down;
10、如果ADC完成之后,如果触摸笔已经松开,则丢弃此次结果;
11、多册测量,求平均值;
12、软件过滤;
***************************************************************************************
13、按下的时候如果是长按,滑动,那就需要启动定时器来定时。
1、static struct timer_list ts_timer;
2、在init中写入:
init_timer(&ts_timer);
ts_timer.expires = 0;
ts_timer.function = ts_timer_function;
add_timer(&ts_timer);
3、在init外设置函数:static void ts_timer_function(unsigned long data)
4、在adc_irq函数中启动定时器:mod_timer(&ts_timer, jiffies + HZ/100); HZ为1s,所以定时10ms;
14、对ts_timer_function()这个函数进行设置;
15、上报事件:
input_event(ts_dev, EV_ABS, ABS_X, x);
input_event(ts_dev, EV_ABS, ABS_Y, y);
input_event(ts_dev, EV_ABS, ABS_PRESSURE, 1);
input_event(ts_dev, EV_KEY, BTN_TOUCH, 1);
input_sync(ts_dev);
多放也不会有什么影响。
**********************************************************************************************************************
二、驱动程序
#include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/sched.h> #include <linux/pm.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/io.h> #include <linux/clk.h> #include <linux/delay.h> static struct timer_list ts_timer; static struct input_dev *ts_dev; struct adc_regs { unsigned long adccon; unsigned long adctsc; unsigned long adcdly; unsigned long adcdat0; unsigned long adcdat1; unsigned long adcupdn; unsigned long adcclrint; unsigned long reserved; unsigned long adcclrintpndnup; }; static struct adc_regs *adc_regs; static void enter_wait_for_pen_down(void) { adc_regs->adctsc = 0xd3; //进入等待中断模式,检测按下 } static void enter_wait_for_pen_up(void) { adc_regs->adctsc = 0x1d3;//进入等待中断模式,检测松开 } static void enter_measure_xy_mode(void) { adc_regs->adctsc = ((1<<7) | (1<<6) | (1<<4) | (1<<3) | (1<<2)); //自动的 xy坐标模式 } static void start_adc(void) { adc_regs->adccon |= (1<<0); //开始adc转换 } static irqreturn_t ts_pen_down_up_isr(int irq, void *dev_id) { unsigned long data0, data1; int down; data0 = adc_regs->adcdat0; //adcdat0存放X坐标 data1 = adc_regs->adcdat1; //adcdat0存放y坐标 down = (!(data0 & (1<<15))) && (!(data1 & (1<<15)));//第十五位判断是按下还是松开,0是down,1是up if (!down) //if not down { //printk("pen up\n"); enter_wait_for_pen_down(); //wait for down; /* 调用evdev_event: 保存,唤醒 */ input_event(ts_dev, EV_ABS, ABS_PRESSURE, 0); //0 is 松开 input_event(ts_dev, EV_KEY, BTN_TOUCH, 0); input_sync(ts_dev); } else //if down { //printk("pen down\n"); //enter_wait_for_pen_up(); /* 进入"自动测量x/y座标模式" */ enter_measure_xy_mode(); /* 启动ADC */ start_adc(); //start ADC change } adc_regs->adcupdn = 0; //触摸笔向上或向下中断寄存器,0表示无触摸笔向下或向上 adc_regs->adcclrint = 0; //清楚ADC中断,在这寄存器上写入任何值都将清楚相关有效的中断 adc_regs->adcclrintpndnup = 0; //查无此寄存器澹? return IRQ_HANDLED; } static irqreturn_t adc_isr(int irq, void *dev_id) { /* 为何不在这里立刻处理ADC到结果? * 因为6410的ADC有个缺点: * 当ADC刚完成时, 必须等待若干ms, * 才能读取adcdat0,adcdata1来判断 * 当前的触摸屏是被按下还是松开 */ /* 启动定时器, 是为了不在中断处理函数里等待 */ mod_timer(&ts_timer, jiffies + HZ/100); //HZ是1秒,so HZ/100 =10ms,10ms后再去判断按下还是松开 enter_wait_for_pen_up(); adc_regs->adcupdn = 0; adc_regs->adcclrint = 0; adc_regs->adcclrintpndnup = 0; return IRQ_HANDLED; } *************************************************************************************** static irqreturn_t adc_isr_ok_old(int irq, void *dev_id)//没用到 { int x,y; int adcdat0,adcdat1; int down; #if 1 /* in auto xy mode, after adc interrupt, have to wait several ms to test up/down */ udelay(1000); udelay(1000); udelay(1000); udelay(1000); #endif adcdat0 = adc_regs->adcdat0; adcdat1 = adc_regs->adcdat1; x = adcdat0 & 0xfff; y = adcdat1 & 0xfff; down = (!(adcdat0 & (1<<15))) && (!(adcdat1 & (1<<15))); //printk("adcdat0 = 0x%x, adc_dat1 = 0x%x\n", adcdat0, adcdat1); if (down) { //printk("enter_wait_for_pen_up\n"); enter_wait_for_pen_up(); //printk("x = %d, y = %d\n", x, y); input_event(ts_dev, EV_ABS, ABS_X, x); input_event(ts_dev, EV_ABS, ABS_Y, y); input_event(ts_dev, EV_ABS, ABS_PRESSURE, 1); //1表示按下,0表示松开 input_event(ts_dev, EV_KEY, BTN_TOUCH, 1); input_sync(ts_dev); /* 启动定时器 */ mod_timer(&ts_timer, jiffies + HZ/100); } else { //printk("enter_wait_for_pen_down\n"); enter_wait_for_pen_down(); input_event(ts_dev, EV_ABS, ABS_PRESSURE, 0); input_event(ts_dev, EV_KEY, BTN_TOUCH, 0); input_sync(ts_dev); } adc_regs->adcupdn = 0; adc_regs->adcclrint = 0; adc_regs->adcclrintpndnup = 0; return IRQ_HANDLED; } static void ts_timer_function_ok_old(unsigned long data) //没用到 { /* 如果触摸笔已经松开, 就没必要再次启动ADC */ /* 否则, 启动ADC */ unsigned long data0, data1; int down; data0 = adc_regs->adcdat0; data1 = adc_regs->adcdat1; down = (!(data0 & (1<<15))) && (!(data1 & (1<<15))); if (down) { enter_measure_xy_mode(); start_adc(); } else { enter_wait_for_pen_down(); input_event(ts_dev, EV_ABS, ABS_PRESSURE, 0); input_event(ts_dev, EV_KEY, BTN_TOUCH, 0); input_sync(ts_dev); } } ********************************************************************** static void ts_timer_function(unsigned long data) { /* 如果触摸笔已经松开, 就没必要再次启动ADC */ /* 否则, 启动ADC */ unsigned long data0, data1; int down; int x, y; data0 = adc_regs->adcdat0; data1 = adc_regs->adcdat1; down = (!(data0 & (1<<15))) && (!(data1 & (1<<15))); if (down) { x = data0 & 0xfff; y = data1 & 0xfff; input_event(ts_dev, EV_ABS, ABS_X, x); input_event(ts_dev, EV_ABS, ABS_Y, y); input_event(ts_dev, EV_ABS, ABS_PRESSURE, 1); input_event(ts_dev, EV_KEY, BTN_TOUCH, 1); input_sync(ts_dev); enter_measure_xy_mode(); start_adc(); } else { enter_wait_for_pen_down(); input_event(ts_dev, EV_ABS, ABS_PRESSURE, 0); input_event(ts_dev, EV_KEY, BTN_TOUCH, 0); input_sync(ts_dev); } } static int ts_init(void) { struct clk *clk; /* 1. 分配input_dev */ ts_dev = input_allocate_device(); /* 2. 设置 */ /* 2.1 能产生哪类事件 */ set_bit(EV_KEY, ts_dev->evbit);//按键事件 set_bit(EV_ABS, ts_dev->evbit);//触摸屏事件 /* 2.2 能产生这类事件里的哪些事件 */ set_bit(BTN_TOUCH, ts_dev->keybit);//按键事件中的触摸屏事件 input_set_abs_params(ts_dev, ABS_X, 0, 0xfff, 0, 0);//这些内容可直接复制粘贴 input_set_abs_params(ts_dev, ABS_Y, 0, 0xfff, 0, 0); input_set_abs_params(ts_dev, ABS_PRESSURE, 0, 1, 0, 0);//压力。只有0和1,要么按下,要么松开。 /* 3. 注册 */ input_register_device(ts_dev); /* 4. 硬件相关 */ adc_regs = ioremap(0x7E00B000, sizeof(struct adc_regs)); clk = clk_get(NULL, "adc"); //使能时钟 clk_enable(clk); /* PCLK_GATE[12]设为1 */ /* bit[16] : 1 = 12-bit A/D conversion * bit[14] : 1 - enable A/D converter prescaler enable * bit[13:6] : A/D converter prescaler value, * PCLK=66500000, adcclk=pclk/(n+1) * 取值13, adclk=66.5MHz/14=4.75 * */ adc_regs->adccon = (1<<16) | (1<<14) | (65<<6);/*貌似没有16位,个人觉得那个不用设置。*/ adc_regs->adcdly = 0xffff; //给它最大的delay延迟时间。 adc_regs->adcclrintpndnup = 0; request_irq(IRQ_TC, ts_pen_down_up_isr, IRQF_SHARED, "pen_down_up", 1); request_irq(IRQ_ADC, adc_isr, IRQF_SHARED, "adc", 1); init_timer(&ts_timer); ts_timer.expires = 0; ts_timer.function = ts_timer_function; add_timer(&ts_timer); /* 进入"wait for interrupt mode", 等待触摸笔按下或松开的模式 */ enter_wait_for_pen_down(); return 0; } static void ts_exit(void) { del_timer(&ts_timer); free_irq(IRQ_TC, 1); free_irq(IRQ_ADC, 1); iounmap(adc_regs); input_unregister_device(ts_dev); input_free_device(ts_dev); } module_init(ts_init); module_exit(ts_exit); MODULE_LICENSE("GPL");