Tabla de contenido
1. Módulo receptor de puerto serie
2. Módulo de envío de puerto serie
Comunicación serial FPGA
El módulo de comunicación en serie escrito arriba no tiene universalidad. Aquí hay un módulo en serie universal, pero si necesita comunicación en serie en el futuro, puede usarlo directamente.
1. Módulo receptor de puerto serie
Nombre de la señal | E / S | Dígitos | Función descriptiva |
clk | yo | 1 | Reloj del sistema 50MHz |
rst_n | yo | 1 | Reinicio de sistema |
rs232_tx | yo | 1 | Puerto de datos en serie |
baud_set | yo | 3 | Señal de selección de velocidad en baudios |
data_byte | EL | 8 | Salida de datos en paralelo |
rx_done | EL | 1 | Recibir un indicador de datos completos de 1 byte |
El código es el siguiente: UART_Byte_Rx.v
//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN
//File name: UART_Byte_Rx.v
//Last modified Date: 2020/5/22
//Last Version:
//Descriptions: 工业级别串口数据接收模块,防干扰。对每位数据内部采样16个点,
// 对中间6位数据进行判定数据是1还是0
//-------------------------------------------------------------------
module UART_Byte_Rx(
input clk ,//系统时钟50MHz
input rst_n ,//系统复位
input rs232_tx ,//串口串行数据发送数据口
input [ 2: 0] baud_set ,//波特率选择信号
output reg [ 7: 0] data_byte ,//并行数据输出
output reg rx_done //接收1字节数据完成标志,rx_done可以作为输出有效信号使用
);
reg [ 13: 0] baud_c ;//波特率对应计数次数(4800bps-10416),(9600bps-5208),(19200bps-2604),
//(38400bps-1302),(57600bps-868),(115200bps-434)
reg rs232_tx_ff0 ;
reg rs232_tx_ff1 ;
reg rs232_tx_ff2 ;
wire tx_neg_flag ;
reg add_flag ;
reg [ 13: 0] cnt0 ;
reg [ 3: 0] cnt1 ;
reg [ 9: 0] cnt2 ;
reg [ 3: 0] cnt3 ;
reg [ 2: 0] cnt_0 ;
reg [ 2: 0] cnt_1 ;
wire add_cnt0 ;
wire end_cnt0 ;
wire add_cnt1 ;
wire end_cnt1 ;
wire add_cnt2 ;
wire end_cnt2 ;
wire add_cnt3 ;
wire end_cnt3 ;
//查找表
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
baud_c <= 5208;
end
else begin
case(baud_set)
0: baud_c = 14'd10416;
1: baud_c = 14'd5208 ;
2: baud_c = 14'd2604 ;
3: baud_c = 14'd1302 ;
4: baud_c = 14'd868 ;
5: baud_c = 14'd434 ;
default:baud_c = 14'd5208 ;//默认9600bps
endcase
end
end
//打两拍 防止亚稳态,同时scan negedge
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rs232_tx_ff0 <= 1;
rs232_tx_ff1 <= 1;
rs232_tx_ff2 <= 1;
end
else begin
rs232_tx_ff0 <= rs232_tx;
rs232_tx_ff1 <= rs232_tx_ff0;
rs232_tx_ff2 <= rs232_tx_ff1;
end
end
//扫描下降沿
assign tx_neg_flag = rs232_tx_ff2 && !rs232_tx_ff1;
//计数标志信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
add_flag <= 0;
end
else if(tx_neg_flag) begin
add_flag <= 1;
end
else if(rx_done)begin
add_flag <= 0;
end
end
//计数器,计数1bit数据长度
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1'b1;
end
end
assign add_cnt0 = add_flag;
assign end_cnt0 = add_cnt0 && cnt0==baud_c-1;
//计数器,计数8位接收数据长度
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1'b1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1== 8;
//比特内部采样点时钟计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt2 <= 0;
end
else if(add_cnt2)begin
if(end_cnt2)
cnt2 <= 0;
else
cnt2 <= cnt2 + 1'b1;
end
end
assign add_cnt2 = add_flag;
assign end_cnt2 = add_cnt2 && (cnt2== (baud_c/16)-1 || end_cnt0);
//一个bit数据中16个采样点计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt3 <= 0;
end
else if(add_cnt3)begin
if(end_cnt3)
cnt3 <= 0;
else
cnt3 <= cnt3 + 1'b1;
end
end
assign add_cnt3 = add_cnt2 && cnt2== (baud_c/16)-1;
assign end_cnt3 = end_cnt0 || (end_cnt2 && cnt3==16-1);
//比特内选取6个采样点是0或1计数
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
cnt_0 <= 0;
cnt_1 <= 0;
end
else if(add_flag) begin
if(cnt3>=6 && cnt3<=11)begin
if(cnt2==baud_c/16/2 && rs232_tx_ff1==0)
cnt_0 <= cnt_0 + 1'b1;
else if(cnt2==baud_c/16/2 && rs232_tx_ff1==1)
cnt_1 <= cnt_1 + 1'b1;
end
else if(end_cnt0)begin
cnt_0 <= 0;
cnt_1 <= 0;
end
end
else begin
cnt_0 <= 0;
cnt_1 <= 0;
end
end
//输出并行数据data_byte
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
data_byte <= 0;
end
else if(end_cnt0 && cnt1>0 && cnt1 <9) begin
if(cnt_0 >= cnt_1)
data_byte[cnt1-1] = 0;
else if(cnt_0 < cnt_1)
data_byte[cnt1-1] = 1;
end
end
//输出接收完成标志信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rx_done <= 0;
end
else if(end_cnt1) begin
rx_done <= 1;
end
else begin
rx_done <= 0;
end
end
endmodule
El código de prueba es el siguiente:
`timescale 1 ns/ 1 ns
module UART_Byte_Rx_tb();
// constants
// test vector input registers
reg [2:0] baud_set;
reg clk;
reg rs232_rx;
reg rst_n;
// wires
wire [7:0] data_byte;
wire rx_done;
parameter clk_period = 20;
// assign statements (if any)
UART_Byte_Rx i1 (
// port map - connection between master ports and signals/registers
.baud_set(baud_set),
.clk(clk),
.data_byte(data_byte),
.rs232_rx(rs232_rx),
.rst_n(rst_n),
.rx_done(rx_done)
);
initial clk = 0;
always #(clk_period/2) clk = ~clk;
initial begin
#1;
rst_n = 0;
baud_set = 0;
rs232_rx = 1;
#(clk_period*5);
rst_n = 1;
baud_set = 3'd1;
#(clk_period*3);
repeat(1)begin
//发送0000_1010
rs232_rx = 0;
#(clk_period*5208);
rs232_rx = 0;
#(clk_period*5208*4);
rs232_rx = 1;
#(clk_period*5208);
rs232_rx = 0;
#(clk_period*5208);
rs232_rx = 1;
#(clk_period*5208);
rs232_rx = 0;
#(clk_period*5208);
rs232_rx = 1;
#(clk_period*5208);
//发送1000_0101
rs232_rx = 0;
#(clk_period*5208);
rs232_rx = 1;
#(clk_period*5208);
rs232_rx = 0;
#(clk_period*5208*4);
rs232_rx = 1;
#(clk_period*5208);
rs232_rx = 0;
#(clk_period*5208);
rs232_rx = 1;
#(clk_period*5208);
rs232_rx = 1;
#(clk_period*5208);
end
#(clk_period*50);
$stop;
end
endmodule
Los gráficos de simulación son los siguientes:
2. Módulo de envío de puerto serie
En correspondencia con el módulo receptor del puerto serie aquí, se envían datos de 8 bits, más un bit de inicio y un bit de parada. La lista de señales de la interfaz del módulo es la siguiente:
Nombre de la señal | E / S | Dígitos | Función descriptiva |
clk | yo | 1 | Reloj del sistema 50MHz |
rst_n | yo | 1 | Reinicio de sistema |
send_en | yo | 1 | Enviar habilitar |
data_byte | yo | 8 | Datos enviados |
baud_set | yo | 3 | Ajuste de velocidad en baudios |
rs232_tx | EL | 1 | FPGA convierte datos en datos en serie y los envía |
tx_done | EL | 1 | Enviar marca de datos completos |
uart_state | EL | 1 | Estado de envío del puerto serie, 1 significa ocupado, 0 significa inactivo |
El código es el siguiente: Uart_Byte_Tx.v
//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 phf的CSDN
//File name: Uart_Byte_Tx.v
//Last modified Date: 2020/5/22
//Last Version:
//Descriptions: 串口发送模块,8位数据位、1位起始位和1位停止位、无校验位
//-------------------------------------------------------------------
module Uart_Byte_Tx(
input clk , //系统时钟
input rst_n , //系统复位
input send_en , //发送使能
input [ 7 : 0 ] data_byte , //发送的数据
input [ 2 : 0 ] baud_set , //波特率设置
output reg rs232_tx , //FPGA将数据转换成串行数据发出
output reg tx_done , //发送数据完毕标志
output reg uart_state //串口发送状态,1为忙,0为空闲
);
reg [ 13: 0] baud_c ;//(4800bps-10416),(9600bps-5208),(19200bps-2604),
//(38400bps-1302),(57600bps-868),(115200bps-434)
wire [ 9: 0] data_out ;
reg [ 15: 0] cnt0 ; //1bit数据长度计数
reg [ 3: 0] cnt1 ; //发送一字节数据对每个字节计数
wire add_cnt0 ; //计数器cnt0加一条件
wire add_cnt1 ; //计数器cnt1加一条件
wire end_cnt0 ; //计数器cnt0结束条件
wire end_cnt1 ; //计数器cnt1结束条件
reg [ 7: 0] data_byte_ff ; //发送使能时将发送的数据寄存下来
//波特率查找表
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
baud_c <= 5208;
end
else begin
case(baud_set)
0: baud_c = 14'd10416;
1: baud_c = 14'd5208 ;
2: baud_c = 14'd2604 ;
3: baud_c = 14'd1302 ;
4: baud_c = 14'd868 ;
5: baud_c = 14'd434 ;
default:baud_c = 14'd5208 ;//默认9600bps
endcase
end
end
//串口状态标志,0为空闲,1为忙
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
uart_state <= 0;
end
else if(send_en) begin
uart_state <= 1;
end
else if(end_cnt1)begin
uart_state <= 0;
end
else begin
uart_state <= uart_state;
end
end
//1bit数据长度计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1'b1;
end
end
assign add_cnt0 = uart_state==1;
assign end_cnt0 = add_cnt0 && cnt0== baud_c-1;
//发送一字节数据对每个字节计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1'b1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1== 10-1;
//串口发送结束标志
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
tx_done <= 0;
end
else if(end_cnt1) begin
tx_done <= 1;
end
else begin
tx_done <= 0;
end
end
//发送使能时将发送的数据寄存下来
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
data_byte_ff <= 0;
end
else if(send_en) begin
data_byte_ff <= data_byte;
end
end
//发送串行数据到串口
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rs232_tx <= 1;
end
else if(uart_state && cnt0==0) begin
rs232_tx <= data_out[cnt1];
end
end
assign data_out = {1'b1,data_byte_ff,1'b0};
endmodule
El código de prueba es el siguiente:
`timescale 1 ns/ 1 ns
module Uart_Byte_Tx_tb();
// constants
// test vector input registers
reg [2:0] baud_set;
reg clk;
reg [7:0] data_byte;
reg rst_n;
reg send_en;
// wires
wire rs232_tx;
wire tx_done;
wire uart_state;
parameter clk_period = 20;
// assign statements (if any)
Uart_Byte_Tx i1 (
// port map - connection between master ports and signals/registers
.baud_set(baud_set),
.clk(clk),
.data_byte(data_byte),
.rs232_tx(rs232_tx),
.rst_n(rst_n),
.send_en(send_en),
.tx_done(tx_done),
.uart_state(uart_state)
);
initial clk = 1;
always #(clk_period/2) clk = ~clk;
initial begin
rst_n = 0;
baud_set = 3'd1;
send_en = 0;
data_byte = 0;
#(clk_period*5);
rst_n = 1;
#(clk_period*5);
send_en = 1;
data_byte = 8'b0001_0100;
#(clk_period);
send_en = 0;
data_byte = 0;
#(clk_period*5208*15);
$stop;
end
endmodule
La forma de onda de simulación es la siguiente:
Cumple con los requisitos de diseño después de la verificación de la simulación.