Linux输入子系统在触摸屏驱动上的实现
Linux输入子系统:事件的编码
Linux输入子系统:事件的编码输入系统协议用类型types和编码codecs来表示输入设备的值并用此来通知用户空间的应用程序。
这篇文档对这些类型和编码进行了说明并且指出什么时候和如何使用这些类型和编码。
一个单一的硬件事件可以产生多个输入事件,每个输入事件包含一个单一数据项的新的数据值。
EV_SYN是一个特别的事件类型,它用来把同一时刻产生的多个输入数据分割为多个数据包。
在下面的描述中,术语事件(event)是指一个涵盖类型,编码和参数值的单一输入事件。
input协议是一个基于状态的协议,只有当相应事件编码对应的参数值发生变化时才会发送该事件。
不过,状态是由Linux的输入子系统进行维护,驱动程序无需维护输入的状态,就算参数值没有变化时向输入子系统发出事件也不会有问题。
用户空间可以用linux/input.h 中定义的EVIOCG*ioctls来获得当前事件编码和参数的状态。
设备的所支持的上报事件种类也可以通过sysfs的class/input/event*/device/capabilities/来获取,设备的特性和可以通过class/input/event*/device/properties来获取。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 本文由DroidPhone 翻译:/droidphone Kernel版本:V3.4.10~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Event types:===========types对应于一个相同逻辑输入结构的一组Codes。
每个type都有一组可用的codes用于产生输入事件。
每个type可用的codes的详细信息请参考Codes一节的内容。
Linux内核i.MX51的触摸屏驱动架构
获取校准 3 个点并列方程组 1 得如下:
X A ( X ) B (Y ) C X D1 A ( X T1) B (Y T1) C X D2 A ( X T 2) B (Y T 2) C Y D0 D ( X T 0) E (Y T 0) F
D0 T0 T0
MX51 的触摸屏驱动架构
2012 年 11 月 27 日
※ 任务目标:
分析整理四线电阻屏工作原理,校屏算法及触摸屏驱动架构;
※ 四线电阻屏工作原理:
电阻屏由 x 层和 y 层组成,中间由细小的绝缘层隔开。当触摸屏没有压力时 x 层,y 层 处于断开状态;当有压力时 x 层和 y 层导通,通过 x 层的探针可以侦测出 y 层的电压,通过 y 层的探针可以侦测出 x 层的电压,而通过电压值可以确定触摸点在 x 层和 y 层的位置,从 而得到触摸点在触摸屏上的位置。
D0
A (X
X D 2)(Y T 1 Y T 2) ( X D1 X D 2)(Y T 0 Y T 2)
3/4
B (X C Y
' T0
'
T0
X T 2)( X D1 X D 2) ( X D0 X D 2)( X T 1 X T 2)
(( X T 2)( X D1) ( X T 1)( X D 2)) Y Tቤተ መጻሕፍቲ ባይዱ1 (( X T 0)( X D 2) ( X T 2)( X D0)) Y T 2 (( X T 1)( X D0) ( X T 0)( X D1))
..........................方程组 1
Y Y
D1
D2
S3C2410触摸屏驱动程序原理图
S3C2410触摸屏驱动程序原理图本文介绍了基于三星S3C2410X微处理器,采用SPI接口与ADS7843触摸屏控制器芯片完成触摸屏模块的设计。
具体包括在嵌入式Linux操作系统中的软件驱动开发,采用内核定时器的下半部机制进行了触摸屏硬件中断程序设计,采用16个时钟周期的坐标转换时序,实现触摸点数据采集的方法,给出了坐标采集的流程。
设计完成的触摸屏驱动程序在博创公司教学实验设备UP-NETARM2410-S平台上运行效果良好。
引言随着信息家电和通讯设备的普及,作为与用户交互的终端媒介,触摸屏在生活中得到广泛的应用。
如何在系统中集成触摸屏模块以及在嵌入式操作系统中实现其驱动程序,都成为嵌入式系统设计者需要考虑的问题。
本文主要介绍在三星S3C2410X微处理器的硬件平台上进行基于嵌入式Linux的触摸屏驱动程序设计。
硬件实现方案SPI接口是Motorola推出的一种同步串行接口,采用全双工、四线通信系统,S3C2410X是三星推出的自带触摸屏接口的ARM920T内核芯片,ADS7843为Burr-Brown生产的一款性能优异的触摸屏控制器。
本文采用SPI接口的触摸屏控制器ADS7843外接四线电阻式触摸屏,这种方式最显著的特点是响应速度更快、灵敏度更高,微处理器与触摸屏控制器间的通讯时间大大减少,提高了微处理器的效率。
ADS7843与S3C2410的硬件连接如图1所示,鉴于ADS7843差分工作模式的优点,在硬件电路中将其配置为差分模式。
图1触摸屏输入系统示意图嵌入式Linux系统下的驱动程序设备驱动程序是Linux内核的重要组成部分,控制了操作系统和硬件设备之间的交互。
Linux 的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,成为设备文件。
应用程序可以打开、关闭、读写这些设备文件,对设备的操作就像操作普通的数据文件一样简便。
为开发便利、提高效率,本设计采用可安装模块方式开发调试触摸屏驱动程序。
linux iio子系统介绍
LINUX工业I/O 子系统翻译:tekkamanninjaIIO概述●工业I/O子系统旨在提供那些在某种意义上作为模数转换器(ADC)的设备支持。
●属于这类型的设备如下:o ADCs(模数转换器)o加速度传感器o陀螺仪o IMUs(惯性测量单位)o电容-数字转换器(CDCs)o压力、温度和光线传感器。
●此子系统的目的在于填补那些分类时处在hwmon(硬件监视器)和输入子系统之间的设备类型。
●hwmon(硬件监视器)直接使用低采样率的传感器,应用于(散热)风扇速度控制和温度测量。
●输入子系统,顾名思义,它是专注于(人机交互)输入设备的:o键盘o鼠标o触摸屏o操纵杆●在某些情况下,这些外设和IIO之间的相当大的重叠。
●典型的(IIO类)设备是通过SPI或I2C连接的。
●尽管典型的DMA操作设备,例如与高速同步串口(McBSP, SPORT) 或高速同步并口(EPI, PPI) 连接的外设也可以属于这个子系统。
●因为后来的非SPI或I2C总线,一般不通过Linux总线驱动来抽象,而是属于处理器平台相关的实现。
•IIO子系统概述IIO(框架)的功能●基本的设备注册和处理●通过sysfs中简单的polled访问设备通道。
●(基于)事件的chrdevs(字符设备)o这些特性和输入子系统非常类似:都为硬件触发的事件提供一个到用户空间的通路。
这些事件包括门限检测、自由落体检测和更加复杂的动作检测。
这些事件本身一般是非常简单的,只是一个事件代码和一个时间戳。
任何与事件关联的数据必须可以通过轮询来访问。
注意:一个给定的设备可能有一个或者多个事件通道。
这些事件(的触发)可以通过sysfs接口来开启和关闭(如果可能)。
●硬件缓冲支持●一些新近的传感器在片内集成了fifo(先入先出)/ ring(环形)缓冲区。
●他们极大地减轻了宿主CPU基于其内部采用时钟来缓存相关大量采样数据上的负担。
●每个环形缓冲通常有一个事件chrdev(字符设备)(与上述普遍的设备类似) 来传递事件(例如缓冲已经填满50% )和一个可访问的chrdev(字符设备)来读回这些缓冲中的裸数据。
文档:、linux-308内核I2C触摸屏移植
Linux3.0.8平台搭建移植文档——I2C触摸屏移植1.I2C子系统goodix电容屏采用I2C接口与处理器连接,我们要首先确保linux内核拥有对I2C子系统的支持,下面我们从子系统的配置和电容屏驱动两个方面进行移植修改1)配置I2C子系统支持#make menuconfigDevice Drivers ---><*> I2C support --->[*] Enable compatibility bits for old user-space (NEW)<*> I2C device interface< > I2C bus multiplexing support (NEW)[*] Autoselect pertinent helper modules (NEW)I2C Hardware Bus support --->进入I2C Hardware Bus support选项,选中以下内容:*** I2C system bus drivers (mostly embedded / system-on-chip) ***< > Synopsys DesignWare (NEW)<*> GPIO-based bitbanging I2C< > OpenCores I2C Controller (NEW)< > PCA9564/PCA9665 as platform device (NEW)<*> S3C2410 I2C Driver< > Simtec Generic I2C interface (NEW)...2.GOODIX电容屏移植1)添加goodix电容屏驱动将“goodix_touch.c”文件copy到drivers/input/touchscreen/目录下,并将"goodix_touch.h"、"goodix_queue.h"文件copy到include/linux/目录下,并修改Kconfig文件及Makefile文件支持触摸屏驱动的配置和编译#vi driver/input/touchscreen/Kconfig在config TOUCHSCREEN_TPS6507X选项的后面添加以下内容:config TOUCHSCREEN_GOODIXtristate "GOODIX based touchscreen"depends on I2ChelpIt is a android driver to support Gooidx's touchscreen whose nameis guitar on s5pv210 platform. The touchscreen can support multi-touch not more than two fingers.Say Y here to enable the driver for the touchscreen on theS5V SMDK board.If unsure, say N.To compile this driver as a module, choose M here:the module will be called goodix_touch.ko.#vi driver/input/touchscreen/Makefile在文件最后添加如下内容:obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_touch.o2)添加i2c_board_info资源(电容屏)#vi arch/arm/mach-s5pv210/mach-smdkv210.c在smdkv210_i2c_devs0结构体数组定义中添加以下内容:...{ I2C_BOARD_INFO("24c08", 0x50), }, /* Samsung S524AD0XD1 */ { I2C_BOARD_INFO("wm8580", 0x1b), },{I2C_BOARD_INFO("Goodix-TS", 0x55),.irq = IRQ_EINT(4),},...3)配置电容屏选项#make menuconfigDevice Drivers --->Input device support --->...[*] Touchscreens ---><*> GOODIX based touchscreen4)make将在arch/arm/boot/下生成编译好的可执行程序zImage下载到开发板即可,执行命令“cat /dev/input/event0”,然后用手触摸屏幕会在终端看到输出打印的乱码信息,表示移植成功,如果没有打印信息或没有“/dev/input/event0”这个设备说明移植失败。
史上最详细的触摸屏驱动分析
/*触摸屏驱动程序及分析*/#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 <plat/regs-adc.h>#include <mach/regs-gpio.h>/* For ts.dev.id.version */#define S3C2410TSVERSION 0x0101#define WAIT4INT(x) (((x)<<8) | \S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \S3C2410_ADCTSC_XY_PST(3))#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))static char *s3c2410ts_name = "s3c2410 TouchScreen";static struct input_dev *dev;static long xp;static long yp;static int count;extern struct semaphore ADC_LOCK;static int OwnADC = 0;static void __iomem *base_addr;/*把GPG12~15 设置为保留模式*/static inline void s3c2410_ts_connect(void){s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);}/*求坐标的平均值,报告坐标,当还是按下状态时,再次调用ADC转换,如果抬起则让触摸屏设置为等待中断模式*/static void touch_timer_fire(unsigned long data){unsigned long data0;unsigned long data1;int updown;/*用于读取ADCDA T数据*/data0 = ioread32(base_addr+S3C2410_ADCDA T0);data1 = ioread32(base_addr+S3C2410_ADCDA T1);/*判断是按下还是送开*/updown = (!(data0 & S3C2410_ADCDA T0_UPDOWN)) && (!(data1 & S3C2410_ADCDA T0_UPDOWN));/*如果按下*/if (updown) {if (count != 0) {/*转换四次后进行事件汇报*/long tmp;tmp = xp;xp = yp;yp = tmp;/* 求平均值*/xp >>= 2;yp >>= 2;/* 报告x、y的绝对坐标值*/input_report_abs(dev, ABS_X, xp);input_report_abs(dev, ABS_Y, yp);/* 报告按键事件,键值为1(代表触摸屏对应的按键被按下) */input_report_key(dev, BTN_TOUCH, 1);/* 报告触摸屏的状态,1表明触摸屏被按下*/input_report_abs(dev, ABS_PRESSURE, 1);/* 同步*/input_sync(dev);}xp = 0;yp = 0;count = 0;/*自动X/Y轴坐标转换模式的设置,自动地进行X轴和Y轴的转换操作,随后产生相应的INT_ADC中断通知转换完毕*/iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);/*如果还没有启动ADC或者ADC转换四次完毕后则启动ADC*/iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);} else {count = 0;/* 如果触摸笔是弹起状态,则提出报告,并让触摸屏处于等待触摸的阶段*//* 报告按键事件,键值为0(代表触摸屏对应的按键被释放) */input_report_key(dev, BTN_TOUCH, 0);/* 报告触摸屏的状态,0表明触摸屏未被按下*/input_report_abs(dev, ABS_PRESSURE, 0);/*同步*/input_sync(dev);/* 设置触摸屏为等待中断模式,等待触摸笔按下*/iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);if (OwnADC) {OwnADC = 0;up(&ADC_LOCK);}}}static struct timer_list touch_timer =TIMER_INITIALIZER(touch_timer_fire, 0, 0);/*当触摸按下时,产生INT_TC中断,会进入到stylus_updown中断处理函数当有中断产生时,会使OwnADC=1,然后读取数据,并判断是抬起还是按下,按下则调用touch_timer_fire()函数,抬起则释放锁,并使OwnADC=0*/static irqreturn_t stylus_updown(int irq, void *dev_id){unsigned long data0;unsigned long data1;int updown;/*注意在触摸屏驱动模块中,这个ADC_LOCK的作用是保证任何时候都只有一个驱动程序使用ADC的中断线,因为在mini2440的adc模块中也会使用到ADC,这样只有拥有了这个锁,才能进入到启动ADC代码部分,注意尽管LDD3中说过信号量因为休眠不适合使用在ISR中,但down_trylock是一个例外,它不会休眠*/if (down_trylock(&ADC_LOCK) == 0) {OwnADC = 1;//获取ADC转换的数据data0 = ioread32(base_addr+S3C2410_ADCDA T0);data1 = ioread32(base_addr+S3C2410_ADCDA T1);updown = (!(data0 & S3C2410_ADCDA T0_UPDOWN)) && (!(data1 & S3C2410_ADCDA T0_UPDOWN));/*判断目前触摸笔的状态updown = 1 触摸笔按下;updown = 0 触摸笔弹起;*/if (updown) {//当触摸笔按下时touch_timer_fire(0); /*这是一个定时器函数,当然在这里只是作为普通函数调用,用来启动ADC*/} else {//当笔弹起时,释放锁OwnADC = 0;up(&ADC_LOCK);}}return IRQ_HANDLED;}/*INT_ADC 中断处理函数读取四次AD转换的值,然后求和,超过四次后执行touch_time_fire()函数*/static irqreturn_t stylus_action(int irq, void *dev_id){unsigned long data0;unsigned long data1;if (OwnADC) {data0 = ioread32(base_addr+S3C2410_ADCDA T0);data1 = ioread32(base_addr+S3C2410_ADCDA T1);xp += data0 & S3C2410_ADCDA T0_XPDA TA_MASK;yp += data1 & S3C2410_ADCDA T1_YPDA TA_MASK;count++;if (count < (1<<2)) { /*如果小于四次重新启动ADC转换*/iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);iowrite32(ioread32(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_ENABLE_STAR T, base_addr+S3C2410_ADCCON);} else {/*如果超过四次,启动1个时间滴答的定时器,停止ADC,然后调用定时器处理函数touch_timer_fire进行数据上报*/mod_timer(&touch_timer, jiffies+1);iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC);}}return IRQ_HANDLED;}static struct clk *adc_clock;/*初始化主要做的工作为:1、获取ADC时钟并使时钟使能2、申请IO虚拟地址3、设置ADCCON,ADCDLY,ACDTSC4、申请input设备5、设置input事件类型,事件值,X,Y坐标的最大最小值6、中断申请7、注册input设备*/static int __init s3c2410ts_init(void){struct input_dev *input_dev; //定义input结构体指针/*获取时钟,触摸屏是挂载在APB BUS上的外围设备,需要时钟控制,见数据手册第7章*/adc_clock = clk_get(NULL, "adc");if (!adc_clock) {//如果时钟获取失败时printk(KERN_ERR "failed to get adc clock source\n");return -ENOENT;}//时钟使能clk_enable(adc_clock);/*I/O内存不要直接进行访问的,最好对其进行映射*/base_addr=ioremap(S3C2410_PA_ADC,0x20);if (base_addr == NULL) {printk(KERN_ERR "Failed to remap register block\n");return -ENOMEM;}/* Configure GPIOs 把YPON,YMON,XPON,XMOND都设置成11,保留模式*/s3c2410_ts_connect();/*使能预分频和设置分频系数S3C2410_ADCCON_PRSCEN:使AD转换使能,是ADCCON的第14位PRSCENS3C2410_ADCCON_PRSCVL(0xFF):预分频系数,取值为0~255:,AD时钟=PCLK/(PRSCVL+1),且必须小于1/5的PCKL*/iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),base_addr+S3C2410_ADCCON);/*设置ADC延时,在等待中断模式下表示产生INT_TC的间隔时间,ADC转换周期等待定时器*/iowrite32(0xffff, base_addr+S3C2410_ADCDLY);/*ADCTSC = 0xd3 ,将触摸屏置为等待中断模式,等待触摸屏被按下产生INT_TC中断*/iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);/* 以下配置2.6内核划分出来的输入设备*//* 分配一个input输入设备*/input_dev = input_allocate_device();if (!input_dev) {//分配input输入设备不成功时printk(KERN_ERR "Unable to allocate the input device !!\n");return -ENOMEM;}dev = input_dev;/*evbit字段用来定义该输入设备可以支持的(产生和响应)的事件的类型,触摸屏设置为支持同步(EN_SYN)、按键(EN_KEY)、绝对坐标(EV_ABS)事件*/dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);/* 设置所支持的按键(键值),触摸屏可以看成只有一个按键的设备*/dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);/* 设置绝对坐标x的最小最大值(0-0x3FF) */input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0);/* 设置绝对坐标y的最小最大值(0-0x3FF) */input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0);input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);dev->name = s3c2410ts_name;dev->id.bustype = BUS_RS232;dev->id.vendor = 0xDEAD;dev->id.product = 0xBEEF;dev->id.version = S3C2410TSVERSION;/*INT_TC, INT_ADC中断注册,问题:两种中断的产生时机?哪种中断会先产生?答案:INT_TC会先产生,并不断的产生*//* Get irqs *///中断处理函数if (request_irq(IRQ_ADC, stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM, "s3c2410_action", dev)) {printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");iounmap(base_addr);return -EIO;}if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,"s3c2410_action", dev)) {printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");iounmap(base_addr);return -EIO;}printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);/* All went ok, so register to the input system *//*注册输入设备*/input_register_device(dev);return 0;}/*主要完成:1、中断的释放2、注销input设备3、释放虚拟内存*/static void __exit s3c2410ts_exit(void){disable_irq(IRQ_ADC);disable_irq(IRQ_TC);free_irq(IRQ_TC,dev);free_irq(IRQ_ADC,dev);if (adc_clock) {clk_disable(adc_clock);clk_put(adc_clock);adc_clock = NULL;}input_unregister_device(dev);iounmap(base_addr);}module_init(s3c2410ts_init);module_exit(s3c2410ts_exit);MODULE_LICENSE("GPL");。
linux设备驱动程序的设计与实现
linux设备驱动程序的设计与实现
Linux设备驱动程序的设计与实现是一个涉及底层系统编程和硬件交互的复杂过程。
下面是一个简单的步骤指南,以帮助你开始设计和实现Linux设备驱动程序:
1. 了解硬件:首先,你需要熟悉你要驱动的硬件设备的规格和特性。
这包括硬件的内存空间、I/O端口、中断请求等。
2. 选择驱动程序模型:Linux支持多种设备驱动程序模型,包括字符设备、块设备、网络设备等。
根据你的硬件设备和需求,选择合适的驱动程序模型。
3. 编写Makefile:Makefile是一个文本文件,用于描述如何编译和链接你的设备驱动程序。
它告诉Linux内核构建系统如何找到并编译你的代码。
4. 编写设备驱动程序:在Linux内核源代码树中创建一个新的驱动程序模块,并编写相应的C代码。
这包括设备注册、初始化和卸载函数,以及支持读写和配置硬件的函数。
5. 测试和调试:编译你的设备驱动程序,并将其加载到运行中的Linux内核中。
使用各种测试工具和方法来验证驱动程序的正确性和稳定性。
6. 文档和发布:编写清晰的文档,描述你的设备驱动程序的用途、用法和已知问题。
发布你的代码以供其他人使
用和改进。
kernel下的uinput机制_示例及概述说明
kernel下的uinput机制示例及概述说明1. 引言1.1 概述在计算机领域中,uinput(user input)机制是一种通过模拟输入设备来实现对系统的控制和操作的方法。
它在kernel中被广泛应用,可以创建虚拟的输入设备,并向系统注入用户自定义的输入事件。
通过uinput机制,我们可以模拟鼠标、键盘、触摸屏等各种输入设备的行为,使得用户或开发者能够以编程的方式进行各种操作和测试。
1.2 文章结构本文将就kernel下的uinput机制进行详细介绍与示例演示。
首先,在"2. uinput机制的原理与用途"部分中,我们将解释什么是uinput机制以及它在系统中起到的作用,进而探讨其背后的实现原理和所涉及到的应用领域。
接着,在"3. 示例:通过uinput模拟输入设备的创建和操作"部分中,我们将会给出具体示例,演示如何使用uinput机制来创建虚拟输入设备,并对其进行模拟操作。
随后,在"4. kernel中uinput机制的相关配置和API接口介绍"部分中,我们将详细介绍如何配置kernel以支持uinput机制,并提供相关API接口的介绍和使用示例。
最后,在"5. 结论与展望"部分中,我们将对uinput机制进行总结回顾,并探讨目前存在的问题以及未来的发展趋势。
1.3 目的本文旨在全面介绍kernel下的uinput机制,帮助读者了解其原理和用途,并提供具体示例和配置指南,使得读者能够深入理解并应用该机制,从而满足各种自动化测试、模拟操作等需求。
同时,对于开发者来说,该文章也提供了一个参考和指导,以便他们在实际项目中更好地利用uinput机制来实现相关功能。
2. uinput机制的原理与用途2.1 什么是uinput机制uinput(User Input)机制是Linux内核中的一个子系统,用于模拟和生成输入事件。
input系统接口及分析.
input系统接⼝及分析.接⼝前⾯说了,linux内核input⼦系统中已经实现了input设备的接⼝函数,这使得我们⼯作量⼤⼤的减轻了。
我们以akm8973芯⽚(⽤于智能⼿机指南针的主功能芯⽚,实际上就⼀电⼦罗盘)为例,来简单看⼀下写⼀个input设备我们需要做的⼯作。
⾸先,在驱动模块加载函数中申请⼀个input设备,并告知input⼦系统它⽀持哪些事件,如下所⽰:akm->input_dev = input_allocate_device();set_bit(EV_ABS, akm->input_dev->evbit);input_set_abs_params(akm->input_dev, ABS_RX, 0, 23040, 0, 0);input_set_abs_params(akm->input_dev, ABS_RY, -11520, 11520, 0, 0);input_set_abs_params(akm->input_dev, ABS_RZ, -5760, 5760, 0, 0);input_set_abs_params(akm->input_dev, ABS_THROTTLE, -30, 85, 0, 0);input_set_abs_params(akm->input_dev, ABS_RUDDER, 0, 3, 0, 0);input_set_abs_params(akm->input_dev, ABS_HAT0X, -2048, 2032, 0, 0);input_set_abs_params(akm->input_dev, ABS_HAT0Y, -2048, 2032, 0, 0);input_set_abs_params(akm->input_dev, ABS_BRAKE, -2048, 2032, 0, 0);以上这些都是为让input⼦系统⽀持的某些参数⽽设置的,EV_ABS表⽰⽀持绝对值坐标,后⾯都是针对这些坐标的⼀些参数访问范围设置。
实验5 触摸屏驱动的移植
一、实验目的
掌握 Linux 下触摸屏驱动程序的原理; 掌握 Linux 下触摸屏应用程序的编写方法。
二、实验环境
硬件:TQ2440 核心子板、PC 机; 软件:Windows 2000/NT/XP、Fedora10、其他嵌入式软件包。
三、完成实验所需时间:约90分钟 四、实验内容
#include <asm/arch/idle.h> #include <asm/arch/fb.h> #include <asm/arch/ts.h>
#include <asm/plat-s3c24xx/s3c2410.h> #include <asm/plat-s3c24xx/s3c2440.h> #include <asm/plat-s3c24xx/clock.h>
}; static void __init smdk2440_map_io(void) {
s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc)); s3c24xx_init_clocks(12000000); s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs)); }
+= elo.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU)
+= fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH)
+= mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712)
platfrom设备驱动框架
platfrom设备驱动框架前⾯编写的设备驱动都⾮常的简单,都是对IO进⾏最简单的读写操作。
像I2C、SPI、LCD 等这些复杂外设的驱动就不能这么去写了,Linux 系统要考虑到驱动的可重⽤性,因此提出了驱动的分离与分层这样的软件思路,在这个思路下诞⽣了我们将来最常打交道的platform 设备驱动,也叫做平台设备驱动。
1、Linux驱动的分离与分层1.1 驱动的分隔与分离对于 Linux 这样⼀个成熟、庞⼤、复杂的操作系统,代码的重⽤性⾮常重要,否则的话就会在 Linux 内核中存在⼤量⽆意义的重复代码。
尤其是驱动程序,因为驱动程序占⽤了 Linux内核代码量的⼤头,如果不对驱动程序加以管理,任由重复的代码肆意增加,那么⽤不了多久Linux 内核的⽂件数量就庞⼤到⽆法接受的地步。
假如现在有三个平台 A、B 和 C,这三个平台(这⾥的平台说的是 SOC)上都有 MPU6050 这个 I2C 接⼝的六轴传感器,按照我们写裸机 I2C 驱动的时候的思路,每个平台都有⼀个MPU6050的驱动,因此编写出来的最简单的驱动框架如图所⽰:每种平台下都有⼀个主机驱动和设备驱动,主机驱动肯定是必须要的,毕竟不同的平台其 I2C 控制器不同。
但是右侧的设备驱动就没必要每个平台都写⼀个,因为不管对于那个 SOC 来说,MPU6050 都是⼀样,通过 I2C 接⼝读写数据就⾏了,只需要⼀个 MPU6050 的驱动程序即可。
如果再来⼏个 I2C 设备,⽐如 AT24C02、FT5206(电容触摸屏)等,如果按照图中的写法,那么设备端的驱动将会重复的编写好⼏次。
显然在 Linux 驱动程序中这种写法是不推荐的,最好的做法就是每个平台的 I2C 控制器都提供⼀个统⼀的接⼝(也叫做主机驱动),每个设备的话也只提供⼀个驱动程序(设备驱动),每个设备通过统⼀的 I2C接⼝驱动来访问,这样就可以⼤⼤简化驱动⽂件,⽐如 54.1.1 中三种平台下的 MPU6050 驱动框架就可以简化为图所⽰:这个就是驱动的分隔,也就是将主机驱动和设备驱动分隔开来,⽐如 I2C、SPI 等等都会采⽤驱动分隔的⽅式来简化驱动的开发。
Linux驱动之LCD驱动编写
Linux驱动之LCD驱动编写在这篇博客中已经分析了编写LCD驱动的步骤,接下来就按照这个步骤来字尝试字节编写LCD驱动。
⽤的LCD屏幕为tft 屏,每个像素点为16bit。
对应与红绿蓝分别为565。
1、分配⼀个fb_info结构2、设置fb_info结构3、硬件相关的操作,配置LCD时钟、配置IO端⼝、配置LCD寄存器。
4、最终注册fbinfo结构到registered_fb数组要理解LCD的⼯作原理,需要了解LCD的时钟,在TFT的LCD中有如下的时钟。
这个⼏个时钟数据在配置LCD寄存器时都说需要设置的。
1、VCLK:两个像素之间的时钟,即两个像素隔多长时间才能显⽰下⼀个像素2、HSYNC:⽔平同步时钟,即第⼀⾏像素点显⽰完成之后隔多长时间才能开始下⼀⾏的显⽰3、VSYNC:垂直⽅向的同步时钟,也叫帧同步信号,即⼀帧数据显⽰完成之后(⼀帧数据表⽰⼀个屏幕显⽰完成,即⼀个显存的数据全部取完),过多长下⼀帧数据才开始显⽰本节需要⽤到的函数:void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp); //分配DMA缓存区给显存//返回值为:申请到的DMA缓冲区的虚拟地址,若为NULL,表⽰分配失败,则需要使⽤dma_free_writecombine()释放内存,避免内存泄漏//参数如下://*dev:指针,这⾥填0,表⽰这个申请的缓冲区⾥没有内容//size:分配的地址⼤⼩(字节单位)//*handle:申请到的物理起始地址//gfp:分配出来的内存参数,标志定义在<linux/gfp.h>,常⽤标志如下://GFP_ATOMIC ⽤来从中断处理和进程上下⽂之外的其他代码中分配内存. 从不睡眠.//GFP_KERNEL 内核内存的正常分配. 可能睡眠.//GFP_USER ⽤来为⽤户空间页来分配内存; 它可能睡眠.分配⼀段DMA缓存区,分配出来的内存会禁⽌cache缓存(因为DMA传输不需要CPU)它和 dma_alloc_coherent ()函数相似,不过 dma_alloc_coherent ()函数是分配出来的内存会禁⽌cache缓存以及禁⽌写⼊缓冲区dma_free_writecombine(dev,size,cpu_addr,handle); //释放缓存//cpu_addr:虚拟地址,//handle:物理地址释放DMA缓冲区, dev和size参数和上⾯的⼀样struct fb_info *framebuffer_alloc(size_t size, struct device *dev); //申请⼀个fb_info结构体,//size:额外的内存,//*dev:指针, 这⾥填0,表⽰这个申请的结构体⾥没有内容int register_framebuffer(struct fb_info *fb_info);//向内核中注册fb_info结构体,若内存不够,注册失败会返回负数int unregister_framebuffer(struct fb_info *fb_info) ;//注销内核中fb_info结构体本节需要⽤到的结构体:fb_info结构体如下:struct fb_info {... ...struct fb_var_screeninfo var; //可变的参数struct fb_fix_screeninfo fix; //固定的参数... ...struct fb_ops *fbops; //操作函数... ...char __iomem *screen_base; //显存虚拟起始地址unsigned long screen_size; //显存虚拟地址长度void *pseudo_palette;//假的16⾊调⾊板,⾥⾯存放了16⾊的数据,可以通过8bpp数据来找到调⾊板⾥⾯的16⾊颜⾊索引值,模拟出16⾊颜⾊来,节省内存,不需要的话就指向⼀个不⽤的数组即可 ... ...};其中操作函数fb_info-> fbops 结构体写法如下:static struct fb_ops s3c_lcdfb_ops = {.owner = THIS_MODULE,.fb_setcolreg = my_lcdfb_setcolreg,//设置调⾊板fb_info-> pseudo_palette,⾃⼰构造该函数.fb_fillrect = cfb_fillrect, //填充矩形,⽤/drivers/video/ cfbfillrect.c⾥的函数即可.fb_copyarea = cfb_copyarea, //复制数据, ⽤/drivers/video/cfbcopyarea.c⾥的函数即可.fb_imageblit = cfb_imageblit, //绘画图形, ⽤/drivers/video/imageblit.c⾥的函数即可};固定的参数fb_info-> fix 结构体如下:struct fb_fix_screeninfo {char id[16]; //id名字unsigned long smem_start; //framebuffer物理起始地址__u32 smem_len; //framebuffer长度,字节为单位__u32 type; //lcd类型,默认值0即可__u32 type_aux; //附加类型,为0__u32 visual; //画⾯设置,常⽤参数如下// FB_VISUAL_MONO01 0 单⾊,0:⽩⾊,1:⿊⾊// FB_VISUAL_MONO10 1 单⾊,1:⽩⾊,0:⿊⾊// FB_VISUAL_TRUECOLOR 2 真彩(TFT:真彩)// FB_VISUAL_PSEUDOCOLOR 3 伪彩// FB_VISUAL_DIRECTCOLOR 4 直彩 __u16 xpanstep; /*如果没有硬件panning就赋值为0 */ __u16 ypanstep; /*如果没有硬件panning就赋值为0 */ __u16 ywrapstep; /*如果没有硬件ywrap就赋值为0 */ __u32 line_length; /*⼀⾏的字节数 ,例:(RGB565)240*320,那么这⾥就等于240*16/8 */ /*以下成员都可以不需要*/ unsigned long mmio_start; /*内存映射IO的起始地址,⽤于应⽤层直接访问寄存器,可以不需要*/__u32 mmio_len; /* 内存映射IO的长度,可以不需要*/__u32 accel;__u16 reserved[3];};可变的参数fb_info-> var 结构体如下:structfb_var_screeninfo{ __u32xres; /*可见屏幕⼀⾏有多少个像素点*/__u32 yres; /*可见屏幕⼀列有多少个像素点*/__u32 xres_virtual; /*虚拟屏幕⼀⾏有多少个像素点 */__u32 yres_virtual; /*虚拟屏幕⼀列有多少个像素点*/__u32 xoffset; /*虚拟到可见屏幕之间的⾏偏移,若可见和虚拟的分辨率⼀样,就直接设为0*/ __u32 yoffset; /*虚拟到可见屏幕之间的列偏移*/__u32 bits_per_pixel; /*每个像素的位数即BPP,⽐如:RGB565则填⼊16*/__u32 grayscale; /*⾮0时,指的是灰度,真彩直接填0即可*/struct fb_bitfield red; //fb缓存的R位域, fb_bitfield结构体成员如下://__u32 offset; 区域偏移值,⽐如RGB565中的R,就在第11位//__u32 length; 区域长度,⽐如RGB565的R,共有5位//__u32 msb_right; msb_right ==0,表⽰数据左边最⼤, msb_right!=0,表⽰数据右边最⼤struct fb_bitfield green; /*fb缓存的G位域*/struct fb_bitfield blue; /*fb缓存的B位域*/ /*以下参数都可以不填,默认为0*/struct fb_bitfield transp; /*透明度,不需要填0即可*/__u32nonstd; /* != 0表⽰⾮标准像素格式*/__u32 activate; /*设为0即可*/__u32height; /*外设⾼度(单位mm),⼀般不需要填*/__u32width; /*外设宽度(单位mm),⼀般不需要填*/__u32 accel_flags; /*过时的参数,不需要填*//* 除了pixclock本⾝外,其他的都以像素时钟为单位*/__u32pixclock; /*像素时钟(⽪秒)*/__u32 left_margin; /*⾏切换,从同步到绘图之间的延迟*/__u32right_margin; /*⾏切换,从绘图到同步之间的延迟*/__u32upper_margin; /*帧切换,从同步到绘图之间的延迟*/__u32lower_margin; /*帧切换,从绘图到同步之间的延迟*/__u32hsync_len; /*⽔平同步的长度*/__u32 vsync_len; /*垂直同步的长度*/__u32 sync;__u32 vmode;__u32 rotate;__u32reserved[5]; /*保留*/}1.写驱动程序:(驱动设置:参考⾃带的LCD平台驱动drivers/video/s3c2410fb.c )1.1 步骤如下:在驱动init⼊⼝函数中:1)分配⼀个fb_info结构体2)设置fb_info 2.1)设置固定的参数fb_info-> fix 2.2) 设置可变的参数fb_info-> var 2.3) 设置操作函数fb_info-> fbops 2.4) 设置fb_info 其它的成员3)设置硬件相关的操作 3.1)配置LCD引脚 3.2)根据LCD⼿册设置LCD控制器 3.3)分配显存(framebuffer),把地址告诉LCD控制器和fb_info4)开启LCD,并注册fb_info: register_framebuffer() 4.1) 直接在init函数中开启LCD(后⾯讲到电源管理,再来优化) 控制LCDCON5允许PWREN信号, 然后控制LCDCON1输出PWREN信号, 输出GPB0⾼电平来开背光, 4.2) 注册fb_info在驱动exit出⼝函数中:1)卸载内核中的fb_info2) 控制LCDCON1关闭PWREN信号,关背光,iounmap注销地址3)释放DMA缓存地址dma_free_writecombine()4)释放注册的fb_info1.2 具体代码如下:#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <asm/io.h> //含有iomap函数iounmap函数#include <asm/uaccess.h>//含有copy_from_user函数#include <linux/device.h>//含有类相关的处理函数#include <linux/fb.h> //含有fb_info结构体定义//#include <asm/dma-mapping.h> //含有dma_free_writecombine宏定义#include <linux/dma-mapping.h> //含有dma_free_writecombine宏定义#include <linux/platform_device.h>//含有平台设备总线模型相关变量#include <linux/mm.h>#include <linux/slab.h>//#include <linux/module.h>//#include <linux/kernel.h>//#include <linux/errno.h>//#include <linux/string.h>//#include <linux/mm.h>//#include <linux/slab.h>//#include <linux/delay.h>//#include <linux/fb.h>//#include <linux/init.h>//#include <linux/dma-mapping.h>//#include <linux/interrupt.h>//#include <linux/workqueue.h>//#include <linux/wait.h>//#include <linux/platform_device.h>//#include <linux/clk.h>//#include <asm/io.h>//#include <asm/uaccess.h>//#include <asm/div64.h>//#include <asm/mach/map.h>//#include <asm/arch/regs-lcd.h>//#include <asm/arch/regs-gpio.h>//#include <asm/arch/fb.h>/*lcd控制寄存器放在⼀个结构体⾥⾯*/struct lcd_regs {unsigned long lcdcon1;unsigned long lcdcon2;unsigned long lcdcon3;unsigned long lcdcon4;unsigned long lcdcon5;unsigned long lcdsaddr1;unsigned long lcdsaddr2;unsigned long lcdsaddr3;unsigned long redlut;unsigned long greenlut;unsigned long bluelut;unsigned long reserved[9];unsigned long dithmode;unsigned long tpal;unsigned long lcdintpnd;unsigned long lcdsrcpnd;unsigned long lcdintmsk;unsigned long lpcsel;};static struct fb_info *s3c_mylcdfb_info;//fb_info结构体static volatile unsigned long *gpbcon;//GPB0⽤于lcd背光的控制static volatile unsigned long *gpbdat;//GPB0⽤于lcd背光的控制static volatile unsigned long *gpccon;static volatile unsigned long *gpdcon;static volatile unsigned long *gpgcon;//GPG4⽤于lcd电源static volatile struct lcd_regs* lcd_regs;//lcd寄存器static u32 pseudo_palette[16]; //调⾊板内存/* from pxafb.c */static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf){chan &= 0xffff; //取出16bit的数据chan >>= 16 - bf->length; //return chan << bf->offset;}static int s3c_mylcdfb_setcolreg(unsigned int regno, unsigned int red,unsigned int green, unsigned int blue,unsigned int transp, struct fb_info *info){unsigned int val;if (regno > 16)return1;/* ⽤red,green,blue三原⾊构造出val */val = chan_to_field(red, &info->var.red);val |= chan_to_field(green, &info->var.green);val |= chan_to_field(blue, &info->var.blue);//((u32 *)(info->pseudo_palette))[regno] = val;pseudo_palette[regno] = val;return0;}static struct fb_ops s3c_mylcdfb_ops = { //操作函数结构体.owner = THIS_MODULE,.fb_setcolreg = s3c_mylcdfb_setcolreg,//待会设置,这个是调⾊板,如果使⽤⼩于16bit的像素需要⽤到 .fb_fillrect = cfb_fillrect,.fb_copyarea = cfb_copyarea,.fb_imageblit = cfb_imageblit,};static int lcd_drv_init(void){/*1、分配⼀个fb_info*/s3c_mylcdfb_info = framebuffer_alloc(0,NULL);//size为额外分配的⼤⼩,这⾥不需要,所以设为0if(s3c_mylcdfb_info==NULL){printk("unframebuffer_alloc\n");return1;}/*2、设置*//*2.1 设置固定的参数*/strcpy(s3c_mylcdfb_info->fix.id, "mylcd");//名字//s3c_mylcdfb_info->fix.smem_start = ;//显存的物理起始地址,后⾯设置s3c_mylcdfb_info->fix.smem_len = 480*272*16/8;//单位为字节,每个像素点占⽤16bit :565,显存的⼤⼩ s3c_mylcdfb_info->fix.type = FB_TYPE_PACKED_PIXELS;//LCD类型,填充像素的类型 tft//s3c_mylcdfb_info->fix.type_aux= //附加的LCD类型,不需要设置s3c_mylcdfb_info->fix.visual = FB_VISUAL_TRUECOLOR;//视觉类型,选择真彩⾊s3c_mylcdfb_info->fix.line_length = 480*16/8; //⼀⾏的长度,单位为字节// s3c_mylcdfb_info->fix.mmio_start = //控制lcd的寄存器的物理地址// s3c_mylcdfb_info->fix.mmio_len = //控制lcd的寄存器的⼤⼩/*2.2 设置可变的参数*/s3c_mylcdfb_info->var.xres = 480;//x⽅向的分辨率s3c_mylcdfb_info->var.yres = 272;//y⽅向的分辨率s3c_mylcdfb_info->var.xres_virtual = 480;//x⽅向的虚拟分辨率s3c_mylcdfb_info->var.yres_virtual = 272;//y⽅向的虚拟分辨率s3c_mylcdfb_info->var.bits_per_pixel = 16;//每个像素的⼤⼩,单位为bits3c_mylcdfb_info->var.grayscale = 0;//灰度值s3c_mylcdfb_info->var.red.length = 5;//红⾊像素占⽤的长度,单位bits3c_mylcdfb_info->var.green.length = 6;//绿⾊像素占⽤的长度,单位bits3c_mylcdfb_info->var.blue.length = 5;//蓝⾊像素占⽤的长度,单位bits3c_mylcdfb_info->var.red.offset= 11;//红⾊像素在16bit中的偏移值s3c_mylcdfb_info->var.green.offset= 6;//绿⾊像素在16bit中的偏移值s3c_mylcdfb_info->var.blue.offset=0;//蓝⾊像素在16bit中的偏移值s3c_mylcdfb_info->var.red.msb_right= 0;//低位在前还是⾼位在前,⼀般⾼位在前,也就是⼩端模式s3c_mylcdfb_info->var.green.msb_right= 0;s3c_mylcdfb_info->var.blue.msb_right=0;s3c_mylcdfb_info->var.activate = FB_ACTIVATE_NOW;//使⽤默认参数,显存⽴刻⽣效/*2.3 设置操作函数*/s3c_mylcdfb_info->fbops = &s3c_mylcdfb_ops;/*2.4 其它的⼀些设置 */s3c_mylcdfb_info->pseudo_palette = pseudo_palette;//调⾊板的地址//s3c_mylcdfb_info->screen_base = ;//显存的虚拟基地址s3c_mylcdfb_info->screen_size = 480*272*16/8;//单位为字节,每个像素点占⽤16bit :565,显存的⼤⼩/*3、硬件相关的操作 *//*3.1、配置GPIO⽤于LCD*/gpbcon = ioremap(0x56000010, 8);//将实际的寄存器地址转换为虚拟地址gpccon = ioremap(0x56000020 , 4);gpdcon = ioremap(0x56000030 , 4);gpgcon = ioremap(0x56000060 , 4);gpbdat = gpbcon + 1;*gpccon = 0xaaaaaaaa; /* GPIO管脚⽤于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */*gpdcon = 0xaaaaaaaa; /* GPIO管脚⽤于VD[23:8] */*gpbcon &= ~(3); /* GPB0设置为输出引脚 */*gpbcon |= 1;*gpbdat &= ~1; /* 输出低电平关闭LCD背光 */*gpgcon |= (3<<8); /* GPG4⽤作LCD_PWREN 电源*//*3.2、根据LCD⼿册设置LCD控制器,⽐如VCLK的频率等 */lcd_regs = ioremap(0X4D000000 , sizeof(struct lcd_regs));/** bit[17:8] : VCLK = HCLK / [(CLKVAL+1) x 2]* 10M = 100M/[(CLKVAL+1) x 2]* CLKVAL = 4** bit[6:5] :PNRMODE = 11显⽰模式,选择TFT模式** bit[4:1] :BPPMODE = 1100;像素=16bit 565** bit[0] :ENVID = 0;先关闭LCD控制器*/lcd_regs->lcdcon1 = (4<<8) | (3<<5) | (0x0c<<1);///** [31:24] : VBPD = 帧同步信号发出后,过多长时间开始显⽰数据,单位为⾏,理解为1⾏的时间* 看LCD⼿册tvb = VBPD + 1 = 2;所以VBPD = 1** [23:14]:LINEVAL + 1= 272;,所以LINEVAL = 271;垂直⽅向尺⼨,多少⾏** [13:6]:VFPD = ⼀帧的数据传输完成之后,过多长时间开始下⼀帧数据的帧同步信号,单位为⾏,理解为1⾏的时间 * 看LCD⼿册tvf = VFPD + 1 = 2;所以VFPD = 1** [5:0]:VSPW = 帧同步信号的脉冲宽度,单位为⾏* 看LCD⼿册tvp = VSPW + 1 =10;所以VSPW = 9*/lcd_regs->lcdcon2 = (1<<24) | (271<<14) | (1<<6) | (9<<0);/** [25:19]:HBPD = ⾏同步信号发出后,经过多少个VCLK,才发送像素的数据,单位为VCLK* 看LCD⼿册thb = HBPD + 1 = 2;所以HBPD=1** [18:8]:HOZVAL + 1 = 480,所以 HOZVAL = 479;⽔平⽅向尺⼨,多少列**[7:0]:HFPD = ⼀⾏的像素数据传输完成之后,经过多长时间,才能发送下⼀个⾏同步信号,单位为VCLK*看LCD⼿册thf = HFPD + 1 = 2;所以HFPD = 1;*/lcd_regs->lcdcon3 = (1<<19) | (479<<8) | (1<<0);/** [7:0]:HSPW = ⾏同步信号的脉冲宽度,单位为VCLK* 看LCD⼿册thp = HSPW + 1 = 41;所以HSPW = 40**/lcd_regs->lcdcon4 = (40<<0);/** [11] :FRM565 = 1;16位模式的格式 R:G:B = 5:6:5* [10] :INVVCLK = 0;VCLK在哪个边沿取数据 = 0表⽰下降沿取数据* [9] :INVVLINE = 1;⾏同步信号是否需要反转= 1需要反转* [8] :INVVFRAME = 1;帧同步信号是否需要反转= 1需要反转* [7] :INVVD = 0; 数据是否需要反转* [6] :INVVDEN = 0; 数据使能信号是否需要反转* [5] :INVPWREN = 0;电源使能信号是否需要反转* [4] :INVLEND = 0;⾏结束信号是否需要反转* [3] :PWREN = 0;电源使能信号,先不使能* [2] :ENLEND = 1;//⾏结束信号先使能* [1:0] :BSWP 、HWSWP = 0 1;字节内部不需要交换,字节间需要交换*/lcd_regs->lcdcon5= (1<<11) | (3<<8) | (1<<2) | (1<<0);/*3.3、显存和调⾊板设置 *//**利⽤dma_alloc_writecombine分配⼀块连续的显存*/s3c_mylcdfb_info->screen_base = dma_alloc_writecombine(NULL,s3c_mylcdfb_info->screen_size,(&(s3c_mylcdfb_info->fix.smem_start)),GFP_KERNEL);//返回虚拟地址if(s3c_mylcdfb_info->screen_base==NULL) //如果显存分配失败,直接返回{printk("undma_alloc_writecombine\n");return1;}/**将显存的地址告诉LCD控制器(物理地址)*/lcd_regs->lcdsaddr1 = (s3c_mylcdfb_info->fix.smem_start >> 1) & (~(3<<30));//起始地址lcd_regs->lcdsaddr2 = ((s3c_mylcdfb_info->fix.smem_start + s3c_mylcdfb_info->screen_size) >> 1) & 0x1fffff;//结束地址lcd_regs->lcdsaddr3 = (480*16/16); /* ⼀⾏的长度(单位: 2字节) *///s3c_lcd->fix.smem_start = xxx; /* 显存的物理地址 *//* 启动LCD */lcd_regs->lcdcon1 |= (1<<0); /* 使能LCD控制器 */lcd_regs->lcdcon5 |= (1<<3); /* 使能LCD本⾝电源 */*gpbdat |= 1; /* 输出⾼电平, 使能背光 *//*4、注册LCD*/register_framebuffer(s3c_mylcdfb_info);printk("register_framebuffer\n");return0;}static void lcd_drv_exit(void){unregister_framebuffer(s3c_mylcdfb_info);lcd_regs->lcdcon1 &= ~(1<<0); /* 关闭LCD本⾝ */*gpbdat &= ~1; /* 关闭背光 */dma_free_writecombine(NULL, s3c_mylcdfb_info->fix.smem_len, s3c_mylcdfb_info->screen_base, s3c_mylcdfb_info->fix.smem_start);iounmap(lcd_regs);iounmap(gpbcon);iounmap(gpccon);iounmap(gpdcon);iounmap(gpgcon);framebuffer_release(s3c_mylcdfb_info);}module_init(lcd_drv_init);module_exit(lcd_drv_exit);MODULE_LICENSE("GPL");2.重新编译内核,去掉默认的LCDmake menuconfig ,进⼊menu菜单重新设置内核参数:进⼊Device Drivers-> Graphics support:<M> S3C2410 LCD framebuffer support //将⾃带的LCD驱动设为模块, 不编进内核中然后make uImage 编译内核make modules 编译模块为什么要编译模块?因为LCD驱动相关的⽂件也没有编进内核,⽽fb_ops⾥的成员fb_fillrect(), fb_copyarea(), fb_imageblit()⽤的都是drivers/video下⾯的3个⽂件,所以需要这3个的.ko模块,如下图所⽰:3.挂载驱动将编译好的LCD驱动模块和drivers/video⾥的3个.ko模块放⼊nfs⽂件系统⽬录中然后烧写内核, 先装载3个/drivers/video下编译好的模块,再来装载LCD驱动模块挂载LCD驱动后, 如下图,可以通过 ls -l /dev/fb* 命令查看已挂载的LCD设备节点:4.测试运⾏测试有两种:echo hello> /dev/tty1 // LCD上便显⽰hello字段cat Makefile>/dev/tty1 // LCD上便显⽰Makeflie⽂件的内容4.1使⽤上节的键盘驱动在LCD终端打印命令⾏vi /etc/inittab //修改inittab, inittab:配置⽂件,⽤于启动init进程时,读取inittab添加->tty1::askfirst:-/bin/sh //将sh进程(命令⾏)输出到tty1⾥,也就是使LCD输出信息然后重启,insmod装载3个/drivers/video下编译好的模块,再来insmod装载LCD驱动模块,tty1设备便有了,就能看到提⽰信息:如下图,我们insmod上⼀节的键盘驱动后,按下enter键,便能在LCD终端上操作linux了从上图可以看到按下enter键,它就启动了⼀个进程号772的-sh进程,如下图发现这个-sh的描述符都指向了tty1:以上内容转载⾃。
基于Wayland管理的Linux设备坐标传递流程可行性分析
基于Wayland管理的Linux设备坐标传递流程可行性分析窦英男1赵丽娜2孙利宏1石唯琛11(东软集团(大连)有限公司辽宁大连 116000)2(中车大连电力牵引研发中心辽宁大连 116000)摘要近年来,随着生活水平的提高,对嵌入式设备的要求也越来越高。
Linux系统之所以被广泛用于嵌入式设备中,一方面是因为其特有的Input输入子系统,方便了各种触摸屏设备的移植。
另一方面是能兼容多种第三方管理系统用来实现丰富的人机交互功能。
本次研究以Linux车载设备为例,从底层触摸屏驱动出发分析了触摸屏坐标传递流程,并引入了Wayland作为第三方窗口管理。
分析对比了有无Wayland窗口管理时触摸屏坐标传递的时间。
研究结果表明在引入Wayland时按压、抬起、滑动等动作坐标平均传递时间小于1ms,满足嵌入式设备性能的需求,应用在Linux系统中是可行的。
关键词Linux嵌入式Input触摸屏Wayland坐标中图分类号TP3文献标志码A DOI: 10.3969/j.issn.1000-386x.2018.01.001计算机应用与软件10引言最早的一批嵌入式设备是基于单片机实现的,由于没有操作系统所以只能完成一些简单的逻辑功能,也不支持丰富的图形界面操作。
随着微电子工艺水平的提高,芯片中集成了微处理器等性能更高的硬件产品,同时软件上也可以实现多任务,内存管理等复杂功能,于是Linux系统在软硬件的飞速发展中应运而生。
Linux有其独特的Input子系统,且能兼容其他第三方软件来进行界面描画,这使得Linux系统在嵌入式领域的应用越来越广泛。
目前市面上流行的嵌入式设备都有丰富的带有触控人机交互的图形操作界面。
如何准确快速的识别触摸屏幕所点击的坐标并完成相对应的功能是嵌入式设备最基本的要求。
本文基于Linux车载娱乐系统对触摸屏坐标传递的流程进行了研究,并对有无Wayland窗口管理系统的坐标传递时间进行了对比实验,得出在引入窗口管理系统的情况下坐标传递时间可以满足复杂嵌入式设备的功能响应,这项技术在Linux系统中是可行的。
mipi dsi的linux kernel 驱动原理
MIPI DSI的Linux Kernel驱动原理
MIPI DSI(Mobile Industry Processor Interface Display Serial Interface)是一种用于连接移动设备显示屏的接口。
MIPI DSI具有低功耗、高带宽、低成本等优点,已被广泛应用于智能手机、平板电脑等移动设备。
MIPI DSI的Linux Kernel驱动主要负责将MIPI DSI协议与Linux内核进行桥接,从而实现Linux内核对MIPI DSI显示屏的控制。
一、MIPI DSI的Linux Kernel驱动原理主要包括以下几个部分:
1.MIPI DSI协议解析:MIPI DSI驱动程序会解析MIPI DSI协议,并将其转换为Linux内核可以理解的格式。
2.显示屏控制:MIPI DSI驱动程序会提供对显示屏的控制接口,例如设置显示屏的分辨率、亮度、对比度等。
3.帧缓冲区管理:MIPI DSI驱动程序会管理显示屏的帧缓冲区,并将其内容传输到显示屏。
二、MIPI DSI的Linux Kernel驱动主要由以下几个模块组成:
1.MIPI DSI主机控制器驱动:负责与MIPI DSI显示屏进行通信。
2.MIPI DSI PHY驱动:负责将MIPI DSI协议转换为物理信号。
3.DRM驱动:负责将MIPI DSI显示屏注册到Linux内核的DRM子系统中。
linux多点触控协议
触摸轨迹
仅有少数设备可以明触的标识真实的 trackingID,多数情况下 trackingID只能来标识一次触摸动作的过程。
手势
多点触摸指定的应用是创建手势动作, TOUCH和 WIDTH参数经常用来区别手指的压力和手指间的距离,另外 MINOR类的参数可以用来区别设备的接触面的大小(点接触还是面接触) ,ORIENTATION可以产生旋转事件。
除了 MAJOR这个参数,还可以提供一个 MINOR参数,手指可以被认为是一个椭圆, MAJOR和 MINOR可以认为是这个椭圆的长轴和短轴,椭圆的中心可以被 ORIENTATION这个参数描述。
ABS_MT_TOOL_TYPE参数用来描述触摸工具的类型(手指,触控笔等)。不同的设备可能有一些其它的信息需要传递到用户层。比如 ABS_MT_BLOB_ID用来描述长方形的边的集合,目前仅有少数设备可以被支持, ABS_MT_TRACKING_ID事件用来向上层报告硬件所采集的手指的触摸轨迹。
TOUCH 和 WIDTH参数给出了个,想想如果一个手指按在玻璃上,透过玻璃你将看到两个区域,一个是手指与玻璃接触的区域,用 ABS_MT_TOUCH_MAJOR描述,一个是手指本身大小的区域, ABS_MT_WIDTH_MAJOR描述, 手指与玻璃接触的面积要小于手指本身的大小,通过这两个参数,可以换算出手指的压力。也可通过 ABS_MT_PRESSURE参数直接提供手指的压力。
仅有少数设备支持,用来区别一个触摸动作的周期。
计算方法
一些设备将触摸面作为一个矩形上报,可以通过下面这些公式来计算出协议中所需要的信息。
触摸屏驱动编写
1、i2c_add_driver将驱动注册到总线上。
2、调用probe函数分配touch_dev结构,ts_init_touch初始化触摸屏设备。
初始化在gpio_request分配io口,初始化workINIT_WORK(&touch_dev->work, zinitix_touch_work),初始化input_dev 并注册inputrequest_irq申请中断对于input初始化:input_mt_init_slots(ts->input_dev, ts->max_touch_num);b类触摸事件需要set_bit(EV_SYN, touch_dev->input_dev->evbit);设置事件响应set_bit(EV_KEY, touch_dev->input_dev->evbit);设置按键响应set_bit(BTN_TOUCH, touch_dev->input_dev->keybit);一次触摸结束响应(后面调用input_report_key(touch_dev->input_dev, BTN_TOUCH, 0);每次报点都要调用。
0表示触摸结束)set_bit(EV_ABS, touch_dev->input_dev->evbit);绝对值事件响应触摸屏调试注意事项:1 、io口配置引脚定义。
(引脚序号是否跟主板一样)2、input初始化,事件设置,参数设置3、多点触摸协议有a类,b类之分。
A类在每次报点后加input_mt_sync(touch_dev->input_dev);B类需要在probe中input_mt_init_slots(ts->input_dev,ts->max_touch_num);再在每次报点前input_mt_slot(ts->input_dev, index);触摸屏驱动编写参考:1、打印信息参考:#ifdef DEBUG#define DBG(fmt ...) printk(KERNINFO fmt)#else#define DBT(fmt ...)#endif#ifdef DEBUGERR#define DEBUGERR(fmt ...) printk(KERNERR fmt) #else##define DEBUGERR(fmt ...)#endif2、互换参考:#ifdef SWAP_XY#define swap_xy(x,y) do{typeof(x) z=x; x=y;y=z;}while(0);#endifX, y 置换注意:input_device中也要置换Input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, X_MAX, 0, 0)Input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, Y_MAX, 0, 0)3、io初始化参考:IO初始化不正确将无法产生中断,#define TOUCH_RST_PIN RK29_PIN6_PC3#define TOUCH_INT_PIN RK29_PIN0_PA2Int ft5306_init_io(void){//用io前首先要申请io,其次注意io设置的延时。
触摸屏驱动软件使用教程
触摸屏驱动软件使用教程第一章:介绍触摸屏驱动软件的作用和分类触摸屏驱动软件是指用于控制和管理触摸屏设备的软件,它能够将触摸屏的触摸信号转化为计算机可识别的输入信号。
根据不同的触摸屏技术,触摸屏驱动软件也分为电容式触摸屏驱动软件、电阻式触摸屏驱动软件、表面声波触摸屏驱动软件等。
第二章:安装触摸屏驱动软件前的准备工作在安装触摸屏驱动软件之前,我们需要确定自己所使用的触摸屏型号和操作系统。
可通过查看触摸屏设备上的标签或查阅相关文档来获取这些信息。
另外,确保计算机已连接触摸屏设备,并且能够正常工作。
第三章:电容式触摸屏驱动软件的安装与配置对于电容式触摸屏,我们需要首先下载适用于该触摸屏型号和操作系统的驱动软件。
下载完成后,双击运行安装程序,按照提示完成安装。
安装完成后,我们需要进入设备管理器,找到触摸屏驱动,确保它已启用并处于正常工作状态。
接着,我们进入控制面板,点击触摸屏选项进行软件配置,根据需要进行灵敏度调整和其他参数设置。
第四章:电阻式触摸屏驱动软件的安装与配置对于电阻式触摸屏,大多数情况下,操作系统已经自带相应的驱动软件,并且会自动识别和安装。
如果没有自动安装驱动,我们可以根据触摸屏型号去官方网站下载对应的驱动程序。
下载完成后,运行安装程序,按照提示完成安装。
安装完成后,我们同样需要进入设备管理器确认驱动已启用并正常工作。
要配置电阻式触摸屏驱动软件,可以在控制面板或者驱动软件提供的设置界面中进行灵敏度调整和其他参数配置。
第五章:表面声波触摸屏驱动软件的安装与配置表面声波触摸屏是一种利用声波传播和反射的技术来获得触摸信息的触摸屏。
安装和配置表面声波触摸屏驱动软件与电容式触摸屏驱动软件类似,首先下载适用于该触摸屏型号和操作系统的驱动软件,运行安装程序完成安装。
再次确认驱动已启用并正常工作后,进入控制面板或者驱动软件提供的设置界面进行相关参数的配置。
第六章:常见问题解决办法6.1 驱动安装失败:可以重新下载驱动软件,并按照安装说明进行操作。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
以 X 轴为例, 当手指去触摸屏幕时, 将吸收相应 位置的声波能量, 声波信号将衰 减, 此 X 轴声波接收 器接收到的波形在时间 t 上形成一个缺口, 通过确24 作者简介: 於琪建 ( 1983- ) , 男, 浙江台州人, 主要从事电子信息系统集成方面的研究. E-m ai:l q ijian1115@ yahoo. com. cn 通信联系人: 张海峰, 男, 副教授, 硕士生导师. E-ma i:l hfzhang0811@ hdu. edu. cn
图 2 file_operations, file, inode关联过程
4 L inux设备模型与输入子系统
L inux 2. 6 内核的一个重要特色是提供了统一的 内核设备模型, 以适应 / 系统的拓扑结构越来越复杂, 智能电源管理、热插拔以及即插即用的支持要求也越 来越高 0的趋势。
简单地说, L inux 设 备模型是基 于内核对 象 kobject来实现的。借助内核对象 kobject机制, 内核通过 / sys向用户控件输出设备的各类信息, 方便了设备的管 理。 / sys是 sysfs文件系统, 它是一个类似于 proc文件 系统的特殊文件系统, 用于将系统中的设备组织成层 次结构, 并向用户模式程序提供关于内核数据结构的 详细信息。
3 L inux字符设备驱动框架
设备驱动程序是操作系统内核和底层硬件之间的 接口。设备驱动程序为上层应用程序屏蔽了硬件的细 节。 L inux系统中设备驱动程序的设计核心思想是: 像 操作普通文件一样对硬件设备进行操作, 它提供了相 应文件 操作函数 的入口点, 如 open( )、read ( ) 、w r ite ( ) 、re lease( ) 等 [ 3-4] 。
图 3 Inpu t子系统层次结构
可以完成触摸屏驱动程序的设计。 D r iver 驱动 层主 要实 现向 内 核注 册 输 入 设备 以 实
现输入设备模型, 并提供将触摸屏采集到的数据上报 Inpu t核心 层的 方法 。 5. 1 输入设备的注册与注销
D r iver 驱动 层通 过调 用如 下 接口 函 数 来 向输 入 子 系统中注册和注销输入设备:
5 触摸屏驱动程序的实现
输入子系统的 3层驱动结构中, Input核心层与 Event处理层已由内核实现, 故采用设备模型与输入子 系统机制, 以简化设备驱动程序的设计。现在只需实 现 Driver驱动层, 即通过调用内核提供的接口函数, 就
# 34#
机电工程
第 26卷
冲区, 而后应用程序根据触摸屏的坐标进行数据校准 ( 坐标转换 ) , 最终光标显示在触摸笔所指的正确的位 置上。
(S choo l of E lectronics Inform ation, H angzhou D ianzi University, H angzhou 310018, Ch ina) Abstrac t: A im ing at simp lify ing the design of traditiona l L inux dr iver and contr ibuting to m anagem ent and m ain tenance o f the dr ive mode,l us ing the input subsystem based on ke rne l ob jec t, the L inux dev ice dr iverps fram ew ork was fu lfilled. The traditiona l m ethod concerned w ith the inte rfaces of file operations w as im proved. The me thod o f only reporting the input event to the L inux system was proposed. T he dr iver w as successfully deve loped. T he test resu lts indicate tha t the driver w orks w el,l and it can control the touch screen prec isely. K ey word s: dev ice dr ive r; dev ice mode;l input subsy stem; touch screen
0引 言
在嵌入式系统中, 触摸屏具有轻便、反应速度快、 节省空间、易于交流等优点, 成为了最简单、方便、自然 的一种人机交互方式。目前, 触摸屏有 4种类型: 电阻 式触摸屏、电容感应式触摸屏、红外线式触摸屏以及表 面声波式触摸屏。它们的实现材料不同, 但在实现原 理上都是一致的。
本研究主要实现 在嵌入式 ARM ( S3C2440) 下的 触摸屏驱动程序, 由于 S3C2440芯片上已有触摸屏的 接口, 为触摸屏驱动的实现创造了条件。
输入子系统 [ 5] 就是借助于 kobject机制 [ 6] 建立起 来的一种设备模型。如图 3所示, 具体由 D river驱动 层、Input核心层、Event处理层 3部分组成。 D river驱 动层主要将输入设备的数据或信息上报给 Input核心 层; Input核心层由 / driver / input / inpu.t c及相关头文件 实现, 它对下提供了 D river驱动层的接口, 对上提供了 Event处理层的接口; Event处理层提供文件操作方法, 它负 责 将数 据 放 到 设备 缓 冲 区, 用 户 程 序 ( 如 M in iGU I) 打开设备节点, 读取缓冲区数据。
L inux设备驱动程序可分为字符设备驱动程序、块 设备驱动程序和网络设备驱动程序, 本研究中触摸屏 设备为字符设备。
字符设备驱动程 序涉及到 3 个重要的数 据结构 体, 即 file_operations, file, inode。它们之间的关系及 其主要结构成员如图 2所示。
用户空间的应用程序要实现对设备文件的操作, 就是对位于 / dev下 的设备文件进行操作。这 些设备 文件在内核中就由 inode结构表示。当用 L inux 系统 提供的 open方法打开一个设备节点文件的时候, 就会 有表示该文件描述符 fd的 file结构指向这个 inode结 构, 具体实现方式是经过 dentry结构体 ( 目录结构体 ) 产生关联。可以有多个文件 描述符指向 inode结 构, 也就是一个设备文件可以按不同的方式打开多次。
( 1) input_reg ister_device( ); ( 2) input_unreg ister_dev ice( ) 。 以上两个函数调用的参数为 input_dev结构体 ( 在 dr iver/ input / inpu.t h中定义 )。注册完成之后, 内核为 输入设备在 Event处理层关联了相应的文件操作方法 ( 在 / driver/ input / tsdev. c中实现 )。这样, 当设备成功 注册后, 就会在 / dev 下生成相应的设备文件, 从而形 成 L inux 设备驱动程序基本框架。 5. 2 触摸屏驱动实现过程 如图 4所示, 首先触摸屏处于等待中断模式 上。 当触摸笔按 下的时候, 触摸屏将 产生一个 中断 INT _ TC。然后触摸屏将触发进入 X /Y 轴单独转化模式, 也 就是当 X 模式转换完成之后, 将产生一个 INT _ADC 中 断, 此时将进入 Y 模式转换, 当 Y 模式转换完成后, 将 又会产生一个 INT _ADC 中断。此时, 进入中断程序, 将分别取得 X 坐标值与 Y 坐标值。然 后驱动程序调 用接口函数 inpu t_report_abs( ), 将取得的值上报给 Inpu t核 心层 。
第 3期
於琪建, 等: L inux 输入子系统在触摸屏驱动上的实现
# 33#
2 S3C2440触摸屏接口
S3C2440芯片支持触摸屏的接口, 其包含触摸屏 控制器、4个外部场效应管及 1个外部电压源, 具体连 接可见芯片手册上的 ADC 和触摸屏接口操作框图 [ 1] 。
该触摸屏接口有 4种工作模式 [ 2 ] : ( 1) 正常转换模式: 用于通用 A /D转化; ( 2) 单独 X /Y 转化模式: 先进行 X 模式转换, 等 X 坐标转换完成之后, 产生 ADC中断, 然后进入 Y 模式转 换, 将 Y 坐标转换完成之后, 又产生一次 ADC中断; ( 3) X /Y 自动转换模式: 触摸屏自动地转换 X 坐 标和 Y 坐标; ( 4) 等待中断模式: 一般触摸屏开始工作后, 将处 于此模式。只有当触摸笔按下时, 触摸屏控制器才产 生一个 INT _TC 中断信号。 触摸屏接口工作模式涉及到 S3C2440 的 A /D 寄 存器主要有 4 个: ADC 控制寄存器、ADC 触摸控制寄 存器、ADC 开始延迟 寄存器、ADC 数据 转化寄存 器。 所得坐标将存入 ADC数据转化寄存器中。
第 26卷第 3期 2009年 3月
机电 工程
M echan ical& E lectrical Eng inee ring M agazine
V o.l 26 N o. 3 M a r. 2009
L inux输入子系统在触摸屏驱动上的实现
於琪建, 张海峰
(杭州电子科技大学 电子信息学院, 浙江 杭州 310018)
图 4 D river驱动层与 Input核心层关联
Input核心层找出对应的事件处理函数, 由 Event 处理层将数据放到设备缓冲区, 如图 5所示。上层驱 动处理函数将在 应用程序 ( 如 M in iGU I) 调用驱 动的 read函数时, 将设备缓冲区的数据拷贝到用户程序缓
摘 要: 为了简化传统 L inux 驱动程序设计及便于对驱动模块的管理与维护, 采用了基于内核对象的输
入子系统, 完成了 L inux设备驱动框架, 改进了传统基于文件操作接口的驱动程序设计方法, 提出了只