【TINY4412】LINUX移植筆記:(23)設備樹LCD觸摸屏驅動

【TINY4412】LINUX移植筆記:(23)設備樹 LCD觸摸屏驅動

宿主機 : 虛擬機 Ubuntu 16.04 LTS / X64
目標板[底板]: Tiny4412SDK - 1506
目標板[核心板]: Tiny4412 - 1412
LINUX內核: 4.12.0
交叉編譯器: arm-none-linux-gnueabi-gcc(gcc version 4.8.3 20140320)
日期: 2017-9-1 19:48:24
作者: SY

簡介

觸摸屏芯片使用 FT5406 ,使用 I2C 總線傳輸觸摸屏數據。
這裏寫圖片描述
這裏寫圖片描述

主要使用 3 根線, I2CSCL1_OUT I2CSDA1_OUT XEINT14_OUT ,一開始參考友善之臂的驅動,以爲觸摸屏數據也是通過 XINT10_OUT 一線傳輸,嘗試讀取數據發現一直讀不到。後來查找資料才知道是採用 I2C 總線傳輸的,暈…

網上下載觸摸芯片的手冊:FT5x06.pdf

這裏寫圖片描述

硬件上只連接了 I2C INT ,其中 I2C 用於傳輸觸摸屏數據。 INT 引腳用於通知 ARM 主機,觸摸屏被按下,可以讀取數據,這樣就不用主機定時 poll 設備,節省大量時間。

I2C 使用 I2C1INT 使用 GPX1-6 引腳。

移植

從目錄 \drivers\input\touchscreen 找到驅動 edt-ft5x06.c

設備樹

參考 exynos4.dtsi

i2c_1: i2c@13870000 {
        #address-cells = <1>;
        #size-cells = <0>;
        compatible = "samsung,s3c2440-i2c";
        reg = <0x13870000 0x100>;
        interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&clock CLK_I2C1>;
        clock-names = "i2c";
        pinctrl-names = "default";
        pinctrl-0 = <&i2c1_bus>;
        status = "disabled";
    };

寫自己的 dts

&i2c_1 {
    samsung,i2c-sda-delay = <100>;
    samsung,i2c-max-bus-freq = <400000>;
    status = "okay";

    ft5406: touchscreen@38 {
        compatible = "edt,edt-ft5406";
        reg = <0x38>;
        interrupt-parent = <&gpx1>;
        interrupts = <6 IRQ_TYPE_EDGE_FALLING>;
        touchscreen-size-x = <800>;
        touchscreen-size-y = <480>;
        touchscreen-fuzz-x = <4>;
        touchscreen-fuzz-y = <7>;
        touchscreen-fuzz-pressure = <2>;
        touchscreen-max-pressure = <2048>;
    };
};

ft5406 作爲 i2c1 的子節點。

Device Drivers  --->
        I2C support  ---> 
                I2C Hardware Bus support  ---> 
                        <*> S3C2410 I2C Driver

Device Drivers  --->                
        Input device support  --->          
                [*]   Touchscreens  --->
                        <*>   EDT FocalTech FT5x06 I2C Touchscreen support 

源碼分析

static int edt_ft5x06_ts_probe(struct i2c_client *client,
                     const struct i2c_device_id *id)
{
   /* 從設備樹獲取數據,主要包括:觸摸分辨率,按壓噪聲值, 水平/垂直噪聲等 */
    touchscreen_parse_properties(input, true, &tsdata->prop);

    /* 請求 INT(GPIO) 中斷,當觸摸屏被按壓時,以中斷的方式通知主機
        edt_ft5x06_ts_isr 爲中斷服務函數 
    */
    error = devm_request_threaded_irq(&client->dev, client->irq,
                    NULL, edt_ft5x06_ts_isr, irq_flags,
                    client->name, tsdata);
}

static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
{
    /* 通過 I2C總線 讀取觸摸屏數據 */
    error = edt_ft5x06_ts_readwrite(tsdata->client,
                    sizeof(cmd), &cmd,
                    datalen, rdbuf);
    /* 報告觸摸屏座標 */
    touchscreen_report_pos(tsdata->input, &tsdata->prop, x, y,
                       true);
}

燒錄

[    0.398601] s3c-i2c 13870000.i2c: slave address 0x00
[    0.398611] s3c-i2c 13870000.i2c: bus frequency set to 390 KHz
[    0.398866] s3c-i2c 13870000.i2c: i2c-1: S3C I2C adapter

[    2.654123] edt_ft5x06 1-0038: GPIO lookup for consumer reset
[    2.659300] edt_ft5x06 1-0038: using device tree for GPIO lookup
[    2.665309] of_get_named_gpiod_flags: can't parse 'reset-gpios' property of node '/i2c@13870000/touchscreen@38[0]'
[    2.675647] of_get_named_gpiod_flags: can't parse 'reset-gpio' property of node '/i2c@13870000/touchscreen@38[0]'
[    2.685953] edt_ft5x06 1-0038: using lookup tables for GPIO lookup
[    2.692049] edt_ft5x06 1-0038: lookup for GPIO reset failed
[    2.697579] edt_ft5x06 1-0038: GPIO lookup for consumer wake
[    2.703234] edt_ft5x06 1-0038: using device tree for GPIO lookup
[    2.709211] of_get_named_gpiod_flags: can't parse 'wake-gpios' property of node '/i2c@13870000/touchscreen@38[0]'
[    2.719466] of_get_named_gpiod_flags: can't parse 'wake-gpio' property of node '/i2c@13870000/touchscreen@38[0]'
[    2.729620] edt_ft5x06 1-0038: using lookup tables for GPIO lookup
[    2.735782] edt_ft5x06 1-0038: lookup for GPIO wake failed
[    2.743112] input input0: DT specifies parameters but the axis 58 is not set up
[    2.748771] input: EP08150M09 as /devices/platform/13870000.i2c/i2c-1/1-0038/input/input0

查看 proc

[root@TINY4412:~]# cat proc/bus/input/devices 
I: Bus=0018 Vendor=0000 Product=0000 Version=0000
N: Name="EP08150M09"
P: Phys=
S: Sysfs=/devices/platform/13870000.i2c/i2c-1/1-0038/input/input0
U: Uniq=
H: Handlers=mouse0 event0 
B: PROP=2
B: EV=b
B: KEY=400 0 0 0 0 0 0 0 0 0 0
B: ABS=2608000 3

I: Bus=0013 Vendor=dead Product=beef Version=0100
N: Name="tiny4412_lcd_key"
P: Phys=
S: Sysfs=/devices/virtual/input/input1
U: Uniq=
H: Handlers=kbd event1 
B: PROP=0
B: EV=3
B: KEY=40000800 40 0 0 0

I: Bus=0019 Vendor=001f Product=0001 Version=0100
N: Name="pwm-beeper"
P: Phys=pwm/input0
S: Sysfs=/devices/platform/buzzer/input/input2
U: Uniq=
H: Handlers=kbd event2 
B: PROP=0
B: EV=40001
B: SND=6

I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="gpio_keys"
P: Phys=gpio-keys/input0
S: Sysfs=/devices/platform/gpio_keys/input/input3
U: Uniq=
H: Handlers=kbd event3 
B: PROP=0
B: EV=3
B: KEY=3c

被識別爲 /dev/input/event0

APP

/*
 * touchscreen driver for tiny4412
 *
 * Copyright (c) 2017
 * Author: SY <[email protected]>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version. 
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h> 
#include <signal.h>
#include <stdbool.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/epoll.h>


#define ABS_X                   0x00
#define ABS_Y                   0x01
#define ABS_Z                   0x02

#define ABS_MT_POSITION_X       0x35    /* Center X touch position */
#define ABS_MT_POSITION_Y       0x36    /* Center Y touch position */



struct input_event {
        struct timeval time;
        unsigned short int type;
        unsigned short int code;
        signed int value;
};

#if 0
static void help(void)
{
        printf("Usage: ./key <id>\n");
}
#endif

bool esc = false;

static void sigint_handler(int dunno)
{
        switch (dunno) {
        case SIGINT:
                esc = true;
                printf("< Ctrl+C > Press.\n");
                break;
        default:
                break;
        }
}

static void* read_handler(void* data)
{
        printf("thread run.\n");

        int epfd = epoll_create1(0);
        if (epfd < 0) {
                perror("epoll_create1");
                return NULL;
        }

        int evfd = open("/dev/input/event0", O_RDONLY);
        if (evfd < 0) {
                perror("[open]");
                esc = true;
        }

        struct epoll_event epoll_event;
        epoll_event.events = EPOLLIN;
        epoll_event.data.fd = evfd;
        if (epoll_ctl(epfd, EPOLL_CTL_ADD, evfd, &epoll_event) < 0) {
                perror("[epoll_ctl]");
                esc = true;
        }

        printf("start epoll...\n");

        struct input_event event;
        const int MAX_EVENT_NUMS = 10;
        const int TIMEOUT = 100;
        struct epoll_event *events = calloc(MAX_EVENT_NUMS, sizeof(struct epoll_event));
        if (!events) {
                perror("mem calloc");
                esc = true;
        }

        while (esc == false) {
                int nums = epoll_wait(epfd, events, MAX_EVENT_NUMS, TIMEOUT);
                for (int i=0; i<nums; ++i) {
                        if (events[i].events & (EPOLLERR | EPOLLHUP)) {
                                perror("epoll");
                                continue;
                        } else if ((events[i].data.fd == evfd) && (events[i].events & EPOLLIN)) {
                                int ret = read(evfd, &event, sizeof(event));
                                if (ret < 0) {
                                        break;
                                }
                                //printf("[key] nums=%d code=%d value=%d\n", nums, event.code, event.value);

                                switch (event.code) {
                case ABS_MT_POSITION_X:
                        printf("X --> %d\n", event.value);
                        break;
                case ABS_MT_POSITION_Y:
                        printf("Y ------> %d\n", event.value);
                                        break;
                                case ABS_X:
                                        //printf("ABS-X: %d\n", event.value);
                                        break;
                                case ABS_Y:
                                        //printf("ABS-Y: %d\n", event.value);
                                        break;
                default:
                        break;
                }
                        }
                }
        }

        if (events) {
                free(events);
        }
        close(epfd);
        close(evfd);
        printf("thread exit.\n");

        pthread_exit(NULL);

        return NULL;
}

int main(int argc, char **argv)
{
        pthread_t thread_read;
        int ret = pthread_create(&thread_read, NULL, read_handler, NULL);
        if (ret) {
                perror("[thread_create]");
                return 1;
        }

        /* Register signal */
        signal(SIGINT, sigint_handler);

        pthread_join(thread_read, NULL);
        printf("done!\n");

        return 0;
}

測試

[root@TINY4412:~]# ./tmp/touchscreen 
thread run.
start epoll...
X --> 608
Y ------> 370
X --> 400
Y ------> 368
Y ------> 367
X --> 46
Y ------> 33
X --> 36
Y ------> 449
X --> 760
Y ------> 79
X --> 778
Y ------> 479
^C< Ctrl+C > Press.
thread exit.
done!
[root@TINY4412:~]# 

參考

LCD-HD101/zh

設備樹學習之(十三)電容觸摸屏驅動

  • 在大部分的ARM主控板中,我們發現,直接使用CPU自帶的ADC轉換器並不能很好的支持大尺寸(7寸以上)的四線電阻觸摸屏,市面上一般採用更加專業的USB或串口觸摸屏擴展模塊來解決。爲了節省ARM主控芯片的有限資源以及減少外擴,我們專門開發了只使用一個普通GPIO就可以實現專業觸摸效果的替代方案,並把它集成到我們的LCD模塊驅動板中,我們稱之爲“一線觸摸(1-Wire)”。它的基本原理是,使用一個低成本的MCU連接一個專業的觸控芯片(在此我們使用的是ADS7843或兼容芯片),採集並處理四線電阻模擬信號,並把濾波(未校準)後的穩定原始數據通過GPIO送給ARM主控,經我們長期反覆測試,即使在19寸這樣大的電阻觸摸屏上,也可以實現非常精準的觸摸效果,不會出現漂移抖動的現象。
  • 另外,當今12寸以內的LCD顯示屏,大都採用了LED背光,我們順便也把背光調節部分也交給MCU來處理,並設置了統一的調節數值區間,最後通過“一線觸摸”的GPIO傳給ARM主控,這樣在ARM端就可以非常方便的來設置背光了。
  • 與此同時,我們還爲我們設計開發的每一款帶“一線觸摸”的LCD模塊設置了編號存儲在MCU中,這樣通過一線通訊讀取到的編號,就可以知道這個LCD模塊的具體類型了,也就可以在bootloader和內核中自動匹配相應的LCD驅動參數,以此來實現無需修改任何配置,即插即用帶”一線觸摸”的LCD模塊。
  • 在電容觸摸LCD模塊中,我們則去掉了電阻觸控芯片,而保留了背光調節和存儲LCD類型編號這2個功能,因此電容觸摸通訊依然是標準的I2C接口。
  • 需要注意的是,我們實現的“一線觸摸”的通訊,和通常所說的單總線接口是不同的。在ARM主控端內部,我們實際採用了一路pwm timer(不是pwm管腳哦)來實現固定的通訊頻率(9600Hz),詳細請查看驅動源代碼。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章