天天看點

Linux驅動之觸摸屏

我的開發闆是fl2440,其他的開發闆在以下代碼中基本上是一樣的,還需要注意的是,不同的核心用的頭檔案可能不一樣。還要注意下lcd顯示屏和觸摸屏是兩個概念,觸摸屏用的是兩章很薄的電阻紙片重合在一起貼在lcd顯示屏上的。觸摸屏是對adc轉換的一種應用,初學者要多看寄存器手冊,上面講得很詳細,開始可以參考别人的代碼,但是一定要了解過來,轉換成自己的思想,廢話不多說了,請看下面的代碼。

核心版本2.6.28.7       虛拟機ubuntu :9.1

#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 <asm/io.h>  

#include <asm/irq.h>  

#include <mach/regs-gpio.h>  

#include <mach/s3c2410_ts.h>  

#include <plat/regs-adc.h>  

struct s3c_ts_regs {   /* 是adc所用到的寄存器,放到結構體中友善我們後面用指針操作 ,隻是定義了這種類型沒有配置設定記憶體,不能添加static,後面代碼中必須把這個結構體的首位址映射到adc寄存器的首位址,我們就可以直接操作adc寄存器了,記住adc寄存器必須是連續的,我們映射是一一對應的,我們才入口函數中映射*/

unsigned long adccon;

unsigned long adctsc;

unsigned long adcdly;

unsigned long adcdat0;

unsigned long adcdat1;

unsigned long adcupdn;

};

static struct input_dev *s3c_ts_dev;                 /* 定義input_dev類型的結構體指針,input輸入事件是重中之重 */

static volatile struct s3c_ts_regs *s3c_ts_regs;

static struct timer_list ts_timer;                 /* 對定時器的操作,友善定時 */

static void enter_wait_pen_down_mode(void)   /* 等待觸摸筆按下模式 */

{

s3c_ts_regs->adctsc = 0xd3;

}

static void enter_wait_pen_up_mode(void)

 /* 等待觸摸筆按下模式 */

s3c_ts_regs->adctsc = 0x1d3;

static void enter_measure_xy_mode(void)   /* 等進入測量模式 */

s3c_ts_regs->adctsc = (1<<3)|(1<<2);//禁止上拉,設定自動進入測量模式

static void start_adc(void)     /* 開始啟動轉換adc */

s3c_ts_regs->adccon |= (1<<0);

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

return 1;

static void s3c_ts_timer_function(unsigned long data)    /* 定時器的子函數 */

if (s3c_ts_regs->adcdat0 & (1<<15))

/* 已經松開 */

input_report_abs(s3c_ts_dev, abs_pressure, 0);   /* 上報事件 */

input_report_key(s3c_ts_dev, btn_touch, 0);

input_sync(s3c_ts_dev);

enter_wait_pen_down_mode();

else

/* 測量x/y坐标 */

enter_measure_xy_mode();   /* 進入測量模式 */

start_adc();

/* 開始轉換後adc */

static irqreturn_t pen_down_up_irq(int irq, void *dev_id)   /* 觸摸筆按下将産生一個中斷 */

//printk("pen up\n");

input_report_abs(s3c_ts_dev, abs_pressure, 0);

//printk("pen down\n");

//enter_wait_pen_up_mode();

enter_measure_xy_mode();

return irq_handled;

static irqreturn_t adc_irq(int irq, void *dev_id)    /* adc轉換完成将産生一個中斷 */

static int cnt = 0;

static int x[4], y[4];

int adcdat0, adcdat1;

/* 優化措施2: 如果adc完成時, 發現觸摸筆已經松開, 則丢棄此次結果 */

adcdat0 = s3c_ts_regs->adcdat0;

adcdat1 = s3c_ts_regs->adcdat1;

cnt = 0;

// printk("adc_irq cnt = %d, x = %d, y = %d\n", ++cnt, adcdat0 & 0x3ff, adcdat1 & 0x3ff);

/* 優化措施3: 多次測量求平均值 */

x[cnt] = adcdat0 & 0x3ff;

y[cnt] = adcdat1 & 0x3ff;

++cnt;

if (cnt == 4)

/* 優化措施4: 軟體過濾 */

if (s3c_filter_ts(x, y))

//printk("x = %d, y = %d\n", (x[0]+x[1]+x[2]+x[3])/4, (y[0]+y[1]+y[2]+y[3])/4);

input_report_abs(s3c_ts_dev, abs_x, (x[0]+x[1]+x[2]+x[3])/4);

