触摸屏驱动编写 您所在的位置:网站首页 触摸屏编写 触摸屏驱动编写

触摸屏驱动编写

2023-10-08 16:21| 来源: 网络整理| 查看: 265

触摸屏要使用到输入子系统

分析之前的输入子系统 输入子系统详解 用linux里面的 input.c 用两边的probe 匹配系统里的软件控制和驱动 写出框架 我们要写的就是 input_dev 在这里插入图片描述

开始代码编写和分析

分配一个input_dev结构体 设置 注册 硬件相关的操作

由于触发按键 中断时候 要用到 input_event 来上报事件

在这里插入图片描述

1分配一个input_dev结构体

在这里插入图片描述

2设置 2.1 能产生哪类事件

产生按键类事件 产生触摸屏绝对位移事件 在这里插入图片描述

能产生这类事件里面的 什么事件

在这里插入图片描述 在这里插入图片描述 ** 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);** 里面的参数 0x3FF 从cup芯片手册上面找到 在这里插入图片描述 在这里插入图片描述 产生的大小是10位 也就是最大 0x3FF input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0); 压力值设置 该触摸屏 不是放下就是松开 所以只有0 和1

3注册

在这里插入图片描述

4硬件相关的操作

先来看看触摸屏使用的过程 在这里插入图片描述

打开cpu的原理图 在这里插入图片描述 在这里插入图片描述 看了看 芯片手册 在这里插入图片描述 这几个引脚 就是用来做ADC的 不用专门的设置

这个ADC转换是 8:1 转换 左边八个物理信号选择一个转换为数字信号 在这里插入图片描述

4.1 设置soc的时钟(CLKCON)

在 SOC 里面 有各种各样的模块 为了省电 一般是关的 这时候 要爆我们的ADC打开 在这里插入图片描述 在这里插入图片描述

4.2初始化ADC/TS寄存器

在芯片手册里面找到寄存器 在这里插入图片描述 给这些寄存器定义一个结构体 在这里插入图片描述 设置指针指向这个结构体 在这里插入图片描述 ioremap 这些地址 在这里插入图片描述 进入内核 看工作时钟的频率 FCLK : cpu工作时钟 HCLK : 内存工作时钟 PCKL : 外设工作时钟 在这里插入图片描述

a.设置ADCCON寄存器

A/D 转换器预分频器使能设置为1 在这里插入图片描述 A/D 转换器预分频值设置 芯片手册里面有个例子 之前找到了 PCLK是50MHz 所以 prescaler value =49 在这里插入图片描述 A/D conversion starts by enable. 先设为0 用到时候再打开

b注册中断函数

当按下 和松开时 会产生中断 注册中断函数 pen_down_up_irq 在这里插入图片描述 这时候 写出这个中断函数 由于会有 按下和松开 所以要分开判断 在芯片手册中 读取ADCDATA0 的15位 进行判断 在这里插入图片描述 当松开的时候 进入等待按下模式 等待下一次被按下 在这里插入图片描述 在这里插入图片描述 当按下的时候 进入测量 xy 坐标模式

在这里插入图片描述 这时候 写出测量xy的函数 芯片手册中有一个自带的x,y 坐标测量模式 在这里插入图片描述 根据提示设置enter_measure_xy_mode()函数

在这里插入图片描述 在这里插入图片描述 于此同时当被按下的时候根据原理图 上拉电阻都是断开的 在这里插入图片描述 在这里插入图片描述 写出函数 在这里插入图片描述

进入这个模式之后 ,使用ADC转换,启动adc

在这里插入图片描述ADC不会瞬间完成,当ADC好了之后,会产生一个ADC中断,注册一个ADC中断函数 在这里插入图片描述 同时写出这个ADC中断函数 ,函数里面可以找到,当前的XY的电压值 x的电压值 在ADCDATA0里面 在这里插入图片描述 y的电压值 在ADCDATA1里面 在这里插入图片描述 先在中断函数里面 打印出ADC的电压值 在这里插入图片描述

在这里插入图片描述 到这里就能显示数值了 但是不够准确 , 不能检测到滑动,现在开始优化措施

优化措施 让数值精确,等电压稳定才会继续

用延时值,ADCDLY,当稳定的时候才发出中断 在这里插入图片描述

丢弃没有检测好的数值,多次测量取值

ADC启动需要一定的时间,有时候触摸已经松开,才完成启动,就把这个测量失误的值丢弃,并且多次测量 建立一个数组,x,y都保存4次,再取平均值 没到4次的话再次启动ADC测量 在这里插入图片描述 在这里插入图片描述

设置软件过滤

当四个值中差距太大了,出现了测量错误,应该丢弃 在这里插入图片描述

启动定时器,记录滑动

创建一个定时器,设置定时器功能,增加这个定时器 在这里插入图片描述 写下定时器的功能 在这里插入图片描述 在测值时候加入定时器 在这里插入图片描述

完整代码

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 #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 /* 测量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 //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 // 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); } 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


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有