Linux RN6752 驱动编写

news2024/11/17 9:56:52

一、概述

关于 RN6752V1 这个芯片这里就不做介绍了,看到这篇笔记的小伙伴应该都明白,虽然说 RN6752V1 芯片是 AHD 信号的解码芯片,但是也可以把芯片当做是一个 YUV 信号的 MIPI 摄像头,所以驱动的编写和 MIPI 摄像头无太大的区别。这里主要是介绍具体的函数,关于 MIPI 驱动的框架程序看我之前的笔记:Linux MIPI 摄像头驱动框架编写(RN6752解码芯片)

二、RN6752 帧格式

RN6752 支持 DVP 和 MIPI 信号,这里我主要是对 MIPI 信号的使用,当然 DVP 通信的操作也可以做参考。

  1. 寄存地配置通过代理商提供的头文件中可以获取到相关寄存器的配置,如下所示:

static const struct sensor_register rn6752_fhd_1080P25_video[] = {
	{ 0x19, 0x0A }, // 视频格式检测滞后控制
	{ 0x81, 0x01 }, // 打开视频解码器
	{ 0xDF, 0xFE }, // 启用HD格式
	{ 0xF0, 0xC0 },	// 使能 FIFO 和 144 MHz 解码器输出
	{ 0xA3, 0x04 }, // 启用 HD 输出
	{ 0x88, 0x40 }, // 禁用 SCLK1 输出
	{ 0xF6, 0x40 }, // 禁用 SCLK3A 输出
 
	/* 切换到ch0(默认;可选) */
	{ 0xFF, 0x00 },	// 寄存器集选择
	{ 0x33, 0x10 }, // 检测中的视频
	{ 0x4A, 0xA8 }, // 检测中的视频
	{ 0x00, 0x20 }, // internal use*
	{ 0x06, 0x08 }, // internal use*
	{ 0x07, 0x63 }, // 高清格式
	{ 0x2A, 0x01 }, // 滤波器控制
	{ 0x3A, 0x24 }, // 在SAV/EAV代码中插入通道ID
	{ 0x3F, 0x10 }, // 通道ID
	{ 0x4C, 0x37 }, // 均衡器
	{ 0x4F, 0x03 }, // 同步控制
	{ 0x50, 0x03 }, // 1080p分辨率
	{ 0x56, 0x02 }, // 144M 和 BT656模式
	{ 0x5F, 0x44 }, // 消隐电平
	{ 0x63, 0xF8 }, // 滤波器控制
	{ 0x59, 0x00 }, // 扩展寄存器存取
	{ 0x5A, 0x48 }, // 扩展寄存器的数据
	{ 0x58, 0x01 }, // 启用扩展寄存器写入
	{ 0x59, 0x33 }, // 扩展寄存器存取
	{ 0x5A, 0x23 }, // 扩展寄存器的数据
	{ 0x58, 0x01 }, // 启用扩展寄存器写入
	{ 0x51, 0xF4 }, // 比例因子1
	{ 0x52, 0x29 }, // 比例因子2
	{ 0x53, 0x15 }, // 比例因子3
	{ 0x5B, 0x01 }, // H-标度控制
	{ 0x5E, 0x08 }, // 启用H缩放控制
	{ 0x6A, 0x87 }, // H-标度控制
	{ 0x28, 0x92 }, // 剪裁
	{ 0x03, 0x80 }, // 饱和
	{ 0x04, 0x80 }, // 颜色
	{ 0x05, 0x04 }, // 尖锐
	{ 0x57, 0x23 }, // 黑色/白色拉伸
	{ 0x68, 0x00 }, // coring
	{ 0x37, 0x33 }, // 
	{ 0x61, 0x6C }, //
#ifdef USE_BLUE_SCREEN
	{ 0x3A, 0x24 }, // AHD 断开链接时,屏幕为蓝色
#else
	{ 0x3A, 0x2C }, // AHD 断开链接时,屏幕为黑色
	{ 0x3B, 0x00 }, //
	{ 0x3C, 0x80 }, //
	{ 0x3D, 0x80 }, //
#endif
	{ 0x2E, 0x30 }, // 强制不播放视频
	{ 0x2E, 0x00 }, // 回归平常
 
	/* mipi 连接 */
	{ 0xFF, 0x09 }, // 切换到 mipi tx1
	{ 0x00, 0x03 }, // enable bias
	{ 0xFF, 0x08 }, // 切换到 mipi csi1
	{ 0x04, 0x03 }, // csi1 和 tx1 重置
	{ 0x6C, 0x11 }, // 禁用 ch 输出,打开 ch0
#ifdef USE_MIPI_4LANES
	{ 0x06, 0x7C }, // mipi 4 线
#else
	{ 0x06, 0x4C }, // mipi 2 线
#endif
	{ 0x21, 0x01 }, // 启用 hs 时钟
	{ 0x34, 0x06 }, //
	{ 0x35, 0x0B }, // 
	{ 0x78, 0xC0 }, // ch0 的 Y/C 计数
	{ 0x79, 0x03 }, // ch0 的 Y/C 计数
	{ 0x6C, 0x01 }, // 启用 ch 输出
	{ 0x04, 0x00 }, // csi1 和 tx1 重置完成
	{ 0x20, 0xAA }, // 
#ifdef USE_MIPI_NON_CONTINUOUS_CLOCK
	{ 0x07, 0x05 }, // 启用非连续时钟
#else
	{ 0x07, 0x04 }, // 启用连续时钟
#endif
	{ 0xFF, 0x0A }, // 切换到 mipi csi3
	{ 0x6C, 0x10 }, // 禁用 ch 输出;关闭 ch0~3
	{REG_NULL, 0x00},
};

注意: 其他格式的寄存器我这里就不附上了,可以参考代理商提供的头文件

  1. 将配置信息存入帧列表中

static const struct rn6752_framesize rn6752_mipi_framesizes[] = {
	{
		.width		= 1280,
		.height		= 720,
		.max_fps = {
			.numerator = 10000,
			.denominator = 250000,
		},
		.regs		= rn6752_fhd_720P25_video,
	}, 
	{
		.width		= 1280,
		.height		= 720,
		.max_fps = {
			.numerator = 10000,
			.denominator = 300000,
		},
		.regs		= rn6752_fhd_720P30_video,
	}, 
	{
		.width		= 1920,
		.height		= 1080,
		.max_fps = {
			.numerator = 10000,
			.denominator = 250000,
		},
		.regs		= rn6752_fhd_1080P25_video,
	}, 
	{
		.width		= 1920,
		.height		= 1080,
		.max_fps = {
			.numerator = 10000,
			.denominator = 300000,
		},
		.regs		= rn6752_fhd_1080P30_video,
	}, 
	{
		.width		= 1280,
		.height		= 960,
		.max_fps = {
			.numerator = 10000,
			.denominator = 250000,
		},
		.regs		= rn6752_fhd_960P25_video,
	}, 
	{
		.width		= 1280,
		.height		= 960,
		.max_fps = {
			.numerator = 10000,
			.denominator = 300000,
		},
		.regs		= rn6752_fhd_960P30_video,
	}
};

  1. 配置默认帧在 rn6752_probe 函数中存入默认支持的帧列表,如下所示

static void rn6752_get_default_format(struct rn6752 *rn6752,
				      struct v4l2_mbus_framefmt *format)
{
	format->width = rn6752->framesize_cfg[2].width; 	/* 设置默认宽度 */
	format->height = rn6752->framesize_cfg[2].height; 	/* 设置默认高度 */
	format->colorspace = V4L2_COLORSPACE_SRGB; 			/* 设置默认色彩空间为标准的 sRGB 色彩空间 */
	format->code = rn6752_formats[0].code; 				/* 设置默认编码格式 */
	format->field = V4L2_FIELD_NONE; 					/* 设置默认场模式 */
}
 
