基于FPGA的SPI读写M25P16 Flash芯片

news2024/12/23 6:49:26

文章目录

  • 一、SPI协议简介
    • 1.1 SPI引脚
    • 1.2 时钟极性和时钟相位
    • 1.3 主从模式
  • 二、Flash(M25P16)
    • 2.1 Flash简介
    • 2.2 M25P16芯片分析
    • 2.3 项目所用指令时序
      • 2.3.1 WREN(06h)
      • 2.3.2 RDID(9Fh)
      • 2.3.3 READ(03h)
      • 2.3.4 PP(02h)
      • 2.3.5 SE(D8h)
  • 三、状态机
  • 四、项目源码

本项目所用FPGA芯片型号为:EP4CE6F17C8
厂家:Altera
Flash芯片型号:M25P16
厂家:海力士

一、SPI协议简介

SPI是串行外设接口(Serial Peripheral Interface)的缩写,是美国摩托罗拉公司(Motorola)最先推出的一种同步串行传输规范,也是一种单片机外设芯片串行扩展接口,是一种高速、全双工、同步通信总线,所以可以在同一时间发送和接收数据,SPI是一种事实标准因此并没有定义速度限制,本次项目所用的Flash芯片支持的最高速率达到了50Mb/s(页编程,扇区擦除等操作)。同时SPI协议支持一主多从,采用片选信号选择从机,具体的主从机制会在后文介绍。

SPI 设备间的数据传输之所以又被称为数据交换,是因为 SPI 协议规定一个 SPI 设备不能在数据通信过程中仅仅只充当一个 “发送者(Transmitter)” 或者 “接收者(Receiver)”。在每个 Clock 周期内,SPI 设备都会发送并接收一个 bit 大小的数据(不管主设备好还是从设备),相当于该设备有一个 bit 大小的数据被交换了。一个 Slave 设备要想能够接收到 Master 发过来的控制信号,必须在此之前能够被 Master 设备进行访问 (Access)。所以,Master 设备必须首先通过 SS/CS pin 对 Slave 设备进行片选, 把想要访问的 Slave 设备选上。 在数据传输的过程中,每次接收到的数据必须在下一次数据传输之前被采样。如果之前接收到的数据没有被读取,那么这些已经接收完成的数据将有可能会被丢弃,导致 SPI 物理模块最终失效。因此,在程序中一般都会在 SPI 传输完数据后,去读取 SPI 设备里的数据, 即使这些数据(Dummy Data)在我们的程序里是无用的(虽然发送后紧接着的读取是无意义的,但仍然需要从寄存器中读出来)。这一点后续会在代码的滤除无效数据以及发送指令后发送无效数据占用CS片选信号线中体现出来。

1.1 SPI引脚

SPI协议共有四根线,分别为:SCLK(时钟线)、MISO(主机输入,从机输出)、MOSI(主机输出、从机输入)、CS(片选信号)。
在这里插入图片描述

  • SCLK:主要的作用是 Master(主)设备往 Slave(从)设备传输时钟信号, 控制数据交换的时机以及速率
  • CS:用于 Master(主)设备片选 Slave (从)设备,使被选中的 Slave(从)设备能够被 Master(主)设备所访问
  • MISO:在 Master(主)上面也被称为 Rx-Channel,作为数据的入口,主要用于SPI 设备接收数据
  • MOSI:在 Master(主)上面也被称为 Tx-Channel,作为数据的出口,主要用于 SPI 设备发送数据

1.2 时钟极性和时钟相位

时钟极性(CPOL):
根据硬件制造商的命名规则不同,时钟极性通常写为CKP或CPOL。时钟极性和相位共同决定读取数据的方式,比如信号上升沿读取数据还是信号下降沿读取数据。

CPOL可以配置为1或0。这意味着你可以根据需要将时钟的默认状态(IDLE)设置为高或低。

  • CKP = 0:时钟空闲IDLE为低电平 0
  • CKP = 1:时钟空闲IDLE为高电平1

时钟相位(CPHA):
根据硬件制造商的不同,时钟相位通常写为CKE或CPHA。顾名思义,时钟相位/边沿,也就是采集数据时是在时钟信号的具体相位或者边沿。

  • CPHA = 0:在时钟信号SCK的第一个跳变沿采样
  • CPHA = 1:在时钟信号SCK的第二个跳变沿采样

从以上介绍我们可以看出,SPI一共有四种传输模式,具体如下:

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

  • CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。

  • CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。

  • CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。

  • CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。

1.3 主从模式

SPI协议通过主机主动拉低拉低CS片选信号选择从机,也正因如此分出如下两种情况:

多根CS片选信号线:

通常,每个从机都需要一条单独的CS线。
如果要和特定的从机进行通讯,可以将相应的CS信号线拉低,并保持其他CS信号线的状态为高电平;如果同时将两个CSS信号线拉低,则可能会出现乱码,因为从机可能都试图在同一条MISO线上传输数据,最终导致接收数据乱码。

具体情况如下图(NSS即片选信号线CS):

在这里插入图片描述

菊花链(只有一根CS片选信号线):
在数字通信世界中,在设备信号(总线信号或中断信号)以串行的方式从一 个设备依次传到下一个设备,不断循环直到数据到达目标设备的方式被称为菊花链。

菊花链的最大缺点是因为是信号串行传输,所以一旦数据链路中的某设备发生故障的时候,它下面优先级较低的设备就不可能得到服务了。
另一方面,距离主机越远的从机,获得服务的优先级越低,所以需要安排好从机的优先级,并且设置总线检测器,如果某个从机超时,则对该从机进行短路,防止单个从机损坏造成整个链路崩溃的情况。

具体情况如下图:
在这里插入图片描述
在这里插入图片描述

  • SCK为时钟信号,8clks表示8个边沿信号
  • 其中D为数据,X为无效数据

参考资料:https://blog.csdn.net/u010632165/article/details/109460814

二、Flash(M25P16)

2.1 Flash简介

