TQ2440触摸屏驱动程序的移植

按照天嵌官方的《linux移植之step by step》手册上的方法,做linux2.6.30.4触摸屏驱动程序的移植没有成功,经过一番摸索发现是2.6.30.4没有合适的ADC驱动,所以就着手自己做ADC和触摸屏驱动的移植,下面是我解决问题的详细过程:

1.找到天嵌给的linux2.6.30.4的源码,进入drivers/char 目录,把ADC驱动程序的源码抠出来,注意还有那个ADC驱动程序要调用的头文件,我的是直接把头文件加到ADC驱动程序的源码里面,下面是加入头文件修改后的ADC驱动程序的源代码(我把他命名为my_adc.c):

#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/clk.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <mach/regs-clock.h>
#include <plat/regs-timer.h>
	 
#include <plat/regs-adc.h>
#include <mach/regs-gpio.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

#ifndef _S3C2410_ADC_H_
#define _S3C2410_ADC_H_

#define ADC_WRITE(ch, prescale)	((ch)<<16|(prescale))

#define ADC_WRITE_GETCH(data)	(((data)>>16)&0x7)
#define ADC_WRITE_GETPRE(data)	((data)&0xff)

#endif /* _S3C2410_ADC_H_ */

#undef DEBUG
//#define DEBUG
#ifdef DEBUG
#define DPRINTK(x...) {printk("EmbedSky_adc: " x);}
#else
#define DPRINTK(x...) (void)(0)
#endif

#define DEVICE_NAME	"adc"

#define ch_0 0
#define ch_1 1
#define ch_2 2
#define ch_3 3

static void __iomem *base_addr;

typedef struct
{
	wait_queue_head_t wait;
	int channel;
	int prescale;
}ADC_DEV;

DECLARE_MUTEX(ADC_LOCK);
static int ADC_enable = 0;

static ADC_DEV adcdev;
static volatile int ev_adc = 0;
static int adc_data;

static struct clk	*adc_clock;

#define ADCCON			(*(volatile unsigned long *)(base_addr + S3C2410_ADCCON))	//ADC control
#define ADCTSC			(*(volatile unsigned long *)(base_addr + S3C2410_ADCTSC))	//ADC touch screen control
#define ADCDLY			(*(volatile unsigned long *)(base_addr + S3C2410_ADCDLY))	//ADC start or Interval Delay
#define ADCDAT0		(*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT0))	//ADC conversion data 0
#define ADCDAT1		(*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT1))	//ADC conversion data 1
#define ADCUPDN		(*(volatile unsigned long *)(base_addr + 0x14))			//Stylus Up/Down interrupt status

#define PRESCALE_DIS	(0 << 14)
#define PRESCALE_EN	(1 << 14)
#define PRSCVL(x)		((x) << 6)
#define ADC_INPUT(x)	((x) << 3)
#define ADC_START		(1 << 0)
#define ADC_ENDCVT		(1 << 15)

#define START_ADC_AIN(ch, prescale) 	do{ 	ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; 		ADCCON |= ADC_START; 	}while(0)

int iSaveADCTSC=0;

static int tq2440_adc_ioctl(
	struct inode *inode, 
	struct file *file, 
	unsigned int cmd, 
	unsigned long arg)
{
	if (arg > 4)
	{
		return -EINVAL;
	}

	switch(cmd)
	{
		case ch_0:

			adcdev.channel=0;
			return 0;

		case ch_1:

			adcdev.channel=1;
			return 0;
		case ch_2:

			adcdev.channel=2;
			return 0;
		case ch_3:

			adcdev.channel=3;
			return 0;

		default:
			return -EINVAL;
	}
}

static ssize_t tq2440_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
	char str[20];
	int value;
	size_t len;
	if (down_trylock(&ADC_LOCK) == 0)
	{
		ADC_enable = 1;
		START_ADC_AIN(adcdev.channel, adcdev.prescale);

		iSaveADCTSC=ADCTSC;
		ADCTSC=ADCTSC&(~0x1<<2);
		ADCCON|=0x01;
		while(ADCCON&0x1);
		while(!ADCCON&(0x1<<15));
		adc_data = ADCDAT0 & 0x3ff;
		ADCTSC=iSaveADCTSC;
		ev_adc = 0;

		DPRINTK("AIN[%d] = 0x%04x, %d\n", adcdev.channel, adc_data, ((ADCCON & 0x80) ? 1:0));

		value = adc_data;
		sprintf(str,"%5d", adc_data);
		copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data));

		ADC_enable = 0;
		up(&ADC_LOCK);

		len = sprintf(str, "%d\n", value);
		if (count >= len)
		{
			int r = copy_to_user(buffer, str, len);
			return r ? r : len;
		}
		else
		{
			return -1;
		}
	}
	else
	{
		return -1;
		//value = -1;
	}
}

static int tq2440_adc_open(struct inode *inode, struct file *filp)
{
	init_waitqueue_head(&(adcdev.wait));

	adcdev.channel=2;
	adcdev.prescale=0xff;

	DPRINTK( "ADC opened\n");

	return 0;
}

static int tq2440_adc_release(struct inode *inode, struct file *filp)
{
	DPRINTK( "ADC closed\n");
	return 0;
}

static struct file_operations dev_fops = {
	.owner		= THIS_MODULE,
	.open		= tq2440_adc_open,
	.ioctl		= tq2440_adc_ioctl,
	.read		= tq2440_adc_read,
	.release	= tq2440_adc_release,
};

static struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &dev_fops,
};

static int __init dev_init(void)
{
	int ret;

	base_addr=ioremap(S3C2410_PA_ADC,0x20);
	if (base_addr == NULL)
	{
		printk(KERN_ERR "failed to remap register block\n");
		return -ENOMEM;
	}

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

	ADCTSC = 0;

	ret = misc_register(&misc);

	printk (DEVICE_NAME" initialized\n");
	return ret;
}

static void __exit dev_exit(void)
{

	if (adc_clock)
	{
		clk_disable(adc_clock);
		clk_put(adc_clock);
		adc_clock = NULL;
	}

	misc_deregister(&misc);
}

EXPORT_SYMBOL(ADC_LOCK);
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");

上述程序的源码以后再作分析。

2.进入2.6.30.4源码目录:drivers/input/touchscreen ,把触摸屏驱动程序的源码抠出来(我把他命名为my_ts.c):

#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 <plat/regs-adc.h>
#include <mach/regs-gpio.h>

#define S3C2410TSVERSION	0x0101

#define WAIT4INT(x)  (((x)<<8) | 		     S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | 		     S3C2410_ADCTSC_XY_PST(3))

#define AUTOPST	     (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | 		     S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))

static char *tq2440ts_name = "TQ2440 TouchScreen";

static	struct input_dev *dev;
static	long xp;
static	long yp;
static	int count;

extern struct semaphore ADC_LOCK;
static int OwnADC = 0;

static void __iomem *base_addr;

static void touch_timer_fire(unsigned long data)
{
  	unsigned long data0;
  	unsigned long data1;
	int updown;

  	data0 = ioread32(base_addr+S3C2410_ADCDAT0);
  	data1 = ioread32(base_addr+S3C2410_ADCDAT1);

 	updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));

 	if (updown) {
 		if (count != 0)
 		{
			long tmp;
                                                                                                 
			tmp = xp;
			xp = yp;
			yp = tmp;
                                                                                                 
                        xp >>= 2;
                        yp >>= 2;

 			input_report_abs(dev, ABS_X, xp);
 			input_report_abs(dev, ABS_Y, yp);

 			input_report_key(dev, BTN_TOUCH, 1);
			input_report_abs(dev, ABS_PRESSURE, 1);
 			input_sync(dev);
 		}

 		xp = 0;
 		yp = 0;
 		count = 0;

 		iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
 		iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
 	}
 	else
 	{
 		count = 0;

 		input_report_key(dev, BTN_TOUCH, 0);
 		input_report_abs(dev, ABS_PRESSURE, 0);
 		input_sync(dev);

 		iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
		if (OwnADC)
		{
			OwnADC = 0;
			up(&ADC_LOCK);
		}
 	}
}

