Verilog 学习第五节(串口发送部分)

news2025/7/9 17:10:27

小梅哥串口部分学习part1

  • 串口通信发送原理
  • 串口通信发送的Verilog设计与调试
  • 串口发送应用之发送数据
  • 串口发送应用之采用状态机实现多字节数据发送

串口通信发送原理

1:串口通信模块设计的目的是用来发送数据的,因此需要有一个数据输入端口
2:串口通信,支持不同的波特率,所以需要一个波特率设置端口
3:串口通信的本质就是将8位并行数据通过一根信号线,在不同的时刻传输并行数据的不同位,通过多个时刻,最终将8位并行数据全部传出
4:串口通信以1位的低电平标志串行传输的开始,待8位数据传输完成之后,在1位的高电平标志传输的结束
5:控制信号,控制并转串模块什么时候开始工作;什么时候一个数据发送完成?须有一个发送开始信号,以及一个发送完成信号
在这里插入图片描述
注:图中少了一个波特率设置端口

串口通信发送的Verilog设计与调试

1:波特率为300起对应的计算公式为1000 000 000/300=3 333 333ns
3 333 333ns/20=166 666次 对应的是18位,所以一般有关波特率设置的端口设置的最大值为18位计数
在这里插入图片描述
源代码


module uart_byte_tx(
    input [7:0]Data,
    input Send_en,
    input Clk,
    input Reset_n,
    input [2:0]Baud_set,
    output reg uart_tx,
    output reg Tx_done
    );
    
    //Baud_set=0  就让波特率=9600
    //Baud_set=1  就让波特率=19200
    //Baud_set=2  就让波特率=38400
    //Baud_set=3  就让波特率=57600
    //Baud_set=4  就让波特率=115200
    reg [17:0]bps_DR;
    always@(*)begin
       case(Baud_set)
       0:bps_DR=1000000000/9600/20;
       1:bps_DR=1000000000/19200/20;
       2:bps_DR=1000000000/38400/20;
       3:bps_DR=1000000000/57600/20;
       4:bps_DR=1000000000/115200/20;
       default:bps_DR=1000000000/9600/20;
       endcase
    end
    
    wire bps_clk;
    assign bps_clk = (div_cnt == 1);
    
    reg [17:0]div_cnt;
    always@(posedge Clk or negedge Reset_n)begin
         if(!Reset_n)
            div_cnt<=0;
         else if(Send_en)begin
            if(div_cnt==bps_DR-1)
                div_cnt<=0;
            else
                div_cnt<=div_cnt+1'b1;
            end
         else
            div_cnt<=0;
    end
    
    reg [3:0]bps_cnt;
    always@(posedge Clk or negedge Reset_n)begin
     if(!Reset_n)
     bps_cnt<=0;
     else if(Send_en)begin
        if(bps_clk)begin
         if(bps_cnt==11)
             bps_cnt<=0;
          else
             bps_cnt<=bps_cnt+1;
         end
      end
      else
         bps_cnt<=1'b1;
        end
      
      //并串转换
      always@(posedge Clk or negedge Reset_n)begin
           if(!Reset_n)begin
              uart_tx<=1'b0;
              Tx_done<=1'b0;
              end
           else begin
           case(bps_cnt)
           1:begin uart_tx<=1'b0;Tx_done=1'b0;end
           2:uart_tx<=Data[0];
           3:uart_tx<=Data[1];
           4:uart_tx<=Data[2];
           5:uart_tx<=Data[3];
           6:uart_tx<=Data[4];
           7:uart_tx<=Data[5];
           8:uart_tx<=Data[6];
           9:uart_tx<=Data[7];
           10:uart_tx<=1'b1;
           11:begin uart_tx<=1'b1;Tx_done=1'b1; end 
           default: uart_tx<=1'b1;
          endcase
          end
          end

          
endmodule


测试文件

