// 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);
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
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- The pipes kept exploding, and finally the power supply was blown away.
- Verilog Golden Reference Guide Chinese Edition
- Analysis of the interface technology circuit of the sensor used in ESP
- 5G infrastructure opens new integration frontiers inside RF chips
- FM1702SL basic operation experience in developing IC card
- Multi-channel optical power meter based on MSP430F2272 MCU
- EEWORLD University ----PI new product: LinkSwitch-TNZ
- I need help with the schematic diagram or other information of USR-GM3P, or a communication module similar to it.
- [FreeRTOS check-in second stop is open] Stack - the key to task switching, closing time is August 17
- TI-PMLK Boost Experiment Board for TPS55340 and LM5122