45 pinctrl子系统 和 gpio子系统
文章目录
- 一、6ull 的 gpio 使用步骤
- 二、pinctrl子系统
- 三、gpio 子系统
- 四、驱动编写
- 五、总结
- 六、相关函数
一、6ull 的 gpio 使用步骤
- 1、设置 pin 的 复用 和 电气属性(通过 pinctrl 子系统 )
- 2、配置 gpio 的输入输出,高低电平(通过 GPIO子系统)
二、pinctrl子系统
pinctrl 和 gpio 子系统详解
- 借助
pinctrl子系统
来设置一个 pin 的 复用 和 电气属性
打开文件imx6ull.dtsi
:
// 详见 imx6ull.dtsi 参考手册 176,1542
iomuxc: iomuxc@020e0000 { // 这个结点表示 IO控制器 外设// compatible属性用来匹配pinctrl驱动compatible = "fsl,imx6ul-iomuxc";// 此为寄存器地址范围,此地址范围的寄存器控制每个 pin 的复用和电气属性// 基地址,大小reg = <0x020e0000 0x4000>;
};
// gpr 控制器,详见 imx6ull.dtsi 参考手册 1475
gpr: iomuxc-gpr@020e4000 {compatible = "fsl,imx6ul-iomuxc-gpr","fsl,imx6q-iomuxc-gpr", "syscon";reg = <0x020e4000 0x4000>;
};......
// iomuxc_snvs 控制器,详见 imx6ull.dtsi 参考手册 1495
iomuxc_snvs: iomuxc-snvs@02290000 {compatible = "fsl,imx6ull-iomuxc-snvs";reg = <0x02290000 0x10000>;
};
- 打开
imx6ull-alientek-emmc.dts
文件:
// 对节点 iomuxc 进行追加
// 追加方式:&标签名
&iomuxc {pinctrl-names = "default";pinctrl-0 = <&pinctrl_hog_1>;imx6ul-evk { // evk 是官方开发板pinctrl_hog_1: hoggrp-1 {fsl,pins = <MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059 /* SD1 RESET */>;};...pinctrl_i2c2: i2c2grp {fsl,pins = <MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0>;};pinctrl_lcdif_dat: lcdifdatgrp {fsl,pins = <MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79...>;};...};
};
pinctrl 子系统就是:创建一个
设备子节点
,然后将此设备所用 pin 的配置信息
都放到这个子节点
里面
注意格式:存放 pin 配置信息的 属性名 一定要是fsl,pins
如何添加 一个 pin 的配置信息
如MX6UL_PAD_UART1_RTS_B__GPIO1_IO19
fsl,pins = <MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059 /* SD1 RESET */>;
在 imx6ul-pinfunc.h
中找到
/** The pin function ID is a tuple of* <mux_reg conf_reg input_reg mux_mode input_val>*/
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
mux_reg | conf_reg | input_reg | mux_mode | input_val |
---|---|---|---|---|
0x0090 | 0x031C | 0x0000 | 0x5 | 0x0 |
iomuxc 节点 首地址
为 0x020e0000
,0x0090
是偏移地址,UART1_RTS_B
绝对地址为 0x020e0090
(imx6ull参考手册 1581)
muxmode:5
表示复用为 GPIO1_IO19
conf_reg:0x031C
,相对于基地址的偏移,0x020e0000 + 0x031c = 0x020e031c
,这个地址寄存器是 UART1_RTS_B
的电气属性配置寄存器,0x17059
是写给此寄存器来配置电气属性的
input_reg :0,偏移为 0,表示 UART1_RTS_B 这个 pin 没有 input 功能
input_val:0,写给 input_reg
寄存器的值,但是这个 pin 没有
- pinctrl 驱动工作原理简介
如何找到 imx6ull 对应的pinctrl 子系统驱动(半导体厂商写好的)
使用 节点的compatible 属性
驱动文件里面有一个描述驱动兼容性
的内容,当设备树节点的 compatible 属性 和 驱动里面的兼容性(也是字符串)匹配时,就表示设备和驱动匹配了,表示设备可以使用该驱动文件
所以只需要全局搜索设备节点里面的 compatible 属性的值 即可找到那个文件,为/driver/pinctrl/freescale/pinctrl-imx6ul.c
,注意其中的of_device_id
结构体
static struct of_device_id imx6ul_pinctrl_of_match[] = {{ .compatible = "fsl,imx6ul-iomuxc", .data = &imx6ul_pinctrl_info, },{ .compatible = "fsl,imx6ull-iomuxc-snvs", .data = &imx6ull_snvs_pinctrl_info, },{ /* sentinel */ }
};
因此设备树中的对应节点使用的驱动是此文件
当驱动和设备节点匹配以后,会执行 probe类 函数 :imx6ul_pinctrl_probe
三、gpio 子系统
- 使用
gpio 子系统
来操作gpio
&usdhc1 {pinctrl-names = "default", "state_100mhz", "state_200mhz";pinctrl-0 = <&pinctrl_usdhc1>; // 此设备相关的io有这3个pinctrl-1 = <&pinctrl_usdhc1_100mhz>;pinctrl-2 = <&pinctrl_usdhc1_200mhz>;cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;keep-power-in-suspend;enable-sdio-wakeup;vmmc-supply = <®_sd1_vmmc>;status = "okay";
};
定义了一个
cd-gpios
属性,属性名自取。属性值描述 io 信息
参考设备绑定文档:devicetree/binfings/gpio/fsl-gpio.txt
此处使用 gpio1_io19
打开参考手册1357
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
:&gpio 表示 使用 gpio1 这一组,19 表示第19个 pin,GPIO_ACTIVE_LOW 是个宏,值为1,表示低电平有效imx6ull.dtsi
中 的 gpio1 节点
gpio1: gpio@0209c000 {compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";reg = <0x0209c000 0x4000>;interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;gpio-controller;#gpio-cells = <2>;interrupt-controller;#interrupt-cells = <2>;};
1、获取gpio所处的设备节点
of_find_node_by_path
2、获取 gpio 编号,of_get_named_gpio
,(int型)
3、申请一个 GPIO 管脚,gpio_request
4、设置 gpio,输入或输出,
gpio_direction_input
或gpio_direction_output
若设置成输入,gpio_get_value
读取某个值
若设置成输出,gpio_set_value
设置输出值gpiolib
两部分,一部分是给原厂编写 gpio底层驱动的,一部分给驱动开发人员使用 gpio 操作函数的
使用gpiochip_add
向系统添加 gpio_chip,这些都是半导体原厂做的,这部分就是最底层的 gpio 驱动gpio驱动
在drivers/gpio
目录下,gpio-xxx.c 文件
为具体的芯片的驱动文件(最底层):gpio_mxc.c
gpiolib位于 应用层api 和 底层驱动之间,gpiochip_add
add的是一个 gpio_chip 结构体,这个结构体里面包含了底层gpio的操作方法,操作gpio时最终都是调用这个结构体里面的成员函数全局搜索某节点的 compatible属性值,就能找到它的驱动文件
// gpio-muxc.c gpio 的 各个控制寄存器偏移地址
static struct mxc_gpio_hwdata imx35_gpio_hwdata = {.dr_reg = 0x00,.gdir_reg = 0x04,.psr_reg = 0x08,.icr1_reg = 0x0c,.icr2_reg = 0x10,.imr_reg = 0x14,.isr_reg = 0x18,.edge_sel_reg = 0x1c,.low_level = 0x00,.high_level = 0x01,.rise_edge = 0x02,.fall_edge = 0x03,
};
- 函数调用流程
mxc_gpio_probe
-> mxc_gpio_get_hw 获取 6ull 的 gpio控制寄存器组的地址
-> bgpio_init (重点)初始化 gpio_chip 结构体
-> gpio_add 想内核添加 gpio_chip
四、驱动编写
- 1、修改设备树
参考imx6ul-pinfunc.h
访问 iomuxc 节点, 在其下添加节点
pinctrl_gpiobeep: beepgrp {fsl,pins = </*MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10b0*/MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10b0>;};
- 在 根结点下添加节点
gpiobeep {compatible = "alientek,gpiobeep" ; // 其实用不到这个compatible 属性pinctrl-names = "default";pinctrl-0 = <&pinctrl_gpiobeep>; // pin 的 配置信息// 此项属性用来获取 gpio 编号beep-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;status = "okay";};
- 2、驱动
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include <linux/fs.h>
#include<linux/slab.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include<linux/of_address.h>
#include<linux/of_irq.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>#define GPIOBEEP_DEVID_CNT 1
#define GPIOBEEP_NAME "gpiobeep"
#define BEEP_OFF 0
#define BEEP_ON 1extern struct gpiobeep_dev gpiobeep;// 自定义 beep设备类型,描述一个 beep 的信息
struct gpiobeep_dev
{dev_t devid; // 设备号u32 major;u32 minor;struct cdev cdev; // 用于注册字符设备struct class *class; // 用于自动创建设备节点,注意成员变量类型是结构体指针struct device *device; // 用于自动创建设备节点,注意成员变量类型是结构体指针struct device_node *nd; // 用来表示设备树中的一个节点,用于获取设备树信息int beep_gpio; // gpio 标号
}; // 数据类型定义一定要放在最前面static ssize_t beep_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{int ret;u8 databuf;struct gpiobeep_dev *dev = filp->private_data;ret = copy_from_user(&databuf, buf, count); // 从 用户空间 到 内核空间if(ret < 0){printk("%d:--------------\r\n", __LINE__);return -1;}if(databuf == BEEP_OFF) // 蜂鸣器不响{gpio_set_value(dev->beep_gpio, 1);}else if(databuf == BEEP_ON) // // 蜂鸣器响{gpio_set_value(dev->beep_gpio, 0);}return 0;
}static int beep_open(struct inode *inode, struct file *filp)
{// 其它操作集成员函数都可以通过 filp->private_data 来访问 beep类型结构体filp->private_data = &gpiobeep;return 0;
}static int beep_release(struct inode *inode, struct file *filp)
{return 0;
}// 操作集函数
static const struct file_operations gpiobeep_fops =
{.owner = THIS_MODULE,.write = beep_write,.open = beep_open,.release = beep_release,
};struct gpiobeep_dev gpiobeep;// 驱动入口函数
static int __init beep_init(void)
{int val;int ret = 0;// 注册字符设备//1 获取设备号gpiobeep.major = 0;if(gpiobeep.major){gpiobeep.devid = MKDEV(gpiobeep.major, 0); // 次设备号设为0// 注册自己指定的设备号ret = register_chrdev_region(gpiobeep.devid, GPIOBEEP_DEVID_CNT, GPIOBEEP_NAME);}else{// 系统分配设备号,就不要再向系统注册了ret = alloc_chrdev_region(&gpiobeep.devid, 0, GPIOBEEP_DEVID_CNT, GPIOBEEP_NAME);gpiobeep.major = MAJOR(gpiobeep.devid);gpiobeep.minor = MINOR(gpiobeep.devid);}if(ret < 0){printk("%d:-------fail--------\r\n", __LINE__);goto fail_devid;}printk("%d: gpiobeep.devid = %u\r\n", __LINE__, gpiobeep.devid);printk("%d: gpiobeep.major = %u\r\n", __LINE__, gpiobeep.major);printk("%d: gpiobeep.minor = %u\r\n", __LINE__, gpiobeep.minor);// 2 初始化 cdev结构体,注册字符设备gpiobeep.cdev.owner = THIS_MODULE;cdev_init(&gpiobeep.cdev, &gpiobeep_fops); // 注意两个参数都是结构体指针ret = cdev_add(&gpiobeep.cdev, gpiobeep.devid, GPIOBEEP_DEVID_CNT);if(ret){printk("%d:-------fail--------\r\n", __LINE__);goto fail_cdevadd;}// 3 自动创建设备节点// 一个 class结构体, 一个 device结构体 ,注意这两个结构体是以指针的形式存在于 自定义beep结构体 中的// 3.1 创建 class结构体gpiobeep.class = class_create(THIS_MODULE, GPIOBEEP_NAME);if(IS_ERR(gpiobeep.class)){ret = PTR_ERR(gpiobeep.class);goto fail_class;}// 3.2 创建 device结构体 // 第一个 NULL 表示副设备, 第二个 NULL 表示 drvdatagpiobeep.device = device_create(gpiobeep.class, NULL, gpiobeep.devid, NULL, GPIOBEEP_NAME);// IS_ERR include/linux/err.hif(IS_ERR(gpiobeep.device)){ret = PTR_ERR(gpiobeep.device);goto fail_device;}// 4. 获取 设备树节点 gpiobeep.nd = of_find_node_by_path("/gpiobeep");if(gpiobeep.nd == NULL){ret = -1;goto fail_findnd;}// 5 获取 beep 对应的 gpio 标号,第二个参数属性名要和设备树文件中的一致gpiobeep.beep_gpio = of_get_named_gpio(gpiobeep.nd, "beep-gpios", 0);if(gpiobeep.beep_gpio < 0){ret = -1;printk("%d:-------fail--------\r\n", __LINE__);goto fail_findnd;}printk("%d:beep_gpio num = %u\r\n", __LINE__, gpiobeep.beep_gpio);// 6 申请 gpio 标号ret = gpio_request(gpiobeep.beep_gpio, "beep-gpio");if(ret){printk("%d:---------fail-----------\r\n", __LINE__);ret = -1;goto fail_findnd;}// 7 申请 gpio 标号 ret = gpio_direction_output(gpiobeep.beep_gpio, 1);if(ret){printk("%d:---------fail------------\r\n", __LINE__);ret = -1;goto fail_findsetdir;}//8 设置 GPIO 输出低电平gpio_set_value(gpiobeep.beep_gpio, 0);return 0;fail_findsetdir: // 释放 gpio标号gpio_free(gpiobeep.beep_gpio);
fail_findnd: // 删除设备device_destroy(gpiobeep.class, gpiobeep.devid);
fail_device: // 删除类class_destroy(gpiobeep.class);
fail_class: // 删除字符设备cdev_del(&gpiobeep.cdev);
fail_cdevadd: // 释放设备号unregister_chrdev_region(gpiobeep.devid, GPIOBEEP_DEVID_CNT);
fail_devid:printk("%d:---------------\r\n", __LINE__);return ret;
}// 驱动出口函数
static void __exit beep_exit(void)
{// 退出驱动时关闭蜂鸣器gpio_set_value(gpiobeep.beep_gpio, 1);// 删除字符设备cdev_del(&gpiobeep.cdev);// 释放设备号unregister_chrdev_region(gpiobeep.devid, GPIOBEEP_DEVID_CNT);// 删除设备device_destroy(gpiobeep.class, gpiobeep.devid);// 删除类class_destroy(gpiobeep.class);// 释放 gpio 标号gpio_free(gpiobeep.beep_gpio);
}module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
五、总结
- 1、添加 pinctrl 信息
在iomuxc
结点下的imx6ul-evk
子节点下,创建一个新的节点
这个新的节点表示某一外设所用到的所有 pin 的配置信息(复用和电气属性)
pinctrl_gpiobeep: beepgrp { // 节点标签一定要是 “pinctrl_xxx”fsl,pins = < // 属性名一定要是 “fsl,pins”MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10b0/*MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10b0*/>;};
- 2、检查当前设备树中要使用的 IO 是否被其他设备使用,若有则要进行处理(屏蔽或者禁用)
- 3、添加设备节点
在设备节点中创建一个属性,此属性所使用的 GPIO
gpiobeep {compatible = "alientek,gpiobeep" ;pinctrl-names = "default";pinctrl-0 = <&pinctrl_gpiobeep>; <----- 蜂鸣器 pin 的配置信息led-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>; <----- 由此属性知道使用哪个 GPIOstatus = "okay";};
- 4、编写驱动,获取对应的 GPIO 编号,申请 IO,成功以后使用 GPIO子系统 提供的 API函数 来操作 GPIO
六、相关函数
- 1、
int of_get_named_gpio (struct device_node *np, const char *propname, int index)
描述:用于获取 gpio编号
np
:设备节点结构体指针
propname
:包含要获取 GPIO 信息的属性名(要和设备书中的属性名一致)
index
:gpio索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO 的编号,若只有一个 GPIO信息 的话,此参数为 0
返回值:正值,获取到的 gpio编号。负值,失败 - 2、
int gpio_request (unsigned gpio, const char *label)
描述:用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用gpio_request
进行申请
gpio
:要申请的gpio标号
label
:给 gpio 设置一个名字
返回值 :0,申请成功。其他值,申请失败
若申请失败,一般都是因为这个 pin 被占用了
例如:开发板上的 led 使用的 gpio 是gpio1 io03
,检查设备树文件dts
&tsc {pinctrl-names = "default";pinctrl-0 = <&pinctrl_tsc>;xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; <----- 此行要注释掉measure-delay-time = <0xffff>;pre-charge-time = <0xfff>;status = "okay";
};...pinctrl_tsc: tscgrp {fsl,pins = <MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0xb0MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0xb0MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xb0 <----- 此行要注释掉MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0xb0>;
};
3、
void gpio_free (unsigned gpio)
描述:若不使用某个 GPIO 了,那么就要调用此函数进行释放
gpio
:要释放的 gpio 标号4、
int gpio_direction_input (unsigned gpio)
描述:此函数用于设置某个 gpio 为输入
gpio
:要设置为输入的 GPIO 标号
返回值:0,设置成功;负值,设置失败5、
int gpio_direction_output (unsigned gpio, int value)
描述:此函数用于设置某个 gpio 为输入,并且设置默认值
gpio
:要设置为输出的 GPIO 标号
value
:gpio 默认输出值
返回值:0,设置成功;负值,设置失败6、
#define gpio_get_value __gpio_get_value
int __gpio_get_value (unsigned gpio)
描述:此函数用于获取某个 GPIO 的值(0 或 1),此函数是个宏
gpio
:要获取的 GPIO 标号
返回值:非负值,得到的 GPIO 值;负值,获取失败7、
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
描述:此函数用于设置某个 GPIO 的值,此函数是个宏
gpio
:要设置的 GPIO 标号
value
:要设置的值8、
int of_gpio_named_count(struct device_node *np, const char *propname)
描述:用于获取设备树某个属性里面定义了几个 GPIO 信息,要注意的是空的 GPIO 信息也会被统计到
np
:设备节点
propname
:要统计的 gpio 属性
返回值:正值,统计到的 GPIO 数量。负值,失败9、
int of_gpio_count(struct device_node *np)
描述:和 of_gpio_named_count 函数一样,但是不同的地方在于,此函数统计的是“gpios”这个属性的 GPIO 数量,而 of_gpio_named_count 函数可以统计任意属性的 GPIO 信息
np
:设备节点
返回值:正值,统计到的 GPIO 数量;负值,失败
45 pinctrl子系统 和 gpio子系统相关推荐
- pinctrl 和 gpio 子系统
内容来自<[正点原子]I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf> 目录 pinctrl 子系统 pinctrl 子系统简介 I.MX6ULL 的 pinctrl 子系统 ...
- 【嵌入式Linux】嵌入式Linux驱动开发基础知识之Pinctrl子系统和GPIO子系统的使用
文章目录 前言 1.Pinctrl子系统 1.1.为什么有Pinctrl子系统 1.2.重要的概念 1.3.代码中怎么引用pinctrl 2.GPIO子系统 2.1.为什么有GPIO子系统 2.2.在 ...
- linux系统中pinctrl 和gpio子系统使用方法(教你点灯)
如何使用pinctrl和gpio子系统点亮led pinctrl 子系统作用 设备树PIN配置 gpio子系统介绍 配置gpio相关 编写驱动程序 编写应用程序 pinctrl 子系统作用 pinct ...
- 【正点原子Linux连载】第四十五章 pinctrl和gpio子系统实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0
1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...
- 【正点原子MP157连载】第二十五章 pinctrl和gpio子系统实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7
1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...
- 使用pinctrl 和 gpio 子系统的字符设备驱动
pinctrl 和 gpio 子系统的字符设备驱动 一. 修改设备树文件 二. LED 灯驱动程序编写 三.makefile 四.应用层代码 运行测试 一. 修改设备树文件 打开 imx6ull-al ...
- pinctrl子系统和gpio子系统
pinctrl子系统gpio子系统与硬件之间的联系 GPIO与IOMUXC寄存器共同配置IO口 GPIO寄存器与gpio子系统相关联:控制IO口输入输出. led {compatible = &quo ...
- pinctrl和gpio子系统
目录 1.pinctrl子系统 1)pinctrl子系统简介 2)I.MX6ULL的的pinctrl子系统驱动 3)设备树中添加pinctrl节点模板 2.gpio子系统 1)gpio子系统简介 2) ...
- Linux设备模型、平台设备驱动、设备树(device tree)、GPIO子系统以及pinctrl子系统介绍
文章目录 一.Linux设备模型介绍 (1)设备驱动模型总体介绍 (2)设备驱动模型文件表现 (3)设备驱动模型工作原理 [1]总线 [2]设备 [3]驱动 [4]注册流程 二.平台设备驱动介绍 (1 ...
- pinctrl 和 gpio 子系统 终极总结
1.内核提供了 pinctrl 和 gpio 子系统用于 gpio驱动.linux是一个庞大而又完善的系统 不可能 让你用裸板开发的方式 去 操作 gpio. 该系统是按照面向对象的设计思想设计 ...
最新文章
- 预示敏捷方法走偏的15个标志——第1部分
- 【MySQL】(万字解析)MySQL表的增删改查(进阶-上)
- android按钮随机数,Android随机数
- 我是如何用机器学习技术帮助 HR 省时间的
- linux tar.gz指定目录,tar.gz包内提取某个文件在指定目录下。
- Junit4 简单教程
- 在Java中,如何使一个字符串的首字母变为大写
- HTML+CSS+JS实现echarts图表炫光分布地图动画
- java的joptionpane空白_java – JOptionPane无法正确显示?
- linux ps画图,PhotoGIMP:让Linux下的GIMP交互体验就像Photoshop
- Android开发笔记(十九)底部标签栏TabBar
- 不卡顿成用户购机第一要素,Mate 9深得人心
- 【深度学习】使用opencv在视频上添加文字和标记框
- java dns 解析域名解析_JavaWeb(3)DNS域名解析
- 云通讯 发送短信模板代码
- 用c语言编写天数计算器,C/C++实现日期计算器的示例代码
- 阿里腾讯暑期实习面试被刷的经历
- 首尾相连数组的最大子数组和
- python描述性统计命令_描述性统计_Python数据分析实战应用_数据挖掘与分析视频-51CTO学院...
- 3款Android版epub阅读器推荐