2012-11-15 10 views
9

Abbiamo un ARM9 che utilizza il kernel 3.2: tutto sembra funzionare correttamente. Recentemente mi è stato chiesto di aggiungere del codice per aggiungere un impulso da 50 ms su alcune linee GPIO all'avvio. Il codice di impulso va bene; Posso vedere le linee andare su e giù, come previsto. Quello che non funziona come mi aspettavo è la funzione udelay(). Leggere i documenti mi fa pensare che le unità siano in microsecondi, ma come misurato nell'analizzatore logico era troppo corto. Così ho finalmente aggiunto questo codice per ottenere 50ms.Come posso provare __udelay() funziona correttamente sul mio sistema embedded ARM?

// wait 50ms to be sure PCIE reset takes 
for (i=0;i<6100;i++) // measured on logic analyzer - seems wrong to me!! 
{ 
    __udelay(2000); // 2000 is max 
} 

Non mi piace, ma funziona correttamente. Ci sono alcune costanti e istruzioni dispari nel codice udelay. Qualcuno può illuminarmi su come dovrebbe funzionare? Questo codice viene chiamato dopo che tutti gli orologi sono inizializzati, quindi tutto il resto sembra ok.

risposta

4

Secondo Linus in this thread:

Se si tratta di circa l'1% di sconto, che è tutto bene. Se qualcuno ha scelto un valore di ritardo che è così sensibile a piccoli errori nel ritardo che essi notano che - o addirittura si nota qualcosa come il 5% - allora hanno scelto anche di ritardo.

udelay() non è mai stato concepito per essere uno strumento di precisione . Soprattutto con CPU che funzionano a frequenze diverse, abbiamo avuto storicamente delle fluttuazioni piuttosto selvagge. Il tradizionale ciclo occupato di finisce per essere influenzato non solo da interrupt, ma anche da cose come l'allineamento della cache (in precedenza lo avevamo incorporato), e successivamente lo basato su TSC dipendeva ovviamente dal fatto che TSC fosse stabile (che essi erano weren per un po ').

Così storicamente, abbiamo visto udelay() essendo davvero off (cioè il 50% di sconto , ecc), non voglio preoccuparsi di cose nel range 1%.

Linus

Quindi non sara 'perfetto. Sta per essere spento. Da quanto dipende da molti fattori. Invece di utilizzare un ciclo for, prendere in considerazione l'utilizzo di mdelay invece. Potrebbe essere un po 'più preciso. Dal O'Reilly Linux Device Drivers libro:

Il udelay chiamata dovrebbe essere chiamato solo per intervalli di tempo brevi, perché la precisione di loops_per_second è solo otto bit, e notevoli errori si accumulano nel calcolo lunghi ritardi. Anche se il ritardo massimo consentito è quasi un secondo (dal momento del calcolo overflow per ritardi prolungati), il valore massimo suggerito per udelay è 1000 microsecondi (un millisecondo). La funzione mdelay aiuta nei casi in cui il ritardo deve essere superiore a un millisecondo.

E 'anche importante ricordare che udelay è una funzione busy-attesa (e quindi mdelay è troppo); altre attività non possono essere eseguite durante l'intervallo di tempo .È quindi necessario prestare particolare attenzione, in particolare con mdelay e evitare di utilizzarlo a meno che non ci sia un altro modo per raggiungere il proprio obiettivo.

Attualmente, il supporto per ritardi superiori a pochi microsecondi e inferiore a una tacca del timer è molto inefficiente. Questo di solito non è un problema , perché i ritardi devono essere sufficienti per essere notati dagli umani o dall'hardware. Un centesimo di secondo è un'adeguata precisione per gli intervalli di tempo relativi all'uomo, mentre un millisecondo è un ritardo di abbastanza lungo per le attività hardware.

In particolare la linea "il valore massimo consigliato per udelay è di 1000 microsecondi (un millisecondo)" attacca fuori di me dal momento che si affermi che il 2000 è il massimo. Da this document sui ritardi inserimento:

mdelay è macro wrapper udelay, per tenere conto di possibili trabocco quando passa grandi argomenti per udelay

Quindi è possibile si sta eseguendo in un errore di overflow. Anche se normalmente non considererei il 2000 un "grande argomento".

Ma se hai bisogno di precisione nei tempi, dovrai gestire l'offset come hai fatto, rollare il tuo o usare un kernel diverso. Per informazioni su come eseguire il rollout della propria funzione di delay usando l'assembler o usando kernel hard real time, vedere questo articolo allo High-resolution timing.

Consulta anche: Linux Kernel: udelay() returns too early?

+0

Il kernel moderni hrtimer_nanosleep dovrebbe essere abbastanza portatile. – cdleonard

+0

@cdleonard 'nanosleep' può usare' udelay' nel suo loop occupato a seconda della modalità scheduler e della lunghezza del delay. Vedi il link che ho fornito su [Tempi ad alta risoluzione] (http://tldp.org/HOWTO/IO-Port-Programming-4.html). –

+0

@ embedded.kyle - apprezzo molto la tua risposta, ma non sono soddisfatto. mdelay chiama appena udelay in un ciclo for, quindi non è di aiuto. E la quantità di udelay è disattivata non è affatto vicina all'1% o al 50% - Mi aspettavo un contatore di cicli di 25 se tutto funzionava perfettamente (2ms ogni loop). Se fosse il 50% di sconto - il mio ciclo sarebbe 50. Ma 6100? È così lontano, mi dà fastidio molto. Grazie ancora, ma sono ancora in missione :) – Jeff

Problemi correlati