转自:https://www.cnblogs.com/lifexy/p/7628889.html

本节的触摸屏驱动也是使用之前的输入子系统


1.先来回忆之前第12节分析的输入子系统

其中输入子系统层次如下图所示,

其中事件处理层的函数都是通过input_register_handler()函数注册到input_handler_list链表中

搜索input_register_handler注册函数,就可以看到都是事件处理层里的函数:

所以最终如下图所示:

右边的驱动事件处理,内核是已经写好了的,所以我们的触摸屏只需要写具体的驱动设备,然后内核会与触摸屏驱动tsdev.c自动连接

2.本节需要用到的结构体成员如下:

struct input_dev {
void *private;
const char *name; //设备名字
const char *phys; //文件路径,比如 input/buttons
const char *uniq;
struct input_id id; unsigned long evbit[NBITS(EV_MAX)]; //表示支持哪类事件,常用有以下几种事件(可以多选)
//EV_SYN 同步事件,当使用input_event()函数后,就要使用这个上报个同步事件
//EV_KEY 键盘事件
//EV_REL (relative)相对坐标事件,比如鼠标
//EV_ABS (absolute)绝对坐标事件,比如摇杆、触摸屏感应
//EV_MSC 其他事件,功能
//EV_LED LED灯事件
//EV_SND (sound)声音事件
//EV_REP 重复键盘按键事件
//(内部会定义一个定时器,若有键盘按键事件一直按下/松开,就重复定时,时间一到就上报事件) //EV_FF 受力事件
//EV_PWR 电源事件
//EV_FF_STATUS 受力状态事件 unsigned long keybit[NBITS(KEY_MAX)]; //存放支持的键盘按键值
//键盘变量定义在:include/linux/input.h, 比如: KEY_L(按键L)、BTN_TOUCH(触摸屏的按键) unsigned long relbit[NBITS(REL_MAX)]; //存放支持的相对坐标值
unsigned long absbit[NBITS(ABS_MAX)]; //存放支持的绝对坐标值,存放下面4个absxxx[]
unsigned long mscbit[NBITS(MSC_MAX)]; //存放支持的其它事件,也就是功能
unsigned long ledbit[NBITS(LED_MAX)]; //存放支持的各种状态LED
unsigned long sndbit[NBITS(SND_MAX)]; //存放支持的各种声音
unsigned long ffbit[NBITS(FF_MAX)]; //存放支持的受力设备
unsigned long swbit[NBITS(SW_MAX)]; //存放支持的开关功能
... ... /*以下4个数组都会保存在上面成员absbit[]里,数组号为:ABS_xx ,位于include/linux/input.h */
/*比如数组0,标志就是ABS_X,以下4个的absXXX[0]就是表示绝对位移X方向的最大值、最小值... */
/*对于触摸屏常用的标志有:
ABS_X(X坐标方向), ABS_Y(Y坐标方向), ABS_PRESSURE(压力方向,比如绘图,越用力线就越粗)* /
int absmax[ABS_MAX + 1]; //绝对坐标的最大值
int absmin[ABS_MAX + 1]; //绝对坐标的最小值
int absfuzz[ABS_MAX + 1]; //绝对坐标的干扰值,默认为0,
int absflat[ABS_MAX + 1]; //绝对坐标的平焊位置,默认为0
... ...

3.本节需要用到的函数:

struct input_dev *input_allocate_device(void);  //向内存中分配input_dev结构体

input_free_device(struct input_dev *dev);   //释放内存中的input_dev结构体

input_register_device(struct input_dev *dev);   //注册一个input_dev,若有对应的驱动事件,
则在/sys/class/input下创建这个类设备 input_unregister_device(struct input_dev *dev); //卸载/sys/class/input目录下的
input_dev这个类设备 set_bit(nr,p); //设置某个结构体成员p里面的某位等于nr,支持这个功能
/* 比如:
set_bit(EV_KEY,buttons_dev->evbit); //设置input_dev结构体buttons_dev->evbit支持EV_KEY
set_bit(KEY_S,buttons_dev->keybit); //设置input_dev结构体buttons_dev->keybit支持按键”S” */ input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat);
//设置绝对位移的支持参数
//dev: 需要设置的input_dev结构体
//axis : 需要设置的数组号,常用的有: ABS_X(X坐标方向), ABS_Y(Y坐标方向), ABS_PRESSURE(压力方向)//min: axis方向的最小值, max:axis方向的最大值, fuzz: axis方向的干扰值, flat:axis方向的平焊位置

input_report_abs(struct input_dev *dev, unsigned int code, int value);
//上报EV_ABS事件
//该函数实际就是调用的input_event(dev, EV_ABS, code, value);
//*dev :要上报哪个input_dev驱动设备的事件
// code: EV_ABS事件里支持的哪个方向,比如X坐标方向则填入: ABS_X
//value:对应的方向的值,比如X坐标126 input_report_key(struct input_dev *dev, unsigned int code, int value);
//上报EV_KEY事件 input_sync(struct input_dev *dev); //同步事件通知,通知系统有事件上报 struct clk *clk_get(struct device *dev, const char *id);
//获得*id模块的时钟,返回一个clk结构体
//*dev:填0即可, *id:模块名字, 比如"adc","i2c"等,名字定义在clock.c中 clk_enable(struct clk *clk);
//开启clk_get()到的模块时钟,就是使能CLKCON寄存器的某个模块的位

4.电阻式触摸屏介绍:

如下图所示,2440开发板使用的是4线触摸屏,该4线连接在2440的AIN4~AIN7引脚上,该引脚专门是用来接收模拟输入信号.

引脚说明:

YM: (Y Minus)触摸屏的Y坐标的负线,也可以用Y -表示

YP : (Y Power)触摸屏的Y坐标的正线, 也可以用Y+表示

XM: (Y Minus)触摸屏的Y坐标的负线, 也可以用X-表示

XP : (Y Power)触摸屏的Y坐标的正线, 也可以用X+表示

4.1  4线触摸屏包含了两个阻性层,如下图所示:

当没有触摸按下时,X层和Y层是分离的,此时就测不到电压

4.2 测X坐标方向时:

如下图,  把XP接3.3V , XM接0V, YP和YM悬空,我们以按压X坐标的中间位置, X层和Y层便闭合了,此时YP就会输出当前X坐标值的1.66V给CPU

4.3 测Y坐标方向时:

如下图, 把YP接3.3V , YM接0V, XP和XM悬空,我们以按压X坐标的中间位置, X层和Y层便闭合了,此时XP就会输出当前X坐标值的1.66V给CPU

5.接下来开始看2440手册

如下图,2440的ADC分辨率为10位(0~0X3FFF)

如下图,若工作在普通ADC模式,则通过寄存器ADCCON->SEL_MUX来选择转换哪个引脚的模拟信号

当设置为ADC等待中断模式时,测到有屏幕笔尖触摸,就会产生INT_TC中断

其中ADC的工作频率最大为2.5MHZ,需要设置寄存器ADCCON->PRSCVL更改分频系数

5.1 获取笔尖触摸按下/松开使用的是ADC等待中断模式:
当笔尖落下时触摸屏控制器产生中断(INT_TC)信号。需要设置寄存器ADCTSC=0xd3/0x1d3

设置寄存器ADCTSC=0x0d3/0x1d3 (X 1101 0011)时(如下图):

开启 YM开关,使能XP上拉, 开启等待中断模式

当有笔尖按下时,X层和Y层闭合,然后会拉低XP和XM电平,输出低电平

设置为0x0d3是检测触摸低电平, 设置为0x1d3是检测触摸上拉电平

(PS:  ADCDAT0的bit15位用来标志笔尖是按下还是松开)

5.2 获取XY坐标时使用的是自动 X/Y 方向转换模式

当ADC转换成功,  X 坐标值到 ADCDAT0 和 Y 坐标值到ADCDAT1 后,就会产生INT_ADC中断

自动获取XY坐标时(如下图):

设置寄存器ADCTSC=0X0C (关闭XP上拉、启动自动XY方向转换)

设置寄存器ADCCON的位[0]=1(开启一次ADC转换,当ADC转换成功该位清0)

6.编写代码

步骤如下:

6.1 在init入口函数中:

1)分配一个input_dev结构体

2)设置input_dev的成员

  -> 2.1)设置input_dev->evbit支持按键事件,绝对位移事件

      (触摸屏:通过按键BTN_TOUCH获取按下/松开,通过绝对位移获取坐标)

  -> 2.2)设置input_dev-> keybit支持BTN_TOUCH触摸屏笔尖按下

  -> 2.3)设置input_dev-> absbit 支持ABS_X、ABS_Y、 ABS_PRESSURE

         input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);

        input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);           // 0x3FF:最大值为10位ADC,

      input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);   //压力最多就是1

