引言:
随着电子技术的不断进步,串口通信已成为嵌入式系统和计算机外设中一种广泛使用的异步通信方式。串口通信因其简单性、可靠性以及对硬件资源的低要求,在数据传输领域扮演着重要角色。在FPGA(现场可编程门阵列)设计中,实现串口通信功能是连接外部处理器和FPGA、实现数据交换的关键技术之一。 本实验旨在通过Verilog语言,设计并实现一个能够在FPGA上运行的串口发送模块。通过本实验,将有机会深入了解FPGA的通信接口设计,掌握Verilog语言在串口通信应用中的编程技巧,以及如何将理论知识应用到实际的硬件设计中。
希望你在本次学习过后,能够有一定的收获!!!
冲啊!!!! ٩(͡๏̯͡๏)۶ ٩(͡๏̯͡๏)۶ ٩(͡๏̯͡๏)۶
- 熟悉FPGA的开发流程
- 练习并且巩固有关于verilog代码的相关内容
- 使用Verilog语言编写程序实现FPGA的串口输出
- 了解熟悉GBK编码
GBK编码概述: GBK是一种用于简体中文字符的编码方式,它是对GB2312编码的扩展,支持更多的汉字和符号。GBK编码可以表示21003个汉字和图形符号,包括6763个汉字,并且兼容ASCII编码中的所有字符。
GBK编码特点:
GBK编码在串口通信中的应用: 在FPGA的串口通信中,经常需要发送或接收文本信息。由于ASCII编码只能表示128个字符,不足以涵盖所有的中文字符,因此,使用GBK编码可以传输完整的中文文本。在设计串口发送模块时,需要将待发送的字符串转换为GBK编码的字节序列,然后通过串口发送出去。
GBK编码转换:
实验中的GBK应用: 在本实验中,将学习如何将字符串转换为GBK编码,并在FPGA上实现串口发送模块,将GBK编码的数据通过串口发送出去。这不仅涉及到GBK编码的知识,还需要掌握Verilog语言的编程技巧和FPGA的串口通信机制。
编码转换网站:查看字符编码(简体中文) (mytju.com)

串口通信概述: 串口通信是一种计算机硬件设备之间常用的串行通信方式,它允许数据以串行的方式在设备之间传输。串口通信广泛应用于嵌入式系统、计算机外设、工业控制系统等领域。
串口通信特点:
串口通信的关键参数:
串口通信在FPGA设计中的应用: 在FPGA设计中,实现串口通信功能可以用于:
FPGA串口通信设计要点:
实验中的串口通信实现: 在本实验中,将学习如何使用Verilog语言在FPGA上实现串口发送模块。这包括:
通过本实验,将深入理解串口通信的原理和实现方法,为未来在更复杂的通信协议和系统设计中打下坚实的基础。
module ctrl (
input clk,
input rst_n,
output tx
);
wire [7:0] send_data[31:0] ; // 发送的数据数组 -- Hello World !!! 你干嘛,哎哟~~~
assign send_data[00] = {8'h48};
assign send_data[01] = {8'h65};
assign send_data[02] = {8'h6C};
assign send_data[03] = {8'h6C};
assign send_data[04] = {8'h6F};
assign send_data[05] = {8'h20};
assign send_data[06] = {8'h57};
assign send_data[07] = {8'h6F};
assign send_data[08] = {8'h72};
assign send_data[09] = {8'h6C};
assign send_data[10] = {8'h64};
assign send_data[11] = {8'h20};
assign send_data[12] = {8'h21};
assign send_data[13] = {8'h21};
assign send_data[14] = {8'h21};
assign send_data[15] = {8'h20};
assign send_data[16] = {8'hC4};
assign send_data[17] = {8'hE3};
assign send_data[18] = {8'hB8};
assign send_data[19] = {8'hC9};
assign send_data[20] = {8'hC2};
assign send_data[21] = {8'hEF};
assign send_data[22] = {8'hA3};
assign send_data[23] = {8'hAC};
assign send_data[24] = {8'hB0};
assign send_data[25] = {8'hA5};
assign send_data[26] = {8'hD3};
assign send_data[27] = {8'hB4};
assign send_data[28] = {8'h7E};
assign send_data[29] = {8'h7E};
assign send_data[30] = {8'h7E};
assign send_data[31] = {8'h0A}; // 换行
parameter TIME_pause = 28'd 50_000_000;
// 每次完整的发送时间 1s
reg [40-1:0] cnt_pause ;
wire add_cnt_pause , end_cnt_pause ;
always @(posedge clk or negedge rst_n)
if (!rst_n)
cnt_pause <= 40'b0;
else if (add_cnt_pause )
if (end_cnt_pause )
cnt_pause <=40'b0;
else
cnt_pause <= cnt_pause +1'd1;
else
cnt_pause <= cnt_pause ;
assign add_cnt_pause = 1'b1;
assign end_cnt_pause = add_cnt_pause && (TIME_pause -1 == cnt_pause );
// 每次完整的发送时间 1s
// 每个1s 切换一次发送状态
reg flag_sent;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
flag_sent <= 1'b0;
else if (end_cnt_pause)
flag_sent <= ~flag_sent;
else
flag_sent <= flag_sent;
end
// 每个1s 切换一次发送状态
// 每一个字符之间的间隔发送时间
reg [40-1:0] cnt_bit_pause ;
wire add_cnt_bit_pause , end_cnt_bit_pause ;
always @(posedge clk or negedge rst_n)
if (!rst_n)
cnt_bit_pause <= 40'b0;
else if (add_cnt_bit_pause )
if (end_cnt_bit_pause )
cnt_bit_pause <=40'b0;
else
cnt_bit_pause <= cnt_bit_pause +1'd1;
else
cnt_bit_pause <= cnt_bit_pause ;
assign add_cnt_bit_pause = flag_sent;
assign end_cnt_bit_pause = add_cnt_bit_pause && (cnt_bit_pause == (300_00 -1));
// 每一个字符之间的间隔发送时间
// 一个句是否发送完成标志
reg one_epoch;
// 单个字符数据
reg [7:0] tx_data ;
reg [6-1:0] cnt_bit ;
wire add_cnt_bit , end_cnt_bit ;
always @(posedge clk or negedge rst_n)
if (!rst_n)
cnt_bit <= 6'b0;
else if (add_cnt_bit)
if (end_cnt_bit )
cnt_bit <=6'b0;
else
cnt_bit <= cnt_bit +1'd1;
else
cnt_bit <= cnt_bit ;
assign add_cnt_bit = flag_sent && end_cnt_bit_pause && !one_epoch;
assign end_cnt_bit = add_cnt_bit && (6'd32-1 == cnt_bit );
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
one_epoch <= 1'b0;
else if (end_cnt_bit)
one_epoch <= 1'b1;
else if(!flag_sent)
one_epoch <= 1'b0;
else
one_epoch <= one_epoch;
end
always @(*) begin
tx_data <= send_data[cnt_bit];
end
uart_tx inst_tx_uart (
.clk (clk),
.rst_n (rst_n),
.din (tx_data),
.din_vld (end_cnt_bit_pause && !one_epoch),
.dout (tx)
);
endmodule
module uart_tx (
input clk,
input rst_n,
input [7:0] din,
input din_vld,
output reg dout
);
parameter BAUD = 434;
reg [8:0] cnt_bsp;
wire add_cnt_bsp;
wire end_cnt_bsp;
reg [3:0] cnt_bit;
wire add_cnt_bit;
wire end_cnt_bit;
reg flag;
reg [8:0] data;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) cnt_bsp <= 0;
else if(add_cnt_bsp)
if(end_cnt_bsp) cnt_bsp <= 0;
else cnt_bsp <= cnt_bsp + 1;
else cnt_bsp <= cnt_bsp;
end
assign add_cnt_bsp = flag;
assign end_cnt_bsp = add_cnt_bsp && (cnt_bsp == BAUD - 1);
always @(posedge clk or negedge rst_n) begin
if(!rst_n) cnt_bit <= 0;
else if(add_cnt_bit)
if(end_cnt_bit) cnt_bit <= 0;
else cnt_bit <= cnt_bit + 1;
else cnt_bit <= cnt_bit;
end
assign add_cnt_bit = end_cnt_bsp;
assign end_cnt_bit = add_cnt_bit && (cnt_bit == 8);
always @(posedge clk or negedge rst_n)begin
if(!rst_n) flag = 0;
else if (din_vld) flag <=1;
else if (end_cnt_bit) flag <=0;
else flag <= flag;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n) data <= !0;
else if (din_vld) data <= {din,1'b0} ; // 数据加上起始位
else data <= data;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n) dout <=1;
else if (cnt_bsp == 1) dout <= data[cnt_bit]; // 串行输出 LSB
else if (end_cnt_bit) dout <= 1'b1;
else dout <= dout;
end
endmodule
模块功能概述: 本实验的目的是设计一个能够在FPGA上运行的串口发送模块,该模块能够通过串口发送预定义的字符串数据。代码中定义了一个发送数据数组send_data,包含了要发送的每个字符的ASCII码或GBK码。
串口发送原理:
send_data来存储要发送的字符数据,每个字符占用8位,数组长度可以根据需要发送的字符数量来定义。assign语句将特定的ASCII码或GBK码赋给send_data数组的每个元素,形成要发送的数据序列。代码实现细节:
flag_sent来控制数据的发送,该标志位每秒切换一次,以实现每秒发送一次数据数组。cnt_pause计数器和相关逻辑来实现发送之间的暂停,以便在发送每个字符后提供适当的延迟。cnt_bit计数器来控制单个字符的位发送过程,确保每个字符的8位数据按顺序发送。uart_tx模块,它负责根据波特率控制信号逐位发送数据,并在每发送完一个字节后添加停止位。波特率发生器:
uart_tx模块内部实现了一个波特率发生器,通过计数器cnt_bsp来控制发送速率,以匹配预设的波特率BAUD。数据发送:
data寄存器存储,并在每个时钟周期发送最低位(LSB),直到8位数据全部发送完毕。串口信号输出:
dout信号负责输出当前正在发送的位,起始位为0,停止位为1。实验关键点:
通过本实验,将能够理解并实践FPGA上串口发送模块的设计,为进一步学习更复杂的串口通信应用打下基础。



v-串口
经过本次实验,我成功设计并实现了一个FPGA上的串口发送模块,该模块能够按照预定的波特率发送存储在数据数组send_data中的字符串。通过这个过程,我深入理解了串口通信的工作原理和在FPGA上实现它所需的关键技术。 实验过程回顾:
send_data,并为每个字符分配了相应的ASCII或GBK编码。flag_sent来控制每秒发送一次数据数组中的字符。cnt_bsp来控制数据的发送速率,以匹配预设的波特率。uart_tx模块,它负责实际的串口信号输出,包括数据位的串行化和波特率控制。实验成果: 通过本次实验,我不仅掌握了Verilog语言在串口通信应用中的编程技巧,还学会了如何将理论知识应用到实际的硬件设计中。我能够独立完成从设计、仿真到硬件测试的整个FPGA设计流程,并且能够通过外部串口工具接收发送的数据。
实验反思: 在实验过程中,我遇到了一些挑战,比如确保数据在发送过程中的时序准确性,以及调试串口通信中的常见问题。通过不断测试和调整,我学会了如何对FPGA设计进行调试,解决可能出现的问题。
未来展望: 虽然本次实验已经完成,但我认识到在FPGA设计和串口通信方面还有许多可以学习和探索的地方。未来,我计划探索更复杂的通信协议,如SPI、I2C等,并尝试将这些技术应用到更大型的FPGA项目中。