HDMI协议实现彩条静态显示方块移动

news2024/11/15 3:59:28

文章目录

  • 前言
  • 一、硬件原理
    • 1、硬件设计原理图
    • 2、引脚图
  • 二、系统设计
    • 1、系统模块框图
    • 2、RTL视图
    • 3、RGB2DVI 模块框图
    • 4、ALTDDIO_OUT IP 核的信号框图
  • 三、HDMI的行&场时序
    • 1、时序图
    • 2、常见显示器时序参数
  • 四、源码
    • 1、video_driver(显示驱动模块)
    • 2、serializer_10_to_1(并转串模块)
    • 3、dvi_encoder(编码模块)
    • 4、video_display(视频显示模块)
  • 五、效果
  • 六、总结
  • 七、参考资料&完整工程


前言

环境:
1、Quartus18.0
2、vscode
3、板子型号:原子哥开拓者2(EP4CE10F17C8)
4、显示屏
要求:
是驱动开拓者开发板上的 HDMI 接口,在显示器上显示彩条图案。HDMI 接口输出的分辨率为 1280*720,刷新速率为 60hz。


一、硬件原理

1、硬件设计原理图

在这里插入图片描述
在这里插入图片描述

  • 分析:

1、在开拓者2的HDMI中,我们只是支持输出,把输入限制死了。
2、HDMI_HPD 指的是热拔插检测(Hot Plug Detect): 当视频设备与接收设备通过 HDMI 连接时,接收设备将 HPD 置为高电平,通知发送设备。当发送设备检测到 HPD 为低电平时,表明断开连接。加了一个抑制二极管做电压敏感保护。
3、SCL、SDA使用CMOS管用于电平的转换作用: 用于 HDMI 发送端和接收端之间交换一些配置信息,通过 I2C 协议通信。

2、引脚图

1、4、7和3、6、9组成了三对差分信号,用于发送颜色数据。

  • CEC: 工业上常用的一个保留的信号引脚。
  • SCL和SDA: 组成了一组I2C的信号线
  • DDC/CED: 用于告知发送端,我们接收端具体可以接收什么样的数据。
  • 插拔检测: 连接好过后接收端会发送一个拉高的信号,发送端在接收到信号过后会向接收端开始发送数据

二、系统设计

1、系统模块框图

在这里插入图片描述

2、RTL视图

在这里插入图片描述

3、RGB2DVI 模块框图

在这里插入图片描述

这里实现了RGB转DVI模块,主要想提一下这里的ddio实现的并转串。这里我们需要一下子将10bit数字转成串行的,这就需要10倍的像素时钟。这里我们只用了一个 5 倍的时钟频率,这是因为 ALTDDIO_OUT IP核可以实现 DDR 的功能,即它在五倍时钟频率的基础上又实现了双倍数据速率。

4、ALTDDIO_OUT IP 核的信号框图

在这里插入图片描述

这里我们选择的数据为为1比特,但是,其有上升沿以及下降沿都进行相应的发送处理。

  • 端口描述:

datain_h: 在 outclock 时钟上升沿输入的数据,输入数据的位宽为WIDTH;
datain_l: 在 outclock 时钟下降沿输入的数据,输入数据的位宽为 WIDTH;
outclock: 用于寄存数据输出的时钟信号,在 outclock 时钟信号的每个电平输出 DDR 数据到 dataout 端口;
dataout: DDR 输出数据端口,位宽为 WIDTH。dataout 端口应该直接提供顶层设计的输出引脚。

  • 下面是其输出时序图:
    在这里插入图片描述

观察发现,在一个时钟·周期内,其输出了两个比特位,那么我们只需要5倍的时钟速率即可。

三、HDMI的行&场时序

1、时序图

在这里插入图片描述

在行&场同步信号有效信号过来时我们的DE(像素有效信号)还不能立刻拉高,我们需要等到显示后沿结束过后才拉高,开始输出有效数据。然后DE拉低进入显示前沿,待显示前沿过后我们的行场同步信号又开始下一行或帧的传输。