`timescale 1ns / 1ns
module uart_byte_tx_tb(
    );
    reg [7:0]Data;
            reg Send_en;
            reg Clk;
            reg Reset_n;
            reg [2:0]Baud_set;
            wire uart_tx;
            wire Tx_done;
     uart_byte_tx uart_byte_tx(
        .Data(Data),
         .Send_en(Send_en),
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Baud_set(Baud_set),
        .uart_tx(uart_tx),
        .Tx_done(Tx_done)
        );
        initial Clk=0;
        always#10 Clk=!Clk;
        initial begin
            Reset_n=0;
            Send_en=0;
            Data=0;
            Baud_set=4;
            #201;
            Reset_n=1;
            Data=8'h57;
            Send_en=1;
            #20;
            //下面的意思是一直在等待Tx_done变量,没有等到的话就不往下进行
            @(posedge Tx_done);
            Send_en=0;
            #20000;
            Data=8'h75;
            Send_en=1;
            #20;
            @(posedge Tx_done);
            Send_en=0;
            #20000;
            $stop;
        end
        
endmodule

仿真截图
在这里插入图片描述

收获:
1:在有多级if else的时候要记得加上begin end 否则可能造成识别不出来,导致结果错误~
2:在主代码中为了模拟实际电路中的赋值情况,可以设置#10代表延时时间,上面记得加`timescale 1ns/1ns,因为在下载到电路板上以及vivado识别的时候会自动略去这里的延时,但是这么写对实际电路仿真的时候却又非常有用

串口发送应用之发送数据

题目:使用上面的串口发送模块,设计一个数据发送器,每10ms以115200的波特率发送一个数据,每次发送的数据比前一个数据大一(计数器)
在这里插入图片描述
在这里插入图片描述
uart_byte_tx代码


module uart_byte_tx(
    input [7:0]Data,
    input Send_Go,
    input Clk,
    input Reset_n,
    input [2:0]Baud_set,
    output reg uart_tx,
    output reg Tx_done
    );
    reg Send_en;
    always@(posedge Clk or negedge Reset_n)begin
     if(!Reset_n)
        Send_en<=0;
     else if(Send_Go)
        Send_en<=1;
      else if(Tx_done)
        Send_en <= 0;
      end
      
      reg [7:0]r_Data;
      always@(posedge Clk)begin
      if(Send_Go)
      r_Data<=Data;
      else
      r_Data<=r_Data;
      end
    //Baud_set=0  就让波特率=9600
    //Baud_set=1  就让波特率=19200
    //Baud_set=2  就让波特率=38400
    //Baud_set=3  就让波特率=57600
    //Baud_set=4  就让波特率=115200
    reg [17:0]bps_DR;
    always@(*)begin
       case(Baud_set)
       0:bps_DR=1000000000/9600/20;
       1:bps_DR=1000000000/19200/20;
       2:bps_DR=1000000000/38400/20;
       3:bps_DR=1000000000/57600/20;
       4:bps_DR=1000000000/115200/20;
       default:bps_DR=1000000000/9600/20;
       endcase
    end
    
    wire bps_clk;
    assign bps_clk = (div_cnt == 1);
    
    reg [17:0]div_cnt;
    always@(posedge Clk or negedge Reset_n)begin
         if(!Reset_n)
            div_cnt<=0;
         else if(Send_en)begin
            if(div_cnt==bps_DR-1)
                div_cnt<=0;
            else
                div_cnt<=div_cnt+1'b1;
            end
         else
            div_cnt<=0;
    end
    
    reg [3:0]bps_cnt;
    always@(posedge Clk or negedge Reset_n)begin
     if(!Reset_n)
     bps_cnt<=0;
     else if(Send_en)begin
        if(bps_clk)begin
         if(bps_cnt==11)
             bps_cnt<=0;
          else
             bps_cnt<=bps_cnt+1;
         end
      end
      else
         bps_cnt<=1'b0;
        end
      
      //并串转换
      always@(posedge Clk or negedge Reset_n)begin
           if(!Reset_n)begin
              uart_tx<=1'b0;
              end
           else begin
           case(bps_cnt)
           1:uart_tx<=1'b0;
           2:uart_tx<=r_Data[0];
           3:uart_tx<=r_Data[1];
           4:uart_tx<=r_Data[2];
           5:uart_tx<=r_Data[3];
           6:uart_tx<=r_Data[4];
           7:uart_tx<=r_Data[5];
           8:uart_tx<=r_Data[6];
           9:uart_tx<=r_Data[7];
           10:uart_tx<=1'b1;
           11: uart_tx<=1'b1;
           default: uart_tx<=1'b1;
          endcase
          end
          end

          always@(posedge Clk or negedge Reset_n)begin
           if(!Reset_n)
              Tx_done<=1'b0;
           else if((bps_cnt==10)&&(bps_clk==1))
              Tx_done<=1'b1;
           else 
              Tx_done<=1'b0;
           end
