Hallo Forum, ich habe eine kleine Aufgabe. Es ist nichts schulisches oÄ sondern nur für ein privates Projekt. Also: ich habe eine (globale) Variable currentID; Diese soll bei jedem Durchlauf eins erhöht werden, allerdings in einem bestimmten Muster: 01-16 17-32 01-16 33-48 01-16 49-64 01-16 65-80 und dann wieder von vorne (hoffe die Zahlenreihe stimmt so ;) ) Also soll praktisch immer ein 16er-Block genommen werden und zwischen jedem block ein mal der block 1-16. eig müsste mann es ja irgendwie mit if(currentID & 16) lösen können, aber ich komme auf keine Lösung... Wahrscheinlich sehr trivial, aber ich bitte trotzdem um Hilfe ;) Danke schon mal
nga schrieb: > Es ist nichts schulisches oÄ sondern nur für ein privates Projekt. Das klänge angesichts dieser exotischen Aufgabe überzeugender, wenn du was zum Projekt verrätst. Kleiner Tip: Wenn ausschliesslich die gezeigten Information vorhanden ist, also ein einziger Zahlenwert zwischen 1 und 80, geht es nicht. Du kannst dir aber aussuchen, ob die fehlende Information aus Daten besteht, oder aus der Position im Verfahren.
:
Bearbeitet durch User
A. K. schrieb: > Das klänge angesichts dieser exotischen Aufgabe überzeugender, wenn du > was zum Projekt verrätst. Haha, okay Also ich habe ein System mit 80 Slaves (angesteuert über einen parallel-Bus und eigenem "Protokoll") Die ersten 16 davon sind kritischer/wichtiger deswegen sollten die so oft ausgelesen werden. Ausgelesen werden diese in einer Timer-ISR, einer nach dem anderen und bei jedem Durchlauf soll der nächste Slave angesprochen werden
Wie wärs mit einer union mit zwei getrennten Zählern? Könnte gehen, habe aber nicht weiter drüber nachgedacht. Martin
:
Bearbeitet durch User
Du brauchst auf alle Fälle zwei Variablen. Eine für den aktuellen Zählstand 1...16 und eine für den Offset (z. B. x * 16). Für beide Variablen reichen 4 Bit. ;-)
i = 0; ISR: if (i & 1) sensor(1 + (i >> 1) % 16) else sensor(17 + (i >> 1)); if (++i == 128) i = 0; Gibt nicht die gleiche Reihenfolge, aber die gleiche Abfragefrequenz.
Ich würde es auch mit 2 getrennten Timern machen. Bsp.: Timer 1 läuft mit 1 ms und fragt die Eingänge 1-16 ab, Timer 2 Läuft mit 5 ms und fragt die restlichen Eingänge ab.
Keiner sagt das das ganze getimed sein soll. In meinem Verständnis geht es rein ums hochzählen.
So kannst Du beliebige Reihenfolgen festlegen: char aSlaves = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 33,34,..... char uIndex = 0; ISR(XXX) { doSomethingWithSlave(uIndex); uIndex++; if(uIndex >= sizeof(aSlaves)) uIndex = 0; }
:
Bearbeitet durch User
Mini Mem schrieb: > Du brauchst auf alle Fälle zwei Variablen. Eine für den aktuellen > Zählstand 1...16 und eine für den Offset (z. B. x * 16). Natürlich, aber ich bin wohl nicht fähig auf eine Lösung zu kommen:
1 | if(++currentID & 16) { |
2 | offset += 16; |
3 | if(offset > 80) offset = 0; |
4 | currentID = 1; |
5 | }
|
6 | values[currentID + offset] = readSlave(currentID + offset); |
A. K. schrieb: > i = 0; > > ISR: > if (i & 1) > sensor(1 + (i >> 1) % 16) > else > sensor(17 + (i >> 1)); > if (++i == 128) > i = 0; > > Gibt nicht die gleiche Reihenfolge, aber die gleiche Abfragefrequenz. Danke schon mal dafür, ich muss den Code nur noch verstehen ;) Werde mich aber mal dransetzen libba schrieb: > Ich würde es auch mit 2 getrennten Timern machen. Bsp.: Timer 1 läuft > mit 1 ms und fragt die Eingänge 1-16 ab, Timer 2 Läuft mit 5 ms und > fragt die restlichen Eingänge ab. Wäre eine Idee, aber ich habe keinen Timer mehr frei (eigentliches Target ist ein AVR aber ich fand dass die Frage allgemein für Programierung gilt, deswegen in diesem Unterforum).
So kannst Du beliebige Reihenfolgen festlegen: 3. Versuch char aSlaves = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 33,34,.....}; char uIndex = 0; ISR(XXX) { doSomethingWithSlave(aSlaves[uIndex]); uIndex++; if(uIndex >= sizeof(aSlaves)) uIndex = 0; }
:
Bearbeitet durch User
Olaf Dreyer schrieb: > So kannst Du beliebige Reihenfolgen festlegen: > > char aSlaves = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, > 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, > 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, > 33,34,..... > > char uIndex = 0; > > ISR(XXX) { > doSomethingWithSlave(uIndex); > uIndex++; > if(uIndex >= sizeof(aSlaves)) > uIndex = 0; > } Eigentlich keine schlechte Idee, aber liegt dass Array dann nicht im RAM und wird erst zur Laufzeit ausgewertet? In einer ISR ist das glaube nicht so gut
Ja das ist richtig. Speicherintensiv, aber flexibel. Und einfach zu verstehen :-) Gruß Olaf
A. K. schrieb: > Martin B. schrieb: >> Keiner sagt das das ganze getimed sein soll. > > Doch. Er sagt das. OK, aber man kann doch auch in einem Timer zwei Variablen zählen.
Olaf Dreyer schrieb: > Speicherintensiv, Ach wo. ROM pur geht auch: const _flash char aSlaves[] = ... wenn leidlich aktueller avr-gcc.
:
Bearbeitet durch User
Olaf Dreyer schrieb: > Ja das ist richtig. Speicherintensiv, aber flexibel. > Und einfach zu verstehen :-) > > Gruß > > Olaf Das array kann const sein und damit im Flash liegen. Aber mit Zahl variablen finde ich das bedeutend besser.
A. K. schrieb: > Olaf Dreyer schrieb: > Speicherintensiv, > > Ach wo. ROM pur geht auch: > const _flash char aSlaves[] = ... > wenn leidlich aktueller avr-gcc. Na ja, Speicher intensiv bleibt es trotzdem. Flash ist auch Speicher. ;-)
Olaf Dreyer schrieb: > Speicherintensiv Man kann ja auch mischen. Nur den Anfangswert für jeweils 16 Durchläufe im Array ablegen. Dann sind das gerade mal acht Byte. (Ich würde neun nehmen und als letztes eine 0 (oder -1) als Endekennung ablegen)
nga schrieb: > Wahrscheinlich sehr trivial, aber ich bitte trotzdem um Hilfe ;) Offensichtlich möchtest du 4 Abfrageblöcke machen, in denen du jeweils einmal von [1 .. 16] und einmal von BlockNr*16 + [1..16] zählst (mit Blocknummer=1..4), also
1 | for BlockNr=1 to 4 |
2 | { |
3 | for j=1 to 16 |
4 | Abfrage (j); |
5 | |
6 | for j=1 to 16 |
7 | Abfrage (BlockNr*16 + j); |
8 | } |
und das ganze in C. Einfacher wird das Programm, wenn die Reihefolge nicht so festgelegt ist
1 | for BlockNr=1 to 4 |
2 | for j=1 to 16 |
3 | { |
4 | Abfrage (j); |
5 | Abfrage (BlockNr*16 + j); |
6 | } |
nga schrieb: > ich muss den Code nur noch verstehen ;) Sollte nicht so schwer sein, das einfach mal durchlaufen zu lassen und den Index auszugeben. Dann siehst du auch, ob die abweichende Reihenfolge den Anforderungen entspricht.
:
Bearbeitet durch User
0815 schrieb: > Offensichtlich möchtest du 4 Abfrageblöcke machen, Jetzt musst du das nur noch so umstellen, dass eine ISR draus wird. ;-) Dass es darum geht hatte er erst verspätet verraten.
:
Bearbeitet durch User
A. K. schrieb: > nga schrieb: >> ich muss den Code nur noch verstehen ;) > > Sollte nicht so schwer sein, das einfach mal durchlaufen zu lassen und > den Index auszugeben. Dann siehst du auch, ob die abweichende > Reihenfolge den Anforderungen entspricht. Ich werde es testen, muss nur noch (selbst ;) ) rausfinden warum geshiftet wird. Ich werde auch noch die Array-Variante austesten und schauen wie gut was funktioniert. Auch 0815s Programm werde ich testen Danke schon mal an alle
Man erkennt ein paar Gesetzmäßigkeiten: * Innerhalb der 16er-Blöcke zählt es stetig hoch. Also ist ein Teil des Ergebnisses schonmal i % 16 (unten a genannt). * Alle 16 ändert sich der Status von "erster block" auf "normal ansteigend". Alle 16 ändert sich auch das entsprechende Bit, was man mit i%16 herausfindet. * Zu a muss man ggf. noch eine Zahl addieren, die alle 32 um 16 springt. uint8_t id(uint8_t i) { uint8_t a = 1 + (i & 15); bool use_first = ! (i%16); uint8_t base = 16 + (i/32)*16; return a + (use_first ? 0 : base); }
1 | int counter() |
2 | {
|
3 | static int a = 0; |
4 | static int b = 16; |
5 | static int i = 0; |
6 | |
7 | if (a++ < 16) |
8 | return a; |
9 | |
10 | if (i++ < 16) |
11 | return ++b; |
12 | |
13 | if (b == 80) |
14 | b = 16; |
15 | |
16 | a = 1; i = 0; |
17 | |
18 | return a; |
19 | }
|
> bool use_first = ! (i%16);
muss natürlich ein & sein, auch im Text.
Ok, ich hab ne Idee. Die ist echt nicht so ganz trivial zu verstehen, aber mich hat das Problem gereizt. Vielleicht hab ich auch was übersehen, dann bitte ich um ne Korrektur. Zunächst mal braucht man als Ausgangspunkt ja einen Anhaltspunkt, wo man gerade in der Sequenz ist. Also zählt man mal statt der gesuchten Folge einfach von 0 bis 127 und schaut wie der gesuchte Wert korrespondiert. Dabei geh ich jetzt mal davon aus, dass der gesuchte Wert bei 0 beginnt und nicht bei 1, weil es das Rechnen erleichtert. Bei Bedarf halt eine 1 am Ende addieren. Also 16er Blöcke: 0 - 15 => 0 - 15 = Ausgangswert - 0 16 - 31 => 16 - 31 = Ausgangswert - 0 32 - 47 => 0 - 15 = Ausgangswert - 32 48 - 63 => 32 - 47 = Ausgangswert - 16 64 - 79 => 0 - 15 = Ausgangswert - 64 80 - 95 => 48 - 63 = Ausgangswert - 32 96 - 111 => 0 - 15 = Ausgangswert - 96 112 - 127 => 64 - 79 = Ausgangswert - 48 Wenn ich nun das obere Nibble des Ausgangswert betrachte und das Nibble des gesuchten Subtraktors daneben schreibe, komm ich auf: 000 | 000 001 | 000 010 | 010 011 | 001 100 | 100 101 | 010 110 | 110 111 | 011 (Das untere Nibble wird ja eh übernommen, also lass ich das hier weg.) Ist das niedrigte Bit auf der linken Seite 0, bleibt der Wert unverändert. Ist er 1, wird der Wert um 1 nach rechts geschoben. Der Wert wäre also (ich nehm mal index als 'Ausgangswert'. index & 16 == 0 ? index & 0xf0 : (index & 0xf0) >> 1; Also jetzt mal in einer Schleife: for( int i = 0; i < 127; ++i) { int subtract = i & 0xf0; if( i & 16) { subtract >>= 1; } int result = i - subtract + 1; // Add 1 to start the counting at 1. } Lieg ich falsch? Hab das jetzt mal nicht gecoded, also praktisch getestet, aber ich hoffe mal die Idee kommt rüber.
1 | int counter() |
2 | {
|
3 | static uint8_t a = 0x7f; |
4 | |
5 | a = (a+1) & 0x7f; |
6 | return (a & 0x10 ? a - ((a>>1) & 0x70) : a & 0x0f) + 1; |
7 | }
|
Ich hab's gerne kurz :-)
1 | int i, j, block[] = { 1, 17, 1, 33, 1, 49, 1, 65 }; |
2 | |
3 | for( i=0; i<sizeof(block)/sizeof(*block); ++i ) |
4 | for( j=0; j<16; ++j ) |
5 | do_somtehing(block[i]+j); |
Nicht getestet! Ich hoffe, ich habe nichts übersehen.
Mist! In meiner Lösung ist zumindest noch ein Fehler! Beim Schieben nach rechts, wird ja das unterste Bit ins untere Nibble geschoben. Also hinter subtract >>= 1; noch ein subtract &= 0xf0; (7f ginge auch, da ja das oberste Bit eh nicht benutzt werden sollte.)
Gerne auch als ISR:
1 | ISR() |
2 | {
|
3 | static int i=0, j=0, block[] = { 1, 17, 1, 33, 1, 49, 1, 65 }; |
4 | |
5 | do_something(block[i]+j); |
6 | if ( ++j < 16 ) |
7 | return; |
8 | j=0; |
9 | if ( ++i < sizeof(block)/sizeof(*block) ) |
10 | return; |
11 | i=0; |
12 | }
|
:
Bearbeitet durch User
1 | #include "stdafx.h" |
2 | |
3 | unsigned char cnt = 0x10; |
4 | |
5 | int ISR() |
6 | {
|
7 | unsigned char address; |
8 | |
9 | address = cnt & 0x0F; |
10 | if( !( cnt & 0x10 ) ) // LSB vom 'Block-Nibble' ungerade |
11 | address += ( cnt & 0xF0 ) >> 1; |
12 | |
13 | cnt++; |
14 | if( cnt == 0x90 ) |
15 | cnt = 0x10; |
16 | |
17 | return address + 1; |
18 | }
|
19 | |
20 | |
21 | int main(int argc, char* argv[]) |
22 | {
|
23 | for( int j = 0; j < 10; j++ ) { |
24 | for( int i = 0; i < 16; i++ ) { |
25 | printf( "%2d ", ISR() ); |
26 | }
|
27 | printf( "\n" ); |
28 | }
|
29 | |
30 | return 0; |
31 | }
|
1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
2 | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
4 | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
6 | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
8 | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
10 | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
Sollte noch kürzer gehen:
1 | ISR() |
2 | {
|
3 | static int i=0, j=0, block[] = { 1, 17, 1, 33, 1, 49, 1, 65 }; |
4 | |
5 | do_something(block[i]+j); |
6 | ++j < 16 || (j=0, ++i < sizeof(block)/sizeof(*block) || i=0); |
7 | }
|
Noch kurz eine Variante ohne if, wenn es etwas kryptischer sein soll ;)
1 | int getID(int input) { |
2 | return 1 + (input & 0x0f) + (((input & 0x10) >> 4) * (((input & 0xe0) + 0x20) >> 1)); |
3 | }
|
input = 0 .. 127 output = wie oben
Ähnlich wie Arc Nets Vorschlag (und genauso kryptisch ;-)), aber zusätzlich ohne die Multiplikation. Außerdem muss die Zählvariable nicht auf 0..127 begrenzt werden, man kann sie einfach bis zum Wrap-Around durchlaufen lassen:
1 | void isr(void) { |
2 | static unsigned char i; |
3 | do_something((i&15|~(i&16)+1&(i>>1&48)+16)+1); |
4 | i++; |
5 | }
|
:
Bearbeitet durch Moderator
Leute, Leute, das geht ja schnell hier ;) Ich werde heute oder in den nächsten Tagen die Lösungen testen, dann werde ich berichten. Nochmal ein großes Dankeschön an alle
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.