Skocz do zawartości

FPGA (własne programy #4): komunikacja FPGA-Arduino UART-RS485


Pomocna odpowiedź

FlyingDutch, widzę, że kurs FPGA ma swoje "drugie życie". Super, że powstają takie projekty! Zachęcam innych do dzielenia się swoimi pomysłami. Od razu uspokoję wszystkich, że kolejny odcinek kursu niedługo się pojawi. Nic tutaj nie uległo zmianie - idziemy zgodnie z planem 😉

Link to post
Share on other sites
FlyingDutch, widzę, że kurs FPGA ma swoje "drugie życie". Super, że powstają takie projekty! Zachęcam innych do dzielenia się swoimi pomysłami. Od razu uspokoję wszystkich, że kolejny odcinek kursu niedługo się pojawi. Nic tutaj nie uległo zmianie - idziemy zgodnie z planem 😉

Cześć Treker,

największym problemem jest brak czasu (jeśli ktoś pracuje na pełen etat, to wolnego czasu jest naprawdę mało). dużo łatwiej jest coś zaprojektować niż porządnie to opisać (przynajmniej w moim przypadku). Ale postaram się opisać jeszcze kilka tematów, które wydają mi się interesujące 😃

Miałbym prośbę, czy moglibyście dodać typ multimedów "mp4" (kodek H.264) do obsługiwanych formatów zalączników (tak, aby linków do wideo prezentacji nie trzeba było zamieszczać na obcych serwerach)?

Akurat typ wideo realmedia nie za bardzo mi działa z programem FFMPEG, który używam do konwersji z formatu MOV, który jest jedynym jaki ma mój aparat do zdjęć.

Co do kursu to jest OK, wiem, że jak chce się coś porządnie wytłumaczyć to zajmuje to sporo czasu, a temat "skończonych automatów stanowych" jest trochę trudniejszy niż poprzednie.

Zyczę Wesołych Swiąt 🙂

Link to post
Share on other sites

FlyingDutch, na ten moment niestety dodawanie filmów prosto na forum nie będzie możliwe, bo nie mamy do tego odpowiednich mechanizmów. Te wbudowane nie nadają się do obsługiwania takiego ruchu jaki mamy teraz na Forbocie + nie działa to poprawnie we wszystkich przeglądarkach. Postaram się zmienić to w przyszłości, przy nowej wersji całego forum, jednak do tego czasu "niestety" najlepiej zamieszczać filmy osadzając w postach YouTube 😉

Link to post
Share on other sites
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

FlyingDutch, na ten moment niestety dodawanie filmów prosto na forum nie będzie możliwe, bo nie mamy do tego odpowiednich mechanizmów. Te wbudowane nie nadają się do obsługiwania takiego ruchu jaki mamy teraz na Forbocie + nie działa to poprawnie we wszystkich przeglądarkach. Postaram się zmienić to w przyszłości, przy nowej wersji całego forum, jednak do tego czasu "niestety" najlepiej zamieszczać filmy osadzając w postach YouTube 😉

OK - no problemo 😃

Link to post
Share on other sites
(edytowany)
Dnia 22.12.2017 o 15:12, FlyingDutch napisał:

Witam wszystkich,

chciałbym przedstawić przykład jednokierunkowej komunikacji pomiędzy płytką FPGA Elbert V2 a mikro-kontrolerem (w tym przypadku klon "Arduino UNO"). Przedstawiam ten przykład jako pierwszy ponieważ jest najprostszy z tematów jakie chciałbym poruszyć, a ma duże zastosowanie praktyczne: komunikacja FPGA -> Arduino na duże odległości za pomocą pojedyńczej pary przewodów (może to być kabel telefoniczny 1-na para lub zwykły kabel sieciowy Ethernet np. kategorii 5+).

Taka potrzeba wynikła z praktycznej potrzeby mojego projektu sterownika rolet na drzwiach balkonowych. Chciałem wyświetlać stan kontrolera na małym wyświetlaczu OLED podłączonym do mikro-kontrolera Arduino UNO. Ponieważ wymagana długość przewodów wynosiła ponad 8 metrów zdecydowałem się na użycie protokołu "RS-485". Na naszej płytce FPGA (Elbert V2) mam zaimplementowaną komunikację UART - tylko Tx wysyłanie, ponieważ komunikacja w drugą stronę nie była mi potrzebna (oczywiście nic nie stoi na przeszkodzie aby zaimplementować dwustronną komunikację UART FPGA-mikro-kontroler).

Co będzie nam potrzebne do realizacji tego przykładu:

1) Zestaw FPGA - Elbert V2 (Xilinx Spartan 3A)

2) Arduino UNO lub jego klon

3) Kabel sieciowy o odpowiedniej dla nas długości (do testu może to być nawet typowy patchcord)

4) 2 sztuki konwertera UART-RS485

5) Środowisko Xilinx "ISE Webpack" najlepiej wersja 14.7

6) Środowisko "Arduino IDE" w wersji powyżej 1.6.5

Ja używam bardzo tanich konwerterów firmy Waveshare (cena 10,63 PLN w kamami.pl). Oto linki:

https://kamami.pl/konwertery-rs485/564661-konwerter-uart-rs485-33v.html

Tutaj manual do tego typu konwertera UART-RS485:

https://www.waveshare.com/w/upload/9/95/RS485-Board-UserManual.pdf

Przepraszam za błąd w pierwszej wersji tego postu zamieściłem link do wersji 5V zamiast 3,3 V tego konwertera UART-RS485

Dopiero teraz zauważyłem, że ten typ konwertera jest także dostępny w Botlandzie. Oto link:

https://botland.com.pl/konwertery-usb-uart-rs232-rs485/4496-konwerter-uart-rs485-33v-arkrj11.html

Można też użyć trochę droższych konwerterów UART-RS485 firmy SparkFun dostępnych w botland.pl (projekt był też z nimi testowany):

https://botland.com.pl/konwertery-usb-uart-rs232-rs485/2449-konwerter-uart-rs485-sp3485-sparkfun.html?search_query=Konwerter+UART-RS485+SP3485&results=2

Rzeczywista implementacja komunikacji UART-RS485 w moim projekcie jest trochę bardziej złożona, ale jako przykład starałem się użyć jak najprostszego kodu, aby nie zaciemniać podstaw. Punktem wyjścia był projekt FPGA-UART na Github'ie o nazwie "FPGA Peripherals":

https://github.com/FPGAwars/FPGA-peripherals/wiki

Tutaj Wiki dla "UART-Tx" z opisem działania kodu FPGA (Verilog):

https://github.com/FPGAwars/FPGA-peripherals/wiki/Asynchronous-serial-transmitter-unit

Zmodyfikowałem trochę ten kod dostępny na stronie projektu:

1) Zmieniłem szybkość transmisji na 9600 bodów ( z 115200 bodów - dla tej prędkości komunikacja pomiędzy Elbertem a Arduino szwankowała).

2) Dostosowałem projekt do płytki Elbert V2 (zegar i plik ucf)

3) zmieniłem wysyłanie komunikatu na naciśniecie przycisku S5 (z wysyłania komunikatu w pętli w oryginale)

Oto zdjęcie zmontowanego układu próbnego (Elbert V2, chiński klon Arduino Uno, dwa konwertery UART-RS485 firmy Waveshare + kabel sieciowy):

IMG_5a3d1238bdc823918.jpg

Zakładam, że wiecie jak zainstalować środowisko "ISE Webpack" - jest to nieżle opisane w kursie FPGA Forbot, więc nie będę poruszał tego tematu. Tak samo zakładam, że potraficie zainstalować środowisko "Arduino IDE" (ja używam wersji 1.8.4).

