Forum: Mikrocontroller und Digitale Elektronik MCP23016 Port Expander Input Realisieren


von Nino K. (lnino)


Angehängte Dateien:

Lesenswert?

Hi an alle.

Ich konnte nun nach etwas Arbeit meinen MCP23016 Port Expander und 
meinen atmega168 miteinander sprechen lassen. Ich habe an dem atmega168 
einen Button gehängt und die LEDs an den MCP23016 gesteckt. Nach drücken 
des Buttons leuchten die LEDs. Projekt gelungen.

Nun möchte ich aber den verkehrten Weg probieren. Und zwar dass die LEDs 
am atmega168 hängen und der Button am MCP23016. Leider komme ich nicht 
dahinter wie ich dort einen Imput(in diesem Fall einen Button) richtig 
anspreche. Das definieren der Input Ports habe ich schon gemacht.

Kann mir jemand dabei helfen?

Zur Erklärung:
Die Leds sind im Moment an GP0.0-GP0.7 gehängt.
Der Button hängt an PB1.

Meinen bestehenden Code mit der Realisierung des Outputs habe ich in 
diesem Thread angehängt.

Hier der Datasheet des MCP23016: 
http://ww1.microchip.com/downloads/en/DeviceDoc/20090C.pdf

von Nino K. (lnino)


Lesenswert?

Keiner eine Idee?

von Programist (Gast)


Lesenswert?

Das ist ein i2C Port Expander und den kann man wie jedes andere I2C- 
gerät ansteuern und konfigurieren.

Aber  das ist glaube Ich nicht richtig

   i2c_write(0x06);             // Auswahl IODIR (pointer)
   i2c_write(0x00);             // DDR Port0 all output
   i2c_write(0xFF);             // DDR Port1 all input 0xFF = B11111111

Das sind die register in die du was schreiben willst, aber was du 
schreiben willst, steht nicht drin.

Das muss ungefähr so aussehen:

i2c_write(0x00,0xFF);             // DDR Port0 all output

Wie das genau geht muss in dem manual für deinen compiler stehen.

von Nino K. (lnino)


Lesenswert?

Hi,

ich denke schon, dass es so stimmt, da das Programm schließlich auch so 
funktioniert.

Außerdem habe ich die Bibliothek von Peter Fleury verwendet, in welcher 
man soweit ich gesehen habe bei den verwendeten Funktionen nur einen 
Parameter übergibt.

Den Teil den zu kommentiert hast ist lediglich die Initialierung des 
I2C.

Das Ansprechen der LEDs erfolgt in der Main Funktion und funktioniert 
ohne Probleme wenn ich die LEDs als Ausgang verwende.

Nur möchte ich eben am Port Expander einen Eigang setzen. Z.b.: einen 
Button.

von Programist (Gast)


Lesenswert?

El Nino schrieb:
> Nur möchte ich eben am Port Expander einen Eigang setzen. Z.b.: einen
>
> Button.

Wo soll er sein? An welchem pin?
Wenn du in das Datenblatt von der Mcp23016 schaust dann wirst du sehen, 
dass das hier (0x06) die addresse von dem IODIR register ist.Und dieser 
register ist dafür zuständig ob die ports eingang oder ausgang sein 
sollen.Also wenn du da eine 1 schreibst dann sind die entsprechenden 
pins input und andersrum eben output. Dieser register ist aber nur für 
den Port0 zuständig.Für den Port1 ist ein anderer register zuständig. 
i2c_write(0x06);             // Auswahl IODIR (pointer)

von Programist (Gast)


Lesenswert?

Für den Port1 ist der IODIR1 register zuständig.Und seine Addresse ist 
0x07
Also wenn alle Pins an dem Port1 input sein sollen dann würde Ich das so 
machen.

   i2c_write(0x07);             // Auswahl IODIR1 (pointer)
   i2c_write(0xFF);             // DDR Port1 all input 0xFF = B11111111

von Programist (Gast)


Lesenswert?

Das ist aber nicht nötig, weil sie automatisch auf input stehen.

von Nino K. (lnino)


Lesenswert?

Hi Programist,

danke für deine Antworten.

Das heisst zu Beginn sind die Ports von GP0 und GP1 als Input gesetzt.
Mit meinem jetzigen Code habe ich also GP0 als Output und GP1 als Input 
gesetzt.

Ist das so korrekt?

Wenn ich nun an GP1.0 einen Button anschließe, wie soll dies dann 
aussehen?

Den "normalen Weg" mit ..
1
if(!(PINB & (1<<PB1))) // When Button is pressed
2
{
3
// do something
4
}
..kann ich ja nicht machen weil es dieses Register auf dem Expander gar 
nicht gibt.

Wie kann ich dann abfragen ob ein Button an GP1.0 gedrückt worden ist?

Gibt es da dann vielleicht eine i2c_read Funktion?

Vielleicht kann mir da jemand weiter helfen?