/* rn6752_mipi_framesizes 是 rn6752 mipi 通信支持的所有帧格式 */
rn6752->framesize_cfg = rn6752_mipi_framesizes;
rn6752->cfg_num = ARRAY_SIZE(rn6752_mipi_framesizes);
/* 获取摄像头传感器支持的图像帧格式 */
rn6752_get_default_format(rn6752, &rn6752->format);
rn6752->frame_size = &rn6752->framesize_cfg[2]; 			/* 设置帧大小 */
rn6752->format.width = rn6752->framesize_cfg[2].width; 		/* 设置宽度 */
rn6752->format.height = rn6752->framesize_cfg[2].height; 	/* 设置高度 */
rn6752->fps = DIV_ROUND_CLOSEST(
	rn6752->framesize_cfg[2].max_fps.denominator,
	rn6752->framesize_cfg[2].max_fps.numerator); 			/* 设置最大帧速率 */

注意:

  • 首先将所有支持的帧列表存入了 rn6752->framesize_cfg 中

  • 将支持的列表数量存入 rn6752->cfg_num 中

  • 将默认支持的帧格式和大小存入 rn6752->format 中,这个在用户空间可以查看

  • 将默认支持的帧大小存入 rn6752->frame_size 中

  • 将默认支持的帧率存入 rn6752->fps 中

  • 以上这些默认变量将在后面的函数中经常用到,所以需要特别注意一下,不然很难理解数据从哪里来的

三、Media 设备节点

之前在 Media 子系统中提到过模块之间的关系查看命令media-ctl -p -d /dev/mediaX,通过命令可以得到驱动中的一些信息,如下图所示

  1. Media 帧大小Media 帧大小是在驱动初始化时,通过 rn6752_get_fmt 函数获取的,程序如下

static int rn6752_get_fmt(struct v4l2_subdev *sd,
			  struct v4l2_subdev_pad_config *cfg,
			  struct v4l2_subdev_format *fmt)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd); /* 获取i2c_client指针 */
	struct rn6752 *rn6752 = to_rn6752(sd);
 
	/* 使用dev_dbg打印日志,显示当前函数进入 */
	// dev_info(&client->dev, "%s enter\n", __func__);
 
	/* 条件成立时,表示要获取正在尝试的格式 */
	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
		struct v4l2_mbus_framefmt *mf;
 
		/* 获取正在尝试的格式 */
		mf = v4l2_subdev_get_try_format(sd, cfg, 0);
		mutex_lock(&rn6752->lock);
		fmt->format = *mf;
		mutex_unlock(&rn6752->lock);
		return 0;
#else
		return -ENOTTY;
#endif
	}
 
	/* 条件不成立时,表示要获取当前的格式 */
	mutex_lock(&rn6752->lock);
	fmt->format = rn6752->format;
	mutex_unlock(&rn6752->lock);
 
	/* 使用dev_dbg打印日志,显示当前格式的代码值、宽度和高度 */
	dev_dbg(&client->dev, "%s: %x %dx%d\n", __func__, rn6752->format.code,
		rn6752->format.width, rn6752->format.height);
 
	return 0;
}

  1. 帧格式判断

Media 设备是通过 rn6752_enum_frame_sizes 和 rn6752_enum_frame_interval 函数枚举了帧大小和帧率,这两个函数主要起到判断的作用,确实当前帧率是否是驱动支持的,程序如下

static int rn6752_enum_frame_sizes(struct v4l2_subdev *sd,
				   struct v4l2_subdev_pad_config *cfg,
				   struct v4l2_subdev_frame_size_enum *fse)
{
	struct rn6752 *rn6752 = to_rn6752(sd);
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	int i = ARRAY_SIZE(rn6752_formats);
	printk(KERN_INFO
	       "rn6752_enum_frame_sizes................................................\n");
 
	dev_dbg(&client->dev, "%s:\n", __func__);
 
	if (fse->index >= rn6752->cfg_num)
		return -EINVAL;
 
	while (--i)
		if (fse->code == rn6752_formats[i].code)
			break;
 
	fse->code = rn6752_formats[i].code;
 
	fse->min_width  = rn6752->framesize_cfg[fse->index].width;
	fse->max_width  = fse->min_width;
	fse->max_height = rn6752->framesize_cfg[fse->index].height;
	fse->min_height = fse->max_height;
	return 0;
}
 
static int rn6752_enum_frame_interval(struct v4l2_subdev *sd,
			   struct v4l2_subdev_pad_config *cfg,
			   struct v4l2_subdev_frame_interval_enum *fie)
{
	struct rn6752 *rn6752 = to_rn6752(sd);
	printk(KERN_INFO
	       "rn6752_enum_frame_interval index: %d....................\n", fie->index );
 
	/* 检查传入的 fie 结构体中的 index 字段是否超出了 rn6752 所支持的帧间隔配置数量(cfg_num) */
	if (fie->index >= rn6752->cfg_num)
		return -EINVAL;
 
	/* 检查传入的 fie 结构体中的 code 字段是否与期望的媒体总线格式(MEDIA_BUS_FMT_UYVY8_2X8)匹配 */
	if (fie->code != MEDIA_BUS_FMT_UYVY8_2X8)
		return -EINVAL;
 
	fie->width = rn6752->framesize_cfg[fie->index].width;           /* 宽 */
	fie->height = rn6752->framesize_cfg[fie->index].height;         /* 高 */
	fie->interval = rn6752->framesize_cfg[fie->index].max_fps;      /* 最大帧率 */
 
	return 0;
}

  1. 帧大小设置

可以通过 rn6752_set_fmt 函数设置帧的大小,程序如下

tatic int rn6752_set_fmt(struct v4l2_subdev *sd,
			  struct v4l2_subdev_pad_config *cfg,
			  struct v4l2_subdev_format *fmt)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	int index = ARRAY_SIZE(rn6752_formats);
	struct v4l2_mbus_framefmt *mf = &fmt->format;
	const struct rn6752_framesize *size = NULL;
	struct rn6752 *rn6752 = to_rn6752(sd);
	printk(KERN_INFO
	       "rn6752_set_fmt................................................\n");
 
	dev_info(&client->dev, "%s enter\n", __func__);
 
	/* 根据传入的参数调整帧大小和帧速率,并返回适合的帧大小和帧速率 */
	__rn6752_try_frame_size_fps(rn6752, mf, &size, rn6752->fps);
 
	/* 遍历rn6752_formats数组 */
	while (--index >= 0)
		if (rn6752_formats[index].code == mf->code)
			break;
 
	if (index < 0)
		return -EINVAL;
 
	/* 色彩空间为sRGB,场为无 */
	mf->colorspace = V4L2_COLORSPACE_SRGB;
	mf->code = rn6752_formats[index].code;
	mf->field = V4L2_FIELD_NONE;
 
	mutex_lock(&rn6752->lock);
 
	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
		mf = v4l2_subdev_get_try_format(
			sd, cfg,
			fmt->pad); /* 使用v4l2_subdev_get_try_format函数获取正在尝试的格式 */
		*mf = fmt->format;
#else
		return -ENOTTY;
#endif
	} else {
		if (rn6752->streaming) {
			mutex_unlock(&rn6752->lock);
			return -EBUSY;
		}
 
		/* 分别设置为获取到的帧大小和传入的格式 */
		rn6752->frame_size = size;
		rn6752->format = fmt->format;
	}
 
	mutex_unlock(&rn6752->lock);
 
	return 0;
}

  1. 帧间隔获取

static int rn6752_g_frame_interval(struct v4l2_subdev *sd,
				   struct v4l2_subdev_frame_interval *fi)
{
	struct rn6752 *rn6752 = to_rn6752(sd);
	printk(KERN_INFO
	       "rn6752_g_frame_interval................................................\n");
 
	mutex_lock(&rn6752->lock);
	fi->interval = rn6752->frame_size->max_fps;
	mutex_unlock(&rn6752->lock);
	return 0;
}

四、总线编码格式

之前有提到过,RN6752 支持 DVP 和 MIPI 总线格式,所以可以在一个驱动中实现两个功能,这里我就是写了 MIPI 的通信方式,我目前对 DVP 也不了解,以后在补上。

刚好驱动中提供了两个函数可以获取驱动总线的格式,如下所示

  1. 获取当前媒体总线配置的函数

static int rn6752_g_mbus_config(struct v4l2_subdev *sd,
				struct v4l2_mbus_config *config)
{
	printk(KERN_INFO
	       "rn6752_g_mbus_config................................................\n");
 
	/* 总线类型是CSI-2 */
	config->type = V4L2_MBUS_CSI2;
	config->flags = V4L2_MBUS_CSI2_4_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
			V4L2_MBUS_CSI2_CHANNEL_1 |
			V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
 
	return 0;
}

  1. 枚举所有支持的媒体总线编码和格式

static int rn6752_enum_mbus_code(struct v4l2_subdev *sd,
				 struct v4l2_subdev_pad_config *cfg,
				 struct v4l2_subdev_mbus_code_enum *code)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	printk(KERN_INFO
	       "rn6752_enum_mbus_code................................................\n");
 
	dev_dbg(&client->dev, "%s:\n", __func__);
 
	if (code->index >= ARRAY_SIZE(rn6752_formats))
		return -EINVAL;
 
	code->code = rn6752_formats[code->index].code;
	return 0;
}

五、电源管理

摄像头每次开启和关闭时,都需要通过电源管理函数配置摄像头电源

static int rn6752_power(struct v4l2_subdev *sd, int on)
{
	struct rn6752 *rn6752 = to_rn6752(sd);
	struct i2c_client *client = rn6752->client;
	int ret = 0;
 
	/* 使用dev_info打印日志,显示当前函数和行号,并打印on参数的值 */
	dev_dbg(&client->dev, "%s(%d) on(%d)\n", __func__, __LINE__, on);
 
	mutex_lock(&rn6752->lock);
 
	if (rn6752->power_on == !! on)
		goto unlock_and_return;
	
	if (on) {
		ret = pm_runtime_get_sync(&client->dev);
		if (ret < 0) {
			pm_runtime_put_noidle(&client->dev);
			goto unlock_and_return;
		}
		rn6752->power_on = true;
	} else {
		pm_runtime_put(&client->dev);
		rn6752->power_on = false;
	}
 
unlock_and_return:
	mutex_unlock(&rn6752->lock);
 
	return ret;
}

六、摄像头控制

由于这里我没有实现太多的控制功能,所以只实现了必要的两个控制,最主要的是复位时执行的 RKMODULE_SET_QUICK_STREAM 功能

static long rn6752_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
	struct rn6752 *rn6752 = to_rn6752(sd);
	// struct rkmodule_hdr_cfg *hdr;
	long ret = 0;
	u32 stream = 0;
 
	// dev_dbg(KERN_INFO "rn6752_ioctl  0x%x..........\n", cmd);
 
	switch (cmd) {
	case RKMODULE_GET_MODULE_INFO:
		rn6752_get_module_info(rn6752, (struct rkmodule_inf *)arg);
		break;
	case RKMODULE_SET_QUICK_STREAM:
		stream = *((u32 *)arg);
 
		rn6752_set_streaming(rn6752, !!stream);
		break;
 
	default:
		ret = -ENOIOCTLCMD;
		break;
	}
	return ret;
}
 
#ifdef CONFIG_COMPAT
static long rn6752_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd,
				  unsigned long arg)
{
	void __user *up = compat_ptr(arg);
	struct rkmodule_inf *inf;
	struct rkmodule_awb_cfg *cfg;
	long ret;
	u32 stream = 0;
 
	// dev_dbg(KERN_INFO "rn6752_compat_ioctl32..........\n");
 
	switch (cmd) {
	case RKMODULE_GET_MODULE_INFO:
		inf = kzalloc(sizeof(*inf), GFP_KERNEL);
		if (!inf) {
			ret = -ENOMEM;
			return ret;
		}
 
		ret = rn6752_ioctl(sd, cmd, inf);
		if (!ret)
			ret = copy_to_user(up, inf, sizeof(*inf));
		kfree(inf);
		break;
	case RKMODULE_AWB_CFG:
		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
		if (!cfg) {
			ret = -ENOMEM;
			return ret;
		}
 
		ret = copy_from_user(cfg, up, sizeof(*cfg));
		if (!ret)
			ret = rn6752_ioctl(sd, cmd, cfg);
		kfree(cfg);
		break;
	case RKMODULE_SET_QUICK_STREAM:
		ret = copy_from_user(&stream, up, sizeof(u32));
		if (!ret)
			ret = rn6752_ioctl(sd, cmd, &stream);
		break;
	default:
		ret = -ENOIOCTLCMD;
		break;
	}
	return 0;
}
#endif

七、数据流控制

整个驱动最重要的便是流控制函数,通过此函数完成了摄像头的启动和停止

static int rn6752_set_streaming(struct rn6752 *rn6752, int on)
{
	struct i2c_client *client = rn6752->client;
	int ret = 0;
 
	dev_info(&client->dev, "%s: on: %d\n", __func__, on);
 
	if (on)
	{
		ret = rn6752_write(client, 0x80, 0x31);
		usleep_range(200, 500);
		ret |= rn6752_write(client, 0x80, 0x30);
		if (ret)
		{
			dev_err(&client->dev, "rn6752 soft reset failed\n");
			return ret;
		}
 
		ret = rn6752_write_array(client, rn6752->frame_size->regs);
		if (ret)
			dev_err(&client->dev, "rn6752 start initialization failed\n");
	}
	else
	{
		ret = rn6752_write(client, 0x80, 0x00);
		if (ret)
			dev_err(&client->dev, "rn6752 soft standby failed\n");
			
	}
	return ret;
}
 
static int rn6752_s_stream(struct v4l2_subdev *sd, int on)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	struct rn6752 *rn6752 = to_rn6752(sd);
	int ret = 0;
	unsigned int fps;
 
	/* 计算帧率和延迟时间 */
	fps = DIV_ROUND_CLOSEST(rn6752->frame_size->max_fps.denominator,
					rn6752->frame_size->max_fps.numerator);
 
	dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on,
		 rn6752->frame_size->width, rn6752->frame_size->height,
		 DIV_ROUND_CLOSEST(rn6752->frame_size->max_fps.denominator,
				   rn6752->frame_size->max_fps.numerator));
 
	mutex_lock(&rn6752->lock);
 
	on = !!on;
 
	if (rn6752->streaming == on)
		goto unlock;
 
	if (on) {
		ret = pm_runtime_get_sync(&client->dev);
		if (ret < 0)
		{
			pm_runtime_put_noidle(&client->dev);
			goto unlock;
		}
	}
 
	rn6752->streaming = on;
	ret = rn6752_set_streaming(rn6752, on);
	if (ret)
		rn6752->streaming = !on;
 
	pm_runtime_put(&client->dev);
 
unlock:
	mutex_unlock(&rn6752->lock);
	return ret;
}

注意: 摄像头驱动中并没有图像接收之类的关系,而数据流操作函数主要的作用是对芯片进行初始化,使摄像头进入工作模式。从上面的驱动程序可以看出,整个驱动并没有其他特别的功能,就是一个 I2C 控制功能,所以摄像头的驱动其实就是一个 I2C 驱动程序。

由于笔记内容有点多,这里我就不附上完成的驱动程序了,其次是驱动程序也比较简单,看完的小伙伴应该都能明白。主要的难度都在调试摄像头驱动上面,我也折腾了很久,有需要的小伙变可以看我后面的笔记

参考资料

gc2145.c 和 imx335.c 驱动程序

文章转载自:浇筑菜鸟

原文链接:https://www.cnblogs.com/jzcn/p/17825502.html

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

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

相关文章

Docker篇之docker部署harbor仓库

一、首先需要安装docker step1&#xff1a;安装docker #1、安装yun源 yum install -y yum-utils #2、配置yum源 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 如果上面源不稳定的话&#xff0c;更换为下列的aliyun源 yu…

爬虫学习 逆向爬虫(六)

多任务异步协程 协程:更高效的利用CPU import timedef func():print("黎明")time.sleep(3)print("还是黎明")func() 等待时机长 sleep时CPU不再工作 IO操作(费时不费力)->阻塞 线程运行阻塞后 移出主线程 移动到下一个 4个任务一个线程 …

首次部署Linux系统的经历

我是一名电子信息工程专业的学生&#xff0c;有次在图书馆上自习的时候无意间看到其他同学的电脑屏幕&#xff0c;黑色的屏幕上显示着一行一行的代码&#xff0c;勾起了我无限的好奇&#xff0c;经过询问得知他是用的Linux操作系统&#xff0c;是和Windows完全不同的系统&#…

Python解释器和环境变量配置(超详细)

一、 python解释器下载 1.百度python官网并打开 官网网址&#xff1a;www.python.org/ 在这里插入图片描述 2.选择DownLoads&#xff0c;Windows并打开 3.在下图页面选择你需要的版本下载即可&#xff08;本文以python 3.10.4版本演示&#xff09; 在这里插入图片描述 二、安…

基于OpenCV的手势识别系统设计与开发

摘要 随着计算机技术与信息处理技术迅速发展&#xff0c;智能化电子设备逐渐进入到日常的生产和生活中&#xff0c;与此同时&#xff0c;人们对电子设备操作过程的便捷化也提出了新的要求&#xff0c;这也促使计算机进行图像处理的技术也得到了发展。近些年兴起的模式识别技术…

蓝桥杯每日一题2023.11.29

题目描述 #include <stdio.h> #include <string.h>void StringInGrid(int width, int height, const char* s) {int i,k;char buf[1000];strcpy(buf, s);if(strlen(s)>width-2) buf[width-2]0;printf("");for(i0;i<width-2;i) printf("-"…

unity UI特效遮罩

