Forum: Mikrocontroller und Digitale Elektronik Drehgeber auf extern Interuppt LPC ARM


von Meyer (Gast)


Lesenswert?

So nun noch mal, habe dies schon mal gepostet, aber unter einer anderen 
nicht mit zutreffenden Überschrift. Ich möchte über einen Drehgeber 
einen externen Interupt auslösen. Hier folgender Code:
1
int zaehler = 0;
2
#define VPBDIV_VAL  0x02                   /*Setze Hardwaretimer[Pclk] auf 30 Mhz */
3
4
__irq void eint0 (void) {
5
6
  IOCLR0 = 0x00000100;                    /* Turn LED On  (P0.8 = 0) */  
7
  ++zaehler;
8
  EXTINT = 0x00000003;
9
  VICVectAddr = 0x0000000;                            // Acknowledge Interrupt
10
}
11
void init_extern(void) 
12
{
13
   /*VPBDIV muss(!!) man auf 0 gesetz werden bevor man ein(!) Register
14
  beschreiben kann*/
15
  // Hardwaretimer auf Null setzen
16
  VPBDIV=0;
17
  //Register kontrolliert falls
18
  EXTMODE = (1<<3);
19
  // Hardwaretimer wird auf 30 Mhz gesetzt 
20
  VPBDIV=VPBDIV_VAL;
21
  // Hardwaretimer auf Null setzen
22
  VPBDIV=0;
23
  // Register kontrolliert welcher Pin eine Flanke auslöst
24
  EXTPOLAR = ~(1<<3);  //fallende Flanke
25
  // Hardwaretimer wird auf 30 Mhz gesetzt 
26
  VPBDIV=VPBDIV_VAL;
27
   // ein Register per Interrupt
28
  VICVectCntl2 = (0x20 | 0x20000) ;
29
  //Vector wird Slot 2 zugewiesen
30
  VICVectAddr2 = (unsigned)eint0;
31
   //Aktiviert Interrupt Eingänge
32
  VICIntEnable = ( 0x20000 );
33
}
34
******** Hauptprogramm  **********/
35
int main (void)
36
{
37
  IODIR1 = 0x00FF0000; 
38
  PINSEL1|=(1<<29);      //P0.30 als EINT3
39
   PINSEL1&=~(1<<28);
40
    IODIR0 = 0x00000100;                     
41
     
42
  init_extern();
43
/*Programm in einer Endlosschleife*/
44
while (1)
45
  {     
46
     
47
  }
So sieht meine Start-Code aus:
1
I_Bit           EQU     0x80   ; when I bit is set, IRQ is disabled
2
F_Bit           EQU     0x40   ; when F bit is set, FIQ is disabled
3
4
UND_Stack_Size  EQU     0x00000000
5
SVC_Stack_Size  EQU     0x00000020
6
ABT_Stack_Size  EQU     0x00000000
7
FIQ_Stack_Size  EQU     0x00000000
8
IRQ_Stack_Size  EQU     0x00000080
9
USR_Stack_Size  EQU     0x00000400
Ich weiß nicht mehr weiter es will nicht laufen. Benutze auch schon 
einen Pull_Down Widerstand um wirklich auf low-Level zu sein!

von Falk B. (falk)


Lesenswert?

Einen Drehgeber tastet man per Timerinterrupt ab, NICHT mit einem 
exterenen Interrupt. Warum das so ist steht im Artikel.

von Peter D. (peda)


Lesenswert?

Meyer wrote:
> Ich weiß nicht mehr weiter es will nicht laufen. Benutze auch schon
> einen Pull_Down Widerstand um wirklich auf low-Level zu sein!

Und wie erzeugst Du "wirklich" High?
Zeig dochmal den Schaltplan zu Deinem Programm.


Peter

von Hotte (Gast)


Lesenswert?

@falk Sehr schön aber wie stelle ich das auf einem ARM board dar. Dort 
habe ich keine Funktion wie sei() oder cli()?

@Dannegger Ich benutze ein Board der Frima Phytec mit einem LPC2294 und 
einem Patch-Feld (HD200) und zu Testzwecken gebe ich nun auch meinen 
Port EINT3 (Port30) eine Spannung von 3.3 Volt drauf um zu sehen ob 
irgendetwas kommt, befor ich meinen Inkrementellen Drehgeber anschließe.

von Peter D. (peda)


Lesenswert?

Hotte wrote:

> @Dannegger Ich benutze ein Board der Frima Phytec mit einem LPC2294 und
> einem Patch-Feld (HD200) und zu Testzwecken gebe ich nun auch meinen
> Port EINT3 (Port30) eine Spannung von 3.3 Volt drauf um zu sehen ob
> irgendetwas kommt, befor ich meinen Inkrementellen Drehgeber anschließe.

Damit kann ich absolut garnichts anfangen.
Du mußt schon den richtigen Schaltplan malen.
D.h. wie ist der Drehgeber und die LED konkret angeschlossen.


Peter

von Hotte M. (marvinthevirus)


Lesenswert?

@ Peter, sorry mein Scanner hat den Geist aufgegeben, habe nun den 
Schaltplan fotografiert. Kann ich dir denn auch zu mailen, da die Datei 
viel zu groß ist? Ich bedanke mich auch schon mal für deine Mühe! Als 
LED benutze ich den Port 0.8 der auch auf dem Board festverdrahtet ist 
und auch voll funktionesfähig ist. Am Port 30 habe ich einen 10 kohm 
Widerstand mit der Masse verbunden:

 pos. Flanke 3.3 Volt
  |
  |
  --------------Eingang MC
  |
  |
  R1 10k[Pulldown]
  |
  GND
Nun gebe ich ganz einfach gehalten mit einem Kabel das den Drehgeber 
simulieren soll 3.3 Volt auf den Port 30.

von let (Gast)


Lesenswert?

Poste doch mal das vollständige Projekt. So kann das kein
Mensch nachvollziehen.

Es gibt so etwas wie cli()/sei() auf den ARM7 Controllern. Daran
soll es nicht scheitern. Im einfachsten Fall kannst du das interrupt
enable Flag für den Timer abschalten.
Ich versuche das jedoch zu vermeiden was mir bisher auch (fast)
immer gelungen ist. Ein Blick in das Listfile und in ein C-Buch
zum Thema 'volatile' zeigt das viele dieser cli()/sei() Orgien
unnötig sind. Das sei() ist in einer Abfrageroutine sogar gefährlich.
Ohne Timer wirst du das Prellen des Encoders aber kaum in den Griff
kriegen. Du könntest natürlich auch mit delays arbeiten aber
Abfragezeiten für einen Encoder im Bereich von ms sind bereits
auf langsameren Controllern unpraktisch. Bei Zyklenzeiten von
~20ns ist ein delay() in der Hauptschleife geradezu unverantwortlich.

von Hotte M. (marvinthevirus)


Angehängte Dateien:

Lesenswert?

So ich glaube dass mit dem externen Interrupt lasse ich liebe, wei ich 
mir den Rat von Herrn Brunner zu Herzen genommen habe. Ich habe jetzt 
versucht das Code-Beispiel Drehgeber von Herrn Dannegger auf einen 
LPC2294 umzuschreiben. Könnte sich jeder mal den Code anschauen ob ich 
irgendwo einen Fehler gemacht habe, wäre verdammt nett von Euch.

Außerdem habe ich noch Verständnisprobleme mit dem Code:
1
if (enc_diff & 1)         // bit 0 = value (1)
2
{
3
enc_last = enc_new;       //speicher new als nächten Wert
4
enc_delta += (enc_diff & 2)-1; // bit 1 = direction (+/-)
5
}

Kann mir jemand mal ganz genau erklären, was ich mit dieser if-Anweisung 
genau mach!

von Hotte M. (marvinthevirus)


Lesenswert?

@let hab gerade vergessen zu fragen, wie bekomme ich die funktionen bei 
einem LPC2294 denn?

von Peter D. (peda)


Lesenswert?

Hotte Meyer wrote:
> LED benutze ich den Port 0.8 der auch auf dem Board festverdrahtet ist
> und auch voll funktionesfähig ist.

So, dann müßte man nur noch wissen, ob die LED gegen VCC oder GND 
geschaltet ist.

Hint:
Kontakte werden üblicher Weise gegen GND geschaltet, weil man dann den 
internen Pullup des MCs aktivieren kann.
LEDs werden üblicher Weise gegen VCC geschaltet.
Kontakte und LEDs sind also in der Regel Low-aktiv.

Ich hab nicht viel Erfahrung mit dem LPC. Ich hab da nicht so den 
richtigen Antrieb, einfache Sachen, die ein 8-Bitter spielend schafft, 
mit nem 32-Bit-Boliden zu machen.


Versuche mal schrittweise vorzugehen:
- LED schalten
- LED über Delayschleife blinken
- LED über Timer blinken
- LED über Timerinterrupt blinken
- LED über Eingangspin schalten
- LED über externen Interrupt schalten.

Danach solltest Du wissen, wo es klemmt.


Peter

von Peter D. (peda)


Lesenswert?

let wrote:
> Es gibt so etwas wie cli()/sei() auf den ARM7 Controllern.

Leider nicht.
Die Applikation kann nicht global Interrupts sperren (privilegierte 
Instruktion).
Es ist daher beim ARM-7 einfacher und schneller, nur den Timerinterrupt 
selber zu sperren.


Peter

von let (Gast)


Lesenswert?

Wenn das Programm im User Mode läuft kann man das CPSR Register
nicht verändern. Aber solange man kein spezielles OS benutzt
das den User Mode für seine Prozesse erfordert kann man die
Anwendung auch gleich im System Mode ausführen.

Ich sehe aber keinen Grund dafür weshalb man alle Interrupts
sperren sollte nur um den Timer-Interrupt zu unterdrücken/verzögern.

@Hotte Meyer:
Eine cli()/sei() Implementierung findest in den Blinky Beispielen von
WinARM.

Du könntest auch mal versuchen was dabei herauskommt wenn du
den Code
1
long encode_read1 (void)
2
{
3
  long val;
4
5
  val = enc_delta;
6
  enc_delta = 0;
7
  return val;
8
}
in diesen
1
long encode_read1 (void)
2
{
3
  static long prev;
4
  long val, tmp;
5
6
  tmp = enc_delta;
7
  val = tmp - prev;
8
  prev = tmp;
9
10
  return val;
11
}
abänderst.

Dann kann dir ruhig ein Interrupt dazwischenfunken, es wird nur einmal
in einem Zyklus auf enc_delta zugegriffen.
Ich habe das gerade mal durch den Compiler gejagt und kann keine
Race Condition erkennen.

von Hotte M. (marvinthevirus)


Lesenswert?

Danke Euch allen. Ich werde das gleich mal ausprobieren, mlede mich 
bestimmt morgen wieder;-)

