FPGA通过I2C控制AT24C64

news2024/11/28 14:42:08

文章目录

  • 前言
  • 一、代码设计框图
  • 二、IIC_drive模块设计
    • 2.1、模块接口:
    • 2.2、代码功能描述:
    • 2.3、IIC协议实现过程:
  • 三、EEPROM_ctrl模块设计
    • 3.1、模块接口:
    • 3.2、代码功能描述
  • 四、EEPROM_drive模块
  • 五、iic_top模块


前言

继上一篇FPGA学习_I2C总线协议内容,本文将基于FPGA通过I2C控制AT24C64(EEPROM芯片)芯片

一、代码设计框图

在这里插入图片描述
完整代码GitHub连接:https://github.com/shun6-6/IIC_EEPROM_Pro

二、IIC_drive模块设计

参考FPGA奇哥系列网课

2.1、模块接口:

module iic_drive#(
    parameter       P_ADDR_WIDTH     = 16   
)(
    input           i_clk               ,
    input           i_rst               ,
    /*----user interface----*/
    input   [6 :0]  i_device_addr       ,//用户输入设备地址
    input   [15:0]  i_operation_addr    ,//用户输入读写数据地址
    input   [7 :0]  i_operation_len     ,//用户输入读写数据长度
    input   [1 :0]  i_operation_type    ,//用户输入读写类型
    input           i_operation_valid   ,//用户输入操作有效信号
    output          o_operation_ready   ,//用户输出操作准备信号

    input   [7 :0]  i_write_date        ,//用户写入数据
    output          o_write_req         ,//用户写数据请求

    output  [7 :0]  o_read_date         ,//输出IIC读到的数据
    output          o_read_valid        ,//数据有效信号
    /*----IIC interface----*/
    output          o_iic_scl           ,//IIC时钟线
    inout           io_iic_sda           //IIC双向数据线
);

2.2、代码功能描述:

该模块将输入的指令以及相关地址和数据通过IIC协议传递给EEPROM芯片,其中状态机一共有11个状态:

localparam      P_ST_IDLE    = 0    ,//状态机-空闲
                P_ST_START   = 1    ,//状态机-起始位
                P_ST_UADDR   = 2    ,//状态机-设备地址
                P_ST_DADDR1  = 3    ,//状态机-数据地址高位
                P_ST_DADDR2  = 4    ,//状态机-数据地址低位
                P_ST_WRITE   = 5    ,//状态机-写数据
                P_ST_REATART = 6    ,//状态机-重启iic总线
                P_ST_READ    = 7    ,//状态机-读数据
                P_ST_WATI    = 8    ,//等待应答后再发生停止位
                P_ST_STOP    = 9    ,//状态机-停止
                P_ST_EMPTY   = 10   ;//空状态

状态转移过程:
P_ST_IDLE :一次操作指令握手成功后,进入START状态;
P_ST_START :该状态下控制SCL和SDA线启动IIC,随后进入写设备地址UADDR状态,
P_ST_UADDRw_st_turn 信号表示一个byte写入结束,即设备地址和读写指令写入(俩个一共为8bit)结束,随后会判断r_st_restart 信号,该信号表示当前操作为读数据操作,因为只有在读数据时,我们采用随机地址读操作,有一次虚写操作,虚写结束后需要重新启动总线,因此若此信号为高需要进入READ读阶段,否则进入写数据地址1(P_ST_DADDR1)阶段;
P_ST_DADDR1r_slave_ack 信号表示从机响应信号,此时响应的是写设备地址阶段的响应信号,如果响应为0,即未响应,则直接进入STOP停止阶段,以重新启动总线,这是由于刚刚写完数据,如果立马进行操作总线,总线处于忙状态,不会响应,若正常响应,则会在顺利写完8bit数据后进入P_ST_DADDR2 阶段。
P_ST_DADDR2 :在写完8bit的低8为地址后(即w_st_turn 拉高),判断当前是读操作还是写操作,写操作则顺利进入写数据WRITE阶段,读操作则需要进入总线重新启动阶段,即P_ST_REATART 。
P_ST_WRITE :当写如byte字节数与用户输入的写数据长度一致时,进入等待WAIT状态,否则继续写;
P_ST_REATART :重启总线,先进入停止STOP阶段,执行停止操作。
P_ST_READ :字节读,此模块只支持字节读操作,但用户要实现多字节读操作也是可以的,该过程会在EEPROM_drive当中被实现,因为EEPROM_drive模块会执行多次字节读。以此实现连续读操作。
P_ST_WATI :等待一拍,等从机ACK结束后进入停止位STOP阶段。
在这里插入图片描述
P_ST_STOP :控制SCL和SDA线结束IIC操作
这里需要注意的是接收完ACK后,不可以直接停止时钟产生,然后直接拉高SDA,需要多产生一个周期SCL,然后在SCL高电平期间拉高SDA以产生停止信号。
P_ST_EMPTY :emmmmm没啥用的一个状态,完全可以将停止位多计数一个时钟执行响应的判断,不过这样写清晰一点,主要是判断是不是需要重启总线(r_st_restart说明是由于读操作导致的重启操作,r_ack_lock说明是由于从机忙没有回应导致的重启总线),如果是则进入START状态,不是就回到IDLE状态。

always @(*)begin
    case (r_st_cur)
        P_ST_IDLE    : r_st_nxt = w_operation_active ? P_ST_START : P_ST_IDLE;
        P_ST_START   : r_st_nxt = P_ST_UADDR;

        P_ST_UADDR   : r_st_nxt = w_st_turn    ? 
                                  r_st_restart ? P_ST_READ : P_ST_DADDR1
                                : P_ST_UADDR;
 
        P_ST_DADDR1  : r_st_nxt = r_slave_ack    ? P_ST_STOP   :                           
                                  w_st_turn      ? P_ST_DADDR2 : P_ST_DADDR1;
 
        P_ST_DADDR2  : r_st_nxt = w_st_turn && ri_operation_type == P_W ? P_ST_WRITE   :   
                                  w_st_turn && ri_operation_type == P_R ? P_ST_REATART :
                                  P_ST_DADDR2;
 
        P_ST_WRITE   : r_st_nxt = w_st_turn && r_wr_cnt == ri_operation_len - 1          
                                             ? P_ST_WATI   : P_ST_WRITE;

        P_ST_REATART : r_st_nxt = P_ST_STOP;

        P_ST_READ   : r_st_nxt = w_st_turn ? P_ST_WATI   : P_ST_READ;//随机读,一次一个byte

        P_ST_WATI    : r_st_nxt = P_ST_STOP;
        P_ST_STOP    : r_st_nxt = r_st_cnt == 1 ? P_ST_EMPTY : P_ST_STOP;
        P_ST_EMPTY   : r_st_nxt = r_st_restart | r_ack_lock ? P_ST_START : P_ST_IDLE;
        default      : r_st_nxt = P_ST_IDLE;
    endcase
end

2.3、IIC协议实现过程:

过程和之前介绍的SPI总线协议类似,通过维护一个计数器,来使数据在SCL时钟下降沿改变,在SCL时钟上升沿被采样
在这里插入图片描述
主要难点是停止位的部分:

  • 写数据结束的停止位在第一张波形图哪里做了详细说明:
  • 读数据结束的停止位:如下图所示:在黄线部分已经读完了数据,后面需要加一个时钟周期传输NO ACK(图中红线部分)表示此次字节读结束,然后停止位的产生一定还需要多一个时钟周期,并且在此时钟周期最后拉高SDA,其实和写数据停止位一样,重复说明防止出错!!!

在这里插入图片描述

三、EEPROM_ctrl模块设计

3.1、模块接口:

module eeprom_ctrl(
    input           i_clk                   ,
    input           i_rst                   ,
    /*----user interface----*/
    input   [2 :0]  i_eeprom_addr           ,
    input   [15:0]  i_user_operation_addr   ,
    input   [1 :0]  i_user_operation_type   ,
    input   [7 :0]  i_user_operation_len    ,
    input           i_user_operation_valid  ,
    output          o_user_operation_ready  ,

    input   [7 :0]  i_user_write_date       ,
    input           i_user_write_valid      ,
    input           i_user_write_sop        ,
    input           i_user_write_eop        ,

    output  [7 :0]  o_user_read_date        ,
    output          o_user_read_valid       ,
    /*----iic drive interface----*/
    output  [6 :0]  o_device_addr           ,
    output  [15:0]  o_operation_addr        ,
    output  [7 :0]  o_operation_len         ,
    output  [1 :0]  o_operation_type        ,
    output          o_operation_valid       ,
    input           i_operation_ready       ,

    output  [7 :0]  o_write_date            ,
    input           i_write_req             ,

    input   [7 :0]  i_read_date             ,
    input           i_read_valid             
);

3.2、代码功能描述

该模块主要讲用户输入的读写指令转化为驱动读写指令,并且将用户输入的写数据存入FIFO,一个一个把8bit数据吐给IIC驱动,因为IIC_drive是串行传输的,同时也把从EEPROM读出的数据暂存入FIFO,读完后一次性连续的传递给用户模块。同时在该模块当中完成了一个上面刚刚提到的操作,连续读 :IIC_drive模块只支持字节读操作,但用户要实现多字节读操作也是可以的,因为EEPROM_drive模块会执行多次字节读。以此实现连续读操作。

localparam      P_ST_IDLE       = 0     ,
                P_ST_WRITE      = 1     ,
                P_ST_WAIT       = 2     ,
                P_ST_READ       = 3     ,
                P_ST_REREAD     = 4     ,
                P_ST_OUT_DATA   = 5     ;
always @(*)begin
    case (r_st_cur)
        P_ST_IDLE     : r_st_nxt = w_user_active && i_user_operation_type == P_W ? P_ST_WRITE :
                                   w_user_active && i_user_operation_type == P_R ? P_ST_WAIT  :
                                   P_ST_IDLE;

        P_ST_WRITE    : r_st_nxt = w_drive_end && ri_user_operation_type == P_W ? P_ST_IDLE : P_ST_WRITE;
        P_ST_WAIT     : r_st_nxt = P_ST_READ;
        P_ST_READ     : r_st_nxt = w_drive_end ? 
                                    r_read_cnt == ri_user_operation_len - 1 ? P_ST_OUT_DATA : P_ST_REREAD
                                    : P_ST_READ;   
        P_ST_REREAD   : r_st_nxt = P_ST_READ; 
        P_ST_OUT_DATA : r_st_nxt = w_fifo_read_empty ? P_ST_IDLE : P_ST_OUT_DATA;
        default       : r_st_nxt = P_ST_IDLE;
    endcase
end

该模块状态机以及相应跳转条件
该功能较为简单,
P_ST_IDLE :根据输入操作类型分别进入写WRITE状态或者是等待WAIT状态,这是因为用户输入的操作指令都会在本地先打一拍然后寄存下来,对于写数据而言,会先将数据存入FIFO然后再进行握手操作,然后再把本地寄存的用户指令再发给IIC驱动,这期间因为FIFO存数据所以指令赋值推后了好多拍,因此指令赋值不会出错,但是进行读数据操作时,用户指令i_user_operation_type先赋值给ri_user_operation_type以寄存,然后将ri_user_operation_type赋值给驱动ro_operation_type,在这个过程中就需要先等待一拍,否则就会出现上一次操作的的ri_user_operation_type给了ro_operation_type,错过了本次正确的i_user_operation_type。 这就是为什么进入读操作时先要进入一次WAIT状态以打一排。
之后的状态都很简单了!!!

四、EEPROM_drive模块

该模块就是例化了上述俩个模块

五、iic_top模块

该模块则是简单实现了一个不断翻转读写状态的的用户模块。

module iic_top(
    input           i_clk       ,
    output          o_iic_scl   ,//IIC时钟线
    inout           io_iic_sda   //IIC双向数据线
    );
localparam      P_WRITE_NUM = 8;
localparam      P_W         = 1     ,//写数据
                P_R         = 2     ;//读数据
reg  [2 :0]     ri_eeprom_addr          ; 
reg  [15:0]     ri_user_operation_addr  ; 
reg  [1 :0]     ri_user_operation_type  ; 
reg  [7 :0]     ri_user_operation_len   ; 
reg             ri_user_operation_valid ; 
wire            o_user_operation_ready  ;
reg  [7 :0]     ri_user_write_date      ; 
reg             ri_user_write_valid     ; 
reg             ri_user_write_sop       ; 
reg             ri_user_write_eop       ; 
wire [7 :0]     o_user_read_date        ;
wire            o_user_read_valid       ;
reg  [7 :0]     r_write_cnt             ;
reg             r_wr_st                 ;
wire            w_user_active           ;
wire            w_clk_5mhz              ;
wire            w_clk_5mhz_lock         ;
wire            w_clk_125khz            ;
wire            w_clk_125khz_rst        ;
assign w_user_active = ri_user_operation_valid & o_user_operation_ready;
SYSCLK_div SYSCLK_div_5mhz
   (
    .clk_out1               (w_clk_5mhz     ),   
    .locked                 (w_clk_5mhz_lock),       
    .clk_in1                (i_clk          )      
);
CLK_DIV_module#(
    .P_CLK_DIV_CNT          (40) //MAX = 65535
)CLK_DIV_module_U(
    .i_clk                  (w_clk_5mhz      ),
    .i_rst                  (~w_clk_5mhz_lock),
    .o_clk_div              (w_clk_125khz    )
    );
rst_gen_module#(
    .P_RST_CYCLE            (1)  
)rst_gen_module_u0(     
    .i_clk                  (w_clk_125khz    ),
    .o_rst                  (w_clk_125khz_rst)
    ); 
eeprom_drive eeprom_drive_u0(
    .i_clk                  (w_clk_125khz    ),
    .i_rst                  (w_clk_125khz_rst),
    .i_eeprom_addr          (ri_eeprom_addr         ),
    .i_user_operation_addr  (ri_user_operation_addr ),
    .i_user_operation_type  (ri_user_operation_type ),
    .i_user_operation_len   (ri_user_operation_len  ),
    .i_user_operation_valid (ri_user_operation_valid),
    .o_user_operation_ready (o_user_operation_ready ),
    .i_user_write_date      (ri_user_write_date     ),
    .i_user_write_valid     (ri_user_write_valid    ),
    .i_user_write_sop       (ri_user_write_sop      ),
    .i_user_write_eop       (ri_user_write_eop      ),
    .o_user_read_date       (o_user_read_date       ),
    .o_user_read_valid      (o_user_read_valid      ),
    .o_iic_scl              (o_iic_scl ),//IIC时钟线
    .io_iic_sda             (io_iic_sda) //IIC双向数据线
    );
always @(posedge w_clk_125khz or posedge w_clk_125khz_rst)begin
    if(w_clk_125khz_rst)begin
        ri_eeprom_addr          <= 'd0;
        ri_user_operation_addr  <= 'd0;
        ri_user_operation_type  <= 'd0;
        ri_user_operation_len   <= 'd0;
        ri_user_operation_valid <= 'd0;
    end
    else if(o_user_operation_ready && r_wr_st == 0)begin
        ri_eeprom_addr          <= 3'b011;
        ri_user_operation_addr  <= 'd0;
        ri_user_operation_type  <= P_W;
        ri_user_operation_len   <= P_WRITE_NUM;
        ri_user_operation_valid <= 'd1;
    end
    else if(o_user_operation_ready && r_wr_st == 1)begin
        ri_eeprom_addr          <= 3'b011;
        ri_user_operation_addr  <= 'd0;
        ri_user_operation_type  <= P_R;
        ri_user_operation_len   <= P_WRITE_NUM;
        ri_user_operation_valid <= 'd1;
    end
    else begin
        ri_eeprom_addr          <= 'd0;
        ri_user_operation_addr  <= 'd0;
        ri_user_operation_type  <= 'd0;
        ri_user_operation_len   <= 'd0;
        ri_user_operation_valid <= 'd0;
    end
end
always @(posedge w_clk_125khz or posedge w_clk_125khz_rst)begin
    if(w_clk_125khz_rst)
        ri_user_write_date <= 'd0;
    else if(ri_user_write_valid)
        ri_user_write_date <= ri_user_write_date + 1;
    else
        ri_user_write_date <= ri_user_write_date;
end 
  
always @(posedge w_clk_125khz or posedge w_clk_125khz_rst)begin
    if(w_clk_125khz_rst)
        ri_user_write_sop <= 'd0;
    else if(w_user_active && ri_user_operation_type == P_W)
        ri_user_write_sop <= 'd1;
    else
        ri_user_write_sop <= 'd0;
end 
always @(posedge w_clk_125khz or posedge w_clk_125khz_rst)begin
    if(w_clk_125khz_rst)
        ri_user_write_valid <= 'd0;
    else if(ri_user_write_eop)
        ri_user_write_valid <= 'd0;
    else if(w_user_active && ri_user_operation_type == P_W)
        ri_user_write_valid <= 'd1;
    else
        ri_user_write_valid <= ri_user_write_valid;
end 
always @(posedge w_clk_125khz or posedge w_clk_125khz_rst)begin
    if(w_clk_125khz_rst)
        ri_user_write_eop <= 'd0;
    // else if((w_user_active || ri_user_write_valid) && r_write_cnt == P_WRITE_NUM - 2)
    //     ri_user_write_eop <= 'd1;
    else if(w_user_active && P_WRITE_NUM == 1)
        ri_user_write_eop <= 'd1;//write 1 byte
    else if(ri_user_write_valid && r_write_cnt == P_WRITE_NUM - 2)
        ri_user_write_eop <= 'd1;//write over 1 byte
    else
        ri_user_write_eop <= 'd0;
end 
always @(posedge w_clk_125khz or posedge w_clk_125khz_rst)begin
    if(w_clk_125khz_rst)
        r_write_cnt <= 'd0;
    else if(r_write_cnt == P_WRITE_NUM - 1)
        r_write_cnt <= 'd0;
    else if(ri_user_write_valid)
        r_write_cnt <= r_write_cnt + 1'd1;
    else
        r_write_cnt <= r_write_cnt;
end 
always @(posedge w_clk_125khz or posedge w_clk_125khz_rst)begin
    if(w_clk_125khz_rst)
        r_wr_st <= 'd0;
    else if(w_user_active)
        r_wr_st <= r_wr_st + 1'd1;
    else
        r_wr_st <= r_wr_st;
end 
endmodule

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

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

相关文章

图解CodeWhisperer的安装使用

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! ​ 目录 &#x1f4d8; CodeWhisperer简介 &#…

MFC界面美化第三篇----自绘按钮(重绘按钮)

1.前言 最近发现读者对我的mfc美化的专栏比较感兴趣&#xff0c;因此在这里进行续写&#xff0c;这里我会计划写几个连续的篇章&#xff0c;包括对MFC按钮的美化&#xff0c;菜单栏的美化&#xff0c;标题栏的美化&#xff0c;list列表的美化&#xff0c;直到最后形成一个完整…

Go——切片

1. 特点 slice并不是数组或数组指针。它通过内部指针和相关属性引用数组片段&#xff0c;以实现变长方案。 切片&#xff1a;切片是数组的一个引用&#xff0c;因此切片是引用类型。但自身是结构体&#xff0c;值拷贝传递。切片的长度可以改变&#xff0c;因此&#xff0c;切片…

arm 解决Rk1126 画框颜色变色问题(RGB转NV12)

在Rv1126上直接对Nv12图像进行绘制时&#xff0c;颜色是灰色。故将Nv12转BGR后绘制图像&#xff0c;绘制完成后转成Nv12&#xff0c;BGR的图像颜色是正常的&#xff0c;但是NV12的图像颜色未画全&#xff0c;如图&#xff1a; 1.排查发现是RGB转NV12的函数出现问题&#xff0c…

Springboot+vue的仓库管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的仓库管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层…

深入理解栈和队列(一):栈

个人主页&#xff1a;17_Kevin-CSDN博客 专栏&#xff1a;《数据结构》 一、栈的概念 栈&#xff08;Stack&#xff09;是一种特殊的线性表&#xff0c;它遵循后进先出&#xff08;Last-In-First-Out&#xff0c;LIFO&#xff09;的原则。栈可以被看作是一个只能在一端进行操作…

记录解决问题--activiti8.2 流程图图片由png改为svg前端不显示图片问题

1.说明 如果是vue svg显示&#xff0c;请查阅其他标准资料&#xff0c;类似使用svg标签。我这里讲的另外一种情况&#xff0c;链接返回的是svg文件&#xff0c;需要用v-html显示图片。 2.activiti6流程图图片格式 ①png格式。可以查看链接返回&#xff0c;以png开头。 ②前端…

如何查看局域网内所有的ip和对应的mac地址

1、windows下查看 方法一、 按快捷键“winr”打开运行界面&#xff0c;输入“CMD”回车: 输入以下命令&#xff1a; for /L %i IN (1,1,254) DO ping -w 1 -n 1 192.168.0.%i 其中 192.168.0.%i 部分要使用要查询的网段&#xff0c;比如 192.168.1.%i 192.168.137.%i 172.16.2…

Nginx日志分割实战指南:让日志管理更轻松

Nginx默认没有提供对日志文件的分割功能&#xff0c;所以随着时间的增长&#xff0c;access.log和error.log文件会越来越大&#xff0c;尤其是access.log&#xff0c;其日志记录量比较大&#xff0c;更容易增长文件大小&#xff0c;影响日志写入性能。 分割Nginx日志的方法有很…

上位机图像处理和嵌入式模块部署(qmacvisual图像预处理)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 不管大家是在读书的时候学习的图像处理&#xff0c;还是在后来的工作中&#xff0c;重新学习了图像处理&#xff0c;相信大家对图像预处理的概念并…

2024年国内彩妆行业市场数据分析:增长机会在哪?

从伊蒂之屋、菲诗小铺等平价韩妆的退出&#xff0c;到全球第一眉妆贝玲妃的落幕&#xff0c;曾经的“网红”-“全网断货选手”纷纷退出中国市场。 有人认为是国内彩妆市场不景气&#xff1f;事实上&#xff0c;国内彩妆市场线上市场规模仍在持续扩大。根据鲸参谋数据统计&…

梅森增益与劳斯稳定判据

梅森增益&#xff1a; 注意前两行的系数&#xff0c;第一行是偶次项&#xff0c;从高幂走向低幂&#xff1b;第二行是奇次项&#xff0c;从高幂走向低幂。 第一中情况&#xff1a; 第二种情况&#xff1a; 讲的真的很好&#xff0c;受益颇多&#xff1a; https://www.bilibili…

整蛊小教程|让朋友手足无措的电脑自动关机

前言 这几天讲到shutdown关机命令&#xff0c;于是就出现了整蛊类的电脑教程。 这个故事我记得很清楚&#xff1a;在2012年的春天……当时的小白对电脑还不是很熟悉。某一天跟着朋友去网吧上网&#xff0c;这时候突然有个朋友发来一个.bat的文件&#xff0c;说双击打开有惊喜…

FX-数组的使用

1一维数组 1.1一维数组的创建和初始化 1.1.1数组的创建 //代码1 int arr1[10]; char arr2[10]; float arr3[1]; double arr4[20]; //代码2 //用宏定义的方式 #define X 3 int arr5[X]; //代码3 //错误使用 int count 10; int arr6[count];//数组时候可以正常创建&#xff1…

供应链安全之被忽略的软件质量管理平台安全

背景 随着我国信息化进程加速&#xff0c;网络安全问题更加凸显。关键信息基础设施和企业单位在满足等保合规的基础上&#xff0c;如何提升网络安全防御能力&#xff0c;降低安全事件发生概率&#xff1f;默安玄甲实验室针对SonarQube供应链安全事件进行分析&#xff0c;强调供…

系统资源耗尽对服务器的影响有什么?

在当今数字化时代&#xff0c;服务器作为核心计算设备&#xff0c;为企业和组织的业务连续性提供了重要保障。然而&#xff0c;随着业务的增长和复杂性的提升&#xff0c;服务器也面临着越来越多的挑战。其中&#xff0c;系统资源耗尽是服务器面临的一个重要问题。今天德迅云安…

中间件-消息队列

消息队列基础知识 什么是消息队列 本处提到的消息队列是指各个服务以及系统组件/模块之间的通信&#xff0c;属于一种中间件。参与消息传递的双方称为生产者和消费者&#xff0c;生产者负责发送消息&#xff0c;消费者负责处理消息。 消息队列作用 通过异步处理&#xff0…

五、C#归并排序算法

简介 归并排序是一种常见的排序算法&#xff0c;它采用分治法的思想&#xff0c;在排序过程中不断将待排序序列分割成更小的子序列&#xff0c;直到每个子序列中只剩下一个元素&#xff0c;然后将这些子序列两两合并排序&#xff0c;最终得到一个有序的序列。 归并排序实现原…

JVM常用垃圾收集器

JVM 4.1 哪些对象可以作为GC ROOT? 虚拟机栈&#xff08;栈帧中的局部变量表&#xff09;中引用的对象本地方法栈中引用的对象方法区静态变量引用的对象方法区常量引用的对象被同步锁持有的对象JNI&#xff08;Java Native Interface&#xff09;引用的对象 4.2 常用垃圾收集…

2.28CACHE,虚拟存储器

主存储器,简称主存。CPU可以直接随机地对其进行访问&#xff0c;也可以和高速缓存器及辅助存储器交换数据。 2> 辅助存储器,简称辅存&#xff0c;不能与CPU直接相连&#xff0c;用来存放当前暂时不用的程序和数据 3> 高速缓冲存储器,位于主存和CPU之间&#xff0c;用来…