Linux驱动开发进阶(七)- DRM驱动程序设计
文章目录
- 1、前言
- 2、DRAM(KMS、GEM)
- 2.1、KMS
- 2.2、GEM
- 3、DRM
- 3.1、驱动结构体
- 3.2、设备结构体
- 3.3、DRM驱动注册
- 3.4、DRM模式设置
- 3.4.1、plane初始化
- 3.4.2、crtc初始化
- 3.4.3、encoder初始化
- 3.4.4、connect初始化
- 4、示例说明
- 5、DRM Simple Display框架
- 6、DRM热插拔
- 7、DRM中的plane update
- 8、DRM相关结构
- 8.1、edid
- 8.2、panel
- 8.3、bridge
1、前言
- 学习参考书籍以及本文涉及的示例程序:李山文的《Linux驱动开发进阶》
- 本文属于个人学习后的总结,不太具备教学功能。
2、DRAM(KMS、GEM)
2.1、KMS
KMS由frame buffer、plane、CRTC、encoder、connector、vblank、property组成
2.2、GEM
略
3、DRM
3.1、驱动结构体
drm驱动结构体很大,里面都是一些操作函数。面对这么多函数,我们先不深究,继续看drm设备结构体。
struct drm_driver {...
}
3.2、设备结构体
drm设备结构体里面不再是操作函数,而是一些版本号、标志位等等。
struct drm_device {...
}
3.3、DRM驱动注册
drm驱动设备并没有完全符合总线设备驱动模型,drm驱动依赖的不再是总线,而是依赖一个父设备,所以注册drm驱动时,需要指定父设备。drm驱动和drm设备的绑定是显示的。
先分配一个drm设备结构体,第一个参数为drm驱动结构体,第二参数为父设备:
struct drm_device *drm_dev_alloc(struct drm_driver *driver,struct device *parent)
然后注册,第一个参数为设备地址,第二个参数为是否执行驱动的load函数,一般填0,即不执行:
int drm_dev_register(struct drm_device *dev, unsigned long flags)
对于drm设备的注销:
void drm_dev_unregister(struct drm_device *dev) // 用于注销drm驱动
void drm_dev_put(struct drm_device *dev) // 用于减少drm设备的引用计数
对于支持热插拔的drm驱动而言,应该使用如下函数:
drm_dev_unplug(struct drm_device *dev)
下面是一个简单的例子,说明如何注册drm驱动:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_gem.h>static struct drm_device *drm_dummy_dev;static void dummy_release(struct device *dev)
{}static struct device dummy_dev = {.init_name = "dummy",.release = dummy_release,
};static struct drm_driver drm_dummy_driver = {.name = "drm-test",.desc = "drm dummy test",.date = "20250409",.major = 1,.minor = 0,
};static int __init drm_test_init(void)
{int ret;ret = device_register(&dummy_dev);if(ret < 0){printk(KERN_ERR "device register error!\n");return ret;}drm_dummy_dev = drm_dev_alloc(&drm_dummy_driver, &dummy_dev);ret = drm_dev_register(drm_dummy_dev, 0);return ret;
}static void __exit drm_test_exit(void)
{drm_dev_unregister(drm_dummy_dev);drm_dev_put(drm_dummy_dev);device_unregister(&dummy_dev);
}module_init(drm_test_init);
module_exit(drm_test_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("qq.com");
MODULE_VERSION("0.1");
MODULE_DESCRIPTION("drm dummy test");
编译成ko文件,加载后,会在/dev/dri/下出现cardX设备节点:
3.4、DRM模式设置
我们知道drm驱动包括KMS和GEM两个部分,其中KMS就是内核模式设置,也是最重要的部分。内核模式就是内核用来显示图像的一种方式,KMS由多个组件构成,包括frame buffer、plane、CRTC、encoder、connector、vblank、property。下面将通过代码来体会这些组件在drm驱动中的作用。
现在我们基于上一个示例程序,实现drm_driver的fops,这些open,release等操作都是由drm子系统实现的。其中drm_ioctl函数实现了应用程序对drm驱动的信息获取,例如版本信息、总线ID、驱动支持的特性等等。但目前这些操作和硬件无关,但应用程序需要根据这些信息来完成相关的设置。
static const struct file_operations drm_fops = {.owner = THIS_MODULE,.open = drm_open,.release= drm_release,.unlocked_ioctl = drm_ioctl,.poll = drm_poll,.read = drm_read,
};
完整示例代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_gem.h>static struct drm_device *drm_dummy_dev;static void dummy_release(struct device *dev)
{}static struct device dummy_dev = {.init_name = "dummy",.release = dummy_release,
};static const struct file_operations drm_fops = {.owner = THIS_MODULE,.open = drm_open,.release= drm_release,.unlocked_ioctl = drm_ioctl,.poll = drm_poll,.read = drm_read,
};static struct drm_driver drm_dummy_driver = {.fops = &drm_fops,.name = "drm-test",.desc = "drm dummy test",.date = "20250409",.major = 1,.minor = 0,
};static int __init drm_test_init(void)
{int ret;ret = device_register(&dummy_dev);if(ret < 0){printk(KERN_ERR "device register error!\n");return ret;}drm_dummy_dev = drm_dev_alloc(&drm_dummy_driver, &dummy_dev);ret = drm_dev_register(drm_dummy_dev, 0);return ret;
}drm_openstatic void __exit drm_test_exit(void)
{drm_dev_unregister(drm_dummy_dev);drm_dev_put(drm_dummy_dev);device_unregister(&dummy_dev);
}module_init(drm_test_init);
module_exit(drm_test_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("qq.com");
MODULE_VERSION("0.1");
MODULE_DESCRIPTION("drm dummy test");
但我们目前还没有实现模式设置要做的事情。实现模式设置实际就是实现各个组件,这些组件包括frame buffer、plane、CRTC、encoder、connector、vblank、property。设置之前,先初始化设置模式结构体,即drm_device中的mode_config字段。
/*** struct drm_mode_config - Mode configuration control structure* @min_width: minimum fb pixel width on this device* @min_height: minimum fb pixel height on this device* @max_width: maximum fb pixel width on this device* @max_height: maximum fb pixel height on this device* @funcs: core driver provided mode setting functions* @fb_base: base address of the framebuffer* @poll_enabled: track polling support for this device* @poll_running: track polling status for this device* @delayed_event: track delayed poll uevent deliver for this device* @output_poll_work: delayed work for polling in process context* @preferred_depth: preferred RBG pixel depth, used by fb helpers* @prefer_shadow: hint to userspace to prefer shadow-fb rendering* @cursor_width: hint to userspace for max cursor width* @cursor_height: hint to userspace for max cursor height* @helper_private: mid-layer private data** Core mode resource tracking structure. All CRTC, encoders, and connectors* enumerated by the driver are added here, as are global properties. Some* global restrictions are also here, e.g. dimension restrictions.*/
struct drm_mode_config {...
}
该结构体实在太膨大了。主要是一些关于drm驱动中用到的东西,例如各种锁、各种属性、各种模式设置的操作结构体。可以通过一个函数来初始化:
int drmm_mode_config_init(struct drm_device *dev)
调用该函数后,还需要手动初始化mode_config的width、height、funcs、async_page_flio,如下所示:
其中min_width设置显示的最小宽度,其它三个也是同一个意思。funcs设置mode_config的操作集合:
其中drm_gem_fb_create用来创建frame buffer,其他两个为额外的检查和提交函数。最后async_page_flip=false表示不支持异步plane。初始化完毕后,还需要初始化plane,crtc,encoder,connector组件。
3.4.1、plane初始化
plane表示图层,即每个显示设备至少有一个图层,每个图层都有自己的frame buffer,图层和frame buffer配合就可以实现图像的保存与处理,drm的plane结构体如下:
struct drm_plane {...
}
drm_plane结构体也很膨大,我们看其中比较重要的5个字段:
分别是crtc指针、fb指针、funcs指针、funcs_helper指针,保存这四个指针是因为图层位于fb和crtc之间,硬件图层处理的数据来自frame buffer中的图像数据,而处理完毕后的数据需要传送给crtc进行信号编码。
初始化drm_plane结构体的函数如下:
int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,uint32_t possible_crtcs,const struct drm_plane_funcs *funcs,const uint32_t *formats, unsigned int format_count,const uint64_t *format_modifiers,enum drm_plane_type type,const char *name, ...)
例如下面定义了一个图层操作结构体,然后使用drm_universal_plane_init对其初始化:
上面的plane_funcs为图层的操作集合,假设此处我们没有物理图层,因此我们可以使用drm提供的软件图层来实现。上面的funcs函数集合实现了图层的基本操作,但这些操作都是通用的,即与硬件无关的,实际中很多显示设备的图层操作都不一样,因此,DRM将这些不一样的操作统一归纳为helper函数,这也就是funcs_helper函数集合的存在原因。例如我们现在的显示器是一块液晶屏,其驱动是ST7789,这块液晶屏使用SPI总线通信,很显然没有硬件图层,因为我们必须实现helper函数来完成图层的图像填充操作。例如下面这段代码:
上面我们定义了一个plane_helper_funcs结构体,其初始化了prepare_fb,该回调函数实现了一个简单的GEM函数,即用于为FB分配内存的接口,然后是初始化atomic_check刷图前的检测工作,返回0表示准备就绪,返回非0表示存在错误,这里我们实现了一个简单的drm_plane_atomic_check函数来对传入的图像参数进行检测,首先获取当前plane的参数,参数保存在state中,然后获取当前crtc的参数,参数也保存在state中,使用drm_atomic_helper_check_plane_state函数对这两个传入的参数进行验证,如果plane和crtc的参数符合,则返回0,否则返回非0。atomic_update回调函数用来初始化刷图函数,该函数用来将plane(图层)上的数据刷新到液晶屏中,由于作者这里使用的是带控制器的液晶屏,内部有控制器和显存,没有硬件CRTC,因此作者这里直接将plane中的数据刷新到LCD上。需要注意的是,刷图的过程中,需要区分pix的像素格式,这里我们支持两种像素格式,即DRM_FORMAT_XRGB8888和DRM_FORMAT_RGB565,对于大部分的视频文件而言,其像素为XRGB8888格式,因此要想支持视频播放,则必须支持DRM_FORMAT_XRGB8888格式,对于一般的虚拟终端而言,其像素格式大多为DRM_FORMAT_RGB565,因此这里也需要对其进行支持。最后,我们需要调用drm_plane_helper_add函数来初始化plane中helper_private字段,该函数如下:
drm_plane_helper_add(primary, &plane_helper_funcs);
上面的代码是对于没有硬件CRTC而言的,直接将图像数据发送到显示器上,而对于有CRTC的SoC而言,我们就需要将数据传送给CRTC的输入接口中,实际上也是一样的,判断图像数据格式,然后将其搬运到CRTC中。对于有CRTC硬件情况下,其操作会更加简单,我们只需要指定数据位置,然后设置寄存器,触发CRTC进行刷新图形即可。有些硬件甚至只需要在开始配置好寄存器后,无需进行软件操作,硬件会自动完成将plane中的数据刷新到CRTC中。
3.4.2、crtc初始化
drm中的crtc结构体如下:
其中primary用来保存drm的主plane结构体,cursor保存drm的光标plane结构体,funcs保存着crtc的操作集合,helper_private保存着crtc的helper操作集合。crtc的初始化,使用如下函数即可初始化:
int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,struct drm_plane *primary,struct drm_plane *cursor,const struct drm_crtc_funcs *funcs,const char *name, ...)
如下图所示,我们定义一个crtc结构体,然后使用drm_crtc_init_with_planes对齐初始化:
上面的代码中funcs结构体与硬件有关,其中enable_vblank为场消隐使能,disable_vblank为场消隐关闭,如果有CRTC硬件,则应该实现各自的功能,这里如果没有硬件,则不需要做消隐处理。我们再来看helper函数集合,如下,我们定义了一个helper函数集合:
crtc_helper_funcs结构体中初始化了crtc_helper_mode_valid字段,该字段用来设置CRTC的显示模式,例如上面我们每次设置CRTC模式都直接将我们定义的mylcd_mode赋值给CRTC的mode,即调用drm_crtc_helper_mode_valid_fixed函数即可。
上面的mylcd_mode结构体定义了其模式为:屏幕宽度像素为240,高度像素为320,屏幕宽度尺寸为37mm,屏幕高度尺寸为49mm。
atomic_check用来完成CRTC参数的检测,和plane一样,首先获取CRTC的参数,参数保存在state中,然后调用相关的API来检测参数是否合法。atomic_enable用来开启显示前的工作,即使能CRTC刷图时序,atomic_disable用来关闭显示,即失能CRTC刷图时序。作者这里由于使用的是带有控制器的液晶屏,没有使用SoC中显示控制器,因此这里enable和disable回调函数对应着屏幕的初始化工作以及退出工作。
我们为crtc添加helper函数集合只需要使用如下函数即可:
static inline void drm_crtc_helper_add(crtc, &crtc_helper_funcs);
该函数会将crtc中的helper_funcs字段初始化为crtc_helper_funcs,代码如下:
3.4.3、encoder初始化
略
3.4.4、connect初始化
略
4、示例说明
不管何种drm驱动,核心都离不开plane、crtc、encoder、connector。
以一个分辨率为240*240,控制芯片为st7789v的液晶显示屏为例,讲解如何开发一个drm驱动。示例程序在:https://gitee.com/li-shan-asked/linux-advanced-development-code/tree/master/part7/drm-st7789-6.1.37
这里的st7789v就是一个显示控制芯片,可以类比为现代SoC里的显示控制模块。所以很多drm驱动都由原厂bsp工程师实现。
5、DRM Simple Display框架
DRM的KMS核心是四个结构体,即p1ane、crtc、encoder、connector。这四个结构体对应着四个显示组件,在软件层面是必须存在的,但硬件不一定存在。既然软件层面是一定存在的,那就可以将这四个结构体封蔽为一个类,这便是DRM的pipe类:
struct drm_simple_display_pipe {struct drm_crtc crtc;struct drm_plane plane;struct drm_encoder encoder;struct drm_connector *connector;const struct drm_simple_display_pipe_funcs *funcs;
};
将上面的示例程序使用drm simple display框架改进后,可以参考:https://gitee.com/li-shan-asked/linux-advanced-development-code/tree/master/part7/drm-st7789-5.16.17
6、DRM热插拔
在connector组件中实现热插拔检测。有两种检测方式,中断和POLL。中断的话,是申请了一个线程化中断,在顶半步的下半部的线程函数里实现热插拔检测和事件发生。POLL就是初始化了延时工作队列,每10s轮询热插拔引脚的状态。
实际的热插拔应该具实际情况来完善。比如在每次插上屏幕后都应该初始化屏幕。在用户态结合udev规则完成其它业务。
7、DRM中的plane update
以上面的示例程序中的plane_update为例:
static void drm_plane_atomic_update(struct drm_plane *plane,struct drm_atomic_state *state)
{int ret;int idx;struct drm_rect rect;struct drm_plane_state *old_pstate,*plane_state;struct iosys_map map[DRM_FORMAT_MAX_PLANES];struct iosys_map data[DRM_FORMAT_MAX_PLANES];struct drm_display *drm = container_of(plane, struct drm_display, primary);struct iosys_map dst_map = IOSYS_MAP_INIT_VADDR(drm->buffer);plane_state = drm_atomic_get_new_plane_state(state, plane);old_pstate = drm_atomic_get_old_plane_state(state, plane);drm_atomic_helper_damage_merged(old_pstate, plane_state, &rect);if (!drm_dev_enter(plane->dev, &idx))return;ret = drm_gem_fb_begin_cpu_access(plane_state->fb, DMA_FROM_DEVICE);if (ret)return;ret = drm_gem_fb_vmap(plane_state->fb, map, data);if (ret)return;if(plane_state->fb->format->format == DRM_FORMAT_XRGB8888) {drm_fb_xrgb8888_to_rgb565(&dst_map, NULL, data, plane_state->fb, &rect, 1);}else if(plane_state->fb->format->format == DRM_FORMAT_RGB565) {drm_fb_memcpy(&dst_map, NULL, data, plane_state->fb, &rect);}drm_gem_fb_vunmap(plane_state->fb, map);fb_set_win(drm, rect.x1, rect.y1, rect.x2 - 1, rect.y2 - 1);gpiod_set_value(drm->dc, 1); //高电平,发送数据spi_write(drm->spi, drm->buffer, (rect.x2 - rect.x1) * (rect.y2 - rect.y1)*2);drm_dev_exit(idx);
}
上面的代码中,定义了两个drmplane_state,一个是老的plane,一个是新的plane。老的plane记录着图像渲染之前的图像,而新的plane记录着渲染的区域,我们将渲染的区域又称为damage区城。为了得到最终的图形,需要使用drm_atomic_helper_damage_merged函数来合并两个plane区域,需要变化的区域被记录到drm_rect中。因此我们在刷新图片的时候,并不需要将整个plane进行刷新,只需要刷新drnrect部分即可。
8、DRM相关结构
8.1、edid
扩展显示器识别数据。给驱动程序获取显示的硬件相关信息。结构体定义如下:
struct edid {u8 header[8];/* Vendor & product info */u8 mfg_id[2];u8 prod_code[2];u32 serial; /* FIXME: byte order */u8 mfg_week;u8 mfg_year;/* EDID version */u8 version;u8 revision;/* Display info: */u8 input;u8 width_cm;u8 height_cm;u8 gamma;u8 features;/* Color characteristics */u8 red_green_lo;u8 black_white_lo;u8 red_x;u8 red_y;u8 green_x;u8 green_y;u8 blue_x;u8 blue_y;u8 white_x;u8 white_y;/* Est. timings and mfg rsvd timings*/struct est_timings established_timings;/* Standard timings 1-8*/struct std_timing standard_timings[8];/* Detailing timings 1-4 */struct detailed_timing detailed_timings[4];/* Number of 128 byte ext. blocks */u8 extensions;/* Checksum */u8 checksum;
} __attribute__((packed));
8.2、panel
panel并不是drm组件中的必要组件,而是为了方便开发者获取显示器信息。将显示器抽象成了panel,即面板。同时edid的信息交给panel来完成。相信这个大家应该常见,在设备树中常出现,一般一个屏幕对应一个panel,里面主要有具体屏幕的时序。panel结构体如下:
其中funcs为panel的操作集合,来完成获取显示器时序,关闭显示器,开启显示器等操作。
8.3、bridge
比如现在有edp,hdmi,rgb等不同的显示设备。对于soc而言,其只有一个显示控制器,经过encoder的信号不能直接输出到connector,而是需要转换为符合具体显示器格式的信号,即引入了bridge。
bridge并不一定是桥接芯片的抽象,也可能是soc的一部分或者一个通道。
相关文章:
Linux驱动开发进阶(七)- DRM驱动程序设计
文章目录 1、前言2、DRAM(KMS、GEM)2.1、KMS2.2、GEM 3、DRM3.1、驱动结构体3.2、设备结构体3.3、DRM驱动注册3.4、DRM模式设置3.4.1、plane初始化3.4.2、crtc初始化3.4.3、encoder初始化3.4.4、connect初始化 4、示例说明5、DRM Simple Display框架6、DRM热插拔7、DRM中的plan…...
Parasoft C++Test软件单元测试_条件宏和断言宏使用方法的详细介绍
系列文章目录 Parasoft C++Test软件静态分析:操作指南(编码规范、质量度量)、常见问题及处理 Parasoft C++Test软件单元测试:操作指南、实例讲解、常见问题及处理 Parasoft C++Test软件集成测试:操作指南、实例讲解、常见问题及处理 进阶扩展:自动生成静态分析文档、自动…...
vue辅助工具(vue系列二)
目录 第一章、安装周边库1.1)状态管理:Pinia1.2)路由管理:Router1.3)HTTP 客户端:Axios1.4)UI 组件库:Element 第二章、下载Vue插件并安装2.1)安装开发者工具2.1.1&#…...
WPF 五子棋项目文档
WPF 五子棋项目文档 1. 项目概述 本项目是一个使用 Windows Presentation Foundation (WPF) 技术栈和 C# 语言实现的桌面版五子棋(Gomoku)游戏。它遵循 MVVM(Model-View-ViewModel)设计模式,旨在提供一个结构清晰、可…...
UniApp 实现兼容 H5 和小程序的拖拽排序组件
如何使用 UniApp 实现一个兼容 H5 和小程序的 九宫格拖拽排序组件,实现思路和关键步骤。 一、实现目标 支持拖动菜单项改变顺序拖拽过程实时预览移动位置拖拽松开后自动吸附回网格兼容 H5 和小程序平台 二、功能结构拆解以及完整代码 完整代码: <…...
谷歌推出统一安全平台-一个平台实现跨云网端主动防护
👋 今天要给大家带来一个超级棒的消息!谷歌云推出了全新的“谷歌统一安全平台”,感觉我们的网络安全问题有救啦!😄 随着企业基础设施变得越来越复杂,保护它们也变得越来越难。攻击面不断扩大,安…...
众趣科技丨沉浸式 VR 体验,助力酒店民宿数字化营销宣传
随着旅游季的到来,各地的旅游景区开始“摩拳擦掌”推出各种活动,吸引更多游客来此游玩。 自去年以来,冰雪游热度持续上升,尤其是对于满心期待的南方游客来说,哈尔滨仍是冰雪旅游的热门目的地。据美团数据显示ÿ…...
DAY05:【pytorch】图像预处理
1、torchvision 功能:计算视觉工具包 torchvision.transforms:常用的图像预处理方法torchvision.datasets:常用数据集的 dataset 实战,MINIST,CIFAR-10,ImageNet等torchvision.model:常用的模…...
真实企业级K8S故障案例:ETCD集群断电恢复与数据保障实践
背景描述 某跨境电商平台生产环境使用Kubernetes(v1.23.17)管理500微服务。某日机房突发市电中断,UPS未能及时接管导致: 3节点ETCD集群(v3.5.4)全部异常掉电 Control-Plane节点无法启动api-server 业务P…...
rbd块设备的id修改
背景 看到有这个需求,具体碰到什么场景了不太清楚,之前做过rbd的重构的研究,既然能重构,那么修改应该是比重构还要简单一点的,我们具体看下怎么操作 数据结构分析 rbd的元数据信息 [rootlab104 ~]# rbd create tes…...
WP最主题专业的wordpress主题开发
WP最主题(wpzui.com) WP最主题是一个提供高品质WordPress主题的平台。它注重主题的设计和功能,旨在为用户提供美观且实用的主题选择。其主题通常具有良好的用户体验、丰富的自定义选项以及优化的性能,能够满足不同类型的网站搭建…...
HomeAssistant本地化部署结合内网穿透打造跨网络智能家居中枢
文章目录 前言1. 添加镜像源2. 部署HomeAssistant3. HA系统初始化配置4. HA系统添加智能设备4.1 添加已发现的设备4.2 添加HACS插件安装设备 5. 安装cpolar内网穿透5.1 配置HA公网地址 6. 配置固定公网地址 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂…...
# 实时人脸性别与年龄识别:基于OpenCV与深度学习模型的实现
实时人脸性别与年龄识别:基于OpenCV与深度学习模型的实现 在当今数字化时代,计算机视觉技术正以前所未有的速度改变着我们的生活与工作方式。其中,人脸检测与分析作为计算机视觉领域的重要分支,已广泛应用于安防监控、智能交互、…...
SAP-ABAP:SAP的Open SQL和Native SQL详细对比
在SAP ABAP开发中,Open SQL和Native SQL是两种操作数据库的方式,它们的核心区别在于可移植性、功能范围及底层实现机制。以下是详细对比: 1. Open SQL:深入解析 1.1 核心特性 数据库抽象层 Open SQL 由 SAP 内核的 Database Interface (DBI) 转换为目标数据库的 SQL(如 …...
基于大模型构建金融客服的技术调研
OpenAI-SB api接口 https://openai-sb.com/ ChatGPT与Knowledge Graph (知识图谱)分享交流 https://www.bilibili.com/video/BV1bo4y1w72m/?spm_id_from333.337.search-card.all.click&vd_source569ef4f891360f2119ace98abae09f3f 《要研究的方向和准备》 https://ww…...
Python设计模式:命令模式
1. 什么是命令模式? 命令模式是一种行为设计模式,它将请求封装为一个对象,从而使您能够使用不同的请求、队列或日志请求,以及支持可撤销操作。 命令模式的核心思想是将请求的发送者与请求的接收者解耦,使得两者之间的…...
30天学Java第八天——设计模式
装饰器模式 Decorator Pattern 装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许通过动态地添加功能来扩展对象的行为,而不需要修改原有的类。 这种模式通常用于增强对象的功能,与继承相比,使用…...
Spring事务系列 三
Spring事务的传播机制 Spring事务系列 一-CSDN博客 Spring事务系列 二-CSDN博客 文章目录 系列文章目录 目录 文章目录 前言 一、Spring事务的传播机制 Propagation.REQUIRED: Propagation.SUPPORTS: Propagation.MANDATORY: Propagation.REQUIRES_NEW: Propagation.NOT_SUPPO…...
文件上传做题记录
1,[SWPUCTF 2021 新生赛]easyupload2.0 直接上传php 再试一下phtml 用蚁剑连发现连不上 那就只要命令执行了 2,[SWPUCTF 2021 新生赛]easyupload1.0 当然,直接上传一个php是不行的 phtml也不行,看下是不是前端验证,…...
机器学习的监督学习与无监督学习
机器学习主要分为监督学习和无监督学习两大类,两者的核心区别在于数据是否带有标签(Label)。以下是它们的详细对比和说明: 1. 监督学习(Supervised Learning) 定义:通过带有标签的训练数据&…...
C++ 入门三:函数与模板
一、内联函数:编译期嵌入的 “高效函数” 1. 什么是内联函数? 核心特性:在编译阶段,内联函数的函数体会直接嵌入到调用它的代码中,避免了普通函数的调用开销(如压栈、跳转、返回)。语法&#…...
解析券商qmt的优缺点
现在已经对于大QMT进行了一步步的深入了解与学习,也已经开始积木式搭建策略,进行交易了,但是,随时不断的深入,发现的问题也越来越多。下面开始逐一解析: 首页 | 迅投知识库 这是详细的说明。 目前券商给大…...
CSE lesson2 chrony服务器
CSE lesson2 chrony服务器 timedatectl命令 NTP(network time protocal)网络时间协议,时钟服务器同步时间的时候会使用到该协议进行时间同步。 #关闭/开启时间同步服务 [rootlocalhost ~]# timedatectl set-ntp 0/1#设置时间(必须关闭时间同步服务才能…...
时光交响曲:杭州的科技与传统交响
故事背景 故事发生在中国浙江杭州,以现代科技与文化传统的交融为背景,展现了人与自然、历史的深刻联系。在晨曦中的茶园、宁静的运河书屋、科技堤坝等地方,每个场景都充满了生机与活力,展示了科技如何赋予传统文化新的生命&#x…...
【大模型智能体】Agent2Agent协议加上MCP协议也许会成为未来Agent智能体系统的标配
之前在文章《基于Claude MCP协议的智能体落地示例》、《MCP(Model Context Protocol) 大模型智能体第一个开源标准协议》我们已经对MCP协议做了介绍,MCP提供了将大模型连接到不同数据源和工具的标准方式,包括内容仓库、商业工具和开发环境。 以上解决的是…...
opencv(C++)处理图像颜色
文章目录 介绍使用策略设计模式比较颜色实现方案计算两个颜色向量之间的距离1. 简单方法:曼哈顿距离计算(Manhattan Distance)2.使用 OpenCV 的 cv::norm 函数3.使用 OpenCV 的 cv::absdiff 函数错误示例 使用 OpenCV 函数实现颜色检测实现方…...
2025年焊接与热切割作业证考试真题分享
焊接与热切割作业属于特种作业操作证考试,理论知识点专业性强、安全规范要求高,如何高效备考成为关键!【100分题库】焊接与热切割作业理论备考题库紧扣最新考试大纲,帮你系统掌握考点,一次通过考试! 1、下…...
AI 代码生成工具如何突破 Java 单元测试效能天花板?
一、传统单元测试的四大痛点 时间黑洞:根据 JetBrains 调研,Java 开发者平均花费 35% 时间编写测试代码覆盖盲区:手工测试覆盖率普遍低于 60%(Jacoco 全球统计数据)维护困境:业务代码变更导致 38% 的测试用…...
【C++游戏引擎开发】第13篇:光照模型与Phong基础实现
一、Phong模型数学原理 1.1 光照叠加公式 L = k a I a + k d I d max ( 0 , n ⋅ l ) + k s I s max ( 0 , r ⋅ v ) α L = k_a I_a + k_d I_d \max(0, \mathbf{n} \cdot \mathbf{l}) + k_s I_s \max(0, \mathbf{r} \cdot \mathbf{v})^\alpha L=kaIa+kdIdmax(0…...
如何在Android系统上单编ko?
文章目录 一、先了解编译驱动需要什么?二、配置makefile1、在Android系统编译LOG上找到编译器信息(一般都会打印出来)2、基于源MK构造 可独立运行的makefile3)进入docker,在此makefile目录下敲make4)最后根…...
虚拟dom工作原理以及渲染过程
浏览器渲染引擎工作流程都差不多,大致分为5步,创建DOM树——创建StyleRules——创建Render树——布局Layout——绘制Painting 第一步,用HTML分析器,分析HTML元素,构建一颗DOM树(标记化和树构建)。 第二步,用…...
无人机视觉定位,常用相机,及相机提供的数据信息
常用相机类型 单目相机:仅使用一个摄像头进行图像采集,结构简单、成本低。它可以获取无人机前方或下方的二维图像信息,包括物体的形状、颜色、纹理等。双目相机:由两个摄像头组成,模拟人类双眼视觉原理,通…...
A2L文件解析
目录 1 摘要2 A2L文件介绍2.1 A2L文件作用2.2 A2L文件格式详解2.2.1 A2L文件基本结构2.2.2 关键元素与声明2.2.3 完整A2L文件示例 3 总结 1 摘要 A2L文件(也称为ASAP2文件)是ECU开发的核心接口文件,用于标定、测量和诊断的关键配置文件&…...
Ansible:role企业级实战
文章目录 实现 nginx 角色创建task文件创建handler文件准备模板文件创建变量文件在playbook中调用角色 实现 memcached 角色创建相关目录创建相关task任务准备模板文件查看目录结构在playbook中调用角色 实现多角色的选择 实现 nginx 角色 卸载httpd,创建相关目录 a…...
vue2使用vue-echarts
1.先安装echarts npm i echarts 2.安装vue-echarts 安装的时候注意下对应的版本 "echarts": "5.5.0", "vue-echarts": "6.7.3",这是我安装的版本 注意事项: 如果安装之后报错:"export watchEffect …...
多光谱相机:海洋管道漏油(溢油)监测
每年海上溢油和化工管道漏油造成的污染事故和经济损失频发,在生态方面,漏油会带来导致水质恶化、生态系统破坏、食物链受损。在经济方面,会造成渔业损失、旅游业损失、航运业损失。在健康方面,会造成食品安全问题,直接…...
Kaggle-Digit Recognizer-(多分类+卷积神经网络CNN)
Digit Recognizer 题意: 给你每个图片的dataframe类型的数据,让你预测出每个图片可能是多少。 思考: 数据处理 1.首先把数据从dadaframe转换成numpy,数据类型改为float32,并且并且展开为1维的28281的形状…...
jQuery多库共存
在现代Web开发中,项目往往需要集成多种JavaScript库或框架来满足不同的功能需求。然而,当多个库同时使用时,可能会出现命名冲突、功能覆盖等问题。幸运的是,jQuery提供了一些机制来确保其可以与其他库和谐共存。本文将探讨如何实现…...
MCU的USB接口作为 USB CDC串口输出
前言: 如下内容是和Chatgpt的问答对话。询问了Chatgpt 关于 MCU微控制器内部的USB端口作为串口输出是怎么工作的,是否需要在上位机上安装串口驱动程序等,Chatgpt解答的很好。 正文: STM32 使用USB作为串行设备端口,需…...
VCode 的 .S 汇编文件里面的注释不显示绿色
1. 确认文件语言模式 打开 .S 文件后,查看 VS Code 右下角的状态栏,确认当前文件的识别模式(如 Assembly、Plain Text 等)。如果显示为 Plain Text 或其他非汇编模式: 点击状态栏中的语言模式(如 Plain Te…...
【数学建模】(智能优化算法)萤火虫算法(Firefly Algorithm)详解与实现
萤火虫算法(Firefly Algorithm)详解与实现 文章目录 萤火虫算法(Firefly Algorithm)详解与实现前言1. 算法原理2. 算法流程3. Python实现4. 算法特点4.1 优点4.2 缺点 5. 应用领域6. 算法变种7. 总结与展望参考文献 前言 大家好,今天给大家介绍一种有趣且高效的群体…...
链路追踪组件学习
目录 1. 为啥需要链路追踪2. 常见的链路追踪组件3. 使用过的链路追踪组件3.1. Spring Cloud Sleuth3.2. Zipkin3.3. Apache SkyWalking 4. 集成Spring Cloud Sleuth框架4.1. 流程步骤4.2 sleuth工作流程 5. 集成zipKin5.1 添加 Zipkin 相关依赖5.2 安装zipkin服务5.3 配置 Zipk…...
# 基于OpenCV与Dlib的人脸融合技术实现
从仿射变换到人脸融合:基于OpenCV和Dlib的图像处理实践 在图像处理领域,仿射变换和人脸融合是两个非常有趣且实用的技术。仿射变换可以用于图像的几何变换,而人脸融合则可以创造出令人惊叹的视觉效果。本文将通过两个具体的代码示例…...
多光谱相机:水环境监测(水体富营养化、黑臭水体、藻类水华)
随着全球水体污染问题日益严峻,水体富营养化、黑臭水体和藻类水华等生态危机对人类健康和水生系统构成重大威胁。传统监测手段(如人工采样、单点传感器)因效率低、覆盖不足、实时性差等局限,难以满足复杂水环境的动态监管需求。多…...
记录一次nginx访问前端首页,一直显示nginx首页问题(实际是nginx访问页面权限问题)
同一台服务器,nginx配置是server { listen 8081; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location /New_mh_other { alias /home/hqu/data/new_mh_other; try…...
windows下命名管道双端通信
实现功能 1、命名管道双端通信(异步) 2、客户端断线重连 PS:多线程版本 PipeWrapper.h #include <windows.h> #include <string> #include <vector> #include "Utils/ThreadObject.h" #include "Utils/T…...
Linux自行实现的一个Shell(15)
文章目录 前言一、头文件和全局变量头文件全局变量 二、辅助函数获取用户名获取主机名获取当前工作目录获取最后一级目录名生成命令行提示符打印命令行提示符 三、命令处理获取用户输入解析命令行执行外部命令 四、内建命令添加环境变量检查和执行内建命令 五、初始化初始化环境…...
powerDesign 逆向 mysql 生成 物理模型,并用VBS脚本整理comment
学习自:https://www.cnblogs.com/xmyjcs/p/8536233.html 文章目录 Reverse Engineer格式化模型执行 VBS 脚本 Reverse Engineer 下面 DBMS 可以通过 ODBC(Open Database Connectivity,开放数据库连接)连接, 需要自己先…...
跨境全域中台:前端独立站群+后端共享云仓的协同作战体系
在全球化浪潮与互联网技术飞速发展的当下,跨境电商已然成为国际贸易领域中最为活跃的力量。据相关数据显示,过去几年跨境电商的年增长率持续保持高位,越来越多的企业投身于这片充满机遇与挑战的蓝海市场。在竞争日益激烈的跨境电商赛道上&…...
国产芯片解析:乐得瑞LDR6500C 超小封装全能芯片,赋能智能设备未来
LDR6500C是乐得瑞科技针对USB-C标准中的Bridge设备而开发的双USB-C DRP接口PD通信芯片,具备切换Data Role功能,支持最高USB PD 100W 充电,并且针对各大品牌设备的 USB-C 兼容性进行了特别优化,非常适合于 USB Type-C 设备快充转接…...