MCP23017 2x8 (1x16) bites portbővítő használata

Hogyan programozzak AVR chipet? Programozók beállításai...
Bascom nyelvű programok...
Válasz küldése
Avatar
nobody_hun
Bitfaragó
Hozzászólások: 425
Csatlakozott: 2005. november 14. hétfő, 7:00

MCP23017 2x8 (1x16) bites portbővítő használata

Hozzászólás Szerző: nobody_hun »

Amikor elfogy a kontrolleren a szabad lábak száma, gyakran felmerülő kérdés, hogy a problémáinkra megoldást jelent-e egy portbővítő használata.
Egyik feladatomnál szembesültem a fenti problémával, ezért jobban körülnéztem, milyen lehetőségeim is vannak.

Első kiválasztottam a PCF8574 volt, ezt már korábban használtam. Jelen esetben azonban hátrányai miatt (nem tud közvetlenül LED-et meghajtani, csak a széles SO16W tokozásban érhető el, ráadásul nem kimondottan olcsó) nem volt alkalmazható.

Tovább kerestem a megoldást, amikor rábukkantam az MCP23XXX sorozatú portbővítőkre. Első pillantásra az adatlapok igen kecsegtető szolgáltatásokkal jellemzték ezt az áramkört, így mellettük tettem le a voksomat.

Persze szegény drótos tótot az alkatrészbeszerzés is húzza, így égen földön nem sikerült DIP tokost beszerezni belőle, hogy tesztpanelen megnézzem mit is tud valójában, mielőtt az éles áramkörbe betervezem.

Robert segített ennek a problémának az áthidalásában, így azonnal nekiestem a közelebbi vizsgálatnak.

A chip:
1,8V-tól 5,5V-ig használható portbővítő, két változatban gyártják: I2C és SPI busszal, jelen ismertetőben az I2C változat található meg.
Tokozását tekintve a QFN, SOIC, SSOP és keskeny DIP tokban létezik, 28 kivezetéssel.
Véleményem szerint valamelyik maszkprogramozott PIC lehet az áramkör alapja, erre az utal, hogy olyan szolgáltatásokat is kínál, amelyet a konkurrensek nem.

Áramkörtervezés:
Pár dologra szeretném felhívni a figyelemet, amivel a későbbi buktatók elkerülhetők:
Az áramkör rendelkezik Reset kivezetéssel, ezt köthetjük kontrollerre, illetve, ha szoftveresen nem kívánjuk kihasználni ezt a lehetőséget, akkor kössük fixen a pozitív tápra.

A két port bitjeinek kivezetése ellentétes az IC-n, az A port 0. bitjével szemben a B port 7. bitjének lába található.

Alapértelmezésben mindkét porthoz 1-1 megszakítás kimenet tartozik, azonban ez szoftveresen konfigurálható akár olyan módon is, hogy a két INT lábat "összekötjük".
Tapasztalataim szerint néhány esetben kellett külső felhúzóellenállás (~4,7k) az INT lábra, mert a kontroller nem érzékelte a megszakításkérelmet.

A bemenetnek konfigurált lábakhoz belső, bitenként ki-bekapcsolható felhúzóellenállás tartozik, így azt külsőleg betervezni nem szükséges.

Bár áramkorlátozó ellenálláson keresztül az IC közvetlenül is képes például LED-et meghajtani, figyelembe kell venni, hogy a VDD lábon keresztül maximálisan 125mA áram folyhat (Maximum current into VDD pin).
Így hiába képes az IC lábanként 20mA (25mA-nál lekapcsol) terhelhetőségre, nem tehetünk minden kimenetre 20mA-es terhelést. A mintakapcsolásban így a LED-ek elé viszonylag nagy, 620R áramkorlátozó ellenállást terveztem.

Tesztkörnyezet:
A legegyszerűbb kapcsolást állítottam össze, 4 darab nyomógomb és 4 darab LED segítségével, melyeket a portbővítő külön portjaihoz csatlakoztattam. Kontrollernek egy M32-t használtam, 8MHz belső RC oszcillátorról.

Vezérlőszoftver:
A chip számtalan belső regiszterrel rendelkezik, ezeken keresztül portbővítés témakörében minden lehetőséget kihasználhatunk. Nem szeretném ezt a témát unalmassá tenni, így nem térek ki részletesen minden regiszter funkciójára, erre ott az adatlap.

Az adatlap tanulmányozása során elsőre feltűnik, hogy a regisztertáblázatban a regiszterek címzésére két lehetőség kínálkozik.

