串口RS232
- 1. 串口简介
- 2. 串口RS232接口
- 3. 代码实现
- UART接收模块
- UART发送模块
- 顶层模块
- 4. 下板验证
1. 串口简介
通用异步收发传输器,英文全称Universal Asynchronous Receiver/Transmitter,简称UART
UART
UART是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的数据转换成并行数据
UART是异步串行通信口,SPI是同步的,双方约定好使用频率一致的时钟,主机发送时钟
UART中,数据的收发都有独立的端口,所以它可以实现全双工通信
RS232传输距离比较近,传输速率也比较慢,数据线只有两条,可以节省IO口
2. 串口RS232接口
是UART串口的一种,没有时钟线只有两个数据线
注意:两个设备间的TXD和RXD应交叉相连
串口数据的发送和接收都是基于帧结构的,起始位+数据位+停止位,共10bit,空闲状态下rx和tx都保持高电平
波特率:来波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,其单位为波特(Baud),Bps
比特率:比特率是每秒传输的比特数。单位为比特(bps位/秒)
波特率源与比特率的关系为:比特率=波特率 * 单个调制状态对应的二进制位数
串口常用波特率有4800、9600、115200
9600Bps的串口比特率就是9600*1bps,串口发送或接收1bit数据的时间称为1个波特,也就是1/9600s。clk = 50MHz,T = 20ns,9600Bps下,传输一个波特需要的时钟个数,cnt = (1 * 10^9)ns / 9600 / 20ns 约等于5208,相当于每个bit的传输间隔需要50MHz时钟下的5208个clk
3. 代码实现
实现50MHz下9600波特率传输
波特率为9600Baud
一秒传输9600个波特
每个波特10bit数据,1bit起始位 + 8bit数据位 + 1bit结束位
每个码元传输需要的clk数 = ((1/9600)*10^9ns) / 20ns≈ 5208
串口传输,每个码元也就是一个bit,也可以理解为传递每个bit需要的clk数
UART接收模块
将上位机发送过来的串行数据进行接收,转换成并行数据
module uart_rx
#(parameter UART_BPS = 'd9600 ,parameter CLK_FREQ = 'd50_000_000
)
(input wire sys_clk ,input wire sys_rst_n ,input wire rx ,output reg [ 7: 0] po_data ,output reg po_flag
);// parameter BAUD_CNT_MAX = 5208; // 9600Bps (1/9600s)/(1/50M) = F/Bps = 50M / 9600localparam BAUD_CNT_MAX = CLK_FREQ / UART_BPS;reg rx_reg1 ; // 同步到系统时钟下reg rx_reg2 ;reg rx_reg3 ; // 打两拍,减小亚稳态危害reg start_flag ; // 开始一个数据帧的传输标志信号reg work_en ; // 标识数据采集范围,接收数据工作使能信号reg [15: 0] baud_cnt ; // 计数1个码元需要的clk,0~8207reg bit_flag ; // 每bit数据稳定点拉高一拍reg [ 3: 0] bit_cnt ; // 10bit数据计数器reg [ 7: 0] rx_data ;reg rx_flag ;always @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)beginrx_reg1 <= 1'b1; // 空闲状态为高rx_reg2 <= 1'b1;rx_reg3 <= 1'b1;endelsebeginrx_reg1 <= rx;rx_reg2 <= rx_reg1;rx_reg3 <= rx_reg2;end// start_flag:检测下降沿always @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)start_flag <= 1'b0;else if (~rx_reg2 && rx_reg3 && work_en == 1'b0) // 检测下降沿,并且现在不在数据采集范围内start_flag <= 1'b1;elsestart_flag <= 1'b0;// work_enalways @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)work_en <= 1'b0;else if (start_flag == 1'b1)work_en <= 1'b1;else if (bit_cnt == 4'd8 && bit_flag == 1'b1)work_en <= 1'b0;elsework_en <= work_en;// baud_cntalways @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)baud_cnt <= 16'd0;else if ((baud_cnt == BAUD_CNT_MAX - 1'b1) ||(work_en == 1'b0) )baud_cnt <= 160'd0;elsebaud_cnt <= baud_cnt + 1'b1; // bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定,拉高一个标志信号表示数据可以被采样always @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)bit_flag <= 1'b0;else if (baud_cnt == BAUD_CNT_MAX / 2 - 1'b1) // 数据稳定的最中间位置bit_flag <= 1'b1;elsebit_flag <= 1'b0;// bit_cntalways @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)bit_cnt <= 4'd0;else if ((bit_cnt == 4'd8) && (bit_flag == 1'b1))bit_cnt <= 4'd0;else if (bit_flag == 1'b1)bit_cnt <= bit_cnt + 1'b1;elsebit_cnt <= bit_cnt;// rx_dataalways @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)rx_data <= 8'b0;else if (((bit_cnt >= 4'd1) && (bit_cnt <= 4'd8)) && (bit_flag == 1'b1))rx_data <= {rx_reg3, rx_data[7:1]};elserx_data <= rx_data;// rx_flagalways @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)rx_flag <= 1'b0;else if ((bit_flag == 1'b1) && (bit_cnt == 4'd8))rx_flag <= 1'b1;elserx_flag <= 1'b0;// po_dataalways @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)po_data <= 8'b0;else if (rx_flag == 1'b1)po_data <= rx_data;// po_flagalways @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)po_flag <= 1'b0;elsepo_flag <= rx_flag;endmodule
`timescale 1ns/1nsmodule uart_rx_tb ();reg sys_clk ;reg sys_rst_n ;reg rx ;wire [ 7: 0] po_data ;wire po_flag ;uart_rx#(.UART_BPS ('d9600 ),.CLK_FREQ ('d50_000_000 ))uart_rx_inst(.sys_clk (sys_clk ),.sys_rst_n (sys_rst_n ),.rx (rx ),.po_data (po_data ),.po_flag (po_flag ));always # 10 sys_clk = ~sys_clk;initial beginsys_clk = 1;sys_rst_n = 0;rx = 1;#10sys_rst_n = 1;end// 模拟发送数据帧initial begin#200rx_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);rx_bit(8'b1100_1010);end// 任务函数
task rx_bit ;input [ 7: 0] data;integer i;beginfor (i = 0; i < 10 ; i = i + 1) begin // 这个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);endend
endtaskendmodule
UART发送模块
将FPGA内部的数据以固定的波特率,并转串,包装成数据帧,发送给上位机
module uart_tx
#(parameter UART_BPS = 'd9600 ,parameter CLK_FREQ = 'd50_000_000
)
(input wire sys_clk ,input wire sys_rst_n ,input wire [ 7: 0] pi_data ,input wire pi_flag ,output reg tx
);localparam BAUD_CNT_MAX = CLK_FREQ / UART_BPS;reg work_en ;reg [15: 0] baud_cnt ;reg bit_flag ;reg [ 3: 0] bit_cnt ;// work_enalways @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)work_en <= 1'b0;else if ((bit_cnt == 4'd9) && (bit_flag == 1'b1))work_en <= 1'b0;else if (pi_flag == 1'b1)work_en <= 1'b1;elsework_en <= work_en;// baud_cntalways @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0) baud_cnt <= 16'd0;else if ((work_en == 1'b0) || (baud_cnt == BAUD_CNT_MAX - 1'b1))baud_cnt <= 16'd0;else if (work_en == 1'b1)baud_cnt <= baud_cnt + 1'b1;// bit_flagalways @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)bit_flag <= 1'b0;else if (baud_cnt == 16'd1) // 每一个波特计数周期,拉高一个bit_flag周期,相邻脉冲间隔为一个波特bit_flag <= 1'b1;elsebit_flag <= 1'b0;// bit_cntalways @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)bit_cnt <= 4'd0;else if ((bit_cnt == 4'd9) && (bit_flag == 1'b1))bit_cnt <= 4'd0;else if ((work_en == 1'b1) && (bit_flag == 1'b1))bit_cnt <= bit_cnt + 1'b1;elsebit_cnt <= bit_cnt;// txalways @ (posedge sys_clk or negedge sys_rst_n)if (sys_rst_n == 1'b0)tx <= 1'b1;else if (bit_flag == 1'b1)case (bit_cnt)0: tx <= 1'b0;1: tx <= pi_data[0];2: tx <= pi_data[1];3: tx <= pi_data[2];4: tx <= pi_data[3];5: tx <= pi_data[4];6: tx <= pi_data[5];7: tx <= pi_data[6];8: tx <= pi_data[7];9: tx <= 1'b1;default: tx <= 1'b1;endcaseendmodule
顶层模块
模拟接收串行数据8’b1100_1010,随后逐比特发送