2440 5.0BSP触摸屏驱动学习(作者:wogoyixikexie@gliet)
//-----------------------------------------------------------------------------------------------------------
// 日期:2009年3月14日11:49:48
// 作者:wogoyixikexie@gliet
// 版权:桂林电子科技大学一系科协wogoyixikexie@gliet
// 最后修改:2009年3月17日10:20:39
//-----------------------------------------------------------------------------------------------------------
以前曾经看过一下子触摸屏驱动,但是那时候火候不到,现在老大叫我看,并且写一篇博客,现在我就边看边写吧。
曾经在nasiry看到touch.dll是这样生成的。
有意思的是tchmdd.lib则比较特殊,在OAK的lib文件夹中居然找不到这个文件,而在项目文件的OAK目录下该文件确是存在的,也就是说该文件在CESYSGEN的过程中产生。根据该链接库提供的接口来看,可以猜测该lib应当由另外几个零散的lib组成,这些链接库的source文件夹在OAK\COMMON\Drivers\Touch目录下。后来在PUBLIC\COMMON\CESYSGEN下的makefile文件内找到如下内容
tchmdd::
@set TARGETTYPE=LIBRARY
@set TARGETNAME=$@
@set RELEASETYPE=OAK
@set TARGETLIBS=
!IFDEF SYSGEN_TRANSCRIBER
@set SOURCELIBS=$(SG_INPUT_LIB)\tchmain.lib $(SG_INPUT_LIB)\tch_trns.lib
echo touch includes transcriber hooks
!ELSE
@set SOURCELIBS=$(SG_INPUT_LIB)\tchmain.lib $(SG_INPUT_LIB)\tchbasic.lib
echo touch is minimal
!ENDIF
确定了Tchmdd.lib的内容就是tchmain.lib+ tch_trns.lib或tchbasic.lib.因为tchmdd需要通过专门的Nmake指令完成生成的动作,所以在Touch驱动目录下都会有一个bat文件用于产生tchmdd.lib文件用于后面的链接库。
之所以需要将tchmdd.lib设定为这种方式生成,而不直接产生可以猜测为不同的编译条件下DDI的接口不同(见第一部分),所以通过这里来选择不同的DDI接口实现。
这样一来Touch驱动就分成两种不同的类型了:
1. 不使用扩展DDI接口的Touch驱动
2. 在TRANSCRIBER编译条件下产生的带扩展的Touch驱动。
哈哈,现在看看我的2440 touch到底用了什么代码,首先找到TOUCH.def文件,内容如下:
LIBRARY TOUCH
EXPORTS
STDAPI(TouchPanelGetDeviceCaps, 8);
STDAPI(TouchPanelEnable,4);
STDAPI(TouchPanelDisable,0);
STDAPI(TouchPanelSetMode, 8);
STDAPI(TouchPanelReadCalibrationPoint, 8);
STDAPI(TouchPanelReadCalibrationAbort, 0);
STDAPI(TouchPanelSetCalibration, 20);
STDAPI(TouchPanelCalibrateAPoint, 16);
STDAPI(TouchPanelPowerHandler, 4);
; @CESYSGEN IF WCESHELLFE_MODULES_MSTRANSCRIBER || SHELLW_MODULES_TRANSCRIBER
TouchReset
TouchRegisterWindow
TouchUnregisterWindow
TouchSetValue
TouchGetValue
TouchCreateEvent
TouchGetFocusWnd
TouchGetLastTouchFocusWnd
TouchGetQueuePtr
; @CESYSGEN ENDIF
很明显,这个DEF文件也受控制了,现在使用dumbin看看touch.dll到底导出了什么函数就可以知道用了什么库了。
由此可以看出,触摸屏驱动使用了非扩展方案。现在开始分析代码。
MDD层:
1.TouchPanelEnable
该函数首先调用TouchPanelpAttach函数,(在TouchPanelpAttach里面又调用和坐标校正相关的TouchPanelSetCalibration函数);DdsiTouchPanelEnable(在PDD中实现)以及初始化触摸屏驱动的按下弹起、滑动两个线程。
2.TouchPanelDisable
该函数调用DdsiTouchPanelDisable(PDD实现——释放触摸屏驱动占用的队内存以及禁止pen up/down中断)以及SetEvent一些事件。
3.TouchPanelGetDeviceCaps
该函数为DDI接口函数。用于查询触摸屏设备支持的具体功能。
调用到的DDSI函数为
DdsiTouchPanelGetDeviceCaps
该函数动作为
(1). 通过DDSI函数查询相应的信息
(2). 当查询屏幕坐标信息时保存屏幕信息,供后面程序映射屏幕坐标
4.TouchPanelSetMode
该函数根据switch语句设置一些优先级采样相关的东西
5.TouchPanelReadCalibrationPoint
该函数主要是读入Common.reg的注册表,但是我对这个注册表的作用还不是很明白。——了解的告知小弟一声。
[HKEY_LOCAL_MACHINE\SYSTEM\GWE]
"ActivityEvent"="PowerManager/ActivityTimer/UserActivity"
dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"System\\GWE", 0, 0, &hk);
dwStatus = RegQueryValueEx(hk, L"ActivityEvent", NULL, &dwType, (LPBYTE) szEventPath, &dwSize);
hevActivity = OpenEvent(EVENT_ALL_ACCESS, FALSE, szEventPath);——不理解的是这里了。
6.TouchPanelReadCalibrationAbort
该函数在效验取消的时候被调用。仅仅设置状态位和事件后返回。
7. TouchPanelSetCalibration
该函数是校准相关的东西,我的数学太烂了,看了代码不知道怎么回事。现在暂时放弃,以后有空再研究了。
8.TouchPanelCalibrateAPoint
该函数用于校正坐标,使用的公式貌似是最小二乘法,数学忘记了,真是后悔以前没有学好。就贴个代码吧。
最后一个MDD函数 TouchPanelPowerHandler
该函数调用PDD的DdsiTouchPanelPowerHandler函数实现功能。
PDD层:
一、TSP_VirtualFree(VOID); TSP_VirtualAlloc(VOID);
上面两个函数分别是释放和获得wince系统的堆内存
二、TSP_SampleStart
采样开始,利用PWM定时器3
三、TSP_SampleStop
停止采样,关闭定时器
四、TSP_PowerOn
该函数初始化AD、定时器,以及消除INT_TC Touch screen interrupt屏蔽
五、TSP_PowerOff
屏蔽INT_TC Touch screen interrupt
六、TSP_CalibrationPointGet
该函数是用来计算五个点的校正坐标的,我们在校正笔针的时候会调用到这个函数
七、TSP_TransXY
该函数为作比较转换函数,被DdsiTouchPanelGetPoint函数调用。对于里面的写法我不懂,TSP_MINX,TSP_MINy的值到底是根据什么来取的?我记得触摸屏驱动和LCD大小是无关的,以前我修改屏幕不同的LCD驱动的时候并没有修改触摸屏就可以用了,这个值到底是怎么搞的呢?也没有相关资料说明真是难以理解的。
八、TSP_GetXY
该函数是读入的X、Y坐标AD值,这个值是连续采样四次(如果要精准,估计加大采样次数即可);被DdsiTouchPanelGetPoint调用
九、DdsiTouchPanelGetDeviceCaps
该函数是一些触摸屏参数设置,采样率等;还有就是点号,校正点的坐标计算等。
十、DdsiTouchPanelSetMode
参数设置
十一、DdsiTouchPanelEnable
该函数用来申请触摸屏用到相关两个中断,不过它的一些写法让人有点费解Irq[0]=-1;Irq[1]=OAL_INTR_FORCE_STATIC;Irq[2]=IRQ_ADC;不知道它为何要这样写。
十二、DdsiTouchPanelDisable
该函数释放对内存等资源。
十三、DdsiTouchPanelAttach;DdsiTouchPanelDetach
两函数没有任何动作。
十四、DdsiTouchPanelPowerHandler
根绝条件是执行TSP_PowerOff、TSP_PowerOn
十五、DdsiTouchPanelGetPoint
两个中断的线程函数,被TouchPanelpISR函数调用——TouchPanelpISR是在TouchPanelEnable函数创建的线程
补充:
(1)触摸屏中断申请
我感觉触摸屏的中断有点奇怪,首次看到这样的写法。(触摸屏的两个中断是绑定同一个线程的,是个经典例子)
// Obtain sysintr values from the OAL for the touch and touch changed interrupts.
//
RETAILMSG(0, (TEXT("enable touch sysintr.\r\n")));
Irq[0]=-1;Irq[1]=OAL_INTR_FORCE_STATIC;Irq[2]=IRQ_ADC;
if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(Irq), &gIntrTouch, sizeof(UINT32), NULL))
{
RETAILMSG(1, (TEXT("ERROR: Failed to request the touch sysintr.\r\n")));
gIntrTouch = SYSINTR_UNDEFINED;
return(FALSE);
}
Irq[0]=-1;Irq[1]=OAL_INTR_FORCE_STATIC;Irq[2]=IRQ_TIMER3;
if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(Irq), &gIntrTouchChanged, sizeof(UINT32), NULL))
{
RETAILMSG(1, (TEXT("ERROR: Failed to request the touch changed sysintr.\r\n")));
gIntrTouchChanged = SYSINTR_UNDEFINED;
return(FALSE);
}
KernelIoControl的第二个参数是指向一个buf的指针,IRQ数组是如何得到处理的呢?在OALIntrRequestSysIntr函数中有
if (
g_oalIrq2SysIntr[irq] == SYSINTR_UNDEFINED ||
(flags & OAL_INTR_FORCE_STATIC) != 0
) {
g_oalIrq2SysIntr[irq] = sysIntr;
}
以前看OALIntrRequestSysIntr都只是看函数名称,没有看它带的参数,现在要好好看看
UINT32 OALIntrRequestSysIntr(UINT32 count, const UINT32 *pIrqs, UINT32 flags)
count *pIrqs flags都有它的作用。
后来在CSDN请教LinHanLao前辈,得到了满意的回复:
aIrqs[0] = -1;
// Using -1 indicates we are not using the legacy calling convention.
关于OAL_INTR_FORCE_STATIC的详细解释在PB帮助文档有说明。
(2)触摸屏中断线程
触摸屏涉及两个中断,一个是按下,弹起笔针,另外一个是笔针滑动中断;他们分别是由INT_TC和Timer3实现的。非常有趣的是这两个中断绑定同一个线程(在CSDN有人问过,现在终于亲自看到了(*^__^*) 嘻嘻……),任何一个中断产生都能启动线程。
弹起、按下笔针中断很容易理解,但是这个滑动却是比较牛,在产生按下动作之后,是通过定时器采样,然后计算亮点的差值来做的,不过我对笔针的框选动作始终不理解,还有触摸屏驱动是如何被应用程序调用的呢?我们如何自己写校验算法呢?毕竟他不是流驱动。以后到CSDN发帖求助吧,这篇博客完之前贴一下它的中断的关键代码。
/* :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: */
/* :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: */
PUBLIC VOID
DdsiTouchPanelGetPoint(TOUCH_PANEL_SAMPLE_FLAGS * pTipStateFlags,
INT * pUncalX,
INT * pUncalY)
{
static INT x, y;
//INT_TC Touch screen interrupt (pen up/down) INT_ADC
if (v_pINTregs->SUBSRCPND & (1<<IRQ_SUB_TC)) /* SYSINTR_TOUCH Interrupt Case*/
{
*pTipStateFlags = TouchSampleValidFlag;
if ( (v_pADCregs->ADCDAT0 & (1 << 15)) |
(v_pADCregs->ADCDAT1 & (1 << 15)) )
{
bTSP_DownFlag = FALSE;
DEBUGMSG(ZONE_TIPSTATE, (TEXT("up\r\n")));
v_pADCregs->ADCTSC &= 0xff;
*pUncalX = x;
*pUncalY = y;
TSP_SampleStop();
}
else
{
bTSP_DownFlag = TRUE;
if (!TSP_GetXY(&x, &y))
*pTipStateFlags = TouchSampleIgnore;
TSP_TransXY(&x, &y);
*pUncalX = x;
*pUncalY = y;
*pTipStateFlags |= TouchSampleDownFlag;
//DEBUGMSG(ZONE_TIPSTATE, (TEXT("down %x %x\r\n"), x, y));
TSP_SampleStart();
}
v_pINTregs->SUBSRCPND = (1<<IRQ_SUB_TC);
v_pINTregs->INTSUBMSK &= ~(1<<IRQ_SUB_TC);
InterruptDone(gIntrTouch);
}
else /* SYSINTR_TOUCH_CHANGED Interrupt Case */
{
// TSP_SampleStart();
if (bTSP_DownFlag)
{
INT tx, ty;
INT dx, dy;
if (!TSP_GetXY(&tx, &ty))
*pTipStateFlags = TouchSampleIgnore;
else
{
TSP_TransXY(&tx, &ty);
// insert by mostek@dstcorp.com
#define X_ERRV 0x3bf
#define Y_ERRV 0x4ff
if ((tx == X_ERRV) && (ty == Y_ERRV))//差值界定
{
tx = x;
ty = y;
}
// ========求两点间的差值=========== mostek
dx = (tx > x) ? (tx - x) : (x - tx);
dy = (ty > y) ? (ty - y) : (y - ty);
if (dx > TSP_CHANGE || dy > TSP_CHANGE)
{
*pUncalX = x = tx;
*pUncalY = y = ty;
//DEBUGMSG(ZONE_TIPSTATE, (TEXT("down-c-v %x %x\r\n"), x, y));
*pTipStateFlags = TouchSampleValidFlag | TouchSampleDownFlag;
}
else
{
*pUncalX = x;
*pUncalY = y;
DEBUGMSG(ZONE_TIPSTATE, (TEXT("down-c %x %x\r\n"), x, y));
*pTipStateFlags = TouchSampleIgnore;
}
}
}
else
{
*pTipStateFlags = TouchSampleIgnore;
TSP_SampleStop();
}
InterruptDone(gIntrTouchChanged);
}
}
相关文章参考:http://topic.csdn.net/u/20081215/16/3ae65482-c8ea-4e09-a725-89a9255eb60f.html
http://blog.csdn.net/gooogleman/archive/2009/03/14/3990238.aspx
/*++
Routine Description:
Convert uncalibrated points to calibrated points.
Autodoc Information:
@doc INTERNAL DRIVERS MDD TOUCH_DRIVER
@func VOID | TouchPanelCalibrateAPoint |
Convert uncalibrated points to calibrated points.
@comm
The transform coefficients are already in vCalcParam.<nl>
@devnote
Note that there is a *4 hiding in the calculations of X and
Y. This is a means of providing sub-pixel accuracy to GWE,
which will theoretically help improve accuracy in inking. It
would be nice if this multiplier were defined in a .h file
by gwe, but it isn't. So make very sure that this value
agrees with the divisor used by gwe in touch.cpp.
--*/
void
TouchPanelCalibrateAPoint(
INT32 UncalX, //@PARM The uncalibrated X coordinate
INT32 UncalY, //@PARM The uncalibrated Y coordinate
INT32 *pCalX, //@PARM The calibrated X coordinate
INT32 *pCalY //@PARM The calibrated Y coordinate
)
{
INT32 x, y;
if ( !v_Calibrated ){
*pCalX = UncalX;
*pCalY = UncalY;
return;
}
//
// Note the *4 in the expression below. This is a kludge
// perpetrated on behalf of gwe. It provides a form of
// sub-pixel accuracy desirable for inking
//
x = (v_CalcParam.a1 * UncalX + v_CalcParam.b1 * UncalY +
v_CalcParam.c1) * 4 / v_CalcParam.delta;
y = (v_CalcParam.a2 * UncalX + v_CalcParam.b2 * UncalY +
v_CalcParam.c2) * 4 / v_CalcParam.delta;
if ( x < 0 ){
x = 0;
}
if ( y < 0 ){
y = 0;
}
*pCalX = x;
*pCalY = y;
}