Forum: Mikrocontroller und Digitale Elektronik Interrupt programmierung mit HI-Tech


von Quigon (Gast)


Lesenswert?

Hallo an die Communtie,
ich mache/schreibe zur zeit meine BA. Meine Stärken liegen eigentlich 
eher in der Hardwareerstellung und weniger in der Softwareerstellung. 
Leider kommt man nicht mehr um den µ-Controler rum.

Benutzen tu ich den PIC16F685 und den Hi-Tech Compiler. Von Assembler 
habe ich null Ahnung, aber einigermaßen in C.

Mein Problem was ich jetzt habe ist folgendes, am Eingang RB7 soll ein 
Interrupt auf Fallender Flanke ausgelöst werden. Dann soll ein Zähler 
gestartet werden der von der ersten fallenden Flanke bis zur nächsten 
fallenden Flanke zählt.

Meine Frage an euch, wie löse ich einen solchen Interrupt an diesem 
Eingang aus? Wie muss ich diese Interrupt routine programmieren? Ehrlich 
gesagt hab ich auf diesem Gebiet mit Interrupts null Ahnung.
Mein Betreuer kann mir auch nicht helfen weil er nur Ahnung von 
Assembler hat.

Also seit bitte Nachsichtig und so erklären das es ein Vollidiot wie ich 
es versteh.

von Martin S. (drunkenmunky)


Lesenswert?

Du musst halt zuerst alles so konfigurieren wie du es haben willst. Also 
z.B. u.a. den Interrupt-on-change an dem Port und alle Interrupts 
einschalten.

Bei neueren PIC16 gibts verschiedene Flags für fallende und steigende 
Flanke, was die Erkennung etwas erleichtert. Weiß nicht ob es der auch 
hat. Ich würd dir sowieso empfehlen einen enhanced-midrange, also einen 
16F1xxx zu nehmen.

PIC16 haben ja eh nur eine Interruptpriorität und einen Interruptvektor. 
Das heißt du brauchst auch in C nur eine ISR. In der fragst du dann ab, 
wer den Interrupt ausgelöst hat. Kennzeichnen tust du die ISR bei 
Hi-Tech mit "interrpupt" von dem Funktionsnamen. Also z.B. so:
1
void interrupt my_isr(void)
2
{
3
  /***** Timer 0 Code *****/
4
  if((TMR0IE)&&(TMR0IF))
5
  {
6
    TMR0IF = 0;  
7
  }
8
}

Was studierst du eigentlich? Aber nicht Elektrotechnik, oder?

von Quigon (Gast)


Lesenswert?

Optoelektronik studiere ich.

Muss ich die Interruptroutine im Hauptprogramm aufrufen oder ist das 
eine Routine die im Hindergrund läuft?

von Martin S. (drunkenmunky)


Lesenswert?

Aufrufen musst du sie gar nicht. Wenn ein Interrupt ausgelöst wurde, 
wird sie automatisch aufgerufen. Sie läuft nicht im Hintergrund, sondern 
unterbricht dein laufendes Programm (deswegen heißt's auch interrupt ;-) 
)

von Lehrmann M. (ubimbo)


Lesenswert?

Quigon schrieb:
> Muss ich die Interruptroutine im Hauptprogramm aufrufen oder ist das
> eine Routine die im Hindergrund läuft?

Der Sinn und Zweck eines Interruptes ist, dass man sich im "Normalen" 
Betrieb nicht darum kümmern muss. Einfachstes Beispiel du möchtest einen 
Eingang auf das vorhandensein des High-Pegels testen:

Möglichkeit 1 - du frägst ihn ständig ab. Das nennt man Polling. Wenn 
das die einzige Aufgabe des µCs ist dann ist das ok - wenn du aber noch 
viele andere Dinge zu tun hast, dann geht dir das auf die Nerven, da es 
immer Zeit kostet, die anderen Vorgänge aufhält und du unter Umständen 
ein Ereignis (Highpegel) verpasst, weil dein µC gerade etwas anderes 
Berechnet und den Eingang gerade nicht abfrägt. Schlecht.

Möglichkeit 2 - der Interrupt. Der läuft immer im Hintergrund und löst 
und dann aus, wenn z.B. ein Pegelwechsel stattgefunden hat. Jetzt wird 
dein Hauptprogramm unterbrochen und in die sog. ISR (Interrupt Service 
Routine) gesprungen. Da kannst du jetzt auswerten, welcher Pegel sich 
geändert hat und ggf. die Information der Pegeländerung 
weiterverarbeiten, etc. Ist die ISR vorbei (=durchgelaufen) erfolgt ein 
Sprung ins Hauptprogramm an die Stelle wo der Ablauf durch den 
auftretenden Interrupt gestört / unterbrochen wurde.

So nun zu deinem Problem: Einen Interrupt können verschiedene Quellen 
auslösen. Eine kleine Einführung findet sich hier für PIC16 - für andere 
PICs gilt das analog. http://www.sprut.de/electronic/pic/int/int.htm
Interessant für dich sind die Eingänge die bei einem Pegelwechsel einen 
Interrupt auslösen. Entweder ist das ein ganzer/halber Port oder ein 
spez. Pin. Meist ist es RB0, können auch mehrere Pins sein. Wichtig ist, 
dass du dir das Datenblatt deines PICs herunterlädst (Mircochip.de) und 
dann mal aufmerksam die Sektion die mit INTERRUPTS betitelt ist 
durchliest. Da steht ganz genau drin, welche Pins Interruptfähig sind 
und ob sie auf positive oder negative Flanken oder auf jeden 
Pegelwechsel reagieren sollen und und und. Vieles aus der Sektion kannst 
du weglassen (Schnittstelleninterrupts, etc..)


Viel Erfolg

von Quigon (Gast)


Lesenswert?

Hallo vielen Dank an euch. Softwaretechnisch dürfte es kein problem mehr 
sein.
Aber jetzt nochmal ne vorsichtige Frage, der Interrupt wird von einem 
Monoflop am RB7 Interrupt of Change ausgelöst.
Das Problem an der Sache ist oder eher ein Verständnisproblem. Am RB7 
liegt durch das Monoflop ein High Pegel an, der Interrupt soll durch das 
kurze Schalten vom Monoflop auf Low Pegel den Interrupt auslösen.

Wird dadurch trotzdem der Interrupt am RB7 ausgelöst? Oder muss ich so 
wie ich es vermute den Ausgang meines Monoflops vertauschen das dann der 
Interrupt durch ein High Pegel ausgelöst wird.

von Martin S. (drunkenmunky)


Lesenswert?

Geht beides.

Lehrmann Michael schrieb:
> Da steht ganz genau drin, welche Pins Interruptfähig sind
> und ob sie auf positive oder negative Flanken oder auf jeden
> Pegelwechsel reagieren sollen und und und.

Positive Flanke - Wechsel von Low auf High
Negative Flanke - Wechsel von High auf Low

von Martin S. (drunkenmunky)


Lesenswert?

Ich hab grad mal geschaut. Bei dem alten Controller, den du verwenden 
willst, löst bei jedem Wechsel ein Interrupt aus. Dann muss du halt 
selbst feststellen, ob es eine postive oder negative Flanke war und dann 
angemessen darauf reagieren.

Die neueren kann man so einstellen, dass nur bei einer negativen Flanke 
ein Interrupt ausgelöst wird. Das hat den Vorteil, dass dein Programm 
nicht unnötig durch postive Flanken unterbrochen wird und du nicht 
selber feststellen musst, ob es negative oder positive Flanke war.

Kannst dir ja z.B. mal den 16F1938 anschauen...

von Quigon (Gast)


Lesenswert?

Ok danke.
Das habe ich jetzt soweit verstanden, muss ich in meiner 
Interruptroutine reinschreiben ob er auf fallender Flanke reagieren soll 
oder wird dies automatisch gemacht?

Und dann mein letztes Problem und das ist wirklich ein 
Verständnisproblem,
ich will ein "software zähler" starten. Der Zähler soll beim ersten 
Interrupt starten und soll beim darauffolgenden Interrupt stoppen. beim 
Zählen soll eine Zeitverzögerung integriert werden.

Zähler natürlich über for (i==0; i==255; i++)
wie bring ich den zähler dann dazu das er beim nächsten Interrupt 
abbricht und muss ich die Zeitverzögerung über eine zählschleife 
realiesieren? oder kann ich da einfach sleep(Zahl) Zahl ein wert 
natürlich, schreiben?

Bevor die Frage kommt für was braucht man das :) dieser wert i wird/soll 
am Ende ins PWM vergleichsregister geschoben werden.

von Martin S. (drunkenmunky)


Lesenswert?

Die Antwort auf deine erste Frage steht schon in meinem letzten Post.

Hier ist noch ein dicker Bock drin, ich denk du findest ihn von selbst:
for (i==0; i==255; i++)

Über was für Zeitspannen reden wir denn da ungefähr?
Wo willst du den Zähler inkrementieren? In deinem Hauptprogramm?

Es gibt für solche Aufgaben eine fertige Einheit, nennt sich Capture 
Module. Unter der Rubrik "Capture/Compare/PWM module".

von Quigon (Gast)


Lesenswert?

Da bin ich leider etwas eingengt durch mein Betreuer/Chef. Der PIC16F685 
ist fix.

Ok du sagt das ich das selber überprüfen muss ob fallende oder 
steigender Flanke, blöde frage wie mach ich das.

Der Zähler wird in einem Unterprogramm sein, weil er nur in einem Fall 
benutzt wird :) und dieser Fall dritt dann ein wenn es ums Dimmen von 
LEDs geht.

von Quigon (Gast)


Lesenswert?

Die unterbrechung sollen 20ms sein

von Martin S. (drunkenmunky)


Lesenswert?

Also du willst die Zeit zwischen 2 negativen Flanken messen, richtig?

Benötigst du den Timer 1 schon für etwas? Der 16F685 hat auch eine 
Capture Einheit.

Die funktioniert so:
-du stellst sie so ein, dass sie bei jeder fallende Flanke reagiert.
-jetzt kopiert die Einheit jedes Mal, wenn sie eine negative Flanke 
detektiert, den Zählerstand (16-bit) in ein Register und setzt ein 
Interruptflag
-den Stand speicherst du dir dann in einer Variable
-beim nächsten Mal hast du dann zwei Werte. Neuer minus alter und du 
hast die Differenz
-Die Prescaler von Timer 1 musst du halt passend einstellen

von Quigon (Gast)


Lesenswert?

Also hab mir die Capture angeschaut. Da ich das PWM benutzen tu weil ich 
dieses ja auch brauch. Kann ich die Capture nicht mehr nutzen oder?

weil wenn ich das trotzdem nutzen kann dann ist das eine tolle sache

Und wie Funktioniert das mit der Flankenüberprüfung?

von Lehrmann M. (ubimbo)


Lesenswert?

Martin S. schrieb:
> Ich hab grad mal geschaut. Bei dem alten Controller, den du verwenden
> willst, löst bei jedem Wechsel ein Interrupt aus. Dann muss du halt
> selbst feststellen, ob es eine postive oder negative Flanke war und dann
> angemessen darauf reagieren.
>
> Die neueren kann man so einstellen, dass nur bei einer negativen Flanke
> ein Interrupt ausgelöst wird. Das hat den Vorteil, dass dein Programm
> nicht unnötig durch postive Flanken unterbrochen wird und du nicht
> selber feststellen musst, ob es negative oder positive Flanke war.

Wenn der PIC16F685 fix ist, dann nimm das CCP (Capture/Compare/PWM) 
Modul. Ist zwar ein kleiner Missbrauch dessen - eignet sich aber 
hervorragend:
http://ww1.microchip.com/downloads/en/devicedoc/41262a.pdf
Datenblatt S. 116


11.1 Capture Mode
In Capture mode, CCPR1H:CCPR1L captures the
16-bit value of the TMR1 register when an event occurs
on pin RC5/CCP1/P1A. An event is defined as one of
the following and is configured by CCP1CON<3:0>:
• Every falling edge
• Every rising edge
• Every 4th rising edge
• Every 16th rising edge
When a capture is made, the interrupt request flag bit,
CCP1IF (PIR1<2>), is set. The interrupt flag must be
cleared in software. If another capture occurs before
the value in register CCPR1 is read, the old captured
value is overwritten by the new captured value.

Also dein Eingangspin in RC5. Ein Capture wird nur gemacht, wenn das 
richtige Ereignis
• Every falling edge
• Every rising edge
• Every 4th rising edge
• Every 16th rising edge
eintritt. Welches das richtige Ereignis ist wird im Register CCP1CON von 
den Bits 0 bis 3 bestimmt. Siehe Dateblatt.

Nachdem der Capture nur bei dem richtigen Ereignis gemacht wird, wird 
auch der Interrupt für einen Capture nur beim richtigen Eregins gesetzt 
und entsprechend nur dann in die ISR gesprungen. Da hast du was du 
suchst.

Quigon schrieb:
> Und dann mein letztes Problem und das ist wirklich ein
> Verständnisproblem,
> ich will ein "software zähler" starten. Der Zähler soll beim ersten
> Interrupt starten und soll beim darauffolgenden Interrupt stoppen. beim
> Zählen soll eine Zeitverzögerung integriert werden.

Dann schau dir das Capture-Modul weiter an:
In Capture mode, CCPR1H:CCPR1L captures the
16-bit value of the TMR1 register when an event occurs
on pin RC5/CCP1/P1A.
Also dein Timer1 läuft ja permanent durch, zum Zeitpunkt des 
Interruptsauftretens wird dieser Wert in die beiden je 8 bit breiten 
Register CCPR1H und CCPR1L gesichert. 2x8bit = 16bit = max. Timerwert.
Mit dem gesicherten Wert, kannst du dann weiterarbeiten, ihn als Basis 
verwenden oder was auch immer. Musst mal noch genauer beschreiben was 
die Aufgabenstellung ist.

von Quigon (Gast)


Lesenswert?

