FPGA串口接收Demo

news2024/11/28 20:59:52

串口接收Demo

简单介绍

在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据

在这里插入图片描述

  • 空闲状态时,为高电平
  • 起始位为一个单位长度低电平,停止位为一个长度高电平
分析
帧格式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aMQEeA18-1669128642072)(https://gitee.com/wangchaosun/picgo/raw/master/image-20221122222307710.png)]

  • 8位数据位
  • 1位停止位
  • 无校验位
基本思路

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LDKeGrYm-1669128642074)(https://gitee.com/wangchaosun/picgo/raw/master/image-20221122223525080.png)]

采集每一位中间时刻的数据作为这一位的数据 ( 也可以每一位多采几个时刻的数据,取众数 )

框图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6yEkqLBs-1669128642075)(https://gitee.com/wangchaosun/picgo/raw/master/image-20221122222417869.png)]

状态机

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tBpPzN04-1669128642076)(https://gitee.com/wangchaosun/picgo/raw/master/image-20221122222530106.png)]

Verilog
`timescale 1ns / 1ps
//
// Engineer: wkk
// Create Date: 2022/11/22 16:35:19
// Module Name: uart_rx
// Description: uart rx function
//
module uart_rx(
    input               sys_clk,
    input               sys_rst_n,
    input               uart_rx,
    output              uart_rx_valid,
    output  [7:0]       uart_rx_data
);

parameter  SYS_CLK         = 100_000_000;
// 115200
parameter  BAUD_COUNT      =  868;
parameter  BAUD_HALF_COUNT =  434;
parameter  TIME_COUNT_LEN  =  12;

localparam IDLE_STATE       = 4'd0;
localparam START_STATE      = 4'd1;
localparam RECV_STATE       = 4'd2;
localparam RECV_D0_STATE    = 4'd3;
localparam RECV_D1_STATE    = 4'd4; 
localparam RECV_D2_STATE    = 4'd5; 
localparam RECV_D3_STATE    = 4'd6; 
localparam RECV_D4_STATE    = 4'd7; 
localparam RECV_D5_STATE    = 4'd8; 
localparam RECV_D6_STATE    = 4'd9; 
localparam RECV_D7_STATE    = 4'd10; 
localparam END_STATE        = 4'd11; 

reg [3:0]   curr_state;
reg [3:0]   next_state;

reg         uart_rx_d0;
reg         uart_rx_d1;
wire        uart_rx_en;

// 开始计时
reg         time_en;
// 计时模式 0: 计数一个波特率周期 1: 计数半个波特率周期  
reg         half_en;
reg         count_en;
reg         [TIME_COUNT_LEN-1:0] time_count;

reg  [7:0]  rx_data;
reg  [3:0]  rx_data_index;

// 计时模块
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n || !time_en) begin 
        time_count <= 0;
        count_en <= 0;
    end 
    else 
    if(half_en)
        if(time_count == BAUD_HALF_COUNT -1 )begin
            time_count <=0;
            count_en <= 1;
        end
        else begin
            time_count <= time_count + 1'b1;
            count_en <= 0;
        end 
    else 
         if(time_count == BAUD_COUNT -1 )begin
            count_en <= 1;
            time_count <= 0;
         end
         else begin
            time_count <= time_count + 1'b1;
            count_en <= 0;
        end
end

// 产生下一状态
always @(*) begin
    case( curr_state )
    IDLE_STATE: begin
        if( uart_rx_en )
            next_state = START_STATE;
        else 
            next_state = IDLE_STATE;
    end
    START_STATE:
        if( count_en)
            next_state = RECV_STATE;
        else
            next_state = START_STATE;
    RECV_STATE:
        if( count_en )
            next_state = RECV_D0_STATE;
        else
            next_state = RECV_STATE;
    RECV_D0_STATE:
        if( count_en )
            next_state = RECV_D1_STATE;
        else
            next_state = RECV_D0_STATE;
    RECV_D1_STATE:
        if( count_en )
            next_state = RECV_D2_STATE;
        else
            next_state = RECV_D1_STATE;
    RECV_D2_STATE:
        if( count_en )
            next_state = RECV_D3_STATE;
        else
            next_state = RECV_D2_STATE;
    RECV_D3_STATE:
        if( count_en )
            next_state = RECV_D4_STATE;
        else
            next_state = RECV_D3_STATE;
    RECV_D4_STATE:
        if( count_en )
            next_state = RECV_D5_STATE;
        else
            next_state = RECV_D4_STATE;
    RECV_D5_STATE:
        if( count_en )
            next_state = RECV_D6_STATE;
        else
            next_state = RECV_D5_STATE;
    RECV_D6_STATE:
        if( count_en )
            next_state = RECV_D7_STATE;
        else
            next_state = RECV_D6_STATE;
    RECV_D7_STATE:
        if( count_en )
            next_state = END_STATE;
        else
            next_state = RECV_D7_STATE;
    END_STATE:
        next_state = IDLE_STATE;
    default: ;
    endcase
end

assign uart_rx_data = rx_data;
assign uart_rx_valid = (curr_state == END_STATE)?1'b1:1'b0;

// 状态输出
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
       rx_data <= 7'b0;
       time_en <= 1'b0;
       half_en <= 1'b0;
       rx_data_index <= 3'b0;
    end else
    case(curr_state)
    IDLE_STATE: begin
        time_en <= 1'b0;
        half_en <= 1'b0;
        rx_data_index <= 3'b0;
    end
    START_STATE: begin
        time_en <= 1'b1;
        half_en <= 1'b1;
    end
    RECV_STATE:begin
       time_en <= 1'b1;
       half_en <= 1'b0;
    end     
    RECV_D0_STATE:
        if(rx_data_index == 3'd0)begin
            rx_data[0] <= uart_rx;
            rx_data_index <= rx_data_index + 1'b1;
        end else
            rx_data[0] <= rx_data[0];      
    RECV_D1_STATE:
        if(rx_data_index == 3'd1)begin
            rx_data[1] <= uart_rx;
            rx_data_index <= rx_data_index + 1'b1;
        end else
            rx_data[1] <= rx_data[1];
    RECV_D2_STATE:
        if(rx_data_index == 3'd2)begin
            rx_data[2] <= uart_rx;
            rx_data_index <= rx_data_index + 1'b1;
        end else
            rx_data[2] <= rx_data[2];
    RECV_D3_STATE:
        if(rx_data_index == 3'd3)begin
            rx_data[3] <= uart_rx;
            rx_data_index <= rx_data_index + 1'b1;
        end else
            rx_data[3] <= rx_data[3];
    RECV_D4_STATE:
        if(rx_data_index == 3'd4)begin
            rx_data[4] <= uart_rx;
            rx_data_index <= rx_data_index + 1'b1;
        end else
            rx_data[4] <= rx_data[4];
    RECV_D5_STATE:
        if(rx_data_index == 3'd5)begin
            rx_data[5] <= uart_rx;
            rx_data_index <= rx_data_index + 1'b1;
        end else
            rx_data[5] <= rx_data[5];
    RECV_D6_STATE:
        if(rx_data_index == 3'd6)begin
            rx_data[6] <= uart_rx;
            rx_data_index <= rx_data_index + 1'b1;
        end else
            rx_data[6] <= rx_data[6];
    RECV_D7_STATE:
        if(rx_data_index == 3'd7)begin
            rx_data[7] <= uart_rx;
            rx_data_index <= rx_data_index + 1'b1;
        end else
            rx_data[7] <= rx_data[7];
    END_STATE:begin
        time_en <= 1'b0;
        half_en <= 1'b0;
        rx_data_index <= 3'b0;
    end
    default: ;
    endcase
end

// catch rising edge 
assign uart_rx_en = (uart_rx_d0 & !uart_rx_d1) ? 1'b1:1'b0;

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        uart_rx_d0 <= 1'b0;
        uart_rx_d1 <= 1'b0;
    end else begin
        uart_rx_d1 <= uart_rx;
        uart_rx_d0 <= uart_rx_d1;
    end 
end

// update curr_state
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) 
        curr_state <= IDLE_STATE;
    else
        curr_state <= next_state;
end

endmodule
testbench
`timescale 1ns / 1ns
//
// Engineer: wkk
// Module Name: uart_rx_tb
//

module uart_rx_tb;
reg         sys_clk;
reg         sys_rst_n;
reg         uart_rx;
wire        uart_rx_valid;
wire [7:0]  uart_rx_data;

parameter BAUD_COUNT      = 20;
parameter BAUD_HALF_COUNT = 10;
parameter TIME_COUNT_LEN  = 5;
uart_rx #(
    .BAUD_COUNT     (BAUD_COUNT),
    .BAUD_HALF_COUNT(BAUD_HALF_COUNT),
    .TIME_COUNT_LEN (TIME_COUNT_LEN)
)u_uart_rx(
    .sys_clk        (sys_clk),
    .sys_rst_n      (sys_rst_n),
    .uart_rx        (uart_rx),
    .uart_rx_valid  (uart_rx_valid),
    .uart_rx_data   (uart_rx_data)
);