2、常见显示器时序参数

在这里插入图片描述

这里没有完整的,我们实验用的是1024x720。其中的e等于a、b、c、d的总和。同样s等于前四个相加。这个对我们后面写代码是必要的,完整文件在结尾。

四、源码

这里文件比较多,我就放三个主要的就行,结尾我会放完整工程。

1、video_driver(显示驱动模块)

这里主要参照上面的行场时序图进行编程理解。

//在video_driver 模块的驱动下按照工业标准的VGA 显示时序
//输出视频信号、行场同步信号以及视频有效信号。这些信号作为RGB2DVI模块的输入
module video_driver (
    input               pixel_clk             ,
    input               sys_rst_n             ,
    input [23:0]        pixel_data            ,//像素点数据

    //RGB接口
    output              video_hs              ,//行同步信号
    output              video_vs              ,//场同步信号
    output              video_de              ,//数据使能
    output [23:0]       video_rgb             ,//RGB888颜色数据

    output [10:0]       pixel_xpos            ,//像素点横坐标
    output [10:0]       pixel_ypos             //像素点纵坐标
);

//1280*720 分辨率时序参数
parameter H_SYNC = 11'd40,      //行同步
          H_BACK = 11'd220,     //行显示后沿
          H_DISP = 11'd1280,    //行有效数据
          H_FRONT = 11'd110,    //行显示前沿
          H_TOTAL = 11'd1650;   //行扫描周期

parameter V_SYNC = 11'd5,      //场同步
          V_BACK = 11'd20,     //场显示后沿
          V_DISP = 11'd720,    //场有效数据
          V_FRONT = 11'd5,    //场显示前沿
          V_TOTAL = 11'd750;   //场扫描周期

reg [10:0] cnt_h;
reg [10:0] cnt_v;

wire video_en;
wire data_req;

assign video_de = video_en;

assign video_hs = (cnt_h < H_SYNC ) ? 1'b0 : 1'b1;
assign video_vs = (cnt_v < V_SYNC ) ? 1'b0 : 1'b1;

//有效信号输出时序
assign video_en = (((cnt_h >= H_SYNC + H_BACK) && (cnt_h < H_SYNC + H_BACK + H_DISP)) 
                && ((cnt_v >= V_SYNC + V_BACK) && (cnt_v < V_SYNC + V_BACK + V_DISP)))
                ? 1'b1 : 1'b0;
//RGB888数据输出
assign video_rgb = video_en ? pixel_data : 24'd0;

//请求像素点颜色数据输入
assign data_req = (((cnt_h >= H_SYNC + H_BACK - 1'b1) && (cnt_h < H_SYNC + H_BACK + H_DISP - 1'b1)) 
                && ((cnt_v >= V_SYNC + V_BACK) && (cnt_v < V_SYNC + V_BACK + V_DISP)))
                ? 1'b1 : 1'b0;

//像素点坐标
assign pixel_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 11'd0;
assign pixel_ypos = data_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 11'd0;

//行计数器对像素时钟计数
//使用同步复位信号:抗干扰能力强、消除毛刺,同步消耗资源较多、
//需要保持一个时钟周期以上才可以复位、可能带来时钟偏移等问题

//异步复位信号则相反消耗资源少、抗干扰弱、有亚稳态问题

