news 2026/4/23 13:53:57

27.串口

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
27.串口

1.串口介绍

串口(通常指 UART,通用异步收发传输器)是一种异步串行通信接口,也是嵌入式系统中最常用的通信方式之一,它通过一根线发送数据、另一根线接收数据,实现设备间的全双工通信。

2.串口组成

1.物理层

2.协议

串口协议:

传输1个字节:

传输多个字节:

3.代码

使用的是米联客的开发板:

串口的实现思路:

状态机+计数器

top.v:

`timescale 1ns / 1ps module top ( input sys_clk_p, //系统差分输入时钟 input sys_clk_n, //系统差分输入时钟 input sys_rst_n, //系外部复位信号,低有效 //UART端口 input uart_rxd , //UART接收端口 output uart_txd , //UART发送端口 output reg [1:0] LED_o ); reg [31:0]tcnt; //***************************************************** //** main code //***************************************************** //转换差分信号 IBUFDS diff_clock ( .I (sys_clk_p), //系统差分输入时钟 .IB(sys_clk_n), //系统差分输入时钟 .O (sys_clk) //输出系统时钟 ); //例化串口模块 uart_loopback uart_looop( .sys_clk(sys_clk), //时钟信号 .sys_rst_n(sys_rst_n), //系外部复位信号,低有效 .uart_rxd (uart_rxd), //UART接收端口 .uart_txd(uart_txd) //UART发送端口 ); always @(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) tcnt <= 32'd0; else if(tcnt < 32'd99_999_999) tcnt <= tcnt + 1'b1; else tcnt <= 32'd0; end wire led_en = (tcnt == 32'd99_999_999); always @(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) LED_o <= 2'b1; else if(led_en) if(LED_o == 2'b10) LED_o <= 2'b1; else LED_o <= LED_o << 1; end endmodule

uart_loopback.v

`timescale 1ns / 1ps module uart_loopback( input sys_clk, //时钟信号 input sys_rst_n, //系外部复位信号,低有效 //UART端口 input uart_rxd , //UART接收端口 output uart_txd //UART发送端口 ); //parameter define parameter CLK_FREQ = 100000000; //定义系统时钟频率 parameter UART_BPS = 115200 ; //定义串口波特率 //wire define wire uart_rx_done; //UART接收完成信号 wire [7:0] uart_rx_data; //UART接收数据 wire [3:0] rx_cnt; //***************************************************** //** main code //***************************************************** //串口接收模块 uart_rx #( .CLK_FREQ (CLK_FREQ), .UART_BPS (UART_BPS) ) u_uart_rx( .clk (sys_clk ), .rst_n (sys_rst_n ), .uart_rxd (uart_rxd ), .uart_rx_done (uart_rx_done), .uart_rx_data (uart_rx_data), .rx_cnt(rx_cnt) ); //串口发送模块 uart_tx #( .CLK_FREQ (CLK_FREQ), .UART_BPS (UART_BPS) ) u_uart_tx( .clk (sys_clk ), .rst_n (sys_rst_n ), .uart_tx_en (uart_rx_done), .uart_tx_data (uart_rx_data), .uart_txd (uart_txd ), .uart_tx_busy ( ) ); ila_1 u_ila_1 ( .clk(sys_clk), // ILA的采样时钟(必须和被观测信号同时钟,此处用系统时钟100MHz) .probe0(sys_rst_n), // 探头0:复位信号(参考用) .probe1(uart_rx_done), // 探头1:UART接收信号(核心观测) .probe2(uart_rx_data[7:0]), // 探头2:UART发送信号(核心观测) .probe3(uart_rxd), // 探头3:LED输出(辅助参考) .probe4(rx_cnt) // 探头4:LED翻转使能(辅助参考) ); endmodule

uart_rx.v

`timescale 1ns / 1ps module uart_rx( input clk , //系统时钟 input rst_n , //系统复位,低有效 input uart_rxd , //UART接收端口 output reg uart_rx_done, //UART接收完成信号 output reg [7:0] uart_rx_data, //UART接收到的数据 output reg [3:0] rx_cnt ); //parameter define parameter CLK_FREQ = 100000000; //系统时钟频率 parameter UART_BPS = 115200 ; //串口波特率 localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次 //reg define reg uart_rxd_d0; reg uart_rxd_d1; reg uart_rxd_d2; reg rx_flag ; //接收过程标志信号 reg [3:0 ] rx_cnt ; //接收数据计数器 reg [15:0] baud_cnt ; //波特率计数器 reg [7:0 ] rx_data_t ; //接收数据寄存器 //wire define wire start_en; //***************************************************** //** main code //***************************************************** //捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号 assign start_en = uart_rxd_d2 & (~uart_rxd_d1) & (~rx_flag); //针对异步信号的同步处理 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin uart_rxd_d0 <= 1'b0; uart_rxd_d1 <= 1'b0; uart_rxd_d2 <= 1'b0; end else begin uart_rxd_d0 <= uart_rxd; uart_rxd_d1 <= uart_rxd_d0; uart_rxd_d2 <= uart_rxd_d1; end end //给接收标志赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) rx_flag <= 1'b0; else if(start_en) //检测到起始位 rx_flag <= 1'b1; //接收过程中,标志信号rx_flag拉高 //在停止位一半的时候,即接收过程结束,标志信号rx_flag拉低 else if((rx_cnt == 4'd9) && (baud_cnt == BAUD_CNT_MAX/2 - 1'b1)) rx_flag <= 1'b0; else rx_flag <= rx_flag; end //波特率的计数器赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) baud_cnt <= 16'd0; else if(rx_flag) begin //处于接收过程时,波特率计数器(baud_cnt)进行循环计数 if(baud_cnt < BAUD_CNT_MAX - 1'b1) baud_cnt <= baud_cnt + 16'b1; else baud_cnt <= 16'd0; //计数达到一个波特率周期后清零 end else baud_cnt <= 16'd0; //接收过程结束时计数器清零 end //对接收数据计数器(rx_cnt)进行赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) rx_cnt <= 4'd0; else if(rx_flag) begin //处于接收过程时rx_cnt才进行计数 if(baud_cnt == BAUD_CNT_MAX - 1'b1) //当波特率计数器计数到一个波特率周期时 rx_cnt <= rx_cnt + 1'b1; //接收数据计数器加1 else rx_cnt <= rx_cnt; end else rx_cnt <= 4'd0; //接收过程结束时计数器清零 end //根据rx_cnt来寄存rxd端口的数据 always @(posedge clk or negedge rst_n) begin if(!rst_n) rx_data_t <= 8'b0; else if(rx_flag) begin //系统处于接收过程时 if(baud_cnt == BAUD_CNT_MAX/2 - 1'b1) begin //判断baud_cnt是否计数到数据位的中间 case(rx_cnt) 4'd1 : rx_data_t[0] <= uart_rxd_d2; //寄存数据的最低位 4'd2 : rx_data_t[1] <= uart_rxd_d2; 4'd3 : rx_data_t[2] <= uart_rxd_d2; 4'd4 : rx_data_t[3] <= uart_rxd_d2; 4'd5 : rx_data_t[4] <= uart_rxd_d2; 4'd6 : rx_data_t[5] <= uart_rxd_d2; 4'd7 : rx_data_t[6] <= uart_rxd_d2; 4'd8 : rx_data_t[7] <= uart_rxd_d2; //寄存数据的高低位 default : ; endcase end else rx_data_t <= rx_data_t; end else rx_data_t <= 8'b0; end //给接收完成信号和接收到的数据赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin uart_rx_done <= 1'b0; uart_rx_data <= 8'b0; end //当接收数据计数器计数到停止位,且baud_cnt计数到停止位的中间时 else if(rx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX/2 - 1'b1) begin uart_rx_done <= 1'b1 ; //拉高接收完成信号 uart_rx_data <= rx_data_t; //并对UART接收到的数据进行赋值 end else begin uart_rx_done <= 1'b0; uart_rx_data <= uart_rx_data; end end endmodule

uart_tx.v

`timescale 1ns / 1ps module uart_tx( input clk , //系统时钟 input rst_n , //系统复位,低有效 input uart_tx_en , //UART的发送使能 input [7:0] uart_tx_data, //UART要发送的数据 output reg uart_txd , //UART发送端口 output reg uart_tx_busy //发送忙状态信号 ); //parameter define parameter CLK_FREQ = 100000000; //系统时钟频率 parameter UART_BPS = 115200 ; //串口波特率 localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次 //reg define reg [7:0] tx_data_t; //发送数据寄存器 reg [3:0] tx_cnt ; //发送数据计数器 reg [15:0] baud_cnt ; //波特率计数器 //***************************************************** //** main code //***************************************************** //当uart_tx_en为高时,寄存输入的并行数据,并拉高BUSY信号 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin tx_data_t <= 8'b0; uart_tx_busy <= 1'b0; end //发送使能时,寄存要发送的数据,并拉高BUSY信号 else if(uart_tx_en) begin tx_data_t <= uart_tx_data; uart_tx_busy <= 1'b1; end //当计数到停止位结束时,停止发送过程 else if(tx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX - BAUD_CNT_MAX/16) begin tx_data_t <= 8'b0; //清空发送数据寄存器 uart_tx_busy <= 1'b0; //并拉低BUSY信号 end else begin tx_data_t <= tx_data_t; uart_tx_busy <= uart_tx_busy; end end //波特率的计数器赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) baud_cnt <= 16'd0; //当处于发送过程时,波特率计数器(baud_cnt)进行循环计数 else if(uart_tx_busy) begin if(baud_cnt < BAUD_CNT_MAX - 1'b1) baud_cnt <= baud_cnt + 16'b1; else baud_cnt <= 16'd0; //计数达到一个波特率周期后清零 end else baud_cnt <= 16'd0; //发送过程结束时计数器清零 end //tx_cnt进行赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) tx_cnt <= 4'd0; else if(uart_tx_busy) begin //处于发送过程时tx_cnt才进行计数 if(baud_cnt == BAUD_CNT_MAX - 1'b1) //当波特率计数器计数到一个波特率周期时 tx_cnt <= tx_cnt + 1'b1; //发送数据计数器加1 else tx_cnt <= tx_cnt; end else tx_cnt <= 4'd0; //发送过程结束时计数器清零 end //根据tx_cnt来给uart发送端口赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) uart_txd <= 1'b1; else if(uart_tx_busy) begin case(tx_cnt) 4'd0 : uart_txd <= 1'b0 ; //起始位 4'd1 : uart_txd <= tx_data_t[0]; //数据位最低位 4'd2 : uart_txd <= tx_data_t[1]; 4'd3 : uart_txd <= tx_data_t[2]; 4'd4 : uart_txd <= tx_data_t[3]; 4'd5 : uart_txd <= tx_data_t[4]; 4'd6 : uart_txd <= tx_data_t[5]; 4'd7 : uart_txd <= tx_data_t[6]; 4'd8 : uart_txd <= tx_data_t[7]; //数据位最高位 4'd9 : uart_txd <= 1'b1 ; //停止位 default : uart_txd <= 1'b1; endcase end else uart_txd <= 1'b1; //空闲时发送端口为高电平 end endmodule

引脚约束文件:

# ============================================= # 时钟约束:差分时钟(P/N对) # ============================================= # 1. 创建主时钟:10ns(100MHz),50%占空比,时钟源P端 create_clock -period 10.000 -name sysclk -waveform {0 5} [get_ports sys_clk_p] # 2. 差分时钟引脚分配 set_property PACKAGE_PIN AE5 [get_ports sys_clk_p] # 3. 差分时钟电平标准(P/N端同时设置DIFF_SSTL12 set_property IOSTANDARD DIFF_SSTL12 [get_ports sys_clk_p] # ============================================= # 复位信号:引脚+电平约束 # ============================================= set_property PACKAGE_PIN B14 [get_ports sys_rst_n] set_property IOSTANDARD LVCMOS18 [get_ports sys_rst_n] # ============================================= # LED灯:引脚+电平约束 # ============================================= set_property PACKAGE_PIN E14 [get_ports {LED_o[1]}] set_property PACKAGE_PIN E13 [get_ports {LED_o[0]}] set_property IOSTANDARD LVCMOS18 [get_ports {LED_o[*]}] # ============================================= # PL调试串口:引脚+电平约束 # ============================================= set_property PACKAGE_PIN A15 [get_ports uart_txd] set_property PACKAGE_PIN B15 [get_ports uart_rxd] set_property IOSTANDARD LVCMOS18 [get_ports {uart_txd uart_rxd}] # ============================================= # 全局配置:开启比特流压缩 # ============================================= set_property BITSTREAM.GENERAL.COMPRESS true [current_design]

4.仿真

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 5:21:52

太阳能热水器(有完整资料)

资料查找方式&#xff1a; 特纳斯电子&#xff08;电子校园网&#xff09;&#xff1a;搜索下面编号即可 编号&#xff1a; CP-51-2021-061 设计简介&#xff1a; 本设计是基于单片机的太阳能热水器系统&#xff0c;主要实现以下功能&#xff1a; 可通过LCD1602显示温度阈值…

作者头像 李华
网站建设 2026/4/23 5:24:17

家电远程控制系统的设计(有完整资料)

资料查找方式&#xff1a; 特纳斯电子&#xff08;电子校园网&#xff09;&#xff1a;搜索下面编号即可 编号&#xff1a; CP-51-2021-070 设计简介&#xff1a; 本设计是基于单片机的家电远程控制系统&#xff0c;主要实现以下功能&#xff1a; 可通过LCD1602显示温度、模…

作者头像 李华
网站建设 2026/4/23 5:23:25

2026年Java面试中经常问的问题(持续更新)

开始的碎碎念 本文大多是各大企业的topN题目&#xff0c;针对java中高级开发&#xff0c;本文会持续收集更新内容&#xff0c;如果大家有优质的Java面试题&#xff0c;也欢迎大家来投稿。 特意整理出这个分类的目录&#xff0c;方便大家平时复习和收藏哈。希望正在准备面试的…

作者头像 李华
网站建设 2026/4/23 5:20:46

HEC-RAS 1D/2D水动力与水环境模拟技术

水动力与水环境模型的数值模拟是实现水资源规划、环境影响分析、防洪规划以及未来气候变化下预测和分析的主要手段。然而&#xff0c;一方面水动力和水环境模型的使用非常复杂&#xff0c;理论繁复&#xff1b;另一方面&#xff0c;免费的水动力和水环境软件往往缺少重要功能&a…

作者头像 李华
网站建设 2026/4/23 5:21:52

房屋民宿短租的设计与实现-开题报告

目录研究背景与意义研究目标研究内容创新点预期成果研究方法可行性分析项目技术支持可定制开发之功能亮点源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作研究背景与意义 随着共享经济和旅游业的快速发展&#xff0c;短租民宿成为住宿市场的…

作者头像 李华