input子系统框架、外设驱动开发

news2025/1/20 1:53:22

一、input子系统基本框架

Linux内核为了两个目的:

  1. 简化纯输入类外设(如:键盘、鼠标、游戏杆、轨迹球、触摸屏。。。等等)的驱动开发
  2. 统一输入类外设产生的数据格式(struct input_event),更加方便应用层编程

设计了输入子系统

在这里插入图片描述

事件处理层:接收来自核心层上报的事件,并选择对应的handler(事件处理器 struct input_handler)去处理。内核维护着多个事件处理器对象,每个input_handler对象专门处理一类事件,所有产生同类事件的设备驱动共用同一个handler。

设备驱动层:主要实现获取硬件设备的数据信息(包括触摸屏被按下、按下位置、鼠标移动、键盘按下等等),并转换为核心层定义的规范事件后提交给核心层,该层每个设备对应一个struct input_dev对象,

核心层:负责连接设备驱动层和事件处理层,为设备驱动层提供输入设备驱动的接口(struct input_dev)以及输入设备驱动的注册函数(input_register_device),为事件处理层提供输入事件驱动的接口;通知事件处理层对事件进行处理。

二、驱动开发步骤

/*init或probe函数中:
1. 创建struct input_dev对象input_allocate_device
2. 设置事件类型以及相关参数set_bit
3. 注册struct input_dev对象input_register_device
*/

/*exit或remove函数中:
1. 注销struct input_dev对象input_unregister_device
2. 销毁struct input_dev对象input_free_device
*/

/*上报事件
	两种事件上报方式:
	1. 对有中断支持的输入设备:在其中断处理函数(上半部或下半部)中上报事件
	2. 对无中断支持的输入设备:使用workqueue循环定时上报(struct delayed_work)
	主要函数:
	input_event
	input_report_abs
	input_sync
*/

相关接口:

/*_init*/
struct input_dev *input_allocate_device(void)//创建对象

void set_bit(struct input_dev *dev,unsigned long whichbits)//设置事件类型

void input_set_abs_params(struct input_dev *dev,unsigned int axis,int min,int max,int fuzz,int flat)

int input_register_device(struct input_dev *dev)//注册input设备到内核

/*_exit*/
void input_unregister_device(struct input_dev *dev)
void input_free_device(struct input_dev *dev)

/*上报事件*/
void input_event(struct input_dev *,unsigned int t,unsigned int c,int v)

void input_report_key(struct input_dev *,unsigned int c,int v) //上报按键事件
void input_report_abs(struct input_dev *,unsigned int c,int v)//上报绝对坐标事件
    
void input_sync(struct input_dev *)//上报完成后需要调用这些函数来通知系统处理完整事件

/*应用层数据类型*/
struct input_event {
    struct timeval time;       // 时间戳
    __u16 type;             // 事件类型
    __u16 code;             // 哪个分值
    __s32 value;            // 具体值      
};

三、key2-input版代码解析

key2.c

#include <linux/module.h>          // Linux内核模块头文件
#include <linux/kernel.h>          // 内核相关功能的头文件
#include <linux/fs.h>              // 文件系统相关功能的头文件
#include <linux/gpio.h>            // GPIO库的头文件
#include <linux/interrupt.h>       // 中断处理相关功能的头文件
#include <linux/of_gpio.h>         // Open Firmware GPIO相关功能的头文件
#include <linux/of_irq.h>          // Open Firmware中断相关功能的头文件
#include <linux/cdev.h>            // 字符设备相关功能的头文件
#include <linux/wait.h>            // 等待队列相关功能的头文件
#include <linux/sched.h>           // 调度相关功能的头文件
#include <linux/poll.h>            // poll相关功能的头文件
#include <linux/mm.h>              // 内存管理相关功能的头文件
#include <linux/input.h>           // 输入子系统相关功能的头文件
#include <linux/delay.h>           // 延时相关功能的头文件
#include <linux/slab.h>            // 内存分配相关功能的头文件
#include <asm/uaccess.h>           // 用户态内核态数据传输相关功能的头文件

struct fs4412key2_dev
{
	struct input_dev *pdev;     // 输入设备结构体指针,用于注册输入事件

	int gpio;                   // GPIO引脚的编号
	int irqno;                  // 中断编号
};

struct fs4412key2_dev *pgmydev = NULL; // 指向驱动程序数据结构的指针

// 中断处理函数,处理按键中断
irqreturn_t key2_irq_handle(int no, void *arg)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	int status1 = 0;
	int status2 = 0;

	// 读取GPIO引脚状态两次,用于防抖
	status1 = gpio_get_value(pmydev->gpio);
	mdelay(1);
	status2 = gpio_get_value(pmydev->gpio);

	// 如果两次状态不一致,认为是抖动,不处理
	if (status1 != status2)
	{
		return IRQ_NONE;
	}

	// 根据按键状态生成输入事件
	if (status1)
	{
		input_event(pmydev->pdev, EV_KEY, KEY_2, 0); // 按键释放事件
		input_sync(pmydev->pdev);                   // 同步输入事件
	}
	else
	{
		input_event(pmydev->pdev, EV_KEY, KEY_2, 1); // 按键按下事件
		input_sync(pmydev->pdev);                   // 同步输入事件
	}

	return IRQ_HANDLED;
}

// 模块初始化函数
int __init fs4412key2_init(void)
{
	int ret = 0;

	struct device_node *pnode = NULL;

	// 查找设备树节点
	pnode = of_find_node_by_path("/mykey2_node");
	if (NULL == pnode)
	{
		printk("find node failed\n");
		return -1;
	}

	// 分配驱动程序数据结构内存
	pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev), GFP_KERNEL);
	if (NULL == pgmydev)
	{
		printk("kmalloc for struct fs4412key2_dev failed\n");
		return -1;
	}

	// 从设备树中获取GPIO引脚编号
	pgmydev->gpio = of_get_named_gpio(pnode, "key2-gpio", 0);

	// 从设备树中获取中断编号
	pgmydev->irqno = irq_of_parse_and_map(pnode, 0);

	// 分配并注册输入设备
	pgmydev->pdev = input_allocate_device();
	set_bit(EV_KEY, pgmydev->pdev->evbit);
	set_bit(KEY_2, pgmydev->pdev->keybit);
	ret = input_register_device(pgmydev->pdev);

	// 请求中断处理函数
	ret = request_irq(pgmydev->irqno, key2_irq_handle, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "fs4412key2", pgmydev);
	if (ret)
	{
		printk("request_irq failed\n");
		input_unregister_device(pgmydev->pdev);
		input_free_device(pgmydev->pdev);
		kfree(pgmydev);
		pgmydev = NULL;
		return -1;
	}
	return 0;
}

// 模块卸载函数
void __exit fs4412key2_exit(void)
{
	// 释放中断
	free_irq(pgmydev->irqno, pgmydev);

	// 注销输入设备
	input_unregister_device(pgmydev->pdev);
	input_free_device(pgmydev->pdev);

	// 释放驱动程序数据结构内存
	kfree(pgmydev);
	pgmydev = NULL;
}

MODULE_LICENSE("GPL"); // 指定模块许可证
module_init(fs4412key2_init); // 指定模块初始化函数
module_exit(fs4412key2_exit); // 指定模块卸载函数

testkey2.c

#include <sys/types.h>
#include <sys/stat.h>
#include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>

#include <stdio.h>

int main(int argc,char *argv[])
{
	int fd = -1;
	struct input_event evt;
	if(argc < 2)
	{
		printf("Argument is too few\n");
		return 1;
	}

	/*open*/
	fd = open(argv[1],O_RDONLY);
	if(fd < 0)
	{
		printf("open %s failed\n",argv[1]);
		return 2;
	}

	/*init mpu6050*/

	while(1)
	{
		read(fd,&evt,sizeof(evt));
		if(evt.type == EV_KEY && evt.code == KEY_2)
		{
			if(evt.value)
			{
				printf("KEY2 DOWN\n");
			}
			else
			{
				printf("KEY2 UP\n");
			}
		}
	}

	/*close*/
	close(fd);
	fd = -1;
	return 0;
}

四、mpu6050-input版代码解析

mpu6050drv.c

