2020-08-16

 

 我上一篇的帖子写了如何写lcd驱动框架,其实在一开始的时候,我相信有的人也有跟我一样的误区,以为LCD和触摸屏是同种概念的东西,但是并不是这样的,LCD是LCD,触摸屏是触摸屏,触摸屏的结构其实是可以说是两张很薄的肉眼看不见的膜,然后贴附在LCD液晶屏上。本帖以电阻式触摸屏为例来简单总结一下触摸屏驱动程序的框架,触摸屏还有一种比较常见的是电容式触摸屏,我们现在的苹果手机所用的触摸屏就是电容式触摸屏,想要了解的朋友可以自行去网上搜索资料。触摸屏的具体模型如下图:

1504363703(1).png
在写触摸屏驱动程序前,我们需要先了解一下电阻式触摸屏(在下文简称触摸屏)的硬件工作原理
想要测量X坐标时:                                      想要测量Y坐标时:
1、xp接高电平                                            1、YP接高电平
2、xm接地                                                 2、YM接地
3、YM和YP不接                                         3、xp和xm不接
4、测YP电压                                              4、测xp电压
这么说大家可能还很难理解,我再换种方式和大家介绍说明一下,我们举测量X坐标时做为例子
 
        大家把图中那个电阻当成是我们的触摸屏,当要测量X坐标时,YM、YP悬空,我们按下触摸屏时,上下两层膜便产生一个触点,通俗点说我们就可以把图中的电阻想象成了一个滑动变阻器(变位器),我们按下触摸屏时产生的触点就可以当成变位器的第三个管脚,触点所在位置的电压值就可以通过计算计算出来了,我们再将得出的电压值进行AD转换得到我们需要的X坐标。这个就是触摸屏的基本工作原理了。
        我所使用的s3c2440的ADC和触摸屏接口结构中会产生两个中断信号,一个是INT_TC(触摸屏被按下产生的中断),另一个是INT_ADC(ADC转换完成产生的中断)

 

触摸屏驱动的编写我们可以根据触摸屏的使用过程来逐步完成

触摸屏使用过程
(1)当没有按下时,触摸屏控制器处于等待中断模式(这里等待的是INT_TC中断)。
(2)一旦被按下就会产生INT_TC中断
(3)在触摸屏中断处理函数里,使其启用下面两种转换模式中的一种:一种是分离的x/y轴坐标转化模式,另一种是连续的x/y轴转换模式。
(4)一旦转换完,就会产生INT_ADC中断
(5)在ADC中断处理函数中上报ADC转换值

        但是这样是有一个问题的,因为我们按下一次,只会产生一个中断,如果我们滑动的话,就不会产生任何效果。为了处理滑动,我们可以在(5)中添加如下功能:启用定时器,每隔一段时间就启用一次AD转化,这样就可以得到按下点的实时信息了。
(6)松开

第一大部分:触摸屏驱动程序的入口函数s3c_ts_init();
第一步:因为触摸屏是工作在输入子系统上的,因此我们需要在初始化函数中完成分配input_dev结构体,设置参数,注册结构体等步骤。
      
        /*分配一个input_dev 结构体*/
        s3c_ts_dev = input_allocate_device();
        /*设置*/
        /*能产生哪类事件*/
        set_bit(EV_KEY, s3c_ts_dev -> evbit);
        set_bit(EV_ABS, s3c_ts_dev -> evbit);

        /*能产生这类事件里的哪些事件*/
        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);
        
        /*注册*/
        input_register_device(s3c_ts_dev);

第二步:另外我们还需要配置clkcon寄存器
        struct clk* clk;
        clk = clk_get(NULL, "abc");
        clk_enable(clk);
        clkcon寄存器的作用就是选择使能哪个模块,如需要该模块便将该模块所对应在clkcon寄存器中的位设置为1,其目的是为了省电,内核在启动的时候会把一些不需要的模块关掉,设置clkcon寄存器,所以当我们需要使用某一个模块的功能的时候需要把它打开。

第三步:既然要产生中断,很明显我们需要申请中断触摸屏中断和ADC结束产生的中断,也就是上文所提到的当触摸屏被按下时产生的中断TRQ_TS,申请中断函数为      
        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);

request_irq申请中断函数的原型为
int request_irq(unsigned int irq, irq_handler_t handler,
                             unsigned long irqflags, const char *devname, void *dev_id)

irq:要申请的硬件中断号
handler:向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它
irqflags:中断处理的属性(中断触发方式)。
若设置了IRQF_DISABLED ,则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽。
若设置了IRQF_SHARED ,则表示多个设备共享中断。
若设置了IRQF_SAMPLE_RANDOM,表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)
devname:设置中断名称,通常是设备驱动程序的名称  在cat /proc/interrupts中可以看到此名称。
dev_id:在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。

第四步:映射需要配置到的寄存器,因为为了便于操作,我们可以将触摸屏驱动需要设置的所有寄存器都放在同一个结构体中,如下:
struct  s3c_ts_regs {
        unsigned long adccon;
        unsigned long adctsc;
        unsigned long adcdly;
        unsigned long adcdat0;
        unsigned long adcdat1;
        unsigned long adcupdn;
};

然后在入口函数中映射
s3c_ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_regs));
该结构体第一个寄存器地址为0x58000000

第五步:设置ADCDLY为最大值, 这使得电压稳定后再发出IRQ_TC中断
ADCDLY:ADC start delay register,也就是延迟ADC转换
s3c_ts_regs->adcdly = 0xffff;

第六步:使用定时器处理长按,滑动的情况
        init_timer(&ts_timer);
        ts_timer.function = s3c_ts_timer_function;
        add_timer(&ts_timer);

第七步:一切设置完毕后等待触摸屏被按下
enter_wait_pen_down_mode(void);


第二大部分:填充第一大部分s3c_ts_init()函数中用橙色字体的各类函数
1、enter_wait_pen_down_mode(void)(等待按下函数)
static void enter_wait_pen_down_mode(void)   
{
        s3c_ts_regs->adctsc = 0xd3;
}


查看我所使用的触摸屏手册可以得到下面内容

1504416774(1).png


 

可以看到当我们需要设置等待中断模式时只需要把adctsc寄存器设置为0xd3即可

 

2、s3c_ts_timer_function(定时器处理函数)
static void s3c_ts_timer_function(unsigned long data)
{
        IF (s3c_ts_regs->adcdat0 & (1<<15))     /*判断是否按下,adcdat0第十五位如果是1为松开,是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
        {
                /* 测量X/Y坐标 */
                enter_measure_xy_mode();
                start_adc();
        }
}

3、enter_measure_xy_mode()(坐标测量函数)
static void enter_measure_xy_mode(void)
{
        s3c_ts_regs->adctsc = (1<<3)|(1<<2);
}

4、start_adc()(开始adc转换)
static void start_adc(void)
{
        s3c_ts_regs->adccon |= (1<<0);
}

接下来是比较重头的两个中断处理函数了

 

5、pen_down_up_irq()(当触摸屏按下时的中断处理函数)
当我们按下触摸屏进入到我们的中断处理函数 pen_down_up_irq()中后我们还需要判断是否仍旧是按下状态
static irqreturn_t pen_down_up_irq(int irq,  void *dev_id)
{
        if(s3c_ts_regs->adcdat0 & (1<<15))       /*判断是否按下,adcdat0第十五位如果是1为松开,是0为按下*/ 
        {
        //        printk("pen up\n");
                enter_wait_pen_down_mode();      /*等待按下处理函数*/
        }

        else 
        {
                //printk("pen down\n");
                //enter_wait_pen_up_mode();
                enter_measure_xy_mode();         /*测量xy坐标函数*/
                start_adc();                                /*adc转换函数*/
        }
        return IRQ_HANDLED;        
}

6、adc_irq()(AD转换完成后产生的ADC中断)
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 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);
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个 SQL 语句,用于向借阅表插入数据。该表包含以下字段:借阅编号、读者编号、书籍编号、借阅日期、归还日期、借阅状态。每条数据表示一次借阅记录。其借阅编号、读者编号、书籍编号、借阅日期和借阅状态是必填项,归还日期为可选项,如果借阅状态为“已还”则必须填写归还日期。 具体插入的数据如下: - 借阅编号:100001,读者编号:123413,书籍编号:0001,借阅日期:2020-11-05,归还日期:NULL,借阅状态:借阅 - 借阅编号:100002,读者编号:223411,书籍编号:0002,借阅日期:2020-9-28,归还日期:2020-10-13,借阅状态:已还 - 借阅编号:100003,读者编号:321123,书籍编号:1001,借阅日期:2020-7-01,归还日期:NULL,借阅状态:过期 - 借阅编号:100004,读者编号:321124,书籍编号:2001,借阅日期:2020-10-09,归还日期:2020-10-14,借阅状态:已还 - 借阅编号:100005,读者编号:321124,书籍编号:0001,借阅日期:2020-10-15,归还日期:NULL,借阅状态:借阅 - 借阅编号:100006,读者编号:223411,书籍编号:2001,借阅日期:2020-10-16,归还日期:NULL,借阅状态:借阅 - 借阅编号:100007,读者编号:411111,书籍编号:1002,借阅日期:2020-9-01,归还日期:2020-9-24,借阅状态:已还 - 借阅编号:100008,读者编号:411111,书籍编号:0001,借阅日期:2020-9-25,归还日期:NULL,借阅状态:借阅 - 借阅编号:100009,读者编号:411111,书籍编号:1001,借阅日期:2020-10-08,归还日期:NULL,借阅状态:借阅

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值