FPGA开发--数码管扫描
文章摘要:
本文主要描述了数码管扫描电路的实现方案;
知识要点:有限状态机,模块例化;
硬件平台: EP4CE6F17C8
开发环境: Quartus II 13.1
数码管扫描模块:
/*
* 功能描述:6位数码管扫描模块
*/
module smg_scan(
input clk, // 时钟信号
input rst_n, // 复位信号
output reg[7:0]data_pin, // 数据引脚,共阳数码管
output reg[5:0]sel_pin, // 选通引脚,低电平导通
input [7:0]seg0, // 数据0
input [7:0]seg1, // 数据1
input [7:0]seg2, // 数据2
input [7:0]seg3, // 数据3
input [7:0]seg4, // 数据4
input [7:0]seg5 // 数据5
);
//--------------------------------------------------
// 有限状态机
parameter S_SEG0 = 4'd1;
parameter S_SEG1 = 4'd2;
parameter S_SEG2 = 4'd3;
parameter S_SEG3 = 4'd4;
parameter S_SEG4 = 4'd5;
parameter S_SEG5 = 4'd6;
reg[3:0] state; // 记录当前状态
/*
* 全部刷新时间不能超过10ms,否则人眼可以感觉到闪烁
* 如果刷新时间缩短至5ms以内,则低速摄像机也扑捉不到闪烁
*/
parameter FLUSH_COUNT = 50_000; // 单个刷新时间1ms,全部刷新时间为6ms
reg[31:0] count; // 刷新记数器
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
sel_pin <= 6'b111111; // 复位时关闭显示
state <= S_SEG0; // 回到初始状态
end
else if(count >= FLUSH_COUNT) begin // 定时时间到
count <= 0; // 重新计数
case(state)
S_SEG0: begin
state <= S_SEG1;
sel_pin <= 6'b111110; // 片选
data_pin <= seg0; // 数据
end
S_SEG1: begin
state <= S_SEG2;
sel_pin <= 6'b111101;
data_pin <= seg1;
end
S_SEG2: begin
state <= S_SEG3;
sel_pin <= 6'b111011;
data_pin <= seg2;
end
S_SEG3: begin
state <= S_SEG4;
sel_pin <= 6'b11_0111;
data_pin <= seg3;
end
S_SEG4: begin
state <= S_SEG5;
sel_pin <= 6'b10_1111;
data_pin <= seg4;
end
S_SEG5: begin
state <= S_SEG0; // 最后一个显示完,下一次该显示示第一个了
sel_pin <= 6'b01_1111;
data_pin <= seg5;
end
default: begin
state <= S_SEG0; // 未知状态,返回第一个状态
end
endcase
end
else begin
count <= count + 1'b1; // 最后一个时钟沿其实是没计数的;
end
end
endmodule
译码器计数器模块:
/*
* 功能描述:异步十进制译码计数器
*/
module decoder(
clk, // 计数时钟
rst_n, // 异步复位
seg, // 译码输出
carry // 进位(做为下级时钟)
);
input clk;
input rst_n;
output reg [7:0] seg;
output reg carry;
//------------------------------------------
reg [3:0] count; // 计数器
// 十进制计数器
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
count <= 0;
carry <= 1'b0;
end
else if(count < 9) begin
count <= count + 1'b1;
carry <= 1'b0;
end
else begin
count <= 0;
carry <= 1'b1; // 产生进位
end
end
// 组合逻辑
always @(*) begin
if(~rst_n) begin
seg <= 8'b1111_1111; // 全灭
end
else begin
case(count)
4'd0:
seg <= 8'b1100_0000;
4'd1:
seg <= 8'b1111_1001;
4'd2:
seg <= 8'b1010_0100;
4'd3:
seg <= 8'b1011_0000;
4'd4:
seg <= 8'b1001_1001;
4'd5:
seg <= 8'b1001_0010;
4'd6:
seg <= 8'b1000_0010;
4'd7:
seg <= 8'b1111_1000;
4'd8:
seg <= 8'b1000_0000;
4'd9:
seg <= 8'b1001_0000;
4'd10:
seg <= 8'b1000_1000;
default:
seg <= 8'hff;
endcase
end
end
endmodule
建立顶层模块(测试用)
/*
* 功能描述:数码管测试程序
*/
module smg_test(
input clk, // 系统时钟
input rst_n, // 系统复位
output [7:0]data_pin, // 数据管数据脚
output [5:0]sel_pin // 数码管片选脚
);
reg[31:0] timer; // 定时器
reg tick;
// 模块连接线
wire[7:0]seg0;
wire[7:0]seg1;
wire[7:0]seg2;
wire[7:0]seg3;
wire[7:0]seg4;
wire[7:0]seg5;
wire[7:0]seg6;
wire c0; // 计数器进位
wire c1;
wire c2;
wire c3;
wire c4;
//-----------------------------------------------
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
begin
timer <= 0;
tick <= 0;
end
else if(timer == 32'd25_000_000)
begin
tick <= 1'b1;
timer <= 0;
end
else begin
tick <= 0;
timer <= timer + 1'b1;
end
end
// 例化数码管显示模块
smg_scan s0(
.clk(clk), // 时钟信号
.rst_n(rst_n), // 复位信号
.data_pin(data_pin), // 数据引脚,共阳数码管
.sel_pin(sel_pin), // 选通信号,低电平导通
.seg0(seg0), // 数据
.seg1(seg1), // 数据
.seg2(seg2), // 数据
.seg3(seg3), // 数据
.seg4(seg4), // 数据
.seg5(seg5) // 数据
);
// 例化6个显示译码器,用于将数字转化为显示段码;
decoder u0(
.clk(tick), // 计数时钟
.rst_n(rst_n), // 异步复位
.seg(seg0), // 译码输出
.carry(c0) // 进位
);
decoder u1(
.clk(c0), // 计数时钟
.rst_n(rst_n), // 异步复位
.seg(seg1), // 译码输出
.carry(c1) // 进位
);
decoder u2(
.clk(c1), // 计数时钟
.rst_n(rst_n), // 异步复位
.seg(seg2), // 译码输出
.carry(c2) // 进位
);
decoder u3(
.clk(c2), // 计数时钟
.rst_n(rst_n), // 异步复位
.seg(seg3), // 译码输出
.carry(c3) // 进位
);
decoder u4(
.clk(c3), // 计数时钟
.rst_n(rst_n), // 异步复位
.seg(seg4), // 译码输出
.carry(c4) // 进位
);
decoder u5(
.clk(c4), // 计数时钟
.rst_n(rst_n), // 异步复位
.seg(seg5), // 译码输出
.carry() // 进位
);
endmodule