【驱动】TP电容屏驱动—5.触摸屏调试笔记

1、硬件
1>复位引脚:GPIO19__TP_RESET,
复位方法: 原来的复位是把复位引脚=“高->低->高”操作;
现在:“拉高->拉低”操作;
Register Descriptions (base: 0x1000_0600)
GPIO23_00_DATA: PIO Pin Data (offset: 0x0020)
GPIO23_00_SET: Set PIO Pin Data Bit (offset: 0x002C)
GPIO23_00_RESET: Clear PIO Pin Data Bit (offset: 0x0030)
测试R418左引脚电平:
未操作:3.256V,高电平
reg s 0
reg r 0x620 //0x320780或0x100780,GPIO19_bit[19]=0;
测试:3.256V,高电平
reg w 0x62c 0x00080000 //GPIO19_SET拉高; 3.256V;
reg w 0x630 0x00080000 //GPIO19_RESET拉低; 3.256V;
测试:3.254V,reg r 0x620=<4>0x100780,bit[19]=0;

# reg r 0x624
<4>0xef87ff,bit[19]=1=输出引脚;

GPIO19: ____|————|____
TP_RESET: ————|____|————3.3V
还是?
GPIO19: ————|____
TP_RESET: ____|————3.3V

Register Descriptions (base: 0x1000_0600)
GPIO39_24_DATA: PIO Pin Data (offset: 0x0048)
GPIO39_24_SET: Set PIO Pin Data Bit (offset: 0x0054)
GPIO39_24_RESET: Clear PIO Pin Data Bit (offset: 0x0058)
测试R418左引脚电平:
未操作:3.256V,高电平
reg r 0x648 //0x1000,bit[14]=0;
reg w 0x654 0x4000 //3.256V,
reg w 0x658 0x4000 //3.256V,


2>内核打印
#cat proc/kmsg & //打开内核打印开关;


问题1:触摸屏的原理:触摸屏->AT24CXX->I2C->芯片MT7620?
答:不是,触摸屏->ILI2117A芯片->I2C->芯片MT7620。

设备地址问题:
2K、4K和8K EEPROM设备都需要一个8位的设备地址字,在启动条件下,为读取或写入操作启用芯片(参见图1)。
该设备地址词由一个强制的一个,零序列组成,第四个最重要的位显示。这是所有EEPROM设备的共同点。
接下来的3位是A2、A1和A0设备地址位,用于2K EEPROM。这3位必须与它们对应的硬连线输入引脚相比。
4K EEPROM只使用A2和A1设备地址位,第三位是内存页面地址位。这两个设备地址比特必须与它们对应的硬连线输入引脚相比。A0 pin没有连接。
8K EEPROM只使用A2设备地址比特与接下来的2位用于内存页面寻址。A2bit必须与相应的硬连线输入pin相比。
A1和A0别针没有连接。
第8位设备地址是read / write操作选择位。如果这个比特是高的,如果这个比特很低,则启动了读操作。
在设备地址的比较中,EEPROM将输出一个零。如果没有进行比较,芯片将返回到备用状态。

STATUS: I2C Status Register (offset: 0x0018)
I2C确认错误检测
0:表示固件正在写入STARTXFR register。
1:表示在发送设备地址、地址或数据之后,主机控制器没有从I2C从设备接收到正确的确认。

问题:I2C设备是否有片选引脚?
主控器件通过地址码建立多机通信的机制,因此I2C总线省去了外围器件的片选线,这样无论总线上挂接多少个器件,其系统仍然为简约的二线结构。


=============================================
i2c通信:
问题1:MT7620A的I2C读字节的操作流程是什么?
答: Start信号->虚写(dev_addr和word_addr)->读数据;
问题2:开始信号怎么发送?
答:单片机是用通过先后配置SDA/SCL的电平状态来实现,如:
void init(void) //总线初始化 将总线都拉高一释放总线  发送启动信号前,要先初始化总线。
即总有检测到总线空闲才开始发送启动信号 
{
sda = 1;
delay();
scl = 1;
delay();
}
void start(void) //开始信号,SCL在高电平期间,SDA一个下降沿则表示启动信号;
{
sda = 1; //释放SDA总线;
delay();
scl = 1;
delay();
sda = 0;
delay();
}

