主题 : Tiny210v2 2G MLC 的 电容触摸屏在Linux 下工作不正常,分析了一下代码,求指正 复制链接 | 浏览器收藏 | 打印
级别: 骑士
UID: 5844
精华: 9
发帖: 64
金钱: 770 两
威望: 154 点
综合积分: 308 分
注册时间: 2009-05-11
最后登录: 2019-05-14
楼主  发表于: 2012-11-07 18:56

 Tiny210v2 2G MLC 的 电容触摸屏在Linux 下工作不正常,分析了一下代码,求指正

0. 我的开发板
===============================================
我的开发板是 TINY210V2 MLC 2G 的这个。
内核版本是 光盘自带的 3.0.8
屏幕是 7存的 电容触摸屏, superboot 在烧写程序的时候显示 S70(auto)。


1. 内核配置文件 - mini210_linux_defconfig
===============================================
CONFIG_TOUCHSCREEN_1WIRE=y
CONFIG_TOUCHSCREEN_IF=y
CONFIG_TOUCHSCREEN_GOODIX=y
CONFIG_TOUCHSCREEN_FT5X0X=y


无论是 android 还是 linux 的配置文件,这几个和touchpanel相关的选项都是 y,也就是编译到内核里。


2. 模块的Makefile - drivers/input/touchscreen/Makefile
===============================================
obj-$(CONFIG_TOUCHSCREEN_1WIRE)        += mini210_1wire_host.o
obj-$(CONFIG_TOUCHSCREEN_IF)        += ts-if.o

ifneq ($(wildcard drivers/input/touchscreen/goodix_touch.c),)
ifeq ($(FA),1)
obj-$(CONFIG_TOUCHSCREEN_GOODIX)    += goodix_touch.o
else
obj-m += goodix_touch.o
endif  #FA
endif

ifneq ($(wildcard drivers/input/touchscreen/ft5x06_ts.c),)
ifeq ($(FA),1)
obj-$(CONFIG_TOUCHSCREEN_FT5X0X)    += ft5x06_ts.o
else
obj-m += ft5x06_ts.o
endif  #FA
endif


这里就很奇怪了,我的理解是 如果存在 drivers/input/touchscreen/ft5x06_ts.c 和 drivers/input/touchscreen/goodix_touch.c 这两个 C
文件的话,就将他们编译成 ft5x06_ts.o 和 goodix_touch.o ,否则什么都不做。

但内核源代码中,我还真的没找到这两个源文件,( 友善不开源吗? )

虽然这两个头文件没有了,但 Makefile 仍然会将 “#define CONFIG_TOUCHSCREEN_GOODIX y"传递给所有的内核源代码,这个宏定义在后来的代码中有用到。


3. 是不是CTP的判断 - arch/arm/mach-s5pv210/mini210-lcds.c
===============================================
#if defined(CONFIG_TOUCHSCREEN_GOODIX) || \
    defined(CONFIG_TOUCHSCREEN_FT5X0X)
static unsigned int ctp_type = CTP_NONE;

static int __init mini210_set_ctp(char *str)
{
    unsigned int val;
    char *p, *end;
   
    printk( "+%s( %s )\n", __FUNCTION__, str );

    p = str;
    val = simple_strtoul(p, &end, 10);
    if (end <= p) {
        return 1;
    }

    if (val < CTP_MAX && mini210_lcd_config[lcd_idx].ctp) {
        ctp_type = val;
    }

    printk( "-%s( ctp_type = %d)\n", __FUNCTION__, ctp_type );
    return 1;
}
__setup("ctp=", mini210_set_ctp);

unsigned int mini210_get_ctp(void)
{
    printk( "+%s()\n", __FUNCTION__ );
    printk( "-%s( ctp_type = %d)\n", __FUNCTION__, ctp_type );

    return ctp_type;
}
EXPORT_SYMBOL(mini210_get_ctp);
#endif


在用Superboot烧写内核参数的时候,比如:
Linux-CommandLine = root=/dev/mtdblock4 console=ttySAC0,115200 init=/linuxrc skipcali=yes ctp=3
如果传递了 ctp=XX ,就会触发 __setup("ctp=", mini210_set_ctp) , mini210_set_ctp就会被调用

这个函数获取 ctp=XX 中的 XX 进行判断,并且还判断了 mini210_lcd_config 表格中的 ctp 信息。然后设置 ctp_type。
其他模块可以调用 mini210_get_ctp 来判断是不是 ctp。

我猜测 , ctp 是 capacitance touch panel 的意思。( 电容触摸屏 )



4. 1wire内核驱动 - drivers/input/touchscreen/mini210_1wire_host.c
===============================================
在模块初始化的代码中, 调用了 mini210_get_ctp 来判断是不是 ctp。如下:

if (mini210_get_ctp() != CTP_NONE) {
    has_ts_data = 0;
    timer_interval = HZ / 20;
    printk( "*%s(): set has_ts_data = 0, timer_interval = 50ms \n", __FUNCTION__ );
}


如果是 ctp 的话,就会将 has_ts_data 设置为 0.
注意这个 has_ts_data , 如果它是0的话, 1wire 驱动就不会去获取 TS 信息了。如下:

void one_wire_timer_proc(unsigned long v)
{
    unsigned char req;

//    printk( "+%s()\n", __FUNCTION__ );

    if (exitting) {
        printk( "*%s(): exitting \n", __FUNCTION__ );
        return;
    }

    one_wire_timer.expires = jiffies + timer_interval;
    add_timer(&one_wire_timer);

    if (lcd_type == 0) {
        req = REQ_INFO;    /* 0x60 */
    } else if (!backlight_init_success) {
        req = 127;    /* 0x7F */
    } else if (backlight_req) {
        req = backlight_req;    /* 0x8? */
        backlight_req = 0;
    } else if (has_ts_data) {
        req = REQ_TS;    /* 0x40 */
    } else {
//        printk( "*%s(): do nothing\n", __FUNCTION__ );
        return;
    }

    start_one_wire_session(req);
   
//    printk( "-%s()\n", __FUNCTION__ );
}


这个函数就是 probe 函数中启动的 timer 处理函数,我理解是每隔一段时间就会被调用一下。

这个函数中默认有一个处理顺序, 先进行 REQ_INFO 的处理,处理完以后才会向下处理 blacklight 背光,然后才是 REQ_TS 。
这个 REQ_TS 我猜测是去获取 touch screen 的信息吧。

但如果 has_ts_data 被设置为 0 的话,就不会去获取了。
在现有代码中,好像 如果传递了 ctp = XX 给内核的话, 就会导致 驱动程序获取完 INFO 和 backlight 信息以后,就再也不去获取 REQ_TS 信息了。

1wire 驱动程序 和 touchpanel 之间的通信,是有时序的, 这个时序应该可以参考 timer_for_1wire_interrupt 这个函数。

如果是 REQ_TS 信息获取到的话, 可以参考 one_wire_session_complete 函数理解数据格式:如下:

static void one_wire_session_complete(unsigned char req, unsigned int res)
{

    unsigned char crc;
    const unsigned char *p = (const unsigned char*)&res;
   
    //printk( "+%s( req = %02X, res = %08X )\n", __FUNCTION__, req, res );
   
    total_received ++;

    last_res = res;

    crc8_init(crc);
    crc8(crc, p[3]);
    crc8(crc, p[2]);
    crc8(crc, p[1]);

    if (crc != p[0]) {
        // CRC dismatch
        if (total_received > 100) {
            total_error++;
        }
        //printk( "*%s( ): CRC check ERROR\n", __FUNCTION__  );
        return;
    }

    switch(req) {
        case REQ_TS:
            {
                unsigned short x,y;
                unsigned pressed;
                x =  ((p[3] >>   4U) << 8U) + p[2];
                y =  ((p[3] &  0xFU) << 8U) + p[1];
                pressed = (x != 0xFFFU) && (y != 0xFFFU);
       
                printk( "*%s( ): REQ_TS done : x=%04X y=%04X pressed=%08X\n", __FUNCTION__, x, y, pressed );
                notify_ts_data(x, y, pressed);
            }
            break;

        case REQ_INFO:
            printk( "*%s( ): REQ_INFO done : %02X-%02X-%02X \n", __FUNCTION__, p[3], p[2], p[1] );
            notify_info_data(p[3], p[2], p[1]);
            break;
        default:
            printk( "*%s( ): REQ_BL done : %02X-%02X-%02X \n", __FUNCTION__, p[3], p[2], p[1] );
            notify_bl_data(p[3], p[2], p[1]);
            break;
    }
   
    //printk( "-%s( )\n", __FUNCTION__ );
}


