学习交流加
- 个人qq:
1126137994- 个人微信:
liu1126137994- 学习交流资源分享qq群:
962535112
今天来记录一下I.MX6开发板移植触摸屏驱动程序的过程分析。在移植驱动程序之前,为了学习,先去分析一下触摸屏驱动程序的框架。加qq1126137994 一起学习更多技术。
1、tsc2007.c触摸屏驱动程序的分析
I.MX6的触摸屏驱动程序的文件名为:TSC2007.C。
在tsc2007.c文件中初始化tsc2007_init函数注册tsc2007_driver结构体,当tsc2007_driver结构体成员驱动name与平台设备层中name匹配相同时调用tsc2007_probe初始化函数,该函数是tsc2007设备驱动函数的入口。imx6的具体tsc2007设备驱动的实现的主要工作在tsc2007_probe里完成。
下面是分析tsc2007_probe函数的调用关系(并非程序源码,只是列举出关键的函数调用):
tsc2007_probe
input_dev = input_allocate_device();//(向input核心层申请)分配一个input_dev结构体。
INIT_DELAYED_WORK;//初始化工作队列,把tsc2007工作任务注册到工作队列中,为下面执行队列
的任务作好准备。
tsc2007_work;//通过input_report_abs函数向上层上报触摸屏屏的X、Y坐标和压力值,
执行tsc2007_calculate_pressure函数进行压力校准,
init_platform_hw;//?
input_set_abs_params;//通过input_dev结构体设置按键和绝对位置事件以及事件下需要处理
的ABS_X、ABS_Y、ABS_PRESSURE的具体事件内容
request_irq(ts->irq, tsc2007_irq, 0,
client->dev.driver->name, ts);//为tsc2007中断引脚申请一个中断处理函数,
schedule_delayed_work;//中断的上半部分
tsc2007_work; //中断的下半部分,
tsc2007_read_values;//读取xy坐标 以及获取压力值
tc->y = tsc2007_xfer(tsc, READ_Y);
tc->x = tsc2007_xfer(tsc, READ_X);
tsc2007_xfer(tsc, PWRDOWN);//另一次循环测量
rt = tsc2007_calculate_pressure // 计算压力
input_report_abs(input, ABS_X, tc.x);
input_report_abs(input, ABS_Y, tc.y);
input_report_abs(input, ABS_PRESSURE, rt);//上报获取到的值
tsc2007_xfer;
data = i2c_smbus_read_word_data; //检测IIC总线的读写操作
val = swab16(data) >> 4; //去掉低4位无效数据(#define MAX_12BIT 由定义知最大为12bit,
所以需要去掉低4位)
return val;//tsc2007_xfer返回一个val值,就是xy坐标的值
input_register_device; //像input核心层注册input_dev结构体
arch\mips\boot\Elf2ecoff.c中有如下定义
#define swab16(x) \
(( \
(((x) & 0x00ffU) << 8) | \
(((x) & 0xff00U) >> 8) )) //将x的值的高8位和低8位互换
当按下触摸板后产生中断,中断里面调用延时函数进入底半部程序。在调度函数里面,先读取坐标,计算压力,然后根据压力大小上报坐标。若触摸板一直按下,那么就重新调用,并通过压力值来看触摸板是否释放。
2、tsc2007.c触摸屏驱动程序的移植
触摸屏驱动程序的核心部分分析完了,下面就开始移植触摸屏驱动程序,其实,内核自带的驱动程序移植起来非常简单,在理解驱动程序的前提下,在平台设备文件中添加一些私有数据信息,以及配置中断引脚等即可。
IMX6的板级初始化函数:Board-mx6q_sabresd.c
由与tsc2007与imx6之间的通信是通过IIC总线进行的,因此在imx6的平台设备层(platform_device)的mx6_sabresd_board_init函数里就需要配置与tsc2007有关的IIC总线。由于是TSC2007所挂的IIC总线是第二根,在平台初始化函数里将会调用i2c_register_board_info函数注册mxc_i2c2_board_info结构体数组。
向mxc_i2c2_board_info结构体数组里添加如下成员:
{
I2C_BOARD_INFO("tsc2007",0x48),
.platform_data = (void *)& tsc2007_data_lyy,
.irq = gpio_io_irq(SABRESD_TS_INT),
}
其中由于TSC2007的A0、A1两个引脚下拉,通过I2C_BOARD_INFO函数注册的从机设备地址是0x48;通过gpio_to_irq函数获取TSC2007中断号,之后会传给平台驱动层,当申请中断会用到;
再构建tsc2007_data_lyy结构体,并添加4个成员:init_platform_hw函数、get_pendown_state函数、irq_pin中断引脚的选择、触摸屏驱动相关参数。
/*
* 注意:一下添加的函数,位置没有固定要求,但是为了整齐统一,还是把它放到与其它设备定义的相通的位置,方便以后查看
*/
#ifdef CONFIG_TOUCHSCREEN_TSC2007
#include <linux/i2c/tsc2007.h>(在头文件里也添加一下)
static int tsc2007_hw_init(void)
{
int err;
err = gpio_request(SABRESD_TS_INT,"tsc2007 irq");
if(err<0)
{
pr_err("tsc2007 irq gpio request err\n");
return err;
}
err = gpio_direction_input(SABRESD_TS_INT);
if(err<0)
{
pr_err("tsc2007 irq gpio init input err %d\n",err);
gpio_free(SABRESD_TS_INT);
return err;
}
printk("tsc2007 irq gpio init success!\n");
return 0;
}
static void tsc2007_hw_remove(void)
{
gpio_free(SABRESD_TS_INT);
}
static int tsc2007_get_pendown_state(void)
{
int state;
state = gpio_get_value(SABRESD_TS_INT);
//printk("tsc2007 get state = %x\n",state);
return (state == 0)? 1:0;
}
static struct tsc2007_platform_data tsc2007_data_lyy= {
.model = 2007,
.x_plate_ohms =180,
.init_platform_hw = tsc2007_hw_init,
.exit_platform_hw = tsc2007_hw_remove,
.get_pendown_state = tsc2007_get_pendown_state,
};
#endif
更改中断引脚号:
将宏SABRESD_TS_INT改为:(之前是3,26)
#define SABRESD_TS_INT IMX_GPIO_NR(6, 7) //lyy (申请中断引脚)
注:在Linux启动的时候会将信息进行收集,i2c适配器会扫描已经静态注册的i2c_board_info,通过调用i2c_register_board_info函数将包含所有I2C设备的i2c_board_info信息的i2c_devinfo变量加入到__i2c_board_list链表中,并调用i2c_new_device为其实例化一个i2c_client。在驱动加载的时候遇到同名的i2c_board_info就会将i2c_client和driver绑定,并且执行driver的probe函数。这种方式一般放在平台的代码中。
i2c_register_board_info(2, mxc_i2c2_board_info,
ARRAY_SIZE(mxc_i2c2_board_info));
注释:
上面的工作,基本完成了驱动程序的移植,但是后来经过测试,发现显示有错误:
Failed to register i2c client tsc2007 at 0x48 (-16)
Can’t create device at 0x48
一开始以为是i2c总线没有识别到,但是经过调试,发现是中断引脚的复用了。
1、首先确定中断的引脚,查看原理图知使用的是:MX6Q_PAD_NANDF_CLE__GPIO_6_7,
在mach-mx6\Board-mx6q_sabresd.h 中加入定义:MX6Q_PAD_NANDF_CLE__GPIO_6_7,当然如果有其它设备用
了这个引脚,要将其注销
2、在板极文件board-mx6q_sabresd.c中注册和申请I2C驱动
首先申请中断的信号,并注释掉原来关于GPIO_6_7的引脚(不然无法识别tsc2007设备)
添加头文件:#define SABRESD_TS_INT IMX_GPIO_NR(6, 7) //lyy
注释掉之前的://#define SABRESD_CAP_TCH_INT1 IMX_GPIO_NR(6, 7)//之前的
OK!到此,程序添加成功,下一步就是校准触摸屏了!!!
3、触摸屏的校准
校准的目的:为了与显示屏的坐标一一对应。我们一般采用软件校准,使用Tslib库里的校准软件进行校准们首先需要移植Tslib库。
编译tslib(放到rootfs/tslib/tslib):
tar xzf tslib-1.4.tar.gz
cd tslib
./autogen.sh
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
我的这一部分是放到开发的环境中做的,其实可以直接在单板上搞,如果单板上是有编译器的话。
然后将系统烧写的单板,在单板上操作:
cd /tslib/tslib/tmp
cp * / -rfd
1.
修改 /etc/ts.conf第1行(去掉#号和第一个空格):
# module_raw input
改为:
module_raw input
2.
export TSLIB_TSDEVICE=/dev/input/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
测试:
ts_calibrate
ts_test
备注:如果出现段错误,解决办法如下:
1、ts.conf文件中的各个设置选项之前不能有空格,否则会出现: Segmentation fault 错误,
我就不小心在module…之前多了个空格,害我查了好久。
2、不要在pointercal对应的目录下,建立一个空的pointercal文件,否则在运行ts_calibrate时,
也可能会出现Segmentation fault
想一起探讨以及获得各种学习资源加我(有我博客中写的代码的原稿):
qq:1126137994
微信:liu1126137994
可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构等技术问题。