input_report_abs(s3c_ts_dev, abs_y, (y[0]+y[1]+y[2]+y[3])/4);

input_report_abs(s3c_ts_dev, abs_pressure, 1);

input_report_key(s3c_ts_dev, btn_touch, 1);

enter_wait_pen_up_mode();

/* 啟動定時器處理長按/滑動的情況 */

mod_timer(&ts_timer, jiffies + hz/100);

static int s3c_ts_init(void)

struct clk* clk;

/* 1. 配置設定一個input_dev結構體 */

s3c_ts_dev = input_allocate_device();

/* 2. 設定 */

/* 2.1 能産生哪類事件 */

set_bit(ev_key, s3c_ts_dev->evbit);

set_bit(ev_abs, s3c_ts_dev->evbit);

/* 2.2 能産生這類事件裡的哪些事件 */

set_bit(btn_touch, s3c_ts_dev->keybit);

input_set_abs_params(s3c_ts_dev, abs_x, 0, 0x3ff, 0, 0);

input_set_abs_params(s3c_ts_dev, abs_y, 0, 0x3ff, 0, 0);

input_set_abs_params(s3c_ts_dev, abs_pressure, 0, 1, 0, 0);

/* 3. 注冊 */

input_register_device(s3c_ts_dev);

/* 4. 硬體相關的操作 */

/* 4.1 使能時鐘(clkcon[15]) */

clk = clk_get(null, "adc");

clk_enable(clk);

/* 4.2 設定s3c2440的adc/ts寄存器 */

s3c_ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_regs));

 /* 這裡就把我們定義的結構體類型和adc寄存器位址相關連起來了,這裡映射函數有興趣的朋友可以去分析分析,看看怎麼把實體位址轉換成虛拟位址的 */

/* bit[14]  : 1-a/d converter prescaler enable

* bit[13:6]: a/d converter prescaler value,

*            49, adcclk=pclk/(49+1)=50mhz/(49+1)=1mhz

* bit[0]: a/d conversion starts by enable. 先設為0

*/

s3c_ts_regs->adccon = (1<<14)|(49<<6);

request_irq(irq_tc, pen_down_up_irq, irqf_sample_random, "ts_pen", null);

request_irq(irq_adc, adc_irq, irqf_sample_random, "adc", null);

/* 優化措施1: 

* 設定adcdly為最大值, 這使得電壓穩定後再發出irq_tc中斷

s3c_ts_regs->adcdly = 0xffff;

/* 優化措施5: 使用定時器處理長按,滑動的情況

init_timer(&ts_timer);

ts_timer.function = s3c_ts_timer_function;

add_timer(&ts_timer);

static void s3c_ts_exit(void)

free_irq(irq_tc, null);

free_irq(irq_adc, null);

iounmap(s3c_ts_regs);

input_unregister_device(s3c_ts_dev);

input_free_device(s3c_ts_dev);

del_timer(&ts_timer);

module_init(s3c_ts_init);

module_exit(s3c_ts_exit);

module_license("gpl");

測試如下:

1. make menuconfig 去掉原來的觸摸屏驅動程式

-> device drivers

  -> input device support

    -> generic input layer

      -> touchscreens

      <>   s3c2410/s3c2440 touchscreens

make uimage

使用新核心啟動

2. insmod s3c_ts.ko

按下/松開觸摸筆

1. ls /dev/event* 

3. ls /dev/event* 

4. hexdump /dev/event0

            秒       微秒   type code    value

0000000 29a4 0000 8625 0008 0003 0000 0172 0000

0000010 29a4 0000 8631 0008 0003 0001 027c 0000

0000020 29a4 0000 8634 0008 0003 0018 0001 0000

0000030 29a4 0000 8638 0008 0001 014a 0001 0000

0000040 29a4 0000 863c 0008 0000 0000 0000 0000

0000050 29a4 0000 c85e 0008 0003 0000 0171 0000

0000060 29a4 0000 c874 0008 0003 0001 027d 0000

0000070 29a4 0000 c87b 0008 0000 0000 0000 0000

0000080 29a4 0000 ed37 0008 0003 0018 0000 0000

0000090 29a4 0000 ed48 0008 0001 014a 0000 0000

00000a0 29a4 0000 ed4a 0008 0000 0000 0000 0000

或者使用tslib庫來測試校驗觸摸屏函數,前提是你lcd驅動程式能夠正常工作,具體實作方法請看我tslib移植這篇文章。

繼續閱讀