程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

linux驱动移植-LCD触摸屏设备驱动

一、触摸屏种类

触摸屏的基本原理是,用手指或其他物体触摸安装在显示器前端的触控屏时,所触摸的位置(以坐标形式)由触摸屏控制器检测,并通过接口(如RS-232串行口)送到CPU,从而确定输入的信息。

触摸屏系统一般包括触摸屏控制器(卡)和触摸检测装置两个部分:

  • 触控屏控制器(卡)的主要作用是从触摸点检测装置上接收触摸信息,并将它转换成触点坐标,再送给CPU,它同时能接收CPU发来的命令并加以执行:
  • 触摸检测装置一般安装在显示器的前端,主要作用是检测用户的触摸位置,并传送给触控屏控制卡;

1.1 电阻触摸屏

电阻类触摸屏的关键在于材料科技。电阻屏根据引出线数多少,分为四线、五线、六线等多线电阻触摸屏。下面以四线电阻式触摸屏为例介绍。

四线电阻式触摸屏的结构如图,在玻璃或丙烯酸基板上覆盖有两层透平,均匀导电的ITO层,分别做为X电极和Y电极,它们之间由均匀排列的透明格点分开绝缘。其中下层的ITO与玻璃基板附着,上层的ITO附着在PET薄膜上。X电极和Y电极的正负端由“导电条”(图中黑色条形部分)分别从两端引出,且X电极和Y电极导电条的位置相互垂直。引出端X-,X+,Y-,Y+一共四条线,这就是四线电阻式触摸屏名称的由来。

当有物体接触触摸屏表面并施以一定的压力时,上层的ITO导电层发生形变与下层ITO发生接触,该结构可以等效为上图中的电路。

计算触点的X,Y坐标分为如下两步:

  •  计算Y坐标,在Y+电极施加驱动电压Vdrive, Y-电极接地,X+做为引出端测量得到接触点的电压,由于ITO层均匀导电,触点电压与Vdrive电压之比等于触点Y坐标与屏高度之比;
  • 计算X坐标,在X+电极施加驱动电压Vdrive, X-电极接地,Y+做为引出端测量得到接触点的电压,由于ITO层均匀导电,触点电压与Vdrive电压之比等于触点X坐标与屏宽度之比;

 

 所以:

$$x=\frac{V_{x_+}}{V_{driver}} \times width_{screen}$$

$$y=\frac{V_{y_+}}{V_{driver}} \times height_{screen}$$

测得的电压通常由ADC转化为数字信号,再进行简单处理就可以做为坐标判断触点的实际位置。

四线电阻式触摸屏除了可以得到触点的X/Y坐标,还可以测得触点的压力,这是因为top layer施压后,上下层ITO发生接触,在触点上实际是有电阻存在的,如图3的Rtouch。压力越大,接触越充分,电阻越小,通过测量这个电阻的大小可以量化压力大小。

1.2 电容触摸屏

是利用人体的电流感应进行工作的。电容式触摸屏是是一块四层复合玻璃屏,玻璃屏的内表面和夹层各涂有一层ITO,最外层是一薄层矽土玻璃保护层,夹层ITO涂层作为工作面,四个角上引出四个电极,内层ITO为屏蔽层以保证良好的工作环境。
当手指触摸在金属层上时,由于人体电场,用户和触控屏表面形成以一个耦合电容,对于高频电流来说,电容是直接导体,于是手指从接触点吸走一个很小的电流。这个电流分从触控屏的四角上的电极中流出,并且流经这四个电极的电流与手指到四角的距离成正比,控制器通过对这四个电流比例的精确计算,得出触摸点的位置。

二、硬件分析

2.1 硬件接线

由于我们Mini2440开发板使用的S3C2440 SOC,只支持四线电阻式触摸屏。这里我们使用的LCD型号为LCD-T35(TD035STEB4),该4线连接在S3C2440的AIN4~AIN7引脚上,该引脚专门是用来接收模拟输入信号:

引脚说明:

  • YM: (Y Minus)触摸屏的Y坐标的负线,也可以用Y -表示;
  • YP : (Y Power)触摸屏的Y坐标的正线, 也可以用Y+表示;
  • XM: (X Minus)触摸屏的X坐标的负线, 也可以用X-表示;
  • XP : (X Power)触摸屏的X坐标的正线, 也可以用X+表示;

2.2 x坐标获取

如下图,  把XP接3.3V 、XM接0V,YP和YM悬空。我们以按压X坐标的中间位置,X层和Y层便闭合,此时YP就会输出当前X坐标值的1.66V给CPU 。

2.3 y坐标获取

如下图, 把YP接3.3V , YM接0V,XP和XM悬空,我们以按压X坐标的中间位置, X层和Y层便闭合了,此时XP就会输出当前X坐标值的1.66V给CPU 。

 

2.4 s3c2440的触摸屏接口

查看s3c2440手册第16章ADC & TOUCH SCREEN INTERFACE章节,触摸屏A/D转换器和触摸屏接口框图如下:

触摸屏接口模式共有四种:

  • 普通转换模式:单转换模式是最适合的通用的ADC转换,此模式可以通过设置ADCCON完成初始化,通过读写ADCDAT0完成;
  • 分离的X/Y方向转换模式;
  • 自动顺序X/Y方向转换模式:自动顺序X/Y方向转换模式需要设置寄存器ADCTSC=0x0C,触摸屏控制器顺序变换触摸X方向和Y方向,在该模式中触摸屏控制器会自动把X坐标写入到ADCDAT0、把Y坐标写入ADCDAT1,并且产生INT_ADC中断源给中断控制器;
  • 等待中断模式:等待中断模式需要设置寄存器ADCTSC=0xD3/0x1D3,当触摸屏笔尖抬起/按下产生INT_TC中断信号,触摸屏控制器产生中断信号后,必须清除等待中断模式(XY_PST设置为0)。

ADC的工作频率最大为2.5MHZ,需要设置寄存器ADCCON->PRSCVL更改分频系数。A/D转换时间:

当PCLK频率在50MHz并且预分频器的值为49时,10位的转换时间为:

$$A/D转换器频率=\frac{50MHZ}{49+1}=1MHz$$

$$转换时间=\frac{1}{1M/5}=\frac{1}{200k}=5us$$

2.5 ADC 和触摸屏接口特殊寄存器

2.5.1 ADC控制寄存器(ADCCON)

寄存器信息:

寄存器名

地址

是否读写

描述

复位值

ADCCON

0x58000000

R/W

ADC控制寄存器

0x3FC4

寄存器位信息:

ADCCON 描述 初始状态
ECFLG [15]

转换结束标志位(只读)

0 = A/D正在转换   1 = A/D转换已结束

 0
PRSCEN [14]

A/D转换器预分频器使能

0 = 禁止    1 = 使能

 0
PRSCVL [13:6]

A/D转换器预分频值

数值范围: 0~255

注意:ADC频率应该设置为低于PCLK的1/5

 0xFF
SEL_MUX [5:3]

模拟输入通道选择

000 = AIN0  001= AIN1  010 = AIN2  011 = AIN3
100 = YM     101 = yp     110 = XM    111 = XP 

 0
STDBM [2]

待机模式选择

0 = 正常工作模式   1 = 待机模式 

 1
READ_ START [1]

读启动 A/D 转换

0 = 禁止读启动操作 1 = 使能读启动操作

0
ENABLE_START [0]

使能A/D转换启动。如果 READ_START 为使能,则此值无效

0 = 无操作      1 =  A/D 转换启动且此位在启动后被清零

0
2.5.2 ADC触摸屏控制寄存器(ADCTSC)

寄存器信息:

寄存器名

地址

是否读写

描述

复位值

ADCTSC

0x58000004

R/W

ADC触摸屏控制寄存器

0x58

寄存器位信息:

ADCTSC 描述 初始状态
UD_SEB [8]

检测笔尖起落状态

0 = 检测笔尖落下中断信号 1 = 检测笔尖抬起中断信号

 0
YM_SEN [7]

YM 开关使能

0 = YM 输出驱动器禁止 1 = YM 输出驱动器使能

 0
YP_SEN [6]

YP 开关使能

0 = YP 输出驱动器禁止 1 = YP 输出驱动器使能

1
XM_SEN [5]

XM 开关使能

0 = XM 输出驱动器禁止 1 = XM 输出驱动器使能

 0
XP_SEN [4]

XP 开关使能

0 = XP 输出驱动器禁止 1 = XP 输出驱动器使能

 1
PULL_UP [3]

上拉开关使能

0 = XP 上拉使能   1 = XP 上拉禁止

1
AUTO_PST [2]

0 = 正常 ADC 转换    1 = 自动顺序X方向和Y方向测量

0
XY_PST [1:0]

 手动测量X方或Y方向

00 = 无操作模式    01 = X 方向测量

10 = Y 方向测量    11 = 等待中断模式

0
2.5.3 ADC转换数据寄存器(ADCDAT0)

寄存器信息:

寄存器名

地址

是否读写

描述

复位值

ADCDAT0

0x5800000C

R

ADC转换数据寄存器

-

寄存器位信息:

ADCDAT0 描述 初始状态
UPDOWN [15]

等待中断模式中笔尖的起落状态

0 = 笔尖落下态 1 = 笔尖抬起态

-
AUTO_PST [14]

自动顺序 X 方向和 Y 方向转换

0 = 正常 ADC 转换 1 = 顺序 X 方向、Y 方向测量

 -
XY_PST [13:12]

手动 X 方向或 Y 方向测量

00 = 无操作模式 01 = X 方向测量

10 = Y 方向测量 11 = 等待中断模式

-
保留 [11:10]

保留

-
XPDATA [9:0]

X 方向转换数值(包括正常 ADC 转换数值)

数值范围:0 至 3FF

-
2.5.4 ADC转换数据寄存器(ADCDAT1)

寄存器信息:

寄存器名

地址

是否读写

描述

复位值

ADCDAT1

0x58000010

R

ADC转换数据寄存器

-

寄存器位信息:

ADCDAT1 描述 初始状态
UPDOWN [15]

等待中断模式中笔尖的起落状态

0 = 笔尖落下态 1 = 笔尖抬起态

-
AUTO_PST [14]

自动顺序X方向和Y方向转换

0 = 正常ADC 转换  1 = 顺序X方向、Y方向测量

 -
XY_PST [13:12]

手动X方向或Y方向测量

00 = 无操作模式 01 = X 方向测量

10 = Y 方向测量 11 = 等待中断模式

-
保留 [11:10]

保留

-
YPDATA [9:0]

Y方向转换数值(包括正常 ADC 转换数值)

数值范围:0 至 3FF

-

三、触摸屏驱动编写步骤

3.1 输入子系统

对于触摸屏驱动,也是使用输入子系统框架进行编写,输入子系统相关内容在linux驱动移植-输入子系统示例里详细分析了:

右边的驱动事件处理,内核是已经写好了的,所以我们只需要编写触摸屏驱动设备,并将其注册到输入子系统即可 。

3.2 驱动入口函数

(1) 我们首先通过input_allocate_device动态创建struct input_dev结构对象dev;

(2) 初始化dev;

  • 通过set_bit设置支持的事件类型:按键事件EV_KEY,绝对位移事件EV_ABS;
  • 通过input_set_capability设置input设备支持的按键类型的事件包括:触摸屏笔尖按下;
  • 通过input_set_abs_params设置input设备支持的绝对位移类型的事件包括:ABS_X、ABS_Y、ABS_PRESSURE;

(3) 然后调用input_register_device注册这个设备;

(4) 初始化触摸屏相关的硬件:

  • 开启ADC时钟,使用clk_get 和clk_enable函数;
  • ioremap获取寄存器地址,设置寄存器ADCCON =(1<<14)|(49<<6),分频值设置为49;
  • 设置寄存器ADCDLY=0xffff,,ADC启动延时时间设为最大值,使触摸按压更加稳定;
  • 开启IRQ_TC中断、并通过IRQ_ADC中断获取XY坐标;
  • 初始化定时器,增加触摸滑动功能;
  • 最后设置寄存器ADCTSC=0x0D3,开启IRQ_TC中断;

3.3 驱动出口函数

  • 移除定时器;
  • 释放中断;
  • iounmap注销地址;
  • 卸载驱动、注销dev;

3.4 IRQ_TC中断函数

  • 若判断触摸屏当前为松开状态,设置寄存器ADCTSC =0xD3,即设置触摸屏为等待笔尖按下中断模式;
  • 若判断触摸屏当前为按下状态,设置寄存器ADCTSC =0x0C,即设置为XY自动转换模式,启动一次ADC转换(ADC转换成功后,会进入IRQ_ADC中断函数);

3.5 IRQ_ADC中断函数

  • 若判断触摸屏当前为松开状态,设置寄存器ADCTSC =0xD3,即设置触摸屏为等待笔尖按下中断模式;
  • 若判断触摸屏当前为按下状态,获取ADCDAT0/1的低10位,计算出X/Y方向坐标值。多次测量(多次设置为XY自动转换模式并启动ADC转换,转换完成就会再次进入IRQ_ADC中断函数),测量4次后判断精度,满足则上报数据,否则设置成等待笔尖抬起中断模式(设置寄存器ADCTSC =0x1D3),10ms后启动定时器超时函数(如果10ms之后仍然还没有笔尖抬起,将会再次进行坐标测量,从而可以处理触摸滑动的问题);

3.6 定时器超时函数

  • 若判断触摸屏当前为松开状态,设置寄存器ADCTSC =0xD3,即设置触摸屏为等待笔尖按下中断模式 ;
  • 若判断触摸屏当前为按下状态,设置寄存器ADCTSC =0x0C,即设置为XY自动转换模式,启动一次ADC转换(ADC转换成功后,会进入IRQ_ADC中断函数);

四、编写代码

4.1 创建项目

在/work/sambashare/drivers路径下创建12.lcd_touch项目,并创建lcd_touch_dev.c和Makefile文件。

4.2 lcd_touch_dev.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/irq.h>        // 包含了mach/irqs.h
#include <linux/interrupt.h>
#include <linux/gpio/machine.h>
#include <mach/gpio-samsung.h>
#include <linux/input.h>
#include <linux/timer.h>
#include <linux/clk.h>
#define IRQF_SAMPLE_RANDOM       0x00000040
/*
 * s3c2440 APC和触摸屏相关寄存器
 */
struct s3c_ts_regs{
    unsigned long adccon;
    unsigned long adctsc;
    unsigned long adcdly;
    unsigned long adcdat0;
    unsigned long adcdat1;
    unsigned long adcupdn;
};
/* 定义一个input_dev结构体 */
static struct input_dev *s3c_ts_dev;
/* 寄存器 */
static struct s3c_ts_regs *s3c_ts_regs;
/* 定时器 */
static struct timer_list s3c_ts_timer;
/* 进入等待笔尖按下中断模式 */
static void enter_wait_pen_down_mode(void)
{
    /* 设置寄存器ADCTSC=0x0d3,开启INT_TC中断,笔尖按下触发*/
    s3c_ts_regs->adctsc = 1 << 7 | 1 << 6 |  1<<4  | 3 << 0;
}
/* 进入等待笔尖抬起中断模式 */
static void enter_wait_pen_up_mode(void)
{
    /* 设置寄存器ADCTSC=0x01d3,开启IRQ_TC中断,笔尖抬起触发*/
    s3c_ts_regs->adctsc = 1<< 8 | 1 << 7 | 1 << 6 |  1<<4  | 3 << 0;
}
/* 进入XY自动转换模式,ADC转换完成后,触发INT_ADC中断 */
static void enter_measure_xy_mode(void)
{
    /* 启动XY自动转换 */
    s3c_ts_regs->adctsc = (1<<3)|(1<<2);
     /* 启动1次ADC转换,开启一次ADC转换,当ADC转换成功该位清0 */
    s3c_ts_regs->adccon |= (1<<0);
}
/*
 * IRQ_TC中断处理服务
 * 笔尖按下或者抬起进入
 */
static irqreturn_t tc_handler(int irq, void *dev_id)
{
    //如果触摸屏当前为松开状态
   if (s3c_ts_regs->adcdat0 & (1<<15))
       {
           /*上报压力值为0,等价 input_event(s3c_ts_dev, EV_ABS, ABS_PRESSURE,  0) */
           input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
           /*上报BTN_TOUCH按键值松开,等价 input_event(s3c_ts_dev, EV_KEY, BTN_TOUCH,  0) */
           input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
           /*上报同步事件,通知系统有事件上报 */
           input_sync(s3c_ts_dev);
           /* 进入等待笔尖按下中断模式 */
           enter_wait_pen_down_mode();
//           printk("tc_handler up\n");
       }
       else
       {
           /* 进入测量X/Y坐标模式并启动一次ADC */
           enter_measure_xy_mode();
//           printk("tc_handler down\n");
       }
    return IRQ_RETVAL(IRQ_HANDLED);
}
/*
 * 计算若干次的采样值是否存在较大误差,如果存在较大误差,放弃这次采样
 */
static int s3c_filter_ts(int x[], int y[])
{
// 定义最大误差
#define ERR_LIMIT 10
    int avr_x, avr_y;
    int det_x, det_y;
    avr_x = (x[0] + x[1])/2;
    avr_y = (y[0] + y[1])/2;
    det_x = (x[2] > avr_x) ? (x[2] - avr_x) : (avr_x - x[2]);
    det_y = (y[2] > avr_y) ? (y[2] - avr_y) : (avr_y - y[2]);
    if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))
        return 0;
    avr_x = (x[1] + x[2])/2;
    avr_y = (y[1] + y[2])/2;
    det_x = (x[3] > avr_x) ? (x[3] - avr_x) : (avr_x - x[3]);
    det_y = (y[3] > avr_y) ? (y[3] - avr_y) : (avr_y - y[3]);
    if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))
        return 0;
    return 1;
}
/*
 * IRQ_ADC中断处理服务 获取x、y坐标
 * 自动顺序X/Y方向转换模式,ADC转换成功后,会进入IRQ_ADC中断函数(系统自动把X坐标写入到ADCDAT0、把Y坐标写入ADCDAT1)
 */
static irqreturn_t adc_handler(int irq, void *dev_id)
{
    /* 计数 */
    static int cnt=0;
    /* 保存x、y  */
       static int x[4],y[4];
    //如果触摸屏当前为松开状态
       if (s3c_ts_regs->adcdat0 & (1<<15))
       {
           /*上报压力值为0,等价 input_event(s3c_ts_dev, EV_ABS, ABS_PRESSURE,  0) */
        input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
           /*上报BTN_TOUCH按键值松开,等价 input_event(s3c_ts_dev, EV_KEY, BTN_TOUCH,  0) */
        input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
           /*上报同步事件,通知系统有事件上报 */
        input_sync(s3c_ts_dev);
        /* 进入等待笔尖按下中断模式 */
        enter_wait_pen_down_mode();
           cnt = 0;
       }
       else
       {   // 笔尖按下后 连续测量4次
           // x坐标
           x[cnt]=s3c_ts_regs->adcdat0 & 0x3ff;
           // y
           y[cnt]=s3c_ts_regs->adcdat1 & 0x3ff;
           cnt++;
        // 4次求平均
           if(cnt == 4){
            // 计算若干次的采样值是否存在较大误差,如果存在较大误差,放弃这次采样
               if (s3c_filter_ts(x, y)){
                   //上报X方向值
                   input_report_abs(s3c_ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);
                   //上报Y方向值
                   input_report_abs(s3c_ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);
                   //上报压力方向值
                   input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1);
                   //上报BTN_TOUCH按键按下
                   input_report_key(s3c_ts_dev, BTN_TOUCH, 1);
                   //上报同步事件,通知系统有事件上报
                   input_sync(s3c_ts_dev);
                   printk("X:  %04d,y:  %04d \n",(x[0]+x[1]+x[2]+x[3])/4,(y[0]+y[1]+y[2]+y[3])/4);      //中值滤波
               }
               cnt = 0;
               /* 进入等待笔尖抬起中断模式 */
               enter_wait_pen_up_mode();
               /* 启动定时器处理长按/滑动的情况 10ms后如果仍然没有笔尖抬起,再次进行测量坐标 */
               mod_timer(&s3c_ts_timer, jiffies + HZ/100);
           }
           else
           {
               /* 进入测量X/Y坐标模式并启动一次ADC */
               enter_measure_xy_mode();
           }
       }
    return IRQ_RETVAL(IRQ_HANDLED);
}
/*
 * 定时器超时函数
 * 将输入转换为转换为统一事件形式
 */
static void s3c_ts_timer_function(struct timer_list *t)
{
    /* 如果触摸屏当前为松开状态 */
    if (s3c_ts_regs->adcdat0 & (1<<15))
    {
        /* 上报压力值为0,等价 input_event(s3c_ts_dev, EV_ABS, ABS_PRESSURE,  0) */
        input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
           /*上报BTN_TOUCH按键值松开,等价 input_event(s3c_ts_dev, EV_KEY, BTN_TOUCH,  0) */
        input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
           /*上报同步事件,通知系统有事件上报 */
        input_sync(s3c_ts_dev);
        /* 进入等待笔尖按下中断模式 */
        enter_wait_pen_down_mode();
    }
    else
    {
         /* 进入测量X/Y坐标模式并启动一次ADC */
         enter_measure_xy_mode();
    }
}
/*
 * 入口函数
 */
static int s3c_ts_init(void)
{
    int err;
    struct clk * clk;
    printk("touch screen driver init\n");
    /* 向内核 申请input_dev结构体 */
    s3c_ts_dev = input_allocate_device();
    /* 设置input_dev、 输入事件code定义在include/uapi/linux/input-event-codes.h */
    set_bit(EV_KEY,s3c_ts_dev->evbit);       // 支持按键事件
    set_bit(EV_ABS,s3c_ts_dev->evbit);       // 支持绝对位移事件
    input_set_capability(s3c_ts_dev,EV_KEY,BTN_TOUCH);   //触摸屏笔尖按下
    input_set_abs_params(s3c_ts_dev, ABS_X, 0, 0x3FF, 0, 0);//s3c2440手册ADC是10位,所以第四个参数设置为3FF
    input_set_abs_params(s3c_ts_dev, ABS_Y, 0, 0x3FF, 0, 0);
    input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0);
    /* 注册input_dev */
    err = input_register_device(s3c_ts_dev);
    if (err) {
       printk("input touch screen driver registration failed\n");
       /* 释放驱动结构体 */
       input_free_device(s3c_ts_dev);
       return err;
    } else {
        printk("input touch screen driver registered successfully\n");
    }
    /* 使能时钟(设置CLKCON[15]) */
    clk = clk_get(NULL,"adc");
    clk_prepare_enable(clk);
    /* 设置s3c2440的ADC/ts寄存器 */
    s3c_ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_regs));
    s3c_ts_regs->adccon = (1<<14)|(49<<6);   // 预分频器使能,分频值为49  1MHZ
    s3c_ts_regs->adcdly = 0xffff;
    /* 注册中断,这里指定了中断线程化处理函数 */
    request_irq(IRQ_TC, tc_handler, IRQF_SAMPLE_RANDOM, "ts_tc", 0);
    request_irq(IRQ_ADC, adc_handler, IRQF_SAMPLE_RANDOM, "ts_adc", 0);
    /* 初始化定时器 */
    timer_setup(&s3c_ts_timer,s3c_ts_timer_function,0);
    add_timer(&s3c_ts_timer);
    /* 进入等待笔尖按下中断模式*/
    enter_wait_pen_down_mode();
    return 0;
}
/*
 * 出口函数
 */
static void __exit s3c_ts_exit(void)
{
    printk("touch screen driver exit\n");
     /* 删除定时器 */
    del_timer(&s3c_ts_timer);
     /* 释放中断 */
    free_irq(IRQ_TC, NULL);
    free_irq(IRQ_ADC, NULL);
    /* 注销虚拟地址 */
    iounmap(s3c_ts_regs);
     /* 卸载类下的驱动设备 */
    input_unregister_device(s3c_ts_dev);
    /* 释放驱动结构体 */
    input_free_device(s3c_ts_dev);
    return;
}
module_init(s3c_ts_init);
module_exit(s3c_ts_exit);
MODULE_LICENSE("GPL");

4.3 Makefile

KERN_DIR :=/work/sambashare/linux-5.2.8
all:
    make -C $(KERN_DIR) M=`pwd` modules
clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order
obj-m += lcd_touch_dev.o

五、测试

我们在linux驱动移植-LCD驱动分析这一节的基础上进行试验。

5.1 配置内核

执行如下命令:

root@zhengyang:/work/sambashare/linux-5.2.8# make menuconfig

配置内核,移除将内核自带的触摸屏驱动:

Device Drivers  --->
    Input device support  -->
        [*] Touchscreens  -->
               <> Samsung S3C2410/generic touchscreen input driver
    Graphics support  --->
        [*] Bootup logo --->
        Frame buffer Devices  --->
               <*> Support for frame buffer devices --->
                     <*> S3C2410 LCD framebuffer support      // 支持S3C2410、S3C2440

保存文件,输入文件名s3c2440_defconfig,在当前路径下生成s3c2440_defconfig:存档:

mv s3c2440_defconfig ./arch/arm/configs/

5.2 编译内核

此时重新执行:

make distclean
make s3c2440_defconfig
make uImage V=1

将uImage复制到tftp服务器路径下:

 cp /work/sambashare/linux-5.2.8/arch/arm/boot/uImage /work/tftpboot/

5.3  烧录内核

开发板uboot启动完成后,内核启动前,按下任意键,进入uboot,下载内核到内存,并写NAND FLASH:

tftp 30000000 uImage
nand erase.part kernel
nand write 30000000 kernel

下载完成后,重启开发板,内核启动完成后会在显示屏上看到启动logo。

5.4  编译LCD触摸屏驱动

在12.lcd_touch_dev路径下编译:

root@zhengyang:/work/sambashare/drivers/12.lcd_touch_dev# cd /work/sambashare/drivers/12.lcd_touch_dev
root@zhengyang:/work/sambashare/drivers/12.lcd_touch_dev# make

拷贝驱动文件到nfs文件系统:

root@zhengyang:/work/sambashare/drivers/12.lcd_touch_dev# cp lcd_touch_dev.ko  /work/nfs_root/rootfs/

5.5 安装驱动

重启开发板,加载lcd驱动,执行如下命令:

insmod lcd_touch_dev.ko

运行命令cat /proc/interrupts可以查看当前系统有哪些中断服务:

[root@zy:/]# cat /proc/interrupts
           CPU0
 29:      21110       s3c  13 Edge      samsung_time_irq
 32:          0       s3c  16 Edge      s3c2410-lcd
 42:          0       s3c  26 Edge      ohci_hcd:usb1
 43:          0       s3c  27 Edge      s3c2440-i2c.0
 55:       1460   s3c-ext   7 Edge      eth0
 74:         14  s3c-level   0 Edge      s3c2440-uart
 75:        117  s3c-level   1 Edge      s3c2440-uart
 83:          0  s3c-level   9 Edge      ts_tc
 84:          0  s3c-level  10 Edge      ts_adc
 87:          0  s3c-level  13 Edge      s3c2410-wdt

查看设备节点文件:

[root@zy:/]# ls /dev/input -l
total 0
crw-rw----    1 0        0          13,  64 Jan  1 00:00 event0

5.6 测试

我们滑动屏幕,从下往上滑动,驱动程序会输出如下信息:

X:  0477,y:  0151
X:  0484,y:  0158
X:  0487,y:  0161
X:  0497,y:  0169
X:  0495,y:  0172
X:  0495,y:  0173
X:  0497,y:  0181
X:  0500,y:  0193
X:  0503,y:  0206
X:  0503,y:  0211
X:  0499,y:  0221
X:  0506,y:  0234
X:  0505,y:  0243
X:  0508,y:  0254
X:  0512,y:  0271
X:  0506,y:  0276
X:  0505,y:  0295
X:  0509,y:  0302
X:  0506,y:  0350
X:  0505,y:  0381
X:  0510,y:  0424
X:  0504,y:  0433
X:  0504,y:  0449
X:  0500,y:  0459
X:  0498,y:  0488
X:  0502,y:  0511
X:  0497,y:  0525
X:  0494,y:  0543
X:  0495,y:  0564
X:  0495,y:  0585
X:  0499,y:  0599
X:  0497,y:  0637
X:  0503,y:  0668
X:  0507,y:  0679 

可以看到x坐标基本不变,y坐标逐渐增大,最大为我们设定的值0x3FF。

我们可以通过从屏幕最左滑动到最右端,从而得到x坐标范围,我这里大概是80~940;

我们可以通过从屏幕最下滑动到最上端,从而得到y坐标范围,我这里大概是100~685(我的触摸屏最上端大概率是损坏了,点击没有反应,正常范围应该是100~900+);

由于我这移植的nfs文件系系统没有hexdump命令,所以就不能执行如下命令查看触摸屏的输入信息了:

hexdump /dev/input/event0 

 六、使用tslib进行测试

6.1 下载tslib

直接到github上下载:

git clone https://github.com/kergoth/tslib

下载完成后,我直接上传到ubuntu服务器如下路径:/work/sambashare/drivers/12.lcd_touch_dev。

跳转到tslib文件夹:

cd tslib/

6.2 编译

首先运行:

./autogen.sh                       // 如果出现如下错误./autogen.sh: 3: ./autogen.sh: autoreconf: not found 先执行apt-get install autoconf automake libtool 
mkdir tmp                    //创建安装目录

然后配置:

CC=arm-linux-gcc ./configure --host=arm-linux --cache-file=arm-linux.cache --prefix=$(pwd)/tmp CFLAGS="-march=armv4t -O2 -Wall -W"

编译安装:

make                                                //编译
make install                                       //安装到tmp目录下    

需要注意的是编译所使用的的版本要和内核编译的版本保持一致,我是用的是arm-linux-gcc4.8.3版本。

可以通过如下命令查看可执行文件的平台属性信息:

root@zhengyang:/work/sambashare/drivers/12.lcd_touch_dev/tslib/tmp# arm-linux-readelf -A bin/ts_test
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "4T"
  Tag_CPU_arch: v4T
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-1
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_rounding: Needed
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align_needed: 8-byte
  Tag_ABI_align_preserved: 8-byte, except leaf SP
  Tag_ABI_enum_size: int

6.3 配置nfs文件系统

进入tmp, 将tmp里面的bin ,etc,include,lib4个目录下的文件拷贝到文件系统的bin ,etc,include,lib4个目录下 :

cd tmp
cp *  /work/nfs_root/rootfs/   -rfd

进入nfs文件系统,修改etc/inittab文件:

cd /work/nfs_root/rootfs/
vim etc/inittab

检查是否会启动:

tty1: tty1::askfirst:-/bin/sh  #在虚拟终端tty1启动askfirst动作的shell,也就是在LCD上会出现Please press Enter to active this console.

若有,前面加#,屏蔽掉,这条命令。

6.4 安装驱动

启动开发板,安装lcd_touch_dev驱动:

insmod lcd_touch_dev.ko

配置LCD和触摸屏环境:

export TSLIB_TSDEVICE=/dev/input/event0           //ts设备文件(触摸屏):event0
export TSLIB_CALIBFILE=/etc/pointercal            //校验文件(calibrate file),存放校验值
export TSLIB_CONFFILE=/etc/ts.conf                //配置文件
export TSLIB_PLUGINDIR=/lib/ts                    //插件文件
export TSLIB_CONSOLEDEVICE=none                   //终端控制台设为NULL
export TSLIB_FBDEVICE=/dev/fb0                    //fb设备文件(LCD):fb0

或者直接写入nfs根文件系统路径下etc/profile文件中。

6.5 测试

运行校准程序,触摸屏依次出现5个点,依次点击之:

[root@zy:/]# ts_calibrate
xres = 240, yres = 320
Took 2 samples...
Top left : X =  260 Y =  736
Took 2 samples...
Top right : X =  771 Y =  729
Took 1 samples...
Bot right : X =  777 Y =  243
Took 1 samples...
Bot left : X =  258 Y =  214
Took 1 samples...
Center : X =  528 Y =  505
-21.327820 0.271702 0.000761
366.129089 0.009783 -0.435114
Calibration constants: -1397740 17806 49 23994636 641 -28515 65536

生成的校准文件名为pointercal,位于/etc目录下。

在开发板上执行如下名,就可以在lcd上绘图了:

ts_test

屏幕最上方会出现三个按钮,分别为“Drag”、“Draw”和“Quit”,默认是第一个,因此,用触摸笔点击任何一处,十字光标便会到那里。

下面是点击Draw按钮并用触摸笔写字的提示信息的一小部分(:

485.820282:    176    118      1
485.850283:    184    120      1
485.880283:    187    126      1
485.910284:    189    134      1
485.970367:    205    163      1
486.000308:    205    165      1
486.090298:    159    165      1
486.120315:    157    159      1
486.150320:    159    151      1
486.210284:    158    156      1
486.330266:    113    325      1
486.360288:    113    327      1
486.397042:    113    327      0

第一列为timeval结构体的两个成员:tv_sec和tv_usec,中间两列分别是X和Y的坐标,最后为pressure,这里可以理解成“触摸事件”,为1表示触摸笔点击了(接触)屏幕,为0表示触摸笔离开了屏幕(这里出现很多的1是正常的,因为写字过程中笔没有离开触摸屏)。
点击屏幕上“Quit”或按Ctrl+C可退出该程序。

七、代码下载

Young / s3c2440_project[drivers]

参考文章

[1]十三、Linux驱动之触摸屏驱动

[2]18.Llinux-触摸屏驱动(详解)

[3]四线电阻触摸屏工作原理的示意图

[4]四线电阻触摸屏原理

[5]常用低成本:四线电阻式触摸屏原理

[6]linux根文件系统之inittab

[7]嵌入式linux启动时运行的inittab文件(zz)

[8]Linux移植随笔:终于解决Tslib的问题了

posted @ 2022-05-01 01:00  大奥特曼打小怪兽  阅读(1344)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步