jeśli nie macie zainstalowanego "Arduino IDE" zajrzyjcie na stronę:

http://www.arduino.cc/en/Main/Software

i pobierzcie pakiet instalacyjny z tej strony.

Będziemy jeszcze potrzebować biblioteki do Arduino do obsługi protokołu RS485:

https://github.com/Protoneer/RS485-Arduino-Library

Pobieramy bibliotekę z tej strony i instalujemy zgodnie z opisem na tej stronie.

Teraz przechodzimy do środowiska do syntezy układu FPGA "ISE Webpack". Oto zrzut ekranu z naszym projektem UART-Tx dla FPGA:

IMG_5a3d1d5cdd23c6130.png

Jak widać po syntezie nasz projekt zajmuje 64 LUT co stanowi około 4% zasobów naszego Spartan3 z Elbert'a.

W pliku UART-SEND.zip spakowany cały projekt FPGA dla "ISE Webpack 14.7":

Jak łączymy wszystko w całość:

1) Wybieramy jedną parę przewodów z kabla sieciowego z obciętymi końcówkami (ja wybrałem parę: pomarańczowy-białopomarańczowy). Do 2 konwerterów UART-RS485 podłączamy przwewody - tak samo dla obu konwerterów:

Pomarańczowy do zacisku śrubowego B

Biało-pomarańczowy do zacisku śrubowego A

Możemy też użyć kabla telefonicznego ze złączem RJ-11 (ja niestety nie mam takiego kabla ani złącz)

Podaję teraz opis pinów dla konwertera UART-RS485 firmy Waveshare:

IMG_5a3d222b591415024.png

Jak wynika z pliku txtstr_UCF.ucf dla naszego projektu FPGA:




NET "clk"       LOC = P129; 
NET "clk"       PERIOD = 12MHz;

NET "rstn" PULLUP;
NET "rstn"  LOC = "P76";

NET "tx"  LOC = "P141" | IOSTANDARD = LVCMOS33;
 

W płytce Elbert do podłączenia konwertera UART-RS485 wykorzystujemy switch "SW5" reset do wysyłania komunikatu (jest to najbardziej dolny switch z pięciu), oraz Header P4 (inaczej niż na zamieszczonym zdjęciu - tam był wykorzystany header P6). Header P4 jest to złącze najbardziej na lewo płytki Elbert pod wyświetlaczem 7-mio segmentowym. Pin 'tx" jest to pierwszy pin w złączu górny najbardziej na prawo - do niego podłączamy PIN oznaczony jako "DI-->" w konwerterze UART (na zdjęciu Pinów dla konwertera oznaczony jako "DI"). Pin "Vcc" z konwertera podłączamy do pinu 3V3 z headera P6 Elberta (dwa najbardziej na lewo piny złącza). Pin "GND" z konwertera do pinu "GND" headera P6 oraz pinb "RSE" konwertera także do masy na headerze P6 (dwa piny góra-dól jedna pozycja na prawo od "3V3" na headezre P6 Elberta). Tym samym mamy załatwione połączenia układu od strony płytki Elbert.

Teraz zajmiemy się połączeniami do pytki "Arduino UNO".Oto PINOUT dla tej płytki:

ARDUINO_V2.png

Istotne są dla nas numery pinów w różowo-fioletowym kolorze oznaczone w legendzie jako "IDE" (takie numery będziemy wpisywać w pliku includes (RS485.h) dla biblioteki "RS485"

dla Arduino. Plik ten jest standardowo umieszczony w lokalizacji:

C:\Users\WaszUserWWindows\Documents\Arduino\libraries\RS485-Arduino-Library-master\RS485.h (dla domyślnej instalacji ARDUINO IDE). Zmieniamy ten plik tak, że RX_PIN ma numer 2, TX_PIN ma numer 8, a ENABLE_PIN ma numer 7 (numery jak na obrazku PINOUT dla Arduino IDE):




#include "Arduino.h"
#include <SoftwareSerial.h>

const byte RX_PIN = 2;
const byte TX_PIN = 8;
const byte ENABLE_PIN = 7;

const int maxMsgLen = 20;
const int STX = 2;
const int ETX = 3;

typedef void (*WriteCallback)  (const byte what);    // send a byte to serial port
typedef int  (*AvailableCallback)  ();    // return number of bytes available
typedef int  (*ReadCallback)  ();    // read a byte from serial port

void fWrite (const byte what);
int fAvailable ();
int fRead ();



//byte crc8 (const char *addr);

//void EncodeMessage(char message[maxMsgLen+1], unsigned char data[maxMsgLen+1+3]);

void RS485_Begin(int baud);
bool RS485_SendMessage(char message[maxMsgLen+1],WriteCallback fWrite,int EnablePin);
bool RS485_ReadMessage(AvailableCallback fAvailable, ReadCallback fRead, char data[maxMsgLen+3+1]);
bool RS485_ReadPlainMessage(AvailableCallback fAvailable, ReadCallback fRead, char message[maxMsgLen+3+1]);
 

Jesli zmienicie numery pinów w pliku "RS485" tak jak w podanym powyżej przykładzie to połączenia pomiedzy płytką "Arduino UNO" wykonujemy następująco:

1) PIN "RO" konwertera ("RO<--") do pinu numer 2 płytki Arduino (numery IDE na PINOUT)

2) PIN "RSE" konwertera do pinu 7 płytki Arduino

3) PIN "Vcc" konvertera do pinu "3.3V" Arduino

4) pin "GND" konwertera do pinu "GND" Arduino (są 2 takie piny w Arduino)

Tak mamy załatwione wszystkie wymagane połączenia.

Przejdźmy teraz do kodu projektów FPGA oraz programu dla Arduino:

Najpierw projekt części FPGA:

w pliku "BAUDO- baudgen _tx" zmieniamy:

parameter BAUDRATE = `B9600




//-----------------------------------------------------------------------------
//-- Baudrate generator
//-- It generates a square signal, with a frequency for communicating at the given
//-- given baudrate
//-- The output is set to 1 only during one clock cycle. The rest of the time is 0
//--------------------------------------------------------------------------------
//-- (c) BQ. December 2015. written by Juan Gonzalez (obijuan)
//-----------------------------------------------------------------------------
//-- GPL license
//-----------------------------------------------------------------------------
`default_nettype none
`include "baudgen.vh"

//----------------------------------------------------------------------------------------
//-- baudgen module
//--
//-- INPUTS:
//--     -clk: System clock (12 MHZ in the iceStick board)
//--     -clk_ena: clock enable:
//--            1. Normal working: The squeare signal is generated
//--            0: stoped. Output always 0
//-- OUTPUTS:
//--     - clk_out: Output signal. Pulse width: 1 clock cycle. Output not registered
//--                It tells the uart_tx when to transmit the next bit
//--      __                                                         __
//--   __| |________________________________________________________| |________________
//--   ->  <- 1 clock cycle
//--
//---------------------------------------------------------------------------------------
module baudgen_tx #(
         parameter BAUDRATE = `B9600  //-- Default baudrate
)(
         input wire rstn,              //-- Reset (active low)
         input wire clk,               //-- System clock
         input wire clk_ena,           //-- Clock enable
         output wire clk_out           //-- Bitrate Clock output
);

//-- Number of bits needed for storing the baudrate divisor
//localparam N = $clog2(BAUDRATE);
localparam N = 17;

