mikrocontroller.net

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


Autor: Der Alois (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:

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

void main()
{
   SET_TRIS_B( 0x00 ); //alle Pins von Port B 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);
      }

}

Die folgende h-Datei habe ich eingebunden:
#include <18F2550.h>
#device adc=8
#use delay(clock=4000000)
#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

Autor: Daniel P. (ppowers)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Der Alois (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Daniel P. (ppowers)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Der Alois (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Daniel P. (ppowers)
Datum:

Bewertung
0 lesenswert
nicht 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
set_timer1(65335); 
einfach weglässt? Die gemessene Frequenz sollte dann etwa 15 Hz 
betragen. Stimmt das denn wenigstens?

Gruß
Daniel

Autor: Gerhard. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Der Alois (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Gerhard. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Der Alois (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Der Alois (Gast)
Datum:

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

Autor: Gerhard. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Daniel P. (ppowers)
Datum:

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

Autor: Der Alois (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Der Alois (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hier ist das .lst-file

Autor: Gerhard. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Der Alois (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann wünsch ich nen guten Arbeitstag. Danke für Eure Hilfe!

Autor: Gerhard. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Gerhard. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Der Alois (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Gerhard. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Der Alois (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Gerhard. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.