蓝桥杯FPGA赛道第三次模拟题代码

一、顶层代码

module test#(
    parameter  SYS_CLK_FREQ = 26'd50_000_000,  // 系统时钟频率50MHz
    parameter  BAUD_RATE = 9600,               // 串口波特率
    parameter  CHECK_BIT = "None"              // 校验位类型(None表示无校验)
)(
    input  wire       sys_clk,    // 系统时钟输入
    input  wire       sys_rst,    // 系统复位(低电平有效)
    input  wire [3:0] key_in,     // 按键输入
    output wire [7:0] sel,        // 数码管位选信号
    output wire [7:0] seg,        // 数码管段选信号
    output reg  [7:0] led,        // LED指示灯
    output wire       tx          // 串口发送线
);

// ========== 状态定义 ==========
localparam STOP  = 3'd0;  // 停止状态
localparam START = 3'd1;  // 启动状态

// ========== 信号定义 ==========
wire [3:0]  key_data;      // 按键消抖后的数据
reg  [31:0] data;          // 数码管显示数据(8位BCD码)
reg  [3:0]  status_count;  // 当前状态(START/STOP)
reg  [7:0]  data_count;    // 计数器值
reg  [3:0]  tx_len;        // 串口发送数据长度

wire        start_send;    // 发送启动信号
reg         start_send_char;  // 字符发送启动标志
reg         start_send_status; // 状态发送启动标志
wire        tx_done;       // 发送完成标志

reg  [7:0]  char_idx;      // 当前发送字符索引
reg  [7:0]  tx_buffer[0:255]; // 发送缓冲区(存储ASCII字符)

reg         tx_done0;      // 发送完成信号打拍(同步用)
reg         tx_done1;      // 发送完成信号打拍(同步用)
wire        send_next;     // 发送下一个字符的触发信号

// ========== 模块实例化 ==========
// 数码管显示模块
seg seg_inst(
    .clk      (sys_clk),
    .rst      (sys_rst),
    .seg      (seg),
    .sel      (sel),
    .dsp_data (data)  // 显示数据输入
);

// 按键消抖模块
key key_inst(
    .sys_clk  (sys_clk),
    .sys_rst  (sys_rst),
    .key_in   (key_in),
    .key_data (key_data)  // 消抖后的按键数据
);

// 串口发送模块
uart_tx#(
    .CLOCK    (SYS_CLK_FREQ),
    .BUAD     (BAUD_RATE),
    .CHECK_BIT(CHECK_BIT)
) uart_tx_list(
    .clk         (sys_clk),
    .rst         (sys_rst),
    .tx_data     (tx_buffer[char_idx]),  // 当前发送字符
    .tx_data_vld (start_send),           // 发送使能
    .ready       (tx_done),              // 发送完成标志
    .tx          (tx)                    // 串口输出
);

// ========== LED控制逻辑 ==========
always @(posedge sys_clk or negedge sys_rst) begin
    if (!sys_rst)
        led <= 8'b1111_1111;  // 复位时LED全灭
    else if (status_count == START)
        led <= 8'b1111_1110;  // START状态时点亮最低位LED
    else
        led <= 8'b1111_1111;  // 其他状态LED全灭
end

// ========== 数码管显示数据处理 ==========
always @(posedge sys_clk or negedge sys_rst) begin
    if (sys_rst == 1'b0)
        // 复位时显示" 0"(11表示不显示)
        data <= {4'd10, 4'd11, 4'd11, 4'd11, 4'd11, 4'd11, 4'd11, 4'd0};
    else begin
        // 动态更新显示数据(BCD码转换)
        data[11:8] <= (data_count > 99) ? (data_count / 100) : 4'd11;  // 百位
        data[7:4]  <= (data_count > 9)  ? (data_count % 100 / 10) : 4'd11;  // 十位
        data[3:0]  <= data_count % 10;  // 个位
    end
end

// ========== 按键处理逻辑 ==========
always @(posedge sys_clk or negedge sys_rst) begin
    if (sys_rst == 1'b0) begin
        data_count <= 8'b0;        // 计数器清零
        status_count <= START;     // 初始状态为START
        start_send_char <= 0;      // 发送标志清零
    end else begin
        case (key_data)  // 根据按键值更新状态
            4'b0001: begin  // 按键1:切换START/STOP状态
                if (status_count == START)
                    status_count <= STOP;
                else if (status_count == STOP)
                    status_count <= START;
            end
            4'b0010: begin  // 按键2:计数器加1(仅在START状态)
                if (status_count == START)
                    data_count <= data_count + 8'd1;
            end
            4'b0100: begin  // 按键3:计数器减1(仅在STOP状态且值>0)
                if ((status_count == STOP) && (data_count > 0))
                    data_count <= data_count - 1;
            end
            4'b1000: begin  // 按键4:触发串口发送
                start_send_char <= 1;
            end
            default: start_send_char <= 0;  // 无按键时清零
        endcase
    end
end

// ========== 发送缓冲区初始化 ==========
always @(posedge sys_clk or negedge sys_rst) begin
    if (!sys_rst) begin
        // 初始化发送缓冲区内容为"COUNT:"
        tx_buffer[0] <= "C";
        tx_buffer[1] <= "O";
        tx_buffer[2] <= "U";
        tx_buffer[3] <= "N";
        tx_buffer[4] <= "T";
        tx_buffer[5] <= ":";
    end
end

// ========== 串口发送数据格式化 ==========
always @(posedge sys_clk or negedge sys_rst) begin
    if (!sys_rst) begin
        tx_len <= 7;  // 默认发送长度("COUNT:X")
    end else begin
        // 根据计数器值动态生成ASCII字符
        if (data_count > 99) begin  // 3位数
            tx_len <= 9;
            tx_buffer[6] <= data_count / 100 + 8'h30;    // 百位ASCII
            tx_buffer[7] <= data_count % 100 / 10 + 8'h30; // 十位ASCII
            tx_buffer[8] <= data_count % 10 + 8'h30;      // 个位ASCII
        end else if (data_count > 9) begin  // 2位数
            tx_len <= 8;
            tx_buffer[6] <= data_count / 10 + 8'h30;     // 十位ASCII
            tx_buffer[7] <= data_count % 10 + 8'h30;      // 个位ASCII
        end else begin  // 1位数
            tx_len <= 7;
            tx_buffer[6] <= data_count + 8'h30;           // 个位ASCII
        end
    end
end

// ========== 串口发送完成检测 ==========
always @(posedge sys_clk or negedge sys_rst) begin
    if (!sys_rst) begin
        tx_done0 <= 0;
        tx_done1 <= 0;
    end else begin
        tx_done0 <= tx_done;  // 打拍同步
        tx_done1 <= tx_done0; // 用于边沿检测
    end
end

// 发送完成信号的上升沿检测
assign send_next = ~tx_done1 && tx_done0;

// 发送使能信号(按键触发或状态机触发)
assign start_send = start_send_char || start_send_status;

// ========== 串口发送状态机 ==========
always @(posedge sys_clk or negedge sys_rst) begin
    if (!sys_rst) begin
        char_idx <= 0;
        start_send_status <= 0;
    end else begin
        if (start_send) begin  // 发送使能有效
            if (char_idx < tx_len - 1) begin  // 未发送完所有字符
                start_send_status <= 1;
                if (send_next)  // 检测到发送完成上升沿
                    char_idx <= char_idx + 1;  // 发送下一个字符
            end else begin
                start_send_status <= 0;  // 发送完成
            end
        end else
            char_idx <= 0;  // 发送结束或未启动时复位索引
    end
end

endmodule

二、数码管模块 

// 数码管驱动模块
// 功能:实现8位数码管的动态扫描显示
module seg
(
    input      wire        clk,        // 系统时钟输入(假设50MHz)
    input      wire        rst,        // 异步复位信号,低电平有效
    input      wire [31:0] dsp_data,   // 显示数据输入,32位分为8个4位BCD码
    output     reg  [7:0]  seg,        // 段选信号输出,控制显示内容
    output     reg  [7:0]  sel         // 位选信号输出,控制数码管选择
);

// ===== 数码管段码定义(共阴极数码管)=====
// 格式:DP g f e d c b a (高位到低位)
localparam [7:0] DIGIT0 = 8'b1100_0000; // 数字0的段码(C0)
localparam [7:0] DIGIT1 = 8'b1111_1001; // 数字1的段码(F9)
localparam [7:0] DIGIT2 = 8'b1010_0100; // 数字2的段码(A4)
localparam [7:0] DIGIT3 = 8'b1011_0000; // 数字3的段码(B0)
localparam [7:0] DIGIT4 = 8'b1001_1001; // 数字4的段码(99)
localparam [7:0] DIGIT5 = 8'b1001_0010; // 数字5的段码(92)
localparam [7:0] DIGIT6 = 8'b1000_0010; // 数字6的段码(82)
localparam [7:0] DIGIT7 = 8'b1111_1000; // 数字7的段码(F8)
localparam [7:0] DIGIT8 = 8'b1000_0000; // 数字8的段码(80)
localparam [7:0] DIGIT9 = 8'b1001_0000; // 数字9的段码(90)
localparam [7:0] DIGITX = 8'b1011_1111; // 特殊符号"X"的段码
localparam [7:0] DIGOFF = 8'b1111_1111; // 关闭显示的段码(FF)
localparam [7:0] DIGITC = 8'hC6;        // 特殊字符"C"的段码

// 1ms计数器的最大值(50MHz时钟下,50000个周期=1ms)
localparam DSP_COUNT = 20'd50000; 

// ===== 内部寄存器定义 =====
reg [19:0] dsp_count;  // 1ms计数器
reg [3:0]  bits;       // 当前扫描的数码管编号(0-7)
reg [3:0]  bcd;        // 当前数码管对应的BCD码

// ===== 1ms定时计数器 =====
always @(posedge clk or negedge rst) begin
    if(!rst) begin
        dsp_count <= 20'd0;  // 复位时清零计数器
    end else begin
        if (dsp_count == DSP_COUNT-1) begin
            dsp_count <= 20'd0;  // 计数到1ms时归零
        end else begin
            dsp_count <= dsp_count + 20'd1;  // 计数器递增
        end
    end
end

// ===== 位选控制逻辑 =====
always @(posedge clk or negedge rst) begin
    if(!rst) begin
        sel <= 8'b1111_1111;  // 复位时关闭所有数码管
        bits <= 4'd0;         // 复位时从第0位数码管开始
    end else begin
        if(dsp_count == DSP_COUNT-1) begin  // 每1ms更新一次
            if(bits == 4'd8) begin
                bits <= 4'd0;  // 扫描完8位数码管后归零
            end else begin
                bits <= bits + 4'd1;  // 指向下一个数码管
            end
            
            // 根据当前数码管编号选择对应的位选信号和BCD码
            case(bits)
                4'd0: begin sel <= 8'b1111_1110; bcd <= dsp_data[31:28]; end  // 第1位数码管
                4'd1: begin sel <= 8'b1111_1101; bcd <= dsp_data[27:24]; end  // 第2位数码管
                4'd2: begin sel <= 8'b1111_1011; bcd <= dsp_data[23:20]; end  // 第3位数码管
                4'd3: begin sel <= 8'b1111_0111; bcd <= dsp_data[19:16]; end  // 第4位数码管
                4'd4: begin sel <= 8'b1110_1111; bcd <= dsp_data[15:12]; end  // 第5位数码管
                4'd5: begin sel <= 8'b1101_1111; bcd <= dsp_data[11:8];  end  // 第6位数码管
                4'd6: begin sel <= 8'b1011_1111; bcd <= dsp_data[7:4];   end  // 第7位数码管
                4'd7: begin sel <= 8'b0111_1111; bcd <= dsp_data[3:0];   end  // 第8位数码管
                default: sel <= 8'b1111_1111;  // 默认关闭所有数码管
            endcase
        end
    end
end

// ===== 段选控制逻辑 =====
always @(posedge clk or negedge rst) begin
    if(!rst) begin
        seg <= DIGOFF;  // 复位时关闭显示
    end else begin
        // 根据BCD码选择对应的段码
        case(bcd)
            4'd0:  seg <= DIGIT0;  // 显示数字0
            4'd1:  seg <= DIGIT1;  // 显示数字1
            4'd2:  seg <= DIGIT2;  // 显示数字2
            4'd3:  seg <= DIGIT3;  // 显示数字3
            4'd4:  seg <= DIGIT4;  // 显示数字4
            4'd5:  seg <= DIGIT5;  // 显示数字5
            4'd6:  seg <= DIGIT6;  // 显示数字6
            4'd7:  seg <= DIGIT7;  // 显示数字7
            4'd8:  seg <= DIGIT8;  // 显示数字8
            4'd9:  seg <= DIGIT9;  // 显示数字9
            4'd10: seg <= DIGITC;  // 显示特殊字符"C"
            default: seg <= DIGOFF; // 其他值关闭显示
        endcase
    end
end

endmodule

 三、按键驱动模块

 

module key(
    input   wire                sys_clk,
    input   wire                sys_rst,
    input   wire    [3:0]       key_in,
	 output reg      [3:0]		  key_data 
);

//按键键值定义
localparam  KEY_VAL_S1 =4'b0001;
localparam  KEY_VAL_S2 =4'b0010;
localparam  KEY_VAL_S3 =4'b0100;
localparam  KEY_VAL_S4=4'b1000;
localparam  KEY_VAL_NL=4'b1111;

localparam  KEY_COUNT_MAX =20'd500000;
//按键状态定义
localparam  IDLE      =3'd0;
localparam  PRESS     =3'd1;
localparam  RELEASE   =3'd2;

//按键扫描计数器,每隔0.01秒扫描一次按键
reg [19:0]   key_count;
//寄存器的定义用来存储不同的状态
reg [2:0]    key_status;

//按键计数更新
always@(posedge sys_clk or negedge sys_rst) begin
    if(sys_rst==1'b0)
        key_count <=0;
        else begin
             if(key_count == KEY_COUNT_MAX-1)begin
                key_count <= 0;
            end else
                key_count = key_count + 1;    
        end
    end

//按键状态更新
always@(posedge sys_clk or negedge sys_rst)begin
    if(sys_rst==1'b0)begin//复位
        key_data <= KEY_VAL_NL;
		  key_status<=IDLE;
	 end else begin
    if(key_data == KEY_VAL_NL) begin
			if(key_count==KEY_COUNT_MAX-1)begin
				if(key_status==IDLE)begin//当前没有按键时,并且按键扫描时间到,进行判断
				   if(key_in!=4'b1111)//当按键键不等于1111
						key_status<=PRESS;//按键处于按下状态
				   end  else if(key_status==PRESS)begin//在有按键按下,赋予键值
							case(key_in)
								4'b1110: begin key_data <=KEY_VAL_S1; key_status <=RELEASE; end 
								4'b1101: begin key_data <=KEY_VAL_S2; key_status <=RELEASE; end
								4'b1011: begin key_data <=KEY_VAL_S3; key_status <=RELEASE; end
								4'b0111: begin key_data <=KEY_VAL_S4; key_status <=RELEASE; end 
								default: begin key_data <=KEY_VAL_NL; key_status <=IDLE;    end 
							endcase
					end else begin 
							if(key_in == 4'b1111)
									key_status <=IDLE;
							end
						end
					end else
					     key_data <=KEY_VAL_NL;
				end 
			end 
endmodule

四、串口驱动模块 

// UART发送模块
// 功能:实现UART串口数据发送,支持可配置波特率和校验位
module uart_tx #(
    parameter CLOCK = 26'd50_000_000,  // 系统时钟频率(默认50MHz)
    parameter BUAD = 9600,            // 波特率(默认9600)
    parameter CHECK_BIT = "None"      // 校验位类型:"None"/"Odd"/"Even"
)(
    input           clk,         // 系统时钟
    input           rst,         // 异步复位,低电平有效
    input   [7:0]   tx_data,     // 待发送数据(8位)
    input           tx_data_vld, // 数据有效信号(高电平有效)
    output  wire    ready,       // 发送就绪信号(高电平表示可以接收新数据)
    output  reg     tx           // 串行数据输出
);

// ===== 参数定义 =====
parameter MAX_1bit = CLOCK/BUAD;  // 每bit所需的时钟周期数

// 状态定义(独热码编码)
localparam IDLE   = 5'b00001,  // 空闲状态
           START  = 5'b00010,  // 起始位
           DATA   = 5'b00100,  // 数据位
           CHECK  = 5'b01000,  // 校验位
           STOP   = 5'b10000;  // 停止位

// ===== 寄存器定义 =====
reg [4:0] cstate;      // 当前状态
reg [4:0] nstate;      // 下一状态
reg [19:0] cnt_baud;   // 波特率计数器
reg [2:0] cnt_bit;     // 位计数器(计数当前发送的bit数)
reg [3:0] bit_max;     // 各状态需要计数的最大bit数
reg [7:0] tx_data_r;   // 发送数据寄存器

// ===== 状态转移条件 =====
wire IDLE_START;   // 空闲→起始位
wire START_DATA;   // 起始位→数据位
wire DATA_CHECK;   // 数据位→校验位
wire DATA_STOP;    // 数据位→停止位(无校验时)
wire CHECK_STOP;   // 校验位→停止位
wire STOP_IDLE;    // 停止位→空闲

// ===== 计数器控制信号 =====
wire add_cnt_baud; // 波特率计数器递增使能
wire end_cnt_baud; // 波特率计数器结束(达到1bit时间)
wire add_cnt_bit;  // 位计数器递增使能
wire end_cnt_bit;  // 位计数器结束(达到当前状态所需bit数)

// ===== 校验位计算 =====
wire check_val;    // 校验位值

// ==============================================
// 波特率计数器(控制每个bit的持续时间)
// ==============================================
always @(posedge clk or negedge rst) begin 
    if(!rst) begin
        cnt_baud <= 'd0;
    end else if(add_cnt_baud) begin 
        if(end_cnt_baud) begin 
            cnt_baud <= 'd0;  // 计数器归零
        end else begin 
            cnt_baud <= cnt_baud + 1'd1;  // 计数器递增
        end 
    end
end 

assign add_cnt_baud = (cstate != IDLE);  // 非空闲状态下计数
assign end_cnt_baud = (add_cnt_baud && (cnt_baud == MAX_1bit - 1'd1));  // 达到1bit时间

// ==============================================
// 位计数器(计数当前状态已发送的bit数)
// ==============================================
always @(posedge clk or negedge rst) begin 
    if(!rst) begin
        cnt_bit <= 'd0;
    end else if(add_cnt_bit) begin 
        if(end_cnt_bit) begin 
            cnt_bit <= 'd0;  // 计数器归零
        end else begin 
            cnt_bit <= cnt_bit + 1'd1;  // 计数器递增
        end 
    end
end 

assign add_cnt_bit = end_cnt_baud;  // 每bit时间结束时递增
assign end_cnt_bit = (add_cnt_bit && (cnt_bit == bit_max - 1'd1));  // 达到当前状态所需bit数

// ==============================================
// 各状态需要计数的bit数
// ==============================================
always @(*) begin 
    case (cstate)
        IDLE  : bit_max = 'd0;   // 空闲状态不计数
        START : bit_max = 'd1;   // 起始位(1bit)
        DATA  : bit_max = 'd8;   // 数据位(8bit)
        CHECK : bit_max = 'd1;   // 校验位(1bit)
        STOP  : bit_max = 'd1;   // 停止位(1bit)
        default: bit_max = 'd0;
    endcase
end

// ==============================================
// 状态转移条件
// ==============================================
assign IDLE_START = (cstate == IDLE) && tx_data_vld;  // 空闲时收到有效数据
assign START_DATA = (cstate == START) && end_cnt_bit; // 起始位发送完成
assign DATA_STOP  = (cstate == DATA) && end_cnt_bit && (CHECK_BIT == "None"); // 无校验时直接到停止位
assign DATA_CHECK = (cstate == DATA) && end_cnt_bit && (CHECK_BIT != "None"); // 有校验时到校验位
assign CHECK_STOP = (cstate == CHECK) && end_cnt_bit; // 校验位发送完成
assign STOP_IDLE  = (cstate == STOP) && end_cnt_bit;  // 停止位发送完成

// ==============================================
// 状态寄存器
// ==============================================
always @(posedge clk or negedge rst) begin 
    if(!rst) begin
        cstate <= IDLE;  // 复位到空闲状态
    end else begin 
        cstate <= nstate; // 状态更新
    end 
end

// ==============================================
// 状态转移逻辑(有限状态机)
// ==============================================
always @(*) begin
    case(cstate)
        IDLE: begin
            if (IDLE_START) nstate = START;  // 收到数据,进入起始位
            else nstate = cstate;            // 保持空闲
        end
        START: begin
            if (START_DATA) nstate = DATA;   // 起始位完成,进入数据位
            else nstate = cstate;            // 保持起始位
        end
        DATA: begin
            if (DATA_CHECK) nstate = CHECK;  // 有校验时进入校验位
            else if (DATA_STOP) nstate = STOP; // 无校验时直接到停止位
            else nstate = cstate;            // 保持数据位
        end
        CHECK: begin
            if (CHECK_STOP) nstate = STOP;  // 校验位完成,进入停止位
            else nstate = cstate;           // 保持校验位
        end
        STOP: begin
            if (STOP_IDLE) nstate = IDLE;  // 停止位完成,回到空闲
            else nstate = cstate;          // 保持停止位
        end
        default: nstate = cstate;          // 默认保持当前状态
    endcase
end

// ==============================================
// 发送数据寄存器(锁存输入数据)
// ==============================================
always @(posedge clk or negedge rst) begin
    if (!rst) begin
        tx_data_r <= 'd0;  // 复位清零
    end else if (tx_data_vld) begin
        tx_data_r <= tx_data;  // 锁存有效数据
    end
end

// ==============================================
// 校验位计算
// ==============================================
// 奇校验:1的个数为奇数时校验位=0
// 偶校验:1的个数为偶数时校验位=0
assign check_val = (CHECK_BIT == "Odd") ? ~^tx_data_r : ^tx_data_r;

// ==============================================
// 串行数据输出
// ==============================================
always @(*) begin 
    case (cstate)
        IDLE  : tx = 1'b1;               // 空闲时保持高电平
        START : tx = 1'b0;               // 起始位(低电平)
        DATA  : tx = tx_data_r[cnt_bit]; // 数据位(LSB first)
        CHECK : tx = check_val;          // 校验位
        STOP  : tx = 1'b1;               // 停止位(高电平)
        default: tx = 1'b1;
    endcase
end

// ==============================================
// 就绪信号(空闲时可接收新数据)
// ==============================================
assign ready = (cstate == IDLE);

endmodule

五、管脚约束

声明: 本代码借鉴网络资料,如有侵权请联系作者删除,如有错误请在评论区或者私信作者纠正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值