title: linux 触摸屏驱动

tags: linux

date: 2018/12/6 18:16:08

toc: true

linux 触摸屏驱动

  • 触摸屏驱动也是使用了输入子系统的框架,同时可以联想一下,也可以结合一下platform总线架构.实际上给的例子也是这样的drivers/input/touchscreen/s3c2410_ts.c

  • 触摸屏驱动没有新知识,混合使用输入子系统以及中断配置和adc的寄存器配置。

输入子系统怎么写?

触摸屏事件

内核已经为我们的触摸屏提供了框架,其中支持了相关的上报事件类型如下

参考下这里,有论述BIN_TOUCH这个事件代码,意思是说表示触摸,且必须是发送的第一个代码也就是先被上报.其中1表示被按下,0表示浮空.这里先不考虑触摸压力,触摸压力这里与这个位也有关系.

ABS_{X,Y} must be reported with the location of the touch. BTN_TOUCH must be used to report when a touch is active on the screen. BTN_{MOUSE,LEFT,MIDDLE,RIGHT} must not be reported as the result of touch contact. BTN_TOOL_ events should be reported where possible.

#define EV_SYN			0x00	//事件同步
//Used to describe state changes of keyboards, buttons, or other key-like devices.
#define EV_KEY 0x01 //按键类事件
//Used to describe absolute axis value changes, e.g. describing the coordinates of a touch on a touchscreen.
#define EV_ABS 0x03 //绝对坐标类型事件 #define BTN_TOUCH 0x14a //指示触摸

事件分类

这里事件分两个等级,也就是一般先设置支持哪些大类事件,再设置支持大类事件的具体事件,对于事件的这个位定义有如下代码:

#define NBITS(x) (((x)/BITS_PER_LONG)+1)
可以看到位标志是存在long类型的变量的,xxx_MAX 也就是支持的最大标志数了,也就是这个宏表示的是数组长度 #define BIT(x) (1UL<<((x)%BITS_PER_LONG))
表示在一个long中的第几位 #define LONG(x) ((x)/BITS_PER_LONG)
表示标志在第几个long unsigned long evbit[NBITS(EV_MAX)];
unsigned long keybit[NBITS(KEY_MAX)];
unsigned long relbit[NBITS(REL_MAX)];
unsigned long absbit[NBITS(ABS_MAX)];
unsigned long mscbit[NBITS(MSC_MAX)];
unsigned long ledbit[NBITS(LED_MAX)];
unsigned long sndbit[NBITS(SND_MAX)];
unsigned long ffbit[NBITS(FF_MAX)];
unsigned long swbit[NBITS(SW_MAX)];

所以手动想设置标志可以这么写了

dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);

当然有更好的函数调用set_bit,这个是用来设置long类型的某一位

set_bit(BTN_TOUCH, s3c_ts_dev->keybit);

事件设置

在使用触摸屏的时候,还需要对一些参数进行设置,使用input_set_abs_params设置绝对值坐标以及压力值

硬件配置

  1. 配置时钟,具体可以参考../归档/linux时钟配置

    struct clk* clk;
    clk = clk_get(NULL, "adc");
    clk_enable(clk);
  2. 配置 ADC寄存器,这里最关键的就是模式的配置,有adc测量模式,等待中断模式。具体参考裸机的ADC触摸屏.md的等效电路章节

  3. 申请中断,配置中断函数

设计思路

注意: 这里判断按下还是松开,一定要先进入等待中断模式,所以中断中都直接进入等待中断模式

这里的滤波只是丢弃第一个后取平均值,没做其他,可以参考

adc中断
if 松开 put(0,0,0)
else 按下 put(data)
if 测量满16次,满足上报 开启定时器,等待下次测量
else 测量不满16次,不满足上报 继续adc测量
ts触摸中断
if 松开 put(0,0,0)
else 按下 启动adc中断 定时器中断
if 松开 put(0,0,0)
else 按下 启动adc中断 put
if 0,0,0 直接上报
else
满16次,上报
不满16次,继续统计

完整程序

