František Pospíšil

Na svých stránkách popisuji cesty, kterými jsem došel k zamýšleným cílům. Cíle se občas během cesty změnily.

Programování

ATmega a rezistivní touchpad

touchpadV článku TFT displej s řadičem SSD1963 jsem popsal způsob ovládání TFT displeje procesorem ATmega162. Popsaný displej má rezistivní touchpad a v tomto článku popíšu, jak na displeji nakreslit tlačítka a s pomocí touchpadu dát programu informaci o jejich stisku.

Nakreslení tlačítka

Tlačítko bude barevný obdélník, ve kterém může být název tlačítka, případně nějaká veličina, kterou je možné po stisku tlačítka měnit rotačním kodérem.

tlačítkoNa displeji bude takových tlačítek několik a je potřeba alespoň 10x za sekundu překreslovat čísla, která jsou v nich. Nejjednodušší by bylo napsat funkci, která nakreslí tlačítko požadovanou barvou a v něm napíše číslo. Tuto funkci potom pouštět pořád dokola, pro všechny tlačítka. Kreslení celého tlačítka trvá dlouho a stisknuto je jenom někdy – takže je to ztráta času a navíc, displej při opakovaném překreslování bliká. Proto jsem se rozhodl naprogramovat dvě funkce. První nakreslí tlačítko. Provedení této funkce trvá delší dobu a je používána jenom tehdy, když je tlačítko stisknuto a je nutné změnit jeho barvu, nebo nápis v tlačítku.

/*********************************************************************************
*
* Function Name : Print_Array_Static
* Description    : vytvori pole pro vypis promenne
* nazev vypisovane promenne, cislo vypisovane promenne, oznaceni, barva ramecku
*
*********************************************************************************/
void Print_Array_Static( const char *UnitName1, const char *UnitNumber, const char *UnitValue, const char *UnitValue1,
unsigned int ColFront, unsigned int ColBack, unsigned int ColBox, unsigned int x, unsigned int y )
{
    TFT_ColorBox( x, x + 238, y, y + 66, ColBack );
    TFT_DrawBox( x, y, x + 238, y + 66, ColBox );
    TFT_PrintString( 2, UnitName1, ColFront, ColBack, x + 3, y + 3 );
    TFT_PrintString( 1, UnitNumber, ColFront, ColBack, x + 23, y + 18 );
    TFT_PrintString( 1, UnitName1, ColFront, ColBack, x + 3, y + 44 );
    TFT_PrintString( 0, UnitNumber, ColFront, ColBack, x + 15, y + 49 );
    TFT_PrintString( 2, UnitValue, ColFront, ColBack, x + 150, y + 3 );
    TFT_PrintString( 1, UnitValue, ColFront, ColBack, x + 110, y + 44 );
    TFT_PrintString( 0, UnitValue1, ColFront, ColBack, x + 215, y + 48 );
}

Druhá funkce kreslí čísla v tlačítku. Funkce trvá krátkou dobu, takže při 16MHz je možné displej z obrázku v záhlaví článku překreslit alespoň 15x za sekundu. Při použití ATxmega128 a kmitočtu 32MHz lze překreslovat 20x a ještě zbyde spousta času na další práci procesoru.

/*********************************************************************************
*
* Function Name : Print_Array_Value
* Description    : vypise promenne do pripraveneho pole
* aktualni hodnota promenne, nastavena hodnota promenne, pocet desetinnych mist, hodnota pomocne promenne, pocet desetinnych mist
*
*********************************************************************************/
void Print_Array_Value( unsigned int ValueAktual, unsigned int ValueSetting, unsigned char Decimal,
unsigned int ValueSetting1, unsigned char Decimal1, unsigned int ColFront, unsigned int ColBack, unsigned int x, unsigned int y )
{
    TFT_PrintNumber( 2, ValueAktual,   5, Decimal,  ColGreen, ColBack, x + 33,  y + 3  );    // pro Tahoma30x32 je y + 3
    TFT_PrintNumber( 1, ValueSetting,  5, Decimal,  ColFront, ColBack, x + 38,  y + 44 );
    TFT_PrintNumber( 0, ValueSetting1, 5, Decimal1, ColFront, ColBack, x + 150, y + 48 );
}

