Forum: PC-Programmierung C-Aufgabe: bedingtes Inkrementieren


von nga (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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
von nga (Gast)


Lesenswert?

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

von Martin B. (martin_b97)


Lesenswert?

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
von Mini Mem (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von libba (Gast)


Lesenswert?

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.

von Martin B. (martin_b97)


Lesenswert?

Keiner sagt das das ganze getimed sein soll. In meinem Verständnis geht 
es rein ums hochzählen.

von (prx) A. K. (prx)


Lesenswert?

Martin B. schrieb:
> Keiner sagt das das ganze getimed sein soll.

Doch. Er sagt das.

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Lesenswert?

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
von nga (Gast)


Lesenswert?

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

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Lesenswert?

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
von nga (Gast)


Lesenswert?

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

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Lesenswert?

Ja das ist richtig. Speicherintensiv, aber flexibel.
Und einfach zu verstehen :-)

Gruß

Olaf

von Martin B. (martin_b97)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

Olaf Dreyer schrieb:
> Speicherintensiv,

Ach wo. ROM pur geht auch:
  const _flash char aSlaves[] = ...
wenn leidlich aktueller avr-gcc.

: Bearbeitet durch User
von Mini Mem (Gast)


Lesenswert?

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.

von Mini Mem (Gast)


Lesenswert?

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

von DirkB (Gast)


Lesenswert?

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)

von 0815 (Gast)


Lesenswert?

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
  }

von (prx) A. K. (prx)


Lesenswert?

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
von (prx) A. K. (prx)


Lesenswert?

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
von nga (Gast)


Lesenswert?

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

von Tom (Gast)


Angehängte Dateien:

Lesenswert?

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);
}

von Noch einer (Gast)


Lesenswert?

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
}

von Tom (Gast)


Lesenswert?

> bool use_first = ! (i%16);
muss natürlich ein & sein, auch im Text.

von Andreas R. (daybyter)


Lesenswert?

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.

von Noch einer (Gast)


Lesenswert?

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
}

von A. H. (ah8)


Lesenswert?

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.

von Andreas R. (daybyter)


Lesenswert?

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

von A. H. (ah8)


Lesenswert?

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
von Karl H. (kbuchegg)


Lesenswert?

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

von A. H. (ah8)


Lesenswert?

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
}

von Arc N. (arc)


Lesenswert?

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

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ä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
von N. G. (newgeneration) Benutzerseite


Lesenswert?

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
Noch kein Account? Hier anmelden.