串口通信是目前比较重要的一种通信方式,主要是用于计算机和外部的通信。
首先简单的介绍一下串口通信的原理:串口用于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个逻辑高位。
高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。
这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步串口通信的数据传输时序如图:下面简单的介绍一下本实验所实现的功能以及相应的程序思想:验实现的功能有上位机向开发板发送数据,开发板接收以后通过数码管显示,并且将接收到的数据传回给上位机,通过串口调试助手将返回的数据显示出来。
其模块图如下:程序思想:(本程序主要是采用特权同学视频教程的程序,本人只是做一个简单的思路总结)1. 通过ISE开发工具,新建4个模块,分别为串口接收波特率产生模块,串口接收模块,串口发送波特率产生模块,串口接收模块,另外再加一个顶层模块。
2. 顶层模块中只是做模块的声明和端口声明,不做任何的逻辑处理。
3. 波特率产生模块(两个模块都一样):主要是通过计数来实现波特率产生,并对数据进行采样。
此处采用9600bps,由于1s中有104166个us,系统时钟为50M,即20us,即需要计数为104166/20=5208,因此循环计数0~5207,并且在计数到2603时对数据进行采样。
4. 串口接收模块:当接收到接收信号置位时,对数据进行采集。
5. 串口发送模块:当接收到发送信号置位时,对数据进行发送。
具体程序如下:/=============================顶层模块=====================================module uart_top(clk,,rst_n,rs232_rx,rs232_tx,led);input clk; //时钟信号50Minput rst_n; //复位信号,低有效input rs232_rx; //数据输入信号output rs232_tx; //数据输出信号output [7:0] led;wire bps_start1,bps_start2;//wire clk_bps1,clk_bps2;wire [7:0] rx_data; //接收数据存储器,用来存储接收到的数据,直到下一个数据接收wire rx_int; //接收数据中断信号,接收过程中一直为高,/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////子模块端口申明///////////////////////////////////speed_select_rx speed_rx( //数据接收波特率选择模块.clk(clk),.rst_n(rst_n),.bps_start(bps_start1),.clk_bps(clk_bps1));uart_rx uart_rx( //数据接收模块.clk(clk),.rst_n(rst_n),.bps_start(bps_start1),.clk_bps(clk_bps1),.rs232_rx(rs232_rx),.rx_data(rx_data),.rx_int(rx_int),.led(led));speed_select_tx speed_tx( //数据发送波特率控制模块 .clk(clk),.rst_n(rst_n),.bps_start(bps_start2),.clk_bps(clk_bps2));uart_tx uart_tx(.clk(clk),.rst_n(rst_n),.bps_start(bps_start2),.clk_bps(clk_bps2),.rs232_tx(rs232_tx),.rx_data(rx_data),.rx_int(rx_int));endmodule/=================================波特率产生模块=====================================module speed_select_rx(clk,rst_n,bps_start,clk_bps);//波特率设定input clk; //50M时钟input rst_n; //复位信号input bps_start; //接收到信号以后,波特率时钟信号置位,当接收到uart_rx传来的信号以后,模块开始运行output clk_bps; //接收数据中间采样点,// `define BPS_PARA 5207;//9600波特率分频计数值// `define BPS_PARA_2 2603;//计数一半时采样reg[12:0] cnt;//分频计数器reg clk_bps_r;//波特率时钟寄存器reg[2:0] uart_ctrl;//波特率选择寄存器always @(posedge clk or negedge rst_n)if(!rst_n)cnt<=13'd0;else if((cnt==5207)|| !bps_start)//判断计数是否达到1个脉宽 cnt<=13'd0;elsecnt<=cnt+1'b1;//波特率时钟启动always @(posedge clk or negedge rst_n) beginif(!rst_n)clk_bps_r<=1'b0;else if(cnt== 2603)//当波特率计数到一半时,进行采样存储clk_bps_r<=1'b1;elseclk_bps_r<=1'b0;endassign clk_bps = clk_bps_r;//将采样数据输出给uart_rx模块endmodule//================================数据接收模块==========================================module uart_rx(clk,rst_n,bps_start,clk_bps,rs232_rx,rx_data,rx_int,led);input clk; //时钟input rst_n; //复位input rs232_rx; //接收数据信号input clk_bps; //高电平时为接收信号中间采样点output bps_start; //接收信号时,波特率时钟信号置位output [7:0] rx_data;//接收数据寄存器output rx_int; //接收数据中断信号,接收过程中为高output [7:0] led;reg [7:0] led;reg rs232_rx0,rs232_rx1,rs232_rx2,rs232_rx3;//接收数据寄存器wire neg_rs232_rx;//表示数据线接收到下沿always @(posedge clk or negedge rst_n) beginif(!rst_n) beginrs232_rx0 <= 1'b0;rs232_rx1 <= 1'b0;rs232_rx2 <= 1'b0;rs232_rx3 <= 1'b0;endelse beginrs232_rx0 <= rs232_rx;rs232_rx1 <= rs232_rx0;rs232_rx2 <= rs232_rx1;rs232_rx3 <= rs232_rx2;endendassign neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0;//串口传输线的下沿标志reg bps_start_r;reg [3:0] num;//移位次数reg rx_int; //接收中断信号always @(posedge clk or negedge rst_n)if(!rst_n) beginbps_start_r <=1'bz;rx_int <= 1'b0;endelse if(neg_rs232_rx) begin//bps_start_r <= 1'b1; //启动串口,准备接收数据rx_int <= 1'b1; //接收数据中断使能endelse if(num==4'd12) begin //接收完有用的信号,bps_start_r <=1'b0; //接收完毕,改变波特率置位,方便下次接收 rx_int <= 1'b0; //接收信号关闭endassign bps_start = bps_start_r;reg [7:0] rx_data_r;//串口数据寄存器reg [7:0] rx_temp_data;//当前数据寄存器always @(posedge clk or negedge rst_n) if(!rst_n) beginrx_temp_data <= 8'd0;num <= 4'd0;rx_data_r <= 8'd0;endelse if(rx_int) begin //接收数据处理if(clk_bps) beginnum <= num+1'b1;case(num)4'd1: rx_temp_data[0] <= rs232_rx;4'd2: rx_temp_data[1] <= rs232_rx;4'd3: rx_temp_data[2] <= rs232_rx;4'd4: rx_temp_data[3] <= rs232_rx;4'd5: rx_temp_data[4] <= rs232_rx;4'd6: rx_temp_data[5] <= rs232_rx;4'd7: rx_temp_data[6] <= rs232_rx;4'd8: rx_temp_data[7] <= rs232_rx;default: ;endcaseled <= rx_temp_data;endelse if(num==4'd12) beginnum <= 4'd0; //数据接收完毕rx_data_r <= rx_temp_data;endendassign rx_data = rx_data_r;endmodule//=================================数据发送模块=========================================module uart_tx(clk,rst_n,bps_start,clk_bps,rs232_tx,rx_data,rx_int);input clk;input rst_n;input clk_bps;//中间采样点input [7:0] rx_data;//接收数据寄存器input rx_int;//数据接收中断信号output rs232_tx;//发送数据信号output bps_start;//发送信号置位reg rx_int0,rx_int1,rx_int2;//信号寄存器,捕捉下降沿 wire neg_rx_int; //下降沿标志always @(posedge clk or negedge rst_n) begin if(!rst_n) beginrx_int0 <= 1'b0;rx_int1 <= 1'b0;rx_int2 <= 1'b0;endelse beginrx_int0 <= rx_int;rx_int1 <= rx_int0;rx_int2 <= rx_int1;endendassign neg_rx_int = ~rx_int1 & rx_int2;//捕捉下沿reg [7:0] tx_data;//待发送数据reg bps_start_r;reg tx_en;//发送信号使能,高有效reg [3:0] num;always @(posedge clk or negedge rst_n) beginif(!rst_n) beginbps_start_r <= 1'bz;tx_en <= 1'b0;tx_data <= 8'd0;endelse if(neg_rx_int) begin//当检测到下沿的时候,数据开始传送 bps_start_r <= 1'b1;tx_data <= rx_data;tx_en <= 1'b1;endelse if(num==4'd11) beginbps_start_r <= 1'b0;tx_en <= 1'b0;endendassign bps_start = bps_start_r;reg rs232_tx_r;always @(posedge clk or negedge rst_n) begin if(!rst_n) beginnum<=4'd0;rs232_tx_r <= 1'b1;endelse if(tx_en) beginif(clk_bps) beginnum<=num+1'b1;case(num)4'd0: rs232_tx_r <= 1'b0;//起始位4'd1: rs232_tx_r <= tx_data[0];//数据位开始 4'd2: rs232_tx_r <= tx_data[1];4'd3: rs232_tx_r <= tx_data[2];4'd4: rs232_tx_r <= tx_data[3];4'd5: rs232_tx_r <= tx_data[4];4'd6: rs232_tx_r <= tx_data[5];4'd7: rs232_tx_r <= tx_data[6];4'd8: rs232_tx_r <= tx_data[7];4'd9: rs232_tx_r <= 1'b1;//数据结束位,1位 default: rs232_tx_r <= 1'b1;endcaseendelse if(num==4'd11)num<=4'd0;//发送完成,复位endendassign rs232_tx =rs232_tx_r;endmodule。