endmodule

uart_tx_test代码顶层模块


module uart_tx_test(
     input Clk,
     input Reset_n,
     output uart_tx
    );
    reg [7:0]Data;
    reg Send_Go;
    wire Tx_done;
     uart_byte_tx uart_byte_tx(
           .Data(Data),
            .Send_Go(Send_Go),
           .Clk(Clk),
           .Reset_n(Reset_n),
           .Baud_set(3'd4),
           .uart_tx(uart_tx),
           .Tx_done(Tx_done)
           );
     //首先计时10ms
     reg [18:0] cnt;
     always@(posedge Clk or negedge Reset_n)begin
         if(!Reset_n)
          cnt<=0;
          else if(cnt==499999)
          cnt<=0;
          else
          cnt<=cnt+1'b1;
         end
      //接着设计何时Send_Go开始发送数据
      always@(posedge Clk or negedge Reset_n)begin
         if(!Reset_n)
         Send_Go<=0;
         else if(cnt==1)
         Send_Go<=1;
         //这里需要注意本来的Send_en是一段时间的信号,比较长,但是Send_Go属于脉冲信号就一下
         else 
         Send_Go<=0;
        end
      //接着设计数据变化
      always@(posedge Clk or negedge Reset_n)begin
       if(!Reset_n)
          Data<=0;
        else if(Tx_done)
         Data<=Data+1;
       end
           
endmodule

uart_tx_test_tb测试文件

`timescale 1ns / 1ns
module uart_tx_test_tb();
    reg Clk;
    reg Reset_n;
    wire uart_tx;
uart_tx_test uart_tx_test(
     .Clk(Clk),
     .Reset_n(Reset_n),
     .uart_tx(uart_tx)
    );
    initial Clk=0;
    always #10 Clk=!Clk;
    initial begin 
        Reset_n=0;
        #201;
        Reset_n=1;
        #50000000;
        $stop;
    end
    
endmodule

仿真截图
在这里插入图片描述

收获
1:对于一些很重要的控制信号最好单独拿出来写,参杂在一些其他功能中很可能会产生其他的影响,例如Tx_done
2:Go一般是单脉冲信号,en是电平信号

串口发送应用之采用状态机实现多字节数据发送

有些人会了8位的串口传送数据但是总会有一些问题例如,
1.ADC,采样的结果是12位的,怎么使用串口发送
2.16位的数据,怎样通过串口发送
3.有多个字节的数据通过串口发送
不能直接将8位改成12位、16位,因为UART规定了,发送的数据位只能有6、7、8位所以应该把大于8位的字节分成多个字节进行发送

三种情况:
1:没有开始发送(上一次的已经发送完成,新的40位数据的发送请求没有出现)
2:来了发送40位数据的请求信号
3:依次发送数据的状态

第一个状态:第一种情况的时候,咱干什么事情?等待传输请求(Trans_Go)的到来,Data40[7:0]给到uart_byte_tx的Data,并同时产生Send_Go信号,启动第一个字节的发送
接着应该等待,等待Tx_Done信号的到来
40位数据是否发完了?发完了,回到第一个状态继续等Trans_Go,没发完,启动下一个8位数据的发送

源代码:

//该模块完成的功能是采用状态机实现多字节数据发送
module uart_tx_data(
     input Clk,
     input Reset_n,
     input [39:0]Data40,
     input Trans_Go,//表示合适开始发送数据,即传输请求
     output reg Trans_Down,//表示一次40位的数据传输完成
     output  uart_tx
    );
       reg [7:0]Data;
       reg Send_Go;
       wire Tx_done;
       
    uart_byte_tx uart_byte_tx(
        .Data(Data),
        .Send_Go(Send_Go),
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Baud_set(3'h4),
        .uart_tx(uart_tx),
        .Tx_done(Tx_done)
        );
        reg [2:0]state;
        
        always@(posedge Clk or negedge Reset_n)begin
             if(!Reset_n)begin
                state<=0;
                Data<=0;
                Send_Go<=0;
                Trans_Down<=0;
                end
             //下面部分也可以用case语句实现,记得加default~
             else if(state==0)begin
                   Trans_Down<=0;
                   if(Trans_Go)begin
                      Data<=Data40[7:0];
                      state<=1;
                      Send_Go<=1;
                   end
                   else begin
                   Data<=Data;
                   state<=0;
                   Send_Go<=0;
                   end
             end
             else if(state==1)begin
                  if(Tx_done)begin
                     Data<=Data40[15:8];
                     state<=2;
                     Send_Go<=1;
                  end
                  else begin
                  Data<=Data;
                  state<=1;
                  Send_Go<=0;
                  end
            end
                          
      else if(state==2)begin
               if(Tx_done)begin
                  Data<=Data40[23:16];
                  state<=3;
                  Send_Go<=1;
               end
               else begin
               Data<=Data;
               state<=2;
               Send_Go<=0;
               end
         end
                                       
                                       else if(state==3)begin
                                                          if(Tx_done)begin
                                                             Data<=Data40[31:24];
                                                             state<=4;
                                                             Send_Go<=1;
                                                          end
                                                          else begin
                                                          Data<=Data;
                                                          state<=3;
                                                          Send_Go<=0;
                                                          end
                                                    end
              else if(state==4)begin
              if(Tx_done)begin
                       Data<=Data40[39:32];
                            state<=5;
                            Send_Go<=1;
                             end
                   else begin
                             Data<=Data;
                            state<=4;
                                 Send_Go<=0;
                              end
                             end        
             else if(state==5)
             begin
             if(Tx_done)begin
             state<=0;
             Trans_Down<=1;
             Send_Go<=0;
             end
             else begin
             Data<=Data;
               state<=5;
               Send_Go<=0;
              end
             end                               
             
             
             
             
             
        end
        
endmodule

测试文件:

`timescale 1ns / 1ns

module uart_tx_data_tb();
    reg Clk;
    reg Reset_n;
    reg [39:0]Data40;
    reg Trans_Go;
    wire Trans_Down;
    wire uart_tx;
    
   uart_tx_data uart_tx_data(
     Clk,
     Reset_n,
     Data40,
     Trans_Go,//表示合适开始发送数据,即传输请求
     Trans_Down,//表示一次40位的数据传输完成
     uart_tx
    );
    initial Clk=0;
    always #10 Clk=!Clk;
    initial begin
         Reset_n=0;
         Data40=0;
         Trans_Go=0;
         #201
         Reset_n=1;
         #201
         Data40=40'h123456789a;
         Trans_Go=1;
         @(posedge Trans_Down);
         Trans_Go=0;
         #201
          Data40=40'ha987654321;
          Trans_Go=1;
          @(posedge Trans_Down);
          #200000
          $stop;
    end
endmodule

仿真截图:
在这里插入图片描述

任务:
1:优化状态机,实现只要2个或3个状态实现发送的功能,并易于修改为发送任意个字节的数据(加个计数器进行二级验证~)
2:思考不使用状态机实现的方法

任务一代码(已进行仿真)

//该模块完成的功能是采用状态机实现多字节数据发送
module uart_tx_data2(
     input Clk,
     input Reset_n,
     input [39:0]Data40,
     input Trans_Go,//表示合适开始发送数据,即传输请求
     output reg Trans_Down,//表示一次40位的数据传输完成
     output  uart_tx
    );
       reg [7:0]Data;
       reg Send_Go;
       wire Tx_done;
       
    uart_byte_tx uart_byte_tx(
        .Data(Data),
        .Send_Go(Send_Go),
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Baud_set(3'h4),
        .uart_tx(uart_tx),
        .Tx_done(Tx_done)
        );
        reg [2:0]state;
        
        //用来记录当前发送到第几个字节了
        reg [2:0]cnt;
        always@(posedge Clk or negedge Reset_n)begin
            if(!Reset_n)begin
               cnt<=0;
            end 
            else if(Tx_done) begin
              if(cnt==4)
               cnt<=0;
               else
               cnt<=cnt+1;
               end
            
         end
        
        always@(posedge Clk or negedge Reset_n)begin
             if(!Reset_n)begin
                state<=0;
                Data<=0;
                Send_Go<=0;
                Trans_Down<=0;
                end
             //下面部分也可以用case语句实现,记得加default~
             else if(state==0)begin
                   Trans_Down<=0;
                   if(Trans_Go)begin
                   Trans_Down<=0;
                   if(Tx_done)begin
                   case(cnt)
                        0:begin Data<=Data40[7:0]; state<=0;end 
                        1:begin Data<=Data40[15:8];state<=0;end
                        2:begin Data<=Data40[23:16];state<=0;end
                        3:begin Data<=Data40[31:24];state<=0;end
                        4:begin Data<=Data40[39:32];state<=1;end
                        default:Data<=Data;
                        endcase
                      Send_Go<=1;
                   end
                   else begin
                   Data<=Data;
                   state<=0;
                   Send_Go<=0;
                   end
                   end
             end      
             else if(state==1)
             begin
             if(Tx_done)begin
             state<=0;
             Trans_Down<=1;
             Send_Go<=0;
             end
             else begin
             Data<=Data;
               state<=1;
               Send_Go<=0;
              end
             end                               
       
        end
        
endmodule

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

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

相关文章

CRM联系人管理是什么?为什么它很重要?

在今天这个快节奏的商业世界里&#xff0c;要记住每个客户的名字和他们的个人喜好是很难的。这就是为什么必须以电子方式存储数据&#xff0c;在需要时与团队成员分享&#xff0c;并不断收集信息以成功地与客户和顾客互动的原因。本文是为那些想利用CRM系统改善客户关系的企业主…

是时候为您的银行机构选择构建一个知识库了!

知识管理和自助服务客户支持在银行业至关重要。选择正确的知识库对于帮助客户和在内部共享信息同样重要。繁重的法规和合规性需求意味着银行必须在他们选择的知识库类型上投入大量思考。许多银行知识库已经过时&#xff0c;无法为客户提供成功使用您的产品和服务所需的信息。在…

算法训练营 day51 动态规划 打家劫舍系列

算法训练营 day51 动态规划 打家劫舍系列 打家劫舍 198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#…

Educational Codeforces Round 143 (Rated for Div. 2) 题解

D. Triangle Coloring 大意&#xff1a; 给定一个有 n 个点 n 条边的无向带权图&#xff0c;保证 n 为 6 的倍数&#xff0c;组成 n/3个三元环&#xff1a; (1,2,3),(4,5,6),⋯。 现在给每个点染上红或蓝两种颜色&#xff0c;要求红色有 n/2 个点、蓝色也有 n/2 个点 。 定义…

【免费教程】 高光谱遥感原理及地表主要信息提取及项目实战经验分享

高光谱分辨率遥感高光谱分辨率遥感是用很窄而连续的光谱通道对地物持续遥感成像的技术。在可见光到短波红外波段其光谱分辨率高达纳米(nm)数量级&#xff0c;通常具有波段多的特点&#xff0c;光谱通道数多达数十甚至数百个以上&#xff0c;而且各光谱通道间往往是连续的&#…

OpenGL ES上下文环境搭建

由于 OpenGL ES 一开始就是为跨平台设计的&#xff0c;所以它本身并不承担窗口管理以及上下文环境构建的职责&#xff0c;这个职责需要由各自的平台来承担。 Android 平台使用的是 EGL&#xff0c;EGL 是 Khronos 创建的一个框架&#xff0c;用来给 OpenGL 的输出与设备的屏幕…

手撸React组件库前必须清楚的9个问题

1. 组件库文档问题 以前常用的组件库文档storybook&#xff0c;包括现在也有用dumi、vitepress做组件库文档等。storybook缺点不美观、webpack的热更新太慢&#xff0c;虽然新版本支持了vite提高了速度但还不算稳定。好在各种文档、mdx、测试等组件第三方工具很多集成进去能很…

day37 动态规划 | 738、单调递增的数字 714、买卖股票的最佳时机含手续费 968、监控二叉树

题目 738、单调递增的数字 给定一个非负整数 N&#xff0c;找出小于或等于 N 的最大的整数&#xff0c;同时这个整数需要满足其各个位数上的数字是单调递增。 &#xff08;当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。&a…

什么是项目沟通管理? 借助系统软件管理项目沟通

在如今竞争激烈的市场环境中&#xff0c;很多企业内部往往有多个项目同时进行着&#xff0c;不同类别的项目需要项目负责人实时跟进&#xff0c;而成功的项目是离不开项目人员之间的日常互相沟通进行。只有良好的沟通&#xff0c;项目经理才能够获取到足够的信息&#xff0c;可…

国产真无线蓝牙耳机哪个好?国产半入耳蓝牙耳机推荐

近几年&#xff0c;生活中随处可见的有戴蓝牙耳机的人&#xff0c;而蓝牙耳机也因为使用更便捷、功能更先进受到了不少用户的喜爱。蓝牙耳机按照佩戴方式来划分&#xff0c;可以有入耳式、半入耳式、头戴式等。在此&#xff0c;我来给大家推荐几款国产半入耳蓝牙耳机&#xff0…

LeetCode 606.根据二叉树创建字符串,102.二叉树的层序遍历和牛客 二叉搜索树与双向链表

文章目录1. 根据二叉树创建字符串2. 二叉树的层序遍历3. 二叉搜索树与双向链表1. 根据二叉树创建字符串 难度 简单 题目链接 解题思路&#xff1a; 这里的意思就是&#xff1a;用前序遍历遍历这颗树。然后左子树和右子树分别在一个括号里。括号里的规则是&#xff1a; 1.左右都…

W800|WIFI|CDK|W80X SDK v1.00.10|官方demo|学习(2):t-connect

W800 SDK代码及相关文档获取地址&#xff1a; https://www.winnermicro.com/html/1/156/158/558.html 1、W800 SDK v1.00.10更新内容&#xff1a; 1. 驱动更新 1&#xff09;提供模组ADC校准功能接口 2&#xff09;修复PSRAM IO复用不完整问题 3&#xff09;Flash驱动修改不再…

CSDN每日一练非降序数组 C语言/C++

题目名称&#xff1a;非降序数组 时间限制&#xff1a;1000ms内存限制&#xff1a;256M 题目描述 写一个函数&#xff0c;传入两个非降序的整数数组&#xff08;A, B&#xff09;&#xff0c;将 A, B 合并成一个非降序数组 C&#xff0c;返回 C&#xff08;不要使用内置 sort 函…

新项目分析

1&#xff1a;数据类型处理 # sep‘\s‘ 这是正则表达式&#xff0c;通过一定规则的表达式来匹配字符串用的 \s 表示空白字符&#xff0c;包括但不限于空格、回车(\r)、换行(\n)、tab或者叫水平制表符(\t)等&#xff0c;这个根据编码格式不同代表的含义也不一样&#xff0c;感…

一文讲解系统调用与函数调用有什么区别?

作为程序员你肯定写过无数的函数&#xff0c;假设有这样两个函数&#xff1a; void funcB() { }void funcA() {funcB(); } 函数之间是可以相互调用的&#xff0c;这很简单很happy有没有。 要知道是代码、是函数就可以相互调用&#xff0c;不管你用什么语言写的。 假设funcB…

2023/02/21 事件循环-eventloop 宏任务 微任务 讲解

1 JS是单线程 js是单线程的。也就是说&#xff0c;同一个时间只能做一件事。作为浏览器脚本语言&#xff0c;与它的用途有关。JavaScript的主要用途是和用户互动&#xff0c;以及操作DOM&#xff0c;这决定了它只能是单线程。 js是单线程的。也就是说&#xff0c;同一个时间只…

如何使用 API 工具做 Websocket 测试

在 API 测试中&#xff0c;对 Websocket 协议的支持呼声越来越高&#xff0c;今天给大家推荐一款 开源的 API 管理工具——Postcat&#xff0c;以及教教大家&#xff0c;如何利用 API 管理工具做 Websocket 测试。 在线 Demo 链接&#xff1a;Postcat - Open Source API Ecosys…

17 个短代码,检验你 Python 基本功

Python 是一门非常优美的语言&#xff0c;其简洁易用令人不得不感概人生苦短。在本文中&#xff0c;蛋糕将带大家回顾 17个非常有用的 Python 技巧&#xff0c;例如查找、分割和合并列表等。这 17 个技巧都非常简单&#xff0c;但它们都很常用且能激发不一样的思路。 人生苦短&…

来一波骚操作,Java内存模型

文章整理自 博学谷狂野架构师 什么是JMM 并发编程领域的关键问题 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息。在编程中&#xff0c;线程之间的通信机制有两种&#xff0c;共享内存和消息传递。 ​ 在共享内存的并发模型里&#xff0c;线程之间共享程序的公共…

项目管理从需求管理开始--不懂需求管理还敢带项目?

分析报告指出&#xff0c;多达76%的项目失败是因为差劲的需求管理&#xff0c;这个是项目失败的最主要原因&#xff0c;比技术、进度失控或者混乱的变更管理还要关键。很多PMO和PM却没有把需求管理重视起来&#xff0c;甚至认为这只是产品经理的事情&#xff0c;自己只做交付即…