Forum: Mikrocontroller und Digitale Elektronik PIC18F13K50 Zähler


von Diotor (Gast)


Lesenswert?

Hi,
ich möchte ein Event loggen (wenn Spannung über Schwellwert). Dazu 
wollte ich den Comparator benutzen: der Interrupt des Comparators soll 
dann einen Timer starten.
Der Timer bekommt seinen Takt vom internen Oszillator (16MHz) / 4 und 
Prescale 16 sollte also mit 250Hz laufen. Kann man das ganze irgendwie 
im Hintergrund laufen lassen, so dass die anderen Funktionen des PIC 
nicht beeinträchtigt werden oder muss ich bei jedem Timerüberlauf die 
Sekunden per Software berechnen und hochzählen?

Danke

von usuru (Gast)


Lesenswert?

mit dem Bit TMR1ON kannst Du z.B. den Timer1 starten und stoppen. TMR1ON 
setzt Du, wenn der Comparator Deine Bedingung erfüllt.

von Lehrmann M. (ubimbo)


Lesenswert?

Diotor schrieb:
> Kann man das ganze irgendwie
> im Hintergrund laufen lassen, so dass die anderen Funktionen des PIC
> nicht beeinträchtigt werden oder muss ich bei jedem Timerüberlauf die
> Sekunden per Software berechnen und hochzählen?

Wie von meinem Vorredner gesagt machen und dann für den Timerüberlauf 
auch einen Interrupt ...

von Diotor (Gast)


Lesenswert?

Danke für die Antworten, so wie ich das Versteh wäre die Umsetzung 
folgende:
PSEUDOCODE
while(!ComparatorInterrupt);
TMR1ON
if(TMR1Interrupt)
...
somit könnte ich also den Zähler umsetzen, aber der PIC kann in dieser 
Zeit z.B. kein I2C Protokoll sprechen. Darum wollte ich fragen ob der 
Zähler hardwaremäßig im Hintergrund laufen kann, sprich parallel laufen 
kann.

von usuru (Gast)


Lesenswert?

das Ganze mal in Pseudocode ohne Interrupt (die Register für den 
Comparator habe ich jetzt mal nicht rausgesucht, weiss ich nicht 
auswendig). Der Timer muss gf. noch den Startwert 0 erhalten.
1
  do until comparator        ; wait for comparator
2
  loop
3
4
  clear_bit PIR1, TMR1IF     ; Timer 1 flag bit rücksetzen
5
  set_bit   T1CON, TMR1ON    ; Timer 1 starten
6
7
  do until PIR1, TMR1IF      ; wait for Timer 1 Overflow
8
  loop

von Carsten S. (dg3ycs)


Lesenswert?

Hi,

usuru schrieb:
> das Ganze mal in Pseudocode ohne Interrupt (die Register für den
> Comparator habe ich jetzt mal nicht rausgesucht, weiss ich nicht
> auswendig). Der Timer muss gf. noch den Startwert 0 erhalten.
>
>
1
> 
2
>   do until comparator        ; wait for comparator
3
>   loop
4
> 
5
>   clear_bit PIR1, TMR1IF     ; Timer 1 flag bit rücksetzen
6
>   set_bit   T1CON, TMR1ON    ; Timer 1 starten
7
> 
8
>   do until PIR1, TMR1IF      ; wait for Timer 1 Overflow
9
>   loop
10
> 
11
>

Das würde aber genau das bedeuten was der TE nicht will...
Die Blockade des sonstigen Programmablaufs.

Diotor schrieb:
> somit könnte ich also den Zähler umsetzen, aber der PIC kann in dieser
> Zeit z.B. kein I2C Protokoll sprechen. Darum wollte ich fragen ob der
> Zähler hardwaremäßig im Hintergrund laufen kann, sprich parallel laufen
> kann.

JA - das kann der PIC. Wie von anderen schon geschrieben - Du musst mit 
Interrupt arbeiten.
Ich habe aber trotzdem noch nicht ganz kapiert was du vorhast...
Also klar ist: Der PIC überwacht eine Spannung, überschreitet diese den 
Grenzwert, dann soll der Zähler gestartet werden...
Aber was soll dann passieren? Also was willst du nun auswerten? Die Zeit 
die seit diesem Ereigniss verstrichen ist? DAs ist AUCH für den 
folgenden Code von Bedeutung...

Aber hie rmal ohne Garantie auf korrekten Syntax wie ich das gerade im 
Kopf habe. (Arbeite durchaus mit mehreren Prozessoren und habe keine 
Lust jetzt genau nachzuschauen...)

#irgendetwas Codeähnliches
double zaehl

void main(void)
{
   // Hier steht das Hauptprogramm
   while(1)
{

void interrupt isr(void)
{
   if (TMR1IF) // Hat der Timer 1 den Interrupt ausgelöst?
   {
      zaehl++
      TMR1IF = 0; // Timer interrupt zurücksetzen
   }

   if (weitere Interrupabfrage) //    {
      ...
      ...
      ...
   }
}

Jetzt wird nach dem Start des Timer 1 bei jedem überlauf ein Interrupt 
ausgelöst. während der Interruptbearbeitung wird der Zähler zaehl um 
eins erhöht. Willst du dann irgendwann die Zeit wissen die seit der 
Auslösung vergangen ist musst du "(zaehl*256)/250= Zeit in Sekunden" 
berechnen.
Zwischen den einzelnen Interrups arbeitet der PIC normal weiter.
Selbst wenn du die Interruptbearbeitung kurzzeitig blockierst belibt 
dein Zählergebniss sogar noch richtig solange die Pause nicht länger als 
eine Überlaufperiode ist...

Aber vorsicht: ICh habe gerade mit heftigen Kopfschmerzen und Fieber zu 
kämpfen. ggf. schreibe ich hier auch gerade Quatsch...

Gruß
Carsten

von Diotor (Gast)


Lesenswert?

Ja genau will einfach die Zeit wissen wann die Spannungsspitze war.
Ist Interrupt ein Schlüsselwort oder wie muss ich den Code verstehen, 
bzw wie wird die Interruptroutine aufgerufen?

von Carsten S. (dg3ycs)


Lesenswert?

Diotor schrieb:
> Ja genau will einfach die Zeit wissen wann die Spannungsspitze war.
> Ist Interrupt ein Schlüsselwort oder wie muss ich den Code verstehen,
> bzw wie wird die Interruptroutine aufgerufen?

Ich bin jetzt davon ausgegangen das dir das bekannt ist, denn du hast ja 
oben erwähnt das der Timerstart auch über den Comparatorinterrupt 
geschehen soll.

ICh kenne deine Vorkenntnisse jetzt nicht, also auch nicht ob du z.b 
etwas von der ASM Programierung der Pics verstehst...
Daher hier nur die Kurzform und anschließend der Verweis auf 
Beispielcode samt Literatur ;-)

Also, intern läuft das mit dem Interrupt ja so ab, das der µC bei einem 
Ereigniss welches einen Interrupt auslöst auf eine bestimmte 
Speciherstelle im Programmspeicher springt und den Befehl der dort steht 
ausführt. Bei den 16er Pics war dafür zum Beispiel 0004 üblich.
Bei 18F kenne ich jetzt 08 und 18. Bei dem 18er gibt es zwei 
Interrupteinsprünge weil es zwei Prioritäten gibt... Weiteres unter den 
Links...

Entweder geht nun an dieser Stelle die Interruptbearbeitung los, oder 
man schreibt einfach einen Sprungbefehl zu einer Abarbeitungsroutine die 
schlicht irgendwo im Code stehen kann.

Bei ASM ist das ganz einfach gewesen, da hat man im Editor einfach mit 
dem Befehl ORG 004 die Speicherzelle ausgewählt und ab da 
losgeschrieben.
(vorher von der Startadresse aber noch einen Sprung auf das 
Hauptprogramm gemachtm also die Interruptbehandlung übersprungen)
Da war es auch noch recht übersichtlich alles direkt auf 04 folgen zu 
lassen.

Bei C geht das nicht so ohne weiteres als C Befehl. (Zumindest habe ich 
es anders gelernt)
Deshalb bindet man diesen einen Sprungbefehl als Assemblerbefehl ein und 
setzt den an die Interruptadresse. Das Sprungziel hingegen ist aber der 
Beginn einer C Funktion. Diese könntest du auch 
"unterbrechungsabarbeitungsschleifevonmirselbst" nennen, passt nur nicht 
zu den Konventionen, Libs und Programmstückchen zum einbinden. Daher 
würde ich mich an die von Microchip vorgegebene Namensgebung halten.

Also zusammenfassend:
1. Die Interruptroutine ist eine von dir selbst erstellte Funktion die 
nicht anders ist als andere Funktionen. Allerdings müssen in dieser 
Funktion die Interruptflags abgefragt werden wenn man die Interrups 
unterscheiden will - Und auch die Flags gelöscht werden, sonst geht es 
gleich wieder los.

2. Rein kommt man über einen Sprungbefehl den man an einer festen Stelle 
im Speicher ablegen muss. Das ist das Besondere am Interrupt !

Wie es jetzt genau geht, da schaue dir mal Übungsbeispiele ("Lesson 
Files") an die man mit dem Pickit3 Debug Express bekommt. Diese kann man 
frei runterladen, dürfte für alle 18er Pics identisch sein, mit den 
Einsprungadressen müsstes du evtl noch mal im 13K50 Datenblatt 
nachschauen (meine die waren aber gleich, bin mir abe rnicht sicher 
-zuviele Proz in verwendung ;-) !)

Zu den "Lesson Files" bekommt man noch das Begleithandbuch in dem etwas 
dazu erklärt ist. Die Lektionen bauen aufeinander auf, falls etwas unkla 
sein sollte ggf. auch mal die Lektion vorher anschauen.
Du kannst sicher auch etwas Copy & Paste machen.
Die Files und das Handbuch findest du hier am ende der Seite:
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en538340&redirects=pickit3
Du möchtest haben:
"PICkit 3 Debug Express Lesson Files"
"PICkit 3 Debug Express Lessons User's Guide"

Interrupt ist Abschnitt 8, habe gerade noch einmal nachgesehen
Davor mal zu schauen ist aber nicht verkehrt...
Ich hoffe das hilft dir weiter ;-)

Die Zeiterfassung solltest du dann selber hinbekommen.
Wenn du aber doch anregungen brauchst, auf Sprut.de gibt es Beispiele 
für eine Uhr, sind zwar in ASM und für den 16er, aber die Grundidee kann 
man dann auch auf C und 18er übertragen. Geht ja nur ums Prinzip.
Ausserdem möchte ich wetten im Netz findet sich auch was für C und 18er.
Bei den Sprut beispielen schätze ich aber das die wirklich rudimentär 
sind, also nur das notwendige drin ist.

Ach ja: Wie genau soll die Uhr denn sein?
Soll die genauer als eine Sekunde sein musst du natürlich noch 
zusätzlich zum Zähler in der Interruptbearbeitung auch noch bei der 
Auswertung den momentanen Stand des TMR1 auslesen...


Gruß
Carsten

von Stampede (Gast)


Lesenswert?

Hallo,

Schoener Post, Carsten.
Noch eine kurze Anmerkung zum Komparator:
Das Ding ist nur bedingt schnell. Neben dem eigentlichen Propagartion 
Delay ist auch zu beachten, dass der Eingang ausreichend ueber dem 
eingestellen Referenzpegel liegt und dass dies lange genug der Fall ist. 
Das musst du bei deinem Signal bzw. dessen Auswertung reuecksichtigen. 
Mal so ein Beispiel: Ich habe einen Komparator auf 3,5V Vref 
eingestellt, und da ein 4 MHz Sinus drangehaengt, dessen Amplitude bei 
rund 3,7V lag (DC Level 2,5V). Der Komparator hat das dann, auch nach 
vielen Perioden, nicht erkannt. Wenn dein Signal aber deutlich langsamer 
ist, ist das kein Problem.

Gruss,
Stampede

von Diotor (Gast)


Lesenswert?

Ich habe versucht die Interruptroutine wie folgt umzusetzen, doch es tut 
sich nichts.
1
void isrh(void);
2
3
#pragma code InterruptVectorHigh = 0x08
4
void InterruptVectorHigh (void)
5
{
6
  _asm
7
    goto isrh
8
  _endasm
9
}
10
11
#pragma code
12
13
#pragma interrupt isrh
14
void isrh(void)
15
{
16
  if (PIR2bits.C1IF) //wenn sich der Comparatorausgang aendert
17
  {  
18
    OpenTimer0( TIMER_INT_ON & T0_16BIT & T0_SOURCE_INT & T0_PS_1_256);
19
    TMR0H = 0xC2;
20
    TMR0L = 0xF7; //Startwert
21
    PIR2bits.C1IF = 0;
22
    if(INTCONbits.TMR0IF)
23
    {
24
      INTCONbits.TMR0IF = 0;
25
      //timer0();
26
    }
27
    //
28
  }
29
}
30
31
void main(void)
32
{....}
Was ist falsch?

von Diotor (Gast)


Lesenswert?

in main() sind noch die Interrupts enabled, das hatte ich vergessen
//Interrupts
  INTCONbits.GIE = 1;
  PIR2bits.C1IF = 0;
  PIE2bits.C1IE = 1;

von usuru (Gast)


Lesenswert?

auf den ersten Blick fehlen noch

INTCONbits.PIE = 1;
INTCONbits.INT0IE = 1;

von usuru (Gast)


Lesenswert?

pardon, vertippt:

muss weg:   INTCONbits.INT0IE = 1;
dafür rein: INTCONbits.TMR0IE = 1;

von Diotor (Gast)


Lesenswert?

Danke das wars (INTCONbits.PEIE)

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.