von Programist (Gast)


Lesenswert?

El Nino schrieb:
> Das heisst zu Beginn sind die Ports von GP0 und GP1 als Input gesetzt.
>
> Mit meinem jetzigen Code habe ich also GP0 als Output und GP1 als Input
>
> gesetzt.
>
>
>
> Ist das so korrekt?

Ja das stimmt.

El Nino schrieb:

> Wie kann ich dann abfragen ob ein Button an GP1.0 gedrückt worden ist?

Dazu musst du zuerst den register GP1 lessen und dann den kopieren auf 
ein anderes register auf den dein programm zugreifen kann.

von Nino K. (lnino)


Lesenswert?

Danke für deine rückmeldung. kannst du mir diesen fall ein 
anwendungsbeispiel nennen? welches register des atmega168 kann Ich dafür 
verwenden? und wie lese Ich den wert des gp1.0 aus? es funktioniert also 
ganz anders als beim ausgang? danke für deine hilfe.

von Programist (Gast)


Lesenswert?

Ich kann dir ein anwendungsbeispiel nennen, aber nicht für deinen 
compiler.
Das funktioniert unterschiedlich bei anderen compilern.Aber du must 
sowieso zuest im allgemeinen programieren lernen, weil du viel zu wenig 
Ahnung davon hast.

von Nino K. (lnino)


Angehängte Dateien:

Lesenswert?

Ich hoffe es ist okay meinen alten Thread wieder aufzuwärmen.

Bin nun wieder dazu gekommen mich dem Thema zu widmen und habe mich 
etwas vertrauter mit dem MCP23016 gemacht.

Die Ausgabe habe ich nun vollends vollstanden und den Code 
dementsprechend angepasst. Ich kann nun jeden Port auf High oder Low 
setzen und Input oder Output.

Die Realisierung des Inputs ist mir jedoch immer noch nicht klar.
Wenn ich annehme an GP1.1 hängt ein Taster und ich möchte im Code 
abfragen ob dieser gedrückt wurde.

Normal mache ich das ja mit: if(!(PINB & (1<<PB1)))

Wie realisiere ich das wenn der Taster nicht am ATMega168 hängt sondern 
am GP1.1 vom MCP23106.

Hat das jemand von euch schon umgesetzt?

von Nino K. (lnino)


Lesenswert?

schiebe nach oben....

von Nino K. (lnino)


Lesenswert?

Hat noch niemand dies umgesetzt?

von Ma B. (drumstick)


Lesenswert?

Hallo Nino

Ich weiss nicht obs für dich aktuell ist, aber ich habe das selbe 
Problem, oder ähnlich :-)

Bei meiner Hardware habe ich 3 Buttons, die ich mit pollen nach dem 
Zustand abfrage.
1
Display_Read(0x0, &Data);
2
  if(Data == 0x08)
3
  {
4
     Switch_On(LED_Blink2);
5
  }

Bei mir sieht das Read dann so aus (Beispiel Demo.c von Keil)
1
bool Display_Read (uint8_t reg, uint8_t *val) 
2
{
3
  uint8_t data[1];
4
  int32_t n;
5
6
  data[0] = reg;
7
  n = ptrI2C->SendData(DISPLAY_I2C_ADDR, data, 1, true);
8
  if (n != 1) return false;
9
10
  n = ptrI2C->ReceiveData(DISPLAY_I2C_ADDR, val, 1, false);
11
  if (n != 1) return false;
12
13
  return true;
14
}

Dies läuft bei mir ohne Probleme, ich kann auch einen Buzzer an einem 
einzelnen Ausgang setzen.

Am anderen Port GP1, habe ich jetzt ein Display im 4 Bit Mode, dass ich 
ansteuern möchte und weil dies nicht funktioniert, frage ich mich, ob 
mein Vorgehen richtig ist. Display Treiber HD44780U.

GP1 outputs:
1
Dispaly_Write(0x07);            
2
Dispaly_Write(0x00);

Dann setze ich die Outputs:
1
Dispaly_Write(0x01);  // hier wähle ich GP1          
2
Dispaly_Write(0x42);   // hier sende ich; 4 um den E zu setzen und 2 high nibble
3
Dispaly_Write(0x48);   // 4 = E, 8 = low nibble
4
Dispaly_Write(0x00);   // 0 = E rücksetzen um die Daten zu übernehmen

Meine Frage nun, da dies so nicht funktioniert, müsste ich nun vor jedem 
Befehl, den GP1 anwählen? Habe dies versucht und hat nicht geklappt. Ich 
komme einfach nicht drauf, wie dies funktioniert.

Vielleicht liegt der Fehler beim Display Treiber?

Vielleicht hat jemand eine Idee?

Grüsse

M.B.

von Ma B. (drumstick)


Lesenswert?

Nachtrag: Ich verwende den STM32F405IG und das yVision von Keil MDK 5

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.