当前位置: 首页 > article >正文

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


http://www.kler.cn/news/234367.html

相关文章:

  • 华为数通方向HCIP-DataCom H12-821题库(单选题:441-460)
  • MacOS - 时间如何显示读秒?
  • 单片机的认识
  • 使用Qt创建项目 Qt中输出内容到控制台 设置窗口大小和窗口标题 Qt查看说明文档
  • Java 学习和实践笔记(3)
  • 火车可视化调车系统
  • 【MySQL】——数值函数的学习
  • kvm qemu 优化 windows 虚拟机速度
  • C++入门学习(二十七)跳转语句—continue语句
  • ubuntu20.04-编译安装Qt5.15.2-C++
  • 【后端高频面试题--Mybatis篇】
  • Vue-53、Vue技术vuex使用
  • 数据结构之基数排序
  • Pandas 对带有 Multi-column(多列名称) 的数据排序并写入 Excel 中
  • Java并发基础:LinkedBlockingDeque全面解析!
  • prometheus之redis_exporter部署
  • 数字孪生:构建未来智慧社区的关键技术
  • CVE-2022-0760 漏洞复现
  • 微服务OAuth 2.1认证授权可行性方案(Spring Security 6)
  • 爬虫为什么要使用代理?
  • Huggingface上传模型
  • 新型RedAlert勒索病毒针对VMWare ESXi服务器
  • PyTorch 2.2大更新!集成FlashAttention-2,性能提升2倍
  • 代码随想录 Leetcode55. 跳跃游戏
  • HiveSQL——设计一张最近180天的注册、活跃留存表
  • 自适应二次元404页面源码
  • antdpro框架npm install 报错,切换tyarn安装成功。
  • 2/7 算法每日N题(二分+双指针)
  • 【Java多线程案例】实现阻塞队列
  • Vue3快速上手(一)使用vite创建项目