HDMI彩条显示实验与方块移动实验

一、HDMI接口简介

        一种数字音视频接口标准,提供高质量的数字音视频传输,同时支持多通道音频、高分辨率视频和其他数据传输功能。提供更高的数据传输带宽(带宽:1s内传输多少比特数据)

  1. 数字传输: HDMI是一种全数字接口,通过数字信号传输音频和视频数据。这有助于防止信号损失,并提供更高的图像和音频质量。

  2. 高清晰度视频: HDMI支持高分辨率视频,包括标准高清晰度(HD)和超高清晰度(UHD)分辨率。常见的分辨率包括720p、1080p、4K等。

  3. 多通道音频: HDMI支持多通道音频传输,包括立体声和环绕声格式。这允许用户在一个单一的HDMI连接上传输高质量音频。

  4. 双向通信: HDMI不仅能够从源设备(如电脑、蓝光播放器)发送信号到显示设备,还能够支持反向通信,使得显示设备可以发送控制信号到源设备。

  5. 支持色彩深度和色域: HDMI支持不同的色彩深度(比如8位、10位、12位)和广色域,有助于提供更丰富的颜色和更高的图像质量。

  6. 多功能: HDMI接口除了传输音视频信号外,还可以支持一些其他功能,如以太网传输(HDMI with Ethernet)、ARC(Audio Return Channel,音频回传通道)等。

        HDMI 向下兼容 DVI。DVI (数字视频接口)只能用来传输视频,而不能同时传输音频;

二、YUV颜色表示

        与 RGB(红绿蓝)不同,YUV 将颜色信息分成亮度(Y,代表亮度)和色度(U、V,代表色度)两部分。YUV 表示的基本思想是,人眼对亮度变化更为敏感,而对色度变化相对不敏感。因此,通过将颜色信息分离,可以更有效地压缩和传输图像。RGB与YUV的转换如下:

三、TMDS协议

(一)概念

         最小化传输差分信号 Transition Minimized Differential Signaling

         DVI 和 HDMI 接口协议在物理层均使用 TMDS 标准传输音视频数据。

  1. 差分信号传输: TMDS使用差分信号传输方式,使用两个引脚来传输一路信号,利用这两个引脚间的电压差的正负极性和大小来决定传输数据的数值( 0 或 1 )。这种方式可以减小电磁辐射和抗干扰性更强。

  2. 过渡最小化编码: TMDS采用过渡最小化编码(Transition Minimized Coding,TMC)技术,以减小信号的过渡时间。这有助于减小电磁干扰,特别是对于高频率的数字信号而言。

  3. 数据通道: TMDS协议通常使用三个数据通道,分别用于红色(R)、绿色(G)和蓝色(B)的视频信息传输。此外,还有一个时钟通道用于同步数据。

  4. TMDS时钟: TMDS时钟通道是一个单独的差分信号通道,用于同步数据通道的传输。它的频率通常是数据通道的两倍。

  5. 数据帧: 数据在TMDS中以数据帧的形式传输,其中包括视频和音频信息。数据帧由特定的同步和控制信息组成。

  6. 带宽和分辨率: TMDS协议的带宽和分辨率直接关联,带宽的增加通常支持更高分辨率的视频传输。不同版本的HDMI和DVI标准支持不同的带宽和分辨率。

(二)信号传输

  • TMDS 连接从逻辑功能上可以划分成两个阶段:“编 / 解码” 和 “并 / 串转换” 。在信号源处完成编码操作,并将数据转为串行传输出去。在接受处完成解码,并将数据转换为并行数据
  • 在编码阶段,编码器将视频源中的像素数据、 HDMI 的音频 / 附加数据,以及行同步和场同步信号分别编码成 10 位的字符流。在并串转换阶段将上述的 10 位字符流转换成串行数据流,并将其从三个差分输出通道发送出去。
  • 这个 10:1 的并转串过程所生成的串行数据速率是实际像素时钟速率的 10 倍。

(三)DVI编码

        DVI 或 HDMI 视频传输所使用的 TMDS 连接通过四个串行通道实现。对于 DVI 来说,其中三个通道分别用于传输视频中每个像素点的红、绿、蓝三个颜色分量(RGB 4:4:4 格式)。 HDMI 默认也是使用三个 RGB 通道,但是它同样可以选择传输像素点的亮度和色度信息(YCrCb 4:4:4 YCrCb 4:2:2 格式)。第四个通道是时钟通道,用于传输像素时钟。独立的 TMDS时钟通道为接收端提供接收的参考频率,保证数据在接收端能够正确恢复。

(四)HDMI编码

        当VDE被拉高时,传输视频有效像素;当ADE被拉高时,传输音频有效数据。

        对于 DVI 传输,整个视频的消隐期都用来传输控制字符。而 HDMI 传输的消隐期除了控制字符之外,还可以用于传输音频或者其他附加数据,比如字幕信息等。这就是 DVI HDMI 协议之间最主要的差别。

(五)TMDS编码算法

       这种算法可以减小传输信号过程的上冲和下冲;而 DC 平衡使信号对传输线的电磁干扰减少;可以用低成本的专用电缆实现长距离、高质量的数字信号传输。

         TMDS 连接从逻辑功能上可以划分成两个阶段:编码和并串转换

        编码阶段算法流程图如下:

每个通道 8-bit 的像素数据都将被转换成 460 个特定 10-bit 字符中的一个。
这个编码机制大致上实现了传输过程中的直流平衡,即一段时间内传输的高电平(数字“ 1” )的个
数大致等于低电平(数字“ 0” )的个数。
每个编码后的 10-bit 字符中状态跳转(“由 1 到 0” 或者“由 0 到 1” )的次数将被限制在五次以内。
每个通道 2-bit 控制信号的状态也要进行编码,编码后分别对应四个不同的 10-bit 控制字
符,分别是 10'b1101010100 , 10'b0010101011 , 10'b0101010100 ,和10'b1010101011 。
每个控制字符都有七次以上的状态跳转。
视频字符和控制字符状态跳转次数的不同将会被用于发送和接收设备的同步。

        在编码之后,3 个通道的 10-bit 字符将进行并串转换,这一过程是通过调用 Quartus 软件中的 ALTDDIO_OUT IP 核来实现。

(六)引脚定义

四、实验目标

        使用HDMI接口,显示彩条

五、程序设计

(一)系统框图

(二)时序图

(三)RGB转DVI

RGB DVI 模块包括 异步复位模块、 DVI 编码模块和 10 位并行转串行模块。设计框图如下:

        整个系统需要两个输入时钟,一个是视频的像素时钟 Pixel Clk ,另外一个时钟 Pixel Clk x5 的频率是像素时钟的五倍。并串转换过程实现的是 10:1 的转换率,理论上转换器需要一个 10 倍像素时钟频率的串行时钟。这里我们只用了一个 5 倍的时钟频率,这是因为 ALTDDIO_OUT IP
可以实现 DDR 的功能,即它在五倍时钟频率的基础上又实现了双倍数据速率。
        编码部分代码:
`timescale 1 ps / 1ps

module dvi_encoder (
  input            clkin,    // pixel clock input
  input            rstin,    // async. reset input (active high)
  input      [7:0] din,      // data inputs: expect registered
  input            c0,       // c0 input
  input            c1,       // c1 input
  input            de,       // de input
  output reg [9:0] dout      // data outputs
);

  
  // Counting number of 1s and 0s for each incoming pixel
  // component. Pipe line the result.
  // Register Data Input so it matches the pipe lined adder
  // output
  
  reg [3:0] n1d; //number of 1s in din
  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

  ///
  // Stage 1: 8 bit -> 9 bit
  // Refer to DVI 1.0 Specification, page 29, Figure 3-5
  ///
  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;

  /
  // Stage 2: 9 bit -> 10 bit
  // Refer to DVI 1.0 Specification, page 29, Figure 3-5
  /
  reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_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;
  parameter CTRLTOKEN1 = 10'b0010101011;
  parameter CTRLTOKEN2 = 10'b0101010100;
  parameter CTRLTOKEN3 = 10'b1010101011;

  reg [4:0] cnt; //disparity counter, MSB is the sign bit
  wire decision2, decision3;

  assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m);
  /
  // [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)]
  /
  assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m));

  
  // pipe line alignment
  
  reg       de_q, de_reg;
  reg       c0_q, c1_q;
  reg       c0_reg, c1_reg;
  reg [8:0] q_m_reg;

  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;
    c1_reg  <=#1 c1_q;

    q_m_reg <=#1 q_m;
  end

  ///
  // 10-bit out
  // disparity counter
  ///
  always @ (posedge clkin or posedge rstin) begin
    if(rstin) 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 

(四)ALTDDIO_OUT IP 核简介

        TMDS 编码之后的数据由 serializer_10_to_1 模块进行并串转换, serializer_10_to_1 模块实现 10:1 的并串转换,并实现 DDR(Double Data Rate)  数据输出,其中 DDR 数据的输出是通过调用 ALTDDIO_OUT IP 核来实现。ALTDDIO_OUT IP 核用于实现 DDR 输出接口的功能,实现了将两路单沿信号转换为双沿信号,即在参考时钟的上升沿和下降沿发送数据。由此实现两倍速率的输出。

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

(五)十倍速率转换模块

        每个通道上的颜色数据将通过 编码器来转换成一个 10 位的像素字符后。需要通过 并串转
换器转换成串行数据,最后由 TMDS 数据通道发送出去。因此串行输出的时钟频率需要是并行输入的10倍。

`timescale 1ns / 1ps

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 define
reg   [2:0]  bit_cnt = 0;    
reg   [4:0]  datain_rise_shift = 0;
reg   [4:0]  datain_fall_shift = 0;
    
//wire define
wire  [4:0]  datain_rise;    
wire  [4:0]  datain_fall;
  
//*****************************************************
//**                    main code
//*****************************************************

//上升沿发送Bit[8]/Bit[6]/Bit[4]/Bit[2]/Bit[0]
assign  datain_rise = {paralell_data[8],paralell_data[6],paralell_data[4],
                       paralell_data[2],paralell_data[0]};

//下降沿发送Bit[9]/Bit[7]/Bit[5]/Bit[3]/Bit[1]                       
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核
ddio_out  u_ddio_out_p(
    .datain_h    (datain_rise_shift[0]),
    .datain_l    (datain_fall_shift[0]),
    .outclock    (serial_clk_5x),
    .dataout     (serial_data_p)
    );

//例化DDIO_OUT IP核
ddio_out  u_ddio_out_n(
    .datain_h    (~datain_rise_shift[0]),
    .datain_l    (~datain_fall_shift[0]),
    .outclock    (serial_clk_5x),
    .dataout     (serial_data_n)
    );    
 
endmodule

六、HDMI方块移动实验

        为完成方块的显示,需要同时使用像素点的横坐标和纵坐标来绘制方块所在的矩形区域,另外还需要知道矩形区域左上角的顶点坐标。由于视频显示的图像在行场同步信号的同步下不停的刷新,因此只要连续改变方块左上角顶点的坐标,并在新的坐标点处重新绘制方块,即可实现方块移动的效果。
        方块移动部分代码如下:
        
//当方块移动到边界时,改变移动方向
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
        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

###本文参考正点原子视频,仅用于自己学习复习,如有侵权,请联系删除。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【VRTK】启用多种VR设备的Passthrough功能

【背景】 透视可以让VR头盔展现AR能力,通过VRTK,可以快速实现多种设备平台可用的透视功能。包括主流的Oculus,Pico等。整个不成不需要自己写代码。 【操作】 针对WaveXR,点击场景中的CameraRigsWaveXR-》WaveRig-》Camera Offset-》Main Camera,追加一个新组件,名为Und…

QT自定义信号和槽

信号和槽 介绍实现创建文件对teacher的h和cpp文件进行处理对student的h和cpp文件进行处理对widget的h和cpp文件进行处理 介绍 Qt中的信号和槽是一种强大的机制&#xff0c;用于处理对象之间的通信。它们是Qt框架中实现事件驱动编程的核心部分。 信号&#xff08;Signal&#x…

vite4项目中,vant兼容750适配

一般非vite项目&#xff0c;使用postcss-px-to-viewport。在设计稿为750时候&#xff0c;可使用以下配置兼容vant。 在vite4项目中&#xff0c;以上配置不行。需要调整下&#xff0c;使用postcss-px-to-viewport-8-plugin&#xff0c;并修改viewportWidth&#xff0c;具体如下…

51单片机定时/计数器相关知识点

51单片机定时/计数器相关知识点 结构组成 51单片机的定时/计数器中有两个寄存器&#xff1a; T0&#xff1a;低位&#xff1a;TL0&#xff08;字节地址8AH&#xff09;高位&#xff1a;TH0&#xff08;字节地址8CH&#xff09;T1&#xff1a;低位&#xff1a;TL1&#xff08…

走向云原生 破局数字化

近年来&#xff0c;随着云计算概念和技术的普及&#xff0c;云原生一词也越来越热门&#xff0c;云原生成为云计算领域的新变量。行业内&#xff0c;华为、阿里巴巴、字节跳动等各个大厂都在“抢滩”云原生市场。行业外&#xff0c;云原生也逐渐出圈&#xff0c;出现在大众视野…

Visual Studio Code安装C#开发工具包并编写ASP.NET Core Web应用

前言 前段时间微软发布了适用于VS Code的C#开发工具包&#xff08;注意目前该包还属于预发布状态但是可以正常使用&#xff09;&#xff0c;因为之前看过网上的一些使用VS Code搭建.NET Core环境的教程看着还挺复杂的就一直没有尝试使用VS Code来编写.NET Core。不过听说C# 开发…

设计模式Java实战,彻底学会

​这是全网最强的Java设计模式实战教程。此教程用实际项目场景&#xff0c;结合SpringBoot&#xff0c;让你真正掌握设计模式。 网址是&#xff1a;Java设计模式实战专栏介绍 - 自学精灵&#xff08;也可以百度搜索“自学精灵”&#xff09;。 本设计模式专栏的威力 用Java实…

MySQL学习笔记2: MySQL的前置知识

目录 1. MySQL是什么?2. 什么是客户端&#xff0c;什么是服务器&#xff1f;3. 服务器的特点4. 安装mysql5. mysql 客户端6. mysql 服务器7. mysql的本体8. MySQL 使用什么来存储数据&#xff1f;9. 数据库的多种含义10. MySQL 存储数据的组织方式 1. MySQL是什么? MySQL 是…

算法每日一题: 被列覆盖的最多行数 | 二进制 - 状态压缩

大家好&#xff0c;我是星恒 今天的题目又是一道有关二进制的题目&#xff0c;有我们之前做的那道 参加考试的最大学生数的 感觉&#xff0c;哈哈&#xff0c;当然&#xff0c;比那道题简单多了&#xff0c;这道题感觉主要的考点就是二进制&#xff0c;大家可以好好总结一下这道…

使用 Maven 的 dependencyManagement 管理项目依赖项

使用 Maven 的 dependencyManagement 管理项目依赖项 介绍 在开发 Java 项目时&#xff0c;管理和协调依赖项的版本号是一项重要而繁琐的任务。 而 Maven 提供了 <dependencyManagement> 元素&#xff0c;用于定义项目中所有依赖项的版本。它允许您指定项目中每个依赖…

