msm-pm660l.dtsi相关节点。

qcom,leds@d000 {compatible = "qcom,leds-qpnp";reg = <0xd000 0x100>;label = "rgb";red_led: qcom,rgb_0 {label = "rgb";qcom,id = <3>;qcom,mode = "pwm";pwms = <&pm660l_pwm_3 0 0>;qcom,pwm-us = <2000>;qcom,max-current = <12>;qcom,default-state = "off";linux,name = "red";qcom,start-idx = <0>;qcom,idx-len = <10>;qcom,duty-pcts =[00 19 32 4b 6464 46 32 19 00];qcom,use-blink;};

qcom,mode = "pwm";模式为PWM ,另外的两个模式是manual、lpg。一个是手动模式,另一个不太清楚。

qcom,pwm-us = <2000>;表示PWM的一个周期是2s。

qcom,max-current = <12>;最大电流12mA

qcom,duty-pcts =  [00 19 32 4b 64 64 46 32 19 00];亮度值从0到64,再从64到0。实现呼吸灯功能,若想实现闪烁,值改为0与64即可。

LPG模式可选属性

qcom,pause-lo:在周期的低端暂停

qcom,pause-hi:在周期的高端暂停

qcom,ramp-step-ms:每个周期间隔(ms)

qcom,lut-flags:lut配置中使用的标志

Leds-qpnp.c (kernel\msm-4.4\drivers\leds)节点解析文件

static struct platform_driver qpnp_leds_driver = {.driver       = {.name       = "qcom,leds-qpnp",.of_match_table   = spmi_match_table,},.probe        = qpnp_leds_probe,.remove      = qpnp_leds_remove,
};
//进入probe函数
static int qpnp_leds_probe(struct platform_device *pdev)
{struct qpnp_led_data *led, *led_array;unsigned int base;struct device_node *node, *temp;int rc, i, num_leds = 0, parsed_leds = 0;const char *led_label;bool regulator_probe = false;node = pdev->dev.of_node;if (node == NULL)return -ENODEV;temp = NULL;while ((temp = of_get_next_child(node, temp)))num_leds++;if (!num_leds)return -ECHILD;led_array = devm_kcalloc(&pdev->dev, num_leds, sizeof(*led_array),GFP_KERNEL);if (!led_array)return -ENOMEM;for_each_child_of_node(node, temp) {led = &led_array[parsed_leds];led->num_leds = num_leds;led->regmap = dev_get_regmap(pdev->dev.parent, NULL);if (!led->regmap) {dev_err(&pdev->dev, "Couldn't get parent's regmap\n");return -EINVAL;}led->pdev = pdev;rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);if (rc < 0) {dev_err(&pdev->dev,"Couldn't find reg in node = %s rc = %d\n",pdev->dev.of_node->full_name, rc);goto fail_id_check;}led->base = base;rc = of_property_read_string(temp, "label", &led_label);if (rc < 0) {dev_err(&led->pdev->dev,"Failure reading label, rc = %d\n", rc);goto fail_id_check;}rc = of_property_read_string(temp, "linux,name",&led->cdev.name);if (rc < 0) {dev_err(&led->pdev->dev,"Failure reading led name, rc = %d\n", rc);goto fail_id_check;}rc = of_property_read_u32(temp, "qcom,max-current",&led->max_current);if (rc < 0) {dev_err(&led->pdev->dev,"Failure reading max_current, rc =  %d\n", rc);goto fail_id_check;}rc = of_property_read_u32(temp, "qcom,id", &led->id);if (rc < 0) {dev_err(&led->pdev->dev,"Failure reading led id, rc =  %d\n", rc);goto fail_id_check;}rc = qpnp_get_common_configs(led, temp);if (rc) {dev_err(&led->pdev->dev, "Failure reading common led configuration, rc = %d\n",rc);goto fail_id_check;}led->cdev.brightness_set    = qpnp_led_set;led->cdev.brightness_get    = qpnp_led_get;if (strcmp(led_label, "wled") == 0) {rc = qpnp_get_config_wled(led, temp);if (rc < 0) {dev_err(&led->pdev->dev,"Unable to read wled config data\n");goto fail_id_check;}} else if (strcmp(led_label, "flash") == 0) {if (!of_find_property(node, "flash-boost-supply", NULL))regulator_probe = true;rc = qpnp_get_config_flash(led, temp, &regulator_probe);if (rc < 0) {dev_err(&led->pdev->dev,"Unable to read flash config data\n");goto fail_id_check;}} else if (strcmp(led_label, "rgb") == 0) {rc = qpnp_get_config_rgb(led, temp);if (rc < 0) {dev_err(&led->pdev->dev,"Unable to read rgb config data\n");goto fail_id_check;}} else if (strcmp(led_label, "mpp") == 0) {rc = qpnp_get_config_mpp(led, temp);if (rc < 0) {dev_err(&led->pdev->dev,"Unable to read mpp config data\n");goto fail_id_check;}} else if (strcmp(led_label, "gpio") == 0) {rc = qpnp_get_config_gpio(led, temp);if (rc < 0) {dev_err(&led->pdev->dev,"Unable to read gpio config data\n");goto fail_id_check;}} else if (strcmp(led_label, "kpdbl") == 0) {bitmap_zero(kpdbl_leds_in_use, NUM_KPDBL_LEDS);is_kpdbl_master_turn_on = false;rc = qpnp_get_config_kpdbl(led, temp);if (rc < 0) {dev_err(&led->pdev->dev,"Unable to read kpdbl config data\n");goto fail_id_check;}} else {dev_err(&led->pdev->dev, "No LED matching label\n");rc = -EINVAL;goto fail_id_check;}if (led->id != QPNP_ID_FLASH1_LED0 &&led->id != QPNP_ID_FLASH1_LED1)mutex_init(&led->lock);led->in_order_command_processing = of_property_read_bool(temp, "qcom,in-order-command-processing");if (led->in_order_command_processing) {/** the command order from user space needs to be* maintained use ordered workqueue to prevent* concurrency*/led->workqueue = alloc_ordered_workqueue("led_workqueue", 0);if (!led->workqueue) {rc = -ENOMEM;goto fail_id_check;}}INIT_WORK(&led->work, qpnp_led_work);rc =  qpnp_led_initialize(led);if (rc < 0)goto fail_id_check;rc = qpnp_led_set_max_brightness(led);if (rc < 0)goto fail_id_check;rc = led_classdev_register(&pdev->dev, &led->cdev);if (rc) {dev_err(&pdev->dev,"unable to register led %d,rc=%d\n",led->id, rc);goto fail_id_check;}if (led->id == QPNP_ID_FLASH1_LED0 ||led->id == QPNP_ID_FLASH1_LED1) {rc = sysfs_create_group(&led->cdev.dev->kobj,&led_attr_group);if (rc)goto fail_id_check;}if (led->id == QPNP_ID_LED_MPP) {if (!led->mpp_cfg->pwm_cfg)break;if (led->mpp_cfg->pwm_cfg->mode == PWM_MODE) {rc = sysfs_create_group(&led->cdev.dev->kobj,&pwm_attr_group);if (rc)goto fail_id_check;}if (led->mpp_cfg->pwm_cfg->use_blink) {rc = sysfs_create_group(&led->cdev.dev->kobj,&blink_attr_group);if (rc)goto fail_id_check;rc = sysfs_create_group(&led->cdev.dev->kobj,&lpg_attr_group);if (rc)goto fail_id_check;} else if (led->mpp_cfg->pwm_cfg->mode == LPG_MODE) {rc = sysfs_create_group(&led->cdev.dev->kobj,&lpg_attr_group);if (rc)goto fail_id_check;}} else if ((led->id == QPNP_ID_RGB_RED) ||(led->id == QPNP_ID_RGB_GREEN) ||(led->id == QPNP_ID_RGB_BLUE)) {if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {rc = sysfs_create_group(&led->cdev.dev->kobj,&pwm_attr_group);if (rc)goto fail_id_check;}if (led->rgb_cfg->pwm_cfg->use_blink) {rc = sysfs_create_group(&led->cdev.dev->kobj,&blink_attr_group);if (rc)goto fail_id_check;rc = sysfs_create_group(&led->cdev.dev->kobj,&lpg_attr_group);if (rc)goto fail_id_check;} else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) {rc = sysfs_create_group(&led->cdev.dev->kobj,&lpg_attr_group);if (rc)goto fail_id_check;}} else if (led->id == QPNP_ID_KPDBL) {if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {rc = sysfs_create_group(&led->cdev.dev->kobj,&pwm_attr_group);if (rc)goto fail_id_check;}if (led->kpdbl_cfg->pwm_cfg->use_blink) {rc = sysfs_create_group(&led->cdev.dev->kobj,&blink_attr_group);if (rc)goto fail_id_check;rc = sysfs_create_group(&led->cdev.dev->kobj,&lpg_attr_group);if (rc)goto fail_id_check;} else if (led->kpdbl_cfg->pwm_cfg->mode == LPG_MODE) {rc = sysfs_create_group(&led->cdev.dev->kobj,&lpg_attr_group);if (rc)goto fail_id_check;}}/* configure default state */if (led->default_on) {led->cdev.brightness = led->cdev.max_brightness;__qpnp_led_work(led, led->cdev.brightness);if (led->turn_off_delay_ms > 0)qpnp_led_turn_off(led);} elseled->cdev.brightness = LED_OFF;parsed_leds++;}dev_set_drvdata(&pdev->dev, led_array);return 0;fail_id_check:for (i = 0; i < parsed_leds; i++) {if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&led_array[i].id != QPNP_ID_FLASH1_LED1)mutex_destroy(&led_array[i].lock);if (led_array[i].in_order_command_processing)destroy_workqueue(led_array[i].workqueue);led_classdev_unregister(&led_array[i].cdev);}return rc;
}

