logo_elektromys.eu

/ STM8 GPIO II.|

/ Úvod |

V předchozím díle jsme si vyzkoušeli chování GPIO jako výstupu typu push-pull. Dalším logickým krokem bude vyzkoušet si chování GPIO nastaveného jako vstup. Přesněji řečeno jako logický vstup. Pomocí něj může mikrokontrolér sledovat logické úrovně které mu přivádí na piny buď lidská obsluha (například pomocí tlačítek a přepínačů) a nebo jiné elektrické obvody jako třeba různé snímače.

/ GPIO jako vstup |

Na našem kitu máme modré tlačítko označené B1, které je připojené na pin PE4 podle schematu níže. VDD je napájecí napětí našeho MCU, tedy 5V. Rezistor R6 je zapojen jako takzvaný "pull-up" a slouží k tomu aby na pin přiváděl 5V v situaci kdy je tlačítko (B1) rozpojené. Kondenzátor C5 a odpor R5 slouží k potlačování zákmitů a povíme si o nich něco jindy. Jak už jsem napsal, když je tlačítko uvolněné, naměříme na PE4 napětí 5V (zkuste si). Stisknutím tlačítka zkratujeme PE4 se zemí a přivedeme na něj tak 0V (zkuste si). Dokud je tlačítko stisknuté, teče skrze pullup rezistor (R6) proud (přibližně 5V/4k7 = 1mA). Pokud máme pin PE4 nastavený jako obyčejný vstup, chová se jako takzvaná "vysoká impedance" (HiZ). Jinak řečeno jako by tam vůbec nebyl. To ale platí jen pokud dodržíme některé podmínky. První z nich je ta že na něj nesmíme přivést napětí větší jak 5.3V a ani nesmí být menší jak -0.3V (při 5V napájení). Jinak řečeno nepřivádějte tam větší napětí jak napájecí napětí čipu (zde 5V) a rozhodně ne záporné.

Konfiguraci jako vstup provádíme naší známou funkcí GPIO_Init argumentem GPIO_MODE_IN_FL_NO_IT. Přečíst si log. úroveň na pinu pak můžeme funkcí GPIO_ReadInputPin. Argumenty této funkce jsou port a pin, stejně jako u všech předchozích funkcí které jsme používali. Funkce vrací hodnotu RESET pokud je na vstupu úroveň "Low" (log.0) nebo cokoli jiného (!) pokud je na vstupu úroveň "High" (log.1). Pojďme si to procvičit.

Cv.1:
Napište program, který rozsvítí LEDku (připojenou na PC5) když uživatel stiskne tlačítko (připojené na PE4) a zhasne ji když uživatel tlačítko uvolní. Jinak řečeno svítí LEDkou dokud je tlačítko zmáčknuté.


Hloupé řešení
Program necháme stále dokola sledovat logickou úroveň tlačítka (PE4) a podle ní neustále zapisovat na LEDku logickou úroveň (PC5). Pro jistotu ještě rekapituluji. Stav tlačítka čteme funkcí GPIO_ReadInputPin a pokud nám vrátí RESET znamená to že z pinu přečetla log.0, což znamená že je tlačítko stisknuté. Naopak pokud vrátí jinou hodnotu než RESET, znamená to že načetla log.1 a že je tlačítko uvolněné.

void main(void){
 GPIO_Init(GPIOC,GPIO_PIN_5,GPIO_MODE_OUT_PP_LOW_SLOW); // nastavíme PC5 jako výstup typu push-pull (LEDka)
 GPIO_Init(GPIOE, GPIO_PIN_4,GPIO_MODE_IN_FL_NO_IT); // nastavíme PE4 jako vstup (tlačítko)

 while (1){ // stále dokola
  if(GPIO_ReadInputPin(GPIOE,GPIO_PIN_4)==RESET){ // kontrolujeme zda je tlačítko stisknuté... (je na PE4 log.0)
   GPIO_WriteHigh(GPIOC,GPIO_PIN_5); // pokud ano tak rozsvěcíme LED  (zapisujeme log.1)
  }else{
   GPIO_WriteLow(GPIOC,GPIO_PIN_5); // pokud ne tak zhasneme LED  (zapisujeme log.0)
  }
 }
}

Chytré řešení
Program necháme rozpoznávat okamžik stisknutí a okamžik uvolnění tlačítka a stav LEDky budeme měnit jen v tyto okamžiky. Věnujte pozornost slovnímu spojení "rozpoznávat okamžik stisknutí". Nyní chceme po našem programu mnohem náročnější akci. Chceme po něm aby poznal OKAMŽIK stisknutí (a uvolnění) tlačítka. To není to samé jako poznat zda je tlačítko stisknuté. Jak takový okamžik poznáme ? Jednoduše. Budeme opět stále dokola číst stav tlačítka (PE4) a porovnávat ho s jeho minulou hodnotou. Tlačítko může být ve dvou stavech ("je stisknuté" a "je uvolněné") a minulý stav tlačítka taktéž ("minule bylo uvolněné" a "minule bylo stisknuté"). Máme tedy čtyři možnosti jaké mohou nastat:
#include "stm8s.h"

uint8_t minuly_stav=1; // zde si budeme ukládat minulý stav tlačítka (1=tlačítko stisknuté, 0=tlačítko uvolněné)
uint8_t aktualni_stav=1;  // zde si budeme ukládat aktuální stav tlačítka (1=tlačítko stisknuté, 0=tlačítko uvolněné)
// u funkce GPIO_ReadInputPin, hodnota RESET znamená log.0 a tedy stisknuté tlačítko