initial begin 
    sys_clk = 0;
    sys_rst_n = 0;
    uart_rx = 1;
 end
 
 always #5 sys_clk = !sys_clk;
 
 initial begin
    #10 sys_rst_n = 1;
    #30 uart_rx = 0;  // 起始位
    #200 uart_rx = 0; 
    #200 uart_rx = 1;
    #200 uart_rx = 1;
    #200 uart_rx = 1;
    #200 uart_rx = 0;
    #200 uart_rx = 1;
    #200 uart_rx = 1;
    #200 uart_rx = 0;
    #200 uart_rx = 1; // 停止位
    #450
    $stop;
 end

 endmodule

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PUpUJd90-1669128642076)(https://gitee.com/wangchaosun/picgo/raw/master/uart_rx_sim.png)]

总结
三段式状态机

使用三个always 模块

  1. 第一个always模块采用同步时序描述状态转移
  2. 第二个always模块采用组合逻辑判断状态转移条件,描述状态转移规律
  3. 第三个always模块描述状态输出(可以使用组合电路输出,也可以使用时序电路输出)
对应代码结构

第一段

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) 
        curr_state <= IDLE_STATE;
    else
        curr_state <= next_state;
end

第二段

always @(*) begin
    case( curr_state )
       // ....
    endcase
end

第三段

always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
	 //...
    end else begin
	//...
    end
end
第二段不使用同步时序逻辑的原因

always 的执行是并行的

倘若使用同步时序逻辑,则:

  • 第一段的内容: curr_state <-- next_state

    将下一状态变为当前状态,状态更新

  • 第二段的内容:需要根据curr_state的值结合其他条件,得出下一状态

  • 第一段改变curr_state的值,第二段需要使用curr_state的值,并且两者是并行执行的,会形成冲突,可能使得第二段使用的curr_state是未更新前的,导致状态转移的错误。

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

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

相关文章

配电站房监控系统方案

配电站为低压用户配送电能&#xff0c;设有中压进线(可有少量出线)、配电变压器和低压配电装置。计讯物联工业网关下配电站房监控系统方案&#xff0c;24小时对运行设备进行不间断数据采集上传服务器&#xff0c;云平台对接&#xff0c;远程实时在线监控设备运行状态 &#xff…

web前端-javascript-标识符(说明,命名规则、不以数字关键字保留字开头、驼峰命名,补充)

文章目录标识符1. 说明2. 命名规则3. 补充标识符 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><script type"text/javascript">//千万不要这么用/* var if 123;console.log(if); *//…

Linux、阿里云服务器用tomcat部署项目

文章目录一、安装JDK和Tomcat1.1 安装JDK2.2 安装Tomcat二、把项目打包成war包&#xff08;jar也可以&#xff0c;但是有区别&#xff09;三、把war包放进webapps里面四、修改tomcat配置五、修改防火墙和开放端口等设置六、在浏览器访问项目一、安装JDK和Tomcat 1.1 安装JDK …

如果你想跨行转做数据分析师,劝你慎重

随着数字化时代的浪潮&#xff0c;数据分析师成了炽手可热的香饽饽&#xff0c;疫情当下&#xff0c;各行各业的失业人员逐渐增多&#xff0c;所以人人都想转行当数据分析师。作为业内人员&#xff0c;说实话&#xff0c;真的不建议&#xff0c;数据分析师真的不是想象的那么简…

【Webpack】webpack的基础使用详细总结 上(建议收藏)

1- 前言&#xff08;前端工程化&#xff09; 实际的前端开发&#xff1a; 模块化&#xff08;js 的模块化、css 的模块化、其它资源的模块化组件化&#xff08;复用现有的 UI 结构、样式、行为&#xff09;规范化&#xff08;目录结构的划分、编码规范化、接口规范化、文档规范…

代码随想录算法训练营第六天|LeetCode 242. 有效的字母异位词 、349. 两个数组的交集 、 202. 快乐数、1. 两数之和

LeetCode 242. 有效的字母异位词 题目链接&#xff1a;242. 有效的字母异位词 方法一&#xff1a; 分析&#xff1a; 两个字符串里的每个字母的个数相等&#xff0c;那么我对每个字符串里的字符串都进行下排序&#xff0c;排出来后岂不是两个字符串的每个字母如果一一对应就…

高通平台开发系列讲解(AI篇)高通神经网络处理引擎工作流程详解

文章目录 一、Model to Runtime Workflow(模型运行流程)二、Basic SNPE Workflow(基本工作流程)2.1、Converting a Network Model(模型转换)2.2、Quantizing a Model(模型量化)沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要介绍高通平台神经网络处…

K_A07_002 基于 STM32等单片机驱动ULN2003模块按键控制步进电机正反转

目录 一、资源说明 二、基本参数 1.参数 2、引脚说明 三、驱动说明 步进电机驱动时序 反向输出 对应程序: 四、部分代码说明 1、接线说明 1.1、STC89C52RCULN2003模块 1.2、STM32F103C8T6ULN2003模块 五、基础知识学习与相关资料下载 六、视频效果展示与程序资料获取 七…

C#使用EPPlus操作Excel(读写)

之所以使用EPPlus操作Excel是因为微软自带的运行效率太低&#xff0c;数据多后会特别慢&#xff0c;不能满足现场要求。如果想速度快&#xff0c;而且只是读取Excel的配置还有另一个办法就是将Excel保存成xml文件&#xff0c;参考我的另一个文章&#xff1a;C# 读取XML格式的Ex…

概率统计·大数定律及中心极限定理【大数定律、中心极限定律】

这一章的学习更多的是为后面的知识作铺垫&#xff0c;所以内容比较少&#x1f358;&#x1f358;&#x1f358;&#xff08;当然也减轻一点复习的负担&#x1f917;&#x1f917;&#x1f917;&#xff09; 依概率收敛 需要概率P极限趋近于1 切比雪夫不等式的特殊情况 前提&…

数据库-sql执行深度剖析以及redo log和undo log(下)(二)

目录 buffer pool change Buffer Log Buffer redo log 随机IO/顺序IO redo log刷盘时机 redo logt特点 redo log结构 Adaptive Hash Index 磁盘区域 undo log 总结更新流程 BInlog 基于上一章sql执行原理基础上&#xff0c;我们来深入探讨sql更新的整个原理。 bu…

力扣(LeetCode)33. 搜索旋转排序数组(C++)

二分查找 二分的本质&#xff0c;是对某种性质的划分&#xff0c;一半满足&#xff0c;另一半不满足&#xff0c;即可划分。 比较 nums[mid]nums[mid]nums[mid] 和 nums[0]nums[0]nums[0] &#xff0c;可以知道 midmidmid 左右哪一端有序。 如果左端有序&#xff0c;我们找往…

66.基于Django学习会话技术

1. 背景介绍 ​ HTTP协议有一个特性就是无状态的&#xff0c;是指协议对于交互性场景没有记忆能力。 ​ 随着动态交互的web应用的出现&#xff0c;HTTP的无状态特性严重阻碍了动态交互应用程序的发展&#xff0c;例如一些购物网站在进行购物时候都会进行了页面跳转/刷新&…

西门子 S7-1200 与 BL200PN 通信示例

准备 IO 模块&#xff1a;耦合器 BL200PN、数字量输出模块 M2082、数字量输入 M1081、 模拟量输入模块 M3401、模拟量输出 M4043。 2、BL200PN、S7-1200、PC 要同一局域网。将 BL200PN 和 S7-1200 上电&#xff0c;打开西 门子 TIA V13 软件&#xff0c;新建项目“BL200PN”…

SpringBoot开发的实用小工具集,YYDS

真正的大师,永远都怀着一颗学徒的心&#xff01; 一、项目简介 springboot开发的实用小工具集 环境搭建说明 开发环境为jdk1.8&#xff0c;基于maven构建&#xff1b; 使用Idea或者eclipase开发&#xff1b; 基于SpringBoot搭建&#xff0c;大大简化了配置操作&#xff1b;…

DBCO-mPEG2000,二苯并环辛炔-mPEG MW 2000具有亲和力和稳定性

DBCO-mPEG2000白色固体&#xff0c; DBCO试剂在水性缓冲液中具有快速的亲和力和稳定性&#xff0c;可用于以高特异性和反应性标记叠氮化物修饰的生物分子。带有 PEG 臂的试剂会增加化合物的亲水性。西安凯新生物科技有限公司​DBCO 试剂已广泛应用于生物偶联、标记和化学生物学…

你需要知道的前端知识点,V8引擎是什么?采用哪些GC算法?

一、简单介绍V8引擎 V8引擎是一款主流的JavaScript执行引擎;V8执行引擎采用及时编译&#xff08;执行速度提升&#xff09;;V8引擎中内存设置有上限&#xff08;下方进行详解&#xff09;; 二、V8回收策略 采用分代回收思想;内存分为新生代对象存储与老生代对象存储;针对不同…

Colmap 实用教程 —— Command-line Interface

https://colmap.github.io/index.html Windows 通过 COLMAP.bat&#xff0c;Linux 通过 colmap 使用命令行调用 Colmap 工具。 Structure-from-Motion 简要介绍 从大范围来看的话&#xff0c;整个流程可以分成以下三个阶段&#xff1a; Feature detection and extractionFe…

MQ通道常用知识列举(一)

MQ的几个基本组件&#xff1a; 1. 什么是通道 通道是分布式队列管理器在IBM MQ MQI 客户端和IBM MQ服务器之间或两个IBM MQ服务器之间使用的逻辑通信链路。通道用于将消息从一个队列管理器移动到另一个队列管理器。 2, 启动通道 对发送方、服务器和请求方通道使用 MQSC 命令…

CentOS 7.6安装JDK8过程(通过官网下载压缩包方式)

Oracle官网JDK下载地址 JDK8下载地址 选择tar.gz压缩包进行下载 复制下载链接&#xff0c;登录服务器&#xff0c;执行wget xxxxx&#xff08;刚刚复制的下载地址&#xff09; 下载完毕后&#xff0c;执行解压命令tar -zxvf xxxxx.tar.gz 配置/etc/profile 执行命令vim /etc…