历史上的今天
返回首页

历史上的今天

今天是:2024年10月12日(星期六)

正在发生

2018年10月12日 | Tiny210触摸屏之触摸屏控制器驱动

2018-10-12 来源:eefocus

tiny_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"

#define MHZ (1000*1000)

#define PRINT_MHZ(m)             ((m) / MHZ), ((m / 1000) % 1000)

struct tiny_ts_regs {

    unsigned long tsadccon1;

    unsigned long tscon1;

    unsigned long tsdly1;

    unsigned long tsdatx1;

    unsigned long tsdaty1;

    unsigned long tspenstat1;

    unsigned long clrintadc1;

    unsigned long reserved;

    unsigned long clrintpen1;

};

static struct input_dev *tiny_ts_dev;

static volatile struct tiny_ts_regs *tiny_ts_regs;

static unsigned long *tsadccon0;

static struct timer_list ts_timer;

static void enter_wait_pen_down_mode(void)

{

    tiny_ts_regs->tscon1 = 0xd3;

}

static void enter_wait_pen_up_mode(void)

{

    tiny_ts_regs->tscon1 = 0x1d3;

}

static void enter_measure_xy_mode(void)

{

    tiny_ts_regs->tscon1 = (1<<3)|(1<<2);

}

static void start_adc(void)

{

    tiny_ts_regs->tsadccon1 |= (1<<0);

}

static int tiny_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 tiny_ts_timer_function(unsigned long data)

{

    if (tiny_ts_regs->tsdatx1 & (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 (tiny_ts_regs->tsdatx1 & (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 = tiny_ts_regs->tsdatx1;

    adcdat1 = tiny_ts_regs->tsdaty1;

    if (tiny_ts_regs->tsdatx1 & (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 (tiny_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结构体 

    tiny_ts_dev = input_allocate_device();

    // 2. 设置 

    // 2.1 能产生哪类事件 

    set_bit(EV_KEY, tiny_ts_dev->evbit);

    set_bit(EV_ABS, tiny_ts_dev->evbit);

    // 2.2 能产生这类事件里的哪些事件 

    set_bit(BTN_TOUCH, tiny_ts_dev->keybit);

    input_set_abs_params(tiny_ts_dev, ABS_X, 0, 0x3FF, 0, 0);

    input_set_abs_params(tiny_ts_dev, ABS_Y, 0, 0x3FF, 0, 0);

    input_set_abs_params(tiny_ts_dev, ABS_PRESSURE, 0, 1, 0, 0);

    // 3. 注册 

    input_register_device(tiny_ts_dev);

    // 4. 硬件相关的操作 

    // 4.1 使能时钟(CLKCON[15]) 

    clk = clk_get(NULL, "adc");

    if (!clk || IS_ERR(clk)) {

        printk(KERN_INFO "failed to get adc clock source\n");

    }

    clk_enable(clk);

    printk("Tiny_TS clock got enabled :: %ld.ld Mhz\n", PRINT_MHZ(clk_get_rate(tiny_clk)));

    // 4.2 设置S3C2440的ADC/TS寄存器 

    tsadccon0 =  ioremap(0xE1700000, 4);

    tiny_ts_regs = ioremap(0xE1701000, sizeof(struct tiny_ts_regs));

    // s5pv210有触摸屏接口0,1

    // Tiny210开发板向外提供接口1

    // 所以这里选择接口1

    tsadccon0 |= 1<<17;

    

    // bit[14]  : 1-A/D converter prescaler enable

    // bit[13:6]: A/D converter prescaler value,

    //            65, ADCCLK=PCLKP/(65+1)=66.7MHz/(65+1)=1MHz

    // bit[2]: 默认为1,即Standby mode,我们要设置为0,正常模式

    // bit[0]: A/D conversion starts by enable. 先设为0

    tiny_ts_regs->tsadccon1 &= ~(1<<2);

    tiny_ts_regs->tsadccon1 |= (1<<14)|(65<<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中断

    tiny_ts_regs->tsdaty1 = 0xffff;

    // 优化措施5: 使用定时器处理长按,滑动的情况 

    init_timer(&ts_timer);

    ts_timer.function = tiny_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(tiny_ts_regs);

    iounmap(tsadccon0);

    input_unregister_device(tiny_ts_dev);

    input_free_device(tiny_ts_dev);

    del_timer(&ts_timer);

}

module_init(s3c_ts_init);

module_exit(s3c_ts_exit);

MODULE_LICENSE("GPL");

===================================================================

测试以及tslib-1.4.tar.gz安装编译方法参考:“触摸屏驱动程序(输入子系统)”


推荐阅读

史海拾趣

富信半导体(FOSAN)公司的发展小趣事

台湾第一电阻(Firstohm)公司的发展故事

故事一:创立与早期技术积累

台湾第一电阻电容器股份有限公司(Firstohm)成立于1969年,初期便专注于电阻器的生产制造。在那个电子工业刚刚起步的年代,Firstohm凭借其敏锐的市场洞察力和对技术的不懈追求,逐步在电阻器领域站稳脚跟。1970年,公司开始接受日本东京应濑无线公司的代工订单,外销至日本市场,这一合作为Firstohm打开了国际市场的大门。此后,公司不断引进和吸收先进技术,如1986年从日本横演电子精工取得的精密级金属皮膜电阻技术,为公司的技术积累奠定了坚实基础。

故事二:晶圆电阻的突破

1987年,Firstohm成功研发并量产晶圆电阻(又称MELF电阻),这一成就标志着公司在电阻器制造技术上的重大突破。晶圆电阻以其优异的性能和广泛的应用领域,迅速获得了市场的认可。Firstohm也因此成为全球少数几家具备晶圆电阻生产能力的厂商之一,为公司后续的发展奠定了技术领先地位。

故事三:国际认证与品质提升

进入21世纪,Firstohm更加重视产品品质和国际化发展。2001年,公司获得ISO14001 Green Dove奖项及ISO9001认证,标志着公司在环境管理和质量管理方面达到了国际先进水平。此后,公司不断通过国际认证,如ISO9001:2008、IECQ品质认证等,进一步提升了产品的竞争力和市场信誉。这些认证不仅是对公司产品质量的认可,也为公司拓展国际市场提供了有力支持。

故事四:创新产品的研发

Firstohm始终坚持创新驱动发展战略,不断推出符合市场需求的新产品。例如,2012年,公司间隙式突波吸收器(SGS)获得日本、中国大陆及韩国专利许可;2017年,成功开发AEC-Q200车规级晶圆电阻;2020年,又成功开发出心电导线图(ECG cable)专用电阻器SSWAT系列。这些创新产品的研发和推出,不仅丰富了公司的产品线,也进一步巩固了公司在电阻器领域的市场地位。

故事五:自动化生产与全球化布局

随着电子行业的快速发展和市场竞争的加剧,Firstohm不断加大在自动化生产和全球化布局方面的投入。公司生产线高度自动化,生产效率和产品品质显著提升。同时,Firstohm积极拓展海外市场,与全球多家知名电子企业建立了长期稳定的合作关系。例如,与美国通用、THOMSON、法国雷诺等公司的合作,不仅提升了公司的品牌知名度,也为公司带来了更多的市场机遇和发展空间。

ELINA INDEK公司的发展小趣事

在电子行业中,技术变革日新月异,市场竞争异常激烈。面对这些挑战,因美纳始终保持着敏锐的洞察力和灵活的反应能力。公司不断调整产品策略和市场布局,积极应对行业变革。同时,因美纳还注重培养员工的创新能力和团队协作精神,为公司的发展提供源源不断的动力。

Forge Europa Ltd公司的发展小趣事

在快速发展的过程中,Forge Europa Ltd公司始终注重品牌与文化的塑造。公司秉承“创新、品质、服务”的企业精神,致力于为客户提供优质的产品和服务。同时,公司还注重员工的培养和发展,建立了完善的培训体系和激励机制,激发了员工的创造力和工作热情。通过这些努力,Forge Europa不仅塑造了一个具有行业影响力的品牌形象,还形成了独特的企业文化,为企业的长远发展奠定了坚实的基础。

APTA Group Inc公司的发展小趣事

APTA Group Inc在企业管理方面同样敢于创新。公司引入了一套先进的管理系统,通过数据分析和智能化决策,大大提高了工作效率和决策准确性。同时,公司还注重员工培训和激励,打造了一支高效、专业的团队。这些创新的管理举措,为APTA的稳健发展提供了坚实保障。

Conxall公司的发展小趣事

为了进一步提升公司的竞争力,Conxall公司积极寻求与其他企业的合作。公司与多家知名电子企业签订了战略合作协议,共同研发新技术、新产品。通过合作,Conxall公司不仅获得了更多的技术资源和市场支持,还提升了自身的品牌形象和知名度。

Ferranti Electric Inc公司的发展小趣事

随着技术的不断发展和创新,Ferranti Electric Inc公司在电气领域取得了显著的成就。特别是在设计和制造用于恶劣气候或关键安全领域的复杂、高可靠性电气产品方面,公司展示了其卓越的技术实力。这些产品不仅广泛应用于军用领域,同时也为民用领域提供了大量电气产品解决方案。公司的全球影响力逐渐扩大,产品远销世界各地。

问答坊 | AI 解惑

动力环境监控系统互联问题

由于各种原因,电信行业运营商网上运行的动力环境集中监控系统(以下简称:监控系统)常常不只一个厂家的系统,而且一般各厂家的系统都根据自己的硬件系统,开发了不同的应用软件。不同厂家的系统不能简单的共享数据与操作、管理平台,不便于集中维 ...…

查看全部问答>

SD卡的动态插入与拔出,在AP层怎么检测?必须自己写个小的驱动?还是只能用定时器不断查询其状态?

SD卡的动态插入与拔出,在AP层怎么检测?必须自己写个小的驱动?还是只能用定时器不断查询其状态?…

查看全部问答>

arms3c2410 急…… 大家给看看吧,初学遇到了点问题

    我现在在编写一个按键驱动程序,我已经实现了两个按键的驱动程序,可我的问题是,首先是板子给的中断口不多(4个左右),我想编写一个8个左右的按键驱动,但必须知道是哪个按键有请求,我想只用一个中断口来实现,我想了想用矩阵按键 ...…

查看全部问答>

WinCE6.0内核模式驱动程序下的缓冲区问题

     之前在WinCE6.0下的音频驱动程序中(内核模式下),申请了32B的空间,直接使用,不做映射和检查嵌入指针。应用程序写32B进来,驱动程序写32B到音频解码芯片中,可以播放音乐。只是这样采用单线程,播放时会有一点“毛刺” ...…

查看全部问答>

请问如何用evc控制explore打开一个指定路径的文件夹浏览?

我要写个evc程序控制打开一个指定路径的文件夹浏览窗 比如触发某个事件,程序能让wince打开存储卡\\\\sd\\\\test\\\\文件夹进行浏览和操作, SHBrowseForFolder行不行?还是有更简单的办法? 平台:evc4.0+ wince5.0+arm…

查看全部问答>

买书了,买书了。给点建议嘛

最近读书生活费存了点,准备买几本书,首先我准备 看点板级开发/嵌入式方面的。 我自己要买的书有2本  《c++ primer 4》 (看了2遍了,还是准备买本)和《代码大全 2》。 SO  再推荐基本  板级开发/嵌入式方面 的 ...…

查看全部问答>

天津电子口岸发展有限公司--高薪诚聘

天津电子口岸发展有限公司 天津电子口岸发展有限公司是在天津市政府与海关总署等有关部委的支持下创立的,注册资金3000万元。公司承担 “天津国际航运中心和物流中心暨天津电子口岸信息系统”的建设和运营工作,根据公司发展需要,现诚聘如下职 ...…

查看全部问答>

你习惯说LM3S,还是说流明,这可是资历问题啊

虽然LM3S6892的名头已经很响了,看到坛子里还有一些人习惯说流明,这应该是原来Luminary那带过来的,当时还没被TI收购前的名字。习惯叫流明的人,说明在TI M3上,真的是很资深啊,因为那时只有很少的一部分人接触过。…

查看全部问答>

数据驱动控制理论及方法的回顾和展望

数据驱动控制理论及方法的回顾和展望…

查看全部问答>

请教:STM32仿真和DOWNLOAD跑,哪里不一样?

那各位在调试ARM STM32 的时候是仿真着跑找问题,还是直接DOWNLOAD跑,然后看串口输出?有区别吗? 之前好像看过论坛一个哥们说,不太一样。…

查看全部问答>