3)注册input_dev 驱动设备到内核中

4)设置触摸屏相关的硬件

  -> 4.1)开启ADC时钟,使用clk_get ()和clk_enable()函数

  -> 4.2) ioremap获取寄存器地址,设置寄存器ADCCON =(1<<14)|(49<<6),分频

  ->4.3)设置寄存器ADCDLY=0xffff,ADC启动延时时间设为最大值,使触摸按压更加稳定

  ->4.4)开启IRQ_TC笔尖中断、开启IRQ_ADC中断获取XY坐标

  -> 4.5)初始化定时器,增加触摸滑动功能

  ->4.6)最后设置寄存器ADCTSC=0x0d3,开启IRQ_TC中断

6.2 在出口函数中:

1)注销内核里的input_dev、

2)释放中断、删除定时器、iounmap注销地址、

3)释放input_dev、

6.3 在IRQ_TC中断函数中:

1)若判断笔尖为松开,设置寄存器ADCTSC =0XD3(按下中断)

2)若判断笔尖按下,设置为XY自动转换模式,启动一次ADC转换,ADC转换成功,会进入ADC中断

6.4 在IRQ_ADC中断函数中:

1)获取ADCDAT0的位[9:0],来算出XY方向坐标值

2)测量n次值保存在数组中,然后再次设置为XY自动转换模式,启动ADC

(PS:要启动ADC转换之前必须设置一次XY为自动转换模式,不然获取的数据会不准)

3)采集完毕,使用快速排序将n次值排序后,以最小值为基准,如有误差非常大的数,则舍弃,如果没有则打印数组的中间值,实现中值滤波。

(PS: 使用快速排序,比冒泡更快,详解:http://www.cnblogs.com/lifexy/p/7597276.html )

4)打印数据后,必须设置寄存器ADCTSC =0X1D3(松开中断IRQ_TC)

(PS:在ADC采样模式下是判断不到ADCDAT0的bit15位的,因为ADCDAT0已被自动设置为X坐标的采样值)

5)设置定时器10ms超时时间

6.5 在定时器超时函数中:

1)判断ADCDAT0的bit15位,若还在按下再次启动ADC转换(实现触摸滑动功能)

2)若松开,设置寄存器ADCTSC =0XD3(按下中断)

最终代码如下:

#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 <asm/plat-s3c24xx/ts.h>
#include <asm/arch/regs-adc.h>
#include <asm/arch/regs-gpio.h> static struct input_dev *ts_dev;
static struct clk *ADC_CLK; //adc时钟
static struct timer_list ts_timer; //定时器 struct adc_regs{
unsigned long adccon;
unsigned long adctsc;
unsigned long adcdly;
unsigned long adcdat0;
unsigned long adcdat1;
unsigned long adcupdn;
}; static volatile struct adc_regs *adc_regs; /*启动TC 函数*/
static void set_pen_down(void)
{
/* 设置寄存器ADCTSC=0x0d3,开启IRQ_TC中断*/
adc_regs->adctsc = 0xd3;
} static void set_pen_up(void)
{
/* 设置寄存器ADCTSC=0x0d3,开启IRQ_TC中断*/
adc_regs->adctsc = 0x1d3;
} /*启动ADC 转换函数*/
static void adc_start(void)
{
adc_regs->adctsc= (1<<3)| (1<<2); //启动XY自动转换
adc_regs->adccon|=(1<<0); //启动1次ADC转换
} /*快速排序,比冒泡更快*/
/*快速排序详解:http://www.cnblogs.com/lifexy/p/7597276.html*/
static void find_frst(int *s,int lift,int right)
{
int i=lift,j=right,temp; //(1)初始化i、j
if(lift>=right)
return ;
temp=s[i]; //(2)以第一个数组为比较值,保存到temp中
while(i<j)
{
while(j>i&&s[j]>=temp) //(3)j--,找小值
j--;
s[i]= s[j]; //保存小值,到s[i]上
while(i<j&&s[i]<=temp) //(4)i++,找大值
i++;
s[j--]=s[i]; //保存大值 到s[j]上
} s[i]=temp; //(5)将比较值放在s[i]上 /*(6)拆分成两个数组 s[0,i-1]、s[i+1,n-1]又开始排序 */
find_frst(s,lift,i-1); //左
find_frst(s,i+1,right); //右
} /*查找X Y坐标偏移值是否太大*/
/*return: 0误差大, 1误差小 */
static int find_xy_offset(int x[], int y[],int n)
{
int i;
for(i=n;i>=1;i--)
{
if(x[i]-x[i-1]>10) //判断是否大于误差10,
return 0;

if(y[i]-y[i-1]>10)  //判断是否大于误差10,
return 0;
} return 1;
} /*定时器函数,实现触摸滑动功能 */
void pen_updown_timer(unsigned long cnt)
{
if((adc_regs->adcdat0>>15)&0x01) //此时笔尖已经抬起
{
set_pen_down(); //设置TC中断
}
else
{
adc_start(); //启动一次ADC转换
} } /*触摸中断IRQ_TC */
static irqreturn_t tc_handler(int irq, void *dev_id)
{ if((adc_regs->adcdat0>>15)&0x01) //此时笔尖已经抬起
{
set_pen_down();
} else
{
adc_start(); //启动一次ADC转换
}
return IRQ_HANDLED;
} /*ADC中断IRQ_ADC:测XY坐标 */
static irqreturn_t adc_handler(int irq, void *dev_id)
{
static int adc_x[5],adc_y[5]; //保存XY坐标
static unsigned char xy_cnt=0; //计数 adc_y[xy_cnt] =adc_regs->adcdat1&0x3ff; //10位ADC
adc_x[xy_cnt] =adc_regs->adcdat0&0x3ff; //10位ADC if (adc_regs->adcdat0 & (1<<15))
{
/* 已经松开 */
xy_cnt = 0;
set_pen_down();
}
else{
xy_cnt++;
if(xy_cnt>=5)
{
xy_cnt=0;
find_frst(adc_x,0,4); // 快速排序X
find_frst(adc_y,0,4); // 快速排序y
if(find_xy_offset(adc_x,adc_y,4))
{
printk("X: %04d,y: %04d \n",adc_x[2],adc_y[2]); //中值滤波
} set_pen_up();
mod_timer(&ts_timer ,jiffies+HZ/100); //启动定时10ms
}
else //在测一次
{
adc_start(); //启动 ADC
}
}
return IRQ_HANDLED;
} /*入口函数*/
static int myts_init(void)
{
/*1. 申请input_dev */
ts_dev=input_allocate_device();
/*2. 设置input_dev*/
set_bit(EV_ABS, ts_dev->evbit);
set_bit(EV_KEY, ts_dev->evbit);
set_bit(BTN_TOUCH, ts_dev->keybit);
input_set_abs_params(ts_dev, ABS_X , 0 , 0x3ff , 0 , 0); //adc是个10位的,所以为0X3FF
input_set_abs_params(ts_dev, ABS_Y , 0 , 0x3ff , 0 , 0); //adc是个10位的,所以为0X3FF
input_set_abs_params(ts_dev, ABS_PRESSURE, 0 , 1 , 0 , 0); //adc是个10位的,所以为0X3FF /*3.注册input_dev 驱动设备到内核中*/
input_register_device(ts_dev); /*4.设置触摸屏相关的硬件*/
/*4.1 开启ADC时钟 */
ADC_CLK =clk_get(0,"adc");
clk_enable(ADC_CLK); /*4.2 设置寄存器ADCCON分频,*/
adc_regs=ioremap(0x58000000, sizeof(struct adc_regs));
adc_regs->adccon=(1<<14)|(49<<6); //50Mhz/(49+1)=1Mhz /*4.3 设置中断IRQ_TC IRQ_ADC */
request_irq(IRQ_TC , tc_handler, IRQF_SAMPLE_RANDOM, "pen_updown", 0);
request_irq(IRQ_ADC , adc_handler, IRQF_SAMPLE_RANDOM, "adc" , 0); /*4.4设置寄存器ADCDLY=0xffff */
adc_regs->adcdly =0xffff; /*4.5 初始化定时器*/
init_timer(&ts_timer);
ts_timer.function =pen_updown_timer;
add_timer(&ts_timer); /*4.6设置寄存器ADCTSC=0x0d3,开启IRQ_TC中断*/
set_pen_down();
return 0;
} /*出口函数*/
static void myts_exit(void)
{
/*1.注销内核里的input_dev、*/
input_unregister_device(ts_dev);
/*2.释放中断、删除定时器、iounmap注销地址、*/
free_irq(IRQ_TC, NULL);
free_irq(IRQ_ADC, NULL); del_timer(&ts_timer);
iounmap(adc_regs); /*3.释放input_dev、*/
input_free_device(ts_dev);
} module_init(myts_init);
module_exit(myts_exit);
MODULE_LICENSE("GPL");

