【驱动】第5课、TS(触摸屏)驱动之学习笔记

前言

  当课程过长时,把课程分为几小节学习,一小节一结束一练习,可减轻学习难度加快进程。最近总感觉困顿,是因为学而不思则罔,练习太少,知识反复练习犯错而咀嚼太少,知识掌握程度太低。本课TS加大练习量!



目录
壹、编程步骤
第一步:编写出可以打印“pen down” "pen up"状态的代码;
1.input_dev系统;
2.s3c_ts_init, s3c_ts_exit;
3.pen_down_up_irq, enter_wait_pen_down_mode, enter_wait_pen_up_mode;
第二步:加入INT_ADC_S中断处理和对触点的坐标读取;
1.start_adc, enter_measure_xy_mode, adc_irq,
第三步:加入触点的多次测量,求平均值/中间值;
第四步:加入触点的坐标x,y的过滤代码,过滤成功则求平均值/中间值;
第五步:加入对长按/滑动的检测,添加定时器功能;
第六步:加入上报功能;
贰、测试
第七步:测试



壹、编程步骤学习笔记
---------------------------------------------------------------------------------
第三步、内核崩溃
1、错误报告:
...
Kernel panic - not syncing: Fatal exception in interrupt:
2、缺陷源码:
static irqreturn_t SubInt_Adcs_irq(int irq, void *dev_id)
{
static int adc_cnt = 0; /* 触发ADC转换的次数计数 */
static int adc_ax[NUM];
static int adc_ay[NUM];
int i, j, tmp;

// x = s3c2440_adc_regs->adcdat0 & 0x3ff;
// y = s3c2440_adc_regs->adcdat1 & 0x3ff;
adc_ax[adc_cnt] = s3c2440_adc_regs->adcdat0 & 0x3ff;
adc_ay[adc_cnt] = s3c2440_adc_regs->adcdat1 & 0x3ff;

if(s3c2440_adc_regs->adcdat0 & (1<<15)) //此时触摸笔为抬起状态;
{
adc_cnt = 0;
enter_wait_pen_down_mode();
}
else if(!(s3c2440_adc_regs->adcdat0 & (1<<15))) //此时触摸笔为落下状态;
{
//adc_cnt++; //问题所在?造成空指针adc_ax/y[7]?修改:释放注释!答:是的!
if(adc_cnt == NUM)
{
/* 排序求中间值,并上报 */
for(i = 0; i < NUM - 1; i++)
for(j = i + 1; j < NUM - 1; j++) //问题所在?造成空指针?j < NUM - 1应改为:j < NUM
{
/* 从小到大排序 */
if(adc_ax[i] > adc_ay[j]) //问题所在?造成空指针?adc_ay[j]应改为adc_ax[j]
{
tmp = adc_ax[i];
adc_ax[i] = adc_ax[j];
adc_ax[j] = tmp;
}
if(adc_ay[i] > adc_ay[j])
{
tmp = adc_ay[i];
adc_ay[i] = adc_ay[j];
adc_ay[j] = tmp;
}
}
printk("x = %d, y = %d\n", adc_ax[3], adc_ay[3]);
adc_cnt = 0;
enter_wait_pen_up_mode();
}
else
{
/* 再次测量 */
//adc_cnt++; //问题所在?造成空指针?修改:若添加在此,上面if(adc_cnt == NUM)改为if(adc_cnt == NUM-1);
enter_auto_measure_mode();
start_adc();
}
}
return IRQ_HANDLED;
}
3、分析:Linux Kernel Panic报错解决思路
<1>有两种主要类型kernel panic:
I. hard panic(也就是Aieee信息输出)
II. soft panic (也就是Oops信息输出)
<2>常见Linux Kernel Panic报错内容:
Kernel panic-not syncing fatal exception in interrupt
kernel panic – not syncing: Attempted to kill the idle task!
kernel panic – not syncing: killing interrupt handler!
Kernel Panic – not syncing:Attempted to kill init !
<3>什么会导致Linux Kernel Panic? 原因?
答:对于hard panic而言,最大的可能性是驱动模块的中断处理(interrupt handler)导致的,一般是因为驱动模块在中断处理程序中访问一个空指
针(null pointre)。一旦发生这种情况,驱动模块就无法处理新的中断请求,最终导致系统崩溃。
4、问题发现及解决
答:查找到问题代码: //adc_cnt++; if(adc_cnt == NUM){...}else{//adc_cnt++; ...}
当语句adc_cnt++; 放在if()判断之前,没有问题,当语句adc_cnt++; 放在if()判断之后,将造成对空指针adc_ax/y[NUM]的访问和操作,随即造成内核崩溃!
修改:语句adc_cnt++; 放在if()判断之前。
编译及驱动模块加载运行结果:成功。
-------------
#define NUM 7
int a[NUM] = {1, 2, 3, 4, 5, 6, 7};
int main(void)
{
    printf("中位数: a[NUM/2] = %d\n", a[NUM/2]);
    return 0;
}
/*在VC++6.0中的运行结果:
中位数: a[NUM/2] = 4
Press any key to continue
*/

-----------------------------------------------------------------
第四步、加入触点的坐标x,y的过滤代码
Y轴最高点与最低点坐标差为:784。
Y轴最高点与最低点坐标差为:803。
字体一般为8x16,TD35屏尺寸:320*240。
因此,如果容错误差为一个字符的显示单元,则:X(误差)=26, Y(误差)=40。
-----------------------------------------------------
第五/六步:加入对长按/滑动的检测,添加定时器并上报
1、错误报告
问题1:(爆发式打印)
Pen Up!
Failed to filter ts contact coordinates!
Pen Up!
Failed to filter ts contact coordinates!
Pen Up!
Failed to filter ts contact coordinates!
Pen Up!
...
问题2:
源码为 s3c2440_adc_regs->adcdly = 0xffff; 时的打印:
Pen Down!
Failed to filter ts contact coordinates!
Pen Down!
Failed to filter ts contact coordinates!
Failed to filter ts contact coordinates!
Pen Down!
Pen Down!
Failed to filter ts contact coordinates!
Pen Up!
源码为 s3c2440_adc_regs->adcdly = 0x3ff; 时的打印:
Pen Up!
Pen Down!
Failed to filter ts contact coordinates!
Failed to filter ts contact coordinates!
Pen Up!
Pen Down!
Pen Up!
Pen Down!
Failed to filter ts contact coordinates!

2、缺陷源码:
问题1:
static irqreturn_t SubInt_Adcs_irq(int irq, void *dev_id)
{
#define LEN 7 /* 触点坐标数组长度 */
static int adc_cnt = 0; /* 已经A/D转换的次数计数值 */
static int adc_ax[LEN];
static int adc_ay[LEN];
adc_ax[adc_cnt] = s3c2440_adc_regs->adcdat0 & 0x3ff;
adc_ay[adc_cnt] = s3c2440_adc_regs->adcdat1 & 0x3ff;

if(s3c2440_adc_regs->adcdat0 & (1<<15)) //此时触摸笔为抬起状态;
{
adc_cnt = 0;
input_report_abs(s3c2440_ts_dev, ABS_PRESSURE, 0);
input_report_key(s3c2440_ts_dev, BTN_TOUCH, 0);
input_sync(s3c2440_ts_dev);

enter_wait_pen_down_mode();
}
else if(!(s3c2440_adc_regs->adcdat0 & (1<<15))) //此时触摸笔为落下状态;
{
adc_cnt++;
if(adc_cnt == LEN){
if(filter_ts(adc_ax, adc_ay, LEN)) /* 过滤A/D转换之后的坐标数据 */
{
/* 取中间值,上报或打印 */
//printk("x = %d, y = %d\n", adc_ax[LEN/2], adc_ay[LEN/2]);
input_report_abs(s3c2440_ts_dev, ABS_X, adc_ax[LEN/2]);
input_report_abs(s3c2440_ts_dev, ABS_Y, adc_ay[LEN/2]);
input_report_abs(s3c2440_ts_dev, ABS_PRESSURE, 1);

input_report_key(s3c2440_ts_dev, BTN_TOUCH, 1);
input_sync(s3c2440_ts_dev);
}
adc_cnt = 0; /* 计数清零 */
enter_wait_pen_up_mode();
mod_timer(&ts_timer, jiffies + HZ/100);
}
else{
enter_auto_measure_mode();
start_adc();
}
}
return IRQ_HANDLED;
}
static void ts_timer_function(unsigned long data){
enter_auto_measure_mode();
start_adc();
}
3、分析
问题1:如上,即使点击一次触摸屏后不再点击触摸屏,也会继续打印。显然,SubInt_Adcs_irq()中触笔状态位的反复判断出现了问题:
if(s3c2440_adc_regs->adcdat0 & (1<<15)) //此时触摸笔为抬起状态;
{...}
else if(!(s3c2440_adc_regs->adcdat0 & (1<<15))) //此时触摸笔为落下状态;
{...}

原因:对各寄存器可读判断标志位,如ADCDAT0'[15]等待中断模式中笔尖的起落状态: 0 = 笔尖落下态 1 = 笔尖抬起态
的内部生成机制不了解,例如理解方式有:
<1>绝对的物理触击屏幕然后置位ADCDAT0'[15]=0或1,且CPU始终动态采集数据;
<2>A/D转换的结果也可影响寄存器ADCDAT0'[15]=0或1置位;
<3>
<4>

问题2:A/D启动或初始化延时寄存器ADCDLY的值,过大时会造成CPU有时捕捉不到触笔状态的变化!

4、问题查找及解决:
问题1:s_timer_function(){...}内也进行触笔状态判断;
问题2:寄存器 s3c2440_adc_regs->adcdly = 0x3ff; 这样配置!



贰、测试
-----------------------------------------------------------------------------------------
第七步、测试:
1. $ ./autogen.sh 错误:
解决:根据tslib编译使用方法,解压tslib-1.4.tar.gz前先进行操作:
sudo apt-get install autoconf
sudo apt-get install automake
sudo apt-get install libtool

2.执行ts_calibrate产生错误Calibration failed
解决:最终还是驱动的问题,更换 s3c2440_ts10.ko 为 s3c2440_ts9.ko,则可实现屏幕校准操作!
s3c2440_ts10.ko的问题是源码s3c2440_ts10.c中:
input_report_abs(ts.dev, ABS_X, ts.xp);
input_report_abs(ts.dev, ABS_Y, ts.yp);
写成了:
input_report_abs(ts.dev, ABS_X, 1);
input_report_abs(ts.dev, ABS_Y, 1);

3.说明:

当根据tslib编译使用方法.TXT的步骤安装过一次,当重启开发板进行第二次触摸屏测试时,虽然不再需要安装新文件,但是仍需要先进行【2.先配置环境变量】,然后才能使用【3.再测试】测试ts驱动模块。

【2.先配置环境变量】
export TSLIB_TSDEVICE=/dev/event0
export TSLIB_CALIBFILE=/etc/pointercal
export TSLIB_CONFFILE=/etc/ts.conf
export TSLIB_PLUGINDIR=/lib/ts
export TSLIB_CONSOLEDEVICE=none
export TSLIB_FBDEVICE=/dev/fb0
【3. 再测试】
ts_calibrate    // "五点校准"法,校准屏幕;
ts_test       // 测试:画图 或 命令屏幕光标跟随触笔走;
ts_print      // 打印触点坐标(LCD屏幕坐标);
ts_print_raw    // 打印触点原始坐标(A/D转换后的电压数据);

猜你喜欢

转载自www.cnblogs.com/xiaohujian/p/10204350.html
今日推荐