MT7620等直接用配置寄存器实现,如:??
ra_outl(RALINK_I2C_STARTXFR_REG, WRITE_CMD); /* I2C_Start */

问题3:应答是怎么回事儿?为什么会报错?
问题4:


============================
触摸屏2:
问题0:MT7620DA的设备地址是怎么处理的?和arm的区别?
答:MT7620DA的7bit设备地址(不包含读写位)存入寄存器RALINK_I2C_DEVADDR_REG(DEVADDR: I2C Device Address Register (offset: 0x0008))
问题1:触摸板是从-主模式还是主-从模式?ALC5621呢?寄存器初始化需要修改吗?
答:应该都是主-从模式;

1月4日-周六最后测试:
源码:
#if 1
printf("\nEnter i2c_test read:\n");
regdat = read_ft5x0x_i2c(FT5x0x_REG_FW_VER);
printf("FT5x0x_REG_FW_VER = 0x%x\n", regdat);
regdat = read_ft5x0x_i2c(FT5x0x_REG_POINT_RATE);
printf("FT5x0x_REG_POINT_RATE = 0x%x\n", regdat);
regdat = read_ft5x0x_i2c(FT5X0X_REG_THGROUP);
printf("FT5X0X_REG_THGROUP = 0x%x\n", regdat);

printf("\nEnter i2c_test read:\n");
regdat = read_ft5x0x_i2c(FT5x0x_REG_FW_VER + 3);
printf("FT5x0x_REG_FW_VER = 0x%x\n", regdat);
regdat = read_ft5x0x_i2c(FT5x0x_REG_POINT_RATE + 3);
printf("FT5x0x_REG_POINT_RATE = 0x%x\n", regdat);
regdat = read_ft5x0x_i2c(FT5X0X_REG_THGROUP + 3);
printf("FT5X0X_REG_THGROUP = 0x%x\n", regdat);

regdat = read_ft5x0x_i2c(0x0);
printf("[0x0] = 0x%x\n", regdat);

regdat = read_ft5x0x_i2c(0x1);
printf("[0x1] = 0x%x\n", regdat);

regdat = read_ft5x0x_i2c(0x2);
printf("[0x2] = 0x%x\n", regdat);

regdat = read_ft5x0x_i2c(0x3);
printf("[0x3] = 0x%x\n", regdat);

regdat = read_ft5x0x_i2c(0x4);
printf("[0x4] = 0x%x\n", regdat);

regdat = read_ft5x0x_i2c(0x5); /* 得到: 0x31cc */
printf("[0x5] = 0x%x\n", regdat);

#else
6: System coming VPT testing!!!!.
i2c_master_init
RALINK_REG_CLKCFG1=0x75AFFFC0

RALINK_REG_PIODATA = 0xb0000620
RALINK_REG_PIODATA = 0xb0000620
RALINK_REG_PIODATA = 0xb0000620
IIC is ready now

Enter i2c_test read:
FT5x0x_REG_FW_VER = 0x0
FT5x0x_REG_POINT_RATE = 0x0
FT5X0X_REG_THGROUP = 0x0

Enter i2c_test read:
FT5x0x_REG_FW_VER = 0x0
FT5x0x_REG_POINT_RATE = 0x0
FT5X0X_REG_THGROUP = 0x0
[0x0] = 0x0
[0x1] = 0x8100
[0x2] = 0x0
[0x3] = 0x100
[0x4] = 0x5630
[0x5] = 0x31cc
=====i2c_test over.=====
问题2:ILI2117的地址线A0没有接吗?为什么读出来的值都是2字节?
答:

问题3:ILI设备设备信息存储在哪些寄存器中?读取的方式?
答:是否是
TP_VENDER //=1; //地址=0x3-> [0x1]高位字节;
FW_TEST_VERSION //=0; //地址=0x5->[0x2]高位字节, 0x6->[0x3]低位字节;
???

==================================================
TS驱动:
1.修改驱动,通过编译;
1.1当驱动与设备系统有较大出入时,使用设备系统的自有接口驱动,如:I2C驱动;
若仍使用系统自带的I2C将需要对系统I2C接口初始化做一定的处理;
2.编写测试程序;
3.实验测试;


Register Descriptions (base: 0x1000_0600)
GPIO71_40_INT: PIO Pin Interrupt Status (offset: 0x0060)


/*
* Simple, straightforward mutexes with strict semantics:简单,直接的互斥体与严格的语义:
*
* - only one task can hold the mutex at a time
* - only the owner can unlock the mutex
* - multiple unlocks are not permitted
* - recursive locking is not permitted
* - a mutex object must be initialized via the API
* - a mutex object must not be initialized via memset or copying
* - task may not exit with mutex held
* - memory areas where held locks reside must not be freed
* - held mutexes must not be reinitialized
* - mutexes may not be used in hardware or software interrupt
* contexts such as tasklets and timers
*
* These semantics are fully enforced when DEBUG_MUTEXES is
* enabled. Furthermore, besides enforcing the above rules, the mutex
* debugging code also implements a number of additional features
* that make lock debugging easier and faster:
*
* - uses symbolic names of mutexes, whenever they are printed in debug output
* - point-of-acquire tracking, symbolic lookup of function names
* - list of all locks held in the system, printout of them
* - owner tracking
* - detects self-recursing locks and prints out all relevant info
* - detects multi-task circular deadlocks and prints out all affected
* locks and tasks (and only those tasks)
*/
/*简单,直接的互斥体与严格的语义:
*一次只能有一个任务持有互斥锁
* -只有拥有者可以解锁互斥锁
* -不允许多次解锁
* -不允许递归锁定
* -互斥对象必须通过API初始化
* -互斥对象不能通过memset或复制来初始化
* -任务可能不退出互斥锁持有
* -持有锁所在的内存区域不能被释放
* -不能重新初始化被持有的互斥对象互斥体不能用于硬件或软件中断
*上下文,如微线程和计时器**当DEBUG_MUTEXES被执行时,这些语义将被完全执行*
启用。此外,除了执行上述规则外,互斥锁还可以执行*调试代码还实现了许多附加功能*使锁调试更容易和更快:*

* -在调试输出中打印互斥对象时,使用它们的符号名
* -获取点跟踪,函数名的符号查找
* -系统中所有锁的清单,打印出来
* -业主追踪检测自递归锁并打印出所有相关信息检测多任务循环死锁并打印出所有受影响的锁和任务(仅限那些任务)
* /
struct mutex {
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
atomic_t count;
spinlock_t wait_lock;
struct list_head wait_list;
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
struct thread_info *owner;
#endif
#ifdef CONFIG_DEBUG_MUTEXES
const char *name;
void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};

Linux的GPIO子系统解析 ( 一 )
/*
* Some platforms don't support the GPIO programming interface.
*
* In case some driver uses it anyway (it should normally have
* depended on GENERIC_GPIO), these routines help the compiler
* optimize out much GPIO-related code ... or trigger a runtime
* warning when something is wrongly called.
*/
/*
*有些平台不支持GPIO编程接口。
*如果一些驱动程序使用它(它通常应该依赖于GENERIC_GPIO),这些例程帮助编译器优化出很多gpio相关的代码…或者在错误调用某个东西时触发运行时警告。
*/
static inline int gpio_is_valid(int number)
static inline int gpio_request(unsigned gpio, const char *label)
static inline void gpio_free(unsigned gpio)
static inline int gpio_direction_input(unsigned gpio)
static inline int gpio_direction_output(unsigned gpio, int value)

static inline int gpio_set_debounce(unsigned gpio, unsigned debounce)
static inline int gpio_get_value(unsigned gpio)
static inline void gpio_set_value(unsigned gpio, int value)
static inline int gpio_cansleep(unsigned gpio)
static inline int gpio_get_value_cansleep(unsigned gpio)
static inline void gpio_set_value_cansleep(unsigned gpio, int value)
static inline int gpio_export(unsigned gpio, bool direction_may_change)
static inline int gpio_export_link(struct device *dev, const char *name, unsigned gpio)
static inline int gpio_sysfs_set_active_low(unsigned gpio, int value)
static inline void gpio_unexport(unsigned gpio)
static inline int gpio_to_irq(unsigned gpio)
static inline int irq_to_gpio(unsigned irq)


