Семисегментное мультиплексирование на Basys2

это мой первый пост, поэтому я надеюсь, что делаю это правильно. Я пытаюсь вывести «4 3 2 1» на четырехзначный семисегментный дисплей на плате BASYS2. Я проверил, чтобы убедиться, что 0 включает сигнал и правильно ли сопоставлены порты. Я считаю, что ошибка связана с моей логикой мультиплексирования, поскольку я могу отображать только одну цифру. Я новичок в Verilog (привык к C) и буду признателен за любые предложения. Спасибо

`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

person BlueSolrac    schedule 04.03.2013    source источник
comment
Вы проверяли это в симуляторе сигналов?   -  person Tim    schedule 04.03.2013
comment
Да. Мои результаты соответствуют ожиданиям. Каждый сигнал включения анода имеет низкий уровень, когда он должен быть, и в это время присутствуют правильные данные катода. Когда я загружаю файл на свою плату FPGA (Basys2), я вижу 8 в течение примерно полсекунды на AN3, а затем постоянно 4. AN2, AN1 и AN0 остаются выключенными.   -  person BlueSolrac    schedule 04.03.2013
comment
При отладке гораздо проще откусывать небольшие фрагменты и тестировать их по отдельности, чтобы сузить круг возможных проблем. Это процесс исключения.   -  person N8TRO    schedule 04.03.2013
comment
Вероятно, вам следует изменить = на <= внутри блока always @(negedge clk). Для справки найдите блокировку (=) и неблокировку (‹=).   -  person Morgan    schedule 04.03.2013
comment
Вы просмотрели все доступные журналы, созданные в процессе загрузки/синтеза, и убедились, что в них нет предупреждений или ошибок?   -  person Tim    schedule 04.03.2013
comment
setdp никогда не назначается и должно быть. Вы должны добавить асинхронный сброс в всегда блок. Во время сброса назначьте цифру, данные, setdp и счетчик предпочтительным начальным условиям.   -  person Greg    schedule 05.03.2013
comment
@Greg Я бы также предпочел, чтобы применялись асинхронные сбросы, но я думаю, что для проектов FPGA обычно просто инициализировать в объявлении, например, reg [19:0] counter = 0;?   -  person Morgan    schedule 05.03.2013
comment
@Greg Непонятно, что именно он пытается сделать с сигналом setdp.   -  person N8TRO    schedule 08.03.2013


Ответы (3)


Я тоже использую basys2 и ищу 7-сегментный драйвер. Код Nathan G у меня не работал (возможно, с использованием системного Verilog?), но я нашел эту статью: http://simplefpga.blogspot.co.uk/2012/12/scrolling-or-moving-text-using-7.html?showComment=1362783256904#c5783969326158067433, и я использовал его в своих целях. Измененный код приведен ниже. Он должен принимать (хотя я еще не проверил декодирование полностью) четыре шестнадцатеричных значения и отображать их на 7-сегментном. В моем примере на моей доске теперь написано «FAAF» (потому что заставить это работать было faf)

ucf файл взят с сайта Digilent:

# Pin assignment for DispCtl
# Connected to Basys2 onBoard 7seg display
NET "seg<0>" LOC = "L14"; # Bank = 1, Signal name = CA
NET "seg<1>" LOC = "H12"; # Bank = 1, Signal name = CB
NET "seg<2>" LOC = "N14"; # Bank = 1, Signal name = CC
NET "seg<3>" LOC = "N11"; # Bank = 2, Signal name = CD
NET "seg<4>" LOC = "P12"; # Bank = 2, Signal name = CE
NET "seg<5>" LOC = "L13"; # Bank = 1, Signal name = CF
NET "seg<6>" LOC = "M12"; # Bank = 1, Signal name = CG

NET "dp" LOC = "N13"; # Bank = 1, Signal name = DP

NET "an<3>" LOC = "K14"; # Bank = 1, Signal name = AN3
NET "an<2>" LOC = "M13"; # Bank = 1, Signal name = AN2
NET "an<1>" LOC = "J12"; # Bank = 1, Signal name = AN1
NET "an<0>" LOC = "F12"; # Bank = 1, Signal name = AN0

Модуль верхнего уровня имеет это:

module top_level(     

//clock input
input CLK,

output [7:0] seg,
output dp,
output [3:0] an

);    

scrolling_name scrolling_name(
.clock(CLK),
.reset(RESET),
.a(seg[0]),
.b(seg[1]),
.c(seg[2]),
.d(seg[3]),
.e(seg[4]),
.f(seg[5]),
.g(seg[6]),
.dp(dp),
.an(an),
//.XPosition(XPosition),
//.YPosition(YPosition)
.XPosition(8'hFA),
.YPosition(8'hAF)
);

endmodule

и файл, который я взял у парня по ссылке и отредактировал:

`timescale 1ns / 1ps

module scrolling_name(
input clock,
input reset,
output a,
output b,
output c,
output d,
output e,
output f,
output g,
output dp,
output [3:0] an,
input [7:0] XPosition,
input [7:0] YPosition
);

reg [28:0] ticker; //to hold a count of 50M
wire click;
reg [3:0] fourth, third, second, first; // registers to hold the LED values

always @ (posedge clock or posedge reset) //always block for the ticker
begin
if(reset)
ticker <= 0;
else if(ticker == 50000000) //reset after 1 second
ticker <= 0;
else
ticker <= ticker + 1;
end

reg [3:0] clickcount; //register to hold the count upto 9. That is why a 4 bit     register is used. 3 bit would not have been enough.

assign click = ((ticker == 50000000)?1'b1:1'b0); //click every second

always @ (posedge click or posedge reset)
begin
 if(reset)
  clickcount <= 0;
 else if(clickcount == 8)
   clickcount <= 0;
  else
  clickcount <= clickcount + 1;

end

always@(posedge clock)
begin
fourth = XPosition[7:4]; 
third = XPosition[3:0]; 

second = YPosition[7:4]; 
first = YPosition[3:0]; 
end

//see my other post on explanation of LED multiplexing.

localparam N = 18;

reg [N-1:0]count;

always @ (posedge clock or posedge reset)
 begin
  if (reset)
   count <= 0;
  else
   count <= count + 1;
 end

reg [6:0]sseg;
reg [3:0]an_temp;

always @ (*)
 begin
  case(count[N-1:N-2])

   2'b00 :
    begin
     sseg = first;
     an_temp = 4'b1110;
    end

   2'b01:
    begin
     sseg = second;
     an_temp = 4'b1101;
    end

   2'b10:
    begin
     sseg = third;
     an_temp = 4'b1011;
    end

   2'b11:
    begin
     sseg = fourth;
     an_temp = 4'b0111;
    end
  endcase
 end
assign an = an_temp;

reg [6:0] sseg_temp;

always @ (*)
 begin
  case(sseg)
   0 : sseg_temp = 7'b1000000; //to display 0
   1 : sseg_temp = 7'b1001111; //to display 1
   2 : sseg_temp = 7'b0100100; //to display 2
   3 : sseg_temp = 7'b0110000; //to display 3
   4 : sseg_temp = 7'b0011001; //to display 4
   5 : sseg_temp = 7'b0010010; //to display 5
   6 : sseg_temp = 7'b0000011; //to display 6
   7 : sseg_temp = 7'b1111000; //to display 7
   8 : sseg_temp = 7'b0000000; //to display 8
   9 : sseg_temp = 7'b0011000; //to display 9
   10 : sseg_temp = 7'b0001000; //to display A
   11 : sseg_temp = 7'b0000011; //to display B
   12 : sseg_temp = 7'b1000110; //to display C
   13 : sseg_temp = 7'b0100001; //to display D
   14 : sseg_temp = 7'b0000110; //to display E
   15 : sseg_temp = 7'b0001110; //to display F

default : sseg_temp = 7'b1111111; //blank
endcase
end

assign {g, f, e, d, c, b, a} = sseg_temp;
assign dp = 1'b1;

endmodule

Пожалуйста, извините за ужасное форматирование/отступы - есть комбинация его, моего и добавления четырех пробелов, чтобы получить переполнение стека, чтобы распознать его как код. У меня нет времени, чтобы сделать вещи красиво, к сожалению. Я могу собрать все это вместе и выложить на дропбокс, если хотите.