void main(void){
 GPIO_Init(GPIOC,GPIO_PIN_5,GPIO_MODE_OUT_PP_LOW_SLOW); // nastavíme PC5 jako výstup typu push-pull (LEDka)
 GPIO_Init(GPIOE, GPIO_PIN_4,GPIO_MODE_IN_FL_NO_IT); // nastavíme PE4 jako vstup (tlačítko)

  while (1){ // stále dokola
 // načteme aktuální stav tlačítka
  if(GPIO_ReadInputPin(GPIOE,GPIO_PIN_4)==RESET){ // zjisti jestli je tlačítko stisknuté 
   aktualni_stav=1; // pokud ano ulož že je stisknuté
  } 
  else{
   aktualni_stav=0;// jinak ulož že je uvolněné
  } 
 
 // teď budeme kontrolovat jestli nenastal "okamžik stisku" nebo "okamžik uvolnění"
 
  if(minuly_stav==1 && aktualni_stav==0){ // je to okamžik stisku ?
   GPIO_WriteHigh(GPIOC,GPIO_PIN_5); // pokud ano rozsvítíme LEDku
  }
 
  if(minuly_stav==0 && aktualni_stav==1){ // je to okamžik uvolnění ?
   GPIO_WriteLow(GPIOC,GPIO_PIN_5); // pokud ano zhasneme LEDku
  }

  minuly_stav = aktualni_stav; // aktuální stav tlačítka už se stal "minulostí" 
  // teď je z něj minulý stav tlačítka a my si jdeme přečíst nový aktuální stav
  }
}

Jistě jste zaznamenali že "chytré řešení" je myšlenkově mnohem náročnější. Už ve druhém cvičení ale zjistíte, že je mnohem užitečnější. Až na výjimky totiž skoro vždycky budete chtít aby program nějak reagoval na stisknutí tlačítka jednorázově. Třeba když píšete na klávesnici chcete jedním stiskem napsat jedno písmeno. Nechcete aby program napsal 1000 písmen jenom proto že za těch 100ms kdy jste tlačítko podrželi zmáčknuté si stihl stav tlačítka přečíst 1000x ...

Cv.2:
Napište program, který jedním stiskem tlačítka rozsvítí LEDku a druhým stiskem ji zhasne. Tedy něco jako "on/off" tlačítko. Tlačítko je na PE4, LEDka na PC5.


Poradíme si podobně jako v předchozím cvičení. Už umíme napsat program, který "odchytí" stisk tlačítka a zareaguje na něj. Stačí tedy přidat funkci na přepínání stavu LEDky. Abychom ale mohli ledku "přepínat" (tedy měnit její stav), musíme si pamatovat v jakém stavu je (jinak bychom nevěděli co s ní udělat). Když je zhasnutá, chceme ji rozsvítit, když rozsvícená zhasnout.

#include "stm8s.h"

uint8_t minuly_stav=0; // zde si budeme ukládat minulý stav tlačítka (1=tlačítko stisknuté, 0=tlačítko uvolněné)
uint8_t aktualni_stav=0;  // zde si budeme ukládat aktuální stav tlačítka (1=tlačítko stisknuté, 0=tlačítko uvolněné)
uint8_t stav_ledky=0; // tady si pamatujeme jestli LEDka svítí nebo je zhasnutá (1=svítí, 0=nesvítí)

void main(void){
 GPIO_Init(GPIOC,GPIO_PIN_5,GPIO_MODE_OUT_PP_LOW_SLOW); // nastavíme PC5 jako výstup typu push-pull (LEDka)
 GPIO_Init(GPIOE, GPIO_PIN_4,GPIO_MODE_IN_FL_NO_IT); // nastavíme PE4 jako vstup (tlačítko)

  while (1){ // stále dokola
  // načteme aktuální stav tlačítka
  if(GPIO_ReadInputPin(GPIOE,GPIO_PIN_4)==RESET){ // zjisti jestli je tlačítko stisknuté 
   aktualni_stav=1; // pokud ano ulož že je stisknuté
  } 
  else{
   aktualni_stav=0;// jinak ulož že je uvolněné
  } 
 
  if(minuly_stav==0 && aktualni_stav==1){ // je to okamžik stisku ?
   // přepneme stav LEDky
   if(stav_ledky==1){ // pokud je LEDka rozsvícená
    GPIO_WriteLow(GPIOC,GPIO_PIN_5); // zhasneme ji...
    stav_ledky=0; // ...a zapamatujeme si že je zhasnutá
   }else{ // jinak je LEDka zhasnutá, takže...
    GPIO_WriteHigh(GPIOC,GPIO_PIN_5); // ...ji rozsvítíme ...
    stav_ledky=1; // ... a zapamatujeme si že je rozsvícená
   }
  }
 
  minuly_stav = aktualni_stav; // aktuální stav tlačítka se stal minulým
  }
}

/ Detaily pro "zvídavé" |

Jistě jste si všimli že přinejmenším program pro poslední cvičení by šel napsat v mnoha ohledech efektivněji. Že docela plýtváme pamětí když použijeme celý bajt na zapamatování stavu 1 nebo 0 (na to by přece stačil jediný bit). Podobně je na tom i proměnná stav_ledky,která také zbytečně zabírá jeden bajt paměti, protože tuto informaci už máme ve skutečnosti uloženou jinde. Chápejte to jako daň za to že se jazyk i práci teprve učíme (a navíc máme paměti dost). Kdo by přece jen chtěl trochu "optimalizovanější" kód, je mu k dispozici.

/ K zapamatování |

| Odkazy /

Home
| V1.01 27.4.2020 /
| By Michal Dudka (m.dudka@seznam.cz) /