Mal wieder meine Haß-Liebe mit und zu Interrupts: ein PIC18F14K22 soll in seiner Counter-Funktion genutzt werden, wozu ich zwecks der 16-bit den Timer1 ausgewählt hatte. Das Prog ist erfrischend kurz: der PIC bewegt sich gemütlich (31 kHz) in einer Nichts-Tun-Schleife und soll durch den Ablauf des Timers1 in die Irq-Routine springen, in welcher er eigentlich auch nur - zunächst aus Testgründen - eine LED toggeln soll. Das im Kreis-Laufen funktioniert, aber der Irq klemmt wieder, der PIC kommt nicht in seine Service-Routine. Der PIC zählt an seinem Port A5 einen externen Takt. Der wird abgeleitet aus der Netzfrequenz. Über einen Optokoppler wird je nur die positive Halbwelle genutzt, was zu sehr sauberem Rombus an a5 führt. Der Counter1 wird mit seinem Maximalwert minus 50 befüllt, will sagen, der müsste noch genau 50 positive Schwinger abzählen - was einer Sekunde entsprechen sollte - und dann überlaufen. Und Interrupten. Aber da hat der andere Vorstellungen.
Der Umgang mit Timern ist mir nur rudimentär geläufig, bei einem Timer0 ohne Irq klappte das mal. Wenn ich das Datenblatt richtig verstehe, wird das sonst unvermeidbare INTCON-Register gar nicht benötigt. Das Prog ist von allem Unnötigen befreit und meiner Meinung nach gut lesbar. Das ASM-File ist hier als Text-File angefügt. Ach ja, das Prog läuft mit dem "mittleren" Assembler, also MPLAB X 5.20 (geht bis 5.35). Vielleicht schaut ja jemand mal rein. Is klar, da fehlt - wie so üblich - ein "nur........". Seufz, wenn nur das "nur" nicht wäre!
Grüße, picass
stundenzaehler03.txt
Kaum eingestellt und auf dem Weg zum Kaffee-Zubereiten. aber zu spät für eine sofortige Korrektur fiel mir auf, wo im obigen Prog ein "NUR" zu finden ist: falsche Zuordnung beim Rücksprung nach einem Durchlauf der Dauerschleife. Da wurde etwas zu weit zurück gesprungen und dadurch das 16-bit-Counter-Register immer wieder neu befüllt. Das ist nun geändert. Aber der IRQ klemmt nach wie vor. Auch im Simulator wird der Überlauf zwar angezeigt, aber nix weiter passiert, also kein Einsprung in die IRQ-Routine. Hier das überarbeitete Prog:
Grüße, picass
stundenzaehler03.txt
Hallo picass,
ich nutze ASM zwar nur, wenn es absolut nötig ist, aber ich sehe kein setzen des GIE- Bit!?
Gruß
PICkel
Du bist wieder beim Pic zurück! 8)
Welches Signal hast Du denn? Ist es ein sauberes Rechtecksignal? Ansonsten würde ich es zuerst über einen Schmitt-Tricker aufbereiten. (z.B. 7414)
Ich würde folgender Maßen vorgehen.
Zuerst nach dem Start würde ich alle Parameter initialisieren. Ich habe jetzt im Überflug nicht gesehen, wo der Port als Eingang für den Timer initialisiert ist.
INTCON Byte setzen:
GIE/GIEH PEIE/GIEL TMR0IE INT0IE RABIE TMR0IF INT0IF RABIF
GIE muss gesetzt werden
PEIE
IPR1,TMR1IP (HI Prio)
PIE1,TMR1IE (1)
PIR1,TMR1IF (0)
T1CON Byte setzen,
TMR1CS: Timer1 Clock Source Select bit
TMR1ON
T1CKPS<1:0>: Timer1 Input Clock Prescale Select bits
T1RUN: Timer1 System Clock Status bit
Nach der Initialisierung würde ich im Hauptprogramm (Main) eine Endlosschleife machen.
zb.
main nop
bra main
im Interrupt:
hier würde ich zuerst einmal abfragen, ob der Interrupt vom Timer ausgelöst wurde.
wenn ja, dann das TMR1IF löschen.
nicht das TMR1IE, das wird noch benötigt.
nun kannst Du im Interrupt den Zählstand abfragen. Ich meine der Timer zählt rückwärts. Und bei erreichen den Zählerstand neu setzen, bzw den Zähler-Offset einfach Timerwert hinzuaddieren bzw abziehen
Den Interrupt beendest Du mit.
RETFIE (Return from Interrupt), kein Sprung nach Main oder sonst wohin!!
Ich habe im Archiv ein Beispiel gefunden, dies ist ein Zeitprogramm welches mit dem Systemtakt ich meine 40Mhz arbeitet. Ich hatte dies im meiner Anfangszeit programmiert, heute würde ich es etwas anders machen. Nicht den Timer direkt setzen, sondern die Zyklen abziehen. Dadurch gibt es keine Ungenauigkeiten. interr_b_txt.txt
Zitat von: pic18 in 27.07.2025, 20:05:34 CESTIch meine der Timer zählt rückwärts.
Über das "Zurück-beim PIC" schreibe ich an anderer Stelle.
Nix, der Timer/Counter zählt nach oben:"The TMR1 register pair (TMR1H:TMR1L) increments
from 0000h to FFFFh and rolls over to 0000h." So das Datenblatt.
Das INICON-Register ! Das beschäftigt mich auch, schon alleine, weil man bei Interrupts bislang an dem nicht vorbei kam. Aber......, da gibt es gleich mehrere "Aber": aber für den Time/Counter1 gibt es ein eigenes Register, über welchen der IRQ ein- u. ausgeschaltet wird. Und das Reg wird in jedem Fall benötigt. Käme nun das INTCON auch noch dazu, würde doppelt gemoppelt und welche Ein-oder-Ausschaltung würde denn dann greifen? Weiter: in dem Beispiel-Code für Timer1 im Datenblatt steht nix von INTCON, nur das Sonder-Reg..
Muss da mal 'ne gehörige Runde experimentieren, gleich heute Nachmittag gehts los.
Ach ja, wie schon beschrieben: das Taktsignal ist ausgesprochen sauber zwischen 0,6 Volt und 4,9 Volt. Der Port A Eingang hat eine Schmitt-Trigger-Funktion.
Grüße, picass
Zitat von: picass in 28.07.2025, 12:39:29 CESTDas INICON-Register !
Um das INTCON- Register kommt man halt nicht drumherum!
Und das 7. Bit im INTCON ist das GIE-Bit. GIE =
Global Interrupt Enable, dieses Bit schaltet also
alle Interrupts ein oder aus, egal welche Interrupts (TMR0IE, TMR1IE,...) aktiviert sind.
Das ist wichtig, um bspw. in der Interruptroutine das "dazwischenfunken" eines weiteren INT's bei mehreren möglichen Interruptquellen zu verhindern. Man setzt also am Anfang der INT-Routine
bcf INTCON,GIE
und am Ende
bsf INTCON,GIE
RETFIE
Außerdem müsste doch wohl in Deinem Prog. statt
blink: ;
bsf PIE1,TMR1IF ;enable timer1 interrupt
stehen:
blink:
bsf PIE1,TMR1IE ;enable timer1 interrupt
Gruß
PICkel
(der hoffentlich die paar ASM- Zeilen richtig geschrieben hat :) )
Das INTCON Register ist schon wichtig, zum einen schaltest du den Interrupt ein, zum anderen kannst Du auch den Lo Interrupt einschalten. Ich kann mich erinnern, das früher ein Fehler in der Doku war das der Lo Interrupt auch ohne Hi Interrupt eingeschaltet werden kann. Dies ist aber nicht so.
T1CON wird auch benötigt, zum einen wegen der Zählung 8 oder 16 Bit T1CKPS1 benötigst Du für den Vorteiler.
So weit erstmal, ich melde mich später wieder
Ich habe hier die Register mal herauskopiert
Name Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 Reset Values on page
INTCON GIE/GIEH PEIE/GIEL TMR0IE INT0IE RABIE TMR0IF INT0IF RABIF 245
IPR1 — ADIP RCIP TXIP SSPIP CCP1IP TMR2IP TMR1IP 248
PIE1 — ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE 248
PIR1 — ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF 248
TMR1H Timer1 Register, High Byte 246
TMR1L Timer1 Register, Low Byte 246
TRISA — — TRISA5 TRISA4 —(1) TRISA2 TRISA1 TRISA0 248
T1CON RD16 T1RUN T1CKPS1 T1CKPS0 T1OSCEN T1SYNC TMR1CS TMR1ON 246
Einige Strampelübungen weiter...., aber nur Teilerfolge. Zunächst war eine der beiden Kontroll-LEDs am falschen Portpin angeschlossen...Shit happens. Aber die andere wollte ja genauso wenig. Das lag dann wiederum daran, dass ich nur ganz rasch im Vorbeigehen auf ein andere, als die bislang genutzte interne Osillator-Quelle geschaltet hatte, nämlich auf den LP-Oszillator. Der machte dann alles derart grauenhaft langsam, dass ich allein deswegen an Defekte dachte. Nu flitzt wieder alles und die LEDs blinken, wie sie sollen. Und dann das INTCON-Reg. Wie konnte ich das nur verschmähen?! Das ist nun wieder im Spiel. Nutzt nur auch nichts, der IRQ klemmt nach wie vor: die IRQ-Routine wird nicht angesprungen.
Das RETFIE ist die übliche Variante, es geht aber auch mit Sprüngen in andere Progteile. Aber dessen Einfügen konnte nichts nützen, soweit kam der PIC erst gar nicht. Jetzt findet sich alles, was in einem meiner erfolgreichen IRQ-Programme auch findet, nur das dort ein Port-on-Change interruptet wird, hier halt der Timer1...... oder halt gerade leider nicht. Es läuft auf das völlig Übliche raus: irgendwo sitzt der dicke Floh und grinst sich einen! :-[ Grüße, picass
Stell Mal den aktuellen Code rein.
Bei dein alten waren ja einige Fehler drin welche genannt wurden.
Ich würde im simolator Mal die Register kontrollieren. Eventuell wird auf einer falschen Bank noch geschrieben o.ä.
@ PICkel
Zitat von: PICkel in 28.07.2025, 13:40:16 CESTMan setzt also am Anfang der INT-Routine
bcf INTCON,GIE
und am Ende
bsf INTCON,GIE
RETFIE
ich denke dass dies nicht notwendig ist, der PIC sichert ja die Parameter und ich denke dass er einen zweiten Interrupt nicht zu lässt. Es sei dem es wird in einem Lo Interrupt ein Hi Interrupt ausgelöst. Dieser hat natürlich Vorrang. Beim Lo-Interrupt müssen natürlich auch die Statusregister usw. separat gesichert werden.
Zitat von: PICkel in 28.07.2025, 13:40:16 CESTbsf PIE1,TMR1IF ;enable timer1 interrupt
bsf PIR1, TMR1IF
@picass hast Du mal meine Beispiele angeschaut, diese müssten eigentlich funktionieren. Es muss nur der Vorteiler geändert werden. Du arbeitest ja mit nur 31Khz
Zitat von: picass in 28.07.2025, 16:34:42 CESTDas RETFIE ist die übliche Variante, es geht aber auch mit Sprüngen in andere Progteile
ich bezweifle, dass du dann aus dem Interrupt wieder richtig zurückkommst. Außerdem läuft der bei ständigen Sprungbefehlen der 31-Level-Stack über. Ein Absturz ist dann programmiert.
Bei RETFIE und RETURN würde ich FAST nehmen. Ich glaube das macht der Compiler automatisch.
Du kannst ja mal dein überarbeitest Prog. hochladen, dann schaue ich mir es mal an.
Zumindest die 'alten' Pics wie 16f628 war es nötig am Anfang einer int Routine GIE aus zu schalten. Ansonsten könnte es passieren das man nie mehr aus der Schleife kam weil ein Ereignis eintrat bevor die Routine komplett durch lief.
Nach meinen Erinnerungen noch... Oder habe ich das nur aus Bauchgefühl gemacht?
Verdammt, ich bekomme dieses Exopand, rsp. das Verkürzen nicht raus. Oder doch jetzt?
Zitat von: pic18 in 29.07.2025, 08:46:18 CEST@ PICkel
Zitat von: PICkel in 28.07.2025, 13:40:16 CESTbsf PIE1,TMR1IF ;enable timer1 interrupt
bsf PIR1, TMR1IF
;--------------------------------------
Ja Jungs, das ist so was mit den Assembeler-Direktriven!
Der gerötete Befehlssatz ist nicht zum Ein-(Aus-)Schalten des timer1-Irq's, das ist dasjenige Flag, welche beim Überlauf des Counters anzeigt.
Versuche mich gleich im Einzelschritt-Debuggen.
Im wahren PIC-Leben wäre im Falle meiner Anwendung eine Störung durch erneuten Irq während des Ablaufes der Irq-Routine nicht zu befürchten. Der gezählte externe Takt der 50-Hz-Netzfrequenz liegt bei 20 mS. In dieser Zeit kann auch der gemütliche 31 kHz-PIC 'ne Menge Cyclen abarbeiten, die arsch-kurze Irqu-Routine allemal.
Muss jetzt wech' und erst mal einen Einzelschritt-Taktgenerator erstellen und dann ein scharfes Auge auf das Counter-Register-Paar werfen.
Grüße, picas
also geht es jetzt?
TMR1IF wird ja automatisch gesetzt beim Überlauf und die Routine muss diese zurücksetzen.
Naürlich muss aber TMR1IE gesetzt sein.
@^Cobra,
@pic18:
Also es wirklich so, dass beim PIC18F14K22 und anderen das GIE-Bit beim Eintritt in den Interrupt gelöscht und bei RETFIE wieder gesetzt wird. Das wusste ich noch nicht.
Siehe DaBla Punkt 7.3:
7.3 Interrupt ResponseWhen an interrupt is responded to, the global interrupt
enable bit is cleared to disable further interrupts.
Und weiter unten steht:
The "return-from-interrupt" instruction, RETFIE, exits
the interrupt routine and sets the GIE bit (GIEH or GIEL
if priority levels are used), which re-enables interrupts.
Das liest sich m.E. aber so, dass bei einem normalen Rücksprung (RETURN) dieses Bit nicht wieder gesetzt wird. Ganz zu schweigen davon, dass der Stack beim Interrupt-Eintritt gefüllt, aber bei den ganzen Sprungbefehlen (BC,BN,...,BRA,...) nicht wieder geleert wird und irgendwann überläuft.
Gruß
PICkel
Das Einzelschritt-Debuggen musste noch warten, auch wenn der dazu nötige Schrittgeber mit einem LM555 verwirklicht werden konnte. Erst hatte ich noch die zu dem Counter und Interrupt gehörenden Befehle überarbeitet, was auch notwendig war. Das erneut korrigierte Prog-File ist unten angefügt. Auch das externe Taktsignal wurde noch mal bearbeitet, sodass die "null"-Schwelle auf ca. 0,3 Volt absackert. Nutzte aber alles nichts, nach wie vor wird die IRQ-Routine noch nicht angesprungen. Gettz ab zum Kaffetrinken und wenn meine Frau nicht dazwischen grätscht, geht es gleich danach weiter.
Grüße, picass
clock-signal.jpgstundenzaehler05.txt
Hallo picass,
in der Programmscheife blink: wird mit
bsf LATB,7 ;test-led an
PORTB.7 eingeschaltet, aber nicht wieder ausgeschaltet.
Setze doch mal
bcf LATB,7 ;test-led aus
vor den BRA blink - Befehl.
Damit solltest Du (ohne Interrupt- Signal) mit dem Oszi an PORTB.7 einen Takt von ca. 1kHz messen können.
Damit ist erstmal sichergestellt, dass der PIC korrekt arbeitet.
Gruß
PICkel
Es gibt einen Teilerfolg: im Simulator funktioniert das zuletzt eingestellte Prog in der Version "05" ! Weil man im Simulatorbetrieb die Port-Eingänge stimulieren kann, ist es möglich, mit Einzelschritt (F7-Taste) und entsprechendem Stimulieren des Port A,5-Pins den Timer zu Überlauf zu bewegen. Sobald das Low-Register des Zähler-Paares von h'ff' auf h'00' springt, wird im Register PIR1 das TMR1IF-Flag gesetzt. Und - tusch - der Simulator springt zur Speicherstelle 00008, wo der Irq-Vektor für die High-Irq's hinterlegt ist. Von dort in die Irq-Routine, löscht brav das Irq-Flag, toggelt die dortige LED und springt nach "retfie" wieder an die vorige Position in der nop-Schleife.
Also dann ist sowohl die Irq-Einstellung als auch die Timer-Einstellung passend - so meine Schlussfolgerung. Was nicht passt ist das Fehlen des Zählens: die externen Impulse kommen nicht durch. Passend dazu ist auch jeder Test mit dem Debugger, als der Live-Test-Version, ein Fehlschlag. Da kann ich mit dem Einzelschritt-Generator noch so viele Schritte auslösen, nix ist mit counten. Das Taktsignal ist ein super Rechteck zwischen 0,3 und 4,7 Volt. Da in der Praxis der Port A,5 Pin als Eingang geschaltet und die digitale Signalform gewählt ist, arbeitet der Pin als Schmitt-Trigger. Der muss schalten. Das tut er wohl auch, aber das Signal wird nicht weiter gereicht.
Hmmmmmm..... jetzt muss noch ein Test her, welcher nur das Verarbeiten des externen Counter-Taktes überprüft.
Grüße, picass
Beim Timer1 gibt es ein spezielles 7.Bit: RD16 im T1CON- Register. Der Timer1 stellt für den Zugriff eine spezielle Funktion zum Lesen und Schreiben beider Bytes bereit. Das ist bei Dir aktiviert:
movlw b'10000111' ;timer1 controll reg:16bit/timer1-oszi aus/keine
Stelle mal testweise um auf:
movlw b'00000111'
Vielleicht hilft das weiter, es kann ja sein, dass beim Schreiben von TMR1H und TMR1L irgendwas schiefgeht.
PICkel
KLATSCH und KREISCH !
Es ist vollbracht: die angesprochene LED blinkt im Sekundentakt!
Neben den ersten, zweiten und dritten Fehlern war es letztlich das Konfigurations-Register für die Timer1-Einstellung, welche alles verhinderte. Da hatte ich das "Timer1 Block Diagramm" - also die stark vereinfachte Skizze der Funktionsblöcke für die Timer1-Funktion - studiert und messerscharf geschlossen, dass das Taktsignal an den Port A,5 zu legen wäre, welcher in dieser Skizze als "OSC1/T13CLK" benannt ist. Und von da aus ginge das Signal über die dargestellte durchgehende interne Leitung zu einem Schmitt-Trigger und von dort weiter zum Übergabepunkt ""Timer1 Clock Input". Anders gesagt: das Signal würde am Timer1-Oszillator vorbei gelenkt, der würde ergo nicht gebraucht.
Weswegen ich ihn im Config-Word abgeschaltet hatte. Das war das Bit 3 oder anders: T1CON,T1OSCEN. Dieser Oszillator war also still gelegt in dem Glauben, dass es ja wegen der durchgängigen Verbindung den nicht braucht. Zudem wird der PIC selbst ja über seinen LFINTOSC angetrieben, was braucht es da noch einen weiteren Oszillator. Aber wie das so ist mit dem Glauben. Tatsächlich läuft das Prog, sobald dieser Oszi doch wieder enabled ist, er läuft nicht, wenn disabled, er läuft, wenn......Er läuft und auch dieser Floh wurde ertappt und zerquetscht ! Schwitz, wieder so'n Kampf und die Stunden gingen dahin. Nun gehe ich dahin, kaufe erst noch schnell 'ne Runde ein und freue mich heute Abend dann mal 'ne etwas größere Runde.
Sehr herzlichen Dank für eure zahlreichen Bemühungen, das war ganz toll von euch. Danke.
Grüße, picass
Ich habe mir mal das Datenblatt angeschaut und schon einen Fehler aus Seite 10 gefunden :o
Timer0 and Timer3 external clock input
Nun zu deinem Prog.
Du hast T1CON geschrieben:
movlw b'10000111' ;timer1 controll reg:16bit/timer1-oszi aus/keine
muss aber
movlw b'10000011' ;timer1 controll reg:16bit/timer1-oszi aus/keine
heißen
/T1SYNC: Timer1 External Clock Input Synchronization Select bit
When TMR1CS = 1:
1 = Do not synchronize external clock input
0 = Synchronize external clock input
außerdem würde ich noch Priorität vom Timer 1 auf Hi setzen.
IPR1 TMR1IP
was mir auch aufgefallen ist, du tust deinen Timer nur beim start in Main "Befüllen" sollte dies nicht in jeden Interrupt sein?
in blink die Test LED einschalten würde ich anders lösen, die LED leuchtet sonst ständig. Das siehst Du nicht, wenn im Interrupt der Ausgang mal kurz toggle wird.
irgendwie war ich etwas zu spät :(
freud mich wenn das Prog. läuft
Hallo picass,
habe mal Dein ASM-File "stundenzaehler05.txt" mit einer funktionierenden ISR versehen. Jetzt blinkt die LED im 1s-Takt. Das ASM-file müßte auch bei Dir funktionieren, habe es im relocatable Code geschrieben.
Das mit dem Input einer Halbwelle habe ich noch nicht richtig verstanden.. naja.
mfG Ottmar
P.S.: was um alles in der Welt veranlasst Dich immer fosc=31kHz zu nehmen?
Bis die LED anfängt zu blinken, erschrickt man regelrecht, man glaubt der Code stimmt nicht.
Zitat von: pic18 in 29.07.2025, 21:44:42 CESTin blink die Test LED einschalten würde ich anders lösen, die LED leuchtet sonst ständig. Das siehst Du nicht, wenn im Interrupt der Ausgang mal kurz toggle wird.
vergiss die Zeile, das ist ja ein anderer Port.
@Ottmar ich habe mal dein Prog. angeschaut, dies arbeitet mit den 31Khz (internal clock (FOSC/4)), nicht mit dem RA5 Takteingang. Da müsste das eine Bit noch gedreht werden.
picass,
PIC18
ich habe es verstanden. Wenn der unten stehende Code über den bestehenden in dem von mitgeteilten ASM-File kopiert wird, klappt das so wie von picass beabsichtigt.Was er beabsichtigt hat, habe ich erst nach dem Upload meiner Datei bemerkt.
Bei mir wird RA5 mit 130 kHz Sinus 2,2Vss direkt beaufschlagt, bei dem der Sinus gerade noch über die interne Triggerschwelle rutscht damit die LED blinkt.
fosc in . 130kHz -> LEDfosc ca. 0,9Hz
Bei wechselnder Frequenz, folgt die Blinkfrequenz dieser Änderung. und die LED bei wechselnder frequenz, entsprechend langsamer/schneller analog blinken läßt.
;---T1CON: TIMER1 CONTROL REGISTER DS.94
; external TMR1 oscillator is enabled when T1OSCEN = 1
; and TMR1CS is set to 1 external clock enabled
; the signal goes straight from pin RA5 throug a schmitt-trigger
; to the clock input of TMR1.
;; movlw b'00000000'
movlw b'00001010'
; ....1.. b3/T1OSCEN 0/1 external Tmr1-clock disabled/enabled
; ......1. b1/TMR1CS 0/1 = Internal(external clock (FOSC/4)
; .......0 b0/TMR1ON 0/1 disable/enable TMR1
movwf T1CON
clrf TMR1H ;clear Hi-Byt ;TMR1 is set o 0x0000
clrf TMR1L ;clear Lo-Byte
Auf diese Darstellung in der angehängten Skizze - ein Auszug aus dem Microchip-Datenblatt - bin ich rein gefallen, was meiner Art entspricht, zunächst alles wörtlich zu nehmen. Da gibt es die scheinbar direkte Verbindung vom Eingangsport PA5 zum internen Übergabepunkt für das Timer1-Signal. Auch jetzt nach Entdeckung des wahren Sachverhaltes ist mir nicht eingängig, warum das nicht direkt durch geht. U.a. geht es ja direkt durch, denn dieser Port kann ja sowohl für digitale wie auch analoge Signale als Eingangspin genutzt werden, ohne dass dieser vermaledeite Oszi in Betrieb gesetzt wird.
Die Synchronisations-Möglichkeit hat keinen Einfluss auf die Funktion meiner Schaltung, egal, ob on oder off, da ändert sich nichts Erkennbares.
Der Input über die Halbwelle ist schlicht erklärbar. Es sollte halt als einfache Taktquelle die 230-V-Netzfrequenz gewählt werden. Bei Vollgleichrichtung müsste dann durch 100 geteilt werden, um auf eine Sekunde zu kommen. Bei Halbwellen reicht ein 50-Teiler, der auch noch den Vorteil hat, dass die Null-Stelle weitaus ausgeprägter ist.
Nicht alles aus der Welt, aber eine nüchterne Betrachtung veranlasst mich, bei dem überwiegenden Teil meiner Schaltungen die niedrige interne Taktfrequenz von 31 kHz zu wählen. Ist an diesem Beispiel wunderbar zu erklären. Die Erzeugung eines Sekunden-Taktes gelingt über diese Halbwellen und einem "speed" mit 20 mS-Taktzyklus. Das ist selbst für den gemütlichen 31-er-PIC noch ein Schnarchmodus, weil...... weil selbst diese Schnarch-Frequenz im Programm noch durch 50 geteilt werden muss. Selbstredend könnte man den PIC auch mit 4 MHz rasen lassen und dann...? Dann müsste man einen irren Teilungsfaktor erstellen, um wieder auf das Ein-Herz-Signal zu kommen. Aus meiner Sicht: was soll der Quatsch?!
Ein passender Vergleich: um mit einem Kfz auf eine Geschwindigkeit von 20 km/h zu kommen, könnte man einen leistungsschwachen und mit nur 1.800 U/min ruhig dahin nagelnden Diesel greifen und den sogar noch im dritten Gang fahren. Genauso könnte man einen Ferrari mit 700 Newtonmeter bei 5.000 U/min betreiben, bei dem dann allerdings vorher noch ein Spezialgetriebe eingeflanscht werden müsste, um die irre Drehzahl runter zu bekommen für den Antrieb.
Bei meiner PIC-Testplatine dauert nach dem Starten auch nichts. Die erste Dunkelpause nach dem Einschalten dauert exakt genau so lang, wie die späteren Dunkelpausen beim Blinken im 1-Herz-Rhytmus. Zudem: wie oft wird eine Schaltung, welche sich im Jahres-Dauer-Einsatz befindet überhaupt eingeschaltet?! Ne, lass ma, für mich und meine PICs passt das.
Grüße, picass
counter1.jpg
Hi picass,
langer Rede kurzer Sinn: Es geht einfacher, ohne Netzanbindung/Optokoppler und Halbwellenbildung, eine exakte 50Hz-Frequenz zu generieren.
Nimm das von mir anfänglich übermittelte ASM-File mit diesen Änderungen:
Internen Oszillator, Fosc = 4Mhz, das ergibt Fosc/4=1.000.000 wc (working cycles).
Diese nimmst Du als clock für den TMR1, dazu den TMR1 overflow-Interrupt. In der ISR machst Du einen Preset 2^16us - 20.000us = 45536 = 0xB1EO.
Die "Denkpause" wird vermieden, wenn man direkt vor Freigabe des Interrupts, (bsf INTCON,GIE) den TMR1 auf den Preset wie nachstehend setzt.
ZitatISR_H:
bcf PIR1,TMR1IF
movlw 0xB1
movwf TMR1H
movlw 0xE0
movwf TMR1L
movlw b'10000000' ;LED ist an bit 7
xorwf PORTA,f ;RA7 toggelt alle 200ms und LED blinkt mt 50Hz
RETFIE FAST
Voila - das geht ganz ohne Vorteiler... fertig! Solls genauer sein, nimm einen 4MHz Quarz.
A
Zitateinen Preset 2^16us - 20.000us = 45536 = 0xB1EO
richtig ist:
2^16us - 10.000us = 55536us = 0xDBF0
denn die Periodendauer von 50Hz ist 20ms und dafür bedarf es 2 ISR-Intervalle zu je 10ms.
Hallo Ottmar !
Deine Rechenübung kann ich nicht nachvollziehen.
Zunächst stimmt deine Zuordnung nicht: h'dbf0' sind d'56.304'
Dann ist völlig unklar, was genau du denn nun den PIC zählen lassen willst, um von seiner hohen Frequenz runter zu kommen auf 1 Hz. Takt soll 4 Mhz sein, ein Cyclus dann 1 Mhz, also jeder Cyclus belegt 1 µS. Dann soll nach deiner Rechnung der Counter1 gesamt 55.536 µS abzählen. Und wie kommt man nun auf eine Sekunde ?
Grüße, picass
Hi picass,
Du hast recht. das war von mir ein Ablesefehler auf dem Windowsrechner "Programmierer", habe "B" anstatt korrekt "8" abgelesen. Die dezimale Berechnung stimmt jedoch, hättest Du das beim Nachvollziehen der Berechnung bemerken können?
Die Blinkfrequenz 1Hz mit dem LF-Osillator habe ich in dem ASM-File vom 25.7 bereits dargestellt.
Ok, ich gebe Dir 2 Beispiele für 50 Hz bzw. 1 Hz Blinkfrequenz mittels TIMER1-overflow-Interrupt, wie man dafür den Vorteiler und den Preset berechnet. Beachte bitte, daß der Arbeitstakt stets 1/4 der Oszillatorfrequenz beträgt. TMR1 zählt nur den Arbeitstakt.
;
a) 50Hz, Periodendauer 0,02s, zusammengesetzt aus
0,01s High, 0,01s Low
b) 1Hz, Periodendauer 1s, zusammengesetzt aus 0,5s High, 0,5s Low
Gesucht a) Interrupt-intervall 0,01s (50Hz
b) " " 0,5s (1Hz)
dazu TMR1.Prescaler
Gegeben:fosc=4MHz (eingestellt z.B. mit OSCCON)
TMR1-Modul (Zählbereich 0-2^16) + TMR1 overflow interrupt
und Prescaler 1:1-1:8 vgl. Register TMR1CON
a) TMR1-Zähltakt = fosc/4 also 1.000.000 wc (Arbeitszyklen/sek.)
ISR-Aufruf muss für 10ms (Peridodendauer 2x10ms) alle 10ms
erfolgen.
Prescaler 1:1 wird verwendet, da TMR1 den Zählbereich offensichtlich
ohne Vorteiler überstreicht, was Rechenarbeit erspart.
Preset: = 2^16 - 10.000 = 55536 = 0xD8F0
b) TMR1-Zähltakt = fosc/4 also 1.000.000 wc (Arbeitszyklen/sek.)
ISR-Aufruf muss für 1Hz (Peridodendauer 2x0,5s) alle 0,s erfolgen
Prescaler: 500.000 wc / 65536 = 7,6 nicht verfügbar!
daher: nächsthöherer, verfügbarer Prescale: 1:8
Preset TMR1: 2^16 - (500.000/8) = 3036 = 0x0BDC
TMR1CON,T1CKPS<1:0> = 11
Der Vorteil einer zweckentsprechend gewählten Oszillatorfrequenz
liegt damit wohl auf der Hand.
mdG Ottmar
das mit dem Timer stellen mache ich schon lange nicht mehr, das ist mir zu ungenau.
Ich würde es so machen:
den Vorteiler auf 32 setzen Timer auf 8 Bit (256 Überlauf)
bei 4Mhz würde ich dann 100ms zählen.
| bedeutet hoch, kann ich hier nicht darstellen.
256 und 32 Teiler 4 TOS/4
4*10|-6 s|-1 = 4MHz
100|10-3s =100ms
(256*32*4)/(4*10|-6s|-1 *100|10-3s) = 8,192E-2 = (256/3125)
jedes mal wenn der Interrupt aufgerufen wird, Variable Z 256 addieren
wenn Z >=3125 ist dann Z=Z-3125 und hier sind 100ms vorbei.
Noch ein Tipp zum Thema Timerauslegung:
Von mikroe gibt es ein Programm, das auch "standalone" läuft: den Timer- Calculator: https://www.mikroe.com/timer-calculator
Hier kann man PIC-Typ, Timer, Taktfrequenz und Verzögerung eingeben und erhält einen Code wahlweise in mikro-C/-Basic oder -Pascal.
Eine Portierung in ASM ist bei den paar Zuweisungen auch kein Problem.
Aber seht selbst im Anhang. Bei Bedarf kann ich Euch ein zip-File mit den Daten bereitstellen.
Gruß
PICkel
Timer_Calculator.jpg
@pic18 Hm... meine Interrupt-Intervalle stimmen auf den Arbeitstakt genau. In der ISR, deren Intervall fast immer einige Arbeitstakte zu kurz ist, gleiche ich das dann mit einigen sinnvollen der auch wirkungslosen Instruktionen aus.
Da muss genau die Taktzyklen abgezählt werden, damit der Timer richtig gestellt wird. Bei mir gleicht sich dies automatisch aus. Gut es ist manchmal ein Takt etwas kürzer. Eine Uhr läuft aber über Monate Ganggenau.
Hi pic18,
das Abzählen fällt leicht, den in MPLAB 8 gibt es einen praktischen Timer zum Zählen der Arbeitstakte zwischen zwei Haltepunkten.
Intervall stoppen, Sollzeit - Istzeit = Extratakte (wc) hinzuzufügen. So kann auch die Ausführungszeit z.B. von Schleifen mit ein paar Clicks, taktgenau festgestellt werden. Nachstehend ein Beispiel.
ISR:
bcf INTCON,TMR0IF
NOP ->1wc
GOTO $ +1 ->2wc
; und so weiter.....und wenn es ganz extrem werden sollte:
isr_taktehinzufügen:
movlw (n) ->1wc n * (8wc) = 8...-x wc zum Abgleich verfügbar
movwf dummy ->1wc
decfsz dummy,f ->2-3wc
GOTO isr_taktehinzufügen ->2wc
;
movlw tmr0preset
movwf TMR0 --- hier endet das alte, beginnt das neue Intervall
Ist schon richtig, nur die Zählerei ist mir zu kompliziert. Zumal ich in C programmiere. Je nachdem welchen C-Compiler genommen wird, ändert sich dann der Code. Dann stimmt alles nicht.