Risorse sulla scrittura di codice C efficiente per microcontrollori? [chiuso]

15

Serve aiuto serio qui. Amo la programmazione. Ultimamente ho letto un sacco di libri (come K & R) e articoli / forum online per linguaggio C. Ho anche provato a leggere il codice di Linux (anche se ero perso da dove cominciare, ma è stato utile dare una sbirciatina alle piccole librerie?).

Ho iniziato come programmatore Java e in Java è molto carino e asciutto; Se i programmi diventano troppo grandi, suddividili in classi e poi in funzioni. Linee guida come, mantenere il codice leggibile e aggiungere commenti. Utilizzare le tecniche di occultamento delle informazioni e OOP. Alcuni dei quali sono validi anche per C.

Sono stato codificato in C ora e finora ho i programmi per funzionare in un modo o nell'altro. Molte persone parlano di prestazioni / efficienza, algoritmo / design, ottimizzazione e manutenibilità. Alcune persone sottolineano l'una più dell'altra, ma per gli ingegneri del software non professionisti spesso si sente qualcosa di simile ad esempio: Linux dev dev non prenderà alcun codice.

La mia domanda è questa: ho in programma di scrivere un codice per microcontrollore a 8 bit senza sprecare risorse . Sappi che sto venendo da sfondo java quindi le cose non sono più le stesse ... risorse / libri / link / suggerimenti saranno molto apprezzati. Le prestazioni e le dimensioni ora contano. Risorse / Trucchi sul codice C efficiente (tra le migliori pratiche) per i microcontrollori a 8 bit?

Inoltre, inline assembly svolge un ruolo vitale e si avvicina allo standard dei microcontroller. Ma c'è una regola generale per l'efficienza che si applica a tutti?

Ad esempio: register unsigned int variable_name; è preferito su char in qualsiasi momento. O utilizzando uint8_t se non hai bisogno di grandi numeri.

EDIT: Grazie mille per tutte le risposte e i suggerimenti. Apprezzo lo sforzo di tutti per la condivisione della conoscenza.

    
posta AceofSpades 01.01.2012 - 02:39
fonte

5 risposte

33

Ho oltre 20 anni di sistemi embedded, per lo più 8 e 16 micros. La risposta breve alla tua domanda è la stessa di qualsiasi altro sviluppo di software: non ottimizzare fino a quando non ne hai la necessità, e quindi non ottimizzare fino a quando non sai cosa devi ottimizzare. Scrivi il tuo codice in modo che sia affidabile, leggibile e mantenibile prima. L'ottimizzazione prematura è tanto, se non più, un problema nei sistemi embedded

Quando programmi "senza sprecare risorse", consideri il tuo tempo una risorsa? Altrimenti, chi ti sta pagando per il tuo tempo e se nessuno, hai qualcosa di meglio da fare con esso. Una volta scelta, qualsiasi progettista di sistemi embedded deve fare il costo dell'hardware rispetto al tempo di ingegnerizzazione. Se spedirai 100 unità, usa un micro più grande, a 100.000 unità, un risparmio di $ 1,00 per unità equivale a 1 anno di sviluppo software (ignorando il time to market, costo opportunità ecc.), A 1 milione di unità, si inizia ottenere ROI per essere ossessivi sull'utilizzo delle risorse, ma attenzione perché molti progetti incorporati non hanno mai ottenuto il marchio da 1 milione perché hanno progettato di vendere 1 milione (alto investimento iniziale con un basso costo di produzione) e sono falliti prima che arrivassero.

Detto questo, è necessario prendere in considerazione e conoscere i sistemi (piccoli) incorporati, perché ciò impedirà il funzionamento, in modi imprevisti, e non solo di rallentare.

a) Stack: di solito hai solo una piccola pila e dimensioni di frame dello stack spesso limitate. È necessario essere consapevoli di ciò che l'utilizzo dello stack è in ogni momento. Attenzione, i problemi di stack causano alcuni dei difetti più insidiosi.

b) Heap - di nuovo, dimensioni di heap ridotte, quindi fai attenzione all'assegnazione di memoria ingiustificata. La frammentazione diventa un problema. Con questi due, è necessario sapere cosa si fa quando si esaurisce - non accade su un sistema di grandi dimensioni a causa dell'OS paging fornito. Ad esempio, quando malloc restituisce NULL, lo controlli e cosa fai. Ogni malva ha bisogno di un assegno e un gestore, codice gonfio? Come guida: non usarlo se c'è un'alternativa. La maggior parte dei sistemi di piccole dimensioni non utilizza la memoria dinamica per questi motivi.

c) Interruzioni hardware - È necessario sapere come gestirle in modo sicuro e tempestivo. È inoltre necessario sapere come rendere sicuro il codice di rientro. Ad esempio, le librerie standard di C non sono generalmente rientranti, quindi non dovrebbero essere usate all'interno di gestori di interrupt.

d) Assemblaggio - ottimizzazione quasi sempre prematura. Al massimo una piccola quantità (in linea) è necessaria per ottenere qualcosa che C non può proprio fare. Come esercizio, scrivi un piccolo metodo nell'assemblaggio manuale (da zero). Fai lo stesso in C. Misura le prestazioni. Scommetto che il C sarà più veloce, so che sarà più leggibile, manutenibile ed estensibile. Ora per la parte 2 dell'esercizio - scrivi un programma utile in assembly e C.
Come un altro esercizio, dai un'occhiata a quanta parte del kernel di Linux è un assemblatore, quindi leggi il paragrafo seguente sul kernel di Linux.

Vale la pena sapere come farlo, potrebbe anche valere la pena conoscere le lingue per uno o due micros comuni.

e) "register unsigned int variable_name", "register" è, ed è sempre stato, un suggerimento per il compilatore, non un'istruzione, nei primi anni '70 (40 anni fa), aveva senso. Nel 2012, è uno spreco di sequenze di tasti poiché i compilatori sono così intelligenti e le istruzioni del micros sono così complesse.

Torna al tuo commento di linux - il problema che hai qui è che non stiamo parlando di un solo milione di unità, stiamo parlando di centinaia di milioni, con una vita di sempre. Vale la pena dedicare tempo e costi tecnici per ottenerla nel modo migliore possibile. Sebbene sia un buon esempio della migliore pratica ingegneristica, sarebbe un suicidio commerciale per la maggior parte degli sviluppatori di sistemi embedded essere pedante come richiede il linux kernal.

    
risposta data 01.01.2012 - 03:15
fonte
3

La tua domanda ("senza sprecare risorse") è troppo generica, quindi è difficile dare molti consigli. Presa alla lettera, se non vuoi sprecare risorse, forse dovresti fare un passo indietro e valutare se hai bisogno di fare qualsiasi cosa, cioè se puoi risolvere il problema in altri modi.

Inoltre, consigli utili dipendono molto dai tuoi vincoli: che tipo di sistema stai costruendo e che tipo di CPU stai usando? È un sistema duro in tempo reale? Quanta memoria hai per codice e dati? Supporta nativamente tutte le operazioni C (in particolare, la moltiplicazione e la divisione) e per quali tipi? Più in generale: leggi l'intero foglio dati e capiscilo .

Il consiglio più importante: mantieni la semplicità.

E.g .: dimentica strutture dati complesse (hash, alberi, eventualmente anche liste concatenate) e usa matrici di dimensioni fisse. L'uso di strutture di dati più complicate è garantito solo dopo aver verificato con misure che gli array sono troppo lenti.

Inoltre, non sovradimensionare (qualcosa che gli sviluppatori di Java / C # hanno una tendenza a fare): scrivi un codice procedurale semplice, senza troppo stratagemma. L'astrazione ha un costo!

Mettiti a tuo agio con l'idea di utilizzare variabili globali e goto [molto utile per le pulizie in assenza di eccezioni];)

Se hai a che fare con gli interrupt, leggi di reentrancy. Scrivere codice di rientro è molto semplice.

    
risposta data 01.01.2012 - 16:59
fonte
3

Sono d'accordo con la risposta di mattnz - per la maggior parte. Ho iniziato a programmare sull'8085 oltre 30 anni fa, poi sulla Z80, poi sono rapidamente migrato all'8031. Successivamente sono passato ai microcontrollori della serie 68300, quindi 80x86, XScale, MXL e (a partire da tardi) 8 bit PICS, che ho Indovina significa che sono tornato al punto di partenza. Per la cronaca, posso affermare che i FAE in diversi importanti produttori di microprocessori usano ancora l'assemblatore, anche se in modo orientato agli oggetti per riutilizzare il codice in modo mirato.

Quello che non vedo nella risposta approvata è una discussione sul tipo di processore di destinazione e / o sull'architettura proposta. E 'un bitter da $ 0,50 con una memoria limitata? È un core ARM9 con pipelining e 8Meg di flash? Gestore della gestione della memoria? Avrà un sistema operativo? Un ciclo while (1)? Un dispositivo consumer con una tiratura iniziale di 100000 unità? Una start up con grandi idee e sogni?

Anche se sono d'accordo sul fatto che i compilatori moderni fanno un ottimo lavoro di ottimizzazione, non ho mai lavorato a un progetto in 30 anni in cui non ho fermato il debugger e ho visto il codice assembly generato per vedere cosa stava succedendo sotto il cappuccio (certamente un incubo quando il pipeline e l'ottimizzazione entrano in gioco), quindi la conoscenza dell'assemblaggio è importante.

E non ho mai avuto CEO, VP of Engineering, cliente che non mi ha spinto a provare a riempire un gallone in un contenitore di quart oa risparmiare 0,05 $ usando una soluzione software per risolvere un problema hardware (hey è solo software giusto? Che cosa è così difficile?). L'ottimizzazione della memoria (codice o dati) conterà sempre.

Il mio punto è che se si guarda il progetto da un punto di vista puramente di programmazione, si otterrà una soluzione più ristretta. Mattnz ha ragione: fallo funzionare, quindi fallo funzionare più velocemente, più in piccolo, meglio, ma devi comunque spendere MOLTO tempo nei requisiti e nei risultati finali prima ancora di pensare alla codifica.

    
risposta data 02.01.2012 - 03:22
fonte
1

La risposta di Manttz mette molto bene i punti chiave su come eseguire la programmazione "close-to-hardware". Questo è dopotutto ciò che C è destinato.

Tuttavia, vorrei aggiungere che mentre la parola chiave strict di "Class" non esiste in C - è abbastanza semplice per pensare in termini di programmazione Object oriented in C anche quando si sono vicini all'hardware.

Puoi prendere in considerazione questa risposta: Migliori pratiche OO per C programmi che spiega questo punto.

Ecco alcune risorse che ti aiuteranno a scrivere un buon codice orientato agli oggetti in C.

a. Programmazione orientata agli oggetti in C
b. questo è un buon posto dove persone si scambiano idee
c. ed ecco il libro completo

Un'altra buona risorsa che vorrei suggerirti è:

The Write Great Code Series . Questo è un libro di due volumi. Il primo libro tratta aspetti essenziali delle macchine nelle opere di livello inferiore. Il secondo libro riguarda "Pensare a basso livello - scrivere ad alto livello"

    
risposta data 01.01.2012 - 18:20
fonte
1

Hai qualche problema. prima vuoi che questo progetto / codice sia portatile? La portabilità ti costa prestazioni e dimensioni, la tua piattaforma di scelta e il compito che stai implementando possono tollerare le dimensioni in eccesso e le prestazioni inferiori?

Sì, assolutamente su una macchina a 8 bit che restituisce caratteri non firmati invece di interi o cortocircuiti non firmati, è un modo per migliorare le prestazioni e le dimensioni. Allo stesso modo su una macchina a 16 bit utilizzare cortocircuiti senza segno e una macchina a 32 bit senza segno. Si può tranquillamente vedere che se per esempio si usano solo input non firmati ovunque per la portabilità del sistema che sta prendendo il sopravvento (ARM, ad esempio, spingendo verso il basso nella potenza più bassa, mercati di dispositivi più piccoli) quel codice è un enorme rom su un micro 8 bit. Ovviamente potresti semplicemente usare unsigned senza int o short o char e lasciare che il compilatore scelga la dimensione ottimale.

Non solo assemblaggio in linea, ma linguaggio di assemblaggio in generale. L'assemblaggio in linea è molto non portatile e più difficile da scrivere rispetto alla sola chiamata a una funzione asm. Sì, masterizzi l'impostazione e il ritorno delle chiamate, ma a un costo di sviluppo, manutenzione e controllo migliori. La regola si applica ancora, scrivila solo su asm, se proprio ne hai bisogno, hai fatto il lavoro per concludere che l'output del compilatore è il tuo problema in quest'area e quanta parte di un guadagno di prestazioni puoi ottenere eseguendolo a mano. Quindi torna alla portabilità e alla manutenzione, non appena inizi a mischiare C e ASM e ogni volta che mischi C e ASM potresti danneggiare la tua portabilità e rendere il progetto meno manutenibile a seconda di chi ci sta lavorando o se questo è un prodotto che stai sviluppando ora e qualcun altro deve mantenere la strada. Una volta che hai fatto quell'analisi, sai automaticamente se devi andare in linea o andare con assemblaggio diretto. Ho più di 25 anni nel campo, scrivo miscele C e asm ogni giorno, vivo su / a livello hardware / software e, beh, non uso mai in linea asm. Raramente vale la pena, anche compilatore specifico, scrivo codice non specifico del compilatore ovunque possibile (quasi ovunque).

La chiave per tutta la tua domanda è di smontare il tuo codice C. Scopri cosa fa il compilatore con il tuo codice e con il tempo, se lo desideri, puoi imparare a manipolare il compilatore per generare il codice che desideri senza dover ricorrere a asm tanto. Con più tempo puoi imparare a manipolare il compilatore per produrre codice efficiente su più target rendendo il codice più portabile senza dover ricorrere a asm. Non dovresti avere problemi a capire perché il char non firmato funzioni meglio come ritorno di stato da una funzione piuttosto che come unsigned in su un micro 8 bit, allo stesso modo il char senza segno diventa più costoso su sistemi a 16 e 32 bit (alcune architetture ti aiutano fuori, alcuni non). Il sistema di compilazione llvm rende questa formazione più rapida in quanto è possibile compilare un programma e quindi esaminare l'output back-end per diversi target senza dover avere più cross compilers diversi (come con gcc).

Alcuni microcontrollori a 8 bit, tutti ?, sono molto ostili al compilatore e nessun compilatore produce un buon codice. Non c'è abbastanza richiesta per creare un mercato di compilazione per quei dispositivi per creare un grande compilatore per quei target, quindi i compilatori che sono lì sono lì per attirare più programmatori business, non asm, e non perché il compilatore è meglio che scritto a mano asm . arm e mips che entrano in questo mondo stanno cambiando quel modello poichè hai obiettivi che hanno compilatori che hanno lavorato molto su di loro, compilatori che producono codice abbastanza buono, ecc. Per i micros con processori come quelli che ovviamente hai ancora il caso in cui devi scendere fino a asm, ma non è così frequente, è molto più semplice dire al compilatore cosa vuoi che non utilizzarlo. Si noti che manipolare un compilatore non è un codice brutto e illeggibile, anzi è il contrario, un codice pulito, semplice, magari riordinando alcuni elementi. Controllando la dimensione delle tue funzioni e il numero di parametri, quel genere di cose fa un'enorme differenza nell'output del compilatore. Evita le caratteristiche del ghie-whiz del compilatore o del linguaggio, KISS, mantienilo semplice, spesso produce codice molto migliore e più veloce.

    
risposta data 14.01.2012 - 17:50
fonte

Leggi altre domande sui tag