2011-01-20 2 views
2

J'ai besoin d'une fonction de temporisation précise écrite en C qui retarde l'exécution du programme pic d'un nombre donné de microsecondes. J'ai trouvé un exemple sur microchipc.com qui utilise ASM, mais le code ne permet que des vitesses d'horloge allant jusqu'à 32000000. Ma vitesse d'horloge doit être 64000000, mais comme je ne comprends pas comment le code fonctionne, je ne peux pas modifier pour faire ce dont j'ai besoin. Quelqu'un peut-il offrir une explication du code ou suggérer comment implémenter quelque chose de similaire?Retard de x microsecondes en C pour pic18f

#if PIC_CLK == 4000000 
    #define DelayDivisor 4 
    #define WaitFor1Us asm("nop") 
    #define Jumpback asm("goto $ - 4") 
#elif PIC_CLK == 8000000 
    #define DelayDivisor 2 
    #define WaitFor1Us asm("nop") 
    #define Jumpback asm("goto $ - 4") 
#elif PIC_CLK == 16000000 
    #define DelayDivisor 1 
    #define WaitFor1Us asm("nop") 
    #define Jumpback asm("goto $ - 4") 
#elif PIC_CLK == 20000000 
    #define DelayDivisor 1 
    #define WaitFor1Us asm("nop"); asm("nop") 
    #define Jumpback asm("goto $ - 6") 
#elif PIC_CLK == 32000000 
    #define DelayDivisor 1 
    #define WaitFor1Us asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop") 
    #define Jumpback asm("goto $ - 12") 
#else 
#error delay.h - please define PIC_CLK correctly 
#endif 

#define DelayUs(x) { \ 
delayus_variable=(unsigned char)(x/DelayDivisor); \ 
asm("movlb (_delayus_variable) >> 8"); \ 
WaitFor1Us; } \ 
asm("decfsz (_delayus_variable)&0ffh,f"); \ 
Jumpback; 

Répondre

6

Il me semble de ce segment:

#elif PIC_CLK == 16000000 
    #define DelayDivisor 1 
    #define WaitFor1Us asm("nop") 
    #define Jumpback asm("goto $ - 4") 
#elif PIC_CLK == 20000000 
    #define DelayDivisor 1 
    #define WaitFor1Us asm("nop"); asm("nop") 
    #define Jumpback asm("goto $ - 6") 
#elif PIC_CLK == 32000000 
    #define DelayDivisor 1 
    #define WaitFor1Us asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop") 
    #define Jumpback asm("goto $ - 12") 

que pour chaque 4 millions supplémentaires en augmentation PIC_CLK, vous avez besoin d'une autre instruction nop.

