Joystick se skládá ze dvou potenciometrů, které jsou umístěny kolmo na sebe a mechanicky spřaženy. Pokud chceme zjistit, v jaké poloze se joystick nachází, stačí nám ADC převodníkem změřit napětí na středním kontaktu každého z potenciometrů.

joystick hw 504

Tlačítko na joysticku zůstane nevyužito. Pokud by ho chtěl někdo používat, stačí kontakt SW na joysticku připojit na nějaký GPIO pin a programově lze zjišťovat funkcí gpio_get( cislo_pinu ) jeho stav.

Tlačítko jde stisknout jenom pokud je joystick ve střední poloze.

Schema zapojení

simple joystick schema

Jednoduchý příklad se nachází přímo v příkladech Pico-SDK (adresář pico-examples)

CMakeLists.txt
cmake_minimum_required(VERSION 3.12)

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

project(joystick_display)

pico_sdk_init()


add_executable(joystick_display
        joystick_display.c
        )

target_link_libraries(joystick_display pico_stdlib hardware_adc)

# create map/bin/hex file etc.
pico_add_extra_outputs(joystick_display)
pico_enable_stdio_usb(joystick_display 1)
pico_enable_stdio_uart(joystick_display 1)

Ve smyčce (trvá přibližně 50ms) čteme nejprve polohu x z ADC0 a potom polohu y z ADC1. V programu nás nezajímá přesná hodnota napětí. V proměnných adc_x_raw a adc_y_raw budeme mít po každém čtení adc_read() nějaké číslo z rozsahu 0 až 4096. (Možná bude rozsah menší, protože se joystick nedá vytočit až do krajních poloh potenciometrů). Pokud je joystick v neutrální poloze, tak naměříme přibližně polovinu (2048).

joystick_display.c
/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

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

int main() {
    stdio_init_all();
    adc_init();
    // Make sure GPIO is high-impedance, no pullups etc
    adc_gpio_init(26);
    adc_gpio_init(27);

    while (1) {
        adc_select_input(0);
        uint adc_x_raw = adc_read();
        adc_select_input(1);
        uint adc_y_raw = adc_read();

        // Display the joystick position something like this:
        // X: [            o             ]  Y: [              o         ]
        const uint bar_width = 60;
        const uint adc_max = (1 << 12) - 1;
        uint bar_x_pos = adc_x_raw * bar_width / adc_max;
        uint bar_y_pos = adc_y_raw * bar_width / adc_max;
        printf("\rX: [");
        for (uint i = 0; i < bar_width; ++i)
            putchar( i == bar_x_pos ? 'o' : ' ');
        printf("]  Y: [");
        for (uint i = 0; i < bar_width; ++i)
            putchar( i == bar_y_pos ? 'o' : ' ');
        printf("]");
        sleep_ms(50);

    }
}

Nepříjemnou vlastností je to, že i když s nebudeme s joystickem hýbat, tak budou naměřené hodnoty poskakovat. Je to způsobeno jednak nepřesností samotného ADC převodníku a dále nestabilitou referenčního napětí 3.3V.

Nestabilitu referenčního napětí můžeme zmenšit přepnutím PWM měniče to neúsporného režimu. Dělá se to takto:

// nastavení měniče na malý šum
gpio_set_function(23, GPIO_FUNC_SIO);
gpio_set_dir( 23, true );
gpio_put( 23, 1 );

Další zlepšení může být třeba tak, že hodnoty adc_x_raw a adc_y_raw převedeme na reálné číslo float, vydělíme 10 a zaokrouhlíme. Nebo lze snížit citlivost ADC bitovým posunem přečtené hodnoty na 8 bitů (256 hodnot). (Spodní bity dělají ono poskakování.) Tím se sníží přesnost i citlivost a zároveň se potlačí šum (ono poskakování). V dalším příkladu je to vyřešeno.