UART的回环实例

举报
李锐博恩 发表于 2021/07/15 04:19:39 2021/07/15
1.8k+ 0 0
【摘要】 根据代码综合出来的RTL电路图可以当成原理框图来看: 程序模块分为顶层Uart_top、发送模块uart_tx、接收模块uart_rx以及时钟产生模块clk_div。uart_rx将收到的包解析出8位数据,再传送给uart_tx发出,形成回环。参考时钟为100Mhz,波特率为9600bps。例子使用最简单的串口设置,没有校验位。 各模块程序如下: 顶层: `tim...

根据代码综合出来的RTL电路图可以当成原理框图来看:

程序模块分为顶层Uart_top、发送模块uart_tx、接收模块uart_rx以及时钟产生模块clk_div。uart_rx将收到的包解析出8位数据,再传送给uart_tx发出,形成回环。参考时钟为100Mhz,波特率为9600bps。例子使用最简单的串口设置,没有校验位。

各模块程序如下:

顶层:


      `timescale 1ns / 1ps
      //
      // Company: 
      // Engineer: 
      // 
      // Create Date: 20:44:47 03/04/2019 
      // Design Name: 
      // Module Name: Uart 
      //
      //
      module Uart_top(
       output txd,
       input rxd,
       input clk
       );
      	wire clk_9600;
      	wire receive_ack;
      	wire [7:0] data;
     	//串口发送模块
     	uart_tx uart_tx_u1(
       .clk(clk_9600),
       .txd(txd),
       .rst(1),
       .data_o(data),
       .receive_ack(receive_ack)
       );
     	//串口接收模块
     	uart_rx uart_rx_u1(
       .rxd(rxd),
       .clk(clk_9600),
       .receive_ack(receive_ack),
       .data_i(data)
       );
     	//时钟模块
     	clk_div clk_div_u1(
       .clk(clk),
       .clk_out(clk_9600)
       );
      endmodule
  
 

接收模块:


      `timescale 1ns / 1ps
      //
      // Company: 
      // Engineer: 
      // 
      // Create Date: 20:52:35 03/04/2019 
      // Design Name: 
      // Module Name: uart_rx 
      // Project Name: 
      //
      module uart_rx(
      	input rxd,
      	input clk,
      	output receive_ack,
      	output reg [7:0] data_i
       );
     	//串口接收状态机分为三个状态:等待,接收,接收完成
      	localparam IDLE = 0,
      RECEIVE = 1,
      RECEIVE_END = 2;
      	reg [3:0] cur_st,nxt_st; //状态机变量
      	reg [4:0] count;
      	always@(posedge clk) begin
      		cur_st <= nxt_st;
      	end
      	always@( * ) begin
      		nxt_st = cur_st;
     		case(cur_st)
     			IDLE: if(!rxd) nxt_st = RECEIVE;  //接收到开始信号,开始接收数据
     			RECEIVE: if(count == 7) nxt_st = RECEIVE_END; //八位数据接收计数
     			RECEIVE_END: nxt_st = IDLE; //接收完成
     			default: nxt_st = IDLE;
      		endcase
      	end
      	always@(posedge clk) begin //接收数据计数
     		if(cur_st == RECEIVE) count <= count + 1;
     		else if(cur_st == IDLE|cur_st == RECEIVE_END)
     			count <= 0;
      	end
      	always@(posedge clk) begin //从高到底发送数据
     	if(cur_st == RECEIVE) begin
      		data_i[6:0] <= data_i[7:1];
      		data_i[7] <= rxd;
      	end
      	end
      	assign receive_ack = (cur_st == RECEIVE_END)?1:0; //接收完成时回复信号 1,否则 0
      endmodule
  
 

发送模块:


      `timescale 1ns / 1ps
      //
      // Company: 
      // Engineer: 
      // 
      // Create Date: 20:52:52 03/04/2019 
      // Design Name: 
      // Module Name: uart_tx 
      // Project Name: 
      //
      module uart_tx(
     	input [7:0] data_o,
     	output reg txd,
     	input clk,
     	input rst,
     	input receive_ack
      );
     	//发送状态机分为四个状态:等待、发送起始位、发送数据、发送结束
     	localparam IDLE = 0,
      SEND_START = 1,
      SEND_DATA = 2,
      SEND_END = 3;
     	reg [3:0] cur_st,nxt_st;
     	reg [4:0] count;
     	reg [7:0] data_o_tmp;
     	always@(posedge clk)
     		cur_st <= nxt_st;
     	always@(*) begin
     		nxt_st = cur_st;
     		case(cur_st)
     			IDLE: if(receive_ack == 1) nxt_st = SEND_START; //接收完成时开始发送数据
     			SEND_START: nxt_st = SEND_DATA; //发送起始位
     			SEND_DATA: if(count == 7) nxt_st = SEND_END; //发送八位数据
     			SEND_END: if(receive_ack == 1) nxt_st = SEND_START; //发送结束
     			default: nxt_st = IDLE;
     		endcase
     	end
     	always@(posedge clk)
     		if(cur_st == SEND_DATA)
     			count <= count + 1;
     		else if(cur_st == IDLE|cur_st == SEND_END)
     			count <= 0;
     	always@(posedge clk) //发送低位到高位
     		if(cur_st == SEND_START)
     			data_o_tmp <= data_o; //将发送数据导入变量
     		else if(cur_st == SEND_DATA)
     			data_o_tmp[6:0] <= data_o_tmp[7:1]; //每发送一位数据后将data_o_tmp右移一位,便于下一个数据的发送
     	always@(posedge clk)
     	if(cur_st == SEND_START)
     		txd <= 0;
     	else if(cur_st == SEND_DATA)
     		txd <= data_o_tmp[0]; //由于每次发送后右移,所以每次发送最低位
     	else if(cur_st == SEND_END)
     		txd <= 1;
      endmodule
  
 

时钟分频模块:


      `timescale 1ns / 1ps
      //
      // Company: 
      // Engineer: 
      // 
      // Create Date: 20:53:08 03/04/2019 
      // Design Name: 
      // Module Name: clk_div 
      // Project Name: 
      //
      module clk_div(
      	input clk,
      	output reg clk_out
       );
      	localparam Baud_Rate = 9600;
      	localparam Div_Num = 'd100_000_000/Baud_Rate; //分频数为时钟速率除以波特率
      	reg [15:0] num;
      	always@(posedge clk)
     	if(num == Div_Num) begin
      		num <= 0;
      		clk_out <= 1;
      	end
     	else begin
      		num <= num + 1;
      		clk_out <= 0;
      	end
      endmodule
  
 

有关串口协议的东西还是第一次涉猎,还有些没有弄懂,例如系统时钟和波特率时钟之间的关系?这里记录再此,回头再看吧。

这种忙成狗子的日子。


更新:2019/05/27

系统时钟和波特率之间的关系是一种分频关系,具体的实现方法见博文:

RS232 波特率时钟产生方法?

将我最新产生的波特率时钟的参数化方法记录于此:


      module BaudGen #(
     	parameter Clkfrequency = 25000_000,
     	parameter Baud = 115200,
     	parameter Oversampling = 8
      )
      (
     	input clk,
     	input enable,
     	output BaudTick
      );
     	parameter Ratio = Clkfrequency/(Baud*Oversampling);
     	parameter AddWidth = 16;
     	parameter Inc = (1<<AddWidth)/Ratio;
     	reg [AddWidth:0] acc = 0;
     	always@(posedge clk) begin
     		if(enable) begin
     			acc <= acc[AddWidth - 1 : 0] + Inc;
     		end
     		else begin
     			acc <= Inc;
     		end
     	end
     	assign BaudTick = acc[AddWidth];
      endmodule
  
 

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/88219653

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。