#include <linux/module.h>          // Linux内核模块头文件
#include <linux/kernel.h>          // 内核相关功能的头文件
#include <linux/fs.h>              // 文件系统相关功能的头文件
#include <linux/i2c.h>             // I2C总线相关功能的头文件
#include <linux/cdev.h>            // 字符设备相关功能的头文件
#include <linux/wait.h>            // 等待队列相关功能的头文件
#include <linux/sched.h>           // 调度相关功能的头文件
#include <linux/poll.h>            // poll相关功能的头文件
#include <linux/slab.h>            // 内存分配相关功能的头文件
#include <linux/mm.h>              // 内存管理相关功能的头文件
#include <linux/input.h>           // 输入子系统相关功能的头文件
#include <linux/io.h>              // I/O内存操作相关功能的头文件
#include <asm/uaccess.h>           // 用户态内核态数据传输相关功能的头文件
#include <asm/atomic.h>            // 原子操作相关功能的头文件

/****************MPU6050内部寄存器地址****************/

#define	SMPLRT_DIV		0x19	//陀螺仪采样率,典型值:0x07(125Hz)
#define	CONFIG			0x1A	//低通滤波频率,典型值:0x06(5Hz)
#define	GYRO_CONFIG		0x1B	//陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define	ACCEL_CONFIG	0x1C	//加速计自检、测量范围及高通滤波频率,典型值:0x18(不自检,2G,5Hz)
#define	ACCEL_XOUT_H	0x3B
#define	ACCEL_XOUT_L	0x3C
#define	ACCEL_YOUT_H	0x3D
#define	ACCEL_YOUT_L	0x3E
#define	ACCEL_ZOUT_H	0x3F
#define	ACCEL_ZOUT_L	0x40
#define	TEMP_OUT_H		0x41
#define	TEMP_OUT_L		0x42
#define	GYRO_XOUT_H		0x43
#define	GYRO_XOUT_L		0x44
#define	GYRO_YOUT_H		0x45
#define	GYRO_YOUT_L		0x46
#define	GYRO_ZOUT_H		0x47
#define	GYRO_ZOUT_L		0x48
#define	PWR_MGMT_1		0x6B	//电源管理,典型值:0x00(正常启用)
#define	WHO_AM_I		0x75	//IIC地址寄存器(默认数值0x68,只读)
#define	SlaveAddress	0x68	//MPU6050-I2C地址

// 定义MPU6050设备结构体
struct mpu6050_dev
{
	struct input_dev * pinput;   // 输入设备结构体指针

	struct i2c_client *pclient; // I2C客户端结构体指针

	struct delayed_work work;   // 延迟工作结构体
};

struct mpu6050_dev *pgmydev = NULL; // 指向MPU6050设备数据结构的指针

// 读取MPU6050寄存器的函数
int mpu6050_read_byte(struct i2c_client *pclt, unsigned char reg)
{
	int ret = 0;
	char txbuf[1] = {reg};
	char rxbuf[1] = {0};

	struct i2c_msg msg[2] = 
	{
		{pclt->addr, 0, 1, txbuf},
		{pclt->addr, I2C_M_RD, 1, rxbuf}
	};

	ret = i2c_transfer(pclt->adapter, msg, ARRAY_SIZE(msg));
	if (ret < 0)
	{
		printk("ret = %d, in mpu6050_read_byte\n", ret);
		return ret;
	}

	return rxbuf[0];
}

// 写入MPU6050寄存器的函数
int mpu6050_write_byte(struct i2c_client *pclt, unsigned char reg, unsigned char val)
{
	int ret = 0;
	char txbuf[2] = {reg, val};

	struct i2c_msg msg[1] = 
	{
		{pclt->addr, 0, 2, txbuf},
	};

	ret = i2c_transfer(pclt->adapter, msg, ARRAY_SIZE(msg));
	if (ret < 0)
	{
		printk("ret = %d, in mpu6050_write_byte\n", ret);
		return ret;
	}

	return 0;
}

// 延迟工作函数,用于读取MPU6050传感器数据
void mpu6050_work_func(struct work_struct *pwk)
{
	struct mpu6050_dev *pmydev = container_of((struct delayed_work *)pwk, struct mpu6050_dev, work);
	unsigned short ax = 0;
	unsigned short ay = 0;
	unsigned short az = 0;
	unsigned short gx = 0;
	unsigned short gy = 0;
	unsigned short gz = 0;
	unsigned short temp = 0;

	// 读取加速度和陀螺仪数据
	ax = mpu6050_read_byte(pmydev->pclient, ACCEL_XOUT_L);
	ax |= (mpu6050_read_byte(pmydev->pclient, ACCEL_XOUT_H) << 8);
	input_report_abs(pmydev->pinput, ABS_X, ax);

	ay = mpu6050_read_byte(pmydev->pclient, ACCEL_YOUT_L);
	ay |= (mpu6050_read_byte(pmydev->pclient, ACCEL_YOUT_H) << 8);
	input_report_abs(pmydev->pinput, ABS_Y, ay);

	az = mpu6050_read_byte(pmydev->pclient, ACCEL_ZOUT_L);
	az |= (mpu6050_read_byte(pmydev->pclient, ACCEL_ZOUT_H) << 8);
	input_report_abs(pmydev->pinput, ABS_Z, az);

	gx = mpu6050_read_byte(pmydev->pclient, GYRO_XOUT_L);
	gx |= (mpu6050_read_byte(pmydev->pclient, GYRO_XOUT_H) << 8);
	input_report_abs(pmydev->pinput, ABS_RX, gx);

	gy = mpu6050_read_byte(pmydev->pclient, GYRO_YOUT_L);
	gy |= (mpu6050_read_byte(pmydev->pclient, GYRO_YOUT_H) << 8);
	input_report_abs(pmydev->pinput, ABS_RY, gy);

	gz = mpu6050_read_byte(pmydev->pclient, GYRO_ZOUT_L);
	gz |= (mpu6050_read_byte(pmydev->pclient, GYRO_ZOUT_H) << 8);
	input_report_abs(pmydev->pinput, ABS_RZ, gz);

	temp = mpu6050_read_byte(pmydev->pclient, TEMP_OUT_L);
	temp |= (mpu6050_read_byte(pmydev->pclient, TEMP_OUT_H) << 8);
	input_report_abs(pmydev->pinput, ABS_MISC, temp);

	input_sync(pmydev->pinput);
	schedule_delayed_work(&pgmydev->work, msecs_to_jiffies(1000)); // 延迟1秒后再次读取数据
}

// 初始化MPU6050传感器
void init_mpu6050(struct i2c_client *pclt)
{
	mpu6050_write_byte(pclt, PWR_MGMT_1, 0x00);
	mpu6050_write_byte(pclt, SMPLRT_DIV, 0x07);
	mpu6050_write_byte(pclt, CONFIG, 0x06);
	mpu6050_write_byte(pclt, GYRO_CONFIG, 0xF8);
	mpu6050_write_byte(pclt, ACCEL_CONFIG, 0x19);
}

// I2C设备驱动的探测函数
static int mpu6050_probe(struct i2c_client *pclt, const struct i2c_device_id *pid)
{
	int ret = 0;

	pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev), GFP_KERNEL);
	if (NULL == pgmydev)
	{
		printk("kmalloc failed\n");
		return -1;
	}
	memset(pgmydev, 0, sizeof(struct mpu6050_dev));

	pgmydev->pclient = pclt;

	init_mpu6050(pgmydev->pclient);

	pgmydev->pinput = input_allocate_device();

	set_bit(EV_ABS, pgmydev->pinput->evbit);
	input_set_abs_params(pgmydev->pinput, ABS_X, -32768, 32767, 0, 0);
	input_set_abs_params(pgmydev->pinput, ABS_Y, -32768, 32767, 0, 0);
	input_set_abs_params(pgmydev->pinput, ABS_Z, -32768, 32767, 0, 0);
	input_set_abs_params(pgmydev->pinput, ABS_RX, -32768, 32767, 0, 0);
	input_set_abs_params(pgmydev->pinput, ABS_RY, -32768, 32767, 0, 0);
	input_set_abs_params(pgmydev->pinput, ABS_RZ, -32768, 32767, 0, 0);
	input_set_abs_params(pgmydev->pinput, ABS_MISC, -32768, 32767, 0, 0);

	ret = input_register_device(pgmydev->pinput);
	if (ret)
	{
		printk("input_register_device failed\n");

		input_free_device(pgmydev->pinput);
		pgmydev->pinput = NULL;

		kfree(pgmydev);
		pgmydev = NULL;
		return -1;
	}

	INIT_DELAYED_WORK(&pgmydev->work, mpu6050_work_func);

	schedule_delayed_work(&pgmydev->work, msecs_to_jiffies(1000)); // 初始化后立即开始读取数据

	return 0;
}

// I2C设备驱动的卸载函数
static int mpu6050_remove(struct i2c_client *pclt)
{
	cancel_delayed_work(&pgmydev->work);

	input_unregister_device(pgmydev->pinput);

	input_free_device(pgmydev->pinput);
	pgmydev->pinput = NULL;

	kfree(pgmydev);
	pgmydev = NULL;

	return 0;
}

// 匹配设备树中的MPU6050节点
struct of_device_id mpu6050_dt[] =
{
	{.compatible = "invensense,mpu6050"},
	{}
};

// 定义MPU6050设备驱动的ID
struct i2c_device_id mpu6050_ids[] =
{
	{"mpu6050", 0},
	{}
};

// 定义MPU6050设备驱动结构体
struct i2c_driver mpu6050_driver =
{
	.driver = {
		.name = "mpu6050",
		.owner = THIS_MODULE,
		.of_match_table = mpu6050_dt,
	},
	.probe = mpu6050_probe,
	.remove = mpu6050_remove,
	.id_table = mpu6050_ids,
};

// 注册MPU6050设备驱动
module_i2c_driver(mpu6050_driver);

MODULE_LICENSE("GPL"); // 指定模块许可证

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

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

相关文章

【LeetCode题目详解】第九章 动态规划part10 121. 买卖股票的最佳时机 122.买卖股票的最佳时机II (day49补)

本文章代码以c为例&#xff01; 股票问题是一个动态规划的系列问题 一、力扣第121题&#xff1a;买卖股票的最佳时机 题目&#xff1a; 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#x…

Android学习之路(14) AMS与PMS详解

Android 系统启动流程与 Zygote、SystemServer 在讲解 Zygote 之前&#xff0c;考虑到不同的系统版本源码都不相同&#xff0c;以下分析的源码基于 Android 8.0.0。 init 进程 当系统启动时&#xff0c;init 进程是继 Linux 内核启动后第二个启动的进程&#xff0c;它是在用…

AOP代理中Cglib使用场景

有接口时会使用JDK动态代理 没有接口实现类的情况下使用Cglib进行动态代理

layui手机端使用laydate时间选择器被输入法遮挡的解决方案

在HTML中&#xff0c;你可以使用input元素的readonly属性来禁止用户输入&#xff0c;但是这将完全禁用输入&#xff0c;而不仅仅是禁止弹出输入法。如果你想允许用户在特定条件下输入&#xff0c;你可以使用JavaScript来动态地切换readonly属性。 readonly属性 增加readonly属…

【iOS】MVC

文章目录 前言一、MVC各层职责1.1、controller层1.2、model层1.3、view层 二、总结三、优缺点3.1、优点3.2、缺点 四、代码示例 前言 MVC模式的目的是实现一种动态的程序设计&#xff0c;使后续对程序的修改和扩展简化&#xff0c;并且使程序某一部分的重复利用成为可能。除此…

【交叉熵损失torch.nn.CrossEntropyLoss详解-附代码实现】

CrossEntropyLoss 什么是交叉熵softmax损失计算验证CrossEntropyLoss 输入输出介绍验证代码 什么是交叉熵 交叉熵有很多文章介绍&#xff0c;此处不赘述。只需要知道它是可以衡量真实值和预测值之间的差距的&#xff0c;因而用交叉熵来计算损失的时候&#xff0c;损失是越小越…

【JavaScript手撕代码】new

目录 手写 手写 /* * param {Function} fn 构造函数 * return {*} **/ function myNew(fn, ...args){if(typeof fn ! function){return new TypeError(fn must be a function)}// 先创建一个对象let obj Object.create(fn.prototype)// 通过apply让this指向obj, 并调用执行构…

SHIB去零计划:创新金融未来,打造稳定数字资产新范式

SHIB去零计划&#xff0c;由星火有限公司发起&#xff0c;以区块链去中心化手段解决信任危机&#xff0c;对抗垄断与不公平问题&#xff0c;破解经济制裁&#xff0c;实现稳定数字资产的快速有效、平等互利交易。星火有限公司&#xff0c;一家跨国运营集团&#xff0c;主营业务…

UIStackView入门使用两个问题

项目中横向一排元素&#xff0c;竖向一排元素&#xff0c;可以使用UIStackView。UIStackView的原理不做介绍&#xff0c;这里主要讲两个初次使用容易出现的两个问题。 首先创建一个stackview -(UIStackView*)titleStackView{if(_titleStackView nil){_titleStackView [UISta…

时序分解 | MATLAB实现北方苍鹰优化算法NGO优化VMD信号分量可视化

时序分解 | MATLAB实现北方苍鹰优化算法NGO优化VMD信号分量可视化 目录 时序分解 | MATLAB实现北方苍鹰优化算法NGO优化VMD信号分量可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 北方苍鹰优化算法NGO优化VMD&#xff0c;对其分解层数&#xff0c;惩罚因子数做优化…

绝对的搜索利器

苏生不惑第450 篇原创文章&#xff0c;将本公众号设为星标&#xff0c;第一时间看最新文章。 今天分享几个文件搜索利器&#xff0c;下载地址在公众号苏生不惑后台回复2023909&#xff0c;你的小电影要藏不住了。 首先自然是Everything https://www.voidtools.com/zh-cn/&#…

python DVWAXSSPOC练习

XSS反射性低难度 数据包 GET /dv/vulnerabilities/xss_r/?name%3Cscript%3Ealert%28%27xss%27%29%3C%2Fscript%3E HTTP/1.1Host: 10.9.75.161Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Ch…

数据结构与算法-BtreeB+Tree

一&#xff1a;引入 作为一个IT从业者大家对数据库肯定是都知道的&#xff0c;大家应该知道在数据库中有个索引&#xff0c;在一张表中用了索引与不用索引那查找效率简直就是天壤之别&#xff0c;但是大家有没思考过&#xff0c;你经常用的索引是什么样的数据结构呢&#xff1f…

Unity中Shader抓取屏幕并实现扭曲效果

文章目录 前言一、屏幕抓取&#xff0c;在上一篇文章已经写了二、实现抓取后的屏幕扭曲实现思路&#xff1a;1、屏幕扭曲要借助传入 UV 贴图进行扭曲2、传入贴图后在顶点着色器的输入参数处&#xff0c;传入一个 float2 uv : TEXCOORD&#xff0c;用于之后对扭曲贴图进行采样3、…

SAP 创建动态内表

创建动态内表 一、根据表名创建内表 程序代码&#xff1a; "复杂方式 SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001. PARAMETERS:p_tab TYPE string. SELECTION-SCREEN END OF BLOCK b1.DATA:lr_struct TYPE REF TO data,lr_table TYPE REF TO data. …

【云原生系列】Docker学习

目录 一、Docker常用命令 1 基础命令 2 镜像命令 2.1 docker images 查看本地主机的所有镜像 2.2 docker search 搜索镜像 2.3 docker pull 镜像名[:tag] 下载镜像 2.4 docker rmi 删除镜像 2.5 docker build 构建镜像 3 容器命令 3.1 如拉取一个centos镜像 3.2 运行…

.env文件详解

.env配置文件 vue会根据 process.env.NODE_ENV 的值&#xff0c;自动加载对应的环境配置文件 .env 全局默认配置文件&#xff0c;在所有的环境中被载入;.env.production 生产环境文件 production;.env.development 开发环境文件 development&#xff1b;.env.test/.env.stagi…

从零开始完整实现-循环神经网络RNN

一 简介 使用 pytorch 搭建循环神经网络RNN&#xff0c;循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;是一类用于 处理序列数据的神经网络架构。与传统神经网络不同&#xff0c;RNN 具有内部循环结构&#xff0c;可以在处理序列数据时保持状态…

MySQL基础篇:掌握数据库基本操作,轻松上手

查看和指定现有的数据库 mysql> show databases; -------------------- | Database | -------------------- | information_schema | | bjpowernode | | eladmin | | mysql | | performance_schema | | sqlalchemy | | s…

makefile之使用函数wildcard和patsubst

Makefile之调用函数 调用makefile机制实现的一些函数 $(function arguments) : function是函数名,arguments是该函数的参数 参数和函数名用空格或Tab分隔,如果有多个参数,之间用逗号隔开. wildcard函数:让通配符在makefile文件中使用有效果 $(wildcard pattern) 输入只有一个参…