TWI (I2C) mátrix billentyűzet kezelése

 

2020-05-30

Mint korábban írtam, a PCF8574 port bővítővel további terveim is vannak. Most bemutatom a működését és a kezelését. Az alábbi szutykos - konkrétan így érkezett Kínából bezacskózva - kis kártyát, hozzávetőlegesen 1 USD-ért szállítják házhoz. Leírjam? Itthon tréfás kereskedők az IC-t a duplája körül mérik, a kártyára láttam 2500HUF árat is, igaz beleszámolták az ÁFÁ-t is. Ki fogunk pusztulni.

A panelen mindkét oldalon szépen fel van írva, melyik érintkező mi. Plusz szolgáltatás, hogy kapunk 3 jumpert is, így könnyen átállíthatjuk a chip címét. Csak nosztalgiázom, hogy volt amikor a számítógép soros kártyáján! jumperekkel kellett beállítsuk a baud rate-et, a formátumot, az IT-t... Azután szintén plusz, hogy a kártya kaszkádolható. Ez azt jelenti, hogy több kártyát egymás után dughatunk, ha több port bire van szükségünk, persze ne feledjük mindegyiket más-más címre állítani. Az én kártyám úgy érkezet, hogy a jumper-ek mindhárom cím lábat földre kötötték, vagyis a címe 0x20 volt.

A kártya portlábai tulajdonképpen open kollektorosak, ha még mond ez valakinek valamit. Az alábbi rajzot a PCF8574 adatlapjából vágtam ki.

Csináltam egy Móricka rajzot, amin jobban el tudom magyarázni a működését. Nincs külön konfigurálás, hogy egy lábat bemenetként vagy kimenetként akarunk használni. A port láb állapotát bármikor le tudjuk olvasni. Ha bemenetként akarjuk használni, akkor a port bitet 1-be kell állítani, így a bemenetet a belső ellenállás felhúzza 1-be, és a külső áramkörünk meg le tudja húzni 0-ba, a működése szerint. Kimenetként használva, amikor 0-ba állítjuk a port bitet, akkor lehúzza 0-ba. Tehát, ha mondjuk egy LED-et akarunk kapcsolni vele, a LED-et a VDD felé kell kössük mondjuk 470 Ohm-os ellenálláson keresztül, és 0-nál fog világítani. Ha valmilyen digitális bemenetet akarunk vezérelni, általában elég a belső felhúzás, de szükség szerint betehető egy 1..4k7 külső felhúzó ellenálás. Beolvasáskor egyszerűen azt is beolvassuk, melyik port lábat állítottuk 0-ba. Ha ezt ilyen egyszerűen is lehet csinálni, akkor miért csinálnak bonyolultabb be/kimeneteket? Azért mert azok nagyobb sebességgel képesek működni. Azoknak a régi számítógépeknek, amik open kollektoros bússzal működtek, az órajelük 20-30 MHz volt, és nem simán egy ellenállással kellet a vezetékeket felhúzni, hanem a vezeték hullámellenállásának megfelelően méretezett ellenállás osztóval kellett a buszt lezárni. De ne aggódjunk, itt max 100 kHz/10 frekinél ez nem játszik.

Alább látható a projektem. Nem sajnáltam újabb 0,7 USD-ért a kínaiaktól berendelni egy membránkapcsolós fólia billenyűzetet. És ez nem csak kakaóbiztos, hanem öntapadós is. Akár az asztalomra is felragaszthatom, vagy bárhova. Összedugásnál az INT lábat kihagyjuk, ez a PCF8574-be beépített interrupt kezelő kimenete. Ha valakit érdekel/használni akarja, olvasson utána a doksijában.

Írtam már, hogy az Internetről leölthető library-k mind sz...ok. Szóval ismét készítetem egy saját objektumot. Valójában annyira kevés kódról van szó, hogy szinte szégyeltem egy külön objektumot csinálni, de azért így kényelmesebb kezelni az áramköreimet. Tulajdonképpen az inicializáláskor meg kell adni, hogy milyen címen is van a chip-pünk. Az Arduino Wire objektumát használjuk a TWI busz kezelésére. Három dolgot tehetünk. A read() beolvassa a port 8 lábát, a write, meg beállítja. Amelyik bitbe 0-át írunk, az földre kerül. A harmadik dolog, az error()-ral lekérdezhetjük, hogy van-e valami probléma? Mondjuk elfelejtettük az Arduino-ra rákötni a kis panelünket, vagy elkötöttük a SCL/SDA vezetékünket... A csillagos sorminta alatt van maga a program. Nem bonyolult. Létrehozzuk az objektumot, megnézzük error()-ral, hogy látjuk-e a chip-pünket? Azután a loop()-ban a mátrix 4 oszlopát (vagy sorát) 0-ba teszem, visszaolvasom, és megnézem, hogy lenyomták-e bármelyik gombot? Ha igen, akkor jön a kitalálós játék. Direkt bináris alakban írtam a számokat, hogy jobban követhető legyen, mikor melyik lábakat rángatom le. A soros porton kiírjuk melyik gombot nyomták le, majd pergésmentesítésként várunk addig, amíg a kezelő az összes gombot fel nem engedi. A csudába, hagytam a programban egy hibát. A loop()-ban nem kellene újra meg újra létrehoznom a k meg a c változót :(.


/******************************************************************************
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2020.05.30.
*   Chip         -  Arduino, PCF8574, keyboard matrix
*   Compiler     -  Arduino IDE 1.8.12
*
*   TWI (I2C) KBD kezelese
*    
*      NANO        MEGA          PCF 8574    
*    -------+    -------+      +----------+  
*           !           !      !          !  
*       GND +--     GND +--  --+ GND  GND +--
*       VCC +--     VCC +--  --+ VCC  VCC +--      +---------+
*        A5 +--     D21 +--  --+ SCL   P0 +--------! S S S S !
*        A4 +--     D20 +--  --+ SDA   P1 +--------! S S S S !
*           !           !      !       P2 +--------! S S S S !
*           !           !      !       P3 +--------! S S S S !
*                            --+ A0       !        +---------+
*                            --+ A1       !          ! ! ! !
*                            --+ A2    P4 +----------+ ! ! !
*                              !       P5 +------------+ ! !
*                              !       P6 +--------------+ !
*                              !       P7 +----------------+
*                              !          !
*                              +----------+
* 
*   A0  A1  A2   HEX address
*    1   1   1   0x27 - default
*    0   1   1   0x26
*    1   0   1   0x25
*    0   0   1   0x24
*    1   1   0   0x23
*    0   1   0   0x22
*    1   0   0   0x21
*    0   0   0   0x20
*    
******************************************************************************/

#include "Arduino.h"
#include "Wire.h"


class kt_pcf8574
{
   public:
   kt_pcf8574( uint8_t);                          // address
   void write( uint8_t);                          // data
   uint8_t read();
   uint8_t error();

   private:
   uint8_t _address;
};



kt_pcf8574::kt_pcf8574( uint8_t a) 
{
  _address= a;
  Wire.begin();
}


void kt_pcf8574::write( uint8_t d)
{
   Wire.beginTransmission( _address);
   Wire.write( d);
   Wire.endTransmission();
}


uint8_t kt_pcf8574::read()
{
   Wire.beginTransmission( _address);
   Wire.requestFrom( _address, 1);
   return Wire.read();
}


uint8_t kt_pcf8574::error()
{
   Wire.beginTransmission( _address);
   return Wire.endTransmission();
}

/******************************************************************************/


kt_pcf8574 x= kt_pcf8574( 0x20);


void setup()
{
   Serial.begin( 9600);
   Serial.println( "kt_pcf8574/keyboard demo");
   if( x.error())
   {
      Serial.print( "PCF8574 chip not ready!");
      while( 1);
   }
   Serial.println();
}


void loop()
{
   uint8_t k;
   uint8_t c;
   x.write( B00001111);
   if ( x.read() != B00001111)
   {
      k= 0;
      x.write( B01111111);    
      c= x.read();
      if( c == B01111110) k= 'A';
      if( c == B01111101) k= 'B';
      if( c == B01111011) k= 'C';
      if( c == B01110111) k= 'D';

      x.write( B10111111);    
      c= x.read();
      if( c == B10111110) k= '3';
      if( c == B10111101) k= '6';
      if( c == B10111011) k= '7';
      if( c == B10110111) k= '#';

      x.write( B11011111);    
      c= x.read();
      if( c == B11011110) k= '2';
      if( c == B11011101) k= '5';
      if( c == B11011011) k= '8';
      if( c == B11010111) k= '0';

      x.write( B11101111);    
      c= x.read();
      if( c == B11101110) k= '1';
      if( c == B11101101) k= '4';
      if( c == B11101011) k= '7';
      if( c == B11100111) k= '*';
      if( k)
      {
         Serial.write( k);
         Serial.println();
         x.write( B00001111);
         while ( x.read() != B00001111);
      }
   } 
}

Ennyi az egész, itt a vége, fuss el véle.