基于GPL329xx linux平台电容屏gsl1680的驱动调试分析

因客户有用到了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这款手指按下的中断信号为周期性的方波。

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

时间: 2024-10-08 08:22:22

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

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

一. 引言 千兆以太网是一种具有高带宽和高响应的新网络技术,相关协议遵循IEEE 802.3规范标准.采用和10M以太网相似的帧格式.网络协议和布线系统,基于光纤和短距离同轴电缆的物理层介质,更适用于交换机.服务器等数据吞吐率大的设备.本文设计实现一种基于嵌入式Linux千兆以太网卡的驱动程序,并完成后续的测试工作和代码移植. 千兆以太网网卡工作在OSI网络架构的物理层和数据链路层,其中物理层由PHY芯片管理,数据链路层由千兆以太网控制器(GMAC)管理.硬件构架上,GMAC控制器由核心层.MT

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

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

基于Linux平台下的僵尸网络病毒《比尔盖茨》

感觉分析的很好,所以决定翻译出来,希望和大家多多交流O(∩_∩)O~ 转载请注明出处:http://blog.csdn.net/u010484477     O(∩_∩)O谢谢 关键字:病毒,linux,信息安全 我昨天写的日志里面提到,家用路由器在x86的CentOS系统下奇怪的自己行动,像是在自己加载处理器.于是我决定爬上去看看,在那里发生了什么,然后我马上意识到有人爬到服务器和挂在进程中的dgnfd564sdf.com.主要是下面几个方面atddd,cupsdd,cupsddh, ksap

基于Linux平台病毒BlackHole病毒解析

今天遇到了一个病毒,代码量不多,但是利用了一个函数的小空子,杀伤力确实挺惊人的. 转载请注明出处:http://blog.csdn.net/u010484477谢谢^_^ 这个病毒前面就是常规的: socket->bind->listen这个过程大家都 下面我想详细说一下它的攻击方式: while ( 1 ) { nsock = accept(sock, (struct sockaddr *)&v10, (socklen_t *)&v9);// wait to link if

基于Linux平台病毒Wirenet.c解析

在分析Wirenet.c时,感觉自己学到了很多很赞的思想,希望跟大家一同交流. 转载请注明出处:http://blog.csdn.net/u010484477谢谢^_^ 这次并不想通篇的进行分析了,我想写出两块病毒的恶意代码,觉得思想挺好的. 一.删除某目录下的所有文件 pathpoint = opendir(path);  //打开一个目录 dirent = readdir(pathpoint);//读取目录,返回dirent结构体指针 fdname = dirent->d_name;//得到

基于Linux平台下网络病毒Caem.c源码及解析

Came.c型病毒在这里主要修改了用户的密码,同时对用户的终端设备进行了监视.希望与大家共同交流 转载请注明出处:http://blog.csdn.net/u010484477     O(∩_∩)O谢谢 #define HOME "/" #define TIOCSCTTY 0x540E #define TIOCGWINSZ 0x5413 #define TIOCSWINSZ 0x5414 #define ECHAR 0x1d #define PORT 39617 #define BU

游戏录屏直播的图文教程(基于云直播平台)

原创教程 ( 转载请注明出处 ) 2017-6-26,今天来做一下是电脑游戏桌面录屏直播的教程,就是把桌面的游戏直播出去,加上话筒做讲解.最终实现在电脑.手机.微信中都可以观看到游戏的直播和讲解画面. 提示:1. 本教程说的是游戏录屏直播的图文教程(基于云直播平台,不是基于自建流媒体直播平台) 2. 若要基于自建的流媒体平台,可以用OBS之类的软件来实现,OBS取流发送到自建平台上,实现直播. STEP1 . 硬件准备及设备连接 场景说明: 1.用户做一场电脑游戏桌面直播,实现PC端.手机端(A

android 电容屏(二):驱动调试之基本概念篇

关键词:android  电容屏 tp 工作队列 中断 多点触摸协议平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310(samsung exynos 4210)  作者:xubin341719(欢迎转载,请注明作者) 参考网站:http://edsionte.com/techblog/archives/1582这部分参考别人的多一点 android 电容屏(一):电容屏基本原理篇 android 电容屏(二):驱动调试之基本概念篇

android 电容屏(三):驱动调试之驱动程序分析篇

平台信息: 内核:linux3.4.39系统:android4.4 平台:S5P4418(cortex a9) 作者:瘋耔(欢迎转载,请注明作者) 欢迎指正错误,共同学习.共同进步!! 关注博主新浪博客:http://weibo.com/cpjphone   以goodix的gt8105为例 一.总体架构 硬件部分:先看一个总体的图吧,其实触摸屏原理也比较简单,触摸屏和主控芯片间的联系,如下主要有三部分: 1.IIC部分,初始化gt8105的数据和传回主控制的坐标位置信息就是通过IIC这条线传输