Touchscreen driver (input subsystem)

Publisher:丝语轻风Latest update time:2016-04-01 Source: eefocus Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
#include "linux/errno.h"
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/slab.h"
#include "linux/input.h"
#include "linux/init.h"
#include "linux/serio.h"
#include "linux/delay.h"
#include "linux/platform_device.h"
#include "linux/clk.h"
#include "asm/io.h"
#include "asm/irq.h"
 
#include "asm/plat-s3c24xx/ts.h"
 
#include "asm/arch/regs-adc.h"
#include "asm/arch/regs-gpio.h"
 
struct s3c_ts_regs {
        unsigned long adccon;
        unsigned long adctsc;
        unsigned long adcdly;
        unsigned long adcdat0;
        unsigned long adcdat1;
        unsigned long adcupdn;
};
 
static struct input_dev *s3c_ts_dev;
static volatile struct s3c_ts_regs *s3c_ts_regs;
 
static struct timer_list ts_timer;
 
//Wait until the stylus is pressed mode
static void enter_wait_pen_down_mode(void)
{
        s3c_ts_regs->adctsc = 0xd3;
}
//Wait until the stylus is released mode
static void enter_wait_pen_up_mode(void)
{
        s3c_ts_regs->adctsc = 0x1d3;
}
//Enter X/Y direction ADC simultaneous conversion mode
static void enter_measure_xy_mode(void)
{
        s3c_ts_regs->adctsc = (1<<3)|(1<<2);
}
//Start ADC conversion
static void start_adc(void)
{
        s3c_ts_regs->adccon |= (1<<0);
}
 
static int s3c_filter_ts(int x[], int y[])
{
#define ERR_LIMIT 10
 
        int avr_x, avr_y;
        int that_x, that_y;
 
        avr_x = (x[0] + x[1])/2;
        avr_y = (y[0] + y[1])/2;
 
        det_x = (x[2] > avr_x) ? (x[2] - avr_x) : (avr_x - x[2]);
        det_y = (y[2] > avr_y) ? (y[2] - avr_y): (avr_y - y[2]);
 
        if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))
                return 0;
 
        avr_x = (x[1] + x[2])/2;
        avr_y = (y[1] + y[2])/2;
 
        det_x = (x[3] > avr_x) ? (x[3] - avr_x) : (avr_x - x[3]);
        det_y = (y[3] > avr_y) ? (y[3] - avr_y): (avr_y - y[3]);
 
        if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))
                return 0;
        return 1;
}
 
static void s3c_ts_timer_function(unsigned long data)
{
        if (s3c_ts_regs->adcdat0 & (1<<15))
        {
                // Already released 
                input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
                input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
                input_sync(s3c_ts_dev);
                enter_wait_pen_down_mode();
        }
        else
        {
                // Measure X/Y coordinates 
                enter_measure_xy_mode();
                start_adc();
        }
}
 
//Stylus press, lift interrupt service function
static irqreturn_t pen_down_up_irq(int irq, void *dev_id)
{
        if (s3c_ts_regs->adcdat0 & (1<<15))
        {
                //printk("pen up\n");
                input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
                input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
                input_sync(s3c_ts_dev);
                enter_wait_pen_down_mode();
        }
        else
        {
                //printk("pen down\n");
                //enter_wait_pen_up_mode();
                enter_measure_xy_mode();
                start_adc();
        }
        return IRQ_HANDLED;
}
 
static irqreturn_t adc_irq(int irq, void *dev_id)
{
        static int cnt = 0;
        static int x[4], y[4];
        int adcdat0, adcdat1;
        // Optimization 2: If the stylus is released when ADC is completed, the result will be discarded 
        adcdat0 = s3c_ts_regs->adcdat0;
        adcdat1 = s3c_ts_regs->adcdat1;
 
        if (s3c_ts_regs->adcdat0 & (1<<15))
        {
                // Already released 
                cnt = 0;
                input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
                input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
                input_sync(s3c_ts_dev);
                enter_wait_pen_down_mode();
        }
        else
        {
                // printk("adc_irq cnt = %d, x = %d, y = %d\n", ++cnt, adcdat0 & 0x3ff, adcdat1 & 0x3ff);
                // Optimization measure 3: Calculate the average value by multiple measurements 
                x[cnt] = adcdat0 & 0x3ff;
                y[cnt] = adcdat1 & 0x3ff;
                ++cnt;
                if (cnt == 4)
                {
                         // Optimization measure 4: software filtering 
                         if (s3c_filter_ts(x, y))
                         {
                               //printk("x = %d, y = %d\n", (x[0]+x[1]+x[2]+x[3])/4, (y[0]+y[1]+y[2]+y[3])/4);
                               input_report_abs(s3c_ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);
                               input_report_abs(s3c_ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);
                               input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1);
                               input_report_key(s3c_ts_dev, BTN_TOUCH, 1);
                               input_sync(s3c_ts_dev);
                        }
                        cnt = 0;
                        enter_wait_pen_up_mode();
 
                        // Start the timer to handle long press/slide 
                        mod_timer(&ts_timer, jiffies + HZ/100);
                }
                else
                {
                                enter_measure_xy_mode();
                                start_adc();
                }
       }
        return IRQ_HANDLED;
}
 
static int s3c_ts_init(void)
{
         struct clk* clk;
         // 1. Allocate an input_dev structure 
         s3c_ts_dev = input_allocate_device();
 
         // 2. Setup 
         // 2.1 What kind of events can be generated 
         set_bit(EV_KEY, s3c_ts_dev->evbit);   //Key events
         set_bit(EV_ABS, s3c_ts_dev->evbit); //Absolute displacement events
 
         // 2.2 Which events of this type can be generated 
         set_bit(BTN_TOUCH, s3c_ts_dev->keybit);
 
         input_set_abs_params(s3c_ts_dev, ABS_X, 0, 0x3FF, 0, 0);
         input_set_abs_params(s3c_ts_dev, ABS_Y, 0, 0x3FF, 0, 0);
         input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0); //Pressure: 1 means pressed, 0 means released
 
 
         // 3. Register 
         input_register_device(s3c_ts_dev);
 
         // 4. Hardware related operations 
         //4.1 Enable clock (CLKCON[15]) 
         clk = clk_get(NULL, "adc");
         clk_enable(clk);
         // 4.2 Set the ADC/TS register of S3C2440 
         s3c_ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_regs));
 
         //bit[14]  : 1-A/D converter prescaler enable
          * bit[13:6]: A/D converter prescaler value,
          *            49, ADCCLK=PCLK/(49+1)=50MHz/(49+1)=1MHz
         * bit[0]: A/D conversion starts by enable. Set to 0 first
         //
         s3c_ts_regs->adccon = (1<<14)|(49<<6);
 
         request_irq(IRQ_TC, pen_down_up_irq, IRQF_SAMPLE_RANDOM, "ts_pen", NULL);
         request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM, "adc", NULL);
 
         //Optimization measure 1: 
         // Set ADCDLY to the maximum value, which will cause the IRQ_TC interrupt to be issued after the voltage stabilizes
         s3c_ts_regs->adcdly = 0xffff;
 
         //Optimization measure 5: Use timer to handle long press and sliding
         init_timer(&ts_timer);
         ts_timer.function = s3c_ts_timer_function;
         add_timer(&ts_timer);
 
         enter_wait_pen_down_mode();
         return 0;
}
 
static void s3c_ts_exit(void)
{
         free_irq(IRQ_TC, NULL);
         free_irq(IRQ_ADC, NULL);
         iounmap(s3c_ts_regs);
         input_unregister_device(s3c_ts_dev);
         input_free_device(s3c_ts_dev);
         del_hours(&ts_hours);
}
 
module_init(s3c_ts_init);
module_exit(s3c_ts_exit);
 
 
MODULE_LICENSE("GPL");
 
================================================================
Analysis:
After loading the driver, run the s3c_ts_init function, and the program enters the wait-for-stylus-down mode enter_wait_pen_down_mode(). When the stylus is pressed, it enters the press interrupt service function, that is, pen_down_up_irq. After entering the interrupt service function, it immediately determines whether the stylus is still pressed. If the stylus is released at this time, the event is reported; if the stylus is still pressed at this time, it enters the ADC conversion mode for both X/Y directions at the same time, and starts ADC conversion. After the ADC conversion is completed, the ADC interrupt service program is entered, and the ADC data in the x and y directions are obtained from adcdat0 and adcdat1, and then it is determined whether the stylus has left. If it has left, the data is reported; otherwise, the ADC conversion data is saved and then it is determined whether the data collected by the ADC has reached 4 times. If it has accumulated to 4 times, the event is reported after software filtering, and the wait for the stylus to leave mode enter_wait_pen_up_mode is entered, and the timer is started to start timing to process continuous pressing events; if it is less than 4 times, the X/Y bidirectional simultaneous ADC conversion mode is entered again, and the ADC conversion is started. When the timing time is up, it enters the timing interrupt service function, determines whether the stylus has left, and reports the event if the stylus is released, and enters the wait for the stylus to press mode, otherwise it enters the X/Y bidirectional simultaneous ADC conversion mode again, and starts the ADC conversion. In order!
 
Test 2th~7th:
1. Make menuconfig to remove the original touch screen driver
-> Device Drivers
  -> Input device support
    -> Generic input layer
      -> Touchscreens
      <>   S3C2410/S3C2440 touchscreens
 
make uImage
Boot with the new kernel
 
2. insmod s3c_ts.ko
Press/release the stylus
 
 
Test 2th~7th:
1. ls /dev/event* 
2. insmod s3c_ts.ko
3. ls /dev/event* 
4. hexdump /dev/event0
           Seconds      Microseconds   type code    value
0000000 29a4 0000 8625 0008 0003 0000 0172 0000
0000010 29a4 0000 8631 0008 0003 0001 027c 0000
0000020 29a4 0000 8634 0008 0003 0018 0001 0000
0000030 29a4 0000 8638 0008 0001 014a 0001 0000
0000040 29a4 0000 863c 0008 0000 0000 0000 0000
0000050 29a4 0000 c85e 0008 0003 0000 0171 0000
0000060 29a4 0000 c874 0008 0003 0001 027d 0000
0000070 29a4 0000 c87b 0008 0000 0000 0000
0000080 29a4 0000 ed37 0008 0003 0018 0000 0000
0000090 29a4 0000 ed48 0008 0001 014a 0000 0000
00000a0 29a4 0000 ed4a 0008 0000 0000 0000 0000
 
For the combined use of LCD and touch screen, please refer to "Tslib compilation and usage"
 
// Temporarily ignore the following three lines of commands
//sudo apt-get install autoconf
//sudo apt-get install automake
//sudo apt-get install libtool
 
Compile:
tar xzf tslib-1.4.tar.gz
cd tslib
./autogen.sh 
 
mkdir tmp
echo "ac_cv_func_malloc_0_nonnull=yes" >arm-linux.cache
./configure --host=arm-linux --cache-file=arm-linux.cache --prefix=$(pwd)/tmp
make
make install      //Install to tmp directory
 
Install:
cd tmp
Then copy all 4 files in the tmp directory to the root directory of the development board
cp * /home/book/workspace/JZ2440_TestFile/system/first_fs -rfd
(The root directory of the development board when the network file system is used for startup, but it is not copied to the flash of the development board at this time. If you want to copy it to the flash of the development board, you can start it without the network file system. Use manual mounting to mount the file system to the mnt directory of the development board, and then copy the files in tmp from the mnt directory to the root directory of the development board. In this way, it is really on the flash of the development board.) 
 
use:
 
Install s3c_ts.ko, lcd.ko first
 
1.
Modify line 1 of /etc/ts.conf (remove the # sign and the first space):
# module_raw input
to:
module_raw input
 
2. Set environment variables
export TSLIB_TSDEVICE=/dev/event0
export TSLIB_CALIBFILE=/etc/pointercal
export TSLIB_CONFFILE=/etc/ts.conf
export TSLIB_PLUGINDIR=/lib/ts
export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
 
Use the following two commands to test:
      ts_calibrate
      ts_test


Reference address:Touchscreen driver (input subsystem)

Previous article:Writing a block device driver to simulate a disk using memory
Next article:LCD driver (input subsystem)

Recommended ReadingLatest update time:2024-11-15 20:27

10.NandFlash driver_write operation
In the previous section, we learned how to read NandFlash page by page. Now let's implement NandFlash page by page. First, let's look at the timing diagram of the NandFlash chip K9F2G08U0A. According to the signal information of the I/O pin corner, we know that the steps required to implement the NandFlas
[Microcontroller]
10.NandFlash driver_write operation
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号