在one_wire_session_complete这个函数中, 我们可以看到如果是 REQ_TS 的话, 驱动程序会上报 ( x,y )这个地方是压下还是抬起。
我理解, 坐标信息是通过 刚才的 通信从 外设获取到了。一共4个字节。
第3字节的高4位+第2字节 是 X。
第3字节的低4位+第1字节 是 Y。
如果 X 和 Y 都不是 0xFFF 的话,就是压下。也就是说 如果没有压下的话, X 和 Y 就都是 0xFFF。


通知 用户空间的应用程序 的时候, 通过唤醒等待队列 ts_waitq 中的进程进行的。如下:

static inline void notify_ts_data(unsigned x, unsigned y, unsigned down)
{
    if (!down && !(ts_status &(1U << 31))) {
        // up repeat, give it up
        return;
    }

    ts_status = ((x << 16) | (y)) | (down << 31);
    ts_ready = 1;
    wake_up_interruptible(&ts_waitq);
}

static ssize_t ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
    unsigned long err;

    printk( "+%s()\n", __FUNCTION__ );

    if (!ts_ready) {
        if (filp->f_flags & O_NONBLOCK){
            printk( "*%s() : NONBLOCK -> EAGAIN\n", __FUNCTION__ );
            return -EAGAIN;
        }
        else{
            wait_event_interruptible(ts_waitq, ts_ready);
        }
    }

    ts_ready = 0;

    if (count < sizeof ts_status) {
        printk( "*%s() : ERROR : count < sizeof(ts_status) \n", __FUNCTION__ );
        return -EINVAL;
    } else {
        count = sizeof ts_status;
    }

    printk( "*%s() : copy_to_user : %08X \n", __FUNCTION__, ts_status );
    err = copy_to_user((void *)buffer, (const void *)(&ts_status), sizeof ts_status);
   
    printk( "-%s()\n", __FUNCTION__ );
    return err ? -EFAULT : sizeof ts_status;
}

static struct file_operations ts_fops = {
    owner:        THIS_MODULE,
    read:        ts_read,   
    poll:       ts_poll,
};

static struct miscdevice ts_misc = {
    .minor        = 181,
    .name        = TOUCH_DEVICE_NAME,
    .fops        = &ts_fops,
};

#define TOUCH_DEVICE_NAME    "touchscreen-1wire"


通过上面的代码,我理解是对 /dev/touchscreen-1wire 这个文件进行 read 操作的时候,
会被放到 ts_waitq 队列上进行睡眠。

1wire 驱动程序 会定周期获取 touch screen 信息,如果获取到了,就唤醒 刚才 read 的那个进程,并将 (x,y)touch ? 信息告诉它。

问题是 传递了 ctp = X 以后, 1wire 的定周期处理,一直走不到 REQ_TS 的处理。导致我的 ts_calibrate 程序一直 block 在 read 里面。


另外, 如果去 读取 /proc/driver/one-wire-info 这个文件的话, 会调用 read_proc 函数获取信息:如下:

static int read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *data)
{
    int len;
   
    printk( "+%s()\n", __FUNCTION__ );

    len = sprintf(buf, "lcd_type %u, firmware_ver %u, total_received %u, total_error %u, last_req %04X, last_res %08X\n",
            lcd_type, firmware_ver, total_received, total_error, last_req, last_res);
    *eof = 1;
   
    printk( "-%s()\n", __FUNCTION__ );
   
    return len;
}


这几个信息,是在 定周期获取信息的处理中,第一步获取 REQ_INFO 中获取到的。



5. TSLIB的1wire plugin - tslib-1.0.0/plugins/one-wire-ts-input.c
===============================================
neechard 文档中的 [ 一线触摸tslib移植完全说明 ] 中,
http://www.aiothome.net/read.php?tid-20248.html
针对 1wire 的 TSLIB 的驱动,就能够和 内核中的 这个部分对应上:

static int misc_read(struct tslib_module_info *inf, struct ts_sample *samp, int nr)
{
    struct tsdev *ts = inf->dev;

    int ret;
    unsigned ts_status;
    ret = read(ts->fd, &ts_status, sizeof ts_status);
    if (ret < 0) {
        return 0;
    }
    if (ret == 0) {
        return 0;
    }

    samp->x = ((ts_status) >> 16) & 0x7FFF;
    samp->y = ts_status & 0x7FFF;
    samp->pressure = ts_status >> 31;
    gettimeofday(&samp->tv,NULL);
    nr = nr;

    return 1;
}

misc_read是 TSLIB 读取 /dev/touchscreen-1wire 设备文件的处理函数,
ts_read 是 /dev/touchscreen-1wire 给应用程序 touch screen 的 坐标信息。
我粗略的比较了一下 数据 格式,应该是可以对上的。X Y touch?









自由,自强,共享,共创。
级别: 论坛版主
UID: 12573
精华: 27
发帖: 8838
金钱: 46490 两
威望: 9298 点
综合积分: 18216 分
注册时间: 2010-01-09
最后登录: 2019-07-16
1楼  发表于: 2012-11-07 19:58
分析的挺好,不过你用的是电容触摸,却分析电阻触摸用的一线驱动好像不对劲吧
新手如何向我们反馈有效的信息,以便解决问题,见此贴:
http://www.arm9home.net/read.php?tid-14431.html

[注]: 此处签名链接仅为指引方向,而非解答问题本身.
级别: 骑士
UID: 5844
精华: 9
发帖: 64
金钱: 770 两
威望: 154 点
综合积分: 308 分
注册时间: 2009-05-11
最后登录: 2019-05-14
2楼  发表于: 2012-11-07 21:06

 回 1楼(mindee) 的帖子

你好,一直没有弄明白 tiny210v2 的电容屏在内核里面是那一个驱动。网页上说的是 一线精准触摸驱动。所以就看的 1wire。如果知道的话,麻烦能告知一下 电容屏驱动名吗? 谢谢。
级别: 新手上路
UID: 80423
精华: 0
发帖: 25
金钱: 125 两
威望: 25 点
综合积分: 50 分
注册时间: 2012-10-24
最后登录: 2024-01-20
3楼  发表于: 2013-01-24 08:55

 问一下

210v2的手册是不是有点问题,手册上说ctp=2对应的4.3寸电容FT5306 , 但我的7寸屏也必须用ctp=2,使用其他参数触摸是混乱的
级别: 新手上路
UID: 60243
精华: 0
发帖: 28
金钱: 140 两
威望: 28 点
综合积分: 56 分
注册时间: 2011-12-07
最后登录: 2013-12-13
4楼  发表于: 2013-01-26 23:15

 回 3楼(cqsrmxxzyx) 的帖子

我用20121228里的光盘编的,也是不能用,后面看到你的回复,试了一下,果然就可以了。
看来这个手册还是不能全信啊
级别: 侠客
UID: 7092
精华: 0
发帖: 74
金钱: 425 两
威望: 160 点
综合积分: 148 分
注册时间: 2009-07-01
最后登录: 2014-08-04
5楼  发表于: 2013-02-09 09:31
7寸屏背后也是FT5306,我的也是用1228日的编译生成的NK,触摸有效果但位置好像只有左半边,而且不准。