/**
* wait_event_interruptible -在条件变为真之前休眠
* @wq:等待的队列
* @condition:要等待的事件的C表达式
* 进程进入休眠状态(TASK_INTERRUPTIBLE),直到@condition的计算结果为true或收到信号。
* 每次唤醒@wq等待队列时都会检查@condition。
* wake_up()必须在更改任何可能更改等待条件结果的变量之后调用。
* 如果被一个信号打断,函数将返回-ERESTARTSYS;如果@condition被赋值为true,则返回0。
*/
#define wait_event_interruptible(wq, condition)

在amlogic芯片的I2C是在drivers/amlogic/i2c/aml_i2c.c中,不过名字为aml_i2c_xfer()。如下:
static const struct i2c_algorithm aml_i2c_algorithm = {   
.master_xfer = aml_i2c_xfer,   
.functionality = aml_i2c_func, 
};

/*General i2c master transfer*/
static int mt7620_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
{
struct aml_i2c *i2c = &aml_i2c_ddata;
struct i2c_msg * p;
unsigned int i;
unsigned int ret=0;
spin_lock(&i2c->lock);
i2c->ops->xfer_prepare(i2c);
for (i = 0; !ret && i < num; i++)
{
p = &msgs[i];
i2c->msg_flags = p->flags;
ret = i2c->ops->do_address(i2c, p->addr, p->buf, p->flags & I2C_M_RD, p->len);
if (ret || !p->len)
continue;
if (p->flags & I2C_M_RD)
ret = i2c->ops->read(i2c, p->buf, p->len);
else
ret = i2c->ops->write(i2c, p->buf, p->len);
}
i2c->ops->stop(i2c);
spin_unlock(&i2c->lock);
if (p->flags & I2C_M_RD)
{
AML_I2C_DBG("read ");
}
else
{
AML_I2C_DBG("write ");
}
for(i=0;i<p->len;i++)
{
AML_I2C_DBG("%x-",*(p->buf)++);
}
AML_I2C_DBG("\n");

/* Return the number of messages processed, or the error code*/ ?
if (ret == 0)
return num;
else
{
printk("[aml_i2c_xfer] error ret = %d \t", ret);
printk( "i2c master %s current slave addr is 0x%x\n", i2c->master_no?"a":"b", i2c->cur_slave_addr);
return ret;
}
}

#(1)编译子目录
# 进入工程的根目录,在shell中运行export TOPDIR=$(pwd)
# 然后直接make appl或者进入子目录运行make

#(2)清除
# make clean 清除.o文件
# make distclean 清除.o和.d文件

问题1:函数的参数在传输过程中会出问题吗?为什么总是出现Segmentation fault?
答:段错误,一般是出现野指针或对空指针的调用而出现的?。
所谓的段错误就是指访问的内存超过了系统所给这个程序的内存空间,通常这个值是由gdtr来保存的,他是一个48位的寄存器,其中的32位是保存由它指向的gdt表,
后13位保存相应于gdt的下标,最后3位包括了程序是否在内存中以及程序的在cpu中的运行级别,指向的gdt是由以64位为一个单位的表,在这张表中就保存着程序运
行的代码段以及数据段的起始地址以及相应的断限和页面交换还有程序运行级别和内存粒度等信息,一旦一个程序发生了越界访问,CPU就会产生相应的异常保护,
于是segmentation fault就出现了。
即“当程序试图访问不被允许访问的内存区域(比如,尝试写一块属于操作系统的内存),或以错误的类型访问内存区域(比如,尝试写一块只读内存)。这个描述
是准确的。为了加深理解,我们再更加详细的概括一下SIGSEGV。段错误应该就是访问了不可访问的内存,这个内存要么是不存在的,要么是受系统保护的。
Ø SIGSEGV是在访问内存时发生的错误,它属于内存管理的范畴
Ø SIGSEGV是一个用户态的概念,是操作系统在用户态程序错误访问内存时所做出的处理。
Ø 当用户态程序访问(访问表示读、写或执行)不允许访问的内存时,产生SIGSEGV。
Ø 当用户态程序以错误的方式访问允许访问的内存时,产生SIGSEGV。
用户态程序地址空间,特指程序可以访问的地址空间范围。如果广义的说,一个进程的地址空间应该包括内核空间部分,只是它不能访问而已。
————————————————
版权声明:本文为CSDN博主「喜欢恋着风」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u010150046/article/details/77775114

问题2:明明在i2c.c中已经定义了mt7620_i2c_xfer()函数,且EXPORT_SYMBOL(mt7620_i2c_xfer)修饰,但是在执行ili2117.ko模块时仍发生错误:
ili2117: Unknown symbol mt7620_i2c_xfer (err 0)
insmod: cannot insert 'ili2117.ko': Success
答:原因找到了,加载ili2117.ko模块之前卸载了板子的所有驱动(包括i2c所在的ramips_mt7620模块),因此必然出现“Unknown symbol mt7620_i2c_xfer”
错误。

问题3:目前的内核是否使用了Linux内核自带的I2C/input子系统、设备树?如何和奕力TP原厂的ILI2117_TP驱动对接起来?
答:都没有,目前公司的系统里面,自定义了i2c驱动,且直接编译进内核了,不过这个i2c功能比较简单,只有i2c_read/i2c_write(),没有比较丰富的
i2c操作函数(尤其i2c子系统常用的内核i2c-core.c中的一些函数,例如:i2c_transfer())。ILI2117_TP驱动的最底层是内核i2c_transfer()函数,而我们的
i2c驱动最底层是i2c_read/i2c_write(),通过查验i2c_transfer()函数可知其到底是调用某CPU的master_i2c_xfer()函数。因此,在i2c.c文件中定义
mt7620_i2c_xfer()函数并实现其内部读写函数:
if (msgs[i].flags & I2C_M_RD) /* 2.读数据 */
{
ret = do_master_read(&msgs[i]);
}
else /* 3.写数据 */
{
ret = do_master_write(&msgs[i]);
}
的定义与实现。
备注:关于内核的I2C/input子系统、设备树可以通过make menuconfig配置选项进系统。

问题4:为什么mt7620_i2c_xfer()函数名字改成i2c_transfer()时,且内部实现一致时,编译时总是发生一些未知/莫名的错误?
答:不要定义和内核函数同名的函数,否则由于头文件包含的原因,极易因调用混乱而产生错误。

问题5:ILI2117_TP持续修改测试方法
答:前言:由于TP和keys_leds的GPIO冲突,执行fv_drv.sh脚本,卸载所有驱动,然后重新加载fvdd/ramips_mt7620,在公司的i2c平台上实现i2c操作,
然后再加载ili2117.ko驱动并调试。
①每次修改前,先把之前的代码复制一份(名字不用改动),然后再修改;
②用D:\shared_dir\driver_ts_20191230\fip11c\build目录下的keys_leds.ko复制到D:\shared_dir\driver_tp_20200116\fip15\build目录下;
③保持fv_drv.sh不变,不要删除该文件(xx_OK.sh是目前可以满足要求的脚本);
④下载fv_drv.sh到/tmp目录,chmod 777 fv_drv.sh;
⑤./fv_drv.sh
⑥insmod fv_drv.sh

问题6:加载ili2117.ko之后,执行期间发生错误:
<4>*** ilitek_get_customer_firmware_version ***
<1>CPU 0 Unable to handle kernel paging request at virtual address 00000000, epc == 80013240, ra == 80013018
原因何在?
答:互斥锁的原因,暂时关闭即可。
//mutex_lock(&ilitek_data->ilitek_mutex);
ret = ilitek_i2c_write_and_read(data, 1, 5, data, 4);
p_customer_fw_ver = kzalloc(sizeof(u8) * 11, GFP_KERNEL);
//mutex_unlock(&ilitek_data->ilitek_mutex);
找到原因了,互斥锁没有初始化。


=============
2020.02.16:
问题7:不管是报点协议A还是协议B,当发生触点中断时,从ILI2117上报SOC的报文格式是一样的吗?
或者说报文格式是什么样的呢?
答:查看数据手册《I2C Programming Guide(ILI2117A/ILI2118A )》——4.3. Data Report;
报点协议与获得从从机ILI2117发送给SOC的报文没有关系,与input上报给应用层的机制有关系。

问题8:坐标数据在报文中的什么位置?如何提取?可以完全绕开输入子系统吗?
坐标怎么计算?
答:
如果只是单纯的得到触点的数据坐标,那么只需要ilitek_i2c_read(buf, read_packet_len)之后读取buf[]的数据即可,
ilitek_touch_down()和ilitek_touch_release()及其他input数据加工上报的函数,
将失去存在的意义。
坐标计算:
int X0= ((buf[1]>>4) & 0xf)<<8 + buf[2];
int Y0= ((buf[1]>>0) & 0xf)<<8 + buf[3];

int X1= ((buf[5]>>4) & 0xf)<<8 + buf[6];
int Y1= ((buf[5]>>0) & 0xf)<<8 + buf[7];

int X2= ((buf[9]>>4) & 0xf)<<8 + buf[10];
int Y2= ((buf[9]>>0) & 0xf)<<8 + buf[11];

#define TOUCH_SCREEN_X_MAX (480) /*LCD_WIDTH,默认值:1080*/
#define TOUCH_SCREEN_Y_MAX (272) /*LCD_HEIGHT,默认值:1920*/


问题9:TP中断添加
答:
(0)复制最新修改的ilitek_mian.c文件为ilitek_mian_2020216.c
(1)查看上次编译的ili2117.ko的时间,查出ilitek_main.c的源码版本,重新编译并查看TP信息读取是否正确;
ili2117.ko:2月4日15:31; 选取ilitek_mian.c
(2)在上次编译的基础上添加中断,并重新编译;


问题10:关于中断程序,是合并到ilitek_main.c文件中好还是仍保留到ilitek_touch_screen.c文件,是否具有可行性?优劣势?
答:合并:可行,优势:逻辑简单;劣势:文件更大了,程序可移植性变差了;
不合并:
问题:两个文件的某些函数将相互调用,关于中断的处理,将变得复杂;

问题11:已经基本实现中断和报点,接下来需要做什么修改?
答:(1)报点函数,逐渐恢复其原来的功能;
(2)开启线程;
(3)中断多次上报问题; 也即上升/下降沿问题;
(4)如何与APP交接数据;
(5)文档;(提前)
(6)4个虚拟键,没有搞啊。


问题12:若在中断处理中有调用I2C,则中断中需要屏蔽中断吗?还用信号量可以吗?
答:(1)应该不需要。
(2)不可以用信号量,否则可能造成休眠,且无法唤醒。
注:
①在中断处理过程中,必须屏蔽中断,否则系统运行会出现错误,这种说法正确吗?
正确答案:B-不正确
解析:
CPU通过指令限制某些设备发出中断请求,称为屏蔽中断。
从CPU要不要接收中断即能不能限制某些中断发生的角度 ,中断可分为
可屏蔽中断 :可被CPU通过指令限制某些设备发出中断请求的中断
不可屏蔽中断:不允许屏蔽的中断如电源掉电

②屏蔽中断和关中断的概念不一样,屏蔽中断是指不允许发生中断,而关中断是为了避免中断寄存器的值被其他的中断发生更改。
采用中断屏蔽技术,会封锁(与中断屏蔽标志相对应的事件 )的响应;

③在中断处理过程中,中断屏蔽功能可以起______的作用。
A.设置中断优先级
B.改变中断优先级
C.增加中断优先级
D.撤销中断优先级
正确答案:B


问题13:请教linux irq 中断能使用mutex互斥锁吗?
答:spin lock 可以用在中断处理程序用, 其他的 如信号量等是不行的,可能引起睡眠的函数都不能用在中断里.
在中断上下文中不能采用同步原语(信号量,管程、同步变量等)和长期互斥原语(这会导致进程睡眠), 而只能
采用短期互斥原语(例如自旋锁)。
解析:在执行临界区代码(非中断环境),获取自旋锁前,先屏蔽本地中断,如此执行临界区代码时将不会有中断发生。
否则,中断响应后,处理函数执行到自旋锁处时将自旋,等待该锁重新可用,但是锁的持有者在该中断处理程序执行
完毕之前不可能运行,这就成为了双重请求死锁。
例:
spinlock_t i2c_lock=SPIN_LOCK_UNLOCKED; //或者DEFINE_SPINLOCK(i2c_lock);(include/linux/spinlock_types.h)
unsigned long flags;
spin_lock_irqsave(&i2c_lock, flags);
/*临界区*/
spin_unlock_irqrestore(&i2c_lock, flags);

