Tijek razvoja softvera za CPLD i MCU
Programiranje CPLD-a
Za programiranje EPM570 (kao i raznih drugih FPGA i CPLD čipova) namijenjen je programski softver Quartus II (Intel Quartus Prime), a programski jezik za opis hardvera (HDL) zove se Verilog. Quartus II omogućuje cijeli proces razvoja FPGA, od pisanja koda i crtanja logičkih shema do simulacije, optimizacije i konačnog prebacivanja dizajna na hardver. Softver također uključuje alate za analizu i sintezu (pretvaranje koda u logička vrata), analizu vremena (timing analysis) te programiranje samog uređaja.
Za programiranje EPM570 morate kupiti i namjenski USB Blaster Programmer koji košta oko 3 eura. Ovaj programator se s jedne strane spaja na USB port računala, a s druge strane na 10-pinski JTAG konektor na razvojnoj pločici EPM570. Jednostavnije ne može biti.
Verilog je kao što smo već spomenuli jezik za opis hardvera (HDL) koji se koristi za modeliranje i simulaciju elektroničkih sustava. Ovaj programski jezik se bitno razlikuje od standardnih programskih jezika (poput C-a) jer ne opisuje slijedne naredbe za MCU (softver) nego opisuje strukturu i ponašanje hardvera, odnosno kako su žice povezane i kako logička vrata međusobno komuniciraju. U tipičnom radnom procesu, Verilog se koristi za pisanje koda koji opisuje kako bi digitalni sklop trebao raditi, a zatim se koristi Quartus II kako bi taj kod kompajlirao i fizički implementirao u FPGA/CPLD čip.
Sve ovo na prvi pogled djeluje komplicirano, odnosno djeluje kao nešto čemu treba posvetiti ogromno vremena za učenje. Međutim, jednostavno programiranje kao ovo za naš projekt u potpunosti može za vas odraditi AI. Uz pomoć AI meni su bila dovoljna 2-3 sata da naučim rad u Quartus II do dovoljne razine da konfiguriran I/O pinove i programiram CPLD pomoću Verilog koda kojeg mi je u potpunosti generirala AI.
Za programiranje svih onih diskretnih logičkih čipova za svaki pojedinačni senzor (Schmitt inverteri, RS Latchevi, multipleksori) treba napisati svega 30-tak linija Verilog koda. Osim toga, unutar CPLD također je sada programirana logika koja detektira promjenu stanja na bilo kojem senzoru kao prolazak metka. Time ESP32 ne mora stalno brzo skenirati sve senzonske linije ne bi li otkrio prolaz metka, već to za njega neusporedivo brže radi sam CPLD i informacije prema ESP32 šalje samo kada postoji neka promjena na senzorima.
Ovime smo dobili izuzetno brz detektorski sustav kojeg ćemo u ESP32 u jednom dijelu morati i znatno usporiti jer bi jedna dijabola (zbog svog oblika) mogla izazvati nekoliko brzih detekcija.
Osnovne upute za rad sa Quartus programom
Sučelje programa Quartus za programiranje našeg CPLD-a EPM570. Evo kratkih uputa kako se vrši samo programiranje kad imate gotovu datoteku s programom (.pof):
- Kreiranje novog projekta (New Project)
- Odabir EPM570 uređaja u projektu (Assignments -> Device)
- Family -> MAX II
- Available devices -> EPM570T100C5
- Postavljanje USB Blastera u Programatoru (Tools -> Programmer)
- Hardware Setup
- Currently selected hardware -> USB-Blaster
- Auto Detect
- Programiranje
- Add File -> odabir .pof datoteke
- Program/Configure -> čekiraj polje (označi kvačicu)
- Start
Programiranje je dakle vrlo jednostavno i ne razlikuje se puno od programiranja u mnogim drugim sličnim programatorima. A sada evo jednostavnog vodiča kako napraviti datoteku s programom (.pof) za EPM570T100C5 pločicu koristeći Verilog jezik, koji ste napisali sami ili uz pomoć AI:
- Izrada projekta u Quartusu (File -> New Project Wizard)
- Odredite mapu gdje će projekt biti spremljen i naziv projekta -> Next
- Family & Device Settings
- Family -> MAX II
- Available devices -> EPM570T100C5
- Finish
- Pisanje koda
- File -> New -> Verilog HDL File (kreiranje datoteke sa programom)
- Nakon što je kod napisan (ili kopiran) datoteka se sprema (File -> Save) pod istim nazivom kako je odabrano u nazivu projekta s time da će ekstenzija datoteke biti .v
Vidimo da je i ovo posve jednostavno, no CPLD još uvijek ne zna na kojim I/O pinovima je fizički spojeni vanjski hardver. To mu morate reći:
- Dodjela pinova (Pin Planner)
- Prvo se klikne na ikonu Start Compilation (ili pritisne Ctrl+L) da Quartus analizira kod
- Nakon što analiza uspješno završi, ide se na Assignments -> Pin Planner
- U donjoj tablici vidjet će se nazivi kojima treba odrediti I/O pinove i ostale atribute poput ulaznog Schmitt trigera za senzorske pinove
- Nakon definiranja pinova Pin Planner se zatvara (automatski sprema unose) i ponovno se pokrene Start Compilation
Nakon što imamo spremljenu programsku datoteku, programiranje se vrši kako smo opisali na početku:
- Spojite USB Blaster na PC i na JTAG konektor pločice. Uključite napajanje pločice.
- Idite na Tools -> Programmer.
- Provjerite piše li gore desno USB-Blaster. Ako ne, kliknite Hardware Setup i odaberite ga.
- Kliknite Auto Detect. Trebao bi se pojaviti EPM570 na slici.
- Ako datoteka nije automatski učitana, kliknite na nju (ili Add File) i odaberite .pof datoteku iz mape output_files.
- Označite kvačicu pod Program/Configure i kliknite Start.
Napomena: Ako se bilo što mijenjate u programu ili Pin Planneru, morate ponovno pokrenuti kompilaciju (plava strelica Start Compilation u glavnom prozoru Quartusa). Tek tada će te promjene biti upisane .pof datoteku koju šaljete na čip.
Programiranje Verilog koda za CPLD MAX II EPM570
Iako u osnovnim uputama za Quartus sve izgleda jednostavno, tijekom programiranja i definiranja pinova može se dogoditi niz grešaka i problema koje je onda potrebno sustavno rješavati. Tako je bilo i tijekom kreiranja programa za našu elektroničku metu. Program ima svoje „mušice“ i neke radnje je potrebno vršiti po točno određenom protokolu i hijerarhiji.
U Verilogu i Quartusu, hijerarhija nije samo način organizacije koda, već temelj načina na koji softver vidi hardver. Stoga su mi se često događale frustrirajuće situacije gdje prvi put neka naredba ili radnja prvi put prođe bez problema, a nakon modifikacija ili promjena u kodu (ili čak kod izrade nove verzije programskog koda) iste naredbe više ne prolaze.
Quartus uvijek traži jedan Top-Level Entity. Ako napravite novi modul ili promijenite ime postojećeg, a niste ga u postavkama postavili kao “Top-Level” (Hierarchy -> Set as Top-Level Entity), Quartus će uporno kompajlirati staru verziju ili javiti grešku jer ne vidi put do vaših novih naredbi. Quartus pokušava uštedjeti vrijeme tako da ne kompajlira sve ispočetka. Ako promijenite nešto duboko u hijerarhiji (npr. u nekom malom modulu koji se poziva unutar pet drugih), Quartus ponekad ne prepozna tu promjenu ispravno. Na primjer, ako dodate novu programsku datoteku u mapu projekta, ali ju ne dodate u “Files” listu unutar Quartusa, on će koristiti staru verziju datoteke koju još uvijek ima u memoriji, dok vi gledate u novi kod i pitate se zašto nema promjena.
Za razliku od klasičnog programiranja, u Verilogu svaki put kad pozovete modul, vi stvarate fizičku kopiju tog hardvera. Ako promijenite logiku unutar modula, to utječe na svaki dio projekta gdje se taj modul koristi. Ako imate grešku u hijerarhiji (npr. krivo spojene žice/portove između modula), program će možda proći analizu, ali će pasti na “Fitteru” (smještanju na čip) jer fizički ne može povezati te dijelove.
Iako je moj programski kod relativno jednostavan (da ne kažem malen) tijekom razvoja i testiranja radio sam dosta promjena i preinaka. Prvi put je sve prošlo „od prve“, a što sam više kasnije modificirao Verilog kod to sam i sve više vremena trošio na traženje načina kako da Quartus i u potpunosti prihvati taj novi kod.
Nakon što nekoliko desetaka puta izmijenite raspored pinova i promijenite osnovni Verilog kod, stvari počinju sjedati na mjesto i naučite kako i gdje najbolje raditi promjene tako da programiranje svaki puta prođe ispravno.
Meni je Verilog kod 99% pisao AI, međutim, prvi kod posve sigurno nikad neće raditi dobro. Bilo je potrebno nekoliko desetaka izmjena koda, a također i nekih izmjena na hardveru, kako bi dobio stabilan sustav detekcije.
Arhitektura Verilog koda za elektroničku metu
Prvo sam radio program za kontrolu samo 64 senzora preko jednog EPM570 i ESP32, no na puno stvari se ipak trebalo gledati unaprijed, dakle za buduću kontrolu svih 128 (2×64) senzora.
Arhitektura prvog rješenja je bila slijedeća:
- 64 interna Schmitt triggera (Assignment Editor -> Input Termination -> Schmitt Trigger).
- 64 RS Latcha (svaki ulaz ima svoj registar koji se postavlja na 1 čim senzor okine)
- Reset linija (ESP32 će poslati jedan signal na EPM570 koji istovremeno briše svih 64 latch-eva nakon čitanja)
- SPI-like Protokol – Umjesto sporog I2C-a, koristi se jednostavan Shift Register (posmični registar). ESP32 će slati “Clock” impulse, a EPM570 će izbacivati bit po bit (64 bita u nizu).
EPM570 pločica ima dostupnih 72 I/O pinova. 64 pina ide na senzore, 6 pinova je komunikacija sa ESP32, a 2 pina ostaju slobodna (kasnije ćemo iskoristiti još jedan pin za Alarm). Ručni unos 70 pina u Pin Planneru bi bio dugotrajan pa sam koristio Tcl skriptu koju mi je generirao AI (View -> Utility Windows -> Tcl Console). Učitavanjem ove skripte svi pinovi se definiraju istog trenutka.
Naravno, ne možete puno toga provjeriti ako ne pišete paralelno kodove za EPM570 i ESP32. Također, s obzirom da će se podaci bežično slati preko LoRa Modula (usko grlo), onda je već na samom početku potrebno razvijati kod tako da paketi podataka koji se šalju budu što manji. Na ovo se ne može puno utjecati jer se mora slati 64/128 bita podataka, međutim, sustav se može optimizirati tako da potreba za slanjem tih podataka bude minimalna, odnosno samo kad se dogodi neka promjena (kad metak aktivira zavjesu).
Osnovna arhitektura sustava, kako smo i prikazali u uvodnoj objavi, izgleda ovako:
- Na Meti: EPM570 (detekcija) -> ESP32 (obrada) -> LoRa Modul (slanje)
- Kod Strijelca: LoRa Modul (Prijem) -> ESP32 (obrada) -> Elecrow CrowPanel 7.0″ ESP32 Display (prikaz rezultata)
Pozitivna ili negativna senzorska logika
Nakon što opisani sustav proradio, trebalo se konačno odlučiti o senzorskoj logici, odnosno da li će aktivno stanje slati logičku nulu ili logičku jedinicu. Odlučio sam da senzori rade u negativnoj logici (1 = mirno, 0 = metak). Sve bi zapravo isto funkcioniralo i u suprotnoj logici, no ako se otpornici nalaze na masi (umjesto na plus polu napajanja) onda oni stalno drže I/O linije EPM570 na masi (pull-down) što doprinosi stabilnosti I/O ulaza kad su laseri isključeni. U slučaju pozitivne logike morali bi uključiti interne pull-up otpornike unutar EPM570 tako da pinove drže u nekom određenom stanju kad su senzori isključeni.
Trebalo je dakle prilagoditi Verilog kod tako da reagira na padajući brid, odnosno da detektira logičku nulu kao prolaz projektila. Međutim, postoji još jedan problem sa tom pozitivnom ili negativnom logikom, a to je odluka da li ću koristiti interne Schmitt triggere ili ne. Uobičajeno bi ih bilo dobro koristiti ali kako smo već opisivali u objavi o senzorima, za male kalibre od 4,5 mm sigurnija detekcija bi bila bez Schmitt triggera (zbog većeg naponskog praga promjene stanja u logičku nulu).
Ako ne koristim interne Schmitt triggere (koji su također i inverteri) onda negativnu logiku sa senzora mogu promijeniti u pozitivnu korištenjem if naredbe u Verilogu koja provjerava da li je senzor 0. Međutim, puno bolji je drugi način uz upotrebu internih invertera gdje se na samom početku invertira cijeli 128-bitni bus u pozitivnu logiku (1 = pogodak, 0 = mirno stanje) te interni RS latchevi rade dalje s čistom pozitivnom logikom. Ovo je bio također jedan od razloga zašto sam se odlučio na pull-down senzorski otpornik jer sam svakako želio koristiti interne Schmitt triggere.
Optimizacija upravljanja RS latchevima
Kad smo već spomenuli RS latcheve, kako što smo opisali u objavi o senzorima, oni služe za ispravnu detekciju nepravilnih impulsa koje ostavlja projektil prolazeći kroz 4 svjetlosne zavjese (dvije horizontalno i dvije vertikalno). Oblik dijabole (pješčani sat) i zračni vrtlozi iza nje mogu uzrokovati “titranje” signala (optički bounce). Također, zbog dvije dvostruke zavjese s razmakom od 7,2 mm znači da će ista dijabola aktivirati senzore u najmanje dva vala.
RS Latch pristup unutar EPM570 je savršeno rješenje za ovaj problem, međutim mora se implementirati na razini svakog pojedinačnog bita. U Verilog kodu svaki od 128 senzora ima svoj neovisni RS latch:
- Prvi kontakt (glava dijabole): Senzor pada na 0, latch skače na 1 i ostaje na 1.
- Prolazak “struka” dijabole: Senzor se na trenutak vrati na 1 (svjetlo prođe), ali latch to ignorira jer je već “zaključan”.
- Prolazak druge zavjese (7,2 mm kasnije): Čak i ako isti senzor ponovno padne na 0 zbog drugog reda zavjese, latch je već na 1 od prvog reda.
EPM570 će dakle zabilježiti “sjenu” (siluetu) dijabole kao uniju svih pogođenih senzora u oba dvostruka reda, bez obzira na titranje signala. Međutim, ovdje moramo biti sigurni da je dijabola potpuno napustila sve zavjese prije nego što se RS latchevi resetiraju za čekanje nove dijabole. Stoga je potrebno dodati softverski delay na ESP32 strani:
- Metak prolazi -> EPM570 digne trigger_alarm.
- ESP32 detektira alarm -> Odmah očita 128 bita (to traje < 1ms).
- ESP32 šalje podatke preko LoRa-e.
- Wait (100 ms): ESP32 čeka da metak fizički napusti prostor mete, 100 ms je više nego dovoljno i za najsporije projektile, a s druge strane čak ni rafalna paljba ne šalje dva projektila za redom tom brzinom.
- Reset: ESP32 šalje kratki impuls na pinRESET.
U ESP32 je dakle dodan kod za čekanje od 100 ms, a u Verilog je dodan kod koji osigurava da jednom postavljeni bit (pogodak) ne može biti poništen ničim osim hardverskim resetom.
Optimizacija koda za LoRa Module
S obzirom da LoRa Modul ne može slati podatke velikom brzinom išao sam na novo rješenje. Meta neće neprestano slati 128 bita podataka o stanju senzora, nego će podatke slati samo ako primijeti da je došlo do promjene u stanju senzora. Detekcija prolaska metka se dakle neće vršiti na prijemnoj strani, nego već u elektronici same mete. Evo nove arhitekture:
- Strana mete: EPM570 (Brza detekcija): EPM570 neprestano skenira senzore (50 MHz). Čim se bilo koji bit promijeni iz 0 u 1, on podiže signal trigger_alarm prema ESP32.
- Strana mete: ESP32 čeka trigger_alarm i čim on okine trenutno očita 64/128 bita iz EPM570 preko SPI sabirnice. Tek tada šalje taj 128-bitni paket preko LoRa-e prijemniku kod strijelca.
- Strana strijelca: ESP32 prima gotov rezultat pogodaka i iscrtava ga na 7″ ekranu.
Kad bi slali 128 bita podataka neprestano preko LoRa-e, zagušili bi frekvenciju i trošiti bateriju, a postoji šansa da u trenutku prolaska metka LoRa modul upravo završava slanje prethodnog (praznog) paketa i propusti trenutak pogotka. Budući da LoRa sada šalje samo stvarne pogotke, komunikacija je čista, brza i nema kašnjenja (latencije) uzrokovanog nepotrebnim slanjem nula.
Dvosmjerna LoRa komunikacija
Slijedeća ideja bila je uvođenje dvosmjerne LoRa komunikacije za test provjere ispravnosti svih senzora. Prijemnik (CrowPanel) šalje upit, a Predajnik (Meta) vraća trenutni 128-bitni status svih senzora. Evo kako bi trebala raditi ta funkcija “Provjera ispravnosti” za sve senzore.
- Na CrowPanelu se pritisne tipka “Test Mete”.
- LoRa (Strijelac -> Meta) šalje kratki paket REQ_STATUS.
- ESP32 (Meta) ne čita latcheve (koji su možda prazni), nego privremeno očita izravno stanje 128 pinova senzora i šalje tih 128 bita natrag preko LoRa-e.
- Na CrowPanelu, ako se primi 128 jedinica (u binarnom obliku), sustav javlja “Meta OK”. Ako je neka nula, javlja “Senzor X u kvaru/zasjenjen”.
Da bi se podržao i “pogodak” i “provjera ispravnosti”, dodan je još jedan mod čitanja u Shift Registar pomoću novog kontrolnog pina mode_select:
- mode_select = 0: load_data / kopira latcheve (pogodak)
- mode_select = 1: load_data / kopira trenutno stanje senzora (provjera)
Odabir GPIO pinova na ESP32
ESP32 razvojna pločica (DevKit) koju ja koristim (ESP32-WROOM-32D) ima na vanjske konektore izvedena ukupno 30 pina. Međutim, ne mogu se baš svi GPIO pinovi koristiti kao ulazno/izlazni pinovi bez ograničenja. Neki od pinova su samo za napajanje i reset, neki od pinova su samo ulazni, a neki prilikom prvog pokretanja modula (uključivanja napajanja i učitavanja programa) moraju imati točno određena stanja (HIGH ili LOW). Ako su u krivom stanju dok se ESP32 pali, program se neće pokrenuti ili će se ESP32 smrznuti, ući u beskonačne petlje i slično.
Također, kod Arduina ESP32 kao i ATmega kontrolera postoje standardni (default) pinovi rezervirani za određene komunikacijske protokole. Kod klasičnih 8-bitnih ATmega kontrolera, hardverski moduli su fiksno vezani za određene pinove. Ako želite koristiti hardverski SPI ili I2C, morate koristiti baš te pinove. ESP32 je mnogo fleksibilniji zahvaljujući GPIO matrici koja omogućuje da se gotovo svaka hardverska funkcija dodijeli bilo kojem pinu. Ipak, postoje “uobičajeni” pinovi koji su tvornički predefinirani.
S obzirom da imam više ESP32-WROOM-32D modula s kojima uobičajeno radim projekte, napravio sam si mali vodič prema kojem vidim koji su pinovi „sigurni“ za korištenje, a koje treba izbjegavati ako imamo viška sigurnih pinova.
- GPIO 1 (TX0 – Transmit) – Preko njega ESP32 šalje podatke na Serial Monitor. Ovaj pin treba izbjegavati za drugo korištenje.
- GPIO 2 – Kod početnog paljenja uvijek mora biti na LOW ili ostavljen nepovezan (standardni način rada i način programiranja). Ako je ovaj pin povučen na HIGH tijekom paljenja, ESP32 će pokušati ući u nepostojeći mod rada ili će se “zamrznuti” i programski kod se uopće neće pokrenuti. Obično je na njega spojena ugrađena LED dioda.
- GPIO 3 (RX0 – Receive) – Preko njega ESP32 prima novi program s računala. Ovaj pin treba izbjegavati za drugo korištenje.
- GPIO 4 – Samostalan i stabilan pin opće namjene
- GPIO 5 – Kod početnog paljenja mora biti na HIGH (standardni način rada). Ako je ovaj pin povučen na LOW tijekom paljenja, ESP32 pokušava ući u mod za “SDIO slave” (komunikacija s SD karticama na poseban način). To obično uzrokuje da se ESP32 uopće ne pokrene ili uđe u “boot loop”. Budući da ovaj pin ima interni pull-up otpornik, on će sam otići u HIGH, međutim treba paziti da ga spojeni izlaz ne povuče na LOW.
- GPIO 12 – Kod početnog paljenja mora biti na LOW. Ako je HIGH kod paljenja, može doći do problema s naponom flash memorije (pokušat će raditi na 1,8V umjesto 3,3V), što će spriječiti podizanje sustava.
- GPIO 13 – samostalan pin opće namjene, dio JTAG sučelja za debugiranje
- GPIO 14 – samostalan pin opće namjene
- GPIO 15 – Kod početnog paljenja mora biti na HIGH. Šalje debug poruke na Serial portu pri paljenju. Budući da ovaj pin ima interni pull-up otpornik, on će sam otići u HIGH, međutim treba paziti da ga spojeni izlaz ne povuče na LOW. U tom slučaju će se spriječiti ispis na Serial monitoru, no obično se normalan start ipak pokrene.
- GPIO 16 – samostalan pin opće namjene, obično se koristi za UART2, na WROOM-32U ili modulima s PSRAM-om su zauzeti (nisu dostupni)
- GPIO 17 – samostalan pin opće namjene, obično se koristi za UART2, na WROOM-32U ili modulima s PSRAM-om su zauzeti (nisu dostupni)
- GPIO 18 – samostalan pin opće namjene, obično se koristi za VSPI (SCK)
- GPIO 19 – samostalan pin opće namjene, obično se koristi za VSPI (MISO)
- GPIO 21 – samostalan pin opće namjene, obično se koristi za I2C (SDA)
- GPIO 22 – samostalan pin opće namjene, obično se koristi za I2C (SCL)
- GPIO 23 – samostalan pin opće namjene, obično se koristi za VSPI (MOSI)
- GPIO 25 – samostalan pin opće namjene, podržava DAC1
- GPIO 26 – samostalan pin opće namjene, podržava DAC2
- GPIO 27 – samostalan pin opće namjene
- GPIO 32 – samostalan pin opće namjene, podržava ADC1
- GPIO 33 – samostalan pin opće namjene, podržava ADC1
- GPIO 34 – Isključivo ulazni pin (nema interni pull-up otpornik i ne može biti izlaz), podržava ADC1
- GPIO 35 – Isključivo ulazni pin (nema interni pull-up otpornik i ne može biti izlaz), podržava ADC1
- GPIO 36 (VP – Voltage Positive) – Isključivo ulazni pin. Pin povezan s ADC1 (najstabilnijim analognim digitalnim pretvaračem) i često se koristi za senzore vrlo slabog signala. Pin je povezan na pozitivni ulaz internog pojačala niskog šuma (Low Noise Amplifier – LNA).
- GPIO 39 (VN – Voltage Negative) – Isključivo ulazni pin. On je povezan na negativni ulaz istog pojačala kao GPIO 36.
- EN (Enable / Reset) – Ovaj pin nije GPIO nego Reset pin za upravljanje radom cijelog čipa. Interno je povučen na “HIGH” (3,3V) kako bi čip radio. Ako se kratko spoji na GND (LOW), ESP32 će se ponovno pokrenuti (restartati).
- VIN – napajanje 5 V (ulaz internog regulatora 3,3 V)
- 3V3 – napajanje 3,3 V (izlaz internog regulatora 3,3 V)
- GND – zajednička masa
GPIO pinovi: 0, 2, 4, 12, 13, 14, 15, 25, 26, 27 se mogu koristiti za ADC2 ulaze ali samo ako se ne koristi Wi-Fi (inače ne rade pouzdano). GPIO pinovi: GPIO 0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33, 34, 35 podržavaju RTC (Real-Time Clock / Deep Sleep). Ovi pinovi su povezani s “Low Power” koprocesorom i mogu probuditi ESP32 iz Deep Sleep moda ili raditi dok je glavni procesor isključen. GPIO pinovi: 34 i 35 su odlični za analogne senzore jer su samo ulazni i nemaju interne smetnje od izlaznih krugova. Inače je za signale koji su samo ulazni dobro koristiti isključivo ulazne pinove (34, 35, 36, 39) jer su stabilniji od ulazno/izlaznih pinova.
Za elektroničku metu GPIO pinove sam definirao na slijedeći način:
Arduino programi za ESP32
Već smo vidjeli kod razvoja Verilog koda da nije moguće potpuno odvojeno programiranje EPM570 modula i ESP32 modula za stranu mete (predajnik) i stranu strijelca (prijemnik). Sustavi moraju međusobno komunicirati te se moraju i paralelno razvijati.
AI je kreirala prve programske kodove za EPM570 i ESP32 prema mojim zahtjevima. Odmah je implementirano i bežično slanje preko LoRa modula. Arduino kodovi za oba ESP32 imaju Debug logove i Serial print naredbe kako bi se mogao provjeriti rad programa jer nije realno očekivati da će sve proraditi od prve.
Prvi testni kodovi
Prvi kodovi su uključivali samo komunikaciju između jednog EPM570 CPLD-a i jednog ESP32. Nakon malo dorađivanja, komunikacija je proradila i to je svakako bio razlog za optimizam. Međutim, u toj fazi svjetlosna zavjesa još uvijek nije bila kompletirana. Mogao sam raditi samo simulaciju okidanja senzora, a i to se moglo izvesti samo probno, na nekoliko senzora. Jednostavno je nemoguće „u zraku“ stabilno povezivati i simulirati sve 64 senzorske linije. Brzina CPLD-a je 50 MHz i bilo kakve nepotrebno duge žice, parazitski kapaciteti i slične pojave mogu izazvati lažna i nasumična okidanja. Kod ovakvih sustava, testiranje koje možete napraviti na testnim pločicama i sa priručnim napajanjima su vrlo ograničena.
Prvi testovi su poslužili samo kao zeleno svjetlo za izradu profesionalne tiskane pločice koja će električki uredno povezati oba CPLD-a, ESP32 i Lo-Ra modul. Tek tada se može ići u proširivanje programskih kodova i vršenje testova čitavog sustava.
Prvi kompletni kodovi
Elektronička meta je prilično složen projekt gdje je potrebno paralelno razvijati i stalno međusobno prilagođavati pojedine hardverske i softverske dijelove. Sam sistem lasera i foto-senzora je sustav kod kojeg treba rješavati niz praktičnih problema. To su problemi svjetlosnih polusjena koji mogu dovesti do nedovoljnog pada napona za promjenu logičkog stanja, problemi temperaturne ovisnosti i drugih nestabilnosti jačine zračenja lasera, problemi same geometrije svjetlosnih linija i tako dalje. Neke problemi se mogu riješiti ili barem ublažiti softverski, neke probleme je treba rješavati isključivo hardverski.
Zbog visoke frekvencije rada detektorskog sustava potrebno je izraditi kvalitetne tiskane pločice, a njih nije baš tako jednostavno i jeftino mijenjati kako se naiđe na neki problem. Stoga je puno stvari treba predvidjeti unaprijed, a ako negdje postoje dvojbe, onda svakako treba ostaviti prostora i osmisliti pločice za što lakšu prenamjenu i mogućnost testiranja što više mogućnosti.
SPI sabirnica
Osnovno je naravno bilo definirati pinove koje ćemo koristiti na EPM570 i ESP32 ali i ostaviti prostora za proširenje ili preinaku dizajna.
U prvoj inačici programa sva četiri modula na meti (LoRa, ESP32 i oba CPLD-a) dijele tri osnovne linije za SPI sabirnicu:
- ESP32 GPIO 18 (SCK) ide na LoRa SCK i na oba EPM570 PIN 96
- ESP32 GPIO 19 (MISO) ide na LoRa MISO I na EPM570-Y PIN 95 (izlaz zadnjeg u nizu)
- ESP32 GPIO 13 (MOSI) ide na LoRa MOSI I na EPM570-X PIN 1 (ulaz prvog u nizu)
Daisy-Chain
Prva ideja je bila povezati dva EPM570 u Daisy-chain kako bi zajedno radili kao jedan niz od 128 bita (kao jedan modul).
Daisy-Chain između dva EPM570 je ostvaren preko pinova 1 i 95: EPM570-X PIN 95 (Izlaz) spaja se na EPM570-Y PIN 1 (Ulaz). Ovime ESP32 šalje podatke u EPM570-X, EPM570-X ih prosljeđuje u EPM570-Y, a EPM570-Y ih vraća u ESP32.
Kontrolne SPI linije
S obzirom da na istu SPI sabirnicu ESP32 imamo spojena dva uređaja (EPM570 i Lo-Ra), moramo uvesti kontrolne linije (selektore) preko kojih će ESP32 određivati s kime kada komunicira:
- ESP32 GPIO 27 ide na LoRa CS. Kad je LOW, ESP32 komunicira sa LoRa modulom.
- ESP32 GPIO 5 ide na oba EPM570 PIN 97 (Load). Kad se pošalje impuls oba EPM570 “uslikaju” stanje senzora u shift registre.
- ESP32 GPIO 17 ide na oba EPM570 PIN 98 (Reset). Kad se pošalje impuls oba EPM570 brišu sve pogotke/latcheve.
Signali Alarma (Povratna informacija)
Već smo rekli da zbog Lo-Ra modula ESP32 neće slati podatke o stanju senzora neprekidno već samo kada se dogodi neka promjena (preleti metak). Štoviše, ni EPM570 neće slati stalno podatke na ESP32 da se bespotrebno ne zagušuje SPI sabirnica, već samo kad se dogodi neka promjena.
Stoga smo predvidjeli slobodne pinove 100 na modulima EPM570 koji šalju signal Alarma prema ESP32 samo kad se detektira pogodak u metu.
U kasnijoj verziji sam se odlučio razdvojiti Alarm linije tako da ESP32 vidi koji mu je točno EPM570 poslao Alarm. To će omogućiti bolju dijagnostiku ukoliko se dogodi da metak aktivira jednu zavjesu (npr. X), a nekako se ne detektiran provuče se kroz drugu (Y). U takvom slučaju će na displeju biti iscrtana horizontalna ili vertikalna crta, odnosno biti će prikazana barem ta jedna os detekcije.
Algoritam prvog koda elektroničke mete
- Metak siječe zraku te EPM570 X i Y podižu ALARM (GPIO 34 i 35) na visoki nivo.
- ESP32 to vidi, spusti LOAD (GPIO 5) da “zamrzne” stanje 128 senzora.
- ESP32 pročita 16 bajtova preko SPI-ja (prvih 8 je X, drugih 8 je Y).
- ESP32 pošalje podatke preko LoRa modula (koristeći isti SPI, ali uz CS LoRa pin).
- ESP32 pošalje RESET (GPIO 17) i meta je spremna za novi metak.
Prva PCB i prvi testni programi
Sa ovom početnom arhitekturom mogao sam izraditi prve tiskane pločice (PCB) za EPM570, ESP32 i Lo-Ra modul.
U ovoj fazi za testiranje koristim stvarnu bežičnu Lo-Ra vezu između dva ESP32, no prijemni ESP32 (strana strijelca) je i dalje na Arduino Serial monitoru.
Prvi testni programi su pokazivali niz problema sa stabilnošću detekcije stanja svih 128 senzora. Bilo je puno lažnih i nepotpunih detekcija, onda bi se detekcije stabilizirale ali X i Y osi nisu radile sinkronizirano. Sama laserska zavjesa je radila dobro, osim što je jedan laser bio neispravan. Zato sam prvo želio uvesti mogućnost „maskiranja“ neispravne optičke linije jer je to uvijek moguć scenarij i u praksi.
Problem Alarm signala
U početnom EPM570 kodu logika ALARM signala je radila “wired-OR”. Ako bilo koji od 128 senzora ne radi (daje logičku 0 jer je laser pomaknut, prljav ili pregorio), RS-latch za taj kanal će se okinuti, a CPLD će držati ALARM liniju visokom. Budući da je ALARM stalno HIGH, ESP32 “misli” da se stalno događa pogodak ili ostaje zaglavljen u petlji.
Stoga je u EPM570 dodan kod za maskiranje neispravnih senzora, odnosno mogućnost da EPM570 ignorira senzore koji nisu osvijetljeni pri početnoj inicijalizaciji. Umjesto jednostavnog OR-a svih senzora, ALARM bi se trebao aktivirati samo na prijelaz iz 1 u 0, i to samo za kanale koji su prethodno bili “zdravi”.
U ESP32 kod je također ugrađena inicijalizacijska sekvenca. ESP32 prvo mora okinuti RESET na EPM570, pričekati par milisekundi da se optika smiri, a tek onda početi pratiti ALARM.
Nekoliko desetaka Verilog i Arduino kodova usmjerenih na ovaj problem nije uspjelo riješiti problem nestabilnih i lažnih detekcija. Sumnja je pala na mogući konflikt na SPI sabirnici.
Konflikt na SPI sabirnici
Budući da CPLD nema svoj CS (Chip Select) pin, on “sluša” i “odgovara” na svaku SPI komunikaciju, uključujući i onu namijenjenu LoRa modulu. Kada LoRa završi slanje, on komunicira preko SPI-ja. CPLD to vidi kao clock signale i “ispuca” svoje podatke ili smeće u shift registre, što može uzrokovati da ALARM ostane aktivan (HIGH).
Modifikacijom koda se osiguralo da ESP32 ne reagira na ALARM dok god traje LoRa sesija i odmah nakon nje.
Ovo i dalje nije riješilo problem nestabilnih (random) i lažnih detekcija.
AI je sugerirao na loše ožičenje i samooscilacije sustava, predlagao je pull-down otpornike i filtre na SPI linijama. Međutim, imam puno iskustva u VF tehnici i znam da sam dizajnirao pločice i napravio ožičenja po svim standardima. Provjerio sam osciloskopom signale na SPI linijama i digitalni signali su bili praktično savršeni (čisti pravokutni signali oštrih bridova, bez istitravanja i drugih šumova). AI je dalje predlagala zamjenu redoslijeda senzora, što je besmisleno i sigurno neće riješiti problem. Prijedlozi su bili i EMI izolacija LoRa modula koji vjerojatno unosi smetnje u EPM570, međutim, moja mjerenja analizatorom spektra su pokazala čist signal bez izraženih EMI smetnji iz LoRa modula.
Opet na početak
S obzirom da AI izmjene kodova nisu davala nikakva poboljšanja, krenuo sam sa kodovima ispočetka. Prvo samo jednostavno snimanje stanja senzora na TX ESP32 bez LoRa komunikacije. Za ovaj test je izbačena ALARM logika i fokus je bio na to da EPM570 radi kao obični skener ulaza, koji svaku sekundu šalje sliku svih 128 senzora.
Čitanje je prolazilo „u paketima“, odnosno uredno bi se iščitao niz od nekoliko senzora, zatim nekoliko senzora prolazi bez detekcije, pa se opet nastavlja niz urednog čitanja nekoliko senzora. Kako da nešto stvara svojevrsne prekide ili pomake u čitanju senzora.
AI ponovno ukazuje na moguće smetnje i inducirane šumove na SPI linijama, predugačke tiskane veze i slične hardverske probleme. Meni je ovo lako izmjeriti i to definitivno nije problem. AI dalje smatra da dolazi do konflikta sa LoRa modulom, međutim, on je isključen. Sugerira dodavanje logičkih vrata na SPI linije kako se bolje razdvojile komunikacije ESP32 sa EPM570 i LoRa.
Ne želim raditi nepotrebne hardverske zahvate jer vidim na osciloskopu da su digitalni signali sami po sebi čisti i da dolaze u paketima kako i treba za ESP sabirnicu. Tražim od AI da softverski maksimalno uspori sve procese čitanja i detekcije, te da maksimalno minimalizira sve linije kodova koje nisu nužno potrebne za obično čitanje stanja senzora.
Mali pomak na bolje, pa na lošije
Nakon usporavanja čitavog sustava i novih ESP32 i EPM570 kodova detekcija radi ispravno, međutim, radi samo jednom nakon Reseta, a potom dalje više ne radi. Sve sugerira da je problem u RS latch logici unutar EPM570 ili u načinu na koji se podaci prebacuju iz latcha u shift registar, dakle u osnovnom Verilog kodu. Moguć je i nedostatak sinkronizacije u smislu da se latchevi unutar EPM570 aktiviraju na 50MHz, a čitanje je sada na sporom SPI clocku.
AI je izmijenila Verilog kod tako da linija Load istovremeno puni shift registar i resetira latcheve za idući ciklus. To bi trebalo riješiti problem sinkronizacije.
Nakon ovoga, detekcija se opet posve pokvarila te nije radila ni ono jednom nakon Reseta. AI novim kodom pokušava stvoriti još „sigurniju“ arhitekturu tako da potpuno razdvaja 50 MHz domenu (detekcija) od SPI domene (prijenos). LOAD signal će samo jednom “okinuti” prijenos iz latcheva u shift registar, ali neće automatski brisati latcheve (to će raditi ručno preko pinRESET).
Sada dobivam podatke u gotovo svakom čitanju, međutim oni su i dalje “potrgani”, razbacani i posve ne sinkronizirani.
AI opet predlaže pull-up otpornike na SPI linijama, stabiliziranje napajanja, dodavanje kondenzatora, filtara i feritnih prstena, optimizaciju ožičenja, zvjezdasto spajanje masa hardvera elektroničke mete i PC-a na kojem imam Arduino, magnetske izolatore za SPI lije i slične hardverske zahvate. U svoj hardver sam bio siguran, no ipak sam probao sam pull-up otpornicima. To nije popravilo stanje.
U slijedećim kodovima AI pokušava popraviti nedostatak sinkronizacije LOAD i SPI u vođenjem „hard reseta“, preciznih tajminga i dodatnih shift funkcija koje bi trebale “zaključati” X i Y linije na njihova mjesta. Mijenja Master-Slave SPI logiku, Load signal definira kao “Master Sync” koji prisilno resetira shift registre u oba EPM570 na početnu poziciju (bit 0) prije svakog čitanja. Definira se promjena stanje izlaznog pina na padajuću ivicu clocka kako bi podatak bio “miran” i spreman kada ga ESP32 pokuša pročitati na uzlaznu ivicu. Onda se prešlo na “Bit-Banging” metodu gdje se svakim taktom clocka (SCK) upravlja „ručno“, čime bi se trebali eliminirati problemi s faznim pomakom, SPI modovima i Daisy-chain kašnjenjem. Sve ovo (i mnogi drugi) su bili pokušaji čime bi se trebao riješiti problem “šetanja”, “trganja” i loše sinkronizacije podataka.
Rezultati promjena bi nekad bili bolji, nekad lošiji, no ni jednom zadovoljavajuće stabilni.
Problem je bio bizaran
Nakon vjerojatno 50-tak promjena kodova, snimanja signala, mjerenja šumova, provjeravanja LoRa modula, pull-up otpornika i niza raznih ideja, ispalo je da je cijelo vrijeme problem u krivo iniciranom pinu za liniju Load. Umjesto pinLOAD = 4, u kodu za ESP32 (meta) cijelo vrijeme je stajalo pinLOAD = 5.
Vratio sam se na početak i uvjerio se da sam dao točne rasporede pinova, štoviše i moj početni program ima uredno definiran pinLOAD = 4. Prvi kod koji je AI izbacio već je imao pogrešnu inicijaciju pina LOAD. Nije mi palo na pamet to provjeravati. Kasnije sam uglavnom samo mijenjao „void loop“ funkciju i uopće nisam obraćao pažnju na „const int“ konstante.
Čim sam promijenio pinLOAD = 4, čitanje svih 128 senzora se odvijalo savršeno, bez ijedne greške. Eko komentara AI na taj previd: „Isprika na previdu, vjerojatno sam u nekom od prethodnih primjera pomiješao pinove. Ako je tvoj LOAD fizički spojen na GPIO 4, to je sasvim u redu i nema potrebe za mijenjanjem ožičenja za taj pin.“
Prvo realno testiranje
Očito da će ove „izmasakrirane“ podatke sada još trebati dorađivati da se iz njih izvuče maksimum, no napravio sam prvo „sobno“ testiranje detekcije sa zračnim pištoljima na dijabole 4,5 mm i kuglice 6 mm.
Očekivano, dijabole od 4,5 mm u cca 40% slučaja ne prekinu obje zavjese, odnosno dijabola se provuče između dvije laserske zrake bez da ih dovoljno zakloni. Kuglice od 6 mm imaju detekciju od 100%, a vjerujem da bi tako bilo i za kalibar 5,5 mm.
Sada već imam ideju kako poboljšati svjetlosnu zavjesu tako da detektira kalibar 4,5 mm, no ako u tome i ne uspijem, ovo je ipak „long range“ meta predviđena za kalibre 5,5 mm i veće.
U svakom slučaju, kad se oporavim od ovih programiranja, idem na optimizaciju koda i nadogradnju sa dodatnim funkcijama. To će u prvom redu biti slanje stanja baterija, a zatim i meteoroloških podataka te podataka o brzini projektila na meti. Kad uspijem termički stabilizirati lasere, onda se kod proširuje na automatsku kalibraciju, detekciju kvara i slično.











