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

FPGA数码管驱动模块

本工程实现了一个完整的 6位动态扫描数码管显示系统。
将任意 30 位二进制整数(最大约10亿)实时显示在数码管上,支持带小数点控制。

模块构成

模块名功能描述
HC_FPGA_Demo_Top工程顶层模块,连接时钟、按键、串口、LED 与数码管等外设
smg数码管显示顶层模块,组合 binary2bcd + digital_tube
binary2bcd使用 Shift-Add-3 算法将二进制转为 6 位 BCD 数字
digital_tube数码管扫描模块,实现段选/位选输出,支持小数点动态控制

顶层模块

module HC_FPGA_Demo_Top
(input CLOCK_XTAL_50MHz,input RESET,input  KEY4,input  SIG_IN,input  RXD,output TXD,output LED0,output LED1,output LED2,output LED3,output  SIG_OUT1,output  SIG_OUT2,output  SIG_OUT3,output  SIG_OUT4,output  SIG_OUT5,output[7:0] DIG,output[5:0] SEL
);parameter CLK_FREQ = 50000000;    //定义系统时钟频率wire [29:0] smg_disp_data;smg u_smg (.i_clk(CLOCK_XTAL_50MHz),.i_rst_n(RESET),.i_data(123456),.i_dp(0),.o_sel(SEL),.o_seg(DIG)
);endmodule

数码管显示模块

/***  数码管显示模块* - 输入:30 位二进制数字* - 输出:6 位数码管段选与位选(带小数点)* - 结构:binary2bcd + digital_tube*/
module smg (input   wire                i_clk       ,   // 时钟input   wire                i_rst_n     ,   // 异步复位,低有效input   wire [29:0]         i_data      ,   // 输入 30bit 二进制数据input   wire [5:0]          i_dp        ,   // 小数点控制(1亮)output  wire [5:0]          o_sel       ,   // 数码管位选(低有效)output  wire [7:0]          o_seg           // 数码管段选(负逻辑)
);// ==================================================// 中间信号定义// ==================================================wire        w_bcd_vld;wire [3:0]  w_bcd_d0, w_bcd_d1, w_bcd_d2, w_bcd_d3, w_bcd_d4, w_bcd_d5;// ==================================================// 二进制转BCD模块(显示6位十进制)// ==================================================binary2bcd u_binary2bcd (.i_clk       (i_clk),.i_rst_n     (i_rst_n),.i_data      (i_data),.o_data      ({w_bcd_d0, w_bcd_d1, w_bcd_d2, w_bcd_d3, w_bcd_d4, w_bcd_d5}),.o_data_vld  (w_bcd_vld));// ==================================================// 数码管扫描显示模块// ==================================================digital_tube #(.P_CLK_FREQ   (50_000_000),.P_SCAN_FREQ  (1_000),.P_DIGIT_NUM  (6)) u_digital_tube (.i_clk     (i_clk),.i_rst_n   (i_rst_n),.i_dig0    (w_bcd_d0),.i_dig1    (w_bcd_d1),.i_dig2    (w_bcd_d2),.i_dig3    (w_bcd_d3),.i_dig4    (w_bcd_d4),.i_dig5    (w_bcd_d5),.i_dp      (i_dp),.o_sel     (o_sel),.o_seg     (o_seg));endmodule

二进制转BCD

