Verilog学习笔记(串口RS232,基于野火教程)

news2024/12/27 13:14:16

目录

一、串口简介  

 二、设计与实现

串口数据回环顶层模块设计

串口接收模块uart_rx

串口发送模块uart_tx

顶层模块rs32_top

三、上板验证  


一、串口简介  

其中SPI和I2C为同步通信接口,双方时钟频率相同。而UART属于异步通信接口,没有统一时钟,靠起始位和终止位来接收数据。

上图为 串口的通信方式,可以同时收发(全双工通信)。其中rx负责接收,tx负责发送,每次发送10bit数据(起始位+8bit数据+停止位),从最低位开始发送。

 

波特率为每秒钟传输的码元数量,单位为Bps。而比特率为每秒传输的bit个数,单位为bps。比特率=波特率x单个调制状态对应的二进制数。在串口中比特率=比特率x1。

 

 二、设计与实现

亚稳态,与建立时间和保持时间有关(可以参考这篇博客数字电路中的亚稳态产生原因和处理方法_IamSarah的博客-CSDN博客):

可以使用多级寄存器来减小亚稳态的危害(多延迟几拍)。

串口传输的波特率为9600,系统的时钟频率为50MHz,那么可以知道传输一位的时间为5208个时钟周期: 

 

串口数据回环顶层模块设计

串口接收模块uart_rx

接收模块时序图设计:

接口模块verilog代码:

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 = CLK_FREQ / UART_BPS;

//将rx信号延迟三拍,可以减少亚稳态的影响
reg          rx_reg1;
reg          rx_reg2;
reg          rx_reg3;

reg          start_flag; //起始位开始
reg          work_en; //计数使能信号
reg  [15:0]  baud_cnt; //计数器,每个码元到来的间隔时间
reg          bit_flag;
reg  [3:0]   bit_cnt;
reg  [7:0]   rx_data;
reg          rx_flag;

//rx信号延迟三拍,减少亚稳态的影响
always@(posedge sys_clk) begin
    rx_reg1 <= rx;
    rx_reg2 <= rx_reg1;
    rx_reg3 <= rx_reg2;
end

//接收数据开始信号start_flag
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        start_flag <= 1'b0;
    end
    else if((rx_reg3 == 1'b1) && (rx_reg2 == 1'b0) && (work_en == 1'b0)) begin
        start_flag <= 1'b1;
    end
    else begin
        start_flag <= 1'b0;
    end
end

//使能信号work_en
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        work_en <= 1'b0;
    end
    else if(start_flag == 1'b1) begin
        work_en <= 1'b1;
    end
    else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))begin
        work_en <= 1'b0;
    end
    else begin
        work_en <= work_en;
    end
end

//计数
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        baud_cnt <= 16'd0;
    end
    else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0)) begin
        baud_cnt <= 16'd0;
    end
    else begin
        baud_cnt <= baud_cnt + 1'b1;
    end
end

//让bit_flag在中间时刻拉高,中间时刻数据更稳定
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        bit_flag <= 1'b0;
    end
    else if(baud_cnt == BAUD_CNT_MAX / 2 - 1) begin
        bit_flag <= 1'b1;
    end
    else begin
        bit_flag <= 1'b0;
    end
end

//bit计数器,计算收到的bit数目
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        bit_cnt <= 4'd0;
    end
    else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) begin
        bit_cnt <= 4'd0;
    end
    else if(bit_flag == 1'b1) begin
        bit_cnt <= bit_cnt + 1'b1;
    end
    else begin
        bit_cnt <= bit_cnt;
    end
end

//rx_data
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        rx_data <= 8'd0;
    end
    else if((bit_flag == 1'b1) && (bit_cnt != 4'd0)) begin
        rx_data <= {rx_reg3, rx_data[7:1]};
    end
    else begin
        rx_data <= rx_data;
    end
end

//rx_flag
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        rx_flag <= 1'b0;
    end
    else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) begin
        rx_flag <= 1'b1;
    end
    else begin
        rx_flag <= 1'b0;
    end
