本文转载自: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. PHP基础知识测试题及解析

      本试题共40道选择题,10道判断题,考试时间1个半小时 一:选择题(单项选择,每题2分): 1. LAMP具体结构不包含下面哪种(A ) A:Windows系统 B:Apache服务器 C:MyS ...

  2. 如何在eclipse中设置断点并调试程序

    eclipse导入源码后可以看见代码但并不能调试,(解决方法) 1.eclipse默认的运行环境不是jdk中的jre.将jre换成jdk: 2. 先去掉勾,apply,在打上勾,apply 参考:ht ...

  3. [ECharts]"echarts/config" is not exists

    今天在给Echarts折线图中的数据点增加点击事件的时候总是出现一个 Uncaught Error: [MODULE_MISS]"echarts/config" is not ex ...

  4. iOS crash log 解析 symbol address = stack address - slide 运行时获取slide的api 利用dwarfdump从dsym文件中得到symbol

    概述: 为什么 crash log 内 Exception Backtrace 部分的地址(stack address)不能从 dsym 文件中查出对应的代码? 因为 ASLR(Address spa ...

  5. 项目关联到svn

    最近因为要升级项目,改用64位的eclipse,原先已经被svn管理的项目需要被复制到另一个工作空间,只需要导入并复制项目到新的工作空间即可 右键,点import 选择已经存在的项目导入工作空间 选择 ...

  6. Burnside引理和polay计数 poj2409 Let it Bead

    题目描述 "Let it Bead" company is located upstairs at 700 Cannery Row in Monterey, CA. As you ...

  7. 30.3 FCL中的混合构造

     30.3.2 Monitor类和同步块 internal sealed class Transaction { private readonly object _lock = new object( ...

  8. MySQL5.7本地首次登录win10报错修改

    1.打开MySQL目录下的my.ini文件,在文件的最后添加一行“skip-grant-tables”,保存并关闭文件.(Win10默认安装,my.ini在C:\ProgramData\MySQL\M ...

  9. 用fallocate进行"文件预留"或"文件打洞"【转】

    转自uestc-leon的博客 内容作了一些修改,查看原文请访问uestc-leon 1. 什么是空洞文件? "在UNIX文件操作中,文件位移量可以大于文件的当前长度,在这种情况下,对该文件 ...

  10. 17.使用原生cross-fiels技术解决搜索弊端

    主要知识点: 原生cross-fiels的用法 原生cross-fiels解决三个弊端     一.原生cross-fiels的用法     GET /forum/article/_search { ...