一、顶层代码
- 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
复制代码 二、数码管模块
三、按键驱动模块
- 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
复制代码 五、管脚束缚
声明: 本代码借鉴网络资料,如有侵权请联系作者删除,如有错误请在批评区或者私信作者改正。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |