Pitanje:
PIC32 vs dsPIC vs ARM vs AVR, je li arhitektura uopće važna kad programiramo na jeziku C?
engineer
2017-12-24 15:38:11 UTC
view on stackexchange narkive permalink

Trenutno koristimo 32-bitni PIC32 mikrokontroler. U redu je za naše potrebe, ali istražujemo i druge mikrokontrolere koji nam mogu biti bolji + imamo i druge projekte za koje odabiremo MCU. U tu svrhu odabrali smo ARM zasnovan SAM DA mikrokontroler koji je isti 32-bitni, ali se temelji na ARM-u (popularniji od PIC32-a - industrijski gledano).

Sada za PIC32 koristimo MPLAB, ali za ARM cortex-M0 koristit ćemo Atmel Studio. Na obje platforme koristit ćemo C jezik. Ono što me brine jest da ćemo koristiti dva 32-bitna mikrokontrolera (iste tvrtke), ali različitih arhitektura. To će nam trebati da naučimo dva različita uređaja i povećat ćemo našu "krivulju učenja" + vrijeme isporuke. Ali s druge strane, također mislim da, budući da ćemo u oba slučaja koristiti C-jezik, krivulja učenja za ARM ne bi se trebala toliko čuti i vrijedi istražiti i taj procesor.

Moje glavno pitanje je, Kako veliku razliku čini arhitektura kada programiramo na C-jeziku jer pruža apstrakciju unutrašnjosti mikrokontrolera. i koje su glavne razlike u MPLAP-u i Atmel Studio, s obzirom na C -jezično programiranje.

Ako stvari rade s PIC32, koji je smisao prebacivanja?Čak i ako se kôd u potpunosti prenese (neće), još uvijek postoji novi lanac alata i IDE na koji se možete naviknuti.U čemu je poanta?Prebaciti se iz vjerskih razloga ili biti "ARM temeljen" (ili bilo što drugo) je glupo.Morate imati dobar razlog, ali niste nam ga pokazali.
Nisam pitao za prebacivanje.Razgovarao sam o odabiru drugačije arhitekture za druge projekte jer radimo na više projekata + ima prostora za poboljšanje u našem postojećem dizajnu.Glavna poanta odnosila se na krivulju učenja i izazove u radu s dvije različite arhitekture u isto vrijeme.
Otkrio sam da Atmel Studio pruža vrhunsko vrijeme od MPLAB-a [youtube video] (https://www.youtube.com/watch?v=648Tx5N9Zoc&t=1s)
Pet odgovori:
#1
+20
filo
2017-12-24 16:31:29 UTC
view on stackexchange narkive permalink

Ovo je prilično uvjerljiva tema. Mogu govoriti u svoje ime (AVR, ARM, MSP430).

Razlika 1 (najznačajnija) je u perifernim uređajima. Svaki od MCU-a ima slične UART, SPI, timere itd. - samo se nazivi i bitovi registracije razlikuju. Većinu vremena to je bilo glavno pitanje s kojim sam se morao suočiti prilikom premještanja koda između čipova. Solution: napišite svoje upravljačke programe s uobičajenim API-jem kako bi vaša aplikacija mogla biti prenosiva.

Razlika 2 je memorijska arhitektura. Ako želite postaviti konstante u flash na AVR, morate koristiti posebne atribute i posebne funkcije za njihovo čitanje. U ARM svijetu samo preusmjeravate pokazivač jer postoji jedan adresni prostor (ne znam kako mali PIC-i to podnose, ali pretpostavio bih da su bliži AVR-u).

Razlika 3 je prijavljivanje i rukovanje prekidima. avr-gcc ima makronaredbu ISR () . ARM ima samo naziv funkcije (poput someUART_Handler () - ako koristite CMSIS zaglavlja i početni kôd). ARM vektori prekida mogu se postaviti bilo gdje (uključujući RAM) i modificirati tijekom izvođenja (vrlo zgodno ako imate, na primjer, dva različita UART protokola koja se mogu prebaciti). AVR ima samo mogućnost korištenja vektora u "glavnom bljeskalici" ili "odjeljku za pokretanje" (pa ako želite drugačije rukovati prekidima, morate upotrijebiti izraz if ).

Razlika 4 - načini mirovanja i kontrola snage. Ako imate potrebu za najmanjom potrošnjom energije, tada morate smanjiti sve značajke MCU-a. To se može znatno razlikovati između MCU-a - neki imaju grublje načine uštede energije, neki mogu omogućiti / onemogućiti pojedinačne periferne uređaje. Neki MCU-ovi imaju podesive regulatore pa ih možete pokretati nižim naponom pri manjoj brzini itd. Ne vidim jednostavan način za postizanje iste učinkovitosti na MCU-u (recimo) s 3 globalna načina napajanja, a drugi sa 7 načina napajanja i upravljanje pojedinačnim perifernim satom.

Ta Jedna od najvažnijih stvari kada se brinete o prenosivosti jest jasno razdvajanje koda na dijelove koji ovise o hardveru (upravljački programi) i hardverski neovisni (aplikacija). Potonje možete razviti i testirati na uobičajenom računalu s lažnim upravljačkim programom (npr. konzola umjesto UART-a). To me spasilo mnogo puta jer je 90% aplikacijskog koda bilo dovršeno prije nego što je prototip hardvera izašao iz peći za reflow :)

Po mom mišljenju dobra stvar kod ARM-a je "monokultura" - dostupnost mnogih kompajlera (gcc, Keil, IAR ... da nabrojimo neke), mnogih free i službeno podržanih IDEs (barem za NXP, STM32, Silicon Labs, Nordic), mnogi alati za otklanjanje pogrešaka (SEGGER - posebno Ozon, ULINK, OpenOCD ...) i mnogi dobavljači čipova (neću ih ni početi imenovati). PIC32 je uglavnom ograničen na Microchip (ali važno je samo ako vam se njihovi alati ne sviđaju.

Što se tiče C koda. To je 99% isto, izjava if je ista, petlja radi na isti način. Međutim, trebali biste brinuti o veličini izvorne riječi. Na primjer, petlja for na AVR-u je najbrža ako za brojač koristite uint8_t , dok je na ARM-u uint32_t najbrži tip (ili int32_t ). ARM bi svaki put morao provjeriti ima li 8-bitnog preljeva ako ste koristili manji tip.

Odabir MCU-a i / ili dobavljača općenito uglavnom se tiče politike i logistike (osim ako imate vrlo jasna inženjerska ograničenja, na primjer: visoka temperatura - koristite MSP430 ili Vorago).Čak i ako aplikacija može raditi na bilo čemu, a samo 5% koda (upravljački programi) moraju se razviti i podržati tijekom životnog vijeka proizvoda - to je i dalje dodatni trošak za tvrtku.Sva mjesta na kojima sam radio imali su omiljenog dobavljača i MCU liniju (poput "odaberite bilo koji Kinetis koji želite, osim ako postoji vrlo dobar razlog da odaberete nešto drugačije").Također pomaže ako imate drugih ljudi za pomoć, pa bih kao menadžer izbjegao imati odjel za razvoj od 5 osoba u kojem bi svi koristili potpuno drugačiji čip.

“AVR je najbrži ako za brojač koristite uint8_t, dok je na ARM uint32_t najbrži tip (ili int32_t).ARM bi svaki put morao provjeriti ima li 8-bitnog preljeva ako ste koristili manji tip. "možete koristiti uint_fast8_t ako vam treba samo 8 bita.
@Michael - sigurno da možete koristiti _fast tipove, ali ne možete računati na ponašanje preljeva.U svom gcc-ovom stdint.h imam "typedef unsigned int uint_fast8_t", što je u osnovi uint32_t :)
Pokušaj pisanja API-ja koji je učinkovit, univerzalan i cjelovit teško je s obzirom na to da različite platforme imaju različite sposobnosti.CPU je vjerojatno manje važan od perifernih uređaja i dizajnerskih odluka donesenih s njima.Na primjer, neki uređaji omogućuju ponovno konfiguriranje različitih perifernih uređaja u bilo kojem trenutku u najviše nekoliko mikrosekundi, dok drugi mogu zahtijevati više koraka raspoređenih u stotinama mikrosekundi ili čak milisekundi.API funkcija koja je namijenjena bivšem uzorku može se koristiti u rutini usluge prekida koja radi na 10 000 Hz, ali ...
... nije mogao podržati takvu upotrebu na platformama koje bi zahtijevale širenje operacija na stotine mikrosekundi.Ne znam zašto se čini da se dizajneri hardvera ne trude jako podržati semantiku API-ja "brzo djelovanje u bilo kojem trenutku", ali mnogi koriste model koji sinkronizira pojedine operacije, a ne navodi, pa ako npr.dan je zahtjev za uključivanje uređaja i kôd shvaća da ne mora biti uključen, kôd mora pričekati da se uređaj uključi prije nego što može izdati zahtjev za isključivanje.Glatko rukovanje u API-u dodaje velike komplikacije.
#2
+12
Oldfart
2017-12-24 16:37:34 UTC
view on stackexchange narkive permalink

Koristio sam nekoliko MCU-a od četiri različita proizvođača. Ponovno je glavni posao upoznavanje perifernih uređaja.

Na primjer, sam UART nije previše složen i lako pronalazim priključke za upravljačke programe. No, zadnji put mi je trebao gotovo jedan dan da dođem do satova, I / O pinovi prekidaju, omogućavaju itd. Sređeno.

GPIO može biti vrlo složen. Bit-set, bit-clear, bit-toggle, posebne funkcije omogućavaju / onemogućuju, tri stanja. Dalje ćete dobiti prekide: bilo koji rub, uspon, pad, nizak nivo, visok nivo, samopročišćavanje ili ne.

Zatim tu su I2C, SPI, PWM, mjerači vremena i još dvadesetak vrsta perifernih uređaja svaki sa svojim vlastitim satom i svaki put kad se registri razlikuju s novim bitovima. Svima je potrebno puno sati da čitaju tablicu s podacima kako podesiti koji bit pod kojim okolnostima.

Posljednji je proizvođač imao puno primjera koda koji sam smatrao neupotrebljivim. Sve je bilo apstrahirano. Ali kad sam ga pronašao, kôd je prošao kroz six! razine poziva funkcije kako bi postavio GPIO bit. Lijepo ako imate procesor od 3 GHz, ali ne i na MCU od 48 MHz. Na kraju je moj kôd bio jedan redak:

  GPIO->set_output = bit.
 

Pokušao sam koristiti više generičkih pokretačkih programa, ali odustao sam. Na MCU se uvijek borite s ciklusima prostora i sata. Otkrio sam da je apstrakcijski sloj prvi koji izlazi kroz prozor ako generirate određeni valni oblik u rutini prekida koja se naziva na 10KHz.

Dakle, sada imam sve što radi i ne planiram se ponovo prebaciti, osim iz vrlo, vrlo dobrog razloga.

Sve gore navedeno mora se amortizirati prema tome koliko proizvoda prodate i što uštedite. Prodaja milijuna: ušteda od 0,10 za prelazak na drugu vrstu znači da možete potrošiti 100.000 na radne sate softvera. Prodajući 1000, morate potrošiti samo 100.

Osobno se zato držim asemblera.Divan binarni, bez apstrakcije.
C-ov pretprocesor može prilično dobro raditi sa stvarima, posebno u kombinaciji s __builtin_constant intrinsics.Ako se definiraju konstante za svaki I / O bit oblika (broj porta * 32 + bitni broj), moguće je napisati makronaredbu za `OUTPUT_HI (n)` koja će dobiti kôd ekvivalentan `GPIOD-> bssr | = 0x400; `ako je` n` konstanta poput 0x6A, ali pozovite jednostavnu potprogram ako `n` nije konstantna.To je već rečeno, većina API-ja dobavljača koje sam vidio kreće se između osrednjih i užasnih.
#3
+8
Wouter van Ooijen
2017-12-24 18:40:51 UTC
view on stackexchange narkive permalink

Ovo je više mišljenje / komentar nego odgovor.

Ne želite i ne biste trebali programirati na C. C ++, kada se koristi na ispravan način , daleko je superiorniji. (U redu, moram priznati, kad se koristi na pogrešan način, daleko je gore od C.) To vas ograničava na čipove koji imaju (moderni) C ++ kompajler, što je otprilike sve što GCC podržava, uključujući AVR ( neka ograničenja, filo spominje probleme neujednačenog adresnog prostora), ali isključujući gotovo sve PIC-ove (PIC32 bi mogao biti podržan, ali još nisam vidio nijedan pristojan port).

Kada programirate algoritme na C / C ++, razlika između izbora koje spominjete je mala (osim što će 8 ili 16-bitni čip biti u ozbiljnom nepovoljnom položaju kada radite puno bitne aritmetike od 16, 32 ili više) . Kad vam zatreba zadnja unca izvedbe, vjerojatno ćete trebati koristiti asembler (ili svoj ili kod koji je dostavio dobavljač ili treća strana). U tom slučaju možda biste trebali ponovno razmotriti odabrani čip.

Kada kodirate hardver, možete koristiti neki apstraktni sloj (koji često pruža proizvođač) ili napisati svoj vlastiti (na temelju podatkovne tablice i / ili primjera koda). Postojeće C apstrakcije IME-a (mbed, cmsis, ...) često su funkcionalno (gotovo) ispravne, ali užasno propadaju u performansama (provjerite da li oldfarts ima oko 6 slojeva indirektnosti radi operacije postavljanja pinova), upotrebljivosti i prenosivosti. Žele vam izložiti svu funkcionalnost određenog čipa, koji vam u gotovo svim slučajevima neće trebati, a radije se ne brinete, a zaključava vaš kôd određenom dobavljaču (i vjerojatno tom određenom čip).

To je slučaj da C ++ može puno bolje: kada se pravilno izvede, skup pinova može proći kroz 6 ili više slojeva apstrakcije (jer to omogućava bolje (prijenosno!) sučelje i kraći kôd), ali pruža sučelje koje je nezavisno od cilja za jednostavne slučajeve i i dalje rezultira istim strojnim kodom kao što biste napisali u asembleru .

Isječak stila kodiranja koji koristim, a koji vas može učiniti entuzijazmom ili užasnuto odvratiti:

  // GPIO dio HAL-a za atsam3xa
enum klasa _port {a = 0x400E0E00U,. . . };

template< _port P, uint32_t prikvači >
struct _pin_in_out_base: _pin_in_out_root {

   statička praznina direction_set_direct (pin_direction d) {
      ((d == pin_direction :: input)
         ? ((Pio *) P) ->PIO_ODR: ((Pio *) P) ->PIO_OER) = (0x1U pin <<);
   }

   statička praznina set_direct (bool v) {
      (v? ((Pio *) P) ->PIO_SODR: ((Pio *) P) ->PIO_CODR) = (0x1U << pribadača);
   }
};

// općenito GPIO treba neku funkcionalnost uzorka
template< _port P, uint32_t prikvači >
pomoću _pin_in_out = _box_creator< _pin_in_out_base< P, prikvači > >;

// Arduino Due ima ugrađeni LED i (pretpostavimo) aktivan je nizak
pomoću _led = _pin_in_out< _port :: b, 27 >;
pomoću led = invert< pin_out< _led > >;
 

U stvarnosti postoje još neki slojevi apstrakcije. Ipak, konačna upotreba led-a, recimo da ga uključimo, ne pokazuje složenost ili detalje cilja (za arduin uno ili ST32 plavu pilulu kôd bi bio identičan).

  target :: led :: init ();
cilj :: vodio :: set (1);
 

Prevodnik se ne plaši svim tim slojevima, a budući da nisu uključene virtualne funkcije, optimizator sve vidi (neki detalji su izostavljeni, poput omogućavanja perifernog sata):

  mov.w r2, # 134217728; 0x8000000
 ldr r3, [računalo, # 24]
 str r2, [r3, # 16]
 str r2, [r3, # 48]
 

Što bih i napisao u asembleru - DA sam shvatio da se PIO registri mogu koristiti s pomacima od zajedničke baze. U ovom bih slučaju to vjerojatno i učinio, ali prevoditelj je daleko bolji u optimizaciji takvih stvari nego ja.

Koliko imam odgovor, to je: napišite apstraktni sloj za svoj hardver, ali to učinite u modernom C ++ (koncepti, predlošci) kako to ne bi štetilo vašoj izvedbi. Uz to na mjestu, možete se lako prebaciti na drugi čip. Možete čak započeti razvoj na nekom slučajnom čipu koji imate oko sebe, upoznati ste ga, imati dobre alate za uklanjanje pogrešaka itd. I odgoditi konačni izbor do kasnije (kada budete imali više informacija o potrebnoj memoriji, brzini procesora itd.).

IMO jedna od slabosti ugrađenog razvoja je prvo odabrati čip (pitanje je koje se često postavlja na ovom forumu: za koji čip bih trebao odabrati .... Općenito je najbolji odgovor: nije važno.)

(uredi - odgovor na "Dakle, C ili C ++ bi prema performansama bili na istoj razini?")

Za iste konstrukcije, C i C ++ su isti. C ++ ima puno više konstrukcija za apstrakciju (samo nekoliko: klase, predlošci, constexpr) koji se, kao i bilo koji alat, mogu koristiti u dobru ili u zlu. Da bi rasprave bile zanimljivije: ne slažu se svi što je dobro ili loše ...

Dakle, C ili C ++ bi trebali biti na istoj razini?Mislim da bi C ++ imao više preopterećenja.Definitivno ste me usmjerili u pravom smjeru, C ++ je put kojim treba ići, a ne C.
C ++ predlošci prisiljavaju polimorfizam vremena kompajliranja koji može biti nula (ili čak negativan) trošak u smislu izvedbe, jer se kôd sastavlja za svaki određeni slučaj upotrebe.To se ipak najbolje prilagođava brzini ciljanja (O3 za GCC).Polimorfizam tijekom izvođenja, poput virtualnih funkcija, može pretrpjeti mnogo veću kaznu, iako je sporiji za održavanje i u nekim slučajevima dovoljno dobar.
Tvrdite da je C ++ bolji, ali onda idete i koristite odljevke u stilu C.Od srama.
@JAB Nikad nisam previše volio uloge u novom stilu, ali pokušat ću.Ali moj trenutni prioritet je na ostalim dijelovima ove knjižnice.Pravi je problem naravno što nisam mogao proslijediti pokazivače kao parametre predloška.
@Hans my cto (Compile Time Objects) stil ima prilično uski slučaj (blizak hardveru, poznata situacija tijekom kompajliranja), više je C-ubojica nego zamjena za tradicionalnu uporabu virtualno utemeljenih OO-a.Korisno je dodavanje da odsutnost neizravnosti omogućuje izračunavanje veličine sloga.
#4
+4
Hans
2017-12-24 18:20:51 UTC
view on stackexchange narkive permalink

Ako dobro razumijem, želite znati koje se arhitektonske karakteristike platforme "pojavljuju" u vašem C jezičnom okruženju, čineći izazovnijim pisanje održivog prijenosnog koda na obje platforme.

C je već prilično fleksibilan po tome što je "prijenosni asembler". Sve platforme koje ste odabrali imaju na raspolaganju GCC / komercijalne kompajlere koji imaju podršku za jezične standarde C89 i C99, što znači da možete pokretati sličan kôd na svim platformama.

Postoji nekoliko razmatranja:

  • Neke su arhitekture Von Neumann (ARM, MIPS), druge su Harvard. Glavna ograničenja nastaju kada vaš C program treba čitati podatke s ROM-a, npr. za ispis nizova neka podaci budu definirani kao "const" ili slično.

Neke platforme / kompajleri mogu sakriti ovo "ograničenje" bolje od drugih. Npr. na AVR-u morate koristiti određene makronaredbe za čitanje ROM podataka. Na PIC24 / dsPIC dostupne su i posebne tblrd upute. Međutim, osim toga, neki dijelovi imaju i značajku "vidljivost programskog prostora" (PSVPAG) koja omogućuje mapiranje stranice FLASH-a u RAM, čineći trenutno adresiranje podataka dostupnim bez tblrd-a. Prevoditelj to može učiniti vrlo učinkovito.

ARM i MIPS su Von Neumannovi, tako da memorijska područja za ROM, RAM i periferne uređaje spakirana na 1 sabirnicu. Nećete primijetiti nikakvu razliku između čitanja podataka iz RAM-a ili "ROM-a".

  • Ako zaronite ispod C i pogledate generirane upute za određene operacije, naći ćete neke velike razlike oko U / I. ARM i MIPS su RISC arhitektura registara za učitavanje skladišta. To znači da pristup podacima na memorijskoj sabirnici mora proći kroz MOV upute. To također znači da će svaka modifikacija periferne vrijednosti dovesti do operacije čitanja-izmjene-pisanja (RMW). Postoje neki dijelovi ARM-a koji podržavaju opseg bitova, koji mapiraju set / clr-bitne registre u I / O perifernom prostoru. Međutim, taj pristup morate sami kodirati.

S druge strane, PIC24 omogućuje ALU operacijama čitanje podataka &write izravno putem neizravnog adresiranja (čak i s izmjenama pokazivača ..). Ovo ima neke karakteristike poput arhitekture poput CISC-a, tako da 1 uputa može obaviti više posla. Ovaj dizajn može dovesti do složenijih procesorskih jezgri, nižih taktova, veće potrošnje energije itd. Na vašu sreću taj je dio već dizajniran. ;-)

Te razlike mogu značiti da PIC24 može biti "oštriji" w.r.t. I / O operacije od sličnog takta ARM ili MIPS čipa. Međutim, možda ćete dobiti puno veći ARM / MIPS dio satara za ista ograničenja cijene / paketa / dizajna. Pretpostavljam da se iz praktičnih pojmova mislim da se puno "učenja platforme" shvaća što arhitektura može, a što ne može raditi, koliko će brzo biti nekoliko operacija itd.

  • Periferne jedinice, upravljanje satom itd. razlikuju se ovisno o obitelji dijelova. Strogo govoreći, to će se također promijeniti unutar ARM ekosustava između dobavljača, osim nekoliko perifernih uređaja vezanih uz Cortex m poput NVIC-a i SysTicka.

Te razlike mogu donekle inkapsulirati upravljački programi uređaja, ali na kraju ugrađeni firmver ima visoku razinu sprege s hardverom, tako da prilagođeni rad ponekad nije moguće izbjeći.

Također, ako napuštate ekosustave Microchip-a / bivšeg Atmela, možda ćete otkriti da dijelovi ARM-a zahtijevaju više podešavanja kako bi ih pokrenuli. Mislim u smislu; omogućavanje satova perifernim uređajima, zatim konfiguriranje perifernih uređaja i njihovo "omogućavanje", zasebno postavljanje NVIC-a itd. To je samo dio krivulje učenja. Jednom kad se sjetite učiniti sve ove stvari, u pravom redoslijedu, pisanje upravljačkih programa za sve ove mikrokontrolere u jednom će se trenutku osjećati prilično slično.

  • Također, pokušajte koristiti biblioteke poput stdint.h, stdbool.h itd. ako već niste.Ove cjelobrojne vrste čine širine eksplicitnim, što čini ponašanje koda najpredvidljivijim između platformi.To može značiti upotrebu 32-bitnih cijelih brojeva na 8-bitnom AVR-u;ali ako vaš kod to treba, neka tako bude.
#5
+3
old_timer
2017-12-24 19:13:36 UTC
view on stackexchange narkive permalink

Da i ne. Iz perspektive programera idealno skrivate detalje skupa uputa. Ali to u određenoj mjeri već nije relevantno, periferne jedinice što je cijela poanta pisanja programa nisu dio skupa uputa. Sada istodobno ne možete samo usporediti dijelove bljeskalice od 4096Byte u tim skupovima uputa, posebno ako koristite C, količinu potrošnje bljeskalice / memorije uvelike određuje skup uputa i kompajler, neki nikada ne bi trebali vidjeti kompajler (kašalj PIC kašalj) zbog količine otpada koji se troši tim sastavljanjem. Kod ostalih je potrošnja bljeskova manja. Izvedba je problem i kada se koristi jezik visoke razine i performanse su bitne u MCU aplikacijama, tako da može napraviti razliku između potrošnje 3 USD po ploči za MCU ili 1 USD. Izgradite milijun jedinica koje su vam 2 milijuna dolara jednostavno poklonili u odnosu na nekoliko desetaka do stotinu tisuća dolara u vremenu razvoja softvera.

Ako se radi o olakšavanju programiranja (uz ukupnu cijenu proizvoda), trebali biste moći preuzeti programerski paket za mcu tako da arhitektura skupa uputa bude nešto što nikada ne vidite, pa ako je to vaš primarni zabrinutost, to nije zabrinutost. I dalje vas troši novac što se tiče troškova proizvoda da biste koristili ove knjižnice, ali, vrijeme stavljanja na tržište moglo bi biti kraće, smatram da knjižnicama treba više vremena / posla za korištenje u usporedbi s izravnim razgovorom s perifernim uređajima .

U krajnjoj liniji skupovi uputa najmanje vas brinu, prijeđite na stvarne probleme.



Ova pitanja su automatski prevedena s engleskog jezika.Izvorni sadržaj dostupan je na stackexchange-u, što zahvaljujemo na cc by-sa 3.0 licenci pod kojom se distribuira.
Loading...