触摸屏滤波的一点心得
引子
最近在编写Nios II的触摸屏驱动,TFT的驱动器为ILI9325,触摸AD为ADS9325。无论是轮询的方式抑或中断的方式,都会出现令人讨厌的散点。经过在SOPC技术联盟群的讨论,达克斯特兄给我一点启示,让我成功消除了散点。
第1种尝试 中位值平均滤波法
首先移植的是liujun6037的代码,他的代码思路为:对X、Y的坐标连续采样十次;不足十次则认为数据无效,不做任何操作;然后对十次数据进行排序;最后取中间三次的数据进行平均,得到最终的X、Y坐标。不同的是,我把冒泡排序换成我常用的选择排序,其实还是O(n^2)。其效果如图1所示。可以清楚地看到,本次尝试很失败,有很多莫名其妙的散点。
代码1 第一种尝试
void ads_GetXY(void)
{
u8 cnt=0;
u8 i, j, k, min;
u16 temp;
u16 tempXY[2][9], XY[2];
do
{
if(ads_ReadXY())
{
tempXY[0][cnt] = X;
tempXY[1][cnt] = Y;
cnt++;
}
}while(cnt<9);
if(cnt==9)
{
for(k=0; k<2; k++)
{ // 降序排列
for(i=0; i<cnt-1; i++)
{
min=i;
for (j=i+1; j<cnt; j++)
{
if (tempXY[k][min] > tempXY[k][j]) min=j;
}
temp = tempXY[k][i];
tempXY[k][i] = tempXY[k][min];
tempXY[k][min] = temp;
}
// 求中间值的均值
XY[k] = (tempXY[k][3]+tempXY[k][4]+tempXY[k][5]+tempXY[k][6]) / 4;
}
}
// 矫正坐标
X = ((XY[0]-350)/11);
Y = ((XY[1]-400)/14);
}
图1 第一种尝试
第2种尝试 差值平均滤波法
由于第一种尝试比较失败,我就在网上搜到了参考2的算法。尝试了一下,效果极差,线条极其发散,图片就不贴了。代码贴到这里作为反面例程。
代码2 第二种尝试
void ads_GetXY(void)
{
u8 cnt=0;
u8 i, j, k, min;
u16 temp;
u16 tempXY[2][9], avgXY[2][3], XY[2];
s16 diffXY[2][3];
do
{
if(ads_ReadXY())
{
tempXY[0][cnt] = X;
tempXY[1][cnt] = Y;
cnt++;
}
}while(cnt<9);
if(cnt==9)
{
for(k=0; k<2; k++)
{ // 取平均值
avgXY[k][0] = (tempXY[k][0]+tempXY[k][1]+tempXY[k][2])/3;
avgXY[k][1] = (tempXY[k][3]+tempXY[k][4]+tempXY[k][5])/3;
avgXY[k][2] = (tempXY[k][6]+tempXY[k][7]+tempXY[k][8])/3;
// 取差值
diffXY[k][0] = avgXY[k][0] - avgXY[k][1];
diffXY[k][1] = avgXY[k][1] - avgXY[k][2];
diffXY[k][2] = avgXY[k][2] - avgXY[k][0];
// 取差值的绝对值
diffXY[k][0] = (diffXY[k][0] > 0) ? diffXY[k][0] : -diffXY[k][0];
diffXY[k][1] = (diffXY[k][1] > 0) ? diffXY[k][1] : -diffXY[k][1];
diffXY[k][2] = (diffXY[k][2] > 0) ? diffXY[k][2] : -diffXY[k][2];
// 取最小的数得平均值
if(diffXY[k][0] < diffXY[k][1])
{
if(diffXY[k][2] < diffXY[k][1])
XY[k] = (avgXY[k][0]+avgXY[k][2])>>1;
else
XY[k] = (avgXY[k][0]+avgXY[k][1])>>1;
}
else if(diffXY[k][2] < diffXY[k][1])
XY[k] = (avgXY[k][0]+avgXY[k][2])>>1;
else
XY[k] = (avgXY[k][1]+avgXY[k][2])>>1;
}
}
// 矫正坐标
X = ((XY[0]-350)/11);
Y = ((XY[1]-400)/14);
}
第3种尝试 中位值平均加阈值滤波法
既然第一种尝试的线条已经比较收敛,那么散点是怎么出来的呢?经过达克斯特兄的一点指导和我的多次实验,终于干掉了这个头疼的散点。原来虽然使用中位值平均滤波法可以稳定获取符合触摸屏范围的数据,但是却无法滤除跳变的散点。对于跳变的散点必须通过加阈值才能消除。
下面贴出我的代码。代码思路:采样符合触摸屏范围的数据若干次,将其排序,取中间两位的差值;若差值大于阈值,则丢弃。因为数据已经排序,因此差值肯定是正值或零值,即无需申明为有符号数。同时由于阈值判断的加入,我们可以将数据的采样次数适当调整,此处仅为4次,所得效果已经非常令人满意。需要注意的是采样数据不宜过多,否则连续的线会变成离散的点。
代码3 第3种尝试
#define SAMP_CNT 4
#define SAMP_CNT_DIV2 2
u8 ads_GetXY(void)
{
u8 i, j, k, min;
u16 temp;
u16 tempXY[2][SAMP_CNT], XY[2];
// 采样
for(i=0; i<SAMP_CNT; i++)
{
if(ads_ReadXY())
{
tempXY[0][i] = X;
tempXY[1][i] = Y;
}
}
// 滤波
for(k=0; k<2; k++)
{ // 降序排列
for(i=0; i<SAMP_CNT-1; i++)
{
min=i;
for (j=i+1; j<SAMP_CNT; j++)
{
if (tempXY[k][min] > tempXY[k][j]) min=j;
}
temp = tempXY[k][i];
tempXY[k][i] = tempXY[k][min];
tempXY[k][min] = temp;
}
// 设定阈值
if((tempXY[k][SAMP_CNT_DIV2]-tempXY[k][SAMP_CNT_DIV2-1]) > 5)
return 0;
// 求中间值的均值
XY[k] = (tempXY[k][SAMP_CNT_DIV2]+tempXY[k][SAMP_CNT_DIV2-1]) / 2;
}
// 矫正坐标
X = ((XY[0]-350)/11);
Y = ((XY[1]-400)/14);
return 1;
}
图片3 第三种尝试
参考
1. liujun6037.2.4寸TFTLCD触摸屏测试通过!(ADS7846/7843)
2. 鹰之翔.毕业设计第六天(触摸屏在S3C2410上的软件滤波 )