串口通信控制LED灯
做这个东西的目的是锻炼一下自己的编程能力以及系统思维能力
首先,清楚自己要干什么,正点原子大家应该都看过,系统框图是一个比较重要的东西,引导我们去设计和思考。
下面先给出系统框图:
模块划分好后,结构就比较清晰了,可以分成三个文件实现,一个是LED控制模块,一个是串口接收模块,还有一个是顶层模块。
串口接收模块
module uart_recv ( input clk, input rst_n, input uart_rx, output reg [7:0] rx_data, output reg data_valid ); reg [1:0] signal; wire falling_edge; always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin signal[0] <= 1'd0; signal[1] <= 1'd0; end else begin signal[0] <= uart_rx; signal[1] <= signal[0]; end end assign falling_edge = ~signal[0] & signal[1]; //检测下降沿 reg rx_flag; always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin rx_flag <= 1'd0; end else if (falling_edge) begin rx_flag <= 1'd1; end else if (data_cnt == 4'd8) begin rx_flag <= 1'd0; end else begin rx_flag <= rx_flag; end end //115200bps 8680ns接收一位数据 系统时钟50M 20ns 每计数434次接收一位数据 reg [8:0] cnt; always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin cnt <= 9'd0; end else if (rx_flag && cnt < 9'd434) begin cnt <= cnt + 1; end else begin cnt <= 9'd0; end end reg [7:0] data; reg [3:0] data_cnt; always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin data <= 8'd0; data_cnt <= 4'd0; end else if (rx_flag && cnt == 9'd434) begin data <= {data[6:0],uart_rx}; data_cnt <= data_cnt + 1; end else if (rx_flag) begin data <= data; data_cnt <= data_cnt; end else data <= 8'd0; data_cnt <= 4'd0; end end always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin rx_data <= 8'd0; data_valid <= 1'd0; end else if (data_cnt == 4'd8) begin rx_data <= data; data_valid <= 1'd1; end else begin rx_data <= 8'd0; data_valid <= 1'd0; end end endmodule
对该模块进行仿真测试
`timescale 1ns / 1ps module uart_recv_tb; reg clk; reg rst_n; reg uart_rx; wire [7:0] rx_data; wire data_valid; // 实例化模块 uart_recv uut ( .clk(clk), .rst_n(rst_n), .uart_rx(uart_rx), .rx_data(rx_data), .data_valid(data_valid) ); // 时钟生成 initial begin clk = 1'b0; forever #10 clk = ~clk; // 50 MHz 时钟 end // 复位信号 initial begin rst_n = 1'b0; #50 rst_n = 1'b1; end // 模拟 UART 数据帧传输(起始位+8数据位+停止位) initial begin uart_rx = 1'b1; // 空闲状态 // 发送起始位(低电平) #8680 uart_rx = 1'b0; // 发送数据位 (10101011) #8680 uart_rx = 1'b1; #8680 uart_rx = 1'b0; #8680 uart_rx = 1'b1; #8680 uart_rx = 1'b0; #8680 uart_rx = 1'b1; #8680 uart_rx = 1'b0; #8680 uart_rx = 1'b1; #8680 uart_rx = 1'b1; // 发送停止位(高电平) #8680 uart_rx = 1'b1; // 等待接收完成 #50000 $finish; end endmodule
测试结果
检测到起始位:
读出数据正确,读有效信号拉高
LED控制模块
module led_control( input clk, input rst_n, input [7:0] data, input data_valid, output reg led ); always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin led <= 1'd0; end else if (data_valid && data == 8'h12) begin led <= 1'd1; end else if (data_valid && data == 8'h23) begin led <= 1'd0; end else begin led <= led; end end endmodule
顶层模块
module top( input clk, input rst_n, input uart_rx, output led ); wire [7:0] data; wire data_valid; uart_recv u_uart_recv( .clk (clk), .rst_n (rst_n), .uart_rx (uart_rx), .rx_data (data), .data_valid (data_valid) ); led_control u_led_control( .clk (clk), .rst_n (rst_n), .data (data), .data_valid (data_valid), .led (led) ); ila_0 your_instance_name ( .clk(clk), // input wire clk .probe0(led), // input wire [0:0] probe0 .probe1(data_valid), // input wire [0:0] probe1 .probe2(data) // input wire [7:0] probe2 ); endmodule
综合仿真
仿真结果正确,串口发送12时led点亮,串口发送23时,led熄灭
上板测试结果与仿真一致