手把手教你写UART(verilog)

最近工作用uart用的比较多,为了让自己更好的掌握这个协议,写了这篇文章,解读了uart程序的编写过程(程序参考了米联客的教程)。

最基础的概念

        UART是用来让两个设备之间传输数据的协议,毕竟我不能直接给你一串01序列,你肯定不知道我表达的意思是啥。通信涉及到这么几个基本的问题:什么时候开始发,什么时候结束?在接收数据的过程中,接收方以什么速率接收?

        不同的协议有不同的解决方式,比如I2C,和SPI,他们会利用一个时钟线来解决这些问题,用数据线确定具体的传输数据,在UART中没有时钟线,那只能靠数据线来解决这些问题了,也就是通过下面这些东西:

        波特率:由于UART没有时钟线,那么就通信双方必须规定传输的速度是多少,比如每秒发10个符号,那么波特率就是10。

        起始位:UART定义当数据总线由高电平变低电平就是开始了,有人可能会疑惑,假如传数据的时候先发了一个1,下一个发0,不会被误判为起始信号?不会的,谜底就在谜面上,因为这是传数据的时候,要传完才会重新根据(1-0)来判断起始位。

        数据位:一个数据占用一个波特率时钟,一般是一次发8个数据位。

        停止位:因为起始时是需要通过高电平拉低来判断的,所以很自然传输完一个字节的数据,就得把数据线拉高对吧。所以一般数据传输结束后会保持1/2个高电平,或者没有新的数据了一直高也行。

        时序图:

发送端代码解析

        开局一根发信号数据线,首先我们试着写一个周期的数据发送。

   reg [7:0]data_send;  //要发送的数据reg uart_out;            //开局的线reg [3:0]count=4'b0;always @(posedge clk)beginif(count==0)uart_out<=1;                       //最开始是高电平else if(count==1)uart_out<=0;                  //拉低信号就是起始位else if(count<10)uart_out<=data_send[count-2]; //依次发送8个数据 else if(count==10) uart_out<=1;                //最后拉高作为停止位count<=count+1;end

        这部分程序能按UART要求的时序把数据发出去。但里面有很多问题。

        首先,它是按系统时钟的速率发的,UART要求的速率是一些固定的速率,比如9600,115200等。(当然如果你自己把接收端也写了,两边统一一下那就随便你定速率了)所以我们首先得把时钟信号变为相应波特率的时钟,那么就需要一个分频的系数:

        BAUD_DIV=时钟频率/波特率。程序如下:

        Interger BAUD_DIV=10416; //这是在100M的时钟下,设置9600波特率的分频系数。wire bps_en;//设置一个使能信号,当计数器到达分频系数就使能。reg[13:0] baud_count;//使能计数器always @(posedge clk)beginif(baud_count<BAUD_DIV)baud_count<=baud_count+1;else  baud_count<=0;endassign       bps_en= (baud_count==BAUD_DIV);//计数器达到分频系数,这个信号放到发送信号程序的判断条件里面,就可以按这个波特率发信号了。

       以上就是最基本的发数据的函数了,按照一定的波特率,一定的发数据的顺序(起始--数据位--终止位)。 除此之外,根据实际的应用,可能要需要加一些别的信号线。比如程序一开始就运行直接开始输出数据了,是不是加一个开启信号,比如是否要记录发了多少bit数据,发了多少组数据了,发送状态是正忙还是空闲等等。

        这里的发送状态是否正忙这个信号线还是挺重要的,当你发送多个周期的数据的时候,需要通过这个信号来切换,当它正忙转为不忙的时候,你就知道:奥,一个8bit的数据传输完了,那我输入的数据可以改变了。

        这段代码可以这样写:

wire uart_busy;
assign uart_busy==(count>0 &count<10)

        这个写的肯定是有问题的,但只是表达一下这个意思,可以通过传输过程的计数器来对这个信号线赋值。

        下面是一个比较完整的发送部分程序,除了上面提到的那几个信号线,还有个数据发送请求,也就是告诉它什么时候开始传数据,看完上面的部分应该会比较好理解了。

`timescale 1ns / 1ns//仿真时间刻度/精度
module uiuart_tx#
(
parameter integer BAUD_DIV = 10416 //设置采样系数 (时钟/采样率-1)
)
(
input clk_i, //系统时钟输入
input uart_rstn_i, //系统复位输入
input uart_wreq_i, //发送数据请求
input [7:0] uart_wdata_i, //发送数据
output uart_wbusy_o, //发送状态忙,代表正在发送数据
output uart_tx_o //uart tx 发送总线
);
localparam UART_LEN = 4'd10; //设置 uart 发送的 bit 数量为 10,代表 1bit 起始位,8bits 数据,1bit 停止位
wire bps_en ; //发送使能
reg uart_wreq_r = 1'b0; //寄存一次 uart_wreq_i
reg bps_start_en = 1'b0; //波特率计数器启动使能,也是发送启动使能
reg [13:0] baud_div = 14'd0; //波特率计数器
reg [9 :0] uart_wdata_r = 10'h3ff; //寄存 uart_wreq_i
reg [3 :0] tx_cnt = 4'd0; //计数发送了多少 bits
assign uart_tx_o = uart_wdata_r[0]; //总线上的数据,始终是 uart_wdata_r[0]
assign uart_wbusy_o = bps_start_en; //总线忙标志,即是 bps_start_en 为有效,即当总线忙于发送,总线忙
// 发送使能
assign bps_en = (baud_div == BAUD_DIV); //产生一次发送使能信号,条件是 baud_div == BAUD_DIV,波特率计数
达成
//波特率计数器
always@(posedge clk_i )begin
if((uart_rstn_i== 1'b0) || (uart_wreq_i==1'b1&uart_wreq_r==1'b0))begin
baud_div <= 14'd0;
end
else begin
if(bps_start_en && baud_div < BAUD_DIV) //bps_start_en 的信号拉高,表示开始发送
baud_div <= baud_div + 1'b1; //且 baud_div < BAUD_DIV 波特率计算,未达到波特率
baud_div+1
else
baud_div <= 14'd0; //达到清零
end
end
always@(posedge clk_i)begin
uart_wreq_r <= uart_wreq_i; //寄存一次 uart_wreq_i 信号
end
//当 uart_wreq_i 从低电平变为高电平,启动发送
always@(posedge clk_i)begin
if(uart_rstn_i == 1'b0)
bps_start_en <= 1'b0; //复位,计数清零
else if(uart_wreq_i==1'b1&uart_wreq_r==1'b0) //uart_wreq_i 上升沿激活
bps_start_en <= 1'b1; //激活后将 bps_start_en 拉高,传输开始
else if(tx_cnt == UART_LEN) //tx_cnt 用于计数当前发送的 bits 数量,当达到预定值 UART_LEN
bps_start_en <= 1'b0; //将 bps_start_en 拉低,传输结束
else
bps_start_en <= bps_start_en;
end
//发送 bits 计数器
always@(posedge clk_i)begin
if(((uart_rstn_i== 1'b0) || (uart_wreq_i==1'b1&uart_wreq_r==1'b0))||(tx_cnt == 10))//当复位、启动
发送、发送完成,重置 tx_cnt
tx_cnt <=4'd0;
else if(bps_en && (tx_cnt < UART_LEN)) //tx_cnt 计数器,每发送一个 bit 加 1
tx_cnt <= tx_cnt + 1'b1;
end
//uart 发送并串移位控制器
always@(posedge clk_i)begin
if((uart_wreq_i==1'b1&uart_wreq_r==1'b0)) //当发送请求有效,寄存需要发送的数据到 uart_wdata_r
uart_wdata_r <= {1'b1,uart_wdata_i[7:0],1'b0};//寄存需要发送的数据,包括 1bit 起始位,8bits 数据,
1bit 停止位
else if(bps_en && (tx_cnt < (UART_LEN - 1'b1))) //shift 9 bits
uart_wdata_r <= {uart_wdata_r[0],uart_wdata_r[9:1]}; //并串转换,将并行数据依次传输
else
uart_wdata_r <= uart_wdata_r;
end
endmodule

    仿真验证  

        这段代码用计数器做了一个复位,用状态机控制输入数据的变化,可以根据自己的需要更改状态3的程序。

module top();localparam      SYS_TIME   =  'd10;//时钟周期,以ns为单位
reg 			 sysclk_i;			//系统时钟initial begin	  //仿真初始化
sysclk_i =0;#2000000 $finish;			
endalways #(SYS_TIME/2) sysclk_i = ~sysclk_i;	  //产生主时钟wire uart_tx_o  ;  // UART 串口发送总线
wire     uart_rstn_i; //内部同步复位
wire     uart_wbusy;  //UART发送驱动器正忙
reg      t1s_dly_en;  //1S延迟
reg[1:0] S_UART_TX;   //UART 发送状态机
reg[1:0] tx_index;    //发送index计数器
reg      uart_wreq;   //UART发送请求  
reg[7:0] uart_wdata;  //UART发送数据寄存器 
reg[7:0] uart_tx_buf[0:2]; //发送缓存
reg [15:0]rst_cnt = 16'd0; //复位计数器assign uart_rstn_i = rst_cnt[15]; //复位//上电通过计数器计数,实现复位
always @(posedge sysclk_i)beginrst_cnt <= (rst_cnt[15] == 1'b0) ? (rst_cnt + 1'b1) : rst_cnt ;
end//数据发送状态机
always @(posedge sysclk_i)beginif(uart_rstn_i==1'b0)begin //初始化uart_tx_buf,为hello fpga等字符共计12 BYTES,以及其他寄存器uart_tx_buf[0]  <=8'b10000001;uart_tx_buf[1]  <=8'b10101010;uart_tx_buf[2]  <=8'd5;uart_wdata      <= 8'd0;uart_wreq       <= 1'b0;S_UART_TX       <= 2'd0;tx_index        <= 2'd0;endelse begincase(S_UART_TX)0:beginif(!uart_wbusy)begin//如果UART发送驱动器不忙uart_wdata <= uart_tx_buf[tx_index];//准备发送数据,发送tx_index所指向的数据uart_wreq <= 1'b1; //设置uart_wreq为高电平,请求发送数据endelse begin //当总线忙uart_wreq <= 1'b0; //重置uart_wreqS_UART_TX <= 2'd1; //进入下一状态endend1:begin//该状态等待总线空闲S_UART_TX <= (uart_wbusy == 1'b0) ? 2'd2: S_UART_TX;end 2:begin//更新tx_index计数器if(tx_index < 3)begin //每一帧发送12个字节tx_index <= tx_index + 1'b1; //tx_index 加计数S_UART_TX  <= 2'd0; //进入下一状态endelse begin //如果tx_index==11 代表所有数据发送完毕tx_index   <= 2'd0; //重置tx_indexS_UART_TX  <= 2'd3; //下一状态end end3: S_UART_TX <= S_UART_TX;   endcaseend   end//例化UART 发送驱动器模块
uiuart_tx#
(
.BAUD_DIV(100000000/115200-1)  //波特率计算    BAUD_DIV = 系统时钟/波特率-1
)
uart_tx_u 
(
.clk_i(sysclk_i),//系统时钟输入
.uart_rstn_i(uart_rstn_i), //系统复位输入
.uart_wreq_i(uart_wreq), //UART发送(写)数据请求
.uart_wdata_i(uart_wdata), //UART发送(写)数据
.uart_wbusy_o(uart_wbusy),//UART发送驱动器忙
.uart_tx_o(uart_tx_o) //UART 发送串行总线
);
endmodule

仿真结果

可以看到,那根开局的线就是按我们想要的时序发送数据的。

接收端

        说完发送端我们再来看看接收端,它也是靠着一根数据线来接收数据的,时序和发送端一致,速率也一致。所以这里就不一点点的讲解了

module uart_rx#
(parameter integer  BAUD_DIV     = 10416  //波特率分频参数,BAUD_DIV=系统时钟频率/波特率-1 比如100M系统时钟,波特率115200 BAUD_DIV= 100_000_000/115200-1
)
(
input clk_i, //系统时钟输入
input uart_rx_rstn_i,//系统复位输入
input uart_rx_i,//uart rx 总线信号输入
output [7:0] uart_rdata_o,//uart rx接收到的数据输出
output uart_rvalid_o// uart rx 接收数据有效信号,当为1的时候uart_rdata_o数据有效
);localparam  BAUD_DIV_SAMP = (BAUD_DIV/8)-1;                            //多次采样,按照波特率系数的八分之一进行采样wire bps_en       ; //波特率使能信号
wire samp_en      ; //采样使能信号
wire bit_cap_done ; //uart rx总线信号采样有效数据完成
wire uart_rx_done ; //uart 1byte 接收完成
wire bit_data     ; //接收的1bit数据
wire uart_rx_int  ; //uart_rx_int的启动信号检测,当变为低电平,代表可能存在起始位(UART 起始位为低电平)reg [13:0]  baud_div = 14'd0;//波特率分频计数器
reg [13:0]  samp_cnt = 14'd0;//采样计数器
reg [4 :0]  uart_rx_i_r = 5'd0;//异步采集多次寄存
reg [3 :0]  bit_cnt=4'd0;//bit 计数器
reg [3 :0]  cap_cnt=4'd0;//cap 计数器
reg [4 :0]  rx_bit_tmp = 5'd0;//rx_bit_tmp用于多次采样,通过计算采样到高电平次数和低电平次数,判断本次采样是高电平还是低电平
reg [7 :0]  rx_data = 8'd0;//数据接收寄存器reg bps_start_en_r = 1'b0;
reg bit_cap_done_r = 1'b0;
reg bps_start_en,start_check_done,start_check_failed;assign bps_en       =   (baud_div == (BAUD_DIV - 1'b1));                     //完成一次波特率传输信号
assign samp_en      =   (samp_cnt == (BAUD_DIV_SAMP - 1'b1 ));               //完成一次波特率采样信号
assign bit_cap_done =   (cap_cnt  == 3'd7);//采样计数
assign uart_rx_done =   (bit_cnt  == 9)&&(baud_div == BAUD_DIV >> 1);//当停止位开始,提前半停止位,发送uart_rx_done信号,以便提前准备进入下一个数据的接收assign bit_data     =   (rx_bit_tmp < 5'd15) ? 0 : 1; //rx_bit_tmp用于多次采样,通过计算采样到高电平次数和低电平次数,判断本次采样是高电平还是低电平,提高抗干扰能力
//连续5次信号拉低,判断开始传输
assign uart_rx_int  =   uart_rx_i_r[4] | uart_rx_i_r[3] | uart_rx_i_r[2] | uart_rx_i_r[1] | uart_rx_i_r[0];
assign uart_rdata_o   =   rx_data;
assign uart_rvalid_o  =   uart_rx_done;   //波特率计数器
always@(posedge clk_i)beginif(bps_start_en && baud_div < BAUD_DIV)                 //baud_div计数,目标值BAUD_DIV baud_div <= baud_div + 1'b1;else baud_div <= 14'd0;
end//8bit采样使能,8倍波特率采样,也就是这个计数器,用于产生8倍过采样
always@(posedge clk_i)beginif(bps_start_en && samp_cnt < BAUD_DIV_SAMP)             //bps_start_en高电平有效,开始对bit进行采样,samp_cnt以8倍于波特率速度对每个bit采样samp_cnt <= samp_cnt + 1'b1;                         //samp_cnt计数+1       else samp_cnt <= 14'd0;                                   //samp_cnt计数清零
end//uart rx bus asynchronous to Synchronous
always@(posedge clk_i)begin uart_rx_i_r <= {uart_rx_i_r[3:0],uart_rx_i};             //uart_rx_i的数据存入uart_rx_i_r进行缓存
end//uart接收启动检查
always@(posedge clk_i)beginif(uart_rx_rstn_i == 1'b0 || uart_rx_done || start_check_failed) //bps_start_en拉低的三种情况,复位、接收完成、校验失败bps_start_en    <= 1'b0;                                               //接收结束else if((uart_rx_int == 1'b0)&(bps_start_en==1'b0))//当判断到uart_rx_int == 1'b0,并且总线之前空闲(bps_start_en==1'b0,代表总线空闲)bps_start_en    <= 1'b1;//使能波特率计数器使能
end//uart接收启动使能
always@(posedge clk_i)beginbps_start_en_r    <= bps_start_en;                              //bps_start_en信号打一拍,方便后续上升沿捕捉
endalways@(posedge clk_i)beginif(uart_rx_rstn_i == 1'b0 || start_check_failed)begin//当系统复位,或者start_check_failed,重置start_check_done和start_check_failedstart_check_done    <= 1'b0;start_check_failed  <= 1'b0;end    else if(bps_start_en == 1'b1&&bps_start_en_r == 1'b0) begin//当检测到start信号,也重置start_check_done和start_check_failedstart_check_done    <= 1'b0;start_check_failed  <= 1'b0;endelse if((bit_cap_done&&bit_cap_done_r==1'b0)&&(start_check_done == 1'b0))begin//第一个波特率采样,用于判断是否一个有效的起始位,如果不是有效的,start_check_failed设置为1start_check_failed <= bit_data ? 1'b1 : 1'b0;start_check_done   <= 1'b1;//不管是否start_check_failed==1,都会设置start_check_done=1,但是start_check_failed==1,会下一个系统时钟重置start_check_done=0end     
end//bits 计数器
always@(posedge clk_i)beginif(uart_rx_rstn_i == 1'b0 || uart_rx_done || bps_start_en == 1'b0)//复位、接收完成、或者总线空闲(bps_start_en == 1'b0),重置bit_cntbit_cnt   <= 4'd0;                                                    else if(bps_en)//每一个bps_en有效,加1bit_cnt <= bit_cnt + 1'b1;  // bit_cnt计数器用于计算当前采样了第几个bit 
end//8次过采样,提高抗干扰
always@(posedge clk_i)beginif(uart_rx_rstn_i == 1'b0 || bps_en == 1'b1 || bps_start_en == 1'b0) begin //当uart_rx_rstn_i=0或者bps_en=1或者bps_start_en==0,重置cap_cnt和rx_bit_tmpcap_cnt     <= 4'd0;rx_bit_tmp  <= 5'd15; endelse if(samp_en)begin//bit采样使能cap_cnt     <= cap_cnt + 1'b1;//cap_cnt用于记录了当前是第几次过采样,1个bit采样8次rx_bit_tmp  <= uart_rx_i_r[4] ? rx_bit_tmp + 1'b1 :  rx_bit_tmp - 1'b1;   //多次采样,如果是高电平+1,如果是低电平-1,最终看本次bit采样结束rx_bit_tmp如果小于15代表是低电平end                                                                                   
end//寄存一次bit_cap_done,用于产生高电平触发脉冲下面用到
always@(posedge clk_i)bit_cap_done_r <= bit_cap_done;always@(posedge clk_i)beginif(uart_rx_rstn_i == 1'b0 || bps_start_en == 1'b0)//当复位或者总线空闲,重置rx_datarx_data  <= 8'd0;  else if(start_check_done&&(bit_cap_done&&bit_cap_done_r==1'b0)&&bit_cnt < 9)//当start_check_done有效,并且bit_cnt<9,每次bit_cap_done有效,完成一次移位寄存rx_data  <= {bit_data,rx_data[7:1]};                                         //串并转换,将数据存入rx_data 中,共8位
endendmodule

仿真结果

仿真代码如下

module uart_top_tb();localparam		BPS 	     = 'd115200		;			        //波特率
localparam 		CLK_FRE    = 'd100_000_000	;	        //系统频率
localparam    CLK_TIME   = 'd1000_000_000/CLK_FRE; //计算系统时钟周期,以ns为单位
localparam		BIT_TIME   = 'd1000_000_000/BPS ;	   //计算出传输每个bit所需要的时间以ns为单位
localparam    NUM_BYTES  = 3;               //需要发送的BYTESreg                    sysclk_p;			      //系统时钟
reg                    uart_rstn;           //系统复位
reg                    bsp_clk ;            //波特率时钟
reg                    uart_tx;             //uart 数据总线发送,接到UART接收模块的uart_rx
wire [7:0]             uart_rx_data_o;      //uart 接收到有效数据
wire                   uart_rvalid_o;       //uart 接收数据有效信号reg [8*NUM_BYTES-1:0]  uart_send_data;      //需要发送的数据
reg [7:0]              uart_send_data_r;    //寄存每次需要发送的BYTEinteger i,j;//例化模块
uiuart_rx uart_top_inst
(
.clk_i(sysclk_p),
.uart_rx_rstn_i(uart_rstn),
.uart_rx_i(uart_tx),
.uart_rdata_o(uart_rx_data_o),
.uart_rvalid_o(uart_rvalid_o)
);//仿真初始化
initial begin	//初始化REG寄存器
sysclk_p =0;
uart_rstn = 0;
bsp_clk  = 0;  
uart_tx  = 1;
i=0;
j=0;uart_send_data   =0;
uart_send_data_r =0;#1000;//延迟1000ns
uart_rstn =1; //复位完成
#1000;//延迟1000nsuart_send_data[(0*8) +: 8] = 8'b1001_0101;//初始化需要发送的第1个BYTE
uart_send_data[(1*8) +: 8] = 8'b0000_0101;//初始化需要发送的第2个BYTE
uart_send_data[(2*8) +: 8] = 8'b1000_0100;//初始化需要发送的第3个BYTE//uart tx 发送数据for(i=0; i<NUM_BYTES;i=i+1)beginuart_send_data_r = uart_send_data[(i*8) +: 8];//寄存需要发送的数据到寄存器$display("uart_send_data : 0x%h",uart_send_data_r);//打印准备发送的数据@(posedge bsp_clk);  //发送起始位1bituart_tx = 1'b0;for(j=0;j<8;j=j+1)begin//发送数据8bits@(posedge bsp_clk);  //发送uart_tx = uart_send_data_r[j];end@(posedge bsp_clk);//发送停止位1bituart_tx = 1'b1;  end@(posedge bsp_clk); #200 $finish;			
endalways #(CLK_TIME/2) sysclk_p = ~sysclk_p;	  //产生主时钟
always #(BIT_TIME/2) bsp_clk  = ~bsp_clk;		//产生波特率时钟endmodule

写在最后

       希望对大家有所帮助,欢迎留言讨论,一起进步~

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

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

相关文章

鸿蒙HarmonyOS应用开发为何选择ArkTS不是Java?

前言 随着智能设备的快速发展&#xff0c;操作系统的需求也变得越来越多样化。为了满足不同设备的需求&#xff0c;华为推出了鸿蒙HarmonyOS。 与传统的操作系统不同&#xff0c;HarmonyOS采用了一种新的开发语言——ArkTS。 但是&#xff0c;刚推出鸿蒙系统的时候&#xff0…

JavaScript进阶(四)---js解构

目录 一.定义&#xff1a; 二.类型&#xff1a; 1.数组解构&#xff1a; 1.1变量和值不匹配的情况 1.2多维数组 2.对象解构 3.对象数组解构 4.函数参数解构 5.扩展运算符 一.定义&#xff1a; JavaScript 中的解构&#xff08;Destructuring&#xff09;是一种语法糖&…

Spring Web MVC入门(2)(请求1)

目录 请求 1.传递单个参数 2.传递多个参数 3.传递对象 4.后端参数重命名(后端参数映射) 非必传参数设置 5.传递数组 请求 访问不同的路径就是发送不同的请求.在发送请求时,可能会带一些参数,所以学习Spring的请求,主要是学习如何传递参数到后端及后端如何接收. 1.传递单…

Java时间复杂度介绍以及枚举

时间复杂度 从小到大&#xff1a; O(1) 常数阶。复杂度为O(1)与问题规模无关 线性阶 O&#xff08;n&#xff09;比如一个for循环中代码执行n遍 n阶 对数阶 int n9; int i1; while(i<n) { i*2; } 2^x>n时候退出。次数xlog2^n 时间复杂度为O(logN) 根号阶 int…

OpenGL笔记十之Shader类的封装

OpenGL笔记十之Shader类的封装 —— 2024-07-10 晚上 bilibili赵新政老师的教程看后笔记 code review! 文章目录 OpenGL笔记十之Shader类的封装1.运行2.目录结构3.main.cpp4.application4.1.CMakeLists.txt4.2.Application.h4.3.Application.cpp 5.assets5.1.shaders&#xf…

Hive及其架构简介

什么是 Hive &#xff1f; 一个基于 Hadoop 的数据仓库&#xff0c;适用于一些高延迟性的应用&#xff08;离线开发&#xff09;&#xff0c;可以将存储在 Hadoop 文件中的结构化、半结构化数据文件映射为一张数据库表&#xff0c;并基于表提供类似 SQL 的查询模型&#xff0c…

前一段时间比较火的刷网课平台源码,带数据库和教程

前一段时间比较火的刷网课平台源码&#xff0c;带数据库和教程。 好在疫情已经结束了&#xff0c;希望今后世上再无网课。 这个代码免费提供给大家学习开发用吧&#xff0c;作为一个php的入门学习案例用用还可以。 使用办法 网站根目录解压 打开nginx.htaccess文件&#x…

3.4、matlab实现SGM/BM/SAD立体匹配算法计算视差图

1、matlab实现SGM/BM/SAD立体匹配算法计算视差图简介 SGM&#xff08;Semi-Global Matching&#xff09;、BM&#xff08;Block Matching&#xff09;和SAD&#xff08;Sum of Absolute Differences&#xff09;都是用于计算立体匹配&#xff08;Stereo Matching&#xff09;的…

Contact Form联系表单自动发送邮件(超级简单)

前几天发现了aoksend推出的这个联系表单的组件&#xff0c;非常好用&#xff0c;只有一个php文件&#xff0c;把php文件放到网站主目录里面。然后去aoksend注册和配置好域名和发信邮箱&#xff0c;可以得到发送密钥&#xff1a;app_key&#xff0c;然后配置好邮件模板&#xff…

数据库内核研发学习之路(二)postgres编译安装

我们在前面安装配置好环境之后&#xff0c;接下来就是去安装编译postgres&#xff0c;不是以前我们常用的一键化安装&#xff0c;而是根据源码进行编译安装。 1、获取postgres的15.2版本的源码 我这里获取的是15.2版本的源码&#xff0c;当然大家也可以获取其他版本的源码&am…

百度安全大模型智能体实践入选信通院“安全守卫者计划”优秀案例

7月3日&#xff0c;由全球数字经济大会组委会主办&#xff0c;中国信息通信研究院&#xff08;以下简称中国信通院&#xff09;与中国通信标准化协会联合承办的2024全球数字经济大会“云和软件安全论坛暨第二届SecGo云和软件安全大会”在北京召开。本届论坛聚焦云和软件安全最新…

RISC-V在线反汇编工具

RISC-V在线反汇编工具&#xff1a; https://luplab.gitlab.io/rvcodecjs/#q34179073&abifalse&isaAUTO 不过&#xff0c;似乎&#xff0c;只支持RV32I、RV64I、RV128I指令集&#xff1a;

ControlNet作者新作Paints-Undo:一键模拟人类绘画过程,再也没人敢说你的图是生成的了!

ControlNet作者敏神又有新项目了。 Paints-Undo 可以生成模拟人类绘画过程的动画。支持输入单图倒推出绘制这个图片某一步的过程&#xff0c;也可以给两张图&#xff0c;生成一个绘制过程动画。 再有人说你的图是生成的就把这个拿给他看&#xff0c;哈哈。下面先看一下展示的…

【JUC】使用CompletableFuture执行异步任务

文章目录 Future接口介绍Future接口常用实现类FutureTaskFuture接口能干什么Future接口相关架构FutureTask初步使用Future编码实战和优缺点分析优点缺点获取结果的方式不优雅结论 完成一些复杂的任务 CompletableFuture对Future的改进CompletableFuture为什么会出现Completable…

解决nginx代理静态资源刷新后404问题

背景 在公司的项目中&#xff0c;有一个管理系统&#xff0c;大致的逻辑是通过nginx代理的静态资源&#xff0c; 正常页面跳转是没有问题的&#xff0c;有的时候我们会使用回车或者F5进行 页面刷新的时候都会出现404问题。 解决 这种我怀疑是nginx的配置不到位的问题。 我在本…

数据库管理-第218期 服务器内存(20240711)

数据库管理218期 2024-07-11 数据库管理-第218期 服务器内存&#xff08;20240711&#xff09;1 内存2 ECC内存3 原理3.1 多副本传输3.2 纠错码3.3 汉明码 总结 数据库管理-第218期 服务器内存&#xff08;20240711&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文…

数据库第六次

视图 salary decimal(10,2) not null default 0 comment ‘工资’, address varchar(200) not null default ‘’ comment ‘通讯地址’, dept_id int comment ‘部门编号’ ); create index idx_name on emp(emp_name); create index idx_birth on emp(birth); create index…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【密钥删除(ArkTS)】

密钥删除(ArkTS) 为保证数据安全性&#xff0c;当不需要使用该密钥时&#xff0c;应该删除密钥。 开发步骤 以删除HKDF256密钥为例。 确定密钥别名keyAlias&#xff0c;密钥别名最大长度为64字节。初始化密钥属性集。用于删除时指定密钥的属性TAG&#xff0c;比如删除的密钥…

【java】力扣 合并k个升序链表

文章目录 题目链接题目描述思路代码 题目链接 23.合并k个升序链表 题目描述 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表 思路 我在这个题里面用到了PriorityQueue(优先队列) 的知识 Prio…

顶顶通呼叫中心中间件实现随时启动和停止质检(mod_cti基于FreeSWITCH)

文章目录 前言联系我们拨号方案启动停止ASR执行FreeSWITCH 命令接口启动ASR接口停止ASR接口 通知配置cti.json配置质检结果写入数据库 前言 顶顶通呼叫中心中间件的实时质检功能是由两个模块组成&#xff1a;mod_asr 和 mod_qc。 mod_asr&#xff1a;负责调用ASR将用户们在通…