ARM DIY(九)陀螺仪调试

news2024/11/16 18:00:34

前言

今天调试六轴陀螺仪 MPU6050

硬件

硬件很简单,使用 I2C 接口,并且没有使用中断引脚。
焊接上 MPU6050 芯片和上拉电阻、滤波电容。
请添加图片描述

检测

MPU6050 是挂在 I2C-0 上的,I2C-0 控制器的驱动已 OK,所以直接使用 I2C-0 检测 MPU6050 是否存在

# i2cdetect -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- 

MPU6050 的地址是 0x68,在 I2C-0 总线上检测到了,说明硬件焊接 OK

读取温度

按照之前写的一篇文章《Banana Pi M1 读取 MPU6050(Shell 脚本方式)》,使用 shell 脚本方式读取温度

# ./mpu6050.sh 
34.7034.8434.8934.9835.0335.7335.0835.1235.1235.2635.1735.3635.3135.2635.3635.31

驱动

一开始使用内核自带的 IIO 驱动,发现只能在 /sys/ 目录下读取相应的坐标值,读取 /dev/iio:device0 有问题,并且需要配置中断引脚才能编译通过。所以就不使用内核自带的驱动了。网上找了个 6050 的驱动
arch/arm/boot/dts/sun8i-v3s-licheepi-zero-dock.dts

&i2c0 {
	pinctrl-0 = <&i2c0_pins>;
	pinctrl-names = "default";
	clock-frequency = <400000>;
	status = "okay";

	mpu6050_2: accelerometer@68_2 {
		compatible = "lyj,mpu6050";
		reg = <0x68>;
		status = "okay";
	};
};

i2c_mpu6050.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/i2c.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include "i2c_mpu6050.h"

/*------------------字符设备内容----------------------*/
#define DEV_NAME "I2C1_mpu6050"
#define DEV_CNT (1)

/*定义 led 资源结构体,保存获取得到的节点信息以及转换后的虚拟寄存器地址*/
static dev_t mpu6050_devno;				 //定义字符设备的设备号
static struct cdev mpu6050_chr_dev;		 //定义字符设备结构体chr_dev
struct class *class_mpu6050;			 //保存创建的类
struct device *device_mpu6050;			 // 保存创建的设备
struct device_node *mpu6050_device_node; //rgb_led的设备树节点结构体

/*------------------IIC设备内容----------------------*/
struct i2c_client *mpu6050_client = NULL; //保存mpu6050设备对应的i2c_client结构体,匹配成功后由.prob函数带回。

/*通过i2c 向mpu6050写入数据
*mpu6050_client:mpu6050的i2c_client结构体。
*address, 数据要写入的地址,
*data, 要写入的数据
*返回值,错误,-1。成功,0  
*/
static int i2c_write_mpu6050(struct i2c_client *mpu6050_client, u8 address, u8 data)
{
	int error = 0;
	u8 write_data[2];
	struct i2c_msg send_msg; //要发送的数据结构体

	/*设置要发送的数据*/
	write_data[0] = address;
	write_data[1] = data;

	/*发送 iic要写入的地址 reg*/
	send_msg.addr = mpu6050_client->addr; //mpu6050在 iic 总线上的地址
	send_msg.flags = 0;					  //标记为发送数据
	send_msg.buf = write_data;			  //写入的首地址
	send_msg.len = 2;					  //reg长度

	/*执行发送*/
	error = i2c_transfer(mpu6050_client->adapter, &send_msg, 1);
	if (error != 1)
	{
		printk(KERN_DEBUG "\n i2c_transfer error \n");
		return -1;
	}

	return 0;
}

/*通过i2c 向mpu6050写入数据
*mpu6050_client:mpu6050的i2c_client结构体。
*address, 要读取的地址,
*data,保存读取得到的数据
*length,读长度
*返回值,错误,-1。成功,0
*/
static int i2c_read_mpu6050(struct i2c_client *mpu6050_client, u8 address, void *data, u32 length)
{
	int error = 0;
	u8 address_data = address;
	struct i2c_msg mpu6050_msg[2];

	/*设置读取位置msg*/
	mpu6050_msg[0].addr = mpu6050_client->addr; //mpu6050在 iic 总线上的地址
	mpu6050_msg[0].flags = 0;					//标记为发送数据
	mpu6050_msg[0].buf = &address_data;			//写入的首地址
	mpu6050_msg[0].len = 1;						//写入长度

	/*设置读取位置msg*/
	mpu6050_msg[1].addr = mpu6050_client->addr; //mpu6050在 iic 总线上的地址
	mpu6050_msg[1].flags = I2C_M_RD;			//标记为读取数据
	mpu6050_msg[1].buf = data;					//读取得到的数据保存位置
	mpu6050_msg[1].len = length;				//读取长度

	error = i2c_transfer(mpu6050_client->adapter, mpu6050_msg, 2);
	if (error != 2) {
		printk(KERN_DEBUG "\n i2c_read_mpu6050 error \n");
		return -1;
	}

	return 0;
}

/*初始化i2c
*返回值,成功,返回0。失败,返回 -1
*/
static int mpu6050_init(void)
{
	int error = 0;

	/*配置mpu6050*/
	error += i2c_write_mpu6050(mpu6050_client, PWR_MGMT_1, 0X00);
	error += i2c_write_mpu6050(mpu6050_client, SMPLRT_DIV, 0X07);
	error += i2c_write_mpu6050(mpu6050_client, CONFIG, 0X06);
	error += i2c_write_mpu6050(mpu6050_client, ACCEL_CONFIG, 0X01);
	if (error < 0) {
		/*初始化错误*/
		printk(KERN_DEBUG "\n mpu6050_init error \n");
		return -1;
	}

	return 0;
}

/*字符设备操作函数集,open函数实现*/
static int mpu6050_open(struct inode *inode, struct file *filp)
{
	// printk("\n mpu6050_open \n");

	/*向 mpu6050 发送配置数据,让mpu6050处于正常工作状态*/
	mpu6050_init();

	return 0;
}

/*字符设备操作函数集,.read函数实现*/
static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
	char data_H;
	char data_L;
	int error;
	short mpu6050_result[6]; //保存mpu6050转换得到的原始数据

	// printk("\n mpu6050_read \n");
	
	i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_L, &data_L, 1);
	mpu6050_result[0] = data_H << 8;
	mpu6050_result[0] += data_L;

	i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_L, &data_L, 1);
	mpu6050_result[1] = data_H << 8;
    mpu6050_result[1] += data_L;

	i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_L, &data_L, 1);
	mpu6050_result[2] = data_H << 8;
	mpu6050_result[2] += data_L;

	i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_L, &data_L, 1);
	mpu6050_result[3] = data_H << 8;
	mpu6050_result[3] += data_L;

	i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_L, &data_L, 1);
	mpu6050_result[4] = data_H << 8;
	mpu6050_result[4] += data_L;

	i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_L, &data_L, 1);
	mpu6050_result[5] = data_H << 8;
	mpu6050_result[5] += data_L;


	// printk("AX=%d, AY=%d, AZ=%d \n",(int)mpu6050_result[0],(int)mpu6050_result[1],(int)mpu6050_result[2]);
	// printk("GX=%d, GY=%d, GZ=%d \n \n",(int)mpu6050_result[3],(int)mpu6050_result[4],(int)mpu6050_result[5]);

	/*将读取得到的数据拷贝到用户空间*/
	error = copy_to_user(buf, mpu6050_result, cnt);
	if(error != 0) {
		printk("copy_to_user error!");
		return -1;
	}

	return 0;
}

/*字符设备操作函数集,.release函数实现*/
static int mpu6050_release(struct inode *inode, struct file *filp)
{
	// printk("\n mpu6050_release \n");
	
	/*向mpu6050发送命令,使mpu6050进入关机状态*/
	return 0;
}

/*字符设备操作函数集*/
static struct file_operations mpu6050_chr_dev_fops = {
	.owner = THIS_MODULE,
	.open = mpu6050_open,
	.read = mpu6050_read,
	.release = mpu6050_release,
};

/*----------------平台驱动函数集-----------------*/
static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	int ret = -1; //保存错误状态码

	printk(KERN_EMERG "\t  match successed  \n");
	/*---------------------注册 字符设备部分-----------------*/

	//采用动态分配的方式,获取设备编号,次设备号为0,
	//设备名称为rgb-leds,可通过命令cat  /proc/devices查看
	//DEV_CNT为1,当前只申请一个设备编号
	ret = alloc_chrdev_region(&mpu6050_devno, 0, DEV_CNT, DEV_NAME);
	if (ret < 0) {
		printk("fail to alloc mpu6050_devno\n");
		goto alloc_err;
	}

	//关联字符设备结构体cdev与文件操作结构体file_operations
	mpu6050_chr_dev.owner = THIS_MODULE;
	cdev_init(&mpu6050_chr_dev, &mpu6050_chr_dev_fops);

	// 添加设备至cdev_map散列表中
	ret = cdev_add(&mpu6050_chr_dev, mpu6050_devno, DEV_CNT);
	if (ret < 0) {
		printk("fail to add cdev\n");
		goto add_err;
	}

	/*创建类 */
	class_mpu6050 = class_create(THIS_MODULE, DEV_NAME);

	/*创建设备 DEV_NAME 指定设备名,*/
	device_mpu6050 = device_create(class_mpu6050, NULL, mpu6050_devno, NULL, DEV_NAME);
	mpu6050_client = client;
	return 0;

add_err:
	// 添加设备失败时,需要注销设备号
	unregister_chrdev_region(mpu6050_devno, DEV_CNT);
	printk("\n error! \n");
alloc_err:

	return -1;
}

static int mpu6050_remove(struct i2c_client *client)
{
	/*删除设备*/
	device_destroy(class_mpu6050, mpu6050_devno);	  //清除设备
	class_destroy(class_mpu6050);					  //清除类
	cdev_del(&mpu6050_chr_dev);						  //清除设备号
	unregister_chrdev_region(mpu6050_devno, DEV_CNT); //取消注册字符设备
	return 0;
}



/*定义ID 匹配表*/
static const struct i2c_device_id gtp_device_id[] = {
	{"lyj,mpu6050", 0},
	{},
};

/*定义设备树匹配表*/
static const struct of_device_id mpu6050_of_match_table[] = {
	{.compatible = "lyj,mpu6050"},
	{/* sentinel */},
};

/*定义i2c总线设备结构体*/
struct i2c_driver mpu6050_driver = {
	.probe = mpu6050_probe,
	.remove = mpu6050_remove,
	.id_table = gtp_device_id,
	.driver = {
		.name = "lyj,mpu6050",
		.owner = THIS_MODULE,
		.of_match_table = mpu6050_of_match_table,
	},
};

/*
*驱动初始化函数
*/
static int __init mpu6050_driver_init(void)
{
	int ret;
	pr_info("mpu6050_driver_init\n");
	ret = i2c_add_driver(&mpu6050_driver);
	return ret;
}

/*
*驱动注销函数
*/
static void __exit mpu6050_driver_exit(void)
{
	pr_info("mpu6050_driver_exit\n");
	i2c_del_driver(&mpu6050_driver);
}

module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);

MODULE_LICENSE("GPL");

test_app.c

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	int error;
	short resive_data[6]; //保存收到的 mpu6050转换结果数据,依次为 AX(x轴角度), AY, AZ 。GX(x轴加速度), GY ,GZ

	/*打开文件*/
	int fd = open(argv[1], O_RDWR);
	if (fd < 0) {
		printf("open file : %s failed !\n", argv[0]);
		return -1;
	}

	/*读取数据*/
	while (1) {
		error = read(fd, resive_data, 12);
		if (error < 0) {
			printf("write file error! \n");
			close(fd);
			/*判断是否关闭成功*/
		}

		/*打印数据*/
		printf("AX = %6d, AY = %6d, AZ = %6d", (int)resive_data[0], (int)resive_data[1], (int)resive_data[2]);
		printf("\t\tGX = %6d, GY = %6d, GZ = %6d\n", (int)resive_data[3], (int)resive_data[4], (int)resive_data[5]);

		sleep(1);
	}

	/*关闭文件*/
	error = close(fd);
	if (error < 0) {
		printf("close file error! \n");
	}

	return 0;
}

Makefile

obj-m = i2c_mpu6050.o
APP = test_app

KDIR=/home/liyongjun/project/board/buildroot/DIY_V3S/build/linux-5.3.5
CROSS_COMPILE=/home/liyongjun/project/board/buildroot/DIY_V3S/host/bin/arm-buildroot-linux-gnueabihf-

all:
	make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) modules
	$(CROSS_COMPILE)gcc -o $(APP) test_app.c

clean:
	make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) clean
	rm $(APP)

测试

安装驱动,运行 APP,转动陀螺仪

# insmod i2c_mpu6050.ko 
[  127.820667] i2c_mpu6050: loading out-of-tree module taints kernel.
[  127.829072] mpu6050_driver_init
[  127.833666] 	  match successed  
# ./test_app /dev/I2C1_mpu6050
AX =    574, AY =    198, AZ =  16784		GX =    -50, GY =   -189, GZ =    -38
AX =    588, AY =    188, AZ =  16768		GX =    -48, GY =   -175, GZ =    -40
AX = -10366, AY =  -4508, AZ =  11164		GX =  -9896, GY =  11097, GZ = -11451
AX = -14110, AY =   2746, AZ =   4270		GX = -27770, GY = -26383, GZ =  17776
AX =  -9850, AY =  10674, AZ =  -3436		GX =  -8408, GY =   5019, GZ =  13314
AX =  -7482, AY = -10730, AZ =  -3054		GX =   4618, GY = -32440, GZ =  17968
AX =   1320, AY =   3748, AZ =  21040		GX = -15680, GY =   8252, GZ =   3408
AX =   9350, AY =  10816, AZ =  -8042		GX =   3906, GY =    640, GZ =  12422
AX =  -8470, AY =  -5844, AZ =   6478		GX =  16069, GY = -32768, GZ =  12481
AX =  -2542, AY = -11888, AZ = -14484		GX =   8449, GY =  22099, GZ = -30254
AX =  -6590, AY =   9106, AZ =  13610		GX =  -4507, GY = -32768, GZ =  28548
AX =  -8738, AY =   6962, AZ =  -3122		GX = -27485, GY = -12743, GZ = -23153
AX =  -8436, AY =  -2534, AZ =  13078		GX =  32767, GY =  10061, GZ = -12789
AX =  -1298, AY = -12532, AZ = -12758		GX = -17934, GY =   3946, GZ =  -5478
AX =   7988, AY =  -3884, AZ =  14934		GX =   6224, GY =  -9852, GZ =  -7318
AX =   7812, AY =  -7544, AZ = -12826		GX = -32768, GY =  -3146, GZ =   3222
AX =   6158, AY =   1790, AZ = -14952		GX =    108, GY =   -324, GZ =   -209

至此,陀螺仪调试 OK

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023 年前端编程 NodeJs 包管理工具 npm 安装和使用详细介绍

npm 基本概述 npm is the world’s largest software registry. Open source developers from every continent use npm to share and borrow packages, and many organizations use npm to manage private development as well. npm 官方网站&#xff1a;https://www.npmjs.…

程序员做哪些副业可以日赚一百?

日赚一百&#xff1f;对程序员来说简直不要太容易&#xff01;下面给程序员们推荐一些日赚100的副业&#xff1a; ①外包接单 程序员简单粗暴赚钱的副业之一。 外包接单的类型包括但不限于&#xff1a;软件开发、硬件开发、小程序功能开发、web开发……大到一个系统的开发、…

外包干了三年,我承认我确实废了……

没错&#xff0c;我也干过外包&#xff0c;一干就是三年&#xff0c;三年后&#xff0c;我废了…… 虽说废的不是很彻底&#xff0c;但那三年我几乎是出差了三年、玩了三年、荒废了三年&#xff0c;那三年&#xff0c;我的技术能力几乎是零成长的。 说起这段三年的外包经历&a…

对象临时中间状态的条件竞争覆盖

Portswigger练兵场之条件竞争 &#x1f984;条件竞争之对象临时中间状态的条件竞争 Lab: Partial construction race conditions&#x1f680;实验前置必要知识点 某些框架尝试通过使用某种形式的请求锁定来防止意外的数据损坏。例如&#xff0c;PHP 的本机会话处理程序模块…

任务管理系统所需功能概述

"任务管理需要有哪些功能&#xff1f;清晰的任务创建与编辑、智能分类和标签系统、提醒与通知功能、进度跟踪与报告、协作与共享功能、集成与兼容性。" 一款优秀的任务管理工具可以帮助我们有效地规划、执行和监控各项任务&#xff0c;提高工作效率。本文将探讨一款理…

深度学习(十一)---zed 调用yolov5 进行识别目标并实时测距

1. 前言 zed 相机测距有2种方式&#xff1a;一种是根据点云数据进行测试&#xff0c;二是根据zed获取深度值进行测距。上篇文章 调用yolov5模型进行实时图像推理及网页端部署 我们讲述了zed调用yolov5进行目标识别&#xff0c;我们在此基础上进一步实现目标测距功能。 2.深度…

Apache httpd漏洞复现

文章目录 未知后缀名解析漏洞多后缀名解析漏洞启动环境漏洞复现 换行解析漏洞启动环境漏洞复现 未知后缀名解析漏洞 该漏洞与Apache、php版本无关&#xff0c;属于用户配置不当造成的解析漏洞。在有多个后缀的情况下&#xff0c;只要一个文件含有.php后缀的文件即将被识别成PHP…

微信多开bat代码

目录标题 创建txt文件右键 点击新建文本文档 复制如下代码进去&#xff0c;将里面的微信地址改成自己的微信地址&#xff08;查看地址方法&#xff1a;右击微信图标->属性->目标&#xff09;复制如下代码 创建txt文件 右键 点击新建文本文档 复制如下代码进去&#xff0…

Linux系统——MySQL安装(CentOS7 超详细演示)

Linux系统安装MySQL MySQL8.0.26-Linux版安装1. 准备一台Linux服务器2. 下载Linux版MySQL安装包3. 上传MySQL安装包4. 创建目录,并解压5. 安装mysql的安装包6. 启动MySQL服务7. 查询自动生成的root用户密码8. 修改root用户密码9. 创建用户10. 并给root用户分配权限11. 通过Data…

2023高教社杯 国赛数学建模E题思路 - 黄河水沙监测数据分析

1 赛题 E 题 黄河水沙监测数据分析 黄河是中华民族的母亲河。研究黄河水沙通量的变化规律对沿黄流域的环境治理、气候变 化和人民生活的影响&#xff0c; 以及对优化黄河流域水资源分配、协调人地关系、调水调沙、防洪减灾 等方面都具有重要的理论指导意义。 附件 1 给出了位…

willchange 优化性能的原理是什么

写在前面 今天说一下性能优化部分的其中一个点&#xff0c;这个点叫做 willchange&#xff0c;说他的原因主要有以下几个&#xff1a;第一很多人知道用这个可以提高性能但是不知道原因是什么&#xff0c;第二&#xff0c;我们用的时候他虽然可以提高性能&#xff0c;但是不代表…

Revit SDK 介绍:GenericModelCreation常规模型的创建

前言 这个例子介绍了如何创建拉伸、放样、扫掠、融合、放样融合&#xff0c;涵盖了一个建模软件需要的基本建模方法。 内容 CreateExtrusion 生成的放样融合接口&#xff1a; m_creationFamily.NewExtrusion(true, curve, sketchPlane, bottomProfile, topProfile)核心逻辑&…

msvcr100.dll丢失应该怎么解决,比较靠谱的五种解决方法分享

首先&#xff0c;我们需要了解什么是msvcr100.dll。msvcr100.dll是Microsoft Visual C 2010 Redistributable Package的一部分&#xff0c;它包含了一些运行时库&#xff0c;用于支持某些程序的运行。简单来说&#xff0c;msvcr100.dll就是一个“桥梁”&#xff0c;连接了我们的…

组织架构图怎么做?手把手教你绘制完美的组织架构图

组织架构图是企业和组织中不可或缺的工具&#xff0c;它以图形化的方式展示了各级层次的关系&#xff0c;帮助人们更好地理解和管理整体结构。本文将为你详细介绍绘制组织架构图的要素、步骤和技巧&#xff0c;并推荐四款强大的绘图软件&#xff0c;让你轻松绘制完美的组织架构…

无涯教程-JavaScript - DELTA函数

描述 DELTA函数测试两个值是否相等。如果number1 number2,则返回1&#xff1b;否则返回1。否则返回0。 您可以使用此功能来过滤一组值。如,通过合计几个DELTA函数,您可以计算相等对的计数。此功能也称为Kronecker Delta功能。 语法 DELTA (number1, [number2])争论 Argum…

RK3588算法盒子maskrom模式下系统烧录

先在firefly官网下载bulidroot文件&#xff0c;然后进行烧录相关文件。 点击upgrade&#xff0c;进行烧录升级&#xff1b; 等待烧录完成后&#xff0c; 重新开机进入loader模式下&#xff0c; 进行烧录Untunt20.04系统的操作就行。

企业电脑文件加密系统 / 防泄密软件——「天锐绿盾」

「天锐绿盾」是一种公司文件加密系统&#xff0c;旨在保护公司内网数据安全&#xff0c;防止信息泄露。该系统由硬件和软件组成&#xff0c;其中包括服务端程序、控制台程序和终端程序。 PC访问地址&#xff1a; isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c…

python趣味编程-太空入侵者游戏

在 Python 中使用 Turtle 的简单太空入侵者游戏免费源代码 使用 Turtle 的简单太空入侵者游戏是一个用Python编程语言编码的桌面游戏应用程序。该项目包含克隆实际太空入侵者游戏的多种功能。该项目可以使正在学习计算机相关课程的学生受益。该应用程序易于学习,可以帮助您发现…

软件设计师(八)算法设计与分析

算法被公认为是计算机科学的基石&#xff0c;算法理论研究的是算法的设计技术和分析技术。 一、算法设计和分析的基本概念 1、算法 &#xff08;Algorithm&#xff09; 算法&#xff1a;是对特定问题求解步骤的一种描述&#xff0c;它是指令的有限序列&#xff0c;其中每一条…

element树形筛选

<el-inputv-model"projectName"placeholder"请输入名称"clearablemaxlength"10"clear"clearTree" /> <el-divider /> <el-treeref"tree"class"filter-tree":data"treeList":props"…