static struct timer_list touch_timer =
		TIMER_INITIALIZER(touch_timer_fire, 0, 0);

static irqreturn_t stylus_updown(int irq, void *dev_id)
{
	unsigned long data0;
	unsigned long data1;
	int updown;

	if (down_trylock(&ADC_LOCK) == 0)
	{
		OwnADC = 1;
		data0 = ioread32(base_addr+S3C2410_ADCDAT0);
		data1 = ioread32(base_addr+S3C2410_ADCDAT1);

		updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));

		if (updown)
		{
			touch_timer_fire(0);
		}
		else
		{
			OwnADC = 0;
			up(&ADC_LOCK);
		}
	}

	return IRQ_HANDLED;
}

static irqreturn_t stylus_action(int irq, void *dev_id)
{
	unsigned long data0;
	unsigned long data1;

	if (OwnADC)
	{
		data0 = ioread32(base_addr+S3C2410_ADCDAT0);
		data1 = ioread32(base_addr+S3C2410_ADCDAT1);

		xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
		yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
		count++;

		if (count < (1<<2))
		{
			iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
			iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
		}
		else
		{
			mod_timer(&touch_timer, jiffies+1);
			iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
		}
	}

	return IRQ_HANDLED;
}

static struct clk	*adc_clock;

static int __init tq2440ts_init(void)
{
	struct input_dev *input_dev;

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

	base_addr=ioremap(S3C2410_PA_ADC,0x20);
	if (base_addr == NULL)
	{
		printk(KERN_ERR "Failed to remap register block\n");
		return -ENOMEM;
	}

	iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),base_addr+S3C2410_ADCCON);
	iowrite32(0xffff,  base_addr+S3C2410_ADCDLY);
	iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);

	/* Initialise input stuff */
	input_dev = input_allocate_device();

	if (!input_dev)
	{
		printk(KERN_ERR "Unable to allocate the input device !!\n");
		return -ENOMEM;
	}

	dev = input_dev;
	dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
	dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);
	input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0);
	input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0);
	input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);

	dev->name = tq2440ts_name;
	dev->id.bustype = BUS_RS232;
	dev->id.vendor = 0xDEAD;
	dev->id.product = 0xBEEF;
	dev->id.version = S3C2410TSVERSION;

	if (request_irq(IRQ_ADC, stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM, tq2440ts_name, dev))
	{
		printk(KERN_ERR "tq2440_ts.c: Could not allocate ts IRQ_ADC !\n");
		iounmap(base_addr);
		return -EIO;
	}
	if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, tq2440ts_name, dev))
	{
		printk(KERN_ERR "tq2440_ts.c: Could not allocate ts IRQ_ADC !\n");
		iounmap(base_addr);
		return -EIO;
	}

	printk(KERN_INFO "%s successfully loaded\n", tq2440ts_name);

	input_register_device(dev);

	return 0;
}

static void __exit tq2440ts_exit(void)
{
	disable_irq(IRQ_ADC);
	disable_irq(IRQ_TC);
	free_irq(IRQ_TC,dev);
	free_irq(IRQ_ADC,dev);

	if (adc_clock)
	{
		clk_disable(adc_clock);
		clk_put(adc_clock);
		adc_clock = NULL;
	}

	input_unregister_device(dev);
	iounmap(base_addr);
}

module_init(tq2440ts_init);
module_exit(tq2440ts_exit);

源码暂时不作分析。