完整的程序如下,与老师的有点不同

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/plat-s3c24xx/ts.h>
#include <asm/arch/regs-adc.h>
#include <asm/arch/regs-gpio.h> /**
* input_dev module test
*/ struct input_dev *g_input_dev;
static struct timer_list touch_timer; #define MAX_ADC_CNT 20
static struct adc_date
{
int _x[MAX_ADC_CNT];
int _y[MAX_ADC_CNT];
unsigned long x;
unsigned long y;
int cnt;
} g_adc_date; struct s3c2440_adc_reg
{
unsigned long adccon;
unsigned long adctsc;
unsigned long adcdly;
unsigned long adcdat0;
unsigned long adcdat1;
unsigned long adcupdn;
};
static volatile struct s3c2440_adc_reg *g_s3c2440_adc_reg;
static int time_enable = 0; #define MODE_ADC_START 0 //get X and Y adc
#define MODE_WAIT_DOWN 1
#define MODE_WAIT_UP 2
#define MODE_WAIT_IRQ 3
static void __inline select_mode(int mode)
{
time_enable = 0;
switch (mode)
{
case MODE_ADC_START:
{
g_s3c2440_adc_reg->adctsc = (1 << 3) | (1 << 2); // mode select
g_s3c2440_adc_reg->adccon |= (1 << 0); //start adc
break;
}
case MODE_WAIT_DOWN:
{
g_s3c2440_adc_reg->adctsc = 0xd3; // mode select
break;
}
case MODE_WAIT_UP:
{
g_s3c2440_adc_reg->adctsc = 0x1d3; // mode select
break;
}
case MODE_WAIT_IRQ:
{
g_s3c2440_adc_reg->adctsc |= 0x03; // mode select
break;
}
default:
{
printk("mode is err,please check");
break;
}
}
} /**
* @param x
* @param y
* @param isdown 1=down,0=up
*
* @return 1 =fifo is full
*/
static int put_adc(int x, int y, int isdown)
{
int i = 0;
if (isdown)
{
g_adc_date._x[g_adc_date.cnt] = x;
g_adc_date._y[g_adc_date.cnt] = y;
//printk("x=%d,y=%d\r\n",x,y);
}
else
{
printk("now is up\r\n");
input_report_abs(g_input_dev, ABS_PRESSURE, 0);
input_report_key(g_input_dev, BTN_TOUCH, 0);
input_sync(g_input_dev);
g_adc_date.cnt = 0;
return 0;
}
g_adc_date.cnt++;
if (g_adc_date.cnt >= MAX_ADC_CNT)
{
g_adc_date.x = 0;
g_adc_date.y = 0;
for (i = 0; i < MAX_ADC_CNT; i++)
{
g_adc_date.x += g_adc_date._x[i];
g_adc_date.y += g_adc_date._y[i];
}
g_adc_date.x /= MAX_ADC_CNT;
g_adc_date.y /= MAX_ADC_CNT; printk("x=%d,y=%d\r\n",g_adc_date.x,g_adc_date.y);
input_report_abs(g_input_dev, ABS_X, g_adc_date.x);
input_report_abs(g_input_dev, ABS_Y, g_adc_date.y);
input_report_abs(g_input_dev, ABS_PRESSURE, 1);
input_report_key(g_input_dev, BTN_TOUCH, 1);
input_sync(g_input_dev);
g_adc_date.cnt = 0;
return 1;
}
else
{
return 0;
}
} static irqreturn_t irq_adc_ts_fun ( int irq, void *dev_id)
{
//printk("get in pendown irq\r\n");
select_mode(MODE_WAIT_IRQ);
if (g_s3c2440_adc_reg->adcdat0 & (1 << 15)) //now is up
{
put_adc(0, 0, 0);
select_mode(MODE_WAIT_DOWN);
}
else
{
// ignore the first adc data for pur_adc ()
select_mode(MODE_ADC_START);
}
return IRQ_HANDLED;
} static irqreturn_t irq_adc_get_fun ( int irq, void *dev_id)
{
int x = (g_s3c2440_adc_reg->adcdat0)&0x3FF;
int y = (g_s3c2440_adc_reg->adcdat1)&0x3FF; select_mode(MODE_WAIT_IRQ);
if (g_s3c2440_adc_reg->adcdat0 & (1 << 15)) //now is up
{
put_adc(0, 0, 0);
select_mode(MODE_WAIT_DOWN);
}
else
{
if(put_adc(x, y, 1))
{
// fifo is full ,hold to sleep for ms
select_mode(MODE_WAIT_UP);
time_enable = 1;
mod_timer(&touch_timer, jiffies + HZ/100);
}
else
{
select_mode(MODE_ADC_START);
}
} return IRQ_HANDLED;
}
static void irq_time_fun(unsigned long data)
{
if (!time_enable) return ;
select_mode(MODE_WAIT_IRQ);
if (g_s3c2440_adc_reg->adcdat0 & (1 << 15)) //now is up
{
put_adc(0, 0, 0);
select_mode(MODE_WAIT_DOWN);
}
else
{
select_mode(MODE_ADC_START);
}
} static int __init dri_init(void)
{
printk("hello, insmod \r\n"); // allocate a memory for input_dev
g_input_dev = input_allocate_device();
//set the input_dev
//设置分两类:产生哪类事件;产生这类事件中的哪些具体事件〿 // event lever class
//set_bit(EV_SYN, g_input_dev->evbit);
set_bit(EV_KEY, g_input_dev->evbit);
set_bit(EV_ABS, g_input_dev->evbit);
// event lever bit
set_bit(BTN_TOUCH, g_input_dev->keybit);
input_set_abs_params(g_input_dev, ABS_X, 0, 0x3FF, 0, 0); //2440 adcbit=10=1024
input_set_abs_params(g_input_dev, ABS_Y, 0, 0x3FF, 0, 0);
input_set_abs_params(g_input_dev, ABS_PRESSURE, 0, 1, 0, 0);//touch or release
//registe the input_dev
input_register_device(g_input_dev); /**
* hardware set
* 1. set clock
* 2. set othee regs
*/
{
struct clk *clk;
clk = clk_get(NULL, "adc");
clk_enable(clk);
} g_s3c2440_adc_reg = ioremap(0x58000000, sizeof(struct s3c2440_adc_reg));
/* bit[14] : 1-A/D converter prescaler enable
* bit[13:6]: A/D converter prescaler value,
* 49, ADCCLK=PCLK/(49+1)=50MHz/(49+1)=1MHz
* bit[0]: A/D conversion starts by enable. 先设丿
*/
g_s3c2440_adc_reg->adccon = (1 << 14) | (49 << 6);
// max delay for adc
g_s3c2440_adc_reg->adcdly = 0xffff; request_irq(IRQ_ADC, irq_adc_get_fun, IRQF_SAMPLE_RANDOM, "adc_get", NULL);
request_irq(IRQ_TC, irq_adc_ts_fun, IRQF_SAMPLE_RANDOM, "adc_ts", NULL);
init_timer(&touch_timer);
touch_timer.function = irq_time_fun;
add_timer(&touch_timer); memset(&g_adc_date, 0x00, sizeof(g_adc_date)); select_mode(MODE_WAIT_DOWN);
return 0;
} static void __exit dri_exit(void)
{
free_irq(IRQ_TC, NULL);
free_irq(IRQ_ADC, NULL);
iounmap(g_s3c2440_adc_reg);
input_unregister_device(g_input_dev);
input_free_device(g_input_dev);
del_timer(&touch_timer);
printk("goodbye, remod \r\n");
} module_init(dri_init);
module_exit(dri_exit);
MODULE_LICENSE("GPL");

