FPGA verilog基本外设练习(五)-串口通信
串口UART
今天主要记录一下使用黑金开发板EP4CE6F17C8型号的串口的经历。因为这块黑金开发板只有usb转串口,所以实验使用的是USB转串口的通信。如下图:
一、任务:采用串口调试助手发送指令控制开发板上面的LED0打开和关闭。
二、实现的RTL图如下:
三、代码实现过程
1、顶层模块uart_top
module uart_top(input sys_clk, //外部50M时钟input sys_rst_n, //外部复位信号,低有效//uart接口input uart_rxd, //UART接收端口output uart_txd, //UART发送端口output led);//parameter define
parameter CLK_FREQ = 50000000; //定义系统时钟频率
parameter UART_BPS = 115200; //定义串口波特率//wire define
wire uart_en_w; //UART发送使能
wire [7:0] uart_data_w; //UART发送数据
wire led_open_flag; //串口接收到打开led的标记
wire led_close_flag; //串口接收到关闭led的标记
wire clk_1m_w; //1MHz时钟,用于Signaltap调试//*****************************************************
//** main code
//*****************************************************
clk_div u_pll( //时钟分频模块,用于调试.inclk0 (sys_clk),.c0 (clk_1m_w)
);uart_recv #( //串口接收模块.CLK_FREQ (CLK_FREQ), //设置系统时钟频率.UART_BPS (UART_BPS)) //设置串口接收波特率
u_uart_recv( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n),.uart_rxd (uart_rxd),.uart_done (uart_en_w),.uart_data (uart_data_w),.led_open_flag (led_open_flag),.led_close_flag (led_close_flag));uart_send #( //串口发送模块.CLK_FREQ (CLK_FREQ), //设置系统时钟频率.UART_BPS (UART_BPS)) //设置串口发送波特率
u_uart_send( .sys_clk (sys_clk),.sys_rst_n (sys_rst_n),.uart_en (uart_en_w),.uart_din (uart_data_w),.uart_txd (uart_txd));led led_u1(.clk (sys_clk ),.rst_n (sys_rst_n ),.led_open_flag (led_open_flag ),.led_close_flag (led_close_flag ),.led (led )
);
endmodule
2、串口接收模块uart_recv
module uart_recv(input sys_clk, //系统时钟input sys_rst_n, //系统复位,低电平有效input uart_rxd, //UART接收端口output reg uart_done, //接收一帧数据完成标志信号output reg [7:0] uart_data, //接收的数据output reg led_open_flag, //打开led的标记output reg led_close_flag //关闭led的标记 );//parameter define
parameter CLK_FREQ = 50000000; //系统时钟频率
parameter UART_BPS = 9600; //串口波特率
localparam BPS_CNT = CLK_FREQ/UART_BPS; //为得到指定波特率,//需要对系统时钟计数BPS_CNT次
//reg define
reg uart_rxd_d0;
reg uart_rxd_d1;
reg [15:0] clk_cnt; //系统时钟计数器
reg [ 3:0] rx_cnt; //接收数据计数器
reg rx_flag; //接收过程标志信号
reg [ 7:0] rxdata; //接收数据寄存器//wire define
wire start_flag;//*****************************************************
//** main code
//*****************************************************
//捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号
assign start_flag = uart_rxd_d1 & (~uart_rxd_d0); //对UART接收端口的数据延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin uart_rxd_d0 <= 1'b0;uart_rxd_d1 <= 1'b0; endelse beginuart_rxd_d0 <= uart_rxd; uart_rxd_d1 <= uart_rxd_d0;end
end//当脉冲信号start_flag到达时,进入接收过程
always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) rx_flag <= 1'b0;else beginif(start_flag) //检测到起始位rx_flag <= 1'b1; //进入接收过程,标志位rx_flag拉高else if((rx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))rx_flag <= 1'b0; //计数到停止位中间时,停止接收过程elserx_flag <= rx_flag;end
end//进入接收过程后,启动系统时钟计数器与接收数据计数器
always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin clk_cnt <= 16'd0; rx_cnt <= 4'd0;end else if ( rx_flag ) begin //处于接收过程if (clk_cnt < BPS_CNT - 1) beginclk_cnt <= clk_cnt + 1'b1;rx_cnt <= rx_cnt;endelse beginclk_cnt <= 16'd0; //对系统时钟计数达一个波特率周期后清零rx_cnt <= rx_cnt + 1'b1; //此时接收数据计数器加1endendelse begin //接收过程结束,计数器清零clk_cnt <= 16'd0;rx_cnt <= 4'd0;end
end//根据接收数据计数器来寄存uart接收端口数据
always @(posedge sys_clk or negedge sys_rst_n) begin if ( !sys_rst_n) rxdata <= 8'd0; else if(rx_flag) //系统处于接收过程if (clk_cnt == BPS_CNT/2) begin //判断系统时钟计数器计数到数据位中间case ( rx_cnt )4'd1 : rxdata[0] <= uart_rxd_d1; //寄存数据位最低位4'd2 : rxdata[1] <= uart_rxd_d1;4'd3 : rxdata[2] <= uart_rxd_d1;4'd4 : rxdata[3] <= uart_rxd_d1;4'd5 : rxdata[4] <= uart_rxd_d1;4'd6 : rxdata[5] <= uart_rxd_d1;4'd7 : rxdata[6] <= uart_rxd_d1;4'd8 : rxdata[7] <= uart_rxd_d1; //寄存数据位最高位default:; endcaseendelse rxdata <= rxdata;elserxdata <= 8'd0;
end//数据接收完毕后给出标志信号并寄存输出接收到的数据
always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) beginuart_data <= 8'd0; uart_done <= 1'b0;endelse if(rx_cnt == 4'd9) begin //接收数据计数器计数到停止位时 uart_data <= rxdata; //寄存输出接收到的数据uart_done <= 1'b1; //并将接收完成标志位拉高endelse beginuart_data <= 8'd0; uart_done <= 1'b0; end
end//判断接收到的是不是打开led标记,即接收到十六进制20always@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)led_open_flag <= 1'b0;else if(uart_data == 8'h20)led_open_flag <= 1'b1;elseled_open_flag <= 1'b0;
end//判断接收到的是不是关闭led标记,即接收到十六进制40always@(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)led_close_flag <= 1'b0;else if(uart_data == 8'h40)led_close_flag <= 1'b1;elseled_close_flag <= 1'b0;
end
endmodule
3、串口发送模块uart_send,主要用来将FPGA接受到的数据再发送给串口调试助手。
module uart_send(input sys_clk, //系统时钟input sys_rst_n, //系统复位,低电平有效input uart_en, //发送使能信号input [7:0] uart_din, //待发送数据output reg uart_txd //UART发送端口);//parameter define
parameter CLK_FREQ = 50000000; //系统时钟频率
parameter UART_BPS = 9600; //串口波特率
localparam BPS_CNT = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次//reg define
reg uart_en_d0;
reg uart_en_d1;
reg [15:0] clk_cnt; //系统时钟计数器
reg [ 3:0] tx_cnt; //发送数据计数器
reg tx_flag; //发送过程标志信号
reg [ 7:0] tx_data; //寄存发送数据//wire define
wire en_flag;//*****************************************************
//** main code
//*****************************************************
//捕获uart_en上升沿,得到一个时钟周期的脉冲信号
assign en_flag = (~uart_en_d1) & uart_en_d0;//对发送使能信号uart_en延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) beginuart_en_d0 <= 1'b0; uart_en_d1 <= 1'b0;end else begin uart_en_d0 <= uart_en; uart_en_d1 <= uart_en_d0; end
end//当脉冲信号en_flag到达时,寄存待发送的数据,并进入发送过程
always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin tx_flag <= 1'b0;tx_data <= 8'd0;end else if (en_flag) begin //检测到发送使能上升沿 tx_flag <= 1'b1; //进入发送过程,标志位tx_flag拉高tx_data <= uart_din; //寄存待发送的数据endelse if ((tx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))begin //计数到停止位中间时,停止发送过程tx_flag <= 1'b0; //发送过程结束,标志位tx_flag拉低tx_data <= 8'd0;endelse begintx_flag <= tx_flag;tx_data <= tx_data;end
end//进入发送过程后,启动系统时钟计数器与发送数据计数器
always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin clk_cnt <= 16'd0; tx_cnt <= 4'd0;end else if (tx_flag) begin //处于发送过程if (clk_cnt < BPS_CNT - 1) beginclk_cnt <= clk_cnt + 1'b1;tx_cnt <= tx_cnt;endelse beginclk_cnt <= 16'd0; //对系统时钟计数达一个波特率周期后清零tx_cnt <= tx_cnt + 1'b1; //此时发送数据计数器加1endendelse begin //发送过程结束clk_cnt <= 16'd0;tx_cnt <= 4'd0;end
end//根据发送数据计数器来给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) uart_txd <= 1'b1; else if (tx_flag)case(tx_cnt)4'd0: uart_txd <= 1'b0; //起始位 4'd1: uart_txd <= tx_data[0]; //数据位最低位4'd2: uart_txd <= tx_data[1];4'd3: uart_txd <= tx_data[2];4'd4: uart_txd <= tx_data[3];4'd5: uart_txd <= tx_data[4];4'd6: uart_txd <= tx_data[5];4'd7: uart_txd <= tx_data[6];4'd8: uart_txd <= tx_data[7]; //数据位最高位4'd9: uart_txd <= 1'b1; //停止位default: ;endcaseelse uart_txd <= 1'b1; //空闲时发送端口为高电平
end
endmodule
4、led模块led
module led(clk,rst_n,led_open_flag,led_close_flag,led);
input clk;
input rst_n;
input led_open_flag;
input led_close_flag;
output reg led;always@(posedge clk or negedge rst_n)beginif(!rst_n)led <= 1'b0;else if(led_open_flag)led <= 1'b1;else if(led_close_flag)led <= 1'b0;
end
endmodule
四、Signal Tap仿真
将程序下载到开发版之后,打开串口调试助手,设置好波特率115200,停止位为1位,数据位为8位,无奇偶校验位(根据上面的程序设置,保持一致)
然后打开串口,注意串口调试助手默认以ASCII码的格式一个字节一个字节的发送(比如需要发送23,那么它会先发送第一个字节2,再发送第二个字节3),所以这里选择16进制发送格式,不发送新行。如果发送新行,我们接受到一个回车键的ASCII码即13。那么我们打开SignalTap,然后在串口调试助手的发送窗口发送16进制的20,可以看到仿真如下:
可以看出接收端口uart_rxd的开始位为0,接着是8位的数据位为00000100,最后是一个停止位为1。请注意最先接收的是数据的最低位,所以真正接收到的数据需要颠倒顺序,即00100000,我们可以看到这就是16进制的20。可以证明我们接收到的数据是正确的。同时看到led信号在接收到20之后,被拉高即led0被点亮。同理我们发送16进制的40,led0将被熄灭。如图:
FPGA verilog基本外设练习(五)-串口通信相关推荐
- 【K210】K210学习笔记五——串口通信
[K210]K210学习笔记五--串口通信 前言 K210如何进行串口通信 K210串口配置 K210串口发送相关定义 K210串口接收相关定义 K210串口发送接收测试 完整源码 前言 本人大四学生 ...
- 7天搞定FPGA精录总结Episode.6 串口通信,系统设计【基于Robei、Altera QuartusⅡ与Python】
之前参加&组织学校排球比赛的时候,商量到我们可以学习那些正规排球大赛一样,决赛的时候在学校体育馆进行,而且我可以在一旁准备一点EDM热歌作为暖场音乐.然后就有同学用一脸奇怪的表情问我:&quo ...
- FPGA verilog基本外设练习(六)- 以太网通信模块
以太网模块 1.实验任务 上位机通过网口调试助手发送数据给FPGA,FPGA开发板通过以太网接口接收数据,并将接收到的数据发送给上位机,完成以太网数据的环回. 2.硬件模块 采用的是黑金的AN8211 ...
- 树莓派外设开发基础—串口通信
文章目录 一.串口通信的一些概念 二.树莓派串口通信设置 三.树莓派串口通信API 四.树莓派与PC通信 1.接线 2.程序与测试 拓展 一.串口通信的一些概念 串口通信通常用在多机通信中. 串口通信 ...
- 实现FPGA Verilog HDL与NIOS II的通信数据交换——利用AVALON总线
平时用FPGA基本都是全程用Verilog HDL编程,当遇到液晶的时候,发现Verilog的还不如C语言来的方便,但是用NIOS来编写的时候,实现NIOS与Verilog的通信又是一个问题,今天用了 ...
- FPGA学习笔记(四)——串口通信之接收数据(调试过程)
本学习笔记主要参考小梅哥B站教学视频,网址如下: https://www.bilibili.com/video/BV1va411c7Dz?p=1 使用的编译器为Vivado,HDL语言为verilog ...
- FPGA自学笔记--串口通信发送多字节数据(verilog版)
1.需求分析 关于uart协议实现这部分大家可以参考我上一篇的博客.<FPGA自学笔记--串口通信实现(vivado&verilog版)>.在上一篇博客中,主要实现了将单字节的数据 ...
- 基于FPGA的红外遥控解码与PC串口通信
基于FPGA的红外遥控解码与PC串口通信 zouxy09@qq.com http://blog.csdn.net/zouxy09 这是我的<电子设计EDA>的课程设计作业(呵呵,这个月都拿 ...
- 【FPGA】——UART串口通信
UART串口简介 串行通信分为两种方式:同步串行通信和异步串行通信.同步串行通信要求通信双方使用同一时钟,异步则没有这个要求.UART是一种采用异步串行通信方式的通用异步收发传输器(univers ...
- fpga板子怎么和电脑连_FPGA与PC串口通信设计与实现
FPGA 与 PC 串口通信设计与实现 朱泽锋 1 赵丹辉 2 王鹏宇 1 [摘 要] 本文采用 Verilog 硬件描述语言,利用 Xilinx 公司的 FPGA 芯片实现 其与 PC 间的串行通信 ...
最新文章
- UE4.18预览第一版发布,共享XR引擎层降低硬件支持难度
- linux:uabntu日常操作
- Fabric 学习笔记-架构初探
- 如何选择MBA教育 读MBA前必答10个问题
- java输出华氏摄氏温度转换表_Python练习题2.10输出华氏-摄氏温度转换表
- SLAM方向公众号、知乎、博客学习参考
- 启动程序端口被占用Address already in use: bind解决方案
- linux 17 中文输入,Rethat Linux Fedora17添加中文输入法
- [android底层]jni中获取自定义的类函数编写
- 一个软件工程师在北京的反省
- Linux关闭桌面进程,Centos进入桌面和退出桌面的方法
- 计算机硬盘满了怎么解决,电脑磁盘满了怎样清理
- Protell99中的铺铜设置
- 问道手游平民玩家什么职业好
- 深度解读Coatue:向加密行业转舵的「老虎环球基金」
- 刨根问底,5问分析法
- 良心推荐5款Python编辑器,请择优选用!
- Epplus获取数据区域的第一行或最后一行
- RecycleView 刷新显示错乱(刷新之后上一个界面的显示还在,出现部分错乱)
- Word怎么转换成PDF免费?分享适合你的Word转PDF方法