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 电容触摸屏
二、硬件分析
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 |
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]
参考文章
[4]四线电阻触摸屏原理