Tento článek popisuje metodu propojení maticové klávesnice 4x4, jako je ta znázorněná na obrázku, s Picem. Existují i ​​jiné metody pro vytvoření tohoto rozhraní (byla by to například pěkná aplikace pro koprocesory PIO — bude popsáno dole). Tato metoda je podle mého nejjednodušší a vystačíme si se znalostmi z programování GPIO.

Maticová klávesnice 4x4

IMG 20250429 164729

schema4x4

Čtení klávesnice

Konfigurace GPIO

Pro skenování klávesnice a detekci stisknutí tlačítek nakonfigurujeme každý z GPIO připojených k řádkům klávesnice (9-12, jak je znázorněno na schématu) jako výstupy a každý z GPIO připojených ke sloupcům klávesnice (13-16, jak je znázorněno na schématu) nakonfigurujeme jako vstupy. Pro každý ze vstupních pinů povolíme interní pull-down rezistory. Pro začátek nastavíme všechny připojené výstupní GPIO na nízkou úroveň.

Použitím C SDK pro RP2040 vypadají všechny tyto konfigurace následovně:

#define BASE_KEYPAD_PIN 9

// Inicializujeme celou klávesnici
gpio_init_mask((0xFF << BASE_KEYPAD_PIN)) ;
// Nastavíme řádkové piny (GP9, GP10, GP11 a GP12) jako výstupní
gpio_set_dir_out_masked((0xF << BASE_KEYPAD_PIN)) ;
// Nastavíme všechny výstupní piny na nízkou úroveň
gpio_put_masked((0xF << BASE_KEYPAD_PIN), (0x0 << BASE_KEYPAD_PIN)) ;
// Zapneme pro vstupní sloupcové piny pull-down rezistory (je to nastaveno implicitně)
gpio_pull_down((BASE_KEYPAD_PIN + 4)) ;
gpio_pull_down((BASE_KEYPAD_PIN + 5)) ;
gpio_pull_down((BASE_KEYPAD_PIN + 6)) ;
gpio_pull_down((BASE_KEYPAD_PIN + 7)) ;

Skenování klávesnice

RP2040 nám umožňuje získat stav všech GPIO portů pomocí funkce gpio_get_all(). Tato funkce vrací 32bitové celé číslo bez znaménka, jehož každý bit představuje stav (vysoký nebo nízký) přidruženého GPIO portu (bit 0 je GPIO 0, bit 1 je GPIO 1 atd.).

Klávesnici prohledáme tak, že postupně nastavíme GPIO připojené ke sloupcům matice na vysokou úroveň a poté přečteme stavy všech 7 GPIO připojených ke klávesnici. Pro usnadnění zavoláme gpio_get_all(), poté posuneme výsledek doprava o 9 (takže stav GPIO 9 bude v bitu 0) a poté maskujeme pomocí 0xff. Tímto způsobem nám zůstane 8bitové číslo, kde každý bit v tomto čísle představuje jeden z pinů klávesnice. První čtyři jsou připojeny k řádkům a další 4 ke sloupcům.

keypad = ((gpio_get_all() >> BASE_KEYPAD_PIN) & 0xFF) ;

Předpokládejme, že nastavíme GPIO 9 a nestiskneme žádné klávesy. Pak žádný z pinů sloupce není zkratován s piny řádku a všechny zůstanou na nízké úrovni. Bit spojený s GPIO 9 bude na vysoké úrovni (protože jsme ho nastavili na vysokou úroveň), ale žádný z ostatních. Takže když skenujeme klávesnici, dostaneme hodnotu 0b0000001 neboli 0x01. Toto je skenovací kód, který odpovídá „nestisknutí“.

Předpokládejme místo toho, že nastavíme GPIO 9 na vysokou úroveň a stiskneme klávesu 1. Jakou hodnotu bychom od klávesnice očekávali? Bit spojený s GPIO 9 bude mít vysokou hodnotu (protože jsme ho nastavili na vysokou hodnotu) a bit spojený s GPIO 13 bude také vysoký, protože stisknutí klávesy 1 ji zkratovalo na GPIO 9. Binární hodnota pro klávesnici tedy bude 0b00010001, neboli 0x11 v hexadecimální soustavě.

