当前位置: 首页 > news >正文

RK3568平台(显示篇)DRM vop驱动程序分析

一.设备树配置

vopb: vop@ff900000 {compatible = "rockchip,rk3399-vop-big";reg = <0x0 0xff900000 0x0 0x2000>, <0x0 0xff902000 0x0 0x1000>;interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH 0>;assigned-clocks = <&cru ACLK_VOP0>, <&cru HCLK_VOP0>;assigned-clock-rates = <400000000>, <100000000>;clocks = <&cru ACLK_VOP0>, <&cru DCLK_VOP0>, <&cru HCLK_VOP0>;clock-names = "aclk_vop", "dclk_vop", "hclk_vop";iommus = <&vopb_mmu>;power-domains = <&power RK3399_PD_VOPB>;resets = <&cru SRST_A_VOP0>, <&cru SRST_H_VOP0>, <&cru SRST_D_VOP0>;reset-names = "axi", "ahb", "dclk";status = "disabled";vopb_out: port {#address-cells = <1>;#size-cells = <0>;vopb_out_edp: endpoint@0 {reg = <0>;remote-endpoint = <&edp_in_vopb>;};vopb_out_mipi: endpoint@1 {reg = <1>;remote-endpoint = <&mipi_in_vopb>;};vopb_out_hdmi: endpoint@2 {reg = <2>;remote-endpoint = <&hdmi_in_vopb>;};vopb_out_mipi1: endpoint@3 {reg = <3>;remote-endpoint = <&mipi1_in_vopb>;};vopb_out_dp: endpoint@4 {reg = <4>;remote-endpoint = <&dp_in_vopb>;};};
};

vop具体实现文件:

drivers/gpu/drm/rockchip/rockchip_drm_vop.c;
drivers/gpu/drm/rockchip/rockchip_vop_reg.c;
drivers/gpu/drm/rockchip/rockchip_drm_vop.h;
drivers/gpu/drm/rockchip/rockchip_vop_reg.h;

二.驱动分析

const struct component_ops vop_component_ops = {.bind = vop_bind,.unbind = vop_unbind,
};

vop的驱动程序主要是从vop_bind开始,其定义在:drivers/gpu/drm/rockchip/rockchip_drm_vop.c

vop_bind函数涉及到对vop设备节点的解析,咱们以vopb设备节点为例进行分析;

static int vop_bind(struct device *dev, struct device *master, void *data)
{// 1. 获取platform_devicestruct platform_device *pdev = to_platform_device(dev);const struct vop_data *vop_data;struct drm_device *drm_dev = data;struct vop *vop;struct resource *res;int ret, irq;// 2. 得到rk3399_vop_bigvop_data = of_device_get_match_data(dev);if (!vop_data)return -ENODEV;/* Allocate vop struct and its vop_win array,初始化vop win */vop = devm_kzalloc(dev, struct_size(vop, win, vop_data->win_size),GFP_KERNEL);if (!vop)return -ENOMEM;vop->dev = dev;vop->data = vop_data;vop->drm_dev = drm_dev;// 设置驱动数据为vopdev_set_drvdata(dev, vop);// 3. 初始化vop winvop_win_init(vop);// 4. 获取第一个内存资源,即<0x0 0xff900000 0x0 0x2000>; VOP_BIG相关寄存器基地址res = platform_get_resource(pdev, IORESOURCE_MEM, 0);// 将VOP_BIG相关寄存器起始物理地址映射到虚拟地址,并返回虚拟地址vop->regs = devm_ioremap_resource(dev, res);if (IS_ERR(vop->regs))return PTR_ERR(vop->regs);vop->len = resource_size(res);// 5. 获取第二个内存资源,即<0x0 0xff902000 0x0 0x1000>; LUT相关寄存器基地址res = platform_get_resource(pdev, IORESOURCE_MEM, 1);if (res) {if (vop_data->lut_size != 1024 && vop_data->lut_size != 256) {DRM_DEV_ERROR(dev, "unsupported gamma LUT size %d\n", vop_data->lut_size);return -EINVAL;}// LUT相关寄存器起始物理地址映射到虚拟地址,并返回虚拟地址vop->lut_regs = devm_ioremap_resource(dev, res);if (IS_ERR(vop->lut_regs))return PTR_ERR(vop->lut_regs);}vop->regsbak = devm_kzalloc(dev, vop->len, GFP_KERNEL);if (!vop->regsbak)return -ENOMEM;// 6. 获取第1个IRQ编号 interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH 0>;irq = platform_get_irq(pdev, 0);if (irq < 0) {DRM_DEV_ERROR(dev, "cannot find irq for vop\n");return irq;}vop->irq = (unsigned int)irq;// 初始化自旋锁和互斥锁spin_lock_init(&vop->reg_lock);spin_lock_init(&vop->irq_lock);mutex_init(&vop->vop_lock);// 7. 创建crtc对象,并和plane关联在一起ret = vop_create_crtc(vop);if (ret)return ret;// 电源管理相关pm_runtime_enable(&pdev->dev);// 8. vop初始化ret = vop_initial(vop);if (ret < 0) {DRM_DEV_ERROR(&pdev->dev,"cannot initial vop dev - err %d\n", ret);goto err_disable_pm_runtime;}// 9. 申请中断,中断处理函数设置为vop_isrret = devm_request_irq(dev, vop->irq, vop_isr,IRQF_SHARED, dev_name(dev), vop);if (ret)goto err_disable_pm_runtime;// 未设置if (vop->data->feature & VOP_FEATURE_INTERNAL_RGB) {vop->rgb = rockchip_rgb_init(dev, &vop->crtc, vop->drm_dev);if (IS_ERR(vop->rgb)) {ret = PTR_ERR(vop->rgb);goto err_disable_pm_runtime;}}// 10. dma初始化rockchip_drm_dma_init_device(drm_dev, dev);return 0;err_disable_pm_runtime:pm_runtime_disable(&pdev->dev);vop_destroy_crtc(vop);return ret;
}

这里我们以设备节点vopb为例,对这段代码进行分析,主要流程如下:

(1) 调用to_platform_device获取platform device,设备节点为vopb

(2) 调用of_device_get_match_data获取与特定设备匹配的数据,这里获取到的数据为rk3399_vop_big;

(3) 调用vop_win_init初始化vop win

(4) 首先调用platform_get_resource(pdev, IORESOURCE_MEM, 0)获取第一个内存资源,即:

<0x0 0xff900000 0x0 0x2000>

0xff900000VOP_BIG相关寄存器基地址;

接着调用devm_ioremap_resource(dev, res)VOP_BIG相关寄存器起始物理地址映射到虚拟地址,并返回虚拟地址;

(5) 调用platform_get_resource(pdev, IORESOURCE_MEM, 1)获取第二个内存资源,即:

<0x0 0xff902000 0x0 0x1000>

0xff902000LUT相关寄存器基地址;

接着调用devm_ioremap_resource(dev, res)LUT相关寄存器起始物理地址映射到虚拟地址,并返回虚拟地址;

(6) 调用platform_get_irq(pdev, 0) 获取第1个IRQ编号:

 interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH 0>

(7) 调用vop_create_crtc(vop)初始化crtc对象,并和plane关联在一起;

(8) 调用vop_initial(vop)进行vop初始化;

(9) 调用devm_request_irq(dev, vop->irq, vop_isr, IRQF_SHARED, dev_name(dev), vop)申请中断,中断处理函数设置为vop_isr

(10) 调用rockchip_drm_dma_init_device(drm_dev, dev)进行dma相关的初始化工作;

vop_bind函数里面比较重要的几个函数:

vo_win_init用于初始化vop win,那么什么是vop win,指定就是vop图层。

vop_create_crtc用于初始化crtc对象,crtcFramebuffer中读取待显示的图像。

vop_initial用户vop初始化。

vo_win_init:初始化vop win。

/** Initialize the vop->win array elements.*/
static void vop_win_init(struct vop *vop)
{const struct vop_data *vop_data = vop->data;unsigned int i;// 遍历每一个图层for (i = 0; i < vop_data->win_size; i++) {// 获取第i个vop winstruct vop_win *vop_win = &vop->win[i];// 获取第i个vop win配置相关寄存器信息const struct vop_win_data *win_data = &vop_data->win[i];// 初始化成员vop_win->data = win_data;vop_win->vop = vop;// rk3399定义了win_yuv2yuv,因此初始化yuv2yuv_dataif (vop_data->win_yuv2yuv)vop_win->yuv2yuv_data = &vop_data->win_yuv2yuv[i];}
}

vop_create_crtc:用于初始化crtc对象。

static int vop_create_crtc(struct vop *vop)
{// 获取vop dataconst struct vop_data *vop_data = vop->data;// 获取device,对应的设备节点为vopbstruct device *dev = vop->dev;// 获取drm devicestruct drm_device *drm_dev = vop->drm_dev;struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp;// 获取drm crtcstruct drm_crtc *crtc = &vop->crtc;struct device_node *port;int ret;int i;/** Create drm_plane for primary and cursor planes first, since we need* to pass them to drm_crtc_init_with_planes, which sets the* "possible_crtcs" to the newly initialized crtc.         * 1. 遍历每一个vop win,每个vop win内部包含一个drm_plane,对类型为primary和cursor plane* 进行初始化*/for (i = 0; i < vop_data->win_size; i++) {// 获取第i个vop winstruct vop_win *vop_win = &vop->win[i];// 获取第i个vop win dataconst struct vop_win_data *win_data = vop_win->data;// 只处理primary和cursor planeif (win_data->type != DRM_PLANE_TYPE_PRIMARY &&win_data->type != DRM_PLANE_TYPE_CURSOR)continue;// 进行plane的初始化,其中funcs被设置为vop_plane_funcsret = drm_universal_plane_init(vop->drm_dev,    // drm设备&vop_win->base,  // 要初始化的plane对象0,  // 可能的CRTCs的位掩码&vop_plane_funcs,  // plane的控制函数集合win_data->phy->data_formats,  // 支持的格式数组(DRM_FORMAT_*)win_data->phy->nformats,   // formats数组的长度win_data->phy->format_modifiers, win_data->type, // plane的类型NULL);if (ret) {DRM_DEV_ERROR(vop->dev, "failed to init plane %d\n",ret);goto err_cleanup_planes;}// 获取当前planeplane = &vop_win->base;// 设置plane的辅助函数helper_private为plane_helper_funcsdrm_plane_helper_add(plane, &plane_helper_funcs);// 如果vop win data配置了x_mir_en/y_mir_en,则调用drm_plane_create_rotation_property为plane附加rotation propertyvop_plane_add_properties(plane, win_data);// 保存primary planeif (plane->type == DRM_PLANE_TYPE_PRIMARY)primary = plane;// 保存cursor planeelse if (plane->type == DRM_PLANE_TYPE_CURSOR)cursor = plane;}// 2. 使用指定的primary and cursor planes初始化的crtc对象,其中crtc回调函数funcs设置为vop_crtc_funcsret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,&vop_crtc_funcs, NULL);if (ret)goto err_cleanup_planes;// 3. 设置crtc的辅助函数helper_private为vop_crtc_helper_funcsdrm_crtc_helper_add(crtc, &vop_crtc_helper_funcs);// 进入if (vop->lut_regs) {drm_mode_crtc_set_gamma_size(crtc, vop_data->lut_size);drm_crtc_enable_color_mgmt(crtc, 0, false, vop_data->lut_size);}/** Create drm_planes for overlay windows with possible_crtcs restricted* to the newly created crtc.* 4. 遍历每一个vop win,每个vop win内部包含一个drm_plane,对类型为overlay plane* 进行初始化*/for (i = 0; i < vop_data->win_size; i++) {// 获取第i个vop winstruct vop_win *vop_win = &vop->win[i];// 获取第i个vop win dataconst struct vop_win_data *win_data = vop_win->data;unsigned long possible_crtcs = drm_crtc_mask(crtc);// 只处理overlay planeif (win_data->type != DRM_PLANE_TYPE_OVERLAY)continue;// 进行plane的初始化,其中funcs被设置为vop_plane_funcsret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,possible_crtcs,&vop_plane_funcs,win_data->phy->data_formats,win_data->phy->nformats,win_data->phy->format_modifiers,win_data->type, NULL);if (ret) {DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n",ret);goto err_cleanup_crtc;}// 设置plane的辅助函数helper_private为plane_helper_funcsdrm_plane_helper_add(&vop_win->base, &plane_helper_funcs);// 如果vop win data配置了x_mir_en/y_mir_en,则调用drm_plane_create_rotation_property为plane附加rotation propertyvop_plane_add_properties(&vop_win->base, win_data);}// 5. 从vopb节点的子节点列表中查找名为port的子节点,也就是vopb_out节点port = of_get_child_by_name(dev->of_node, "port");if (!port) {DRM_DEV_ERROR(vop->dev, "no port node found in %pOF\n",dev->of_node);ret = -ENOENT;goto err_cleanup_crtc;}// 6. 初始化工作队列drm_flip_work_init(&vop->fb_unref_work, "fb_unref",vop_fb_unref_worker);// 初始化完成量,init_completion(&vop->dsp_hold_completion);init_completion(&vop->line_flag_completion);// 设置port节点crtc->port = port;// 7. 对crtc进行自刷新相关的辅助函数初始化ret = drm_self_refresh_helper_init(crtc);if (ret)DRM_DEV_DEBUG_KMS(vop->dev,"Failed to init %s with SR helpers %d, ignoring\n",crtc->name, ret);return 0;err_cleanup_crtc:drm_crtc_cleanup(crtc);
err_cleanup_planes:list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list,head)drm_plane_cleanup(plane);return ret;err_cleanup_crtc:drm_crtc_cleanup(crtc);
err_cleanup_planes:list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list,head)drm_plane_cleanup(plane);return ret;
}

(1) 遍历每一个vop win,每个vop win内部包含一个drm_plane,对类型为primarycursor plane进行初始化;具体是调用drm_universal_plane_init来初始化drm_plane,并且添加plane辅助函数、设置属性等;其中funcs被设置为vop_plane_funcs,定义在drivers/gpu/drm/rockchip/rockchip_drm_vop.c

static const struct drm_plane_funcs vop_plane_funcs = {.update_plane   = drm_atomic_helper_update_plane,.disable_plane  = drm_atomic_helper_disable_plane,.destroy = vop_plane_destroy,.reset = drm_atomic_helper_plane_reset,.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,.format_mod_supported = rockchip_mod_supported,
};

(2) 调用drm_crtc_init_with_planes使用指定的primary and cursor planes初始化的crtc对象,其中crtc回调函数funcs设置为vop_crtc_funcs,定义在drivers/gpu/drm/rockchip/rockchip_drm_vop.c

static const struct drm_crtc_funcs vop_crtc_funcs = {.set_config = drm_atomic_helper_set_config,.page_flip = drm_atomic_helper_page_flip,.destroy = vop_crtc_destroy,.reset = vop_crtc_reset,.atomic_duplicate_state = vop_crtc_duplicate_state,.atomic_destroy_state = vop_crtc_destroy_state,.enable_vblank = vop_crtc_enable_vblank,.disable_vblank = vop_crtc_disable_vblank,.set_crc_source = vop_crtc_set_crc_source,.verify_crc_source = vop_crtc_verify_crc_source,
};

(3) 调用drm_crtc_helper_add设置crtc的辅助函数helper_privatevop_crtc_helper_funcs,定义在drivers/gpu/drm/rockchip/rockchip_drm_vop.c

static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {.mode_fixup = vop_crtc_mode_fixup,.atomic_check = vop_crtc_atomic_check,.atomic_begin = vop_crtc_atomic_begin,.atomic_flush = vop_crtc_atomic_flush,.atomic_enable = vop_crtc_atomic_enable,.atomic_disable = vop_crtc_atomic_disable,
};

(4) 遍历每一个vop win,每个vop win内部包含一个drm_plane,对类型为overlay plane进行初始化;

(5) 调用of_get_child_by_namevopb节点的子节点列表中查找名为port的子节点,也就是vopb_out节点;

(6) 调用drm_flip_work_init(&vop->fb_unref_work, "fb_unref",vop_fb_unref_worker)初始化工作队列;

vop_initial

vop_initial用户vop初始化,定义在drivers/gpu/drm/rockchip/rockchip_drm_vop.c

static int vop_initial(struct vop *vop)
{struct reset_control *ahb_rst;int i, ret;// 根据时钟名称hclk_vop获取时钟,设备节点属性clock-names、clocks,指定了名字为hclk_vop对应的时钟为<&cru HCLK_VOP0>vop->hclk = devm_clk_get(vop->dev, "hclk_vop");if (IS_ERR(vop->hclk)) {DRM_DEV_ERROR(vop->dev, "failed to get hclk source\n");return PTR_ERR(vop->hclk);}// 根据时钟名称aclk_vop获取时钟,设备节点属性clock-names、clocks,指定了名字为aclk_vop对应的时钟为<&cru ACLK_VOP0>vop->aclk = devm_clk_get(vop->dev, "aclk_vop");if (IS_ERR(vop->aclk)) {DRM_DEV_ERROR(vop->dev, "failed to get aclk source\n");return PTR_ERR(vop->aclk);}// 根据时钟名称dclk_vop获取时钟,设备节点属性clock-names、clocks,指定了名字为dclk_vop对应的时钟为<&cru DCLK_VOP0>vop->dclk = devm_clk_get(vop->dev, "dclk_vop");if (IS_ERR(vop->dclk)) {DRM_DEV_ERROR(vop->dev, "failed to get dclk source\n");return PTR_ERR(vop->dclk);}// 电源相关,,使能设备的runtime pm功能 暂且忽略ret = pm_runtime_resume_and_get(vop->dev);if (ret < 0) {DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret);return ret;}// dclk时钟准备,使其处于可用状态,但不启用它ret = clk_prepare(vop->dclk);if (ret < 0) {DRM_DEV_ERROR(vop->dev, "failed to prepare dclk\n");goto err_put_pm_runtime;}/* Enable both the hclk and aclk to setup the vop,hclk时钟准备和使能 */ret = clk_prepare_enable(vop->hclk);if (ret < 0) {DRM_DEV_ERROR(vop->dev, "failed to prepare/enable hclk\n");goto err_unprepare_dclk;}// aclk时钟准备和使能ret = clk_prepare_enable(vop->aclk);if (ret < 0) {DRM_DEV_ERROR(vop->dev, "failed to prepare/enable aclk\n");goto err_disable_hclk;}/** do hclk_reset, reset all vop registers. 获取ahb相应的reset句柄*/ahb_rst = devm_reset_control_get(vop->dev, "ahb");if (IS_ERR(ahb_rst)) {DRM_DEV_ERROR(vop->dev, "failed to get ahb reset\n");ret = PTR_ERR(ahb_rst);goto err_disable_aclk;}// 对传入的reset资源进行复位操作reset_control_assert(ahb_rst);// 睡眠,单位为微妙usleep_range(10, 20);// 对传入的reset资源进行解复位操作reset_control_deassert(ahb_rst);// 设置rk3399_vop_big.intr.clear所描述寄存器相应位的值VOP_INTR_SET_TYPE(vop, clear, INTR_MASK, 1);       // 设置rk3399_vop_big.intr.enable所描述寄存器相应位的值VOP_INTR_SET_TYPE(vop, enable, INTR_MASK, 0);// 备份vop相关寄存器的值for (i = 0; i < vop->len; i += sizeof(u32))vop->regsbak[i / 4] = readl_relaxed(vop->regs + i);// 设置rk3399_vop_big.misc.global_regdone_en所描述寄存器相应位的值VOP_REG_SET(vop, misc, global_regdone_en, 1);// 设置rk3399_vop_big.common.dsp_blank所描述寄存器相应位的值VOP_REG_SET(vop, common, dsp_blank, 0);// 遍历每一个vop winfor (i = 0; i < vop->data->win_size; i++) {// 获取第i个vop winstruct vop_win *vop_win = &vop->win[i];// 获取第i个vop win dataconst struct vop_win_data *win = vop_win->data;int channel = i * 2 + 1;// 设置rk3399_vop_big.win[i].phy.channel所描述寄存器相应位的值VOP_WIN_SET(vop, win, channel, (channel + 1) << 4 | channel);// 禁止当前vop winvop_win_disable(vop, vop_win);// 设置rk3399_vop_big.win[i].phy.gate所描述寄存器相应位的值VOP_WIN_SET(vop, win, gate, 1);}// 设置rk3399_vop_big.common.cfg_done所描述寄存器相应位的值,enable reg configvop_cfg_done(vop);/** do dclk_reset, let all config take affect. 获取dclk相应的reset句柄*/vop->dclk_rst = devm_reset_control_get(vop->dev, "dclk");if (IS_ERR(vop->dclk_rst)) {DRM_DEV_ERROR(vop->dev, "failed to get dclk reset\n");ret = PTR_ERR(vop->dclk_rst);goto err_disable_aclk;}// 对传入的reset资源进行复位操作reset_control_assert(vop->dclk_rst);// 睡眠,单位为微妙usleep_range(10, 20);// 对传入的reset资源进行解复位操作reset_control_deassert(vop->dclk_rst);// 禁止时钟hclkclk_disable(vop->hclk);// 禁止时钟aclkclk_disable(vop->aclk);// 使能标志位vop->is_enabled = false;pm_runtime_put_sync(vop->dev);return 0;err_disable_aclk:clk_disable_unprepare(vop->aclk);
err_disable_hclk:clk_disable_unprepare(vop->hclk);
err_unprepare_dclk:clk_unprepare(vop->dclk);
err_put_pm_runtime:pm_runtime_put_sync(vop->dev);return ret;
}

该函数主要做了两件事件:

  • vop相关时钟初始化:aclk_vopdclk_vophclk_vop
  • vop相关寄存器配置,底层通过vop_reg_set设置配置寄存器值;

相关文章:

RK3568平台(显示篇)DRM vop驱动程序分析

一.设备树配置 vopb: vopff900000 {compatible "rockchip,rk3399-vop-big";reg <0x0 0xff900000 0x0 0x2000>, <0x0 0xff902000 0x0 0x1000>;interrupts <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH 0>;assigned-clocks <&cru ACLK_VOP0>, &…...

vue3 动态加载组件

//模版调用 <component :is"geticon(item.icon)" />//引入 import { ref, onMounted, markRaw, defineAsyncComponent } from vue;//异步添加icon图标组建 function geticon(params) {const modules import.meta.glob(../components/icons/*.vue);const link …...

Latex on overleaf入门语法

Latex on overleaf入门语法 前言基本结构序言 简单的格式化命令添加注释&#xff1a;%加粗、斜体、下划线有序列表、无序列表 添加图片图片的标题、标签和引用 添加表格一个简单的表格为表格添加边框标题、标签、引用 数学表达式基本的数学命令 基本格式摘要段落、新行章节、分…...

使用Echarts来实现数据可视化

目录 一.什么是ECharts? 二.如何使用Springboot来从后端给Echarts返回响应的数据&#xff1f; eg:折线图&#xff1a; ①Controller层&#xff1a; ②service层&#xff1a; 一.什么是ECharts? ECharts是一款基于JavaScript的数据可视化图标库&#xff0c;提供直观&…...

一文搞懂GIT

文章目录 1. GiT概述1.1 GIT概述1.2 GIT安装 2. GIT组成3. GIT基本命令3.1 基本命令3.2 分支操作3.3 远程操作3.4 标签操作3.5 其他命令 1. GiT概述 1.1 GIT概述 Git 是一个分布式版本控制系统&#xff0c;被广泛应用于软件开发中。 Git 具有众多优点&#xff0c;比如&#…...

jQuery入门(四)案例

jQuery 操作入门案例 一、复选框案例 功能: 列表的全选&#xff0c;反选&#xff0c;全不选功能实现。 实现步骤和分析&#xff1a; - 全选 1. 为全选按钮绑定单击事件。 2. 获取所有的商品项复选框元素&#xff0c;为其添加 checked 属性&#xff0c;属性值为 true。 -…...

揭秘MITM攻击:原理、手法与防范措施

中间人攻击发生时&#xff0c;攻击者会在通讯两端之间插入自己&#xff0c;成为通信链路的一部分。攻击者可以拦截、查看、修改甚至重新定向受害者之间的通信数据&#xff0c;而不被双方察觉。这种攻击常见于未加密的Wi-Fi网络、不安全的HTTP连接或者通过社会工程学手段诱导受害…...

【YOLOv8】一文全解+亮点介绍+训练教程+独家魔改优化技巧

前言 Hello&#xff0c;大家好&#xff0c;我是cv君&#xff0c;最近开始在空闲之余&#xff0c;经常更新文章啦&#xff01;除目标检测、分类、分隔、姿态估计等任务外&#xff0c;还会涵盖图像增强领域&#xff0c;如超分辨率、画质增强、降噪、夜视增强、去雾去雨、ISP、海…...

创建mvp ubo(uniform buffer object)

创建过程&#xff1a; 创建一个uniform buffer查找buffer memory requirements分配、绑定buffer memorymap buffer memory拷贝mvp data to buffer memoryunmap buffer memory 示例代码&#xff1a; glm::mat4 projection glm::perspective(glm::radians(45.0f), 1.0f, 0.1f…...

1.GPIO

理论说明 输入 上拉输入&#xff1a;拉高电平 下拉输入&#xff1a;拉低电平 浮空输入&#xff1a;不拉高也不拉低电平 输出 开漏输出&#xff1a;不能输出高电平&#xff08;P-MOS不可用&#xff0c;则只能低电平&#xff09; 推挽输出&#xff1a;可输出高低电平 输出速率…...

C++必修:STL之vector的了解与使用

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 贝蒂的主页&#xff1a;Betty’s blog 1. C/C中的数组 1.1. C语言中的数组 在 C 语言中&#xff0c;数组是一组相同类型…...

【MySQL】索引 【上】 {没有索引的查询/磁盘/mysql与磁盘IO/初识索引}

文章目录 1.没有索引存在的问题2. 认识磁盘MySQL与存储MySQL与磁盘交互基本单位建立共识图解IO认识索引 在关系数据库中&#xff0c;索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构&#xff0c;它是某个表中一列或若干列值的集合和相应的指向表中物…...

GO goroutine状态流转

Gidle -> Grunnable newproc获取新的goroutine&#xff0c;并放置到P运行队列中 这也是go关键字之后实际编译调用的方法 func newproc(fn *funcval) {// 获取当前正在运行中的goroutinegp : getg()// 获取调用者的程序计数器地址&#xff0c;用于调试和跟踪pc : getcallerp…...

DLMS/COSEM中的信息安全:DLMS/COSEM安全概念(上)

DLMS/COSEM中的信息安全描述并规定: ——DLMS/COSEM安全概念; ——选择加密算法; ——安全密钥; ——使用加密算法进行实体认证、xDLMS APDU保护和COSEM数据保护。 1.综述 DLMS/COSEM服务器的资源(COSEM对象属性和方法)可以由在应用连接内的DLMS/COSEM客户机访问。 在AA…...

C语言第九天笔记

数组的概念 什 么是数组 数组是 相同类型&#xff0c; 有序数据的集合。 数 组的特征 数组中的数据被称为数组的 元素&#xff0c;是同构的 数组中的元素存放在内存空间里 (char player_name[6]&#xff1a;申请在内存中开辟6块连续的基于char类 型的变量空间) 衍生概念&…...

智慧环卫可视化:科技赋能城市清洁管理

图扑智慧环卫可视化通过实时监控、数据分析和智能调度&#xff0c;提高环卫作业效率&#xff0c;优化资源配置&#xff0c;提升城市清洁水平&#xff0c;实现城市管理的精细化和现代化。...

【力扣】SQL题库练习5

高级查询和连接 1341.电影评分 表&#xff1a;Movies ------------------------ | Column Name | Type | ------------------------ | movie_id | int | | title | varchar | ------------------------ movie_id 是这个表的主键(具有唯一值的列)。 ti…...

永结无间Ⅸ--你不需要LLM Agent

人们将目光锁定在下一个闪亮的事物上。FOMO 是人性的一部分。这也适用于企业。就像数据科学成为每个企业分析功能的热潮一样&#xff0c;Agentic Architecture 是大多数 AI 雷达上的热门目标。 但您是否考虑过您是否真的需要它&#xff1f; 实际情况是&#xff0c;您不需要 A…...

Simulink|基于粒子群算法的永磁同步电机多参数辨识

目录 主要内容 模型研究 结果一览 下载链接 主要内容 仿真程序参考文献《改进粒子群算法的永磁同步电机多参数辨识》&#xff0c;采用粒子群算法与simulink模型结合的方式&#xff0c;对永磁同步电机进行多参数辨识。程序以定子绕组电阻、d轴电感、q轴电感和永磁…...

程序如何自动点击亚马逊商户后台的“邀请评论”按钮

要在亚马逊上自动点击“邀请评论”按钮&#xff0c;可以使用自动化脚本来实现。由于你希望自动化操作&#xff0c;我提供一个示例代码&#xff0c;使用 Selenium WebDriver 来执行这个任务。Selenium 是一个流行的浏览器自动化工具&#xff0c;能够模拟用户操作&#xff0c;例如…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;使用DevEco Studio作为开发工具&#xff0c;采用Java语言实现&#xff0c;包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...