Ta bort kontaktstuds? AVR Assembler
Ta bort kontaktstuds? AVR Assembler
Jag har nu försökt på olika vis att få till detta men lyckas inte så bra, det blir en massa register, flaggor och kod i onödan(känns det som). Har läst i några andra trådar och fått det att fungera, men på väldigt krångliga vis. Problemet känns väldigt enkelt, vet inte om jag har blivit knäpp eller hur det är.
Jag har en knapp som är kopplad till en pinne på min Atmega168 - när man trycker på knappen blir pinnen hög och när man släpper knappen blir pinnen låg via ett 12k motstånd.
Detta vill jag få till:
När man trycker ska ett avbrott inträffa
Kontaktstuds-filter på ca 10-200 ms
Om man håller in knappen ska INTE avbrottet ske kontinuerligt, bara en gång när man trycker.
Kontaktstuds när man släpper knappen ska filtreras bort, samma 10-200 ms kanske
Jag har nu löst det såhär krångligt:
Knappen är kopplad till Ext Int. som startar en timer(flagga talar om ifall timern redan är startad) som efter 200 ms genererar ett avbrott. Där kollas om Ext Int-pinnen fortfarande är hög, då anses knapptryckningen som giltig(flaggan tas bort så timern kan starta på nytt).
Det blir giltiga tryck på löpande band om man hållen in knappen(kanske går att lösa med ytterligare flaggor?).
Måste väll finnas något enklare sätt?
Läste om att man kan ha ett timer-avbrott som sker kontinuerligt(tex var 200:e ms) och kollar status på knappen. Om den är hög två gånger i rad så anses det som giltigt(borde ju dock ge en variation på 200-400 ms då?). Alternativt en räknare som räknar upp eller ner beroende på status, vid vissa värden anses det vara giltigt eller ogiltigt(tex. räknare till 20, <5 = ogiltig >15 = giltig).
Kan någon hjälpa mig in på rätt spår?
Jag har en knapp som är kopplad till en pinne på min Atmega168 - när man trycker på knappen blir pinnen hög och när man släpper knappen blir pinnen låg via ett 12k motstånd.
Detta vill jag få till:
När man trycker ska ett avbrott inträffa
Kontaktstuds-filter på ca 10-200 ms
Om man håller in knappen ska INTE avbrottet ske kontinuerligt, bara en gång när man trycker.
Kontaktstuds när man släpper knappen ska filtreras bort, samma 10-200 ms kanske
Jag har nu löst det såhär krångligt:
Knappen är kopplad till Ext Int. som startar en timer(flagga talar om ifall timern redan är startad) som efter 200 ms genererar ett avbrott. Där kollas om Ext Int-pinnen fortfarande är hög, då anses knapptryckningen som giltig(flaggan tas bort så timern kan starta på nytt).
Det blir giltiga tryck på löpande band om man hållen in knappen(kanske går att lösa med ytterligare flaggor?).
Måste väll finnas något enklare sätt?
Läste om att man kan ha ett timer-avbrott som sker kontinuerligt(tex var 200:e ms) och kollar status på knappen. Om den är hög två gånger i rad så anses det som giltigt(borde ju dock ge en variation på 200-400 ms då?). Alternativt en räknare som räknar upp eller ner beroende på status, vid vissa värden anses det vara giltigt eller ogiltigt(tex. räknare till 20, <5 = ogiltig >15 = giltig).
Kan någon hjälpa mig in på rätt spår?
Re: Ta bort kontaktstuds? AVR Assembler
Jag såg att HaD hade en post om just avstudsning igår. En bunt olika exempel från läsarna, dock ingen "rankning" av vad som är bra och dåligt. Skulle tippa att mycket är skrivet i C (har inte hunnit läst igenom själv ännu) men det går ju alltid att porta.
http://hackaday.com/2010/11/09/debounce ... -them-all/
http://hackaday.com/2010/11/09/debounce ... -them-all/
- jonnorberg
- Inlägg: 850
- Blev medlem: 19 mars 2008, 17:45:40
- Ort: Årsta
Re: Ta bort kontaktstuds? AVR Assembler
Vet inte om detta är vad du vill ha svar på...
...men är det inte bara att lägga en "wait" precis efter du får signal på ingången så får du väll det du vill ha.
if "input" =1
wait 200ms
var=1
...men är det inte bara att lägga en "wait" precis efter du får signal på ingången så får du väll det du vill ha.
if "input" =1
wait 200ms
var=1
Re: Ta bort kontaktstuds? AVR Assembler
Istället för interrupt direkt från pinnen kan du ha en timer som snurrar hela tiden och gör ett avbrott varje 50-100 mS där du kollar pinnens status. Om den är "nedtryckt" två gånger i rad så går du till "vad du ska göra". I samma rutin kollar du om den är uppsläppt två gånger i rad - då gör du "uppsläppt-rutinen". Du behöver en flagga som minns var du befinner dig i processen. "normal" => "nedtryckt1" => "nedtryckt2" => "uppsläppt1" => "uppsläppt2 = detsamma som normal igen". Det kan vara ett register som räknar från 0 till 3 och om igen... skulle du befinna dig på "1" och du läser "uppsläppt" så innebär det falsk signal,och du återgår till "normal" (0). Hoppas du hängde med.
Re: Ta bort kontaktstuds? AVR Assembler
> när man trycker på knappen blir pinnen hög och när man släpper
> knappen blir pinnen låg via ett 12k motstånd.
Av olika anledningar, som vi inte går in på här, så är det vanligare/bättre att
ha motståndet till 5V (eller vad nu "hög" är) och knappen till jord.
> När man trycker ska ett avbrott inträffa...
Just p.g.a av kontaktstudsarna så är interrupt/avbrott ganska olämpligt här.
Säg att knappen studsar 10 gånger varje gång man trycker, då får du sannolikt
10 avbrott för varje tryckning. Interrupt/avbrott skulle fungera OK om man
använder extern krets för avstudsningen så att alla avbrott är "giltiga".
> ...borde ju dock ge en variation på 200-400 ms då?
Jo, och om det spelar roll i din applikation så kör du inte var 200:de ms,
det avgör du ju själv. Kör timeravbrottet med 100 ms intervall istället. Eller 50 ms.
Välj vad som passar i just ditt fall. Det behöver inte heller vara en jämt värde, ta
det som både passar och är enkelt timermässigt (fre-running) att konfigurera. Om
det blir 78 ms spelar ju ingen som helst roll (om inte timern används till något annat).
> knappen blir pinnen låg via ett 12k motstånd.
Av olika anledningar, som vi inte går in på här, så är det vanligare/bättre att
ha motståndet till 5V (eller vad nu "hög" är) och knappen till jord.
> När man trycker ska ett avbrott inträffa...
Just p.g.a av kontaktstudsarna så är interrupt/avbrott ganska olämpligt här.
Säg att knappen studsar 10 gånger varje gång man trycker, då får du sannolikt
10 avbrott för varje tryckning. Interrupt/avbrott skulle fungera OK om man
använder extern krets för avstudsningen så att alla avbrott är "giltiga".
> ...borde ju dock ge en variation på 200-400 ms då?
Jo, och om det spelar roll i din applikation så kör du inte var 200:de ms,
det avgör du ju själv. Kör timeravbrottet med 100 ms intervall istället. Eller 50 ms.
Välj vad som passar i just ditt fall. Det behöver inte heller vara en jämt värde, ta
det som både passar och är enkelt timermässigt (fre-running) att konfigurera. Om
det blir 78 ms spelar ju ingen som helst roll (om inte timern används till något annat).
-
- Inlägg: 56
- Blev medlem: 16 augusti 2010, 10:29:32
- Ort: Vallentuna
Re: Ta bort kontaktstuds? AVR Assembler
Vill man använda timers och öka precisionen kan man ju minska ner den till 50ms och göra kontrollen efter 4 avbrott.
Annars skulle jag skriva logiken så här (under föutsättning att du kommer in i avbrottsrutinen när ExtInt går hög OCH låg.)
Avbrottsrutin ExtInt:
wait 200ms //wait for debounce
If Port = High & VarStatus = Low
VarStatus = High
varButtonJustPressed = True
else if Port = Low & VarStatus = High
VarStatus = Low
end-if
HuvudSlinga:
If varButtonJustPressed = True then
varButtonJustPressed = False
/* Kod som ska exekveras när knappen trycks in */
.
.
end-if
Annars skulle jag skriva logiken så här (under föutsättning att du kommer in i avbrottsrutinen när ExtInt går hög OCH låg.)
Avbrottsrutin ExtInt:
wait 200ms //wait for debounce
If Port = High & VarStatus = Low
VarStatus = High
varButtonJustPressed = True
else if Port = Low & VarStatus = High
VarStatus = Low
end-if
HuvudSlinga:
If varButtonJustPressed = True then
varButtonJustPressed = False
/* Kod som ska exekveras när knappen trycks in */
.
.
end-if
Re: Ta bort kontaktstuds? AVR Assembler
jesse: Ja, det var ju ett bra tips. Det var ju betydligt enklare än alla "mina" lösningar
Ska prova det så fort jag får tid.
Sodjan: Jo, jag vet att man brukar koppla pinnen till 5 V och dra den låg med knappen. Hade inte kopplat så på platta nu, men jag ska göra om det så att det blir så =)
Ja jag har märkt att det kan generera många avbrott på en gång, var inne på att i avbrottsrutinen stänga av avbrottet och sen aktivera det igen i timer-rutinen. Men...det var ju det som kändes så "fel"
Med "variation på 200-400 ms" tänkte jag mest att det blir väll alltid variation när man har en timer som "rullar". Har man den på 50 ms så kommer ju en giltig knapptryckning behöva vara minst 50 - 100 ms. Alltså det finns väll en (liten)risk att en knapptryckning på (nästan) 100 ms inte blir giltig vid ett tillfälle medan en 50 ms tryckning kan bli giltig vid ett annat tillfälle? Eller tänker jag fel här? Det har ingen betydelse i det jag håller på med nu men visst blir det så?

Sodjan: Jo, jag vet att man brukar koppla pinnen till 5 V och dra den låg med knappen. Hade inte kopplat så på platta nu, men jag ska göra om det så att det blir så =)
Ja jag har märkt att det kan generera många avbrott på en gång, var inne på att i avbrottsrutinen stänga av avbrottet och sen aktivera det igen i timer-rutinen. Men...det var ju det som kändes så "fel"

Med "variation på 200-400 ms" tänkte jag mest att det blir väll alltid variation när man har en timer som "rullar". Har man den på 50 ms så kommer ju en giltig knapptryckning behöva vara minst 50 - 100 ms. Alltså det finns väll en (liten)risk att en knapptryckning på (nästan) 100 ms inte blir giltig vid ett tillfälle medan en 50 ms tryckning kan bli giltig vid ett annat tillfälle? Eller tänker jag fel här? Det har ingen betydelse i det jag håller på med nu men visst blir det så?

Re: Ta bort kontaktstuds? AVR Assembler
Nej, du tänker inte fel, i teorin, men i praktiken så fungerar det ändå. 
Sen så kan du köra med 10 ms intervall och kräva 10 st "hög" i rad, då
blir intervallet 100-110 ms istället (+ tiden som det tar att "studsa färdigt").
Skillnaden på 100 och 200 ms är det sannolikt ingen som märker heller.

Sen så kan du köra med 10 ms intervall och kräva 10 st "hög" i rad, då
blir intervallet 100-110 ms istället (+ tiden som det tar att "studsa färdigt").
Skillnaden på 100 och 200 ms är det sannolikt ingen som märker heller.
-
- Inlägg: 87
- Blev medlem: 29 november 2010, 00:32:55
Re: Ta bort kontaktstuds? AVR Assembler
Mitt recept på snabb debouncer:
Interrupt på pinnen + en timer med interrupt:
I din ISR:
om det var ext int
stäng av ext interrupt
flagga för ändrad knapp
nollställ ext int flagga
starta timer
om det var timer int
slå på ext interrupt
Det kanske blir lite krångligare men ...kompromisslöst, snabbt o härligt.
/Pelle
Interrupt på pinnen + en timer med interrupt:
I din ISR:
om det var ext int
stäng av ext interrupt
flagga för ändrad knapp
nollställ ext int flagga
starta timer
om det var timer int
slå på ext interrupt
Det kanske blir lite krångligare men ...kompromisslöst, snabbt o härligt.
/Pelle
Re: Ta bort kontaktstuds? AVR Assembler
Ja, det är en rellativt "snygg" lösning. Fördelen är att det
körs väldigt lite kod jämfört med andra lösningar, inte mer
än alldeles nödvändigt. Nackdelen är dock att en hel timer
blir upplåst till denna funktion.
körs väldigt lite kod jämfört med andra lösningar, inte mer
än alldeles nödvändigt. Nackdelen är dock att en hel timer
blir upplåst till denna funktion.
Re: Ta bort kontaktstuds? AVR Assembler
Jag har genom åren lärt mig att knapptryckningar sällan är tidskritiska, därför kopplar jag dom oftast till en timer-ISR.
Jag gör i essens om jesse beskriver och lägger in en "n-key-rollover" funktion också och ofta en key-buffer där varje knapptryckning sparas till programmet "förbrukar" den.
Jag har med framgång låtit bli att det ska 2 avläsningar till för att den ska detekteras men med risk för störningar bör man ha 2 på varandra följande för att få bra säkerhet och då bör timern köra med minst 20Hz (50ms).
Om vi antar att knapparna är få nog att passa på en port blir det som följer:
byte Förra_Resultat; // Variabel som ska sparas mellan varje ISR
ISR:
Data = Läs_Knapp_Port;
Resultat = Data .AND. Förra_Resultat; // Här maskas enkel-avläsningar bort
Förra_Resultat = Data; // Spara avläsningen till nästa gång
Nu finns det lite olika vägar att gå, "Resultat" innehåller en eller fler giltiga knapptryckningar men de kan även vara "gamla"
Ett sätt kan vara att ha ytterligare en byte där man sätter en bit när man har "utfört" knapptryckningen, då kan man maska bort dessa från "Resultat".
Nya_Knappar = Resultat .AND. .NOT. Redan_Utförd; // Maskar ut till bara nya tryckningar
Redan_Utförd = Redan_Utförd .AND. Resultat; // Tar bort gamla utförda när knappen släpps
Detta skulle då betyda att en reaktion ser ut dom följer:
if(Nya_Knappar .AND. <knapp-bit>)
{
Redan_Utförd = Redan_Utförd .OR. <knapp-bit>; // Markera den som redan utförd.
Gör själva jobbet som ska göras.
}
Jag gör i essens om jesse beskriver och lägger in en "n-key-rollover" funktion också och ofta en key-buffer där varje knapptryckning sparas till programmet "förbrukar" den.
Jag har med framgång låtit bli att det ska 2 avläsningar till för att den ska detekteras men med risk för störningar bör man ha 2 på varandra följande för att få bra säkerhet och då bör timern köra med minst 20Hz (50ms).
Om vi antar att knapparna är få nog att passa på en port blir det som följer:
byte Förra_Resultat; // Variabel som ska sparas mellan varje ISR
ISR:
Data = Läs_Knapp_Port;
Resultat = Data .AND. Förra_Resultat; // Här maskas enkel-avläsningar bort
Förra_Resultat = Data; // Spara avläsningen till nästa gång
Nu finns det lite olika vägar att gå, "Resultat" innehåller en eller fler giltiga knapptryckningar men de kan även vara "gamla"
Ett sätt kan vara att ha ytterligare en byte där man sätter en bit när man har "utfört" knapptryckningen, då kan man maska bort dessa från "Resultat".
Nya_Knappar = Resultat .AND. .NOT. Redan_Utförd; // Maskar ut till bara nya tryckningar
Redan_Utförd = Redan_Utförd .AND. Resultat; // Tar bort gamla utförda när knappen släpps
Detta skulle då betyda att en reaktion ser ut dom följer:
if(Nya_Knappar .AND. <knapp-bit>)
{
Redan_Utförd = Redan_Utförd .OR. <knapp-bit>; // Markera den som redan utförd.
Gör själva jobbet som ska göras.
}
-
- Inlägg: 87
- Blev medlem: 29 november 2010, 00:32:55
Re: Ta bort kontaktstuds? AVR Assembler
Det går ju att låta varje knapp ha en egen variabel som räknar upp istället. Då kan man använda en gemensam timer och dela den med andra saker, så länge den går inte stängs av.
Jag håller med att det inte är tidskritiskt för det mesta. Men ändå. En blixtsnabbt reaktion från ett gränssnitt kan göra att helheten känns bättre och snabbare.
/Pelle
Jag håller med att det inte är tidskritiskt för det mesta. Men ändå. En blixtsnabbt reaktion från ett gränssnitt kan göra att helheten känns bättre och snabbare.
/Pelle
Re: Ta bort kontaktstuds? AVR Assembler
Precis, det är ju den lite mer generella lösningen. Fungerar
för ett variabelt antal knappar och man får en "timebase"
som kan användas till annat i applikationen...
för ett variabelt antal knappar och man får en "timebase"
som kan användas till annat i applikationen...
Re: Ta bort kontaktstuds? AVR Assembler
gunnerfeldt: visst kan man men varför ska man?
Om en kontakt är sluten då är den det oavsett om det är 10 avkänningar eller 2, om den stutsar är den definitiv stabil efter 2'ndra avläsningen.
Alltså är det rikligt att kolla om den är på nu och var det förut. Avkänningen ska ske med maximalt 100ms i reaktionstid, med 2 avläsningar blir det alltså 50ms per avkänning och inte snabbare är den maximala stutstid för brytaren (finns i databladet) + 10% eller så.
Detta ger även snabb respons, med 100ms känns det som "direkt kontakt".
Om en kontakt är sluten då är den det oavsett om det är 10 avkänningar eller 2, om den stutsar är den definitiv stabil efter 2'ndra avläsningen.
Alltså är det rikligt att kolla om den är på nu och var det förut. Avkänningen ska ske med maximalt 100ms i reaktionstid, med 2 avläsningar blir det alltså 50ms per avkänning och inte snabbare är den maximala stutstid för brytaren (finns i databladet) + 10% eller så.
Detta ger även snabb respons, med 100ms känns det som "direkt kontakt".