Läs av matris-tangentbord

Ett vanligt sätt att koppla in många knappar till en mikrokontroller utan att använda för många anslutningar är att använda ett så kallat matris-tangentbord. Knapparna i ett sådan är arrangerade elektriskt i rader och kolumner. I vårt exempel använder vi oss av ett tangentbord med 16 knappar och där varje knapp är inkopplad till en rad och en kolumn i ett rutmönster. När en knapp trycks in så kopplas dess rad- och kolumn-anslutningar ihop. Eftersom varje knapp har en unik rad-/kolumn-kombination så går det att räkna ut vilken knapp som trycks ner genom att mäta vilken rad och vilken kolumn som är ihopkopplade. I den här guiden visar vi hur man kan läsa av detta med ett Arduino-kort.

Kopplingen

Beskrivningen av koden

Det första vi gör i vår kod är att sätta upp två vektor-variabler (arrayer) där vi ställer in vilka pinnar på Arduino-kortet som tangentbordets rader respektive kolumner är anslutna till.

I setup() lägger vi in kod som ska köras vid uppstart. Vi aktiverar serie-kommunikationen så att vi kan skicka ut meddelande om vilken knapp som trycks ner. Vi säger sedan till Arduino-kortet att vi vill att de anslutningar som är kopplade till raderna på tangentbordet ska vara utgångar, och de som är kopplade till kolumnerna ska vara ingångar. Vi anger dessutom att ingångarna ska ha deras så kallade pull-up-motstånd aktiverade. Detta är motstånd som är inbygda i Arduino-kontrollern som sitter mellan ingången och plus (5 Volt). Dessa motstånd är till för att garantera att en ingång som inte har något anslutit alltid kommer att läsas av som en etta (hög). Detta är viktigt eftersom knapparna på vårt tangentbord inte har någon koppling alls när de inte är intryckta.

Själva hjärtat i koden är en funktion som vi kallar readKeyboard(). Denna funktion scannar tangentbordet och om en knapp är nertryckt så lämnar den tillbaka numret på knappen. Om ingen knapp är nertryckt lämnar den tillbaka noll. Hemligheten i scanningen är att kolumn-ingångarna kommer alltid att läsas av som höga, såvida inte en knapp är nertryckt och raden som knappen är ansluten till är satt till låg. Så vad koden gör är att sätta en av raderna låg och sedan läsa in de fyra kolumn-ingångarna en efter en. Om en av dessa ingångar är låg så vet koden att knappen som är ansluten till just den raden och kolumnen är nertryckt. Om ingen av ingångarna var låg så sätter återställer den raden till hög och sätter nästa rad till låg, läser av ingångarna igen osv. Koden måste alltså läsa av varje knapp en och en, men eftersom mikrokontrollern är väldigt snabb så tar det högst några hundra mikrosekunder.

Fysiska knappar har något som kallas kontakt-studs (contact bounce). Detta uppstår när metalldelarna inne i knappen flyttar sig och fysiskt studsar till några gånger när knappen trycks ner, eller strömbrytarens läge ändras. Detta sker så snabbt att vi människor hinner inte uppfatta det, men för en mikrokontroller är det ett stort problem eftersom den hinner läsa av knapparna så snabbt att den ser det här kontakt-studeset som om att knappen trycks in och släpps flera gånger snabbt. I vår kod här betyder det att varje knapptryckning kommer att se ut som flera. Det finns många sätt att råda bot på detta. Det enklaste är att helt enkelt vänta lite när man detekterar den första förändringen. 100 millisekunder brukar vara mer än tillräckligt, men det varierar mellan olika knapp- och strömbrytartyper.

Själva huvudloopen i vårt program anropar vår readKeyboard()-funktion om och om igen och när en knapptryckning upptäcks så skriver den ut numret på knappen som tryckts ner till serieanslutningen. För att inte skriva ut meddelandet om och om igen så länge en knapp är nertryckt så har vi även en variabel ”last_button” i vilken vi lagrar numret på den knapp som trycktes ner senast. Så länge knappnumret vi läser av från tangentbordet är samma som det vi lagrat i ”last_button” så vet vi att inget hänt och det är bara om dessa två skiljer sig som vi vet att en ny knapptryckning skett och vi skriver ut det till serieanslutningen.

Koden

// Här anger vi vilka pinnar som rad-anslutningarna på tangentbordet
// är kopplade till. Rad 1 till 4.
int row_pins[4] = {
  5, 4, 3, 2
};

// Här anger vi vilka pinnar som kolumn-anslutningarna på tangentbordet
// är kopplade till. Kolumn 1 till 4.
int col_pins[4] = {
  6, 7, 8, 9
};

void setup() {
  // Aktivera seriekommunikation i 115200 baud hastighet.
  Serial.begin(115200);
  // Skriv ut startmeddelande på serieporten för att visa
  // att systemet startat upp korrekt.
  Serial.println("Startar knappsats");

  // Ställ in rad-pinnarna till utgångar.
  for (int row = 0; row < 4; row++) {
    pinMode(row_pins[row], OUTPUT);
  }
  // Ställ in kolumn-pinnarna till ingångar. För att garantera
  // att ingångarna har en definitiv signalnivå aktiverar vi
  // även pull-up-motstånden som gör att ingångarna läses av som
  // höga om inget annat är inkopplat.
  for (int col = 0; col < 4; col++) {
    pinMode(col_pins[col], INPUT_PULLUP);
  }
}


// Denna funktion gör själva inläsningen av knappsatsen.
int readKeyboard() {
  // Sätt alla radutgångar till att vara höga. Enbart en utgång
  // får vara låg åt gången.
  for (int row = 0; row < 4; row++) {
    digitalWrite(row_pins[row], HIGH);
  }

  // Gå igenom alla raderna en efter en.
  for (int row = 0; row < 4; row++) {
    // Sätt aktuell rad-utgång till låg.
    digitalWrite(row_pins[row], LOW);
    // Gå igenom alla kolumnerna en efter en.
    for (int col = 0; col < 4; col++) {
      // Läs in kolumn-igången. Tack vare pull-up-motståndet
      // så kommer alla ingångar att läsas av som höga, såvida
      // inte en knapp är nertryckt och knappen är kopplad till
      // den radutgång som vi tidigare satte till låg.
      if (digitalRead(col_pins[col]) == LOW) {
        // Vi vet nu att vi hittat en knapp som är nertryckt.
        // När en knapp trycks in kommer metalldelarna i den att studsa
        // till några gånger. Detta ser ut som att knappen trycks in och
        // släpps flera gånger snabbt. Vi väntar därför 100ms tills
        // knappen stabiliserat sig.
        delay(100);
        // Räkna ut vilken knapp som är nertryckt och lämna tillbaka
        // knappens nummer till koden som anropade den här funktionen.
        // Vi måste lägga till ett eftersom koden börjar räkna på noll,
        // men knapparna är numrerade från ett. Vi använder siffran noll
        // för att berätta att ingen knapp är nertryckt.
        return ((row * 4) + col + 1);
      }
    }
  }
  // Om vi kommer hit betyder det att ingen knapp är nertryckt
  // så vi lämnar tillbaka noll istället.
  return 0;
}


// För att koden inte ska trigga om och om igen när en knapp hålls
// nertryckt behöver vi en variabel som kommer ihåg vilken knapp
// som trycktes ner senast.
int last_button = 0;

void loop() {
  // Läs av vilken knapp som är nertryckt (om någon) med hjälp av
  // vår readButton()-funtkion ovan.
  int button = readKeyboard();
  // Om knappnumret är större än noll betyder det att en knapp
  // faktiskt är nertryckt.
  if (button > 0) {
    // Kolla om vi redan reagerat på den här knapptryckningen.
    if (button != last_button) {
      // Detta är ett nytt knapptryck så vi skriver ut det på
      // serieanslutningen.
      Serial.print("Knapp tryckt: ");
      Serial.println(button);
      // Lagra knapptryckningen i vår variabel så att koden inte
      // reagerar på den igen nästa varv.
      last_button = button;
    }
  }
  else {
    // Om ingen knapp är nertryckt så nollställer vi vår variabel
    // så att vi kan reagera nästa gång en knapp trycks ner.
    last_button = 0;
  }
}

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *