Forum: Mikrocontroller und Digitale Elektronik Ein- und Ausgänge am selben Port (nicht Pin)


von Attila (Gast)


Lesenswert?

Hallo Leute,

Folgendes:
Ich möchte am selben Port über unterschiedliche Pins Ein- und Ausgänge 
haben.
Als Ausgänge habe ich drei Pins für ein Freigabe, Richtungs, und 
Taktsignal für einen Schrittmotor.
Als Eingang haben ich einen Taster der sobald er ausgelöst wird über 
interrupts den Schrittmotor stoppt.
Beides fürs sich funktioniert wenn ich es getrennt probiere.
Sobald ich jedoch beides gleichzeitig in den Controller lade 
funktioniert die Ansteuerung für den Motor über die drei Leitungen nicht 
mehr.
Daher meine Frage:
Ist es überhaupt möglich am selben Port gleichzeitig Ein- und Ausgänge 
zu verarbeiten?
Wenn ja würde ich um eine kurze Hilfestellung bitten wie die 
Konfiguration der I/Os in dem Fall auszusehen hat.
Verwendet wird ein ATmega6450 und programmiert wird mit AVR-GCC in AVR 
Studio 5.
Vielen Dank schon mal für eure Hilfe! :)

lg Attila

von Lasse S. (cowz) Benutzerseite


Lesenswert?

Ja, ist einwandfrei möglich. Dein Fehler liegt in Zeile 42, weil du da 
die Port-Zuweisungen falsch schreibst.

von Uwe (de0508)


Lesenswert?

Lbr Attila,

bitte sende uns doch dein Programm zu, als Anhang wäre nicht schlecht.

Sonst findet man in AVR-Tutorial noch viele Infos !

Zu deiner Frage, ja es geht.

http://www.mikrocontroller.net/articles/AVR-Tutorial:_IO-Grundlagen

von Jakob B. (teddynator)


Lesenswert?


von nafets (Gast)


Lesenswert?

Das ist mein Code. Ich nutze allerdings einen AT90CAN128. Sollte aber 
bei dir auch so ähnlich aussehen.
1
 void InitPorts (void)
2
{
3
  DDRA = 0xFF;           // set all pins to ba a output
4
  DDRB = 0x80;           // set PortB7 to be a output
5
  DDRC = 0x01;           // set PortC0 to be a output
6
  DDRD = 0x2F;           // set pin 0,1,2 and 5 to be output
7
}

Gruß Stefan

von Attila (Gast)


Lesenswert?

Vielen Danke einmal für die schnellen Antworten! Immer wieder ein 
Wahnsinn :)
Ja ich dachte mir schon, dass das gehen muss.
Meine Code sieht wie folgt aus (die relevanten Sachen):
1
DDRH = (1<<PH0) | (1<<PH4) | (1<<PH5) | (0<<PH6) | (0<<PH7); // PH0, PH4 und PH5 als Ausgang und PH6 und PH7 als Eingang
2
PORTH = (0<<PH0) | (0<<PH4) | (0<<PH5) | (1<<PH6) | (1<<PH7); // PH6 und PH7 Pullup einschalten  
3
4
EIMSK = (1<<PCIE0 | 1<<PCIE1 | 1<<PCIE2 | 1<<PCIE3);
5
PCMSK2 = (1<<PCINT22 | 1<<PCINT23); // Pin Change Mask Register 2
6
7
ISR (PCINT2_vect) // ISR Routine für Endlagentaster 2
8
{
9
  _delay_ms(2000);
10
  endlagentaster2 = 1;   
11
}

von Karl H. (kbuchegg)


Lesenswert?

Attila schrieb:

> ISR (PCINT2_vect) // ISR Routine für Endlagentaster 2
> {
>   _delay_ms(2000);
>   endlagentaster2 = 1;
> }

Und was genau ist endlagentaster2 ?

