hc595_ctl
shizhong
第一次写csdn,自学FPGA有一段时间了,七月中旬买的开发板,到现在一个多月终于稍微学懂点FPGA了。
这个分享的是野火FPGA开发指南数码管动态显示拓展训练的试题。
一开始一头雾水,野火教程只讲到动态扫描,自己在网上找秒表的教程,很少,并且野火的板子是使用74hc595驱动数码管,这点跟其他平台的板子不太一样。参考了明德扬一个教程里面的设计方法。
实验目标是生成一个时分秒时钟,设计思路是分别对时分秒的个位,十位进行设计,正好对应6段数码管。计时设计完成之后,直接用顶层模块调用shizhong模块和74hc595驱动模块,74hc595驱动原理野火有详细教程,话不多说直接上代码。
时钟设计
module shizhong
(
input sys_clk,//50MHz
input sys_rst_n,//复位信号
output wire[7:0] seg,//数码管段选信号,
output wire[5:0] sel//位选信号
);
parameter CNT_MAX = 50_000;//动态扫描计时最大值
parameter CNT_MAX_1S = 50_000_000;//1s计数器计时最大值
reg[15:0] cnt_1ms;//动态扫描
reg[2:0] cnt_6;//对动态扫描计时6次,方便位选
reg[5:0] sel_reg;//位选信号中间变量
reg[25:0] cnt_1s;//1s计数器
reg[3:0] m_g;//秒个位
reg[2:0] m_s;
reg[3:0] f_g;
reg[3:0] f_s;
reg[3:0] s_g;
reg[3:0] s_s;
reg[7:0] seg_reg;//段选信号中间变量
reg[3:0] x;//对时个位变量法计数最大值,因为时个位的跳转可能为9(9,19),也可能为3(23)
reg[3:0] data;//很重要,将时分秒各数据赋值到数码管的中间变量
//设置动态扫描时间为1ms的计时器cnt_1ms
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
cnt_1ms <= 0;
else if(cnt_1ms==CNT_MAX-1)
cnt_1ms <= 0;
else
cnt_1ms <= cnt_1ms + 1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
cnt_6 <= 0;
else if((cnt_1ms==CNT_MAX-1)&&(cnt_6 > 3'd4))
cnt_6 <= 0;
else if(cnt_1ms==CNT_MAX-1)
cnt_6 <= cnt_6 + 1;
else
cnt_6 <= cnt_6;
//位选信号sel_reg 赋值
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
sel_reg <= 6'b000_001;
/* else if(cnt_6==0)
sel_reg <= 6'b000_001;
else if(cnt_6==1)
sel_reg <= 6'b000_010;
else if(cnt_6==2)
sel_reg <= 6'b000_100;
else if(cnt_6==3)
sel_reg <= 6'b001_000;
else if(cnt_6==4)
sel_reg <= 6'b010_000;
else
sel_reg <= 6'b100_000; */
else case(cnt_6)
3'd0:sel_reg <= 6'b000_001;
3'd1:sel_reg <= 6'b000_010;
3'd2:sel_reg <= 6'b000_100;
3'd3:sel_reg <= 6'b001_000;
3'd4:sel_reg <= 6'b010_000;
3'd5:sel_reg <= 6'b100_000;
default:sel_reg <= 6'b000_001;
endcase
//秒设计
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
cnt_1s <= 0;
else if(cnt_1s==CNT_MAX_1S-1)
cnt_1s <= 0;
else
cnt_1s <= cnt_1s + 1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
m_g <= 0;
else if((cnt_1s==CNT_MAX_1S-1)&&(m_g==4'd9))
m_g <= 0;
else if(cnt_1s==CNT_MAX_1S-1)
m_g <= m_g +1;
else
m_g <= m_g;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
m_s <= 0;
else if((cnt_1s==CNT_MAX_1S-1)&&(m_s==4'd5)&&(m_g==4'd9))
m_s <= 0;
else if((cnt_1s==CNT_MAX_1S-1)&&(m_g==4'd9))
m_s <= m_s +1;
else
m_s <= m_s;
//分设计
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
f_g <= 0;
else if((cnt_1s==CNT_MAX_1S-1)&&(f_g==4'd9)&&(m_s==4'd5)&&(m_g==4'd9))
f_g <= 0;
else if((cnt_1s==CNT_MAX_1S-1)&&(m_s==4'd5)&&(m_g==4'd9))
f_g <= f_g +1;
else
f_g <= f_g;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
f_s <= 0;
else if((cnt_1s==CNT_MAX_1S-1)&&(f_s==4'd5)&&(f_g==4'd9)&&(m_s==4'd5)&&(m_g==4'd9))
f_s <= 0;
else if((cnt_1s==CNT_MAX_1S-1)&&(f_g==4'd9)&&(m_s==4'd5)&&(m_g==4'd9))
f_s <= f_s +1;
else
f_s <= f_s;
//时设计
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
s_g <= 0;
else if((cnt_1s==CNT_MAX_1S-1)&&(s_g==x-1)&&(f_s==4'd9)&&(f_g==4'd9)&&(m_s==4'd5)&&(m_g==4'd9))
s_g <= 0;
else if((cnt_1s==CNT_MAX_1S-1)&&(f_s==4'd9)&&(f_g==4'd9)&&(m_s==4'd5)&&(m_g==4'd9))
s_g <= s_g +1;
else
s_g <= s_g;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
s_s <= 0;
else if((cnt_1s==CNT_MAX_1S-1)&&(s_s==4'd2)&&(s_g==x-1)&&(f_g==4'd9)&&(m_s==4'd5)&&(m_g==4'd9))
s_s <= 0;
else if((cnt_1s==CNT_MAX_1S-1)&&(s_g==x-1)&&(f_g==4'd9)&&(m_s==4'd5)&&(m_g==4'd9))
s_s <= s_s +1;
else
s_s <= s_s;
always@(*) begin
if(s_s==4'd2)
x = 4'd4;
else
x = 4'd10;
end
//段选seg_reg
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
seg_reg <= 8'hc0;
else case(data)
4'd0:seg_reg <= 8'hc0;
4'd1:seg_reg <= 8'hf9;
4'd2:seg_reg <= 8'ha4;
4'd3:seg_reg <= 8'hb0;
4'd4:seg_reg <= 8'h99;
4'd5:seg_reg <= 8'h92;
4'd6:seg_reg <= 8'h82;
4'd7:seg_reg <= 8'hf8;
4'd8:seg_reg <= 8'h80;
4'd9:seg_reg <= 8'h90;
default:seg_reg <= 8'hc0;
endcase
always@(*) begin
case(cnt_6)
0:data = m_g;
1:data = m_s;
2:data = f_g;
3:data = f_s;
4:data = s_g;
5:data = s_s;
default:data = m_g;
endcase
end
assign sel = sel_reg;
assign seg = seg_reg;
endmodule
74HC595模块
module hc595_ctl
(
input wire clk , //系统时钟,频率50MHz
input wire rst_n , //复位信号,低有效
input wire [5:0] smg_sel , //数码管位选信号
input wire [7:0] smg_dig , //数码管段选信号
output reg stcp , //数据存储器时钟
output reg shcp , //移位寄存器时钟
output reg ds , //串行数据输入
output wire oe //使能信号,低有效
);
reg [1:0] cnt_4 ; //分频计数器
reg [3:0] cnt_bit ; //传输位数计数器
//wire define
wire [13:0] data ; //数码管信号寄存
//将数码管信号寄存
assign data = {smg_dig[0],smg_dig[1],smg_dig[2],smg_dig[3],smg_dig[4],smg_dig[5],smg_dig[6],smg_dig[7],smg_sel};
//将复位取反后赋值给其即可
assign oe = ~rst_n;
//分频计数器:0~3循环计数
always@(posedge clk or negedge rst_n)
if(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:每输入一位数据加一
always@(posedge clk or negedge rst_n)
if(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 clk or negedge rst_n)
if(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 clk or negedge rst_n)
if(rst_n == 1'b0)
shcp <= 1'b0;
else if(cnt_4 >= 4'd2)
shcp <= 1'b1;
else
shcp <= 1'b0;
//ds:将寄存器里存储的数码管信号输入即
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
ds <= 1'b0;
else if(cnt_4 == 2'd0)
ds <= data[cnt_bit];
else
ds <= ds;
endmodule
总结:从三月份开始学数电,四五月份开始断断续续学Verilog,现在自己能上手操作一些东西了,对于我这个零基础转行的人来说有莫大的欣慰。时钟设计对我来说真的是不小的挑战,通过这一周的找资料设计,对调用模块,写仿真代码,跑波形,管教绑定学习有非常大的帮助。总之FPGA的学习是一个操作性很强的过程,在不断的试错找bug,调试,再试错的过程中学习是最快的。多上手,遇到问题就解决,相信一定能入行。