Touch screen driver analysis S3C2440_ts.c

Publisher:SereneHeartLatest update time:2016-12-02 Source: eefocus Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

// There are many mysteries in just over 200 lines of program, especially the processing after the cursor is lifted.

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
# include

#include
#include

/* For ts.dev.id.version */
#define S3C2410TSVERSION 0x0101
//When x is 0, it is waiting for the press interrupt, and when x is 1, it is waiting for the lift interrupt
#define WAIT4INT(x) (((x)<<8) | \
       S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
       S3C2410_ADCTSC_XY_PST(3))
//Automatically and continuously measure the X and Y coordinates
#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
       S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
//Device name
static char *tq2440ts_name = "TQ2440 TouchScreen";

static struct input_dev *dev;//
static long xp;
static long yp;
static int count;

extern struct semaphore ADC_LOCK; //Declare a semaphore. This semaphore is defined in other files.
//The flag is set to 1 in the press interrupt handler and to 0 in the lift handler. It is judged in the AD conversion end interrupt handler.
//If it is 1, read the AD conversion number, and if it is 0, do nothing.
static int OwnADC = 0;
//Register base address
static void __iomem *base_addr;
//Pin configuration
static inline void tq2440_ts_connect(void)
{
 s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
 s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);
 s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);
 s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
}
//The timer timing time reaches the processing function, which is directly called in the press and lift interrupt processing function.
//The timer is triggered in the AD conversion end interrupt processing function and called after the delay
static void touch_timer_fire(unsigned long data)
{
   unsigned long data0;
   unsigned long data1;
 int updown;

   data0 = ioread32(base_addr+S3C2410_ADCDAT0);
   data1 = ioread32(base_addr+S3C2410_ADCDAT1);
//
  updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));

  if (updown) {//Press interrupt to execute the following statements
   if (count != 0)
   {
   long tmp;
                                                                                                 
   tmp = xp;
   xp = yp;
   yp = tmp;
                                                                                                 
                        xp >>= 2;//Four AD conversions to find the average value
                        yp >>= 2;

    input_report_abs(dev, ABS_X, xp);
    input_report_abs(dev, ABS_Y, yp); //Send the values ​​of x and y to user space

    input_report_key(dev, BTN_TOUCH, 1);//Report cursor press event
    input_report_abs(dev, ABS_PRESSURE, 1);
    input_sync(dev);//Indicates the end of the report
   }
/*The following five sentences are the statements executed when the function is directly called in the press interrupt processing function
, while the above statements are the statements executed when the timer is triggered and the function is called after delay in the AD conversion interrupt processing function when the 4 AD conversions are completed
(report the press result to the user space). The following five sentences will also be executed after the report is completed
to initialize the variables and trigger the second four AD conversions. This AD conversion will be executed until the cursor is lifted, that is, updown is 0
*/
   xp = 0;
   yp = 0;
   count = 0;
//Each press has four AD conversions. The following is the first AD conversion triggered in the press interrupt, and the other three are triggered in the AD conversion interrupt processing function
   iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
   iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
  }
  else
  {
   /*There are two places to handle the cursor lift, here and in the lift interrupt function. This function can trigger AD conversion, and the AD conversion initiated by this function
   will result in four consecutive AD conversions. This function may be called many times during the process of pressing and lifting the cursor. The first time
   is called in the press interrupt function, and the subsequent times are
   called after the timer is triggered in the AD conversion end interrupt function after four AD conversions are completed. So the whole time can be divided into two time periods, one is the time process of waiting for this function to be called, and the other is the time process of
   four AD conversions. The lifting of the cursor may occur in either of these two time periods. When the cursor is lifted in
   the previous time period, the interrupt lifting function will be executed, that is, the two sentences OwnADC = 0;up(&ADC_LOCK); will be executed
   . When it is lifted in the latter time period, the interrupt function will not be executed. Because the lifting interrupt will only be executed when WAIT4INT(1), and
   the lifting interrupt will not be executed during the AD conversion process. So the lifting interrupt processing function may not be executed, but it will definitely be executed here*/
   count = 0;

   input_report_key(dev, BTN_TOUCH, 0); //Report cursor lift event to user space
   input_report_abs(dev, ABS_PRESSURE, 0);
   input_sync(dev); //End of report

   iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC); //Put in the interrupt waiting state
  if (OwnADC) //If the interrupt function is executed, it will not be executed here
  {
   OwnADC = 0;
   up(&ADC_LOCK);
  }
  }
}

static struct timer_list touch_timer = //define a kernel timer
  TIMER_INITIALIZER(touch_timer_fire, 0, 0); //initialize the timer and assign it to the processing function touch_timer_fire
//cursor is pressed and lifted interrupt processing function
static irqreturn_t stylus_updown(int irq, void *dev_id)
{
 unsigned long data0;
 unsigned long data1;
 int updown;

 if (down_trylock(&ADC_LOCK) == 0) //Get the semaphore and release it in the up-handling function
 {
  OwnADC = 1; //Setting this flag to 1 indicates that the cursor is in the pressed state, and clear it in the up-handling function
  data0 = ioread32(base_addr+S3C2410_ADCDAT0);
  data1 = ioread32(base_addr+S3C2410_ADCDAT1);

  updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));

  if (updown)
  {
   touch_timer_fire(0); //If the cursor is pressed, this function is called
  }
  else //Statement executed when the cursor is raised
  {
   OwnADC = 0; //Clear
   up(&ADC_LOCK); //Release the semaphore
  }
 }

 return IRQ_HANDLED; //////
}

///AD conversion end interrupt processing function
static irqreturn_t stylus_action(int irq, void *dev_id)
{
 unsigned long data0;
 unsigned long data1;

 if (OwnADC) //OwnADC is 1, indicating that it is in the cursor press interrupt
 {
  data0 = ioread32(base_addr+S3C2410_ADCDAT0);
  data1 = ioread32(base_addr+S3C2410_ADCDAT1);

  xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
  yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
  count++;

  if (count < (1<<2))//Four AD conversions, add the four conversion values ​​to get the average value
  {//Trigger AD conversioniowrite32
   (S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
   iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
  }
  else
  {
   mod_timer(&touch_timer, jiffies+1);//Trigger kernel
   timeriowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC);//Put the device in the waiting state for the lift interrupt
  }
 }

 return IRQ_HANDLED;
}

static struct clk *adc_clock;

static int __init tq2440ts_init(void)
{
 struct input_dev *input_dev;

 adc_clock = clk_get(NULL, "adc");//Get clock "adc"
 if (!adc_clock)
 {
  printk(KERN_ERR "failed to get adc clock source\n");
  return -ENOENT;
 }
 clk_enable(adc_clock);//Enable clock
//Map a section of IO memory with S3C2410_PA_ADC as the starting point
 base_addr=ioremap(S3C2410_PA_ADC,0x20);
 if (base_addr == NULL)
 {
  printk(KERN_ERR "Failed to remap register block\n");
  return -ENOMEM;
 }

 /* Configure GPIOs */
 tq2440_ts_connect(); //Configure GPIOs

 iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),base_addr+S3C2410_ADCCON);
 iowrite32(0xffff, base_addr+S3C2410_ADCDLY);
 iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC); //Put the device in the waiting interrupt state

 /* Initialise input stuff */
 input_dev = input_allocate_device(); // Allocate memory for the input device structure input_dev and do corresponding initialization

 if (!input_dev)
 {
  printk(KERN_ERR "Unable to allocate the input device !!\n");
  return -ENOMEM;
 }

 dev = input_dev;
 dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS); //Set the supported events
 dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH); //Set the supported keybit
 input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0); //Set the maximum and minimum values
 ​​input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0);
 input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);

 dev->name = tq2440ts_name;
 dev->id.bustype = BUS_RS232;
 dev->id.vendor = 0xDEAD;
 dev->id.product = 0xBEEF;
 dev->id.version = S3C2410TSVERSION;
