-
`timescale 1ns/1ns //////////////////////////////////////////////////////////////////////// // Author : EmbedFire // Create Date : 2019/04/01 // Module Name : i2c_ctrl // Project Name : ad // Target Devices: Altera EP4CE10F17C8N // Tool Versions : Quartus 13.0 // Description : i2c_ctrl // // Revision : V1.0 // Additional Comments: // // 实验平台: 野火_征途Pro_FPGA开发板 // 公司 : http://www.embedfire.com // 论坛 : http://www.firebbs.cn // 淘宝 : https://fire-stm32.taobao.com //////////////////////////////////////////////////////////////////////// module i2c_ctrl #( parameter DEVICE_ADDR = 7'b1010_000 , //i2c设备地址 parameter SYS_CLK_FREQ = 26'd50_000_000 , //输入系统时钟频率 parameter SCL_FREQ = 18'd250_000 //i2c设备scl时钟频率 ) ( input wire sys_clk , //输入系统时钟,50MHz input wire sys_rst_n , //输入复位信号,低电平有效 input wire wr_en , //输入写使能信号 input wire rd_en , //输入读使能信号 input wire i2c_start , //输入i2c触发信号 input wire addr_num , //输入i2c字节地址字节数 input wire [15:0] byte_addr , //输入i2c字节地址 input wire [7:0] wr_data , //输入i2c设备数据 output reg i2c_clk , //i2c驱动时钟 output reg i2c_end , //i2c一次读/写操作完成 output reg [7:0] rd_data , //输出i2c设备读取数据 output reg i2c_scl , //输出至i2c设备的串行时钟信号scl inout wire i2c_sda //输出至i2c设备的串行数据信号sda ); //************************************************************************// //******************** Parameter and Internal Signal *********************// //************************************************************************// // parameter define localparam CNT_CLK_MAX = (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3 ; //cnt_clk计数器计数最大值 0-24 移位运算符号右移三位是二进制下右移三位 localparam CNT_START_MAX = 8'd100; //cnt_start计数器计数最大值 localparam IDLE = 4'd00, //初始状态 START_1 = 4'd01, //开始状态1 SEND_D_ADDR = 4'd02, //设备地址写入状态 + 控制写 ACK_1 = 4'd03, //应答状态1 SEND_B_ADDR_H = 4'd04, //字节地址高八位写入状态 ACK_2 = 4'd05, //应答状态2 SEND_B_ADDR_L = 4'd06, //字节地址低八位写入状态 ACK_3 = 4'd07, //应答状态3 WR_DATA = 4'd08, //写数据状态 ACK_4 = 4'd09, //应答状态4 START_2 = 4'd10, //开始状态2 SEND_RD_ADDR = 4'd11, //设备地址写入状态 + 控制读 ACK_5 = 4'd12, //应答状态5 RD_DATA = 4'd13, //读数据状态 N_ACK = 4'd14, //非应答状态 STOP = 4'd15; //结束状态 // wire define wire sda_in ; //sda输入数据寄存 wire sda_en ; //sda数据写入使能信号 // reg define reg [7:0] cnt_clk ; //系统时钟计数器,控制生成clk_i2c时钟信号 reg [3:0] state ; //状态机状态 reg cnt_i2c_clk_en ; //cnt_i2c_clk计数器使能信号 reg [1:0] cnt_i2c_clk ; //clk_i2c时钟计数器,控制生成cnt_bit信号 reg [2:0] cnt_bit ; //sda比特计数器 reg ack ; //应答信号 reg i2c_sda_reg ; //sda数据缓存 reg [7:0] rd_data_reg ; //自i2c设备读出数据 //************************************************************************// //******************************* Main Code ******************************// //************************************************************************// // cnt_clk:系统时钟计数器(0-24)循环,控制生成clk_i2c时钟信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_clk <= 8'd0; else if(cnt_clk == CNT_CLK_MAX - 1'b1) cnt_clk <= 8'd0; else cnt_clk <= cnt_clk + 1'b1; // i2c_clk:i2c驱动时钟 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) i2c_clk <= 1'b1; else if(cnt_clk == CNT_CLK_MAX - 1'b1) i2c_clk <= ~i2c_clk; // cnt_i2c_clk_en:cnt_i2c_clk计数器使能信号 always@(posedge i2c_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_i2c_clk_en <= 1'b0; else if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3)) cnt_i2c_clk_en <= 1'b0; else if(i2c_start == 1'b1) cnt_i2c_clk_en <= 1'b1; // cnt_i2c_clk(0-3):i2c_clk时钟计数器循环,控制生成cnt_bit信号 always@(posedge i2c_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_i2c_clk <= 2'd0; else if(cnt_i2c_clk_en == 1'b1) cnt_i2c_clk <= cnt_i2c_clk + 1'b1; // cnt_bit:sda比特计数器 always@(posedge i2c_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_bit <= 3'd0; else if((state == IDLE) || (state == START_1) || (state == START_2) || (state == ACK_1) || (state == ACK_2) || (state == ACK_3) || (state == ACK_4) || (state == ACK_5) || (state == N_ACK)) cnt_bit <= 3'd0; else if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3)) cnt_bit <= 3'd0; else if((cnt_i2c_clk == 2'd3) && (state != IDLE)) cnt_bit <= cnt_bit + 1'b1; // state:状态机状态跳转 always@(posedge i2c_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) state <= IDLE; else case(state) IDLE: if(i2c_start == 1'b1) state <= START_1; else state <= state; START_1: if(cnt_i2c_clk == 3) state <= SEND_D_ADDR; else state <= state; SEND_D_ADDR: if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3)) state <= ACK_1; else state <= state; ACK_1: if((cnt_i2c_clk == 3) && (ack == 1'b0)) begin if(addr_num == 1'b1) state <= SEND_B_ADDR_H; else state <= SEND_B_ADDR_L; end else state <= state; SEND_B_ADDR_H: if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3)) state <= ACK_2; else state <= state; ACK_2: if((cnt_i2c_clk == 3) && (ack == 1'b0)) state <= SEND_B_ADDR_L; else state <= state; SEND_B_ADDR_L: if((cnt_bit == 3'd7) && (cnt_i2c_clk == 3)) state <= ACK_3; else state <= state; ACK_3: if((cnt_i2c_clk == 3) && (ack == 1'b0)) begin if(wr_en == 1'b1) state <= WR_DATA; else if(rd_en == 1'b1) state <= START_2; else state <= state; end else state <= state; WR_DATA: if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3)) state <= ACK_4; else state <= state; ACK_4: if((cnt_i2c_clk == 3) && (ack == 1'b0)) state <= STOP; else state <= state; START_2: if(cnt_i2c_clk == 3) state <= SEND_RD_ADDR; else state <= state; SEND_RD_ADDR: if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3)) state <= ACK_5; else state <= state; ACK_5: if((cnt_i2c_clk == 3) && (ack == 1'b0)) state <= RD_DATA; else state <= state; RD_DATA: if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3)) state <= N_ACK; else state <= state; N_ACK: if(cnt_i2c_clk == 3) state <= STOP; else state <= state; STOP: if((cnt_bit == 3'd3) &&(cnt_i2c_clk == 3)) state <= IDLE; else state <= state; default: state <= IDLE; endcase // ack:应答信号 always@(*) case (state) IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L, WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK: ack <= 1'b1; ACK_1,ACK_2,ACK_3,ACK_4,ACK_5: if(cnt_i2c_clk == 2'd0) ack <= sda_in; else ack <= ack; default: ack <= 1'b1; endcase // i2c_scl:输出至i2c设备的串行时钟信号scl always@(*) case (state) IDLE: i2c_scl <= 1'b1; START_1: if(cnt_i2c_clk == 2'd3) i2c_scl <= 1'b0; else i2c_scl <= 1'b1; SEND_D_ADDR,ACK_1,SEND_B_ADDR_H,ACK_2,SEND_B_ADDR_L, ACK_3,WR_DATA,ACK_4,START_2,SEND_RD_ADDR,ACK_5,RD_DATA,N_ACK: if((cnt_i2c_clk == 2'd1) || (cnt_i2c_clk == 2'd2)) i2c_scl <= 1'b1; else i2c_scl <= 1'b0; STOP: if((cnt_bit == 3'd0) &&(cnt_i2c_clk == 2'd0)) i2c_scl <= 1'b0; else i2c_scl <= 1'b1; default: i2c_scl <= 1'b1; endcase // i2c_sda_reg:sda数据缓存 always@(*) case (state) IDLE: begin i2c_sda_reg <= 1'b1; rd_data_reg <= 8'd0; end START_1: if(cnt_i2c_clk <= 2'd0) i2c_sda_reg <= 1'b1; else i2c_sda_reg <= 1'b0; SEND_D_ADDR: if(cnt_bit <= 3'd6) i2c_sda_reg <= DEVICE_ADDR[6 - cnt_bit]; else i2c_sda_reg <= 1'b0; ACK_1: i2c_sda_reg <= 1'b1; SEND_B_ADDR_H: i2c_sda_reg <= byte_addr[15 - cnt_bit];/* 高八位写入状态 */ ACK_2: i2c_sda_reg <= 1'b1; SEND_B_ADDR_L: i2c_sda_reg <= byte_addr[7 - cnt_bit];/* 低八位写入状态 */ ACK_3: i2c_sda_reg <= 1'b1; WR_DATA: i2c_sda_reg <= wr_data[7 - cnt_bit]; ACK_4: i2c_sda_reg <= 1'b1; START_2: if(cnt_i2c_clk <= 2'd1) i2c_sda_reg <= 1'b1; else i2c_sda_reg <= 1'b0; SEND_RD_ADDR: if(cnt_bit <= 3'd6) i2c_sda_reg <= DEVICE_ADDR[6 - cnt_bit]; else i2c_sda_reg <= 1'b1; ACK_5: i2c_sda_reg <= 1'b1; RD_DATA: if(cnt_i2c_clk == 2'd2) rd_data_reg[7 - cnt_bit] <= sda_in; else rd_data_reg <= rd_data_reg; N_ACK: i2c_sda_reg <= 1'b1; STOP: if((cnt_bit == 3'd0) && (cnt_i2c_clk < 2'd3)) i2c_sda_reg <= 1'b0; else i2c_sda_reg <= 1'b1; default: begin i2c_sda_reg <= 1'b1; rd_data_reg <= rd_data_reg; end endcase // rd_data:自i2c设备读出数据 always@(posedge i2c_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rd_data <= 8'd0; else if((state == RD_DATA) && (cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3)) rd_data <= rd_data_reg; // i2c_end:一次读/写结束信号 always@(posedge i2c_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) i2c_end <= 1'b0; else if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3)) i2c_end <= 1'b1; else i2c_end <= 1'b0; // sda_in:sda输入数据寄存 assign sda_in = i2c_sda; // sda_en:sda数据写入使能信号 assign sda_en = ((state == RD_DATA) || (state == ACK_1) || (state == ACK_2) || (state == ACK_3) || (state == ACK_4) || (state == ACK_5)) ? 1'b0 : 1'b1; // i2c_sda:输出至i2c设备的串行数据信号sda 1'bz高阻态 /* 高阻态的应用主要是在总线结构上,一般总线上会挂有多个设备,当主设备和其中一个 从设备间建立通信的时候,其它的从设备IO一般是高阻态,这样就不会影响正在通信的设备。 */ assign i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz; endmodule
`timescale 1ns/1ns //////////////////////////////////////////////////////////////////////// // Author : EmbedFire // Create Date : 2019/04/01 // Module Name : pcf8591_adda // Project Name : ad // Target Devices: Altera EP4CE10F17C8N // Tool Versions : Quartus 13.0 // Description : AD电压测量模块 // // Revision : V1.0 // Additional Comments: // // 实验平台: 野火_征途Pro_FPGA开发板 // 公司 : http://www.embedfire.com // 论坛 : http://www.firebbs.cn // 淘宝 : https://fire-stm32.taobao.com //////////////////////////////////////////////////////////////////////// module pcf8591_ad ( input wire sys_clk , //输入系统时钟,50MHz????? input wire sys_rst_n , //输入复位信号,低电平有效 input wire i2c_end , //i2c设备一次读/写操作完成 input wire [7:0] rd_data , //输出i2c设备读取数据 output reg rd_en , //输入i2c设备读使能信号 output reg i2c_start , //输入i2c设备触发信号 output reg [15:0] byte_addr , //输入i2c设备字节地址 output wire [19:0] po_data //数码管待显示数据 ); //************************************************************************// //******************** Parameter and Internal Signal *********************// //************************************************************************// //parameter define parameter CTRL_DATA = 8'b0100_0000; //AD/DA控制字 parameter CNT_WAIT_MAX= 18'd6_9999 ; //采样间隔计数最大值 parameter IDLE = 3'b001, AD_START = 3'b010, AD_CMD = 3'b100; //wire define wire [31:0] data_reg/* synthesis keep */; //数码管待显示数据缓存 //reg define reg [17:0] cnt_wait; //采样间隔计数器 reg [4:0] state ; //状态机状态变量 reg [7:0] ad_data ; //AD数据 //********************************************************************// //***************************** Main Code ****************************// //********************************************************************// //cnt_wait:采样间隔计数器 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_wait <= 18'd0; else if(state == IDLE) if(cnt_wait == CNT_WAIT_MAX) cnt_wait <= 18'd0; else cnt_wait <= cnt_wait + 18'd1; else cnt_wait <= 18'd0; //state:状态机状态变量 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) state <= IDLE; else case(state) IDLE: if(cnt_wait == CNT_WAIT_MAX) state <= AD_START; else state <= IDLE; AD_START: state <= AD_CMD; AD_CMD: if(i2c_end == 1'b1) state <= IDLE; else state <= AD_CMD; default:state <= IDLE; endcase //i2c_start:输入i2c设备触发信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) i2c_start <= 1'b0; else if(state == AD_START) i2c_start <= 1'b1; else i2c_start <= 1'b0; //rd_en:输入i2c设备读使能信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rd_en <= 1'b0; else if(state == AD_CMD) rd_en <= 1'b1; else rd_en <= 1'b0; //byte_addr:输入i2c设备字节地址 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) byte_addr <= 16'b0; else byte_addr <= CTRL_DATA; //ad_data:AD数据 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) ad_data <= 8'b0; else if(i2c_end == 1'b1) //(state == AD_CMD) && (i2c_end == 1'b1)) ad_data <= rd_data; //data_reg:数码管待显示数据缓存 assign data_reg = ((ad_data * 3300) >> 4'd8);///3.3v电压 二进制下右移8位/ //po_data:数码管待显示数据 assign po_data = data_reg[19:0]; endmodule
`timescale 1ns/1ns //////////////////////////////////////////////////////////////////////// // Author : EmbedFire // Create Date : 2019/07/10 // Module Name : bcd_8421 // Project Name : top_seg_595 // Target Devices: Altera EP4CE10F17C8N // Tool Versions : Quartus 13.0 // Description : 二进制数转BCD码 // // Revision : V1.0 // Additional Comments: // // 实验平台: 野火_征途系列FPGA开发板 // 公司 : http://www.embedfire.com // 论坛 : http://www.firebbs.cn // 淘宝 : https://fire-stm32.taobao.com //////////////////////////////////////////////////////////////////////// module bcd_8421 ( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低电平有效 input wire [19:0] data , //输入需要转换的数据 output reg [3:0] unit , //个位BCD码 output reg [3:0] ten , //十位BCD码 output reg [3:0] hun , //百位BCD码 output reg [3:0] tho , //千位BCD码 output reg [3:0] t_tho , //万位BCD码 output reg [3:0] h_hun //十万位BCD码 ); //********************************************************************// //******************** Parameter And Internal Signal *****************// //********************************************************************// //reg define reg [4:0] cnt_shift ; //移位判断计数器 reg [43:0] data_shift ; //移位判断数据寄存器 reg shift_flag ; //移位判断标志信号 //********************************************************************// //***************************** Main Code ****************************// //********************************************************************// //cnt_shift:从0到21循环计数 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_shift <= 5'd0; else if((cnt_shift == 5'd21) && (shift_flag == 1'b1)) cnt_shift <= 5'd0; else if(shift_flag == 1'b1) cnt_shift <= cnt_shift + 1'b1; else cnt_shift <= cnt_shift; //data_shift:计数器为0时赋初值,计数器为1~20时进行移位判断操作????? always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data_shift <= 44'b0; else if(cnt_shift == 5'd0) data_shift <= {24'b0,data}; //数据拼接 else if((cnt_shift <= 20) && (shift_flag == 1'b0)) begin data_shift[23:20] <= (data_shift[23:20] > 4) ? (data_shift[23:20] + 2'd3) : (data_shift[23:20]); data_shift[27:24] <= (data_shift[27:24] > 4) ? (data_shift[27:24] + 2'd3) : (data_shift[27:24]); data_shift[31:28] <= (data_shift[31:28] > 4) ? (data_shift[31:28] + 2'd3) : (data_shift[31:28]); data_shift[35:32] <= (data_shift[35:32] > 4) ? (data_shift[35:32] + 2'd3) : (data_shift[35:32]); data_shift[39:36] <= (data_shift[39:36] > 4) ? (data_shift[39:36] + 2'd3) : (data_shift[39:36]); data_shift[43:40] <= (data_shift[43:40] > 4) ? (data_shift[43:40] + 2'd3) : (data_shift[43:40]); end else if((cnt_shift <= 20) && (shift_flag == 1'b1)) data_shift <= data_shift << 1; else data_shift <= data_shift; //shift_flag:移位判断标志信号,用于控制移位判断的先后顺序 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) shift_flag <= 1'b0; else shift_flag <= ~shift_flag; //当计数器等于20时,移位判断操作完成,对各个位数的BCD码进行赋值 从左往右数 第一个是43 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) begin unit <= 4'b0; ten <= 4'b0; hun <= 4'b0; tho <= 4'b0; t_tho <= 4'b0; h_hun <= 4'b0; end else if(cnt_shift == 5'd21) begin unit <= data_shift[23:20]; ten <= data_shift[27:24]; hun <= data_shift[31:28]; tho <= data_shift[35:32]; t_tho <= data_shift[39:36]; h_hun <= data_shift[43:40]; end endmodule
`timescale 1ns/1ns //////////////////////////////////////////////////////////////////////// // Author : EmbedFire // Create Date : 2019/07/11 // Module Name : hc595_ctrl // Project Name : seg_595_static // Target Devices: Altera EP4CE10F17C8N // Tool Versions : Quartus 13.0 // Description : 595控制模块 // // Revision : V1.0 // Additional Comments: // // 实验平台: 野火_征途Pro_FPGA开发板 // 公司 : http://www.embedfire.com // 论坛 : http://www.firebbs.cn // 淘宝 : https://fire-stm32.taobao.com //////////////////////////////////////////////////////////////////////// module hc595_ctrl ( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低有效 input wire [5:0] sel , //数码管位选信号 DIG6(sel[0])对应的就是开发板上最右侧数码管。 input wire [7:0] seg , //数码管段选信号 output reg stcp , //数据存储器时钟 产生STCP时钟,将移位寄存器里的数据送入存储寄存器。 output reg shcp , //移位寄存器时钟 产生SHCP时钟,将串行数据输入引脚DS上的数据串行移入移位寄存器。 output reg ds , //串行数据输入 output wire oe //使能信号,低有效?????? ); //********************************************************************// //****************** Parameter and Internal Signal *******************// //********************************************************************// //reg define reg [1:0] cnt_4 ; //分频计数器 reg [3:0] cnt_bit ; //传输位数计数器 //wire define wire [13:0] data ; //数码管信号寄存 //********************************************************************// //***************************** Main Code ****************************// //********************************************************************// //将数码管信号寄存??? 数码管信号寄存在data中 assign data = {seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7],sel}; //将复位取反后赋值给其即可????????? assign oe = ~sys_rst_n; /* STCP上升沿时移位寄存器的数据会进入数据存储寄存器,通过让OE(输出使能输入)低即可让存储寄存器的数据进行输出。 */ //分频计数器:0~3循环计数 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_4 <= 2'd0; else if(cnt_4 == 2'd3) cnt_4 <= 2'd0; else cnt_4 <= cnt_4 + 1'b1; //cnt_bit:每输入一位数据加一 /* 当cnt等于3时让cnt_bit计数器加1,让其从0到13循环计数,每个数值代表传输一位数据。 */ always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_bit <= 4'd0; else if(cnt_4 == 2'd3 && cnt_bit == 4'd13) cnt_bit <= 4'd0; else if(cnt_4 == 2'd3) cnt_bit <= cnt_bit + 1'b1; else cnt_bit <= cnt_bit; //stcp:14个信号传输完成之后产生一个上升沿? always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) stcp <= 1'b0; else if(cnt_bit == 4'd13 && cnt_4 == 2'd3) stcp <= 1'b1; else stcp <= 1'b0; //shcp:产生四分频移位时钟 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) shcp <= 1'b0; else if(cnt_4 >= 4'd2) shcp <= 1'b1; else shcp <= 1'b0; //ds:将寄存器里存储的数码管信号输入即????? always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) ds <= 1'b0; else if(cnt_4 == 2'd0) ds <= data[cnt_bit]; else ds <= ds; endmodule
`timescale 1ns/1ns //////////////////////////////////////////////////////////////////////// // Author : EmbedFire // Create Date : 2019/07/10 // Module Name : seg_dynamic // Project Name : ad // Target Devices: Altera EP4CE10F17C8N // Tool Versions : Quartus 13.0 // Description : 数码管动态显示 // // Revision : V1.0 // Additional Comments: // // 实验平台: 野火_征途Pro_FPGA开发板 // 公司 : http://www.embedfire.com // 论坛 : http://www.firebbs.cn // 淘宝 : https://fire-stm32.taobao.com //////////////////////////////////////////////////////////////////////// module seg_dynamic ( input wire sys_clk , //系统时钟,频率50MHz input wire sys_rst_n , //复位信号,低有效 input wire [19:0] data , //数码管要显示的值 input wire [5:0] point , //小数点显示,高电平有效 input wire seg_en , //数码管使能信号,高电平有效 input wire sign , //符号位,高电平显示负号 output reg [5:0] sel , //数码管位选信号 output reg [7:0] seg //数码管段选信号 ); //********************************************************************// //****************** Parameter and Internal Signal *******************// //********************************************************************// //parameter define parameter CNT_MAX = 16'd49_999; //数码管刷新时间计数最大值 //wire define wire [3:0] unit ; //个位数 wire [3:0] ten ; //十位数 wire [3:0] hun ; //百位数 wire [3:0] tho ; //千位数 wire [3:0] t_tho ; //万位数 wire [3:0] h_hun ; //十万位数 //reg define reg [23:0] data_reg ; //待显示数据寄存器 reg [15:0] cnt_1ms ; //1ms计数器 reg flag_1ms ; //1ms标志信号 reg [2:0] cnt_sel ; //数码管位选计数器 reg [5:0] sel_reg ; //位选信号 reg [3:0] data_disp ; //当前数码管显示的数据 reg dot_disp ; //当前数码管显示的小数点 //********************************************************************// //***************************** Main Code ****************************// //********************************************************************// //data_reg:控制数码管显示数据 point没用到????? always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data_reg <= 24'b0; //若显示的十进制数的十万位为非零数据或需显示小数点,则六个数码管全显示 else if((h_hun) || (point[5])) data_reg <= {h_hun,t_tho,tho,hun,ten,unit}; //若显示的十进制数的万位为非零数据或需显示小数点,则值显示在5个数码管上 //打比方我们输入的十进制数据为20’d12345,我们就让数码管显示12345而不是012345 else if(((t_tho) || (point[4])) && (sign == 1'b1))//显示负号 data_reg <= {4'd10,t_tho,tho,hun,ten,unit};//4'd10我们定义为显示负号 else if(((t_tho) || (point[4])) && (sign == 1'b0)) data_reg <= {4'd11,t_tho,tho,hun,ten,unit};//4'd11我们定义为不显示 //若显示的十进制数的千位为非零数据或需显示小数点,则值显示4个数码管 else if(((tho) || (point[3])) && (sign == 1'b1)) data_reg <= {4'd11,4'd10,tho,hun,ten,unit}; else if(((tho) || (point[3])) && (sign == 1'b0)) data_reg <= {4'd11,4'd11,tho,hun,ten,unit}; //若显示的十进制数的百位为非零数据或需显示小数点,则值显示3个数码管 else if(((hun) || (point[2])) && (sign == 1'b1)) data_reg <= {4'd11,4'd11,4'd10,hun,ten,unit}; else if(((hun) || (point[2])) && (sign == 1'b0)) data_reg <= {4'd11,4'd11,4'd11,hun,ten,unit}; //若显示的十进制数的十位为非零数据或需显示小数点,则值显示2个数码管 else if(((ten) || (point[1])) && (sign == 1'b1)) data_reg <= {4'd11,4'd11,4'd11,4'd10,ten,unit}; else if(((ten) || (point[1])) && (sign == 1'b0)) data_reg <= {4'd11,4'd11,4'd11,4'd11,ten,unit}; //若显示的十进制数的个位且需显示负号 else if(((unit) || (point[0])) && (sign == 1'b1)) data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd10,unit}; //若上面都不满足都只显示一位数码管 else data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd11,unit}; //cnt_1ms:1ms循环计数 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_1ms <= 16'd0; else if(cnt_1ms == CNT_MAX) cnt_1ms <= 16'd0; else cnt_1ms <= cnt_1ms + 1'b1; //flag_1ms:1ms标志信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) flag_1ms <= 1'b0; else if(cnt_1ms == CNT_MAX - 1'b1) flag_1ms <= 1'b1; else flag_1ms <= 1'b0; //cnt_sel:从0到5循环数,用于选择当前显示的数码管 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_sel <= 3'd0; else if((cnt_sel == 3'd5) && (flag_1ms == 1'b1)) cnt_sel <= 3'd0; else if(flag_1ms == 1'b1) cnt_sel <= cnt_sel + 1'b1; else cnt_sel <= cnt_sel; //数码管位选信号寄存器 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) sel_reg <= 6'b000_000; else if((cnt_sel == 3'd0) && (flag_1ms == 1'b1)) sel_reg <= 6'b000_001; else if(flag_1ms == 1'b1) sel_reg <= sel_reg << 1; else sel_reg <= sel_reg; //控制数码管的位选信号,使六个数码管轮流显示 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) data_disp <= 4'b0; else if((seg_en == 1'b1) && (flag_1ms == 1'b1)) case(cnt_sel)//对应case语句里面选择对应的输出 3'd0: data_disp <= data_reg[3:0] ; //给第1个数码管赋个位值 3'd1: data_disp <= data_reg[7:4] ; //给第2个数码管赋十位值 3'd2: data_disp <= data_reg[11:8] ; //给第3个数码管赋百位值 3'd3: data_disp <= data_reg[15:12]; //给第4个数码管赋千位值 3'd4: data_disp <= data_reg[19:16]; //给第5个数码管赋万位值 3'd5: data_disp <= data_reg[23:20]; //给第6个数码管赋十万位值 default:data_disp <= 4'b0 ; endcase else data_disp <= data_disp; //dot_disp:小数点低电平点亮,需对小数点有效信号取反??????? always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) dot_disp <= 1'b1; else if(flag_1ms == 1'b1) dot_disp <= ~point[cnt_sel]; else dot_disp <= dot_disp; //控制数码管段选信号,显示数字??? always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) seg <= 8'b1111_1111; else case(data_disp) 4'd0 : seg <= {dot_disp,7'b100_0000}; //显示数字0 4'd1 : seg <= {dot_disp,7'b111_1001}; //显示数字1 4'd2 : seg <= {dot_disp,7'b010_0100}; //显示数字2 4'd3 : seg <= {dot_disp,7'b011_0000}; //显示数字3 4'd4 : seg <= {dot_disp,7'b001_1001}; //显示数字4 4'd5 : seg <= {dot_disp,7'b001_0010}; //显示数字5 4'd6 : seg <= {dot_disp,7'b000_0010}; //显示数字6 4'd7 : seg <= {dot_disp,7'b111_1000}; //显示数字7 4'd8 : seg <= {dot_disp,7'b000_0000}; //显示数字8 4'd9 : seg <= {dot_disp,7'b001_0000}; //显示数字9 4'd10 : seg <= 8'b1011_1111 ; //显示负号 4'd11 : seg <= 8'b1111_1111 ; //不显示任何字符 default:seg <= 8'b1100_0000; endcase //sel:数码管位选信号赋值 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) sel <= 6'b000_000; else sel <= sel_reg; //********************************************************************// //*************************** Instantiation **************************// //********************************************************************// //---------- bsd_8421_inst ---------- bcd_8421 bcd_8421_inst ( .sys_clk (sys_clk ), //系统时钟,频率50MHz .sys_rst_n (sys_rst_n), //复位信号,低电平有效 .data (data ), //输入需要转换的数据 .unit (unit ), //个位BCD码 .ten (ten ), //十位BCD码 .hun (hun ), //百位BCD码 .tho (tho ), //千位BCD码 .t_tho (t_tho ), //万位BCD码 .h_hun (h_hun ) //十万位BCD码 ); endmodule