Forum: Mikrocontroller und Digitale Elektronik Problem mit Interrupt des Timer1 bei PIC18F2550


von Der Alois (Gast)


Lesenswert?

Hallo zusammen,
bin gerade dabei den Timer1 des o.g. PICs auszuprobieren. Habe dazu das 
folgende ganze einfache Testprogramm mit dem CCS-C-Compiler geschrieben:
1
#int_TIMER1
2
TIMER1_isr()
3
      {
4
      OUTPUT_HIGH(PIN_B7);
5
      delay_cycles(10);
6
      OUTPUT_LOW(PIN_B7);
7
      delay_cycles(10);
8
      set_timer1(65335); //65535-200=65335
9
      }
10
11
void main()
12
{
13
   SET_TRIS_B( 0x00 ); //alle Pins von Port B sind Ausgang
14
   setup_adc_ports(NO_ANALOGS|VSS_VDD);
15
   setup_adc(ADC_OFF);
16
   setup_spi(FALSE);
17
   setup_wdt(WDT_OFF);
18
   setup_timer_0(RTCC_OFF);
19
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
20
   setup_timer_2(T2_DISABLED,0,1);
21
   setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
22
   setup_comparator(NC_NC_NC_NC);
23
   setup_vref(FALSE);
24
   enable_interrupts(INT_TIMER1);
25
   enable_interrupts(GLOBAL);
26
   
27
   while(1)
28
      {
29
      OUTPUT_HIGH(PIN_B0);
30
      delay_cycles(100);
31
      OUTPUT_LOW(PIN_B0);
32
      delay_cycles(100);
33
      }
34
35
}

Die folgende h-Datei habe ich eingebunden:
1
#include <18F2550.h>
2
#device adc=8
3
#use delay(clock=4000000)
4
#fuses NOWDT,WDT128,XT, NOPROTECT, NOBROWNOUT, BORV20, NOPUT, NOCPD, STVREN, NODEBUG, LVP, NOWRT, NOWRTD, IESO, FCMEN, NOPBADEN, NOWRTC, NOWRTB, NOEBTR, NOEBTRB, NOCPB, MCLR, NOLPT1OSC, XINST

Das Problem ist jetzt, dass er ständig nur die ISR durchläuft und 
offenbar nie die Main-Funktion. Zudem stimmt die Ausgangsfrequenz an 
PIN_B7 nicht; sie beträgt rund 200 kHz und zwar unabhängig vom Prescaler 
des Timer 1 und unabhängig vom Reload-Wert. An PIN_B0 kommt gar nichts 
an.
Ich verwende einen Quarz mit 4 MHz.

Kann mir jemand sagen wo mein Fehler liegt?
Gruß Alois

von Daniel P. (ppowers)


Lesenswert?

Hallo,

das hier:
>       set_timer1(65335); //65535-200=65335
halte ich für sehr problematisch. Der Overhead für die 
Interrupt-Funktion, die der CCS Compiler erzeugt kann schonmal leicht 
einige 100 Instructions betragen. Bei Deiner Timerkonfiguration bleiben 
ja gerade mal 178 Instructions zwischen den Timerüberläufen. Ich glaube 
das haut so nicht hin. Was genau hast Du denn bitte vor???

Gruß
daniel

von Der Alois (Gast)


Lesenswert?

Hallo,
ich möchte nachher alle 200µs einen Interrupt erzeugen. Der Timer zählt 
mit einer Frequenz von 1 MHz, entspricht 1µs Periodendauer. Für 200µs 
dachte ich dann brauche ich dementsprechend 200 Durchläufe, daher kommt 
der Wert. Sollte aber mal nur ein erster Test sein. Und wie gesagt 
ändert sich seltsamerweise auch dann nichts wenn ich den Wert z.B. auf 
30000 ändere oder den Prescaler erhöhe.
Gruß Alois

von Daniel P. (ppowers)


Lesenswert?

Hast Du das ganze schonmal mit dem 8-Bit-Timer versucht (Timer0)?

Außerdem haben manche CCS C Builtin Funktionen bugs.
Versuch mal die Register zu Fuß zu setzen.
Sobald Du den Prescaler änderst, sollte sich die Frequenz am Ausgang 
auch ändern. Alle 200µs einen Interrupt mit dem CCS C Compiler und das 
bei nur 4 MHz Takt ist jedenfalls Glückssache...

Testweise könntest Du mal die PLL anschmeißen. Dann hättest Du immerhin 
einen 16 MHz takt und die Interruptlatenz würde sich verringern.

von Der Alois (Gast)


Lesenswert?

Hallo Daniel,
Danke für Deine Tipps. Habe sämtliche von Dir genannten Punkte schon 
durchprobiert; es hilft nichts. Wieso meinst Du dass das ganze bei 4 MHz 
eine Glückssache ist?
Gruß Alois

von Daniel P. (ppowers)


Lesenswert?

Der Alois wrote:
> Wieso meinst Du dass das ganze bei 4 MHz eine Glückssache ist?
Wegen dem oben bereits erwähnten Overhead der Interrupt-Funktion, der 
automatisch vom Compiler erzeugt wird (Register sichern usw...). Man 
kann nicht mit Sicherheit sagen, wieviele Instructions dieser Overhead 
umfasst;

Stellen wir uns mal vor - worst case - der Overhead vor und nach Deiner 
eigentlichen Interrupt-Funktion beträgt insgesamt 178 Instructions. In 
diesem Fall würde der PIC direkt nach der Abarbeitung des Interrupts 
direkt wieder in den Interrupt springen, da die 200µs schon wieder 
abgelaufen sind.
Würdest Du nun die 4xPLL verwenden und den Timer1-Prescaler auf 4 
einstellen, so würde der Overhead natürlich immernoch die (angenommenen) 
178 Instructions benötigen, dafür aber keine 178µs mehr benötigen 
sondern nurnoch ca. 45µs. Der PIC hätte also wieder "Freizeit" um 
Befehle im Hauptprogramm auszuführen.

Mal was anderes:
Hast Du schonmal gemessen was passiert, wenn Du die Zeile
1
set_timer1(65335);
einfach weglässt? Die gemessene Frequenz sollte dann etwa 15 Hz 
betragen. Stimmt das denn wenigstens?

Gruß
Daniel

von Gerhard. (Gast)


Lesenswert?

Habe bei mir das ganze mit einem vorhandenen 18F4620 ausprobiert. Bei 
16MHz und einen Reloadwert von 65485 (Um die 16MHz zu beruecksichtigen, 
also bei 50us) funktioniert das einwandfrei.  Die zweite Delay ist 
uebrigens unnoetig.

#include <18F4620.h>
#device adc=8
#use delay(clock=16000000, RESTART_WDT)
#fuses HS, NOPROTECT, NOPUT, BROWNOUT, NOLVP


#int_TIMER1
TIMER1_isr()
      {
      OUTPUT_HIGH(PIN_E2);
      delay_cycles(10);
      OUTPUT_LOW(PIN_E2);
      //delay_cycles(10); // Ist Zeitverschwendung
      set_timer1(65485); //65535-200=65335
      }

void main()
{
   SET_TRIS_E( 0x00 ); //alle Pins von Port E sind Ausgang
   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_OFF);
   setup_spi(FALSE);
   //setup_wdt(WDT_OFF);
   setup_timer_0(RTCC_OFF);
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
   setup_timer_2(T2_DISABLED,0,1);
   setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);

   while(1)
      {
      OUTPUT_HIGH(PIN_B0);
      delay_cycles(100);
      OUTPUT_LOW(PIN_B0);
      delay_cycles(100);
      }

}

MFG
Gerhard

von Der Alois (Gast)


Lesenswert?

Hallo,
die Frequenz beträgt, wenn ich die genannte Zeile weglasse, 222 kHz an 
PIN_B7; an PIN_B0 kommt nichts an. Habe die PLL hierbei aktiviert.
Gruß Alois

von Gerhard. (Gast)


Lesenswert?

Habe vergessen: Der Timer Re-load sollte als erstes gemacht werden, da 
das ja in die totale Zeit eingeht.

#int_TIMER1
TIMER1_isr()
      {
      set_timer1(65485); //65535-200=65335
      OUTPUT_HIGH(PIN_E2);
      delay_cycles(10);
      OUTPUT_LOW(PIN_E2);
      //delay_cycles(10);
      }

Im List File sieht man dass genau 20 Instruktion fuer den Setup des 
Interrupts gebraucht werden und von der Zahl eignetlich abgezogen werden 
sollten, damit die 200us genauer stimmen. Also 65335 + 20 = 65355

MFG,
Gerhard

von Der Alois (Gast)


Lesenswert?

Vielen Dank für Eure Mühen. Fast wie im Chat hier :-) Habe jetzt die 
Tipps von Gerhard mal umgesetzt. Die Periodendauer an PIN_B7 beträgt nun 
exakt 4,000µs und an PIN_B0 kommt nach wie vor nichts an.
Gruß Alois

von Der Alois (Gast)


Lesenswert?

Noch was: Wenn ich jetzt den Wert auf 30000 ändere, dann passiert exakt 
das gleiche wie im vorigen Post beschrieben.

von Gerhard. (Gast)


Lesenswert?

Hallo Alois,

Versuche mal eine kleiner Re-load Nummer, z.B. 64335 (1ms). Vielleicht 
verbraucht die ISR alles und der PIC hat keine Zeit die Main Loop 
abzulaufen. Uebriegens, aufpassen bei meinem zurrueckgesannten Code, da 
ich bei mir andere PINS verwendet habe. (SET_TRIS_E( 0x00 ); //alle Pins 
von Port E sind Ausgang)

Gerhard

von Daniel P. (ppowers)


Lesenswert?

häng doch mal bitte das .lst-File an. Vielleicht kommen wir da dem 
Problem auf die Schliche.

von Der Alois (Gast)


Lesenswert?

Hallo Gerhard,
leider habe ich auch bei 64335 das gleiche Bild. Die anderen 
Einstellungen bezüglich des verwendeten Ausgangs habe ich wie in meinem 
ersten Post.
Gruß Alois

von Der Alois (Gast)


Angehängte Dateien:

Lesenswert?

Hier ist das .lst-file

von Gerhard. (Gast)


Lesenswert?

Hallo Alois,

ich werde mich morgen wieder melden da ich jetzt in meiner Firma 
arbeiten muss;-) (Halb neun Frueh bei mir). Vielleicht mir spaetr noch 
was ein.

Gerhard

von Der Alois (Gast)


Lesenswert?

Dann wünsch ich nen guten Arbeitstag. Danke für Eure Hilfe!

von Gerhard. (Gast)


Lesenswert?

Habe mir den Listfile ganz schnell angesehen und nichts Verdaechtiges in 
der Main loop finden koennen. Wenn es geht werde ich mir das heute Abend 
noch einmal vornehmen - das kann einem ja selber passieren;-) Ist ganz 
gut zu wissen warum solche Sachen nicht immer auf Anhieb funktionieren. 
Muss jetz aber wirklich weg.

Gerhard

von Gerhard. (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Alois,

habe mir die Sache heute Abend angesehen und Dein Program funktioniert 
bei mir ungeaendert zu Hause. Zur Ansicht lege ich zwei 
Oszillogrammbilder und das Testprogramm bei.

Fuer das 200us Bild wurde der TIMER1 Vorteiler auf DIv/4 umgestellt 
damit der 1us Takt wieder stimmt. Den TIMER1 Reloadwert muss man 
geringfuegig eichen, damit die Interrupt Setup Zeit korrigiert werden 
kann. Bei mir und mit 16MHZ Taktfrequenz, musste ich 12 abziehen, d.h. 
mit 65347 wurde die ISR Periode genau 200us.

Dein Originalprogram produziert uebrigenz mit 16MHz Taktfrequenz exakt 
62.5us . Ich habe ueberigens die unnoetigen Setups auskommentiert - die 
sind eigentlich fuer das Testprogram noch nicht noetig.

Die Perioden der Main Loop sind nicht gleichfoermig wie man am 
Oszillogramm sehen kann. Bei jeder ISR Aktivitaet, wird die Main loop 
etwas beeinflusst.

COMPARE Methode:
----------------

Anstatt dieser Methode koennte man auch die COMPARE Funktion ausnuetzen 
(PIN RC1 oder 2). Dann wuerde der Ausgangspuls direkt vom COMPARE 
Register flip-Flop getseuert werden und die zugehoerige ISR ist nicht 
ganz so zeitkritisch und geht im Prinzip nicht in die Genauigkeit des 
Ausgangsignal ein.

Das geht mit: INT_CCP1(2) ISR und abwechselnd

setup_ccp2(CCP_COMPARE_CLR_ON_MATCH) und CCP1 mit dem naechsten Wert 
setzen
setup_ccp2(CCP_COMPARE_SET_ON_MATCH) und CCP1 mit dem naechsten Wert 
setzen

was nur schnell genug erfolgen muss, damit der naechste Compare 
ausserhalb der programmierten Periode liegt. Das funktioniert wunderbar.

Der TIMER1 muss genau wie bei Deinem Beispiel mit 1MHz getaktet werden

CCP1 wird dann bei jedem Interrupt fuer den naechsten Zeitpunkt 
vorgesetzt wie unten gezeigt:

CCP1 =   1200       1210                        1400     1410
-------->|<------------- 200us ---------------->|<----------- 200us 
-------
         |               (T1)                   |
         |<--10us-->| (T2)                      |
         |__________|                           |_________
_________/          \___________________________/ 
\_______________
RC2 oder RC1        ^
                    |_____ ISR_CCP1 
...setup_ccp2(CCP_COMPARE_SET_ON_MATCH)

         ^
         |_______ ISR_CCP1 ... setup_ccp2(CCP_COMPARE_CLR_ON_MATCH)
         _         __                           __        _
ISR: ___/  \_______/  \_________________________/  \______/  \_______
Setze: CCP1=1210 CCP1=1400                  CCP1=1410  CCP1=1600 usw.

Wenn man mit 16-bit unsigned arbeitet dann macht der Overflow nichts 
aus.
Aus 65300...65500 werden dann 165... 365 ... 565 usw. Man muss nur immer 
die Offset zum augenblicklichen Wert dazu addieren.

Es ist hier nur wichtig dass der naechste CLR/SET bevor dem naechsten 
Interrupt erfolgt). Also T2 darf nicht kuerzer wie die Zeitdauer der ISR

Die Genauigkeit dieser Methode ist jetzt nur mehr von der PIC HArdware 
abhaengig und nicht vom Zeitablauf des Programs. Es hat keinen Einfluss 
mehr solange der Interrupt nicht nenneswert behindert wird.

Experimentiere mal damit. Ist ja ganz interessannt.

MFG
Gerhard

von Der Alois (Gast)


Lesenswert?

Hallo Gerhard,
vielen vielen Dank für Deine Mühe. Habe es heute morgen nochmal 
probiert, es ging aber wieder nicht. Habe jetzt dann direkt in den PIC 
geschrieben, dann ging es auf Anhieb. Da hat wohl der Bootloader 
irgendwelche Einstellungen vorgenommen, wodurch der Timer nicht 
funktioniert hat.
Gruß Alois

von Gerhard. (Gast)


Lesenswert?

Hallo Alois,

das Problem mit dem Bootloader ist etwas komisch. Habe eigentlich mit 
B.L. nie Probleme gehabt in der Beziehung. Freut mich jedenfalls, dass 
es jetzt geht. Wie gesagt, man musss die TIMER1 Re-load Nummer den 
internen Verzoegerungen anpassen falls Genauigkeit notwendig ist.

Welcher Bootloade (Zur Warnung) ist das?

Viele Gruesse,
Gerhard

von Der Alois (Gast)


Lesenswert?

Hallo,
es war der Bootloader 5 für 4 MHz. Kann es mir aber nicht erklären, dass 
das einen solchen Einfluß hat, da ich die ganzen Konfigurationen gleich 
vorgenommen habe wie Sprut.
Jedenfalls bekomme ich nach etwas anpassen jetzt exakt 200µs hin.
Gruß Michael

von Gerhard. (Gast)


Lesenswert?

Hallo Michael,

den Bootloader den ich ab und zu verwende, hat m.W. keine Seiteneffekte, 
nur ist er etwas gross. Ich verwende ihn deshalb nur bei groesseren 
PICS. Der Bootloader ist in Deiner CCS Library zu finden. Ich habe ihn 
etwas abgaendert, funktioniert aber sehr gut. Es geht halt einfach mit 
einem Terminal Program.

Gerhard

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.