end

always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        po_data <= 8'd0;
        po_flag <= 1'b0;
    end
    else if(rx_flag == 1'b1)begin
        po_data <= rx_data;
        po_flag <= 1'b1;
    end
    else begin
        po_flag <= 1'b0;
    end
end

endmodule

仿真testbench代码:

`timescale 1ns/1ns
module tb_uart_rx();

reg sys_clk;
reg sys_rst_n;
reg rx;
wire [7:0] po_data;
wire       po_flag;

always #10 sys_clk = ~sys_clk;

task rx_bit(
    input   [7:0]   data
);
    integer i;
    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);
    end
endtask

initial begin
    sys_clk = 1'b1;
    sys_rst_n <= 1'b0;
    rx <= 1'b1;
    #20
    sys_rst_n <= 1'b1;
    
end

initial begin
    #200
    rx_bit(8'd0);
    rx = 1'b1;
    #200
    rx_bit(8'd1);
    rx = 1'b1;
    #200
    rx_bit(8'd2);
    rx = 1'b1;
    #200
    rx_bit(8'd3);
    rx = 1'b1;
    #200
    rx_bit(8'd4);
    rx = 1'b1;

end

uart_rx
#(
    .UART_BPS('d9600)         , //波特率
    .CLK_FREQ('d50_000_000)     //系统时钟频率缩小100倍
)
uart_rx_inst
(
    .sys_clk        (sys_clk)       ,
    .sys_rst_n      (sys_rst_n)     ,
    .rx             (rx)            ,
    .po_data        (po_data)       ,
    .po_flag        (po_flag)
);

endmodule

仿真结果:

串口发送模块uart_tx

 

发送模块时序图设计:

 模块verilog代码:

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
);

parameter BAUD_CNT_MAX = CLK_FREQ / UART_BPS;

reg          work_en; //计数使能信号
reg  [15:0]  baud_cnt; //计数器,每个码元到来的间隔时间
reg          bit_flag;
reg  [3:0]   bit_cnt;
reg  [7:0]   pi_data_reg;

always@(posedge sys_clk) begin
    if(pi_flag == 1'b1) begin
        pi_data_reg <= pi_data;
    end
    else if(bit_cnt != 4'd0 && bit_cnt != 4'd9 && bit_flag == 1'b1) begin
        pi_data_reg[6:0] <= pi_data_reg[7:1];
    end
    else begin
        pi_data_reg <= pi_data_reg;
    end
end

//work_en信号控制
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        work_en <= 1'b0;
    end
    else if(pi_flag == 1'b1) begin
        work_en <= 1'b1;
    end
    else if(work_en == 1'b1 && bit_flag == 1'b1 && bit_cnt == 4'd9) begin
        work_en <= 1'b0;
    end
    else begin
        work_en <= work_en;
    end
end

//baud_cnt计数器逻辑
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        baud_cnt <= 16'd0;
    end
    else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0)) begin
        baud_cnt <= 16'd0;
    end
    else begin
        baud_cnt <= baud_cnt + 1'b1;
    end
end

//bit_flag信号
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        bit_flag <= 1'b0;
    end
    else if(work_en == 1'b1 && baud_cnt == 16'd0) begin
        bit_flag <= 1'b1;
    end
    else begin
        bit_flag <= 1'b0;
    end
end

//bit_cnt信号
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        bit_cnt <= 4'd0;
    end
    else if(bit_cnt == 4'd9 && bit_flag == 1'b1) begin
        bit_cnt <= 4'd0;
    end
    else if(bit_flag == 1'b1)begin
        bit_cnt <= bit_cnt + 1'b1;
    end
    else begin
        bit_cnt <= bit_cnt;
    end
end

//tx信号
always@(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        tx <= 1'b1; //空闲状态下为1
    end
    else if(bit_cnt == 4'd0 && bit_flag == 1'b1) begin
        tx <= 1'b0;
    end
    else if(bit_cnt != 4'd0 && bit_cnt != 4'd9 && bit_flag == 1'b1)begin
        tx <= pi_data_reg[0:0];
    end
    else if(bit_cnt == 4'd9 && bit_flag == 1'b1) begin
        tx <= 1'b1;
    end
    else begin
        tx <= tx;
    end
end

endmodule

仿真代码:

`timescale 1ns/1ns
module tb_uart_tx();

reg sys_clk;
reg sys_rst_n;
wire tx;
reg [7:0] pi_data;
reg       pi_flag;

always #10 sys_clk = ~sys_clk;


initial begin
    sys_clk = 1'b1;
    sys_rst_n <= 1'b0;
    #20
    sys_rst_n <= 1'b1;
    
end

initial begin
    #2000
    pi_data <= 8'b1010_1010;
    pi_flag <= 1'b1;
    #20
    pi_flag <= 1'b0;
end

uart_tx
#(
    .UART_BPS('d9600)         , //波特率
    .CLK_FREQ('d50_000_000)     //系统时钟频率缩小100倍
)
uart_tx_inst
(
    .sys_clk        (sys_clk)       ,
    .sys_rst_n      (sys_rst_n)     ,
    .pi_data        (pi_data)       ,
    .pi_flag        (pi_flag)       ,
    .tx             (tx)
);

endmodule

 仿真结果:

顶层模块rs32_top

模块verilog代码:

module rs232_top(
    input   wire            sys_clk     ,
    input   wire            sys_rst_n   ,
    input   wire            rx          ,
    output  wire            tx
);

wire [7:0] data;
wire data_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(data)     ,
    .po_flag(data_flag)
);

uart_tx
#(
    .UART_BPS('d9600)        , //波特率
    .CLK_FREQ('d50_000_000)     //系统时钟频率
) 
uart_tx_inst
(
    .sys_clk(sys_clk)     ,
    .sys_rst_n(sys_rst_n)   ,
    .pi_data(data)     ,
    .pi_flag(data_flag)     ,
    .tx(tx)
);

endmodule

testbench代码:

module tb_rs232();

reg sys_clk;
reg sys_rst_n;
reg rx;
wire tx;

always #10 sys_clk = ~sys_clk;

task rx_bit(
    input   [7:0]   data
);
    integer i;
    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);
    end
endtask

initial begin
    sys_clk = 1'b1;
    sys_rst_n <= 1'b0;
    #20
    sys_rst_n <= 1'b1;
    rx <= 1'b1;
end

initial begin
    #200
    rx_bit(8'd0);
    rx = 1'b1;
    #200
    rx_bit(8'd1);
    rx = 1'b1;
    #200
    rx_bit(8'd2);
    rx = 1'b1;
    #200
    rx_bit(8'd3);
    rx = 1'b1;
    #200
    rx_bit(8'd4);
    rx = 1'b1;
end

rs232_top rs232_top_inst(
    .sys_clk(sys_clk)     ,
    .sys_rst_n(sys_rst_n)   ,
    .rx(rx)          ,
    .tx(tx)
);
endmodule

仿真结果: 

三、上板验证  

首先要绑定管脚:

我们使用的是usb转串口,引脚分配按下图所示:

原理图上N6所在位置:

E1所在位置:

 M15所在位置:

N5所在位置:

烧写程序:

 点击start即可下载程序:

板子连线如下,要注意的地方就是下图红框内的两个线帽要接正确:

 下载完成后打开野火串口调试助手,按照下图配置: 

打开串口后在下面窗口输入要发送的数据,点击发送后可以看到上面收到的数据无误: 

至此,rs232串口的verilog实现及上板验证结束!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/594968.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

旅游网项目(SpringBoot2.7.1 + SpringMVC + Mybatis-Plus3.5.0)

技术选型 JAVA版本&#xff1a;JDK17 数据库&#xff1a;Mysql5.7Navicat 后端框架&#xff1a;SpringBoot3.0.6 SpringMVC Mybatis-Plus3.5.0 权限控制&#xff1a;SpringSecurity 前端框架&#xff1a;AdminLTE2 模板引擎&#xff1a;Thymeleaf 工具类&#xff1a;发…

WIN10系统解决IDEA动不动就卡顿一下

1、前言 不知为啥&#xff0c;最近idea一直在卡顿&#xff0c;输入几个代码都会卡两秒&#xff0c;鼠标滚动文件卡两秒&#xff0c;点击打开文件卡两秒&#xff0c;就算是点击上方的工具栏&#xff0c;它也要等两秒才会出来菜单&#xff01; 卡顿的时候整个idea直接无响应&a…

Java性能权威指南-总结3

Java性能权威指南-总结3 性能测试方法原则4:尽早频繁测试小结 Java性能调优工具箱操作系统的工具和分析CPU使用率 性能测试方法 原则4:尽早频繁测试 这是最后的原则。性能测试应该作为开发周期不可或缺的一部分。理想情况下&#xff0c;在代码提交到中心源代码仓库前&#xf…

【数据分享】1929-2022年全球站点的逐月最高气温(Shp\Excel\12000个站点)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;其中又以气温指标最为常用&#xff01;说到气温数据&#xff0c;最详细的气温数据是具体到气象监测站点的气温数据&#xff01; 之前我们分享过1929-2022年全球气象站…

Win10搭建Nacos2.2.3集群版

Nacos是Alibaba提供的服务注册发现的管理平台&#xff0c;其优异的性能越来越受到广大开发者的喜爱&#xff0c;在构建分布式微服务项目中通常会首选Nacos作为注册/配置中心&#xff0c;在实际开发中为了提升服务的可用性和稳定性&#xff0c;通常都会搭建集群版&#xff0c;有…

《强风吹拂》呐!你喜欢跑步吗?

《强风吹拂》呐&#xff01;你喜欢跑步吗&#xff1f; 三浦紫苑&#xff0c;1976生于东京。主要作品有《多田便利屋》《强风吹拂》《哪啊哪啊~神去村》《编舟记》等 林佩瑾、李建铨、杨正敏 译 文章目录 《强风吹拂》呐&#xff01;你喜欢跑步吗&#xff1f;[toc]动漫摘录箱根驿…

Go Web下gin框架使用(一)

〇、前言 在前面&#xff0c;已经在这篇文章中详细地讨论了 gin 框架下的模板渲染问题&#xff0c;这篇文章主要对 gin 框架的使用进行讨论。 一、不同的路由 以下可以选择不同的路由进行渲染&#xff1a; r : gin.Default()type usr struct {Name string json:"name&…

八、go语言键盘输入和打印输出

键盘输入和打印输出 一、打印输出 1.1 fmt包 fmt包实现了类似C语言printf和scanf的格式化I/O。格式化verb&#xff08;‘verb’&#xff09;源自C语言但更简单。 详见官网fmt的API&#xff1a;https://golang.google.cn/pkg/fmt/ 1.2 导入包 import "fmt"1.3 常…

MyBatis——MyBatis项目搭建

但凡是框架&#xff0c;使用都是分三步走 1.导入jar文件&#xff0c;用maven导入 2.处理配置文件 3.开发业务代码 1.创建maven项目导入相关依赖 在pom文件中导入MyBatis相关依赖jar文件 安装lombok 在File->Settings Pugins 中安装lombok 要想启动lombok的话还需要在B…

GPT带你飞:Chat GPT吊打面试官,实时获取答案,分享调用OpenAI API key+完整源码脚本哦!

目录 福利&#xff1a;文末纯分享中文版CHAT GPT镜像&#xff0c;不存在魔法&#xff0c;纯分享免费使用 故事发生了 火爆GitHub 所以大家注意 网友看了之后调侃到&#xff0c;为了防止线上面试作弊&#xff0c;以后只好把面试都改成线下了。 如何安装 既然是调用GPT的AP…

nodejs基于vue的汽车订票客运站售票网站

使用Mysql创建数据表保存本系统产生的数据。系统可以提供信息显示和相应服务&#xff0c;其管理员负责审核会员充值&#xff0c;审核客户购票信息以及会员购票信息&#xff0c;管理客运班次与留言板&#xff0c;管理会员等级。客户查看客运班次&#xff0c;购买并支付车票&…

【Selenium】常用的Selenium基础使用模板和简单封装

前言 近来又用上了 Selneium &#xff0c;因为反复用到&#xff0c;所以在这里将一些常用的方法封装起来&#xff0c;方便后续的使用。 在这篇文章中&#xff0c;我们将探讨 Selenium 的基础模板和基础封装&#xff0c;以便更好地理解 Selenium 的使用方法。 在Selenium的使用…

python基础----03-----if语句、while、for循环、range语句、continue和break

一 布尔类型和比较运算符 1.1 布尔类型和比较运算符 定义变量存储布尔类型数据&#xff1a;变量名称 布尔类型字面量。 布尔类型不仅可以自行定义同时也可以通过计算的来。也就是使用比较运算符进行比较运算得到布尔类型的结果。在C/C中&#xff0c;比较运算符称之为关系运算…

如何解决航空企业数字化转型中的痛点?

数字化时代&#xff0c;越来越多的企业开始关注数字技术&#xff0c;希望通过数字化改造提高企业效率和竞争力&#xff0c;为企业创造更多的商机和利润。今天就来同大家探讨航空领域&#xff0c;小程序在企业数字化转型中发挥的作用、 航空业员工端App的敏捷转型挑战 技术上的…

Java 异常机制:是Java 提供的一种识别及响应错误的一致性机制。

。 目录 友情提醒第一章、异常概述1.1&#xff09;我们常说的异常是什么1.2&#xff09;异常的作用1.3&#xff09;Java异常体系和分类1.4&#xff09;演示异常的产生 第二章、定义异常与抛出异常&#xff1a;throw2.1&#xff09;自定义异常类&#xff1a;继承Exception或Run…

网络io与io多路复用select/poll/epoll

一、网络IO请求 网络I/O请求是指在计算机网络中&#xff0c;向其他主机或服务器发送请求或接收响应的操作。这些请求可以包括获取网页、下载文件、发送电子邮件等。网络I/O请求需要使用合适的协议和通信方式来进行数据传输&#xff0c;例如HTTP、FTP、SMTP等。 要完成一个完整…

字节面试过了,薪资都谈好了20K*13,结果挂在这里....

一般提到面试&#xff0c;肯定都会想问一下面试结果&#xff0c;我就大概的说一下面试结果&#xff0c;哈哈&#xff0c;其实不太想说&#xff0c;因为挺惨的&#xff0c;并没有像很多大佬一样 ”已拿字节阿里腾讯各大厂offer”&#xff0c;但是毕竟是自己的经历&#xff0c;无…

让效果图渲染做到最佳的几个小诀窍

制作出优秀的效果图需要多方面的技术支持&#xff0c;而渲染是其中非常关键的一步。一份精美的效果图需要经过高质量的渲染才能呈现出最佳的效果。本文将分享一些关于如何让效果图渲染做到最佳的小诀窍&#xff0c;包括专注的小细节、优化场景设置和灯光、纹理、图像应用最终修…

医疗电子红外线人体额温枪方案

在当前新冠疫情背景下&#xff0c;红外线人体额温枪成为疫情防控必备的设备之一。红外线人体额温枪采用红外线技术&#xff0c;无需接触人体&#xff0c;通过测量人体表面温度来判断人体是否发热。其测量快速、准确、不接触等特点&#xff0c;使其广泛应用于机场、车站、医院、…