//行计数器对像素时钟进行计数
always @(posedge pixel_clk) begin
    if(!sys_rst_n)
        cnt_h <= 11'd0;
    else if(cnt_h == H_TOTAL - 1'b1)
        cnt_h <= 11'd0;
    else 
        cnt_h <= cnt_h + 1'b1;
end

//场计数器对行计数,一个场内的行计完切换下一帧
always @(posedge pixel_clk) begin
    if(!sys_rst_n)
        cnt_v <= 11'd0;
    else if(cnt_h == H_TOTAL - 1'b1) begin
        if(cnt_v < V_TOTAL - 1'b1)
            cnt_v <= cnt_v + 1'b1;
        else
            cnt_v <= 11'd0;
    end
end

endmodule

2、serializer_10_to_1(并转串模块)

这里通过例化一个PLL实现一个五倍的像素时钟和一个ALTDDIO_OUT IP核,用于并转串的操作。

module serializer_10_to_1(
    input               serial_clk_5x,//输入串行数据时钟
    input    [9:0]      paralell_data,//输入并行数据

    output              serial_data_p,//输出串行差分数据p
    output              serial_data_n //输出串行差分数据N
);

reg [2:0] bit_cnt = 0;
reg [4:0] datain_rise_shift = 0;
reg [4:0] datain_fall_shift = 0;

wire [4:0] datain_rise;
wire [4:0] datain_fall;

//拆分数据
assign datain_rise = {paralell_data[8],paralell_data[6],paralell_data[4],
                      paralell_data[2],paralell_data[0]};

assign datain_fall = {paralell_data[9],paralell_data[7],paralell_data[5],
                      paralell_data[3],paralell_data[1]};

//位计数器
always @(posedge serial_clk_5x) begin
    if(bit_cnt == 3'd4)
        bit_cnt <= 1'b0;
    else
        bit_cnt <= bit_cnt + 1'b1;
end

//移位寄存器,用于并行数据的每一位
always @(posedge serial_clk_5x) begin
    if(bit_cnt == 3'd4)begin
        datain_rise_shift <= datain_rise;
        datain_fall_shift <= datain_fall;
    end
    else begin
        datain_rise_shift <= datain_rise_shift[4:1];
        datain_fall_shift <= datain_fall_shift[4:1];
    end
end

//例化DDIO_OUT IP核,因为我们我们是差分传输,实际上是两条线,需要例化两个IP
ddio_out ddio_out_inst_1(
    .datain_h    (datain_rise_shift[0]),
    .datain_l    (datain_fall_shift[0]),
    .outclock    (serial_clk_5x),
    .dataout     (serial_data_p)
);
ddio_out ddio_out_inst_2(
    .datain_h    (~datain_rise_shift[0]),
    .datain_l    (~datain_fall_shift[0]),
    .outclock    (serial_clk_5x),
    .dataout     (serial_data_n)
);
endmodule

3、dvi_encoder(编码模块)

这里参照那张编码流程图更容易理解。

/***********
下面会用到一个#1,在1ns过后才开始进行赋值
作用:该延迟有效地实现了 1ns clk-to-q 或 rst_n-to-q 延迟,使用波形查看器查看时可以轻松解释。
比如:波形查看器中的小延迟还可以很容易地看到时序逻辑输出的值在时钟边沿之前是什么,
通过将波形查看器光标放在时钟边沿本身,大多数波形查看工具将显示相应的靠近波形显示左侧的信号名称旁边的二进制、十进制或十六进制值。
***********/
`timescale 1ps/1ps

module dvi_encoder(
    input               clkin   ,//像素时钟
    input               rst_n   ,//异步复位同步释放的复位信号、高有效
    input   [7:0]       din     ,//8位数据
    input               c0      ,//控制信号1
    input               c1      ,//控制信号2
    input               de      ,//发送时序

    output reg [9:0]    dout    //编码后的数据
);

reg [3:0] n1d;//1的个数
reg [7:0] din_q;//

//计算1的个数
always @(posedge clkin) begin
    n1d <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];

    din_q <=#1 din;
end

wire  decision1;

assign decision1 = (n1d > 4'h4) || ((n1d == 4'h4) && (din_q[0] == 1'b0));

wire [8:0] q_m;
assign q_m[0] = din_q[0];
assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);
assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);
assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);
assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);
assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);
assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);
assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);
assign q_m[8] = (decision1) ? 1'b0 : 1'b1;

//计算q_m的1的个数用于下一个decision条件
reg [3:0] n1q_m, n0q_m;

always @(posedge clkin) begin
    n1q_m <=#1 q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];
    n0q_m <=#1 4'h8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);
end

//两个控制信号的四种编码情况
parameter CTRLTOKEN0 = 10'b1101010100,
          CTRLTOKEN1 = 10'b0010101011,
          CTRLTOKEN2 = 10'b0101010100,
          CTRLTOKEN3 = 10'b1010101011;

reg [4:0] cnt ;
wire decision2,decision3;

assign decision2 = (cnt == 5'h0) || (n0q_m == n1q_m);
assign decision3 = ((~cnt[4]) && (n1q_m > n0q_m)) || ((cnt[4]) && (n1q_m < n0q_m));

reg de_q, de_reg;
reg c0_q,c1_q;
reg c0_reg,c1_reg;
reg [8:0] q_m_reg;
//应该是延后一个时钟周期用于与前面寄存din_q同步
always @(posedge clkin)begin
    de_q <=#1 de;
    de_reg <=#1 de_q;

    c0_q <=#1 c0;
    c0_reg <=#1 c0_q;
    c1_q <=#1 c1;
    c0_reg <=#1 c1_q;

    q_m_reg <=#1 q_m;
end

always @(posedge clkin or posedge rst_n) begin
    if(rst_n)begin
        dout <= 10'h0;
        cnt <= 5'h0;
    end
    else begin
        if(de_reg) begin//有效数据传输过程
            if(decision2) begin
                dout[9] <=#1 ~q_m_reg[8];
                dout[8] <=#1 q_m_reg[8];
                dout[7:0] <=#1 (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0];

                cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m);
            end
            else begin
                if(decision3)begin
                    dout[9] <=#1 1'b1;
                    dout[8] <=#1 q_m_reg[8];
                    dout[7:0] <=#1 ~q_m_reg[7:0];

                    cnt <=#1 cnt + {q_m_reg[8],1'b0} + (n0q_m - n1q_m);
                end
                else begin
                    dout[9] <=#1 1'b0;
                    dout[8] <=#1 q_m_reg[8];
                    dout[7:0] <=#1 q_m_reg[7:0];

                    cnt <=#1 cnt - {~q_m_reg[8],1'b0} + (n1q_m - n0q_m);
                end
            end
        end
        else begin
            case ({c1_reg , c0_reg})
                2'b00: dout <=#1 CTRLTOKEN0; 
                2'b01: dout <=#1 CTRLTOKEN1;
                2'b10: dout <=#1 CTRLTOKEN2; 
                default: dout <=#1 CTRLTOKEN3;
            endcase

            cnt <=#1 5'h0;
        end
    end
end
endmodule

4、video_display(视频显示模块)

//这个模块是用于生成显示图案的
//这里我们只需要实现彩条的显示,
//其实就是通过x、y的坐标对不同范围的频幕给不同的颜色即可
module video_display(
    input                   pixel_clk       ,//75MHZ
    input                   sys_rst_n       ,
    input [10:0]            pixel_xpos      ,//像素点横坐标
    input [10:0]            pixel_ypos      ,//像素点纵坐标

    output reg [23:0]       pixel_data//像素点数据,传回驱动模块用于在有效数据发送期间进行发送
    
);
/**************
这里的方块动态显示模块实际上就是依据左上角的一个坐标确定整个方块的位置
并给小方块颜色,同样根据左上角坐标点的位置判定是否到达边界。
**************/ 

parameter H_DISP = 11'd1280;//分辨率--行
parameter V_DISP = 11'd720;//分辨率--列

localparam SIDE_W  = 11'd40,//屏幕边框
           BLOCK_W = 11'd40,//方块宽度
           BLUE    = 24'b00000000_00000000_11111111,  //屏幕颜色,蓝色
           WHITE   = 24'b11111111_11111111_11111111,  //背景颜色,白色
           BLACK   = 24'b00000000_00000000_00000000;  //方块颜色, 黑色
//左上角方块初始位置
reg [10:0] block_x = SIDE_W;
reg [10:0] block_y = SIDE_W;

reg [21:0] div_cnt;//分频计数器
reg h_direct;//指示水平移动方向 1:右移,0:左移
reg v_direct;//指示垂直移动方向 1:向下,0:向上

wire move_en;//方块移动使能,100HZ

assign move_en = (div_cnt == 22'd742500) ? 1'b1 : 1'b0;

//计数实现时钟分频
always @(posedge pixel_clk) begin
    if(!sys_rst_n)begin
        div_cnt <= 22'd0;
    end
    else begin
        if(div_cnt < 22'd742500)
            div_cnt <= div_cnt + 1'b1;
        else
            div_cnt <= 22'd0;
    end
end

//判断方块移动到边界,改变移动方向
always @(posedge pixel_clk) begin
    if(!sys_rst_n) begin
        h_direct <= 1'b1;               //初始向右移动
        v_direct <= 1'b1;               //初始竖直向下
    end
    else begin
        //判断水平位置
        if(block_x == SIDE_W - 1'b1) //到达左边界,水平向右
            h_direct <= 1'b1;
        else if(block_x == H_DISP - SIDE_W - BLOCK_W)
            h_direct <= 1'b0;
        else
            h_direct <= h_direct;
        //判断垂直位置
        if(block_y == SIDE_W - 1'b1)
            v_direct <= 1'b1;
        else if(block_y == V_DISP - SIDE_W - BLOCK_W)
            v_direct <= 1'b0;
        else
            v_direct <= v_direct;
    end
end

//根据方块移动方向,改变坐标
always @(posedge pixel_clk) begin
    if(!sys_rst_n)begin
        block_x <= SIDE_W;           //初始化方块坐标
        block_y <= SIDE_W;
    end
    else if(move_en) begin//10ms改变坐标,实现动态效果
        if(h_direct)
           block_x <= block_x + 1'b1;//向右
        else
            block_x <= block_x - 1'b1;

        if(v_direct)
            block_y <= block_y + 1'b1;//向下
        else
            block_y <= block_y - 1'b1;
    end
    else begin
        block_x <= block_x;
        block_y <= block_y;
    end
end

//不同区域绘制不同颜色

always @(posedge pixel_clk) begin
    if(!sys_rst_n)
        pixel_data <= BLACK;
    else begin
         if(  (pixel_xpos < SIDE_W) || (pixel_xpos >= H_DISP - SIDE_W)
           || (pixel_ypos < SIDE_W) || (pixel_ypos >= V_DISP - SIDE_W))
             pixel_data <= BLUE;                 //绘制屏幕边框为蓝色
         else
         if(  (pixel_xpos >= block_x) && (pixel_xpos < block_x + BLOCK_W)
           && (pixel_ypos >= block_y) && (pixel_ypos < block_y + BLOCK_W))
             pixel_data <= BLACK;                //绘制方块为黑色
         else
             pixel_data <= WHITE;                //绘制背景为白色
    end
end
endmodule

五、效果

由于博主离校在外,没有支持HDMI的显示屏,无法对效果进行演示,在经过多方寻找有心无力,还冲动买了一块,后面考虑到用不了多少次就退款了。等我返校有时间了再给大伙试试效果,上面的代码就当理解学习先吧,有条件的可以自己试试效果。


六、总结

说实话这个相对前面的学习好像又上了一个坡,还是有一定的理解难度的。这个项目我拢共看了四便,手敲了一遍代码过后理解更加的深刻,之前只用过VGA做过屏幕的显示,这次又是不一样的体验。这个项目里涉及到有PLL、DDIO、HDMI协议等,我觉得里面我觉得比较巧妙的就是并转串那里,真的太棒了。可惜的就是没有能够进一步去了解到DDIO这个IP,这是我第一次使用,仅仅是配置了下。下一步准备看看如何显示动态的画面。

七、参考资料&完整工程

1、以上资料均来自正点原子的教学视频或开拓者2开发教程:原子官方
2、完整的显示器时序参数文件:显示器时序参数文件 提取码:hzjj
2、源码:https://github.com/no1jiangjiang/HDMI

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

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

相关文章

微信公众号接入AI ChatGPT机器人bot的详细教程

微信公众号可以与客服系统进行对接&#xff0c;实现智能自动回复或者人工回复的公众号客服系统实现对接的前提是需要公众号为认证的服务号&#xff0c;实现的功能概况&#xff1a;公众号客服接口对接&#xff0c;公众号模板消息提醒&#xff0c;网页授权获取到微信的昵称头像&a…

Python实现GA遗传算法优化支持向量机回归模型(SVR算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;最早是由美国的 John holland于20世…

tinkerCAD案例:24. Ruler - Measuring Lengths 标尺 -量勺

tinkerCAD案例&#xff1a;24. Ruler - Measuring Lengths 标尺 - 测量长度 Project Overview: 项目概况&#xff1a; A machine shop, where any idea can become a reality, can cost millions and million of dollars. Still, the most important tool in the shop is the…

基于Open3D的点云处理12-体素化

体素化Voxelization 体素&#xff08;voxel&#xff09;是像素&#xff08;pixel&#xff09;、体积&#xff08;volume&#xff09;和元素&#xff08;element&#xff09;的组合词&#xff0c;相当于3D空间中的像素; 体素化是通过用空间均匀大小的体素网格(voxel grid)来模…

openpnp - ReferenceStripFeeder 改版零件

文章目录 openpnp - ReferenceStripFeeder 改版零件概述笔记整体效果散料飞达主体磁铁仓盖板飞达编带中间压条飞达编带两边压条装配体用的8mm编带模型END openpnp - ReferenceStripFeeder 改版零件 概述 官方推荐了ReferenceStripFeeder的模型smd_strip_feeders_mod_tray.zip…

【C++学习】STL容器——vector

目录 一、vector的介绍及使用 1.1 vector的介绍 1.2 vector的使用 1.2.1 vector的定义 1.2.2 vector iterator 的使用 1.2.3 vector 空间增长问题 1.2.4 vector 增删查改 1.2.5 vector 迭代器失效问题&#xff08;重点&#xff09; 二、vector深度剖析及模拟实现 ​编辑…

【JavaSE】数组的定义与使用

【本节目标】 1. 理解数组基本概念 2. 掌握数组的基本用法 3. 数组与方法互操作 4. 熟练掌握数组相关的常见问题和代码 目录 1. 数组的基本概念 1.1什么是数组 1.2 数组的创建及初始化 1.3 数组的使用 2. 数组是引用类型 2.1基本类型变量与引用类型变量的区别 2.2再谈引用…

慎思笃行,兴业致远:金融行业的数据之道

《中庸》中说&#xff0c;“博学之&#xff0c;审问之&#xff0c;慎思之&#xff0c;明辨之&#xff0c;笃行之”。这段话穿越千年&#xff0c;指引着中国千行百业的发展。对于金融行业来说&#xff0c;庞大的数据量可以说是“博学”的来源。但庞大的数据体量&#xff0c;既是…

网络通讯(服务端搭建)

一.本篇概况 本篇文章主要以C语言为主&#xff0c;通过C语言中所设定的函数以及环境来将网络通讯的服务端进行搭建。注&#xff1a;本篇并未涉及服务端与客户端之间的收发数据。 二.代码实现 1.初始化套接字库&#xff1a; if(WSAStartup(MAKEWORD(2, 2), &wsaData) ! 0…

你真的了解Java中的数组吗?

你真的了解Java中的数组吗&#xff1f; 数组是基本上所有语言都会有的一种数据类型&#xff0c;它表示一组相同类型的数据的集合&#xff0c;具有固定的长度&#xff0c;并且在内存中占据连续的空间。在C&#xff0c;C等语言中&#xff0c;数组的定义简洁清晰&#xff0c;而在J…

PDF.js实现搜索关键词高亮显示效果

在static\PDF\web\viewer.js找到定义setInitialView方法 大约是在1202行&#xff0c;不同的pdf.js版本不同 在方法体最后面添加如下代码&#xff1a; // 高亮显示关键词---------------------------------------- var keyword new URL(decodeURIComponent(location)).searchP…

【C语言进阶篇】看完这篇结构体文章,我向数据结构又进了一大步!(结构体进阶详解)

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 &#x1f4cb; 前言1 结构体的声明1.1 结构的基础知识1.2 结构的声明1.2.1 . 匿名结构体类型声明1.2.2 匿名结构…

【深度学习实践】垃圾检测

简介 本项目使用深度学习目标检测开源框架PaddleDetection中的yolox算法实现了垃圾检测&#xff0c;本文包含了从头训练yolox模型和直接使用训练好的模型进行推理的代码及相关权重。 一、数据集准备 本次训练的数据集为coco格式&#xff0c;共包含150张垃圾的照片&#xff0…

每日一题——丢失的数字

丢失的数字 题目链接 注&#xff1a;这一题的解法建立在位运算——异或^的基础之上&#xff0c;如果位运算和异或操作符不太了解&#xff0c;建议先看看&#xff1a; 位运算详解 只出现一次的数字 思路 同样&#xff0c;这题要求时间复杂度为O(n)&#xff0c;空间复杂度为O…

二叉树中的深搜

一)计算布尔二叉树的值 2331. 计算布尔二叉树的值 - 力扣&#xff08;LeetCode&#xff09; 1)计算布尔二叉树需要从叶子节点向上进行计算&#xff0c;从下向上进行计算 2)完整二叉树是同时拥有左孩子和右孩子&#xff0c;或者是完全没有右孩子 3)当我只是盯着根节点来看的时候…

JSON Web 令牌 (JWT)攻击

一、什么是JSON Web 令牌 &#xff08;JWT&#xff09; JSON Web令牌&#xff08;JWT&#xff09;是一种开放标准&#xff08;RFC 7519&#xff09;&#xff0c;用于在网络应用间传递声明信息。它是一种轻量级、自包含的安全性传输格式&#xff0c;通常用于在身份验证和授权过程…

三更博客系统(完整笔记+前后台系统代码实现)

三更博客前后端分离系统 前后端分离博客系统1.技术栈2.创建工程3.博客前台3.0 准备工作3.1 SpringBoot和MybatisPuls整合配置测试 3.1 热门文章列表3.1.0 文章表分析3.1.1 需求3.1.2 接口设计3.1.3 基础版本代码实现3.1.4 使用VO优化3.1.5 字面值处理 3.2 Bean拷贝工具类封装3.…

C#百万数据处理

C#百万数据处理 在我们经验的不断增长中不可避免的会遇到一些数据量很大操作也复杂的业务 这种情况我们如何取优化如何去处理呢&#xff1f;一般都要根据业务逻辑和背景去进行合理的改进。 文章目录 C#百万数据处理前言一、项目业务需求和开发背景项目开发背景数据量计算业务需…

OpenFeign原理浅析

OpenFeign原理我个人觉得是非常简单的&#xff0c;如果你对Spring非常了解&#xff0c;知道FactoryBean&#xff0c;以及注入bean的方式&#xff0c;并掌握动态代理&#xff0c;那么自己实现一个类似这样的Http代理客户端是一点问题也没有的&#xff01; 使用流程 首先我们先过…

BLE连接、配对和绑定

参考&#xff1a;一篇文章带你解读蓝牙配对绑定 参考&#xff1a;BLE安全之SM剖析(1) 参考&#xff1a;BLE安全之SM剖析&#xff08;2&#xff09; 参考&#xff1a;BLE安全之SM剖析(3) 参考&#xff1a;https://blog.csdn.net/chengbaojin/article/details/103691046 参考&…