10 新字符设备驱动文件

一、新字符设备驱动原理

  因为  register_chrdev 和 unregister_chrdev 两个函数是老版本驱动文件,现在可以用新字符设备驱动 API 函数。

1. 分配和和释放设备号

  使用 register_chrdev 函数注册字符设备的时候只需要给定一个主设备号即可,但是这样会带来两个问题: 

1、需要我们事先去确定哪些主设备号没有使用;

2、会将主设备号下的所有次设备号都用掉。比如设置 LED 主设备号为 200,那么 0~1048575 这个区间的次设备号全部都被 LED 一个设备分走了。一个 LED 设备肯定只能有一个主设备号,一个次设备号。 

  解决这两个问题最好的方法就是在使用设备号的时候向 Linux 内核申请,需要几个就申请几个,如果没有指定设备号的话就使用如下函数来申请设备号: 

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

  如果给定了设备的主设备号和次设备号就使用如下所示函数来注册设备号即可: 

/** @param - from : 要申请的起始设备号(自己给定的设备号)* @param - count : 申请的数量* @param - name : 设备名字*/
int register_chrdev_region(dev_t from, unsigned count, const char *name);

  无论是通过  alloc_chrdev_region  函数还是  register_chrdev_region 函数,统一使用以下释放函数:

void unregister_chrdev_region(dev_t from, unsigned count);

  使用新字符设备驱动,设备号分配实例:

int major; /* 主设备号 */
int minor; /* 次设备号 */
dev_t devid; /* 设备号 *//* 设备号分配 */
if (major) 
{/* 定义了主设备号 */devid = MKDEV(major, 0);    // 如果 major 有效的话就使用 MKDEV 来构建设备号,次设备号选择 0,并且大部分驱动次设备号都选择 0register_chrdev_region(devid, 1, "test");
} 
else 
{/* 没有定义设备号 */alloc_chrdev_region(&devid, 0, 1, "test"); /* 申请设备号 */major = MAJOR(devid); /* 获取分配号的主设备号 */minor = MINOR(devid); /* 获取分配号的次设备号 */
}/* 设备号释放 */
unregister_chrdev_region(devid, 1);     /* 注销设备号 */

2. 新的字符设备注册方法

① 字符设备结构

  编写字符设备驱动之前需要定义一个 cdev 结构体变量,这个变量就表示一个字符设备:

struct cdev test_cdev;

② 初始化字符设备

  定义好 cdev 变量以后就要使用 cdev_init 函数对其进行初始化:

void cdev_init(struct cdev *cdev, const struct file_operations *fops);

  初始化示例如下:

struct cdev testcdev;/* 设备操作函数 */
static struct file_operations test_fops = {.owner = THIS_MODULE,/* 其他具体的初始项 */
};testcdev.owner = THIS_MODULE;
cdev_init(&testcdev, &test_fops); /* 初始化 cdev 结构体变量 */

③ 添加字符设备

  cdev_add 函数用于向 Linux 系统添加字符设备(cdev 结构体变量),首先使用 cdev_init 函数完成对 cdev 结构体变量的初始化,然后使用 cdev_add 函数向 Linux 系统添加这个字符设备。 

/** @param - p : 指向要添加的字符设备(cdev结构体变量)* @param - dev : 设备所使用的设备号* @param - count : 要添加的设备数量*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count);

  添加 cdev_add 示例如下:

struct cdev testcdev;/* 设备操作函数 */
static struct file_operations test_fops = {.owner = THIS_MODULE,/* 其他具体的初始项 */
};testcdev.owner = THIS_MODULE;
cdev_init(&testcdev, &test_fops);  /* 初始化 cdev 结构体变量 */
cdev_add(&testcdev, devid, 1);     /* 添加字符设备 */

  Linux 内核中大量的字符设备驱动都是采用这种方法向 Linux 内核添加字符设备。 

④ 卸载字符设备

  cdev_del 函数是从 Linux 内核中删除相应的字符设备:

void cdev_del(struct cdev *p);

  删除字符设备示例如下:

cdev_del(&testcdev); /* 删除 cdev */

二、自动创建设备节点

  使用 modprobe 加载驱动模块成功的话就会自动在/dev 目录下创建对应的设备文件。 

1. mdev 

  使用 mdev 来实现设备节点文件的自动创建与删除。buildroot 已经帮我们处理好了 mdev 。

2. 创建和删除类

  自动创建设备节点的工作是在驱动程序的入口函数中完成的,一般在 cdev_add 函数后面添加自动创建设备节点相关代码。 

/** @description : 创建的类* @param - owner : 一般为 THIS_MODULE* @param - name : 设备名字* @return : 指向结构体 class 的指针,也就是创建的类*/
struct class *class_create (struct module *owner, const char *name);/* 类删除函数 */
void class_destroy(struct class *cls);    // cls 就是要删除的类

3. 创建设备

  自动创建设备节点还需要在这个类下创建一个设备。使用 device_create 函数在类下面创建设备:

/** @param - cls : 设备要创建哪个类下面* @param - parent : 父设备,一般为 NULL* @param - devt : 设备号* @param - drvdata : 设备可能会使用的一些数据,一般为 NULL* @param - fmt : 设备名字,如果设置 fmt=xxx 的话,就会生成/dev/xxx 这个设备文件* @return : 创建好的设备*/
struct device *device_create(struct class *cls,struct device *parent,dev_t devt,void *drvdata,const char *fmt, ...)/* 删除创建的设备 */
void device_destroy(struct class *cls, dev_t devt);  
// classs 是要删除的设备所处的类
// devt 是要删除的设备号

4. 示例

struct class *class; /* 类 */
struct device *device; /* 设备 */
dev_t devid; /* 设备号 *//* 驱动入口函数 */
static int __init xxx_init(void)
{/* 创建类 */class = class_create(THIS_MODULE, "xxx");/* 创建设备 */device = device_create(class, NULL, devid, NULL, "xxx");return 0;
}/* 驱动出口函数 */
static void __exit led_exit(void)
{/* 删除设备 */device_destroy(newchrled.class, newchrled.devid);/* 删除类 */class_destroy(newchrled.class);
}module_init(led_init);
module_exit(led_exit);

三、设置文件私有数据

  每个硬件设备都有一些属性,比如主设备号(dev_t),类(class)、设备(device)、开关状态(state)等等,在编写驱动的时候你可以将这些属性全部写成变量的形式,但对于一个设备的所有属性信息我们最好将其做成一个结构体。编写驱动 open 函数的时候将设备结构体作为私有数据添加到设备文件中:

struct test_dev {dev_t devid;             /* 设备号 */struct cdev cdev;        /* cdev */struct class *class;     /* 类 */struct device *device;   /* 设备 */int major;               /* 主设备号 */int minor;               /* 次设备号 */
};struct test_dev testdev;/* open 函数 */
static int test_open(struct inode *inode, struct file *filp)
{filp->private_data = &testdev; /* 设置私有数据 */return 0;
}

  在 open 函数里面设置好私有数据以后,在 write、read、close 等函数中直接读取 private_data 即可得到设备结构体。 

四、驱动程序编写

1. LED 驱动程序编写

  在 3/linux/atk-mpl/Drivers 下创建 3_newchrled 子目录,并且 VScode 工作区名为 newchrled,并且新建 newchrled.c 文件。

#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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define NEWCHRLED_CNT			1		  	/* 设备号个数 */
#define NEWCHRLED_NAME			"newchrled"	/* 名字 */
#define LEDOFF 					0			/* 关灯 */
#define LEDON 					1			/* 开灯 *//* 寄存器物理地址 */
#define PERIPH_BASE     		     	(0x40000000)
#define MPU_AHB4_PERIPH_BASE			(PERIPH_BASE + 0x10000000)
#define RCC_BASE        		    	(MPU_AHB4_PERIPH_BASE + 0x0000)	
#define RCC_MP_AHB4ENSETR				(RCC_BASE + 0XA28)
#define GPIOI_BASE						(MPU_AHB4_PERIPH_BASE + 0xA000)	
#define GPIOI_MODER      			    (GPIOI_BASE + 0x0000)	
#define GPIOI_OTYPER      			    (GPIOI_BASE + 0x0004)	
#define GPIOI_OSPEEDR      			    (GPIOI_BASE + 0x0008)	
#define GPIOI_PUPDR      			    (GPIOI_BASE + 0x000C)	
#define GPIOI_BSRR      			    (GPIOI_BASE + 0x0018)/* 映射后的寄存器虚拟地址指针 */
static void __iomem *MPU_AHB4_PERIPH_RCC_PI;
static void __iomem *GPIOI_MODER_PI;
static void __iomem *GPIOI_OTYPER_PI;
static void __iomem *GPIOI_OSPEEDR_PI;
static void __iomem *GPIOI_PUPDR_PI;
static void __iomem *GPIOI_BSRR_PI;/* newchrled设备结构体 */
struct newchrled_dev
{dev_t devid;			    /* 设备号 */struct cdev cdev;		    /* cdev */struct class *class;		/* 类 */struct device *device;	    /* 设备 */int major;				    /* 主设备号 */int minor;				    /* 次设备号 */
};struct newchrled_dev newchrled;	/* led设备 *//** @description		: LED打开/关闭* @param - sta 	: LEDON(0) 打开LED,LEDOFF(1) 关闭LED* @return 			: 无*/
void led_switch(u8 sta)
{u32 val = 0;if(sta == LEDON) {val = readl(GPIOI_BSRR_PI);val |= (1 << 16);	writel(val, GPIOI_BSRR_PI);}else if(sta == LEDOFF) {val = readl(GPIOI_BSRR_PI);val|= (1 << 0);	writel(val, GPIOI_BSRR_PI);}	
}/** @description		: 取消映射* @return 			: 无*/
void led_unmap(void)
{/* 取消映射 */iounmap(MPU_AHB4_PERIPH_RCC_PI);iounmap(GPIOI_MODER_PI);iounmap(GPIOI_OTYPER_PI);iounmap(GPIOI_OSPEEDR_PI);iounmap(GPIOI_PUPDR_PI);iounmap(GPIOI_BSRR_PI);
}/** @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 = &newchrled; /* 设置私有数据 */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;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0];		/* 获取状态值 */if(ledstat == LEDON) {	led_switch(LEDON);		/* 打开LED灯 */} else if(ledstat == LEDOFF) {led_switch(LEDOFF);	/* 关闭LED灯 */}return 0;
}/** @description		: 关闭/释放设备* @param - filp 	: 要关闭的设备文件(文件描述符)* @return 			: 0 成功;其他 失败*/
static int led_release(struct inode *inode, struct file *filp)
{return 0;
}/* 设备操作函数 */
static struct file_operations newchrled_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)
{u32 val = 0;int ret;/* 初始化LED *//* 1、寄存器地址映射 */MPU_AHB4_PERIPH_RCC_PI = ioremap(RCC_MP_AHB4ENSETR, 4);GPIOI_MODER_PI = ioremap(GPIOI_MODER, 4);GPIOI_OTYPER_PI = ioremap(GPIOI_OTYPER, 4);GPIOI_OSPEEDR_PI = ioremap(GPIOI_OSPEEDR, 4);GPIOI_PUPDR_PI = ioremap(GPIOI_PUPDR, 4);GPIOI_BSRR_PI = ioremap(GPIOI_BSRR, 4);/* 2、使能PI时钟 */val = readl(MPU_AHB4_PERIPH_RCC_PI);val &= ~(0X1 << 8); /* 清除以前的设置 */val |= (0X1 << 8);  /* 设置新值 */writel(val, MPU_AHB4_PERIPH_RCC_PI);/* 3、设置PI0通用的输出模式。*/val = readl(GPIOI_MODER_PI);val &= ~(0X3 << 0); /* bit0:1清零 */val |= (0X1 << 0);  /* bit0:1设置01 */writel(val, GPIOI_MODER_PI);/* 3、设置PI0为推挽模式。*/val = readl(GPIOI_OTYPER_PI);val &= ~(0X1 << 0); writel(val, GPIOI_OTYPER_PI);/* 4、设置PI0为高速。*/val = readl(GPIOI_OSPEEDR_PI);val &= ~(0X3 << 0); /* bit0:1 清零 */val |= (0x2 << 0); /* bit0:1 设置为10*/writel(val, GPIOI_OSPEEDR_PI);/* 5、设置PI0为上拉。*/val = readl(GPIOI_PUPDR_PI);val &= ~(0X3 << 0); /* bit0:1 清零*/val |= (0x1 << 0); /*bit0:1 设置为01*/writel(val,GPIOI_PUPDR_PI);/* 6、默认关闭LED */val = readl(GPIOI_BSRR_PI);val |= (0x1 << 0);writel(val, GPIOI_BSRR_PI);/* 注册字符设备驱动 *//* 1、创建设备号 */if (newchrled.major) {		/* 定义了设备号 */newchrled.devid = MKDEV(newchrled.major, 0);ret = register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);if(ret < 0) {pr_err("cannot register %s char driver [ret=%d]\n",NEWCHRLED_NAME, NEWCHRLED_CNT);goto fail_map;}} else {						/* 没有定义设备号 */ret = alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME);	/* 申请设备号 */if(ret < 0) {pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", NEWCHRLED_NAME, ret);goto fail_map;}newchrled.major = MAJOR(newchrled.devid);	/* 获取分配号的主设备号 */  // 这两行可有可无newchrled.minor = MINOR(newchrled.devid);	/* 获取分配号的次设备号 */}printk("newcheled major=%d,minor=%d\r\n",newchrled.major, newchrled.minor);	/* 2、初始化cdev */newchrled.cdev.owner = THIS_MODULE;cdev_init(&newchrled.cdev, &newchrled_fops);/* 3、添加一个cdev */ret = cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);if(ret < 0){goto del_unregister;}/* 4、创建类 */newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);if (IS_ERR(newchrled.class)) {goto del_cdev;}/* 5、创建设备 */newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);if (IS_ERR(newchrled.device)) {goto destroy_class;}return 0;destroy_class:class_destroy(newchrled.class);
del_cdev:cdev_del(&newchrled.cdev);
del_unregister:unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT);
fail_map:led_unmap();return -EIO;}/** @description	: 驱动出口函数* @param 		: 无* @return 		: 无*/
static void __exit led_exit(void)
{/* 取消映射 */led_unmap();/* 注销字符设备驱动 */cdev_del(&newchrled.cdev);/*  删除cdev */unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT); /* 注销设备号 */device_destroy(newchrled.class, newchrled.devid);class_destroy(newchrled.class);
}module_init(led_init);
module_exit(led_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("LXS");
MODULE_INFO(intree, "Y");

  这里做了一个流程图方便理解:

2.  编写测试 APP 

  这个用上次的那个 ledApp.c 来测试。

五、运行测试

1.  编译驱动程序

  编写 Makefile:

KERNELDIR := /home/alientek/linux/atk-mpl/linux/my_linux/linux-5.4.31	# Linux内核源码路径
CURRENT_PATH := $(shell pwd)		# 获取当前所处路径
obj-m := newchrled.o		build: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

  输入命令:

make
# 成功后会出现一个 newchrled.ko 驱动模块文件

2.  编译测试 APP

  利用交叉编译器编译 APP 测试程序:

arm-none-linux-gnueabihf-gcc ledApp.c -o ledApp

3.  运行测试

  需要将 ledApp newchrled.ko 复制到 /home/alientek/linux/nfs/rootfs/lib/modules/5.4.31/ 目录下。

depmod              # 第一次加载驱动的时候需要运行此命令
modprobe newchrled  # 加载驱动

  申请主设备号为 241,次设备号为 0。并且驱动会自动在 /dev 目录下创建设备节点文件 /dev/newchrdev:

  再测试一下 LED 红灯是否可以工作:

./ledApp /dev/newchrled 1  # 打开 LED 灯./ledApp /dev/newchrled 0  # 关闭 LED 灯# 最后卸载驱动
rmmod newchrled

总结:跟上一章相比,多了设备自动分配、新的字符设备注册方法、自动创建设备节点和设置文件私有数据,其他和上一章相差不大。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/218857.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pytest之allure测试报告03:allure动态自定义报告

1、测试用例模块中引入allure&#xff1a;import allure 2、yaml文件中定义添加title、story的值&#xff1a; 3、测试用例中读取调用。eg:allure.dynamic.title() 4、运行报告查看&#xff1a;成功动态展示yaml文件中配置的story、title

【Spark精讲】Spark内存管理

目录 前言 Java内存管理 Java运行时数据区 Java堆 新生代与老年代 永久代 元空间 垃圾回收机制 JVM GC的类型和策略 Minor GC Major GC 分代GC Full GC Minor GC 和 Full GC区别 Executor内存管理 内存类型 堆内内存 堆外内存 内存管理模式 静态内存管理 …

时序预测 | Python实现LSTM电力需求预测

时序预测 | Python实现LSTM电力需求预测 目录 时序预测 | Python实现LSTM电力需求预测预测效果基本描述程序设计参考资料预测效果 基本描述 该数据集因其每小时的用电量数据以及 TSO 对消耗和定价的相应预测而值得注意,从而可以将预期预测与当前最先进的行业预测进行比较。使用…

git代码管理学习文档

1.版本控制 每一版本都会发生变化 更新版本&#xff0c;回退版本 版本控制实际就是控制文件的变化 服务器端和每个人的电脑上都会记录版本的变化&#xff0c;也就是说整个团队都记录了版本的变化。 不需要连网&#xff0c;他是分布式的&#xff0c;在自己电脑上也可以操作。 …

Docker构建镜像时空间不足:/var/lib/docker,no space left on device

背景 在一次更新业务服务功能后&#xff0c;重新在服务器上构建微服务镜像&#xff0c;在构建镜像时报错空间不足&#xff1a; /var/lib/docker, no space left on device 赶紧用 df -h 看了下磁盘使用情况&#xff0c;果然&#xff0c; devicemapper 已经满了。。由于需要紧急…

Python+Requests+Pytest+YAML+Allure实现接口自动化

本项目实现接口自动化的技术选型&#xff1a;PythonRequestsPytestYAMLAllure &#xff0c;主要是针对之前开发的一个接口项目来进行学习&#xff0c;通过 PythonRequests 来发送和处理HTTP协议的请求接口&#xff0c;使用 Pytest 作为测试执行器&#xff0c;使用 YAML 来管理测…

【halcon深度学习】目标检测的数据准备过程中的一个库函数determine_dl_model_detection_param

determine_dl_model_detection_param “determine_dl_model_detection_param” 直译为 “确定深度学习模型检测参数”。 这个过程会自动针对给定数据集估算模型的某些高级参数&#xff0c;强烈建议使用这一过程来优化训练和推断性能。 过程签名 determine_dl_model_detection…

智能优化算法应用:基于秃鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于秃鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于秃鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.秃鹰算法4.实验参数设定5.算法结果6.参考文献7.MA…

西南科技大学数据库实验二(表数据插入、修改和删除)

一、实验目的 &#xff08;1&#xff09;学会用SQL语句对数据库进行插入、修改和删除数据操作 &#xff08;2&#xff09;掌握insert、update、delete命令实现对表数据插入、修改和删除等更新操作。 二、实验任务 创建数据库&#xff0c;并创建Employees表、Departments表和…

力扣108. 将有序数组转换为二叉搜索树(三种思路)

给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 > 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5…

科大讯飞(深圳)测开面试真题

一面&#xff08;测试组长面&#xff09; 1、上家公司项目以及团队的规模是怎么样的&#xff1f; 2、你负责的项目整体的流程是怎么样的&#xff1f; 3、自动化实施过程中&#xff0c;是如何和业务测试进行沟通的&#xff1f; 4、在上家公司你已经是专职做自动化了&#xf…

linux 调试工具 GDB 使用

gdb是linux下常用的代码调试工具&#xff0c;本文记录常用命令。 被调试的应用需要使用 -g 参数进行编译&#xff0c;如不确定可使用如下命令查看是否支持debug readelf -S filename | grep "debug" 启动调试 gdb binFile 例如要调试sshd&#xff1a; 调试带参数…

k8s中pod监控数据在grafana中展示

实现目标:将kubesphere[K8S]中运行的pod监控数据在grafana平台进行展示。 前提说明:需要在k8s每个集群中内置的prometheus配置中将pod指标数据远程写入到victoriametrics持久化数据库中。 实现效果如下: CPU使用量: round(sum by (namespace, pod) (irate(container_cpu…

在React中实现好看的动画Framer Motion(案例:跨DOM元素平滑过渡)

前言 介绍 Framer Motion 是一个适用于 React 网页开发的动画库&#xff0c;它可以让开发者轻松地在他们的项目中添加复杂和高性能的动画效果。该库提供了一整套针对 React 组件的动画、过渡和手势处理功能&#xff0c;使得通过声明式的 API 来创建动画变得简单直观。 接下来…

2 使用postman进行接口测试

上一篇&#xff1a;1 接口测试介绍-CSDN博客 拿到开发提供的接口文档后&#xff0c;结合需求文档开始做接口测试用例设计&#xff0c;下面用最常见也最简单的注册功能介绍整个流程。 说明&#xff1a;以演示接口测试流程为主&#xff0c;不对演示功能做详细的测试&#xff0c;…

AR眼镜_AR智能眼镜整机硬件方案定制

AR眼镜的主要模块包括显示、光学模组、传感器和摄像头、主板、音频和网络连接等。其中&#xff0c;光学显示、主板处理器是决定AR眼镜成本的关键&#xff0c;光机占整体AR眼镜成本43%、处理器占整体成本31%。 AR眼镜的主板设计难点在于尺寸要足够小且要处理好散热问题。主板上的…

CSS 基础

文章目录 CSS 常见的属性CSS 常见样式行内样式内嵌样式导入样式 CSS 选择器标签选择器id选择器类选择器全局选择器属性选择器组合选择器 CSS 常见应用表格列表导航栏下拉菜单提示工具图片廊 CSS (Cascading Style Sheets&#xff0c;层叠样式表&#xff09;&#xff0c;是一种用…

3.qml 3D-Node类学习

Node类是在View3D 中的对象基础组件&#xff0c;用于表示3D空间中的对象&#xff0c;类似于Qt Quick 2D场景中的Item&#xff0c;介绍如下所示&#xff1a; 如上图可以看到&#xff0c;Node类的子类非常多&#xff0c;比如Model类(显示3D模型)、ParticleSystem3D粒子系统类、Li…

appium2.0.1安装完整教程+uiautomator2安装教程

第一步&#xff1a;根据官网命令安装appium&#xff08;Install Appium - Appium Documentation&#xff09; 注意npm前提是设置淘宝镜像&#xff1a; npm config set registry https://registry.npmmirror.com/ 会魔法的除外。。。 npm i --locationglobal appium或者 npm…

如何预防最新的.locked、.locked1勒索病毒感染您的计算机?

尊敬的读者&#xff1a; 近期&#xff0c;网络安全领域迎来一股新潮——.locked、.locked1勒索病毒的威胁&#xff0c;其先进的加密技术令人生畏。本文将深入剖析.locked、.locked1勒索病毒的阴谋&#xff0c;提供特色数据恢复策略&#xff0c;并揭示锁定恶劣行径的先锋预防手…