antidender op Arduinopoort?


Hoe interessant ook, ik verwacht niet dat ik het in mijn softwarematige ontdendering nodig heb.
Met 16 bits (of 32) gevuld met loopjes van 1mS, kan ik al aardig de dender opvangen, verwacht ik.
Hoewel ook hardwarematig het zeer intrigerend is, ga ik het eerst softwarematig proberen.

Ik ben er nog niet.
De intenties zijn er. De rest moet nog worden vorm gegeven. :-)

Ik moet hier weer vaker komen... Wat kun je zo'n forum als deze gaan missen. :-)

Mensen...
Ik heb het softwarematige deel eens door mijn hoofd laten gaan.
Wat volgens mij nog een issue is, is dat de processor constant (elke mS) moet nagaan wat de staat van de poort is. Is deze 0 of 1.
Dan kom ik natuurlijk nooit toe aan andere processen als het omzetten van de punten en strepen naar de juiste karakters.

Ik dacht nog even aan een interrupt, die bij verandering van de staat van de poort, uit het programma springt en de poort een paar mS gaat uitlezen om te determineren of de poort laag of hoog is.
Maar dan moet bij niet bij elke bounce tussen 1 en 0 opnieuw die interrupt induiken.

Hebben jullie daar tips in?
Ik zat nog te mijmeren om bij elke interrupt "even" uit het reguliere programma te springen om de poortverandering te beoordelen op 1 of 0, wat maar 15mS duurt, om daarna weer verder te gaan met deze nieuwe info.
Als dat maar 15mS strak duurt, kan ik die waarde meenemen bij andere tijdsgebonden factoren.

Ik moet hier weer vaker komen... Wat kun je zo'n forum als deze gaan missen. :-)
hennep

Golden Member

Als ik de bijdragen van anderen hierboven doorlees dan zie ik delay/delay_ms staan. Als je dat nu eens niet gaat gebruiken. Tijdens een delay presteert de controller helemaal niets, verlies van rekenkracht dus. Als een hardloper die passen op de plaats maakt :-)
Als je aan het begin van de loop() functie dit zet:

code:

void loop() {
    static unsigned long vorige_waarde = millis();
    if( vorige_waarde != millis() ) {
        vorige_waarde = millis();        
	toetsafhandeling();
    }
    ...

Dan controleert je programma iedere milliseconde de toets/knop/sleutel, oftewel iedere keer als de millisecondenteller een stapje vooruit gaat.
Zo verlies je geen kostbare rekenkracht.

Het is een andere manier van programmeren waar je even aan moet wennen. De lussen in je programma mogen dan niet te lang duren. Als je de rest van je programma zo schrijft dat een while/for nooit langer dan een paar honderd microseconden duurt dan werkt dit goed.

Voor toetsen gebruik ik bijna altijd een timer interrupt. Die wordt dan elke milliseconde even aktief, leest de poort, doet een stap in de debounce en klaar. Duurt dan een paar microseconden.

Hennep en deKees,
Zitten jullie hier op dezelfde lijn?

Zo komt het mij als leek even over...
@deKees, ik ge me even inlezen in die timerinterrupt.
Ik zat zelf te denken aan een interrupt on change, maar ik weet niet of dat opgaat op een specifieke poort en of hij dan bij een afwijking van de voorgaande waarde getriggerd wordt.

@Hennep, Wat je omschrijft, spreekt me wel aan.
Maar ik begrijp je code niet helemaal.
Was dat geënt op eerdere code hier in dit topic gepost?
Ik ga het even doornemen.

Als ik het goed doorgrond heb je een loop met een [static unsigned long] wat een specifieke 32 bits reeks is, die alleen binnen deze loop geldt, right? (ik probeer zijdelings nog wat op te steken van programmeercode :-) )
De naam van die "long" is vorige_waarde, right?
Wat is "millis()" dan, in deze context? Is dat een gegeven code die elke miliseconde voorbij komt?
Dat probeer ik even te doorgronden.

[edit]
enige tijd tussen dit schrijven en het posten.
Intussen had ik al wat info opgedaan.
Ik lijk er niet echt naast te zitten en dat Millis is een praktische oplossing.
[/edit]

Ik moet hier weer vaker komen... Wat kun je zo'n forum als deze gaan missen. :-)

lees ook zeker eens op de site van Nick Gammon, dit geeft een verhelderende uitleg over de werking van de microcontroller!

uitleg van loop met millis: http://www.gammon.com.au/blink
uitleg van interrupts: http://www.gammon.com.au/interrupts
uitleg van debounce: http://www.gammon.com.au/switches

in het laatste deel legt hij uit hoe je dat met millis in een loop doet.
Het is misschien heel technisch uitgelegd, hopelijk is het begrijpbaar.

hennep

Golden Member

@joram.achten,
Mooie site, die code onder de kop "Debouncing without delay" spreekt mij wel aan.

@Fantomaz,
DeKees en ik zitten in zoverre op hetzelfde spoor dat we geen rekentijd verspillen aan een pauze.
De methode van DeKees is nauwkeuriger, die controleert precies op de timerinterrupt de aanslag. Mijn methode controleert de aanslag als er minstens 1 ms is verstreken. Als dat iedere keer 0.5 ms te laat is dan kan de debouncetijd oplopen. Is dat erg?
Een ding waar je rekening mee moet houden als je een interrupt gaat gebruiken, declareer alle globale variabelen die door de interrupt worden geraakt als volatile.

code:


void loop() {
    static unsigned long vorige_waarde = millis();

De variabele 'vorige_waarde' is hier een unsigned long. Die is gedefinieerd binnen de context van loop() en dus bestaat die alleen binnen loop(). Buiten loop kun je die niet gebruiken.

Normaal verdwijnt zo een variable zodra loop() eindigt, en wordt bij een nieuwe aanroep van loop() weer opnieuw aangemaakt.
Maar doordat die hier 'static' is gebeurt dat niet. De variabele blijft bestaan en de inhoud blijft behouden en wordt weer beschikbaar zodra loop() later weer wordt aangeroepen.

Dat geldt ook voor de initialisatie (= millis()). Normaal gebeurt dat telkens wanneer loop() wordt aangeroepen, maar voor een static variabele gebeurt dat alleen bij de eerste aanroep na een reset van het systeem.

hennep

Golden Member

Andere functies dan loop() kunnen de variabele "vorige_waarde" niet benaderen. Je zou nog een stapje verder kunnen gaan. Ook de rest van de code binnen de loop() functie moet van die variabele afblijven :-)
Bij nader inzien zou ik het nog beter zo kunnen doen:

code:

void toetsafhandeling( void ) {
    static unsigned long vorige_waarde = millis();
    if( vorige_waarde != millis() ) {
        vorige_waarde = millis();        
	...
        // debounce code  
        ...
    }
}

void loop() {  
    toetsafhandeling();
    ...
}

Maar als je denkt dat jij je prima kunt beheersen en niet met je vingers aan verboden variabelen komt, dan kan het ook met een globale variabele:

code:

unsigned long vorige_waarde = millis();

void loop() {
    if( vorige_waarde != millis() ) {
        vorige_waarde = millis();        
	toetsafhandeling();
    }
    ...
}

Ik denk dat we dit wel beroepsdeformatie mogen noemen :-) Als je functies schrijft die in teamverband worden gebruikt, dan moet je soms wat beveiligingen inbouwen tegen grijpgrage vingertjes :-)

hennep,

Voor een beginnende programmeur is er al voldoende "nieuw" en ingewikkeld, dat jou "betere" loop constructie of deKees z'n voorstel om interrupts te gebruiken wel leuk zijn, maar m.i. te hoog gegrepen.

Jou argument: "verspilling van rekenkracht" gaat er bij mij niet in. Als die CPU niets anders te doen heeft, dan valt er niets te verspillen.

