antidender op Arduinopoort?

Tja, dat stamt nog uit het stenen tijdperk, toen C net was uitgevonden.

Om getallen te printen werd de funktie printf() uitgevonden. Die krijgt een variabel aantal parameters mee, maar de eerste is een string. En binnen die string staan dan format codes die altijd met een procent beginnen.

Dus dan krijg je bijvoorbeeld

code:


   printf ("Het getal is %d", x);

Dan krijg je "Het getal is 100" op het scherm, dus de %d wordt vervangen door de waarde van x.

Als je het getal in hex wilt zien dan gebruik je %X ipv %d

code:


   printf ("Het getal is %X", x);

Dat geeft dan "Het getal = AA" als x gelijk is aan 170.

Maar meestal wil je ook laten zien dat het hex is, dus dan zet je er 0x voor in de string. Dat betekent niks voor de compiler, dus dat wordt gewoon afgeprint:

code:


   printf ("Het getal is 0x%X", x);

Dat geeft dan "Het getal = 0xAA" als x gelijk is aan 170.

Nu zijn er inmiddels allerlei varianten van die prinf(). De belangrijkste is sprintf() Die doet dezelfde formatting, maar schrijft het resultaat in een textbuffer :

code:


   char TextBuffer[100]; 
   sprintf (TextBuffer, "Het getal is 0x%X", x);

En zo zijn er nog allerlei variaties op de format specifier.
Als je dat wilt gebruiken dan heb je een extra header nodig:

code:


   #include <stdio.h>

Maar deze funktie is niet meer zo populair voor micro'tjes want die heeft nogal flink wat code nodig, en is gevaarlijk als de tekst te lang wordt voor de buffer, of als je fouten maakt met de format specifier.

Interessant!
Wat ik me dan afvraag is waarom dit tussen de accolades is gehouden.
Je zou verwachten dat het Printf("Het getal is" %d)
Omdat anders %d als klare tekst wordt gezien...

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

Ja de "%d" moet binnen de quotes en is dus een onderdeel van de string. De printf funktie leest de string en vervangt de %d door een getal.

Dat werkt ook als de %d midden in de string staat en als er meer van zijn:

code:



   int i = 1;
   int y = 2;
   float w = 12.3;
   float h = 15.8;
 
   printf ("Getal nr %d(=%6.2f) is groter dan getal nr %d (=%9.4f)\n", i, w, y, h);

Dat geeft dan :
"Getal nr 1(= 12.30) is groter dan getal nr 2(= 15.8000)"

Dus %f is voor floats, en met %9.4f geef je aan dat je 9 cijfers wilt met 4 achter de komma.

Tussen al de code door toch even melden dat ik in het verleden meermaals de MC14490 heb ingezet voor hardware-matige ontdendering ;)

Er is spanning zonder stroom maar geen stroom zonder spanning
hennep

Golden Member

Houdt er wel rekening mee dat printf met %f op een arduino niet werkt. Dat leuke voorbeeld van DeKees kun je dus niet even uitproberen op je arduino die je voor morse in gedachten had.

printf() werkt wel op arduino, ook voor floats, maar niet bij default inderdaad. Daarvoor moet je een paar instellingen in de compiler aanpassen, en je moet aangeven waar de output naartoe moet, en je moet kiezen tussen een mini-printf(geen floats) en een complete printf (wel floats maar kost meer ruimte).

hennep

Golden Member

Ik gebruikte tot nu toe de dtostrf functie om floats in een string te zetten. Maar die functie is ook officieel depricated.
Hoe zet je %f het weer aan in sprintf? Met welke instelling?
Ik ben niet verknocht aan de sprintf functie, dus als ik geheugen kan besparen graag. Die dtostrf was voor mij al goed genoeg.

In het geval dtostrf ook verdwenen is, kan het ook nog zo:

code:

#include <stdio.h>
#include <math.h>

...
    float f = -12345.6789;

    // 2 decimalen
    printf( "%s%d.%02d\n", (f<0)?"-":"", (int)fabs(f), ((int)fabs(f*100))%100 );
    // 3 decimalen
    printf( "%s%d.%03d\n", (f<0)?"-":"", (int)fabs(f), ((int)fabs(f*1000))%1000 );
...

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. :-)