Såklart kommer en räknare som räknar till mer än 1 om knappen är påverkat och som nollar räknaren när knappen INTE är påverkat att fungera som debounce! ingen snack om det och det är ju essensen av alla råd om debounce. Hur man sedan integrerar denna räknare, om det är en separat funktion med en timer-interrupt eller liknande beror mest på programmeringsteknik och krav på svarstider osv.
Själv använder jag som tidigare skrivit en timer till att ge "systeminterrupt" som då kan användas till olika saker. En av dessa är "alltid" avläsning och avstutsning av knappar och (om det finns och behövs) sensorer. Sedan sparas de avstutsade indikationer i variabler, då kan "huvudprogrammet" läsa dom och är det då indikerat '1' kan det räkna med att det är vad som gäller.
Fördelen är att jag alltid vet vilka villkor och tider som gäller för knappar och sensorer, ingen delay() behöver utföras då interrupten gör sitt jobb och sedan "försvinner", alltså kör hela programmet snabbare och finns det kritiska saker (nödstopp?) kan interruptrutinen utföra panikåtgärden direkt om det behövs.
Tar vi detta projekt som exempel kan man utöka init() till att innehålla:
Kod: Markera allt
OPTION = 0x86; // Set interrupt frekvens till 30,5Hz
TMR0 = 0; // Start på noll
INTCON = 0x90; // Tillåter Timer0 interrupt
Sedan får du lägga in följande också:
Kod: Markera allt
#define Knapp_1 0x04
typedef unsigned char BYTE;
volatile BYTE Key_Pressed;
BYTE Key_Used;
void interrupt(void)
{
#define KEY_BITS Knapp_1 /* bara en knapp just nu, kan dock expanderas i andra projekt */
static BYTE Previous, Used;
BYTE Now;
T0IF = 0; // Nolla interruptflaggan
// Now = ~PORTA & KEY_BITS; // Använd denna rad om knapptryckningen ger en nolla
Now = PORTA & KEY_BITS; // Använd denna rad om knapptryckningen ger en etta
if(Previous == Now) Key_Pressed = Now & ~Used; // Kopiera bitmönster från knapptryckningen
else if(!Now) Key_Pressed = 0; // Om ingen knapp är tryckt ska det nollas
Previous = Now; // Kom ihåg hur det var
Used = Now; // Kom ihåg om knappen är rapporterat
}
Då så, på detta sätt kommer interruptrutinen att kolla knapp-porten 30 gg/sek helt utan delay(). Är det två avläsningar efter varandra som ger identisk värde kommer detta värde att skrivas in i Key_Pressed, helt utan att du behöver göra något.
Det enda du behöver kolla i detta projekt är om Key_Pressed är noll eller icke-noll Alltså kommer din koll att se ut som:
if(Key_Pressed)
goto AnslutTest;
OBS: Denna rutin ger bara en "knapp tryckt" eller inte. Om en knapptryckning ska utlösa en grej när man trycker på den och bara en gång per tryckning kan man utöka det hela lite vid att helt enkelt att skriva en nolla till Key_Pressed när det som ska utlösas av knapptryckningen är påbörjat (eller avslutat).
Då kan man trycka in knappen och hålla den inne en halvtimme utan att det sker igen och igen.
EDIT: såg i koden att du låter den vänta 3 sek (anslutningstest) och om du utökar interruptrutinen lite kan du få detta att fungera enkelt:
Kod: Markera allt
typedef unsigned int WORD;
volatile WORD Delay_Counter;
void Delay(WORD Timer_Value)
{
Delay_Counter = Timer_Value; // Sätt värde, 30,5 steg per sekund.
while(Delay_Counter); // Vänta på att den blir noll
}
void interrupt(void)
{
#define KEY_BITS Knapp_1 /* bara en knapp just nu, kan dock expanderas i andra projekt */
static BYTE Previous, Used;
BYTE Now;
T0IF = 0; // Nolla interruptflaggan
// Now = ~PORTA & KEY_BITS; // Använd denna rad om knapptryckningen ger en nolla
Now = PORTA & KEY_BITS; // Använd denna rad om knapptryckningen ger en etta
if(Previous == Now) Key_Pressed = Now & ~Used; // Kopiera bitmönster från knapptryckningen
else if(!Now) Key_Pressed = 0; // Om ingen knapp är tryckt ska det nollas
Previous = Now; // Kom ihåg hur det var
Used = Now; // Kom ihåg om knappen är rapporterat
if(Delay_Counter) Delay_Counter--; // Räkna ner om det behövs
}
Jag använder mycket ofta detta sätt, då kan jag skapa fler timers som kan användas på fler sätt. Om jag t.ex. vill att en funktion ska ha en viss starttid kan jag - i main-loopen - sätta en flagga som betyder att programmet egentligen ska hålla på med vaddetnuär MEN om jag samtidig med att flaggan sätts laddar Delay_Counter med ett värde kan jag skriva såhär:
Kod: Markera allt
BYTE Sequence;
Main-loop:
...
if(Key_Pressed)
{
Sequence = 1; / Indikera att sekvensen ska köra
Key_Pressed = 0; // Knapptryckningen är använd, nolla den
Motor = 1; // Slå på en tänkt motor som måste få 10 sek att komma upp i varv
Delay_Counter = 305; // Värdet för 10 sek
}
if(Sequence && !Delay_Counter)
{
switch(Sequence)
{
case xx osv.
... Gör något i sekvensen - men BARA om "Delay_Counter" är noll.
När sekvensen är klar är det bara att återställa Sequence till noll och ingen sekvens utförs
På detta vis kan programmet göra saker i viss ordning med viss tid mellan varje sak.
}
}
}