Hallo,
ich stehe gerade selber auf dem Schlauch und finde den Baum vor lauter
Wald nicht m(
Ich möchte mit einem Tiny25 Ereignisse zählen (z.B. via Taster/Reed
o.ä.) und die Anzahl davon mit einem Vorgabewert vergleichen.
Timer1 läuft und gibt mir Sekunden und Minuten an.
Über einen Poti soll nun eingestellt werden wieviele Ereignisse pro
Minute stattfinden müssen um einen Schaltvorgang auszulösen.
Meine erste Idee ist nun im Timer neben den Sekunden noch den
Taster/Eingang abzufragen und zu entprellen um dann ein Flag zu setzen.
So und nun steh ich auf dem Schlauch und bekomme es aktuell nicht
gebacken wie ich nun das pro Minute realisiere ?
Zwischenzeit nehmen oder extra Sekundenzähler lokal im IRQ ?
Das ganze ist nicht Zeitkritisch, maximal sollen 60 Ereignisse pro
Minute vorkommen können.
Wo ist mein Denkfehler ?
kopfkratzer schrieb:> Zwischenzeit nehmen oder extra Sekundenzähler lokal im IRQ ?> Das ganze ist nicht Zeitkritisch, maximal sollen 60 Ereignisse pro> Minute vorkommen können.
Die Frage ist eher:
Wann beginnt eigentlich die Minute zu laufen?
Du kannst natürlich in deiner Timer ISR bei der Implementierung der Uhr,
an der Stelle an der die Minuten weitergeschaltet werden einen
Ereigniszähler auf 0 setzen. In der Hautpschleife werden die
Tastendrücke ausgewertet und der Zähler entsprechend erhöht.
Vor dem 0-setzen des Zählers prüfst du noch, ob du unter dem Limit warst
und reagierst entsprechend.
1
uint16_tCount;
2
uint16_tCountMax=50;// per ADC änderbar
3
4
ISR(...Timer...)
5
{
6
...
7
8
Second++;
9
if(Second==60)
10
{
11
if(Count>CountMax)
12
LEDein
13
else
14
LEDaus
15
16
Count=0;
17
}
18
}
19
20
intmain()
21
{
22
...
23
24
while(1)
25
{
26
if(get_keypress(1<<KEY))
27
Count++;
28
29
...
30
}
31
}
Aber das wird nur dann funktionieren, wenn es da einen kontinuierlichen
Tastendruck-Strom gibt.
Hast du 30 Tastedrücke in den letzten 10 Sekunden einer Minute und 30
Tastendrücke in den ersten 10 Sekunden der nächsten Minute, dann ist
zwar jede einzelne Minute für sich unter dem Limit von 50. Aber
betrachtet man die 'Minute, die bei einem Sekundenstand von 30 begonnen
hätte und auch wieder endet, dann wäre diese Minute mit einem Count von
60 über dem Limit.
D.h. so einfach ist die Sache gar nicht, weil nicht klar ist, was 'in
einer Minute' eigentlich bedeuten soll. Ist das die Minute die mit dem
Sekundenzeiger auf 0 (oder irgendeiner anderen Zahl) beginnt, oder sind
das 'die letzten 60 Sekunden' (egal zu welchem Zeitpunkt sich diese
Frage stellt)
Ich würde eventuell so eine Art Sliding Window Ansatz ins Kalkül ziehen.
D.h. ich weiß von allen Tastendrücken der letzten 60 Sekunden, wann
genau sie in diesen 60 Sekunden aufgetreten sind. Mit jeder Sekunde
fallen hinten aus dieser Liste von Ereignissen diejenigen raus, die über
60 sind. Bei einem Tastendruck wird am anderen Ende der Liste zusammen
mit einem Timestamp angefügt. Die Länge der benutzten Liste darf nie
größer als das Limit sein.
Implementieren kann man das als eine Variation eines Ringbuffers.
> Das ganze ist nicht Zeitkritisch, maximal sollen 60 Ereignisse pro Minute
vorkommen können.
Also: überschaubare Zahlen, so das man auch ein wenig urassen darf. :-)
128 Bytes SRAM beim Tiny25 ist natürlich nicht viel, aber mit einem
maximal erwarteten Limit von 60 noch beherrschbar.
Ich denke, man muss hier jede Minute einzeln betrachten. Das läuft auf
besagten Ringpuffer hinaus, der permanent aufsummiert werden muss, um
die in der letzten Minute insgesamt gedrückten Tasten zu erkennen. Dies
wäre soweit noch einfach, allerdings kommt die Aufgabe ja irgendwo her
und ich vernute hier wenigstens noch die Themen ->Totzeit und ->
Entprellung. Die erstere Thematik wäre statistisch zu lösen, wie beim
Geigerzähler, die zweite braucht eine Analyse, was denn nun 2 Tasten
waren und was eine, die prellt.
Hallo!
Soll es etwas wie ein Schlagzahlmesser beim Kanu oder beim Spinning
werden: wenn Sollwert erreicht, geht die grüne Lampe an?
Dafür zählt man nicht über eine Minute, sondern misst die Zeit zwischen
einzelnen Impulsen, mittelt eventuell gleitend über mehrere Abstände und
entscheidet dann, Sollwert erreicht oder nicht.
Route 66 schrieb:> Hallo!> Soll es etwas wie ein Schlagzahlmesser beim Kanu oder beim Spinning> werden: wenn Sollwert erreicht, geht die grüne Lampe an?>> Dafür zählt man nicht über eine Minute, sondern misst die Zeit zwischen> einzelnen Impulsen, mittelt eventuell gleitend über mehrere Abstände und> entscheidet dann, Sollwert erreicht oder nicht.
Wenn die Impulse durch eine Rotation oder einen Rhythmus gleichverteilt
sind.
Dann kann man sozusagen die "Runrate" bestimmen durch den Abstand von
zwei Impulsen.
Wenn die Impulse aber zufällig kommen, dann muss ich für 1 Minute zählen
(und mir von "jetzt" die letzten 60 Sekunden ansehen) und entscheiden.
Karl Heinz schrieb:> Ich würde eventuell so eine Art Sliding Window Ansatz ins Kalkül ziehen.> D.h. ich weiß von allen Tastendrücken der letzten 60 Sekunden, wann> genau sie in diesen 60 Sekunden aufgetreten sind. Mit jeder Sekunde> fallen hinten aus dieser Liste von Ereignissen diejenigen raus, die über> 60 sind. Bei einem Tastendruck wird am anderen Ende der Liste zusammen> mit einem Timestamp angefügt. Die Länge der benutzten Liste darf nie> größer als das Limit sein.> Implementieren kann man das als eine Variation eines Ringbuffers.>>> Das ganze ist nicht Zeitkritisch, maximal sollen 60 Ereignisse pro Minute> vorkommen können.> Also: überschaubare Zahlen, so das man auch ein wenig urassen darf. :-)>> 128 Bytes SRAM beim Tiny25 ist natürlich nicht viel, aber mit einem> maximal erwarteten Limit von 60 noch beherrschbar.
Das könnte man noch variieren und eine Bitmap als Speicher wählen:
ich habe 60 Bits = 8 Bytes als Speicher für die Ereignisse.
Jedes Bit repräsentiert 1 Sekunde und es gibt einen Zeiger für Start und
Ende des Ringpuffers.
Also habe ich immer einen Überblick über 1 Minute, kompakt in 8 Bytes.
Dann ist die Auflösung natürlich nur 1 Sekunde, aber genau das war ja
gefragt.
@ Conny G. (konrad_g)
>Dann kann man sozusagen die "Runrate" bestimmen durch den Abstand von>zwei Impulsen.
Fürher (tm) hieß das einfach Periodendauer.
>Wenn die Impulse aber zufällig kommen, dann muss ich für 1 Minute zählen>(und mir von "jetzt" die letzten 60 Sekunden ansehen) und entscheiden.
Nennt sich Mittelung oder Tiefpassfilter.
Conny G. schrieb:> Das könnte man noch variieren und eine Bitmap als Speicher wählen:> ich habe 60 Bits = 8 Bytes als Speicher für die Ereignisse.> Jedes Bit repräsentiert 1 Sekunde und es gibt einen Zeiger für Start und> Ende des Ringpuffers.
gute Idee.
Ich hätte das viel banaler gemacht. Aber so gehts natürlich auch, wenn
es nicht mehr als 1 Ereignis pro Sekunde geben kann.
Ich hätt ganz einfach bei Auftreten eines Ereignisses eine 0 in den
Ringpuffer eingeschoben. Die 0 ist der Zeitstempel.
Jede Sekunde läuft die ISR und klappert den Ringpuffer ab und erhöht
alle Zeitstempel um 1. Alle Zeitstempel über 60 fliegen raus aus dem
Ringpuffer.
Mir ist nämlich ums verrecken nicht in 2 Minuten eingefallen, wie ich
das mit der aktuellen Sekundennummer im Ringpuffer hinkriegen könnte, so
dass alle Zeitstempel mit einer Differenz größer als 60 Sekunden zur
aktuellen Sekundennummer erkennbar sind. :-) Daher der pragmatische
Ansatz: jede Sekunde werden alle Einträge im Ringpuffer um 1 Sekunde
älter und die ältesten fliegen raus. Bei seinen Zeitvorgaben mach ich
mir da keine Sorgen, dass das zu langsam wird, aber es wurmt mich, dass
das natürlich nicht sehr elegant ist :-)
Falk Brunner schrieb:> @ Conny G. (konrad_g)>>>Dann kann man sozusagen die "Runrate" bestimmen durch den Abstand von>>zwei Impulsen.>> Fürher (tm) hieß das einfach Periodendauer.
Mja, stimmt :-)
>>Wenn die Impulse aber zufällig kommen, dann muss ich für 1 Minute zählen>>(und mir von "jetzt" die letzten 60 Sekunden ansehen) und entscheiden.>> Nennt sich Mittelung oder Tiefpassfilter.
Ja....! Das ist auch noch ein Ansatz: bei jedem Impuls die Zeitabstände
einem gleitenden Mittelwert hinzufügen.
Das hat eine gewisse Unschärfe, aber vielleicht ist die ja ok.
Conny G. schrieb:> Das könnte man noch variieren und eine Bitmap als Speicher wählen:> ich habe 60 Bits = 8 Bytes als Speicher für die Ereignisse.> Jedes Bit repräsentiert 1 Sekunde und es gibt einen Zeiger für Start und> Ende des Ringpuffers.> Also habe ich immer einen Überblick über 1 Minute, kompakt in 8 Bytes.> Dann ist die Auflösung natürlich nur 1 Sekunde, aber genau das war ja> gefragt.
Danke,
genau das ist die Lösung die mir nicht eingefallen ist ;-)
Jetzt stellt sich mir nur noch die Frage wie ich das am sinnvollsten
realisiere ?
Geht das auf dem Tiny25:
1
.
2
.
3
.
4
//Buffer allozieren
5
volatileuint8_t*pEvent=malloc(8*sizeof(uint8_t));
6
7
ISR(..timer...)
8
{
9
...
10
//Taster/Ereignis abfragen inclusive Entprellung
11
event=getkey();
12
...
13
uSecond++%60;
14
//Event hineinschreiben
15
pEvent[uSecond]=event;
16
...
17
}
18
.
19
.
20
.
Oder zerschieße ich mir damit irgendwas ?
Bzw. reicht der Tiny25 dann noch aus, wegen malloc & Co. ?
Wobei mir ja gerade auffällt das keine Bitmanipulation ist o_O
kopfkratz
OK kann mir bitte mal jemand ein passendes Tutorial bzw. Beispielcode
nennen ?
Oder muß ich jedes uint8_t einzeln mit einer Bitmaske bearbeiten und
dann eines weiter gehen, sobald ich das letzte Bit benutzt habe ?
Dann wäre ein einfaches Array sinnvoller ?
Danke nochmals ;-)
Ein Grundkurs C Programmierung wäre nicht schlecht.
Der Beispielcode kann nicht vernünftig gehen. Man sollte Bits, Bytes und
malloc() schon ein bisschen kennen.
Der ++% Operator ist schon eine Klasse für sich... Macht wohl nur nicht
das, was gedacht wurde.
// end eins hochzählen und ggf im Ringpuffer "umbrechen"
8
end=(end++%60);
9
10
// byte und bit-Adresse berechnen
11
uint8_tbyteOfs=second/8;
12
uint8_tbitOfs=second%8;
13
14
if(bitValue)
15
{
16
eventBuffer[byteOfs]|=(1<<bitOfs);
17
}
18
else
19
{
20
eventBuffer[byteOfs]&=~(1<<bitOfs);
21
}
22
23
/* jede Sekunde */
24
ISR(..timer...)
25
{
26
if(getKey())
27
{
28
addBit(getKey());
29
}
30
}
Hier stimmt jetzt die Ringpufferverwaltung noch nicht, denn man müsste
auch das "start" noch weitersetzen, wenn der Puffer voll ist sodass
Elemente im Puffer immer = 60.
Conny G. schrieb:> Hier stimmt jetzt die Ringpufferverwaltung
Hmm.
Wenn ich mich nicht verhaut habe, braucht man doch den Ringpuffer im
Grunde gar nicht mehr.
Du hast 60 Bit, jedes hält fest, ob in dieser Sekunde ein Ereignis
eingetreten ist oder nicht. Das reicht doch schon völlig, genau so wie
du das gemacht hast. Mit der Sekundennummer (nenn 'end' einfach um in
'sekunde') wird ein Bit von diesen 60 gesetzt oder gelöscht. Alles was
man noch braucht, ist die Anzahl der gesetzten Bit von diesen 60. Das
ist aber einfach zu realisiern. Man braucht nur mitzählen.
(aus Übersichtsgründen nenne ich ein paar Dinge um. Am Prinzip hat sich
aber nicht viel geändert)
1
uint8_teventBuffer[8];
2
uint8_tnrEvents=0;
3
uint8_tsecond=0;
4
5
voidregisterEvent(uint8_tsecond,uint8_tbitValue)
6
{
7
// byte und bit-Adresse berechnen
8
uint8_tbyteOfs=second/8;
9
uint8_tbitOfs=second%8;
10
11
// den vorhergehenden 'Status' für diese Sekunde austragen
12
if(eventBuffer[byteOfs]&(1<<bitOfs))
13
nrEvents--;
14
15
// den neuen eintragen
16
if(bitValue)
17
{
18
eventBuffer[byteOfs]|=(1<<bitOfs);
19
}
20
else
21
{
22
eventBuffer[byteOfs]&=~(1<<bitOfs);
23
}
24
25
// und gegebenenfalls zählen
26
if(bitValue)
27
nrEvents++;
28
}
29
30
/* jede Sekunde */
31
ISR(..timer...)
32
{
33
second++;
34
if(second==60)
35
second=0;
36
37
uint8_tevent=getKey();
38
registerEvent(second,event);
39
}
nrEvents müsste hier eigentlich ständig die gesuchte Zahl enthalten (die
Anzahl an Ereignissen in den letzten 60 Sekunden).
Voraussetzung: nicht mehr als 1 Event pro Sekunde, obwohl man das auch
noch aufweichen könnte, wenn das zu restriktiv ist. Das die ISR im
Sekundentakt die Eingänge prüft, ist ja recht willkürlich einfach so
festgelegt. Die könnte das ja auch im halbe Sekunde-Takt machen und in
120 Bits den Status festhalten.
Conny G. schrieb:> Mmmh, jetzt brauchen wir den Ringpuffer nur noch um eins zu> dekrementieren, wenn das letzte Bit 1 war.> Bekommen wir ihn noch ganz weg? :-)
Der Ringbuffer existiert so überhaupt nicht mehr.
Das Array ist einfach nur ein Flag-Array in dem zu jeder Sekundennummer
vermerkt ist, ob ein Ereignis aufgetreten ist oder nicht. Da die
Sekunden sequentiell in der ISR abgearbeitet werden, überschreibt nach
60 Sekunden die jeweils dann vorliegende Ereignis-Information
automatisch die Information von 1 Minute früher.
Alles was ich noch ergänzt habe, ist das mitzählen der 1-Bits in diesem
Array. Dann braucht man da nicht aufwändig die 1-Bits zählen. Die
Information ergibt sich ganz zwangsläufig aus dem 'alten' Status von vor
1 Minute und dem jeweils neu einzuschreibenden Status von jetzt.
Stell dir eine Analoguhr vor, bei dem es zu jeder Sekunde 1 Fach gibt
(so wie bei einem Roulett-Kessel). Im Fach kann eine Murmel liegen oder
nicht.
Nach 1 Sekunde schiebt sich der Sekundenzeiger um 1 weiter.
Man sieht im Fach, auf das der Sekundenzeiger zeigt, nach: liegt da eine
Murmel drinnen, dann gab es 1 Minute vorher ein Ereignis, daher wird von
der Gesamtzahl der Ereignisse 1 abgezogen, weil dieses Ereignis ja
aufgrund der vergangenen Zeit aus der Statistik rausfliegt. Damit ist
die Buchhaltung für 'alte Ereignisse' erledigt. Hat man ein neues
Ereignis, welches zu registrieren ist, dann wird wieder eine Murmel
reingelegt und der Zähler um 1 erhöht. Hat man kein Ereignis, dann
bleibt das Fach leer.
Und so geht das weiter. Sekunde für Sekunde. Der Zustand von vor 1
Minute wird jedesmal aufgehoben (das Fach geleert und die Summe
gegebenenfalls korrigiert) und je nach aufgetretenem Zustand wird das
Fach befüllt oder auch nicht (und der Zähler entsprechend hochgezählt
oder auch nicht).
Karl Heinz schrieb:> Der Ringbuffer existiert so überhaupt nicht mehr.
Ist klar, ich hab ihn unkorrekterweise nur nochmal so genannt.
Auch klar, was jetzt daraus geworden ist.
Nach ein bisschen nachdenken kam ich auch zu dem Schluss, dass man sich
den Puffer nicht sparen kann.
Denn ich muss irgendwie an das Event vor 60 Sekunden herankommen und das
geht nur über die ganze Historie.
Coole Lösung geworden, ganz simpel und nur 8 Bytes Speicher.
Karl Heinz schrieb:> Das Array ist einfach nur ein Flag-Array in dem zu jeder Sekundennummer> vermerkt ist, ob ein Ereignis aufgetreten ist oder nicht. Da die> Sekunden sequentiell in der ISR abgearbeitet werden, überschreibt nach> 60 Sekunden die jeweils dann vorliegende Ereignis-Information> automatisch die Information von 1 Minute früher.
Ah, jetzt. Habe beim Überfliegen eins nicht erkannt: jede Sekunde hat
jetzt ein festes Bit!
Das ist eine coole Vereinfachung, ja!