因客户有用到了gsl1680 7寸电容屏,所以拿了一块过来,便在329xx的平台上面开始调试了。

大概浏览了一下所提供的资料,只有介绍模组的资料跟一份中文版的datasheet,datasheet只是说了个大概,没有提到读取触摸坐标的寄存器。不过还好有给一份在其他处理器平台的驱动,所以读取坐标的部分代码移植过来就可以了。

gsl1680接口跟其他的电容屏一样,也是i2c接口的,貌似市面上的电容屏都是i2c接口,电容屏自带了微控制器MCU,用与处理采样,坐标转换等,还有一些抖动算法处理,完后将坐标保存在固定的寄存器里面,之后会给出一个中断信号输出到pin脚,通知主控读取坐标。不过手上拿到这一款内部是没有flash的,即自身不带firmware的,初始化时要靠主控将firmware透过i2c接口加载到触摸屏的RAM里面,之后电容屏才能正常工作。个人觉得这种方式有两个缺点:1.firmware有8000多个值,加载需要花掉大概10s左右,增加了开机时间(后续可以将i2c clk调为400K试试看);2.本身不带firmware,需要加载才能工作,无疑给调试增加了难度。不过,相对电阻屏,电容屏的调试还是比较简单的,不必处理采样/转换,还有干扰/抖动等问题。电容屏而言,自带了的MCU,所以这些工作都已经帮你做好了,倒是很方便。

下面我们具体先看一下gsl1680硬件模组的接口,总共有12个pin,有效的只有6个pin,分别是GND,VCC,SDA,SCL,IO CNTL,INT。

1----GND       2-----NC

3----NC          4-----NC

5----NC          6-----GND

7----SDA        8-----SCL

9----IO Cntl  10-----INT

11----VDD     12-----VDD

简单说明一下:

1,6 :pin接GND;

2,3,4,5  悬空

7,8    i2c接口

9   IO Cntl  硬件reset/wake pin

10 INT 中断pin脚

11,12   VCC接3.0或者3.3V

了解了上面的硬件接口,触摸屏需要占用的主控的硬件资源有:i2c接口,一个外部中断,一个GPIO(用于控制触摸屏硬件reset/power down进入省电模式)就可以完成触摸屏坐标的获取了。

下面,先介绍一下驱动具体流程。

1.module_init函数里面,注册平台设备platform device;

2.注册platform device driver;

static struct platform_device gp_tp_device = {
 .name = "gp_tp",
 .id   = -1,
 .dev = {
  .release = gp_tp_device_release,
 }
};

static struct platform_driver gp_tp_driver = {
       .driver         = {
        .name   = "gp_tp",
        .owner  = THIS_MODULE,
       },
       .probe          = gp_tp_probe,
       .remove         = gp_tp_remove,
       .suspend        = gp_tp_suspend,
       .resume         = gp_tp_resume,

};

上面的platform_driver里面的probe函数将会完成以下几件事情:

1)创建单一内核线程,注册内核线程函数,用于中断下半部的触摸屏坐标的读取,以及将坐标值上报给linux input子系统;

通过调用create_singlethread_workqueue API创建一个工作队列内核线程;

2)创建一个event设备,从触摸屏获取到的坐标会上报给event,用户层将会从这个event读取到触摸屏按下的坐标;

3)i2c 设备的申请;用于与触摸屏通信跟加载firmware;

4)申请一个GPIO口,用于控制触摸屏的reset/wake ,即接到9 IO Cntl pin脚;

5)软件/硬件复位触摸屏,加载firmware;

6)申请/注册外部中断函数;

以上就是在probe函数里面完成的工作,接下来就是等待触摸屏按下,然后中断函数通知内核线程函数去读取触摸的坐标,上报给event,

之后也是不断循环这个过程。

  触摸屏驱动probe函数完整的实现代码如下所示:

/** device driver probe*/
static int __init gp_tp_probe(struct platform_device *pdev)
{
 int rc;
 int ret = 0;
 int intidx, slaveAddr;
 int io_wake= -1;
 gpio_content_t ctx; 
 unsigned int debounce = 1;//27000; /*1ms*/
 //gp_board_touch_t *touch_config = NULL;
 
 print_info("Entering gp_tp_probe\n");

#ifdef VIRTUAL_KEYS
 virtual_keys_init();
#endif

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

/* Create single thread work queue */
 ts.touch_wq = create_singlethread_workqueue("touch_wq");
 if (!ts.touch_wq)
 {
  print_info("%s unable to create single thread work queue\n", __func__);
  ret = -ENOMEM;
  goto __err_work_queue;
 }
 INIT_WORK(&ts.mt_set_nice_work, gp_mt_set_nice_work);
 queue_work(ts.touch_wq, &ts.mt_set_nice_work);

ts.dev = input_allocate_device();
 if ( NULL==ts.dev ){
  print_info("Unable to alloc input device\n");
  ret = -ENOMEM;
  goto __err_alloc;
 }

I2C_REQUEST:
 gp_i2c_handle = gslx680_i2c_request();
 if (gp_i2c_handle == -1) {
  DIAG_ERROR("cap touch panel iic request error!\n");
  goto I2C_REQUEST;
  return -ENOMEM;
 }

__set_bit(EV_ABS, ts.dev->evbit);

input_set_abs_params(ts.dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_X - 1, 0, 0);
 input_set_abs_params(ts.dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y - 1, 0, 0);
 input_set_abs_params(ts.dev, ABS_MT_TRACKING_ID, 0, (MAX_CONTACTS + 1), 0, 0);
 input_set_abs_params(ts.dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);

input_set_capability(ts.dev, EV_KEY, KEY_BACK);
 input_set_capability(ts.dev, EV_KEY, KEY_HOME);
 input_set_capability(ts.dev, EV_KEY, KEY_MENU);

ts.dev->name = "gp_ts";
 ts.dev->phys = "gp_ts";
 ts.dev->id.bustype = BUS_I2C;

/* All went ok, so register to the input system */
 rc = input_register_device(ts.dev);
 if (rc) {
  ret = -EIO;
  goto __err_reg_input;
 }
 /* request cpt reset pin*/
 intidx = MK_GPIO_INDEX( RST_CHANNEL, RST_FUNC, RST_GID, RST_PIN );
 touch_reset = gp_gpio_request(intidx, "touch_reset"); /* GPIO1[7] ---- */
 if(IS_ERR((void*)touch_reset)) {
  print_info("%s unable to register client\n", __func__);
  ret = -ENOMEM;
  goto __err_register;
 }
 gp_gpio_set_output(touch_reset, 1,0);

DBG_PRINT("=======gslx680 ctp test=====\n");
 gp_i2c_handle = gslx680_i2c_request();
 if (gp_i2c_handle == -1) {
  DIAG_ERROR("cap touch panel iic request error!\n");
  return -ENOMEM;
 }

gp_gpio_set_output(touch_reset, 0,0);
 msleep(100);
 gp_gpio_set_output(touch_reset, 1,0);
 msleep(100);
 reset_chip();
 gp_load_ctp_fw();
 startup_chip();
 msleep(50);
 reset_chip();
 startup_chip();

//gp_i2c_bus_release(gp_i2c_handle);

INIT_WORK(&ts.mt_work, gp_multi_touch_work);

intidx = MK_GPIO_INDEX( INT_IRQ_CHANNEL, INT_IRQ_FUNC, INT_IRQ_GID, INT_IRQ_PIN );
 ts.client = gp_gpio_request(intidx, "touch_int"); /* GPIO1[7] ---- */
 if(IS_ERR((void*)ts.client)) {
  print_info("%s unable to register client\n", __func__);
  ret = -ENOMEM;
  goto __err_register;
 }
 gp_gpio_set_input(ts.client, GPIO_PULL_LOW);
 gp_gpio_irq_property(ts.client, GPIO_IRQ_EDGE_TRIGGER|GPIO_IRQ_ACTIVE_RISING, &debounce);
 gp_gpio_register_isr(ts.client, gp_ts_callback, (void *)ts.client);

print_info("End gp_tp_probe\n");

return 0;

__err_register:
 input_unregister_device(ts.dev);
__err_reg_input:
 //gp_ti2c_bus_release(ts.i2c_handle);
__err_i2c:
// kfree(ts.i2c_handle); 
__err_i2c_allocate:
 //gp_gpio_release(ts.touch_reset);
__err_pin_request:
 input_free_device(ts.dev);
__err_alloc:
 destroy_workqueue(ts.touch_wq);
__err_work_queue:
 return ret;
}

触摸屏按下中断处理函数如下所示

/**
 * interrupt callback
 */
