Algorytm eliminacji drgań styków w klawiaturze 68-klawiszowej z polifoni±
Re: Obsługa klawiatury i drgania styków
From: "Marek Dzwonnik" <mdz_at_nospam_message.pl>
Subject: Re: Obsługa klawiatury i drgania styków
Date: Tue, 7 May 2002 22:49:21 +0200
Użytkownik "~Filut~" <pawel_at_nospam_filuts.waw.pl> napisał w wiadomo¶ci
news:ab8rii$5ev$1_at_nospam_news.tpi.pl...
Użytkownik <mstanisz_at_nospam_poczta.onet.pl> napisał w wiadomo¶ci
news:56fe.00000e39.3cd7ea64_at_nospam_newsgate.onet.pl...
Czy kto¶ może podesłać przykład algorytmu, który wyeliminował by ten
problem
Tak, ale muszę sprawdzać 68 klawiszy (muzycznych) z założeniem, że może
być
wci¶niętych np 10 klawiszy :(
68 klawiszy i polifonia powiadasz?
To ja Ci napiszę co zazwyczaj robię ze swoimi klawiaturami - może co¶ z tego
wykorzystasz
W obsludze przerwań zegarowych, generowanych co kilka...kilkadziesiąt ms
czytam stan portów klawiatury. Potem zestawiam to co się odczytało z
historią poprzednich odczytów.
Załóżmy, że czytam tylko jeden 8-bitowy port i umieszczam (zanegowany) wynik
odczytu w zmiennej current. Następnie biorę do porównania poprzednie stany
zapamiętane w zmiennych pra1, pra2... (zazwyczaj tyle wystarczy).
- Komplet '1' na wybranym bicie jednocze¶nie we wszystkich zmiennych
(current, pra1, pra2) oznacza klawisz stabilnie wci¶nięty.
- Komplet '0' na wybranym bicie jednocze¶nie we wszystkich zmiennych
(current, pra1, pra2) oznacza klawisz stabilnie zwolniony.
- Kombinacja '0' i '1' oznacza drgający zestyk.
W realizacji C51 wychodzi to mniej wiecej tak:
unsigned char old, stable, delta_on, delta_off, current, pra1, pra2...;
void obsluga_timera() interrupt .. // np. co 10ms
{
/* ble..ble.. to co trzeba zrobic w przerwaniu od timera */
// odczyt portu klawiatury:
// klawisz wcisniety ->
// -> '0' na linii portu
// -> '1' na danym bicie w zmiennej current
current = ~XBYTE[port];
old = stable; // zapamiętujemy dotychczasowe stany stabilne
// nowe stany stabilne
// (wszytkie operacje logiczne - bitowe! )
stable = stable | ( current & pra1 & pra2... ); // stabilne '1'
stable = stable & ( current | pra1 | pra2... ); // stabilne '0'
// zmiany stanu
delta_on = stable & ~old; // wcisniecia: ( nowe_1 and stare_0 )
delta_off = ~stable & old; // wcisniecia ( nowe_0 and stare_1 )
/*
// mozna tez kumulowac info o zmianach stanu
// - zalezy to od sposobu w jaki zdarzenia beda obslugiwane
delta_on = delta_on | (stable & ~old);
delta_off = delta_off | (~stable & old);
*/
// obsluga zdarzen
if (delta_on)
/* sprawdzamy co zostało wci¶niete i robimy co trzeba */
}
if (delta_off)
/* sprawdzamy co zostało zwolnione i działamy */
}
// aktualizujemy historie
// elegancko byloby zrobic jakies FIFO, ale do kilku zmiennych
// chamskie kopiowanie wychodzi najtaniej:
// ... pra3=pra2; // itp.
pra2 = pra1;
pra1 = current;
} // obsluga_timera()
Jeszcze parę słów komentarza:
- jeżeli jest więcej portów do odczytania (a zwykle jest) to
- caly kawalek kodu cało¶c ląduje w petli ganiajacej po portach
(lub adresuajcej kolejne kolumny matrycy)
- ze zmiennych skalarnych (stable, pra1,...) robią się wektory, albo
lepiej jeden (lub dwa) wektory struktur opisujących stan portu. Ja zwykle
używam dwóch struktur:
* stałej (code) przechowującej adres portu oraz maskę istotnych bitów.
* zmiennej (data) zawierajacej stan klawiszy (stable, pra1, pra2... i
ew. delta_on, delta_off do asynchronicznej obslugi zdarzen)
- obsluga zdarzen odbywa sie synchronicznie z obsluga przerwania - czyli ta
częsc musi trwac krótko, np.:
- w przerwaniu tylko ustawiać bitową flagę kbd_event.
- resztę obslugi robić, w petli - w wątku podstawowym;
(ten sposob wymaga wlasnie kumulacji delta_on i delta_off) np:
// watek podstawowy
while (1)
{
if (kbd_event)
{
kbd_event = 0;
DISABLE_INT;
delta_on_kopia = delta_on;
delta_on = 0;
delta_off_kopia = delta_off;
delta_off = 0;
ENABLE INT;
/*
czytamy kolejne bity z delta_on_kopia i delta_off_kopia
podejmujemy dzialania zwiazane z obsluga zdarzen
/*
}
} // if
} // while
- obsluga w ten sposób ma o tyle zaletę, opóźnienie reakcji na każdy
klawisz jest identyczne i stałe.
- do czytania u-switchy calkowicie wystarczy np. T=10ms i dwa poziomy
historii pra1, pra2
- przyjecie T=15ms i 3 poziomow historii da 60ms opoznienia i powinno
wystaczyc do pozbycia sie trzaskow z klawiszy foliowych
- opóznienie 60ms powinno byc akceptowalne dla grajka
--
Marek Dzwonnik mdz_at_nospam_message.pl
GG: #2061027 (zwykle jako 'dostępny-niewidoczny')
From: "Marek Kaprynski" <marekk_at_nospam_comms.eee.strath.ac.uk>
Subject: Re: Obsługa klawiatury i drgania styków
Date: Wed, 8 May 2002 10:16:06 +0100
Marek Dzwonnik <mdz_at_nospam_message.pl> wrote in message
news:3cd83d32_at_nospam_news.home.net.pl...
To ja Ci napiszę co zazwyczaj robię ze swoimi klawiaturami - może co¶ z
tego
wykorzystasz
W obsludze przerwań zegarowych, generowanych co kilka...kilkadziesiąt ms
czytam stan portów klawiatury. Potem zestawiam to co się odczytało z
historią poprzednich odczytów.
Ja zrobilem to troche inaczej, jakkolwiek tez w tym kierunku :-)
Odczyt klawiszy realizuje przerwanie (ktore zreszta wykorzystuje tez do
wyswietlania) - jesli nastapilo wcisniecie klawisza, jest to zapamietywane i
rozpoczyna sie zwiekszanie zmiennej, ktora przez zadany czas (liczbe wywolan
przerwania) musi pozostac w niezmienionym stanie. Jesli to sie uda klawisz
jest uznawany za wcisniety.
Jesli w miedzy czasie styki sie rozwarly, licznik jest kasowany (juz bez
jakiegokolwiek czekania). W sumie to podobna technika, z tym ze dlugosc
"pamieci historii" okreslam poczatkowa wartoscia licznika.
Pozdrawiam
Marek
From: "Marek Dzwonnik" <mdz_at_nospam_message.pl>
Subject: Re: Obsługa klawiatury i drgania styków
Date: Wed, 8 May 2002 13:14:34 +0200
Użytkownik "Marek Kaprynski" <marekk_at_nospam_comms.eee.strath.ac.uk> napisał w
wiadomo¶ci news:abaqd3$oc1$1_at_nospam_dennis.cc.strath.ac.uk...
Ja zrobilem to troche inaczej, jakkolwiek tez w tym kierunku :-)
Odczyt klawiszy realizuje przerwanie (ktore zreszta wykorzystuje tez do
wyswietlania) - jesli nastapilo wcisniecie klawisza, jest to zapamietywane
i
rozpoczyna sie zwiekszanie zmiennej, ktora przez zadany czas (liczbe
wywolan
przerwania) musi pozostac w niezmienionym stanie. Jesli to sie uda klawisz
jest uznawany za wcisniety.
Jesli w miedzy czasie styki sie rozwarly, licznik jest kasowany (juz bez
jakiegokolwiek czekania). W sumie to podobna technika, z tym ze dlugosc
"pamieci historii" okreslam poczatkowa wartoscia licznika.
Z tym, że takie rozwiązanie wymaga albo osobnego licznika dla każego
klawisza (pytanie dotyczyło 68 szt., z czego do 10 jednocze¶nie), albo
pewnej puli liczników przydzielanych dynamicznie - co trochę komplikuje
sprawę. Kiedy¶ stosowałem jeszcze inne rozwiązanie. Kolejne bity odczytywane
z linii portu wsuwałem do rejestru przesuwnego. A następnie badałem, czy na
N młodszych bitach występują same zera lub same jedynki. Ale niestety
wychodziło to raczej nieefektywnie.
Rozwiązanie, które stosuję obecnie, ma dla mnie następujące zalety:
- przerwanie 10ms odpowiednie dla klawiatury, jest jednocze¶nie podstawą
czasu dla paru innych rzeczy (m.in. programowe liczniki czasu).
- na historię jednego klawisza przypadają zaledwie 3..4 bity.
- wstępna obróbka 8 klawiszy odbywa się równolegle (np. całej kolumny
matrycy) i daje informację zarówno o zmianach (+) jak i (-).
- można nałożyć maskę bitów istotnych. Np. w przypadku gdy w 8-bitowym
porcie klawisze wiszą tylko na kilku liniach, nalożenie maski pozwala na
szybkie wyeliminowanie reakcji na zmiany stanu pozostałych linii
- można wykrywać fakt wystąpienia zmiany w całej grupie 8 klawiszy (delta_on
!=0 lub delta_off!=0 ), a dopiero następnie rozpoznawać bit po bicie co
zostało wci¶niete. Zdarzenia 'kbd' są stosunkowo rzadkie, dzieki czemu
eliminiuje się sporo pustego przeglądania.
- łatwo zrealizować obsługę zdarzeń. U siebie np. m.in. używam do tego
rozbudowanego mechanizmu komuniaktów.
- aplikacja ma w każdej chwili dostęp do stanu stabilnego wybranego klawisza
(stable.bit). Dzięki temu łatwo zrobić obsługę kombinacji typu
Shift-cos_tam.
- całą konfigurację związaną z obslugą klawiatury trzymam w stałych
strukturach w pamięci programu (code). Tzn. adresy portow, maski, kody i
adresatow komunikatow (zdarzen) przypisanych do danego klawisza itp. Daje to
b. dużą elastyczno¶ć, dzięki czemu np. w swoich zastosowaniach mogę
traktować w ten sam sposób zarówno przyciski jak i inne czujniki
(indukcyjne, optyczne).
MDz
From: "~Filut~" <pawel_at_nospam_filuts.waw.pl>
Subject: Re: Obsługa klawiatury i drgania styków
Date: Wed, 8 May 2002 17:36:56 +0200
Użytkownik "Marek Dzwonnik" <mdz_at_nospam_message.pl> napisał w wiadomo¶ci
news:3cd907f9_at_nospam_news.home.net.pl...
Użytkownik "Marek Kaprynski" <marekk_at_nospam_comms.eee.strath.ac.uk> napisał w
wiadomo¶ci news:abaqd3$oc1$1_at_nospam_dennis.cc.strath.ac.uk...
Ja zrobilem to troche inaczej, jakkolwiek tez w tym kierunku :-)
Odczyt klawiszy realizuje przerwanie (ktore zreszta wykorzystuje tez do
wyswietlania) - jesli nastapilo wcisniecie klawisza, jest to
zapamietywane
i
rozpoczyna sie zwiekszanie zmiennej, ktora przez zadany czas (liczbe
wywolan
przerwania) musi pozostac w niezmienionym stanie. Jesli to sie uda
klawisz
jest uznawany za wcisniety.
Jesli w miedzy czasie styki sie rozwarly, licznik jest kasowany (juz bez
jakiegokolwiek czekania). W sumie to podobna technika, z tym ze dlugosc
"pamieci historii" okreslam poczatkowa wartoscia licznika.
Z tym, że takie rozwiązanie wymaga albo osobnego licznika dla każego
klawisza (pytanie dotyczyło 68 szt., z czego do 10 jednocze¶nie), albo
pewnej puli liczników przydzielanych dynamicznie - co trochę komplikuje
sprawę. Kiedy¶ stosowałem jeszcze inne rozwiązanie. Kolejne bity
odczytywane
z linii portu wsuwałem do rejestru przesuwnego. A następnie badałem, czy
na
N młodszych bitach występują same zera lub same jedynki. Ale niestety
wychodziło to raczej nieefektywnie.
Wła¶nie skończyłem pisać program z użyciem bufora cyklicznego (szkoda, że
nie ma w PIC`u tak jak w DSP Motoroli (56000) specjalnych rejestrów) i
opisanego przez ciebie algorytmu. Na 16 klawiszach działa ! (efek
wyeliminowany).
Jutro podepnę resztę - zobaczymy. Coraz mniej mam pamięci programu :(
W najgorszym przypadku dam opóźnienia (~6-10 ms).
Mój instrument będzie dla "spokojnego" muzyka ;)
Pozdrawiam
From: "Marek Kaprynski" <marekk_at_nospam_comms.eee.strath.ac.uk>
Subject: Re: Obsługa klawiatury i drgania styków
Date: Wed, 8 May 2002 18:08:52 +0100
Marek Dzwonnik <mdz_at_nospam_message.pl> wrote in message
news:3cd907f9_at_nospam_news.home.net.pl...
Użytkownik "Marek Kaprynski" <marekk_at_nospam_comms.eee.strath.ac.uk> napisał w
wiadomo¶ci news:abaqd3$oc1$1_at_nospam_dennis.cc.strath.ac.uk...
Ja zrobilem to troche inaczej, jakkolwiek tez w tym kierunku :-)
Odczyt klawiszy realizuje przerwanie (ktore zreszta wykorzystuje tez do
wyswietlania) - jesli nastapilo wcisniecie klawisza, jest to
zapamietywane
i
rozpoczyna sie zwiekszanie zmiennej, ktora przez zadany czas (liczbe
wywolan
przerwania) musi pozostac w niezmienionym stanie
[...]
Z tym, że takie rozwiązanie wymaga albo osobnego licznika dla każego
klawisza (pytanie dotyczyło 68 szt., z czego do 10 jednocze¶nie),
Zgadza sie... ja mam aktualnie dwa klawisze, wiec to nie problem :-)
albo pewnej puli liczników przydzielanych dynamicznie - co trochę
komplikuje
sprawę.
Ano... w asemblerze PIC'a tego sie nie podejme :-)))
Kiedy¶ stosowałem jeszcze inne rozwiązanie. Kolejne bity odczytywane
z linii portu wsuwałem do rejestru przesuwnego. A następnie badałem, czy
na
N młodszych bitach występują same zera lub same jedynki. Ale niestety
wychodziło to raczej nieefektywnie.
Rozwiązanie, które stosuję obecnie, ma dla mnie następujące zalety:
- przerwanie 10ms odpowiednie dla klawiatury, jest jednocze¶nie podstawą
czasu dla paru innych rzeczy (m.in. programowe liczniki czasu).
U mnie tez sie doczepiam do przerwania sterujacego wyswietlaniem kolejnych
cyfr na wysw. 7-seg.
- na historię jednego klawisza przypadają zaledwie 3..4 bity.
No wlasnie tu lezy roznica - u mnie to jest 8 bitow...
- wstępna obróbka 8 klawiszy odbywa się równolegle (np. całej kolumny
matrycy)
Duza zaleta - szczegolnie przy wiekszych klawiaturach...
inne czujniki
(indukcyjne, optyczne).
A propos czujnikow - potrzebuje zrobic czujnik zblizeniowy, na poczatku
pomyslalem o podczerwieni (fotodioda (mi sie trafila PIN) i dioda IR). Tylko
ma to jedna wade dla mnie - do dzialania czujnika potrzebna jest aktywnosc
procesora, a ja myslalem coby go uspic i obudzic przerwaniem... a aktywnego
procesora potrzebuje by caly czas nie swiecic diodka. Pomyslalem tak - jak
przerwanie ma sprawdzic czy czujnik jest aktywny to zapala diodke, sprawdza
stan wejscia, gasi diodke i wychodzi. Powinno byc to dosc energooszczedne...
ale z usypiania trzeba w tym przypadku zrezygnowac. A swoja droga to tego
PIN'a chyba sfajczylem :-(((
Moze ma ktos gotowe rozwiazanie takiego czujnika ?
Pozdrawiam
Marek
From: "Marek Dzwonnik" <mdz_at_nospam_message.pl>
Subject: Re: Obsługa klawiatury i drgania styków
Date: Wed, 8 May 2002 19:45:59 +0200
Użytkownik "Marek Kaprynski" <marekk_at_nospam_comms.eee.strath.ac.uk> napisał w
wiadomo¶ci news:abbm3i$2ek$1_at_nospam_dennis.cc.strath.ac.uk...
A propos czujnikow - potrzebuje zrobic czujnik zblizeniowy, na poczatku
pomyslalem o podczerwieni (fotodioda (mi sie trafila PIN) i dioda IR).
Tylko
ma to jedna wade dla mnie - do dzialania czujnika potrzebna jest aktywnosc
procesora, a ja myslalem coby go uspic i obudzic przerwaniem... a
aktywnego
procesora potrzebuje by caly czas nie swiecic diodka. Pomyslalem tak - jak
przerwanie ma sprawdzic czy czujnik jest aktywny to zapala diodke,
sprawdza
stan wejscia, gasi diodke i wychodzi. Powinno byc to dosc
energooszczedne...
ale z usypiania trzeba w tym przypadku zrezygnowac. A swoja droga to tego
PIN'a chyba sfajczylem :-(((
Moze ma ktos gotowe rozwiazanie takiego czujnika ?
Do tej pory miałem do czynienia tylko z prostymi czujnikami optycznymi
(transmisyjne nad->odb, odbiciowe - od obiektu, refleksyjne - od
odbły¶nika).
Z tym, że czynnie używałem tylko czujników odbiciowych. (np. takich:
www.sels.com.pl -> SCOO-1000 ). Dobry wynalazek, gdy trzeba wykryć z
powtarzalno¶cią np. 10mm pojawienie się jakiego¶ obiektu w odległo¶ci
kilkudziesięciu cm od czujnika. W zasadzie traktowałem je jak czarną
skrzynkę z gotową zahermetyzowaną optyką i elektroniką w ¶rodku oraz
wyj¶ciem on/off na zewnątrz. Z tym, że nie miałem żadnej potrzeby
oszczędnego gospodarowania energią (maszyna i tak bierze do 40kW :-). ZTCP
taki czujnik bierze ok. 20mA i w zasadzie można by kluczować mu zasilanie.
ale nie wiem czy ma to sens w Twoim przypadku.
--
Marek Dzwonnik mdz_at_nospam_message.pl
GG: #2061027 (zwykle jako 'dostępny-niewidoczny')