Je n'ai pas utilisé les précédents car ils utilisent simplement une fonction de mise à l'échelle à des vitesses d'horloge inférieures - puisque vous ne pouvez pas exécuter la moitié d'un nop, ils réduisent simplement le nombre de boucles à la moitié ou au quart. plein nop que de nombreuses fois. Donc, pour 64 millions (dont 32 millions de plus que le dernier), vous auriez besoin de huit autres instructions nop (32 millions divisé par 4 millions) et, puisque chacun augmente la taille de saut de 2 (le PIC18F ayant un 2 largeur d'instruction -Byte), vous devriez pouvoir utiliser les éléments suivants:

#elif PIC_CLK == 32000000 
    #define DelayDivisor 1 
    #define WaitFor1Us asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop") 
    #define Jumpback asm("goto $ - 12") 
#elif PIC_CLK == 64000000 
    #define DelayDivisor 1 
    #define WaitFor1Us asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop") \ 
        asm("nop"); asm("nop"); asm("nop"); asm("nop"); \ 
        asm("nop"); asm("nop"); asm("nop"); asm("nop"); 
    #define Jumpback asm("goto $ - 28") 
#else 
#error delay.h - please define PIC_CLK correctly 
#endif 

en résumé, ce sont les valeurs dont vous avez besoin pour chaque valeur PIC_CLK, sur hasard que la prochaine génération sera encore plus rapide:

PIC_CLK Divisor NOP count Jump size 
--------- ------- --------- --------- 
    1000000  16   1   4 
    2000000  8   1   4 
    4000000  4   1   4 
    8000000  2   1   4 
16000000  1   1   4 
20000000  1   2   6 
24000000  1   3   8 
28000000  1   4   10 
32000000  1   5   12 
64000000  1   13   28 
96000000  1   21   44 
128000000  1   29   60 

Ou, si vous voulez t les formules pour les valeurs supérieures ou égales à 16 millions:

divisor = 1 
nopcount = picclk/4000000 - 3 
jumpsize = nopcount * 2 + 2 
+0

Merci, je pense que cela va fonctionner, mais puisque j'utilise un compilateur différent, je dois maintenant changer comment tout l'assemblage est appelé ... – Huggzorx

1

Le code des boucles simplement sur un ensemble de nop -Instructions pour un certain laps de temps. L'instruction movlb est utilisée pour charger le BSR (seulement un registre de 8 bits, donc le décalage). L'instruction decfsz est ensuite utilisée pour décrémenter le compteur de boucle et ignorer l'instruction suivante si le résultat est zéro pour sortir de la boucle. Si l'instruction suivante n'est pas ignorée, l'instruction Jumpback est appelée (goto) qui revient en haut de la boucle. Puisque chaque instruction sur un 18F est de deux octets de large (les instructions de double mot sont de quatre octets), vous devez sauter 12 lignes en arrière pour la version 32MHz (5 nop s et decfsz).

Maintenant, vous pouvez suivre les conseils de paxdiablo et faire une nouvelle version avec plus de nop s, mais cela prendrait de l'espace inutile si vous ne courez que @ 64MHz de toute façon. Je pense que vous pourriez faire quelque chose comme

#if PIC_CLK == 64000000 
    #define WaitFor1NOP asm("nop") 
    #define Jumpback asm("goto $ - 4") 
#else 
#error delay.h - please define PIC_CLK correctly 
#endif 

#define DelayUs(x) { \ 
delayus_variable=(unsigned char)(x*SOME_NUMBER); \ 
asm("movlb (_delayus_variable) >> 8"); \ 
WaitFor1NOP; } \ 
asm("decfsz (_delayus_variable)&0ffh,f"); \ 
Jumpback; 

Ici some_number est le nombre de nop s vous devez boucle sur pour atteindre @ 64MHz 1 us, 13 selon un excellent calcul de paxdiablo.

EDIT:

paxdiablo porté à mon attention que cette solution limite la portée des temps de retard plus que le sien, puisque le plus grand nombre, vous pouvez passer à la macro est 1/13 ce qui se passe dans un unsigned char . Un char non signé est 8 bits, ce qui nous laisse avec 255/13 = 19. Je ne sais pas si c'est trop petit pour vous.Vous pouvez contourner ce problème en appelant la macro de délai plusieurs fois, peut-être même en créant une nouvelle macro pour le faire pour vous.

+0

Ce n'est peut-être pas une mauvaise idée. Les ifdef eux-mêmes ne font plus de code puisqu'ils ne sont pas compilés. Vous avez raison de dire que votre solution utilise 24 octets de moins que la mienne mais vous devez faire attention car le prix que vous payez est réduit. Puisque vous multipliez usecs par SOME_NUMBER (qui sera au passage de 13), la limite supérieure que vous pouvez passer est restreinte en conséquence (au 1/13ème de la solution extra-24-byte). Dommage que 13 soit premier, on aurait pu compromettre un peu :-) – paxdiablo

+0

Je ne parlais pas de l'ifdefs, je les ai juste laissés de côté car ils ne seront pas très utiles quand même s'il ne tourne qu'à 64MHz. Vous soulevez un bon point avec la limitation de temps, cependant. Je vais modifier ma réponse. – Oystein

+0

Je vais vous donner un +1 pour cela, car il se peut que la mémoire soit le facteur le plus limitant. Je n'ai aucune idée de l'étroitesse de la mémoire sur le système OP mais je me souviens de mes jours de travail sur les puces 8051 et 1802A et c'était très serré, donc ça peut être très important. – paxdiablo