void gp_ts_callback(void* client)
{
 gp_gpio_enable_irq(ts.client, 0);
 queue_work(ts.touch_wq, &ts.mt_work);
}

  linux的中断机制一般分为两个部分:上半部跟下半部。上半部处理的工作一般要尽可能的少,快速;而将繁重、繁琐、处理时间比较长的工作留在下半部去处理。所以,上半部执行的任务算是比较轻松的,这里只是清掉了外部中端状态寄存器,然后调度下半部工作的运行。

下面我们再看一下,触摸屏按下中断的下半部的工作:

static void
gp_mt_set_nice_work(
 struct work_struct *work
)
{
 print_info("[%s:%d]\n", __FUNCTION__, __LINE__);
 set_user_nice(current, -20);
}

static inline unsigned int  join_bytes(unsigned char a, unsigned char b)
{
 unsigned int ab = 0;
 ab = ab | a;
 ab = ab << 8 | b;
 return ab;
}

static void
gp_multi_touch_work(
 struct work_struct *work
)
{

int i,ret;
 char touched, id;
 unsigned short x, y;
 unsigned int pending;
 int irq_state;
  char tp_data[(MULTI_TP_POINTS + 1)*4 ];
  
 //print_info("WQ  gp_multi_touch_work.\n");

#if ADJUST_CPU_FREQ
 clockstatus_configure(CLOCK_STATUS_TOUCH,1);
#endif

ret = gsl_ts_read(0x80, tp_data, sizeof(tp_data));
 if( ret < 0) {
  print_info("gp_tp_get_data fail,return %d\n",ret);
  gp_gpio_enable_irq(ts.client, 1);
  return;
 }

touched = (tp_data[0]< MULTI_TP_POINTS ? tp_data[0] : MULTI_TP_POINTS);
 for (i=1;i<=MAX_CONTACTS;i++) {
  id_state_flag[i] = 0;  
 }
 //printk("point = %d  ",touched);
 for (i = 0; i < touched; i++) {
  id = tp_data[4 *( i + 1) + 3] >> 4;
  x = join_bytes(tp_data[4 *( i + 1) + 3] & 0xf,tp_data[4 *( i + 1) + 2]);
  y = join_bytes(tp_data[4 *( i + 1) + 1],tp_data[4 *( i + 1) + 0]);  
  if(1 <= id && id <= MAX_CONTACTS){
   record_point(x, y, id);
   report_data(x_new, y_new, 10, id);
   id_state_flag[id] = 1;
  }
 }
 if (touched == 0) {
  input_mt_sync(ts.dev);
 }
 for(i=1;i<=MAX_CONTACTS;i++)
 { 
  if( (0 == touched) || ((0 != id_state_old_flag[i]) && (0 == id_state_flag[i])) )
  {
   id_sign[i]=0;
  }
  id_state_old_flag[i] = id_state_flag[i];  
 }

#if ADJUST_CPU_FREQ
 if(touched == 0){
  clockstatus_configure(CLOCK_STATUS_TOUCH,0);
 }
#endif
 ts.prev_touched = touched;
 input_sync(ts.dev);

__error_check_touch_int:
__error_get_mt_data:

/* Clear interrupt flag */
 pending = (1 << GPIO_PIN_NUMBER(ts.intIoIndex));
 gpHalGpioSetIntPending(ts.intIoIndex, pending);
 gp_gpio_enable_irq(ts.client, 1);
}

  先从触摸屏控制器0x80的寄存器开始读取24个byte,然后将读回来的数据进行解析,得到当前触摸的点数,坐标等,之后经过相关的处理后,上报给event。因为是采集多点坐标,然后一起处理这些点的坐标,所以上报完一个点要调用input_mt_sync,直到上报完当前所有按下的点的坐标之后,再调用input_sync 函数达到同步的目的。

  以上大致分析了gsl1680电容屏的linux驱动,其实电容屏的驱动处理流程都是大同小异的,处理流程都差不多,具有通用性。

  之前还调试过一款墩泰的ft5x06的电容屏,这款比较简单,初始化不用加载firmware,接口也基本差不多,只是ft5x06 手指按下时中断输出引脚为低电平,然而gsl1680这款手指按下的中断信号为周期性的方波。

以上仅供参考,不足之处还请指正。