函数spin_lock_irqsave():保存中断的当前状态,禁止本地中断,然后获取指定的锁。
函数spin_unlock_reqrestore():对指定的锁解锁,让中断恢复到加锁前的状态。所以即使中断最初是被禁止的,代码也不会错误地激活它们。

问题14:互斥量定义成全局还是静态?
答:暂时全局吧。

问题15:read_proc的功能类似字符操作read函数,用什么方法传输信息到应用层?
对坐标数据以什么格式传输到应用层?
答:
方案1:用/proc节点文件保存触点数据
(1)参考《linux设备驱动程序》第三版的第四章“调试技术”的‘/proc文件’一节:
(2)与伊工沟通,是直接把I2C传输过来的数据直接拷过去,还是重新处理之后再传过去,如果处理该怎么处理?
答:方案1:ilitek_read_data_and_report()仍保留坐标和虚拟键的处理,并同时保存从ILI2117传输过来的数据
到全局静态缓冲区report_buf[50];

方案2:研究网上的坐标上报案例;


问题16:问伊工,有没有虚拟键,坐标范围(即起始坐标和长宽);
答:有,坐标范围未知。


问题17:cat /proc/ks_tp_report 命令问什么会造成多次proc_read操作?
答:proc_read函数:static int ilitek_tp_report_proc_read(char * buf, char **start, off_t offset, int count, int *eof, void *data);
proc_read函数的问题,若函数处理中,调用函数形参offset、count,则函数处理会出问题,造成无限打印。具体根本原因,尚未知。
例1:简单led的/proc_read处理:
static int led_proc_read(char * buf, char **start, off_t offset, int count, int *eof, void *data)
{
if (count > 4)
count = 4;
memcpy(buf, "test", count);
*eof = 1;
return count;
}
例2:
static int temp = 0;
static ssize_t proc_read(char *buffer, char **buffer_location,off_t offset, int buffer_length, int *eof, void *data)
{
sprintf(buffer,"read ,demo temp is %d",temp);
printk(KERN_NOTICE "read ,demo temp is %d\n",temp);
return 0;
}
操作:
# insmod ili2117.ko
(进行触摸屏点击操作...)
# ls /proc/
... ks_tp_report
# cat /proc/ks_tp_report
<4>ilitek_tp_report_proc_read is here!
<4>--- start read ---
<4>--- read over ---

注:对于/proc节点文件,echo就是写,cat就是读操作。

问题18:ILI2117发给SOC的43字节报文中,那个手指触点ID的排位规则是什么?按照时间吗?
如果触摸滑动只要手指不离开屏幕,无论做什么动作(横向滑动/圆周滑动等),该手指的ID
是不是都不会变化?
答:ID0的资料只会放在byte1-byte3,ID1的资料只会放在byte5-byte7,不是按时间先后的,
你一直按着,ID是不会跳动的。
如果byte1-byte3都为0xff,ID0就為release,以此类推。


每个触摸点都有一个ID,它是按照该触摸点加入到触摸动作中的顺序来分配的。触摸点的ID从用户接触触摸屏开始到接触释放期间会保持不变。
当接触点释放时,与之关联的触摸点就不再属于该触摸动作的一部分了。
例如,如果使用两根手指触摸屏幕,分配给第一个触摸点的ID就是1,分配给第二个接触点的ID就是2。
如果第二根手指从触摸屏离开,则只有触摸点1仍然是该触摸动作的一部分。
如果又有另外一根手指加入到该触摸动作中,分配给新的触摸点的ID是3,该触摸动作此时有1和3两个触摸点。(存疑,实验现象显示,当第二个手指
离开后,该ID释放,再有手指按下时,将仍分配ID2。若第三手指在第二手指未离开时按下,则分配ID3,即使手指2离开仍保持ID不变)


check_sum = p_msg[0] + ... + p_msg[41];
check_sum = (uint8_t) ((-check_sum) & 0xFF);
if (check_sum != buf[42]){ERROR!}

测试1:
动作:先放第一个手指Pnt[0],再放第二个手指Pnt[1],Pnt[1]水平向左滑动,
然后再放第三个手指Pnt[2],然后放开第二个手指Pnt[1]。
打印:
①先放第一个手指Pnt[0]
<4>Data Report: Pnt[0]=(228, 120)
<4> Data Report: release point counter = 9

