本文转载自:http://blog.csdn.net/encourage2011/article/details/51679332

本文描述在RK3126平台上添加一个新的TP驱动(gslx680驱动)以及详细的驱动代码信息。如有不足之处,敬请指出。

1、修改dts,添加新的i2c设备。

在 arch/arm/boot/dts/rk312x-sdk-v2.2.dtsi中添加i2c设备的相关信息:

ts@40 {
compatible = "gslX680";
reg = <0x40>;
wake-gpio = <&gpio0 GPIO_D3 GPIO_ACTIVE_LOW>;
irp-gpio = <&gpio0 GPIO_A2 IRQ_TYPE_LEVEL_HIGH>;
revert_x = <0>;
revert_y = <0>;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
&i2c2 {
status = "okay";
/*
ts@55 {
compatible = "goodix,gt8xx";
reg = <0x55>;
touch-gpio = <&gpio1 GPIO_B0 IRQ_TYPE_LEVEL_LOW>;
reset-gpio = <&gpio2 GPIO_C1 GPIO_ACTIVE_LOW>;
//power-gpio = <&gpio0 GPIO_C5 GPIO_ACTIVE_LOW>;
max-x = <1280>;
max-y = <800>;
};*/ ts@40 {
compatible = "gslX680";
reg = <0x40>;
//wake-gpio = <&gpio0 GPIO_D3 GPIO_ACTIVE_LOW>;
irp-gpio = <&gpio0 GPIO_A2 IRQ_TYPE_LEVEL_HIGH>;
revert_x = <0>;
revert_y = <0>;
};
/* ... */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

表示i2c2总线上下挂在了多个i2c设备。 
其中ts@40是表示此i2c设备的设备类型为触摸屏,设备地址为0x40(7位地址,注意:在i2c的传输函数中,会将此地址左移一位,因此实际上gslx680的i2c设备地址为0x80)。该节点下有多个属性: 
1、compatible = "gslX680";属性用于驱动和设备的绑定。表示特定的设备名称,此处为gslX680; 
2、reg = <0x40>;属性表示此设备的i2c地址为0x40,等同于@40; 
3、wake-gpio = <&gpio0 GPIO_D3 GPIO_ACTIVE_LOW>;表示复位引脚使用的是GPIO0 中的GPIO_D3这个引脚,低电平有效。 
irp-gpio = <&gpio0 GPIO_A2 IRQ_TYPE_LEVEL_HIGH>;表示中断引脚使用的是GPIO0中的GPIO_A2这个引脚,高电平触发。 
很奇怪,为什么这里没有上电的信息,以及在整个驱动程序中都没有给ic上电的操作。在前面的MTK平台上的tp驱动都有上电的动作,暂时还搞不懂在RK平台上为什么没有。 
4、revert_x = <0>; revert_y = <0>;标记x和y是否需要翻转。 
在上述的信息中,可以通过of接口获取到属性对应的值。在后面的probe()函数中就会使用到。

注:关于dts的详细信息可以查看ARM Linux 3.x的设备树(Device Tree)Device Tree Usage

2、修改Makefile、Kconfig、defconfig

(1)、修改Makefile添加gslx680驱动

在 drivers/input/touchscreen/Makefile中添加驱动: 
obj-$(CONFIG_TOUCHSCREEN_GSLX680) += gslx680/。 
只要当配置了CONFIG_TOUCHSCREEN_GSLX680的选项才会去编译gslx680目录下的内容。在配置内核的时候会通过make menuconfig来配置对应的选项。或者是直接在defconfig文件中强制设置该选项。

注:如果不想要这么复杂,可以将该语句写成obj-y += gslx680/来强制编译该驱动。

(2)、修改Kconfig添加驱动配置描述

在 drivers/input/touchscreen/Kconfig中添加驱动配置描述:

config TOUCHSCREEN_GSLX680
tristate "gslX680 touchscreen driver"
help
gslX680 touchscreen driver
  • 1
  • 2
  • 3
  • 4

(3)、配置defconfig设置编译驱动

一般在内核中会有配置好的默认的config文件供参考,可以直接修改defconfig来选择编译某个驱动。此处在arch/arm/configs/rockchip_defconfig文件中添加CONFIG_TOUCHSCREEN_GSLX680=y并将该文件拷贝到kernel目录下命名为.config即可。

2、添加i2c驱动

#define GSLX680_I2C_NAME    "gslX680"
#define GSLX680_I2C_ADDR 0x40 static const struct i2c_device_id gsl_ts_id[] = {
{GSLX680_I2C_NAME, 0},
{}
}; MODULE_DEVICE_TABLE(i2c, gsl_ts_id); static struct i2c_driver gsl_ts_driver = {
.driver = {
.name = GSLX680_I2C_NAME,
.owner = THIS_MODULE,
},
#ifndef CONFIG_HAS_EARLYSUSPEND
// .suspend = gsl_ts_suspend,
// .resume = gsl_ts_resume,
#endif
.probe = gsl_ts_probe,
.remove = gsl_ts_remove,
.id_table = gsl_ts_id,
}; static int __init gsl_ts_init(void)
{
int ret;
printk("==gsl_ts_init==\n");
ret = i2c_add_driver(&gsl_ts_driver);
printk("ret=%d\n",ret);
return ret;
}
static void __exit gsl_ts_exit(void)
{
printk("==gsl_ts_exit==\n");
i2c_del_driver(&gsl_ts_driver);
return;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

注册名字为GSLX680_I2C_NAME的i2c驱动,即gslx680,该驱动支持的设备名为字gsl_ts_id[]里的设备名称。因为我们在dts中已注册了一个名字为gslx680的i2c设备。因此,设备与驱动可以匹配成功并正确执行probe()函数。 
至于设备与驱动是如何匹配的,可以参照Linux i2c子系统

3、gsl_ts_probe()

static int  gsl_ts_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
struct gsl_ts *ts;
int rc;
struct device_node *np = client->dev.of_node;
enum of_gpio_flags wake_flags;
unsigned long irq_flags; // 检查i2c适配器的能力
printk("GSLX680 Enter %s\n", __func__);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
{
dev_err(&client->dev, "I2C functionality not supported\n");
return -ENODEV;
} // 为ts申请内核空间
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
if(!ts)
return -ENOMEM;
printk("==kzalloc success=\n"); ts->client = client;
i2c_set_clientdata(client, ts);
ts->device_id = id->driver_data; // 从设备节点np中获取到irq和wake 的gpio的信息
ts->irq_pin=of_get_named_gpio_flags(np, "irp-gpio", 0, (enum of_gpio_flags *)&irq_flags);
ts->wake_pin=of_get_named_gpio_flags(np, "wake-gpio", 0, &wake_flags); // 为设备申请gpio,并设置默认电平
if(gpio_is_valid(ts->wake_pin))
{
rc = devm_gpio_request_one(&client->dev, ts->wake_pin, (wake_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, "gslX680 wake pin");
if(rc != 0)
{
dev_err(&client->dev, "gslX680 wake pin error\n");
return -EIO;
}
g_wake_pin = ts->wake_pin;
//msleep(100);
}
else
{
dev_info(&client->dev, "wake pin invalid\n");
}
if(gpio_is_valid(ts->irq_pin))
{
rc = devm_gpio_request_one(&client->dev, ts->irq_pin, (irq_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, "gslX680 irq pin");
if (rc != 0)
{
dev_err(&client->dev, "gslX680 irq pin error\n");
return -EIO;
}
}
else
{
dev_info(&client->dev, "irq pin invalid\n");
} // 创建工作队列,申请input设备
rc = gslX680_ts_init(client, ts);
if(rc < 0)
{
dev_err(&client->dev, "GSLX680 init failed\n");
goto error_mutex_destroy;
} gsl_client = client; // 从设备节点中获取属性信息
of_property_read_u32(np,"revert_x",&revert_x);//sss
of_property_read_u32(np,"revert_y",&revert_y);//sss // 初始化IC,包括复位,测试i2c以及加载ic配置信息
init_chip(ts->client);
check_mem_data(ts->client); // 申请中断号
ts->irq=gpio_to_irq(ts->irq_pin); //If not defined in client
if (ts->irq)
{
// 为client->dev设备的中断号ts->irq申请irq_flags触发的中断,中断服务子程序为gsl_ts_irq
rc = devm_request_threaded_irq(&client->dev, ts->irq, NULL, gsl_ts_irq, irq_flags | IRQF_ONESHOT, client->name, ts);
if(rc != 0)
{
printk(KERN_ALERT "Cannot allocate ts INT!ERRNO:%d\n", rc);
goto error_req_irq_fail;
}
//disable_irq(ts->irq);
}
else
{
printk("gsl x680 irq req fail\n");
goto error_req_irq_fail;
} ts->tp.tp_resume = gsl_ts_late_resume;
ts->tp.tp_suspend = gsl_ts_early_suspend;
tp_register_fb(&ts->tp); #ifdef CONFIG_HAS_EARLYSUSPEND
ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
//ts->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1;
ts->early_suspend.suspend = gsl_ts_early_suspend;
ts->early_suspend.resume = gsl_ts_late_resume;
register_early_suspend(&ts->early_suspend);
#endif #ifdef GSL_MONITOR
printk( "gsl_ts_probe () : queue gsl_monitor_workqueue\n"); INIT_DELAYED_WORK(&gsl_monitor_work, gsl_monitor_worker);
gsl_monitor_workqueue = create_singlethread_workqueue("gsl_monitor_workqueue");
queue_delayed_work(gsl_monitor_workqueue, &gsl_monitor_work, 1000);
#endif printk("[GSLX680] End %s\n", __func__); return 0; //exit_set_irq_mode:
error_req_irq_fail:
free_irq(ts->irq, ts); error_mutex_destroy:
input_free_device(ts->input);
kfree(ts);
return rc;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131

(1)、自定义的数据结构gsl_ts

一般在自己的驱动程序中,都会为该驱动程序封装一个数据结构,这里的gsl_ts就充当这种角色。 
在该驱动程序中自定义了一个数据结构:

struct gsl_ts {
struct i2c_client *client;
struct input_dev *input;
struct work_struct work;
struct workqueue_struct *wq;
struct gsl_ts_data *dd;
u8 *touch_data;
u8 device_id;
int irq;
int irq_pin;
int wake_pin;
struct tp_device tp;
#if defined(CONFIG_HAS_EARLYSUSPEND)
struct early_suspend early_suspend;
#endif
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

client表示一个i2c的设备; 
input表示一个输入设备; 
work表示一个工作,用于处理中断到来之后获取坐标等信息; 
wq表示一个工作队列,将上面的work加入到该工作队列中; 
ddtouch_data用来存储坐标的相关信息; 
device_id表示i2c设备的设备号; 
irq申请的中断号; 
irq_pin中断引脚; 
wake_pin复位引脚;这两个引脚信息可以通过dts获取到. 
tp创建一个为tp_device的数据结构,里面有个成员notifier_block用来接收LCD背光灯的亮暗的通知进而调用suspend()resume()。主要的实现在tp_suspend.h中。至于这里面的详细机制还不是很明白。

(2)、检查i2c适配器的能力

在很多i2c设备驱动程序中,一进入probe()就要检查i2c适配器的能力。现在还不清楚这么做的目的是什么。后面会仔细的学习一下linux 的i2c子系统。

(3)、获取wake和irq的引脚信息

在前面配置dts说过:dts中设备节点的属性的值可以通过of_接口获取到。

// 从设备节点np中获取到irq和wake 的gpio的信息
ts->irq_pin=of_get_named_gpio_flags(np, "irp-gpio", 0, (enum of_gpio_flags *)&irq_flags);
ts->wake_pin=of_get_named_gpio_flags(np, "wake-gpio", 0, &wake_flags);
  • 1
  • 2
  • 3

可以通过设备节点np获取到它irq-gpio这一个属性的值存放在ts->irq_pin中,以及将其flag刚到变量irq_flags中。因此我们可以知道:

ts->irq_pin = GPIO_A2
irq_flag = IRQ_TYPE_LEVEL_HIGH
  • 1
  • 2

同理,wake_pin也是一样。

(4)、申请gpio并设置输出电平

将irq和wake 引脚电平都设置输出低电平。

(5)、gslX680_ts_init

static int gslX680_ts_init(struct i2c_client *client, struct gsl_ts *ts)
{
struct input_dev *input_device;
int rc = 0; printk("[GSLX680] Enter %s\n", __func__); // 配置获取坐标信息
ts->dd = &devices[ts->device_id]; if(ts->device_id == 0)
{
ts->dd->data_size = MAX_FINGERS * ts->dd->touch_bytes + ts->dd->touch_meta_data;
ts->dd->touch_index = 0;
}
// 申请空间存放坐标信息
ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
if(!ts->touch_data)
{
pr_err("%s: Unable to allocate memory\n", __func__);
return -ENOMEM;
} // 申请一个input_dev 设备
input_device = input_allocate_device();
if (!input_device) {
rc = -ENOMEM;
goto error_alloc_dev;
} // 初始化input_device
ts->input = input_device;
input_device->name = GSLX680_I2C_NAME;
input_device->id.bustype = BUS_I2C;
input_device->dev.parent = &client->dev;
input_set_drvdata(input_device, ts); //
#ifdef REPORT_DATA_ANDROID_4_0
__set_bit(EV_ABS, input_device->evbit);
__set_bit(EV_KEY, input_device->evbit);
__set_bit(EV_REP, input_device->evbit);
__set_bit(INPUT_PROP_DIRECT, input_device->propbit);
input_mt_init_slots(input_device, (MAX_CONTACTS+1),0);
#else
input_set_abs_params(input_device,ABS_MT_TRACKING_ID, 0, (MAX_CONTACTS+1), 0, 0);
set_bit(EV_ABS, input_device->evbit);
set_bit(EV_KEY, input_device->evbit);
__set_bit(INPUT_PROP_DIRECT, input_device->propbit);
input_device->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
#endif #ifdef HAVE_TOUCH_KEY
input_device->evbit[0] = BIT_MASK(EV_KEY);
//input_device->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
for (i = 0; i < MAX_KEY_NUM; i++)
set_bit(key_array[i], input_device->keybit);
#endif set_bit(ABS_MT_POSITION_X, input_device->absbit);
set_bit(ABS_MT_POSITION_Y, input_device->absbit);
set_bit(ABS_MT_TOUCH_MAJOR, input_device->absbit);
set_bit(ABS_MT_WIDTH_MAJOR, input_device->absbit); input_set_abs_params(input_device,ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);
input_set_abs_params(input_device,ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);
input_set_abs_params(input_device,ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);
input_set_abs_params(input_device,ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0); // 创建工作队列
ts->wq = create_singlethread_workqueue("kworkqueue_ts");
if(!ts->wq)
{
dev_err(&client->dev, "Could not create workqueue\n");
goto error_wq_create;
}
flush_workqueue(ts->wq); // 初始化工作 ts->work,其操作为 gslX680_ts_worker()
INIT_WORK(&ts->work, gslX680_ts_worker); // 向input子系统注册一个input_dev
rc = input_register_device(input_device);
if (rc)
goto error_unreg_device; return 0; error_unreg_device:
destroy_workqueue(ts->wq);
error_wq_create:
input_free_device(input_device);
error_alloc_dev:
kfree(ts->touch_data);
return rc;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

gslX680_ts_init()中主要做了如下工作:

配置获取坐标信息

每次当中断来了之后,就要求通过i2c去读取坐标的信息,至于从哪里读取以及读取多少个,都是通过ts->dd来决定的。

为存储坐标信息申请空间

坐标信息放在ts->touch_data中。

申请及初始化input_dev设备,向input子系统注册该设备

这里面的内容涉及到input子系统,我还没有做过深入的了解。

初始化工作 ts->work

ts->work对应的操作为gslX680_ts_worker(),在中断来了之后,会queue_work(ts->wq, &ts->work);ts->work工作起来,就会去读取坐标等信息,然后通过input子系统上报给Android系统。

(6)、获取属性信息

通过of_接口获取revert_x和revert_y的信息,以此来决定坐标是否要翻转。

(7)、初始化ic

初始化的内容会放到一个全局的数组之中,这项工作一般都要FAE来完成。

(8)、申请中断号以及中断服务子程序

通过devm_request_threaded_irq接口为设备申请一个中断服务子程序gsl_ts_irq(),触发方式为irq_flagsIRQ_TYPE_LEVEL_HIGH高电平触发。

(9)、配置休眠唤醒

在前面说过,tp的休眠唤醒是通过LCD亮暗屏来决定的,这个动作由tp_register_fb()来实现。

ts->tp.tp_resume = gsl_ts_late_resume;
ts->tp.tp_suspend = gsl_ts_early_suspend;
tp_register_fb(&ts->tp);
  • 1
  • 2
  • 3

注:如果申请资源出错的话一定要记得释放资源以及前面的资源。比如说这里为ts申请的内核空间、申请的中断号、申请的input设备、申请的工作队列。

上述probe()配置完成之后就是等待中断,如果中断到来,关闭中断,启动工作去读取坐标等信息并通过input子系统上报,之后再使能中断。如此反复。

4、中断服务子程序

static irqreturn_t gsl_ts_irq(int irq, void *dev_id)
{
struct gsl_ts *ts = dev_id; print_info("========gslX680 Interrupt=========\n"); disable_irq_nosync(ts->irq); if (!work_pending(&ts->work))
{
queue_work(ts->wq, &ts->work);
} return IRQ_HANDLED;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

一旦有中断到来,立马调用gsl_ts_irq(),在这个中断服务子程序中先判断ts->work是否挂起,如果没有挂起就启动工作队列ts->wq的工作ts->workts->workgslX680_ts_worker()对应,主要用来读取坐标信息。

5、休眠唤醒

关于休眠和唤醒的内容根据ic的特性设置。如休眠的时候需要关闭中断、配置进入休眠模式、拉低wake引脚。唤醒的时候唤醒ic,使能wake引脚、使能中断等。

 
 

Rockchip平台TP驱动详解【转】的更多相关文章

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

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

  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. Java开源生鲜电商平台-盈利模式详解(源码可下载)

    Java开源生鲜电商平台-盈利模式详解(源码可下载) 该平台提供一个联合买家与卖家的一个平台.(类似淘宝购物,这里指的是食材的购买.) 平台有以下的盈利模式:(类似的平台有美菜网,食材网等) 1. 订 ...

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

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

  7. Java生鲜电商平台-优惠券系统设计详解

    Java生鲜电商平台-优惠券系统设计详解 优惠券作为电商最常用的营销手段,对于商家而言可以起到拉新.促活.提高转化的作用,对用户而言也可以获得实惠,今天就来谈谈优惠券系统的设计逻辑. 我对于优惠券系统 ...

  8. AgileEAS.NET SOA中间件平台/敏捷软件开发平台 and SQL详解

    AgileEAS.NET SOA中间件平台/敏捷软件开发平台 http://www.smarteas.net/ SQL详解: http://www.w3school.com.cn/sql/func_d ...

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

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

随机推荐

  1. html5——web存储

    基本概念 1.传统方式我们以document.cookie来进行存储的,但是由于其存储大小只有4k左右,并且解析也相当的复杂,给开发带来诸多不便 2.h5存储设置.读取方便,而且容量较大,sessio ...

  2. JS——样式类的添加

    1.注意current前有个空格 this.className = this.className + " current"; 2.直接将class所有的值替换成current th ...

  3. Twisted web开发教程

    最近在网上看到一篇twisted web开发文章,将它实践了一下,twisted 提供基本的url路由 和 控制器,模板与模型需要外部扩展 1.目录浏览 2.get请求 3.url路由 4.接受带参数 ...

  4. C# 后台按键 视频播放器 全屏后无法 触发

    第一种  (全屏不可触发) protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Win ...

  5. docker安装后出现Cannot connect to the Docker daemon

    启动docker service docker start docker安装后出现Cannot connect to the Docker daemon You need to add user in ...

  6. 写一个 sum方法,在使用下面任一语法调用时,都可以正常工作

    console.log(sum(2,3)); // Outputs 5 console.log(sum(2)(3)); // Outputs 5 (至少)有两种方法可以做到: 方法1: functio ...

  7. 【转】关于JMeter线程组中线程数,Ramp-Up Period,循环次数之间的设置概念

    关于JMeter线程组中线程数,Ramp-Up Period,循环次数之间的设置概念 笔者是个刚刚踏入压力测试领域不到2个月的小菜,这里分享一下线程组中3个参数之间关系的个人见解,不喜请!喷!,望大家 ...

  8. APIshop精选接口助力双十一电商业务

    距离2018年双11的购物盛典已经不到一个月了,各大电商之间的战役已经悄然打响,今年的双11仍会是一场电商鏖战,想必又会打破2017年双11近2540亿的全网成交总额记录. 据统计,去年双11全天共产 ...

  9. CTF实战(隐写术):欢迎来到地狱

    前言:请登录实验吧开启刷题模式,拿到的flag返回该网站验证正确性. 下载“欢迎来到地狱”CTF题目(总共三个文件

  10. 《奋斗吧!菜鸟》 第八次作业:Alpha冲刺

    项目 内容 这个作业属于哪个课程 任课教师链接 作业要求 https://www.cnblogs.com/nwnu-daizh/p/11012922.html 团队名称 奋斗吧!菜鸟 作业学习目标 A ...