Zeig doch bitte möglichst den kompletten Code. So komplex wird der schon 
nicht sein.
Irgendwo hast du höchst wahrscheinlich eine Zuweisung an einen Port 
gemacht, wo du eigentlich mittels | bzw & ein Bit setzeh bzw. löschen 
müsstest. Du siehst es selber nicht, aber wir werden die Stelle finden. 
Nur: Dazu brauchen wir deinen Code.
Eine andere Möglichkeit ist ein vergessenes volatile. Aber auch das kann 
man nur erkennen, wenn man den kompletten Code sieht.

Über das was du da mit dieser ISR verbrochen hast, reden wir dann später 
noch.

von Attila (Gast)


Angehängte Dateien:

Lesenswert?

Der komplette Code is echt ziemlich viel, aber bitte :)
Das komische ist ja das es solange die Interrupts für den einen Pin 
deaktiviert sind alles einwandfrei funktioniert.
Das ich nicht sehr effektiv programmiere ist mir bewusste, aber naja :P

von Dietmar (Gast)


Lesenswert?

Attila schrieb:
> ISR (PCINT2_vect) // ISR Routine für Endlagentaster 2
> {
>   _delay_ms(2000);
>   endlagentaster2 = 1;
> }

Dir ist aber schon klar das du in der ISR den uC für 2 SEKUNDEN 
"schlafen" legst und er nichts anderes mehr machen kann? Ist das 
wirklich nötig? Kommentiere die Zeile mal aus und versuche's nochmal.

Alternativ: Erkläre uns wofür du das delay.... benötigst.

Gruß Dietmar

von XXX (Gast)


Lesenswert?

Hallo

DDRH = (1<<PH0) | (1<<PH4) | (1<<PH5) | (0<<PH6) | (0<<PH7);
PORTH = (0<<PH0) | (0<<PH4) | (0<<PH5) | (1<<PH6) | (1<<PH7);

Schau doch bitte mal in das Tutorial, wie das mit der Bitmanipulation
ist. Eine 0 irgendwohin geschoben und dann verodert bringts
irgendwie nicht.

Gruß
Joachim

von Karl H. (kbuchegg)


Lesenswert?

Nicht sehr effizient ist gut gesagt. Das ist so ziemlich genau so wie 
man es nicht macht.
Die Ansteuerung der Schrittmotoren macht man mittels Interrupts (um das 
Timing einzuhalten), während Taster ruhig gepollt werden können, bzw. 
die Endtaster werden ganz einfach vor der Ausgabe eines Schrittes 
abgefragt, ob man überhaupt noch einen Schritt in diese Richtung machen 
kann.

Abgesehen davon wimmelt es nur so von delays, die deinem Programm ein 
Reaktionsverhalten aufzwingen, welches dich in den Wahsinn treiben wird.

Aber abgesehen davon.
1
void drehrichtung(void)
2
{
3
// X-Richtung
4
  if (xrichtung == 0)
5
  {
6
    PORTA = (1<<PA2) | (0<<PA6); 
7
  }
8
  if (xrichtung == 1)
9
  {
10
    PORTA = (1<<PA2) | (1<<PA6); 
11
  }  
12
// Y-Richtung
13
  if (yrichtung == 0)
14
  {
15
    PORTH = (1<<PH0) | (0<<PH4); 
16
  }
17
  if (yrichtung == 1)
18
  {
19
    PORTH = (1<<PH0) | (1<<PH4); 
20
  } 
21
}

Hier zb sind ein ganzer Haufen Fehler enthalten.
Du willst ein paar Pins schalten, schaltest aber jeweils den kompletten 
Port um! Ein absolutes NoNo. Die Funktion soll nur IHRE Pins bedienen 
und alle anderen in Ruhe lassen. Dazu brauchst du die Bit-setz bzw. 
Bit-Lösch Operationen

 Bit Setzen
    PORTA |= ( 1 << PA2 );

Bit löschen

    PORTA &= ~( 1 << PA2 );

Die Quittung, die du erhältst, wenn du das nicht sauber machst, hast du 
ja jetzt gesehen. Ob du dasselbe auch noch an anderen Stellen machst, 
hab ich nicht weiter kontrolliert.


Abgesehen davon: Du hast ein graphisches LCD drann. Das eignet sich 
wunderbar um in einer Ecke Kontroll und Statusmeldungen während der 
Debug Phase auszugeben. Dadurch kann dir dein Programm selber erzählen 
warum es was macht bzw. nicht macht. Nutze diese Möglichkeit.

von Karl H. (kbuchegg)


Lesenswert?

1
    _delay_us(delay);

das wird nix.
Weder _delay_us noch _delay_ms kann man mit einer Variablen benutzen. 
Das muss zwingend eine konstanter Zahlenwert sein, sonst stimmen die 
Zeiten hinten und vorne nicht.

von Karl H. (kbuchegg)


Lesenswert?

Nope.
Das ganze Konzept, dass die Endlagentaster auf einen Interrupt gehen, 
ist murks

Frag doch hier
1
void motorinit(void)
2
{
3
// X-Achse Ansteuerung
4
  sei();
5
  do 
6
  {
7
    xrichtung = 1;
8
    drehrichtung();
9
    PORTA ^= (1<<PA7);
10
    _delay_us(1);
11
  } 
12
  while (endlagentaster2 == 0); 
13
14
         .....

ganz einfach den Taster selber ab anstatt dass sich der erst kompliziert 
über einen Pin Change Interrupt samt globaler Variabler (die einmal 
volatile und einmal nicht volatile ist) bemerkbar machen kann. Ist doch 
viel einfacher und zuverlässiger.

von Attila (Gast)


Lesenswert?

Ok vielen Dank für die rasche Hilfe, es funktioniert jetzt.
Danke für die vielen Ratschläge was das programmieren betrifft, werde 
mir diese du Herzen nehmen :)

lg Attila

von Dietmar (Gast)


Lesenswert?

Was war es denn jetzt?

von Karl H. (kbuchegg)


Lesenswert?

Attila schrieb:
> Ok vielen Dank für die rasche Hilfe, es funktioniert jetzt.
> Danke für die vielen Ratschläge was das programmieren betrifft, werde
> mir diese du Herzen nehmen :)
>

Fang gleich damit an, dir ein paar Ausgabefunktionen zu schreiben. Das 
immer gleiche
1
    itoa(yposition, ausgabe_yposition, 10);
2
    if (100 <= yposition && yposition <= 200)
3
    {
4
      GLCD_WriteText(ausgabe_yposition,5,16);
5
    }
6
    if (10 <= yposition && yposition < 100)
7
    {
8
      GLCD_WriteText(null,5,16);  
9
      GLCD_WriteText(ausgabe_yposition,6,16);
10
    }
11
    if (0 <= yposition && yposition < 10)
12
    {
13
      GLCD_WriteText(null,5,16);  
14
      GLCD_WriteText(null,6,16);  
15
      GLCD_WriteText(ausgabe_yposition,7,16);
16
    }

geht doch auf keine Kuhhaut. Das ist doch immer derselbe Code in den 
einzelnen Zahlen-Ausgaben, bis auf den Variablennamen und die Position, 
wo die Ausgabe hin soll. Ein perfekter Kandidat für eine Funktion, die 
auf diese Spezialaufgabe spezialisiert ist.
1
void GLCD_WriteValue( int Value, int Column, int Line )
2
{
3
  char ValueAsText[20];
4
5
  itoa( Value, ValueAsText, 10 );
6
7
  if (100 <= Value && Value<= 200)
8
  {
9
    GLCD_WriteText( ValueAsText, Column, Line );
10
  }
11
  else if (10 <= Value && Value < 100)
12
  {
13
    GLCD_WriteText( null, Column, Line );  
14
    GLCD_WriteText( ValueAsText, Column+1, Line );
15
  }
16
  else if (0 <= Value && Value < 10)
17
  {
18
    GLCD_WriteText( null,Column, Line );  
19
    GLCD_WriteText( null,Column + 1, Line );  
20
    GLCD_WriteText( ValueAsText, Column + 2, Line );
21
  }
22
}

