赞
踩
一、UART通信简介
通用异步收发器,
特点:串行、异步、全双工通信
优点:通信线路简单,传输距离远
缺点:传输速度慢
数据传输速率:波特率(单位:baud,波特)
常见的波特率为:1200、2400、4800、19200、38400、57600、115200
最常用的:9600、115200
数据通信格式:1个数据位+n个数据位+1个校验位+1个结束位
其中n个数据位:通常为8位,即1个字节
空闲位:当总线处于空闲状态时信号线的状态为1,表示当前线路没有进行数据传输。
起始位:每开始一次通信时发送方先发出一个逻辑0的信号,表示传输字符的开始。(因为总线空闲时为高电平,所以开始一次通信时先发送一个明显区别于空闲状态的信号,即低电平)
数据位:起始位之后就是我们所要传输的数据,数据位可以是5,6,7,8,9位等,构成一个字符。先发送最低位,最后发送最高位
奇偶校验位
数据位加上这一位后,使得1的位数应为偶数(偶校验)或奇数(奇校验)。
串口校验的几种方式:
停止位:
它是一个字符数据的结束标志。可以是1位、2位的高电平。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备之间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟的机会。停止位个数越多,数据传输越稳定,但是数据传输速度也越慢
传输时间:计数=时钟频率除以波特频率
二、Verilog实现:
1.UART_TX设计框图
信号 | 位宽 | 类型 | 功能描述 |
clk | 1bit | input | 工作时钟,频率50MHz |
tx_data | 8bit | input | 发送数据 |
tx_flag | 1bit | input | 发送数据的有效标志信号 |
tx | 1bit | output | 串口发送信号 |
Verilog代码:
uart_tx:
- module uart_tx
- #(
- parameter UART_BPS = 'd9600, //串口波特率
- parameter CLK_FREQ = 'd50_000_000 //时钟频率
-
- )
-
- (
- input wire clk, //系统时钟
- input wire rstn, //全局复位
- input wire [7:0] tx_data, //发送8bit数据
- input wire tx_flag, //发送数据有效标志信号
- output reg tx //串转并后的1bit数据
- );
-
- localparam cnt_max = CLK_FREQ / UART_BPS;
- reg [12:0] bd_cnt;
- reg bit_flag;
- reg [3:0] bit_cnt;
- reg work_en; //接收数据工作使能信号
-
- //work_en:接收数据工作使能信号
- always @(posedge clk or negedge rstn) begin
- if (rstn == 1'b0)
- work_en <= 1'b0;
- else if (tx_flag == 1'b1)
- work_en <= 1'b1;
- else if((bit_flag == 1'b1)&&(bit_cnt == 4'd9))
- work_en <= 1'b0;
- end
- //bd_cnt:波特率计数器计数,从0计数到5207
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- bd_cnt <= 13'b0;
- else if((bd_cnt == cnt_max - 1) ||(work_en == 1'b0))
- bd_cnt <= 13'b0;
- else if(work_en == 1'b1)
- bd_cnt <= bd_cnt + 1'b1;
- end
- //bit_flag : 当bd_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- bit_flag <= 1'b0;
- else if(bd_cnt == 13'd1)
- bit_flag <= 1'd1;
- else
- bit_flag <= 1'b0;
- end
-
- //bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零
- always@(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- bit_cnt <= 4'b0;
- else if((bit_flag == 1'b1)&&(bit_cnt == 4'd9))
- bit_cnt <= 4'b0;
- else if((bit_flag == 1'b1)&&(work_en == 1'b1))
- bit_cnt <= bit_cnt + 1'b1;
-
- end
-
- //tx:输出数据在满足uart协议(起始位为0,停止位为1)的情况下一位一位输出
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- tx <= 1'b1; //空闲状态为高电平
- else if(bit_flag == 1'b1)
- case(bit_cnt)
- 0: tx <= 1'b0 ;
- 1: tx <= tx_data[0];
- 2: tx <= tx_data[1];
- 3: tx <= tx_data[2];
- 4: tx <= tx_data[3];
- 5: tx <= tx_data[4];
- 6: tx <= tx_data[5];
- 7: tx <= tx_data[6];
- 8: tx <= tx_data[7];
- 9: tx <= 1'b1;
- default : tx <= 1'b1;
- endcase
- end
-
-
- endmodule
testbench:
- module tb_uart_tx();
-
- reg clk;
- reg rstn;
- reg [7:0] tx_data;
- reg tx_flag;
- wire tx;
-
- //初始化系统时钟,全局复位
- initial begin
- clk = 1'b1;
- rstn = 1'b0;
- #20;
- rstn <= 1'b1;
- end
- //模拟发送8次数据,分别为0~7
- initial begin
- tx_data <= 8'b0;
- tx_flag <= 1'b0;
- #200
- //发送数据0
- tx_data <= 8'd0;
- tx_flag <= 1'b1;
- #20
- tx_flag <= 1'b0;
- // 每发送1bit数据需要5208个时钟周期,一帧数据为10bit
- //所以需要数据延时(5208*20*10)后再产生下一个数据
- #(5208*20*10);
-
- //发送数据1
- tx_data <= 8'd1;
- tx_flag <= 1'b1;
- #20
- tx_flag <= 1'b0;
- #(5208*20*10);
- //发送数据2
- tx_data <= 8'd2;
- tx_flag <= 1'b1;
- #20
- tx_flag <= 1'b0;
- #(5208*20*10);
-
- //发送数据3
- tx_data <= 8'd3;
- tx_flag <= 1'b1;
- #20
- tx_flag <= 1'b0;
- #(5208*20*10);
- //发送数据4
- tx_data <= 8'd4;
- tx_flag <= 1'b1;
- #20
- tx_flag <= 1'b0;
- #(5208*20*10);
-
- //发送数据5
- tx_data <= 8'd5;
- tx_flag <= 1'b1;
- #20
- tx_flag <= 1'b0;
- #(5208*20*10);
- //发送数据6
- tx_data <= 8'd6;
- tx_flag <= 1'b1;
- #20
- tx_flag <= 1'b0;
- #(5208*20*10);
-
- //发送数据7
- tx_data <= 8'd7;
- tx_flag <= 1'b1;
- #20
- tx_flag <= 1'b0;
-
- end
- //clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
- always #10 clk = ~clk;
- uart_tx uart_tx_inst(
- .clk(clk),
- .rstn(rstn),
- .tx_data(tx_data),
- .tx_flag(tx_flag),
- .tx(tx)
- );
- endmodule
仿真截图:
接收模块
uart_rx的设计框图:
信号 | 位宽 | 类型 | 功能描述 |
clk |
1bit |
Input |
工作时钟,频率50MHz |
rstn |
1bit |
Input |
复位信号,低电平有效 |
rx |
1bit |
Input |
串口接收信号 |
rx_data |
8bit |
output |
串口接收后转成的8bit数据 |
rx_flag |
1bit |
output |
串口接收后转成的8bit数据有效标志 |
|
|
|
|
Verilog代码:
- module uart_rx
- #(
- parameter UART_BPS = 'd9600,
- parameter CLK_FREQ = 'd50_000_000
- )
-
- (
- input wire clk,
- input wire rstn,
- input wire rx, //串口接收数据
- output reg[7:0] rx_data, //串转并后的8bit数据
- output reg rx_flag //串转并后的数据有效标志信号
- );
-
- localparam cnt_max = CLK_FREQ / UART_BPS;
-
- reg rx_reg1;
- reg rx_reg2;
- reg rx_reg3;
-
- reg start_nedge;
- reg work_en;
- reg [12:0] bd_cnt;
- reg bit_flag;
- reg [3:0] bit_cnt;
- reg [7:0] data;
- reg flag;
-
- //插入两级寄存器进行数据同步,用来消除亚稳态
- //rx_reg1:第一级寄存器,寄存器空闲状态复位为1
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- rx_reg1 <= 1'b1;
- else
- rx_reg1 <= rx;
- end
-
- //rx_reg2:第二级寄存器,寄存器空闲状态复位为1
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- rx_reg2 <= 1'b1;
- else
- rx_reg2 <= rx_reg1;
- end
-
- //reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
- always @(posedge clk or rstn) begin
- if(rstn == 1'b0)
- rx_reg3 <= 1'b1;
- else
- rx_reg3 <= rx_reg2;
- end
-
- //start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- start_nedge <= 1'b0;
- else if((~rx_reg2) && (rx_reg3))
- start_nedge <= 1'b1;
- else start_nedge <= 1'b0;
- end
-
- //work_en:接收数据工作使能信号
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- work_en <= 1'b0;
- else if(start_nedge == 1'b1)
- work_en <= 1'b1;
- else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
- work_en <= 1'b0;
- end
- //bd_cnt:波特率计数器计数,从0计数到5207
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- bd_cnt <= 13'b0;
- else if((bd_cnt == cnt_max - 1)||(work_en == 1'b0))
- bd_cnt <= 13'b0;
- else if(work_en == 1'b1)
- bd_cnt <= bd_cnt + 1'b1;
- end
- //bit_flag : bd_cnt计数器计数到中间数时采样的数据最稳定
- //此时拉高一个标志信号表示数据可以被取走
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- bit_flag <= 1'b0;
- else if(bd_cnt == cnt_max/2 - 1)
- bit_flag <= 1'b1;
- else
- bit_flag <= 1'b0;
- end
- //bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)
- //都接收完成后计数器清零
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- bit_cnt <= 4'b0;
- else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
- bit_cnt <= 4'b0;
- else if(bit_flag == 1'b1)
- bit_cnt <= bit_cnt + 1'b1;
- end
-
- //data:输入数据进行移位
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- data <= 8'b0;
- else if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
- data <= {rx_reg3,data[7:1]};
- end
- //flag:输入数据移位完成时flag拉高一个时钟的高电平
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- flag <= 1'b0;
- else if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
- flag <= 1'b1;
- else
- flag <= 1'b0;
- end
- //rx_data:输出完整的8位有效数据
- always @(posedge clk or negedge rstn) begin
- if(rstn == 1'b0)
- rx_data <= 8'b0;
- else if(rx_flag == 1'b1)
- rx_data <= data;
- end
-
- //rx_flag:输出数据有效标志(比flag延后一个时钟周期,为了和rx_data同步)
- always @(posedge clk or rstn) begin
- if(rstn == 1'b0)
- rx_flag <= 1'b0;
- else
- rx_flag <= flag;
- end
-
- endmodule
tb_uart_rx:
- module tb_uart_rx();
-
- reg clk;
- reg rstn;
- reg rx;
- wire [7:0] rx_data;
- wire rx_flag;
-
- //初始化系统时钟,全局复位和输入信号
- initial begin
- clk = 1'b1;
- rstn <= 1'b0;
- rx <= 1'b1;
- #20
- rstn <= 1'b1;
- end
-
- //模拟发送8次数据,分别为0~7
- initial begin
- #200
- rx_bit(8'd0);
- rx_bit(8'd1);
- rx_bit(8'd2);
- rx_bit(8'd3);
- rx_bit(8'd4);
- rx_bit(8'd5);
- rx_bit(8'd6);
- rx_bit(8'd7);
- end
-
- always #10 clk = ~clk;
-
- //定义一个名为rx_bit的任务,每次发送的数据有10位
- //data的值分别为0~7由i的值传递进来
- //任务以task开头, 后面紧跟任务名,调用时使用
-
- task rx_bit(
- input [7:0] data
- );
- integer i;
- //用for循环产生一帧数据,for括号中最后执行的内容只能写i=i+1;
-
- for(i=0;i<10;i=i+1) begin
- case(i)
- 0: rx <= 1'b0;
- 1: rx <= data[0];
- 2: rx <= data[1];
- 3: rx <= data[2];
- 4: rx <= data[3];
- 5: rx <= data[4];
- 6: rx <= data[5];
- 7: rx <= data[6];
- 8: rx <= data[7];
- 9: rx <= 1'b1;
-
- endcase
- #(5208*20); // 每发送1位数据延时5208个时钟周期
- end
- endtask //任务以endtask结束
-
- uart_rx uart_rx_inst(
- .clk(clk),
- .rstn(rstn),
- .rx(rx),
- .rx_data(rx_data),
- .rx_flag(rx_flag)
- );
-
- endmodule
仿真截图
参考资料:
5. 串口rs232 — [野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开发板 文档
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。