串口收发实验笔记(USB转串口)

1. 异步串口通信协议

1.1 UART 收发传输时序时序图

         发送和接收波特率必须保持一致才能正确通信。波特率是指1秒最大传输的数据位数,包括起始位、数据位、校验位、结束位。假如通信波特率设定为9600,那么一个数据位的时间长度是1/9600秒。串口的波特率是指每秒收/发的bit数,此实验设为115200bit/s。因此要将50MHz的系统时钟进行适当的分频。如上图,每计到CYCLE个时钟脉冲就传输一位。因此CYCLE=50_000_000/115200。

#(
	parameter CLK_FRE = 50,      //clock frequency(Mhz)
	parameter BAUD_RATE = 115200 //serial baud rate
)

//计算符合波特率的时钟频率
localparam                       CYCLE = CLK_FRE * 1000000 / BAUD_RATE;//1比特时间

1.2 uart_rx.v

接收数据状态图:

         “S_IDLE”状态为空闲状态,上电后进入“S_IDLE”,如果信号“rx_pin”有下降沿,我们认为是串口的起始位,进入状态“S_START”,等一个BIT时间起始位结束后进入数据位接收状态“S_REC_BYTE”,本实验中数据位设计是8位,接收完成以后进入“S_STOP”状态,在“S_STOP”没有等待一个BIT周期,只等待了半个BIT时间,这是因为如果等待了一个周期,有可能会错过下一个数据的起始位判断,最后进入“S_DATA”状态,将接收到的数据送到其他模块。为了满足采样定理,在接受数据时每个数据都在波特率计数器的时间中点进行采样,以避免数据出错的情况:

reg                              rx_d0;            //delay 1 clock for rx_pin
reg                              rx_d1;            //delay 1 clock for rx_d0

assign rx_negedge = rx_d1 && ~rx_d0;//下降沿:如果为1,说明接收到低电平的开始位

always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)
	begin
		rx_d0 <= 1'b0;
		rx_d1 <= 1'b0;	
	end
	else
	begin
		rx_d0 <= rx_pin;
		rx_d1 <= rx_d0;
	end
end

//receive serial data bit data:接收串行数据
always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)
		rx_bits <= 8'd0;
	else if(state == S_REC_BYTE && cycle_cnt == CYCLE/2 - 1)//处于接收状态且正好半个CYCLE周期
		rx_bits[bit_cnt] <= rx_pin;                         //接收一位数据
	else
		rx_bits <= rx_bits; 
end

1.3 uart_tx.v

状态图:

         上电后进入“S_IDLE”空闲状态,如果有发送请求,进入发送起始位状态“S_START”,起始位发送完成后进入发送数据位状态“S_SEND_BYTE”,数据位发送完成后进入发送停止位状态“S_STOP”,停止位发送完成后又进入空闲状态。在数据发送模块中,从顶层模块写入的数据直接传递给寄存器‘tx_reg’,并通过‘tx_reg’寄存器模拟串口传输协议在状态机的条件转换下进行数据传送:

module uart_tx
#(
	parameter CLK_FRE = 50,      //clock frequency(Mhz)
	parameter BAUD_RATE = 115200 //serial baud rate
)
(
	input                        clk,              //clock input   
	input                        rst_n,            //asynchronous reset input, low active 
	input[7:0]                   tx_data,          //data to send        要发送的并行数据
	input                        tx_data_ready,    //send ready         告诉用户已经准备好可以发送数据了
	output reg                   tx_data_valid,    //data to be sent is valid 一个字节发送完成标志
	output                       tx_pin            //serial data output 发送一个串行数据输出
);
//calculates the clock cycle for baud rate 
localparam                       CYCLE = CLK_FRE * 1000000 / BAUD_RATE;//发送1位数据需要时间
//state machine code
localparam                       S_IDLE       = 1;
localparam                       S_START      = 2;//start bit
localparam                       S_SEND_BYTE  = 3;//data bits
localparam                       S_STOP       = 4;//stop bit
reg[2:0]                         state;
reg[2:0]                         next_state;
reg[15:0]                        cycle_cnt; //baud counter: cycle循环计数器(与波特率有关)
reg[2:0]                         bit_cnt;//bit counter
reg[7:0]                         tx_data_latch; //latch data to send: 暂存要发送的数据
reg                              tx_reg; //serial data output         暂存当前要发送的位

assign tx_pin = tx_reg;//按位发送数据

//发送数据状态机
always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)
		state <= S_IDLE;
	else
		state <= next_state;
end

always@(*)
begin
	case(state)
		S_IDLE:
			if(tx_data_ready == 1'b1)//接收到可以发送数据的信号
				next_state <= S_START;
			else
				next_state <= S_IDLE;
		S_START:
			if(cycle_cnt == CYCLE - 1)//one data cycle 
				next_state <= S_SEND_BYTE;
			else
				next_state <= S_START;
		S_SEND_BYTE:
			if(cycle_cnt == CYCLE - 1  && bit_cnt == 3'd7)//发送 8bit data
				next_state <= S_STOP;
			else
				next_state <= S_SEND_BYTE;
		S_STOP:
			if(cycle_cnt == CYCLE - 1)//1bit cycle
				next_state <= S_IDLE;
			else
				next_state <= S_STOP;
		default:
			next_state <= S_IDLE;
	endcase
end
//一个字节发送完成信号
always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)
		begin
			tx_data_valid <= 1'b0;
		end
	else if(state == S_IDLE)
		if(tx_data_ready == 1'b1)//应答可以发送
			tx_data_valid <= 1'b0;//请求发送
		else
			tx_data_valid <= 1'b1;//请求
	else if(state == S_STOP && cycle_cnt == CYCLE - 1)//一个字节发送结束
			tx_data_valid <= 1'b1;//给出发送有效信号
end

//把要发送的字节存入寄存器等待发送
always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)
		begin
			tx_data_latch <= 8'd0;
		end
	else if(state == S_IDLE && tx_data_ready == 1'b1)
			tx_data_latch <= tx_data;//锁存数据
		
end

//切换到下一个要发送的位
always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)
		begin
			bit_cnt <= 3'd0;
		end
	else if(state == S_SEND_BYTE)
		if(cycle_cnt == CYCLE - 1)     //一个CYCLE周期结束(即一个位发送完)
			bit_cnt <= bit_cnt + 3'd1;//发送比特数
		else
			bit_cnt <= bit_cnt;
	else
		bit_cnt <= 3'd0;
end

//波特率相关计数
always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)
		cycle_cnt <= 16'd0;
	else if((state == S_SEND_BYTE && cycle_cnt == CYCLE - 1) || next_state != state)
		cycle_cnt <= 16'd0;//计完一个CYCLE周期或者状态改变则计数复位
	else
		cycle_cnt <= cycle_cnt + 16'd1;	//1bit时间计数
end
//发送串行比特数据(给出当前要发送的位数据)
always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)
		tx_reg <= 1'b1;
	else
		case(state)
			S_IDLE,S_STOP:
				tx_reg <= 1'b1;  //发送高电平结束位
			S_START:
				tx_reg <= 1'b0;  //发送低电平起始位
			S_SEND_BYTE:
				tx_reg <= tx_data_latch[bit_cnt];
			default:
				tx_reg <= 1'b1; 
		endcase
end

endmodule 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A u g

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值