/** 二进制转BCD* Shift-Add-3 Algorithm* 输入:i_data(二进制)* 输出:o_data(BCD),o_data_vld(完成标志)*/
module binary2bcd #(parameter P_DATA_WIDTH = 30,     // 输入数据位宽parameter P_CLK_FREQ   = 50_000_000 // 时钟频率(保留风格一致性)
) (input   wire                i_clk        , // 时钟input   wire                i_rst_n      , // 异步复位,低有效input   wire [P_DATA_WIDTH-1:0] i_data   , // 输入二进制数output  reg  [36:0]     o_data       , // 输出9位BCD码,共36位output  reg                o_data_vld    // 有效标志
);// ==================================================// Local Parameters// ==================================================localparam L_CNT_SHIFT_NUM = P_DATA_WIDTH;localparam L_SHIFT_WIDTH   = P_DATA_WIDTH + 36;  // BCD(9位) + 二进制// ==================================================// Registers// ==================================================reg [6:0]       r_cnt_shift;reg [L_SHIFT_WIDTH-1:0] r_data_shift;reg             r_shift_flag;// ==================================================// 移位控制计数器// ==================================================always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n)r_cnt_shift <= 7'd0;else if ((r_cnt_shift == L_CNT_SHIFT_NUM + 1) && r_shift_flag)r_cnt_shift <= 7'd0;else if (r_shift_flag)r_cnt_shift <= r_cnt_shift + 1'b1;end// ==================================================// BCD移位转换逻辑// ==================================================always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n)r_data_shift <= {L_SHIFT_WIDTH{1'b0}};else if (r_cnt_shift == 0)r_data_shift <= {{36{1'b0}}, i_data};else if ((r_cnt_shift <= L_CNT_SHIFT_NUM) && !r_shift_flag) beginif (r_data_shift[65:62] > 4) r_data_shift[65:62] <= r_data_shift[65:62] + 3;if (r_data_shift[61:58] > 4) r_data_shift[61:58] <= r_data_shift[61:58] + 3;if (r_data_shift[57:54] > 4) r_data_shift[57:54] <= r_data_shift[57:54] + 3;if (r_data_shift[53:50] > 4) r_data_shift[53:50] <= r_data_shift[53:50] + 3;if (r_data_shift[49:46] > 4) r_data_shift[49:46] <= r_data_shift[49:46] + 3;if (r_data_shift[45:42] > 4) r_data_shift[45:42] <= r_data_shift[45:42] + 3;if (r_data_shift[41:38] > 4) r_data_shift[41:38] <= r_data_shift[41:38] + 3;if (r_data_shift[37:34] > 4) r_data_shift[37:34] <= r_data_shift[37:34] + 3;if (r_data_shift[33:30] > 4) r_data_shift[33:30] <= r_data_shift[33:30] + 3;end else if ((r_cnt_shift <= L_CNT_SHIFT_NUM) && r_shift_flag)r_data_shift <= r_data_shift << 1;end// ==================================================// 交替控制加3/移位// ==================================================always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n)r_shift_flag <= 1'b0;elser_shift_flag <= ~r_shift_flag;end// ==================================================// 结果输出// ==================================================always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n) begino_data     <= 36'd0;o_data_vld <= 1'b0;end else if (r_cnt_shift == L_CNT_SHIFT_NUM + 1) begino_data     <= r_data_shift[65:30]; // 取出 BCD 码部分o_data_vld <= 1'b1;end else begino_data_vld <= 1'b0;endendendmodule

数码管扫描显示模块

/*** ===============================================* 数码管扫描显示模块(Digital Tube Driver)* ===============================================*/module digital_tube #(parameter P_CLK_FREQ     = 50_000_000,     // 输入时钟频率(Hz)parameter P_SCAN_FREQ    = 1_000,          // 数码管刷新频率(Hz)parameter P_DIGIT_NUM    = 6               // 数码管位数
) (input   wire                i_clk       ,   // 时钟信号input   wire                i_rst_n     ,   // 异步复位,低有效input   wire    [3:0]       i_dig0      ,   // 各位数字输入(0~F)input   wire    [3:0]       i_dig1      ,input   wire    [3:0]       i_dig2      ,input   wire    [3:0]       i_dig3      ,input   wire    [3:0]       i_dig4      ,input   wire    [3:0]       i_dig5      ,input   wire    [P_DIGIT_NUM-1:0] i_dp  ,   // 小数点控制(1亮)output  reg     [P_DIGIT_NUM-1:0] o_sel ,   // 数码管位选(低有效)output  reg     [7:0]       o_seg           // 数码管段选(低有效)
);// ==================================================// 扫描频率计数器// ==================================================localparam integer L_SCAN_CNT_MAX = P_CLK_FREQ / P_SCAN_FREQ;reg [15:0] r_scan_cnt;wire       w_scan_cnt_end = (r_scan_cnt == L_SCAN_CNT_MAX - 1);always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n)r_scan_cnt <= 0;else if (w_scan_cnt_end)r_scan_cnt <= 0;elser_scan_cnt <= r_scan_cnt + 1'b1;end// ==================================================//  位索引计数器// ==================================================reg [2:0] r_digit_idx;always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n)r_digit_idx <= 0;else if (w_scan_cnt_end)r_digit_idx <= (r_digit_idx == P_DIGIT_NUM - 1) ? 0 : r_digit_idx + 1'b1;end// ==================================================// 数码管片选与数据选择// ==================================================reg  [3:0] r_hex;reg        r_dp;always @(*) begincase (r_digit_idx)3'd0: begin o_sel = 6'b111110; r_hex = i_dig0; r_dp = i_dp[0]; end3'd1: begin o_sel = 6'b111101; r_hex = i_dig1; r_dp = i_dp[1]; end3'd2: begin o_sel = 6'b111011; r_hex = i_dig2; r_dp = i_dp[2]; end3'd3: begin o_sel = 6'b110111; r_hex = i_dig3; r_dp = i_dp[3]; end3'd4: begin o_sel = 6'b101111; r_hex = i_dig4; r_dp = i_dp[4]; enddefault: begin o_sel = 6'b011111; r_hex = i_dig5; r_dp = i_dp[5]; endendcaseend// ==================================================// 数码管段码定义(负逻辑)// ==================================================localparam [7:0] L_NUM0 = ~8'h3f;localparam [7:0] L_NUM1 = ~8'h06;localparam [7:0] L_NUM2 = ~8'h5b;localparam [7:0] L_NUM3 = ~8'h4f;localparam [7:0] L_NUM4 = ~8'h66;localparam [7:0] L_NUM5 = ~8'h6d;localparam [7:0] L_NUM6 = ~8'h7d;localparam [7:0] L_NUM7 = ~8'h07;localparam [7:0] L_NUM8 = ~8'h7f;localparam [7:0] L_NUM9 = ~8'h6f;localparam [7:0] L_NUMA = ~8'h77;localparam [7:0] L_NUMB = ~8'h7c;localparam [7:0] L_NUMC = ~8'h39;localparam [7:0] L_NUMD = ~8'h5e;localparam [7:0] L_NUME = ~8'h79;localparam [7:0] L_NUMF = ~8'h71;// ==================================================// 段选译码输出// ==================================================always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n)o_seg <= 8'hff;else begincase (r_hex)4'h0: o_seg[6:0] <= L_NUM0[6:0];4'h1: o_seg[6:0] <= L_NUM1[6:0];4'h2: o_seg[6:0] <= L_NUM2[6:0];4'h3: o_seg[6:0] <= L_NUM3[6:0];4'h4: o_seg[6:0] <= L_NUM4[6:0];4'h5: o_seg[6:0] <= L_NUM5[6:0];4'h6: o_seg[6:0] <= L_NUM6[6:0];4'h7: o_seg[6:0] <= L_NUM7[6:0];4'h8: o_seg[6:0] <= L_NUM8[6:0];4'h9: o_seg[6:0] <= L_NUM9[6:0];4'ha: o_seg[6:0] <= L_NUMA[6:0];4'hb: o_seg[6:0] <= L_NUMB[6:0];4'hc: o_seg[6:0] <= L_NUMC[6:0];4'hd: o_seg[6:0] <= L_NUMD[6:0];4'he: o_seg[6:0] <= L_NUME[6:0];4'hf: o_seg[6:0] <= L_NUMF[6:0];default: o_seg[6:0] <= 7'b0000000;endcaseo_seg[7] <= ~r_dp;  // 小数点控制:高亮 = 段信号为低endendendmodule
http://www.lryc.cn/news/603728.html

相关文章:

  • windows软件ARM64和AMD64(x64)区别,如何查看电脑支持哪种
  • 沪铝本周想法
  • C++ 模板补充
  • 网工知识——OSPF摘要知识
  • 重生之我在暑假学习微服务第四天《Docker-下篇》
  • 《林景媚与时间守护者》
  • 博途SCL: Input、Output、Static、Temp、Constant、InOut 的详细介绍及案例
  • 实现视频实时马赛克
  • DevOps 详解
  • PHP入门:从0到1开启Web开发之旅
  • Apache Ignite 的对等类加载(Peer Class Loading, P2P Class Loading)机制
  • Apache服务器指南
  • 《Spring Cloud Gateway 深度剖析:从核心原理到企业级实战》
  • SpringCloud之Gateway
  • SpringBoot之起步依赖
  • 【变更性别】
  • 【Linux篇】补充:消息队列和systemV信号量
  • 从本地 Docker 部署的 Dify 中导出知识库内容(1.6版本亲测有效)
  • 数分思维12:SQL技巧与分析方法
  • 主数据管理系统能代替数据中台吗?
  • stm32开发 -- RC522模块与AS608模块相关
  • RHCE综合项目:分布式LNMP私有博客服务部署
  • 远程Qt Creator中文输入解决方案
  • Django模型开发:模型字段、元数据与继承全方位讲解
  • 如何在Linux系统下进行C语言程序的编写和debug测试
  • Apache Ignite 关于 容错(Fault Tolerance)的核心机制
  • 城市元宇宙:未来城市治理的革新路径
  • Apache Ignite 的服务(Services)功能
  • X509Certificate2.GetNameInfo(X509NameType.UrlName, false)
  • 为什么分类任务偏爱交叉熵?MSE 为何折戟?