Also der Pin ist bei mir ein Ausgang für das PWM. Was jetzt eh erstmal 
wichtig ist, ist der Interrupt das dieser Funktioniert. Da ich den RA2 
Eingang noch frei habe, werde ich von meinem Ursprüngliche Eingang RB7 
eine Leitung auf den RA EIngang legen. Und mit dem RA2 Eingang werd ich 
den Interrupt aufbauen. Soviel wie ich gelesen hab kann ich an diesem 
Eingang sehr gut mit dem Interrupt arbeiten.

Den das Problem ist, es ist eine kleine Firma und im Prinzip arbeite ich 
schon mit einer fertigen Platine also das heist keine änderungen 
möglich. Hab schon davor zwei von den Prototypplatinen zwei kaputt 
gemacht. und wenn ich jetzt noch hergehe und des ganze layout umwerf, 
dann reist mir glaub mein chef den kopf ab :).

Ja das hört sich nach verzweiflung an. Aber alle andere Programmteile 
funktionieren bis auf Interrupt und der Zähler.

Kurze Verständnis wie das ganze funktionieren soll, also über das mono 
wird ein Interrupt ausgelöst. Der Zähler dient dazu wie schon gesagt 
zwischen den ersten und zweiten interrupt zu zählen mit jeweils einer 
zeitverzögerung. Dieser Wert wird soll dann ist PWM vergleichsregister 
geschoben werden und somit die LEDs dim up bzw. dim down. Aber auch 
dieser wert soll im eeprom gespeichert werden.

Aber das sind alles einfache dinge die schon funktionieren bis auf 
zähler und interrupt halt :)

von Lehrmann M. (ubimbo)


Lesenswert?

Quigon schrieb:
> Der Zähler dient dazu wie schon gesagt
> zwischen den ersten und zweiten interrupt zu zählen mit jeweils einer
> zeitverzögerung. Dieser Wert wird soll dann ist PWM vergleichsregister
> geschoben werden und somit die LEDs dim up bzw. dim down. Aber auch
> dieser wert soll im eeprom gespeichert werden.

Zu gut Deutsch: du brauchst die Zeit zwischen den beiden Interrupts? 
Okay das ist nicht soo schwer. Also du nimmst einen Timer und den 
Timerinterrupt. Durch diesen bekommst du eine Zeitbasis. Das heißt dein 
Timer (z.B. 8bit Timer -> siehe Sektion TIMER im Datenblatt) zählt (ohne 
dein Zutun) von 0 bis 255, immer wieder, unendlich lang. Jedesmal wenn 
der Timerwert bei 255 angelangt ist, geht es wieder bei 0 weiter. Diesen 
Sprung von 255 -> 0 nennt man Überlauf (das 8 bit Timerwertregister ist 
voll) und dieser ist eine Interruptquelle (siehe Sektion INTERRUPTS im 
Datenblatt). D.h. deine ISR wird auch im Falle eines Timerüberlaufes 
(255->0) ausgelöst. Das kann man insofern zuverlässig nutzen, dass 
(solange keine anderen nennenswerten / zeitintensiven Interrupts 
auftreten) so alle gewisse Zeitabstände der Timerüberlaufinterrupt 
auftritt (Timer hat von 0 bis 255 gezählt). Dies gibt dir eine 
Zeitbasis. Die TimerTickZeit (Zeit die der Timer zum erhöhen seines 
Wertes um den Wert 1 braucht, 0 -> 1 -> 2 -> 3 -> ....) ist in gewissen 
Grenzen einstellbar (siehe Sektion TIMER im Datenblatt). Wenn du also 
weißt wielange es dauert bis der Wert um 1 erhöht wurde, kannst du dir 
ausrechnen wie lange es dauert, bis ein Überlauf des Timers auftritt 
(256 x TimerTickZeit). In der ISR des Timerüberlaufes zählst du dann 
deine Variable (ich nenne sie time_count) jeweils um eines hoch: 
time_count++;

Also dann der ganze Ablauf:

erster Interrupt (in der ISR des I/O-Interrupts schauen, ob 
entsprechende Flanke auftaucht, etc) -> Timer nullen (Timerwert=0) -> 
Timerinterrupts aktivieren -> ersten Interrupt löschen nicht vergessen


jetzt läuft die ISR des Timerinterrupts ständig duch, die Zählvariable 
time_count erhöht sich mit jedem Timerinterrupt. Währenddessen kann im 
Hauptprogramm ständig was passieren, diese wird ja immer unterbrochen, 
sobald ein Timerinterrupt auftritt. Was nicht sein sollte, ist dass noch 
andere Interrupts auftreten, da diese u.U dazu führen könnten, dass der 
Timerinterrupt nicht rechtzeitig bearbeitet wird (da gerade ein andere 
Interrupt aktiv ist). Abgesehen von dem zu erwartenden Interrupt für die 
nächste Flanke, der wird ja erst aktiv, wenn die Flanke auch auftaucht 
und dann ist die Timerinterrupt ja nichtmehr interessant.

zweiter Interrupt ausgelöst durch die zweite Flanke des mono. 
Timerinterrupts deaktiveren (time_count kann somit nichtmehr erhöht 
werden). time_count "sichern" -> Wert in andere Variable geben. 
time_count wiede =0 setzen, Timerinterrupts wieder aktivieren, die 
nächste Zeitspanne wird gemessen.

Wir erinnern uns, dass wir time_count vorhin gesichert hatten. Das ist 
enorm wichtig - wir wissen wie oft der Timerinterrupt aufgetaucht ist 
(time_count bzw. die Sicherung dessen verrät uns das) und wir wissen, 
wie lange es dauert, bis ein Timerinterrupt auftaucht. Druch die Anzahl 
der Timerüberlauf und die Zeit, wie lange es dauert, bis ein Überlauf zu 
Stande kam, wissen wir wieviel Zeit zwischen den beiden I/O-Interrupts 
(Flanken) vergangen ist. Das ist deine gesuchte Zeit. Die können wir 
dann z.B. im Hauptprogramm (bloss außerhalb der ISR) weiterverarbeiten.

In der Zwischenzeit läuft bereits die nächste Messung bis zur nächsten 
Flanke, ......


....


hoffe geholfen zu haben

von Quigon (Gast)


Lesenswert?

Interrupt Routine geschieben, Er macht nichts wenn ein interrupt 
ausgelöst wird.
Hier der code
#include <htc.h>
#include <pic.h>
#include <pic16f685.h>

#include <math.h>
#include <stdio.h>


#ifndef _XTAL_FREQ

  // Unless specified elsewhere, 20MHz system frequency is assumed

  #define _XTAL_FREQ 20000000

#endif

#define Hand    !RC3
#define Auto    RC3
#define DimUp    !RC4
#define DimDown    RC4




//********************************************************************** 
*********************************//
//Globale Variabeln
//Variable a ist Usereingestellte PWM Wert der Eingelesen wird vom 
EEPROM bzw. noch erstellt werden muuss
//Variable c und z dienen zur Speicherung und Auslesen vom EEPROM
//********************************************************************** 
*********************************//
volatile char a;

volatile unsigned char c,z;

int i;
long int j;



void handfkt(void);
void autofkt(void);
void not(void);
void pwm(void);

void init()
  {
//**********************************************************POR********* 
*********************************//
    //POR an PORT B

    IOCB  = 0b11110000;
    PORTB  = 0b00000000;
    TRISB  = 0b11110000;

    //POR an PORT C

    ANSEL  = 0b11111111;
    ANSELH  = 0b00001111;
    CCP1CON = 0b00000000;
    PORTC  = 0b00000000;
    TRISC  = 0b11111111;

//*********************************************************PORTINI****** 
*********************************//
    PORTA  = 0b00111111;
    TRISA2  = 0b00111111;

  //PORTB
    TRISB  = 0b11000000;
    PORTB  = 0b11110000;

  //PORTC

    TRISC  =  0b11011111;
    PORTC  =   0b11111111;

    ANSEL  =  0b00111111;
    ANSELH  =  0b00000000;

  //PWM

    CCP1CON  =  0b00001101;
    T2CON  =  0b01111110;

  //Interrupt

    INTCON   =  0b11011011;
    IOCB  =  0b11110000;
    IOCA2  =  0b00111111;
  }



void interrupt InterruptRutine(void)    // Interruptroutine
{
char schalter;
schalter=PORTC &0b00001000;
GIE=0;

if(INTF)
  {
  switch   (schalter)
    {
      case  0: handfkt();
      case  1: autofkt();
    }


  INTF=0;    // Flag zurücksetzen

  }

}




int main()
  {
    init();

    turn:;

    INTEDG=0;      // Fallende Flanke am RA2
    GIE=1;        // Interrupts erlauben
    INTE=1;


    goto turn;
  }

von Martin S. (drunkenmunky)


Lesenswert?

Wenn du den Eingang oderso verlegst, wieso nicht grad auf RB7?

Musst du nicht noch IOCA2 aktivieren wenn du den IOC benutzten willst?

Deine Programmierkünste lassen etwas noch etwas zu wünschen übrig...

Quigon schrieb:
> char schalter;
> schalter=PORTC &0b00001000;
> GIE=0;
>
> if(INTF)
>   {
>   switch   (schalter)

Wieso nicht einfach:
switch   (RB3)

Quigon schrieb:
> turn:;
>
>     goto turn;

so macht  man in C keine Schleife, schreib lieber:
while(1)
{
//Dauerschleife
}

Quigon schrieb:
> INTEDG=0;      // Fallende Flanke am RA2
>     GIE=1;        // Interrupts erlauben
>     INTE=1;

Wieso machst du das ständig? Reicht doch einmal in der Initialisierung.
Und wenn du die Interrupts bei einer ISR ausschalten willst, dann 
schalte sie einfach am Ende wieder an.

von Quigon (Gast)


Lesenswert?

IOCA2 war ein verschreiber muss heisen IOCA
GIE=1 hab ich in die Interrupt geschoben unter INF=0 das gleiche auch 
mit INTE=1
das INTEDG=0 in die ini

jetzt steht nur noch in init(); while(1);

und das programm hängt jetzt in main bei init() und reagiert auch nicht 
auf interrupt.

Solangsam bekomm ich echt die krise mit dem .... nichts funktioniert. 
Und hab echt keinen schimmer mehr was ich machen soll.

von Lehrmann M. (ubimbo)


Lesenswert?

Quigon schrieb:
> #include <stdio.h>

Himmel hilf ... das ist doch nicht dein Ernst oder? Wofür brauchst du 
StandartInputOutput in diesem Projekt?

Quigon schrieb:
> //********************************************************************** 
*********************************//
> //Globale Variabeln
> //Variable a ist Usereingestellte PWM Wert der Eingelesen wird vom
> EEPROM bzw. noch erstellt werden muuss
> //Variable c und z dienen zur Speicherung und Auslesen vom EEPROM
> //********************************************************************** 
*********************************//
> volatile char a;
>
> volatile unsigned char c,z;

Alles  was wir nicht brauchen wird im Fehlerfalle rausgenommen oder 
auskommentiert. Apropos - läuft der PIC überhaupts - kannst du das 
irgendwie Verifizieren? Wie siehts mit den Configbits aus? Was für 
welche hast du gewählt?

Quigon schrieb:
> IOCB  = 0b11110000;
>     PORTB  = 0b00000000;
>     TRISB  = 0b11110000;

Zuerst der TRIS, dann der PORT! Nähmen wir mal an, der Port wäre als 
Input konfiguriert. Dann schreibst du (recht sinnlos) das Register PORTB 
mit Nullen voll. Im nächsten Augenblick steht aber sofort wieder der 
Wert der am Port anliegt drin. Bevor od. Während du sagst ob Eingang 
oder Ausgang. Dann setzt du alles als Ausgang und siehe da im 
PortRegister steht das was am Port anlag und nicht deine 0.

Quigon schrieb:
> ANSEL  = 0b11111111;
>     ANSELH  = 0b00001111;

Ich glaube nicht, dass du alle Eingänge als analoge Eingänge nutzen 
möchtest! Ist genau falsch. Datenblatt Seite 98:

1 = Analog input. Pin is assigned as analog input.(1)
0 = Digital I/O. Pin is assigned to port or special function.
Note 1: Setting a pin to an analog input automatically disables the 
digital input circuitry, weak pull-ups, and interrupt-on-change if 
available. The corresponding TRIS bit must be set to Input mode in order 
to allow external control of the voltage on the pin.

Man beachte Note1 !!

Quigon schrieb:
> PORTC  = 0b00000000;
>     TRISC  = 0b11111111;

hatten wir schonmal den Fehler

Quigon schrieb:
> PORTA  = 0b00111111;
Willst du wirklich zuerst eine Kombination an den Ausgang legen? Was 
soll das werden?

>     TRISA2  = 0b00111111;

Dieses Register gibt es nicht. Es gibt zwar glaube ich ein Bit mit dem 
Namen TRISA2 aber das meinst du nicht und es wäre nur ein Bit (1 oder 0)
Das hier ist nix! Ich nehme an die "2" gehört weg. Eingänge / Ausgänge 
musst du selbst schauen, dass das passt. 1=Eingang , 0=Ausgang

Quigon schrieb:
> //PORTB
>     TRISB  = 0b11000000;
>     PORTB  = 0b11110000;
>
>   //PORTC
>
>     TRISC  =  0b11011111;
>     PORTC  =   0b11111111;
>
>     ANSEL  =  0b00111111;
>     ANSELH  =  0b00000000;

Und weils so schön war gleich mehrmal alles initaliseren - am besten 
auch nochmal falsch =)

Quigon schrieb:
> T2CON  =  0b01111110;

So jetzt wird spannend: Erzähl mal wie du das jetzt durchgerechnet hast 
- alle wieviel Zeiteinheiten bekommst du denn jetzt deinen Interrupt 
geliefert? Erzähl mal, nur damit wir sehen, dass du es verstanden hast.

Quigon schrieb:
> INTCON   =  0b11011011;

