了解串口通信首先要了解串口通信的原理:
串口通信的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总长不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。典型地,串口用于ASCII码字符的传输。通信使用3根线完成:(1)地线,(2)发送,(3)接收。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但是不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配:
a,波特率:这是一个衡量通信速度的参数。它表示每秒钟传送的bit的个数。例如300波特表示每秒钟发送300个bit。当我们提到时钟周期时,我们就是指波特率例如如果协议需要4800波特率,那么时钟是4800Hz。这意味着串口通信在数据线上的采样率为4800Hz。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。
b,数据位:这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。
c,停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
d,奇偶校验位:在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
发送模块的设计:
串口发送,通过按键控制指令类型。可在PC端显示相应指令。
指令一:I like FPGA.
指令二:I like Verilog.
指令三:I like 107.
后来加了指令切换功能通过按键切换(这里没有画出来)
模块设计:
div.v
moduleuart_div(clk,rst_n,bps_sel,bps_clk ); inputclk ;//输入系统时钟inputrst_n ;//复位信号input [1:0] bps_sel ;//波特率选择outputbps_clk ;//输出波特率时钟信号regbps_clk ; parameterBPS_4800=13'd5208 ,BPS_9600=13'd2604 ,BPS_19200=13'd1302 ,BPS_115200=13'd217 ;reg [12:0] div_cnt ; reg [12:0] time_div ; //波特率选择模块always (*)beginif(rst_n==1'b0)begintime_div=BPS_9600; endelsebegincase(bps_sel) 2'b00: time_div = BPS_4800;2'b01: time_div = BPS_9600;2'b10: time_div = BPS_19200;2'b11: time_div = BPS_115200;default:time_div=BPS_9600; endcaseendend//波特率时钟计数模块always (posedgeclkornegedgerst_n)beginif(rst_n==1'b0)begindiv_cnt<=1'b0;endelseif(div_cnt==time_div-1'b1)begindiv_cnt<=0; endelsebegindiv_cnt<=div_cnt+1'b1;endend//波特率时钟输出模块always (posedgeclkornegedgerst_n)beginif(rst_n==1'b0)beginbps_clk<=1'b0;endelseif(div_cnt<(time_div-1'b1))beginbps_clk<=1'b1;endelsebeginbps_clk<=1'b0;endendendmodule
串口通信发送模块:
moduleuart_txd(clk,rst_n,Date_byte,send_en,txd,txd_finish,uart_state ); inputclk ;//输入时钟inputrst_n ;//复位信号input [7:0] Date_byte ;//发送数据inputsend_en ;//发送使能outputtxd ;//串口发送outputtxd_finish ;//发送完成位outputuart_state ;//串口状态位regtxd ; regtxd_finish ; reguart_state ; reg [7:0] date_buf ; //数据寄存模块always (posedgeclkornegedgerst_n)beginif(rst_n==1'b0)begindate_buf<=8'd0;endelseif(send_en==1'b1)begindate_buf<=Date_byte; endelsebegindate_buf<=date_buf; endend//串口状态模块always (posedgeclkornegedgerst_n)beginif(rst_n==1'b0)beginuart_state<=1'b0;endelseif(send_en==1'b1)beginuart_state<=1'b1;endelseif(txd_finish==1'b1)beginuart_state<=1'b0;endelsebeginuart_state<=uart_state; endendreg [3:0] dat_cnt ; //数据计数模块always (posedgeclkornegedgerst_n)beginif(rst_n==1'b0)begindat_cnt<=4'd0;endelseif(txd_finish==1'b1)begindat_cnt<=4'd0;endelseif(send_en==1'b1)begindat_cnt<=dat_cnt+1'b1;endelsebegindat_cnt<=4'd0;endend//串口发送完成标志模块always (posedgeclkornegedgerst_n)beginif(rst_n==1'b0)begintxd_finish<=1'b0;endelseif(dat_cnt==4'd10)begintxd_finish<=1'b1;endelsebegintxd_finish<=1'b0;endend//数据发送模块always (posedgeclkornegedgerst_n)beginif(rst_n==1'b0)begintxd<=1'b1;endelseif(send_en==1'b0)begintxd<=1'b1;endelsebegincase(dat_cnt) 4'd0 :txd<=1'b1; 4'd1 :txd<=1'b0;//发送位4'd2 :txd<=date_buf[0];4'd3 :txd<=date_buf[1];4'd4 :txd<=date_buf[2];4'd5 :txd<=date_buf[3];4'd6 :txd<=date_buf[4];4'd7 :txd<=date_buf[5];4'd8 :txd<=date_buf[6];4'd9 :txd<=date_buf[7];4'd10:txd<=1'b1;//停止位default:txd<=1'b1;endcaseendendendmodule
数据缓冲模块:
module txd_data_buf(clk,rst_n,txd_finish,sel_dat,dat_buf_en,data_out ); input clk ;//输入时钟 input rst_n ;//复位信号 input txd_finish;//一位数据发送完成标志 input [1:0] sel_dat ; output [7:0] data_out ; reg [7:0] data_out ; output dat_buf_en;//数据发送使能位 reg dat_buf_en; reg [4:0] dat_cnt ; parameter DAT_FULL_1 = 4'd12 , DAT_FULL_2 = 4'd15 , DAT_FULL_3 = 4'd11 ; //数据发送使能模块 always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin dat_buf_en<=1'b0; end else if(sel_dat==2'b01)begin if(dat_cnt==dat_ful)begin dat_buf_en<=1'b0; end else begin dat_buf_en<=1'b1; end end else if(sel_dat==2'b10)begin if(dat_cnt==dat_ful)begin dat_buf_en<=1'b0; end else begin dat_buf_en<=1'b1; end end else if(sel_dat==2'b11)begin if(dat_cnt==dat_ful)begin dat_buf_en<=1'b0; end else begin dat_buf_en<=1'b1; end end else begin dat_buf_en<=dat_buf_en; end end //数据计数模块 always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin dat_cnt<=1'b0; end else if(dat_cnt==dat_ful)begin dat_cnt<=dat_cnt; end else if(dat_buf_en==1'b0)begin dat_cnt<=1'b0; end else if(txd_finish==1'b1)begin dat_cnt<=dat_cnt+1'b1; end else begin dat_cnt<=dat_cnt; end end //数据选择模块 always@(*)begin if(rst_n==1'b0)begin data_out=0; end else if(dat_buf_en==1'b0)begin data_out=0; end else if(sel_dat==2'b01)begin case(dat_cnt) 4'd0 :data_out="I"; 4'd1 :data_out=" "; 4'd2 :data_out="L"; 4'd3 :data_out="i"; 4'd4 :data_out="k"; 4'd5 :data_out="e"; 4'd6 :data_out=" "; 4'd7 :data_out="F"; 4'd8 :data_out="P"; 4'd9 :data_out="G"; 4'd10:data_out="A"; 4'd11:data_out="."; default:data_out=0; endcase end else if(sel_dat==2'b10)begin case(dat_cnt) 4'd0 :data_out="I"; 4'd1 :data_out=" "; 4'd2 :data_out="L"; 4'd3 :data_out="i"; 4'd4 :data_out="k"; 4'd5 :data_out="e"; 4'd6 :data_out=" "; 4'd7 :data_out="V"; 4'd8 :data_out="e"; 4'd9 :data_out="r"; 4'd10:data_out="i"; 4'd11:data_out="l"; 4'd12:data_out="o"; 4'd13:data_out="g"; 4'd14:data_out="."; default:data_out=0; endcase end else if(sel_dat==2'b11)begin case(dat_cnt) 4'd0 :data_out="I"; 4'd1 :data_out=" "; 4'd2 :data_out="L"; 4'd3 :data_out="i"; 4'd4 :data_out="k"; 4'd5 :data_out="e"; 4'd6 :data_out=" "; 4'd7 :data_out="1"; 4'd8 :data_out="0"; 4'd9 :data_out="7"; 4'd10:data_out="."; default:data_out=0; endcase end end reg [3:0] dat_ful; always@(*)begin if(rst_n==1'b0)begin dat_ful=4'd15; end else if(sel_dat==2'b01)begin dat_ful=DAT_FULL_1; end else if(sel_dat==2'b10)begin dat_ful=DAT_FULL_2; end else if(sel_dat==2'b11)begin dat_ful=DAT_FULL_3; end else begin dat_ful=dat_ful; end end endmodule
控制电路,控制指令发送:
module control(clk,rst_n,key_entr,key_down,sel_dat,bps_sel ); input clk; input rst_n; input key_entr,key_down; output reg [1:0]sel_dat; output reg [1:0]bps_sel; //按键抖动判断逻辑 wire key; //所有的按键相与的结果,用于按键触发判断 assign key =key_entr; reg[3:0]keyr ; //按键值key的缓冲寄存器 always@(posedge clk or negedge rst_n) begin if(!rst_n) keyr <=4'b1111; else keyr <={keyr[2:0],key}; end wire key_neg =~keyr[2] &keyr [3];//有按键被按下 wire key_pos =keyr[2] &~keyr [3];// 有按键被释放 //定时器计数逻辑,用于对按键的消抖的判断 reg [19:0] cnt; always@(posedge clk or negedge rst_n) begin if(!rst_n) cnt <=20'd0; else if(key_pos||key_neg) cnt <=20'd0; else if(cnt <20'd999_999) cnt <= cnt +1'b1; else cnt <=20'd0; end reg [1:0] key_value_0; reg [1:0] key_value_1; //定时采取按键值 always@(posedge clk or negedge rst_n) begin if(!rst_n) begin key_value_0 <=2'b11; key_value_1 <=2'b11; end else begin if(cnt ==20'd999_999) key_value_0 <={key_entr,key_down}; else key_value_1 <=key_value_0; end end wire [1:0] key_press=key_value_1 & ~key_value_0; //切换指令控制 always@(posedge clk or negedge rst_n) begin if(!rst_n) sel_dat<=1'b0; else if(key_press[1]==1'b1)begin if(sel_dat==2'b11)begin sel_dat<=2'b01; end else begin sel_dat<=sel_dat+1'b1; end end else begin sel_dat<=sel_dat; end end //切换波特率控制 always@(posedge clk or negedge rst_n) begin if(!rst_n) bps_sel<=2'b01; else if(key_press[0]==1'b1)begin if(bps_sel==2'b11)begin bps_sel<=2'b00; end else begin bps_sel<=bps_sel+1'b1; end end else begin bps_sel<=bps_sel; end end endmodule
顶层文件
module top(ext_clk_25m,ext_rst_n,uart_tx,key_down,key_entr,led ); input ext_clk_25m ;//系统时钟 input ext_rst_n ;//复位信号 input key_down ;//波特率选择 input key_entr ;//指令选择 output[1:0] led ;//指示bps output uart_tx ;//串口发送 assign led[1:0]=bps_sel[1:0]; wire bps_clk ; wire txd_finish ; wire uart_state ; wire [7:0] data_out ; wire dat_buf_en ; wire [1:0] bps_sel ; wire [1:0] sel_dat ; //数据缓冲区 txd_data_buf u_buf( .clk(bps_clk), .rst_n(ext_rst_n), .txd_finish(txd_finish), .sel_dat(sel_dat), .dat_buf_en(dat_buf_en), .data_out(data_out) ); //波特率选择模块 uart_div u_div( .clk(ext_clk_25m), .rst_n(ext_rst_n), .bps_sel(bps_sel), .bps_clk(bps_clk) ); //串口发送模块 uart_txd u_txd( .clk(bps_clk), .rst_n(ext_rst_n), .Date_byte(data_out), .send_en(dat_buf_en), .txd(uart_tx), .txd_finish(txd_finish), .uart_state(uart_state) ); //按键控制模块 control u_con( .clk(ext_clk_25m), .rst_n(ext_rst_n), .key_entr(key_entr), .key_down(key_down), .sel_dat(sel_dat), .bps_sel(bps_sel) ); endmodule
串口助手验证: