Jump to content
ELFORUM - Forumul Electronistilor
Sign in to follow this  
RoGeorge

Placă de dezvoltare FPGA, dintr-un panou de imprimantă

Recommended Posts

Cu vreo 5 ani în urmă, o firma foarte mare pentru care lucram, a cumpărat câteva zeci de imprimante multifuncţionale HP Color Laserjet 4730 MFP. Toate bune şi frumoase, doar că după câteva luni, imprimantele începuseră să cadă ca muştele, din cauza unei greşeli de proiectare. Nu le mai mergea panoul de control.

 

Panourile astea erau o adevărată bijuterie "Made in Japan". Aveau pe ele aşa:

- 6 LED-uri (3 verzi + 1 roşu + o combinaţie roşu/verde)

- tastatură numerică cu 20 de butoane fizice

- un LCD alb negru, lat de 20 cm, cu touch screen şi backlight

- difuzor piezo (buzzer)

Imprimantele fiind în garanţie, când se strica un panou, HP-ul trimitea altul nou, iar cel defect era aruncat la gunoi. :41

Când am aflat, am rugat un fost coleg şi prieten, căruia vreau să-i mulţumesc, şi lui şi şefului lui, să-mi dea un panou defect, să nu-l mai arunce, pentru că aveam de gând să recuperez LCD-ul şi touch screen-ul. Mi-a dat două panouri care urmau să ajungă la gunoi. Acum, îmi pare rău că nu le-am luat pe toate.

 

Uitându-mă prin măruntaiele bestiei, am văzut un FPGA, un Xilinx Spartan IIE, cu 50 000 porţi. Tare aş fi vrut să văd ce-i poate pielea, dar la vremea aceea credeam că placa e defectă. În rest, LCD-ul părea greu de folosit, touch screen-ul la fel, nici nu ştiam dacă sunt bune sau arse, aşa că am montat totul la loc şi aproape că am uitat de ele.

 

Mai târziu, s-a aflat că de vină era ansamblarea mecanica, electronica panoului mergea perfect, dar asta e altă poveste.

 

Acum câteva săptămâni, ratza spunea că se fac angajări la firma la care lucrează el. Unul din job-uri cerea cunoştinţe de FPGA.

 

Asta mi-a adus aminte de ceva ce vroiam să fac de multă vreme, şi anume să mă joc cu FPGA-uri. Am început să caut plăci de dezvoltare, dar erau cam scumpe, între 50 euro cipul chior pe o placă de test, până la 500-1000 euro pentru o placă de dezvoltare cu LCD şi touch screen. Atât costă plăcile ieftine, pentru amatori, cele profesionale costă cam căt un automobil...

:bataie

 

Preţurile astea mi-au refresh-at instantaneu memoria, şi m-au făcut să-mi aduc aminte de FPGA-ul văzut pe placa panoului de imprimantă, panou numai bun de placă de dezvoltare.

:da

 

Întotdeauna mi-a plăcut să fac reverse engineering (adică mai pe şleau, să desfac lucrurile ca să vad cum au fost făcute), iar la banii ăştia, sigur merită încercat.

Să vedem ce-o ieşi.

:freaza:

 

Posted Image

 

Posted Image

 

Posted Image

 

Posted Image

 

Posted Image

 

Posted Image

 

Posted Image

 

Posted Image

 

Posted Image

 

Posted Image

 

Posted Image

 

Posted Image

 

Posted Image

 

Posted Image

 

Posted Image

Share this post


Link to post
Share on other sites

Sunt multe firme care arunca asa.Asa era la celalalt loc de munca unde am fost.Dar a fost cam tirziu cind am aflat ce se intimpla cu ele.Mergeu la REMAT.De asemenea este si la noul loc de munca,se strica se arunca.

Share this post


Link to post
Share on other sites

ce mi-ar placea si mie asemena jucarii, nu se ofera nimeni sa le salveze si apoi sa castige un banut modic?

Share this post


Link to post
Share on other sites

ce mi-ar placea si mie asemena jucarii, nu se ofera nimeni sa le salveze si apoi sa castige un banut modic?

Dac ai avea pe cineva pe la remat,ai avea posibilitatea. De la fabrici multe se duc.Acum cinci ani am fost la un remat unde aveam contract pentru reparati si erau aduse aparatura multa de la spital ca exemplu.

Share this post


Link to post
Share on other sites

Într-adevăr, unele lucruri care ajung la REMAT, ar trebui salvate. Citisem de cineva care cumpărase din UK, cu o sumă ridicol de mică, un LASER defect folosit la operaţii medicale. Reparaţia a fost trivială, o sursă defectă, apoi l-a folosit la o mulţime de experimente interesant, în final vroia să-l folosească la un CNC, pentru decupat diferite materiale. Nu mai am link-ul.

 

Revenind la panoul de imprimantă, mai întâi am căutat datasheet-ul de la fiecare integrat de pe placă. Aşa mi-am făcut o idee despre schema bloc. La un inventar mai amănunţit, s-a dovedit că placa are următoarele:

- FPGA Xilinx Spartan IIE, XC2S50E-6, 50 000 porţi, adică 768 slice-uri

- oscilator 50 MHz

- keypad numeric + alte câteva butoane (în total 20 push buttons)

- LCD alb/negru 256*64 (nu sunt sigur de rezoluţie)

- backlight alb pentru LCD

- controller LCD cu EPSON S1D13503

- 64K x 16 SRAM legat la controller-ul LCD-ului

- touch screen rezistiv

- controller (ADC) pentru touch screen, făcut cu ADS7846N

- senzor pentru temperatură şi pentru tensiunea de alimentare (incluse în ADS7846N)

- un canal ADC cu SAR la 125 KHz, rămas liber de la ADS7846N

- buzzer piezo cu volum control (2 biţi, PWM + ON/OFF)

- 4 LED-uri + 1 LED bicolor (roşu + verde)

- câteva conexiuni cu buffer-e legate la un conector, 3 in + 1 out, potrivite pentru o comunicaşie serială, sincronă sau asincronă

- PROM de configurare

- conector JTAG standard, cu 9 pini la 0.1 inch (nu era montat, dar era foarte bine că existau găuri pentru el)

Ca dotări, e numai bună de placă didactică cu FPGA, are pe ea mai mult decât suficient pentru dezvoltat aplicaţii în VHDL, Verilog sau alte HDL.

 

Alimentările deja le ştiam, pentru că pusesem osciloscopul pe conectorul plăcii, la o imprimantă funcţională. Altfel n-aş fi ştiut cu cât să alimentez placa (da, la câteva zile după ce am obţinut plăcile, am luat osciloscopul de acasă, l-am băgat într-o geantă de voiaj, şi am făcut măsurătorile la servici, pe o imprimantă căreia tocmai i se înlocuia un alt panou defect).

 

Am obţinut ce scrie cu albastru pe foaia din poză. Ce scrie cu negru, a fost adăugat zilele astea.

http://i48.tinypic.com/6ofx90.jpg

 

Placa are nevoie de 24V, mai tîrziu aveam să aflu că sunt pentru LCD backlight şi pentru buzzer, şi de 3.3V pentru partea logică. Am încropit la repezeală un alimentator, dintr-o sursă în comutaţie de 24V (recuperată de la o altă imprimantă defectă), urmată de un LM7812 de 12V şi de un LDO LF33 din care se obţin 3.3V. De 12 V nu era nevoie, dar LF33 nu suporta mai mult de 16V pe intrare, nu puteam să-l leg direct la 24V. Aşa că am pus întăi un LM7812, care suportă maxim 36V, urmat de un LF33. Schema alimentatorului nu o mai pun, nici n-am desenat-o. E cea din catalog, cu căteva zeci de microfarazi în paralel cu 100 nF, atât pe intrări cât şi pe ieşiri, la fiecare stabilizator. Montajul e făcut în aer, nu arată prea frumos, dar eram nerăbdător să pornesc jucăria.

 

Posted Image

 

Pornind de la datasheet-ul FPGA-ului, am văzut că pinii pentru JTAG sunt dedicaţi, nu pot fi mapaţi altundeva. Urmărind traseele pe cablaj, am descoperit că pinii JTAG merg la găurile corespunzătoare conectorului CN4. Spre norocul meu, deşi toată placa era în tehnologie SMD şi conţinea tot felul de conectori greu de găsit, pentru CN4 fuseseră prevăzute găuri pentru pini standard, de 1x9 la 0.1 inch. Acum aveam şi o cale de a vorbi cu FPGA-ul, pentru a-l reprograma.

 

Odată aflat conectorul JTAG, îmi mai trebuia un programator JTAG pentru FPGA-uri Xilinx Spartan IIE, plus softul de rigoare.

 

Căutând despre JTAG, am găsit un link care explică foarte clar ce este, cum a apărut şi ce se poate face cu JTAG-ul.

http://www.fpga4fun.com/JTAG.html

Recomand tot site-ul fpga4fun, este foarte clar şi concis.

 

După ce am priceput mai bine ca niciodată ce este JTAG-ul, am căutat programator. Scumpe rău şi astea. De obicei sunt incluse în placa de dezvoltare pe care o cumperi. Există şi programatoare separate, dar la preturi de 50-100 euro. Prea mult. Am decis să-mi fac singur. Iarăşi, din fericire, chiar producătorul cipului, Xilinx, dădea o schemă foarte simplă de programator pentru portul paralel:

http://www.xilinx.com/support/documenta ... xtp029.pdf

Calculatorul meu are port paralel, iar restul pieselor le aveam deja prin casă. Faţă de schema originală, am adăugat un LED conectat la o poartă liberă, ca să văd când lucrează programatorul. Mai târziu aveam să constat că am făcut foarte bine, LED-ul e deosebit de util.

 

După ceva meştereală, am obţinut asta:

 

Posted Image

 

Buuun, avem o placă alimentată şi legată la calculator.

:dans:

Mai trebuie doar să vorbim cu ea.

 

Xilinx oferă o variantă gratuită de software pentru configurat şi programat FPGA-urile lor, numită ISE WebPack. E un pachet foarte mare, e nevoie de cîţiva GB liberi pentru download-at şi alţi câţiva pentru instalat. Făcut curat prin calculator, download-at versiunea cea mai nouă, instalat şi... surpriză! Spartan IIE, FPGA-ul de pe placa mea, nu era suportat.

icon_hang

Căutat problema pe net, ultima variantă care accepta Spartan IIE era ISE 10.1. Făcut iar loc pe hard, download-at alţi GB, instalat alţi GB. Toate astea au durat o zi.

:nebunrau:

În final, am reuşit să vorbesc cu FPGA-ul şi să-i citesc ID-ul.

 

Victorie totală!

:hamac

Share this post


Link to post
Share on other sites

Am recitit. Cred că am scris poveşti prea lungi. Din cauza asta, conţinutul devine neclar. De exemplu, când am spus că "Toate astea au durat o zi.", mă refeream doar la download şi la instalare de programe, nu la restul. Trebuia să fi scris mai multe mesaje scurte, pe zile, nu tot deodată. Voi încerca să fiu mai concis.

 

Următorul pas a fost scoaterea schemei, pe bucăţi şi nu foarte detaliat, doar atât cât să aflu cum, şi la ce pini, e legat fiecare periferic. A durat destul de mult, încă n-am terminat. Ca să nu mai piardă timpul şi alţii, schemele:

 

Schema 1

Posted Image

 

Schema 2

Posted Image

 

Schema 3

Posted Image

 

Schema 4

Posted Image

 

Din schemele de mai sus, am scris fişierul UCF (User Constraint File). UCF-ul este un fişier în care se specifică, printre altele, legăturile dintre pinii FPGA-ului şi schema noastră. De exemplu, în UCF se scrie cum vrem să botezăm fiecare pin al FPGA-ului şi unde e legat, dacă pinii au rezistenţe de pull-up sau pull-down, etc. Acest fişier este folosit de programele cu care se configurează FPGA-ul (ISE).

 

Mai jos, fişierul UCF pentru PWB51652J-V0 (aşa se cheamă placa din poze).

#========================================================#	Pin assignement for XILINX Spartan 2E - XC2S50E#	Printer display board PWB51652J-V0 (Mihai)##	Author: RoGeorge#	Created: 2012.07.08#	Version: 2012.07.21 V0.7#========================================================#========================================================#	TSC - Touch Screen Controller - ADS7846#========================================================NET	"TSC_DCLK"		LOC = "P77";NET	"NTSC_CS"		LOC = "P78";NET	"TSC_DIN"		LOC = "P79";NET	"TSC_BUSY"		LOC = "P82";NET	"TSC_DOUT"		LOC = "P44";NET	"NTSC_PENIRQ"	LOC = "P80";#========================================================#	CN1 Connector - 10 pins connector to outside world#========================================================NET "CN1_MOSI"	LOC = "P85";	# CN1 pin 4NET "CN1_SCLK"	LOC = "P83";	# CN1 pin 6NET "NCN1_SS"	LOC = "P86";	# CN1 pin 8NET "CN1_MISO"	LOC = "P84";	# CN1 pin 2#========================================================#	SPK - Speaker - Buzzer BZ1#========================================================NET "NSPK_VOL" LOC = "P48";		# PWM '0' -> (BZ+ = 24V)								#	  '1' -> (BZ+ =  0V)NET "SPK_BIT"	LOC = "P47";	#     '0' -> (BZ- = BZ+)								#     '1' -> (BZ- =  0V)#========================================================#	KBD - Keyboard buttons - 8 x 3 matrix#========================================================#	1	2	3	4	5	6	7	8	- A0_#	9	*	0	#	C	y	p	bl	- A1_#	NC	NC	NC	NC	gr	gl	gu	br	- A2_#	|	|	|	|	|	|	|	|#   D0_	D1_	D2_	D3_	D4_	D5_	D6_	D7_##	y = Yellow button#	p = Pink button#	bl = Blue Left button#	gr = Grey Right button#	gl = Grey Left button#	gu = Grey Up button#	br = Blue Right button#========================================================#	8 input columns with pullup resistorsNET "NKBD_D<7>"	LOC = "P27";	# Data input D7 keyboardNET "NKBD_D<6>"	LOC = "P28";	# Data input D6 keyboardNET "NKBD_D<5>"	LOC = "P29";	# Data input D5 keyboardNET "NKBD_D<4>"	LOC = "P30";	# Data input D4 keyboardNET "NKBD_D<3>"	LOC = "P31";	# Data input D3 keyboardNET "NKBD_D<2>"	LOC = "P32";	# Data input D2 keyboardNET "NKBD_D<1>"	LOC = "P38";	# Data input D1 keyboardNET "NKBD_D<0>"	LOC = "P39";	# Data input D0 keyboardNET "NKBD_D<?>" PULLUP;			# Pullup resistors#	3 output rows with pullup resistorsNET "NKBD_A<2>"	LOC = "P23";	# Address A2 keyboardNET "NKBD_A<1>"	LOC = "P26";	# Address A1 keyboardNET "NKBD_A<0>"	LOC = "P24";	# Address A0 keyboardNET "NKBD_A<?>" PULLUP;			# Pullup resistors#========================================================#	LED - 6 discrete LEDs, common anod#========================================================NET "NLED<6>"	LOC = "P18";NET "NLED<5>"	LOC = "P21";NET "NLED<4>"	LOC = "P22";NET "NLED<3>"	LOC = "P15";NET "NLED<2>"	LOC = "P20";NET "NLED<1>"	LOC = "P14";#========================================================#	LCD controller - EPSON S1D13503F01#========================================================#	controll busNET "LCD_BKL"		LOC = "P13";	#LCD backlightNET "LCD_READY"		LOC = "P12";NET "LCD_OSC1"		LOC = "P67";NET "LCD_RESET"		LOC = "P11";NET "NLCD_MEMR"		LOC = "P10";NET "NLCD_MEMW"		LOC = "P8";NET "NLCD_MEMCS"	LOC = "P7";NET "NLCD_IOR"		LOC = "P6";NET "NLCD_IOW"		LOC = "P5";NET "NLCD_IOCS"		LOC = "P4";NET "NLCD_BHE"		LOC = "P3";#	data bus - 16 bitsNET "LCD_DB7"	LOC = "P132";NET "LCD_DB6"	LOC = "P131";NET "LCD_DB5"	LOC = "P125";NET "LCD_DB4"	LOC = "P124";NET "LCD_DB3"	LOC = "P123";NET "LCD_DB2"	LOC = "P122";NET "LCD_DB1"	LOC = "P121";NET "LCD_DB0"	LOC = "P118";NET "LCD_DB8"	LOC = "P133";NET "LCD_DB9"	LOC = "P134";NET "LCD_DB10"	LOC = "P137";NET "LCD_DB11"	LOC = "P138";NET "LCD_DB12"	LOC = "P139";NET "LCD_DB13"	LOC = "P140";NET "LCD_DB14"	LOC = "P141";NET "LCD_DB15"	LOC = "P142";#	address bus - 20 bitsNET "LCD_AB0"	LOC = "P87";NET "LCD_AB1"	LOC = "P89";NET "LCD_AB2"	LOC = "P92";NET "LCD_AB3"	LOC = "P93";NET "LCD_AB4"	LOC = "P94";NET "LCD_AB5"	LOC = "P95";NET "LCD_AB6"	LOC = "P96";NET "LCD_AB7"	LOC = "P97";NET "LCD_AB8"	LOC = "P98";NET "LCD_AB9"	LOC = "P100";NET "LCD_AB10"	LOC = "P101";NET "LCD_AB11"	LOC = "P102";NET "LCD_AB12"	LOC = "P103";NET "LCD_AB13"	LOC = "P104";NET "LCD_AB14"	LOC = "P69";NET "LCD_AB15"	LOC = "P106";NET "LCD_AB16"	LOC = "P114";NET "LCD_AB17"	LOC = "P115";NET "LCD_AB18"	LOC = "P116";NET "LCD_AB19"	LOC = "P117";#========================================================#	CN3 connector - outputs buffered w open drain#========================================================#	Obs.: +3.3V must be supplied by CN3 pin3 or CN1 pin7NET	"NCN3_PIN1"	LOC = "P50";NET "NCN3_PIN5"	LOC = "P43";#========================================================#	Configuration PROM#========================================================NET	"DONE"	LOC = "P71";#========================================================#	Clock and Reset#========================================================NET "CLK"	LOC = "P129";# NET "RESET"	LOC = "Pyy";#========================================================#	Timing constraint of 50 MHz onboard oscillator#	name of the clock signal is CLK#========================================================NET "CLK"	TNM_NET = "CLK";TIMESPEC "TS_CLK" = PERIOD "CLK" 20 ns HIGH 50 %;
Acum, avem tot ce trebuie ca să începem să programăm. În exemplele care vor urma, voi folosi limbajul VHDL.

 

Pentru cei care nu ştiu, dacă ne uităm din avion, un FPGA este un circuit integrat în care poţi băga ce schemă vrei tu. Schema se poate desena sau se poate scrie într-un limbaj de programare care să descrie schema. Un exemplu de astfel de limbaj este VHDL. Vorbim aici de scheme cu circuite digitale (numerice), adică scheme cu porţi logice, numărătoare şi alte de-astea, nu scheme analogice, cum ar fi cele cu amplificatoare operaţionale. Pe placa din poze, FPGA-ul este integratul ăla mare şi gras, şi cu 144 de picioare, pe care scrie Xilinx Spartan. Xilinx e firma care-l produce. Spartan este numele comercial pe care Xilinx l-a dat unei game de FPGA-uri produse de ei.

 

Acronime:

FPGA - Field Programmable Gate Array

UCF - User Constraint File

ISE - Integrated Synthesis Environment

HDL - Hardware Description Language

VHSIC - Very High Speed Integrated Circuit

VHDL - VHSIC HDL

 

 

Disclaimer: sunt începător în domeniul FPGA şi HDL, prin urmare, s-ar putea ca uneori să scriu prostii.

Dacă observaţi greşeli de orice fel, chiar şi de gramatică, vă rog să-mi atrageţi atenţia :bataie , ca să le pot îndrepta.

Share this post


Link to post
Share on other sites

Adaug o completare la lista acronimelor :VHDL (VHSIC HDL) - Very High Speed Integrated Circuit Hardware Description Language.L.E. Acum am vazut ca ai scris ce inseamna HDL. Trebuiau concatenate cele doua acronime.

Share this post


Link to post
Share on other sites

Mulţumesc pentru completare.

 

Să continuăm. Avem alimentare, avem programator, avem softul instalat, ştim dotările (perifericele) plăcii şi la ce pini ai FPGA-ului sunt legate. E momentul să începem să băgăm diferite scheme în FPGA, ca să comandăm perifericele.

 

Ce schemă să facem? Să clipească un LED? Nu, e prea complicat. Altceva mai simplu. Citim tastatura plăcii, iar pentru fiecare buton apăsat (de la 1 la 6), aprindem unul din cele 5 LED-uri. Cu alte cuvinte, legăm câte un led la fiecare buton, ca în schema din Fig. 1:

 

Fig. 1

Posted Image

 

 

 

Dacă ne uităm la poza cu Schema 1 (cea publicată în mesajele anterioare, nu o confundaţi cu Fig. 1 de acum), observăm că LED-urile şi butoanele sunt deja legate la FPGA, ca în Fig. 2.

 

Fig. 2

Posted Image

 

 

 

Dar noi vrem să facem un montaj ca în Fig. 1, ce ne facem?

Foarte simplu :95 , tăiem traseele de cablaj, apoi lipim nişte fire de la butoane, direct la LED-uri. :85

:bataie

 

Nu, nu vom face asta, glumeam doar.

Vom face legăturile între LED-uri şi butoane în interiorul FPGA-ul, programându-l să se "transforme" ca în schema din Fig. 3.

 

Fig. 3

Posted Image

 

 

 

Pentru a obţine schema din Fig. 3, programul scris în limbajul VHDL, arată astfel:

 

Prog. 1

library IEEE;use IEEE.STD_LOGIC_1164.ALL;entity KBD_to_LED isport(	NKBD_A: out std_logic_vector(2 downto 0);	NKBD_D: in std_logic_vector(7 downto 0);		NLED:	out std_logic_vector(1 to 6));end KBD_to_LED;architecture Behavioral of KBD_to_LED isbegin	NKBD_A(0) <= '0';	NKBD_A(2 downto 1) <= "11";		NLED(1) <= NKBD_D(0);	NLED(2) <= NKBD_D(1);	NLED(3) <= NKBD_D(2);	NLED(4) <= NKBD_D(3);	NLED(5) <= NKBD_D(4);	NLED(6) <= NKBD_D(5);end Behavioral;
Să explicăm puţin programul de mai sus.

Mai întâi despre notaţiile care apar. KBD vine de la KeyBoarD (tastatură) iar NKBD este semnalul negat. Aşa am botezat noi mai devreme, în fişierul USL, firele legate la butoane. D vine de la Data, pe unde se citeşte matricea de butoane, iar A vine de la Address, pe unde se adresează diferite rânduri din matricea de butoane. De exemplu, pinul P24 al FPGA-ului, a fost botezat (în fişierul USL) cu numele de NKBD_A(0), Not KeyBoarD Address line 0, iar pinul P31, corespunzător butonului "4" din scheme, a fost botezat în USL cu NKBD_D(3), Not KeyBoarD Data line 3.

 

Acum, să revenim la cod. Observăm că mai întâi se declară felul semnalelor, în instrucţiunea "port". În cazul nostru, semnale (adică pinii FPGA-ului) pot fi de tip intrare sau ieşire, şi sunt vectori, adică sunt mai multe semnale grupate. "NKBD_D: in std_logic_vector(7 downto 0);" înseamnă că avem grupate 8 semnale de tip intrare, de la NKBD_D(7) până la NKBD_D(0), fiecare din cele 8 corespund unui pin al FPGA-ului. Cărui pin corespunde fiecare semnal, scrie în fişierul USL.

 

Aproape orice pin al FPGA-ului poate fi intrare sau ieşire, după cum vrem noi. Trebuie doar să-l configurăm. După ce am declarat de ce fel sunt pinii (adică i-am configurat ca intrări sau ieşiri), începem să îi folosim, în secţiunea "architecture".

 

Să luăm prima instrucţiune, "NKBD_A(0) <= '0';". Asta înseamnă că lui NKBD_A(0) îi atribuim valoare "0" logic. "0" logic înseamnă GND, adică pus la masă. "1" logic înseamnă Vcc, în cazul nostru +3.3V. Cu alte cuvinte, "NKBD_A(0) <= '0';" înseamnă că legăm la masă pinul P24 al FPGA-ului.

 

Acum vă voi spune secretele VHDL-ului.

- Toate instrucţiunile se execută simultan (în paralel, deodată) nu ca la alte limbaje de programare, cum ar fi de exemplu Basic sau C, unde instrucţiunile se execută pe rând, secvenţial. Cu alte cuvinte, în programul de mai sus putem schimba ordinea instrucţiunilor cum vrem noi, pentru că oricum se execută toate deodată. Există şi instrucţiuni care se execută secvenţial, le vom întâlni pe parcurs, dar nu în programul de mai sus.

- Semnul "<=" nu înseamnă "mai mic sau egal", ca în alte limbaje. Înseamnă atribuire. Ca să înţelegem mai bine semnul de atribuire din VHDL şi faptul că toate se execută simultan, decât să-l citim ca "egal", mai bine e să-l citim şi să ne gândim la el ca "este legat la". Deci, "NLED(1) <= NKBD_D(0);" înseamnă că LED 1 este legat la keyboard data 0.

- Dacă totuşi întâlnim semnul de atribuire "<=" într-un bloc de instrucţiuni secvenţiale, "<=" tot nu se execută secvenţial. Toate atribuirile de tip "<=" se execută numai la ieşirea din blocul secvenţial, simultan şi în ordinea apariţiei. De exemplu, dacă într-un bloc secvenţial avem "NLED(1) <= '0';" şi apoi "NLED(1) <= '1';", semnalul nu va lua niciodată valoarea '0', va lua doar valoarea '1'.

Gata, acum ştiţi esenţa VHDL-ului.

 

După ce programăm FPGA-ul cu codul din Prog. 1, vom obţine următorul rezultat:

 

Movie 1 - urmăriţi ce se întâmplă cu LED-urile plăcii atunci când apăsăm pe butoane.

Ue_GTZCvyUA

 

Tocmai am făcut primul nostru program în VHDL. Putem spune oficial că ştim să programăm un FPGA! :dans:

 

 

 

 

Acum, că şim VHDL :rade: , să facem o schemă puţin mai complicată, în care LED-ul 1 să se aprindă numai atunci când apăsăm simultan pe butoanele "1" şi "2", ca în Fig. 4.

 

Fig. 4

Posted Image

 

 

 

Codul VHDL corespunzător montajului din Fig. 4 este:

 

Prog. 2

library IEEE;use IEEE.STD_LOGIC_1164.ALL;entity KBD_to_LED isport(	NKBD_A: out std_logic_vector(2 downto 0);	NKBD_D: in std_logic_vector(7 downto 0);		NLED:	out std_logic_vector(1 to 6));end KBD_to_LED;architecture Behavioral of KBD_to_LED isbegin	NKBD_A(0) <= '0';	NKBD_A(2 downto 1) <= "11";		NLED(1) <= NKBD_D(0) or NKBD_D(1);	NLED(2) <= '0';	NLED(3) <= NKBD_D(2);	NLED(4) <= NKBD_D(3);	NLED(5) <= NKBD_D(4);	NLED(6) <= NKBD_D(5);end Behavioral;

 

Rezultatul se vede în Movie 2.

 

Movie 2 - urmăriţi ce se întâmplă cu primul LED din stânga plăcii, se aprinde numai dacă apăsăm butoanele "1" şi "2" simultan, altfel rămâne stins.

qoLr3yUV-Yw

 

 

 

Încă o încercare şi gata. Până acum, ca să stea LED-ul aprins, trebuia să ţinem butonul apăsat. Acum, vrem să facem ceva care să ţină minte pe care din butoanele "1" sau "2" am apăsat ultima dată, adică un circuit care să memoreze ultimul buton apăsat (vorbim doar de butoanele "1" şi "2", restul rămân ca până acum). Pentru asta, vom face un montaj cu un bistabil, ca în Fig. 5.

 

Fig. 5

Posted Image

 

 

 

Programul pentru schema din Fig. 5, este:

 

library IEEE;use IEEE.STD_LOGIC_1164.ALL;entity KBD_to_LED isport(	NKBD_A: out std_logic_vector(2 downto 0);	NKBD_D: in std_logic_vector(7 downto 0);		NLED:	out std_logic_vector(1 to 6));end KBD_to_LED;architecture Behavioral of KBD_to_LED is	signal q: std_logic;	signal nq: std_logic;	begin	NKBD_A(0) <= '0';	NKBD_A(2 downto 1) <= "11";		q <= NKBD_D(0) nand nq;	nq <= NKBD_D(1) nand q;	NLED(1) <= q;	NLED(2) <= nq;	NLED(3) <= NKBD_D(2);	NLED(4) <= NKBD_D(3);	NLED(5) <= NKBD_D(4);	NLED(6) <= NKBD_D(5);end Behavioral;
Observăm că aici apar două semnale interne FPGA-ului, numite "q" şi "nq", şi declarate la început, în arhitectura blocului KBD_to_LED.

 

Comportamentul schemei din Fig. 5, adică rezultatul programului Prog. 3, îl vedem în ultimul film:

 

Movie 3 - urmăriţi ce se întâmplă cu primele două LED-uri din stânga plăcii.

QFPFcVpuKPQ

 

Acronime:

LED - Light Emitting Diode

Share this post


Link to post
Share on other sites

Blinking LED

 

Toată lumea face, la început, şi un program cu un LED care clipeşte. LED-ul care se aprinde, sau uneori clipeşte, este similarul clasicului "Hello, world!", din programarea calculatoarelor. "Hello, world!" a fost iniţial (1974) un exemplu foarte simplu de program scris în C. Apoi, expresia s-a răspândit în lumea IT-ului, iar acum are sensul de program foarte simplu, de test, sau iniţiatic. Multe tutoriale în diverse domenii îşi încep prezentarea cu un program "Hello, world!".

 

Cum să facem un LED să clipească? Dacă scriem pe rând

LED <= '0';LED <= '1';LED <= '0';LED <= '1';...
o merge?

:aut:

 

Nu merge, pentru că în VHDL toate instrucţiunile se execută simultan, nu pe rând. Atunci, cum facem?

 

Luăm semnalul de ceas de la oscilatorul de 50 MHz, îl divizăm cu 50 000 000, şi obţinem un semnal de 1 Hz. La semnalul de 1 Hz, legăm un LED care, evident, va clipi o dată pe secundă.

:dans:

 

Să detaliem puţin schema. Divizorul cu 50 milioane îl facem cu ajutorul unui numărător. La fiecare semnal de ceas (CLK) venit de la oscilator, se incrementează numărătorul. Când ajungem la 50 milioane, resetăm numărătorul. Astfel obţinem un divizor cu 50 milioane. Pentru că la intrarea divizorului băgăm o frecvenţă de 50 MHz (atăt scoate oscilatorul plăcii), la ieşirea din divizor vom avea 1 Hz.

 

Deoarece vrem ca semnalul de 1 Hz să aibă factorul de umplere 50%, vom face un mic artificiu. Mai întâi punem un divizor cu 25 milioane (l-am numit prescaler) din care vom obţine 2 Hz, dar cu factor de umplere diferit de 50%, urmat de un bistabil care va diviza şi el cu 2. În final obţinem 1 Hz, cu PWM 50%.

 

În afara LED-ului care bate secunda, mai adăugam căteva facilităţi:

- numărăm şi afişăm pe LED-urile 1 şi 2, în binar, secundele (facem asta doar pe 2 biţi, adică numărăm doar 4 secunde, apoi se reia). Pentru asta, vom face un contor legat la semnalul de 2 Hz, contor numit cnt_led

- din butonul 1 punem pauză, iar din butonul 2 continuăm. Pentru asta, facem un FF ca în exemplul din Fig. 5, iar ieşirea lui o folosim ca semnal de validare (enable - en) a numărării. Dacă en = 0, e pauză, prescaler-ul nu mai numără, dacă en = 1, continuă numărarea.

- din butonul 3 aducem la zero (dăm reset - rst), atât la prescaler (cnt_div) cât şi la numărătorul de secunde (cnt_led).

- semnalele reset, 1 Hz, enable/disable şi numărul secundei, le afişăm pe LED-uri

Schema va arăta ca în Fig. 6. Împreună cu FPGA-ul, am desenat şi panoul plăcii, ca să se vadă unde este fiecare LED.

 

Fig. 6

Posted Image

 

 

 

Schema din Fig. 6 e descrisă în VHDL ca în Prog. 6

 

Prog. 6

------------------------------------------------------------------------------------ Company: -- Engineer: -- -- Create Date:    10:10:59 07/26/2012 -- Design Name: -- Module Name:    blinking_LED - Behavioral -- Project Name: -- Target Devices: -- Tool versions: ---- Description: --			 button 1 enable counting--			 button 2 disable counting--			 button 3 reset----			 LED 1 & 2 - binary count upto 4 seconds--			 LED 3 - reset indicator--			 LED 4 - blinking with 1 Hz--			 LED 5 & 6 - indicate enable(green)/disable(red) counting---- Dependencies: ---- Revision: -- Revision 0.01 - File Created-- Additional Comments: ------------------------------------------------------------------------------------library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_ARITH.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;entity blinking_LED isport(	NKBD_A: out std_logic_vector(2 downto 0);	NKBD_D: in std_logic_vector(7 downto 0);		NLED: out std_logic_vector(6 downto 1);		CLK: in std_logic);end blinking_LED;architecture Behavioral of blinking_LED is	-- prescaler divider constant = 24 999 999	constant divider: std_logic_vector(24 downto 0) := "1011111010111100000111111";		-- other signals	signal q, nq, en, rst: std_logic;		signal cnt_div: std_logic_vector(24 downto 0);	-- prescaler 25 000 000	signal cnt_led: std_logic_vector(2 downto 0);	-- for 50% PWM of 1 Hz	begin	q <= NKBD_D(0) nand nq;	-- FF gate 1	nq <= NKBD_D(1) nand q;	-- FF gate 2		en <= nq; -- for program clarity	rst <= not NKBD_D(2); -- for program clarity, gate 4	NKBD_A <= "110";	NLED(4) <= not cnt_led(0);	-- display 1 Hz beat, gate 3	NLED(1) <= not cnt_led(1);	-- display seconds binary counter bit 1, gate 6	NLED(2) <= not cnt_led(2);	-- display seconds binary counter bit 2, gate 5		NLED(3) <= NKBD_D(2);	-- display reset button status		NLED(5) <= nq;	-- display red when counting is disabled	NLED(6) <= q;	-- display green when counting is enabled		-- prescaler counter with asynchronous reset and synchronous enable	prescaler: process(CLK, rst, en)	begin		if rst = '1' then			cnt_div <= (others => '0');		elsif CLK'event and CLK = '1' and en = '1' then			if cnt_div = divider then				cnt_div <= (others => '0');			else				cnt_div <= cnt_div + '1';			end if;		end if;	end process;		-- seconds counter with asynchronous reset	counter: process(cnt_div(24), rst)	begin		if rst = '1' then			cnt_led <= (others => '0');		elsif cnt_div(24)'event and cnt_div(24) = '1' then			cnt_led <= cnt_led + '1';		end if;	end process;end Behavioral;
Observăm că au apărut două blocuri numite process. Instrucţiunile din interiorul unui process, se execută secvenţial, nu paralel. Imediat după cuvântul process, apare o listă de semnale între paranteze. Acea listă se numeşte sensitivity list.

 

Un process se execută numai atunci cănd apare un event la semnalele din sensitivity list. Un event înseamnă o schimbare de stare. De exemplu, un front crescător al unui semnal este un event.

 

Instrucţiunile de tip "if ... end if;" se execută secvenţial. Ele nu pot fi folosite în afara blocului process.

 

Expresia "if CLK'event and CLK = '1'" înseamnă: "dacă apare o schimbare la semnalul CLK şi valoarea actuală este 1 logic" atunci... Cu alte cuvinte, expresia detectează frontul crescător al semnalului CLK. Ea poate fi citită "pe frontul crescător al lui CLK" execută...

 

Orice urmează după "--" este comentariu, nu contează la execuţia programului.

 

Expresia "(others => '0')" umple restul biţilor cu zero (nu trebuie să mai specificăm noi câţi biţi de zero să pună). De exemplu,

cnt_div <= (others => '0');
este echivalent cu

cnt_div <= "0000000000000000000000000";

 

 

Rezultatul programului se vede în filmul Movie 6. Mai întâi se aprinde LED-ul verde al programatorului de sub placă. După ce se termină programarea FPGA-ului (2-3 secunde), LED-urile 5-6 (dreapta jos) se aprind verde, LED-ul 4 (dreapta sus) începe să bată secunda, iar LED-urile 1 şi 2 (din stânga LCD-ului), numără până la 4, în binar. Dacă se apasă butonul 1, se pune pauză, iar LED-urile 5-6 se aprin roşu. Dacă se apasă butonul 2, LED-urile 5-6 se aprind iarăşi verde, iar numărătoarea continuă. Oricând se apasă butonul 3, toate numărătoarele sunt aduse la 0.

 

Movie 6

Eck3qoH_1hE

 

 

 

Acronime:

IT - Information Technology

CLK - Clock

FF - Flip Flop

PWM - Pulse Width Modulation

LCD - Liquid Crystal Display

Share this post


Link to post
Share on other sites

Felicitari , este exceptional crash cursul asta in FPGA.Ti-a mai spus cineva ca ai talent de profesor foarte bun ?

Share this post


Link to post
Share on other sites

RoGeorge. Verilog stii?incearca sa vezi daca reusesti sa afli cum poti sa folosesti cumva touch-ul sau display-ul...poti face chestii frumusele daca le dai de capatoricum..bune aplicatiile pentru cei incepatori...mai ales ca le-ai pus si schemele si codul explicat si tot.well done :da

Share this post


Link to post
Share on other sites

Mulţumesc pentru aprecieri.La început am tot oscilat între diferite moduri de exprimare. Până la urmă, am ales modul autoritativ, dar nu sunt nici profesor, nici expert în domeniu. Sunt începător în ale FPGA-urilor.Deocamdată, voi rămâne la VHDL.Sper să reuşesc să vorbesc şi cu restul perifericelor. :da

Share this post


Link to post
Share on other sites

Musical Notes with PWM Volume Control

(Note muzicale cu controlul volumului prin PWM)

 

Ajunge cu atâtea LED-uri şi butoane.

 

De data asta vom încerca să folosim un alt periferic de pe placă, şi anume difuzorul piezoelectric.

 

Vom face o "miniclapă" electronică, adică un generator de ton care scoate note muzicale în funcţie de butoanele apăsate.

Volumul sunetelor va putea fi controlat din butonul mare, prin PWM.

 

Mai întâi, ce este PWM-ul?

PWM vine de la "Pulse Width Modulation", în traducere , impulsuri de frecvenţă constantă, dar cu lăţime variabilă (lăţime ~ factor de umplere).

 

Aşa arată un PWM cu 1%, 50% respectiv 99% factor de umplere:

 

Fig. 7

Posted Image

 

Observăm că PWM = 0% echivalează cu 0V, iar PWM = 100% cu +V. Mai mult, se poate demonstra că tensiunea medie a unui semnal PWM este proporţională cu factorul de umplere.

De exemplu, dacă avem un semnal care are:

"0" logic = 0 V şi

"1" logic = 3.3 V,atunci un PWM cu factor de umplere 25% (PWM 25%) va avea tensiunea medie de 0.825 V (0.825 V = 3.3 V * 25 /100).

 

PWM volume control

Să ne uităm la Schema 1 din mesajele anterioare.

Acolo găsim schema amplificatorului pentru difuzor (unde scrie 10.07.2012). Pinul P47, botezat SPK_BIT în fişierul UCF, comută difuzorul între 0 şi +V. Acest pin poate fi folosit pentru a genera diferite sunete. Un alt pin, P48, numit NSPK_VOL (cu ajutorul fişierului UCF), este urmat de un integrator. Din acest motiv, NSPK_VOL poate regla (prin PWM) tensiunea +V aplicată difuzorului, adică volumului sunetului. În funcţie de semnalul PWM de pe pinul NSPK_VOL, +V poate lua valori între 0 şi +24 V.

 

Cum generăm un semnal cu factor de umplere variabil (PWM)?

Există mai multe metode, vom folosi o metodă des întâlnită în microcontroller-e.

Facem un numărător care numără în mod continuu, şi un comparator digital. Comparatorul compară valoarea la care a ajuns numărătorul, cu o mărime dată, mărime care este exact factorul de umplere dorit. Când numărătorul trece prin 0, se setează un FF. Când numărătorul atinge valoarea PWM dată, se resetează FF.

 

Schema bloc ar arăta cam aşa:

 

Fig. 8

Posted Image

 

iar implementarea schemei în VHDL:

 

Prog. 7

--*******************************************-- PWM volume control--*******************************************	-- PWM counter and keyboard address shift register	process(CLK)	begin		if CLK'event and CLK = '1' then			-- keyboard			case nkbd_en is				when "110" =>					nkbd_en <= nkbd_en_next;					en <= not (NKBD_D(0) and NKBD_D(1) and NKBD_D(2) and NKBD_D(3) and 									NKBD_D(4) and NKBD_D(5) and NKBD_D(6) and NKBD_D(7));				when "101" | "011" =>					nkbd_en <= nkbd_en_next;				when others => nkbd_en <= "110";			end case;									if pwm_cnt(17 downto 10) = pwm_reg then				pwm <= '0';			elsif pwm_cnt = b"00_0000_0000_0000_0000" then				pwm <= '1';			end if;						pwm_cnt <= pwm_cnt + 1;		end if;	end process;	nkbd_en_next <= nkbd_en(1 downto 0) & nkbd_en(2);	-- volume level	process(pwm_cnt(17))	begin		if pwm_cnt(17)'event and pwm_cnt(17) = '1' then			if NKBD_D(7) = '0' and nkbd_en(1) = '0' and pwm_reg /= 255 then				pwm_reg <= pwm_reg + '1';			elsif NKBD_D(7) = '0' and nkbd_en(2) = '0' and pwm_reg /= 0 then				pwm_reg <= pwm_reg - '1';			end if;		end if;	end process;
Cum generăm note muzicale?

Avem pe placă un oscilator de 50 MHz, şi ştim ce frecvenţă are fiecare notă muzicală. Tot ce ne trebuie este un divizor ales astfel încât:

50_MHz / constanta_din_divizor = frecvenţa_notei_dorite

Sărind peste teoria notelor muzicale (de ce frecvenţele astea şi nu altele), frecvenţele dorite sunt:

 

Tabel 1

Posted Image

 

Pentru a le obţine, vom face un divizor care se va încarcă cu diferite constante de divizare, în funcţie de butonul apăsat. În felul ăsata, divizorul va scoate diferite note, după cum se apasă butoanele.

 

Schema bloc arată cam aşa:

 

Fig. 9

Posted Image

 

Schema de mai sus a fost descrisă în VHDL. Codul principal arată astfel:

 

Prog. 8

------------------------------------------------------------------------------------ Company: -- Engineer: -- -- Create Date:    08:22:56 07/17/2012 -- Design Name: -- Module Name:    Buzzer - SPK -- Project Name: -- Target Devices: -- Tool versions: -- Description: ---- Dependencies: ---- Revision: -- Revision 0.01 - File Created-- Additional Comments: ------------------------------------------------------------------------------------library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_ARITH.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;entity Buzzer isgeneric (FREQ_DIV_BITS: integer := 17);port(	NSPK_VOL: out std_logic;	SPK_BIT: out std_logic;		NKBD_A: out std_logic_vector(2 downto 0);	NKBD_D: in std_logic_vector(7 downto 0);		NLED: out std_logic_vector(1 to 6);		CLK: in std_logic);end Buzzer;architecture SPK of Buzzer isconstant	DO_max_count: std_logic_vector(FREQ_DIV_BITS - 1 downto 0) := conv_std_logic_vector(95556 - 1, FREQ_DIV_BITS); --17 bits counterconstant	RE_max_count: std_logic_vector(FREQ_DIV_BITS - 1 downto 0) := conv_std_logic_vector(85131 - 1, FREQ_DIV_BITS);constant	MI_max_count: std_logic_vector(FREQ_DIV_BITS - 1 downto 0) := conv_std_logic_vector(75843 - 1, FREQ_DIV_BITS);constant	FA_max_count: std_logic_vector(FREQ_DIV_BITS - 1 downto 0) := conv_std_logic_vector(71586 - 1, FREQ_DIV_BITS);constant	SOL_max_count: std_logic_vector(FREQ_DIV_BITS - 1 downto 0) := conv_std_logic_vector(63776 - 1, FREQ_DIV_BITS);constant	LA_max_count: std_logic_vector(FREQ_DIV_BITS - 1 downto 0) := conv_std_logic_vector(56818 - 1, FREQ_DIV_BITS);constant	SI_max_count: std_logic_vector(FREQ_DIV_BITS - 1 downto 0) := conv_std_logic_vector(50619 - 1, FREQ_DIV_BITS);constant	DO_up_max_count: std_logic_vector(FREQ_DIV_BITS - 1 downto 0) := conv_std_logic_vector(47778 - 1, FREQ_DIV_BITS);component Note_generator_w_en is	generic (		B: integer); -- number of bits for counter	port (		max_count: in std_logic_vector(B - 1 downto 0);		out_note: out std_logic;		en: in std_logic;		clk: in std_logic);end component;signal max_count_for_note: std_logic_vector(FREQ_DIV_BITS - 1 downto 0);signal en: std_logic;signal nkbd_en: std_logic_vector(2 downto 0);signal nkbd_en_next: std_logic_vector(2 downto 0);signal pwm_cnt: std_logic_vector(17 downto 0);signal pwm_reg: std_logic_vector(7 downto 0);signal pwm: std_logic;signal reset_tick: std_logic; begin	note_generator: Note_generator_w_en		generic map(			B => FREQ_DIV_BITS)		port map(			max_count => max_count_for_note,			out_note => SPK_BIT,			en => en,			clk => CLK);	process(CLK)	begin		if CLK'event and CLK = '1' and nkbd_en(0) = '0' then			if NKBD_D(0) = '0' then max_count_for_note <= DO_max_count; end if;			if NKBD_D(1) = '0' then max_count_for_note <= RE_max_count; end if;			if NKBD_D(2) = '0' then max_count_for_note <= MI_max_count; end if;			if NKBD_D(3) = '0' then max_count_for_note <= FA_max_count; end if;			if NKBD_D(4) = '0' then max_count_for_note <= SOL_max_count; end if;			if NKBD_D(5) = '0' then max_count_for_note <= LA_max_count; end if;			if NKBD_D(6) = '0' then max_count_for_note <= SI_max_count; end if;			if NKBD_D(7) = '0' then max_count_for_note <= DO_up_max_count; end if;		end if;	end process;	NKBD_A <= nkbd_en;	NLED(1) <= not en;	NLED(2 to 4) <= (others => '1');	NLED(5) <= pwm;	NLED(6) <= not pwm;	NSPK_VOL <= pwm;	--*******************************************-- PWM volume control--*******************************************	-- PWM counter and keyboard address shift register	process(CLK)	begin		if CLK'event and CLK = '1' then			-- keyboard			case nkbd_en is				when "110" =>					nkbd_en <= nkbd_en_next;					en <= not (NKBD_D(0) and NKBD_D(1) and NKBD_D(2) and NKBD_D(3) and 									NKBD_D(4) and NKBD_D(5) and NKBD_D(6) and NKBD_D(7));				when "101" | "011" =>					nkbd_en <= nkbd_en_next;				when others => nkbd_en <= "110";			end case;									if pwm_cnt(17 downto 10) = pwm_reg then				pwm <= '0';			elsif pwm_cnt = b"00_0000_0000_0000_0000" then				pwm <= '1';			end if;						pwm_cnt <= pwm_cnt + 1;		end if;	end process;	nkbd_en_next <= nkbd_en(1 downto 0) & nkbd_en(2);	-- volume level	process(pwm_cnt(17))	begin		if pwm_cnt(17)'event and pwm_cnt(17) = '1' then			if NKBD_D(7) = '0' and nkbd_en(1) = '0' and pwm_reg /= 255 then				pwm_reg <= pwm_reg + '1';			elsif NKBD_D(7) = '0' and nkbd_en(2) = '0' and pwm_reg /= 0 then				pwm_reg <= pwm_reg - '1';			end if;		end if;	end process;end SPK;
acest cod foloseşte un divizor descris într-un alt fişier VHDL:

 

Prog. 9

------------------------------------------------------------------------------------ Company: -- Engineer: -- -- Create Date:    17:13:47 07/17/2012 -- Design Name: -- Module Name:    Note_generator_w_en - SPK -- Project Name: -- Target Devices: -- Tool versions: -- Description: ---- Dependencies: ---- Revision: -- Revision 0.01 - File Created-- Additional Comments: ------------------------------------------------------------------------------------library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_ARITH.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;entity Note_generator_w_en is	generic (		B: integer := 18); -- number of bits for counter	port (		max_count: in std_logic_vector(B - 1 downto 0);		out_note: out std_logic;		en: in std_logic;		clk: in std_logic);end Note_generator_w_en;architecture SPK of Note_generator_w_en is	signal counter: std_logic_vector(B - 1 downto 0);	signal counter_next: std_logic_vector(B - 1 downto 0);	signal out_bit: std_logic;	signal out_bit_next: std_logic;	signal zero: std_logic_vector(B - 1 downto 0) := (others => '0');begin	-- modulo N counter	process(clk)	begin		if clk'event and clk = '1' then			counter <= counter_next;			out_bit <= out_bit_next;		end if;	end process;	-- next state	counter_next <= counter - '1' when counter /= zero else max_count;	out_bit_next <= out_bit when counter /= zero else not out_bit; -- make output PWM = 50%		-- output state	out_note <= out_bit when en = '1' else '0';end SPK;
Noţiuni noi VHDL

Observăm că de data asta avem două fişiere cu cod VHDL (Prog. 7 nu se pune, Prog. 7 este doar o bucată extrasă din Prog. 8, pentru claritatea explicaţiilor).

Prog. 9 este un divizor de frecvenţă generic, care divizează ceasul de 50 MHz cu max_count, obţinând astfel nota dorită, iar al doilea fişier, Prog. 8, este programul principal, care se foloseşte de blocul Prog. 9. Avantajul folosirii unor fişiere diferite este că putem refolosi codul la diferite scheme. Odată scris un fişier care implementează un divizor, îl putem folosi şi la alte scheme viitore care vor avea în componenţa lor divizoare de frecvenţă.

 

Asemănător divizorului, se pot scrie diferite blocuri, mai simple sau mai complicate, blocuri care pot fi folosite şi la alte montaje cu FPGA-uri. Astfel de montaje standard predefinite se numesc IP-uri (în traducere, Proprietăţi Intelectuale). Avantajul lor este că poţi folosi în montajul tău blocuri foarte complexe, gata făcute şi testate de alţii. De obicei IP-urile se cumpără pe bani grei, dar dacă eşti o firmă mare, merită să le cumperi decât să pierzi luni întregi cu dezvoltarea unor astfel de blocuri, reinventând roata.

:aut:

Ca un exemplu, există scheme (descrise în cod HDL) care implementează diverse periferice hardware, cum ar fi chip-ul standard de interfaţă serială, UART 16550, sau implementează protocoale, ca USB sau PCI-Express, ca să dau numai câteva exemple dintre cele mai cunoscute.

 

Bineînţeles, blocurile IP pot conţine scheme foarte complicate, cum ar fi interfeţe, microprocesoare, microcontroller-e, DSP-uri, etc.. De exemplu, există blocuri IP care conţin schema unui întreg microprocesor Z80 sau a unui microcontroller AVR, cum ar fi cele din Arduino, doar că merg la 100 MHz sau chiar mai mult, faţă de un Arduino la numai 16 MHz.

:dans:

Partea frumoasă este că există foarte multe IP-uri gratuite, lucru foarte important pentru hobby-şti. În schimb, cele care nu sunt gratuite sunt foarte, foarte scumpe. E bine că unele IP-urile sunt scumpe, dezvoltaţi şi voi aşa ceva şi puneţi-le la vânzare!

:freaza:

 

Deja m-am întins foarte mult, nu voi mai explica cum se foloseşte un bloc definit în alt fişier, deja există foarte multe cărţi şi explicaţii pe Internet (ce-i drept, majoritatea în engleză, în română nu prea sunt).

 

Observăm două cuvinte noi, "constant" şi "generic".

Vă invit să citiţi pe Internet despre fiecare, sunt importante.

 

În schema noastră, numărul max_count este schimbat în funcţie de butonul apăsat. Astfel, pentru fiecare buton apăsat, se obţine o altă notă:

"1" - Do

"2" - Re

"3" - Mi

"4" - Fa

"5" - Sol

"6" - La (440 Hz)

"7" - Si

"8" - Do de sus

Rezultatul este cel din videoclip:

Observaţi, vă rog, că fiecare buton între "1" şi "8" corespunde unei note. Dacă apăsăm mai multe butoane simultan, atunci va fi cântată o singură notă, şi anume nota cea mai înaltă (generatorul nostru nu este polifonic).

Din butonul mare se reglează volumul.

 

Movie 7

2JPb_6ddvTM

 

Este unul din primele programe (o versiune beta), şi are cel puţin două inconveniente :jytuiyu :

- dacă apăsăm simultan două note (două butoane), va fi cântată nota cea mai înaltă (aşa a fost făcut programul - "clapa" noastră nu este polifonică)

- dacă apăsăm simultan nota do de sus (butonul "8") şi reducere volum (butonul "BL"), montajul nu mai funcţionează corect, apar salturi în intensitatea semnalului sonor (ăsta e un bug)

Movie 8

RoTgJsst9dg

 

Ceva, ceva merge, mai rămâne să optimizăm schema şi să remediem inconvenientele.

:da

 

 

 

Acronime:

PWM - Pulse Width Modulation

IP - Intellectual Property

DSP - Digital Signal Processor

Share this post


Link to post
Share on other sites

super tare, poate ar trebui pus SI la tutoriale

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...