Flash 存储器(FLASH EEPROM)又称闪存,快闪。它是EEPROM的一种。它结合了ROM和RAM的长处。不仅具备电可擦除可编程(EEPROM)的特点,还不会断电丢失数据同时可以快速读取数据。Flash和EEPROM之间的主要区别在于擦除方式和工作原理。传统的EEPROM可以按字节或页进行擦除和编程,而Flash通常以扇区为单位进行擦除。此外,Flash的擦写寿命较短,需要注意控制擦写次数,而EEPROM没有这个限制。

2.2 M25P16芯片分析

在这里插入图片描述
该芯片的特性总结如上图:

  • 16Mbit存储空间(32个扇区 每个扇区256页 每页256字节)
  • 支持SPI接口
  • 数据可保存20年

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

由上图可以看出该芯片仅支持SPI模式0或模式3,同时该芯片传输方式为MSB,高字节先发。

在这里插入图片描述

由上图可以看出:

  • 在发出页编程指令前需要先发送写使能(WREN)指令
  • 页编程只能将1重置为0,因此在页编程之前需要先发送一次扇区擦除指令,将存储数据全部置为1
  • 同时在后续编程中,在发出扇区擦除指令前同样需要发送一次写使能指令
  • 该芯片有一个状态寄存器,我们可以通过读取状态寄存器的WIP位获取芯片是否处于忙状态,WEL位获取芯片是否处于写锁存。通过读状态寄存器我们可以在后续页编程和扇区擦除时不必强行等待手册所要求的操作最大时间。为了简写代码,本人未用到读状态寄存器的命令,而是在SE和PP指令发送后强行等待了手册所规定的最大等待时间。状态寄存器格式如下图:在这里插入图片描述

芯片相关指令如下图:

在这里插入图片描述
可以看出 页编程(PP)、扇区擦除(SE)、读(READ)指令均需要三个字节的地址数据(扇区地址+页地址+数据地址)

一些重要时间:

在这里插入图片描述

  • 由上图可以看出M25P16芯片要求片选信号要求在数据到来之前提前拉低至少5ns,同时在数据传输结束后至少继续拉低100ns才能被拉高
    在这里插入图片描述
  • 页编程指令发送后等待5ms
  • 扇区擦除指令发送后等待3s

时钟频率选择
在这里插入图片描述
由上图可以看出,除了读数据指令时钟最大速度为20MHz外其余指令均能达到50MHz,为了便于代码编写,本次项目在开发板50MHz的基础上进行了四分频,12.5MHz的时钟能够满足所用的所有指令。

2.3 项目所用指令时序

2.3.1 WREN(06h)

在执行PP,SE,BE,RESR指令前需要执行写使能操作。片选信号拉低后,开始执行写使能指令,接着传输一字节指令。指令发送完后,片选信号置为高电平.
在这里插入图片描述

  • 发送1字节指令

2.3.2 RDID(9Fh)

读ID指令一共会返回三个字节的数据,第一个数据为厂商ID(如下图20h代表的是意法半导体的厂商ID),后两个数据可以理解为器件ID。
在这里插入图片描述

在这里插入图片描述

  • 发送1字节指令
  • 接收3字节数据

2.3.3 READ(03h)

在这里插入图片描述

  • 发送1字节的指令
  • 发送3字节的地址
  • 接收1字节的数据

2.3.4 PP(02h)

在这里插入图片描述

2.3.5 SE(D8h)

在这里插入图片描述

三、状态机

接口模块:
SPI接口模块实际就是切割SPI时序图拆解出不同的状态,再在不同的状态中实现SPI时序,以写使能时序图为例:
在这里插入图片描述
我们可以拆分出四个状态:CS片选信号在数据传输前需要提前拉低的一段时间DELAY1(最少tSHSH 5ns),发送指令状态DATA,指令发送结束后片选信号延迟一段时间DELAY2,以及手册要求的片选恢复tSHSL(100ns)恢复状态HOLD。

控制模块:
该模块思路可以天马行空,博主就不给出状态机了,可以参考博主后续源码。

四、项目源码

控制模块:

