例程数码管实现999秒计时,精度为0.1秒,按照历程的功能,推测四位数码管应该为动态显示,即四位数码管的段选信号同时控制,轮流点亮位选信号。因为余晖效应人眼看四位数码管时四个数码管是同时亮的。动态显示的优点是节省IO资源,缺点是每个时刻只有一个数码管点亮,视觉效果上比静态数码管差(个人觉得没区别)。
现在看一下数码管的原理图:通过原理图可知数码管为共阴极,位选信号由AN0,AN1,AN2,AN3控制,段选信号由CG,CC,CH,CD,CE,CA,CF,CB控制,由此可知为八段数码管。
以下为四位位选控制信号和八段段选控制信号对应的FPGA引脚:
接下来分析代码实现,首先是输入输出接口,这里除了时钟和复位(还不太习惯,安路这个板子没有复位输入,依然是“rst.v”来实现复位给寄存器赋初值)信号外还有位选信号sm_bit位宽为四,段选信号sm_seg位宽为八,此外还定义了一个常量CNT_TIME,具体值为2400_000,2400000个系统时钟周期刚好实现0.1s的计时功能,用于动态显示四个数码管。
想要实现动态显示的话需要做到两布,一是段选信号的控制,数码管的四个位的段选都是并联的,所以只需要8个FPGA引脚来进行控制,如果只是给段选信号的话这时看到的现象是乱序的,因为这个段选信号包含了百位,十位,各位以及小数点后一位的段选信号;二是位选信号的控制,位选信号的作用是让段选信号在合适的时间在合适的位置显示,比如这次的段选信号是十位显示的数据,此时就让十位的数码管导通、下次的段选信号是个位的显示信号,下一次就选通个位的数码管点亮。
always@(posedge clk_24m or negedge rst_n) // 小数点后一位
begin
if(!rst_n)
sm_bit1_num <= 4'h0;
else if(cnt == CNT_TIME)//0.1s
begin
if(sm_bit1_num == 9)
sm_bit1_num <= 4'h0;
else
sm_bit1_num <= sm_bit1_num + 1'b1;
end
else
sm_bit1_num <= sm_bit1_num;
end
always@(posedge clk_24m or negedge rst_n) // 个位
begin
if(!rst_n)
sm_bit2_num <= 4'h0;
else if(cnt == CNT_TIME && sm_bit1_num == 9)
begin
if(sm_bit2_num == 10 )
sm_bit2_num <= 4'h0;
else
sm_bit2_num <= sm_bit2_num + 1;
end
else
sm_bit2_num <= sm_bit2_num;
end
always@(posedge clk_24m or negedge rst_n) // 十位
begin
if(!rst_n)
sm_bit3_num <= 4'h0;
else if(cnt == CNT_TIME && sm_bit2_num == 9 && sm_bit1_num == 9)
begin
if(sm_bit3_num == 10 )
sm_bit3_num <= 4'h0;
else
sm_bit3_num <= sm_bit3_num + 1;
end
else
sm_bit3_num <= sm_bit3_num;
end
always@(posedge clk_24m or negedge rst_n) // 百位
begin
if(!rst_n)
sm_bit4_num <= 4'h0;
else if(cnt == CNT_TIME && sm_bit3_num == 9 && sm_bit2_num == 9 && sm_bit1_num == 9)
begin
if(sm_bit4_num == 10)
sm_bit4_num <= 4'h0;
else
sm_bit4_num <= sm_bit4_num + 1;
end
else
sm_bit4_num <= sm_bit4_num;
end
此端代码控制每位数码管显示的具体数字数字,此时还不是具体的段选信号。
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
cnt_w <= 18'd0;
else if(cnt_w == 18'b111_111_111_111_111_111) // 0.1s 1/24M*262144
// else if(&cnt_w)
cnt_w <= 18'd0;
else
cnt_w <= cnt_w + 1;
end
这里是位选信号变换的间隔,由cnt_w控制(18位)。
always@(posedge clk_24m or negedge rst_n)
begin
if(!rst_n)
sm_seg_num <= 4'h0;
else
begin
case( cnt_w[17:16] )
2'b00:sm_seg_num <= sm_bit1_num;
2'b01:sm_seg_num <= sm_bit2_num;
2'b10:sm_seg_num <= sm_bit3_num;
2'b11:sm_seg_num <= sm_bit4_num;
endcase
end
end
这里让四个位的具体数字幅值给段选寄存器sm_seg_num,后面根据这个寄存器的值选择具体的段选信号。这里sm_seg_num的幅值由上面提到的cnt_w高二位控制,刚好可以控制四个数码管,刚开始以为这里的cnt_w和计数器cnt的间隔是一致的,其实不然,计数器的间隔是0.1S也就是计数2400000次,而这里的cnt_w为18位,计数次数为262144次,比0.1S小,也就是说四位数码管以小于0.1S的间隔依次点亮。
always@(*)
begin
case ( sm_seg_num )
S0:
sm_seg_reg <= 8'hc0;
S1:
sm_seg_reg <= 8'hf9;
S2:
sm_seg_reg <= 8'ha4;
S3:
sm_seg_reg <= 8'hb0;
S4:
sm_seg_reg <= 8'h99;
S5:
sm_seg_reg <= 8'h92;
S6:
sm_seg_reg <= 8'h82;
S7:
sm_seg_reg <= 8'hf8;
S8:
sm_seg_reg <= 8'h80;
S9:
sm_seg_reg <= 8'h90;
default:sm_seg_reg <= 8'hc0;
endcase
end
这里给段选寄存器sm_seg_num具体的段选信号。
例程运行效果:
本帖最后由 1nnocent 于 2022-7-23 21:42 编辑这芯片性能如何,安路是国产做的最大的FPGA厂家吧?