1)实验平台:正点原子STM32MP157开发板
2)购买链接:https://item.taobao.com/item.htm?&id=629270721801
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-318813-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子STM32MP157技术交流群:691905614

第二十四章 设备树下的LED驱动实验

上一章我们详细的讲解了设备树语法以及在驱动开发中常用的OF函数,本章我们就开始第一个基于设备树的Linux驱动实验。本章在第二十二章实验的基础上完成,只是将其驱动开发改为设备树形式而已。

24.1 设备树LED驱动原理
在《第二十二章 新字符设备驱动实验》中,我们直接在驱动文件newchrled.c中定义有关寄存器物理地址,然后使用io_remap函数进行内存映射,得到对应的虚拟地址,最后操作寄存器对应的虚拟地址完成对GPIO的初始化。本章我们在第四十二章实验基础上完成,本章我们使用设备树来向Linux内核传递相关的寄存器物理地址,Linux驱动文件使用上一章讲解的OF函数从设备树中获取所需的属性值,然后使用获取到的属性值来初始化相关的IO。本章实验还是比较简单的,本章实验重点内容如下:
①、在stm32mp157d-atk.dts文件中创建相应的设备节点。
②、编写驱动程序(在第二十二章实验基础上完成),获取设备树中的相关属性值。
③、使用获取到的有关属性值来初始化LED所使用的GPIO。
24.2 硬件原理图分析
本实验的硬件原理参考21.2小节即可。
24.3 实验程序编写
24.3.1 修改设备树文件
在根节点“/”下创建一个名为“stm32mp1_led”的子节点,打开stm32mp157d-atk.dts文件,在根节点“/”最后面输入如下所示内容:

示例代码44.3.1.1 stm32mp1_led节点
1    stm32mp1_led {2        compatible = "atkstm32mp1-led";
3        status = "okay";
4        reg = <0X50000A28 0X04     /* RCC_MP_AHB4ENSETR        */
5                0X5000A000 0X04        /* GPIOI_MODER              */
6                0X5000A004 0X04        /* GPIOI_OTYPER             */
7                0X5000A008 0X04        /* GPIOI_OSPEEDR            */
8                0X5000A00C 0X04        /* GPIOI_PUPDR              */
9                0X5000A018 0X04 >;  /* GPIOI_BSRR               */
10   };
第2行,属性compatible设置stm32mp1_led节点兼容为“atkstm32mp1-led”。
第3行,属性status设置状态为“okay”。
第4~9行,reg属性,非常重要!reg属性设置了驱动里面所要使用的寄存器物理地址,比如第4行的“0X50000A28 0X04”表示STM32MP1的RCC_MP_AHB4ENSETR寄存器,其中寄存器地址为0X50000A28,长度为4个字节。

设备树修改完成以后输入如下命令重新编译一下stm32mp157d-atk.dts:
make dtbs
编译完成以后得到stm32mp157d-atk.dtb,使用新的stm32mp157d-atk.dtb启动Linux内核。Linux启动成功以后进入到/proc/device-tree/目录中查看是否有“stm32mp1_led”这个节点,结果如图24.3.1.1所示:

图24.3.1.1 stm32mp1_led节点
如果没有“stm32mp1_led”节点的话请重点检查下面两点:
①、检查设备树修改是否成功,也就是stm32mp1_led节点是否为根节点“/”的子节点。
②、检查是否使用新的设备树启动的Linux内核。
可以进入到图24.3.1.1中的stm32mpl_led目录中,查看一下都有哪些属性文件,结果如图24.3.1.2所示:

图24.3.1.2 stm32mp1_led节点文件
大家可以用cat命令查看一下compatible、status等属性值是否和我们设置的一致。
24.3.2 LED灯驱动程序编写
设备树准备好以后就可以编写驱动程序了,本章实验在第二十二章实验驱动文件newchrled.c的基础上修改而来。新建名为“4_dtsled”文件夹,然后在4_dtsled文件夹里面创建vscode工程,工作区命名为“dtsled”。工程创建好以后新建dtsled.c文件,在dtsled.c里面输入如下内容:

示例代码24.3.2.1 dtsled.c文件内容
1   #include <linux/types.h>
2   #include <linux/kernel.h>
3   #include <linux/delay.h>
4   #include <linux/ide.h>
5   #include <linux/init.h>
6   #include <linux/module.h>
7   #include <linux/errno.h>
8   #include <linux/gpio.h>
9   #include <linux/cdev.h>
10  #include <linux/device.h>
11  #include <linux/of.h>
12  #include <linux/of_address.h>
13  #include <asm/mach/map.h>
14  #include <asm/uaccess.h>
15  #include <asm/io.h>
16
17  /***************************************************************
18  Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
19  文件名         : dtsled.c
20  作者          : 正点原子Linux团队
21  版本          : V1.0
22  描述          : LED驱动文件。
23  其他          : 无
24  论坛          : www.openedv.com
25  日志          : 初版V1.0 2020/12/19 正点原子Linux团队创建
26  ***************************************************************/
27  #define DTSLED_CNT          1               /* 设备号个数    */
28  #define DTSLED_NAME         "dtsled"      /* 名字       */
29  #define LEDOFF              0           /* 关灯           */
30  #define LEDON               1           /* 开灯           */
31
32  /* 映射后的寄存器虚拟地址指针 */
33  static void __iomem *MPU_AHB4_PERIPH_RCC_PI;
34  static void __iomem *GPIOI_MODER_PI;
35  static void __iomem *GPIOI_OTYPER_PI;
36  static void __iomem *GPIOI_OSPEEDR_PI;
37  static void __iomem *GPIOI_PUPDR_PI;
38  static void __iomem *GPIOI_BSRR_PI;
39
40  /* dtsled设备结构体 */
41  struct dtsled_dev{42      dev_t devid;                /* 设备号      */
43      struct cdev cdev;           /* cdev         */
44      struct class *class;    /* 类            */
45      struct device *device;      /* 设备           */
46      int major;                  /* 主设备号     */
47      int minor;                  /* 次设备号     */
48      struct device_node  *nd; /* 设备节点    */
49  };
50
51  struct dtsled_dev dtsled;       /* led设备    */
52
53  /*
54   * @description    : LED打开/关闭
55   * @param - sta    : LEDON(0) 打开LED,LEDOFF(1) 关闭LED
56   * @return         : 无
57   */
58  void led_switch(u8 sta)
59  {60      u32 val = 0;
61      if(sta == LEDON) {62          val = readl(GPIOI_BSRR_PI);
63          val |= (1 << 16);
64          writel(val, GPIOI_BSRR_PI);
65      }else if(sta == LEDOFF) {66          val = readl(GPIOI_BSRR_PI);
67          val|= (1 << 0);
68          writel(val, GPIOI_BSRR_PI);
69      }
70  }
71
72  /*
73   * @description    : 取消映射
74   * @return         : 无
75   */
76  void led_unmap(void)
77  {78      /* 取消映射 */
79      iounmap(MPU_AHB4_PERIPH_RCC_PI);
80      iounmap(GPIOI_MODER_PI);
81      iounmap(GPIOI_OTYPER_PI);
82      iounmap(GPIOI_OSPEEDR_PI);
83      iounmap(GPIOI_PUPDR_PI);
84      iounmap(GPIOI_BSRR_PI);
85  }
86
87  /*
88   * @description    : 打开设备
89   * @param – inode  : 传递给驱动的inode
90   * @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量
91   *                    一般在open的时候将private_data指向设备结构体。
92   * @return         : 0 成功;其他 失败
93   */
94  static int led_open(struct inode *inode, struct file *filp)
95  {96      filp->private_data = &dtsled; /* 设置私有数据 */
97      return 0;
98  }
99
100 /*
101  * @description    : 从设备读取数据
102  * @param - filp   : 要打开的设备文件(文件描述符)
103  * @param - buf    : 返回给用户空间的数据缓冲区
104  * @param - cnt    : 要读取的数据长度
105  * @param - offt   : 相对于文件首地址的偏移
106  * @return         : 读取的字节数,如果为负值,表示读取失败
107  */
108 static ssize_t led_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *offt)
109 {110     return 0;
111 }
112
113 /*
114  * @description    : 向设备写数据
115  * @param - filp   : 设备文件,表示打开的文件描述符
116  * @param - buf    : 要写给设备写入的数据
117  * @param - cnt    : 要写入的数据长度
118  * @param - offt   : 相对于文件首地址的偏移
119  * @return         : 写入的字节数,如果为负值,表示写入失败
120  */
121 static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
122 {123     int retvalue;
124     unsigned char databuf[1];
125     unsigned char ledstat;
126
127     retvalue = copy_from_user(databuf, buf, cnt);
128     if(retvalue < 0) {129         printk("kernel write failed!\r\n");
130         return -EFAULT;
131     }
132
133     ledstat = databuf[0];       /* 获取状态值       */
134
135     if(ledstat == LEDON) {
136         led_switch(LEDON);          /* 打开LED灯       */
137     } else if(ledstat == LEDOFF) {138         led_switch(LEDOFF);         /* 关闭LED灯       */
139     }
140     return 0;
141 }
142
143 /*
144  * @description    : 关闭/释放设备
145  * @param – filp   : 要关闭的设备文件(文件描述符)
146  * @return         : 0 成功;其他 失败
147  */
148 static int led_release(struct inode *inode, struct file *filp)
149 {150     return 0;
151 }
152
153 /* 设备操作函数 */
154 static struct file_operations dtsled_fops = {155     .owner = THIS_MODULE,
156     .open = led_open,
157     .read = led_read,
158     .write = led_write,
159     .release =  led_release,
160 };
161
162 /*
163  * @description    : 驱动出口函数
164  * @param          : 无
165  * @return         : 无
166  */
167 static int __init led_init(void)
168 {169     u32 val = 0;
170     int ret;
171     u32 regdata[12];
172     const char *str;
173     struct property *proper;
174
175     /* 获取设备树中的属性数据          */
176     /* 1、获取设备节点:stm32mp1_led     */
177     dtsled.nd = of_find_node_by_path("/stm32mp1_led");
178     if(dtsled.nd == NULL) {179         printk("stm32mp1_led node nost find!\r\n");
180         return -EINVAL;
181     } else {182         printk("stm32mp1_lcd node find!\r\n");
183     }
184
185     /* 2、获取compatible属性内容 */
186     proper = of_find_property(dtsled.nd, "compatible", NULL);
187     if(proper == NULL) {188         printk("compatible property find failed\r\n");
189     } else {190         printk("compatible = %s\r\n", (char*)proper->value);
191     }
192
193     /* 3、获取status属性内容 */
194     ret = of_property_read_string(dtsled.nd, "status", &str);
195     if(ret < 0){196         printk("status read failed!\r\n");
197     } else {198         printk("status = %s\r\n",str);
199     }
200
201     /* 4、获取reg属性内容 */
202     ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 12);
203     if(ret < 0) {204         printk("reg property read failed!\r\n");
205     } else {206         u8 i = 0;
207         printk("reg data:\r\n");
208         for(i = 0; i < 12; i++)
209             printk("%#X ", regdata[i]);
210         printk("\r\n");
211     }
212
213     /* 初始化LED           */
214     /* 1、寄存器地址映射    */
215     MPU_AHB4_PERIPH_RCC_PI = of_iomap(dtsled.nd, 0);
216     GPIOI_MODER_PI = of_iomap(dtsled.nd, 1);
217     GPIOI_OTYPER_PI = of_iomap(dtsled.nd, 2);
218     GPIOI_OSPEEDR_PI = of_iomap(dtsled.nd, 3);
219     GPIOI_PUPDR_PI = of_iomap(dtsled.nd, 4);
220     GPIOI_BSRR_PI = of_iomap(dtsled.nd, 5);
221
222     /* 2、使能PI时钟 */
223     val = readl(MPU_AHB4_PERIPH_RCC_PI);
224     val &= ~(0X1 << 8);  /* 清除以前的设置  */
225     val |= (0X1 << 8);   /* 设置新值             */
226     writel(val, MPU_AHB4_PERIPH_RCC_PI);
227
228     /* 3、设置PI0通用的输出模式。*/
229     val = readl(GPIOI_MODER_PI);
230     val &= ~(0X3 << 0);      /* bit0:1清零         */
231     val |= (0X1 << 0);       /* bit0:1设置01   */
232     writel(val, GPIOI_MODER_PI);
233
234     /* 3、设置PI0为推挽模式。*/
235     val = readl(GPIOI_OTYPER_PI);
236     val &= ~(0X1 << 0);      /* bit0清零,设置为上拉*/
237     writel(val, GPIOI_OTYPER_PI);
238
239     /* 4、设置PI0为高速。*/
240     val = readl(GPIOI_OSPEEDR_PI);
241     val &= ~(0X3 << 0);      /* bit0:1 清零        */
242     val |= (0x2 << 0);       /* bit0:1 设置为10     */
243     writel(val, GPIOI_OSPEEDR_PI);
244
245     /* 5、设置PI0为上拉。*/
246     val = readl(GPIOI_PUPDR_PI);
247     val &= ~(0X3 << 0);      /* bit0:1 清零            */
248     val |= (0x1 << 0);       /* bit0:1 设置为01     */
249     writel(val,GPIOI_PUPDR_PI);
250
251     /* 6、默认关闭LED */
252     val = readl(GPIOI_BSRR_PI);
253     val |= (0x1 << 0);
254     writel(val, GPIOI_BSRR_PI);
255
256     /* 注册字符设备驱动 */
257     /* 1、创建设备号 */
258     if (dtsled.major) {         /*  定义了设备号      */
259         dtsled.devid = MKDEV(dtsled.major, 0);
260         ret = register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
261         if(ret < 0) {262             pr_err("cannot register %s char driver [ret=%d]\n",DTSLED_NAME, DTSLED_CNT);
263             goto fail_map;
264         }
265     } else {                    /* 没有定义设备号      */
266         ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);   /* 申请设备号 */
267         if(ret < 0) {268             pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n",
DTSLED_NAME, ret);
269             goto fail_map;
270         }
271         dtsled.major = MAJOR(dtsled.devid); /* 获取分配号的主设备号 */
272         dtsled.minor = MINOR(dtsled.devid); /* 获取分配号的次设备号 */
273
274     }
275     printk("dtsled major=%d,minor=%d\r\n",dtsled.major,
dtsled.minor);
276
277     /* 2、初始化cdev */
278     dtsled.cdev.owner = THIS_MODULE;
279     cdev_init(&dtsled.cdev, &dtsled_fops);
280
281     /* 3、添加一个cdev */
282     ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);
283     if(ret < 0)
284         goto del_unregister;
285
286     /* 4、创建类        */
287     dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
288     if (IS_ERR(dtsled.class)) {289         goto del_cdev;
290     }
291
292     /* 5、创建设备       */
293     dtsled.device = device_create(dtsled.class, NULL, dtsled.devid,
NULL, DTSLED_NAME);
294     if (IS_ERR(dtsled.device)) {295         goto destroy_class;
296     }
297
298     return 0;
299
300 destroy_class:
301     class_destroy(dtsled.class);
302 del_cdev:
303     cdev_del(&dtsled.cdev);
304 del_unregister:
305     unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
306 fail_map:
307     led_unmap();
308     return -EIO;
309 }
310
311 /*
312  * @description    : 驱动出口函数
313  * @param          : 无
314  * @return         : 无
315  */
316 static void __exit led_exit(void)
317 {318     /* 取消映射         */
319     led_unmap();
320
321     /* 注销字符设备驱动 */
322     cdev_del(&dtsled.cdev); /*  删除cdev */
323     unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /*注销*/
324
325     device_destroy(dtsled.class, dtsled.devid);
326     class_destroy(dtsled.class);
327 }
328
329 module_init(led_init);
330 module_exit(led_exit);
331 MODULE_LICENSE("GPL");
332 MODULE_AUTHOR("ALIENTEK");
333 MODULE_INFO(intree, "Y");

dtsled.c文件中的内容和第二十二章的newchrled.c文件中的内容基本一样,只是dtsled.c中包含了处理设备树的代码,我们重点来看一下这部分代码。
第48行,在设备结构体dtsled_dev中添加了成员变量nd,nd是device_node结构体类型指针,表示设备节点。如果我们要读取设备树某个节点的属性值,首先要先得到这个节点,一般在设备结构体中添加device_node指针变量来存放这个节点。
第177~183行,通过of_find_node_by_path函数得到stm32mp1_led节点,后续其他的OF函数要使用device_node。
第186~191行,通过of_find_property函数获取stm32mp1_led节点的compatible属性,返回值为property结构体类型指针变量,property的成员变量value表示属性值。
第194~199行,通过of_property_read_string函数获取stm32mp1_led节点的status属性值。
第202~211行,通过of_property_read_u32_array函数获取stm32mp1_led节点的reg属性所有值,并且将获取到的值都存放到regdata数组中。第209行将获取到的reg属性值依次输出到终端上。
第215~220行,使用of_iomap函数一次性完成读取reg属性以及内存映射,of_iomap函数是设备树推荐使用的OF函数。
24.3.3 编写测试APP
本章直接使用第二十二章的测试APP,将上一章的ledApp.c文件复制到本章实验工程下即可。
24.4.1 编译驱动程序和测试APP
1、编译驱动程序
编写Makefile文件,本章实验的Makefile文件和第二十章实验基本一样,只是将obj-m变量的值改为dtsled.o,Makefile内容如下所示:
示例代码24.4.1.1 Makefile文件

1  KERNELDIR := /home/zuozhongkai/linux/my_linux/linux-5.4.31
......
4  obj-m := dtsled.o
......
11 clean:
12  $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4行,设置obj-m变量的值为dtsled.o。
输入如下命令编译出驱动模块文件:
make -j32
编译成功以后就会生成一个名为“dtsled.ko”的驱动模块文件。
2、编译测试APP
输入如下命令编译测试ledApp.c这个测试程序:
arm-none-linux-gnueabihf-gcc ledApp.c -o ledApp
编译成功以后就会生成ledApp这个应用程序。
24.4.2 运行测试
将上一小节编译出来的dtsled.ko和ledApp这两个文件拷贝到rootfs/lib/modules/5.4.31目录中,重启开发板,进入到目录lib/modules/5.4.31中,输入如下命令加载dtsled.ko驱动模块:
depmod //第一次加载驱动的时候需要运行此命令
modprobe dtsled //加载驱动
驱动加载成功以后会在终端中输出一些信息,如图24.4.2.1所示:

图24.4.2.1 驱动加载成功以后输出的信息
从图24.4.2.1可以看出,stm32mp1_led这个节点找到了,并且compatible属性值为“atkstm32mp1-led”,status属性值为“okay”,reg属性的值为“0X50000A28 0X4 0X5000A000 0X4 0X5000A004 0X4 0X5000A008 0X4 0X5000A00C 0X4 0X5000A018 0X4”,这些都和我们设置的设备树一致。
驱动加载成功以后就可以使用ledApp软件来测试驱动是否工作正常,输入如下命令打开LED灯:
./ledApp /dev/dtsled 1 //打开LED灯
输入上述命令以后观察开发板上的红色LED灯是否点亮,如果点亮的话说明驱动工作正常。在输入如下命令关闭LED灯:
./ledApp /dev/dtsled 0 //关闭LED灯
输入上述命令以后观察开发板上的红色LED灯是否熄灭。如果要卸载驱动的话输入如下命令即可:
rmmod dtsled.ko

【正点原子MP157连载】第二十四章 设备树下的LED驱动实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7相关推荐

  1. 【正点原子Linux连载】第四十四章 设备树下的LED驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  2. 韦东山 IMX6ULL和正点原子_「正点原子Linux连载」第四十四章设备树下的LED驱动实验...

    1)实验平台:正点原子Linux开发板 2)摘自<正点原子I.MX6U嵌入式Linux驱动开发指南> 关注官方微信号公众号,获取更多资料:正点原子 上一章我们详细的讲解了设备树语法以及在驱 ...

  3. 【正点原子MP157连载】第三十五章 设备树下的platform驱动编写-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  4. linux 正点原子ov5640_【正点原子FPGA连载】第二十六章基于OV5640的二值化实验-摘自【正点原子】领航者 ZYNQ 之嵌入式开发指南 (amobbs.com 阿莫电子论坛)...

    本帖最后由 正点原子 于 2020-10-26 16:21 编辑 QQ群头像.png (1.78 KB) 2020-10-24 10:50 上传5)关注正点原子公众号,获取最新资料 100846rel ...

  5. 【正点原子MP157连载】 第四章 开发环境搭建-摘自【正点原子】【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  6. 【正点原子MP157连载】第二十五章 pinctrl和gpio子系统实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  7. 【正点原子FPGA连载】第二十四章HDMI彩条显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

    1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码+手册+视频下载地址:ht ...

  8. 【正点原子FPGA连载】 第二十四章 RTC实时时钟LCD显示实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

    1)实验平台:正点原子领航者ZYNQ开发板 2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码+手册+视频下 ...

  9. 第二十四章 并发编程

    第二十四章 并发编程 爱丽丝:"但是我不想进入疯狂的人群中" 猫咪:"oh,你无能为力,我们都疯了,我疯了,你也疯了" 爱丽丝:"你怎么知道我疯了&q ...

最新文章

  1. 使用postman修改SAP Marketing Cloud contact主数据
  2. nginx-启动gzip、虚拟主机、请求转发、负载均衡
  3. 任意半径局部直方图类算法在PC中快速实现的框架。
  4. Python入门100题 | 第029题
  5. 程序员啊程序员...济南昊锐科技
  6. base64还原_冰蝎3.0流量分析与还原
  7. 创建一个简单的ArcGIS Server ASP.NET网页
  8. C++---list(列表)模板
  9. spring cloud中gateway存在的意义是什么?
  10. exists查询慢_8个SQL查询效率优化原则
  11. python是干嘛的-python到底拿来干什么
  12. 架构篇--系统监控--spring-boot2.0.X 系统原生信息监控,SQL信息监控,cpu温度监控报警,cup磁盘内存使用率监控报警,自定义端点监控以及子节点获取,系统异常邮件通知
  13. python如何打开npy文件_操作python如何实现npy格式文件转换为txt文件
  14. jQuery仿百度商桥在线客服代码
  15. Qt QImage scaled方法缩放中的问题
  16. 计算机毕业设计PHP+安卓移动LYQ电子商城APP(源码+程序+lw+远程调试)
  17. android视频快速压缩视频教程,Android上的快速视频压缩
  18. 基于SpringBoot的社区小型图书管理系统的设计与实现
  19. 百度违规屏蔽关键词判定标准查询工具
  20. Vue的基本使用步骤

热门文章

  1. CCS3.3下载UBOOT到NANDFLASH
  2. 微软账户登不上解决方案
  3. 在Java Web项目中建src/main/java包
  4. 亲测有效的UCOSIII嵌入STM32F103C8T6实验,介绍常见移植问题,付实测代码!!!
  5. 21天打卡挑战学习MySQL—Day第一周 第一篇
  6. Spring自定义数据源配置不当引起的Mybatis拦截器Interceptors 失效/不生效
  7. ubuntu16.04+nvidia gt740m+cuda7.5+caffe安装、测试经历
  8. 李沐-动手学深度学习-pytorch版-”d2lzh_pytorch”包的使用
  9. Java--入门指引
  10. git 将一个本地文件目录提交到远程仓库的步骤