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
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
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
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
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.
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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