arm linux触摸屏 驱动,ARM-Linux驱动-触摸屏驱动分析

//#defineCONFIG_TOUCHSCREEN_S3C2410_DEBUG

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#defineTRUE1//CoAsiaadded

#defineFALSE0//CoAsiaadded

#defineFILTER_LIMIT25//CoAsiaadded

/*Forts.dev.id.version*/

#defineS3C2410TSVERSION0x0101

#defineTSC_SLEEP(S3C2410_ADCTSC_PULL_UP_DISABLE|S3C2410_ADCTSC_XY_PST(0))

#defineWAIT4INT(x)(((x)<<8)|\

S3C2410_ADCTSC_YM_SEN|S3C2410_ADCTSC_YP_SEN|S3C2410_ADCTSC_XP_SEN|\

S3C2410_ADCTSC_XY_PST(3))

#defineAUTOPST(S3C2410_ADCTSC_YM_SEN|S3C2410_ADCTSC_YP_SEN|S3C2410_ADCTSC_XP_SEN|\

S3C2410_ADCTSC_AUTO_PST|S3C2410_ADCTSC_XY_PST(0))

#defineDEBUG_LVL"<3>"//KERN_DEBUG

staticchar*s3c2440ts_name="s3c2440TouchScreen";

/*

*Per-touchscreendata.

*/

//定义s3c2440触摸屏使用的数据结构体

structs3c2440ts{

structinput_dev*dev;

longxp;

longyp;

intcount;

intshift;

};

staticstructs3c2440tsts;

staticstructclk*adc_clock;

//__iomem声明地址空间是设备地址映射空间

staticvoid__iomem*base_addr;

//函数声明

staticvoidtouch_timer_fire(unsignedlongdata);

staticirqreturn_ttc_irq(intirq,void*dev_id);

staticirqreturn_tadc_irq(intirq,void*dev_id);

staticint__inits3c2440ts_probe(structplatform_device*pdev);

staticints3c2440ts_remove(structplatform_device*pdev);

staticints3c2440ts_resume(structplatform_device*pdev);

//定义定时器

staticstructtimer_listtouch_timer=

TIMER_INITIALIZER(touch_timer_fire,0,0);

//IRQ_TC中断处理函数

staticirqreturn_ttc_irq(intirq,void*dev_id)

{

//data0,data1用于存放读取的ADCDAT数据寄存器的值

unsignedlongdata0;

unsignedlongdata1;

intupdown;//用于存放光标的按下或提起的状态

//读取ADCDAT0、ADCDAT1数据寄存器的值

data0=readl(base_addr+S3C2410_ADCDAT0);

data1=readl(base_addr+S3C2410_ADCDAT1);

//查看数据寄存器的第15位的值

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

/*TODOweshouldnevergetaninterruptwithupdownsetwhile

*thetimerisrunning,butmaybeweoughttoverifythatthe

*timerisntrunninganyways.*/

//如果data0和data1的第15位都是0,则updown为1,则通过函数touch_timer_fire()函数来启动ADC转换

if(updown)

touch_timer_fire(0);

returnIRQ_HANDLED;

}

staticvoidtouch_timer_fire(unsignedlongdata)

