S5PV210 (TQ210) study notes - touch screen driver writing

Publisher:数字航海家Latest update time:2015-08-19 Source: eefocusKeywords:S5PV210 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
The driver of resistive touch screen is relatively simple, and can be written using the input subsystem driver framework, while the driver of capacitive touch screen is relatively complex, because capacitive touch is generally controlled by I2C interface. I was depressed for several days when I wrote the capacitive touch driver myself. Of course, it was not because the I2C capacitive touch driver was cumbersome, but mainly because the touch screen driver of Tianqian TQ210 was provided in a modular manner, and the source code was not open, and there was no touch chip manual. I had consulted Tianqian related people through technical consulting groups and telephone consultations, and wanted to ask them for the touch protocol instead of the so-called touch driver source code, but they were restricted by the confidentiality agreement and did not provide source code and manuals. We don't pursue these boring issues, as long as we find the model of the touch chip and find the corresponding manual according to the touch chip model, then we can write the so-called capacitive touch screen driver ourselves.

One-touch chip analysis

First, remove the four screws of the touch screen and turn over the touch screen to observe. You can see the touch chip on the touch screen cable. Observe the chip model carefully (if you can't see clearly, you can use a magnifying glass and a flashlight to view it). We can see that the touch screen control chip of TQ210 is GT811. Then I found the chip manual of GT811 (these materials have been uploaded to my CSDN resources, please support me). With the manual, it is not difficult to write the driver.

GT811 leads to 6 pins, namely VCC, GND, I2CSDA, I2CSCL, INT and RESET. Although the INT pin is not necessary, the development of efficient and resource-saving touch screen drivers often uses interrupt mode. The following is the pin diagram of GT811:

I used a multimeter to measure the pins of the touch module. The actual line sequence is GND, SDA, SDL, INT, RESET and VDD. The initialization sequence of GT811 is as follows:

  1. (1) Initialize the INT pin to a floating input state and initialize the RESET pin to an output state and output a low level  
  2. (2) Delay 1ms  
  3. (3) Initialize the RESET pin to a floating input state and enable the pull-up  
  4. (4) Write the GT811 register configuration table  
  5. (5) Configure the INT pin as needed  
For specific operations, please refer to the code section.

 

2. I2C driver writing

The I2C driver is also based on the bus structure, but it is divided into two types, one is the Legacy method and the other is the New Style method. The Legacy method is no longer supported in the new kernel, but Teacher Wei Dongshan’s video still analyzes the Legacy method. You can use Source Insight to track and analyze the New Style method yourself. I will not go into details here. You can refer to the following code for details.

  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6. #include   
  7. #include   
  8. #include   
  9.   
  10. const static unsigned short normal_address[] = {0x5d, I2C_CLIENT_END};  
  11. static unsigned gt811_rst;  
  12. static unsigned gt811_int;  
  13. static struct input_dev *ts_input;  
  14. static struct workqueue_struct *wq;  
  15. static struct work_struct work;  
  16.   
  17. static struct i2c_client * this_client = NULL;  
  18.   
  19. static unsigned int status = 0;  
  20.   
  21. static int i2c_read_bytes(struct i2c_client *client, uint8_t *buf, int len)  
  22. {  
  23.     struct i2c_msg msgs[2];  
  24.     int ret=-1;  
  25.       
  26.     msgs[0].flags=!I2C_M_RD;  
  27.     msgs[0].addr=client->addr;  
  28.     msgs[0].len=2;  
  29.     msgs[0].buf=&buf[0];  
  30.   
  31.     msgs[1].flags=I2C_M_RD;  
  32.     msgs[1].addr=client->addr;  
  33.     msgs[1].len=len-2;  
  34.     msgs[1].buf=&buf[2];  
  35.       
  36.     ret=i2c_transfer(client->adapter,msgs, 2);  
  37.     return ret;  
  38. }  
  39.   
  40. static int i2c_write_bytes(struct i2c_client *client,uint8_t *data,int len)  
  41. {  
  42.     struct i2c_msg msg;  
  43.     int ret=-1;  
  44.       
  45.     msg.flags=!I2C_M_RD;  
  46.     msg.addr=client->addr;  
  47.     msg.len=len;  
  48.     msg.buf=data;     
  49.       
  50.     ret=i2c_transfer(client->adapter,&msg, 1);  
  51.     return ret;  
  52. }  
  53.   
  54. static const struct i2c_device_id ts_id[] = {  
  55.     { "tq210-ts", 0 },  
  56.     { }  
  57. };  
  58.   
  59. static int ts_init_panel(struct i2c_client *client){  
  60.     short ret=-1;  
  61.     uint8_t config_info[] = {  
  62.         0x06,0xA2,  
  63.         0x12,0x10,0x0E,0x0C,0x0A,0x08,0x06,0x04,0x02,0x00,0xE2,0x53,0xD2,0x53,0xC2,0x53,  
  64.         0xB2,0x53,0xA2,0x53,0x92,0x53,0x82,0x53,0x72,0x53,0x62,0x53,0x52,0x53,0x42,0x53,  
  65.         0x32,0x53,0x22,0x53,0x12,0x53,0x02,0x53,0xF2,0x53,0x0F,0x13,0x40,0x40,0x40,0x10,  
  66.         0x10,0x10,0x0F,0x0F,0x0A,0x35,0x25,0x0C,0x03,0x00,0x05,0x20,0x03,0xE0,0x01,0x00,  
  67.         0x00,0x34,0x2C,0x36,0x2E,0x00,0x00,0x03,0x19,0x03,0x08,0x00,0x00,0x00,0x00,0x00,  
  68.         0x14,0x10,0xEC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0D,0x40,  
  69.         0x30,0x3C,0x28,0x00,0x00,0x00,0x00,0xC0,0x12,0x01     
  70.     }; [page]
  71.     config_info[62] = 480 >> 8;  
  72.     config_info[61] = 480 & 0xff;  
  73.     config_info[64] = 800 >> 8;  
  74.     config_info[63] = 800 & 0xff;  
  75.     ret = i2c_write_bytes(client, config_info, sizeof(config_info)/sizeof(config_info[0]));  
  76.     if(ret < 0) {  
  77.         printk(KERN_ERR "GT811 Send config failed! ");  
  78.         return ret;   
  79.     }  
  80.     return 0;  
  81. }  
  82.   
  83. static irqreturn_t gt811_int_handler(int irq, void *devid){  
  84.     disable_irq_nosync(this_client->irq);  
  85.     queue_work(wq, &work);  
  86.     return IRQ_RETVAL(IRQ_HANDLED);  
  87. }  
  88.   
  89. static void ts_work_func(struct work_struct* work){  
  90.     int ret;  
  91.     unsigned char point_data[19] = {0x07, 0x21, 0};  
  92.     unsigned short input_x = 0;  
  93.     unsigned short input_y = 0;  
  94.     unsigned short input_p = 0;  
  95.       
  96.     ret=i2c_read_bytes(this_client, point_data, sizeof(point_data)/sizeof(point_data[0]));  
  97.     if(ret <= 0){  
  98.         printk("Failed ");  
  99.         return;  
  100.     }  
  101.   
  102.     if(point_data[2]&0x1){  
  103.         status = 1;  
  104.         input_y = 479-((point_data[4]<<8)|point_data[5]);  
  105.         input_x = 799-((point_data[6]<<8)|point_data[7]);  
  106.         input_p = point_data[8];  
  107.   
  108.         printk("stat: %d, x: %d, y: %d, p: %d ", point_data[2], input_x, input_y,  
  109.             input_p);  
  110.     }  
  111.     else if(status){  
  112.         status = 0;  
  113.         printk("up ");  
  114.     }  
  115.   
  116.     enable_irq(this_client->irq);  
  117. }  
  118.   
  119. static int ts_probe(struct i2c_client *client, const struct i2c_device_id *id){  
  120.     int retry, ret;  
  121.     char test_data;  
  122.   
  123.     printk("ts_probe ");  
  124.   
  125.     test_data = 0;  
  126.   
  127.     gt811_rst = S5PV210_GPD0(3);  
  128.     gt811_int = S5PV210_GPH1(6);  
  129.     gpio_request(gt811_rst, "reset");  
  130.     gpio_request(gt811_rst, "tsint");  
  131.       
  132.     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))   
  133.     {  
  134.         dev_err(&client->dev, "Must have I2C_FUNC_I2C. ");  
  135.         return -ENODEV;  
  136.     }  
  137.   
  138.     s3c_gpio_setpull(gt811_rst, S3C_GPIO_PULL_UP);  
  139.       
  140.     for(retry=0;retry < 5; retry++)  
  141.     {  
  142.         gpio_direction_output(gt811_rst, 0);  
  143.         msleep(1);  
  144.         gpio_direction_input(gt811_rst);  
  145.         msleep(100);  
  146.       
  147.         ret =i2c_write_bytes(client, &test_data, 1);  
  148.         if (ret > 0)  
  149.             break;  
  150.         dev_info(&client->dev, "GT811 I2C TEST FAILED! Please check the HARDWARE connect ");  
  151.     }  
  152.   
  153.     if(ret <= 0)  
  154.     {  
  155.         dev_err(&client->dev, "Warning: I2C communication might be ERROR! ");  
  156.         return -ENODEV;  
  157.     }  
  158.   
  159.     for(retry = 0; retry != 5; ++ retry){  
  160.         ret = ts_init_panel(client);  
  161.         if(ret != 0){  
  162.             continue;  
  163.         }  
  164.         else{  
  165.             break;  
  166.         }  
  167.     }  
  168.   
  169.     if(ret != 0){  
  170.         printk("GT811 Configue failed! ");  
  171.         return -ENODEV;  
  172.     }  
  173.   
  174.     this_client = client;  
  175.       
  176.     ts_input = input_allocate_device();  
  177.     if (IS_ERR(ts_input)) {  
  178.         printk("GT811 allocate ts input device failed! ");  
  179.         return -ENOMEM;  
  180.     }  
  181.   
  182.     ts_input->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);  
  183.     ts_input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);  
  184.     ts_input->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);  
  185.   
  186.     input_set_abs_params(ts_input, ABS_Y, 0, 799, 0, 0);  
  187.     input_set_abs_params(ts_input, ABS_X, 0, 479, 0, 0);  
  188.     input_set_abs_params(ts_input, ABS_PRESSURE, 0, 255, 0, 0);  
  189.   
  190.     ts_input->name = "tq210-ts";  
  191.     ts_input->phys = "input/ts";  
  192.     ts_input->id.bustype = BUS_I2C;  
  193.     ts_input->id.product = 0xBEEF;  
  194.     ts_input->id.vendor =0xDEAD;  
  195.   
  196.     ret = input_register_device(ts_input);  
  197.     if(ret < 0){  
  198.         printk("Unable register %s input device! ", ts_input->name);  
  199.         input_free_device(ts_input);  
  200.         return -ENOMEM;  
  201.     }  
  202.   
  203.     client->irq = IRQ_EINT(14);  
  204.   
  205.     s3c_gpio_setpull(gt811_int, S3C_GPIO_PULL_UP);  
  206.   
  207.     if(request_irq(IRQ_EINT(14), gt811_int_handler, IRQF_TRIGGER_FALLING, "gt811-int", NULL) < 0){  
  208.         printk("Request irq for gt811 failed! ");  
  209.         input_unregister_device(ts_input);  
  210.         input_free_device(ts_input);  
  211.         return -ENOMEM;  
  212.     }  
  213.   
  214.     wq = create_workqueue("ts_handle_thread");  
  215.     if(wq == NULL){  
  216.         printk(KERN_ALERT "crete workqueue failed! ");  
  217.         input_unregister_device(ts_input);  
  218.         input_free_device(ts_input);  
  219.         free_irq(IRQ_EINT(14), NULL);  
  220.         return -ENOMEM;  
  221.     }  
  222.   
  223.     INIT_WORK(&work, ts_work_func);  
  224.       
  225.     return 0;  
  226. }  
  227.   
  228. static int ts_remove(struct i2c_client *client){  
  229.       
  230.     free_irq(IRQ_EINT(14), NULL);  
  231.     enable_irq(client->irq);  
  232.     flush_workqueue(wq);  
  233.     destroy_workqueue(wq);  
  234.       
  235.     input_unregister_device(ts_input);  
  236.     input_free_device(ts_input);  
  237.   
  238.     gpio_free(gt811_rst);  
  239.     gpio_free(gt811_int);  
  240.     return 0;  
  241. } [page]
  242.   
  243. static struct i2c_driver ts_driver = {  
  244.     .driver = {  
  245.         .name = "tq210-ts",  
  246.         .owner = THIS_MODULE,  
  247.     },  
  248.   
  249.     .probe = ts_probe,  
  250.     .remove = ts_remove,  
  251.     .id_table = ts_id,  
  252.     .address_list = normal_address,  
  253. };  
  254.   
  255. static int ts_init(void){  
  256.     printk("init ");  
  257.     i2c_add_driver(&ts_driver);  
  258.     return 0;  
  259. }  
  260.   
  261. static void ts_exit(void){  
  262.     i2c_del_driver(&ts_driver);  
  263.     printk("exit ");  
  264. }  
  265.   
  266. module_init(ts_init);  
  267. module_exit(ts_exit);  
  268. MODULE_LICENSE("GPL");  
This is not a complete code. On the one hand, there is no exception handling, and on the other hand, there is no message reporting. It simply drives the touch screen part of TQ210. If you need to use it, you can make slight modifications.

 

Three registered TS I2C module devices

Registering the I2C module of TS is very simple. Add the I2C address of TS to the I2C channel 2 structure in the Linux kernel file arch/arm/mach-s5pv210/mach-smdkv210.c file, which is 0x5d. After adding, it is as follows
  1. static struct i2c_board_info smdkv210_i2c_devs2[] __initdata = {  
  2.     /* To Be Updated */  
  3.     { I2C_BOARD_INFO("tq210-ts", 0x5d), },  
  4. ;  

Four tslib test tutorials (ubuntu)

1. Install git
  1. sudo apt-get install git  

2. Download the latest tslib
  1. git clone https://github.com/kergoth/tslib  

3. Install auto
  1. sudo apt-get install autoconf automake libtool  

4. Compile tslib
  1. ./autogen.sh   
  2. mkdir tmp  
  3. echo "ac_cv_func_malloc_0_nonnull=yes" >arm-linux.cache  
  4. ./configure --host=arm-linux --cache-file=arm-linux.cache --prefix=$(pwd)/tmp  
  5. make  
  6. make install  

5. Install tslib
  1. cd tmp  
  2. cp * /nfsroot/rootfs -rfd  

6. Configure tslib
  1. Modify /etc/ts.conf  
  2. Will go  
  3. # module_raw input  
  4. to:  
  5. module_raw input  
  6. (Actually, it removes the # sign and the first space in the high line)  

7. Configure tslib running environment variables
  1. export TSLIB_TSDEVICE=/dev/input/event1 //This needs to be modified according to your own event location. The new kernel is in /dev/input/event*  
  2. export TSLIB_CALIBFILE=/etc/pointercal  
  3. export TSLIB_CONFFILE=/etc/ts.conf  
  4. export TSLIB_PLUGINDIR=/lib/ts  
  5. export TSLIB_CONSOLEDEVICE=none  
  6. export TSLIB_FBDEVICE=/dev/fb0  

8. Calibration (capacitive screen does not actually need calibration, it is only for testing the touch screen driver)
  1. Run ts_calibrate and perform calibration according to the prompts  

9. Free drawing
  1. Run ts_test and click the draw button to draw freely. The effect is as shown below.  
 

Five conclusions

The code listed in this article simply implements the acquisition of touch coordinates, but does not implement operations such as touch message reporting. These operations need to be implemented by yourself.
 
I improved the driver mentioned above. The following is the result of the test on TQ210 using the latest version of tslib. It also supports multi-touch. I uploaded the code to my resources. Please download it if you need it. The resource is a bit expensive, please forgive me. . .
 
Keywords:S5PV210 Reference address:S5PV210 (TQ210) study notes - touch screen driver writing

Previous article:S5PV210 (TQ210) study notes - USB HOST transplantation
Next article:S5PV210 (TQ210) study notes - LCD driver writing

Recommended ReadingLatest update time:2024-11-16 14:49

Zhu's ARM bare metal learning notes (II): S5PV210 memory map
S5PV210 Introduction S5PV210 is a microprocessor based on ARM Crotex-A8 architecture 32-bit CPU.  It has 32 address lines and 32-bit data lines. The 32 address lines determine that the CPU address space is 4G at most. How to allocate this 4G memory space is called memory mapping. S5PV210 Memory Map The S5PV210 datash
[Microcontroller]
Zhu's ARM bare metal learning notes (II): S5PV210 memory map
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号