//-- Counter for implementing the divisor (it is a BAUDRATE module counter)
//-- (when BAUDRATE is reached, it start again from 0)
reg [N-1:0] divcounter = 0;

always @(posedge clk)

 if (!rstn)
   divcounter <= 0;

 else if (clk_ena)
   //-- Normal working: counting. When the maximum count is reached, it starts from 0
   divcounter <= (divcounter == BAUDRATE - 1) ? 0 : divcounter + 1;
 else
   //-- Counter fixed to its maximum value
   //-- When it is resumed it start from 0
   divcounter <= BAUDRATE - 1;

//-- The output is 1 when the counter is 0, if clk_ena is active
//-- It is 1 only for one system clock cycle
assign clk_out = (divcounter == 0) ? clk_ena : 0;

endmodule

 

W zamieszczonym projekcie jest to już zmienione (są wszystkie wymagane zmiany).

Główny plik projektu entity - txstr wygląda następująco:




//-----------------------------------------------------------------------------------------
//-- txstr: Uart_tx example 2
//-- Transmission of string
//-- The reset signal is connected to the dtr signal (in file txstr.pcf)
//-- Fot this example to work is necessary to open a serial terminal (gtkterm for example)
//-- and deactivate DTR. Every time a reset is done, a string appears on the terminal
//-- Fixed BAUDRATE: 115200
//-----------------------------------------------------------------------------------------
//-- (C) BQ. December 2015. Written by Juan Gonzalez (Obijuan)
//-- GPL license
//-----------------------------------------------------------------------------------------
`default_nettype none
`include "baudgen.vh"

