在linux內核文件中,自帶一個s3c2410_ts.c文件,實現了觸摸屏的功能。現在參考該文件重新寫一個驅動文件。
在init函數中,
1.硬件相關配置:
使能ADC時鐘
clk = clk_get(NULL, "adc");
clk_enable(clk);
其中clk_get函數在/arm/plat-s3cxx/s3c2410-clock.c中定義
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");
整個代碼基本參考韋東山視頻來寫,在測試過程中發現在ADC採集過程中,已經up,數據丟棄,這樣有些問題,不知mini2440的觸摸屏什麼問題,屏幕上方一小塊區域內,在點擊過程中就UP了,只好把那段濾波代碼註釋掉
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*/
最後經測試整個區域,觸摸都可用