A regiszter címzés módját az IOCON regiszter SEQOP bitje határozza meg. Ha ez a bit 0, akkor az IC regiszterei byte üzemmódban, ha 1, akkor szekvenciális üzemmódban címezhetők.
Személy szerint javaslom a szekvenciális módot, mert ebben az esetben nem kell új regiszter címet kiküldeni, hanem egymás után vezérelhetők/olvashatók a regiszterek tartalma (lásd mintaprogramban az inicializálás szubrutint).
Byte üzemmód esetén a portokhoz tartozó regiszterek címei átlapolódnak, közöttük az IOCON regiszter BANK bitjével tudunk váltani.

Bár ez a leírás az I2C verzióhoz készült, fontosnak tartok pár szót szólni az IOCON regiszter HAEN bitjéről:
SPI üzemmódban ezzel a bittel állítható, hogy az IC figyelembe vegye-e a hardveresen beállított címet (A0, A1, A2), vagy sem.

A mintaprogramban a következőket valósítottam meg:
Bekapcsolást követően mind a 4 LED világít. Ha lenyomunk egy gombot, akkor az IC megszakítást generál, a kontroller kiolvassa, hogy melyik láb állapotában történt változás, majd annak megfelelő LED-et kioltja.
Ha minden LED-et kikapcsoltunk, akkor nem történik semmi egészen addig, míg a kontrollert nem indítjuk újra.

A tesztprogram fordítást követően ~1.5k (M32 esetén 4%) lett, így a kisebb AVR-ekkel is kipróbálhatjuk.

Bekapcsolást/reset-et követően az áramkör minden portja bemenetként kerül konfigurálásra. Ez rendkívül ésszerű, hiszen kimenet esetén a rákötött más áramkör rövidzárba vihetné az adott lábat.

Bemenetek:
Elsőre úgy gondoltam, hogy elegendő kiolvasni a bemenet regiszterét és abból kinyerni azt a bitet, amelyhez tartozó lábon a változás történt. Ez így addig jó is volt, amíg nem nyomtam le újból ugyanazt a gombot.
A felmerült problémát úgy orvosolhatjuk, hogy elsőként kiolvassuk az INTCAPA regisztert. Ennek a regiszternek a bemenethez tartozó bitjének tartalma mindaddig ugyanaz marad, amíg nem töröljük vagy nem olvassuk ki a porthoz tartozó regiszter (jelen esetben a GPIOA) tartalmát.
Az INTCAPA és GPIOA regiszterek tartalmát külön változóban tároljuk és a kiértékelésnél összehasonlítjuk.

Bemenetek által generált megszakítások:
A bemenetnek konfigurált láb választhatóan kétféle megszakítási üzemmódban működhet:
Az egyik lehetőség, hogy a láb állapotát egy előre beállított értékhez hasonlítjuk, amelyet a DEFVALA regiszterben állítunk be. Üzem közben az IC akkor fog megszakítást generálni, ha a bemeneten a DEFVALA regiszter megfelelő bitjének értékétől különböző érték jelenik meg.
Jellemző felhasználási terület, amikor egy előre meghatározott nyomógombot nyomhat csak meg a felhasználó, a többire az áramkör "érzéketlen".

A másik lehetőség, hogy az IC "megjegyzi" a láb előző állapotát, és ha ahhoz képest eltérés mutatkozik, akkor generál megszakítást.

A porthoz tartozó megszakítás-generálás üzemmódját az INTCONA regiszterrel vezérelhetjük, továbbá a megszakítás generálást engedélyezni kell a GPINTENA regiszterben (lásd mintaprogramban az inicializálás szubrutint).

Beszélnünk kell még a prell-jelenségről: a prell sokunk életét megkeseríti, léteznek hardveres és szoftveres trükkök a kiküszöbölésére. Ennél az IC-nél gyakorlatilag mindezt elfelejthetjük. Hogy miért?
Nos erre a választ a megszakítási rendszer adja: amikor egy láb állapota megváltozik vagy eltér az előre beállított értéktől (DEFVALA), akkor megszakítás generálódik, majd a port értékét az IC átmásolja az INTCAPA regiszterbe. A megszakítás mindaddig aktív marad, ameddig az INTCAPA vagy a GPIOA regiszterek értékét ki nem olvassuk. Így, hiába jelentkezik a kapcsolónál vagy a nyomógombnál a felületi egyenetlenségből adódó prell-jelenség, az IC nem fog minden egyes tüskénél újabb és újabb megszakítást generálni.

A rendszer hátránya abban jelentkezik, hogy ilyen körülmények között szoftveresen kicsit nehezebb megoldani olyan feladatot, mint amikor például a nyomvatartott gomb hatására egy számláló számlálási sebessége felgyorsul.

Kimenetek:
Nos, ebben a témakörben igazán nem sok mindenről lehet beszélni. A mintaprogramban a LED_STATE byte méretű változó, amelynek bitjeit változtatjuk, majd az egész bájtot kiírjuk a kimeneti portra (lásd Kbd_ledstate szubrutin).
Természetesen a helyzet bonyolódhat, amikor egy porton belül egyszerre használunk be- és kimeneteket, de ettől sem kell megijedni. Kevert üzemmód esetén olvassuk be a port értékét, változtassuk meg a kimenetek bitjeinek állapotát, a bemenetek bitjeit töltsük fel a DEFVAL regiszter értékeivel és úgy írjuk vissza a megfelelő portra.

Remélem a fenti leírással sikerült kedvet adnom az áramkör kipróbálásához!

A kapcsolási rajz:
Kép

Valamint a tesztprogram:

Kód: Egész kijelölése

'------------------------------------------------------------------------------
' Microchip MCP23017 I2C portbővítő mintaprogram
'
' Írta: nobody_hun 2010. április
' Program írásának kezdete: 2010-04-16
' Program befejezve.......: 2010-04-17
'------------------------------------------------------------------------------
' CHIP ÉS COMPILER CONFIG
'------------------------------------------------------------------------------
$regfile = "m32def.dat"
$crystal = 8000000
$baud = 9600

Config Lcdpin = Pin , Db4 = Porta.2 , Db5 = Porta.3 , Db6 = Porta.4 , Db7 = Porta.5 , E = Porta.1 , Rs = Porta.0

Config Lcd = 16 * 1
'------------------------------------------------------------------------------
' KONFIGURÁCIÓ
'------------------------------------------------------------------------------
'Be- és kimenetek beállítása
Config Sda = Portc.1
Config Scl = Portc.0
Config Twi = 400000
'------------------------------------------------------------------------------
' ÁLLANDÓK ÉS VÁLTOZÓK
'------------------------------------------------------------------------------
'Állandók
Const Kbd_w = &B01000000
Const Kbd_r = &B01000001
'------------------------------------------------------------------------------
'Változók
'Felhasználva: nyomógombok és LED-ek
Dim Kbd_data As Byte , Kbd_data2 As Byte                    '7->Gomb1,6->Gomb2,5->Gomb3,4->Gomb4
Dim Kbd_int_processed As Bit                                '0=Amikor az INT megtörténik, 1=Amikor az INT feldolgozásra került
Dim Led_state As Byte                                       'LED-ek: LED4->3, LED3->2, LED2->1, LED1->0
'------------------------------------------------------------------------------
'Változók kezdeti értéke
Set Kbd_int_processed
Led_state = 255                                             'Világítson az összes LED
'------------------------------------------------------------------------------
' SZUBRUTINOK DEKLARÁCIÓJA
'------------------------------------------------------------------------------
'Szubok
Declare Sub Kbd_init
Declare Sub Kbd_ledstate
'------------------------------------------------------------------------------
' PERIFÉRIÁK INICIALIZÁLÁSA
'------------------------------------------------------------------------------
I2cinit

Cls

Call Kbd_init
'------------------------------------------------------------------------------
' MEGSZAKÍTÁS(OK) BEÁLLÍTÁSA
'------------------------------------------------------------------------------
Enable Interrupts
Enable Int0
On Int0 Inthandle
'------------------------------------------------------------------------------
' FŐPROGRAM
'------------------------------------------------------------------------------
Do
   If Kbd_int_processed = 0 Then
'LCD-re kiírjuk a beolvasott értékeket binárisan, hogy lássuk mi is történt
      Upperline
      Lcd Bin(kbd_data)
      Lowerline
      Lcd Bin(kbd_data2)
'A lenyomott gombnak megfelelő LED-et kikapcsoljuk
      If Kbd_data.7 = 0 And Kbd_data2.7 = 0 Then
         Led_state.3 = 0
         Led_state.2 = 1
         Led_state.1 = 1
         Led_state.0 = 1
      End If
      If Kbd_data.6 = 0 And Kbd_data2.6 = 0 Then
         Led_state.3 = 1
         Led_state.2 = 0
         Led_state.1 = 1
         Led_state.0 = 1
      End If
      If Kbd_data.5 = 0 And Kbd_data2.5 = 0 Then
         Led_state.3 = 1
         Led_state.2 = 1
         Led_state.1 = 0
         Led_state.0 = 1
      End If
      If Kbd_data.4 = 0 And Kbd_data2.4 = 0 Then
         Led_state.3 = 1
         Led_state.2 = 1
         Led_state.1 = 1
         Led_state.0 = 0
      End If
      Set Kbd_int_processed
      Call Kbd_ledstate
      Kbd_data2 = 255
   End If
Loop
'------------------------------------------------------------------------------
'| MEGSZAKÍTÁS(OK) RUTINJA(I)
'------------------------------------------------------------------------------
Inthandle:
   Kbd_data = 255