/**************************************功能介绍***********************************
Date	: 
Author	: majiko
Version	: 
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module spi_control( 
    input 	wire				clk		,
    input 	wire				rst_n	,
    input   wire                read_req,
    input   wire                rdid_req,
    input   wire                wen_req ,
    input   wire                ready   ,
    input   wire    [23:0]      read_addr,  //需要读的数据地址
    input   wire    [7:0]       read_num,   //需要读出的数据0~255
    input   wire    [23:0]      wen_adder,//需要写入的数据地址
    input   wire    [7:0]       wen_num , //需要写入的数据数量
    input   wire    [7:0]       data_in,    //需要写入的数据
    input   wire                data_in_vld,
    output  wire    [7:0]       data_out,   //返回的数据
    output  wire                data_out_vld,

    //spi接口
    input   wire                miso    ,
    output  wire                cs_n    ,
    output  wire                sclk    ,
    output  wire                mosi        //主机输出给从机
);								 
//---------<参数定义>--------------------------------------------------------- 
localparam  IDLE   = 10'b0000000001,
            RDID   = 10'b0000000010,
            READ   = 10'b0000000100,
            WEN    = 10'b0000001000,
            WAIT   = 10'b0000010000,
            SE     = 10'b0000100000,
            TSE    = 10'b0001000000,
            PP     = 10'b0010000000,
            TPP    = 10'b0100000000,
            HOLD   = 10'b1000000000;
parameter   MAX_TXE = 28'd150_000_000,//3s
            MAX_TPP = 18'd150_000;//3ms

//---------<内部信号定义>-----------------------------------------------------
reg 	[9:0]	cstate     ;//现态
reg	    [9:0]	nstate     ;//次态
wire            idle2rdid;
wire            idle2read;
wire            idle2wen ;
wire            rdid2hold;
wire            read2hold;
wire            wen2wait;
wire            wait2se   ;
wire            se2tse   ;
wire            tse2wen  ;
wire            wait2pp   ;
wire            pp2tpp   ;
wire            tpp2idle ;
wire            hold2idle;

reg			[2:0]	cnt_bit	   	;
wire				add_cnt_bit	;
wire				end_cnt_bit	;

reg			[3:0]	cnt_byte	   	;
wire				add_cnt_byte	;
wire				end_cnt_byte	;
reg         [3:0]   num             ;

//子模块例化参数
reg         [7:0]   master_data_in  ;
wire                master_data_in_vld;
wire                done            ;
wire        [7:0]   master_data_out ;
wire                master_data_out_vld;
reg         [23:0]  id_data         ;
wire                master_ready    ;

//数据接收
reg         rdid_flag;
wire      [7:0]  read_data;
wire             read_data_vld;
reg              read_flag;
reg			[2:0]	cnt_read	   	;
wire				add_cnt_read	;
wire				end_cnt_read	;

//tse延时
reg			[27:0]	cnt_tse	   	;
wire				add_cnt_tse	;
wire				end_cnt_tse	;

//tpp延时
reg			[17:0]	cnt_tpp	   	;
wire				add_cnt_tpp	;
wire				end_cnt_tpp	;

//wen跳转使能
reg           wen_flag;

//数据寄存
reg             data_in_r;

//****************************************************************
//                  状态机
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cstate <= IDLE;
    end 
    else begin 
        cstate <= nstate;
    end 
end

//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
    case(cstate)
        IDLE : begin
                    if (idle2rdid) begin
                        nstate = RDID;
                    end
                    else if (idle2read) begin
                        nstate = READ;
                    end
                    else if (idle2wen) begin
                        nstate = WEN;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        RDID : begin
                    if (rdid2hold) begin
                        nstate = HOLD;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        READ : begin
                    if (read2hold) begin
                        nstate = HOLD;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        WEN  : begin
                    if (wen2wait) begin
                        nstate = WAIT;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        WAIT :  begin
                    if (wait2se) begin
                        nstate = SE;
                    end
                    else if (wait2pp) begin
                        nstate = PP;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        SE   : begin
                    if (se2tse) begin
                        nstate = TSE;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        TSE  : begin
                    if (tse2wen) begin
                        nstate = WEN;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        PP   : begin
                    if (pp2tpp) begin
                        nstate = TPP;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        TPP  : begin
                    if (tpp2idle) begin
                        nstate = IDLE;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        HOLD : begin
                    if (hold2idle) begin
                        nstate = IDLE;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        default : nstate = IDLE;
    endcase
end


    assign idle2rdid = cstate == IDLE && rdid_req;
    assign idle2read = cstate == IDLE && read_req;
    assign idle2wen  = cstate == IDLE && wen_req ;
    assign rdid2hold = cstate == RDID && end_cnt_byte;
    assign read2hold = cstate == READ && end_cnt_byte;
    assign wen2wait  = cstate == WEN  && end_cnt_byte;
    assign wait2se   = cstate == WAIT  && done && ~wen_flag;
    assign se2tse    = cstate == SE   && end_cnt_byte;
    assign tse2wen   = cstate == TSE  && end_cnt_tse;
    assign wait2pp   = cstate == WAIT  && done && wen_flag;
    assign pp2tpp    = cstate == PP   && end_cnt_byte;
    assign tpp2idle  = cstate == TPP  && end_cnt_tpp;
    assign hold2idle = cstate == HOLD && done;

            
//第三段:描述输出,时序逻辑或组合逻辑皆可
//****************************************************************
//                  byte计数器
//**************************************************************** 


always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_bit <= 3'd0;
    end 
    else if(add_cnt_bit)begin 
        if(end_cnt_bit)begin 
            cnt_bit <= 3'd0;
        end
        else begin 
            cnt_bit <= cnt_bit + 1'b1;
        end 
    end
end 

assign add_cnt_bit = ((cstate == RDID)||(cstate == READ)||(cstate == WEN)||(cstate == SE)||(cstate == PP)) && master_ready;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 7;



always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_byte <= 4'd0;
    end 
    else if(add_cnt_byte)begin 
        if(end_cnt_byte)begin 
            cnt_byte <= 4'd0;
        end
        else begin 
            cnt_byte <= cnt_byte + 1'b1;
        end 
    end
end 

assign add_cnt_byte = end_cnt_bit;
assign end_cnt_byte = add_cnt_byte && cnt_byte == num-1;

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        num <= 1;
    end
    else if (cstate == RDID) begin
        num <= 4;
    end
    else if (cstate == READ) begin
        num <= 4 + read_num;
    end
    else if (cstate == WEN) begin
        num <= 1;
    end
    else if (cstate == SE) begin
        num <= 4;
    end
    else if (cstate == PP) begin
        num <= 5;
    end
    else begin
        num <= 1;
    end
end

//****************************************************************
//                      指令发送
//****************************************************************
always @(*) begin
    case (cstate)
        RDID : case (cnt_byte)
                    0 : master_data_in = 8'h9f;
                    1 : master_data_in = 8'hff;
                    2 : master_data_in = 8'hff;
                    3 : master_data_in = 8'hff;
                    default: master_data_in = 8'hff;
               endcase
        READ : case (cnt_byte)
                    0 : master_data_in = 8'h03;
                    1 : master_data_in = read_addr[23:16];
                    2 : master_data_in = read_addr[15:8];
                    3 : master_data_in = read_addr[7:0];
                    default: master_data_in = 8'hff;
               endcase
        WEN  : case (cnt_byte)
                    0 : master_data_in = 8'h06;
                    default: master_data_in = 8'hff;
               endcase
        SE   : case (cnt_byte)
                    0 : master_data_in = 8'hd8;
                    1 : master_data_in = read_addr[23:16];
                    2 : master_data_in = read_addr[15:8];
                    3 : master_data_in = read_addr[7:0];
                    default: master_data_in = 8'hff;
               endcase
        PP   : case (cnt_byte)
                    0 : master_data_in = 8'h02;
                    1 : master_data_in = read_addr[23:16];
                    2 : master_data_in = read_addr[15:8];
                    3 : master_data_in = read_addr[7:0];
                    4 : master_data_in = 8'hF1;
                    default: master_data_in = 8'hff;
               endcase
    endcase
end
assign  master_data_in_vld = add_cnt_byte;
//****************************************************************
//                  数据寄存
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        data_in_r <= 0;
    end
    else if (data_in_vld) begin
        data_in_r <= data_in;
    end
    else begin
        data_in_r <= data_in_r;
    end
end

//****************************************************************
//                  滤除无用数据
//****************************************************************
reg			[3:0]	cnt_out	   	;
wire				add_cnt_out	;
wire				end_cnt_out	;
reg         [3:0]   num_out     ;

always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_out <= 'd0;
    end 
    else if (hold2idle || tpp2idle) begin
        cnt_out <= 'd0;
    end
    else if(add_cnt_out)begin 
        if(end_cnt_out)begin 
            cnt_out <= 'd0;
        end
        else begin 
            cnt_out <= cnt_out + 1'b1;
        end 
    end
end 

assign add_cnt_out =  master_data_out_vld;
assign end_cnt_out = add_cnt_out && cnt_out == num_out - 1;

always @(*) begin
    if (!rst_n) begin
        num_out = 0;
    end
    else if (cstate == RDID) begin
        num_out = 1;
    end
    else if (cstate == READ) begin
        num_out = 4;
    end
end
//****************************************************************
//                      记录操作
//****************************************************************
reg       [1:0]      op_flag;
`define         OP_RDID 1
`define         OP_READ 2    
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        op_flag <= 0;
    end
    else if (idle2rdid) begin
        op_flag <= `OP_RDID;
    end
    else if (idle2read) begin
        op_flag <= `OP_READ;
    end
    else if (hold2idle) begin
        op_flag <= 0;
    end
end
//****************************************************************
//                  记录数据
//****************************************************************
//读id使能
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        rdid_flag <= 0;
    end
    else if (end_cnt_out && op_flag == `OP_RDID) begin
        rdid_flag <= 1;
    end
    else if (hold2idle) begin
        rdid_flag <= 0;
    end
end
//读read使能
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        read_flag <= 0;
    end
    else if (end_cnt_out && op_flag == `OP_READ) begin
        read_flag <= 1;
    end
    else if (hold2idle) begin
        read_flag <= 0;
    end
end
assign data_out = master_data_out;
assign data_out_vld = (rdid_flag || read_flag) && master_data_out_vld;
//****************************************************************
//                      WEN跳转使能
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        wen_flag <= 0;
    end
    else if (tse2wen) begin
        wen_flag <= 1;
    end
    else if (tpp2idle) begin
        wen_flag <= 0;
    end
end
//****************************************************************
//                      延时开始使能
//****************************************************************
reg            delay_flag;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        delay_flag <= 0;
    end
    else if ((cstate == TSE) && done) begin
        delay_flag <= 1;
    end
    else if ((cstate == TPP) && done) begin
        delay_flag <= 1;
    end
    else if (end_cnt_tpp || end_cnt_tse) begin
        delay_flag <= 0;
    end
end

//****************************************************************
//                      TSE延时
//****************************************************************
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_tse <= 'd0;
    end 
    else if(add_cnt_tse)begin 
        if(end_cnt_tse)begin 
            cnt_tse <= 'd0;
        end
        else begin 
            cnt_tse <= cnt_tse + 1'b1;
        end 
    end
end 

assign add_cnt_tse = (cstate == TSE) && delay_flag;
assign end_cnt_tse = add_cnt_tse && cnt_tse == MAX_TXE - 1;

//****************************************************************
//                          TPP延时
//****************************************************************
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_tpp <= 'd0;
    end 
    else if(add_cnt_tpp)begin 
        if(end_cnt_tpp)begin 
            cnt_tpp <= 'd0;
        end
        else begin 
            cnt_tpp <= cnt_tpp + 1'b1;
        end 
    end
end 

assign add_cnt_tpp = (cstate == TPP) && delay_flag;
assign end_cnt_tpp = add_cnt_tpp && cnt_tpp == MAX_TPP - 1;

//****************************************************************
//                      子模块例化
//****************************************************************
spi_master spi_master_inst( 
    /*input 	wire		*/		.clk		    (clk),
    /*input 	wire		*/		.rst_n	        (rst_n),
    /*input   wire    [7:0] */      .data_in        (master_data_in),
    /*input   wire          */      .data_in_vld    (master_data_in_vld),   
    /*input   wire          */      .miso           (miso),
    /*output  wire          */      .master_ready   (master_ready),
    /*output  reg     [7:0] */      .data_out       (master_data_out),
    /*output  wire          */      .data_out_vld   (master_data_out_vld),  
    /*output  wire          */      .done           (done),
    /*output  reg           */      .cs_n           (cs_n),
    /*output  reg           */      .sclk           (sclk),
    /*output  reg           */      .mosi           (mosi)//主机输出给从机
);