Натан

person Nathan Hutton    schedule 08.03.2013
comment
Я только что запустил свой код, там была одна небольшая ошибка, которую я исправил. (Я ошибся в объявлениях портов) Если у вас работает, это здорово. Но я бы подумал о реорганизации макета вашего кода, чтобы сделать его более читабельным. - person N8TRO; 09.03.2013
comment
@Nathan G - Да, объявления портов были одной из всплывающих ошибок ... Но в конце концов сегменты просто остались пустыми. Не знаю, почему, и я немного торопился, поэтому просто пошел дальше. Возможно, что-то действительно простое, например назначение контактов. Я предполагаю, что если оригинальный постер найдет мой код полезным, я почищу его через несколько дней/когда у меня будет время. - person Nathan Hutton; 09.03.2013
comment
Ребята, спасибо вам обоим за ваш код. Я чувствую себя очень глупо. Мой код был действительно в порядке. Мой UCF был неправильным. У меня часы были привязаны к RCCLK (C8) вместо MCLK (B8). Дох! Я узнал больше о Verilog, изучив оба ваших кода. Мне также нравится пример прокручиваемого текста, который вы нашли, так как я могу реализовать его в будущем. - person BlueSolrac; 11.03.2013
comment
@BlueSolrac - По моему мнению, поиск небольших ошибок всегда занимает больше всего времени. Хорошим советом является загрузка файла ucf с сайта Digilent: digilentinc.com/ Products/Detail.cfm?Prod=BASYS2 - вы можете раскомментировать нужные вам строки из их файла, не прибегая к усилиям по поиску назначений контактов (и они тоже должны быть правильными, в отличие от маркировки печатной платы). - person Nathan Hutton; 12.03.2013

Вот модифицированная версия одного из моих любимых проектов. Он должен работать именно так, как вы хотели: отображать 4 3 2 1 на дисплее 4dig7seg. Я протестировал его на Amani GTX CPLD, и он должен без проблем перенестись на вашу плату.

Мне нравится разделять проекты на отдельные модули, чтобы поддерживать порядок. Модуль верхнего уровня принимает встроенные часы в качестве входных данных и выводит 14 сигналов на четырехразрядный семисегментный последовательный дисплей. Убедитесь, что контакты назначены правильно.

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 

Далее у меня есть простой делитель часов. количество регистров = 500 можно изменить, чтобы разделить ваши часы на любую частоту, которую вы хотите. Выходной сигнал должен хорошо выглядеть в диапазоне от 120 Гц до 2 МГц.

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   

Затем мы должны повернуть цифры. Обратите внимание, что если вы замените аноды и катоды в «одно и то же» время, будет небольшая утечка тока в соседние сегменты, вызывающая «фантомный» эффект.

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 
person Community    schedule 07.03.2013

Ближе к концу у вас есть:

always @(*) begin
...
  if (setdp == 1) //decimal point
    cathodedata = cathodedata & 8'hFE;

always @* подразумевает комбинаторный блок, который не является триггером для разрыва циклов и т. д. Возможно, ваш симулятор этого не уловил, но я ожидаю, что у него будут реальные проблемы при синтезе.

По сути, у вас есть катодные данные, управляющие собой.

Я бы заменил это на что-то вроде:

always @(*) begin
...
  if (setdp == 1) //decimal point
    cathodedata_mask = cathodedata & 8'hFE;
  else 
    cathodedata_mask = cathodedata;
person Morgan    schedule 06.03.2013
comment
Думаю, я не согласен с тем, что его код вызовет проблемы с синтезом, я почти уверен, что использовал такие конструкции раньше. cathodedata уже был бы установлен предыдущим оператором case, поэтому я думаю, что это по сути похоже на размещение мультиплексора на выходе оператора case и управление выводом оператора case на один вход и управление выводом оператора case. и перешел с FE на другой порт. Выходной провод будет называться cathodedata. - person Tim; 07.03.2013
comment
Я понимаю вашу точку зрения @Tim, катодные данные всегда назначаются непосредственно перед этим. Я думаю, что я бы избегал такого кодирования. - person Morgan; 07.03.2013