FPGA 串口通信
文章目录
-
- FPGA 串口通信
-
- 基础原理
- 异步串行通信UART
- Verilog 实现
-
- 串口接收
-
- 1. 介绍
- 2. 程序实现
-
- 严格按照状态机实现
- 非严格按照状态机实现( 目前使用 )
- 串口发送
-
- 1. 介绍
- 2. 程序实现
-
- 严格按照状态机实现
- 非严格按照状态机实现( 目前使用 )
- 回环测试
-
- 测试框图
- 测试代码
- 米联客参考代码
-
- 串口接收
- 串口发送
- 状态机总结
-
- 三段式状态机
基础原理
-
并行通信
数据的各个位使用多条数据线同时进行传输
-
串行通信
通信线路简单,占用引脚资源少,但是传输速度较慢
-
串行通信分类
-
传输方向
- 单工, 只能沿一个方向传输
- 半双工, 数据传输可以沿两个方向,但是需要分时进行
- 全双工,可以同时双向传输
异步串行通信UART
特点: 异步、串行
在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据
-
协议层:通信协议(数据格式、传输速率)
UART串口通信需要两根信号线来实现,一根用于串口发送,另一个负责串口接收。
校验位: 奇偶校验
串口通信的速率使用波特率来表示,每秒传输的二进制数据的位数 bps
-
物理层:接口类型,电平标准等
异步串行通信的接口标准: RS232, RS422, RS485
-
RS232接口
常见接口有DB9接口
常用就三个引脚:RXD, TXD, GND
Verilog 实现
串口接收
1. 介绍
-
简单介绍
在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据
- 空闲状态时,为高电平
- 起始位为一个单位长度低电平,停止位为一个长度高电平
-
分析
- 8位数据位
- 1位停止位
- 无校验位
-
基本思路
采集每一位中间时刻的数据作为这一位的数据 ( 也可以每一位多采几个时刻的数据,取众数 )
-
框图
-
状态机
2. 程序实现
严格按照状态机实现
程序:
`timescale 1ns / 1ps
//
// Engineer: wkk
// Create Date: 2022/11/22 16:35:19
// Module Name: uart_rx
// Description: uart rx function
//
module uart_rx(input sys_clk,input sys_rst_n,input uart_rx,output uart_rx_valid,output [7:0] uart_rx_data
);parameter SYS_CLK = 100_000_000;
// 115200
parameter BAUD_COUNT = 868;
parameter BAUD_HALF_COUNT = 434;
parameter TIME_COUNT_LEN = 12;localparam IDLE_STATE = 4'd0;
localparam START_STATE = 4'd1;
localparam RECV_STATE = 4'd2;
localparam RECV_D0_STATE = 4'd3;
localparam RECV_D1_STATE = 4'd4;
localparam RECV_D2_STATE = 4'd5;
localparam RECV_D3_STATE = 4'd6;
localparam RECV_D4_STATE = 4'd7;
localparam RECV_D5_STATE = 4'd8;
localparam RECV_D6_STATE = 4'd9;
localparam RECV_D7_STATE = 4'd10;
localparam END_STATE = 4'd11; reg [3:0] curr_state;
reg [3:0] next_state;reg uart_rx_d0;
reg uart_rx_d1;
wire uart_rx_en;// 开始计时
reg time_en;
// 计时模式 0: 计数一个波特率周期 1: 计数半个波特率周期
reg half_en;
reg count_en;
reg [TIME_COUNT_LEN-1:0] time_count;reg [7:0] rx_data;
reg [3:0] rx_data_index;// 计时模块
always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n || !time_en) begin time_count <= 0;count_en <= 0;end else if(half_en)if(time_count == BAUD_HALF_COUNT -1 )begintime_count <=0;count_en <= 1;endelse begintime_count <= time_count + 1'b1;count_en <= 0;end else if(time_count == BAUD_COUNT -1 )begincount_en <= 1;time_count <= 0;endelse begintime_count <= time_count + 1'b1;count_en <= 0;end
end// 产生下一状态
always @(*) begincase( curr_state )IDLE_STATE: beginif( uart_rx_en )next_state = START_STATE;else next_state = IDLE_STATE;endSTART_STATE:if( count_en)next_state = RECV_STATE;elsenext_state = START_STATE;RECV_STATE:if( count_en )next_state = RECV_D0_STATE;elsenext_state = RECV_STATE;RECV_D0_STATE:if( count_en )next_state = RECV_D1_STATE;elsenext_state = RECV_D0_STATE;RECV_D1_STATE:if( count_en )next_state = RECV_D2_STATE;elsenext_state = RECV_D1_STATE;RECV_D2_STATE:if( count_en )next_state = RECV_D3_STATE;elsenext_state = RECV_D2_STATE;RECV_D3_STATE:if( count_en )next_state = RECV_D4_STATE;elsenext_state = RECV_D3_STATE;RECV_D4_STATE:if( count_en )next_state = RECV_D5_STATE;elsenext_state = RECV_D4_STATE;RECV_D5_STATE:if( count_en )next_state = RECV_D6_STATE;elsenext_state = RECV_D5_STATE;RECV_D6_STATE:if( count_en )next_state = RECV_D7_STATE;elsenext_state = RECV_D6_STATE;RECV_D7_STATE:if( count_en )next_state = END_STATE;elsenext_state = RECV_D7_STATE;END_STATE:next_state = IDLE_STATE;default: ;endcase
endassign uart_rx_data = rx_data;
assign uart_rx_valid = (curr_state == END_STATE)?1'b1:1'b0;// 状态输出
always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n) beginrx_data <= 7'b0;time_en <= 1'b0;half_en <= 1'b0;rx_data_index <= 3'b0;end elsecase(curr_state)IDLE_STATE: begintime_en <= 1'b0;half_en <= 1'b0;rx_data_index <= 3'b0;endSTART_STATE: begintime_en <= 1'b1;half_en <= 1'b1;endRECV_STATE:begintime_en <= 1'b1;half_en <= 1'b0;end RECV_D0_STATE:if(rx_data_index == 3'd0)beginrx_data[0] <= uart_rx;rx_data_index <= rx_data_index + 1'b1;end elserx_data[0] <= rx_data[0]; RECV_D1_STATE:if(rx_data_index == 3'd1)beginrx_data[1] <= uart_rx;rx_data_index <= rx_data_index + 1'b1;end elserx_data[1] <= rx_data[1];RECV_D2_STATE:if(rx_data_index == 3'd2)beginrx_data[2] <= uart_rx;rx_data_index <= rx_data_index + 1'b1;end elserx_data[2] <= rx_data[2];RECV_D3_STATE:if(rx_data_index == 3'd3)beginrx_data[3] <= uart_rx;rx_data_index <= rx_data_index + 1'b1;end elserx_data[3] <= rx_data[3];RECV_D4_STATE:if(rx_data_index == 3'd4)beginrx_data[4] <= uart_rx;rx_data_index <= rx_data_index + 1'b1;end elserx_data[4] <= rx_data[4];RECV_D5_STATE:if(rx_data_index == 3'd5)beginrx_data[5] <= uart_rx;rx_data_index <= rx_data_index + 1'b1;end elserx_data[5] <= rx_data[5];RECV_D6_STATE:if(rx_data_index == 3'd6)beginrx_data[6] <= uart_rx;rx_data_index <= rx_data_index + 1'b1;end elserx_data[6] <= rx_data[6];RECV_D7_STATE:if(rx_data_index == 3'd7)beginrx_data[7] <= uart_rx;rx_data_index <= rx_data_index + 1'b1;end elserx_data[7] <= rx_data[7];END_STATE:begintime_en <= 1'b0;half_en <= 1'b0;rx_data_index <= 3'b0;enddefault: ;endcase
end// catch rising edge
assign uart_rx_en = (uart_rx_d0 & !uart_rx_d1) ? 1'b1:1'b0;always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n) beginuart_rx_d0 <= 1'b0;uart_rx_d1 <= 1'b0;end else beginuart_rx_d1 <= uart_rx;uart_rx_d0 <= uart_rx_d1;end
end// update curr_state
always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n) curr_state <= IDLE_STATE;elsecurr_state <= next_state;
endendmodule
testbench:
`timescale 1ns / 1ns
//
// Engineer: wkk
// Module Name: uart_rx_tb
//module uart_rx_tb;
reg sys_clk;
reg sys_rst_n;
reg uart_rx;
wire uart_rx_valid;
wire [7:0] uart_rx_data;parameter BAUD_COUNT = 20;
parameter BAUD_HALF_COUNT = 10;
parameter TIME_COUNT_LEN = 5;
uart_rx #(.BAUD_COUNT (BAUD_COUNT),.BAUD_HALF_COUNT(BAUD_HALF_COUNT),.TIME_COUNT_LEN (TIME_COUNT_LEN)
)u_uart_rx(.sys_clk (sys_clk),.sys_rst_n (sys_rst_n),.uart_rx (uart_rx),.uart_rx_valid (uart_rx_valid),.uart_rx_data (uart_rx_data)
);initial begin sys_clk = 0;sys_rst_n = 0;uart_rx = 1;endalways #5 sys_clk = !sys_clk;initial begin#10 sys_rst_n = 1;#30 uart_rx = 0; // 起始位#200 uart_rx = 0; #200 uart_rx = 1;#200 uart_rx = 1;#200 uart_rx = 1;#200 uart_rx = 0;#200 uart_rx = 1;#200 uart_rx = 1;#200 uart_rx = 0;#200 uart_rx = 1; // 停止位#450$stop;endendmodule
非严格按照状态机实现( 目前使用 )
程序
`timescale 1ns / 1ns
//
// Engineer: wkk
//
// Create Date: 2023/03/15 09:37:21
// Design Name:
// Module Name: uart_rx
//
//
module uart_rx(input i_clk ,input i_rst_n,input i_data,output [7:0] o_data,output o_data_valid
);parameter I_CLK_FREQ = 27_000_000 ;
parameter BAUDRATE = 115200 ;
parameter COUNTER_LEN = 12 ;
localparam COUNT_MAX = I_CLK_FREQ / BAUDRATE ;
reg i_data_d0 ;
reg i_data_d1 ;
wire i_data_negedge_valid ;reg start_rx ; // 开始接收
reg [4:0] bit_index ;reg [COUNTER_LEN-1:0] time_counter ;
wire counter_en ;
wire counter_half_en ;reg [7:0] o_data_reg ;
// 检测下降沿
assign i_data_negedge_valid = i_data_d1 & (~i_data_d0);
always @(posedge i_clk or negedge i_rst_n) beginif( !i_rst_n ) begini_data_d0 <= 1'b1;i_data_d1 <= 1'b1;end else begini_data_d0 <= i_data;i_data_d1 <= i_data_d0;end
end// 开始信号
always @(posedge i_clk or negedge i_rst_n) beginif( !i_rst_n ) start_rx <= 1'b0;else if(start_rx == 1'b0 && i_data_negedge_valid)start_rx <= 1'b1;else if(start_rx == 1'b1 && bit_index== 4'd9)start_rx <= 1'b0;elsestart_rx <= start_rx;
endassign counter_half_en = (time_counter == (COUNT_MAX >> 1 ));
assign counter_en = (time_counter == COUNT_MAX-1);
// 计时器
always @(posedge i_clk or negedge i_rst_n) beginif( !i_rst_n )time_counter <= {COUNTER_LEN{1'b0}};else if(start_rx) if(time_counter == COUNT_MAX-1) time_counter <= {COUNTER_LEN{1'b0}};elsetime_counter <= time_counter+1'b1;elsetime_counter <= {COUNTER_LEN{1'b0}};
end
// bit_index 控制
always @(posedge i_clk or negedge i_rst_n) beginif( !i_rst_n ) bit_index <= 4'b0;else if(start_rx) if(counter_en)bit_index <= bit_index + 4'b1;elsebit_index <= bit_index;elsebit_index <= 4'b0;
end//输出
always @(posedge i_clk or negedge i_rst_n) beginif( !i_rst_n ) o_data_reg <= 7'b0;else if( counter_half_en )case ( bit_index )4'd1:o_data_reg[0] <= i_data_d0;4'd2:o_data_reg[1] <= i_data_d0;4'd3:o_data_reg[2] <= i_data_d0;4'd4:o_data_reg[3] <= i_data_d0;4'd5:o_data_reg[4] <= i_data_d0;4'd6:o_data_reg[5] <= i_data_d0;4'd7:o_data_reg[6] <= i_data_d0;4'd8:o_data_reg[7] <= i_data_d0;default:o_data_reg <= o_data_reg;endcaseelseo_data_reg <= o_data_reg;
endassign o_data_valid = (bit_index== 4'd9);
assign o_data = o_data_reg;endmodule
testbench
`timescale 1ns / 1ns
//
// Company:
// Engineer: wkk
//
// Create Date: 2023/03/15 10:03:32
// Design Name:
// Module Name: usart_rx_tb
// Project Name:
//module usart_rx_tb();
reg i_clk;
reg i_rst_n;
reg i_data;
wire [7:0] o_data;
wire o_data_valid;uart_rx#(
.I_CLK_FREQ(10),
.BAUDRATE (2)
)uart_rx_inst(i_clk ,i_rst_n,i_data,o_data,o_data_valid
);initial begini_clk = 1'b0;i_rst_n = 1'b0;i_data = 1'b1;
end
always #5 i_clk = ~i_clk;initial begin$display("start\r\n--------------------");$monitor($time,"o_data_valid: %b",o_data_valid );#10 i_rst_n = 1'b1;#50 i_data = 1'b0;#50 i_data = 1'b1;#50 i_data = 1'b0;#50 i_data = 1'b1;#50 i_data = 1'b0;#50 i_data = 1'b1;#50 i_data = 1'b1;#50 i_data = 1'b1;#50 i_data = 1'b0;#50 i_data = 1'b1;#50//01110101#100;$stop;
endendmodule
串口发送
1. 介绍
2. 程序实现
严格按照状态机实现
程序
`timescale 1ns / 1ps
//
// Engineer: wkk
// Create Date: 2022/12/02 20:41:01
// Module Name: uart_tx
// Description: uart_tx demo
//
module uart_tx(input sys_clk ,input sys_rst_n ,input uart_w_en ,input wire [7:0] uart_data ,output uart_out
);parameter SYS_CLK = 100_000_000 ;
parameter TIME_MAX_COUNT = 868 ;
parameter TIME_COUNT_LEN = 12 ;localparam IDLE_STATE = 4'd0 ;
localparam START_STATE = 4'd1 ;
localparam D0_STATE = 4'd2 ;
localparam D1_STATE = 4'd3 ;
localparam D2_STATE = 4'd4 ;
localparam D3_STATE = 4'd5 ;
localparam D4_STATE = 4'd6 ;
localparam D5_STATE = 4'd7 ;
localparam D6_STATE = 4'd8 ;
localparam D7_STATE = 4'd9 ;
localparam END_STATE = 4'd10;reg [7:0] uart_tx_data;reg [3:0] next_state;
reg [3:0] curr_state;reg [TIME_COUNT_LEN-1:0] time_counter;
wire time_en;
reg count_en;reg uart_tx_out;// update state
always @(*) beginif(!sys_rst_n) curr_state = IDLE_STATE;elsecurr_state = next_state;
endassign time_en = (time_counter == TIME_MAX_COUNT -1)? 1'b1:1'b0;
// timer
always @(posedge sys_clk or negedge sys_rst_n ) beginif(!sys_rst_n || count_en == 0 ) time_counter <= 'd0;else if(time_counter == TIME_MAX_COUNT -1 )time_counter <= 'd0;elsetime_counter <= time_counter + 1'd1;
end// create next state
always @(posedge sys_clk or negedge sys_rst_n ) beginif(!sys_rst_n) beginnext_state <= IDLE_STATE;endelse case(curr_state)IDLE_STATE :if(uart_w_en)next_state <= START_STATE;elsenext_state <= next_state;START_STATE:if(time_en)next_state <= D0_STATE;elsenext_state <= next_state;D0_STATE :if(time_en)next_state <= D1_STATE;elsenext_state <= next_state;D1_STATE :if(time_en)next_state <= D2_STATE;elsenext_state <= next_state;D2_STATE :if(time_en)next_state <= D3_STATE;elsenext_state <= next_state;D3_STATE :if(time_en)next_state <= D4_STATE;elsenext_state <= next_state;D4_STATE :if(time_en)next_state <= D5_STATE;elsenext_state <= next_state;D5_STATE :if(time_en)next_state <= D6_STATE;elsenext_state <= next_state;D6_STATE :if(time_en)next_state <= D7_STATE;elsenext_state <= next_state;D7_STATE :if(time_en)next_state <= END_STATE;elsenext_state <= next_state;END_STATE :if(time_en)next_state <= IDLE_STATE;elsenext_state <= next_state;default:next_state <= IDLE_STATE;endcase
endassign uart_out = uart_tx_out;
// out
always @(posedge sys_clk or negedge sys_rst_n ) beginif(!sys_rst_n)beginuart_tx_out <= 1'b1;uart_tx_data <= 8'd0;count_en <= 1'b0; endelse case(curr_state)IDLE_STATE : begin uart_tx_out <= 1'b1; count_en <= 1'b0; endSTART_STATE: begin uart_tx_out <= 1'b0; count_en <= 1'b1; uart_tx_data <= uart_data; end D0_STATE : uart_tx_out <= uart_tx_data[0]; D1_STATE : uart_tx_out <= uart_tx_data[1]; D2_STATE : uart_tx_out <= uart_tx_data[2]; D3_STATE : uart_tx_out <= uart_tx_data[3]; D4_STATE : uart_tx_out <= uart_tx_data[4]; D5_STATE : uart_tx_out <= uart_tx_data[5]; D6_STATE : uart_tx_out <= uart_tx_data[6]; D7_STATE : uart_tx_out <= uart_tx_data[7]; END_STATE : begin uart_tx_out <= 1'b1; count_en <= 1'b0; end default: beginuart_tx_out <= 1'b1; count_en <= 1'b0; end
endcase
end
endmodule
testbench
`timescale 1ns / 1ns
//
// Engineer: wkk
// Create Date: 2022/12/02 20:41:01
// Module Name: uart_tx_tb
// Description: uart_tx demo testbench
//
module uart_tx_tb;
reg sys_clk ;
reg sys_rst_n ;
reg uart_w_en ;
reg [7:0] uart_data ;
wire uart_out ;uart_tx #(.TIME_MAX_COUNT (2),.TIME_COUNT_LEN (2)
)u_uart_tx(.sys_clk (sys_clk ), .sys_rst_n (sys_rst_n),.uart_w_en (uart_w_en),.uart_data (uart_data),.uart_out (uart_out )
);
initial beginsys_clk = 1'b0;sys_rst_n = 1'b0;uart_w_en = 1'b0;
endalways #5 sys_clk = ~sys_clk;initial begin#10 sys_rst_n = 1;#10uart_data = 8'b10011101;uart_w_en = 1;#20uart_w_en = 0;#20000$stop;
end
endmodule
- uart_w_en 信号最少要持续2个时钟周期
- uart_w_en 信号如果持续超过一个串口数据帧的时间长度,会重复发送
非严格按照状态机实现( 目前使用 )
程序
`timescale 1ns / 1ns
//
// Engineer: wkk
//
// Create Date: 2023/03/14 22:54:49
// Design Name:
// Module Name: uart_tx
//module uart_tx(input i_clk ,input i_rst_n ,input [7:0] i_data ,input i_data_valid ,output o_data
);
parameter I_CLK_FREQ = 27_000_00 ;
parameter BAUDRATE = 115200 ;
parameter COUNTER_LEN = 12 ; localparam COUNT_MAX = I_CLK_FREQ / BAUDRATE ;reg [7:0] i_data_reg ;
reg i_data_reg_valid ;reg start_tx ;
reg [3:0] bit_num ;
reg o_data_reg ;
reg [COUNTER_LEN-1:0] time_counter ;
wire time_counter_en ;// 缓存数据
always @(posedge i_clk or negedge i_rst_n) beginif(! i_rst_n ) begini_data_reg <= 7'b0;i_data_reg_valid <= 1'b0;end else if( i_data_valid ) begini_data_reg <= i_data;i_data_reg_valid <= 1'b1;endelse begini_data_reg <= i_data_reg;i_data_reg_valid <= 1'b0;end
endalways @(posedge i_clk or negedge i_rst_n)beginif(! i_rst_n )start_tx <= 1'b0;else if(i_data_reg_valid) start_tx <= 1'b1;else if(bit_num == 4'd9)start_tx <= 1'b0;elsestart_tx <= start_tx;
endassign time_counter_en = (time_counter == COUNT_MAX -1) ? 1'b1 :1'b0;
always @(posedge i_clk or negedge i_rst_n) beginif(! i_rst_n )time_counter <= {COUNTER_LEN{1'b0}};else if( start_tx )if(time_counter == COUNT_MAX -1 ) time_counter <= {COUNTER_LEN{1'b0}};elsetime_counter <= time_counter + 1'b1;elsetime_counter <= {COUNTER_LEN{1'b0}};
endalways @(posedge i_clk or negedge i_rst_n) beginif(! i_rst_n )bit_num <= 4'b0;else if( start_tx )if( time_counter_en )bit_num <= bit_num +1'b1;elsebit_num <= bit_num;elsebit_num <= 4'b0;
endalways @(posedge i_clk or negedge i_rst_n) beginif(! i_rst_n )o_data_reg <= 1'b1;else if( start_tx )case( bit_num)4'd0: o_data_reg <= 1'b0;4'd1: o_data_reg <= i_data_reg[0];4'd2: o_data_reg <= i_data_reg[1];4'd3: o_data_reg <= i_data_reg[2];4'd4: o_data_reg <= i_data_reg[3];4'd5: o_data_reg <= i_data_reg[4];4'd6: o_data_reg <= i_data_reg[5];4'd7: o_data_reg <= i_data_reg[6];4'd8: o_data_reg <= i_data_reg[7];4'd9: o_data_reg <= 1'b1;default: o_data_reg <= 1'b1;endcaseelseo_data_reg = 1'b1;
end
assign o_data = o_data_reg;endmodule
testbench
`timescale 1ns / 1ns
//
// Company:
// Engineer: wkk
//
// Create Date: 2023/03/14 23:52:48
// Design Name:
// Module Name: usart_tx_tb
// Project Name:
//module usart_tx_tb();
reg i_clk ;
reg i_rst_n ;
reg [7:0] i_data ;
reg i_data_valid ;
wire o_data ;uart_tx # (.I_CLK (20),.BAUDRATE (10)
)uart_tx_inst(i_clk ,i_rst_n ,i_data ,i_data_valid ,o_data
);initial begini_clk = 1'b0;i_rst_n = 1'b0;i_data_valid = 1'b0;
endalways #10 i_clk = ~i_clk;initial begin#20;i_rst_n = 1'b1;#20;i_data = 8'b10110001;i_data_valid= 1'b1;#20i_data_valid = 1'b0;#500;i_data = 8'b11111111;i_data_valid= 1'b1;#20i_data_valid = 1'b0;//$monitor($time,"\to_data: %b",o_data);#100;$stop;
endendmodule
回环测试
测试框图
测试代码
-
verilog
`timescale 1ns / 1ns // // Company: // Engineer: wkk // // Create Date: 2023/03/15 15:35:05 // Design Name: // Module Name: usart_demo // / module usart_demo(input i_clk ,input i_rst_n ,input i_data ,output o_data );parameter I_CLK_FREQ = 100_000_000 ; parameter BAUDRATE = 115200 ;wire [7:0] data; wire data_valid;uart_rx#(.I_CLK_FREQ(I_CLK_FREQ),.BAUDRATE(BAUDRATE) )uart_rx_inst(.i_clk (i_clk),.i_rst_n (i_rst_n),.i_data (i_data),.o_data (data),.o_data_valid (data_valid) );uart_tx#(.I_CLK_FREQ(I_CLK_FREQ),.BAUDRATE(BAUDRATE) )uart_tx_inst(.i_clk (i_clk),.i_rst_n (i_rst_n),.i_data (data),.i_data_valid (data_valid),.o_data (o_data) );endmodule
-
testbench
`timescale 1ns / 1ps // // Company: // Engineer: wkk // // Create Date: 2023/03/15 15:43:27 // Design Name: // Module Name: usart_demo_tb // //module usart_demo_tb(); reg i_clk ; reg i_rst_n ; reg i_data ; wire o_data ;usart_demo usart_demo_inst(i_clk ,i_rst_n ,i_data ,o_data );initial begini_clk = 1'b0;i_rst_n = 1'b0; endalways #5 i_clk = ~i_clk;initial begin#10 i_rst_n = 1'b1;#20 i_data = 1'b0;#50 i_data = 1'b1;#50 i_data = 1'b1;#50 i_data = 1'b0;#50 i_data = 1'b1;#50 i_data = 1'b0;#50 i_data = 1'b0;#50 i_data = 1'b1;#50 i_data = 1'b1;#50 i_data = 1'b1;#100 $stop; endendmodule
-
实测结果
米联客参考代码
串口接收
`timescale 1ns / 1ps//
/*
Company : Liyang Milian Electronic Technology Co., Ltd.
Brand: 米联客(msxbo)
Technical forum:uisrc.com
taobao: osrc.taobao.com
Create Date: 2019/02/27 22:09:55
Module Name: uart_rx_path
Description:
The serial port receiving module has a baud rate of 9600. It does 8 samplings in
each sampling cycle and has good anti-interference ability.
Copyright: Copyright (c) msxbo
Revision: 1.0
Signal description:
1) _i input
2) _o output
3) _n activ low
4) _dg debug signal
5) _r delay or register
6) _s state mechine
*/module uart_rx(
input clk_i,
input uart_rx_i,
output [7:0] uart_rx_data_o,
output uart_rx_done);parameter [12:0] BAUD_DIV = 14'd5207;//波特率时钟,9600bps,50Mhz/9600 - 1'b1=5207
parameter [12:0] BAUD_DIV_CAP = (BAUD_DIV/8 - 1'b1);//8次采样滤波去毛刺reg [12:0] baud_div = 0; //波特率设置计数器
reg bps_start_en = 0; //波特率启动标志always@(posedge clk_i)beginif(bps_start_en && baud_div < BAUD_DIV) baud_div <= baud_div + 1'b1;else baud_div <= 13'd0;
endreg [12:0] samp_cnt = 0;
always@(posedge clk_i)beginif(bps_start_en && samp_cnt < BAUD_DIV_CAP) samp_cnt <= samp_cnt + 1'b1;else samp_cnt <= 13'd0;
end//数据接收缓存器
reg [4:0] uart_rx_i_r=5'b11111;
always@(posedge clk_i)uart_rx_i_r<={uart_rx_i_r[3:0],uart_rx_i};
//数据接收缓存器,当连续接收到五个低电平时,即uart_rx_int=0时,作为接收到起始信号
wire 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];parameter START = 4'd0;
parameter BIT0 = 4'd1;
parameter BIT1 = 4'd2;
parameter BIT2 = 4'd3;
parameter BIT3 = 4'd4;
parameter BIT4 = 4'd5;
parameter BIT5 = 4'd6;
parameter BIT6 = 4'd7;
parameter BIT7 = 4'd8;
parameter STOP = 4'd9;reg [3:0] RX_S = 4'd0;
wire bps_en = (baud_div == BAUD_DIV);
wire rx_start_fail;
always@(posedge clk_i)beginif(!uart_rx_int&&bps_start_en==1'b0) beginbps_start_en <= 1'b1;RX_S <= START;endelse if(rx_start_fail)beginbps_start_en <= 1'b0;endelse if(bps_en)begincase(RX_S)START:RX_S <= BIT0; //RX bit0BIT0: RX_S <= BIT1; //RX bit1BIT1: RX_S <= BIT2; //RX bit2BIT2: RX_S <= BIT3; //RX bit3BIT3: RX_S <= BIT4; //RX bit4BIT4: RX_S <= BIT5; //RX bit5BIT5: RX_S <= BIT6; //RX bit6BIT6: RX_S <= BIT7; //RX bit7BIT7: RX_S <= STOP; //RX STOPSTOP: bps_start_en <= 1'b0;default: RX_S <= STOP;endcase end
end //滤波采样,在每个波特率周期采样,samp_en一个周期内出现8次,rx_tmp初值,15为中间值,如果采样为1则增加,否则减少
reg [4:0] rx_tmp = 5'd15;
reg [4:0] cap_cnt = 4'd0;
wire samp_en = (samp_cnt == BAUD_DIV_CAP);//采样使能always@(posedge clk_i)beginif(samp_en)begincap_cnt <= cap_cnt + 1'b1;rx_tmp <= uart_rx_i_r[4] ? rx_tmp + 1'b1 : rx_tmp - 1'b1;endelse if(bps_en) begin //每次波特率时钟使能,重新设置rx_tmp初值为15rx_tmp <= 5'd15; cap_cnt <= 4'd0;end
end
//当采样7次取值,大于16为采样1,小于16为采样0
reg cap_r = 1'b0;
wire cap_tmp = (cap_cnt == 3'd7);
reg ap_tmp_r = 1'b0;
reg ap_tmp_r1 = 1'b0;
wire cap_en = (!ap_tmp_r1&&ap_tmp_r);
reg cap_en_r = 1'b0;
always@(posedge clk_i)beginap_tmp_r <= cap_tmp;ap_tmp_r1 <= ap_tmp_r;cap_en_r <= cap_en;
endalways@(posedge clk_i)beginif(cap_en&&bps_start_en)begincap_r <= (rx_tmp > 5'd15) ? 1 : 0; end else if(!bps_start_en)begincap_r <= 1'b1;end
end//以下状态机里面保存好数据
reg [7:0] rx = 8'd0;
reg start_bit = 1'b1;
always@(posedge clk_i)beginif(cap_en_r)begincase(RX_S)BIT0: rx[0] <= cap_r;BIT1: rx[1] <= cap_r;BIT2: rx[2] <= cap_r;BIT3: rx[3] <= cap_r;BIT4: rx[4] <= cap_r;BIT5: rx[5] <= cap_r;BIT6: rx[6] <= cap_r;BIT7: rx[7] <= cap_r;default: rx <= rx; endcase end
end assign rx_start_fail = (RX_S == START)&&cap_en_r&&(cap_r == 1'b1);
assign uart_rx_done = (RX_S == STOP)&& cap_en;
assign uart_rx_data_o = rx;endmodule
串口发送
`timescale 1ns / 1ps
//
/*
Company : Liyang Milian Electronic Technology Co., Ltd.
Brand: 米联客(msxbo)
Technical forum:uisrc.com
taobao: osrc.taobao.com
Create Date: 2019/02/27 22:09:55
Module Name: uart_tx_path
Description:
The baud rate of this serial port is 9600
Copyright: Copyright (c) msxbo
Revision: 1.0
Signal description:
1) _i input
2) _o output
3) _n activ low
4) _dg debug signal
5) _r delay or register
6) _s state mechine
*/module uart_tx(input clk_i,input [7:0] uart_tx_data_i, //待发送数据input uart_tx_en_i, //发送发送使能信号output uart_tx_o,output uart_busy
);parameter [12:0] BAUD_DIV = 14'd5207;//波特率时钟,9600bps,50Mhz/9600 - 1'b1=5207
//波特率发生器,实际就是分配器
reg bps_start_en = 1'b0;
reg [12:0] baud_div = 13'd0;assign uart_busy = bps_start_en;always@(posedge clk_i)beginif(bps_start_en && baud_div < BAUD_DIV) baud_div <= baud_div + 1'b1;else baud_div <= 13'd0;
endreg [9:0] uart_tx_data_r = 10'h3ff;
wire bps_en = (baud_div == BAUD_DIV);
reg [3:0] tx_cnt = 4'd0;
assign uart_tx_o = uart_tx_data_r[0];
always@(posedge clk_i)begin
//首先当发送使能有效,寄存数据if(uart_tx_en_i) beginbps_start_en <= 1'b1;tx_cnt <= 4'd0;uart_tx_data_r <= {1'b1,uart_tx_data_i[7:0],1'b0};endelse if(!bps_start_en)begin//当bps_start_en为0让状态机处于停止状态uart_tx_data_r <= 10'h3ff;tx_cnt <= 4'd0;end
// 通过移位发送数据if(bps_en && tx_cnt < 4'd9)beginuart_tx_data_r <= {uart_tx_data_r[0],uart_tx_data_r[9:1]};tx_cnt <= tx_cnt + 1'b1;endelse if(bps_en)beginbps_start_en <= 1'd0;end
end endmodule
状态机总结
三段式状态机
使用三个always 模块
- 第一个always模块采用同步时序描述状态转移
- 第二个always模块采用组合逻辑判断状态转移条件,描述状态转移规律
- 第三个always模块描述状态输出(可以使用组合电路输出,也可以使用时序电路输出)
对应代码结构
第一段
always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n) curr_state <= IDLE_STATE;elsecurr_state <= next_state;
end
第二段
always @(*) begincase( curr_state )// ....endcase
end
第三段
always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n) begin//...end else begin//...end
end
第二段不使用同步时序逻辑的原因
always 的执行是并行的
倘若使用同步时序逻辑,则:
-
第一段的内容: curr_state <– next_state
将下一状态变为当前状态,状态更新
-
第二段的内容:需要根据curr_state的值结合其他条件,得出下一状态
-
第一段改变curr_state的值,第二段需要使用curr_state的值,并且两者是并行执行的,会形成冲突,可能使得第二段使用的curr_state是未更新前的,导致状态转移的错误。
查看全文
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dgrt.cn/a/2190121.html
如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!
相关文章:
FPGA 串口通信
FPGA 串口通信 文章目录FPGA 串口通信基础原理异步串行通信UARTVerilog 实现串口接收1. 介绍2. 程序实现严格按照状态机实现非严格按照状态机实现( 目前使用 )串口发送1. 介绍2. 程序实现严格按照状态机实现非严格按照状态机实现( 目前使用 &……
Matlab安装勾选产品说明
在安装Matlab2023a的时候,需要勾选所需的产品(组件),一共是112个。
其中很多都不是必须的,可以视个人需求进行选择,这里提供了112个产品的描述和详情链接。
组件名称英文描述中文描述MATLAB科学计算语言S……
(排序4)堆排序与冒泡排序
堆排序
堆排序的话其实就是把数组看成一颗完全二叉树,因为我们事先就知道数组的话可以等价于一颗完全二叉树,然后再把数组看成完全二叉树的基础之上,在给这个完全二叉树进行建堆,比如说我需要升序排列的话那我应该要建一个大根堆……
如何使用图吧工具箱进行CPU和显卡双烤
如何使用图吧工具箱进行双烤1. 概述2. 用图吧工具箱进行CPU和显卡的压力测试2.1 CPU 压力测试2.2 显卡压力测试2.3 在AIDA64中查看CPU和显卡的温度结束语1. 概述
图吧工具箱是一款功能强大的硬件检测工具合集,且开源、免费、绿色; 集成了硬件检测、评分……
arduino多任务的实现:光强检测+程序计时+控灯
多任务可以同时执行多个函数,无须等待传统的delay似的排队操作。受到某位大神的笔记启发和实践后,整理成适用于个人的项目。总体流程:声明任务、添加任务、创建任务、激活任务。 整体代码(删除了一些内容):……
Springboot异常统一处理,并保存异常日志到数据库中
一、为什么要进行统一异常处理
如果发生了异常我们应该让接口可以返回统一的结果。有好的展示给接口调用方。方便我们对异常进行记录,和错误排查。我们可能对某些异常比较关注,比如说我们监控某个IP或者用户一天发送短信的数量,当超出一定数……
【蓝桥杯】DFS正确入门方式 | DFS + 递归与递推习题课(上) | 一节课教你爆搜!——学习笔记
目录
同系列文章——传送门
【蓝桥杯】DFS深度优先练习题——基础入门模板(1)_小卢先冲的博客-CSDN博客第一题:递归实现指数型枚举、第二题:全排列问题、第三题:组合的输出https://blog.csdn.net/weixin_61082895/ar……
RecycleView小结
RecycleView四级缓存
一级缓存:用于存放当前屏幕可显示区域的ViewHolder,目的是为了方便更新数据,以及对View操作时更加快捷二级缓存:用于缓存最近滑动出屏幕的ViewHolder,目的是为了当用户将该View滑出屏幕外时又突然……
大数据项目实战之数据仓库:数仓数据同步策略——第3章 数仓环境准备
文章目录第3章 数仓环境准备3.1 Hive安装部署3.2 Hive元数据配置到MySQL3.2.1 拷贝驱动3.2.2 配置Metastore到MySQL3.3 启动Hive3.3.1 初始化元数据库3.3.2 启动Hive客户端第3章 数仓环境准备
3.1 Hive安装部署
1)把apache-hive-3.1.2-bin.tar.gz上传到linux的/op……
Spring Boot基础学习之(十):修改员工的信息
注意:spring boot专栏是一个新手项目,博文顺序则是功能实现的流程,如果有看不懂的内容可以到前面系列去了解。 本次项目所有能够使用的静态资源可以免费进行下载
静态资源
在本篇代码DAO层将通过Java文件去实现,在这里就不连接数……
vscode开发常用的工具栏选项,查看源码技巧以及【vscode常用的快捷键】
一、开发常用的工具栏选项
1、当前打开的文件快速在左侧资源树中定位: 其实打开了当前的文件已经有在左侧资源树木定位了,只是颜色比较浅 2、打开太多文件的时候,可以关闭 3、设置查看当前类或文件的结构 OUTLINE
相当于idea 查看当前类或接……
数据要素化条件之一:原始性
随着技术的发展,计算机不仅成为人类处理信息的工具,而且逐渐地具有自主处理数据的能力,出现了替代人工的数据智能技术。数据智能的大规模使用需要关于同一分析对象或同一问题的、来源于不同数据源的海量数据。这种数据必须是针对特定对象的记……
【面试题 高逼格利用 类实现加法】编写代码, 实现多线程数组求和.
编写代码, 实现多线程数组求和.关键1. 数组的初始化关键2. 奇偶的相加import java.util.Random;public class Thread_2533 {public static void main(String[] args) throws InterruptedException {// 记录开始时间long start System.currentTimeMillis();// 1. 给定一个很长的……
一个python训练
美国:28:麻省理工学院,斯坦福大学,哈佛大学,加州理工学院,芝加哥大学,普林斯顿大学,宾夕法尼亚大学,耶鲁大学,康奈尔大学,哥伦比亚大学,密歇根大学安娜堡分校,约翰霍普金斯大学,西北大学,加州大学伯克利分校,纽约大学,加州大学洛杉矶分校,杜克大学,卡内基梅隆大学,加州大学圣地……
Mybatis03学习笔记
目录 使用注解开发
设置事务自动提交
mybatis运行原理
注解CRUD
lombok使用(偷懒神器,大神都不建议使用)
复杂查询环境(多对一)
复杂查询环境(一对多)
动态sql环境搭建
动态sql常用标签……
编程日记2023/4/16 14:55:50
设置或取得c# NumericUpDown 编辑框值的方法,(注意:不是Value值)
本人在C#开发中使用到了NumericUpDown控件,但是发现该控件不能直接控制显示值,经研究得到下面的解决办法
NumericUpDown由于是由多个控件组合而来的控件,其中包含一个类似TextBox的控件,若想取得或改变其中的值要使用如下方法
N……
编程日记2023/4/16 14:55:46
使用NPOI 技术 的SetColumnWidth 精确控制列宽不能成功的解决办法(C#)
在使用NPOI技术开发自动操作EXCEL软件时遇到不能精确设置列宽的问题。
如
ISheet sheet1 hssfworkbook.CreateSheet("Sheet1");
sheet1.SetColumnWidth(0, 50 * 256); // 在EXCEL文档中实际列宽为49.29
sheet1.SetColumnWidth(1, 100 * 256); // 在EXCEL文……
编程日记2023/4/16 14:55:46
Mysql 数据库zip版安装时basedir datadir 路径设置问题,避免转义符的影响
本人在开发Mysql数据库自动安装程序时遇到个很奇怪的问题,其中my.ini的basedir 的路径设置是下面这样的:
basedir d:\测试\test\mysql
但是在使用mysqld安装mysql服务时老是启动不了,报1067错误,后来查看window事件发现一个独特……
java stream sorted排序 考虑null值
项目里使用到排序, java里没有像C# 里的linq,只有stream,查找stream.sorted源码看到有个
Comparator.nullsLast
然后看了一下实现,果然是能够处理null值的排序,如:minPriceList.stream().sorted(Comparator.comparing(l -> l.g……
spring @EnableConfigurationProperties 实现原理
查看DataSourceAutoConfiguration源码,发现如下代码: Configuration ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) EnableConfigurationProperties(DataSourceProperties.class) Import({ DataSourcePoolMetadataProvidersCon……
编程日记2023/4/16 14:55:44