Ik ben vrij nieuw met het programmeren. De enige ervaring hiervoor was gevorderde PHP5, en een klein beetje Basic 15 jaar terug.
Ik ben nu bezig met het leren om een PIC te programmeren en wil dit in Assembler doen. Heb veel tutorials doorgelezen en ben op het punt van interupts aangekomen. De opgedane kennis wil ik nu toepassen, maar kom daarbij met een paar vragen.
Hoe kan ik het beste een delay routine samenstellen, die op meerdere snelheden etc toch de gewenste ms of seconde vertraging oplevert?
Hoe kan ik zorgen dat een interupt niet de delay routine verstoord? Dus als ik een seconde wil wachten, een interupt doet zich voor en wordt uitgevoerd en dat ie daarna weer verder gaat in de delay. Hier wil ik dus kunnen bepalen of hij opnieuw moet beginnen met de delay, af moet breken of mss toch wel verder gaan.
Bedankt vast voor de moeite.
Als je de delay laat bepalen door een interupt krijg je altijd dezelfde tijd tussen interupts.
Een interrupt heef nu eenmaal voorrang op de rest van het programma.
Je zou een delay bit kunnen zetten.
deze lezen als je in interrupt gaat.
dan bepalen hoeveel de delay ingekort moet zijn voordat je terugkeer vanuit de interrupt.
Houd er rekening mee dat je waarde van de delay ook in W kan staan en dus terug gezet word als je uit de interrupt komt.
De meeste PICs hebben 1 of meer hardware timers aan boord. Als je dus een hardware timer (plus eventueel een interrupt routine) gebruikt krijg je altijd de goede vertraging.
Op 3 augustus 2007 23:57:40 schreef JoWi:
De meeste PICs hebben 1 of meer hardware timers aan boord. Als je dus een hardware timer (plus eventueel een interrupt routine) gebruikt krijg je altijd de goede vertraging.
Ik ben van plan om de 16F628A te gaan gebruiken. Zou je eventueel een voorbeeld kunnen geven hoe ik dat zou moeten doen?
Het opslaan en het terugzetten van het werkregister doe ik automatisch aan het begin van de interupt routine. Zou het zetten van een delay-bit niet zorgen voor een extra instructie tijdens de delay?
Even een ander vraagje mbt delay routines. Ik ben flink aan het rekenen geweest en ben voor een redelijke delay routine hierop uitgekomen:
DELAY:
MOVWF ms
RLF ms, F ; ms mag niet groter dan 127 zijn
d1 MOVLW d'70'
MOVWF x
d2 MOVLW d'1'
MOVWF y
d3 DECFSZ y, F
GOTO d3
DECFSZ x, F
GOTO d2
DECFSZ ms, F
GOTO d1
RETURN
Dit geeft een delay van precies 1ms bij gebruik van een 4MHz kristal met een overhead van 3 instructies. Het probleem hier is alleen dat deze routine geen grotere delays aankan dan 127 ms omdat het anders in de carry/borrow overloopt (dit vanwege de bitshift aan het begin).
Is er een manier om hier omheen te werken door bijv. de borrow te kunnen gebruiken in de delay zonder extra overhead te krijgen?
picsels
Volgend project: 7-seg gaming displays; dan dec-1982 labvoeding afmaken, dan funcgen met ad9833 afmaken, dan...
Je kunt toch ook zoiets doen:
; DELAY_1s: wacht 1 seconde
DELAY_1S:
MOVLW d'10' ; wacht 10x 100ms
MOVWF sec
delay00 MOVLW d'100' ; wacht 100ms
CALL DELAY
DECFSZ sec
GOTO delay00
RETURN
(Misschien nog wat corrigeren voor de overhead in het loopje.)
[Bericht gewijzigd door picsels op (44%)]
Ik heb iets dergelijks al geprobeerd, maar daardoor krijg ik in de beste berekening dus een overhead van 7x delay00 + 1. Enkele keren delay aanroepen met een waarde van 125 werkt wat dat betreft dus wel wat beter, maar een betrouwbare tijdsmeting kan ik er niet mee maken. Hoe hebben andere dit opgelost? Bijv. een klok die altijd precies loopt?
Bij een klok zoals ik die eens ontworpen en gebouwd heb deed ik in eerste instantie de seconden via een interrupt.
Toetsen en refresh van de dot display deed ik in normale code.
Door slim aanroepen wil dat prima.
in een tweede versie deed ik het met een bressenham algoritme in een timer interrupt.
Ook hier de display refresh, color/pwm , en toetsen in gewone runtime.
Maar een delay kun je dan niet gebruiken lijkt me, of zie ik dat verkeerd?
Is het mss mogelijk dat ik TMR0 ook als instructieteller laat optreden? Hiermee kan zou tussen je normale instructies zo nu en dan de teller laten uitlezen, en dan de beginstand vanaf laten trekken om zo het aantal nS te bepalen dat er voorbij gegaan zijn. Dit kan ik dan in het main programma zetten en dat als er een interupt plaats heeft dat deze gewoon uitgevoerd kan worden zonder in de weg te komen van de teller
Of denk ik nu compleet verkeerd?
Sponcebert
Ik ben er klaar mee voor
Je timer interrupt geeft een constante periode, je moet deze gewoon tellen en hieruit de de tijd bepalen in tikken.
Buiten je timerinterrupt neem je een "snapshot" van je tikken en rekent deze om in een tijd. Zo gebeurt dit al jaren in je PC !
Enige wat je niet wil, is dat je interrupt langer duurt dan je timer interval, want dan mis je een tik.
Ik had de datasheet nog eens doorgenomen. Ik kwam toen met het idee om de timer te prescalen met 1:8 om zo te zorgen dat elke 125 timer counts voor 1 ms zouden staan. Dan te zorgen voor een timer-overflow-interupt die zou dan bij 255 zijn waarbij ik de timer met 2 verhoog en een bufferbit die elke 25 keer daarna de timer met nog eens 1 zou verhogen. Is dat een goede manier?
Sponcebert
Ik ben er klaar mee voor
Handig is ook om af te tellen. Stel dat 1 ms 235 klokpulsen is, neem je een register, zet daar 235 in, tel af tot 0 in elke interrupt, en bij nul zet je er weer 235 in en verhoogt de secondes.
Over het algemeen is het het makkelijkst om de "timerinterupt" gewoon ongestoordt te laten lopen.
Om een redelijk precieze telling te krijgen zal ik dan dus beter de prescaler 1:4 instellen zodat ik per overflow vrijwel precies 1 ms verder ben. Dan houd ik 20 instructies per overflow over die ik dus zal moeten onthouden in een aparte bit.
Ik heb inmiddels ook al redelijk veel gelezen over de TMR0 en ben ergens tegen gekomen dat op het moment dat de interupt optreed de TMR0 niet verder telt totdat deze weer uit de interupt routine is gekomen. Klopt dat?
Dan kom ik ook weer bij iets anders, waar ik niet uitkom bij de verschillende topics.
Ik wil gebruik maken van verschillende interupts. Zo wil ik RB1 en RB2 bijv. gebruiken voor ontvangen van data, welke getriggerd wordt door RB0 (aanmelden van de data zo te spreken). Hoe kan ik het onderscheid maken tussen de verschillende interupts (dus is het een TMR0 interupt geweest of een RB0 interupt), waarbij ik niet het risico loop om data mis te lopen.
Sponcebert
Ik ben er klaar mee voor
PIC ken ik niet zo goed, maar meestal spring je bij een specieke interupt naar een specifiek adres in je geheugen ( interupt vector ), anders zul je dat moeten checken.
Ik zie in de datasheet niets over het stoppen van de timer. Wel moet je de interupt vlag weer wissen anders krijg je geen nieuwe interrupt. Maar de timer zal wel doorlopen.
Ok.
Het adres voor een interupt is op 0x04, maar dat is onafhankelijk van de interupt. Zowel de TMR0 interupt, RB0 interupt en de RB-on-change interupt verwijzen naar dit adres. Kan er dus niet echt wijs uit worden hoe je dan de TMR0 interupt kan herkennen (RB0 en RB-on-change, van poorten RB1t/m4 kan je pollen) uit de andere interupts. Ook zie ik geen andere topics waar ik wijs uit wordt.
Een PIC heeft geen vectored interrupts.
In de interrupt routine moet je uitzoeken wat de bron is door de vlaggen af te zoeken. Deze staan onder andere in INTCON, T0IF is voor de timer0 int, INTF voor de RB0/INT lijn etc.
Wel het vlaggetje clearen anders komt ie gelijk weer terug in de interrupt routine. En ja: er kunnen meerdere interrupt tegelijk optreden. De meest belangrijke moet je dan eerst verwerken.
Ah ok. Dan zal ik dus de vlaggen controleren.
Ik zat zelf te denken aan een soort niveau gebasseerde afhandeling. Waarbij niveau 1 tm 4 in het main programma afgehandeld zullen worden dmv een stack (fifo). Niveau 5 wordt dan getriggerd door RB0 interupt wat dus data afhandeling betekend en in de interupt zelf zal worden gedaan. Is dat een goede manier om dit te programmeren?