【FPGA零基础学习之旅#17】搭建串口收发与储存双口RAM系统
🎉欢迎来到FPGA专栏~搭建串口收发与储存双口RAM系统
- ☆* o(≧▽≦)o *☆嗨~我是小夏与酒🍹
- ✨博客主页:小夏与酒的博客
- 🎈该系列文章专栏:FPGA学习之旅
- 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
- 📜 欢迎大家关注! ❤️
🎉 目录-串口收发与储存双口RAM系统
- 一、效果演示
- 二、基础知识
- 2.1 实现目标
- 2.2 所需基础模块
- 三、系统分析
- 四、代码编写
- 4.1 控制模块
- 4.2 顶层模块
- 五、仿真测试激励文件
- 5.1 key_model
- 5.2 testbench编写
- 5.3 仿真结果
- 六、板级验证
一、效果演示
🥝输入数据:
🥝输出数据:
🥝串口助手分析:
按下第一次按键,FPGA开始连续发送数据,按下第二次按键,FPGA停止发送数据。
二、基础知识
2.1 实现目标
使用按键消抖、串口发送与接收模块,以及双端口RAM模块实现串口发送数据到FPGA中,FPGA接收到数据后将数据存储在双口RAM中,当按下按键时FPGA将RAM中存储的数据再通过串口发送出去,再次按下按键后,FPGA停止发送数据。
2.2 所需基础模块
相关模块学习文章:
🥝【FPGA零基础学习之旅#10】按键消抖模块设计与验证(一段式状态机实现);
🥝【FPGA零基础学习之旅#13】串口发送模块设计与验证;
🥝【FPGA零基础学习之旅#14】串口发送字符串;
🥝【FPGA零基础学习之旅#15】串口接收模块设计与验证(工业环境);
🥝【FPGA零基础学习之旅#16】嵌入式块RAM-双口ram的使用。
三、系统分析
参考小梅哥FPGA设计的系统框图:
通过串口发送数据到FPGA中,FPGA接收到数据后将数据存储在双口RAM的一段连续空间中。当需要时,按下按键S0,则FPGA将RAM中存储的数据通过串口发送出去;再次按下S0,则停止数据发送。
进行功能划分:串口接收模块、按键消抖模块、RAM模块、串口发送模块以及控制模块。
在此给出所需使用的模块代码:
按键消抖模块:
//
//模块:按键消抖模块
//key_state:输出消抖之后按键的状态
//key_flag:按键消抖结束时产生一个时钟周期的高电平脉冲
//
module KeyFilter(input Clk,input Rst_n,input key_in,output reg key_flag,output reg key_state
);//按键的四个状态localparamIDLE = 4'b0001,FILTER1 = 4'b0010,DOWN = 4'b0100,FILTER2 = 4'b1000;//状态寄存器reg [3:0] curr_st;//边沿检测输出上升沿或下降沿wire pedge;wire nedge;//计数寄存器reg [19:0]cnt;//使能计数寄存器reg en_cnt;//计数满标志信号reg cnt_full;//计数满寄存器//------<边沿检测电路的实现>------//边沿检测电路寄存器reg key_tmp0;reg key_tmp1;//边沿检测always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)beginkey_tmp0 <= 1'b0;key_tmp1 <= 1'b0;endelse beginkey_tmp0 <= key_in;key_tmp1 <= key_tmp0;end endassign nedge = (!key_tmp0) & (key_tmp1);assign pedge = (key_tmp0) & (!key_tmp1);//------<状态机主程序>------ //状态机主程序always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endelse begincase(curr_st)IDLE:beginkey_flag <= 1'b0;if(nedge)begincurr_st <= FILTER1;en_cnt <= 1'b1;endelsecurr_st <= IDLE;endFILTER1:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b0;curr_st <= DOWN;en_cnt <= 1'b0;end else if(pedge)begincurr_st <= IDLE;en_cnt <= 1'b0;endelsecurr_st <= FILTER1;endDOWN:beginkey_flag <= 1'b0;if(pedge)begincurr_st <= FILTER2;en_cnt <= 1'b1;endelsecurr_st <= DOWN;endFILTER2:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b1;curr_st <= IDLE;en_cnt <= 1'b0;end else if(nedge)begincurr_st <= DOWN;en_cnt <= 1'b0;endelsecurr_st <= FILTER2;enddefault:begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endendcaseendend//------<20ms计数器>------ //20ms计数器//Clk 50_000_000Hz//一个时钟周期为20ns//需要计数20_000_000 / 20 = 1_000_000次always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt <= 20'd0;else if(en_cnt)cnt <= cnt + 1'b1;elsecnt <= 20'd0;endalways@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt_full <= 1'b0;else if(cnt == 999_999)cnt_full <= 1'b1;elsecnt_full <= 1'b0;endendmodule
串口接收模块:
//
//模块名称:串口接收模块(工业环境)
//
module uart_byte_rx(input Clk,//50Minput Rst_n,input [2:0] baud_set,input data_rx,output reg [7:0] data_byte,output reg Rx_Done
);reg s0_Rx,s1_Rx;//同步寄存器reg tmp0_Rx,tmp1_Rx;//数据寄存器reg [15:0]bps_DR;//分频计数器计数最大值reg [15:0]div_cnt;//分频计数器reg bps_clk;//波特率时钟reg [7:0]bps_cnt;reg uart_state;reg [2:0] r_data_byte [7:0];reg [2:0]START_BIT;reg [2:0]STOP_BIT;wire nedge;//--------<同步寄存器处理>--------
//用于消除亚稳态always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)begins0_Rx <= 1'b0;s1_Rx <= 1'b0;endelse begins0_Rx <= data_rx;s1_Rx <= s0_Rx;endend//--------<数据寄存器处理>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)begintmp0_Rx <= 1'b0;tmp1_Rx <= 1'b0;endelse begintmp0_Rx <= s1_Rx;tmp1_Rx <= tmp0_Rx;endend//--------<下降沿检测>-------- assign nedge = !tmp0_Rx & tmp1_Rx;//--------<div_cnt模块>--------
//得到不同计数周期的计数器always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)div_cnt <= 16'd0;else if(uart_state)beginif(div_cnt == bps_DR)div_cnt <= 16'd0;elsediv_cnt <= div_cnt + 1'b1;endelsediv_cnt <= 16'd0;end
//--------<bps_clk信号的产生>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)bps_clk <= 1'b0;else if(div_cnt == 16'd1)bps_clk <= 1'b1;elsebps_clk <= 1'b0;end//--------<bps_clk计数模块>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)bps_cnt <= 8'd0;else if(bps_cnt == 8'd159 || (bps_cnt == 8'd12 && (START_BIT > 2)))bps_cnt <= 8'd0;else if(bps_clk)bps_cnt <= bps_cnt + 1'b1;elsebps_cnt <= bps_cnt;end//--------<Rx_Done模块>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)Rx_Done <= 1'b0;else if(bps_cnt == 8'd159)Rx_Done <= 1'b1;elseRx_Done <= 1'b0;end //--------<波特率查找表>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)bps_DR <= 16'd324;else begincase(baud_set)0:bps_DR <= 16'd324;1:bps_DR <= 16'd162;2:bps_DR <= 16'd80;3:bps_DR <= 16'd53;4:bps_DR <= 16'd26;default:bps_DR <= 16'd324;endcaseend end//--------<采样数据接收模块>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)beginSTART_BIT <= 3'd0;r_data_byte[0] <= 3'd0; r_data_byte[1] <= 3'd0;r_data_byte[2] <= 3'd0; r_data_byte[3] <= 3'd0;r_data_byte[4] <= 3'd0; r_data_byte[5] <= 3'd0;r_data_byte[6] <= 3'd0; r_data_byte[7] <= 3'd0;STOP_BIT <= 3'd0;endelse if(bps_clk)begincase(bps_cnt)0:beginSTART_BIT <= 3'd0;r_data_byte[0] <= 3'd0;r_data_byte[1] <= 3'd0;r_data_byte[2] <= 3'd0;r_data_byte[3] <= 3'd0;r_data_byte[4] <= 3'd0;r_data_byte[5] <= 3'd0;r_data_byte[6] <= 3'd0;r_data_byte[7] <= 3'd0;STOP_BIT <= 3'd0; end6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rx;22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rx;38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rx;54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rx;70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rx;86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rx;102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rx;118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rx;134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rx;150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rx;default:beginSTART_BIT <= START_BIT;r_data_byte[0] <= r_data_byte[0];r_data_byte[1] <= r_data_byte[1];r_data_byte[2] <= r_data_byte[2];r_data_byte[3] <= r_data_byte[3];r_data_byte[4] <= r_data_byte[4];r_data_byte[5] <= r_data_byte[5];r_data_byte[6] <= r_data_byte[6];r_data_byte[7] <= r_data_byte[7];STOP_BIT <= STOP_BIT;endendcaseendend//--------<数据状态判定模块>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)data_byte <= 8'd0;else if(bps_cnt == 8'd159)begindata_byte[0] <= r_data_byte[0][2];data_byte[1] <= r_data_byte[1][2];data_byte[2] <= r_data_byte[2][2];data_byte[3] <= r_data_byte[3][2];data_byte[4] <= r_data_byte[4][2];data_byte[5] <= r_data_byte[5][2];data_byte[6] <= r_data_byte[6][2];data_byte[7] <= r_data_byte[7][2];endelse;end//--------<uart_state模块>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)uart_state <= 1'b0;else if(nedge)uart_state <= 1'b1;else if(Rx_Done || (bps_cnt == 8'd12 && (START_BIT > 2)))uart_state <= 1'b0;elseuart_state <= uart_state;endendmodule
串口发送模块:
//
//模块名称:串口发送模块
//
module uart_byte_tx(input Clk,input Rst_n,input [7:0] data_byte,input send_en,input [2:0] baud_set,output reg uart_tx,output reg Tx_Done,output reg uart_state
);reg bps_clk;//波特率时钟reg [15:0]div_cnt;//分频计数器reg [15:0]bps_DR;//分频计数最大值reg [3:0]bps_cnt;//波特率计数时钟//定义数据的起始位和停止位localparam START_BIT = 1'b0;localparam STOP_BIT = 1'b1;reg [7:0]r_data_byte;//数据寄存器//--------<uart状态模块>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)uart_state <= 1'b0;else if(send_en)uart_state <= 1'b1;else if(bps_cnt == 4'd11)//bps_cnt计数达到11次,即发送结束uart_state <= 1'b0;elseuart_state <= uart_state;end//--------<使能分频计数模块>-------
// assign en_cnt = uart_state;//--------<寄存待发送的数据,使数据保持稳定>--------always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)r_data_byte <= 8'd0;else if(send_en)r_data_byte <= data_byte;elser_data_byte <= r_data_byte;end//--------<波特率查找表>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)bps_DR <= 16'd5207;else begincase(baud_set)0:bps_DR <= 16'd5207;1:bps_DR <= 16'd2603;2:bps_DR <= 16'd1301;3:bps_DR <= 16'd867;4:bps_DR <= 16'd433;default:bps_DR <= 16'd5207;endcaseend end//--------<Div_Cnt模块>--------
//得到不同计数周期的计数器always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)div_cnt <= 16'd0;else if(uart_state)begin // assign en_cnt = uart_state;if(div_cnt == bps_DR)div_cnt <= 16'd0;elsediv_cnt <= div_cnt + 1'b1;endelsediv_cnt <= 16'd0;end
//--------<bps_clk信号的产生>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)bps_clk <= 1'b0;else if(div_cnt == 16'd1)bps_clk <= 1'b1;elsebps_clk <= 1'b0;end//--------<bps_cnt计数模块>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)bps_cnt <= 4'd0;else if(bps_cnt == 4'd11)//clr信号bps_cnt <= 4'd0;else if(bps_clk)bps_cnt <= bps_cnt + 1'b1;elsebps_cnt <= bps_cnt;end//--------<Tx_Done模块>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)Tx_Done <= 1'b0;else if(bps_cnt == 4'd11)Tx_Done <= 1'b1;elseTx_Done <= 1'b0;end//--------<数据位输出模块-10选1多路器>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)uart_tx <= 1'b1;else begincase(bps_cnt)0:uart_tx <= 1'b1;1:uart_tx <= START_BIT;2:uart_tx <= r_data_byte[0];3:uart_tx <= r_data_byte[1];4:uart_tx <= r_data_byte[2];5:uart_tx <= r_data_byte[3];6:uart_tx <= r_data_byte[4];7:uart_tx <= r_data_byte[5];8:uart_tx <= r_data_byte[6];9:uart_tx <= r_data_byte[7];10:uart_tx <= STOP_BIT;default:uart_tx <= 1'b1;endcaseendendendmodule
四、代码编写
上述已给出按键消抖模块和串口收发模块,在本文中主要编写控制模块和顶层模块。
4.1 控制模块
为了实现FPGA将接收到的数据存储到双口RAM的一段连续空间中,就需要设计一个可以实现写地址数据自加的控制逻辑,且其控制信号为串口接收模块输出的Rx_Done信号。每来一个Rx_Done就表明接收成功一字节数,地址数进行加一:
assign wren = Rx_Done;always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)wraddress <= 8'd0;else if(Rx_Done)wraddress <= wraddress + 1'b1;elsewraddress <= wraddress;
end
当按下按键S0,FPGA将RAM中存储的数据通过串口发送出去。需要实现按键按下即启动连续读操作,再次按下可暂停读操作:
reg do_send;always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)do_send <= 1'd0;else if(Key_flag && !Key_state)do_send <= ~do_send;
endalways@(posedge Clk or negedge Rst_n)beginif(!Rst_n) rdaddress <= 8'd0;else if(do_send && Tx_Done)rdaddress <= rdaddress + 8'd1;elserdaddress <= rdaddress;
end
在仿真双端口RAM时发现其输出会延迟两个系统时钟周期。这是为了保证数据变化稳定之后才进行数据输出,所以在此将驱动Send_en的信号接两级寄存器进行延迟两拍。当按键按下后启动一次发送,然后判断上一字节是否发送结束,是则进行下一字节发送否则不进行下一次发送:
reg r0_send_done,r1_send_done;always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)beginr0_send_done <= 1'b0;r1_send_done <= 1'b0;endelse beginr0_send_done <= (do_send && Tx_Done);r1_send_done <= r0_send_done; end
endalways@(posedge Clk or negedge Rst_n)beginif(!Rst_n)Send_en <= 1'b0;else if(Key_flag && !Key_state)Send_en <= 1'b1;else if(r1_send_done)Send_en <= 1'b1;elseSend_en <= 1'b0;
end
为了保证RAM地址操作的有效性,在写地址和读地址代码部分加上范围限制:
//--------<dpram写地址加1>--------
always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)wraddress <= 8'd0;else if(Rx_Done)wraddress <= wraddress + 1'b1;else if(wraddress > 8'd255) //当写地址大于配置ip核时的值时,返回到0地址;wraddress <= 8'd0;elsewraddress <= wraddress;
end//--------<dpram读地址加1>--------
always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)rdaddress <= 8'd0;else if(do_send && Tx_Done)rdaddress <= rdaddress + 8'd1;else if(rdaddress > 8'd255) //当读地址大于255时,返回到0地址;rdaddress <= 8'd0;elserdaddress <= rdaddress;
end
完整的控制模块代码:system_ctrl.v
module system_ctrl(input Clk,input Rst_n,input Key_flag,input Key_state,input Rx_Done,input Tx_Done,output wren,output reg Send_en,output reg [7:0] rdaddress,output reg [7:0] wraddress
);assign wren = Rx_Done;reg do_send;reg r0_send_done;reg r1_send_done;//--------<dpram写地址加1>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)wraddress <= 8'd0;else if(Rx_Done)wraddress <= wraddress + 1'b1;else if(wraddress > 8'd255) //当写地址大于配置ip核时的值时,返回到0地址;wraddress <= 8'd0;elsewraddress <= wraddress;end//--------<翻转标志信号>--------
//按下一次按键开始连续发送数据,再按一次按键停止发送;always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)do_send <= 1'b0;else if(Key_flag && !Key_state)do_send <= ~do_send;end//--------<dpram读地址加1>-------- always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)rdaddress <= 8'd0;else if(do_send && Tx_Done)rdaddress <= rdaddress + 8'd1;else if(rdaddress > 8'd255) //当读地址大于255时,返回到0地址;rdaddress <= 8'd0;elserdaddress <= rdaddress;end//--------<RAM的两拍延迟>--------
//双端口RAM的输出延迟两个系统时钟周期;always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)beginr0_send_done <= 1'b0;r1_send_done <= 1'b1;endelse beginr0_send_done <= (do_send && Tx_Done);r1_send_done <= r0_send_done;endend//--------<按键控制与连续读操作>--------
//Send_en由按键信号和r1_send_done信号同时控制;
//r1_send_done信号使得串口连续读取dpram的数据;always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)Send_en <= 1'b0;else if(Key_flag && !Key_state)Send_en <= 1'b1;else if(r1_send_done)Send_en <= 1'b1;elseSend_en <= 1'b0;endendmodule
控制模块的RTL视图:
4.2 顶层模块
将串口接收模块、按键消抖模块、RAM模块、串口发送模块以及控制模块例化到顶层模块中。
uart_system_top.v:
module uart_system_top(input Clk,input Rst_n,input key_in,input uart_rx,output uart_tx
);wire [7:0]rx_data;wire [7:0]tx_data;wire Key_flag;wire Key_state;wire Rx_Done;wire Tx_Done;wire wren;wire Send_en;wire [7:0]rdaddress;wire [7:0]wraddress;uart_byte_rx uart_byte_rx(.Clk(Clk),.Rst_n(Rst_n),.baud_set(3'd0),.data_rx(uart_rx),.data_byte(rx_data),.Rx_Done(Rx_Done));dpram dpram(.clock(Clk),.data(rx_data),.rdaddress(rdaddress),.wraddress(wraddress),.wren(wren),.q(tx_data));KeyFilter KeyFilter(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(Key_flag),.key_state(Key_state));uart_byte_tx uart_byte_tx(.Clk(Clk),.Rst_n(Rst_n),.data_byte(tx_data),.send_en(Send_en),.baud_set(3'd0),.uart_tx(uart_tx),.Tx_Done(Tx_Done),.uart_state());system_ctrl system_ctrl(.Clk(Clk),.Rst_n(Rst_n),.Key_flag(Key_flag),.Key_state(Key_state),.Rx_Done(Rx_Done),.Tx_Done(Tx_Done),.wren(wren),.Send_en(Send_en),.rdaddress(rdaddress),.wraddress(wraddress));endmodule
顶层模块的RTL视图:
五、仿真测试激励文件
5.1 key_model
key_model仿真模型用于有按键控制信号的项目进行仿真测试,模拟实际情况中的按键抖动。 在仿真时将该模型也添加到工程中使用。
key_model.v:
`timescale 1ns/1nsmodule key_model(press,key);input press;output reg key;reg [15:0]myrand;initial beginkey = 1'b1; endalways@(posedge press)press_key;task press_key;beginrepeat(50)beginmyrand = {$random}%65536;//0~65535;#myrand key = ~key; endkey = 0;#25000000;repeat(50)beginmyrand = {$random}%65536;//0~65535;#myrand key = ~key; endkey = 1;#25000000; end endtaskendmodule
5.2 testbench编写
完整的仿真测试激励文件:
uart_system_top_tb.v:
`timescale 1ns/1ns
`define clock_period 20module uart_system_top_tb;reg Clk;reg Rst_n;wire Key_in;wire uart_rx;wire uart_tx;reg [7:0]data_byte_t;reg send_en;wire [2:0]baud_set;wire Tx_Done;reg press;assign baud_set = 3'd0;uart_system_top uart_system_top(.Clk(Clk),.Rst_n(Rst_n),.key_in(Key_in),.uart_rx(uart_tx),.uart_tx(uart_rx));uart_byte_tx uart_byte_tx(.Clk(Clk),.Rst_n(Rst_n),.data_byte(data_byte_t),.send_en(send_en),.baud_set(baud_set),.uart_tx(uart_tx),.Tx_Done(Tx_Done),.uart_state());key_model key_model(.press(press),.key(Key_in));initial Clk = 1;always#(`clock_period / 2) Clk = ~Clk;initial beginRst_n = 1'b0;press = 0;data_byte_t = 8'd0;send_en = 1'd0;#(`clock_period*20 + 1 );Rst_n = 1'b1;#(`clock_period*50);data_byte_t = 8'haa;send_en = 1'd1;#`clock_period;send_en = 1'd0; @(posedge Tx_Done)#(`clock_period*5000);data_byte_t = 8'h55;send_en = 1'd1;#`clock_period;send_en = 1'd0;@(posedge Tx_Done)#(`clock_period*5000);data_byte_t = 8'h33;send_en = 1'd1;#`clock_period;send_en = 1'd0; @(posedge Tx_Done)#(`clock_period*5000);data_byte_t = 8'haf;send_en = 1'd1;#`clock_period;send_en = 1'd0;@(posedge Tx_Done)#(`clock_period*5000);press = 1;#(`clock_period*3)press = 0;#(`clock_period*2000000)$stop;endendmodule
5.3 仿真结果
六、板级验证
🥝输入数据储存到双口RAM中:
🥝输出RAM中的数据:
🧸结尾
- ❤️ 感谢您的支持和鼓励! 😊🙏
- 📜您可能感兴趣的内容:
- 【FPGA零基础学习之旅#14】串口发送字符串
- 【Python】串口通信-与FPGA、蓝牙模块实现串口通信(Python+FPGA)
- 【Arduino TinyGo】【最新】使用Go语言编写Arduino-环境搭建和点亮LED灯
- 【全网首发开源教程】【Labview机器人仿真与控制】Labview与Solidworks多路支配关系-四足爬行机器人仿真与控制
相关文章:

【FPGA零基础学习之旅#17】搭建串口收发与储存双口RAM系统
🎉欢迎来到FPGA专栏~搭建串口收发与储存双口RAM系统 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒🍹 ✨博客主页:小夏与酒的博客 🎈该系列文章专栏:FPGA学习之旅 文章作者技术和水平有限,如果文中出现错误࿰…...
建立Line类
目录 程序设计 程序分析 系列文章 计算机上的线实际上是线段,要求包含两个端点;颜色为彩虹色;线的粗细是类变量,至少包含show方法。 程序设计 Work5类: package work;import java.util.Scanner;public class Work5 { public static void main(String[] args) {// …...

10_集成学习方法:随机森林、Boosting
文章目录 1 集成学习(Ensemble Learning)1.1 集成学习1.2 Why need Ensemble Learning?1.3 Bagging方法 2 随机森林(Random Forest)2.1 随机森林的优点2.2 随机森林算法案例2.3 随机森林的思考(--->提升学习) 3 随机森林(RF&a…...

工业通信网关常用的工业通信协议
在工业领域中常常有不同的设备协同工作,而这些设备的通信协议和数据格式也有所差异,要想实现不同通信设备之间的数据传输互通,工业网关是一个重要的设备。 什么是工业网关 工业网关是一种能够连接多种不同设备并实现数据的收集、传输、处理和…...

如何将音频与视频分离
您一定经历过这样的情况:当你非常喜欢视频中的背景音乐时,希望将音频从视频中分离出来,以便你可以在音乐播放器中收听音乐。有没有一种有效的方法可以帮助您快速从视频中提取音频呢?当然是有的啦,在下面的文章中&#…...
【antd】form表单为空校验失效 form.item.rules传入非所需的api属性时,引起为空自动验证失效问题
现象 form表单的rules设置后,在form表单项为空时,不提醒required(正常现象),当开始输入后,马上触发了required为空校验,但此时表担心Input明明是有值的。 问题背景: form.item.ru…...
数据可视化的常见工具
Tableau: Tableau是一种流行的商业数据可视化工具,可以连接各种数据源,创建交互式仪表板和报告。它提供了强大的图表和图形功能。 Power BI: Power BI是微软的数据分析和可视化工具,与Microsoft生态系统紧密集成。它支持从多个数据源创建可视…...

不希望你的数据在云中?关闭iPhone或Mac上的iCloud
如果你不想使用iCloud,可以很容易地从设备设置中选择退出并关闭它。当你禁用iCloud时,它会删除该设备对iCloud的访问,但不会删除苹果服务器上的任何数据。我们将在本文末尾向你展示如何做到这一点。 注销iCloud并完全禁用它 如果你根本不…...

10 个最佳免费 PDF 压缩工具软件
PDF 是一种全球流行的文件格式,可在不损失质量或文本对齐的情况下传输文档。问题是许多文件共享应用程序和网站限制您可以共享或上传的 PDF 的大小。 10 个最佳免费 PDF 压缩工具软件 在这种情况下,您将需要一个可以为您减小 PDF 文件大小的应用程序。P…...

LVS+keepalived高可用集群
1、定义 keepalived为lvs应运而生的高可用服务。lvs的调度器无法做高可用,keepalived实现的是调度器的高可用,但keepalived不只为lvs集群服务的,也可以做其他代理服务器的高可用,比如nginxkeepalived也可实现高可用(重…...

虚拟化 vs. 裸金属:K8s 部署环境架构与特性对比
伴随着 IT 云化转型的逐步推进,越来越多的用户加入应用容器化改造的行列,并使用 Kubernetes(K8s)进行容器部署管理。然而,令不少用户感到困惑的是,由于大部分应用此前都部署在虚拟化或超融合环境࿰…...

C语言程序设计——题目:一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
题目:一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少? 程序分析: 假设该数为 x。 1、则:x 100 n2, x 100 168 m2 2、计算等式:m2 - n2…...

Python中使用cv2.resize()函数批量自定义缩放图像尺寸
目录 常用插值缩放方法缩放示例代码总结 常用插值缩放方法 cv2.resize()函数中的interpolation参数指定了图像缩放时使用的插值方法。以下是常用的插值方法: cv2.INTER_NEAREST:最近邻插值。该方法通过选择最接近目标像素的原始像素来进行插值。它是最…...

驱动开发5 阻塞IO实例、IO多路复用
1 阻塞IO 进程1 #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> #include <string.h>int main(int argc, char co…...

ElasticSearch:实现高效数据搜索与分析的利器!项目中如何应用落地,让我带你实操指南。
1.难点解答 收集到几个问题: elasticsearch是单独建一个项目,作为全文搜索使用,还是直接在项目中直接用? ES 服务器是要单独部署的,你可以把 ES 理解为 Redis。 新增数据时,插入到mysql中,需不…...

2023了,是时候使用pnpm了!
2023了,是时候使用pnpm了! Excerpt 2023了,是时候使用pnpm了! 什么是pnpm pnpm代表performant npm(高性能的npm),同npm和Yarn,都属于Javascript包管理安装工具,它较npm和…...

asp.net文档管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio
一、源码特点 asp.net文档管理系统是一套完善的web设计管理系统,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为vs2010,数据库为sqlserver2008,使用c#语言开发 asp.net文档管理系统 二、功能介绍 (1…...

Parallels Client for Mac:改变您远程控制体验的革命性软件
在当今数字化的世界中,远程控制软件已经成为我们日常生活和工作中不可或缺的一部分。在众多远程控制软件中,Parallels Client for Mac以其独特的功能和出色的性能脱颖而出,让远程控制变得更加简单、高效和灵活。 Parallels Client for Mac是…...
Julia数组详解
文章目录 向量数列矩阵特殊数组数组函数 Julia系列:编程初步 向量 Julia中有两种向量,一种是类型统一的,另一种则可包含不同类型的变量,例如下面两个向量都是允许存在的 aNum [1,2,3] # 类型为 3-element Vector{Int64} aAny…...
用事务代码查看视图的函数
文章目录 1 Introduction2 Code 1 Introduction If we continue to see view with T-code. We can use the function for it . 2 Code REPORT z_websrv_con.CALL FUNCTION VIEW_MAINTENANCE_CALLEXPORTINGaction U "操作类型:U修改…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...

376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...

【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...