Touchpad ovládaný obvodem XPT2046

Z datového listu obvodu XPT2046 je zřejmé, že po SPI sběrnici je potřeba poslat osmibitový řídící znak a obvod odpoví dvanáctibitovou hodnotou, kterou naměřil AD převodník.

XPT2046Maximální kmitočet komunikace, kterou zvládne XPT2046, je někde kolem 2,5MHz. To znamená, že když procesor běží na 16MHz a je potřeba přenést osm bitů na kmitočtu 2MHz po sběrnici SPI, tak procesor může 64 taktů dělat něco jiného. ATxmega běží na 32MHz, takže to je zbytečných 128 taktů čekání na každý bajt komunikace.

Je celkem jednoduché najít na internetu knihovnu funkcí, která prostřednictvím SPI kanálu čte data z AD převodníku obvodu a vrací souřadnice místa na displeji, které je stisknuto. Funkce, která má vrátit souřadnice stisknutého místa, komunikuje s obvodem XPT2046 takto:

  • odešle řídící znak 0xD0 pro připojení odporové vrstvy X na napájení
  • přijme data z AD převodníku, který je připojen na osu Y
  • odešle řídící znak 0x90 pro připojení odporové vrstvy Y na napájení
  • přijme data z AD převodníku, který je připojen na osu X

Všechny knihovny, které jsem našel, čekají na reakci SPI sběrnice několik set taktů procesoru pro každé čtení dat. Další ztrátou času je přepínání mezi osou X a osou Y. Po odeslání kódu 0x90 je potřeba několik µs počkat, než se ustálí napětí připojené na odporovou vrstvu Y.

XPT2046 ovládaný v přerušení

To se mi nelíbilo, proto jsem se rozhodl použít pro komunikaci s XPT2046 systém přerušení. Činnosti, které má procesor vykonávat pravidelně, několikrát za sekundu, je vhodné naprogramovat do funkce, kterou spouští přerušení časovače. V mém případě je časovač nastaven tak, aby program jeho přerušení byl spouštěn 1000x za sekundu. Ještě budu potřebovat, aby funkce přerušení časovače a přerušení SPI kanálu vykonávala pokaždé něco jiného, to bude zobrazovat proměnná Touch_Counter.

Program přerušení časovače:

ISR( TCC0_OVF_vect )
{
    switch ( Touch_Counter )
    {
        case 0:
        {
            XPT2046_set_CS;              // pocatek komunikace s XPT2046
            Touch_Counter++;
            SPIE_DATA = 0xD0;            // kod pro osu X
            break;
        }
        case 1:
        {
            SPIE_DATA = 0;
            Touch_Counter++;             // mezera po kodu X a 0 pro cteni dat
            break;
        }
        case 4:
        {
            SPIE_DATA = 0;               // mezera po kodu Y a 0 pro cteni dat
            Touch_Counter++;
            break;
        }
    }
}

Program přerušení od SPI kanálu:

ISR( SPIE_INT_vect )
{
    switch ( Touch_Counter )
    {
        case 2:
        {
            Touch_X = SPIE_DATA << 5;        // cteni dat pro osu X
            SPIE_DATA = 0;
            Touch_Counter++;
            break;
        }
        case 3:
        {
            Touch_X += SPIE_DATA >> 3;       // cteni dat pro osu X
            SPIE_DATA = 0x90;                // kod pro osu Y
            Touch_Counter++;
            break;
        }
        case 5:
        {
            Touch_Y = SPIE_DATA << 5;        // cteni dat pro osu Y
            SPIE_DATA = 0;
            Touch_Counter++;
            break;
        }
        case 6:
        {
            Touch_Y += SPIE_DATA >> 3;        // cteni dat pro osu Y
            Touch_Counter = 0;
            XPT2046_clr_CS;                   // konec komunikace s XPT2046
            break;
        }
    }
}

Obvod XPT2046 dokáže na pinu /PENIRQ generovat impuls v okamžiku, kdy je stisknut touchpad. Tento impuls lze využít k zavolání přerušení v procesoru. To zatím nebudu používat, program ovládaný časovačem poběží periodicky a v proměnné Touch_X a Touch_Y budou stále aktuální údaje z AD převodníku XPT2046. V další části programu vyhodnotím, zda je stisknut touchpad a vypočítám průměrné hodnoty z osmi měření. Počítadlo měřených hodnot je proměnná Touch_Counter1.

if(( Touch_X > 10 ) && ( Touch_Y < 4085 ))        // je dotyk
{
    if( Touch_Counter1 < 8 )
    {
        Touch_XAdd += Touch_X;                    // pricte pro vypocet prumeru
        Touch_YAdd += Touch_Y;
        Touch_Counter1++;
    }
    if( Touch_Counter1 == 1 )
    {
        Touch_X -= TouchError;                    // zjisti, jesti neni moc velky rozdil mezi 1 a druhym vzorkem
        Touch_Y -= TouchError;
        if((( Touch_XAdd - Touch_X ) > ( TouchError * 2 )) || (( Touch_YAdd - Touch_Y ) > ( TouchError * 2 )))
        {
            Touch_Counter1 = 0;    // je moc velky rozdil mezi prvnim a druhym vzorkem, zacne se znovu
            Touch_XAdd = 0;
            Touch_YAdd = 0;
        }
    }
    if( Touch_Counter1 == 8 )                    // je prijato 8 vzorku
    {
        Touch_OutX = ( Touch_XAdd >> 3 ) - TouchError;        // zjisti, jestli neni moc velky rozdil mezi prumerem a poslednim vzorkem
        Touch_OutY = ( Touch_YAdd >> 3 ) - TouchError;
        if ((( Touch_X - Touch_OutX ) < ( TouchError * 2 )) && (( Touch_Y - Touch_OutY ) < ( TouchError * 2 )))
        {
            Touch_OutX = 480 - (( Touch_XAdd * 480 ) >> 15 );    // prepocita na souradnice displeje
            Touch_OutY = 272 - (( Touch_YAdd * 272 ) >> 15 );
        }
    }
}

Průměrné hodnoty jsou při výpočtu porovnávány s druhou a poslední naměřenou hodnotou. Pokud je rozdíl příliš velký, znamená to, že tlak na dotykovou plochu touchpadu je malý, nebo že se dotýkáme velkou plochou – bříškem prstu. V tom případě je mezi vrstvou X a Y velký a proměnlivý odpor, který způsobuje chybu měření.

Když je chyba měření malá, jsou naměřené hodnoty přepočítány na souřadnice displeje. Použité rovnice jsou jednoduché, nezohledňují posunutí, nebo pootočení dotykové vrstvy na panelu displeje. To by bylo možné korigovat s použitím maticového počtu, jak je popsáno např. zde. Pro ovládání tlačítek to nebude potřeba, program nemusí být tak moc přesný.

Následující kousek programu vyhodnotí, které tlačítko bylo stisknuto a podle toho nastaví proměnnou Setting_Value.

if(( Touch_OutX >  10 ) && ( Touch_OutX < 230 ) && ( Touch_OutY >  24 ) && ( Touch_OutY <  74 ))     // podle souradnic nastavi promennou, ktera se meni
{
    Setting_Value = 0x01;
}
if(( Touch_OutX >  10 ) && ( Touch_OutX < 230 ) && ( Touch_OutY >  94 ) && ( Touch_OutY < 144 ))
{
    Setting_Value = 0x02;
}
if(( Touch_OutX > 250 ) && ( Touch_OutX < 470 ) && ( Touch_OutY >  24 ) && ( Touch_OutY <  74 ))
{
    Setting_Value = 0x04;
}
if(( Touch_OutX > 250 ) && ( Touch_OutX < 470 ) && ( Touch_OutY >  94 ) && ( Touch_OutY < 144 ))
{
    Setting_Value = 0x08;
}

Na základě změny stavu proměnné Setting_Value se změní zabarvení tlačítek. Zároveň se podle proměnné Setting_Value řídí chod programu přerušení, které je generováno rotačním kodérem. Plochy, které jsou touchpadem detekovány jako tlačítka, jsou po obvodu alespoň o pět pixelů menší, aby nedocházelo k přehmatům.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *