本篇文章为gpio外设驱动,使用内核的gpio驱动框架来操作soc的gpio外设,首先介绍所有外设引脚配置都需要的pinctrl,再介绍本篇的重点-gpio外设驱动,最后给出三个标准的gpio外设驱动:led、蜂鸣器、按键。

文章目录

  • 一、pinctrl和gpio子系统概述
  • 二、pinctrl子系统
    • 2.1 简介
    • 2.2 设备树解析
    • 2.3 示例-添加任意外设pinctrl配置信息
  • 三、gpio子系统
  • 四、示例一-led驱动
  • 五、示例二-蜂鸣器驱动
  • 六、示例三-按键输入

一、pinctrl和gpio子系统概述

  • pinctrl:配置pin的复用功能和电气属性,除了配置gpio外设外,i2c、spi等通用外设也适用。
  • gpio:提供操作gpio的api。

二、pinctrl子系统

2.1 简介

  pinctrl子系统主要与设备树结合使用,在设备树里面设置好某个pin的相关属性后,其他的初始化工作均由 pinctrl 子系统来完成,pinctrl子系统源码目录为drivers/pinctrl。iomuxc设备树如下。

// imx6ull.dtsi
iomuxc: iomuxc@020e0000 {compatible = "fsl,imx6ul-iomuxc";reg = <0x020e0000 0x4000>;
}
// imx6ull-alientek-emmc.dts
&iomuxc {pinctrl-names = "default";pinctrl-0 = <&pinctrl_hog_1>;imx6ul-evk {pinctrl_hog_1: hoggrp-1 {fsl,pins = <MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058>;};
......pinctrl_flexcan1: flexcan1grp{fsl,pins = <MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX 0x1b020MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX 0x1b020>;};......pinctrl_wdog: wdoggrp {fsl,pins = <MX6UL_PAD_LCD_RESET__WDOG1_WDOG_ANY 0x30b0>;};};
};

2.2 设备树解析

  imx6ull.dtsi的iomuxc节点的reg属性描述了iomuxc外设的基地址为0x020e0000,在imx6ull-alientek-emmc.dts追加了iomuxc节点信息,pinctrl_hog_1是pinctrl配置节点的第一个节点,其下的fsl,pins属性解释如下:

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059

  MX6UL_PAD_UART1_RTS_B__GPIO1_IO19宏定义如下,MX6UL_PAD_UART1_RTS_B可以有UART1_DCE_RTS 、UART1_DTE_CTS 等复用功能,MX6UL_PAD_UART1_RTS_B__GPIO1_IO19代表普通GPIO,后面的0x17059是GPIO的配置信息:上下拉、输入输出等配置。

#define MX6UL_PAD_UART1_RTS_B__UART1_DCE_RTS 0x0090 0x031C 0x0620 0x0 0x3#define MX6UL_PAD_UART1_RTS_B__UART1_DTE_CTS 0x0090 0x031C 0x0000 0x0 0x0#define MX6UL_PAD_UART1_RTS_B__ENET1_TX_ER 0x0090 0x031C 0x0000 0x1 0x0#define MX6UL_PAD_UART1_RTS_B__USDHC1_CD_B 0x0090 0x031C 0x0668 0x2 0x1#define MX6UL_PAD_UART1_RTS_B__CSI_DATA05 0x0090 0x031C 0x04CC 0x3 0x1

2.3 示例-添加任意外设pinctrl配置信息

  在2.1节的iomuxc节点的imx6ul-evk节点下新建一个pinctrl节点pinctrl_test,其fsl,pins属性设置为 MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config。

pinctrl_test: testgrp {fsl,pins = <MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config /*config        是具体设置值*/>;
}

三、gpio子系统

  gpio子系统提供api函数来操作gpio,soc原厂已经写好底层的驱动程序,我们需要在设备树中对gpio进行配置,之后在驱动程序里面读取设备树的配置信息用于初始化外设,后面就能用api操作gpio。

四、示例一-led驱动

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define GPIOLED_CNT            1           /* 设备号个数 */
#define GPIOLED_NAME        "gpioled" /* 名字 */
#define LEDOFF              0           /* 关灯 */
#define LEDON               1           /* 开灯 *//* gpioled设备结构体 */
struct gpioled_dev{dev_t devid;         /* 设备号   */struct cdev cdev;        /* cdev     */struct class *class;  /* 类        */struct device *device;    /* 设备    */int major;               /* 主设备号   */int minor;              /* 次设备号   */struct device_node  *nd; /* 设备节点 */int led_gpio;            /* led所使用的GPIO编号        */
};struct gpioled_dev gpioled;   /* led设备 *//** @description        : 打开设备* @param - inode     : 传递给驱动的inode* @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量*                       一般在open的时候将private_data指向设备结构体。* @return             : 0 成功;其他 失败*/
static int led_open(struct inode *inode, struct file *filp)
{filp->private_data = &gpioled; /* 设置私有数据 */return 0;
}/** @description      : 从设备读取数据 * @param - filp  : 要打开的设备文件(文件描述符)* @param - buf    : 返回给用户空间的数据缓冲区* @param - cnt  : 要读取的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}/** @description      : 向设备写数据 * @param - filp   : 设备文件,表示打开的文件描述符* @param - buf     : 要写给设备写入的数据* @param - cnt     : 要写入的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 写入的字节数,如果为负值,表示写入失败*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[1];unsigned char ledstat;struct gpioled_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0];        /* 获取状态值 */if(ledstat == LEDON) { gpio_set_value(dev->led_gpio, 0);    /* 打开LED灯 */} else if(ledstat == LEDOFF) {gpio_set_value(dev->led_gpio, 1);    /* 关闭LED灯 */}return 0;
}/** @description      : 关闭/释放设备* @param - filp   : 要关闭的设备文件(文件描述符)* @return             : 0 成功;其他 失败*/
static int led_release(struct inode *inode, struct file *filp)
{return 0;
}/* 设备操作函数 */
static struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release =    led_release,
};/** @description : 驱动出口函数* @param       : 无* @return       : 无*/
static int __init led_init(void)
{int ret = 0;/* 设置LED所使用的GPIO *//* 1、获取设备节点:gpioled */gpioled.nd = of_find_node_by_path("/gpioled");if(gpioled.nd == NULL) {printk("gpioled node not find!\r\n");return -EINVAL;} else {printk("gpioled node find!\r\n");}/* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);if(gpioled.led_gpio < 0) {printk("can't get led-gpio");return -EINVAL;}printk("led-gpio num = %d\r\n", gpioled.led_gpio);/* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */ret = gpio_direction_output(gpioled.led_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* 注册字符设备驱动 *//* 1、创建设备号 */if (gpioled.major) {       /*  定义了设备号 */gpioled.devid = MKDEV(gpioled.major, 0);register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);} else {                     /* 没有定义设备号 */alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申请设备号 */gpioled.major = MAJOR(gpioled.devid);   /* 获取分配号的主设备号 */gpioled.minor = MINOR(gpioled.devid);  /* 获取分配号的次设备号 */}printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);  /* 2、初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);/* 3、添加一个cdev */cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);/* 4、创建类 */gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)) {return PTR_ERR(gpioled.class);}/* 5、创建设备 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.device)) {return PTR_ERR(gpioled.device);}return 0;
}/** @description  : 驱动出口函数* @param       : 无* @return       : 无*/
static void __exit led_exit(void)
{/* 注销字符设备驱动 */cdev_del(&gpioled.cdev);/*  删除cdev */unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注销设备号 */device_destroy(gpioled.class, gpioled.devid);class_destroy(gpioled.class);
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("qwz");

五、示例二-蜂鸣器驱动

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define BEEP_CNT           1       /* 设备号个数 */
#define BEEP_NAME           "beep"    /* 名字 */
#define BEEPOFF             0       /* 关蜂鸣器 */
#define BEEPON              1       /* 开蜂鸣器 *//* beep设备结构体 */
struct beep_dev{dev_t devid;            /* 设备号   */struct cdev cdev;        /* cdev     */struct class *class;  /* 类        */struct device *device;    /* 设备    */int major;               /* 主设备号   */int minor;              /* 次设备号   */struct device_node  *nd; /* 设备节点 */int beep_gpio;           /* beep所使用的GPIO编号       */
};struct beep_dev beep;     /* beep设备 *//** @description       : 打开设备* @param - inode     : 传递给驱动的inode* @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量*                       一般在open的时候将private_data指向设备结构体。* @return             : 0 成功;其他 失败*/
static int beep_open(struct inode *inode, struct file *filp)
{filp->private_data = &beep; /* 设置私有数据 */return 0;
}/** @description      : 向设备写数据 * @param - filp   : 设备文件,表示打开的文件描述符* @param - buf     : 要写给设备写入的数据* @param - cnt     : 要写入的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 写入的字节数,如果为负值,表示写入失败*/
static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[1];unsigned char beepstat;struct beep_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}beepstat = databuf[0];     /* 获取状态值 */if(beepstat == BEEPON) {   gpio_set_value(dev->beep_gpio, 0);   /* 打开蜂鸣器 */} else if(beepstat == BEEPOFF) {gpio_set_value(dev->beep_gpio, 1);  /* 关闭蜂鸣器 */}return 0;
}/** @description      : 关闭/释放设备* @param - filp   : 要关闭的设备文件(文件描述符)* @return             : 0 成功;其他 失败*/
static int beep_release(struct inode *inode, struct file *filp)
{return 0;
}/* 设备操作函数 */
static struct file_operations beep_fops = {.owner = THIS_MODULE,.open = beep_open,.write = beep_write,.release =   beep_release,
};/** @description : 驱动出口函数* @param       : 无* @return       : 无*/
static int __init beep_init(void)
{int ret = 0;/* 设置BEEP所使用的GPIO *//* 1、获取设备节点:beep */beep.nd = of_find_node_by_path("/beep");if(beep.nd == NULL) {printk("beep node not find!\r\n");return -EINVAL;} else {printk("beep node find!\r\n");}/* 2、 获取设备树中的gpio属性,得到BEEP所使用的BEEP编号 */beep.beep_gpio = of_get_named_gpio(beep.nd, "beep-gpio", 0);if(beep.beep_gpio < 0) {printk("can't get beep-gpio");return -EINVAL;}printk("led-gpio num = %d\r\n", beep.beep_gpio);/* 3、设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP */ret = gpio_direction_output(beep.beep_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* 注册字符设备驱动 *//* 1、创建设备号 */if (beep.major) {      /*  定义了设备号 */beep.devid = MKDEV(beep.major, 0);register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME);} else {                        /* 没有定义设备号 */alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME);  /* 申请设备号 */beep.major = MAJOR(beep.devid); /* 获取分配号的主设备号 */beep.minor = MINOR(beep.devid);    /* 获取分配号的次设备号 */}printk("beep major=%d,minor=%d\r\n",beep.major, beep.minor);   /* 2、初始化cdev */beep.cdev.owner = THIS_MODULE;cdev_init(&beep.cdev, &beep_fops);/* 3、添加一个cdev */cdev_add(&beep.cdev, beep.devid, BEEP_CNT);/* 4、创建类 */beep.class = class_create(THIS_MODULE, BEEP_NAME);if (IS_ERR(beep.class)) {return PTR_ERR(beep.class);}/* 5、创建设备 */beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME);if (IS_ERR(beep.device)) {return PTR_ERR(beep.device);}return 0;
}/** @description  : 驱动出口函数* @param       : 无* @return       : 无*/
static void __exit beep_exit(void)
{/* 注销字符设备驱动 */cdev_del(&beep.cdev);/*  删除cdev */unregister_chrdev_region(beep.devid, BEEP_CNT); /* 注销设备号 */device_destroy(beep.class, beep.devid);class_destroy(beep.class);
}module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("qwz");

六、示例三-按键输入

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define KEY_CNT            1       /* 设备号个数    */
#define KEY_NAME        "key" /* 名字       *//* 定义按键值 */
#define KEY0VALUE       0XF0    /* 按键值      */
#define INVAKEY         0X00    /* 无效的按键值  *//* key设备结构体 */
struct key_dev{dev_t devid;         /* 设备号   */struct cdev cdev;        /* cdev     */struct class *class;  /* 类        */struct device *device;    /* 设备    */int major;               /* 主设备号   */int minor;              /* 次设备号   */struct device_node  *nd; /* 设备节点 */int key_gpio;            /* key所使用的GPIO编号        */atomic_t keyvalue;        /* 按键值      */
};struct key_dev keydev;        /* key设备 *//** @description    : 初始化按键IO,open函数打开驱动的时候*                   初始化按键所使用的GPIO引脚。* @param         : 无* @return       : 无*/
static int keyio_init(void)
{keydev.nd = of_find_node_by_path("/key");if (keydev.nd== NULL) {return -EINVAL;}keydev.key_gpio = of_get_named_gpio(keydev.nd ,"key-gpio", 0);if (keydev.key_gpio < 0) {printk("can't get key0\r\n");return -EINVAL;}printk("key_gpio=%d\r\n", keydev.key_gpio);/* 初始化key所使用的IO */gpio_request(keydev.key_gpio, "key0");    /* 请求IO */gpio_direction_input(keydev.key_gpio);    /* 设置为输入 */return 0;
}/** @description      : 打开设备* @param - inode     : 传递给驱动的inode* @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量*                       一般在open的时候将private_data指向设备结构体。* @return             : 0 成功;其他 失败*/
static int key_open(struct inode *inode, struct file *filp)
{int ret = 0;filp->private_data = &keydev;     /* 设置私有数据 */ret = keyio_init();                /* 初始化按键IO */if (ret < 0) {return ret;}return 0;
}/** @description      : 从设备读取数据 * @param - filp  : 要打开的设备文件(文件描述符)* @param - buf    : 返回给用户空间的数据缓冲区* @param - cnt  : 要读取的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret = 0;int value;struct key_dev *dev = filp->private_data;if (gpio_get_value(dev->key_gpio) == 0) {         /* key0按下 */while(!gpio_get_value(dev->key_gpio));       /* 等待按键释放 */atomic_set(&dev->keyvalue, KEY0VALUE);   } else {    atomic_set(&dev->keyvalue, INVAKEY);     /* 无效的按键值 */}value = atomic_read(&dev->keyvalue);ret = copy_to_user(buf, &value, sizeof(value));return ret;
}/** @description      : 向设备写数据 * @param - filp   : 设备文件,表示打开的文件描述符* @param - buf     : 要写给设备写入的数据* @param - cnt     : 要写入的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 写入的字节数,如果为负值,表示写入失败*/
static ssize_t key_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}/** @description      : 关闭/释放设备* @param - filp   : 要关闭的设备文件(文件描述符)* @return             : 0 成功;其他 失败*/
static int key_release(struct inode *inode, struct file *filp)
{return 0;
}/* 设备操作函数 */
static struct file_operations key_fops = {.owner = THIS_MODULE,.open = key_open,.read = key_read,.write = key_write,.release =    key_release,
};/** @description : 驱动入口函数* @param       : 无* @return       : 无*/
static int __init mykey_init(void)
{/* 初始化原子变量 */atomic_set(&keydev.keyvalue, INVAKEY);/* 注册字符设备驱动 *//* 1、创建设备号 */if (keydev.major) {      /*  定义了设备号 */keydev.devid = MKDEV(keydev.major, 0);register_chrdev_region(keydev.devid, KEY_CNT, KEY_NAME);} else {                        /* 没有定义设备号 */alloc_chrdev_region(&keydev.devid, 0, KEY_CNT, KEY_NAME);  /* 申请设备号 */keydev.major = MAJOR(keydev.devid); /* 获取分配号的主设备号 */keydev.minor = MINOR(keydev.devid);    /* 获取分配号的次设备号 */}/* 2、初始化cdev */keydev.cdev.owner = THIS_MODULE;cdev_init(&keydev.cdev, &key_fops);/* 3、添加一个cdev */cdev_add(&keydev.cdev, keydev.devid, KEY_CNT);/* 4、创建类 */keydev.class = class_create(THIS_MODULE, KEY_NAME);if (IS_ERR(keydev.class)) {return PTR_ERR(keydev.class);}/* 5、创建设备 */keydev.device = device_create(keydev.class, NULL, keydev.devid, NULL, KEY_NAME);if (IS_ERR(keydev.device)) {return PTR_ERR(keydev.device);}return 0;
}/** @description  : 驱动出口函数* @param       : 无* @return       : 无*/
static void __exit mykey_exit(void)
{/* 注销字符设备驱动 */cdev_del(&keydev.cdev);/*  删除cdev */unregister_chrdev_region(keydev.devid, KEY_CNT); /* 注销设备号 */device_destroy(keydev.class, keydev.devid);class_destroy(keydev.class);
}module_init(mykey_init);
module_exit(mykey_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("qwz");

【linux驱动】gpio外设驱动相关推荐

  1. Linux主机驱动与外设驱动分离思想

    - by 宋宝华(Barry Song) 1主机.外设驱动分离的意义 在Linux设备驱动框架的设计中,除了有分层设计实现以外,还有分隔的思想.举一个简单的例子,假设我们要通过SPI总线访问某外设,在 ...

  2. [转]Linux主机驱动与外设驱动分离思想

    - by 宋宝华(Barry Song) 1主机.外设驱动分离的意义 在 Linux设备驱动框架的设计中,除了有分层设计实现以外,还有分隔的思想.举一个简单的例子,假设我们要通过SPI总线访问某外设, ...

  3. Linux主机驱动与外设驱动分离思想[转宋老师]

    1主机.外设驱动分离的意义 在Linux设备驱动框架的设计中,除了有分层设计实现以外,还有分隔的思想.举一个简单的例子,假设我们要 通过SPI总线访问某外设,在这个访问过程中,要通过操作CPU XXX ...

  4. Linux驱动的软件架构(三):主机驱动与外设驱动分离的设计思想

    <Linux设备驱动开发详解>的学习笔记 1.主机驱动与外设驱动分离 Linux中的SPI.I2C.USB等子系统都利用了典型的把主机驱动和外设驱动分离的想法,让主机端只负责产生总线上的传 ...

  5. linux编写gpio驱动程序,linux driver ------ GPIO的驱动编写和调用

    判断哪些文件被编译进内核: 1.通过 make menuconfig 查看 2.比如查看gpio类型的文件,输入 ls drivers/gpio/*.o,有生成.o文件表示被编译进内核 在编写驱动程序 ...

  6. i2c--ioctl--主机控制器驱动(i2c_adapter)--外设驱动(i2c_driver)

    updating... i2c驱动目录 [root@localhost i2c]# pwd /opt/FriendlyArm/mini2440/linux-2.6.32.2/drivers/i2c [ ...

  7. Linux 通用gpio口驱动,rockchip

    dts文件 gpio_rs485: gpio_rs485 {status = "okay";compatible = "gpio,px30-gpio";cnam ...

  8. Linux SPI总线设备驱动模型详解

    随着技术不断进步,系统的拓扑结构越来越复杂,对热插拔.跨平台移植性的要求越来越高,早期的内核难以满足这些要求,从linux2.6内核开始,引入了总线设备驱动模型.其实在linux2.4总线的概念就已经 ...

  9. Linux 设备驱动开发思想 —— 驱动分层与驱动分离

    前面我们学习I2C.USB.SD驱动时,有没有发现一个共性,就是在驱动开发时,每个驱动都分层三部分,由上到下分别是: 1.XXX 设备驱动 2.XXX 核心层 3.XXX 主机控制器驱动 而需要我们编 ...

最新文章

  1. 教程:1、Linux简介
  2. unity角色移动代码_教你3个步骤实现Unity小地图
  3. mysql更新锁机制_mysql查询更新时的锁表机制分析
  4. 一梦江湖卡在获取服务器信息,一梦江湖小技巧,不氪金,卡级玩家怎么获取资源提升修为...
  5. Five ways to maximize Java NIO and NIO.2--转
  6. vc6下c++编译器通不过,c编译器通过
  7. Python基础入门:endswith() 函数
  8. 防止UI界面被输入法遮挡(画面随输入法自适应)
  9. 减速箱箱体尺寸是怎样计算的_减速箱的结构和工作原理
  10. Spring Boot快速搭建Web开发框架(Hibernate+Thymeleaf)
  11. hadoop集群的搭建与配置(2)
  12. mysql建三行三列表格_基于另一个表中列的名称在mysql中创建表
  13. 群晖 上传 源文件不存在_群晖NAS配多大容量硬盘合适?
  14. VoLTE Precondition资源预留
  15. 迅雷5vip帐号获取器
  16. How to hide the New! icon or control how long the icon is displayed.
  17. 微信公众平台开发之微信红包的实现
  18. 图像识别的核心思想是什么!
  19. CF1385 D. a-Good String (分治+递归)
  20. 【无标题】MobaXterm远程连接服务器跑深度学习

热门文章

  1. OleDbCommand更新数据的一些问题
  2. hnustOJ - 1693: 原石法阵
  3. 训练RBM-Hinton系列论文《A practical guide to training Restricted Boltzmann Machines》
  4. 最近设计的一套欧式感觉超强的酒柜
  5. .data文件导入到excel表中(以马疝病为例)
  6. 阿里云服务器续费流程(2023年新版教程)
  7. Java: ParameterizedType用法与简介
  8. php字符串类型详解,php数字类型之字符串类型详解_PHP教程
  9. 历史上影响最大的10首诗
  10. 慕课头条:Facebook需抢座上班;比亚迪招聘拿offer超简单;华强北已破解Airpods防盗机制...