NS2009是一款带I2C接口的4线电阻触摸屏控制电路包含12位分辨率A/D转换器。NS2009能通过执行两次A/D此外,还可以测量触摸屏上添加的压力。在2.7V在典型的工作状态下,功耗可小于0.75mW。可用于手机(手机等)、触摸屏显示器、个人数字助理(PDA)便携式设备,POS机器终端设备等。
开发平台:Linux操作系统
内核版本:3.10
摘要:本文基于ns2009驱动程序,主要内容是ns采样、滤波2009电阻触摸屏。
1.ns相关数据结构
struct ns2009_ts { spinlock_t irq_lock; struct i2c_client *client; struct input_dev *input_dev; wait_queue_head_t irq_wait; struct timer_list timer; int (*get_pen_status)(void); int irq_is_disable; bool stopped; unsigned int irq; };
该结构包含i2c子系统、输入子系统、定时器系统、定时器和中断,是实现按键坐标从采集到报告到应用层的重要组成部分。
struct ns2009_data { unsigned int x; unsigned int y; unsigned int z; };
输入子系统将结构保存的内容报告到应用层,以保存按键坐标和压力值。
为了保证接触的准确性,需要对收集到的坐标进行过滤。这里的过滤方案如下:一次接触取5个坐标,然后冒泡排序,过滤丢弃漂移较大的坐标,从头到尾寻求平均值。相关代码如下:
(1)冒泡排序算法
/*根据从小到大的冒泡排序*/ static inline void bubble_sort(int a[], int n) { int i = 0,j = 0; int temp = 0; for(i=0; i<n-1; i ) { int isSorted = 1; for(j = 0; j<n-1-i; j ) { if(a[j] > a[j 1]) { isSorted = 0; temp = a[j]; a[j] = a[j 1]; a[j 1]=temp; } } if(isSorted) break; } }
(2)滤波算法
static inline int tsc_filter(const u32 *x, const u32 *y, const int cread) { int tmp = 0; int new_value, i = 0; int value = 0; for(i= 1; i < cread; i ) { value = x[i-1]; new_value = x[i]; tmp = new_value - value; if (tmp > ERR_LIMIT)///直接丢弃漂移较大的点 return 0; } tmp = 0; value = 0; new_value = 0; for(i= 1; i < cread; i ) { value = y[i-1]; new_value = y[i]; tmp = new_value - value; if (tmp > ERR_LIMIT)///直接丢弃漂移较大的点 return 0; } return 1; }
(3)用滤波算法读取坐标函数
static int ns2009_read_coordinates(struct ns2009_ts *ts, struct ns2009_data *data, unsigned int *z1, unsigned int *z2) { int i = 0; unsigned int temp_x = 0, temp_y = 0; //unsigned int xsum = 0, ysum = 0; unsigned int yvals[READ_NUM], xvals[READ_NUM]; //unsigned int cread = READ_NUM; //u8 ADC_ON = PWRDOWN; u8 ADC_OFF = PWRUP; rtp_i2c_write_bytes(ts->client, &ADC_OFF, 1); //usleep_range(50, 80); for(i = 0; i < READ_NUM; i ) { xvals[i] = ns2009_xfer(ts, READ_X); yvals[i] = ns2009_xfer(ts, READ_Y); if (!is_valid_coor(xvals[i], yvals[i])) continue; } bubble_sort(xvals, READ_NUM); bubble_sort(yvals, READ_NUM); if(!tsc_filter(xvals, yvals, READ_NUM)) { ns2009_dbg("Coor quality too bad\n"); return -1; } if (READ_NUM <= 3) { for (i = 0; i < READ_NUM; i ) { temp_x = xvals[i]; temp_y = yvals[i]; } temp_x /= READ_NUM; temp_y /= READ_NUM; } else { for(i = 1; i < READ_NUM - 1; i ) { temp_x = xvals[i]; temp_y = yvals[i]; } temp_x /= READ_NUM - 2; temp_y /= READ_NUM - 2; } //data->x = XADC_MAX - temp_x; data->x = temp_x; data->y = temp_y; *z1 = ns2009_xfer(ts, READ_Z1); *z2 = ns2009_xfer(ts, READ_Z2); return 0; }
由于读取触摸屏坐标是一个复杂而耗时的过程,因此不能直接在硬中断中读取。在linux为了在中断执行时间尽可能短和中断处理之间找到平衡, Linux 将中断处理程序分为两个半部分:上半部分( top half)和底半部( bottom half)。在顶部完成尽可能少的紧急功能,它通常只是读取寄存器中的中断状态,并在注册中断工作后清除中断标志。底部几乎所有的事情都被中断了,可以被新的中断打断,这是底部和顶部之间最大的区别,因为顶部通常被设计成不可中断的。底部相对不是很紧急,相对耗时,不在硬件中断服务程序中执行。linux实现底半部的机制主要包括tasklet、工作队列和软中断。
因此,电阻触摸屏驱动读取触摸屏坐标的函数应放在软中断中。在本触摸屏驱动代码中,注册中断函数为:
int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)
参数说明
irq:中断号
handler:硬件中断处理函数
thread_fn:软件中断处理函数,thread_fn将在内核线程中执行
irqflag:触发标志中断
devname:设备名
dev_id:传参中断函数
可参考此驱动Linux内核ts2007.c写驱动程序,基本添加上述滤波和读取,然后改变注册中断的方式。