von Meyer (Gast)


Angehängte Dateien:

Lesenswert?

Habe den Code geändert, doch in der Funktion long encode_read1 (void) 
wird mir immer Null an mein Hauptprogramm übergeben und ich weiß nicht 
warum. Kann mir vielleicht jemand sagen wo mein Fehler liegt?

von Peter D. (peda)


Lesenswert?

Meyer wrote:
> Habe den Code geändert, doch in der Funktion long encode_read1 (void)
> wird mir immer Null an mein Hauptprogramm übergeben und ich weiß nicht
> warum. Kann mir vielleicht jemand sagen wo mein Fehler liegt?
1
#define PHASE_A     0x00000800    // Port 11
2
#define PHASE_B    0x00002000    // Port 13

Zahlen sind keine Porteingänge, sondern nur Zahlen.
Und diese beiden Zahlen sind immer wahr.

Hast Du auch beide Pullups aktiviert oder Pullups angeschlossen?


Peter

von Hotte M. (marvinthevirus)


Angehängte Dateien:

Lesenswert?

Erst einmal Danke an alle dir mir geholfen haben. Es läuft endlich mit 
vollster Zufriedenheit. Besonderen Dank an Herrn Dannegger der die 
Code-Vorlage bereit gestellt hat.
Meine letzten beiden Fehler waren 1. Zuweisung der Ports und 2. Abruf 
der Ports. Manchmal sieht man den Wald vor lauter Bäumen nicht.
Für alle die es interressiert hab ich meinen Code noch mal hochgeladen.

Schönen Abend noch, die nächsten Probleme kommen bestimmt. ;-)

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.