Předpokládejme, že bychom místo toho stiskli 2. Pak by bity klávesnice spojené s GPIO 9 a 14 byly vysoké. Dostali bychom skenovací kód 0b00100001, neboli 0x21.

Dále předpokládejme, že bychom stiskli 3. Pak by bity klávesnice spojené s GPIO 9 a 15 byly vysoké. Dostali bychom skenovací kód 0b01000001, neboli 0x41.

Nakonec předpokládejme, že bychom stiskli A. Pak by bity klávesnice spojené s GPIO 9 a 15 byly vysoké. Dostali bychom skenovací kód 0b10000001, neboli 0x81.

Pokud bychom dostali jiný skenovací kód než 0x01, 0x11, 0x21, 0x41 nebo 0x81, pak to znamená, že uživatel stiskl dvě klávesy současně. Můžete rozšířit kódy kláves, aby vyhovovaly těmto případům, ale prozatím je budeme považovat za „neplatné stisknutí“.

Níže uvedená tabulka uvádí tyto případy pro řádek 0 a rozšiřuje se o skenovací kódy pro další tři řádky klávesnice.

GPIO16 GPIO15 GPIO14 GPIO13 GPIO12 GPIO11 GPIO10 GPIO9 řídící kód sken kód význam

0

0

0

0

0

0

0

1

0x1

0x01

nestisknuto

0

0

0

1

0

0

0

1

0x1

0x11

stisknuto 1

0

0

1

0

0

0

0

1

0x1

0x21

stisknuto 2

0

1

0

0

0

0

0

1

0x1

0x41

stisknuto 3

1

0

0

0

0

0

0

1

0x1

0x81

stisknuto A

x

x

x

x

0

0

0

1

0x1

jiné

neplatné stisknutí

0

0

0

0

0

0

1

0

0x2

0x02

nestisknuto

0

0

0

1

0

0

1

0

0x2

0x12

stisknuto 4

0

0

1

0

0

0

1

0

0x2

0x22

stisknuto 5

0

1

0

0

0

0

1

0

0x2

0x42

stisknuto 6

1

0

0

0

0

0

1

0

0x2

0x82

stisknuto B

x

x

x

x

0

0

1

0

0x2

jiné

neplatné stisknutí

0

0

0

0

0

1

0

0

0x4

0x04

nestisknuto

0

0

0

1

0

1

0

0

0x4

0x14

stisknuto 7

0

0

1

0

0

1

0

0

0x4

0x24

stisknuto 8

0

1

0

0

0

1

0

0

0x4

0x44

stisknuto 9

1

0

0

0

0

1

0

0

0x4

0x84

stisknuto C

x

x

x

x

0

1

0

0

0x4

jiné

neplatné stisknutí

0

0

0

0

1

0

0

0

0x8

0x08

nestisknuto

0

0

0

1

1

0

0

0

0x8

0x18

stisknuto *

0

0

1

0

1

0

0

0

0x8

0x28

stisknuto 0

0

1

0

0

1

0

0

0

0x8

0x48

stisknuto #

1

0

0

0

1

0

0

0

0x8

0x88

stisknuto D

x

x

x

x

1

0

0

0

0x8

jiné

neplatné stisknutí

Všimněte si, že můžeme provést rychlou kontrolu, zda bylo stisknuto nějaké tlačítko, maskováním klávesnice pomocí 0xFF a kontrolou, zda je výsledek nenulový. Pokud je výsledek nenulový, znamená to, že byl stisknut jeden nebo více GPIO sloupců (16, 15, 14 a/nebo 13). Poté můžeme porovnat celý skenovací kód s platnými skenovacími kódy a určit, které tlačítko bylo stisknuto.

Naprogramovaná sekvence kroků vypadá v Céčku takto:
#define BASE_KEYPAD_PIN 9
#define KEYROWS         4
#define NUMKEYS         16

// klávesa na klávesnici            0     1     2     3     4     5     6     7     8     9
//                                  A     B     C     D     *     #
unsigned int keycodes[NUMKEYS]  = { 0x28, 0x11, 0x21, 0x41, 0x12, 0x22, 0x42, 0x14, 0x24, 0x44,
                                    0x81, 0x82, 0x84, 0x88, 0x18, 0x48};
unsigned int scancodes[KEYROWS] = { 0x01, 0x02, 0x04, 0x08} ;
unsigned int button = 0xF0 ;
unsigned int keypad;
int i;
// Skenujeme klávesnici
for (i=0; i<KEYROWS; i++) {
    // Nastavíme řádek na jedničku
    gpio_put_masked((0xF << BASE_KEYPAD_PIN), (scancodes[i] << BASE_KEYPAD_PIN)) ;
    // potřebujeme malou prodlevu
    sleep_us(1) ;
    // Čteme kód klávesy
    keypad = ((gpio_get_all() >> BASE_KEYPAD_PIN) & 0xFF) ;
    // Vyskočíme, pokud je nějaké tlačítko stisknuto
    if (keypad & button) break ;
}
// Pokud jsme našli tlačítko . . .
if (keypad & button) {
    // Ověříme kód klávesy.
    for (i=0; i<NUMKEYS; i++) {
        if (keypad == keycodes[i]) break ; // podařilo se
    }
    // Pokud se nám ověření klávesy nepodařilo, vrátíme neplatný kód klávesy
    if (i==NUMKEYS) (i = -1) ;
}
// Cokoliv jiného znamená neplatný kód klávesy nebo nestisknuté tlačítko.
else (i=-1) ;

Po spuštění výše uvedeného kódu bude hodnota proměnné i == -1 pro nestisknutí nebo neplatné stisknutí, v opačném případě bude mít hodnotu stisknuté klávesy (0 pro klávesu 0, 1 pro klávesu 1, …​, 10 pro klávesu A, 11 pro klávesu B, 12 pro klávesu C, 13 pro klávesu D a 14 pro klávesu * a 15 pro klávesu #).

Praktický test

Praktický test uděláme tak, že si budeme stisknuté klávesy zobrazovat pomocí stdio. U klávesnice na horním obrázku nejsou klasické spínače, ale odporové s hodnotou přechodového odporu okolo \(45\Omega\), rezistory R1 až R4 jsem nahradil hodnotou \(470\Omega\), protože byly po ruce. Možná by v zapojení odpory R1 až R4 nemusely být, protože proud do výstupního pinu je omezen implicitně na 2mA.

Tabulka 1. Konektor maticové klávesnice je zapojen takto (zleva doprava):
pin význam barva v mé konstrukci

1

1. řádek

oranžová

2

2. řádek

žlutá

3

3. řádek

zelená

4

4. řádek

modrá

5

1. sloupec

fialová

6

2. sloupec

šedá

7

3. sloupec

bílá se šedým pruhem

8

4. sloupec

černá

Konstrukce prototypu

IMG 20250430 091508

/* Testování maticové klávesnice 4x4
    (c) Jirka Chráska 2025, <jirka@lixis.cz>
*/

#include <stdio.h>
#include <pico/stdlib.h>


#define BASE_KEYPAD_PIN 9
#define KEYROWS         4
#define NUMKEYS         16

// Inicializujeme celou klávesnici
void keypad_init()
{
    gpio_init_mask((0xFF << BASE_KEYPAD_PIN));
    // Nastavíme řádkové piny (GP9, GP10, GP11 a GP12) jako výstupní
    gpio_set_dir_out_masked((0xF << BASE_KEYPAD_PIN));
    // Nastavíme všechny výstupní piny na nízkou úroveň
    gpio_put_masked((0xF << BASE_KEYPAD_PIN), (0x0 << BASE_KEYPAD_PIN));
    // Zapneme pro vstupní sloupcové piny pull-down rezistory (je to nastaveno implicitně)
    gpio_pull_down((BASE_KEYPAD_PIN + 4));
    gpio_pull_down((BASE_KEYPAD_PIN + 5));
    gpio_pull_down((BASE_KEYPAD_PIN + 6));
    gpio_pull_down((BASE_KEYPAD_PIN + 7));
}

int keypad_read()
{
// klávesa na klávesnici            0     1     2     3     4     5     6     7     8     9
//                                  A     B     C     D     *     #
unsigned int keycodes[NUMKEYS]  = { 0x28, 0x11, 0x21, 0x41, 0x12, 0x22, 0x42, 0x14, 0x24, 0x44,
                                    0x81, 0x82, 0x84, 0x88, 0x18, 0x48};
unsigned int scancodes[KEYROWS] = { 0x01, 0x02, 0x04, 0x08} ;
unsigned int button = 0xF0 ;
unsigned int keypad;
int i;

    // Skenujeme klávesnici
    for (i=0; i<KEYROWS; i++) {
        // Nastavíme řádek na jedničku
        gpio_put_masked((0xF << BASE_KEYPAD_PIN), (scancodes[i] << BASE_KEYPAD_PIN));
        // potřebujeme malou prodlevu
        sleep_us(1) ;
        // Čteme kód klávesy
        keypad = ((gpio_get_all() >> BASE_KEYPAD_PIN) & 0xFF);
        // Vyskočíme, pokud je nějaké tlačítko stisknuto
        if (keypad & button) break;
    }
    // Pokud jsme našli tlačítko . . .
    if (keypad & button) {
        // Ověříme kód klávesy.
        for (i=0; i<NUMKEYS; i++) {
            if (keypad == keycodes[i]) break ; // podařilo se
        }
        // Pokud se nám ověření klávesy nepodařilo, vrátíme neplatný kód klávesy
        if (i==NUMKEYS) (i = -1);
    }
    // Cokoliv jiného znamená neplatný kód klávesy nebo nestisknuté tlačítko.
    else (i=-1) ;
return i;
}

int main()
{
int klavesa = -1;
int predchozi_klavesa = -1;
char c[NUMKEYS] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','*','#'};
    stdio_init_all();
    sleep_ms(2000);
    printf("Test klávesnice.\n");
    keypad_init();
    while( true ) {
        klavesa = keypad_read();
        if( klavesa != -1 && klavesa != predchozi_klavesa ) {
            printf("Stisknuta klávesa %c\n",c[klavesa]);
            predchozi_klavesa = klavesa;
        }
        else {
            predchozi_klavesa = -1;
        }
        sleep_ms(200);
    }
}

Po uplynutí 200 ms se bude stisk klávesy opakovat, budu-li ji stále držet.

cmake_minimum_required(VERSION 3.13)

set(PICO_BOARD pico)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)

project(keypad4x4 C CXX ASM)

pico_sdk_init()

add_executable (keypad4x4 mkeypad4x4.c)

target_link_libraries(keypad4x4 LINK_PUBLIC
  pico_stdlib
  )

pico_add_extra_outputs(keypad4x4)
pico_enable_stdio_usb(keypad4x4 1)
pico_enable_stdio_uart(keypad4x4 0)
Výstup na minicomu
Test klávesnice.
Stisknuta klávesa 1
Stisknuta klávesa 2
Stisknuta klávesa 5
Stisknuta klávesa 5
Stisknuta klávesa 6
Stisknuta klávesa 8
Stisknuta klávesa 7
Stisknuta klávesa 7
Stisknuta klávesa 7
Stisknuta klávesa 7
Stisknuta klávesa 7
Stisknuta klávesa 7
Stisknuta klávesa 8
Stisknuta klávesa 8
Stisknuta klávesa 8
Stisknuta klávesa 8
Stisknuta klávesa 9
Stisknuta klávesa 9
Stisknuta klávesa 9
Stisknuta klávesa 9
Stisknuta klávesa C
Stisknuta klávesa C
Stisknuta klávesa C
Stisknuta klávesa C
Stisknuta klávesa A
Stisknuta klávesa A
Stisknuta klávesa 3
Stisknuta klávesa 2
Stisknuta klávesa 5
Stisknuta klávesa 1
Stisknuta klávesa 4
Stisknuta klávesa 5
Stisknuta klávesa 6
Stisknuta klávesa 7
Stisknuta klávesa 8
Stisknuta klávesa D
Stisknuta klávesa #
Stisknuta klávesa #
Stisknuta klávesa *
Stisknuta klávesa *
Stisknuta klávesa *
Stisknuta klávesa 5
Konstrukce prototypu

IMG 20250430 091535

Závěr

Tato konstrukce a program neumožňuje rozpoznat stisknutí dvou tlačítek najednou.

Maticovou klávesnici budu moci použít třeba k vylepšení Generátoru obdélníkových pulsů proměnné frekvence a střídy, protože bude mnohem jednoduší zadat kmitočet a střídu pomocí číselných hodnot a ne pomocí tlačítek.

Obsluha maticové klávesnice implementovaná pomocí PIO

Zde je implementace toho samého programu pomocí PIO stavového stroje a přerušení. Přerušení je vyvoláno při odchytnutí stisku klávesy.

keyboard.c
/* keyboard.c
   (c) Jirka Chráska 2025, jirka@lixis.cz
   ovladač klávesnice 4x4 pomocí PIO
   BSD 3 clause license
*/

#include <stdio.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "keyboard.pio.h"



static PIO pio = pio0;
static uint sm;
static uint8_t pio_irq;
static uint offset;
static bool klavesa = false;

char pismeno = '\0';


static char klavesy[4][4] = {
                            '1', '2', '3', 'A',
                            '4', '5', '6', 'B',
                            '7', '8', '9', 'C',
                            '*', '0', '#', 'D'
                            };

// obsluha přerušení vyvolaná při stisku klávesy
static void pio_irq_obsluzna_funkce( void )
{
    while( !pio_sm_is_rx_fifo_empty( pio, sm ) ) {
        if( pio_sm_get_rx_fifo_level(pio, sm) == 2 ) {
            uint32_t x,y;
            x = pio_sm_get( pio, sm );
            y = pio_sm_get( pio, sm );
            int i, j;
            for( i=0; i<4; i++) {
                if( x >> i == 1) break;
            }
            for( j=0; j<4; j++) {
                if( y >> j == 1 ) break;
            }
            pismeno = klavesy[j][i];
            klavesa = true;
        }
    }
    irq_clear(pio_irq);
}


int main( void )
{
    stdio_init_all();
    sleep_ms(2000);
    gpio_init(25);
    gpio_set_dir(25,true);
    gpio_put(25,1);
    printf("Test klávesnice 4x4.\n");
    printf("Stiskněte klávesu na počítači.\n");
    getchar();
    gpio_put(25,0);
    queue_init( &fifo, sizeof(uint32_t), FIFO_SIZE);
    offset = pio_add_program(pio, &keyboard_program);
    sm = pio_claim_unused_sm(pio, true);
    keyboard_program_init(pio, sm, offset, 9, 13);

    // najdeme volné přerušení
    pio_irq = pio_get_irq_num(pio, 0);
    if( irq_get_exclusive_handler( pio_irq ) ) {
        pio_irq++;
        if( irq_get_exclusive_handler( pio_irq ) ) {
            panic("Nenalezen volný interrupt.");
        }
    }
    printf("pio_irq=%d\n",pio_irq);
    // nastavíme přerušení
    irq_add_shared_handler( pio_irq, pio_irq_obsluzna_funkce, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY ); // přidáme sdílenou obsluhu
    irq_set_enabled( pio_irq, true ); // spustíme
    const uint irq_index = pio_irq - pio_get_irq_num( pio, 0 ); // obdržíme číslo přerušení
    pio_set_irqn_source_enabled( pio, irq_index, pio_get_rx_fifo_not_empty_interrupt_source( sm ), true); // nastavíme pio, aby nám řekl, když je něco v RX frontě
    printf("irq_index=%d\n",irq_index);

    while(1) {
        gpio_put(25,1);
        sleep_ms(100);
        gpio_put(25,0);
        sleep_ms(100);
        if( klavesa == true ) {
            printf("Stisknuta klávesa '%c'\n",pismeno);
            klavesa = false;
        }
    }
}
keyboard.pio
; klavesnice
; (c) Jirka Chráska 2025; <jirka@lixis.cz>
.program keyboard
;
.define KEYBOARD_IRQ 0
.wrap_target

nastav_radek_1:
    set pins, 1 [31]        ; nastavím 1. řádek klávesnice na vysokou úroveň a čekám 31 tiků hodin
    set X, 0x1              ; do registru X dám jedničku (to bude posléze souřadnice x)
    in pins, 4              ; přečtu sloupcové piny
    mov Y, isr              ; přečtenou hodnotu pinů si uložím do registru Y (to bude posléze souřadnice y)
    jmp !Y, nastav_radek_2  ; když je Y nulové (nic není v tomto řádku stisknuto) poklačuji dalším řádkem
    jmp rx_fifo             ; v Y mám souřadnici stisknuté klávesy, skáču na odeslání dat

