NUC970平台触摸屏驱动移植

  1. 原理概述ios

    1.首先要区分lcd和触摸屏,lcd是一个屏幕,触摸屏是贴在lcd上的两层膜。框架

    2.四线电阻触摸屏的原理:触摸屏就是上下两层膜,好比上层表明x轴(XM:负端,XP:正端),下层表明y轴(YM:负端,YP:正端)。当读取x轴坐标时,XP接3.3v,XM接地,从YM读取按下点的电压值做为模拟输入信号,再通过AD转换后就获得了x轴坐标。同理,当读取y轴坐标时,YP接3.3v,YM接地,从XM读取按下点的电压值做为模拟输入,再通过AD转换后就获得了y轴坐标。函数

    触摸屏驱动程序采用中断方式对触摸笔的按下状态进行检测,若是检测到触摸笔按下将产生中断并触发一个事件通知一个工做线程开始采集数据。测试

    ADC触摸屏驱动的工做流程如图 21所示。ui

    图 21 ADC触摸屏工做流程线程

     

  2. 技术实现事件

  3. ADC触摸屏驱动框架分析工作流

    ADC触摸屏驱动目录如图 31所示。it

    图 31 ADC触摸屏驱动所在目录io

    bspnuc970/driver_module/touch_drv/src/touch_drv.c文件主要提供SylixOS的ADC触摸屏驱动。主要关注函数如程序清单 31所示。

    程序清单 31

    INT  __tsDrv (VOID) 
    INT  __tsDevCreate (PCHAR         pcName) 
    static void __tsThread (TS_DEV   *ptsDev) 
    static irqreturn_t __tsIsr (void  *arg)
  4. 框架的流程图

    整个框架的流程如图 33所示。

    图 33 ADC触摸屏驱动框架

  5. 代码实现

  6. BSP中驱动配置

    根据NUC970相关芯片手册,配置寄存器地址并定义touch screen设备结构。如程序清单 32所示。

    程序清单 32

    /*********************************************************************************************************
      touch screen 设备结构
    *********************************************************************************************************/
    typedef struct {
        LW_DEV_HDR                 TS_devhdr;                               /*  设备头                      */
        touchscreen_event_notify   TS_tData;                                /*  采集到的数据                */
        BOOL                       TS_bIsReadRel;                           /*  是否读取的 release 操做     */
        LW_HANDLE                  TS_hThread;                              /*  扫描线程                    */
        LW_SEL_WAKEUPLIST          TS_selwulList;                           /*  select() 等待链             */
        LW_SPINLOCK_DEFINE        (TS_slLock);                              /*  自旋锁                      */
    } TS_DEV;
  7. __tsDrv

    该函数注册一系列触摸屏设备驱动程序,包括:

    1)    驱动程序中的创建函数__tsOpen;

    2)    驱动程序中的关闭函数__tsClose;

    3)    驱动程序中的读函数__tsRead;

    具体实现如程序清单 33所示。

    程序清单 33

    INT  __tsDrv (VOID)
    {
    ……
    
        /*
         *  安装触摸屏驱动,LW_NULL为待实现功能
         */
        touch_dev_num = iosDrvInstall(__tsOpen,
                                  LW_NULL,
                                  __tsOpen,
                                  __tsClose,
                                  __tsRead,
                                  LW_NULL,
                                  LW_NULL);
    ……
    }
  8. __tsDevCreate

    该函数建立一个触摸屏设备,并将__tsDrv中注册的驱动程序与设备名绑定。建立成功后对该设备的open,close,read操做将调用__tsDrv中注册的驱动程序__tsOpen,__tsClose,__tsRead。具体实现如程序清单 34所示。

    程序清单 34

    INT  __tsDevCreate (PCHAR     pcName)
    {
    ……
        iTemp = (INT)iosDevAdd(&ptsDev->TS_devhdr,
                               pcName,
                               touch_dev_num);
    ……
    }
  9. __tsThread

    该函数是在__tsOpen中建立的触摸屏服务线程,其在执行过程当中等待__tsIsr中断函数发送二进制信号量,收到信号后执行后续操做。具体实现如程序清单 35所示。

    程序清单 35

    static void __tsThread (TS_DEV  *ptsDev)
    {
    ……
            /*
             *  等待INT_TC中断,进入中断服务函数,发送二进制信号量
             */
            API_SemaphoreBPend(adc_mst, LW_OPTION_WAIT_INFINITE);
    ……
            /*
             *  Menu Start Conversion
             *  (开始转换)
             */
            uiRegVal = readl(REG_ADC_IER) | ADC_IER_MIEN;
            writel(uiRegVal, REG_ADC_IER);
    
            uiRegVal = readl(REG_ADC_CTL) | ADC_CTL_MST;
            writel(uiRegVal, REG_ADC_CTL);
    
            if (__tsGetXY(&ix, &iy)) {
                /*
                 *  当前有点击操做.
                 */
                LW_SPIN_LOCK_QUICK(&ptsDev->TS_slLock, &iregInterLevel);
                ptsDev->TS_tData.kstat |= MOUSE_LEFT;
                ptsDev->TS_tData.xmovement = ix;
                ptsDev->TS_tData.ymovement = iy;
                LW_SPIN_UNLOCK_QUICK(&ptsDev->TS_slLock, iregInterLevel);
                SEL_WAKE_UP_ALL(&ptsDev->TS_selwulList,
                                SELREAD);                                   /*  释放全部等待读的线程        */
            } else {
                /*
                 *  当前没有点击操做.
                 */
                if (ptsDev->TS_tData.kstat & MOUSE_LEFT) {
                    LW_SPIN_LOCK_QUICK(&ptsDev->TS_slLock, &iregInterLevel);
                    ptsDev->TS_tData.kstat &= (~MOUSE_LEFT);
                    LW_SPIN_UNLOCK_QUICK(&ptsDev->TS_slLock, iregInterLevel);
                }
    
                if (ptsDev->TS_bIsReadRel == LW_FALSE) {                      /*  没有读取到释放操做          */
                    SEL_WAKE_UP_ALL(&ptsDev->TS_selwulList,
                                    SELREAD);                               /*  释放全部等待读的线程        */
                }
            }
        }
    }
    static INT  __tsGetXY (INT  *pX, INT  *pY)
    {
        /*
         *  等待INT_ADC中断,进入中断服务函数,发送二进制信号量adc_sem
         */
        API_SemaphoreBPend(adc_sem, LW_OPTION_WAIT_INFINITE);
    
        *pX = abs_xp;
        *pY = abs_yp;
    
        return send_press;
    }
  10. __tsIsr

    该函数是由__tsOpen注册的中断服务函数,在中断服务函数中进行判断,判断不一样中断信号进行不一样的中断处理,并给__tsThread发送二进制信号量。具体实现如程序清单 36所示。

    程序清单 36

    static irqreturn_t __tsIsr (void *arg)
    {
    ……
        /*
         *  检测触摸转换是否完成
         */
        if(uisr & ADC_ISR_TF) {
            writel(ADC_ISR_TF, REG_ADC_ISR);                                /*  清除标志位                  */
    
            /*
             *  若转换完成,赋值并使touchGetXY返回1
             *  不然清零并使touchGetXY返回0
             */
            uiRegVal = readl(REG_ADC_XYDATA);
    
            abs_xp        = (uiRegVal >> ADC_XYDATA_XBIT) & 0xFFF;
            abs_yp        = (uiRegVal >> ADC_XYDATA_YBIT) & 0xFFF;
            send_press    = TF_ON;
    
            printk("%d    abs_xp = %d, abs_yp = %d \n", __LINE__, abs_xp, abs_yp);
    
            API_SemaphoreBPost(adc_sem);                                    /*  释放一个信号给等待任务      */
        } else {
            abs_xp        = XDATA_CLR;
            abs_yp        = YDATA_CLR;
            send_press    = TF_OFF;
    
            API_SemaphoreBPost(adc_sem);
        }
    
        /*
         *  检测是否触摸按下 / 松开
         */
        if((uisr & ADC_ISR_PEUEF) && (uier & ADC_IER_PEUEIEN)) {
            uiRegVal = ADC_ISR_PEUEF | ADC_ISR_PEDEF;
            writel(uiRegVal, REG_ADC_ISR);
        } else if((uisr & ADC_ISR_PEDEF) && (uisr & ADC_IER_PEDEIEN)) {
            uiRegVal = readl(REG_ADC_ISR) | ADC_ISR_PEUEF | ADC_ISR_PEDEF;
            writel(uiRegVal, REG_ADC_ISR);
    
            API_SemaphoreBPost(adc_mst);
        }
    
        /*
         *  检测全部转换是否完成
         */
        if(uisr & ADC_ISR_MF) {
            uiRegVal = readl(REG_ADC_ISR) | ADC_ISR_MF;
            writel(uiRegVal, REG_ADC_ISR);
        }
    
        return  (LW_IRQ_HANDLED);
    }
  11. __tsOpen

    Open函数调用Init函数初始化并建立触摸屏服务线程。具体实现如程序清单 37所示。

    程序清单 37

    static LONG  __tsOpen (TS_DEV  *ptsDev,
                            PCHAR    pcName,
                            INT      iFlags,
                            INT      iMode)
    {
    ……
        	__tsInit();
    ……
      /*
         *  建立触摸屏服务线程
         */
          ptsDev->TS_hThread = API_ThreadCreate("t_touch",
                                             (PTHREAD_START_ROUTINE)__tsThread,
                                             &threadattr, LW_NULL);
    ……
    }
  12. __tsInit

    Init函数中建立二进制信号量,注册中断服务函数。具体实现如程序清单 38所示。

    程序清单 38

    static void  __tsInit (void)
    {
    ……
        API_SemaphoreBCreate("ad_thread_sem",
                              0,
                              LW_OPTION_WAIT_FIFO,
                              &adc_sem);
        API_SemaphoreBCreate("ad_thread_mst",
                              0,
                              LW_OPTION_WAIT_FIFO,
                              &adc_mst);
    ……
        /*
         *  安装ADC 中断服务程序,
         */
        API_InterVectorConnect(VIC_CHANNEL_ADC,
                               (PINT_SVR_ROUTINE)__tsIsr,
                               (PVOID)NULL,
                               "touchscr");
        API_InterVectorEnable(VIC_CHANNEL_ADC);
    ……
    }
  13. __tsClose

    Close函数用于删除以前建立的信号量和线程。具体实现如程序清单 39所示。

    程序清单 39

    static INT __tsClose (TS_DEV  *ptsDev)
    {
    ……
                /*
                 *  Disable Pen Down Event
                 */
                writel(CLEAR_ALL, REG_ADC_IER);
    
                API_ThreadForceDelete(&ptsDev->TS_hThread, LW_NULL);
    
                API_SemaphoreBDelete(&adc_sem);
                API_SemaphoreBDelete(&adc_mst);
    ……
    }
  14. __tsRead

    Read函数获得ADC值并上报。具体实现如程序清单 310所示。

    程序清单 310

    static ssize_t __tsRead (TS_DEV                        *ptsDev,
                             touchscreen_event_notify      *pnotify,
                             size_t                         stNbyte)
    {
        INTREG           iregInterLevel;
    
        if (stNbyte == 0) {
            return  (ERROR_NONE);
        }
    
        LW_SPIN_LOCK_QUICK(&ptsDev->TS_slLock, &iregInterLevel);
    
        pnotify->ctype   = ptsDev->TS_tData.ctype;
        pnotify->kstat   = ptsDev->TS_tData.kstat;
        pnotify->xanalog = ptsDev->TS_tData.xanalog;
        pnotify->yanalog = ptsDev->TS_tData.yanalog;
    
        printk("%d    ix = %d    iy = %d\n", __LINE__, ptsDev->TS_tData.xanalog, ptsDev->TS_tData.yanalog);
    
        if (ptsDev->TS_tData.kstat & MOUSE_LEFT) {                          /*  读取到点击事件              */
            ptsDev->TS_bIsReadRel = LW_FALSE;                               /*  须要确保应用读到释放操做    */
        } else {
            ptsDev->TS_bIsReadRel = LW_TRUE;                                /*  已经读取到释放操做          */
        }
    
        LW_SPIN_UNLOCK_QUICK(&ptsDev->TS_slLock, iregInterLevel);
    
        return  (sizeof(touchscreen_event_notify));
    }
  15. 内核模块加载

    当ADC触摸屏驱动完成后,编译生成touch_drv.ko文件,经过tftp服务下载至/lib/modules/路径下,经过执行modulereg touch_drv.ko命令动态加载至内核,如图 34所示:

    图 34 modulereg touch_drv.ko动态加载

    加载成功后,经过执行modules命令能够查看当前已加载.ko模块,如图 35所示:

    图 35查看当前已加载.ko模块测试

    在正常加载的状况下,启动任意一个QT程序,进行触控操做,若是正确响应触控事件,说明基本完成移植。