sheldon_blogs

Linux学习: 触摸屏驱动

一、Linux输入子系统的结构:

 

 

二、触摸屏驱动代码:

  s3c_ts.c

 

复制代码
#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;

static void enter_wait_pen_down_mode(void)
{
    s3c_ts_regs->adctsc = 0xd3;
}

static void enter_wait_pen_up_mode(void)
{
    s3c_ts_regs->adctsc = 0x1d3;
}

static void enter_measure_xy_mode(void)
{
    s3c_ts_regs->adctsc = (1<<3)|(1<<2);
}

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 det_x, det_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))
    {
        /* 已经松开 */
        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
    {
        /* 测量X/Y坐标 */
        enter_measure_xy_mode();
        start_adc();
    }
}


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;
    
    
    /* 优化措施2: 如果ADC完成时, 发现触摸笔已经松开, 则丢弃此次结果 */
    adcdat0 = s3c_ts_regs->adcdat0;
    adcdat1 = s3c_ts_regs->adcdat1;

    if (s3c_ts_regs->adcdat0 & (1<<15))
    {
        /* 已经松开 */
        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);
        /* 优化措施3: 多次测量求平均值 */
        x[cnt] = adcdat0 & 0x3ff;
        y[cnt] = adcdat1 & 0x3ff;
        ++cnt;
        if (cnt == 4)
        {
            /* 优化措施4: 软件过滤 */
            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();

            /* 启动定时器处理长按/滑动的情况 */
            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. 分配一个input_dev结构体 */
    s3c_ts_dev = input_allocate_device();

    /* 2. 设置 */
    /* 2.1 能产生哪类事件 */
    set_bit(EV_KEY, s3c_ts_dev->evbit);
    set_bit(EV_ABS, s3c_ts_dev->evbit);

    /* 2.2 能产生这类事件里的哪些事件 */
    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);


    /* 3. 注册 */
    input_register_device(s3c_ts_dev);

    /* 4. 硬件相关的操作 */
    /* 4.1 使能时钟(CLKCON[15]) */
    clk = clk_get(NULL, "adc");
    clk_enable(clk);
    
    /* 4.2 设置S3C2440的ADC/TS寄存器 */
    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. 先设为0
     */
    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);

    /* 优化措施1: 
     * 设置ADCDLY为最大值, 这使得电压稳定后再发出IRQ_TC中断
     */
    s3c_ts_regs->adcdly = 0xffff;

    /* 优化措施5: 使用定时器处理长按,滑动的情况
     * 
     */
    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_timer(&ts_timer);
}

module_init(s3c_ts_init);
module_exit(s3c_ts_exit);


MODULE_LICENSE("GPL");
复制代码

  Makefile

复制代码
KERN_DIR = /work/system/linux-2.6.22.6

all:
    make -C $(KERN_DIR) M=`pwd` modules 

clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

obj-m    += s3c_ts.o
复制代码

 

三、驱动测试:

1. make menuconfig  //去掉原来的触摸屏驱动程序
-> Device Drivers
  -> Input device support
    -> Generic input layer
      -> Touchscreens
      <>   S3C2410/S3C2440 touchscreens

2. ls /dev/input/event*    //确认当前event节点有哪些
3. insmod s3c_ts.ko  //加载触摸屏驱动模块
4. ls /dev/input/event*   //查看触摸屏节点
5. hexdump /dev/input/event0 //十六进制打印输入数据
            秒       微秒   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 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

 

posted on   sheldon_blogs  阅读(1214)  评论(0编辑  收藏  举报

编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示