nastav_radek_2:
    set pins, 2 [31]        ; nastavím 2. řádek klávesnice na vysokou úroveň a čekám 31 tiků hodin
    set X, 0x2              ; do registru X dám dvojku (to bude posléze souřadnice x)
    in pins, 4              ; přečtu sloupcové piny
    mov Y, isr              ; přečtenou hodnotu pinů si uložím do registru Y (to bude posléze souřadnice y)
    jmp !Y, nastav_radek_3  ; když je Y nulové (nic není v tomto řádku stisknuto) poklačuji dalším řádkem
    jmp rx_fifo             ; v Y mám souřadnici stisknuté klávesy, skáču na odeslání dat

nastav_radek_3:
    set pins, 4 [31]        ; nastavím 3. řádek klávesnice na vysokou úroveň a čekám 31 tiků hodin
    set X, 0x4              ; do registru X dám čtyřku (to bude posléze souřadnice x)
    in pins, 4              ; přečtu sloupcové piny
    mov Y, isr              ; přečtenou hodnotu pinů si uložím do registru Y (to bude posléze souřadnice y)
    jmp !Y, nastav_radek_4  ; když je Y nulové (nic není v tomto řádku stisknuto) poklačuji dalším řádkem
    jmp rx_fifo             ; v Y mám souřadnici stisknuté klávesy, skáču na odeslání dat

nastav_radek_4:
    set pins, 8 [31]        ; nastavím 4. řádek klávesnice na vysokou úroveň a čekám 31 tiků hodin
    set X, 0x8              ; do registru X dám osmičku (to bude posléze souřadnice x)
    in pins, 4              ; přečtu sloupcové piny
    mov Y, isr              ; přečtenou hodnotu pinů si uložím do registru Y (to bude posléze souřadnice y)
    jmp !Y nastav_radek_1   ; nic není stisknuto, vracím se na 1. řádek

rx_fifo:                    ; poslání dat (souřadnic klávesy) a vyvolání IRQ
    push                    ; pošlu souřadnici y (sloupec)
    in X, 4 [2]             ; řádek si vytáhnu z registru X
    push                    ; pošlu souřadnici x (řádek)
    irq KEYBOARD_IRQ  rel
    wait 0 pin 0            ; čekám, až budou klávesy uvolněny
    wait 0 pin 1
    wait 0 pin 2
    wait 0 pin 3
.wrap

% c-sdk {

#include "hardware/clocks.h"
#include "hardware/gpio.h"

static inline void keyboard_program_init (PIO pio, uint sm, uint offset, uint row_pin, uint col_pin) {

    // řádkové piny jsou nastaveny jako výstupní
    pio_gpio_init(pio, row_pin);
    pio_gpio_init(pio, row_pin+1);
    pio_gpio_init(pio, row_pin+2);
    pio_gpio_init(pio, row_pin+3);
    pio_sm_set_consecutive_pindirs(pio, sm, row_pin, 4, true);

    // sloupcové piny (vstupní)
    pio_sm_set_consecutive_pindirs(pio, sm, col_pin, 4, false);


    // Konfigurace stavového stroje
    pio_sm_config c = keyboard_program_get_default_config (offset);

    // konfigurace vstupního posuvného registru (Input Shift Register)
    sm_config_set_in_shift (&c,
                            false,       // posunujeme doprava
                            false,       // autopush je vypnut
                            32);         //


    // piny pro funkci IN
    sm_config_set_in_pins( &c, col_pin );
    // piny pro funkci SET
    sm_config_set_set_pins( &c, row_pin, 4 );

    // Tik hodin stavového stroje nastavíme na 4 kHz
    float div = clock_get_hz (clk_sys) / 4000;
    sm_config_set_clkdiv (&c, div);

    // Konfiguraci šoupneme do stavového stroje
    pio_sm_init (pio, sm, offset, &c);

    // Stavový stroj spustíme
    pio_sm_set_enabled (pio, sm, true);
}
%}
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)

set(PICO_BOARD pico)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)

project(keyboard C CXX ASM)

pico_sdk_init()

add_executable (keyboard keyboard.c)

pico_generate_pio_header(keyboard ${CMAKE_CURRENT_LIST_DIR}/keyboard.pio)

target_link_libraries(keyboard LINK_PUBLIC
  pico_stdlib
  hardware_pio
  )

pico_add_extra_outputs(keyboard)
pico_enable_stdio_usb(keyboard 1)
pico_enable_stdio_uart(keyboard 0)
Testovací výstup z minicomu
Test klávesnice 4x4.
Stiskněte klávesu na počítači.
pio_irq=7
irq_index=0
Stisknuta klávesa 'A'
Stisknuta klávesa '6'
Stisknuta klávesa '*'
Stisknuta klávesa '0'
Stisknuta klávesa '#'
Stisknuta klávesa 'D'
Stisknuta klávesa '5'
Stisknuta klávesa '1'
Stisknuta klávesa '3'
Stisknuta klávesa '3'
Stisknuta klávesa '3'
Stisknuta klávesa '3'
Stisknuta klávesa 'B'
Stisknuta klávesa 'B'
Stisknuta klávesa '7'
Stisknuta klávesa '7'
Stisknuta klávesa '4'
Stisknuta klávesa '4'
Stisknuta klávesa '9'
Stisknuta klávesa '9'
Stisknuta klávesa '*'
Stisknuta klávesa '0'
Stisknuta klávesa '4'
Stisknuta klávesa '8'
Stisknuta klávesa '5'
Stisknuta klávesa '9'
Stisknuta klávesa '6'
Stisknuta klávesa 'C'
Stisknuta klávesa 'B'
Stisknuta klávesa '7'
Stisknuta klávesa '*'
Stisknuta klávesa '*'

Ten samý program na odchytávání klávesnice pomocí PIO v MicroPythonu

V Pythonu je program trochu stručnější. Dělá to samé, co program v Céčku.

# ovladač klávesnice 4x4 tlačítka
# (c) Jirka Chráska 2025, jirka@lixis.cz

from machine import Pin
import rp2

@rp2.asm_pio(set_init=(rp2.PIO.OUT_LOW,)*4)
def keypad():
    wrap_target()
    label("set_row_1")
    set(pins, 1) [31]  # nastavíme row_1 na 1 a čekáme na stisk
    set(x,0x1)
    in_(pins,4)
    mov(y, isr)
    jmp(not_y,"set_row_2")
    jmp("rx_fifo")

    label("set_row_2")
    set(pins, 2) [31]
    set(x,0x2)
    in_(pins,4)
    mov(y, isr)
    jmp(not_y,"set_row_3")
    jmp("rx_fifo")

    label("set_row_3")
    set(pins, 4) [31]
    set(x,0x4)
    in_(pins,4)
    mov(y, isr)
    jmp(not_y,"set_row_4")
    jmp("rx_fifo")

    label("set_row_4")
    set(pins, 8) [31]
    set(x,0x8)
    in_(pins,4)
    mov(y, isr)
    jmp(not_y,"set_row_1")


    label("rx_fifo")
    push()              # do fronty dáme souřadnici y(sloupec)
    in_(x,4)[2]
    push()              # a potom do fronty dáme souřadnici x(řádek )
    irq(rel(0))

    wait(0,pin,0)       # počkáme, až bude klávesa uvolněna
    wait(0,pin,1)
    wait(0,pin,2)
    wait(0,pin,3)
    wrap()              # celkem 31 instrukcí

keys=[["1","2","3","A"],
      ["4","5","6","B"],
      ["7","8","9","C"],
      ["*","0","#","D"]]

def keypad_irq(sm):
    y=sm.get()
    x=sm.get()
    for i in range(4):
        if x >> i == 1:
            break;
    for j in range(4):
        if y >> j == 1:
            break;
    print(keys[i][j])

smkeypad=rp2.StateMachine(0, keypad, freq=2000, set_base=Pin(9), in_base=Pin(13))
smkeypad.irq(keypad_irq)
smkeypad.active(1)

Zdroje a odkazy