//-- Top entity
module txstr #(
         parameter BAUDRATE = `B9600
)(
         input wire clk,   //-- System clock
         input wire rstn,  //-- Reset (active low)
         output wire tx    //-- Serial data output
);

//-- Connecting wires
wire ready;
reg start = 0;
reg [7:0] data;

//-- Serial Unit instantation
uart_tx #(
   .BAUDRATE(BAUDRATE)  //-- Set the baudrate

 ) TX0 (
   .clk(clk),
   .rstn(rstn),
   .data(data),
   .start(start),
   .tx(tx),
   .ready(ready)
);
//-- It only counts when the cena control signal is enabled
reg [2:0] char_count;
reg cena;  

//-- Multiplexer with the 8-character string to transmit
always @*
 case (char_count)
   8'd0: data <= "H";
   8'd1: data <= "e";
   8'd2: data <= "l";
   8'd3: data <= "l";
   8'd4: data <= "o";
   8'd5: data <= "!";
   8'd6: data <= ".";
   8'd7: data <= ".";
   default: data <= ".";
 endcase

//-- Characters counter
             //-- Counter enable

always @(posedge clk)
 if (!rstn)
   char_count = 0;
 else if (cena)
   char_count = char_count + 1;


//--------------------- CONTROLLER

localparam INI = 0;
localparam TXCAR = 1;
localparam NEXTCAR = 2;
localparam STOP = 3;

//-- fsm state
reg [1:0] state;
reg [1:0] next_state;

//-- Transition between states
always @(posedge clk) begin
 if (!rstn)
   state <= INI;
 else
   state <= next_state;
end

//-- Control signal generation and next states
always @(*) begin
 next_state = state;
 start = 0;
 cena = 0;

 case (state)
   //-- Initial state. Start the trasmission
   INI: begin
     start = 1;
     next_state = TXCAR;
   end

   //-- Wait until one car is transmitted
   TXCAR: begin
     if (ready)
       next_state = NEXTCAR;
   end

   //-- Increment the character counter
   //-- Finish when it is the last character
   NEXTCAR: begin
     cena = 1;
     if (char_count == 7)
       next_state = STOP; //next_state = STOP; | next_state = INI;
     else
       next_state = INI;
   end

 endcase
end


endmodule

 

W projekcie jest także wygenerowany plik binarny konfiguracji FPGA o nazwie "txstr.bin". Plik ten za pomocą programu "Elbert V2 config" wczytujemy do naszej płytki FPGA.

Do Arduino-UNO (lub jego klonu) wczytujemy następujący program:




// Receive
#include <RS485.h>
#include <SoftwareSerial.h>

char message[maxMsgLen+3+1];

void setup()
{
 Serial.begin(9600);
 Serial.println("System Startup - Receiver");

 RS485_Begin(9600);
}

void loop()
{
 if(RS485_ReadPlainMessage(fAvailable,fRead,message))
 {
   Serial.print("Receiving:");
   Serial.println(message);
 }
}
 

Jak widać program oprócz biblioteki "RS485" używa także biblioteki "SoftwareSerial" 9jeśli nie mamy jej zainstalowanej w "Arduino IDE" musimy ją pobrać z sieci i zainstalować.

Po wgraniu programów do płytki FPGA i Arduino włączamy oba układy (ja zasilam oba z wyjść USB w moim kompie). Następnie z IDE Arduino menu "Tools"-> "Serial Monitor"

Pojawi się okienko serial monitora, ustawiamy prędkość na 9600 baud i "No line endings"

Teraz każde naciśniecie switch'a "SW5" w płytce Elbert spowoduje pojawienie się komunkatu: "Receiving:Hello!.."

Patrz zamieszczony zrzut ekranu:

IMG_5a3d39564fdb26974.png

A tutaj link do prezentacji wideo działania układu (mp4):

https:

//www.dropbox.com/s/d3oyce8krxwlmbc/MVI_0042.mp4?dl=0

UART_SEND.zip 20 MB · 195 downloads

https://www.sunrom.com/p/rs485-ttl-module

Jak mam taki moduł gdzie zamiast RSE jest RE i DE to użyć RE zamiast RSE czy DE też powinnam podłączyć?

Działa mi samo "Receiving:" bez "Hello!..". Podpięłam RE do VCC, bo sprawdziłam, że wtedy odbieranie jest wyłączone (to po stronie elberta)

@FlyingDutch

Edytowano przez monsiw
Link to post
Share on other sites

Znalazłam kod, który pozwala wysyłać przez UART z terminala cyfrę na wyświetlacz 7-segmentowy, ale dedykowany do płytki GO BOARD i zastanawiam się jak go przerobić by działał do elbertv2.

Oryginał

UART_RX.v

/////////////////////////////////////////////////////////////////////
// File Downloaded from http://www.nandland.com
/////////////////////////////////////////////////////////////////////
// This file contains the UART Receiver.  This receiver is able to
// receive 8 bits of serial data, one start bit, one stop bit,
// and no parity bit.  When receive is complete o_rx_dv will be
// driven high for one clock cycle.
// 
// Set Parameter CLKS_PER_BIT as follows:
// CLKS_PER_BIT = (Frequency of i_Clock)/(Frequency of UART)
// Example: 25 MHz Clock, 115200 baud UART
// (25000000)/(115200) = 217
 
module UART_RX
  #(parameter CLKS_PER_BIT = 217)
  (
   input        i_Clock,
   input        i_RX_Serial,
   output       o_RX_DV, //--DV data valid, pojedynczy puls na cykl zegara
   output [7:0] o_RX_Byte //-- bajt otrzymywany z komputera
	//--receive from computer to fpga : serial (one bit at a time) to parrallel (you can see entire byte)
   );
   
  parameter IDLE         = 3'b000;
  parameter RX_START_BIT = 3'b001;
  parameter RX_DATA_BITS = 3'b010;
  parameter RX_STOP_BIT  = 3'b011;
  parameter CLEANUP      = 3'b100;
  
  reg [7:0]     r_Clock_Count = 0;
  reg [2:0]     r_Bit_Index   = 0; //8 bits total
  reg [7:0]     r_RX_Byte     = 0;
  reg           r_RX_DV       = 0;
  reg [2:0]     r_SM_Main     = 0;
  
  
  // Purpose: Control RX state machine
  always @(posedge i_Clock)
  begin
      
    case (r_SM_Main)
	 //--operacje dla poszczegolnych stanow w automacie
      IDLE :
        begin
          r_RX_DV       <= 1'b0;
          r_Clock_Count <= 0;
          r_Bit_Index   <= 0;
          
          if (i_RX_Serial == 1'b0)          // Start bit detected
            r_SM_Main <= RX_START_BIT;
          else
            r_SM_Main <= IDLE;
        end
      
      // Check middle of start bit to make sure it's still low
      RX_START_BIT :
        begin
          if (r_Clock_Count == (CLKS_PER_BIT-1)/2)
			 //--tu spr czy jestesmy w srodku bitu0
          begin
            if (i_RX_Serial == 1'b0)
				//--jesli stan jest niski czyli mamy wyzwolenie zboczem
            begin
              r_Clock_Count <= 0;  // reset counter, found the middle
              r_SM_Main     <= RX_DATA_BITS; //--przejscie do stanu w ktorym otrzymuje sie dane (Kolejne bity)
            end
            else
              r_SM_Main <= IDLE;
          end
          else
          begin
            r_Clock_Count <= r_Clock_Count + 1;
            r_SM_Main     <= RX_START_BIT;
          end
        end // case: RX_START_BIT
      
      
      // Wait CLKS_PER_BIT-1 clock cycles to sample serial data
      RX_DATA_BITS :
        begin
          if (r_Clock_Count < CLKS_PER_BIT-1)
			 //-- jesli odliczanie cykli zegara jest mniejsze od limitu to wykonuje nastepujace instrukcje
          begin
            r_Clock_Count <= r_Clock_Count + 1;
            r_SM_Main     <= RX_DATA_BITS;
          end
          else
          begin
            r_Clock_Count          <= 0;//--reset licznika
            r_RX_Byte[r_Bit_Index] <= i_RX_Serial;//--probkowanie linii danych
            
            // Check if we have received all bits
            if (r_Bit_Index < 7)
            begin
				//--dopoki indeks w rejestrze r_RX_Byte jest mniejszy od 7 to nastepuje:
              r_Bit_Index <= r_Bit_Index + 1;
              r_SM_Main   <= RX_DATA_BITS;
            end
            else
            begin
              r_Bit_Index <= 0;
              r_SM_Main   <= RX_STOP_BIT;
            end
          end
        end // case: RX_DATA_BITS
      
      
      // Receive Stop bit.  Stop bit = 1
      RX_STOP_BIT :
        begin
          // Wait CLKS_PER_BIT-1 clock cycles for Stop bit to finish
          if (r_Clock_Count < CLKS_PER_BIT-1)
          begin
            r_Clock_Count <= r_Clock_Count + 1;
     	    r_SM_Main     <= RX_STOP_BIT;
          end
          else
          begin
       	    r_RX_DV       <= 1'b1;//-- to mowi ze dana zawarta w bajcie jest aktualna i mozna zajrzec do rejestru r_RX_Byte
            r_Clock_Count <= 0;
            r_SM_Main     <= CLEANUP;
          end
        end // case: RX_STOP_BIT
      
      
      // Stay here 1 clock
      CLEANUP :
		//--aby sie upewnic ze wszystko jest ok
        begin
          r_SM_Main <= IDLE;
          r_RX_DV   <= 1'b0;
        end
      
      
      default :
        r_SM_Main <= IDLE;
      
    endcase
  end    
  
  assign o_RX_DV   = r_RX_DV;
  assign o_RX_Byte = r_RX_Byte;
  
endmodule // UART_RX

UART_RX_To_7_Seg_Top.v

module UART_RX_To_7_Seg_Top
  (input i_Clk,     // Main Clock
   input i_UART_RX, // UART RX Data
			 
	output [7:0] o_led_segment,
	output [2:0] o_led_enable);
	
 	wire [7:0] w_led_segment;
	wire [2:0] w_led_enable;
	
   wire w_RX_DV;
   wire [7:0] w_RX_Byte;
 
  // 25,000,000 / 115,200 = 217
  UART_RX #(.CLKS_PER_BIT(217)) UART_RX_Inst
  (.i_Clock(i_Clk),
   .i_RX_Serial(i_UART_RX),
   .o_RX_DV(w_RX_DV),
   .o_RX_Byte(w_RX_Byte));
    
   
  // Binary to 7-Segment Converter 
  Binary_To_7Segment SevenSeg1_Inst
  (.i_Clk(i_Clk),
   .i_Binary_Num(w_RX_Byte[7:0]),
   .o_led_enable(w_led_enable));
    
  assign o_led_segment = ~w_led_segment;

endmodule

pins.ucf

CONFIG VCCAUX = "3.3" ;

# Clock 12 MHz
NET "i_Clk"                  LOC = P129  | IOSTANDARD = LVCMOS33 | PERIOD = 12MHz;

NET "led_segment[7]"             LOC = P117   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "led_segment[6]"             LOC = P116   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "led_segment[5]"             LOC = P115   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "led_segment[4]"             LOC = P113   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "led_segment[3]"             LOC = P112   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "led_segment[2]"             LOC = P111   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "led_segment[1]"             LOC = P110  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "led_segment[0]"             LOC = P114   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;

NET "led_enable[2]"             LOC = P120   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "led_enable[1]"             LOC = P121   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "led_enable[0]"             LOC = P124   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
  
//input i_UART_RX
	
//input        i_RX_Serial,
//output       o_RX_DV,
//output [7:0] o_RX_Byte

 

  • Lubię! 1
Link to post
Share on other sites

Z czym konkretnie jest problem?

 

Generalnie, jeżeli kod modułu UART_RX jest prawidłowy, to nie powinien wymagać wewnętrznych zmian.
Jedyna zmiana (na zewnątrz tego modułu – w nadrzędnej jednostce projektowej) jest opisana w komentarzach: parametr CLKS_PER_BIT. Dla przykładowego zegara 25 MHz i żądanej prędkości transmisji 115200 b/s, wynosi 217 – jest to iloraz tych wartości. Musisz to policzyć dla Twojego zegara 12 MHz i oczekiwanej prędkości i wpisać tę wartość w pliku nadrzędnym, tam, gdzie  używasz modułu UART_RX.

 

Poza tym musisz dodać gdzieś w pliku ucf deklarację pinu Rx używanego w uarcie. Z tego co kojarzę, to płytka ElbertV2 nie ma wbudowanego uartu – czy posiadasz konwerter USB-UART?

 

 

 

Jeśli chodzi o obsługę wyświetlaczy, to trzeba dopisać jakiś kod multipleksujący. Widać, że w oryginalnym kodzie segmenty dwóch wyświetlaczy są sterowane bezpośrednio, jednak na płytce ElbertV2 tak nie można tego zrealizować ze względu na elektryczne połączenia. W przykładzie masz dwa wyświetlacze siedmiosegmentowe sterowane bezpośrednio – linie sterujące każdym segmentem każdego wyświetlacza są podłączone do FPGA. W przypadku płytki ElbertV2 do układu FPGA jest doprowadzony jeden pakiet linii sterujących segmentami wyświetlacza, a za pomocą dodatkowych sygnałów led_enable wybierany jest odpowiedni wyświetlacz. Pozwala to na spore oszczędności w liczbie użytych pinów układu programowalnego.
W Twoim przypadku musisz zrealizować następującą sekwencję:
1. wystawienie na wyjście led_segment sygnałów do sterowania segmentami z pierwszego wyświetlacza, jednoczesna aktywacja wejścia zezwalającego tylko tego wyświetlacza (pozostałe wejścia zezwalające wyłączone);
2. wystawienie na wyjście sygnałów z drugiego wyświetlacza (z drugiego modułu Binary_To_7Segment), dezaktywacja wejścia zezwalającego pierwszego wyświetlacza i jednoczesna aktywacja wejścia zezwalającego drugiego wyświetlacza.
Taką naprzemienną pracę musisz ciągle kontynuować. To przełączanie w najprostszym przypadku może się odbywać co takt zegara, ale można także zmniejszyć tę częstotliwość, ale tak, żeby nie stracić wrażenia jednoczesnego świecenia obydwu wyświetlaczy. Jest to tak zwane dynamiczne lub multipleksowane sterowanie wyświetlaczami siedmiosegmentowymi.

 

Czy posiadasz kod modułu Binary_To_7Segment? Musisz użyć dwóch takich modułów, jak w oryginalnym kodzie. Ich argumentem wejściowym obok zegara jest czterobitowy wektor – 4 bity są wyświetlane jako liczba szesnastkowa. Jeżeli nie masz kodu, to musisz samodzielnie napisać kod konwertujący liczbę na odpowiednie segmenty do wyświetlenia (to już się przewinęło w temacie, tylko tam był przypadek liczby trzybitowej, a tutaj musisz rozszerzyć do czterech bitów).

 

 

Możesz też zastosować inne podejście, z jednym modułem Binary_To_7Segment (lub własnym odpowiednikiem). Przełączanie można zrealizować na wejściu tego modułu, na które podasz naprzemiennie młodszą i starszą połówkę odebranego bajtu. Jednak nadal trzeba zapewnić odpowiednie sterowanie sygnałami led_enable, tak, żeby we właściwym momencie przełączać aktywne wyświetlacze.

 

 

 

 

  • Lubię! 1
Link to post
Share on other sites

To jest ten program Binary_To_7Segment 

///////////////////////////////////////////////////////////////////////////////
// File downloaded from http://www.nandland.com
///////////////////////////////////////////////////////////////////////////////
// This file converts an input binary number into an output which can get sent
// to a 7-Segment LED.  7-Segment LEDs have the ability to display all decimal
// numbers 0-9 as well as hex digits A, B, C, D, E and F.  The input to this
// module is a 4-bit binary number.  This module will properly drive the
// individual segments of a 7-Segment LED in order to display the digit.
// Hex encoding table can be viewed at:
// http://en.wikipedia.org/wiki/Seven-segment_display
///////////////////////////////////////////////////////////////////////////////
 
module Binary_To_7Segment 
  (
   input       i_Clk,
   input [3:0] i_Binary_Num,
   output      o_Segment_A,
   output      o_Segment_B,
   output      o_Segment_C,
   output      o_Segment_D,
   output      o_Segment_E,
   output      o_Segment_F,
   output      o_Segment_G
   );
 
  reg [6:0]    r_Hex_Encoding = 7'h00;
   
  // Purpose: Creates a case statement for all possible input binary numbers.
  // Drives r_Hex_Encoding appropriately for each input combination.
  always @(posedge i_Clk)
    begin
      case (i_Binary_Num)
        4'b0000 : r_Hex_Encoding <= 7'h7E;
        4'b0001 : r_Hex_Encoding <= 7'h30;
        4'b0010 : r_Hex_Encoding <= 7'h6D;
        4'b0011 : r_Hex_Encoding <= 7'h79;
        4'b0100 : r_Hex_Encoding <= 7'h33;          
        4'b0101 : r_Hex_Encoding <= 7'h5B;
        4'b0110 : r_Hex_Encoding <= 7'h5F;
        4'b0111 : r_Hex_Encoding <= 7'h70;
        4'b1000 : r_Hex_Encoding <= 7'h7F;
        4'b1001 : r_Hex_Encoding <= 7'h7B;
        4'b1010 : r_Hex_Encoding <= 7'h77;
        4'b1011 : r_Hex_Encoding <= 7'h1F;
        4'b1100 : r_Hex_Encoding <= 7'h4E;
        4'b1101 : r_Hex_Encoding <= 7'h3D;
        4'b1110 : r_Hex_Encoding <= 7'h4F;
        4'b1111 : r_Hex_Encoding <= 7'h47;
      endcase
    end // always @ (posedge i_Clk)
 
  // r_Hex_Encoding[7] is unused
  assign o_Segment_A = r_Hex_Encoding[6];
  assign o_Segment_B = r_Hex_Encoding[5];
  assign o_Segment_C = r_Hex_Encoding[4];
  assign o_Segment_D = r_Hex_Encoding[3];
  assign o_Segment_E = r_Hex_Encoding[2];
  assign o_Segment_F = r_Hex_Encoding[1];
  assign o_Segment_G = r_Hex_Encoding[0];
 
endmodule // Binary_To_7Segment

Mam taki konwerter KONWERTER FTDI FT232RL USB TTL RS232 Programator

Widzę, że jest złącze JTAG na płytce i po jego pinach nie wydaje mi się, żebym mogła użyć tego konwertera. Chyba, że w innym miejscu można podłączyć ten konwerter. Jeszcze takie posiadam:

Programator ISP USBasp Atmel AVR + Taśma

Moduł konwerter MAX485 TTL UART RS485 Arduino

 

Czy w linii instrukcji case np. 4'b0000 : r_Hex_Encoding <= 7'h7E;  trzeba zamienić 7 na 8? (aby pasowało do tego co jest w kodzie z 7-segmentowym wyświetlaczem innego użytkownika)

 

Link to post
Share on other sites

Ten pierwszy konwerter wygląda najbardziej obiecująco. Na próbę podłączyłbym po prostu kabelkami do goldpinów – na pewno masę i pin Rx, nie wiem jak z zasilaniem (czy jest zasilony z usb, ale pewnie tak). W każdym razie musi podawać 3V3 na pin FPGA.

Nie wiem o jakim kodzie jest mowa. Jeżeli każdy segment wyświetlacza to osobne wyjście modułu, to nic nie musisz zmieniać. Jeżeli zbierzesz je w wektor, to długość powinna się zgadzać. Zawsze możesz też podać na stałe sygnał dezaktywujący ten nieużywany segment i się nim nie przejmować (używać tylko fragmentu wektora bitów, bez tego jednego elementu). Tak by było chyba najprościej – wyprowadzić sygnał r_Hex_Encoding na wyjście modułu Binary_To_7Segment i w nadrzędnej jednostce projektowej wysterować tym sygnałem bity 6:0 wektora led_segment. A na led_segment[7] podać stałą '1' (jeżeli taki sygnał dezaktywuje ten segment).

 

 

Link to post
Share on other sites
// This file converts an input binary number into an output which can get sent
// to a 7-Segment LED.  7-Segment LEDs have the ability to display all decimal
// numbers 0-9 as well as hex digits A, B, C, D, E and F.  The input to this
// module is a 4-bit binary number.  This module will properly drive the
// individual segments of a 7-Segment LED in order to display the digit.
// Hex encoding table can be viewed at:
// http://en.wikipedia.org/wiki/Seven-segment_display
 
module Binary_To_7Segment(
   input       i_Clk,
   input [3:0] i_Binary_Num,
  
   output      o_Segment_7,
  
   output      o_Segment_A,
   output      o_Segment_B,
   output      o_Segment_C,
   output      o_Segment_D,
   output      o_Segment_E,
   output      o_Segment_F,
   output      o_Segment_G,
	
   output [2:0] led_enable
   ); 
  reg [7:0]    r_Hex_Encoding = 8'h00;   
  // Purpose: Creates a case statement for all possible input binary numbers.
  // Drives r_Hex_Encoding appropriately for each input combination.
  
  reg [2:0] led_enable = 3'b000;
  wire SevenSeg1_Inst=1;
  wire SevenSeg2_Inst=0;
  
  always @(posedge i_Clk)
    begin
	
	 if(SevenSeg1_Inst==1 && SevenSeg2_Inst==0)
		assign led_enable[0]=1;
		assign led_enable[1]=0;
      case (i_Binary_Num)
        4'b0000 : r_Hex_Encoding <= ~8'hFC;
        4'b0001 : r_Hex_Encoding <= ~8'h60;
        4'b0010 : r_Hex_Encoding <= ~8'hDA;
        4'b0011 : r_Hex_Encoding <= ~8'hF2;		 
        4'b0100 : r_Hex_Encoding <= ~8'h66;  		  
        4'b0101 : r_Hex_Encoding <= ~8'hB6;
        4'b0110 : r_Hex_Encoding <= ~8'hBE;		  
        4'b0111 : r_Hex_Encoding <= ~8'hE0;
        4'b1000 : r_Hex_Encoding <= ~8'hFE;		  
        4'b1001 : r_Hex_Encoding <= ~8'hF6;
        4'b1010 : r_Hex_Encoding <= ~8'hEE;		  
        4'b1011 : r_Hex_Encoding <= ~8'h3E;
        4'b1100 : r_Hex_Encoding <= ~8'h9C;
        4'b1101 : r_Hex_Encoding <= ~8'h7A;
        4'b1110 : r_Hex_Encoding <= ~8'h9E;
        4'b1111 : r_Hex_Encoding <= ~8'h8E;
      endcase
		assign SevenSeg2_Inst=1;
		assign SevenSeg1_Inst=0;
	 else if(SevenSeg2_Inst==1 && SevenSeg1_Inst==0)
		assign led_enable[0]=0;
		assign led_enable[1]=1;	
      case (i_Binary_Num) 
        4'b0000 : r_Hex_Encoding <= ~8'hFC;
        4'b0001 : r_Hex_Encoding <= ~8'h60;
        4'b0010 : r_Hex_Encoding <= ~8'hDA;
        4'b0011 : r_Hex_Encoding <= ~8'hF2;		 
        4'b0100 : r_Hex_Encoding <= ~8'h66;  		  
        4'b0101 : r_Hex_Encoding <= ~8'hB6;
        4'b0110 : r_Hex_Encoding <= ~8'hBE;		  
        4'b0111 : r_Hex_Encoding <= ~8'hE0;
        4'b1000 : r_Hex_Encoding <= ~8'hFE;		  
        4'b1001 : r_Hex_Encoding <= ~8'hF6;
        4'b1010 : r_Hex_Encoding <= ~8'hEE;		  
        4'b1011 : r_Hex_Encoding <= ~8'h3E;
        4'b1100 : r_Hex_Encoding <= ~8'h9C;
        4'b1101 : r_Hex_Encoding <= ~8'h7A;
        4'b1110 : r_Hex_Encoding <= ~8'h9E;
        4'b1111 : r_Hex_Encoding <= ~8'h8E;
      endcase
		assign SevenSeg2_Inst=0;
		assign SevenSeg1_Inst=1;
		
    end // always @ (posedge i_Clk) 
	 
  assign o_Segment_7 = r_Hex_Encoding[7];
  assign o_Segment_A = r_Hex_Encoding[6];
  assign o_Segment_B = r_Hex_Encoding[5];
  assign o_Segment_C = r_Hex_Encoding[4];
  assign o_Segment_D = r_Hex_Encoding[3];
  assign o_Segment_E = r_Hex_Encoding[2];
  assign o_Segment_F = r_Hex_Encoding[1];
  assign o_Segment_G = r_Hex_Encoding[0]; 
endmodule // Binary_To_7Segment

 

Zastanawiam się jak wskazać na wybór konkretnego modułu. Na razie roboczo zrobiłam zmienne z tych modułów, ale nie wydaje mi się to odpowiednim rozwiązaniem.

Dodatkowo nie wiem dlaczego w "Hierarchy" mam ten plik z "?" w ikonie

2021-09-10-11-06-46.thumb.jpg.77f06b546fc4517911617bf2d813ec20.jpg

Zrobiłam też negację linii wyświetlacza w Binary_To_7Segment.v, bo w kodzie Elvisa widziałam, że to przez funcjonalność płytki elbert, a tutaj widzę, że są negacje w pliku UART_RX_To_7_Seg_Top.v na samym końcu. To oznacza, że niepotrzebnie je wstawiłam do Binary_To_7Segment.v?

module UART_RX_To_7_Seg_Top
  (input i_Clk,     // Main Clock
   input i_UART_RX, // UART RX Data
   // Segment1 is upper digit, Segment2 is lower digit
   output o_Segment1_A,
   output o_Segment1_B,
   output o_Segment1_C,
   output o_Segment1_D,
   output o_Segment1_E,
   output o_Segment1_F,
   output o_Segment1_G,
   output o_Segment2_A,
   output o_Segment2_B,
   output o_Segment2_C,
   output o_Segment2_D,
   output o_Segment2_E,
   output o_Segment2_F,
   output o_Segment2_G);
 
  wire w_RX_DV;
  wire [7:0] w_RX_Byte;
   
  wire w_Segment1_A, w_Segment2_A;
  wire w_Segment1_B, w_Segment2_B;
  wire w_Segment1_C, w_Segment2_C;
  wire w_Segment1_D, w_Segment2_D;
  wire w_Segment1_E, w_Segment2_E;
  wire w_Segment1_F, w_Segment2_F;
  wire w_Segment1_G, w_Segment2_G;
 
  // 12,000,000 / 115,200 = 104
  UART_RX #(.CLKS_PER_BIT(104)) UART_RX_Inst
  (.i_Clock(i_Clk),
   .i_RX_Serial(i_UART_RX),
   .o_RX_DV(w_RX_DV),
   .o_RX_Byte(w_RX_Byte));
    
   
  // Binary to 7-Segment Converter for Upper Digit
  Binary_To_7Segment SevenSeg1_Inst
  (.i_Clk(i_Clk),
   .i_Binary_Num(w_RX_Byte[7:4]),
   .o_Segment_A(w_Segment1_A),
   .o_Segment_B(w_Segment1_B),
   .o_Segment_C(w_Segment1_C),
   .o_Segment_D(w_Segment1_D),
   .o_Segment_E(w_Segment1_E),
   .o_Segment_F(w_Segment1_F),
   .o_Segment_G(w_Segment1_G));
    
  assign o_Segment1_A = ~w_Segment1_A;
  assign o_Segment1_B = ~w_Segment1_B;
  assign o_Segment1_C = ~w_Segment1_C;
  assign o_Segment1_D = ~w_Segment1_D;
  assign o_Segment1_E = ~w_Segment1_E;
  assign o_Segment1_F = ~w_Segment1_F;
  assign o_Segment1_G = ~w_Segment1_G;
   
   
  // Binary to 7-Segment Converter for Lower Digit
  Binary_To_7Segment SevenSeg2_Inst
  (.i_Clk(i_Clk),
   .i_Binary_Num(w_RX_Byte[3:0]),
   .o_Segment_A(w_Segment2_A),
   .o_Segment_B(w_Segment2_B),
   .o_Segment_C(w_Segment2_C),
   .o_Segment_D(w_Segment2_D),
   .o_Segment_E(w_Segment2_E),
   .o_Segment_F(w_Segment2_F),
   .o_Segment_G(w_Segment2_G));
   
  assign o_Segment2_A = ~w_Segment2_A;
  assign o_Segment2_B = ~w_Segment2_B;
  assign o_Segment2_C = ~w_Segment2_C;
  assign o_Segment2_D = ~w_Segment2_D;
  assign o_Segment2_E = ~w_Segment2_E;
  assign o_Segment2_F = ~w_Segment2_F;
  assign o_Segment2_G = ~w_Segment2_G;
endmodule

 

Edytowano przez monsiw
Link to post
Share on other sites
38 minut temu, monsiw napisał:

Zastanawiam się jak wskazać na wybór konkretnego modułu. Na razie roboczo zrobiłam zmienne z tych modułów, ale nie wydaje mi się to odpowiednim rozwiązaniem.

Można to uprościć zbierając pojedyncze sygnały w wektor, wtedy przełączanie odbywałoby się całym wektorem naraz.

 

44 minuty temu, monsiw napisał:

Dodatkowo nie wiem dlaczego w "Hierarchy" mam ten plik z "?" w ikonie

Tak się dzieje, gdy na przykład nie ma w projekcie pliku o danej nazwie. Ale ISE ma dużo bugów i często dzieje się tak bez przyczyny. Sprawdź w pierwszej kolejności, czy nie masz jakiś literówek w nazwach. Następnie spróbuj usunąć ten plik z hierarchii i dodaj go ponownie. Czasem u mnie działało otwarcie pliku projektu (z rozszerzeniem xise) w notatniku i usunięcie fragmentu dotyczącego danego pliku. Następnie mogłem dodać plik normalnie i był już widoczny w ISE. Ale jeżeli w coś takiego chcesz się bawić, to zrób najpierw kopię zapasową.

 

52 minuty temu, monsiw napisał:

Zrobiłam też negację linii wyświetlacza w Binary_To_7Segment.v, bo w kodzie Elvisa widziałam, że to przez funcjonalność płytki elbert, a tutaj widzę, że są negacje w pliku UART_RX_To_7_Seg_Top.v na samym końcu. To oznacza, że niepotrzebnie je wstawiłam do Binary_To_7Segment.v?

Tak. Duża część osób za naturalne uważa logiczną '1' jako poziom aktywny sygnału, tutaj chyba poziomem aktywnym (gdy segment wyświetlacza świeci) jest '0', stąd chyba wynika taki zabieg „stylistyczny".
 


Ogólnie, w tej chwili ten kod jest trochę pomieszany. Zmienna led_enable powinna być jakoś sterowana z modułu najwyżej w hierarchii. Wyjścia jednostki projektowej będącej najwyżej w hierarchii muszą się zgadzać z tymi w pliku z ograniczeniami ucf.

 

Jak dla mnie, to powinno to wyglądać jakoś tak (nie jest to na pewno jedyne słuszne rozwiązanie):

1. Plik najwyższego poziomu:

  • Powinien mieć wejście zegarowe, wejście Rx, wyjścia led_enableled_segment. Wszystkie te wejścia/wyjścia powinny się też znaleźć w pliku ucf.
  • Wewnątrz powinny się znaleźć moduły podrzędne: 1 odbiornik UART, 2 moduły do konwersji połówki bajtu na wektor określający, które segmenty należy zaświecić, żeby wyświetlić daną cyfrę.
  • Wewnątrz trzeba zorganizować multipleksowanie pomiędzy wyjściami tych dwóch modułów dokonujących konwersji i jednoczesne  przełączanie bitów wyjścia led_enable.

2. Plik z modułem Binary_To_7Segment:

  • Wejście zegarowe, wejście wektor 4 bitów, wyjście najlepiej wektorowe.

Nie pisałem nigdy w Verilogu, dlatego przykładowy (pseudo)kod multipleksujący w VHDL:

mux: process(Clk)
variable wybranyBajtMlodszy : boolean := TRUE;
begin
  if rising_edge(Clk) then
  	led_enable <= (others => '1');
  	if wybranyBajtMlodszy then
  		led_enable(0) <= '0';
		led_segment <= led_segment_pierwszy_modul; -- Cały wektor przełączany
	else
		led_enable(1) <= '0';
		led_segment <= led_segment_drugi_modul;
  	end if;
	wybranyBajtMlodszy := not wybranyBajtMlodszy;
  end if;
end process mux;

Oczywiście, to multipleksowanie nie musi być co takt zegara, można go opóźnić (na przykład licznikiem). Założyłem tutaj, że sygnał led_enable jest aktywny poziomem niskim (do weryfikacji).

 

Inne, przykładowe, chyba prostsze rozwiązanie, to użycie jednego modułu Binary_To_7Segment i multiplekser na jego wejściu. Wówczas wyjście led_segment może być sterowane bezpośrednio z tego modułu. Wtedy trzeba utworzyć pomocniczy wektor czterobitowy, podać go na wejście tego modułu, a przełączanie zrealizować przypisując do tego wektora naprzemiennie najstarsze cztery bity z odbiornika UART i najmłodsze. Do obsługi pozostaje też wciąż sygnał led_enable.

 

 

 

Link to post
Share on other sites

Moduł Binary_To_7Segment.v przyjmuje w konstrukcji case odpowiednio górną lub dolną połówkę bajtu. Czyli muszę w tym kodzie multipleksującym w Top Module podać odpowiednie segmenty wyświetlacza w if lub else co następnie z modułu Binary_To_7Segment przejdzie na wyjście płytki. Problem, że nie do końca wiem jak je podać. Tak roboczo wygląda zamysł:

`define true 1
`define false 0
  
//reszta kodu w pliku nadrzędnym bez zmian  

reg wybranyBajtMlodszy = true;

always @(posedge i_Clock)
begin 
  	assign led_enable = 3'b111;	
  	if wybranyBajtMlodszy==1
  		assign led_enable[0] = 0;
		Binary_To_7Segment(o_Segment_7+o_Segment_A+o_Segment_B+o_Segment_C);
	else
		assign led_enable[1] = 0;
		Binary_To_7Segment(o_Segment_D+o_Segment_E+o_Segment_F+o_Segment_G);		
	assign wybranyBajtMlodszy = ~wybranyBajtMlodszy;
end 

Tak wygląda Binary_To_7Segment.v

// This file converts an input binary number into an output which can get sent
// to a 7-Segment LED.  7-Segment LEDs have the ability to display all decimal
// numbers 0-9 as well as hex digits A, B, C, D, E and F.  The input to this
// module is a 4-bit binary number.  This module will properly drive the
// individual segments of a 7-Segment LED in order to display the digit.
// Hex encoding table can be viewed at:
// http://en.wikipedia.org/wiki/Seven-segment_display
 
module Binary_To_7Segment(
   input       i_Clk,
   input [3:0] i_Binary_Num,
	output      o_Segment_7,
   output      o_Segment_A,
   output      o_Segment_B,
   output      o_Segment_C,
   output      o_Segment_D,
   output      o_Segment_E,
   output      o_Segment_F,
   output      o_Segment_G
   ); 
  reg [7:0]    r_Hex_Encoding = 8'h00;   
  // Purpose: Creates a case statement for all possible input binary numbers.
  // Drives r_Hex_Encoding appropriately for each input combination.
  
  always @(posedge i_Clk)
    begin
      case (i_Binary_Num) 
        4'b0000 : r_Hex_Encoding <= 8'hFC;
        4'b0001 : r_Hex_Encoding <= 8'h60;
        4'b0010 : r_Hex_Encoding <= 8'hDA;
        4'b0011 : r_Hex_Encoding <= 8'hF2;		 
        4'b0100 : r_Hex_Encoding <= 8'h66;  		  
        4'b0101 : r_Hex_Encoding <= 8'hB6;
        4'b0110 : r_Hex_Encoding <= 8'hBE;		  
        4'b0111 : r_Hex_Encoding <= 8'hE0;
        4'b1000 : r_Hex_Encoding <= 8'hFE;		  
        4'b1001 : r_Hex_Encoding <= 8'hF6;
        4'b1010 : r_Hex_Encoding <= 8'hEE;		  
        4'b1011 : r_Hex_Encoding <= 8'h3E;
        4'b1100 : r_Hex_Encoding <= 8'h9C;
        4'b1101 : r_Hex_Encoding <= 8'h7A;
        4'b1110 : r_Hex_Encoding <= 8'h9E;
        4'b1111 : r_Hex_Encoding <= 8'h8E;
      endcase		
    end // always @ (posedge i_Clk) 
	 
  assign o_Segment_7 = r_Hex_Encoding[7];
  assign o_Segment_A = r_Hex_Encoding[6];
  assign o_Segment_B = r_Hex_Encoding[5];
  assign o_Segment_C = r_Hex_Encoding[4];
  assign o_Segment_D = r_Hex_Encoding[3];
  assign o_Segment_E = r_Hex_Encoding[2];
  assign o_Segment_F = r_Hex_Encoding[1];
  assign o_Segment_G = r_Hex_Encoding[0]; 
endmodule // Binary_To_7Segment

 

Edytowano przez monsiw
Link to post
Share on other sites

Nie bardzo rozumiem tę koncepcję. Pojedynczy wyświetlacz siedmiosegmentowy ma 7 segmentów + kropkę dziesiętną. Zobacz przykładowy rysunek z Forbota. Segmenty, to te kreski. Oznacza się je na przykład literami od A do G, tak jak na rysunku, czy w kodzie.

Jeżeli chcesz wyświetlić na przykład liczbę 0, to musisz zapalić linie dookoła, czyli segmenty A, B, C, D, E, F (6 segmentów). Żeby wyświetlić liczbę 8 zapalasz wszystkie siedem segmentów, i tak dalej. Stąd biorą się tego typu przypisania (gdy sobie rozpiszesz to na bity, wiedząc, że kolejne bity opisują kolejne segmenty, to powinno być to jasne):

4'b0000 : r_Hex_Encoding <= 8'hFC;


Na pojedynczym wyświetlaczu możesz wyświetlić jedną cyfrę szesnastkową z zakresu 0-F (czyli dziesiętnie 0-15). Taka jedna cyfra szesnastkowa zajmuje cztery bity – od 0000 do 1111.

Z UARTu otrzymujesz cały bajt, czyli osiem bitów, czyli w zapisie szesnastkowym dwie cyfry – od 00 do FF (dziesiętnie od 0 do 255). Ten bajt możesz wyświetlić na dwóch wyświetlaczach.

Nie da się wyświetlić jednej cyfry używając tylko 4 segmentów. Czy rozumiesz już dlaczego coś takiego:

Binary_To_7Segment(o_Segment_7+o_Segment_A+o_Segment_B+o_Segment_C);

nie ma sensu?
W tym miejscu w kodzie powinno znaleźć się przepisanie całego wektora wyjściowego (obejmującego wszystkie segmenty) z określonego modułu Binary_To_7Segment. Poza tym chyba trzeba jakoś tego ifa zamknąć.

 

Na płytce ElbertV2 linie sterujące poszczególnymi segmentami każdego z wyświetlaczy są połączone – masz do dyspozycji tylko ten jeden zestaw + bity led_enable do wyboru właściwego wyświetlacza. Możesz spróbować przenalizować schemat podłączenia wyświetlaczy siedmiosegmentowych na płytce, może to Ci trochę rozjaśni istotę zagadnienia, bo widać, że moje posty nie są wystarczająco zrozumiałe:
image.thumb.png.77f7197f9b45d8b236ac3fdfe1b6ca6e.png

 

Jak wspominałem, alternatywą jest użycie jednego modułu Binary_To_7Segment i sterowanie jego wejściem i_Binary_Num z poziomu modułu nadrzędnego. W tej chwili nie wiem, jaka koncepcja została wybrana.

 

Link to post
Share on other sites

Faktycznie wtedy już nie miałam głowy do tego.

module UART_RX_To_7_Seg_Top
  (input i_Clk,     // Main Clock
   input i_UART_RX, // UART RX Data
   // Segment1 is upper digit, Segment2 is lower digit
   output o_Segment_7,	
   output o_Segment_A,
   output o_Segment_B,
   output o_Segment_C,
   output o_Segment_D,
   output o_Segment_E,
   output o_Segment_F,
   output o_Segment_G,
	
	output reg [2:0] led_enable);
 
   wire w_RX_DV;
   wire [7:0] w_RX_Byte;
   wire w_Segment_7, w_Segment_A, w_Segment_B, w_Segment_C, 
   w_Segment_D, w_Segment_E, w_Segment_F, w_Segment_G;	
	
	wire x,y;
	reg wybranyBajtMlodszy = 1;

  // 12,000,000 / 115,200 = 104
  UART_RX #(.CLKS_PER_BIT(104)) UART_RX_Inst
  (.i_Clock(i_Clk),
   .i_RX_Serial(i_UART_RX),
   .o_RX_DV(w_RX_DV),
   .o_RX_Byte(w_RX_Byte));
	
	
  // Binary to 7-Segment Converter for Lower Digit
  Binary_To_7Segment SevenSeg_Inst
  (.i_Clk(i_Clk),
   .i_Binary_Num(w_RX_Byte[x:y]),
	.o_Segment_7(w_Segment_7),	
	.o_Segment_A(w_Segment_A),
	.o_Segment_B(w_Segment_B),
	.o_Segment_C(w_Segment_C),
	.o_Segment_D(w_Segment_D),
	.o_Segment_E(w_Segment_E),
	.o_Segment_F(w_Segment_F),
	.o_Segment_G(w_Segment_G)); 
	assign o_Segment_7 = ~w_Segment_7;  
	assign o_Segment_A = ~w_Segment_A;
	assign o_Segment_B = ~w_Segment_B;
	assign o_Segment_C = ~w_Segment_C;
	assign o_Segment_D = ~w_Segment_D;
	assign o_Segment_E = ~w_Segment_E;
	assign o_Segment_F = ~w_Segment_F;
	assign o_Segment_G = ~w_Segment_G;

	always @(posedge i_Clk)
	begin 

		led_enable = 3'b111;
		
		if (wybranyBajtMlodszy)
		begin
			led_enable[0] = 0;
			x=3;y=0;
		end  
		
		else
		begin
		  led_enable[1] = 0;
		  x=7;y=4;
		  
		end
		
		wybranyBajtMlodszy = ~wybranyBajtMlodszy;
	end 
	
endmodule

Głównym problemem jest to jak przekazać konkretne bity do modułu. Nie mogę zrobić w powyższy sposób, ale nie wiem jak zachować ten zamysł napisany powyżej

Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.