S3C2440驱动篇—触摸屏驱动分析

2024-04-16 08:18

本文主要是介绍S3C2440驱动篇—触摸屏驱动分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

S3C2440触摸屏驱动

一.硬件简介

       S3C2440触摸屏接口与ADC接口集成在一起,触摸屏X、Y坐标所产生的模拟信号通过通道7、5输入,2440提供触摸屏接口有4种处理模式:普通转换模式、分离的X/Y轴坐标转换模式、自动X/Y轴坐标转换模式、等待中断模式。具体参考2440硬件手册16章。

二.驱动实现

       下面是触摸屏驱动源码,其中使用了linux输入子系统input。暂时还没研究这一块,想深入了解可参考相关资料。

   1. #include <linux/errno.h>  2. #include <linux/kernel.h>  3. #include <linux/module.h>  4. #include <linux/slab.h>  5. #include <linux/input.h>  6. #include <linux/init.h>  7. #include <linux/serio.h>  8. #include <linux/delay.h>  9. #include <linux/platform_device.h>  10. #include <linux/clk.h>  11. #include <linux/gpio.h>  12. #include <asm/io.h>  13. #include <asm/irq.h>  14. #include <plat/regs-adc.h>  15. #include <mach/regs-gpio.h>  16.   17. #define S3C2410TSVERSION 0x0101  18. #define DEVICE_NAME "YC2440_TS"  19.   20. //定义一个WAIT4INT宏,该宏将对ADC触摸屏控制寄存器进行操作S3C2410_ADCTSC_YM_SEN这些宏都定义在regs-adc.h中  21. #define WAIT4INT(x) (((x)>>8)|S3C2410_ADCTSC_YM_SEN|S3C2410_ADCTSC_YP_SEN|S3C2410_ADCTSC_XP_SEN|S3C2410_ADCTSC_XY_PST(3))  22. #define AUTOPST (S3C2410_ADCTSC_YM_SEN|S3C2410_ADCTSC_YP_SEN|S3C2410_ADCTSC_XP_SEN|\  23.         S3C2410_ADCTSC_AUTO_PST|S3C2410_ADCTSC_XY_PST(0))  24.   25. static struct input_dev *dev;  //输入设备结构体  26. static long xp;    //记录转换后的X、Y坐标值  27. static long yp;  28. static int count;  29.   30. extern struct semaphore ADC_LOCK;  //在ADC驱动中定义的信号量  31. static int ownADC = 0;  32.   33. static struct clk *adc_clk;  34.   35. static void __iomem *base_addr;  36. //touch_timer_fire函数分三块执行,下面1、2、3分别实现不同功能  37. static void touch_timer_fire(unsigned long data)  38. {        39.     unsigned long data0;  40.     unsigned long data1;  41.     int updown;  42.   43.     data0 = ioread32(base_addr+S3C2410_ADCDAT0);  44.     data1 = ioread32(base_addr+S3C2410_ADCDAT1);  45.        //判断触摸屏是按下、抬起状态  46.     updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));  47.          48.     if(updown)                    49.     {  50.         if(count != 0)     //1.如果触摸屏按下,并且ADC已转换,则报告事件、数据  51.         {  52.             long tmp;  53.               54.             tmp = xp;  55.             xp = yp;  56.             yp = tmp;  57.   58.             xp >>= 2;  59.             yp >>= 2;  60.   61. #ifdef CONFIG_TOUCHSCREEN_DEBUG   62.             struct timeval tv;  63.             do_gettimeofday(&tv);  64.             printk(KERN_DEBUG "T:%06d, X:%03d, Y:%03d\n",(int)tv.tv_usec,xp,yp);  65. #endif  66.   67.             input_report_abs(dev,ABS_X,xp);  68.             input_report_abs(dev,ABS_Y,yp);  69.   70.             input_report_key(dev,BTN_TOUCH,1);  71.             input_report_abs(dev,ABS_PRESSURE,1);  72.             input_sync(dev);    //等待接收方受到数据后回复确认,用于同步  73.         }  74.         //2.如果触摸屏按下,并且没有ADC转换,则启动ADC转换  75.         xp = 0;  76.         yp = 0;  77.         count = 0;  78.   79.         iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST,base_addr+S3C2410_ADCTSC);  80.         iowrite32(ioread32(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_ENABLE_START,base_addr+S3C2410_ADCCON);  81.     }  82.     else    //3.如果触摸屏抬起状态,则报告事件、数据,重置等待按下状态  83.     {  84.         count = 0;  85.   86.         input_report_key(dev,BTN_TOUCH,0);  87.         input_report_abs(dev,ABS_PRESSURE,0);  88.         input_sync(dev);  89.   90.         iowrite32(WAIT4INT(0),base_addr+S3C2410_ADCTSC);    //将触摸屏重新设置为等待中断状态  91.   92.         if(ownADC)        //如果触摸屏抬起,就意味着这一次的操作结束,所以就释放ADC资源的占有  93.         {  94.             printk(KERN_INFO "up\n");  95.             ownADC = 0;  96.             up(&ADC_LOCK);  97.         }  98.     }  99. }  100. //定义并初始化了一个定时器touch_timer,定时器服务程序为touch_timer_fire  101. static struct timer_list touch_timer=TIMER_INITIALIZER(touch_timer_fire,0,0);  102.   103. static irqreturn_t stylus_updown(int irq,void *dev_id)  //触摸屏中断服务程序,触摸屏按下、抬起执行  104. {  105.     unsigned long data0;  106.     unsigned long data1;  107.     int updown;  108.   109.     if(down_trylock(&ADC_LOCK)==0)  110.     {  111.         ownADC = 1;  112.         data0 = ioread32(base_addr+S3C2410_ADCDAT0);  113.         data1 = ioread32(base_addr+S3C2410_ADCDAT1);  114.   115.         updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));  116.   117.         if(updown)  118.         {      //如果触摸屏按下,则执行touch_timer_fire的功能2  119.             printk(KERN_INFO "down\n");  120.             touch_timer_fire(0);      121.         }  122.         else  123.         {      //如果抬起,结束一次操作,释放相应资源  124.             printk(KERN_INFO "up-irq\n");  125.             ownADC = 0;  126.             up(&ADC_LOCK);  127.         }  128.     }  129.     return IRQ_HANDLED;  130. }  131.   132. static irqreturn_t stylus_action(int irq,void *dev_id)    //ADC中断服务程序  133. {  134.     unsigned long data0;  135.     unsigned long data1;  136.       137.     if(ownADC)  138.     {      //读取一次转换值  139.         data0 = ioread32(base_addr+S3C2410_ADCDAT0);  140.         data1 = ioread32(base_addr+S3C2410_ADCDAT1);  141.   142.         xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;  143.         yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;  144.         count++;  145.   146.         if(count<(1<<2)) //如果转换次数小于4次,重启AD转换  147.         {  148.             iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST,base_addr+S3C2410_ADCTSC);  149.             iowrite32(ioread32(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_ENABLE_START,base_addr+S3C2410_ADCCON);  150.         }  151.         else   //如果转换4次,启动1个时间滴答的定时器,最终调用touch_timer_fire功能1或功能3  152.         {  153.             mod_timer(&touch_timer,jiffies+1);  154.             iowrite32(WAIT4INT(1),base_addr+S3C2410_ADCTSC); //置触摸屏等待抬起中断  155.         }  156.     }  157.   158.     return IRQ_HANDLED;  159. }  160.   161. static int __init s3c2440ts_init(void)  162. {  163.     int ret;      164.   165.     adc_clk = clk_get(NULL,"adc");  166.     if(!adc_clk)  167.     {  168.         printk(KERN_ERR "Failed to get adc clock\n");  169.         return -ENOENT;  170.     }  171.   172.     clk_enable(adc_clk);  173.   174.     base_addr = ioremap(S3C2410_PA_ADC,0x20);  175.     if(base_addr==NULL)  176.     {  177.         printk(KERN_ERR "Failed to remap register\n");  178.         ret = -EINVAL;  179.         goto err_remap;  180.     }  181.     //初始化AD转换参数,置触摸屏等待按下中断  182.     iowrite32(S3C2410_ADCCON_PRSCEN|S3C2410_ADCCON_PRSCVL(0xff),base_addr+S3C2410_ADCCON);  183.     iowrite32(0xffff,base_addr+S3C2410_ADCDLY);  184.     iowrite32(WAIT4INT(0),base_addr+S3C2410_ADCTSC);  185.     //输入设备申请空间,位于include/linux/input.h  186.     dev = input_allocate_device();  187.     if(!dev)  188.     {  189.         printk(KERN_ERR "unable to allocate input_dev\n");  190.         ret = -ENOMEM;  191.         goto err_alloc;  192.     }  193.   194.     /*下面初始化输入设备,即给输入设备结构体input_dev的成员设置值。evbit字段用于描述支持的事件, 195.       这里支持同步事件、按键事件、绝对坐标事件,BIT宏实际就是对1进行位操作,定义在linux/bitops.h中*/  196.     dev->evbit[0] = BIT(EV_SYN)|BIT(EV_KEY)|BIT(EV_ABS);  197.     /*keybit字段用于描述按键的类型,在input.h中定义了很多,这里用BTN_TOUCH类型来表示触摸屏的点击*/  198.     dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);  199.     /*对于触摸屏来说,使用的是绝对坐标系统。这里设置该坐标系统中X和Y坐标的最小值和最大值(0-1023范围) 200.          ABS_X和ABS_Y就表示X坐标和Y坐标,ABS_PRESSURE就表示触摸屏是按下还是抬起状态*/  201.     input_set_abs_params(dev,ABS_X,0,0x3ff,0,0);  202.     input_set_abs_params(dev,ABS_Y,0,0x3ff,0,0);  203.     input_set_abs_params(dev,ABS_PRESSURE,0,1,0,0);  204.   205.     dev->name = DEVICE_NAME;             /*设备名称*/  206.     dev->id.bustype = BUS_RS232;         /*总线类型*/  207.     dev->id.vendor = 0xDEAD;             /*经销商ID号*/  208.     dev->id.product = 0xBEEF;            /*产品ID号*/  209.     dev->id.version = S3C2410TSVERSION;  /*版本ID号*/  210.   211.     //申请中断,IRQ_ADC为AD、触摸屏共享  212.     ret = request_irq(IRQ_ADC,stylus_action,IRQF_SHARED|IRQF_SAMPLE_RANDOM,DEVICE_NAME,dev);  213.     if(ret)  214.     {  215.         printk(KERN_ERR "request IRQ_ADC fail\n");  216.         ret = -EINVAL;  217.         goto err_alloc;  218.     }  219.   220.     ret = request_irq(IRQ_TC,stylus_updown,IRQF_SAMPLE_RANDOM,DEVICE_NAME,dev);  221.     if(ret)  222.     {  223.         printk(KERN_ERR "requers IRQ_TS fail\n");  224.         ret = -EINVAL;  225.         goto err_adcirq;  226.     }  227.   228.     printk(KERN_INFO "%s successfully load\n",DEVICE_NAME);  229.   230.     /*把dev触摸屏设备注册到输入子系统中*/  231.     input_register_device(dev);  232.     return 0;  233.   234.   235.   236. err_adcirq:  237.     free_irq(IRQ_ADC,dev);  238.   239. err_alloc:  240.     iounmap(base_addr);  241.   242. err_remap:  243.     clk_disable(adc_clk);  244.     clk_put(adc_clk);  245.       246.     return ret;  247. }  248.   249. static void __exit s3c2440ts_exit(void)  250. {  251.     disable_irq(IRQ_ADC);  252.     disable_irq(IRQ_TC);  253.     free_irq(IRQ_ADC,dev);  254.     free_irq(IRQ_TC,dev);  255.   256.     if(adc_clk);  257.     {  258.         clk_disable(adc_clk);  259.         clk_put(adc_clk);  260.         adc_clk = NULL;  261.     }  262.       263.     input_unregister_device(dev);  264.     iounmap(base_addr);  265. }  266.   267. module_init(s3c2440ts_init);  268. module_exit(s3c2440ts_exit);  269.   270. MODULE_LICENSE("GPL");  271. MODULE_AUTHOR("Zechin Liao");  

(1)如果触摸屏感觉到触摸,则触发触摸屏中断即进入stylus_updown,获取ADC_LOCK后判断触摸屏状态为按下,则调用touch_timer_fire启动ADC转换;

(2)当ADC转换启动后,触发ADC中断即进入stylus_action,如果这一次转换的次数小于4,则重新启动ADC进行转换,如果4次完毕后,启动1个时间滴答的定时器,停止ADC转换,也就是说在这个时间滴答内,ADC转换是停止的;

(3)这里为什么要在1个时间滴答到来之前停止ADC的转换呢?这是为了防止屏幕抖动。

(4)如果1个时间滴答到来则进入定时器服务程序touch_timer_fire,判断触摸屏仍然处于按下状态则上报事件和转换的数据,并重启ADC转换,重复第(2)步;

(5)如果触摸抬起了,则上报释放事件,并将触摸屏重新设置为等待中断状态。

 

这里采用动态编译,如果把触摸屏驱动编译进内核,参考ADC驱动。

编辑Makefile:

   1. ifneq ($(KERNELRELEASE),)      2.     obj-m:=s3c2440_ts.o      3. else      4.     KERNELDIR:=/home/liao/image/linux-2.6.32.2      5.     PWD:=$(shell pwd)      6.     all:      7.     make -C $(KERNELDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-      8.     clean:      9.     rm -rf *.ko *.o *.mod.c *.mod.o *.symvers      10. endif  

# make

# insmod s3c2440_ts.ko

# cat /proc/devices   查看input设备号

# cat /bus/input/devices 查看触摸屏设备信息

# mkdir /dev/input

# mknod /dev/input/event0 c 13 64  添加设备文件

三.测试

编写、编译下面测试程序,运行,点击触摸屏会打印采集的AD转换值。这只是一个简单应用,真正触摸屏应用还需要坐标转换、校正,可参考tslib应用。

   1. #include <stdio.h>  2. #include <stdlib.h>  3. #include <fcntl.h>  4. #include <linux/input.h>  5.   6. struct input_event buff;  7.   8. int main(void)  9. {  10.     int fd;  11.     int ret;  12.     int count=0;  13.       14.     fd = open("/dev/input/event0",O_RDWR);  15.     if(fd<0)  16.     {  17.         perror("can not open event0\n");  18.         return -1;  19.     }  20.   21.     while(1)  22.     {  23.         ret = read(fd,&buff,sizeof(struct input_event));  24.         //if(ret == sizeof(struct input_event))  25.             printf("type:%d\n code:%d\n,value:%d\n",buff.type,buff.code,buff.value);  26.         count++;  27.         printf("count=%d\n",count);  28.     }  29.     close(fd);  30. }  


这篇关于S3C2440驱动篇—触摸屏驱动分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/908260

相关文章

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

找不到Anaconda prompt终端的原因分析及解决方案

《找不到Anacondaprompt终端的原因分析及解决方案》因为anaconda还没有初始化,在安装anaconda的过程中,有一行是否要添加anaconda到菜单目录中,由于没有勾选,导致没有菜... 目录问题原因问http://www.chinasem.cn题解决安装了 Anaconda 却找不到 An

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

C++ 各种map特点对比分析

《C++各种map特点对比分析》文章比较了C++中不同类型的map(如std::map,std::unordered_map,std::multimap,std::unordered_multima... 目录特点比较C++ 示例代码 ​​​​​​代码解释特点比较1. std::map底层实现:基于红黑

Spring、Spring Boot、Spring Cloud 的区别与联系分析

《Spring、SpringBoot、SpringCloud的区别与联系分析》Spring、SpringBoot和SpringCloud是Java开发中常用的框架,分别针对企业级应用开发、快速开... 目录1. Spring 框架2. Spring Boot3. Spring Cloud总结1. Sprin

Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析

《Spring中BeanFactoryPostProcessor的作用和示例源码分析》Spring的BeanFactoryPostProcessor是容器初始化的扩展接口,允许在Bean实例化前... 目录一、概览1. 核心定位2. 核心功能详解3. 关键特性二、Spring 内置的 BeanFactory

MyBatis-Plus中Service接口的lambdaUpdate用法及实例分析

《MyBatis-Plus中Service接口的lambdaUpdate用法及实例分析》本文将详细讲解MyBatis-Plus中的lambdaUpdate用法,并提供丰富的案例来帮助读者更好地理解和应... 目录深入探索MyBATis-Plus中Service接口的lambdaUpdate用法及示例案例背景

MyBatis-Plus中静态工具Db的多种用法及实例分析

《MyBatis-Plus中静态工具Db的多种用法及实例分析》本文将详细讲解MyBatis-Plus中静态工具Db的各种用法,并结合具体案例进行演示和说明,具有很好的参考价值,希望对大家有所帮助,如有... 目录MyBATis-Plus中静态工具Db的多种用法及实例案例背景使用静态工具Db进行数据库操作插入

Go使用pprof进行CPU,内存和阻塞情况分析

《Go使用pprof进行CPU,内存和阻塞情况分析》Go语言提供了强大的pprof工具,用于分析CPU、内存、Goroutine阻塞等性能问题,帮助开发者优化程序,提高运行效率,下面我们就来深入了解下... 目录1. pprof 介绍2. 快速上手:启用 pprof3. CPU Profiling:分析 C

MySQL表锁、页面锁和行锁的作用及其优缺点对比分析

《MySQL表锁、页面锁和行锁的作用及其优缺点对比分析》MySQL中的表锁、页面锁和行锁各有特点,适用于不同的场景,表锁锁定整个表,适用于批量操作和MyISAM存储引擎,页面锁锁定数据页,适用于旧版本... 目录1. 表锁(Table Lock)2. 页面锁(Page Lock)3. 行锁(Row Lock