'Kiolvassuk, melyik láb okozta a megszakítást
   I2cstart
   I2cwbyte Kbd_w
   I2cwbyte &H10                                            'INTCAPA
   I2cstart
   I2cwbyte Kbd_r
   I2crbyte Kbd_data2 , Nack
   I2cstop

'Kiolvassuk a GPIOA-t is, mert ez törli a INTCAPA regisztert
   I2cstart
   I2cwbyte Kbd_w
   I2cwbyte &H12                                            'GPIOA
   I2cstart
   I2cwbyte Kbd_r
   I2crbyte Kbd_data , Nack
   I2cstop

   Reset Kbd_int_processed
Return
'------------------------------------------------------------------------------
'| RUTINOK A BILLENTYŰZETNEK ÉS A LEDEKNEK
'------------------------------------------------------------------------------
Sub Kbd_init
'MCP23017
'Beállítjuk a portok irányát: GPIOA: bemenet, GPIOB: kimenet
   I2cstart
   I2cwbyte Kbd_w
   I2cwbyte &H00
   I2cwbyte 255                                             'IODIRA
   I2cwbyte 0                                               'IODIRB
   I2cstop

'Felhúzó ellenállások engedélyezése
'LED-ekhez ajánlott kikapcsolni
   I2cstart
   I2cwbyte Kbd_w
   I2cwbyte &H0C
   I2cwbyte 255                                             'GPPUA
   I2cwbyte 0                                               'GPPUB
   I2cstop

'A GPIOA alapértelmezett értéke, ehhez fogja hasonlítani a változást
   I2cstart
   I2cwbyte Kbd_w
   I2cwbyte &H06                                            'DEFVALA
   I2cwbyte 255
   I2cstop

'GPIOA megszakításvezérlésének beállítása
'0->A láb előző állapotával hasonlítja össze
'1->A DEFVAL regiszterben megadott értékkel hasonlítja össze
   I2cstart
   I2cwbyte Kbd_w
   I2cwbyte &H08                                            'INTCONA
   I2cwbyte 0
   I2cstop

'Engedélyezzük a nyomógomboknak a megszakítást
   I2cstart
   I2cwbyte Kbd_w
   I2cwbyte &H04                                            'GPINTENA
   I2cwbyte 255
   I2cstop

'Ha minden OK, kigyújtjuk az összes LED-et
   Call Kbd_ledstate
End Sub
'------------------------------------------------------------------------------
Sub Kbd_ledstate
'A LED-ek állapotát kiíjuk a B portra
   I2cstart
   I2cwbyte Kbd_w
   I2cwbyte &H13
   I2cwbyte Led_state                                       'GPIOB
   I2cstop
End Sub
'------------------------------------------------------------------------------
"Az IC-k füsttel működnek. Ha kijön belőlük a füst, nem működnek tovább." - ismeretlen szerző
"Az ID:FFFFFF egyenlő az Atmel még nem kiadott processzorával."
Avatar
Robert
Elektronbűvölő
Hozzászólások: 10213
Csatlakozott: 2005. december 9. péntek, 7:00

Hozzászólás Szerző: Robert »

Huh, szép hosszú:)


Egy két kiegészítés, ill tapasztalat:
- a chip I2C vonalon 100kHz, 400kHz illetve a >1 MHz frekvenciát is támogatja.
- a resetláb bekötése kritikus , lebegőben tényleg antenna...
- a chip 3 lábon át különböző címekre is rakható (ezeket is be kell kötni, a belső felhúzó vagy nincs, vagy nagyon nagy értékű!)
- az INT kezelésben open kollektoros az INT kezelése. Nem mindíg hasznos ez. AVR oldalon segít(het), ha az INT lábon levő belső felhózót bekapcsoljuk.
- a chip mindenféle tápfeszültségről megy. Nálam 3.3V-s I2C buszon az 5V-s IC teljesen jól működött (I2C buszon a GND fele ZPD3v3 zener, a felhúzó ellenállás 5Vre ment (2*4k7).
- nagyobb távolság esetén a I2C sebességet le kell venni! 10 m esetén 2x1k2 lett berakva a busz végén (Mastertől legtávolabb) és a sebesség 50 kHz lett

Az MCP23017 a 2*8/16 bites portbővítő I2C busszal, a MCP23S17 az SPI-s. Az SPI-s kezelése fura, mert részben SPI, részen I2C jelleggel kezelhető.

I2C-s chipből a 8bites portbővítő a MCP23008.


Az adatlap alapján érdemes a regisztereket illetve a regisztersorrendet követni. Eléggé be lehet bonyolódni.
Én az egyik portot 8 ki, a másikat 8 bemenetnek használtam. Így a regsizterekkel jól össze lehet zavarodni:).
Válasz küldése