using System.Collections; using System.Collections.Generic; using UnityEngine;/**UI特效遮罩 1.需要将ScrollRect 的遮罩Mask 换为 2D Mask2.将特效的Render里面的 Masking 设置为*/ public class UIParticleMaskControll : MonoBehaviour {// Start is called before …

《HelloGitHub》第 92 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01; 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 https://github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 …

手敲myarraylist,深入了解其运行逻辑

1、自定义MyArrayList类 该类里面基本有两个属性&#xff0c;一个是用来存放数据的数组&#xff0c;另外一个是用来描述已经存放数据的数量。同时设置arraylist表的默认长度为10&#xff1b;代码如下&#xff1a; public class MyArrayList {private int[] elem;private int u…

基于matlab的图像去噪算法设计与实现

摘 要 随着我们生活水平的提高&#xff0c;科技产品飞速更新换代&#xff0c;在信息传输中&#xff0c;图像传输所占的比重越来越大。但自然噪声会在图像传输时干扰其传输过程&#xff0c;甚至会使图片不能表达其原来的意义。去噪处理就是为了去除图像中的噪声&#xff0c;从而…

出口贸易媒体发稿7种方法提升转化率的秘密武器解析-华媒舍

出口贸易成为了许多企业发展的重要方向。在这个竞争激烈的市场中&#xff0c;如何让自己的产品脱颖而出&#xff0c;吸引更多客户并提高转化率&#xff0c;成为了每个企业家都面临的挑战。本文将向大家介绍7种提升转化率的秘密武器&#xff1a;出口贸易媒体发稿方法。 1. 出口贸…

如何获取1688的订单详情

获取1688订单详情需要申请 第一步&#xff1a;先去1688-开放平台申请&#xff0c;申请不一定能通过&#xff0c;审批很严。 第二步&#xff1a;首次登录请先注册&#xff0c;注册成功后即可登录。 第三步&#xff1a;选择&#xff1a;我是第三方开发者 第三方是封装好的ap…

如何判断数据库慢 SQL 查询?

慢 SQL 查询通常指执行时间较长或者消耗大量系统资源的查询。要判断一个 SQL 查询是否慢&#xff0c;可以考虑以下几个方面&#xff1a; 执行时间&#xff1a; 观察查询执行所需的时间。如果一个查询花费了相对较长的时间才能返回结果&#xff0c;可能就是慢查询的一个指标。通…

JavaScript黑科技:简洁有用的一行代码,让你的开发效率飙升!

说在前面 在这篇技术博客中&#xff0c;我们将向你介绍一些令人惊叹的JavaScript黑科技&#xff0c;这些只需一行代码就能实现的简洁而有用的功能&#xff0c;将极大地提升你的开发效率。无论是优化代码、增加交互性&#xff0c;还是实现复杂的逻辑&#xff0c;这些代码片段将成…

【教程】 一文部署配置并入门 Redis

综述 什么是Redis Redis官网——Redis.io Redis, 作为一个高性能的键值对数据库&#xff0c;主要应用于以下场景&#xff1a; 缓存系统&#xff1a;由于其高速读写能力&#xff0c;Redis 非常适合用作缓存系统&#xff0c;减少数据库负载。 会话存储&#xff08;Session St…

c++基本常见错误总结

我们无论是在学习中还是在工作当中&#xff0c;总是会遇到各种各样的c编译错误问题&#xff0c;经常会有一种情况就是上一次好像遇到过这种问题&#xff0c;但是就是想不起来了&#xff08;我就是这样&#xff09;所以下面这一篇文章就是总结自己遇到的编译以及运行错误。 注意…

Javase | Java常用类 (不断补充中...)

目录: 1.Object类2.String类3.StringBuffer类4.Math类5.Random类6.包装类(不断补充中...) 1.Object类 Object类是Java语言中的所有类的超类&#xff0c;即所有类的根。它中描述的所有方法&#xff0c;所有类都可以使用。 equals( ) : 指示其他某个对象与此对象“是否相等” (比…

SD-WAN是否将终结IPsec VPN?

在网络架构的演进历程中&#xff0c;IPsec VPN一直扮演着至关重要的技术角色。而近年来备受关注的SD-WAN技术日益成熟&#xff0c;各大服务供应商纷纷将其与IPsec VPN进行对比&#xff0c;似乎预示着SD-WAN必然替代传统的IPsec VPN。 然而事实究竟如何&#xff1f;SD-WAN等于IP…

Vue项目解决van-calendar 显示白色空白,需滑动一下屏幕,才可正常显示

问题描述&#xff0c;如图 ipad(平板&#xff09;或者 H5移动端引入Vant组件的日历组件&#xff08;van-calendar&#xff09;&#xff0c;初始化显示空白&#xff0c;需滚动一下屏幕&#xff0c;才可正常显示 解决方法 需在van-calendar上绑定open"openCalendar"事件…

AI堆栈之战正在升温

原创 | 文 BFT机器人 从OpenAI和微软最近的现状可以看出&#xff0c;争夺更多新兴AI堆栈的竞争正在加剧。AI堆栈是用于开发和部署AI应用程序的技术、框架和工具的集合&#xff0c;通常包括多个层或组件。 在微软Ignite会议和OpenAI首届DevDay大会的背景下&#xff0c;一些行业…