Java网络编程 、UDP、TCP、Socket通信

这个是第一篇&#xff0c;我先写udp&#xff0c; 首先我解释一下这个的特点是什么&#xff0c;他的特点主要是&#xff1a; 我发送消息之后就不管这个消息的任何情况&#xff0c;也就是&#xff0c;我只要把这个消息发送出去就不管了 这个是大白话的解释&#xff0c;具体的就…

el 消除inpu输入框内容和下拉内容

输入这个就好了,clearable @clear="getList()" 非常简单 <span class="type-box"><span class="label">订单状态</span><el-select v-model="params.orderStatus" placeholder="请选择" class=&…

openGauss学习笔记-188 openGauss 数据库运维-常见故障定位案例-core问题定位

文章目录 openGauss学习笔记-188 openGauss 数据库运维-常见故障定位案例-core问题定位188.1 磁盘满故障引起的core问题188.1.1 问题现象188.1.2 原因分析188.1.3 处理办法 188.2 GUC参数log_directory设置不正确引起的core问题188.2.1 问题现象188.2.2 原因分析188.2.3 处理办…

html js加载本地文件报错处理,跨域问题

这个问题是怎么来的&#xff1f;我写了一个本地html文件&#xff0c;里面通过three.js加载并显示一个本地三维模型&#xff0c;结果报错了。 报错如下&#xff1a; Access to XMLHttpRequest at file:///C:/model/quater.mtl from origin null has been blocked by CORS poli…

GUI设计基础

层次结构 要学GUI&#xff0c;大概先知道它的层次结构&#xff0c;如下图所示&#xff0c;我们要设计的就是下面这个几个东西。 菜单uimenu 建立一级菜单项的函数调用格式&#xff1a; hmuimenu(h_parent,PropertyNamel,valuel,propertyName2,value2&#xff0c;...); hm 是…

云原生十二问

一、什么是云原生&#xff1f; 云原生是在云计算环境中构建、部署和管理现代应用程序的软件方法。现代企业希望构建高度可扩展、灵活且具有弹性的应用程序&#xff0c;可以快速更新以满足客户需求。为此&#xff0c;他们使用现代工具和技术&#xff0c;这些工具和技术本质上支…

从零开始的OpenGL光栅化渲染器构建1

前言 参照Learnopengl&#xff0c;我开始回顾OpenGL中的内容&#xff0c;最终目标是构建一个玩具级的光栅化渲染器&#xff0c;最好还能和之前做的光线追踪渲染器相结合&#xff0c;希望能够有所收获吧~ 包管理 之前我用CMake配置过OpenGL的环境&#xff0c;这样做出来的项目…

【Java EE初阶六】多线程案例(单例模式)

1. 单例模式 单例模式是一种设计模式&#xff0c;设计模式是我们必须要掌握的一个技能&#xff1b; 1.1 关于框架和设计模式 设计模式是软性的规定&#xff0c;且框架是硬性的规定&#xff0c;这些都是技术大佬已经设计好的&#xff1b; 一般来说设计模式有很多种&#xff0c;…

抖音字幕视频怎么做能滚动 抖音个性字幕怎么做 抖音短视频用什么软件剪辑

不管是抖音短视频&#xff0c;还是其他影视网站的影视剧&#xff0c;字幕基本都是必不可少的&#xff0c;字幕本身就能加强观众对视频的理解&#xff0c;而且像一些滚动字幕&#xff0c;会更加吸引观众的注意力&#xff0c;那抖音字幕视频怎么做能滚动&#xff1f;抖音个性字幕…

Hierarchical Clusting模型

介绍&#xff1a; Hierarchical Clustering 是一种常用的聚类方法&#xff0c;它通过构建一个层次化的聚类树&#xff08;或者称为聚类图&#xff09;&#xff0c;将数据点逐步合并组成不同的聚类簇。 Hierarchical Clustering 的主要思想是将相似的数据点归为一类&#xff0c…