转自: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. Spring Boot 2 + Thymeleaf:服务器端表单验证

    表单验证分为前端验证和服务器端验证.服务器端验证方面,Java提供了主要用于数据验证的JSR 303规范,而Hibernate Validator实现了JSR 303规范.项目依赖加入spring-b ...

  2. dedecmsV5.7 arclist 如何调用副栏目的文章

    问题:用arclist 调用某个栏目下的文章的时候,发现无法调用出副栏目是这个栏目的文章. 然后就上百度搜了一番,记录一下我搜到的解决方法: 1.打开/include/taglib/arclist.l ...

  3. [数组] Leetcode 189.旋转数组

  4. 201871010116-祁英红《面向对象程序设计(java)》第十五周学习总结

    博文正文开头格式:(2分) 项目 内容 <面向对象程序设计(java)> https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://ww ...

  5. vmware workstations 虚拟机安装CentOS

    1.下载vmware ,我的版本是从上学时保存网盘的,版本比较低,链接如下: 链接:https://pan.baidu.com/s/19QP0q8xmPWIPn-rziPTvKg 提取码:lvh9 2 ...

  6. C语言快速入门一:win10系统环境搭建

    0.搭建环境:WIN10 64位 1.下载minGW.zip编译器 2.解决上述文件,配置环境变量 3.配置变成后验证:打开cmd命令行,输入gcc -v 提示以下内容,说明编译器安装成功 D:\mm ...

  7. AcWing 13. 找出数组中重复的数字

    习题地址 https://www.acwing.com/solution/acwing/content/2919/. 题目描述给定一个长度为 n 的整数数组 nums,数组中所有的数字都在 0∼n−1 ...

  8. IT兄弟连 Java语法教程 数组 使用foreach循环遍历数组元素

    从JDK5之后,Java提供了一种更简单的循环:foreach循环,也叫作增强for循环,这种循环遍历数组和集合更加简洁.使用foreach循环遍历数组和集合元素时,无需获得数组或集合的长度,无需根据 ...

  9. 【Linux命令】nohup命令用法

    nohup命令用法 当我们想将某个脚本或程序运行在后台的时候.我们一般会在程序或脚本后面添加 & 字符来表示在后台运行,但使用& 运行在后台,当我们将shell窗口关闭时,该脚本或程序 ...

  10. js获取计算机操作系统版本

    如题,想要获取当先计算机的操作系统和版本号的话,可以用如下方法. 首先,创建osversion.js文件,文件里面的代码如下 var osData = [ { name: 'Windows 2000' ...