基于FPGA的I2C读写EEPROM

news2024/12/23 4:24:11

文章目录

  • 前言
  • 一、I2C协议
    • 1.1 I2C协议简介
    • 1.2 物理层
    • 1.3 协议层
  • 二、EEPROM
    • 2.1 型号及硬件规格
    • 2.2 各种读写时序
  • 三、状态机设计
  • 四、项目源码:
  • 五、实现效果
  • 参考资料

前言

本次项目所用开发板FPGA芯片型号为:EP4CE6F17C8 EEPROM芯片型号为:24LC04B UART串口设置为:波特率115200
无校验位

本次项目仅实现了EEPROM的单字节读写。若要实现连续读写可以在下文的EEPROM控制模块设置计数器控制读写字数。

一、I2C协议

1.1 I2C协议简介

I2C总线是Philips公司在八十年代初推出的一种同步、串行、半双工 的总线,主要用于近距离、低速的芯片之间的通信;I2C总线有两根双向的信号线,一根数据线SDA用于收发数据,一根时钟线SCL用于通信双方时钟的同步;I2C总线硬件结构简单,简化了PCB布线,降低了系统成本,提高了系统可靠性,因此在各个领域得到了广泛应用。

I2C是一种多主机多从机总线,通过呼叫应答的方式实现主机与从机间的通信。每一个I2C设备都有一个唯一的7位地址,也就是说同一根线上最多挂载127个I2C从机设备(主机自己占一个地址)。主机有权发起和结束一次通信,从机只能被动呼叫;当总线上有多个主机同时启用总线时,I2C也具备冲突检测和仲裁的功能来防止错误产生。

1.2 物理层

在这里插入图片描述

  • I2C支持多主机多从机

  • I2C有两条线,一条SCL串行时钟线,用于数据收发同步;一条SDA串行数据线,用来表示数据

  • 每个连接到IIC总线的设备都有一个唯一的地址(ID),主机可以通过地址与不同的从机建立连接

  • I2C 总线通过上拉电阻接到电源。当 IIC 设备空闲时,设备会输出高阻态,当所有设备都空闲,都输出高阻态时,由上拉电阻把 IIC总线拉成高电平。

  • I2C总线有仲裁机制:当多个主机同时发起传输时,触发仲裁机制,最终只给一个主机授权。

  • 具有三种传输模式,每种传输模式对应的传输速率不同,具体速率如下:
    在这里插入图片描述
    有些I2C变种还包含了快速+(1Mbit/s)和超高速(5Mbit/s 单向传输)模式。

1.3 协议层

  • 空闲状态:I2C协议规定,在空闲状态下SDA数据线和SCL时钟线均处于高电平。
  • 开始位:当SCL处于高电平时,SDA数据线被拉低,则认为检测到起始位,一次数据传输开始。
  • 数据传输:同时,协议规定在SCL高电平时期SDA数据线必须保持稳定,在SCL低电平时,SDA才允许发生改变。主机进行数据读写时,I2C协议规定,数据传输时先发送寻址字节,即7位从机地址+0/1。其中0表示主机写,1表示主机读。寻址字节发送完成后才是数据字节
  • 应答位:I2C协议规定,主机每次向从机传输1字节数据后,需要接收一次从机的应答信号。0为接收成功;1为接受失败,没有响应。
  • 停止位:当SCL为高电平时,数据线SDA拉高,则认为检测到停止位,一次数据传输结束。
    在这里插入图片描述
    在这里插入图片描述

二、EEPROM

2.1 型号及硬件规格

EEPROM的全称是“电可擦除可编程只读存储器”,即Electrically Erasable Programmable Read-Only Memory。本次项目中使用的EEPROM型号为24LC04B。

在这里插入图片描述
由手册可以看出,24LC04B支持的最大时钟频率为400KHz。

在这里插入图片描述
由手册得出该型号EEPROM共有两个Block 每个Block的存储容量为256×8bit。
在这里插入图片描述
在这里插入图片描述

由手册得出,设备地址为1010xxB0共七位(xx为dont care B0为块选择),1为读操作,0为写操作。

2.2 各种读写时序

单字节写

在这里插入图片描述

  • 先写开始位
  • 然后写控制字节,从机接收到发应答信号
  • 然后写数据地址,从机接收到发应答信号,如果数据地址是2位的话,就继续写数据地址,从机接收到发应答信号
  • 然后写数据,从机接收到发应答信号
  • 最后写结束位

页写

在这里插入图片描述

  • 页节写先开始位
  • 然后写控制字节,从机接收到应答信号
  • 然后写数据地址,从机接收到应答信号,如果数据地址是2位的话,就继续写数据地址,从机接收到应答信号
  • 然后写数据,从机接收到应答信号
  • 然后继续写数据,直到写完全部的数据
  • 最后是结束位

当前地址读

在这里插入图片描述

  • 当前地址读先开始位
  • 然后写控制字节,从机接收到应答信号,然后读数据,无应答信号
  • 最后结束位

随机地址读和顺序读

在这里插入图片描述

  • 随机读先写开始位
  • 然后写控制字节,从机接收到应答信号
  • 然后dummuy write 虚写,写数据地址,从机接收到应答信号
  • 然后开始位
  • 然后读控制字节,从机接收到应答信号
  • 然后读数据
  • 最后结束位

三、状态机设计

I2C接口模块:
在这里插入图片描述
EEPROM控制模块:
在这里插入图片描述

四、项目源码:

EEPROM驱动模块:

/**************************************功能介绍***********************************
Date	: 2023年8月28日 
Author	: majiko
Version	: 1.0
Description: eeprom驱动模块
             
             cmd       0      1
             开始位    NO    YES          
             写数据    NO    YES      
             读数据    NO    YES          
             停止位    NO    YES              
             应答位    ACK   NO_ACK
*********************************************************************************/
    
module eeprom_driver( 
    input       wire            clk         ,
    input       wire            rst_n       ,
    input       wire    [7:0]   wr_data     ,
    input       wire    [4:0]   cmd         ,
    input       wire            cmd_vld     ,

    inout       wire            i2c_sda     ,
    output      reg             i2c_scl     ,

    output      wire    [7:0]   rd_data     ,
    output      wire            rd_data_vld ,
    output      reg             done        ,
    output      reg             rev_ack         
);

//内部参数定义
    //命令宏定义
    `define START_BIT  5'b00001
    `define WRITE_BIT  5'b00010
    `define READ_BIT   5'b00100
    `define STOP_BIT   5'b01000
    `define ACK_BIT    5'b10000

    `define ACK        0
    `define NO_ACK     1

    //状态定义
    parameter   IDLE    = 7'b000_0001,
                START   = 7'b000_0010,
                WR_DATA = 7'b000_0100,
                R_ACK   = 7'b000_1000,
                STOP    = 7'b001_0000,
                RD_DATA = 7'b010_0000,
                T_ACK   = 7'b100_0000;

    //i2c速率以及采样点定义
    parameter   SCL_MAX = 50_000_000/100_000; //100K速率
    parameter   SCL_1_4 = SCL_MAX/4;
    parameter   SCL_3_4 = SCL_MAX*3/4;


    

//内部信号定义
    //状态寄存器
    reg         [6:0]   cstate          ;
    reg         [6:0]   nstate          ;

    reg         [7:0]   wr_data_r       ;//输入数据寄存
    reg         [4:0]   cmd_r           ;//输入命令寄存

    reg         [7:0]   rev_data        ;         

    //跳转条件定义
    wire                idle2start      ;
    wire                idle2wr_data    ;
    wire                idle2rd_data    ;
    wire                start2wr_data   ;
    wire                wr_data2r_ack   ;
    wire                r_ack2stop      ;
    wire                r_ack2idle      ;
    wire                stop2idle       ;
    wire                rd_data2t_ack   ;
    wire                t_ack2stop      ;
    wire                t_ack2idle      ;

    //bit计数器
    reg			[3:0]	cnt_bit	   	    ;
    wire				add_cnt_bit	    ;
    wire				end_cnt_bit	    ;
    reg         [3:0]   bit_max         ;

    //i2c分频计数器
    reg			[8:0]	cnt_scl	   	    ;
    wire				add_cnt_scl	    ;
    wire				end_cnt_scl	    ;

    //三态门信号定义
    reg                 sda_out         ;
    wire                sda_in          ;
    reg                 sda_en          ;
    

//****************************************************************
//--数据寄存 命令寄存
//****************************************************************
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            wr_data_r <= 1'b0;
        end
        else if(cmd_vld)begin
            wr_data_r <= wr_data;
        end
        else begin
            wr_data_r <= wr_data_r;
        end
    end

    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cmd_r <= 1'b0;
        end
        else if(cmd_vld)begin
            cmd_r <= cmd;
        end
        else begin
            cmd_r <= cmd_r;
        end
    end
//****************************************************************
//--读写状态机
//****************************************************************
    //第一段 状态定义
    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 if(idle2wr_data)begin
                                nstate = WR_DATA;
                            end
                            else if(idle2rd_data)begin
                                nstate = RD_DATA;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end
            START   :   begin
                            if(start2wr_data)begin
                                nstate = WR_DATA;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end
            WR_DATA :   begin
                            if(wr_data2r_ack)begin
                                nstate = R_ACK;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end            
            R_ACK   :   begin
                            if(r_ack2stop)begin
                                nstate = STOP;
                            end
                            else if(r_ack2idle)begin
                                nstate = IDLE;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end
            STOP    :   begin
                            if(stop2idle)begin
                                nstate = IDLE;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end
            RD_DATA :   begin
                            if(rd_data2t_ack)begin
                                nstate = T_ACK;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end
             T_ACK  :   begin
                            if(t_ack2stop)begin
                                nstate = STOP;
                            end
                            else if(t_ack2idle)begin
                                nstate = IDLE;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end                                
            default:    nstate = IDLE;
        endcase
    end

    assign idle2start    = (cstate == IDLE) && cmd_vld && (cmd & `START_BIT); 
    assign idle2wr_data  = (cstate == IDLE) && cmd_vld && (cmd & `WRITE_BIT);  
    assign idle2rd_data  = (cstate == IDLE) && cmd_vld && (cmd & `READ_BIT);

    assign start2wr_data = (cstate == START  ) && end_cnt_bit && (cmd_r & `WRITE_BIT);

    assign wr_data2r_ack = (cstate == WR_DATA) && end_cnt_bit;

    assign r_ack2stop    = (cstate == R_ACK)   && end_cnt_bit && (cmd_r & `STOP_BIT);       
    assign r_ack2idle    = (cstate == R_ACK)   && end_cnt_bit && !(cmd_r & `STOP_BIT);

    assign stop2idle     = (cstate == STOP   ) && end_cnt_bit;   
    assign rd_data2t_ack = (cstate == RD_DATA) && end_cnt_bit;

    assign t_ack2stop    = (cstate == T_ACK)   && end_cnt_bit && (cmd_r & `STOP_BIT);   
    assign t_ack2idle    = (cstate == T_ACK)   && end_cnt_bit && !(cmd_r & `STOP_BIT);   

//****************************************************************
//--i2c时钟分频
//****************************************************************
    
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_scl <= 'd0;
        end 
        else if(add_cnt_scl)begin 
            if(end_cnt_scl)begin 
                cnt_scl <= 'd0;
            end
            else begin 
                cnt_scl <= cnt_scl + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_scl = cstate != IDLE;
    assign end_cnt_scl = add_cnt_scl && cnt_scl == SCL_MAX - 1'b1;

    //assign i2c_scl = (cnt_scl == (SCL_MAX - 1'b1) >> 1'b1 || stop2idle) ? 1'b1 : 1'b0;
    
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            i2c_scl <= 1'b1;
        end
        else if(cnt_scl == (SCL_MAX - 1) >> 1'b1 || stop2idle)begin
            i2c_scl <= 1'b1;
        end
        else if(end_cnt_scl)begin
            i2c_scl <= 1'b0;
        end
        else begin
            i2c_scl <= i2c_scl;
        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_scl;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max - 1'b1;
    
    always @(*)begin
        if(cstate == WR_DATA || cstate == RD_DATA)begin
            bit_max = 'd8;
        end
        else begin
            bit_max = 'd1;
        end
    end

//****************************************************************
//--三态门控制
//****************************************************************
    assign sda_in = i2c_sda;
    assign i2c_sda = sda_en ? sda_out : 1'bz;

    //使能信号开启
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            sda_en <= 1'b0;
        end
        else if(wr_data2r_ack || stop2idle || idle2rd_data)begin
            sda_en <= 1'b0;
        end
        else if(idle2wr_data || idle2start || rd_data2t_ack || r_ack2stop || start2wr_data)begin
            sda_en <= 1'b1;
        end
        else begin
            sda_en <= sda_en;
        end
    end
//****************************************************************
//--数据发送
//****************************************************************
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            sda_out <= 1'b1;
        end
        else begin
            case(cstate)
                IDLE    :   sda_out <= 1'b1;
                START   :   begin
                                if(cnt_scl == SCL_1_4)begin
                                    sda_out <= 1'b1;
                                end
                                else if(cnt_scl == SCL_3_4)begin
                                    sda_out <= 1'b0;
                                end
                                else begin
                                    sda_out <= sda_out;
                                end
                            end
                WR_DATA :   begin
                                if(cnt_scl == SCL_1_4)begin
                                    sda_out <= wr_data_r[7-cnt_bit];
                                end
                                else begin
                                    sda_out <= sda_out;
                                end
                            end
                T_ACK   :   begin
                                if(cnt_scl == SCL_1_4)begin
                                    if(cmd & `ACK_BIT)begin
                                        sda_out <= `NO_ACK;
                                    end
                                    else begin
                                        sda_out <= `ACK;
                                    end
                                end
                                else begin
                                    sda_out <= sda_out;
                                end
                            end
                STOP    :   begin
                                if(cnt_scl == SCL_1_4)begin
                                    sda_out <= 1'b0;
                                end
                                else if(cnt_scl == SCL_3_4)begin
                                    sda_out <= 1'b1;
                                end
                                else begin
                                    sda_out <= sda_out;
                                end
                            end
                default :   sda_out <= 1'b1;
            endcase
        end
    end

//****************************************************************
//--数据接收
//****************************************************************

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            rev_ack <= 1'b0;
            rev_data <= 1'b0;
        end
        else begin
            case(cstate)
            R_ACK   :   begin
                            if(cnt_scl == SCL_3_4)begin
                                rev_ack <= sda_in;
                            end
                            else begin
                                rev_ack <= rev_ack;
                            end
                        end
            RD_DATA :   begin
                            if(cnt_scl == SCL_3_4)begin
                                rev_data[7-cnt_bit] <= sda_in;
                            end
                            else begin
                                rev_data <= rev_data;
                            end
                        end
            default :   ;
            endcase
        end
    end

//****************************************************************
//--接口输出
//****************************************************************
   
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            done <= 1'b0;
        end
        else begin
            done <= t_ack2idle || r_ack2idle || stop2idle;
        end
    end

    //assign done = (t_ack2idle || r_ack2idle || stop2idle) ? 1'b1 : 1'b0;

    assign rd_data = (t_ack2idle || t_ack2stop) ? rev_data : 1'b0;
    assign rd_data_vld = t_ack2idle || t_ack2stop;



endmodule

EEPROM控制模块:

/**************************************功能介绍***********************************
Date	: 2023年8月30日 
Author	: majiko
Version	: 1.0
Description: EEPROM控制模块
*********************************************************************************/
    
module eeprom_control
#(
    parameter ADDR_BIT = 8              //从机寄存器地址
)
(
    input       wire                        clk             ,
    input       wire                        rst_n           ,
    input       wire    [6:0]               device_id       ,//i2c从机设备地址
    input       wire                        wr_req          ,
    input       wire                        rd_req          ,
    input       wire    [ADDR_BIT - 1:0]    reg_addr        ,//配置寄存器地址
    input       wire                        reg_addr_vld    ,
    input       wire    [7:0]               wr_data         ,
    input       wire                        wr_data_vld     ,

    output      wire    [7:0]               rd_data         ,
    output      wire                        rd_data_vld     ,
    output      wire                        ready           ,

    output      wire                        i2c_scl         ,
    inout       wire                        i2c_sda          
);

//参数定义
    //命令宏定义
    `define START_BIT 5'b00001
    `define WRITE_BIT 5'b00010
    `define READ_BIT  5'b00100
    `define STOP_BIT  5'b01000
    `define ACK_BIT   5'b10000

    parameter   IDLE    = 6'b000_001,
                WR_REQ  = 6'b000_010,
                WR_WAIT = 6'b000_100,
                RD_REQ  = 6'b001_000,
                RD_WAIT = 6'b010_000,
                DONE    = 6'b100_000;

    parameter   WR_CTRL_BYTE = 8'b1010_0000,
                RD_CTRL_BYTE = 8'b1010_0001;

//内部信号定义
    //状态寄存器
    reg         [5:0]   cstate          ;
    reg         [5:0]   nstate          ;

    //跳转条件定义
    wire                idle2rd_req     ;
    wire                idle2wr_req     ;
    wire                wr_req2wr_wait  ;
    wire                wr_wait2wr_req  ;
    wire                wr_wait2done    ;
    wire                rd_req2rd_wait  ;
    wire                rd_wait2done    ;
    wire                rd_wait2rd_req  ;
    wire                done2idle       ;

    wire                done            ;


    reg         [4:0]   cmd             ;
    reg                 cmd_vld         ;
    reg         [7:0]   op_wr_data      ;

    reg         [7:0]   addr_r          ;
    reg         [7:0]   wr_data_r       ;

    //字节计数器
    reg			[2:0]	cnt_byte	   	;
    wire				add_cnt_byte	;
    wire				end_cnt_byte	;
    reg         [2:0]   byte_mawr_addr  ;

//****************************************************************
//--寄存输入数据
//****************************************************************
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            addr_r <= 1'b0;
        end
        else if(reg_addr_vld)begin
            addr_r <= reg_addr;
        end
        else begin
            addr_r <= addr_r;
        end
    end

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            wr_data_r <= 1'b0;
        end
        else if(wr_data_vld)begin
            wr_data_r <= wr_data;
        end
        else begin
            wr_data_r <= wr_data_r;
        end
    end

//****************************************************************
//--状态机
//****************************************************************
    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(idle2rd_req)begin
                                nstate = RD_REQ;
                            end
                            else if(idle2wr_req)begin
                                nstate = WR_REQ;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end
            WR_REQ  :   begin
                            if(wr_req2wr_wait)begin
                                nstate = WR_WAIT;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end
            WR_WAIT :   begin
                            if(wr_wait2done)begin
                                nstate = DONE;
                            end
                            else if(wr_wait2wr_req)begin
                                nstate = WR_REQ;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end
            RD_REQ  :   begin
                            if(rd_req2rd_wait)begin
                                nstate = RD_WAIT;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end
            RD_WAIT :   begin
                            if(rd_wait2done)begin
                                nstate = DONE;
                            end
                            else if(rd_wait2rd_req)begin
                                nstate = RD_REQ;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end
            DONE    :   begin
                            if(done2idle)begin
                                nstate = IDLE;
                            end
                            else begin
                                nstate = cstate;
                            end
                        end
            default :   ;
        endcase
    end


assign idle2rd_req    = cstate == IDLE && rd_req;
assign idle2wr_req    = cstate == IDLE && wr_req;

assign wr_req2wr_wait = cstate == WR_REQ  && 1'b1;
assign wr_wait2wr_req = cstate == WR_WAIT && done;
assign wr_wait2done   = cstate == WR_WAIT && end_cnt_byte;

assign rd_req2rd_wait = cstate == RD_REQ  && 1'b1;
assign rd_wait2done   = cstate == RD_WAIT && end_cnt_byte;
assign rd_wait2rd_req = cstate == RD_WAIT && done;

assign done2idle      = cstate == DONE && 1'b1;

//****************************************************************
//--字节计数器
//****************************************************************
    
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_byte <= 'd0;
        end 
        else if(add_cnt_byte)begin 
            if(end_cnt_byte)begin 
                cnt_byte <= 'd0;
            end
            else begin 
                cnt_byte <= cnt_byte + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_byte = done;
    assign end_cnt_byte = add_cnt_byte && cnt_byte == byte_mawr_addr - 1'b1;
    
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            byte_mawr_addr <= 1'b1;
        end
        else if(wr_req)begin
            byte_mawr_addr <= 'd3;
        end
        else if(rd_req)begin
            byte_mawr_addr <= 'd4;
        end
        else if(end_cnt_byte)begin
            byte_mawr_addr <= 1'b1;
        end
        else begin
            byte_mawr_addr <= byte_mawr_addr;
        end
    end

//****************************************************************
//--接口模块控制
//****************************************************************

    task TX;
        input           task_cmd_vld;
        input   [4:0]   task_cmd;
        input   [7:0]   task_wr_data;
        begin
            cmd_vld    = task_cmd_vld;
            cmd        = task_cmd;
            op_wr_data = task_wr_data;
        end
    endtask

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
            TX(0,5'h0,8'h00);
        end
        else begin
            case(cstate)
                RD_REQ  :   case(cnt_byte)
                                0   :   TX(1,(`START_BIT | `WRITE_BIT),WR_CTRL_BYTE);//写控制字节
                                1   :   TX(1,(`WRITE_BIT),addr_r[7:0]);//写地址
                                2   :   TX(1,(`START_BIT | `WRITE_BIT),RD_CTRL_BYTE);//读控制字节
                                3   :   TX(1,(`ACK_BIT| `READ_BIT | `STOP_BIT),8'h00);//读数据
                                default :   TX(0,cmd,op_wr_data);
                            endcase
                WR_REQ  :   case(cnt_byte)
                                0   :   TX(1,(`START_BIT | `WRITE_BIT),WR_CTRL_BYTE);//写控制字节
                                1   :   TX(1,(`WRITE_BIT),addr_r[7:0]);//写地址
                                2   :   TX(1,(`WRITE_BIT | `STOP_BIT),wr_data_r);//写数据
                                default :   TX(0,cmd,op_wr_data);
                            endcase
                default :   TX(0,cmd,op_wr_data);
            endcase
        end
    end


    eeprom_driver u_eeprom_driver( 
        /*input       wire            */.clk         (clk           ),
        /*input       wire            */.rst_n       (rst_n         ),
        /*input       wire    [7:0]   */.wr_data     (op_wr_data    ),
        /*input       wire    [4:0]   */.cmd         (cmd           ),
        /*input       wire            */.cmd_vld     (cmd_vld       ),

        /*inout       wire            */.i2c_sda     (i2c_sda       ),
        /*output      wire            */.i2c_scl     (i2c_scl       ),

        /*output      wire    [7:0]   */.rd_data     (rd_data       ),
        /*output      wire            */.rd_data_vld (rd_data_vld   ),
        /*output      wire            */.done        (done          ),
        /*output      reg             */.rev_ack     () 
    );

    // iic_interface iic_interface_inst(
        // /* input            */.clk          (clk        ),
        // /* input            */.rst_n        (rst_n      ),
        // /* input   [4:0]    */.cmd          (cmd        ),
        // /* input            */.cmd_vld      (cmd_vld    ),
        // /* output           */.done         (done       ),   //操作结束
        // /* output  reg      */.rev_ack      (           ),   //接收到的响应信号
        // /* input   [7:0]    */.wr_data      (op_wr_data ),   //伴随cmd_vld信号一起接收写数据
        // /* output  [7:0]    */.rd_data      (rd_data    ),
        // /* output           */.rd_data_vld  (rd_data_vld),
        // /* output reg       */.iic_scl      (i2c_scl    ),
        // /* inout            */.iic_sda      (i2c_sda    ) 
    // );


    assign ready = cstate == IDLE;


endmodule

UART RX模块:

//****************************************************************
//--majiko 2023-8-15 UART RX模块
//****************************************************************
module uart_rx 
#(
    parameter   BORT        = 20'd115200,
    parameter   CLOCK       = 50_000_000,
    parameter   CHECK_BIT   = "None"      //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
)
(
    input       wire             clk         ,
    input       wire             rst_n       ,
    input       wire             ready       ,//准备接受数据信号
    input       wire             rx          ,

    output      wire             rx_data_vld ,//数据有效信号
    output      wire     [7:0]   rx_data      
);

//参数定义
    parameter TIME_BORT = CLOCK/BORT;

    //状态机状态定义
    parameter   IDLE  = 4'b0001,//空闲状态电平默认拉高
                START = 4'b0010,//起始位赋值
                DATA  = 4'b0100,//数据位赋值
                CHECK = 4'b1000;
    
    //波特率115200 1bit传输时间计数器参数

//内部信号定义
    //状态寄存器
    reg         [3:0]   cstate          ;
    reg         [3:0]   nstate          ;

    //状态跳转条件
    wire                idle2start      ;
    wire                start2data      ;
    wire                data2idle       ;
    wire                data2check      ;
    wire                check2idle      ;

    //1Baud时间计数器
    reg			[8:0]	cnt_bort	   	;
    wire				add_cnt_bort	;
    wire				end_cnt_bort	;

    //比特计数器(复用)
    reg			[2:0]	cnt_bit	   	    ;
    wire				add_cnt_bit	    ;
    wire				end_cnt_bit	    ;
    reg         [3:0]   bit_max         ;//计数器复用信号

    reg         [7:0]   rx_temp         ;    
    
    reg                 check_bit       ;
    wire                check_temp      ;

    reg                 rx_r1           ;
    reg                 rx_r2           ;
    wire                rx_nedge        ;

//rx同步至时钟域 并检测下降沿
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            rx_r1 <= 1'b1;
            rx_r2 <= 1'b1;
        end
        else begin
            rx_r1 <= rx;
            rx_r2 <= rx_r1;
        end
    end

    assign rx_nedge = ~rx_r1 && rx_r2;

//三段式状态机
    //第一段 时序逻辑
    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(data2idle)begin
                            nstate = IDLE;
                        end
                        else if(data2check)begin
                            nstate = CHECK;
                        end
                        else begin
                            nstate = cstate;
                        end
                    end
            CHECK:  begin
                        if(check2idle)begin
                            nstate = IDLE;
                        end
                        else begin
                            nstate = cstate;
                        end
                    end
            default :   nstate = IDLE;
        endcase
    end

    assign idle2start = cstate == IDLE  && rx_nedge;//检测到开始位,结束空闲状态,开始接收数据
    assign start2data = cstate == START && end_cnt_bit;
    assign data2idle  = cstate == DATA  && end_cnt_bit && CHECK_BIT == "None";
    assign data2check = cstate == DATA  && end_cnt_bit;
    assign check2idle = cstate == CHECK && end_cnt_bit;

    //1Baud所需时间
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bort <= 'd0;
        end 
        else if(add_cnt_bort)begin 
            if(end_cnt_bort)begin 
                cnt_bort <= 'd0;
            end
            else begin 
                cnt_bort <= cnt_bort + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_bort = cstate != IDLE;
    assign end_cnt_bort = add_cnt_bort && cnt_bort == TIME_BORT - 1'b1;

    //8bit共需时间
    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_bort;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max - 1'b1;

//bit计数器复用
    always@(*)begin
        case(cstate)
            IDLE    :   bit_max = 1'b0;
            START   :   bit_max = 1'b1;
            DATA    :   bit_max = 4'd8;//8位数据位
            CHECK   :   bit_max = 1'b1;//校验位
            default :   bit_max = 1'b0;
        endcase
    end


//状态输出
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            rx_temp <= 1'b0;
        end
        else if(cstate == DATA && cnt_bort == (TIME_BORT >> 1))begin
            rx_temp[cnt_bit] <= rx_r1;
        end
        else begin
            rx_temp <= rx_temp;
        end
    end

    assign rx_data = rx_temp;

//校验位
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            check_bit <= 1'b0;
        end
        else if(cstate == CHECK && cnt_bort == (TIME_BORT >> 1))begin
            check_bit <= rx;
        end
        else begin
            check_bit <= check_bit;
        end
    end
    
    assign check_temp = CHECK_BIT == "Odd" ? ~^rx_data : ^rx_data;

    assign rx_data_vld = CHECK_BIT == "None" ? data2idle 
                        : (check2idle && check_temp == check_bit) ? 1
                        : 0;
    
    
endmodule

UART TX模块:

//****************************************************************
//--majiko 2023-8-15 UART TX模块
//****************************************************************
module uart_tx 
#( 
    parameter   BORT      = 20'd115200,//波特率
    parameter   CLOCK     = 50_000_000,//系统时钟参数
    parameter   CHECK_BIT = "None"     //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
) 
(
    input       wire             clk         ,
    input       wire             rst_n       ,
    input       wire    [7:0]    tx_data     ,
    input       wire             tx_data_vld ,//数据有效信号

    output      wire             ready       ,//准备接受数据信号
    output      reg              tx  
);

//参数定义
    parameter TIME_BORT = CLOCK/BORT;

    //状态机状态定义
    parameter   IDLE  = 5'b00001,//空闲状态电平默认拉高
                START = 5'b00010,//起始位赋值
                DATA  = 5'b00100,//数据位赋值
                CHECK = 5'b01000,//奇偶校验位
                STOP  = 5'b10000;//停止位赋值
    
    //波特率115200 1bit传输时间计数器参数