1 = Enables all unmasked interrupts (OK)
1 = Enables all unmasked peripheral interrupts (OK)
0 = Disables the TMR0 interrupt (OK)
1 = Enables the RA2/INT external interrupt (Na wo hast du denn nun 
deinen Eingang eigentlich hingelegt)
1 = Enables the PORTA/PORTB change interrupt (OK)
0 = TMR0 register did not overflow
1 = The RA2/INT external interrupt occurred (must be cleared in 
software) (So du setzt also einfach mal dieses Bit und löst in der 
Software einen Interrupt aus ?! Was soll das?)
1 = When at least one of the PORTA or PORTB general purpose I/O pins 
changed state (must be cleared in software) (So du setzt also einfach 
mal dieses Bit und löst in der Software einen Interrupt aus ?! Was soll 
das?)

Note 1: IOCA or IOCB register must also be enabled.

Quigon schrieb:
> IOCB  =  0b11110000;

Gleich mal alles einschalten?! Warum das - um möglichst viele Fehler von 
offenen Pins die undefinierte Zustände einnehmen zu bekommen ? Sinn?

Quigon schrieb:
> IOCA2  =  0b00111111;

Schonwieder ein Regist das es nicht gibt. Das Bit IOCA2 gibt es zwar 
aber das kann man so nicht ansprechen! Das willst du auch nocht - du 
meinst IOCA - auch hier wieder ... hauptsache möglichst viele Interrupts 
anschalten? Sinn? Zweck? Nachgedacht?

Quigon schrieb:
> schalter=PORTC &0b00001000;

Erklärung was das soll?

Quigon schrieb:
> GIE=0;

Wenn du meinst ....

Quigon schrieb:
> if(INTF)

Unterscheidest du garnicht von welcher Quelle (Timer, I/O, ... der 
Interrupt kommt)???? Also egal welcher Interrupt auftritt, z.B. der 
aktivierte Timerinterrupt, egal hauptsache ausführen ? Oo

Quigon schrieb:
> switch   (schalter)
>     {
>       case  0: handfkt();
>       case  1: autofkt();
>     }

Das kannst du dir sparen - Null wird das nie. Da ein paar Zeilen vorher:
> schalter=PORTC &0b00001000;

Quigon schrieb:
> INTF=0;    // Flag zurücksetzen

Gut - Interrupt einschalten (GIE=1) hast du vergessen. GIE schaltet man 
auch eigentlich nicht ab ... außer es wäre notwendig.

Quigon schrieb:
> turn:;
>
>     INTEDG=0;      // Fallende Flanke am RA2
warum kommt das nicht in der Init ?
>     GIE=1;        // Interrupts erlauben
Lass das - einfach garnicht ausschalten - am besten einfach in der Init 
einmal an und fertig!
>     INTE=1;
Bist du sicher, dass du so auf Bits zugereifen kannst? Sorry kenne 
Hitech nicht so gut. Eigentlich kann man nicht so einfach auf ein Bit 
zugreifen. Kann mich aber täuschen. Bei meinem C18 geht das so: 
INTCONbits.INTE = 1;
>
>
>     goto turn;

Merke dir das eine Gesetz: Verwende NIE NIE NIE NIE NIE "Goto" in C und 
schon gleich NIE NIE NIE NIE NIE NIE NIE NIE NIE NIENIE NIE NIE NIE NIE 
NIE NIE NIE NIE NIE NIE NIE NIE NIE NIE NIE NIE NIE NIE NIE NIE NIE NIE 
NIE NIE wieder auf einem Mikrocontroller !!!!

while(1){
 ...
}

statt

> turn:;

turn:           // kein ; !!!!!
...
goto turn;


Puuh hoffe geholfen zu haben

von Quigon (Gast)


Lesenswert?

Also das ist das einzige positive an HI-TECH das du das PORTbits.... 
nicht brauchst. Ich glaube ich geh jetzt lieber mal zum Optiker und lass 
mir ne brille verschreiben man man .... Ich greif auf kein Timer zu.
Aber wenn man den GIE auf Null setzt dann wird die Routine abgearbeitet 
ist das eigentlich nicht ein Vorteil?

So werd es jetzt nochmal probieren.....

von Quigon (Gast)


Lesenswert?

void init()
  {
//**********************************************************POR********* 
*********************************//
    //POR an PORT B

    IOCB  = 0b11110000;
    TRISB  = 0b11110000;
    PORTB  = 0b00000000;

    //POR an PORT C

    ANSEL  = 0b00100000;
    ANSELH  = 0b00000000;
    CCP1CON = 0b00000000;
    TRISC  = 0b11111111;
    PORTC  = 0b00000000;


//*********************************************************PORTINI****** 
*********************************//

    TRISA  = 0b00000100;
    PORTA  = 0b00111111;


  //PORTB
    TRISB  = 0b11000000;
    PORTB  = 0b11110000;

  //PORTC

    TRISC  =  0b11011111;
    PORTC  =   0b11111111;

    ANSEL  =  0b00111111;
    ANSELH  =  0b00000000;

  //PWM

    CCP1CON  =  0b00001101;
    T2CON  =  0b01111110;

  //Interrupt

    INTCON   =  0b11011011;
    IOCB  =  0b11110000;
    IOCA  =  0b00111111;
    INTEDG  =0;
    GIE    =1;
  }