Dus laat TS eerst maar eens z'n code schrijven zonder geavanceerde truken en interrupts, als dat eenmaal werkt en er een performance probleem is dan gaan we terug en de juiste truken aanrijken om het beter te krijgen.

Jij (hennep, deKees en ik) kunnen allemaal een statemachine schrijven die toetsenafhandeling doet, en in de verschillende states verschillende acties onderneemt. Voor een beginner is het veel eenvoudiger om een stukje code te hebben waarbij bijvoorbeeld na het drukken op 1 toets een variabele aangepast kan worden (up/down) totdat die toets nogmaals ingedrukt wordt.

Da's mijn mening.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Op 30 juni 2020 12:59:31 schreef rew:
Da's mijn mening.

En die wordt gewaardeerd. :-)

Het programma zal telkens op scherp staan om te detecteren hoelang de Key ingang hoog is, of juist laag.
Zoals iemand eerder al eens vluchtig had berekend, zal bij 40Wpm de aanslag van een punt ongeveer 50mS kosten. Dat is extreem veel als je kunt spelen met enkele millisecondes.
Mijn intentie is dan ook om middels Millis() te beoordelen hoelang de staat van de poort is veranderd, waarbij hij de eerste 15mS moet negeren alszijnde Bounce.
Dus wanneer na een geruime reeks van nulletjes plots de ingang een één wordt, gaat hij dat moment als ijkpunt nemen om te zien of dit ook (overwegend) langere tijd een één blijft. Dat zal na 15 milliseconden wel duidelijk zijn, en hij herkent na >50mS wel of het een punt is, of zelfs langer wanneer het een streep blijkt.
Enfin, dat is een ander deel van het programma.

Het is wel zo dat tijdens het verwerken van dat andere deel van het programma, op de achtergrond die ingang gemonitord moet blijven.
Want adhv de tijden die de poort hoog of laag is geweest, zal het programma determineren of het een punt of streep is, of de momenten van null binnen een karakter vallen, binnen een woord, of juist binnen afzonderlijke woorden.
Afhankelijk van die tijden weet het programma dat ook om te zetten naar ASCII karakters, die dit middels het HID protocol naar de USB poort worden gestuurd.
Dat zal ook enige tijd vergen, hoewel ik daar geen loopje in heb zitten met enige delay.

Met dat ik dit typ realiseer ik me dat Millis() niet kan werken omdat hij weliswaar de tijd monitort, maar niet of iets een null of één is. |:-(
de bitshift die eerder was voorgesteld, waarbij elke mS de waarde van de key wordt vastgelegd voor later gebruik.
Ik zal toch met iets van een timer interrupt moeten werken, die elke mS even kijkt naar de staat van de ingang en deze registreert. ;)

Begreep ik nou dat die Millis() ook te resetten is??
Nieuwsgierigheid... Want ik kan hem misschien iet eens gebruiken. :-)

Ik moet hier weer vaker komen... Wat kun je zo'n forum als deze gaan missen. :-)

millis() werkt best wel, maar geeft alleen de tijd. Als je ook de status van de toets wilt weten heb je extra variabelen nodig.

En resetten van millis() is een slecht idee. Ik weet niet of het kan, maar als je dat zou doen dat krijg je problemen met allerlei funkties die millis() gebruiken voor tijdmeting. Je kunt wel millis() uitlezen als een actie start, zodat je later een nieuwe millis() daarmee kunt vergelijken en kunt uitrekenen hoeveel tijd ertussen zit.

De meest simpele vorm van debounce is gewoon niet te vaak de toets inlezen. Als je maar 1 x per 10 millis() leest dan is de toets allang uitgedenderd. Dus dan krijg je:

code:


void loop()
{
   static unsigned long ToetsTimer = millis();
   static byte ToetsStatus;
   
   if( ( millis() - ToetsTimer) >= 10)
   {  ToetsTimer += 10;

      ToetsStatus = digitalRead(ToetsPin);
   } 

}

Dus net zoiets als hennep ook al liet zien, alleen nog een stuk trager.

Op 30 juni 2020 13:50:39 schreef Fantomaz:
Zoals iemand eerder al eens vluchtig had berekend, zal bij 40Wpm de aanslag van een punt ongeveer 50mS kosten.

Da's tweemaal fout: niet vluchtig, en niet 40 wpm. :)
Per definitie komt 12 wpm overeen met punten van 100 ms.
Punten van 50 ms horen dus bij 24 wpm. Bij 40 wpm is de punt 30 ms lang.

En ook: als een punt (bij 12 wpm) 100 ms duurt, wil dat zeggen dat je maximaal 5 punten per seconde kunt seinen (geen 10). Iedere punt wordt immers gevolgd door een pauze, en die is ook minimaal een puntlengte.

code:

             +----+    +----+    +--
             |    |    |    |    |
12 wpm . . --+    +----+    +----+   . . .
              100  100  100  100 ms

Amateurverkeer (ander morse-verkeer is er niet veel meer) zit praktisch altijd tussen, zeg, 16 en 25 wpm.

Keramisch, kalibratie, parasitair: woordenlijst.org
hennep

Golden Member

@Fantomaz, Is dit bruikbaar?

De drie bestanden bij elkaar zetten zoals dit:

Op 30 juni 2020 15:32:33 schreef hennep:
@Fantomaz, Is dit bruikbaar?

De drie bestanden bij elkaar zetten zoals dit: [bijlage]

Ik denk wel dat het bruikbaar is, maar ik probeer zoiets zelf eerst te beredeneren.
Dat is ook wel wat de sjeu van het programmeren.
Zeker voor een leek als ikzelf, die wel wil weten waar hij mee bezig is.

@FET,
Ik had het zelf even nagerekend door PARIS in morse te ontleden en de het aantal tijdseenheden te tellen. Ik was inderdaad de pauzemomenten vertegen.

@deKees,
Als ik 1x per 10mS ga uitlezen, kan het zijn dat ik een facet van een karakter mis. Hoewel een punt of een pauzemoment in een karakter langer duurt dan 10mS, kan ik me voorstellen dat ik nét in een moment zit waarbij door denderen de poort net even laag is, ipv hoog.

Daarbij... Ik ben vaak bezig met andere zaken op de voorgrond, wanneer op de achtergrond alles wel getimed moet blijven.
Niet alleen òf de poort hoog is, maar ook hoelang hij hoog is, of juist hoelang hij daarintegen laag is.

Ik moet hier weer vaker komen... Wat kun je zo'n forum als deze gaan missen. :-)

foutje

[Bericht gewijzigd door Fantomaz op 30 juni 2020 21:27:55 (99%)]

Ik moet hier weer vaker komen... Wat kun je zo'n forum als deze gaan missen. :-)

foutje

[Bericht gewijzigd door Fantomaz op 30 juni 2020 21:27:31 (91%)]

Ik moet hier weer vaker komen... Wat kun je zo'n forum als deze gaan missen. :-)

Ik zat even te denken en ik ga het volgende proberen:

Een ISR op de change van de poort waarop de sleutel is aangesloten.
Zodra deze is veranderd, kom ik in een loop die 15 loopjes maakt van 1mS.
Binnen die tijd zal de dender er wel uit zijn en kan gedetermineerd worden of de poort (definitief) hoog of juist laag is.

Die kk = (kk << 1)|k code van Rew is daarvoor ideaal.

Het is wellicht wat onorthodox om delays in een loop te bouwen, maar het zet de basis voor een langere tijd waar ik 15mS vanaf haal.
Binnen die sequence heb ik geen andere zaken te regelen.

Maar hoe schrijf ik de code nu zo, dat de loop:

VOID Keystatus ()

Na 15 cycles zal terugkeren naar het standaard programma?
Ik ben nog wat ouderwets schijnt, met Gosub en return.
Ik begreep dat ik in de VOID voorwaarde de condities stel waarmee ik de loop actief houd, maar hoe kom ik uit die loop?

Ik moet hier weer vaker komen... Wat kun je zo'n forum als deze gaan missen. :-)
hennep

Golden Member

Er zijn vele wegen die naar Rome leiden.

code:

unsigned long step = millis();
unsigned long stoptijd = millis() + 15;
for( int i=0; stoptijd>millis(); i++ ) {
    if( step != millis() ) {
        step = millis();
        //doe iets eens per ms 
        if( hetisnuwelgenoeggeweestbinnen15ms ) {
            break;
        }
    }
}

of

code:

for( int i=0; i<15; i++ ) {
    delay_ms(1); // delay_ms gebruik ik nooit meer, ms als param?
    //doe iets eens per ms, de loop stop na 15ms 
}

Zelf heb ik een voorkeur voor de bovenste functie omdat ik binnen de loop nog andere code uit kan voeren. Tijdens delay_ms heb je geen controle meer, om nog even door te zagen over delays :-)
Ik heb de code niet gecompileerd, zo uit mijn hoofd geklopt. Vergeef me een typfoutje.

Als je eerder uit de loop wilt springen dan doe je dat met "break;". "Goto" kennen we gelukkig niet in C en "Gosub" doe je bij het aanroepen van iedere functie.

Op 3 juli 2020 10:32:22 schreef hennep:

...Tijdens delay_ms heb je geen controle meer, om nog even door te zagen over delays :-)

Die 15 mS hoeft hij niets te doen dan monitoren. Zo lang is dat niet.
Ik ben het wel met jou, en vele tutorals eens, dat er geen delays in ISR horen. Maar hier kan dat geen kwaad.

Als je eerder uit de loop wilt springen dan doe je dat met "break;". "Goto" kennen we gelukkig niet in C en "Gosub" doe je bij het aanroepen van iedere functie.

Ik meende te herinneren dat Goto wel mogelijk is, maar wordt afgeraden.
Maar als je die Break gebruikt, gaat dan het programma uit de loop en verder waar hij eerst was gebleven?

code:

for( int i=0; i<15; i++ ) {
    delay_ms(1); // delay_ms gebruik ik nooit meer, ms als param?
    //doe iets eens per ms, de loop stop na 15ms 
}

Hoe vertaal ik dat eerste stukje For-code?
In eerste instantie is i 0.
Zolang i minder is dan 15, vul ik i aan met 1

Is dat ook de volgorde bij dat regeltje? Beginwaarde;voorwaarde;wat-te-doen?

En nu ken ik de For Next loops wel van vroeger, maar dan sprong hij uiteindelijk uit die loop.
Hoe gaat dat hier? Bij gebrek aan een next "afsluiting" zal hij dus dat For loopje uitgaan. Maar gaat hij dan meteen uit de VOID loop?
Of moet ik een Break commando plaatsen ter afronding van die For loop, zodat hij na de For voorwaarden uit de VOID loop springt?

Ik moet hier weer vaker komen... Wat kun je zo'n forum als deze gaan missen. :-)
hennep

Golden Member

Op 3 juli 2020 21:31:50 schreef Fantomaz:Is dat ook de volgorde bij dat regeltje? Beginwaarde;voorwaarde;wat-te-doen?

code:

for( beginwaarde; eindconditie; ophoogfunctie ) {
    wat-te-doen;
}

ophoogfunctie kan met 1 verhogen of verlagen maar zou ook in stappen kunnen verhogen of verlagen, of een echte functie die een waarde retourneert.

Hoe gaat dat hier? Bij gebrek aan een next "afsluiting" zal hij dus dat For loopje uitgaan.

Alles binnen de accolades {} wordt uitgevoerd zolang de eindconditie niet is bereikt.

Maar gaat hij dan meteen uit de VOID loop?

Als er verder niets staat tussen de } van de "for" en de } van loop functie, dan ja.

Of moet ik een Break commando plaatsen ter afronding van die For loop, zodat hij na de For voorwaarden uit de VOID loop springt?

Een break is alleen nodig als je uit de for-loop wilt springen voordat de eindconditie is bereikt.
Als de for loop is beeindigd door zijn eigen conditie, dan gaat de code verder na de sluit accolade.

EDIT: Als ik zo naar het niveau van jouw vragen kijk, dan verwacht ik dat je een AVR hebt kapot geprogrammeerd voordat je morse decoder klaar is. Ik neem tenminste aan dat je voor ieder testje de Arduino opnieuw programmeert.
Ik zou dan eens overwegen om voor het testen van dit soort elementaire stukjes code een C-compiler te installeren.
Deze is gratis en doet het op windows/linux/mac: http://www.codeblocks.org/downloads

Op 4 juli 2020 12:09:26 schreef hennep: Als ik zo naar het niveau van jouw vragen kijk, dan verwacht ik dat je een AVR hebt kapot geprogrammeerd voordat je morse decoder klaar is. Ik neem tenminste aan dat je voor ieder testje de Arduino opnieuw programmeert.
Ik zou dan eens overwegen om voor het testen van dit soort elementaire stukjes code een C-compiler te installeren.
Deze is gratis en doet het op windows/linux/mac: http://www.codeblocks.org/downloads

Nee hoor! O-)
Ik wil graag alles weten en probeer altijd eerst code te doorgronden voor ik hem test.
Ik ken de For/Next of While/Wend, Repeat/until lussen wel hoor. :-)
Maar omdat VOID geen endcode heeft, vroeg ik me af wat de conditie was die hen uit die loop haalde.
Accolades dus... Dat is duidelijk. Voorts wist ik niet zeker of een break onder een conditie ervoor zorgde dat het programma uit de complete loop ging of niet.
Het zit wel ergens, maar in Basic en niet in C(++).
Werken met interrupts, anders dan Gosub/return, is nieuw voor me.

En waar ik moeite mee heb (bevattelijk vermogen van snelheid) zijn de loops van enkele millisecondes.
Daarbij valt dat ook slecht te monitoren.

Neemt overigens niet weg dat ik even ga neuzen bij die C compiler. ;-)

Ik moet hier weer vaker komen... Wat kun je zo'n forum als deze gaan missen. :-)
hennep

Golden Member

Op 4 juli 2020 14:25:53 schreef Fantomaz:
En waar ik moeite mee heb (bevattelijk vermogen van snelheid) zijn de loops van enkele millisecondes.
Daarbij valt dat ook slecht te monitoren.

Dat monitoren valt wel mee. Ik toggle vaak een overbodige pin in de loop functie om te zien hoe snel de code achter elkaar wordt uitgevoerd. Dat kun je dan prima met een scope bekijken.
Het verbetert je "bevattelijke vermogen" aanzienlijk. :-)

Ik kan de Integer na 15 of 16 loops wel laten zien in de serial monitor, maar tijdens het doorlopen van die loop zie ik niet wat er gebeurt.

Ik moet hier weer vaker komen... Wat kun je zo'n forum als deze gaan missen. :-)
hennep

Golden Member

Ik begrijp wat je bedoelt. Sommige dingen zijn gewoon niet te debuggen. Het denderen van een contact is er een van. Je moet gewoon aannemen dat het gebeurt, dat gaat te snel om het in programmacode te kunnen volgen.
Je zou nog wel alle timing van ms naar seconden kunnen brengen en zelf een paar keer stuiteren op de toets/seinsleutel.
Op die manier heb ik de DebounceKey classe ook gecontroleerd. Die staat overigens ondertussen op github. Ik heb daar ondertussen ook nog een tweetal methods aan toegevoegd om te zien hoe lang de tijd is tussen de laatste twee flanken en hoe hoeveel tijd is verstreken sinds de laatste flank. Ik snap dat je zoiets zelf graag wilt bouwen, zo zit ik zelf ook in mekaar. Als het je aan inspiratie ontbreekt kun je daar zien hoe ik het heb aangepakt.