ich stehe vor folgendem Problem: über eine Interruptschleife bekomme ich kontinuierlich Werte herein, den Großteil davon brauche ich nicht. Kommt aber eine Startsequenz, sagen wir mal 6 mal die"0", sind die nächsten 20 Werte relevant und sollen dann ausgegeben werden.
Ich steh' völlig auf dem Schlauch, wie ich das am geschicktesten angehe. Ich dachte erst, in einer Schleife x mal den hereinkommenden Wert gegen den Sollwert aus einem Array zu vergleichen. Und wenn es nicht passt Schleifenabbruch.
Sobald eine 0 kommt, merkst Du Dir das in einem Zähler. Dann liest Du weiter. Wenn es 6 Nullen sind, liest Du die nächsten 20 Zeichen in ein Array. Wenn vor der 6. Null etwas anderes kommt, stellst Du den Nullenzähler wieder auf 0 und wartest wieder auf Nullen.
Das könnte aber ebenfalls versagen, wenn das/die letzte/n Byte/s ebenfalls Nullen sind, Die ja ebenfalls gezählt werden.
So hast Du am Anfang Deiner 6x die Null-Sequenz bereits ein/zwei Nullen gezählt und wirst dann ab der 6.ten gezählten Null irrtümlich das Array mit den letzten Nullen füllen - denke, hier muß noch was zeitliches mit rein, daß der Counter sicher resettet wird.
Kannst Du schon die Daten empfangen und Dir im Terminal ausgeben lassen?
Eigentlich müssten genauere Infos vom TO kommen. Die Vorschläge können nur die Infos wiederspiegeln.
Wenn 6 mal Null als Starft gilt, dann darf das natürlich nicht im Payload auftauchen.
Nicht wirklich - wir haben einen Datenstrom, an Dessen Ende zwei Nullen gesendet werden.
Den Anfang (mit den 6 Nullen) haben wir nicht mitbekommen, da wir eben nicht rechtzeitig zugehört haben.
Nun haben wir also schon bis 2 gezählt.
Der nächste Anfang kommt, dieses Mal 6 Nullen am Stück - nur nach bereits 4 Nullen haben wir unsere erwarteten 6 Nullen voll - die letzten zwei Nullen der Preamble kommen dann irrtümlich in die Nutzdaten.
Einlesemodus aktiv? -> Nein, dann
aktueller Wert ist 0? Wenn ja, dann Level + 1 ansonsten Level = 0
nächster Wert ist 0? Wenn ja, dann Level + 1 ansonsten Level = 0
nächster Wert ist 0? Wenn ja, dann Level + 1 ansonsten Level = 0
nächster Wert ist 0? Wenn ja, dann Level + 1 ansonsten Level = 0
nächster Wert ist 0? Wenn ja, dann Level + 1 ansonsten Level = 0
nächster Wert ist 0? Wenn ja, dann Level + 1 ansonsten Level = 0
nächster Wert ist 1? Wenn ja, dann Level + 1 ansonsten Level = 0
Wenn Level = 7 dann Einlesemodus aktiv ansonsten nicht aktiv
Wenn Einlesemodus aktiv dann die nächsten 19 Werte in ein Array einlesen
Wenn 19 Werte eingelesen, dann Level = 0 und Einlesemodus nicht aktiv
Das müsste es doch sein?!
Oder doch wieder zu einfach gedacht?
Alternativ den eingelesenen Wert in ein Array speicher und jedes Mal prüfen, ob "0000001" im Array steht. Wenn ja, dann einlesen, und eventuell die Prüfung ausschalten für die nächsten 19 Werte, danach wieder die Prüfung einschalten.
Ganz genau: weder noch, sondern wird als Controlbit bezeichnet.
Als Erläuterung, es handelt sich um das FDX-B Protokoll. Also 10x"0" als Header, "1", danach 117 Bit Payload. Zu Testzwecken brauche ich da allerdings erstmals etwas mehr, Funksignale haben leider durchaus Fehler zwischendrin.
Einlesemodus aktiv? -> Nein, dann
aktueller Wert ist 0? Wenn ja, dann Level + 1 ansonsten Level = 0
PS: Das wäre ja eine "ganz zu Fuß Lösung", die ich vermutlich auch programmiert bekomme. Richtig effizient ist das allerdings nicht.
Edit: Gibt es denn kein Schieberegister als Softwarefunktion? Dann bräuchte ich nur den jeweils neuen Wert reinschieben und gegen den Sollwert vergleichen.
Es gibt in C++ zwar das Äquivalent zu Schieberegistern, wird aber auch nicht "effizienter"
Den Vorschlag könnte man zB so realisieren:
// check for a series of up to 255 zero bits
class ZeroSequence {
byte count;
public:
ZeroSequence() : count(0) {}
void init() { count = 0; }
byte add(bool x) {
// return number of preceeding zeroes (false), not including this x
byte c = count;
if (x) count = 0; else count++;
return c;
}
};
Und hier ein Testprogramm dazu:
int main()
{
const bool Test[] = { 0,0,1,0,0,0,0,0,0,1,1 };
const byte Rslt[] = { 0,1,2,0,1,2,3,4,5,6,0 };
const size_t len = sizeof(Test) / sizeof(Test[0]);
printf("ZeroSequence Test (%u)\n", len);
ZeroSequence seq;
for (size_t i = 0; i < len; i++) {
byte result = seq.add(Test[i]);
if (Rslt[i] != result) {
printf("Failed at %u : Returned %u (expected %u)", i, result, Rslt[i]);
}
if (Test[i] == 1 && result == 6) printf("Success (%u)!\n", i+1);
}
printf("ZeroSequence Test Done\n");
return 0;
}
So, jetzt hatte ich Zeit mich mit dem Code zu beschäftigen. Vorab, Michael, dein Code ist mir zu hoch. Da verstehe ich zuwenig C als dass ich damit was sinnvolles anfangen könnte.
Bisherige Einschränkung: ich fülle immer einen Buffer und werte den Datenfluß nachträglich aus, was natürlich den Nachteil hat, dass valide Ergebnisse die erst am Bufferende auftauchen, nicht mehr komplett sind.
Ich fürchte aber, in die Interruptroutine läßt sich die Auswertung nicht ohne weiteres einbauen, bei rund 125micros Pulsdauer. Ein erster Versuch ergab jedenfalls keine sinnvollen Daten mehr.
#define SAMPLESIZE 500
int receiverPin = 2;
int counter = 0;
int level = 0;
static unsigned int timings[SAMPLESIZE];
static unsigned long lastTime = 0;
static int interruptPin = 0;
void setup() {
Serial.begin(115200);
interruptPin = digitalPinToInterrupt(receiverPin);
Serial.println("Ready! ");
attachInterrupt(interruptPin, handleInterrupt, CHANGE);
}
void loop() {
delay(10);// damit dieser sketch funktioniert
if (counter > 400) {
detachInterrupt(interruptPin);
for (unsigned int i = 1; i < counter; i +=1){
if (timings[i]>100 && timings[i]<150){
level +=1;
}
else{
level=0;
}
if (level == 20){
for (unsigned int j = i-19; j < i + 200; j +=1){
Serial.print(timings[j]);
Serial.print(",");
}
Serial.println();
level = 0;
}
}
counter = 0;
attachInterrupt(interruptPin, handleInterrupt, CHANGE);
};
}
void handleInterrupt() {
const unsigned long time = micros();
const unsigned long duration = time - lastTime;
timings[counter++] = duration;
lastTime = time;
}
Wenn Du den Buffer als Ring-Speicher benutzt?
counter zählst Du in der ISR zwar hoch, wendest aber gleichzeitig MODULO mit der Bufferlänge an.
Modulo = Rest einer Division
(x+1)%Länge -> x=0...(Länge-1)
So schreibst Du 'immer im KReis' die neuen Werte in den Buffer.
counter ist hier quasi Deine Schreib-Adresse.
Nun musst Du 'nur' in loop() mit einer Lese-Adresse die Daten überprüfen.
Hierbei musst Du drauf achten, daß der Lese-Zeiger den Schreib-Zeiger nicht überholt.
Auch den Lese-Zeiger kannst Du per ++ und %(Modulo) weiter schieben, wenn Du mit der aktuellen Stelle fertig bist.
Lesen wird erst gestoppt, wenn der Lese-Zeiger den Wert des Schreib-Zeiger erreicht hat.
Also, die Prüfung wird dann ausgesetzt - ohne neue Daten wirst Du wohl auch Nichts prüfen müssen, oder?
Wenn's zu kompliziert geschwätzt ist, gerne Nachfragen
Bisherige Einschränkung: ich fülle immer einen Buffer und werte den Datenfluß nachträglich aus, was natürlich den Nachteil hat, dass valide Ergebnisse die erst am Bufferende auftauchen, nicht mehr komplett sind.
Ich fürchte aber, in die Interruptroutine läßt sich die Auswertung nicht ohne weiteres einbauen, bei rund 125micros Pulsdauer.
Für eine ISR wären hundert µs (1600 Takte) ein Haufen Zeug. Die Manchester-Decodierung könnte aber locker in der ISR stattfinden. Und dann auch gleich die Startsequenz erkennen. Ist ja jetzt nicht soo schwierig. (Nullen (kurze Pulse) zählen und bei der ersten Eins prüfen, wie ich es früher vorgeschlagen habe)
Ob du nach der Startsequenz für die Nutzdaten ein ganzes Byte / Bit verschwendest, ist dann nicht mehr ganz so wild, da es ja auch deutlich weniger Daten sind.
Dein int timings[500], sowie attach/detachInterrupt sind meiner Meinung nach die Probleme in deinem Sketch.
Danke Der Ansatz klingt vernünftig. Beim Ringspeicher weiß ich was er tut, das war's aber dann auch schon. Ebenso Modulo-Division, bei der ich noch nicht im Ansatz verstehe, wozu das hier gut sein soll.
Wenn's zu kompliziert geschwätzt ist, gerne Nachfragen
michael_x:
Für eine ISR wären hundert µs (1600 Takte) ein Haufen Zeug.
...
Dein int timings[500], sowie attach/detachInterrupt sind meiner Meinung nach die Probleme in deinem Sketch.
ist erstmal ein schlechtes Zeichen.
Ersteres macht mir Hoffnung, dass es umsetzbar ist. Ich fürchte nur nicht unbedingt von mir
Für den Versuch mit Auswertung im Interrupt, hatte ich keinen Buffer, und auch kein detach interrupt. Weil es aber gar nicht geklappt hatte, gibt es den Code auch nicht mehr
Und das mit dem Delay werde ich mir mal ansehen, ob der wirklich nötig ist.
PS: warum haut es beim editieren mir immer code-Schnipsel rein ?
Um die Unterscheidung kurz-lang in einer ISR schneller zu machen, kam mir die Idee: statt zweier int-Vergleiche könntest du einzelne Bits testen: kurz ist 128 ... 195, lang ist 256 ... 319.
Wenn du (neu-alt) >> 6 rechnest ( /64 ) gibt es nur ein Byte-Resultat
0 oder 1 0 .. 127 us zu kurz
2 KURZ
3 unklar
4 LANG
5 oder mehr: zu lang
Was im Fehlerfall zu tun ist, weiß ich nicht.
Einfach stattdessen >> 7 rechnen und
1 = KURZ
2 = LANG
auswerten.