endmodule

接口模块:

/**************************************功能介绍***********************************
Date	: 
Author	: majiko
Version	: 
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module spi_master( 
    input 	wire				clk		,
    input 	wire				rst_n	,
    input   wire    [7:0]       data_in ,
    input   wire                data_in_vld,
    input   wire                miso    ,
    output  wire                master_ready,
    output  reg     [7:0]       data_out,
    output  wire                data_out_vld,
    output  wire                done    ,
    output  reg                 cs_n    ,
    output  reg                 sclk    ,
    output  reg                 mosi        //主机输出给从机
);								 
//---------<参数定义>--------------------------------------------------------- 
parameter   DIV         = 4;//时钟分频倍数
parameter   MAX100      = 5;//100ns

localparam  IDLE        = 5'b00001,//
            DELAY_ONE   = 5'b00010,//
            DATA        = 5'b00100,//
            DELAY_TWO   = 5'b01000,//
            HOLD        = 5'B10000;
//---------<内部信号定义>-----------------------------------------------------
//fifo数据缓存
wire                fifo_empty;
wire                fifo_full;
wire        [7:0]   fifo_data;
wire                fifo_rd_req;
//时钟分频
reg			[1:0]	cnt_div	   	;
wire				add_cnt_div	;
wire				end_cnt_div	;
//状态机参数定义
reg 	    [4:0]	cstate     ;//现态
reg	        [4:0]	nstate     ;//次态
wire                idle2delay_one;
wire                delay_one2data;
wire                data2delay_two;
wire                delay_two2hold;
wire                hold2idle     ;
//bit计数器参数
reg			[2:0]	cnt_bit	   	;
wire				add_cnt_bit	;
wire				end_cnt_bit	;

//延时计数器参数
reg			[2:0]	cnt_delay	   	;
wire				add_cnt_delay	;
wire				end_cnt_delay	;


//****************************************************************
//                      寄存数据缓存
//****************************************************************
fifo_rx	fifo_rx_inst (
	.aclr       ( ~rst_n         ),
	.clock      ( clk           ),
	.data       ( data_in       ),
    .q          ( fifo_data     ),
	.rdreq      ( fifo_rd_req   ),
	.wrreq      ( data_in_vld   ),
	.empty      ( fifo_empty    ),
	.full       ( fifo_full     )

	);
// assign  fifo_rd_req = !fifo_empty && data2delay_two;//因为采用了浅显模式,如果使用delay_one2data会导致第一个数据丢失
//提前一点读取新数据,让fifo的empty信号早点更新
//(根据仿真调整的,仿真体现出来的结果是,fifo的empty信号晚了一个时钟周期,导致在DATA状态多待了一个时钟周期1
//从而导致SCLK信号有毛刺,以及div计数器未归0)
assign  fifo_rd_req = !fifo_empty && (cnt_bit == 7) &&(cnt_div == DIV - 2) ;//为了使fifo连续读取数据
//****************************************************************
//                      状态机
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cstate <= IDLE;
    end 
    else begin 
        cstate <= nstate;
    end 
end

//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
    case(cstate)
        IDLE      : begin
                        if (idle2delay_one) begin
                            nstate = DELAY_ONE;
                        end
                        else begin
                            nstate = cstate;
                        end
                    end
        DELAY_ONE : begin
                        if (delay_one2data) begin
                            nstate = DATA;
                        end
                        else begin
                            nstate = cstate;
                        end
                    end
        DATA      : begin
                        if (data2delay_two) begin
                            nstate = DELAY_TWO;
                        end
                        else begin
                            nstate = cstate;
                        end
                    end
        DELAY_TWO : begin
                        if (delay_two2hold) begin
                            nstate = HOLD;
                        end
                        else begin
                            nstate = cstate;
                        end
                    end
        HOLD      : begin
                        if (hold2idle) begin
                            nstate = IDLE;
                        end
                        else begin
                            nstate = cstate;
                        end
                    end
        default : nstate = IDLE;
    endcase
end
assign  idle2delay_one = cstate == IDLE      &&  !fifo_empty;
assign  delay_one2data = cstate == DELAY_ONE &&  1;
assign  data2delay_two = cstate == DATA      &&  fifo_empty;
assign  delay_two2hold = cstate == DELAY_TWO &&  1;
assign  hold2idle      = cstate == HOLD      &&  end_cnt_delay;
            
//第三段:描述输出,时序逻辑或组合逻辑皆可
//****************************************************************
//                  时钟分频
//****************************************************************
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_div <= 'd0;
    end 
    else if(add_cnt_div)begin 
        if(end_cnt_div)begin 
            cnt_div <= 'd0;
        end
        else begin 
            cnt_div <= cnt_div + 1'b1;
        end 
    end
end 

assign add_cnt_div = cstate == DATA;
assign end_cnt_div = add_cnt_div && cnt_div == DIV - 1;
//产生时钟sclk
always @(*) begin
    if (!rst_n) begin
        sclk =  1;
    end
    else if (cstate == DATA && (cnt_div < (DIV >> 1))) begin
        sclk =  0;
    end
    else begin
        sclk =  1;
    end
end
//****************************************************************
//                  bit计数器
//****************************************************************
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_bit <= 'd0;
    end 
    else if(add_cnt_bit)begin 
        if(end_cnt_bit)begin 
            cnt_bit <= 'd0;
        end
        else begin 
            cnt_bit <= cnt_bit + 1'b1;
        end 
    end
end 

assign add_cnt_bit = end_cnt_div;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 7;
//****************************************************************
//                  mosi数据传输
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        mosi <= 1;
    end
    else if (cstate == DATA && (cnt_div == 0)) begin
        mosi <= fifo_data[7-cnt_bit];
    end
    else begin
      mosi   <= mosi;
    end
end
//****************************************************************
//                  csn产生
//****************************************************************
always @(*) begin
    if (!rst_n) begin
        cs_n = 1;
    end
    else if (cstate == IDLE || cstate == HOLD) begin
        cs_n = 1;
    end
    else begin
        cs_n = 0;
    end
end
//****************************************************************
//                 保持延迟
//****************************************************************
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_delay <= 'd0;
    end 
    else if(add_cnt_delay)begin 
        if(end_cnt_delay)begin 
            cnt_delay <= 'd0;
        end
        else begin 
            cnt_delay <= cnt_delay + 1'b1;
        end 
    end
end 

assign add_cnt_delay = cstate == HOLD;
assign end_cnt_delay = add_cnt_delay && cnt_delay == MAX100 - 1;
//****************************************************************
//                          数据接收 MISO
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        data_out <= 0;
    end
    else if (cstate == DATA && (cnt_div == DIV >> 1)) begin
        data_out <= {data_out[6:0],miso};
    end
end
assign data_out_vld = end_cnt_bit;
assign done         = hold2idle;
assign master_ready = ~fifo_full;

endmodule

串口输出模块:

/**************************************功能介绍***********************************
Date	: 
Author	: majiko
Version	: 
Description: 串口发送模块
*********************************************************************************/

