mini2440 簡單touch screen驅動程序

模塊加載後打印出水平和垂直方向的ad轉換結果,沒有做座標的轉換,只爲學習一下touch screen接口的編程方法,代碼記錄如下:

#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>

static struct input_dev *ts_dev; //定時器用於處理長按和滑動

static struct ts_regs_t{
	unsigned long adccon;
	unsigned long adctsc;
	unsigned long adcdly;
	unsigned long adcdat0;
	unsigned long adcdat1;
	unsigned long adcupdn;
};
static volatile struct ts_regs_t *ts_regs;
static volatile unsigned long *clkcon;
static struct clk *adc_clk;
static struct timer_list ts_timer;

void enable_adc_clock(void){
	(*clkcon) |= (1<<15);
}

void enter_wait4down_mode(void){
	ts_regs->adctsc = 0xd3; //等待中斷模式,檢測觸摸屏按下
}

void enter_wait4up_mode(void){
	ts_regs->adctsc = 0x1d3; //等待中斷模式,檢測觸摸屏鬆開
}

//進入測量xy座標的工作模式
void enter_measure_xy_mode(void){
	ts_regs->adctsc = (1<<3)|(1<<2);
}

//啓動ac轉換
void start_adc(void){
	ts_regs->adccon |= (1<<0);
}

//adc中斷處理函數
static irqreturn_t grh_handle_adc_irq(int irq, void *dev_id){
	static int adc_count = 0;
	static int xv=0, yv=0;

	if(ts_regs->adcdat0 & (1<<15)){
		enter_wait4down_mode();
		adc_count = 0;
		xv = yv = 0;
	}
	else{ //多次取值取平均值
		if(adc_count == 4){
			printk(KERN_EMERG"count=%d x=%d y=%d\r", adc_count, xv>>2, yv>>2);

			//上報事件
			input_report_abs(ts_dev, ABS_X, xv>>2);
			input_report_abs(ts_dev, ABS_Y, yv>>2);
			input_report_abs(ts_dev, ABS_PRESSURE, 1);
			input_report_key(ts_dev, BTN_TOUCH, 1);
			input_sync(ts_dev);
			
			adc_count = 0;
			xv = yv = 0;
			enter_wait4up_mode();
			mod_timer(&ts_timer, jiffies+HZ/100); //10ms
		}
		else{
			adc_count ++;
			xv += (ts_regs->adcdat0)&0x3ff;
			yv += (ts_regs->adcdat1)&0x3ff;
			enter_measure_xy_mode();
			start_adc();
		}
	}
	return IRQ_HANDLED;
}


//tc中斷處理函數
static irqreturn_t grh_handle_tc_irq(int irq, void *dev_id){
	if(ts_regs->adcdat0 & (1<<15)){
		//printk(KERN_EMERG"pen up!\n");
		enter_wait4down_mode();
	}
	else{
		//printk(KERN_EMERG"pen down\n");
		//enter_wait4up_mode();
		enter_measure_xy_mode();
		start_adc();
	}
	return IRQ_HANDLED;
}

//定時器超時處理函數
static void grh_handle_timer_int(unsigned long num){
	if(ts_regs->adcdat0 & (1<<15)){
		//上報事件
		input_report_abs(ts_dev, ABS_PRESSURE, 0);
		input_report_key(ts_dev, BTN_TOUCH, 0);
		input_sync(ts_dev);

		enter_wait4down_mode();
	}
	else{
		enter_measure_xy_mode();
		start_adc();
	}
}


static int ts_init(void){ 
	ts_dev = input_allocate_device();

	//設置能夠產生哪些事件
	set_bit(EV_KEY, ts_dev->evbit);
	set_bit(EV_ABS, ts_dev->evbit);
	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); /*設置壓力的取值範圍,只有0和1*/
	
	/*註冊*/
	input_register_device(ts_dev);

	//ioremap
	ts_regs = ioremap(0x58000000, sizeof(struct ts_regs_t));
	clkcon = ioremap(0x4C00000C, 4);

	//使能時鐘,使得adc可以正常工作
	adc_clk = clk_get(NULL, "adc");
    if (!adc_clk) {  
        printk(KERN_ERR "failed to get adc clock source\n");  
        return -ENOENT;  
    }  
    clk_enable(adc_clk);
	
	

	/*
	bit[14] PRESCEN=1 使能預分頻
	bit[13:6] PRESCVL=49 預分頻數值爲49(pclk=50.625M) adcclk = 50.625M/50=1.0125MHz
	bit[2] STDBM=0 不進入等待模式
	bit[1] READ_START=0
	bit[0] ENABLE_START=0
	*/
	ts_regs->adccon = (1<<14)|(49<<6);
	ts_regs->adcdly = 0xffff; //設定延時,保證數據的準確性


	//註冊中斷
	if( request_irq(IRQ_TC, grh_handle_tc_irq, IRQF_SAMPLE_RANDOM, "irq_tc", ts_dev) ){
		printk(KERN_EMERG"allocate IRQ_TC error!\n");
		return -EIO;
	}

	if( request_irq(IRQ_ADC, grh_handle_adc_irq, IRQF_SHARED|IRQF_SAMPLE_RANDOM, "irq_adc", ts_dev) ){
		printk(KERN_EMERG"allocate IRQ_ADC error!\n");
		return -EIO;
	}

	init_timer(&ts_timer);
	ts_timer.function = grh_handle_timer_int;
	add_timer(&ts_timer);

	
	enter_wait4down_mode();//等待中斷模式,檢測觸摸屏按下
	
	
	return 0;
}

static void ts_exit(void){
	iounmap(ts_regs);
	iounmap(clkcon);
	free_irq(IRQ_TC, ts_dev);
	free_irq(IRQ_ADC, ts_dev);
	input_unregister_device(ts_dev);
	input_free_device(ts_dev);
	del_timer(&ts_timer);
}

module_init(ts_init);
module_exit(ts_exit);
MODULE_AUTHOR("GRH");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("TOUCH SCREEN DRIVER");
MODULE_LICENSE("GPL");


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章