13. 串口接收模块的项目应用案例

news2024/11/17 22:31:42

1. 使用串口来控制LED灯工作状态

使用串口发送指令到FPGA开发板,来控制第7课中第4个实验的开发板上的LED灯的工作状态。

LED灯的工作状态:让LED灯按指定的亮灭模式亮灭,亮灭模式未知,由用户指定,8个变化状态为一个循环,每个变化状态的时间值可以根据不同的应用场景来选择。

1.1 原理如下

1.2 任务简化为:

如何使用串口接收8个字节的数据,判断并输出其中的数据。

2. 写设计代码,仿真代码并仿真

2.1 设计代码(重要)

1.在完成uart_cmd解析数据模块时,要掌握移位寄存器的使用,以及延后一拍判定的方法

2.修改了uart_byte_rx1串口接收模块代码,修改了rx_data八位数据接收条件,原先是发送了在出现tx_done信号后得到输出八位数据,这样会导致我们在后续模块通过tx_done信号接收数据时接收的是前一个八位数据。现在改为了tx_done信号出现时,八位数据已准备输出了。

3.修改了counter_led灯模块代码

module uart_rx_ctrl_led(
    clk,
    rstn,
    uart_rx,
    blaud_set,
    led
);

    input clk;
    input rstn;
    input uart_rx;
    input [2:0]blaud_set;
    output led;
    
    wire [7:0]data;
    wire rx_done;
    wire [7:0] ctrl;
    wire [31:0] times;
    
    uart_byte_rx1 uart_byte_rx1_inst(
        .clk(clk),
        .rstn(rstn),
        .blaud_set(blaud_set),
        .uart_rx(uart_rx),
        .data(data),
        .rx_done(rx_done)
    );
    
    uart_cmd uart_cmd_inst(
        .clk(clk),
        .rstn(rstn),
        .rx_data(data),
        .rx_done(rx_done),
        .ctrl(ctrl),
        .times(times)
    );
        
    counter_led4 counter_led4_inst(
        .clk(clk),
        .rstn(rstn),
        .ctrl(ctrl),
        .times(times),
        .led(led)
    );
    
endmodule
module uart_byte_rx1(
    clk,
    rstn,
    blaud_set,
    uart_rx,
    data,
    rx_done
);

    input clk;
    input rstn;
    input [2:0]blaud_set;
    input uart_rx;
    output reg [7:0] data;
    output rx_done;

    reg [8:0] bps_dr;
    always@(*)
        case(blaud_set)
            0:bps_dr = 1000000000/9600/16/20;
            1:bps_dr = 1000000000/19200/16/20;
            2:bps_dr = 1000000000/38400/16/20;
            3:bps_dr = 1000000000/57600/16/20;
            4:bps_dr = 1000000000/115200/16/20;
            default : bps_dr = 1000000000/9600/16/20;
        endcase
        
    //边沿信号检测
    reg [1:0] uart_rx_r; //用两位寄存器分别存储两个时间沿的uart_rx信号
    always@(posedge clk) begin
        uart_rx_r[0] <= uart_rx;
        uart_rx_r[1] <= uart_rx_r[0];
    end
    
    //将两位寄存器的值直接通过导线输出进行判断(不需要再使用寄存器)
    wire nedge_uart_rx;  //掌握一下这个方法,之前一直使用的是寄存器
    //法一:
    //assign nedge_uart_rx = ((uart_rx_r[0] == 0)&&(uart_rx_r == 1));
    //法二:
    assign nedge_uart_rx = (uart_rx_r == 2'b10);
    
    reg rx_en;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        rx_en <= 0;
    else if(nedge_uart_rx)
        rx_en <= 1;
    else if(rx_done)
        rx_en <= 0;
    
    
    //周期计数器
    reg [8:0] div_cnt;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        div_cnt <= 0;
    else if(rx_en) begin
        if(div_cnt == bps_dr - 1)
            div_cnt <= 0;
        else
            div_cnt <= div_cnt + 1'd1;
    end
    else
        div_cnt <= 0;
    
    
    wire [3:0]bps_clk_16x; //(一定要记得加位宽)采样信号,这种写法很灵活
    assign bps_clk_16x = bps_dr/2; //采样每一段的中点值,同时也可以用它来计数。
    
    //发送一字节的数据有需要十个数据位,每位数据有16个小段供采样,共160
    reg [7:0]bps_cnt;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        bps_cnt <= 0;
    else if(rx_en) begin
        if(bps_cnt == 159)
            bps_cnt <= 0;
        else if(div_cnt ==bps_clk_16x)
            bps_cnt <= bps_cnt + 1'd1; 
    end
    else  
        bps_cnt <= 0;
    
    reg[2:0] r_data[7:0];//二维数据,代表八个r_data,每个r_data有3位寄存器存储数值。
    reg[2:0] sta_data;
    reg[2:0] sto_data;
    always@(posedge clk or negedge rstn)
    if(!rstn)begin
        sta_data <= 0;
        sto_data <= 0;
        r_data[0] <= 0; //语法规定,二维数组赋值要分开赋值
        r_data[1] <= 0;    
        r_data[2] <= 0;
        r_data[3] <= 0;    
        r_data[4] <= 0;  
        r_data[5] <= 0;    
        r_data[6] <= 0; 
        r_data[7] <= 0;        
    end
    else if(div_cnt == bps_clk_16x - 1)
    case(bps_cnt) //下面合在一起的写法是允许的
        0:begin
        r_data[0] <= 0; 
        r_data[1] <= 0;    
        r_data[2] <= 0;
        r_data[3] <= 0;    
        r_data[4] <= 0;  
        r_data[5] <= 0;    
        r_data[6] <= 0; 
        r_data[7] <= 0;
        end   
        5,6,7,8,9,10,11: sta_data <= sta_data + uart_rx;
        21,22,23,24,25,26,27: r_data[0] <= r_data[0] + uart_rx;
        37,38,39,40,41,42,43: r_data[1] <= r_data[1] + uart_rx;
        53,54,55,56,57,58,59: r_data[2] <= r_data[2] + uart_rx;
        69,70,71,72,73,74,75: r_data[3] <= r_data[3] + uart_rx;
        85,86,87,88,89,90,91: r_data[4] <= r_data[4] + uart_rx;
        101,102,103,104,105,106,107: r_data[5] <= r_data[5] + uart_rx;
        117,118,119,120,121,122,123: r_data[6] <= r_data[6] + uart_rx;
        133,134,135,136,137,138,139: r_data[7] <= r_data[7] + uart_rx;
        149,150,151,152,153,154,155: sto_data <= sto_data + uart_rx;
        default:;
    endcase        
    
    reg rx_done;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        rx_done <= 0;
    else if(bps_cnt == 159) begin
        rx_done <= 1;
    end
    else
        rx_done <= 0;
    
    //数据接收完成后赋值给data输出
    always@(posedge clk or negedge rstn)
    if(!rstn)
        data <= 0;
    else if(bps_cnt == 159)begin //修改了之前的判定条件(重要)
        data[0] <= (r_data[0] >= 4 ) ? 1 : 0;
        data[1] <= (r_data[1] >= 4 ) ? 1 : 0;
        data[2] <= (r_data[2] >= 4 ) ? 1 : 0;
        data[3] <= (r_data[3] >= 4 ) ? 1 : 0;
        data[4] <= (r_data[4] >= 4 ) ? 1 : 0;
        data[5] <= (r_data[5] >= 4 ) ? 1 : 0;
        data[6] <= (r_data[6] >= 4 ) ? 1 : 0;
        data[7] <= (r_data[7] >= 4 ) ? 1 : 0;
    end
    
     // data[1] <= r_data[1][2]
    
    // 0:3'd000
    // 1:3'd001
    // 2:3'd010
    
    // 4:3'd100
    // 5:3'd101
    // 6:3'd110
    // 7:3'd111 利用第3位的区别给data赋值
    
endmodule

module uart_cmd(
    clk,
    rstn,
    rx_data,
    rx_done,
    ctrl,
    times
);

    input clk;
    input rstn;
    input [7:0]rx_data;
    input rx_done;
    output reg[7:0]ctrl;
    output reg[31:0]times;

    reg [7:0] data_str[7:0];
    always@(posedge clk)
    if(rx_done)begin
        data_str[7] <= rx_data;
        data_str[6] <= data_str[7];
        data_str[5] <= data_str[6];
        data_str[4] <= data_str[5];
        data_str[3] <= data_str[4];
        data_str[2] <= data_str[3];
        data_str[1] <= data_str[2];
        data_str[0] <= data_str[1];
    end
    
//  使判断并取数据的触发条件在存数据的后一拍
//    reg r_rx_done;
//    always@(posedge clk)
//    if(rx_done)
//        r_rx_done <= rx_done;
    reg r_rx_done;
    always@(posedge clk)
    if(rx_done)
        r_rx_done <= 1;
    else 
        r_rx_done <= 0;

    always@(posedge clk or negedge rstn)
    if(!rstn)begin
        times <= 0;
        ctrl <= 0;
    end
    else 
    if(r_rx_done)
        if((data_str[0] == 8'h55) && (data_str[1] == 8'hA5) && (data_str[7] == 8'hF0))begin
            times[7:0] <= data_str[2];
            times[15:8] <= data_str[3];
            times[23:16] <= data_str[4];
            times[31:24] <= data_str[5];
            ctrl[7:0] <= data_str[6];
        end
    else begin
        ctrl <= ctrl;
        times <= times;
    end
endmodule
module counter_led4(
	clk,
	rstn,
	ctrl,
	times,
	led
);
	
	input clk;
	input rstn;
	input [7:0] ctrl; 
	input [31:0] times; 
	output reg led;
	
	reg[31:0] counter;
	always@(posedge clk or negedge rstn)
	if(!rstn)
		counter <= 0;
//为了防止times为0时减1会得到非常到的数值导致归0时间很长(重要)
	else if(counter >= times - 1'd1) 
		counter <= 0;
	else
		counter <= counter + 1'd1;
    
    reg [2:0]counter2;
    always@(posedge clk or negedge rstn)
	if(!rstn)
		counter2 <= 0;
	else if(counter == times - 1'd1)
	   counter2 <= counter2 + 1'd1;
		
		
	always@(posedge clk or negedge rstn)
	if(!rstn)  
	    led <= 0;
	else case(counter2)
	   0 : led <= ctrl[0];
	   1 : led <= ctrl[1];
	   2 : led <= ctrl[2];
	   3 : led <= ctrl[3];
	   4 : led <= ctrl[4];
	   5 : led <= ctrl[5];
	   6 : led <= ctrl[6];
	   7 : led <= ctrl[7];
	   default : led <= led;
	endcase
	
endmodule

2.2 仿真代码

`timescale 1ns / 1ps

module uart_rx_ctrl_led_tb();

    reg clk;
    reg rstn;
    reg uart_rx;
    wire led;
    
    wire [2:0]blaud_set;
    assign blaud_set = 3'd4;

    uart_rx_ctrl_led uart_rx_ctrl_led_inst(
        .clk(clk),
        .rstn(rstn),
        .uart_rx(uart_rx),
        .blaud_set(blaud_set),
        .led(led)
    );
    
    initial clk = 1;
    always #10 clk = ~clk;
    
    initial begin
        rstn = 0;
        uart_rx = 1;
        #201;
        rstn = 1;
        #200;
        uart_tx_byte(8'h55);
        #90000;
        uart_tx_byte(8'ha5);
        #90000;
        uart_tx_byte(8'h9a);
        #90000;
        uart_tx_byte(8'h78);
        #90000;
        uart_tx_byte(8'h56);
        #90000;
        uart_tx_byte(8'h34);
        #90000;
        uart_tx_byte(8'h21);
        #90000;
        uart_tx_byte(8'hf0);
        #90000;
        $stop;

    end
    
    
    task uart_tx_byte;
        input [7:0] tx_data;
        begin
            uart_rx = 1;
            #20;
            uart_rx = 0;
            #8680;
            uart_rx = tx_data[0];
            #8680;
            uart_rx = tx_data[1];
            #8680;
            uart_rx = tx_data[2];
            #8680;
            uart_rx = tx_data[3];
            #8680;
            uart_rx = tx_data[4];
            #8680;
            uart_rx = tx_data[5];
            #8680;
            uart_rx = tx_data[6];
            #8680;
            uart_rx = tx_data[7];
        end
    endtask
    



endmodule

仿真波形

3. 调试(重要)

3.1 tx_done信号到来,data_str[]正确接收到了数据且符合协议,但times和ctrl却仍未0,并未产生正确输出。

通过波形分析原因

通过代码找分析原因

 

 3.2 模块接口给错导致的错误

 3.3 counter计数判定条件需要修改

4. 上板验证(通过串口调试助手发送数据)

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

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

相关文章

中创ET4410 台式LCR数字电桥 简单开箱测评

最近买了一台LCR电桥&#xff0c;完善一下自己实验室的设备&#xff0c;选了中创ET4410&#xff0c;这款性价比高一点。 1199元在PDD买的&#xff0c;好像胜利的VC4090C也是找中创代工的。 ET4410介绍 本系列LCR数字电桥是采用自动平衡电桥原理设计的元件参数分析仪&#xf…

linux进程(环境变量)

目录 正文&#xff1a; 常见环境变量 和环境变量相关的的命令 通过代码获取环境变量 主函数参数 三个参数 参数调用 进程优先级 查看系统进程 PRI和NI 优先级修改 前言&#xff1a; 环境变量 (environment variables) 一般是指在操作系统中用来指定操作系统运行环境…

第68讲表单验证实现

表单验证实现 Form 组件允许你验证用户的输入是否符合规范&#xff0c;来帮助你找到和纠正错误。 Form 组件提供了表单验证的功能&#xff0c;只需为 rules 属性传入约定的验证规则&#xff0c;并将 form-Item 的 prop 属性设置为需要验证的特殊键值即可。 const rulesref({u…

掌握Pandas数据筛选方法与高级应用全解析【第70篇—python:数据筛选】

文章目录 掌握Pandas&#xff1a;数据筛选方法与高级应用全解析1. between方法2. isin方法3. loc方法4. iloc方法5. 查询复杂条件的结合应用6. 避免inplace参数7. 利用Lambda函数进行自定义筛选8. 处理缺失值9. 多条件排序10. 数据统计与分组 总结&#xff1a; 掌握Pandas&…

NLP_引入注意力机制

文章目录 点积注意力创建两个张量x1和x2计算张量点积&#xff0c; 得到原始权重对原始权重进行归一化求出注意力分布的加权和 缩放点积注意力编码器-解码器注意力定义Attention类重构Decoder类重构Seq2Seq类可视化注意力权重 注意力机制中的 Q、K、V自注意力多头自注意力注意力…

Excel——重复项处理

一、高亮重复项 选择需要高亮重复项的列/单元格 选择【数据】——【重复项】—— 【高亮重复项】 如果高亮重复项的数据较长&#xff0c;例如&#xff1a;身份证号、银行卡&#xff0c;可以勾选下列选项&#xff0c;能够精准检查重复项。 结果如下所示 如果想要清除高亮的重复…

Java并发基础:ArrayBlockingQueue全面解析!

内容摘要 ArrayBlockingQueue类是一个高效、线程安全的队列实现&#xff0c;它基于数组&#xff0c;提供了快速的元素访问&#xff0c;并支持多线程间的同步操作&#xff0c;作为有界队列&#xff0c;它能有效防止内存溢出&#xff0c;并通过阻塞机制平衡生产者和消费者的速度…

【Qt 学习之路】在 Qt 使用 ZeroMQ

文章目录 1、概述2、ZeroMQ介绍2.1、ZeroMQ 是什么2.2、ZeroMQ 主线程与I/O线程2.3、ZeroMQ 4种模型2.4、ZeroMQ 相关地址 3、Qt 使用 ZeroMQ3.1、下载 ZeroMQ3.2、添加 ZeroMQ 库3.3、使用 ZeroMQ3.4、相关 ZeroMQ 案例 1、概述 今天是大年初一&#xff0c;先给大家拜个年&am…

c++2024寒假J312实战班2.4

长话短说&#xff0c;简明扼要一直是我的行事风格&#xff0c;如有不精准的地方&#xff0c;就到网上去搜&#xff0c;好吧。 今天分享我们做的四道题&#xff0c;都挺简单的&#xff0c;就是难思考。 题目列表&#xff1a; 1.Maximum Subarray Sum 2.分解因数 3.公交换乘 4.…

05-Java原型模式 ( Prototype Pattern )

原型模式 摘要实现范例 原型模式&#xff08;Prototype Pattern&#xff09;是用于创建重复的对象&#xff0c;同时又能保证性能原型模式实现了一个原型接口&#xff0c;该接口用于创建当前对象的克隆当直接创建对象的代价比较大时&#xff0c;则采用这种模式 例如&#xff0c…

用bootstrap结合jQuery实现简单的模态对话框

嗨害嗨&#xff0c;我又来了奥。今天呢&#xff0c;给大家分享一个工作中常用到的插件——模态对话框的用法。想必大家在工作中也遇到很多页面&#xff0c;需要用模态对话框进行交互的吧&#xff0c;现在呢&#xff0c;就让我们一起来了解一下它的使用吧。 首先&#xff0c;我…

《动手学深度学习(PyTorch版)》笔记8.3

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过&…

【RabbitMQ(二)】:Exchange 详解 | Message Convert 消息转换器

文章目录 03. 使用 Java 代码去操控 RabbitMQ3.1 快速入门3.1.1 创建父子项目3.1.2 编写代码 3.2 Work 模型3.3 RabbitMQ 中的三类交换机3.3.1 Fanout 扇出交换机3.3.2 Direct 交换机3.3.3 Topic 交换机 3.4 声明队列交换机3.4.1 方式一&#xff1a;书写 Config 类3.4.2 方式二…

MySQL篇之回表查询

一、聚集索引 将数据存储与索引放到了一块&#xff0c;索引结构的叶子节点保存了行数据。特点&#xff1a;必须有,而且只有一个。 聚集索引选取规则: 1. 如果存在主键&#xff0c;主键索引就是聚集索引。 2. 如果不存在主键&#xff0c;将使用第一个唯一&#xff08;UNIQUE&am…

算法学习——LeetCode力扣栈与队列篇2

算法学习——LeetCode力扣栈与队列篇2 150. 逆波兰表达式求值 150. 逆波兰表达式求值 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。…

ClickHouse--01--简介

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1. ClickHouse 简介官网&#xff1a; [https://clickhouse.com/docs/zh](https://clickhouse.com/docs/zh) 1.1 大数据处理场景1.2 什么是 ClickHouse1.3 OLAP 场景…

Tuxera NTFS2024永久免费版磁盘读写软件

Tuxera NTFS 2024由Tuxera公司开发&#xff0c;是一款专为Mac系统设计的NTFS磁盘读写软件。以下是这款软件的具体优势&#xff1a; 全面的读写功能&#xff1a;Tuxera NTFS 2024允许Mac用户全面读写NTFS格式的硬盘、U盘、SD卡等存储设备。这意味着用户可以在Mac上自由读取和写…

Android Graphics 图像显示系统 - 开篇

“ 随着学习的不断深入和工作经验的积累&#xff0c;欲将之前在博客中整理的Android Graphics知识做进一步整理&#xff0c;并纠正一些理解上的错误&#xff0c;故开设Graphics主题系列文章 ” 序言 由于工作需要&#xff0c;也源于个人兴趣&#xff0c;终于下决心花时间整理一…

gem5学习(19):gem5内存系统——The gem5 Memory System

目录 一、Model Hierarchy 二、CPU 三、Data Cache Object 四、Tags & Data Block 五、MSHR and Write Buffer Queues 六、Memory Access Ordering 七、Coherent Bus Object 八、Simple Memory Object 九、Message Flow 1、Memory Access Ordering&#xff08;re…

[每周一更]-(第86期):PostgreSQL入门学习和对比MySQL

入门学习PostgreSQL可以遵循以下步骤&#xff1a; 安装 PostgreSQL&#xff1a; 首先&#xff0c;你需要在你的计算机上安装 PostgreSQL。你可以从 PostgreSQL 官方网站 下载适合你操作系统的安装包&#xff0c;并按照官方文档的指导进行安装。 学习 SQL&#xff1a; PostgreS…