und die verwendest du dann zb hier
1
void position(void)
2
{
3
  ....
4
5
  if (joystick_oben == 1 && werttoggle == 0 && xposition < 200)
6
  {
7
    xposition = xposition + 1;
8
    GLCD_WriteValue( xposition, 5, 13 );
9
  }
10
  if (joystick_unten == 1 && werttoggle == 0 && xposition > 0)
11
  {
12
    xposition = xposition - 1;
13
    GLCD_WriteValue( xposition, 5, 13 );
14
  }
15
16
// Y-Koordinate eingabe
17
  if (joystick_oben == 1 && werttoggle == 1 && yposition < 200)
18
  {
19
    yposition = yposition + 1;  
20
    GLCD_WriteValue( yposition, 5, 16 );
21
  }
22
  if (joystick_unten == 1 && werttoggle == 1 && yposition > 0)
23
  {
24
    yposition = yposition - 1;
25
    GLCD_WriteValue( yposition, 5, 16 );
26
  }
27
}
28
29
void positionausgabe(void)
30
{
31
  if (menueebene == 0)
32
  {
33
    GLCD_WriteValue( xposition, 5, 13 );
34
    GLCD_WriteValue( yposition, 5, 16 );
35
  }
36
}

und schon ist dein Code an dieser Stelle um über 80% kürzer geworden, 
und was das Beste ist: er hat auch noch an Klarheit und Übersicht 
gewonnen. Was vorher 3 Bildschirmseiten gebraucht hat, geht jetzt auf 
etwas mehr als eine halbe Seite. (und wenn man wollte, könnte man die 
Funktion noch kürzer machen)
Und so gibt es in deinem Code noch viele andere Dinge, die man mit ein 
wenig Organisation kürzer und überschaubarer machen kann. Du läufst 
nämlich Gefahr, dass du dir selbst eine Unüberschaubarkeit und 
Komplexität zurecht programmierst, die du nicht mehr beherrschen und 
überblicken kannst. Völlig unnötiger Weise. Wenn ein Code funktioniert, 
dann ist man noch lange nicht fertig. Dann heißt es zurücklehnen, sich 
das Machwerk ansehen und überlegen, wie man das Machwerk besser machen 
kann.

Und wenn du dann in einiger Zeit drauf kommen wirst, dass du den Fall 
negative Zahlen ja komplett übersehen hast, dann hast du genau nur eine 
einzige Stelle, nämlich diese Funktion, die du pimpen musst, damit alle 
Ausgaben auch mit negativen Zahlen korrekte Anzeigen produzieren.


(Kann es sein, dass du in C mit der Argumentübergabe auf Kriegsfuss 
stehst? Wenn ich mir deine Funktionen so ansehe fällt mir auf, dass du 
alles immer über globale Variablen abhandelst. Selbst dann, wenn sich 
für die Funktion ein Argument förmlich aufdrängt!)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Attila schrieb:
> Ok vielen Dank für die rasche Hilfe, es funktioniert jetzt.

Du hast Dein Programm anhand Karl Heinz' Tipps innerhalb einer knappen 
Stunde derart umschreiben können, dass es nun funktioniert? Ich bin 
jetzt echt baff...

von Attila (Gast)


Lesenswert?

Das Problem war nur wie Karl Heinz Buchegger geschrieben hat, dass ich 
ständig den ganzen Port geschrieben und nicht nur die einzlenen Bits.
Der restliche Code funktioniert ja, wenn auch über 1000 Ecken und 
teilweise sehr umständlich. Warum ich die Endlagentaster mit Interrupts 
gelöst habe weiß ich mittlerweile selber nicht mehr...
Vielen Dank für die Tipps bezüglich der besseren Programmgestaltung. Ich 
weiß selber das ich teilweise sehr umständlich programmiert habe, aber 
ich bin etwas im Zeitdruck gewesen und von daher ging es mir in erster 
Linie nur darum das es funktioniert.

lg und nochmals vielen vielen Dank für die rasche und tolle Hilfe
Attila

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.