测试

记录下命令

mount -t nfs -o nolock,vers=2 172.16.45.222:/home/book/stu /mnt && cd /mnt/old/dri_test/study_code/18th/  && insmod cfbfillrect.ko && insmod cfbimgblt.ko && insmod cfbcopyarea.ko && insmod lcd.ko

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
  1. 去除原始的触摸屏的驱动Device Drivers-> Input device support -> Touchscreens,注意这里上面还有个触摸的interface,第一次去错了,小心一下。去除这个驱动之后可以cat /proc/inttupts看下有没有adc中断。如果没有去除这个模块,在注册自己的驱动,后卸载时提示无法释放空的中断,也就是注册驱动失败

  1. 内核还需要修改LCD_mach-smdk2440.c,这个文件里面有frambuf的接口驱动,不不修改将无法加载上一课做的lcd驱动程序,然后去除lcd模块,重新编译上节课的lcd驱动,再编译模块make modules
  2. 编译触摸屏驱动
  3. 注意文件系统的vi /etc/inittab,在lcd实验中使用tty1会影响实验,这里注释掉tty1::askfirst:-/bin/sh
  4. 使用hexdump /dev/eventxxx来查看,起始代码刚开始有打印的,在put_adc里面恢复打印函数也能看出来
  5. 使用ts_lib测试

ts_lib 使用

  1. ts_lib有一些依赖,需要先安装这个依赖,或者参考这个

    sudo apt-get install autoconf
    sudo apt-get install automake
    sudo apt-get install libtool
  2. 编译

    tar xzf tslib-1.4.tar.gz
    cd tslib
    ./autogen.sh # 先要安装依赖,否则提示autoreconf: not found mkdir tmp #这个临时目录以后放编译结果
    echo "ac_cv_func_malloc_0_nonnull=yes" >arm-linux.cache
    ./configure --host=arm-linux --cache-file=arm-linux.cache --prefix=$(pwd)/tmp
    make
    make install #安装到tmp #安装
    cd tmp
    cp * -rf /nfsroot # 这个是复制到根文件系统
  3. 安装触摸屏,lcd驱动

  4. 修改 /etc/ts.conf第1行(去掉#号和第一个空格),# module_raw input改为module_raw input

  5. 设置环境变量

    export TSLIB_TSDEVICE=/dev/event1  #这是指触摸屏设备
    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 #lcd
  6. 校准使用命令 ts_calibrate

  7. 画画,ts_test,打印坐标 ts_printts_print_raw打印原始adc值

问题小结

  • 卸载驱动时提示释放空的中断

    这个问题是因为内核中已经有中断占用,也就是原有的触摸屏驱动没有被去除

  • lcd驱动加载的时候提示段错误

    1. 内核中的mach-smdk2440.c需要修改
    2. 内核中的lcd模块配置为模块
    3. 重新编译lcd驱动
  • 启动脚本去除tty1

    在lcd中添加了lcd作为tty1的输出,需要删除掉

  • ts_lib编译时提示autoreconf: not found,这是因为没有安装相关依赖

    sudo apt-get install autoconf
    sudo apt-get install automake
    sudo apt-get install libtool

linux 触摸屏驱动的更多相关文章

  1. Linux触摸屏驱动测试程序范例【转】

    转自:http://blog.sina.com.cn/s/blog_4b4b54da0102viyl.html 转载2015-05-09 16:28:27 标签:androiditlinux 触摸屏驱 ...

  2. linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-119723.html linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟 xxxxxxxxxx ...

  3. 基于FT5x06嵌入式Linux电容触摸屏驱动

    **************************************************************************************************** ...

  4. 【Linux高级驱动】触摸屏驱动的移植

    触摸屏驱动的移植 流程 注意:看框架图 1.添加input.c组件 Device Drivers  ---> Input device support  --->  Generic inp ...

  5. Linux学习: 触摸屏驱动

    一.Linux输入子系统的结构: 二.触摸屏驱动代码: s3c_ts.c #include <linux/errno.h> #include <linux/kernel.h> ...

  6. linux 输入子系统之电阻式触摸屏驱动

    一.输入子系统情景回忆ING...... 在Linux中,输入子系统是由输入子系统设备驱动层.输入子系统核心层(Input Core)和输入子系统事件处理层(Event Handler)组成.其中设备 ...

  7. 【Linux开发】linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟

    linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  8. Linux设备驱动模型之I2C总线

    一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,提供了与具体硬件无关的I2C读写函数. (2)I2 ...

  9. linux设备驱动概述,王明学learn

    linux设备驱动学习-1 本章节主要学习有操作系统的设备驱动和无操作系统设备驱动的区别,以及对操作系统和设备驱动关系的认识. 一.设备驱动的作用 对设备驱动最通俗的解释就是“驱使硬件设备行动” .设 ...

随机推荐

  1. EOS开发实战

    EOS开发实战   在上一篇文章<EOS开发入门>中,我们为大家介绍了EOS的节点启动和合约部署和调用等入门知识.本次我们来实现一个复杂的例子,可以为其取一个高大上的名字-悬赏任务管理系统 ...

  2. web框架开发-Django用户认证组件

    可以用认证组件做什么 针对session的缺陷, 跟新数据时,不跟新key键, 用户认证组件是删除后再重建 用户认证组件很多功能可以直接使用 利用用户认证表(auth_user,通过Django自己创 ...

  3. Django-CRM项目学习(四)-stark的分页器与搜索框

    1.分页器 分页器相关知识点,请查看以下链接 https://www.cnblogs.com/gbq-dog/p/10724859.html 2.代码归类 归类前代码 header_list = [] ...

  4. P1546 最短网络 Agri-Net题解(克鲁斯卡尔)

    P1546 最短网络 Agri-Net 那么这个题是一道最小生成树的板子题 在此讲解kruskal克鲁斯卡尔方法: 原理: 并查集在这里被用到: 众所周知:树满足这样一个定理:如果 图 中有n个节点并 ...

  5. 通过C#学Proto.Actor模型》之Remote

    Proto.Actor中提供了基于tcp/ip的通迅来实现Remote,可以通过其Remot实现对Actor的调用. 先来看一个极简单片的远程调用. 码友看码: 引用NuGet包 Proto.Acto ...

  6. IDEA 创建包和类及基本操作

    创建包和类步骤如下: 1. 展开创建的工程,在源代码目录 src 上,鼠标右键,选择 new->package ,键入包名 com.itheima.demo ,点击确定. 2. 在创建好的包上, ...

  7. AtCoDeerくんと選挙速報 / AtCoDeer and Election Report AtCoder - 2140 (按比例扩大)

    Problem Statement AtCoDeer the deer is seeing a quick report of election results on TV. Two candidat ...

  8. c提高第三次作业

    1. char buf[] = "abcdef"; //下面有啥区别? const char *p = buf; //p指向的内存不能变 char const *p = buf; ...

  9. 解决SVN一直弹出登录问题,eclipse.tmatesoft.com

    Windows->preferences->Igonored Resources Add Pattern.. .project .classpath .settings 添加这三个: 把C ...

  10. Announcing the public preview of Azure Dev Spaces

    Today, we are excited to announce the public preview of Azure Dev Spaces, a cloud-native development ...