基于GPL329xx linux平台电容屏gsl1680的驱动调试分析的更多相关文章

  1. Android电容屏(二):驱动调试分析【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/7833383 以goodix的gt8105为例 一.总体架构 硬件部分:先看一个总体的图 ...

  2. 基于嵌入式Linux的千兆以太网卡驱动程序设计及测试

    一. 引言 千兆以太网是一种具有高带宽和高响应的新网络技术,相关协议遵循IEEE 802.3规范标准.采用和10M以太网相似的帧格式.网络协议和布线系统,基于光纤和短距离同轴电缆的物理层介质,更适用于 ...

  3. MongoDb Windows linux平台环境及主流编程语言驱动安装同时配置mongoDb的远程连接

    <一,>MongoDB 简介篇Ruiy; MongoDB是一个高性能,开源,无模式的文档型数据库,是当前NoSql数据库中比较热门的一种.它在许多场景下可用于替代传统的关系型数据库或键/值 ...

  4. ilitek的电容屏驱动程序ilitek_aimvx.c的分析

    本文记录了ili驱动程序. 参考  http://linhui.568.blog.163.com/blog/static/962652682011786352856/ 后记:现在的情况是,在win上是 ...

  5. golang 2行代码在基于arm linux的树莓派、orangepi上运行http web服务

    go语言(golang)简化了跨平台交叉编译步骤,支持在windows系统下交叉编译基于arm+linux平台的应用,运行时无需其它依赖库.以下以一个简单的http server为例,先上源码: ** ...

  6. .net项目在linux平台的CI流程(基于Jenkins+mono+jexus)

    内容较多,主要分为以下4方面内容: Jenkins的安装部署(centos 7+) .net在linux平台CI流程所需的插件管理&配置 Jenkins配置连接Gitlab(也可使用对应插件连 ...

  7. 基于Linux平台的libpcap源码分析和优化

    目录 1..... libpcap简介... 1 2..... libpcap捕包过程... 2 2.1        数据包基本捕包流程... 2 2.2        libpcap捕包过程... ...

  8. 转:基于 linux 平台的 libpcap 源代码分析

    libpcap 是 unix/linux 平台下的网络数据包捕获函数包,大多数网络监控软件都以它为基础.Libpcap 可以在绝大多数类 unix 平台下工作,本文分析了 libpcap 在 linu ...

  9. 基于Linux平台的Lotus Domino 8系统部署五部曲(全视频展示)

    基于Linux平台的Lotus Domino 8系统部署五部曲(全视频展示),学习就像看电影 第一部:安装部署 第二部:配置Domino 第三部:Notes8客户端配置 第四部:为Domino系统加装 ...

随机推荐

  1. [mock]7月25日

    1. 将一个数组分成左右两部分,使得右边的某个连续子段和减去左边的某个连续字段和最小[7,8,9,|3,5,-1] sum right - sum left minimal 想到左右分一刀,O(n), ...

  2. Callable与Future的简单介绍

    Callable与Future的介绍 Callable与 Future 两功能是Java在后续版本中为了适应多并法才加入的,Callable是类似于Runnable的接口,实现Callable接口的类 ...

  3. Hibernate逍遥游记-第15章处理并发问题-002悲观锁

    1. 2. hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.connection.driver_class=com.mys ...

  4. 2014-9-17二班----9 web project

    http://localhost:8080/rwkj1/indexServlet             跳转                    http://localhost:8080/rwk ...

  5. Qt网络通信骨架解析,QtClient QtServer QtSerialPort

    http://blog.csdn.net/Dr_Abel/article/details/52469134#t18

  6. 8、SpringMVC源码分析(3):分析ModelAndView的形成过程

    首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throw ...

  7. How to Determine the Version of Oracle XML Publisher for Oracle E-Business Suite 11i and Release 12 (Doc ID 362496.1)

    Modified: 29-Mar-2014 Type: HOWTO In this DocumentGoal   Solution   1. Based upon an output file gen ...

  8. Redis系列文章导读

    1. Redis简介 1.1 Redis VS Memcached 2. Redis安装教程 3. Redis数据类型 4. Redis常用命令 4.1 key 4.2 string 4.3 hash ...

  9. C# MySQL 数据库操作类

    using System; using System.Configuration; using System.Collections; using System.Data; using MySql.D ...

  10. git for windows+TortoiseGit客户端的使用

        一.安装Git客户端 全部安装均采用默认! 1. 安装支撑软件 : https://code.google.com/p/msysgit/downloads/list?q=full+instal ...