在linux内核文件中,自带一个s3c2410_ts.c文件,实现了触摸屏的功能。现在参考该文件重新写一个驱动文件。
在init函数中,
1.硬件相关配置:
使能ADC时钟
clk = clk_get(NULL, "adc"); clk_enable(clk);
static struct clk init_clocks_disable[] = { { .name = "nand", .id = -1, .parent = &clk_h, .enable = s3c2410_clkcon_enable, .ctrlbit = S3C2410_CLKCON_NAND, }, { .name = "sdi", .id = -1, .parent = &clk_p, .enable = s3c2410_clkcon_enable, .ctrlbit = S3C2410_CLKCON_SDI, }, { .name = "adc", .id = -1, .parent = &clk_p, .enable = s3c2410_clkcon_enable, .ctrlbit = S3C2410_CLKCON_ADC, .......
进一步追踪,可以看到函数:
int s3c2410_clkcon_enable(struct clk *clk, int enable) { unsigned int clocks = clk->ctrlbit; unsigned long clkcon; clkcon = __raw_readl(S3C2410_CLKCON); if (enable) clkcon |= clocks; else clkcon &= ~clocks; /* ensure none of the special function bits set */ clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER); __raw_writel(clkcon, S3C2410_CLKCON); return 0; }
整个函数无非就是配置2440的CLKCON寄存器。在该配置中配置ADC(&Touch Screen) [15]这一位。
之后分配一个input_dev结构,这一系列过程可以参考前面的输入子系统,类似。
然后注册一个TC和ADC中断。
最后就是在中断中进行数据处理了。
代码如下:
#include <linux/errno.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/init.h> #include <linux/serio.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/gpio.h> #include <asm/io.h> #include <asm/irq.h> #include <plat/regs-adc.h> #include <mach/regs-gpio.h> #define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \ S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0)) static struct input_dev *ts_dev; static struct timer_list ts_timer; static struct clk *adc_clock; struct s3c_ts_regs { unsigned long adccon; unsigned long adctsc; unsigned long adcdly; unsigned long adcdat0; unsigned long adcdat1; unsigned long adcupdn; }; static volatile struct s3c_ts_regs *ts_regs; static void ts_wait_down_mode(void) { ts_regs->adctsc = 0xd3; } static void ts_wait_up_mode(void) { ts_regs->adctsc = 0x1d3; } static void ts_measure_xy_mode(void) { ts_regs->adctsc =(1<<2)|(1<<3); //S3C2410_ADCTSC_PULL_UP_DISABLE|/*(1<<3)*/AUTOPST; } static void ts_start_ad(void) { ts_regs->adccon |= 1; } static int s3c_filter_ts(int x[], int y[]) { #define ERR_LIMIT 10 int avr_x, avr_y; int det_x, det_y; avr_x = (x[0] + x[1])/2; avr_y = (y[0] + y[1])/2; det_x = (x[2] > avr_x) ? (x[2] - avr_x) : (avr_x - x[2]); det_y = (y[2] > avr_y) ? (y[2] - avr_y) : (avr_y - y[2]); if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT)) return 0; avr_x = (x[1] + x[2])/2; avr_y = (y[1] + y[2])/2; det_x = (x[3] > avr_x) ? (x[3] - avr_x) : (avr_x - x[3]); det_y = (y[3] > avr_y) ? (y[3] - avr_y) : (avr_y - y[3]); if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT)) return 0; return 1; } static irqreturn_t ts_adc_handler(int irq, void *dev_id) { int adcdat0,adcdat1; static int cnt=0; static int x[4],y[4]; adcdat0 = ts_regs->adcdat0; adcdat1 = ts_regs->adcdat1; /* if(adcdat0 & (1<<15)) //如果在采集过程中,已经up,数据丢弃 { printk("ts_adc_handler TS_up \n"); cnt = 0; input_report_abs(ts_dev, ABS_PRESSURE, 0); input_report_key(ts_dev, BTN_TOUCH, 0); input_sync(ts_dev); ts_wait_down_mode(); } else*/ { //printk("TS_down \n"); x[cnt] = adcdat0& 0x3ff; y[cnt] = adcdat1& 0x3ff; cnt++; if(cnt >= 4) { //printk("TS_down \n"); if(s3c_filter_ts(x,y)) { //printk("adcdat0=%d,adcdat1=%d\n",(x[0]+x[1]+x[2]+x[3])>>2,\ // (y[0]+y[1]+y[2]+y[3])>>2 ); input_report_abs(ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4); input_report_abs(ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4); input_report_abs(ts_dev, ABS_PRESSURE, 1); input_report_key(ts_dev, BTN_TOUCH, 1); input_sync(ts_dev); } cnt=0; ts_wait_up_mode(); /* 启动定时器处理长按/滑动的情况 */ mod_timer(&ts_timer, jiffies + HZ/100); } else { ts_measure_xy_mode(); ts_start_ad(); } } return IRQ_HANDLED; } static irqreturn_t ts_tc_handler(int irq, void *dev_id) { if(ts_regs->adcdat0 & (1<<15)||ts_regs->adcdat1 & (1<<15)) { //printk("ts_tc_handler TS_up \n"); input_report_abs(ts_dev, ABS_PRESSURE, 0); input_report_key(ts_dev, BTN_TOUCH, 0); input_sync(ts_dev); ts_wait_down_mode(); } else { //down mode //printk("TS_down \n"); ts_measure_xy_mode(); ts_start_ad(); } return IRQ_HANDLED; } static void ts_timer_handler(unsigned long dat) { if(ts_regs->adcdat0 & (1<<15)||ts_regs->adcdat1 & (1<<15)) { /* 已经松开 */ input_report_abs(ts_dev, ABS_PRESSURE, 0); input_report_key(ts_dev, BTN_TOUCH, 0); input_sync(ts_dev); ts_wait_down_mode(); } else { //down mode //printk("TS_down \n"); ts_measure_xy_mode(); ts_start_ad(); } return; } static int mini2440_ts_init(void) { int error; /*1.分配一个input_dev结构*/ ts_dev = input_allocate_device(); if (!ts_dev) { printk("input_allocate failed!\n"); error = -ENOMEM; return error; } /*2.设置 */ /* 2.1 设置能产生哪类事件 */ set_bit(EV_SYN, ts_dev->evbit); set_bit(EV_ABS, ts_dev->evbit); set_bit(EV_KEY, ts_dev->evbit); /*2.2 设置能产生这类操作的哪些事件 */ set_bit(BTN_TOUCH, ts_dev->keybit); input_set_abs_params(ts_dev, ABS_X, 0, 0x3FF, 0, 0); input_set_abs_params(ts_dev, ABS_Y, 0, 0x3FF, 0, 0); input_set_abs_params(ts_dev, ABS_PRESSURE, 0, 1, 0, 0); error = input_register_device(ts_dev); if (error) { printk("input_register_device failed!\n"); error = -ENOMEM; return error; } /*3.硬件相关配置*/ adc_clock = clk_get(NULL, "adc"); if (!adc_clock) { printk(KERN_ERR "failed to get adc clock source\n"); return -ENOENT; } clk_enable(adc_clock); ts_regs = ioremap(0x58000000,sizeof(struct s3c_ts_regs)); ts_regs->adccon = (1<<14)|(0xff<<6); ts_regs->adcdly = 0xffff; ts_wait_down_mode(); /*3.1初始化定时器*/ init_timer(&ts_timer); ts_timer.function = ts_timer_handler; add_timer(&ts_timer); /*3.2中断配置*/ error = request_irq(IRQ_ADC, ts_adc_handler, IRQF_SAMPLE_RANDOM, "ts_adc", NULL); if (error) { printk("request_irq failed!\n"); error = -ENOMEM; return error; } error = request_irq(IRQ_TC, ts_tc_handler, IRQF_SAMPLE_RANDOM, "mini2440_ts", NULL); if (error) { printk("IRQ_TC_irq failed!\n"); error = -ENOMEM; return error; } //ts_wait_down_mode(); return 0; } static void mini2440_ts_exit(void) { free_irq(IRQ_ADC,NULL); free_irq(IRQ_TC,NULL); del_timer(&ts_timer); //input_unregister_device(ts_regs); input_unregister_device(ts_dev); input_free_device(ts_dev); } /* 这两行指定驱动程序的初始化函数和卸载函数 */ module_init(mini2440_ts_init); module_exit(mini2440_ts_exit); /* 描述驱动程序的一些信息 */ MODULE_LICENSE("GPL");
static irqreturn_t ts_adc_handler(int irq, void *dev_id) { int adcdat0,adcdat1; static int cnt=0; static int x[4],y[4]; adcdat0 = ts_regs->adcdat0; adcdat1 = ts_regs->adcdat1; /* if(adcdat0 & (1<<15)) //如果在采集过程中,已经up,数据丢弃 { printk("ts_adc_handler TS_up \n"); cnt = 0; input_report_abs(ts_dev, ABS_PRESSURE, 0); input_report_key(ts_dev, BTN_TOUCH, 0); input_sync(ts_dev); ts_wait_down_mode(); } else*/