3.个人建议自己先把上述源码都交叉编译成内核模块,至于怎么交叉编译成内核模块,这里就不再赘述了,在编译模块的过程中可以检查驱动程序的错误与否,不然你要是直接抠出来就放进内核源码里面,要是等下你编译内核的时候出错,这个就比较麻烦和耗时间了,好了,如果编译内核模块的这个过程中驱动程序源码没有什么问题的话,接下来你就可以放心大胆地将my_adc.c和my_ts.c放进内核源码中,并且修改相应的kconfig和Makefile文件了,具体步骤如下:

3.1 把my_adc.c放入内核源码drivers/char 这个目录下,并且修改这个目录下的kconfig和Makefile文件:

在kconfig里面把config TQ2440_ADC的代码改成下面所示的代码:

config MY_ADC
	bool "My ADC driver for TQ2440"
	depends on ARCH_S3C2440
	default y if ARCH_S3C2440
	help
	  My ADC driver for TQ2440.

在Makefile里面把obj-$(CONFIG_TQ2440_ADC) += EmbedSky_adc.o 改成下面所示的代码:

obj-$(CONFIG_MY_ADC)		+= my_adc.o

3.2 把my_ts.c放入内核源码drivers/input/touchscreen下,并修改这个目录下的kconfig和Makefile文件:

在kconfig里面把原来的config TOUCHSCREEN_TQ2440的代码修改成如下所示的代码:

config TOUCHSCREEN_MY2440
	tristate " MY2440 TouchScreen input driver"
	depends on ARCH_S3C2410 && INPUT && INPUT_TOUCHSCREEN && MY_ADC
	help
	  Say Y here if you have the MY2440 TouchScreen.
          and depends on MY_ADC

	  If unsure, say N.

	  To compile this driver as a module, choose M here: the
	  module will be called my_ts.

在Makefile里面把原来的obj-$(CONFIG_TOUCHSCREEN_TQ2440) += tq2440_ts.o 修改成下面所示的代码:

obj-$(CONFIG_TOUCHSCREEN_MY2440) += my_ts.o

4.至此修改linux内核源码的工作就结束了,接下来我们来配置和编译内核:

进入修改后的源码目录linux2.6.30.4下,找到config_EmbedSky_W43(我的显示屏是4.3寸的),然后输入指令:cp config_EmbedSky_W43 .config ,再然后输入指令:make menuconfig ,接着会弹出内核配置的图形界面,具体操作如下:

4.1 进入Device Drivers--> Character devices 把 My ADC driver for TQ2440 这个选项选上,如下图所示:

4.2 进入 Device Drivers  --->Input device support  ---> Touchscreens  --->MY2440 TouchScreen input driver 这个选项选上,如下图所示:

4.3 最后回到 Linux Kernel Configuration ,选择 Save an Alternate Configuration File ,保存你刚才所做的所有设置。

5.好了,到此内核源码的修改和内核的配置工作你都完成了,接下来你就可以编译内核了,输入指令:make 然后就开始编译内核了,等到结束后,找到zImage.bin这个文件便是我们编译好的内核镜像文件,然后把这个内核烧写进开发板,你会发现触摸屏就奇迹般地能够用了,至此TQ2440触摸屏驱动程序的移植就结束了。

时间: 2024-10-25 01:04:42

TQ2440触摸屏驱动程序的移植的相关文章

TQ2440四线电阻式触摸屏驱动程序的分析

一,编写触摸屏驱动程序的准备知识之一:输入子系统  1.输入子系统简单介绍 linux系统提供了input子系统,按键.触摸屏.鼠标等输入型设备都可以利用input接口函数来实现设备驱动. 2.输入子系统的组成 输入子系统由驱动层(drivers).输入子系统核心层(input core)和事件处理层(Event Handler)三部分组成. 驱动层:将底层的硬件输入转化为统一的事件型式,向输入核心层汇报. 输入子系统核心层:为驱动层提供输入设备注册与操作接口,如input_register_d

针对TQ2440开发板上linux2.6.30.4内核的交叉编译和驱动程序的移植

最近由于要做TQ2440(ARM9)开发板上的驱动实验,所以要在主机(ubuntu13.10)上针对前面的目标板上的系统内核进行交叉编译. 一,内核的交叉编译: 1.准备2.6.30.4版本的内核源码包,这个我使用的是天嵌公司提供的内核源码包linux-2.6.30.4_20121214.tar.bz2. 2.打开终端输入指令:tar vxjf linux-2.6.30.4_20121214.tar.bz2 解压内核源码包得到一个名字为opt的文件夹,然后输入指令:sudo mv opt /us

u-boot-2015.01在tq2440上的初步移植

作者: 彭东林 邮箱: [email protected] QQ:   405728433 开发板:     tq2440 工具:       Win7 + VMware + Debian6 U-boot版本: u-boot-2015.01 Linux版本:  天嵌自带的 linux-2.6.30.4 GCC版本:     gcc version 4.3.3 (Sourcery G++ Lite 2009q1-176)   之前由于移植过u-boot-2014.04到tq2440上,现在移植u-

【Linux驱动】TQ2440 LED驱动程序

★总体介绍 LED驱动程序主要实现了TQ2440开发板上的4个LED灯的硬件驱动,实现了对引脚GPIOB5.GPIOB6.GPIOB7.GPIOB8的高低电平设置(common-smdk.c中已经实现了对引脚的配置),利用测试程序调用该驱动程序,通过命令控制LED灯的亮灭. ★详细介绍 1.驱动程序代码:My_led.c #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #

触摸屏驱动程序

触摸屏驱动程序框架与上一片文章的输入子系统类似,只是底层驱动由按键变成了触摸屏. S3C2440的ADC相关寄存器: struct s3c_ts_regs { unsigned long adccon; unsigned long adctsc; unsigned long adcdly; unsigned long adcdat0; unsigned long adcdat1; unsigned long adcupdn; }; 1.分配input_dev结构体 struct input_de

网卡触摸屏驱动程序

一.Linux网络体系架构 二.网卡驱动设计 三.Dm9000网卡驱动分析 四.Linux输入子系统 五.触摸屏驱动设计 网卡触摸屏驱动程序

Linux驱动学习之TQ2440 DM9000E网卡驱动移植(Linux-2.6.30.4)

引言 在之前的文章中,我们介绍了如何使用Scala IDE也就是eclipse中集成的Scala开发插件来进行Scala语言程序的开发,在使用了一段时间之后,发现eclipse对Scala的支持并不是很好.用户体验比较差,比如联想速度比较慢等.由于在公司一直使用的Scala开发工具是Intellij IDEA(好吧,其实我使用Scala IDE的目的就是想试一下这两个各有什么优缺点),各方面感觉还不错,所以在此介绍一下这个开发环境. Intellij IDEA是jetbrain开发的一个IDE,

005触摸屏驱动程序

一. 触摸屏驱动程序之概念介绍(第十六课/第一节) 内核会带有(s3c2410_ts.c)触摸屏的驱动程序(平台驱动),大概浏览,然后仿造 注册一个平台驱动 若内核里面有同名的平台设备驱动程序的时候,probe函数就会被调用 分配一个input_dev结构体 设置它 注册 当有事情发生时会上报事件 1th.现在开始写触摸屏驱动代码** 先从入口函数开始 第一步:分配一个input_dev结构体 第二步:设置 设置能产生哪类事件 设置能产生该类事件里哪些事件 第三步:注册 触摸屏原理: 触摸屏使用

linux驱动之触摸屏驱动程序

触摸屏归纳为输入子系统,这里主要是针对电阻屏,其使用过程如下 :当用触摸笔按下时,产生中断.在中断处理函数处理函数中启动ADC转换x,y坐标.ADC结束,产生ADC中断,在ADC中断处理函数里上报(input_event)启动定时器,再次启动定时器(可以处理滑动.长按),松开按键.其驱动程序的写法和之前写输入子系统的写法基本上一致.写出入口函数,出口函数并加以修饰,加入相关头文件,然后开始完善各函数,在入口函数中分配input_dev结构体,设置(能产生哪类事件,能产生这类事件中的哪些事件),注