Seven Segment Multiplexing on Basys2

BlueSolrac picture BlueSolrac · Mar 4, 2013 · Viewed 17.4k times · Source

this is my first post so I hope I'm doing this correctly. I'm trying to output a "4 3 2 1" on a four digit seven segment display on a BASYS2 board. I have checked to make sure that 0 enables the signal and that I have the ports mapped correctly. I believe the error is within my multiplexing logic since I am only able to display a single digit. I'm new to Verilog (am used to C) and would appreciate any suggestions. Thanks

`timescale 1ns / 1ps

module main (clock, AN0, AN1, AN2, AN3, CA, CB, CC, CD, CE, CF, CG, CDP);


//USED FOR SEVEN SEG
input clock;

output AN0, AN1, AN2, AN3, CA, CB, CC, CD, CE, CF, CG, CDP;

reg [7:0] cathodedata; //cathode data
reg [3:0] anodedata; //anode data
reg [2:0] digit = 1;
reg [6:0] data;
reg setdp;
reg [19:0] counter = 0;

assign CA = cathodedata [7];
assign CB = cathodedata [6];
assign CC = cathodedata [5];
assign CD = cathodedata [4];
assign CE = cathodedata [3];
assign CF = cathodedata [2];
assign CG = cathodedata [1];
assign CDP = cathodedata [0];
assign AN3 = anodedata [3];
assign AN2 = anodedata [2];
assign AN1 = anodedata [1];
assign AN0 = anodedata [0];
//USED FOR SEVEN SEG

    //Multiplexing
    //Board Clock: 50MHz
    //p = t*f
    //t = 16ms
    //p = 16ms * 50*10^6 = 800,000 cycles
    //200,000 cycles for each digit
    //Refreshed every 16ms (~60Hz)

always@(negedge clock)
begin
    if (digit == 1)
        begin
            if (counter == 200_000)
                begin
                    digit = 2;
                end
            else
                begin
                counter = counter + 1;
                data = 4;
                end
        end
    else if (digit == 2)
        begin
            if (counter == 400_000)
                begin
                    digit = 3;
                end
            else
                begin
                    counter = counter + 1;
                    data = 3;
                end
        end
    else if (digit == 3)
        begin
            if (counter == 600_000)
                begin
                    digit = 4;
                end
            else
                begin
                    counter = counter + 1;
                    data = 2;
                end
        end
    else if (digit == 4)
        begin
            if (counter == 800_000)
                begin
                    digit = 1;
                    counter = 0;
                end 
            else
                begin
                    counter = counter + 1;
                    data = 1;
                end
        end 
end


always @ (*)
begin

    case (data)
        6'd0: cathodedata = 8'b00000011; //0
        6'd1: cathodedata = 8'b10011111; //1
        6'd2: cathodedata = 8'b00100101; //2
        6'd3: cathodedata = 8'b00001101; //3
        6'd4: cathodedata = 8'b10011001; //4
        6'd5: cathodedata = 8'b01001001; //5
        6'd6: cathodedata = 8'b01000001; //6
        6'd7: cathodedata = 8'b00011111; //7
        6'd8: cathodedata = 8'b00000001; //8
        6'd9: cathodedata = 8'b00001001; //9
        6'd10: cathodedata = 8'b00010001; //A
        6'd11: cathodedata = 8'b11000001; //B
        6'd12: cathodedata = 8'b01100011; //C
        6'd13: cathodedata = 8'b10000101; //D
        6'd14: cathodedata = 8'b00100001; //E
        6'd15: cathodedata = 8'b01110001; //F
        default: cathodedata = 8'b11111111; //default all off
    endcase

    if (setdp == 1) //decimal point
        cathodedata = cathodedata & 8'hFE;

    case(digit)
        0: anodedata = 4'b1111; //all OFF
        4: anodedata = 4'b1110; //AN0
        3: anodedata = 4'b1101; //AN1
        2: anodedata = 4'b1011; //AN2
        1: anodedata = 4'b0111; //AN3
        default:
        anodedata = 4'b1111; //all OFF
    endcase

end 
endmodule

Answer

N8TRO picture N8TRO · Mar 7, 2013

Here's a modified version of one of my pet projects. It should do exactly as you wanted: display 4 3 2 1 on the 4dig7seg display. I tested it on an Amani GTX CPLD, and it should transfer cleanly to your board.

I like separating projects into separate modules to keep organized. The top level module takes the on board clock as an input and outputs 14 signals to a four-digit-seven-segment serial display. Make sure the pins are correctly assigned.

module SevenSeg(clk, odig, onum, col, ap); 

input clk;         // 50 MHz oscillator 
output [3:0] odig; // selected digit output
output [7:0] onum; // selected display output
output col = 1;    // turns the colon off 
output ap  = 1;    // turns the apostrophe off 

wire clock;        // divided oscillator to slow the display output 

parameter a   = 8'b11111110; // low means on, high means off  
parameter b   = 8'b11111101; // these are just parameters  
parameter c   = 8'b11111011; // defining each of the 
parameter d   = 8'b11110111; // seven segments 
parameter e   = 8'b11101111; // making life easier
parameter f   = 8'b11011111; 
parameter g   = 8'b10111111; 
parameter dp  = 8'b01111111; 

parameter off = 8'b11111111; 

parameter one    =  b & c;             // parameters for outputs
parameter two    =  a & b & d & e & g; 
parameter three  =  a & b & c & d & g; 
parameter four   =  b & c & f & g; 


    wire [7:0] port1 = one;   // This is set up so these can change dynamically...
    wire [7:0] port2 = two;   // ... if so desired.     
    wire [7:0] port3 = three;
    wire [7:0] port4 = four;


slowclk m1(clk, clock);   // divides clk by 500, for 50 MHz in, 100 kHz out 

digitize m2(clock, port1, port2, port3, port4, odig, onum); // rotates the digit outputs 

endmodule 

Next I have a simple clock divider. The case count = 500 can be modified to divide your clock to whatever frequency you like. The output should look fine in the range of about 120 Hz to about 2 MHz.

module slowclk(clk, clock); 

input clk; 
output reg clock; 
integer count; 

always @(posedge clk) 
case(count)      
500: begin clock <= clock + 1; count <= 0; end   // Change 500 to any divisor you like
default:  count <= count + 1;   
endcase 
endmodule   

Then we have to rotate the digits. Note if you change the anodes and cathodes at the "same" time, there will be a small leakage current into the neighboring segments causing a "ghost" effect.

module digitize(clock, num1, num2, num3, num4, odig, onum); 

input wire clock; 
input [7:0] num1; // rightmost digit 
input [7:0] num2; 
input [7:0] num3; 
input [7:0] num4; // leftmost digit 

output reg [3:0] odig; 
output reg [7:0] onum; 

parameter [7:0] off = 8'b11111111; 

reg [3:0] dstate; 

always @(posedge clock) 
case(dstate) 
0:begin   odig   <= 4'b0001; dstate <=  1; end 
1:begin   onum   <= num1;    dstate <=  2; end 
2:begin   onum   <= off;     dstate <=  3; end  // off states prevent 'ghosting' 
3:begin   odig   <= 4'b0010; dstate <=  4; end  // when changing output digit 
4:begin   onum   <= num2;    dstate <=  5; end 
5:begin   onum   <= off;     dstate <=  6; end 
6:begin   odig   <= 4'b0100; dstate <=  7; end 
7:begin   onum   <= num3;    dstate <=  8; end 
8:begin   onum   <= off;     dstate <=  9; end 
9:begin   odig   <= 4'b1000; dstate <= 10; end 
10:begin  onum   <= num4;    dstate <= 11; end 
11:begin  onum   <= off;     dstate <=  0; end 
default:  dstate <= 0; 
endcase 
endmodule