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

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. 上板验证(通过串口调试助手发送数据)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《CSS 简易速速上手小册》第7章:CSS 预处理器与框架(2024 最新版)

文章目录 7.1 Sass&#xff1a;更高效的 CSS 编写7.1.1 基础知识7.1.2 重点案例&#xff1a;主题颜色和字体管理7.1.3 拓展案例 1&#xff1a;响应式辅助类7.1.4 拓展案例 2&#xff1a;深色模式支持 7.2 Bootstrap&#xff1a;快速原型设计和开发7.2.1 基础知识7.2.2 重点案例…

微信小程序的了解和使用

微信小程序 微信小程序的项目组成 pages 文件夹 用于存放所有的小程序页面 logs 文件夹 用于存放所有的日志文件 utils 文件夹 用于存放工具性质的模块 js app.js 小程序的入口文件 app.json 小程序的全局配置文件 app.wxss 全局样式文件 project.config.json 项目配置文…

解放双手!ChatGPT助力编写JAVA框架!

摘要 本文介绍了使用 ChatGPT逐步创建 一个简单的Java框架&#xff0c;包括构思、交流、深入优化、逐步完善和性能测试等步骤。 亲爱的Javaer们&#xff0c;在平时编码的过程中&#xff0c;你是否曾想过编写一个Java框架去为开发提效&#xff1f;但是要么编写框架时感觉无从下…

4核8g服务器能支持多少人访问?2024新版测评

腾讯云轻量4核8G12M轻量应用服务器支持多少人同时在线&#xff1f;通用型-4核8G-180G-2000G&#xff0c;2000GB月流量&#xff0c;系统盘为180GB SSD盘&#xff0c;12M公网带宽&#xff0c;下载速度峰值为1536KB/s&#xff0c;即1.5M/秒&#xff0c;假设网站内页平均大小为60KB…

程序员如何 “高效学习”?

开篇先说说我吧&#xff0c;马上人生要步入30岁的阶段&#xff0c;有些迷茫&#xff0c;更多的是焦虑&#xff0c;因为行业的特殊性导致我无时无刻不对 “青春饭” 的理论所担忧。担忧归担忧&#xff0c;生活还要继续&#xff0c;我们都知道这行全靠 “学习” 二字&#xff0c;…

树莓派编程基础与硬件控制

1.编程语言 Python 是一种泛用型的编程语言&#xff0c;可以用于大量场景的程序开发中。根据基于谷歌搜 索指数的 PYPL&#xff08;程序语言流行指数&#xff09;统计&#xff0c;Python 是 2019 年 2 月全球范围内最为流行 的编程语言 相比传统的 C、Java 等编程语言&#x…

【Linux】Linux下的基本指令

Linux下的基本指令 Linux 的操作特点&#xff1a;纯命令行ls 指令文件 pwd命令Linux的目录结构绝对路径 / 相对路径&#xff0c;我该怎么选择&#xff1f; cd指令touch指令mkdir指令&#xff08;重要&#xff09;rmdir指令rm 指令&#xff08;重要&#xff09;man指令&#xff…

静态时序分析:建立时间分析

静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 在静态时序分析中&#xff0c;建立时间检查约束了触发器时钟引脚&#xff08;时钟路径&#xff09;和输入数据引脚&#xff08;数据路径&#xff09;之间的时序关系&#x…

HSM加密机原理:密钥管理和加密操作从软件层面转移到物理设备中 DUKPT 安全行业基础8

HSM加密机原理 硬件安全模块&#xff08;HSM&#xff09;是一种物理设备&#xff0c;设计用于安全地管理、处理和存储加密密钥和数字证书。HSM广泛应用于需要高安全性的场景&#xff0c;如金融服务、数据保护、企业安全以及政府和军事领域。HSM提供了一种比软件存储密钥更安全…

【前端高频面试题--Vue基础篇】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;前端高频面试题 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac;前端高频面试题--Vue基础篇 Vue基本原理双向绑定与MVVM模型Vue的优点计算属性与监听属性计算属性监…

QGIS Desktop工具转换png文件为TIF文件

https://qgis.org/ QGIS Desktop 3.34.3 菜单 Layer -> Georeferencer 选择 文件 点击 加载的图片的左上角的点 弹窗中没有数据 录入 0 0 再加入右侧下面的点 调整下 数据 点击 绿色的箭头按钮 输出TIF文件 GeoServer中添加 存储仓库 点击 GeoTIFF 录入&#xff0c;选…

51单片机编程应用(C语言):篮球比赛计分器

设计思路 1.LCD1602显示A 000:B 000 右下角显示24的数字&#xff0c;显示一节时间12&#xff1a;00. 2.规定矩阵键盘每一位表示的含义 s1按下&#xff0c;A队加一分 s2按下&#xff0c;A队加两分 s3按下&#xff0c;A队加三分 s4按下&#xff0c;A队减一分 如…

搜索二维矩阵[中等]

一、题目 给你一个满足下述两条属性的m x n整数矩阵&#xff1a; 【1】每行中的整数从左到右按非严格递增顺序排列。 【2】每行的第一个整数大于前一行的最后一个整数。 给你一个整数target&#xff0c;如果target在矩阵中&#xff0c;返回true&#xff1b;否则&#xff0c;返…

猫头虎分享已解决Bug || 日志文件过大(Log File Oversize):LogFileOverflow, ExcessiveLoggingError

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

数据分析基础之《pandas(6)—高级处理》

一、缺失值处理 1、如何处理nan 两种思路&#xff1a; &#xff08;1&#xff09;如果样本量很大&#xff0c;可以删除含有缺失值的样本 &#xff08;2&#xff09;如果要珍惜每一个样本&#xff0c;可以替换/插补&#xff08;计算平均值或中位数&#xff09; 2、判断数据是否…

springboot178智能学习平台系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

【Make编译控制 06】CMake初步使用

目录 一、概述与安装 二、编译源文件 三、无关文件管理 一、概述与安装 CMake是一个跨平台的项目构建工具&#xff0c;相比于Makefile&#xff0c;CMake更加高级&#xff0c;因为CMake代码在执行的时候是会先翻译生成Makefile文件&#xff0c;再调用Makefile文件完成项目构…

【Linux】学习-基础IO—上

Linux基础IO—上 复习c语言接口 你真的懂文件吗&#xff1f; 文件的打开与关闭 深入了解文件读与写(C语言级别) 系统文件I/O 我们知道&#xff0c;文件是放在磁盘(硬件)上的&#xff0c;我们用代码访问文件的思路是&#xff1a; 写代码 -> 编译 -> 生成可执行exe …

fast.ai 机器学习笔记(三)

机器学习 1&#xff1a;第 8 课 原文&#xff1a;medium.com/hiromi_suenaga/machine-learning-1-lesson-8-fa1a87064a53 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 来自机器学习课程的个人笔记。随着我继续复习课程以“真正”理解它&#xff0c;这些笔记将继续更…

【北邮鲁鹏老师计算机视觉课程笔记】03 edge 边缘检测

【北邮鲁鹏老师计算机视觉课程笔记】03 1 边缘检测 有几种边缘&#xff1f; ①实体上的边缘 ②深度上的边缘 ③符号的边缘 ④阴影产生的边缘 不同任务关注的边缘不一样 2 边缘的性质 边缘在信号突变的地方 在数学上如何寻找信号突变的地方&#xff1f;导数 用近似的方法 可以…