//Apply for AD conversion end interrupt
 if (request_irq(IRQ_ADC, stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM, tq2440ts_name, dev))
 {
  printk(KERN_ERR "tq2440_ts.c: Could not allocate ts IRQ_ADC !\n");
  iounmap(base_addr);
  return -EIO;
 } //Apply for press and lift interrupt
 if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, tq2440ts_name, dev))
 {
  printk(KERN_ERR "tq2440_ts.c: Could not allocate ts IRQ_ADC !\n");
  iounmap(base_addr);
  return -EIO;
 }

 printk(KERN_INFO "%s successfully loaded\n", tq2440ts_name); //Register the input device. A device node  input_register_device(dev) with
eventn (n is 0, 1, 2, etc.) will be generated under /dev ;

 return 0;
}

static void __exit tq2440ts_exit(void)
{
 disable_irq(IRQ_ADC);
 disable_irq(IRQ_TC);
 free_irq(IRQ_TC,dev);//Disable interrupt first and then release
 free_irq(IRQ_ADC,dev);

 if (adc_clock)
 {
  clk_disable(adc_clock);
  clk_put(adc_clock); //disable the clock first and then release
  adc_clock = NULL;
 }

 input_unregister_device(dev);
 iounmap(base_addr);
}


module_init(tq2440ts_init);
module_exit(tq2440ts_exit);


Reference address:Touch screen driver analysis S3C2440_ts.c

Previous article:S3C2440 Linux LCD Driver Interpretation
Next article:Linux-2.6.26 kernel ported to S3C2440 platform

Recommended ReadingLatest update time:2024-11-16 12:23

s3c2440 bare metal - UART programming - 2 - UART programming implementation
UART Programming 1. Initialization Our 2440 supports 3 UART serial ports, taking uart0 as an example. Then we need to implement the following functions to complete the most basic functions of the serial port: (1) uart0_init() is used to initialize the serial port (2) p
[Microcontroller]
S3C2440 bare metal ------Nor Flash programming_identification
1. Write a menu program First, we write a test menu program to obtain norFlash information and read and write NorFlash. The code is as follows: void nor_flash_test(void) { char c;   while (1) { /* Print menu for us to choose test content*/ printf(" Scan nor flashnr"); printf(" Erase nor flashnr"); prin
[Microcontroller]
S3C2440 bare metal ------Nor Flash programming_identification
Design of wireless monitoring and alarm system through embedded Linux and S3C2440 processor
With the rapid development of computer network technology, mobile communication technology, and multimedia technology, wireless monitoring systems have been widely used in military, industrial, agricultural and other occasions because of their convenience, practicality, and easy installation. They have also entered pe
[Microcontroller]
Design of wireless monitoring and alarm system through embedded Linux and S3C2440 processor
Some unclear concepts in S3C2440
UART Universal Asynchronous Receiver/Transmitter, UART is the abbreviation of Universal Asynchronous Receiver/Transmitter. UART is a chip used to control computers and serial devices. SPI interface SPI (Serial Peripheral Interface) bus system is a synchronous serial peripheral interface that enables MCU to communica
[Microcontroller]
ADC driver implementation for ARM Linux S3C2440
Hardware Description: The S3c2440 has a 10-bit CMOS ADC analog-to-digital converter that supports 8 analog channel inputs, 10-bit resolution, and a maximum speed of 500KSPS (500 kilosamples per second). As can be seen from the figure: the analog ADC includes two functions, one is the touch
[Microcontroller]
ADC driver implementation for ARM Linux S3C2440
S3C2440 Hardware Programming Example
S3C2440 Hardware Programming Example - GPIO (I) Assembly Program: @****************************************************************************** @ Function: LED lighting program, light up LED1-4 @************************************************************************
[Microcontroller]
S3C2440 Hardware Programming Example
s3c2440 bare metal - LCD programming - 2 - LCD controller
1. LCD controller block diagram As can be seen from the figure above, the S3C2440 LCD controller is used to transmit video data and generate necessary control signals, such as VFRAME, VLINE, VCLK, VM, etc. In addition to control signals, the S3C2440 also has a video data port, namely VD . By s
[Microcontroller]
Application of s3c2440 watchdog timer
The main function of the watchdog timer is to reset the system after the program runs away due to interference, so as not to make the system die forever.   Its principle is not much different from that of a general timer, that is, you need to set a period of time first, and when this period of time is exceeded, it wi
[Microcontroller]
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号