②再放第二个手指Pnt[1],第二个手指Pnt[1]水平向左滑动
<4>Data Report: Pnt[0]=(227, 118)
<4> Data Report: Pnt[1]=(116, 130)
<4> Data Report: release point counter = 8

<4>Data Report: Pnt[0]=(226, 114)
<4> Data Report: Pnt[1]=(110, 135)
<4> Data Report: release point counter = 8

<4>Data Report: Pnt[0]=(225, 110)
<4> Data Report: Pnt[1]=(90, 147)
<4> Data Report: release point counter = 8

<4>Data Report: Pnt[0]=(225, 111)
<4> Data Report: Pnt[1]=(82, 151)
<4> Data Report: release point counter = 8

<4>Data Report: Pnt[0]=(225, 111)
<4> Data Report: Pnt[1]=(80, 153)
<4> Data Report: release point counter = 8

<4>Data Report: Pnt[0]=(225, 111)
<4> Data Report: Pnt[1]=(73, 158)
<4> Data Report: release point counter = 8

<4>Data Report: Pnt[0]=(225, 112)
<4> Data Report: Pnt[1]=(70, 159)

③再放第三个手指Pnt[2]
<4>Data Report: Pnt[0]=(224, 115)
<4> Data Report: Pnt[1]=(70, 159)
<4> Data Report: Pnt[2]=(368, 170)
<4> Data Report: release point counter = 7

④放开第二个手指Pnt[1]
<4>Data Report: Pnt[0]=(224, 117)
<4> Data Report: release point id = 1
<4>Data Report: Pnt[2]=(368, 170)
<4> Data Report: release point counter = 8

<4>Data Report: Pnt[0]=(224, 118)
<4> Data Report: Pnt[2]=(368, 170)
<4> Data Report: release point counter = 8

<4>*** ilitek_read_data_and_report ***
<4>Data Report: Pnt[0]=(224, 119)
<4> Data Report: release point id = 2
<4>Data Report: release point counter = 9
<4>*** ilitek_read_data_and_report ***
<4>Data Report: Pnt[0]=(223, 119)
<4> Data Report: release point counter = 9


测试2:手指1按下不动,手指2按下然后抬起,再然后手指3按下并抬起:
<4>Data Report: Pnt[0]=(368, 68)
<4> Data Report: release point counter = 9

<4>Data Report: Pnt[0]=(368, 68)
<4> Data Report: Pnt[1]=(115, 61)
<4> Data Report: release point counter = 8

<4>Data Report: Pnt[0]=(368, 68)
<4> Data Report: release point id = 1
<4>Data Report: release point counter = 9
...
<4>Data Report: Pnt[0]=(368, 68)
<4> Data Report: release point counter = 9

<4>Data Report: Pnt[0]=(368, 68)
<4> Data Report: Pnt[1]=(95, 253)
<4> Data Report: release point counter = 8

<4>Data Report: Pnt[0]=(368, 68)
<4> Data Report: release point id = 1
<4>Data Report: release point counter = 9

<4>Data Report: release point id = 0
<4>Data Report: release point counter = 10

问题19:触摸屏支持的手势和产生的事件类型
手势 描述 产生的事件
旋转 两根手指做旋转动作,一根手指绕另一根手指做顺时针运动来让该对象顺时针旋转,反之亦然。 · ROTATION_STARTED· ROTATE· ROTATION_FINISHED
滚动 滑动动作,向上或向下滑动来做竖直滚动,向左或向右滑动来做水平滚动 · SCROLL_STARTED· SCROLL· SCROLL_FINISHED如果用鼠标的滚轮来滚动,则只会产生SCROLL事件
轻扫 通过屏幕或者触摸板向上、下、左、右方向的轻扫动作。对角线运动不会被识别为轻扫动作。 · SWIPE_LEFT· SWIPE_RIGHT· SWIPE_UP· SWIPE_DOWN
每个轻扫手势只会生成一个轻扫事件,但也会生成SCROLL_STARTED、SCROLL和SCROLL_FINISHED事件。
缩放 两支手指做捏的动作,捏合在一起表示缩小,分开表示放大。 · ZOOM_STARTED· ZOOM· ZOOM_FINISHED

猜你喜欢

转载自www.cnblogs.com/xiaohujian/p/12370842.html