参考资料
Documentation/input/input-programming.txt
Documentation/input/event-codes.txt
一、准备QEMU
启动虚拟设备:./qemu-imx6ull-gui.sh
在ubuntu-18.04_imx6ul_qemu_system/imx6ull-system-image
目录下修改设备树和内核
二、QEMU触摸屏操作方法
2.1 原理:
- 鼠标在屏幕上按下、松开:
- QEMU改变GPIO电平、产生中断
- 在touch_pressure_register中记录压力值
- 鼠标在屏幕上滑动
- 在touch_x_register、touch_y_register中记录坐标
2.2 寄存器说明
地址 | 寄存器 | 说明 |
---|---|---|
0x021B4000 | touch_pressure_register | 记录触摸屏压力值,只有0、1两个取值。1表示按下,0表示松开 |
0x021B4004 | touch_x_register | 记录触摸屏X坐标 |
0x021B4008 | touch_y_register | 记录触摸屏Y坐标 |
0x021B400C | touch_clean_register | 写入任意值,就会清零上述3个寄存器(仅用于测试) |
鼠标在屏幕上按下、松开:
- QEMU改变GPIO电平、产生中断
- 在touch_pressure_register中记录压力值
鼠标在屏幕上滑动 - 在touch_x_register、touch_y_register中记录坐标
仅能模拟单点触摸,不能模拟多点触摸
三、编写驱动程序
#include <linux/module.h>
#include <linux/init.h>
#include <linux/irqreturn.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#define TOUCHSCREEN_POLL_TIME_MS 10
struct qemu_ts_con {
volatile unsigned int pressure;
volatile unsigned int x;
volatile unsigned int y;
volatile unsigned int clean;
};
static struct input_dev *g_input_dev;
static int g_irq;
static struct qemu_ts_con *ts_con;
struct timer_list ts_timer;
static void ts_irq_timer(unsigned long _data)
{
if(ts_con->pressure) {
input_event(g_input_dev, EV_ABS, ABS_X, ts_con->x);
input_event(g_input_dev, EV_ABS, ABS_Y, ts_con->y);
input_sync(g_input_dev);
mod_timer(&ts_timer, jiffies + msecs_to_jiffies(TOUCHSCREEN_POLL_TIME_MS));
}
}
static irqreturn_t input_dev_demo_irq(int irq, void *dev_id)
{
/* read data */
/* report data */
//input_event(g_input_dev, EV_KEY, XX, 0);
//input_sync(g_input_dev);
//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
if(ts_con->pressure) {
input_event(g_input_dev, EV_ABS, ABS_X, ts_con->x);
input_event(g_input_dev, EV_ABS, ABS_Y, ts_con->y);
input_event(g_input_dev, EV_KEY, BTN_TOUCH, 1);
input_sync(g_input_dev);
mod_timer(&ts_timer,
jiffies + msecs_to_jiffies(TOUCHSCREEN_POLL_TIME_MS)); //更新中断时间
} else {
input_event(g_input_dev, EV_KEY, BTN_TOUCH, 0);
input_sync(g_input_dev);
}
return IRQ_HANDLED;
}
static int input_dev_demo_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int error = 0;
//struct resource *irq;
struct resource *io;
int gpio;
/* get hardware info from device tree */
gpio = of_get_gpio(pdev->dev.of_node, 0);
if(!gpio) {
printk(KERN_ERR"of_get_gpio error\n");
return -1;
}
/* alloc/set/register input_dev */
g_input_dev = devm_input_allocate_device(dev);
if(!g_input_dev) {
printk(KERN_ERR"devm_input_allocate_device error\n");
return -1;
}
g_input_dev->name = "input_dev_demo";
g_input_dev->phys = "input_dev_demo";
g_input_dev->dev.parent = dev;
g_input_dev->id.bustype = BUS_HOST;
g_input_dev->id.vendor = 0x0001;
g_input_dev->id.product = 0x0001;
g_input_dev->id.version = 0x0001;
/* set 1: which type event ? */
__set_bit(EV_KEY, g_input_dev->evbit);
__set_bit(EV_ABS, g_input_dev->evbit);
__set_bit(INPUT_PROP_DIRECT, g_input_dev->propbit); //设备触摸设备标志位,可以让tslib识别到
/* set 2: which event? */
__set_bit(BTN_TOUCH, g_input_dev->keybit);
__set_bit(ABS_X, g_input_dev->absbit);
__set_bit(ABS_Y, g_input_dev->absbit);
/* set 3: event params ? */
input_set_abs_params(g_input_dev, ABS_X, 0, 0xffff, 0, 0); //设置X和Y的范围,起始值
input_set_abs_params(g_input_dev, ABS_Y, 0, 0xffff, 0, 0);
error = input_register_device(g_input_dev);
/* hardware operation */
io = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取IO资源
ts_con = ioremap(io->start, io->end - io->start);
g_irq = gpio_to_irq(gpio); //获取中断号
error = request_irq(g_irq, input_dev_demo_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "input_dev_demo_irq", NULL);
setup_timer(&ts_timer, ts_irq_timer, (unsigned long)NULL); //初始化定时器
return 0;
}
static int input_dev_demo_remove(struct platform_device *pdev)
{
del_timer_sync(&ts_timer); //删除定时器
iounmap(ts_con); //释放io资源
free_irq(g_irq, NULL);
input_unregister_device(g_input_dev);
return 0;
}
static struct of_device_id input_dev_demo_of_match[] = {
{ .compatible = "100ask,input_dev_demo" },
{ },
};
static struct platform_driver input_dev_demo_driver = {
.probe = input_dev_demo_probe,
.remove = input_dev_demo_remove,
.driver = {
.name = "input_dev_demo",
.of_match_table = input_dev_demo_of_match,
}
};
static int __init input_dev_demo_init(void)
{
return platform_driver_register(&input_dev_demo_driver);
}
static void __exit input_dev_demo_exit(void)
{
platform_driver_unregister(&input_dev_demo_driver);
}
module_init(input_dev_demo_init);
module_exit(input_dev_demo_exit);
MODULE_LICENSE("GPL");
四、上机实验
[root@qemu_imx6ul:/mnt/home/picture/input]# hexdump /dev/input/event3
0000000 051f 0000 c466 0008 0003 0000 23f5 0000
0000010 051f 0000 c466 0008 0003 0001 3698 0000
0000020 051f 0000 c466 0008 0001 014a 0001 0000
0000030 051f 0000 c466 0008 0000 0000 0000 0000
0000040 0520 0000 dc31 0009 0001 014a 0000 0000
0000050 0520 0000 dc31 0009 0000 0000 0000 0000
0000060 0528 0000 acf5 0008 0003 0000 2289 0000
五、使用tslib支持触摸
在Ubuntu上执行下列命令。
- 编译
tar xJf tslib-1.21.tar.xz
cd tslib-1.21
./configure --host=arm-linux-gnueabihf --prefix=/
make
make install DESTDIR=$PWD/tmp
- 复制头文件/库到工具链(非必须, 编译其他APP时需要)
cd tslib-1.21/tmp/
cp include/* /home/book/100ask_imx6ull-qemu/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/6.2.1/../../../../arm-linux-gnueabihf/include
cp -d lib/*so* /home/book/100ask_imx6ull-qemu/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/
-
复制库、APP到开发板
假设在Ubuntu的/home/book/nfs_rootfs目录下有tslib-1.21。
在开发板上执行:
mount -t nfs -o nolock,vers=3 10.0.2.2:/home/book/nfs_rootfs /mnt
cp /mnt/tslib-1.21/tmp/lib/* -drf /lib
cp /mnt/tslib-1.21/tmp/bin/* /bin
cp /mnt/tslib-1.21/tmp/etc/ts.conf -d /etc
- 使用tslib
export TSLIB_TSDEVICE=/dev/input/event3
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