//内部信号定义
    //状态寄存器
    reg         [4:0]   cstate          ;
    reg         [4:0]   nstate          ;

    //状态跳转条件
    wire                idle2start      ;
    wire                start2data      ;
    wire                data2check      ;
    wire                data2stop       ;              
    wire                check2stop      ;
    wire                stop2idle       ;

    //1Baud时间计数器
    reg			[8:0]	cnt_bort	   	;
    wire				add_cnt_bort	;
    wire				end_cnt_bort	;

    //比特计数器(复用)
    reg			[2:0]	cnt_bit	   	    ;
    wire				add_cnt_bit	    ;
    wire				end_cnt_bit	    ;
    reg         [3:0]   bit_max         ;//计数器复用信号

    reg         [7:0]   tx_data_r       ;//寄存数据

    wire                check_bit       ;
     

//三段式状态机
    //第一段 时序逻辑
    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 :   nstate <= IDLE;
        endcase
    end

    assign idle2start = cstate == IDLE  && tx_data_vld;//数据有效信号拉高,结束空闲状态,开始接收数据
    assign start2data = cstate == START && end_cnt_bit;
    assign data2check = cstate == DATA  && end_cnt_bit;
    assign data2stop  = cstate == DATA  && end_cnt_bit && CHECK_BIT == "None"; 
    assign check2stop = cstate == CHECK && end_cnt_bit;
    assign stop2idle  = cstate == STOP  && end_cnt_bit;

    //1Baud所需时间
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bort <= 'd0;
        end 
        else if(add_cnt_bort)begin 
            if(end_cnt_bort)begin 
                cnt_bort <= 'd0;
            end
            else begin 
                cnt_bort <= cnt_bort + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_bort = cstate != IDLE;
    assign end_cnt_bort = add_cnt_bort && cnt_bort == TIME_BORT - 1'b1;

    //不同状态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_bort;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max - 1'b1;

//bit计数器复用
    always@(*)begin
        case(cstate)
            IDLE    :   bit_max = 1'b0;
            START   :   bit_max = 1'b1;//1位起始位数据
            DATA    :   bit_max = 4'd8;//8位数据位
            CHECK   :   bit_max = 1'b1;//1位奇偶校验位
            STOP    :   bit_max = 1'b1;//1位停止位数据
            default :   bit_max = 1'b0;
        endcase
    end
    

//状态输出
    //输入数据寄存
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            tx_data_r <= 1'b0;
        end
        else if(tx_data_vld)begin
            tx_data_r <= tx_data;
        end
        else begin
            tx_data_r <= tx_data_r;
        end
    end

    //奇偶校验位
    assign check_bit = CHECK_BIT == "Odd" ? ~^tx_data_r : ^tx_data_r;

    //数据输出
    always@(*)begin
        case(cstate)
            IDLE    :   tx = 1'b1;//空闲时为高电平
            START   :   tx = 1'b0;//起始位低电平
            DATA    :   tx = tx_data_r[cnt_bit];//数据位从低位开始发送
            CHECK   :   tx = check_bit;
            STOP    :   tx = 1'b1;//停止位高电平
            default :   tx = 1'b1;
        endcase
    end

    assign ready = cstate == IDLE;
    
    
endmodule

顶层模块:

module top(
    input       wire            clk         ,
    input       wire            rst_n       ,
    input       wire            rx          ,
	input	    wire            key_in		,

    inout       wire            i2c_sda     ,
    output      wire            i2c_scl     ,
    output      wire            tx 

);

    wire             ready       ;
    wire    [7:0]    rx_data     ;
    wire    [7:0]    tx_data     ;
    wire             rx_data_vld ;
    wire             tx_data_vld ;

    wire             key_out     ;



//模块例化
    eeprom_control  u_eeprom_control
    (
        /*input       wire                        */.clk             (clk           ),
        /*input       wire                        */.rst_n           (rst_n         ),
        /*input       wire    [6:0]               */.device_id       (7'b1010_000   ),
        /*input       wire                        */.wr_req          (rx_data_vld   ),
        /*input       wire                        */.rd_req          (key_out       ),
        /*input       wire    [ADDR_BIT - 1:0]    */.reg_addr        (0),
        /*input       wire                        */.reg_addr_vld    (rx_data_vld   ),
        /*input       wire    [7:0]               */.wr_data         (rx_data       ),
        /*input       wire                        */.wr_data_vld     (rx_data_vld   ),

        /*output      wire    [7:0]               */.rd_data         (tx_data       ),
        /*output      wire                        */.rd_data_vld     (tx_data_vld   ),
        /*output      wire                        */.ready           (),

        /*output      wire                        */.i2c_scl         (i2c_scl       ),
        /*inout       wire                        */.i2c_sda         (i2c_sda       )
    );


    // iic_control u_eeprom_control2
    // (
        // /*input       wire                        */.clk             (clk           ),
        // /*input       wire                        */.rst_n           (rst_n         ),
        // /*input       wire    [6:0]               */.device_id       (7'b1010_000   ),
        // /*input       wire                        */.wr_req          (rx_data_vld   ),
        // /*input       wire                        */.rd_req          (key_out       ),
        // /*input       wire    [ADDR_BIT - 1:0]    */.addr            (0),
        // /*input       wire                        */.addr_vld        (rx_data_vld   ),
        // /*input       wire    [7:0]               */.wr_data         (rx_data       ),
        // /*input       wire                        */.wr_data_vld     (rx_data_vld   ),
        // /*output      wire    [7:0]               */.rd_data         (tx_data       ),
        // /*output      wire                        */.rd_data_vld     (tx_data_vld   ),
        // /*output      wire                        */.ready           (),
        // /*output      wire                        */.iic_scl         (i2c_scl       ),
        // /*inout       wire                        */.iic_sda         (i2c_sda       )
    // );

    uart_rx u_uart_rx
    (
       .clk         (clk         ),
       .rst_n       (rst_n       ),
       .ready       (            ),
       .rx          (rx          ),
    
       .rx_data_vld (rx_data_vld ),
       .rx_data     (rx_data     ) 
    );
    
    uart_tx u_uart_tx
    (
       .clk         (clk         ),
       .rst_n       (rst_n       ),
       .tx_data     (tx_data     ),
       .tx_data_vld (tx_data_vld ),
    
       .ready       (ready       ),
       .tx          (tx          ) 
    );

    // ctrl u_ctrl (
        //  /*input        wire               */.clk         (clk         ),
        //  /*input        wire               */.rst_n       (rst_n       ),
        //  /*input        wire               */.rx_data_vld (rx_data_vld ),
        //  /*input        reg        [7:0]   */.rx_data     (rx_data     ),
        //  /*input        wire               */.ready       (ready       ),
        //  /*output       wire       [7:0]   */.tx_data     (),
        //  /*output       wire               */.tx_data_vld ()
    // );


    key_filter#(.WIDTH(1)) u_key_filter          
    (
        /*input       wire                    */.clk     (clk       ),
        /*input       wire                    */.rst_n   (rst_n     ),
        /*input       wire  [WIDTH - 1:0]     */.key_in  (key_in    ),
        /*output      reg   [WIDTH - 1:0]     */.key_out (key_out   )
    );



endmodule

按键消抖模块:

module key_filter#(parameter WIDTH = 4)           //参数化按键位宽
(
    input       wire                    clk     ,
    input       wire                    rst_n   ,
    input       wire  [WIDTH - 1:0]     key_in  ,//按键输入信号

    output      reg   [WIDTH - 1:0]     key_out  //输出稳定的脉冲信号
);

parameter MAX = 20'd1_000_000;

reg     [19:0]                  cnt_delay       ; //20ms延时计数寄存器
wire                            add_cnt_delay   ; //开始计数的标志
wire                            end_cnt_delay   ; //结束计数的标志

reg     [WIDTH - 1:0]           key_r0          ; //同步
reg     [WIDTH - 1:0]           key_r1          ; //打一拍
reg     [WIDTH - 1:0]           key_r2          ; //打两拍

wire    [WIDTH - 1:0]           nedge           ; //下降沿寄存器


//同步打拍
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        key_r0 <= {WIDTH{1'b1}};
        key_r1 <= {WIDTH{1'b1}};
        key_r2 <= {WIDTH{1'b1}};
    end
    else begin
        key_r0 <= key_in; //同步
        key_r1 <= key_r0; //寄存一拍
        key_r2 <= key_r1; //寄存两拍
    end
end

//20ms计数器
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_delay <= 1'b0;
    end
    else if(add_cnt_delay )begin
        if(nedge)begin //检测到下降沿从0开始计数
            cnt_delay <= 1'b0;
        end
        else if(cnt_delay == MAX - 1'b1)begin
            cnt_delay <= cnt_delay; //计数计满结束后保持,避免产生多个输出脉冲
        end
        else begin
            cnt_delay <= cnt_delay + 1'b1;
        end
    end
    else begin
        cnt_delay <= 1'b0;
    end
end

assign nedge = ~key_r1 & key_r2; //下降沿检测
assign add_cnt_delay = 1'b1; 
assign end_cnt_delay = add_cnt_delay && cnt_delay == MAX - 1'b1;


//key_out脉冲信号赋值
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_out <= 'd0;
    end
    else if(cnt_delay == MAX - 2'd2)begin //计数计满前一个脉冲时产生按键脉冲
        key_out <= ~key_in;
    end
    else begin
        key_out <= 'd0;
    end
end

endmodule

五、实现效果

在这里插入图片描述
可以看出,能够正常进行单个字节的收发。

参考资料

https://blog.csdn.net/zhangduang_KHKW/article/details/121953275

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

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

相关文章

[C++从入门到精通] 11.回顾类内初始化、默认构造函数、=default

&#x1f4e2;博客主页&#xff1a;https://loewen.blog.csdn.net&#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;本文由 丶布布原创&#xff0c;首发于 CSDN&#xff0c;转载注明出处&#x1f649;&#x1f4e2;现…

Python爬虫(二十二)_selenium案例:模拟登陆豆瓣

本篇博客主要用于介绍如何使用seleniumphantomJS模拟登陆豆瓣&#xff0c;没有考虑验证码的问题&#xff0c;更多内容&#xff0c;请参考&#xff1a;Python学习指南 #-*- coding:utf-8 -*-from selenium import webdriver from selenium.webdriver.common.keys import Keysimp…

辅助寄存器是干什么用的

目录 请问CPU 的 MREQ 引脚和 IORQ 引脚分别是干什么用的 那这里的引脚是什么含义呢&#xff1f; 程序是指令和数据的集合 辅助寄存器是干什么用的 寄存器的用途取决于它的类型 PC 寄存器也叫作“程序指针”&#xff0c;存储着指向 CPU 接下来 要执行的指令的地址。PC 寄存…

jmeter添加断言(详细图解)

先创建一个线程组&#xff0c;再创建一个http请求。 为了方便观察&#xff0c;我们添加两个监听器&#xff0c;察看结果树和断言结果。 添加断言&#xff1a;响应断言&#xff0c;响应断言也是比较常用的一个断言 设置响应断言&#xff1a;正常情况下响应代码是200。选择响应代…

固态硬盘删除的资料能恢复吗?

固态硬盘&#xff08;SSD&#xff09;作为一种存储设备&#xff0c;在读写速度和抗摔性方面具有显著优势&#xff0c;因此备受许多用户的青睐。然而&#xff0c;在使用过程中&#xff0c;由于人为误操作或设备内部故障&#xff0c;固态硬盘可能会导致数据丢失。所以固态硬盘删除…

【WinRAR】去除请购买WinRAR许可

新建rarreg.key文件 在WinRAR安装目录新建rarreg.key文件&#xff0c;文件内容如下: RAR registration datawncnUnlimited Company LicenseUID1b064ef8b57de3ae9b5264122122509b52e35fd885373b214a4a64cc2fc1284b77ed14fa2066ebfca6509f9813b32960fce6cb5ffde62890079861be57…

聊聊分布式架构02——Http到Https

目录 HTTP通信协议 请求报文 响应报文 持久连接 状态管理 HTTPS通信协议 安全的HTTPS HTTP到HTTPS的演变 对称加密 非对称加密 混合加密机制 证书机构 SSL到底是什么 HTTPS是身披SSL外壳的HTTP HTTP通信协议 一次HTTP请求的通信流程&#xff1a;客户端浏览器通过…

slam从入门到精通(稍复杂一点的运动控制)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 ros本身只是提供了一个框架&#xff0c;上面对应客户需求&#xff0c;下面对应各个传感器&#xff0c;中间就是各个算法和决策措施。但是robot本身…

SpringCloud之Hystrix高版本熔断器源码解析

Hystrix官方已经停止开发了&#xff0c;Hystrix官方推荐使用新一代熔断器作为Resilience4j。作为新一代的熔断器&#xff0c;Resilience4j有很多优势&#xff0c;比如依赖少&#xff0c;模块化程度较好等优势。 Resilience4j是受Hystrix启发而做的熔断器&#xff0c;通过管理远…

【动手学深度学习】课程笔记 00-03 深度学习介绍及环境配置

目录 00-01 课程安排 02 深度学习介绍 深度学习实际应用的流程 完整的故事 03 环境配置 00-01 课程安排 1. 学习了这门课&#xff0c;你将收获什么&#xff1f; 深度学习的经典和最新模型&#xff1a;LeNet&#xff0c;ResNet&#xff0c;LSTM&#xff0c;BERT&#xff1…

Linux程序崩溃时的信号量(signal)说明

一、概念说明 在程序崩溃的时候&#xff0c;我们将会获取到两个信息: • signal: 信号量&#xff0c;下文将会详细的说明不同的信号量及其含义 • code: 错误码, 除了几个所有信号量(signal) 公共的错误码(code)&#xff0c;一般不同信号量(signal)有特定的错误码(code)&#x…

智能工业通信解决方案!钡铼BL124实现Modbus转Ethernet/IP互联!

钡铼技术BL124 Modbus转Ethernet/IP协议网关是一款专为工业自动化领域而设计的先进设备。它提供了可靠的通信解决方案&#xff0c;能够将Modbus通信协议与Ethernet/IP通信协议进行高效转换&#xff0c;实现不同类型设备之间的无缝集成和通信。 添加图片注释&#xff0c;不超过 …

冲刺十五届蓝桥杯P0004递增三元组

文章目录 题目解析代码如下 题目 递增三元组 解析 用到线性代数的知识&#xff0c;原来的三元组一共有27钟组合&#xff0c;不一一列举了。如果将三元组排序一下&#xff0c;得到的27钟组合和原来时一样的&#xff0c;只是顺序变了而已。 我们以b组为核心&#xff0c;遍历b组…

矢量图绘制软件EazyDraw mac中文版软件介绍

EazyDraw mac是一款功能强大且易于使用的矢量绘图软件。 EazyDraw mac软件介绍 矢量绘图工具&#xff1a;EazyDraw 提供了一套全面的矢量绘图工具&#xff0c;包括直线、曲线、多边形、文本框、图形填充等。用户可以使用这些工具创建和编辑精确的矢量图形&#xff0c;无论是简…

电动主轴与气动主轴的优缺点

随着工业自动化的不断发展&#xff0c;主轴的应用越来越广泛&#xff0c;并且不断改进优化。目前&#xff0c;市面上常用的主轴主要有两种&#xff1a;电动主轴和气动主轴。为了更好地选择和使用主轴&#xff0c;我们需要了解电动主轴和气动主轴各有什么优缺点&#xff1f; 电动…

徐明君:企业管理的新视角,业务与行为的整合

随着社会的发展和科技的进步&#xff0c;企业管理在社会化大生产的背景下愈显重要。近日&#xff0c;在一次企业管理的论坛上&#xff0c;众多业界人士针对企业管理的新视角进行了深入探讨&#xff0c;强调了业务管理和行为管理并重的重要性。 业务管理主要侧重于对企业的各种…

CS5366最新设计电路|Typec转HDMI 8K带PD方案设计|带DSC视频压缩技术Typec扩展方案

CS5366支持4K24/25/30/50/60Hz刷新率的HDR&#xff0c;CS5366集成DSC decoded影像解压缩技术,可将DPRX 4Lanes等效宽推升至97.2Gbps或 DPRX 2Lanes等效带宽推升至48.6Gbps ,此功能可改善画面延迟、影像撕裂等问题,可让用户在观看电影或是电玩游戏等高效能影像时有更好的体验。…

unity设计模式——代理模式

Subject类&#xff0c;定义了Real Subject和Proxy的共用接口&#xff0c;这样就在任何使用Real Subject的地方都可以使用Proxy。 abstract class Subject : MonoBehaviour {public abstract void Request(); } RealSubject类&#xff0c;定义Proxy所代表的真实实体。 class R…

nginx高可用配置(五)

keepalived keepalived安装 1.#进入根目录下的 usr目录 cd /usr 2.#安装keepalived yum install keepalived -y 3.安装完成后在根目录etc下会有个keepalived目录 4.进入keepalived目录 cd keepalived/ 5.ll命令查看&#xff0c;会有个配置文件 keepalived.conf 6.ke…

【已解决】Python打包文件执行报错:ModuleNotFoundError: No module named ‘pymssql‘

【已解决】Python打包文件执行报错&#xff1a;ModuleNotFoundError: No module named pymssql 1、问题2、原因3、解决 1、问题 今天打包一个 tkinter pymssql 的项目的时候&#xff0c;打包过程很顺利&#xff0c;但是打开软件的时候&#xff0c;报错 ModuleNotFoundError: …