{

//用于存储数据寄存器ADCDAT0、ADCDAT1的值

unsignedlongdata0;

unsignedlongdata1;

//用于存放光标是否被按下

intupdown;

data0=readl(base_addr+S3C2410_ADCDAT0);

data1=readl(base_addr+S3C2410_ADCDAT1);

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

//printk("Thenumberofupdownis%d\n",updown);

//如果光标被按下,执行

if(updown)

{

//ts.count!=0表示ADC已经转换过,下面就报告事件和光标位置数据

if(ts.count!=0)

{

ts.xp>>=ts.shift;//这里shift为2,这里实际上是求均值,四次的和/4,这样定位更加准确

ts.yp>>=ts.shift;

#ifdefCONFIG_TOUCHSCREEN_S3C2410_DEBUG

{

structtimevaltv;

do_gettimeofday(&tv);

printk(DEBUG_LVL"T:%06d,X:%03ld,Y:%03ld\n",(int)tv.tv_usec,ts.xp,ts.yp);

}

#endif

/*

下面的函数位于/include/linux/input.h,作用是报告事件

staticinlinevoidinput_report_abs(structinput_dev*dev,unsignedintcode,intvalue)

{

input_event(dev,EV_ABS,code,value);

}

*/

//报告X,Y的绝对坐标

input_report_abs(ts.dev,ABS_X,ts.xp);

input_report_abs(ts.dev,ABS_Y,ts.yp);

//报告事件,1代表光标被按下

input_report_key(ts.dev,BTN_TOUCH,1);

//报告触摸屏状态,1代表触摸屏被按下

input_report_abs(ts.dev,ABS_PRESSURE,1);

//等待接收方的确认,用于事件的同步

input_sync(ts.dev);

}

//现在光标被按下,并且ADC转换没有启动

ts.xp=0;

ts.yp=0;

ts.count=0;

//设置触摸屏控制寄存器的值为0xdcB:11011100,设置控制寄存器上拉无效,自动转换X,Y坐标

//printk("S3C2410_ADCTSC:0x%x\n",S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST);

writel(S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST,base_addr+S3C2410_ADCTSC);

//启动ADC转换

writel(readl(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_ENABLE_START,base_addr+S3C2410_ADCCON);

}

else//光标没有被按下

{

ts.count=0;

//报告事件及光标的位置状态

input_report_key(ts.dev,BTN_TOUCH,0);

input_report_abs(ts.dev,ABS_PRESSURE,0);

//等待接收方的应答,用于同步

input_sync(ts.dev);

//设置触摸屏控制寄存器为等待中断模式

writel(WAIT4INT(0),base_addr+S3C2410_ADCTSC);

}

}

staticirqreturn_tadc_irq(intirq,void*dev_id)

{

//用于存放数据寄存器的数据

unsignedlongdata0;

unsignedlongdata1;

//读取数据,这次主要读取的是位置数据

data0=readl(base_addr+S3C2410_ADCDAT0);

data1=readl(base_addr+S3C2410_ADCDAT1);

ts.xp+=data0&S3C2410_ADCDAT0_XPDATA_MASK;//累加四次准换结果的X坐标和

ts.yp+=data1&S3C2410_ADCDAT1_YPDATA_MASK;//累加四次准换结果的Y坐标和

ts.count++;//转换次数加一

//如果转换次数小于4

if(ts.count

{

//再次设置触摸屏控制寄存器上拉不使能、自动X、Y转换模式

writel(S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST,base_addr+S3C2410_ADCTSC);

//再次启动ADC转换

writel(readl(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_ENABLE_START,base_addr+S3C2410_ADCCON);

}

else//这时,ADC转换四次完成,延迟一个系统滴答,执行touch_timer_fire()函数

{

mod_timer(&touch_timer,jiffies+1);

writel(WAIT4INT(1),base_addr+S3C2410_ADCTSC);

}

returnIRQ_HANDLED;

}

/*

*Thefunctionsforinserting/removingusasamodule.

*/

/*

该结构体定义在/include/linux/platform_device.h

structplatform_device{

constchar*name;

intid;

structdevicedev;

u32num_resources;

structresource*resource;

};

*/

staticint__inits3c2440ts_probe(structplatform_device*pdev)

{

intrc;

/*

下面结构体定义在/include/mach/s3c2410_ts.h

structs3c2410_ts_mach_info{

intdelay;

intpresc;

intoversampling_shift;

};

*/

structs3c2410_ts_mach_info*info;

structinput_dev*input_dev;

/*

void*platform_data;//Platformspecificdata,devicecoredoesnttouchit

*/

info=(structs3c2440_ts_mach_info*)pdev->dev.platform_data;

if(!info)

{

printk(KERN_ERR"Hm...toobad:noplatformdataforts\n");

return-EINVAL;

}

#ifdefCONFIG_TOUCHSCREEN_S3C2410_DEBUG

printk(DEBUG_LVL"Enterings3c2440ts_init\n");

#endif

//由于ADC转换需要时钟,这里获取时钟

adc_clock=clk_get(NULL,"adc");

if(!adc_clock){

printk(KERN_ERR"failedtogetadcclocksource\n");

return-ENOENT;

}

clk_enable(adc_clock);//使能时钟

#ifdefCONFIG_TOUCHSCREEN_S3C2410_DEBUG

printk(DEBUG_LVL"gotandenabledclock\n");

#endif

//通过ioremap实现物理地址到虚拟地址的转换

base_addr=ioremap(S3C2410_PA_ADC,0x20);

if(base_addr==NULL){

printk(KERN_ERR"Failedtoremapregisterblock\n");

return-ENOMEM;

}

//设置ADCCON控制寄存器为0x4c40,设置预分频有效,预分频值为B:110001D:49

//printk("ADCCONis0x%x\n",S3C2410_ADCCON_PRSCEN|S3C2410_ADCCON_PRSCVL(info->presc&0xFF));

if((info->presc&0xff)>0)

writel(S3C2410_ADCCON_PRSCEN|S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\

base_addr+S3C2410_ADCCON);

else

writel(0,base_addr+S3C2410_ADCCON);

/*Initialiseregisters*/

/*

设置ADC开始延时寄存器ADCDLY:0x4e20

*/

//printk("ADCDLY:0x%x\n",info->delay&0xffff);

if((info->delay&0xffff)>0)

writel(info->delay&0xffff,base_addr+S3C2410_ADCDLY);

/*

设置ADC触摸屏控制寄存器ADC_TSC:0xd3B:11010011

[8]检测光标按下中断信号

[7]YM输出驱动有效(GND)

[6]YP输出驱动无效(AIN5)

[5]XM输出驱动无效(Hi-z)

[4]XP输出驱动无效(AIN7)

[3]XP上拉有效

[2]普通ADC转换

[0:1]等待中断模式测量X和Y的坐标

*/

//printk("ADC_TSC:0x%x\n",WAIT4INT(0));

writel(WAIT4INT(0),base_addr+S3C2410_ADCTSC);

/*Initialiseinputstuff*/

memset(&ts,0,sizeof(structs3c2440ts));

/*

下面的函数

为新的输入设备分配内存。

使用free_device()释放没有被注册的函数,使用input_unregister_device()解除已经注册的设备

定义在/drivers/input/input.c

structinput_dev*input_allocate_device(void)

{

structinput_dev*dev;

dev=kzalloc(sizeof(structinput_dev),GFP_KERNEL);

if(dev){

dev->dev.type=&input_dev_type;

dev->dev.class=&input_class;

device_initialize(&dev->dev);

mutex_init(&dev->mutex);

spin_lock_init(&dev->event_lock);

INIT_LIST_HEAD(&dev->h_list);

INIT_LIST_HEAD(&dev->node);

__module_get(THIS_MODULE);

}

returndev;

}

*/

input_dev=input_allocate_device();

if(!input_dev){

printk(KERN_ERR"Unabletoallocatetheinputdevice!!\n");

return-ENOMEM;

}

//下面初始化输入设备信息

ts.dev=input_dev;

ts.dev->evbit[0]=BIT_MASK(EV_SYN)|BIT_MASK(EV_KEY)|

BIT_MASK(EV_ABS);

ts.dev->keybit[BIT_WORD(BTN_TOUCH)]=BIT_MASK(BTN_TOUCH);

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

//ts.dev->private=&ts;

ts.dev->name=s3c2440ts_name;

ts.dev->id.bustype=BUS_RS232;

ts.dev->id.vendor=0xDEAD;

ts.dev->id.product=0xBEEF;

ts.dev->id.version=S3C2410TSVERSION;

ts.shift=info->oversampling_shift;

//printk("shift:%d\n",ts.shift);

/*Getirqs*/

//申请ADC中断,注意,中断类型为IRQF_SAMPLE_RANDOM|IRQF_SHARED,这样在使用触摸屏的时候

//可以调试自己的ADC转换驱动,中断处理函数为adc_irq

if(request_irq(IRQ_ADC,adc_irq,IRQF_SAMPLE_RANDOM|IRQF_SHARED,

"s3c2440_action",ts.dev)){

printk(KERN_ERR"s3c2440_ts.c:CouldnotallocatetsIRQ_ADC!\n");

iounmap(base_addr);

return-EIO;

}

//申请TC中断,中断处理函数为tc_irq

if(request_irq(IRQ_TC,tc_irq,IRQF_SAMPLE_RANDOM,

"s3c2440_action",ts.dev)){

printk(KERN_ERR"s3c2440_ts.c:CouldnotallocatetsIRQ_TC!\n");

free_irq(IRQ_ADC,ts.dev);

iounmap(base_addr);

return-EIO;

}

printk(KERN_INFO"%ssuccessfullyloaded\n",s3c2440ts_name);

/*Allwentok,soregistertotheinputsystem*/

/*这里注册设备

函数功能:

*Thisfunctionregistersdevicewithinputcore.Thedevicemustbe

*allocatedwithinput_allocate_device()andallitscapabilities

*setupbeforeregistering.

*Iffunctionfailsthedevicemustbefreedwithinput_free_device().

*Oncedevicehasbeensuccessfullyregistereditcanbeunregistered

*withinput_unregister_device();input_free_device()shouldnotbe

*calledinthiscase.

函数原型如下:

intinput_register_device(structinput_dev*dev)

{

staticatomic_tinput_no=ATOMIC_INIT(0);

structinput_handler*handler;

constchar*path;

interror;

__set_bit(EV_SYN,dev->evbit);

init_timer(&dev->timer);

if(!dev->rep[REP_DELAY]&&!dev->rep[REP_PERIOD]){

dev->timer.data=(long)dev;

dev->timer.function=input_repeat_key;

dev->rep[REP_DELAY]=250;

dev->rep[REP_PERIOD]=33;

}

if(!dev->getkeycode)

dev->getkeycode=input_default_getkeycode;

if(!dev->setkeycode)

dev->setkeycode=input_default_setkeycode;

snprintf(dev->dev.bus_id,sizeof(dev->dev.bus_id),

"input%ld",(unsignedlong)atomic_inc_return(&input_no)-1);

error=device_add(&dev->dev);

if(error)

returnerror;

path=kobject_get_path(&dev->dev.kobj,GFP_KERNEL);

printk(KERN_INFO"input:%sas%s\n",

dev->name?dev->name:"Unspecifieddevice",path?path:"N/A");

kfree(path);

error=mutex_lock_interruptible(&input_mutex);

if(error){

device_del(&dev->dev);

returnerror;

}

list_add_tail(&dev->node,&input_dev_list);

list_for_each_entry(handler,&input_handler_list,node)

input_attach_handler(dev,handler);

input_wakeup_procfs_readers();

mutex_unlock(&input_mutex);

return0;

}

*/

rc=input_register_device(ts.dev);

if(rc){

free_irq(IRQ_TC,ts.dev);

free_irq(IRQ_ADC,ts.dev);

clk_disable(adc_clock);

iounmap(base_addr);

return-EIO;

}

return0;

}

staticints3c2440ts_remove(structplatform_device*pdev)

{

disable_irq(IRQ_ADC);

disable_irq(IRQ_TC);

free_irq(IRQ_TC,ts.dev);

free_irq(IRQ_ADC,ts.dev);

if(adc_clock){

clk_disable(adc_clock);

clk_put(adc_clock);

adc_clock=NULL;

}

input_unregister_device(ts.dev);

iounmap(base_addr);

return0;

}

#ifdefCONFIG_PM

staticints3c2440ts_suspend(structplatform_device*pdev,pm_message_tstate)

{

writel(TSC_SLEEP,base_addr+S3C2410_ADCTSC);

writel(readl(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_STDBM,

base_addr+S3C2410_ADCCON);

disable_irq(IRQ_ADC);

disable_irq(IRQ_TC);

clk_disable(adc_clock);

return0;

}

staticints3c2440ts_resume(structplatform_device*pdev)

{

structs3c2440_ts_mach_info*info=

(structs3c2440_ts_mach_info*)pdev->dev.platform_data;

clk_enable(adc_clock);

msleep(1);

enable_irq(IRQ_ADC);

enable_irq(IRQ_TC);

if((info->presc&0xff)>0)

writel(S3C2410_ADCCON_PRSCEN|S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\

base_addr+S3C2410_ADCCON);

else

writel(0,base_addr+S3C2410_ADCCON);

/*Initialiseregisters*/

if((info->delay&0xffff)>0)

writel(info->delay&0xffff,base_addr+S3C2410_ADCDLY);

writel(WAIT4INT(0),base_addr+S3C2410_ADCTSC);

return0;

}

#else

#defines3c2440ts_suspendNULL

#defines3c2440ts_resumeNULL

#endif

/*

下面是/linux/platform_device.h定义的platform_driver结构体

structplatform_driver{

int(*probe)(structplatform_device*);//设备的检测,所以需要先前的设备注册

int(*remove)(structplatform_device*);//删除该设备

void(*shutdown)(structplatform_device*);//关闭该设备

int(*suspend)(structplatform_device*,pm_message_tstate);

int(*suspend_late)(structplatform_device*,pm_message_tstate);

int(*resume_early)(structplatform_device*);

int(*resume)(structplatform_device*);

structpm_ext_ops*pm;

structdevice_driverdriver;//设备驱动,定义在include/linux/device.h中

};

内核提供的platform_driver结构体的注册函数为platform_driver_register(),该函数定义在driver/base/platform.c中

*/

staticstructplatform_drivers3c2440ts_driver={

.driver={

.name="s3c2440-ts",

.owner=THIS_MODULE,

},

.probe=s3c2440ts_probe,

.remove=s3c2440ts_remove,

.suspend=s3c2440ts_suspend,

.resume=s3c2440ts_resume,

};

staticint__inits3c2440ts_init(void)

{

intrc;

rc=platform_driver_register(&s3c2440ts_driver);

if(rc<0)

printk(KERN_ERR"platform_driver_registererror!\n");

returnrc;

}

staticvoid__exits3c2440ts_exit(void)

{

platform_driver_unregister(&s3c2440ts_driver);

}

module_init(s3c2440ts_init);

module_exit(s3c2440ts_exit);

MODULE_AUTHOR("YANMING");

MODULE_DESCRIPTION("Mys3c2440touchscreendriver");

MODULE_LICENSE("GPL");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值