led->cdev.brightness_set    = qpnp_led_set;这里和led_class联系起来了。

static void qpnp_led_set(struct led_classdev *led_cdev,enum led_brightness value)
{struct qpnp_led_data *led;led = container_of(led_cdev, struct qpnp_led_data, cdev);if (value < LED_OFF) {dev_err(&led->pdev->dev, "Invalid brightness value\n");return;}if (value > led->cdev.max_brightness)value = led->cdev.max_brightness;led->cdev.brightness = value;if (led->in_order_command_processing)queue_work(led->workqueue, &led->work);elseschedule_work(&led->work);
}

设置了灯的亮度值,并在 结尾开启了一个work队列,也就是INIT_WORK(&led->work, qpnp_led_work);最后调用到下面函数

static int qpnp_rgb_set(struct qpnp_led_data *led)
{int rc;int duty_us, duty_ns, period_us;if (led->cdev.brightness) {if (!led->rgb_cfg->pwm_cfg->blinking)led->rgb_cfg->pwm_cfg->mode =led->rgb_cfg->pwm_cfg->default_mode;if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {rc = pwm_change_mode(led->rgb_cfg->pwm_cfg->pwm_dev,PM_PWM_MODE_PWM);if (rc < 0) {dev_err(&led->pdev->dev,"Failed to set PWM mode, rc = %d\n",rc);return rc;}period_us = led->rgb_cfg->pwm_cfg->pwm_period_us;if (period_us > INT_MAX / NSEC_PER_USEC) {duty_us = (period_us * led->cdev.brightness) /LED_FULL;rc = pwm_config_us(led->rgb_cfg->pwm_cfg->pwm_dev,duty_us,period_us);} else {duty_ns = ((period_us * NSEC_PER_USEC) /LED_FULL) * led->cdev.brightness;rc = pwm_config(led->rgb_cfg->pwm_cfg->pwm_dev,duty_ns,period_us * NSEC_PER_USEC);}if (rc < 0) {dev_err(&led->pdev->dev,"pwm config failed\n");return rc;}}rc = qpnp_led_masked_write(led,RGB_LED_EN_CTL(led->base),led->rgb_cfg->enable, led->rgb_cfg->enable);if (rc) {dev_err(&led->pdev->dev,"Failed to write led enable reg\n");return rc;}if (!led->rgb_cfg->pwm_cfg->pwm_enabled) {pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);led->rgb_cfg->pwm_cfg->pwm_enabled = 1;}} else {led->rgb_cfg->pwm_cfg->mode =led->rgb_cfg->pwm_cfg->default_mode;if (led->rgb_cfg->pwm_cfg->pwm_enabled) {pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);led->rgb_cfg->pwm_cfg->pwm_enabled = 0;}rc = qpnp_led_masked_write(led,RGB_LED_EN_CTL(led->base),led->rgb_cfg->enable, RGB_LED_DISABLE);if (rc) {dev_err(&led->pdev->dev,"Failed to write led enable reg\n");return rc;}}led->rgb_cfg->pwm_cfg->blinking = false;qpnp_dump_regs(led, rgb_pwm_debug_regs, ARRAY_SIZE(rgb_pwm_debug_regs));return 0;
}

qpnp_led_masked_write,这个函数的作用就是把相应的信息写入到寄存器中 ,使能led。

中间部分的配置,根据led_label=rgb进入qpnp_get_config_rgb()开始配置led。

static int qpnp_get_config_rgb(struct qpnp_led_data *led,struct device_node *node)
{int rc;u8 led_mode;const char *mode;led->rgb_cfg = devm_kzalloc(&led->pdev->dev,sizeof(struct rgb_config_data), GFP_KERNEL);if (!led->rgb_cfg)return -ENOMEM;if (led->id == QPNP_ID_RGB_RED)led->rgb_cfg->enable = RGB_LED_ENABLE_RED;else if (led->id == QPNP_ID_RGB_GREEN)led->rgb_cfg->enable = RGB_LED_ENABLE_GREEN;else if (led->id == QPNP_ID_RGB_BLUE)led->rgb_cfg->enable = RGB_LED_ENABLE_BLUE;elsereturn -EINVAL;rc = of_property_read_string(node, "qcom,mode", &mode);if (!rc) {led_mode = qpnp_led_get_mode(mode);if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {dev_err(&led->pdev->dev, "Selected mode not supported for rgb\n");return -EINVAL;}led->rgb_cfg->pwm_cfg = devm_kzalloc(&led->pdev->dev,sizeof(struct pwm_config_data),GFP_KERNEL);if (!led->rgb_cfg->pwm_cfg) {dev_err(&led->pdev->dev,"Unable to allocate memory\n");return -ENOMEM;}led->rgb_cfg->pwm_cfg->mode = led_mode;led->rgb_cfg->pwm_cfg->default_mode = led_mode;} else {return rc;}rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->pdev, node);if (rc < 0) {if (led->rgb_cfg->pwm_cfg->pwm_dev)pwm_put(led->rgb_cfg->pwm_cfg->pwm_dev);return rc;}return 0;
}