void interrupt isr()    // Interruptroutine
{
char schalter;
schalter=PORTC &0b00001000;


if(INTF)
  {
  GIE=0;
  switch   (schalter)
    {
      case  1: handfkt();
      case  0: autofkt();
    }


  INTF=0;    // Flag zurücksetzen
  GIE=1;
  INTE=1;
  }

}




int main()
  {
    init();



    while(1);
  }


so siehts jetzt aus und auf interrupt reagiert er nicht......

Config bits setz ich über MPLAB

von Martin S. (drunkenmunky)


Lesenswert?

Wenn du eh nicht liest was man dir schreibt, wozu das dann überhaupt?

Setz doch erst mal ALLES um, was mach dir so geschrieben hat!

von Lehrmann M. (ubimbo)


Lesenswert?

Quigon schrieb:
> Ich greif auf kein Timer zu.

Quigon schrieb:
> T2CON  =  0b01111110;

so dann schau mal nach, was für einen Zweck T2CON hat.

Jetzt steht in der init() immernoch so viel Müll und immernoch alles 
doppelt. Ist es denn so schwer das mal zu bereinigen. Du hast ja 
keinerlei Übersicht - hätte ich auch nicht. Ich hab doch so gut wie zu 
jeder Zeile eine Anmerkung gemacht. Vielleicht denkst du mal drüber nach 
was ich da geschrieben habe und ziehst Konsequenzen daraus.

Quigon schrieb:
> Also das ist das einzige positive an HI-TECH das du das PORTbits....
> nicht brauchst.

Asche auf mein Haupt - sorry ....

Quigon schrieb:
> Config bits setz ich über MPLAB

Mir egal wo du sie setzt - wichtig ist welche du setzt. Läuft denn der 
PIC überhaupts an. KontrollLED dazuschalten, die einfach beim 
Programmstart angeht. Oder lass die LED angehen, wenn er zum ersten mal 
in die ISR springt, dann siehst du zumindest, ob überhaupts ein 
Interrupt auftritt. Vielleicht ist es ja auch ein Fehler in deiner 
Schaltung.

Quigon schrieb:
> void interrupt isr()    // Interruptroutine
> {

Ist das überhaupts eine valide ISR in HiTech? Wie gesagt ich kenn den 
Compiler nicht.

Wie sieht denn deine Beschaltung überhaupts jetzt aus? Ich glaube du 
verwendest RA2/INT als Interruptpin.

von Quigon (Gast)


Lesenswert?

Ok machen wir es so wie in der Schule :( Stept by Step)
das ist die Initialiesierung
void init()
  {
//**********************************************************POR********* 
*********************************//
    //POR an PORT B

    IOCB  = 0b11110000;
    TRISB  = 0b11110000;
    PORTB  = 0b00000000;

    //POR an PORT C

    ANSEL  = 0b00100000;
    ANSELH  = 0b00000000;
    CCP1CON = 0b00000000;
    TRISC  = 0b11111111;
    PORTC  = 0b00000000;


//*********************************************************PORTINI****** 
*********************************//

    TRISA  = 0b00000100;
    PORTA  = 0b00111111;


  //PORTB
    TRISB  = 0b11000000;
    PORTB  = 0b11110000;

  //PORTC

    TRISC  =  0b11011111;
    PORTC  =   0b11111111;

    ANSEL  =  0b00111111;
    ANSELH  =  0b00000000;

  //PWM

    CCP1CON  =  0b00001101;
    T2CON  =  0b01111110;

  //Interrupt

    INTCON   =  0b11011000;
    IOCA  =  0b00000000;
    IOCB  =  0b00000000;

    INTEDG  =0;
    GIE    =1;
  }

IOCA/IOCB brauch ich dann eigentlich nicht wenn ich am RA2 über das 
Monoflop einen Interrupt also extern auslöse oder sehe ich das grad 
falsch.
Du kannst dir vorstellen ich drücke einen Schalter schalter drück 
monoflop senden kurzer low impuls. Schalter loslassen zweiter kurzer low 
impuls. Das heist ich kann den abstand zwischen interrupt1 und 2 frei 
varieren und dadurch das pwm bzw das dimmen regeln.

Zum T2CON
also dies ist der Timer für das PWM :)
bit7 ist 0
über den bit6-3 stell ich den teiler ein für das pwm
bit2 timer on
bit1-0 der teiler für timer

ist das soweit jetzt mal richtig? oder hab ich was überlesen?

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.