diff --git a/IMX6ULL/doc_pic/05_Input/03_输入系统应用编程.docx b/IMX6ULL/doc_pic/05_Input/03_输入系统应用编程.docx index 41b0986..b2f1d2f 100644 Binary files a/IMX6ULL/doc_pic/05_Input/03_输入系统应用编程.docx and b/IMX6ULL/doc_pic/05_Input/03_输入系统应用编程.docx differ diff --git a/IMX6ULL/doc_pic/05_Input/DRV_03_编写最简单的触摸屏驱动程序_基于QEMU.md b/IMX6ULL/doc_pic/05_Input/DRV_03_编写最简单的触摸屏驱动程序_基于QEMU.md new file mode 100644 index 0000000..6eae37e --- /dev/null +++ b/IMX6ULL/doc_pic/05_Input/DRV_03_编写最简单的触摸屏驱动程序_基于QEMU.md @@ -0,0 +1,254 @@ +## 编写最简单的触摸屏驱动程序_基于QEMU + +参考资料: + +* Linux 5.x内核文档 +* Documentation\input\input-programming.rst + + * Documentation\input\event-codes.rst +* Linux 4.x内核文档 + * Documentation\input\input-programming.txt + * Documentation\input\event-codes.txt + +* 本节视频代码:GIT仓库中 + + ```shell + IMX6ULL\source\05_Input\03_touchscreen_qemu\ + 01_irq_ok + 02_all_ok +STM32MP157\source\A7\05_Input\03_touchscreen_qemu\ + 01_irq_ok + 02_all_ok + ``` + + + +### 1. 写在前面的话 + +目前百问网主推的开发板是IMX6ULL、STM32MP157。 +但是也推出了一块虚拟的开发板:IMX6ULL_QEMU,对QEMU进行了大量的修改,比如增加了更多外设的模拟。 +使用QEMU的原因有3: + +* 降低学习成本 + * 初学阶段,可以不买开发板,使用QEMU即可入门。 +* 深入学习内核及驱动 + * 使用QEMU可以非常方便地调试内核、查看驱动程序执行过程 + * 有助于深入研究内核及驱动 +* 学习某些驱动时可以用QEMU模拟硬件,简化硬件的操作,把精力放在驱动程序框架本身 + +后面的视频里,会使用QEMU来讲解某些驱动程序。 +**注意**: + +* 使用QEMU不是必须的 +* QEMU只是提供另一个角度的学习方法,比如: + * LCD驱动:使用QEMU可以时,可以简化硬件的操作 + * 中断子系统:可以跟踪调用过程 +* 你可以只看QEMU相关的视频,不使用QEMU来操作 +* 在真实的开发板上讲解的内容,会覆盖QEMU视频的知识 + + + +### 2. 准备工作 + +在2021.03.27,我们给QEMU增加了新的功能:模拟触摸屏。 +如果你是在这个时间之前下载了百问网的QEMU映像,那么需要重新下载。 +下载、使用方法请参考:http://wiki.100ask.org/Qemu + +下面以Ubuntu 18.04简单介绍一下。 + +#### 2.1 下载 + +执行命令: + +```shell +git clone https://e.coding.net/weidongshan/ubuntu-18.04_imx6ul_qemu_system.git +``` + +#### 2.2 安装运行环境 + +执行命令: + +```shell +$ cd buntu-18.04_imx6ul_qemu_system +$./install_sdl.sh // 提示输入用户密码,等待安装完成 +``` + +#### 2.3 运行QEMU + +执行命令: + +```shell +$./qemu-imx6ull-gui.sh // 启动后,登录名是root,无需密码 +``` + + + +### 3. QEMU触摸屏操作方法 + +![image-20210327154310507](pic/05_Input/13_qemu_touchscreen.png) + +寄存器说明如下: + + +| 地址 | 寄存器 | 说明 | +| ---------- | ----------------------- | ------------------------------------------------------------ | +| 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中记录坐标 +* 仅能模拟单点触摸,不能模拟多点触摸 + +### 4. 编写驱动程序 + +* request_irq + +* 在中断处理函数里 + + * 上报按下、松开的事件 +* 如果触摸屏被按下,启动定时器 + +* 如果触摸屏被松开,取消定时器 + +* 在定时器函数里 + + * 如果触摸屏还是被按下的状态,上报坐标值,并设置下一个超时时间 + + + +### 5. 上机实验 + +#### 5.1 设置工具链 + +在Ubuntu中执行: + +```shell +export ARCH=arm +export CROSS_COMPILE=arm-linux-gnueabihf- +export PATH=$PATH:/home/book/100ask_imx6ull-qemu/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin +``` + +#### 5.2 编译内核/设备树 + +在Ubuntu中执行: + +```shell +book@100ask:~/100ask_imx6ull-qemu$ cd linux-4.9.88 +book@100ask:~/100ask_imx6ull-qemu/linux-4.9.88$ make mrproper +book@100ask:~/100ask_imx6ull-qemu/linux-4.9.88$ make 100ask_imx6ull_qemu_defconfig +book@100ask:~/100ask_imx6ull-qemu/linux-4.9.88$ make zImage -jN //编译zImage内核镜像,其中N参数可以根据CPU个数,来加速编译系统。 +book@100ask:~/100ask_imx6ull-qemu/linux-4.9.88$ make dtbs //编译设备树文件 +``` + +成功的话,可以得到: + +```shell +arch/arm/boot/zImage // 内核 +arch/arm/boot/dts/100ask_imx6ull_qemu.dtb // 设备树 +``` + +复制到如下目录: + +```shell +$ cd ubuntu-18.04_imx6ul_qemu_system/imx6ull-system-image +$ ls +100ask_imx6ull_qemu.dtb rootfs.img rootfs.tar.gz zImage +``` + + + +#### 5.3 启动QEMU + +在Ubuntu中执行: + +```shell +$ cd ubuntu-18.04_imx6ul_qemu_system +$ ./qemu-imx6ull-gui.sh +``` + + + +#### 5.4 挂载NFS + +在QEMU中执行: + +```shell +$ mount -t nfs -o nolock,vers=3 10.0.2.2:/home/book/nfs_rootfs /mnt +``` + +开启printk: + +```shell +echo "7 4 1 7" > /proc/sys/kernel/printk +``` + + + +#### 5.5 编译、使用tslib + +在Ubuntu上执行下列命令。 + +* 编译 + +```shell +tar xJf tslib-1.21.tar.xz +cd tslib-1.21 +./configure --host=arm-linux-gnueabihf --prefix=/ +make +make install DESTDIR=$PWD/tmp +``` + +* 复制头文件/库到工具链(非必须, 编译其他APP时需要) + +```shell +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。 + 在开发板上执行: + +```shell +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 +```shell +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 +``` + + +#### 5.5 退出QEMU + +```shell +要退出QEMU,可以同时按住ctrl+a,松开后再输入'x' +``` + diff --git a/IMX6ULL/doc_pic/05_Input/pic/05_Input/13_qemu_touchscreen.png b/IMX6ULL/doc_pic/05_Input/pic/05_Input/13_qemu_touchscreen.png new file mode 100644 index 0000000..2ed0baa Binary files /dev/null and b/IMX6ULL/doc_pic/05_Input/pic/05_Input/13_qemu_touchscreen.png differ diff --git a/IMX6ULL/source/05_Input/03_touchscreen_qemu/01_irq_ok/Makefile b/IMX6ULL/source/05_Input/03_touchscreen_qemu/01_irq_ok/Makefile new file mode 100644 index 0000000..70e6b17 --- /dev/null +++ b/IMX6ULL/source/05_Input/03_touchscreen_qemu/01_irq_ok/Makefile @@ -0,0 +1,20 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_imx6ull-qemu/linux-4.9.88 + +all: + make -C $(KERN_DIR) M=`pwd` modules + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + +obj-m += touchscreen_qemu.o + diff --git a/IMX6ULL/source/05_Input/03_touchscreen_qemu/01_irq_ok/touchscreen_qemu.c b/IMX6ULL/source/05_Input/03_touchscreen_qemu/01_irq_ok/touchscreen_qemu.c new file mode 100644 index 0000000..f4436b0 --- /dev/null +++ b/IMX6ULL/source/05_Input/03_touchscreen_qemu/01_irq_ok/touchscreen_qemu.c @@ -0,0 +1,129 @@ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct input_dev *g_input_dev; +static int g_irq; +static irqreturn_t input_dev_demo_isr(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__); + + return IRQ_HANDLED; +} + + +/* alloc/set/register platform_driver */ +static int input_dev_demo_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int error; + //struct resource *irq; + int gpio; + + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + + gpio = of_get_gpio(pdev->dev.of_node, 0); + + /* get hardware info from device tree */ + + /* alloc/set/register input_dev */ + g_input_dev = devm_input_allocate_device(dev); + + 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 = 0x0100; + + /* set 1: which type event ? */ + __set_bit(EV_KEY, g_input_dev->evbit); + __set_bit(EV_ABS, g_input_dev->evbit); + + /* set 2: which event ? */ + __set_bit(BTN_TOUCH, g_input_dev->keybit); + __set_bit(ABS_MT_SLOT, g_input_dev->absbit); + __set_bit(ABS_MT_POSITION_X, g_input_dev->absbit); + __set_bit(ABS_MT_POSITION_Y, g_input_dev->absbit); + + /* set 3: event params ? */ + input_set_abs_params(g_input_dev, ABS_MT_POSITION_X, 0, 0xffff, 0, 0); + input_set_abs_params(g_input_dev, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0); + + error = input_register_device(g_input_dev); + + /* hardware opration */ + //irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + //g_irq = irq->start; + g_irq = gpio_to_irq(gpio); + error = request_irq(g_irq, input_dev_demo_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "input_dev_demo_irq", NULL); + + return 0; +} + +static int input_dev_demo_remove(struct platform_device *pdev) +{ + free_irq(g_irq, NULL); + input_unregister_device(g_input_dev); + return 0; +} + +static const 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) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + 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"); + + diff --git a/IMX6ULL/source/05_Input/03_touchscreen_qemu/01_irq_ok/touchscreen_qemu.dts b/IMX6ULL/source/05_Input/03_touchscreen_qemu/01_irq_ok/touchscreen_qemu.dts new file mode 100644 index 0000000..ec60a43 --- /dev/null +++ b/IMX6ULL/source/05_Input/03_touchscreen_qemu/01_irq_ok/touchscreen_qemu.dts @@ -0,0 +1,9 @@ +/ { + input_dev_demo { + compatible = "100ask,input_dev_demo"; + reg = <0x021B4000 16>; + //interrupt-parent = <&gpio1>; + //interrupts = <5 IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING>; + gpios = <&gpio1 5 1>; + }; +}; diff --git a/IMX6ULL/source/05_Input/03_touchscreen_qemu/02_all_ok/Makefile b/IMX6ULL/source/05_Input/03_touchscreen_qemu/02_all_ok/Makefile new file mode 100644 index 0000000..70e6b17 --- /dev/null +++ b/IMX6ULL/source/05_Input/03_touchscreen_qemu/02_all_ok/Makefile @@ -0,0 +1,20 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_imx6ull-qemu/linux-4.9.88 + +all: + make -C $(KERN_DIR) M=`pwd` modules + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + +obj-m += touchscreen_qemu.o + diff --git a/IMX6ULL/source/05_Input/03_touchscreen_qemu/02_all_ok/touchscreen_qemu.c b/IMX6ULL/source/05_Input/03_touchscreen_qemu/02_all_ok/touchscreen_qemu.c new file mode 100644 index 0000000..a0cb081 --- /dev/null +++ b/IMX6ULL/source/05_Input/03_touchscreen_qemu/02_all_ok/touchscreen_qemu.c @@ -0,0 +1,182 @@ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) // pressed + { + 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_isr(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); + + /* start timer */ + 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); + + /* cancel timer */ + } + + return IRQ_HANDLED; +} + + +/* alloc/set/register platform_driver */ +static int input_dev_demo_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int error; + //struct resource *irq; + struct resource *io; + int gpio; + + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + + gpio = of_get_gpio(pdev->dev.of_node, 0); + + /* get hardware info from device tree */ + + /* alloc/set/register input_dev */ + g_input_dev = devm_input_allocate_device(dev); + + 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 = 0x0100; + + /* set 1: which type event ? */ + __set_bit(EV_KEY, g_input_dev->evbit); + __set_bit(EV_ABS, g_input_dev->evbit); + + /* 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); + input_set_abs_params(g_input_dev, ABS_Y, 0, 0xffff, 0, 0); + + error = input_register_device(g_input_dev); + + /* hardware opration */ + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ts_con = ioremap(io->start, io->end - io->start + 1); + + + //irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + //g_irq = irq->start; + g_irq = gpio_to_irq(gpio); + error = request_irq(g_irq, input_dev_demo_isr, 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); + free_irq(g_irq, NULL); + input_unregister_device(g_input_dev); + return 0; +} + +static const 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) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + 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"); + + diff --git a/IMX6ULL/source/05_Input/03_touchscreen_qemu/02_all_ok/touchscreen_qemu.dts b/IMX6ULL/source/05_Input/03_touchscreen_qemu/02_all_ok/touchscreen_qemu.dts new file mode 100644 index 0000000..ec60a43 --- /dev/null +++ b/IMX6ULL/source/05_Input/03_touchscreen_qemu/02_all_ok/touchscreen_qemu.dts @@ -0,0 +1,9 @@ +/ { + input_dev_demo { + compatible = "100ask,input_dev_demo"; + reg = <0x021B4000 16>; + //interrupt-parent = <&gpio1>; + //interrupts = <5 IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING>; + gpios = <&gpio1 5 1>; + }; +}; diff --git a/IMX6ULL/source/05_Input/03_touchscreen_qemu/tslib-1.21.tar.xz b/IMX6ULL/source/05_Input/03_touchscreen_qemu/tslib-1.21.tar.xz new file mode 100644 index 0000000..30f1450 Binary files /dev/null and b/IMX6ULL/source/05_Input/03_touchscreen_qemu/tslib-1.21.tar.xz differ diff --git a/README.md b/README.md index 44a6ed9..d998691 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,15 @@ git clone https://e.coding.net/weidongshan/doc_and_source_for_drivers.git * 2021.03.26 发布"Input子系统":DRV_02_编写input_dev驱动框架 +* 2021.03.29 发布"Input子系统": + + ```shell + DRV_03_编写最简单的触摸屏驱动程序之IRQ_基于QEMU + DRV_04_编写最简单的触摸屏驱动程序之完善_基于QEMU + ``` + + + diff --git a/STM32MP157/doc_pic/05_Input/03_输入系统应用编程.docx b/STM32MP157/doc_pic/05_Input/03_输入系统应用编程.docx index 41b0986..b2f1d2f 100644 Binary files a/STM32MP157/doc_pic/05_Input/03_输入系统应用编程.docx and b/STM32MP157/doc_pic/05_Input/03_输入系统应用编程.docx differ diff --git a/STM32MP157/doc_pic/05_Input/DRV_03_编写最简单的触摸屏驱动程序_基于QEMU.md b/STM32MP157/doc_pic/05_Input/DRV_03_编写最简单的触摸屏驱动程序_基于QEMU.md new file mode 100644 index 0000000..6eae37e --- /dev/null +++ b/STM32MP157/doc_pic/05_Input/DRV_03_编写最简单的触摸屏驱动程序_基于QEMU.md @@ -0,0 +1,254 @@ +## 编写最简单的触摸屏驱动程序_基于QEMU + +参考资料: + +* Linux 5.x内核文档 +* Documentation\input\input-programming.rst + + * Documentation\input\event-codes.rst +* Linux 4.x内核文档 + * Documentation\input\input-programming.txt + * Documentation\input\event-codes.txt + +* 本节视频代码:GIT仓库中 + + ```shell + IMX6ULL\source\05_Input\03_touchscreen_qemu\ + 01_irq_ok + 02_all_ok +STM32MP157\source\A7\05_Input\03_touchscreen_qemu\ + 01_irq_ok + 02_all_ok + ``` + + + +### 1. 写在前面的话 + +目前百问网主推的开发板是IMX6ULL、STM32MP157。 +但是也推出了一块虚拟的开发板:IMX6ULL_QEMU,对QEMU进行了大量的修改,比如增加了更多外设的模拟。 +使用QEMU的原因有3: + +* 降低学习成本 + * 初学阶段,可以不买开发板,使用QEMU即可入门。 +* 深入学习内核及驱动 + * 使用QEMU可以非常方便地调试内核、查看驱动程序执行过程 + * 有助于深入研究内核及驱动 +* 学习某些驱动时可以用QEMU模拟硬件,简化硬件的操作,把精力放在驱动程序框架本身 + +后面的视频里,会使用QEMU来讲解某些驱动程序。 +**注意**: + +* 使用QEMU不是必须的 +* QEMU只是提供另一个角度的学习方法,比如: + * LCD驱动:使用QEMU可以时,可以简化硬件的操作 + * 中断子系统:可以跟踪调用过程 +* 你可以只看QEMU相关的视频,不使用QEMU来操作 +* 在真实的开发板上讲解的内容,会覆盖QEMU视频的知识 + + + +### 2. 准备工作 + +在2021.03.27,我们给QEMU增加了新的功能:模拟触摸屏。 +如果你是在这个时间之前下载了百问网的QEMU映像,那么需要重新下载。 +下载、使用方法请参考:http://wiki.100ask.org/Qemu + +下面以Ubuntu 18.04简单介绍一下。 + +#### 2.1 下载 + +执行命令: + +```shell +git clone https://e.coding.net/weidongshan/ubuntu-18.04_imx6ul_qemu_system.git +``` + +#### 2.2 安装运行环境 + +执行命令: + +```shell +$ cd buntu-18.04_imx6ul_qemu_system +$./install_sdl.sh // 提示输入用户密码,等待安装完成 +``` + +#### 2.3 运行QEMU + +执行命令: + +```shell +$./qemu-imx6ull-gui.sh // 启动后,登录名是root,无需密码 +``` + + + +### 3. QEMU触摸屏操作方法 + +![image-20210327154310507](pic/05_Input/13_qemu_touchscreen.png) + +寄存器说明如下: + + +| 地址 | 寄存器 | 说明 | +| ---------- | ----------------------- | ------------------------------------------------------------ | +| 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中记录坐标 +* 仅能模拟单点触摸,不能模拟多点触摸 + +### 4. 编写驱动程序 + +* request_irq + +* 在中断处理函数里 + + * 上报按下、松开的事件 +* 如果触摸屏被按下,启动定时器 + +* 如果触摸屏被松开,取消定时器 + +* 在定时器函数里 + + * 如果触摸屏还是被按下的状态,上报坐标值,并设置下一个超时时间 + + + +### 5. 上机实验 + +#### 5.1 设置工具链 + +在Ubuntu中执行: + +```shell +export ARCH=arm +export CROSS_COMPILE=arm-linux-gnueabihf- +export PATH=$PATH:/home/book/100ask_imx6ull-qemu/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin +``` + +#### 5.2 编译内核/设备树 + +在Ubuntu中执行: + +```shell +book@100ask:~/100ask_imx6ull-qemu$ cd linux-4.9.88 +book@100ask:~/100ask_imx6ull-qemu/linux-4.9.88$ make mrproper +book@100ask:~/100ask_imx6ull-qemu/linux-4.9.88$ make 100ask_imx6ull_qemu_defconfig +book@100ask:~/100ask_imx6ull-qemu/linux-4.9.88$ make zImage -jN //编译zImage内核镜像,其中N参数可以根据CPU个数,来加速编译系统。 +book@100ask:~/100ask_imx6ull-qemu/linux-4.9.88$ make dtbs //编译设备树文件 +``` + +成功的话,可以得到: + +```shell +arch/arm/boot/zImage // 内核 +arch/arm/boot/dts/100ask_imx6ull_qemu.dtb // 设备树 +``` + +复制到如下目录: + +```shell +$ cd ubuntu-18.04_imx6ul_qemu_system/imx6ull-system-image +$ ls +100ask_imx6ull_qemu.dtb rootfs.img rootfs.tar.gz zImage +``` + + + +#### 5.3 启动QEMU + +在Ubuntu中执行: + +```shell +$ cd ubuntu-18.04_imx6ul_qemu_system +$ ./qemu-imx6ull-gui.sh +``` + + + +#### 5.4 挂载NFS + +在QEMU中执行: + +```shell +$ mount -t nfs -o nolock,vers=3 10.0.2.2:/home/book/nfs_rootfs /mnt +``` + +开启printk: + +```shell +echo "7 4 1 7" > /proc/sys/kernel/printk +``` + + + +#### 5.5 编译、使用tslib + +在Ubuntu上执行下列命令。 + +* 编译 + +```shell +tar xJf tslib-1.21.tar.xz +cd tslib-1.21 +./configure --host=arm-linux-gnueabihf --prefix=/ +make +make install DESTDIR=$PWD/tmp +``` + +* 复制头文件/库到工具链(非必须, 编译其他APP时需要) + +```shell +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。 + 在开发板上执行: + +```shell +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 +```shell +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 +``` + + +#### 5.5 退出QEMU + +```shell +要退出QEMU,可以同时按住ctrl+a,松开后再输入'x' +``` + diff --git a/STM32MP157/doc_pic/05_Input/pic/05_Input/13_qemu_touchscreen.png b/STM32MP157/doc_pic/05_Input/pic/05_Input/13_qemu_touchscreen.png new file mode 100644 index 0000000..2ed0baa Binary files /dev/null and b/STM32MP157/doc_pic/05_Input/pic/05_Input/13_qemu_touchscreen.png differ diff --git a/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/01_irq_ok/Makefile b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/01_irq_ok/Makefile new file mode 100644 index 0000000..70e6b17 --- /dev/null +++ b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/01_irq_ok/Makefile @@ -0,0 +1,20 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_imx6ull-qemu/linux-4.9.88 + +all: + make -C $(KERN_DIR) M=`pwd` modules + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + +obj-m += touchscreen_qemu.o + diff --git a/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/01_irq_ok/touchscreen_qemu.c b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/01_irq_ok/touchscreen_qemu.c new file mode 100644 index 0000000..f4436b0 --- /dev/null +++ b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/01_irq_ok/touchscreen_qemu.c @@ -0,0 +1,129 @@ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct input_dev *g_input_dev; +static int g_irq; +static irqreturn_t input_dev_demo_isr(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__); + + return IRQ_HANDLED; +} + + +/* alloc/set/register platform_driver */ +static int input_dev_demo_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int error; + //struct resource *irq; + int gpio; + + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + + gpio = of_get_gpio(pdev->dev.of_node, 0); + + /* get hardware info from device tree */ + + /* alloc/set/register input_dev */ + g_input_dev = devm_input_allocate_device(dev); + + 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 = 0x0100; + + /* set 1: which type event ? */ + __set_bit(EV_KEY, g_input_dev->evbit); + __set_bit(EV_ABS, g_input_dev->evbit); + + /* set 2: which event ? */ + __set_bit(BTN_TOUCH, g_input_dev->keybit); + __set_bit(ABS_MT_SLOT, g_input_dev->absbit); + __set_bit(ABS_MT_POSITION_X, g_input_dev->absbit); + __set_bit(ABS_MT_POSITION_Y, g_input_dev->absbit); + + /* set 3: event params ? */ + input_set_abs_params(g_input_dev, ABS_MT_POSITION_X, 0, 0xffff, 0, 0); + input_set_abs_params(g_input_dev, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0); + + error = input_register_device(g_input_dev); + + /* hardware opration */ + //irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + //g_irq = irq->start; + g_irq = gpio_to_irq(gpio); + error = request_irq(g_irq, input_dev_demo_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "input_dev_demo_irq", NULL); + + return 0; +} + +static int input_dev_demo_remove(struct platform_device *pdev) +{ + free_irq(g_irq, NULL); + input_unregister_device(g_input_dev); + return 0; +} + +static const 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) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + 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"); + + diff --git a/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/01_irq_ok/touchscreen_qemu.dts b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/01_irq_ok/touchscreen_qemu.dts new file mode 100644 index 0000000..ec60a43 --- /dev/null +++ b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/01_irq_ok/touchscreen_qemu.dts @@ -0,0 +1,9 @@ +/ { + input_dev_demo { + compatible = "100ask,input_dev_demo"; + reg = <0x021B4000 16>; + //interrupt-parent = <&gpio1>; + //interrupts = <5 IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING>; + gpios = <&gpio1 5 1>; + }; +}; diff --git a/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/02_all_ok/Makefile b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/02_all_ok/Makefile new file mode 100644 index 0000000..70e6b17 --- /dev/null +++ b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/02_all_ok/Makefile @@ -0,0 +1,20 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_imx6ull-qemu/linux-4.9.88 + +all: + make -C $(KERN_DIR) M=`pwd` modules + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + +obj-m += touchscreen_qemu.o + diff --git a/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/02_all_ok/touchscreen_qemu.c b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/02_all_ok/touchscreen_qemu.c new file mode 100644 index 0000000..a0cb081 --- /dev/null +++ b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/02_all_ok/touchscreen_qemu.c @@ -0,0 +1,182 @@ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) // pressed + { + 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_isr(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); + + /* start timer */ + 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); + + /* cancel timer */ + } + + return IRQ_HANDLED; +} + + +/* alloc/set/register platform_driver */ +static int input_dev_demo_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int error; + //struct resource *irq; + struct resource *io; + int gpio; + + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + + gpio = of_get_gpio(pdev->dev.of_node, 0); + + /* get hardware info from device tree */ + + /* alloc/set/register input_dev */ + g_input_dev = devm_input_allocate_device(dev); + + 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 = 0x0100; + + /* set 1: which type event ? */ + __set_bit(EV_KEY, g_input_dev->evbit); + __set_bit(EV_ABS, g_input_dev->evbit); + + /* 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); + input_set_abs_params(g_input_dev, ABS_Y, 0, 0xffff, 0, 0); + + error = input_register_device(g_input_dev); + + /* hardware opration */ + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ts_con = ioremap(io->start, io->end - io->start + 1); + + + //irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + //g_irq = irq->start; + g_irq = gpio_to_irq(gpio); + error = request_irq(g_irq, input_dev_demo_isr, 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); + free_irq(g_irq, NULL); + input_unregister_device(g_input_dev); + return 0; +} + +static const 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) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + 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"); + + diff --git a/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/02_all_ok/touchscreen_qemu.dts b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/02_all_ok/touchscreen_qemu.dts new file mode 100644 index 0000000..ec60a43 --- /dev/null +++ b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/02_all_ok/touchscreen_qemu.dts @@ -0,0 +1,9 @@ +/ { + input_dev_demo { + compatible = "100ask,input_dev_demo"; + reg = <0x021B4000 16>; + //interrupt-parent = <&gpio1>; + //interrupts = <5 IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING>; + gpios = <&gpio1 5 1>; + }; +}; diff --git a/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/tslib-1.21.tar.xz b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/tslib-1.21.tar.xz new file mode 100644 index 0000000..30f1450 Binary files /dev/null and b/STM32MP157/source/A7/05_Input/03_touchscreen_qemu/tslib-1.21.tar.xz differ