//---------<模块及端口声名>------------------------------------------------------
module uart_tx#
(
    parameter   BPS = 115200,
    parameter   CLK_FRE = 50_000_000,
    parameter   CHECK_BIT = "NONE"//NONE 不校验 DDO奇数 EVEN偶数

)
( 
    input 	wire				        clk		,
    input 	wire				        rst_n	,
    input   wire                        tx_data_vld,//数据发送标志
    input   wire    [7:0]               tx_data  ,//发送数据
    output  reg                         tx       , 
    output  wire                        ready       //准备好发送
);								 
//---------<参数定义>--------------------------------------------------------- 
//状态机参数定义
localparam  IDLE   = 5'b00001,//空闲状态
            START  = 5'b00010,//发送开始位
            DATA   = 5'b00100,//发送8bit数据
            CHECK  = 5'b01000,//发送校验位
            STOP   = 5'b10000;//发送结束位

parameter   MAX_1bit = CLK_FRE/BPS;//传输1bit数据所需时间

//---------<内部信号定义>-----------------------------------------------------
//状态机参数定义
reg 	[4:0]	cstate     ;//现态
reg	    [4:0]	nstate     ;//次态    

wire    idle2start;
wire    start2data;
wire    data2check ;
wire    data2stop;
wire    check2stop;
wire    stop2idle ;

wire             temp;//奇偶校验位

//计数器参数定义
reg         [3:0]   num             ;
reg			[8:0]	cnt_start	   	;
wire				add_cnt_start	;
wire				end_cnt_start	;

reg			[11:0]	cnt_data	   	;
wire				add_cnt_data	;
wire				end_cnt_data	;

reg			[2:0]	cnt_num	   	;
wire				add_cnt_num	;
wire				end_cnt_num	;

reg			[8:0]	cnt_stop	   	;
wire				add_cnt_stop	;
wire				end_cnt_stop	;

reg         [7:0]   tx_data_r       ;

//****************************************************************
//                      发送模块状态机
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cstate <= IDLE;
    end 
    else begin 
        cstate <= nstate;
    end 
end

//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
    case(cstate)
        IDLE  : begin
                    if (idle2start) begin
                        nstate = START;
                    end
                    else begin
                        nstate = cstate;
                    end            
                end
        START : begin
                    if (start2data) begin
                        nstate = DATA;
                    end
                    else begin
                        nstate = cstate;
                    end            
                end
        DATA  : begin
                    if (data2check) begin
                        nstate = CHECK;
                    end
                    else if (data2stop) begin
                        nstate = STOP;
                    end
                    else begin
                        nstate = cstate;
                    end            
                end
        CHECK : begin
                    if (check2stop) begin
                        nstate = STOP;
                    end
                    else begin
                        nstate = cstate;
                    end
                end
        STOP  : begin
                    if (stop2idle) begin
                        nstate = IDLE;
                    end
                    else begin
                        nstate = cstate;
                    end            
                end
        default : ;
    endcase
end

assign idle2start = cstate == IDLE  && tx_data_vld ; //接收到vld信号开始发送数据
assign start2data = cstate == START && end_cnt_num;//计时发送1bit后跳转
assign data2check = cstate == DATA  && end_cnt_num && CHECK_BIT != "NONE";//计时发送8bit数据后,需要校验位
assign data2stop  = cstate == DATA  && end_cnt_num && CHECK_BIT == "NONE";//不需要校验位
assign check2stop = cstate == CHECK && end_cnt_num;//发送1bit校验位
assign stop2idle  = cstate == STOP  && end_cnt_num;//计时发送1bit数据后跳转



//第三段:描述输出,时序逻辑或组合逻辑皆可
always @(*) begin
    if (!rst_n) begin
        tx <= 1;
    end
    else case (cstate)
        IDLE : tx = 1;//拉高
        START: tx = 0;//拉低发送开始位
        DATA: tx = tx_data_r[cnt_num];//发送数据
        CHECK: tx = temp;//奇偶校验位
        STOP : tx = 1;//拉高发送结束位
        default: tx = 1;
    endcase
end

assign ready = cstate == IDLE;

//****************************************************************
//              数据寄存
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        tx_data_r <= 8'd0;
    end
    else if (tx_data_vld) begin
        tx_data_r <= tx_data;
    end
end
//****************************************************************
//                  奇偶校验位
//****************************************************************

assign    temp = (CHECK_BIT == "DDO")? ~^tx_data_r:^tx_data_r;         
//****************************************************************
//                          1bit数据
//**************************************************************** 



always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_data <= 12'd0;
    end 
    else if(add_cnt_data)begin 
        if(end_cnt_data)begin 
            cnt_data <= 12'd0;
        end
        else begin 
            cnt_data <= cnt_data + 1'b1;
        end 
    end
end 

assign add_cnt_data = cstate != IDLE;
assign end_cnt_data = add_cnt_data && cnt_data == MAX_1bit-1;
//****************************************************************
//                          选择数据位计数器
//****************************************************************


always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_num <= 3'd0;
    end 
    else if(add_cnt_num)begin 
        if(end_cnt_num)begin 
            cnt_num <= 3'd0;
        end
        else begin 
            cnt_num <= cnt_num + 1'b1;
        end 
    end
end 

assign add_cnt_num = end_cnt_data;
assign end_cnt_num = add_cnt_num && cnt_num == num - 1;


//****************************************************************
//                  数据控制
//****************************************************************
always @(*) begin
    case (cstate)
        IDLE :  num = 1;     
        START:  num = 1;
        DATA :  num = 8;
        CHECK:  num = 1;
        STOP :  num = 1;
        default: num = 1;
    endcase
end


endmodule

按键消抖模块:

/**************************************功能介绍***********************************
Date	: 
Author	: majiko
Version	: 
Description: 按键消抖的参数化模块(高电平有效)
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module key_debounce 
#(parameter N = 4)
( 
    input 	wire				clk		,
    input 	wire				rst_n	,
    input   wire    [N-1:0]     key_in  ,//N个按键输入
    output  reg     [N-1:0]     key_out  //N个按键输出

);								 
//---------<参数定义>--------------------------------------------------------- 
parameter   MAX_20MS = 20'd999_999;

//---------<内部信号定义>-----------------------------------------------------
//延时计数器参数
reg			[19:0]	cnt_delay	   	;
wire				add_cnt_delay	;
wire				end_cnt_delay	;

//边沿检测参数
reg     [N-1:0] key_in_r0;
reg     [N-1:0] key_in_r1;
reg     [N-1:0] key_in_r2;

wire            nedge   ;
wire            podge   ;

reg         flag;//flag驱动使能

//****************************************************************
//                     计数器延迟20ms
//****************************************************************    
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_delay <= 20'd0;
    end 
    else if(add_cnt_delay)begin 
        if(end_cnt_delay)begin 
            cnt_delay <= 20'd0;
        end
        else begin 
            cnt_delay <= cnt_delay + 1'b1;
        end 
    end
end 

assign add_cnt_delay = flag;
assign end_cnt_delay = add_cnt_delay && cnt_delay == MAX_20MS;

//****************************************************************
//                  下降沿检测
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        key_in_r0 <= {N{1'b1}};
        key_in_r1 <= {N{1'b1}};
        key_in_r2 <= {N{1'b1}};
    end
    else begin
        key_in_r0 <= key_in;
        key_in_r1 <= key_in_r0;
        key_in_r2 <= key_in_r1;
    end
end 
assign nedge = |(~key_in_r1&key_in_r2);
assign podge = |(key_in_r1&~key_in_r2);

//****************************************************************
//              flag使能
//****************************************************************

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        flag <= 0;
    end
    else if (nedge) begin
        flag <= 1'b1;
    end
    else if (end_cnt_delay) begin
        flag <= 0;
    end
    else begin
        flag <= flag;
    end
end   

//****************************************************************
//              赋值输出
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        key_out <= {N{1'b0}};
    end
    else if (end_cnt_delay) begin
        key_out <= ~key_in_r1;
    end
    else begin
        key_out <= {N{1'b0}};
    end
end
endmodule

顶层模块:

/**************************************功能介绍***********************************
Date	: 
Author	: majiko
Version	: 
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module top( 
    input 	wire				clk		,
    input 	wire				rst_n	,
    input   wire    [2:0]       key_in  ,

    input   wire                miso    ,
    output  wire                cs_n    ,
    output  wire                sclk    ,
    output  wire                mosi    ,
    output  wire                tx      

);								 
//---------<参数定义>--------------------------------------------------------- 
wire    [2:0]   key_debounce;
wire    [7:0]   data_out    ;
wire            data_out_vld;
wire    [7:0]   tx_data     ;
wire            tx_data_vld ;
wire            ready       ;
wire            fifo_empty  ;
wire            fifo_full   ;
//---------<内部信号定义>-----------------------------------------------------
key_debounce 
#(.N(3))
key_debounce_inst
( 
    /*input 	wire			*/	.clk		(clk),
    /*input 	wire			*/	.rst_n	    (rst_n),
    /*input   wire    [N-1:0]   */  .key_in     (key_in),//N个按键输入
    /*output  reg     [N-1:0]   */  .key_out    (key_debounce)//N个按键输出

);