7.测试运行

7.1 重新设置编译内核(去掉默认的触摸屏驱动)

make menuconfig ,进入menu菜单重新设置内核参数:

进入Device Drivers-> Input device support -> Touchscreens ->

< >   S3C2410/S3C2440 touchscreens     //将自带的触摸屏驱动去掉, 不编进内核和模块

然后make uImage 编译内核

将新的触摸屏驱动模块放入nfs文件系统目录中

7.2然后烧写内核,装载触摸屏驱动模块

如下图, 通过 ls -l /dev/event* 命令可以看到我们的触摸屏驱动的设备为event0

7.3 测试运行:

如下图所示,可以看到在同一个点按下时,变化都一致,没有误差

有了定时器后,也能支持滑动功能, 如下图滑动Y方向:

此时的驱动只是打印数据,并没有上报EV_KYE事件和EV_ABS事件

8.添加上报事件

设置上报事件之前还要删除printk打印信息,步骤如下:

8.1 在IRQ_TC中断函数滤波处,添加:

input_report_abs(ts_dev, ABS_X, adc_x[2]);   //上报X方向值
input_report_abs(ts_dev, ABS_X, adc_y[2]); //上报Y方向值
input_report_abs(ts_dev, ABS_PRESSURE, 1); //上报压力方向值
input_report_key(ts_dev,BTN_TOUCH,1); //上报BTN_TOUCH按键值按下
input_sync(ts_dev); //上报同步事件,通知系统有事件上报

8.2 在(ADCDAT0的bit15位==1)触摸松开处,添加:

input_report_abs(ts_dev, ABS_PRESSURE, 0);  //上报压力值为0
//(PS:必须要上报一次压力值,否则压力值会一直为1,会影响tslib的测试运行)
input_report_key(ts_dev,BTN_TOUCH,0); //上报BTN_TOUCH按键值松开
input_sync(ts_dev); //上报同步事件,通知系统有事件上报

9.测试运行:

如下图, 通过 ls -l /dev/event* 命令可以看到我们的触摸屏驱动的设备为event0

9.1使用hexdump命令来调试代码

(hexdump命令调试代码详解地址:http://www.cnblogs.com/lifexy/p/7553550.html)

测试效果如下:

(PS:必须要保证有ABS_X、ABS_Y、压力、触摸按键上传,不然TSLIB测试会失败)

9.2 使用TSLIB应用程序测试

(TSLIB安装以及使用详解地址: http://www.cnblogs.com/lifexy/p/7628780.html)

TSLIB: 为触摸屏驱动获得的采样提供诸如滤波、去抖、校准等功能,通常作为触摸屏驱动的适配层,为上层的应用提供了一个统一的接口。

校验界面如下图所示:

运行测试如下图所示,能随意画图:

最终,触摸屏驱动测试成功

下章开始学习:

19.Linux-USB总线驱动分析

18.Llinux-触摸屏驱动(详解)【转】的更多相关文章

  1. S3C2440触摸屏驱动详解

    2440的触摸屏转换接口搭载在ADC接口之上,使用上比ADC接口多了一些花样,首先,触摸屏接口有几种转换模式 1. 普通转换模式 单转换模式是最合适的通用ADC转换.此模式可以通过设置ADCCON(A ...

  2. 16.Linux-LCD驱动(详解)

    在上一节LCD层次分析中,得出写个LCD驱动入口函数,需要以下4步: 1) 分配一个fb_info结构体: framebuffer_alloc(); 2) 设置fb_info 3) 设置硬件相关的操作 ...

  3. 16.Linux-LCD驱动(详解)【转】

    转自:https://www.cnblogs.com/lifexy/p/7604011.html 在上一节LCD层次分析中,得出写个LCD驱动入口函数,需要以下4步: 1) 分配一个fb_info结构 ...

  4. linux usb 驱动详解

    linux usb 驱动详解 USB 设备驱动代码通过urb和所有的 USB 设备通讯.urb用 struct urb 结构描述(include/linux/usb.h ). urb 以一种异步的方式 ...

  5. 25.Linux-Nor Flash驱动(详解)

    1.nor硬件介绍: 从原理图中我们能看到NOR FLASH有地址线,有数据线,它和我们的SDRAM接口相似,能直接读取数据,但是不能像SDRAM直接写入数据,需要有命令才行 1.1其中我们2440的 ...

  6. 使用VS2010编译MongoDB C++驱动详解

    最近为了解决IM消息记录的高速度写入.多文档类型支持的需求,决定使用MongoDB来解决. 考虑到MongoDB对VS版本要求较高,与我现有的VS版本不兼容,在leveldb.ssdb.redis.h ...

  7. 18.Llinux-触摸屏驱动(详解)

    本节的触摸屏驱动也是使用之前的输入子系统 1.先来回忆之前第12节分析的输入子系统 其中输入子系统层次如下图所示, 其中事件处理层的函数都是通过input_register_handler()函数注册 ...

  8. 13.Linux键盘驱动 (详解)

    版权声明:本文为博主原创文章,未经博主允许不得转载. 在上一节分析输入子系统内的intput_handler软件处理部分后,接下来我们开始写input_dev驱动 本节目标: 实现键盘驱动,让开发板的 ...

  9. 21.Linux-写USB键盘驱动(详解)

    本节目的: 根据上节写的USB鼠标驱动,来依葫芦画瓢写出键盘驱动 1.首先我们通过上节的代码中修改,来打印下键盘驱动的数据到底是怎样的 先来回忆下,我们之前写的鼠标驱动的id_table是这样: 所以 ...

随机推荐

  1. C#后台架构师成长之路-高阶知识体系核心

    了解了这些东西,熟悉了运用基本都是高工级别的了,其他修修补补就行了.... 1.三种预定义特性:attributeUsage.Conditional.obsolete,允许创建自定义特性,派生自Sys ...

  2. 阿里云ubuntu 16.04 搭建pptpd 第二版

    前言:1.我常用的服务器在国内,但我又有某方面的需求,所以想要搭建一个pptpd的服务器    2.但我又不常用,所以感觉阿里云包年包月的不划算,所以准备采用阿里云按量付费的实例来搭建pptpd,并形 ...

  3. 实战项目-用例评审-问题总结-Dotest-董浩

    实战项目-用例评审-问题总结 内部班项目用例评审,总结的问题:供大家参考!提升用例最好的方式,可以互相执行下(评审),就会明白自己的差距或者需要避免的点在哪里.(前提是会) 1)覆盖率 原型中提到的一 ...

  4. WPF 动态列(DataGridTemplateColumn) 绑定数据 (自定义控件)对象绑定

    原文:WPF 动态列(DataGridTemplateColumn) 绑定数据 (自定义控件)对象绑定 WPF 动态列(DataGridTemplateColumn) 绑定数据 (自定义控件) 上面的 ...

  5. Educational Codeforces Round 76 (Rated for Div. 2) D. Yet Another Monster Killing Problem 贪心

    D. Yet Another Monster Killing Problem You play a computer game. In this game, you lead a party of

  6. 算法与数据结构基础 - 数组(Array)

    数组基础 数组是最基础的数据结构,特点是O(1)时间读取任意下标元素,经常应用于排序(Sort).双指针(Two Pointers).二分查找(Binary Search).动态规划(DP)等算法.顺 ...

  7. SqlServer PIVOT行转列

    PIVOT通过将表达式某一列中的唯一值转换为输出中的多个列来旋转表值表达式,并在必要时对最终输出中所需的任何其余列值执行聚合. 测试数据 INSERT INTO [TestRows2Columns] ...

  8. javascript的10个开发技巧

    总结10个提高开发效率的JavaScript开发技巧. 1.生成随机的uid. const genUid = () => { var length = 20; var soupLength = ...

  9. 10、Fiddler中设置断点修改Response

    当然Fiddler中也能修改Response 第一种:打开Fiddler 点击Rules-> Automatic Breakpoint  ->After Response  (这种方法会中 ...

  10. Core源码(二) Linq的Distinct扩展

    先贴源码地址 https://github.com/dotnet/corefx/tree/master/src/System.Linq/src .NET CORE很大一个好处就是代码的开源,你可以详细 ...