先解析出led的模式,有三种模式。

static int qpnp_led_get_mode(const char *mode)
{if (strcmp(mode, "manual") == 0)return MANUAL_MODE;else if (strcmp(mode, "pwm") == 0)return PWM_MODE;else if (strcmp(mode, "lpg") == 0)return LPG_MODE;elsereturn -EINVAL;
};

随后解析出PWM的配置

static int qpnp_get_config_pwm(struct pwm_config_data *pwm_cfg,struct platform_device *pdev,struct device_node *node)
{struct property *prop;int rc, i, lut_max_size;u32 val;u8 *temp_cfg;const char *led_label;pwm_cfg->pwm_dev = of_pwm_get(node, NULL);if (IS_ERR(pwm_cfg->pwm_dev)) {rc = PTR_ERR(pwm_cfg->pwm_dev);dev_err(&pdev->dev, "Cannot get PWM device rc:(%d)\n", rc);pwm_cfg->pwm_dev = NULL;return rc;}if (pwm_cfg->mode != MANUAL_MODE) {rc = of_property_read_u32(node, "qcom,pwm-us", &val);if (!rc)pwm_cfg->pwm_period_us = val;elsereturn rc;}pwm_cfg->use_blink =of_property_read_bool(node, "qcom,use-blink");if (pwm_cfg->mode == LPG_MODE || pwm_cfg->use_blink) {pwm_cfg->duty_cycles =devm_kzalloc(&pdev->dev,sizeof(struct pwm_duty_cycles), GFP_KERNEL);if (!pwm_cfg->duty_cycles) {dev_err(&pdev->dev, "Unable to allocate memory\n");rc = -ENOMEM;goto bad_lpg_params;}prop = of_find_property(node, "qcom,duty-pcts",&pwm_cfg->duty_cycles->num_duty_pcts);if (!prop) {dev_err(&pdev->dev, "Looking up property node qcom,duty-pcts failed\n");rc =  -ENODEV;goto bad_lpg_params;} else if (!pwm_cfg->duty_cycles->num_duty_pcts) {dev_err(&pdev->dev, "Invalid length of duty pcts\n");rc =  -EINVAL;goto bad_lpg_params;}rc = of_property_read_string(node, "label", &led_label);if (rc < 0) {dev_err(&pdev->dev,"Failure reading label, rc = %d\n", rc);return rc;}if (strcmp(led_label, "kpdbl") == 0)lut_max_size = PWM_GPLED_LUT_MAX_SIZE;elselut_max_size = PWM_LUT_MAX_SIZE;pwm_cfg->duty_cycles->duty_pcts =devm_kzalloc(&pdev->dev,sizeof(int) * lut_max_size,GFP_KERNEL);if (!pwm_cfg->duty_cycles->duty_pcts) {dev_err(&pdev->dev, "Unable to allocate memory\n");rc = -ENOMEM;goto bad_lpg_params;}pwm_cfg->old_duty_pcts =devm_kzalloc(&pdev->dev,sizeof(int) * lut_max_size,GFP_KERNEL);if (!pwm_cfg->old_duty_pcts) {dev_err(&pdev->dev, "Unable to allocate memory\n");rc = -ENOMEM;goto bad_lpg_params;}temp_cfg = devm_kzalloc(&pdev->dev,pwm_cfg->duty_cycles->num_duty_pcts *sizeof(u8), GFP_KERNEL);if (!temp_cfg) {dev_err(&pdev->dev, "Failed to allocate memory for duty pcts\n");rc = -ENOMEM;goto bad_lpg_params;}memcpy(temp_cfg, prop->value,pwm_cfg->duty_cycles->num_duty_pcts);for (i = 0; i < pwm_cfg->duty_cycles->num_duty_pcts; i++)pwm_cfg->duty_cycles->duty_pcts[i] =(int) temp_cfg[i];rc = of_property_read_u32(node, "qcom,start-idx", &val);if (!rc) {pwm_cfg->lut_params.start_idx = val;pwm_cfg->duty_cycles->start_idx = val;} elsegoto bad_lpg_params;pwm_cfg->lut_params.lut_pause_hi = 0;rc = of_property_read_u32(node, "qcom,pause-hi", &val);if (!rc)pwm_cfg->lut_params.lut_pause_hi = val;else if (rc != -EINVAL)goto bad_lpg_params;pwm_cfg->lut_params.lut_pause_lo = 0;rc = of_property_read_u32(node, "qcom,pause-lo", &val);if (!rc)pwm_cfg->lut_params.lut_pause_lo = val;else if (rc != -EINVAL)goto bad_lpg_params;pwm_cfg->lut_params.ramp_step_ms =QPNP_LUT_RAMP_STEP_DEFAULT;rc = of_property_read_u32(node, "qcom,ramp-step-ms", &val);if (!rc)pwm_cfg->lut_params.ramp_step_ms = val;else if (rc != -EINVAL)goto bad_lpg_params;pwm_cfg->lut_params.flags = QPNP_LED_PWM_FLAGS;rc = of_property_read_u32(node, "qcom,lut-flags", &val);if (!rc)pwm_cfg->lut_params.flags = (u8) val;else if (rc != -EINVAL)goto bad_lpg_params;pwm_cfg->lut_params.idx_len =pwm_cfg->duty_cycles->num_duty_pcts;}return 0;bad_lpg_params:pwm_cfg->use_blink = false;if (pwm_cfg->mode == PWM_MODE) {dev_err(&pdev->dev, "LPG parameters not set for blink mode, defaulting to PWM mode\n");return 0;}return rc;
};

配置完后就能通过brightness_set调用qpnp_led_set了。

qcom sdm660 led 分析相关推荐

  1. QCOM Sensor SEE 分析--初始化

    写在前面 17年到23年这么多年了只有几篇文章,看了几篇博客和高通文档后还是对SEE一脸懵逼,只能自己瞎JB乱看一通代码,然后再瞎JB记录一下,也不知道对不对.目标是把注册驱动时要填充的那两个结构体的 ...

  2. 【高通SDM660平台 Android 10.0】(19) --- Camera_focus、Camera_snapshot、volume_up 按键工作原理分析

    [高通SDM660平台 Android 10.0]19 --- Camera_focus.Camera_snapshot.volume_up 按键工作原理分析 一. DTS代码配置 二. Kernel ...

  3. [sdm660 android9.0]补光灯代码分析

    高通相关dtsi解析 驱动程序 v2 用于在背景光线昏暗时为相机传感器提供照明以捕捉良好的画面.它也可用于手电筒/手电筒应用. 它是 Qualcomm Technologies Inc. 参考平台上 ...

  4. android_驱动_qcom_【高通SDM660平台】(1) ---Bringup Guide

    [高通SDM660平台]Camera 驱动 Bringup Guide 一.Kernel 代码移植 1. DTS 文件配置 1.1 sdm660.dtsi 1.2 sdm660-camera.dtsi ...

  5. 【高通SDM660平台】(1) --- Camera 驱动 Bringup Guide

    [高通SDM660平台]Camera 驱动 Bringup Guide 一.Kernel 代码移植 1. DTS 文件配置 1.1 sdm660.dtsi 1.2 sdm660-camera.dtsi ...

  6. Qcom 相机点亮流程学习笔记

    和你一起终身学习,这里是程序员Android 经典好文推荐,通过阅读本文,您将收获以下知识点: 一.Kernel 代码移植 二.Vendor 代码移植 三.扩展 一.Kernel 代码移植 1. DT ...

  7. 全球及中国深紫外LED行业盈利状况与前景动态研究报告2022版

    全球及中国深紫外LED行业盈利状况与前景动态研究报告2022版 --------------------------------------- [修订日期]:2021年12月 [搜索鸿晟信合研究院查看 ...

  8. CIE颜色空间是如何用来设计更好的led的

    LED照明工程师工作最了解国际照明委员会(CIE)1931 x,y的颜色空间(或CIE颜色空间)是用来表征白光LED的基础上.纯彩色或白色发光二极管的输出可以根据图上的x和y坐标定义颜色(色相)和饱和 ...

  9. 2021-2027全球与中国紫外线消毒LED市场现状及未来发展趋势

    2021-2027全球与中国紫外线消毒LED市场现状及未来发展趋势 2020年,全球紫外线消毒LED市场规模达到了 亿元,预计2027年将达到 亿元,年复合增长率(CAGR)为 %. 本报告研究全球与 ...

最新文章

  1. Java并发编程—常见面试题
  2. 前端QRCode.js生成二维码插件
  3. SAP CRM email office integration
  4. 为什么猫王不应该访问Java
  5. 前端学习(2370):组件之间的通讯方式
  6. 旅游流的概念_2020年去张家界凤凰古城旅游亲身体验经历分享——实用攻略(图文)...
  7. [Cacti] cacti监控mongodb性能实战
  8. git status怎么操作_新手 git 简明操作指南
  9. 玩转iOS开发:iOS 8 新特性《UIVisualEffect》
  10. 猎洞20年老兵的经验之谈
  11. C++变量初始化问题
  12. ICP许可证的办理条件
  13. 详解:Java的重载方法与示例
  14. 第一篇:FC-SAN存储技术
  15. CHM Editor V1.3.3.7(chm编辑、修改chm文件、chm编辑器)
  16. 怎样把自己喜欢的微信表情包(动态)导出来,我三岁半的表弟都会...
  17. Linux 文件锁的原理、实现和应用
  18. Fabric 版本问题
  19. [ACNOI2022]Bitset Dog
  20. [Thoughts]敏捷方法大全

热门文章

  1. winxp计算机无法访问,分享WinXP工作组计算机无法访问的解决方法
  2. Android 11 上传图片适配 和 第三方登录
  3. Apollo6.0代码Lattice算法详解——Part6:轨迹评估及碰撞检测对象构建
  4. Fortran的subroutine可变数组作为形参
  5. C++模板元编程(7)typename的其他用法
  6. 红外测温仪额温枪产品/芯片/PCBA/传感器
  7. 一文带你分分钟掌握智能手机处理器的前世今生,再也不用担心妈妈老婆女友让我选手机啦
  8. 什么是网络爬虫,网络爬虫的职能是什么
  9. 顶象陈树华:安全也能共享?你没看错!
  10. matplotlib折线图(设置图片大小和图片保存)