spi_control spi_control_inst( 
    /*input 	wire		*/		.clk		    (clk),
    /*input 	wire		*/		.rst_n	        (rst_n),
    /*input   wire          */      .read_req       (key_debounce[1]),
    /*input   wire          */      .rdid_req       (key_debounce[0]),
    /*input   wire          */      .wen_req        (key_debounce[2]),
    /*input   wire          */      .ready          (),
    /*input   wire    [23:0]*/      .read_addr      (24'h000000),  //需要读的数据地址
    /*input   wire    [7:0] */      .read_num       (3),   //需要读出的数据0~255
    /*input   wire    [7:0] */      .data_in        (8'h11),
    /*input   wire          */      .data_in_vld    (1),
    /*output  wire    [7:0] */      .data_out       (data_out),   //返回的数据
    /*output  wire          */      .data_out_vld   (data_out_vld),

    /*//spi接口*/
    /*input   wire          */      .miso           (miso),
    /*output  wire          */      .cs_n           (cs_n),
    /*output  wire          */      .sclk           (sclk),
    /*output  wire          */      .mosi           (mosi)    //主机输出给从机
);

uart_tx#
(
    .BPS         (115200)     ,
    .CLK_FRE     (50_000_000) ,
    .CHECK_BIT   ("NONE")     //NONE 不校验 DDO奇数 EVEN偶数

)uart_tx_inst
( 
    /*input   wire			*/	  .clk		      (clk),
    /*input   wire			*/	  .rst_n	      (rst_n),
    /*input   wire          */    .tx_data_vld    (tx_data_vld),
    /*input   wire    [7:0] */    .tx_data        (tx_data),
    /*output  reg           */    .tx             (tx), 
    /*output  wire          */    .ready          (ready)
);

fifo_tx	fifo_tx_inst (
	.aclr       ( ~rst_n ),
	.clock      ( clk ),
	.data       ( data_out ),
	.wrreq      ( data_out_vld ),
    .q          ( tx_data ),
    .rdreq      ( tx_data_vld ),
	.empty      ( fifo_empty ),
	.full       ( fifo_full )
	);
assign  tx_data_vld = ready && ~fifo_empty;
    
    
    
endmodule

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

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

相关文章

什么是著作权?对此你了解多少?(二)

上一篇&#xff0c;已经为大家介绍了著作权的概念、著作权的分类以及著作权的内容等相关基础性认识。那么著作权如何取得呢&#xff1f; 我国采取的自动取得原则&#xff0c;当作品创作完成后&#xff0c;只要符合法律上作品的条件&#xff0c;著作权就产生了。著作权人可以申请…

百分点科技受邀参加“一带一路”国际合作高峰论坛

10月17-18日&#xff0c;第三届“一带一路”国际合作高峰论坛在北京成功举行。作为新一代信息技术出海企业代表&#xff0c;百分点科技董事长兼CEO苏萌受邀出席高峰论坛开场活动——“一带一路”企业家大会&#xff0c;与来自82个国家和地区的企业或机构、有关国际组织、经济机…

ArmSoM-W3之RK3588 MPP环境配置

1. 简介 瑞芯微提供的媒体处理软件平台&#xff08;Media Process Platform&#xff0c;简称 MPP&#xff09;是适用于瑞芯微芯片系列的 通用媒体处理软件平台。该平台对应用软件屏蔽了芯片相关的复杂底层处理&#xff0c;其目的是为了屏蔽不 同芯片的差异&#xff0c;为使用者…

el-input: 把不符合正则校验的值动态清空,只保留符合的值

<el-input v-model"form.profit" placeholder"请输入授权专利新增利润" input"handleInput" clearable />/*** 不符合正则校验,清空*/const handleInput () > {if (form.value.profit) {if (!/^\d*\.?\d*$/.test(form.value.profit))…

Error- Loaded runtime CuDNN library: 8.0.4 but source was compiled with: 8.1.0.

运行tensorflow2.5训练代码之后会出现如下报错&#xff1a; Loaded runtime CuDNN library: 8.0.4 but source was compiled with: 8.1.0. CuDNN library needs to have matching major version and equal or higher minor version. If using a binary install, upgrade your…

es6(三)——常用es6(函数、数组、对象的扩展)

ES6的系列文章目录 第一章 Python 机器学习入门之pandas的使用 文章目录 ES6的系列文章目录0、数值的扩展一、函数的扩展1、函数的默认值2、函数的reset参数 二、数组的扩展1. 将对象转成数组的Array.from()2. 将对象转成数组的Array.from()3. 实例方法 find()&#xff0c;fin…

24.项目开发之量化交易抓取数据QuantTradeData(三)

后端业务&#xff1a;分页查询股票列表基础信息 需求说明 将来股票列表基础信息会在前端页面进行展示&#xff0c;成千上万条数据是不会一次性展示在页面的&#xff0c;而是分页展示。 环境搭建 pom.xml导入依赖 <dependency><groupId>com.github.pagehelper<…

文心大模型4.0开启测试申请,百度智能云为大模型落地五大需求提供最优解

写在前面 面向企业客户启动文心大模型4.0 API调用服务测试申请&#xff0c;服务超过17000家客户&#xff0c;在各行各业的近500个场景中进行大模型应用落地探索……自今年3月面世以来&#xff0c;百度智能云千帆大模型平台作为全球首个一站式企业级大模型平台&#xff0c;为业…

PS修改背景色,线框底图

1、打开图片&#xff0c;ctrlj复制一层 2、图像-调整-反相 3、ctrll调整色阶&#xff0c;将中间的色块向右移&#xff0c;灰色线和字体的会变黑

移动端签名组件封装 借用插件 vue-esign

目录 需求实现讲解工具 - 图片旋转、base64 转换为 file 对象组件封装组件全局注册组件使用效果展示 需求 移动端需要实现手机横屏手写签名并上传签名图片功能。 实现讲解 vue-esign 插件文档地址 https://www.npmjs.com/package/vue-esign SignCanvas 组件封装原理&#xff1a…

【数据结构】830+848真题易错题汇总(自用)

【数据结构】830848易错题汇总(10-23) 文章目录 【数据结构】830848易错题汇总(10-23)选择题填空题判断题简答题&#xff1a;应用题&#xff1a;算法填空题&#xff1a;算法设计题&#xff1a;(待补) 选择题 1、顺序栈 S 的 Pop(S, e)操作弹出元素 e&#xff0c;则下列(C )是正…

虹科分享 | 选择SAS还是NVMe?虹科网络基础带您一探究竟!

存储架构师需要通过确保他们选择的存储解决方案提供支持其生态系统所需的安全性、稳定性、可扩展性和管理特性来应对当今的业务挑战。当他们考虑采用新的存储技术时&#xff0c;在采用新技术之前&#xff0c;他们应该权衡和审查一些基本的考虑因素。新的存储协议不断进入市场&a…

Postman for Mac - 轻松进行API测试的利器

在当今的数字化时代&#xff0c;应用程序编程接口(API)已成为推动软件创新和互操作性的核心动力。API测试作为确保服务质量的重要一环&#xff0c;也越来越受到开发者的重视。其中&#xff0c;Postman作为一款极其流行的API测试工具&#xff0c;其简洁易用的界面和强大的功能&a…

美妆品牌如何有效利用软文推广引流获客

近年来随着美妆品牌的转型升级和居民消费观念的转变&#xff0c;美妆行业取得了更大发展空间&#xff0c;新产品不断涌现&#xff0c;消费者拥有更多选择&#xff0c;那么在竞争激烈的市场中美妆品牌如何才能突破重围&#xff0c;找出新的价值增长点呢&#xff1f; 一、 细分消…

NewStarCTF2023week3-阳光开朗大男孩

下载附件解压得到两个txt文本 secret.txt一看很明显是核心价值观编码 解码得到 this_password_is_s000_h4rd_p4sssw0rdddd flag.txt最开始没看出来是什么&#xff0c;主要是之前没遇到过 题目提示&#xff1a;我是阳光开朗大男孩&#xff5e;阳光开朗大男孩&#xff5e; 我…

线程池工作原理

1&#xff1a;处理Runnable 任务的方法 package ThreadPooITest;import java.util.concurrent.*;//目标&#xff1a;线程池创建 public class ThreadPoolTest {public static void main(String[] args) {//1:通过ThreadPoolExectorExecutorService pool new ThreadPoolExecuto…

数据模型设计必读方法论!很实用

数据架构的重要构件之一是数据模型&#xff0c;当然从数据架构的视角来说的数据模型是指企业级数据模型。本篇文章更多是讨论如何设计和管理数据模型&#xff0c;此处的数据模型是泛指在组织中通过数据建模的过程&#xff0c;来发现、分析和确定数据需求范围&#xff0c;并用于…

6.(vue3.x+vite)路由传参query与params区别

前端技术社区总目录(订阅之前请先查看该博客) 效果截图 一:路由传参有两种方式:params与query params与query区别 1:param,路由带“/”,query带“?” 2:query传过来的参数会显示到地址栏中 而params传过来的参数可以显示参数或隐藏参数到地址栏中(vue-router 4.1.4不…

MSVCR80.DLL 丢失修复方法:完美解决你的问题!

MSVCR80.DLL 是 Microsoft Visual C Redistributable Package 中的一个动态链接库文件&#xff0c;它扮演着非常重要的角色。然而&#xff0c;当你在运行某些应用程序时&#xff0c;可能会遇到“MSVCR80.DLL 丢失”错误。这时候你就需要采取一些措施来解决这个问题了&#xff0…

待办任务清单app哪个软件好用?

手机成了我们生活的延伸&#xff0c;而对于繁忙的工作节奏&#xff0c;一款好用的任务提醒软件就如同一位贴心的助手&#xff0c;时刻提醒我们的待办任务。在众多软件中&#xff0c;敬业签是一款比较不错的记录待办任务清单的工具。 在手机上&#xff0c;打开敬业签&#xff0…