Forum: Compiler & IDEs Weihnachtsstern stürzt ab


von Christian S. (schoker)


Lesenswert?

Hallo,

bisher konnte ich alle auftretenden Fehler und Probleme selber lösen, 
indem ich einfach mal die Weiten des Internets genutzt habe um zu 
gewissen Problematiken zu recherchieren. Doch jetzt habe ich schon eine 
Woche vergeblich versucht dieses Problem zu lösen.

Zunächst einmal eine kleine Projektbeschreibung:

Ich habe einen Weihnachtsstern gebaut, welcher aus 5*8 LEDs besteht. 
Diese LEDs hängen an 5 in Reihe geschalteten Schieberegistern (74HC595, 
in welche ich das aktuelle Leuchtbild seriell reinschiebe und dann auf 
dessen Ausgänge übertrage. Das ganze wird mit einem ATTiny45 gesteuert. 
Dazu gibt es noch einen Eingang als Interrupt, um zwischen verschiedenen 
Blinkmustern zu wechseln. Deweiteren gibt es noch einen Spannungsteiler 
mit einem lichtabhängigen Widerstand, der erkennt, ob es Hell oder 
Dunkel ist. Um somit die LEDs ausschalten zu können.

Jetzt tritt folgendes Phänomen auf: Immer wenn alle LEDs ausgeschaltet 
werden sollen, dann stürzt der ATTiny45 ab und das Programm startet sich 
von vorne.
Soweit konnte ich es jedenfalls schon einschränken, denn wenn ich das 
Programm so umschreibe, dass niemals alle LEDs ausgeschaltet sind, dann 
funktioniert es einwandfrei.

Den Code der einzelnen Modi (Blinkmuster) habe ich mal bewusst entfernt, 
da dieser meiner Einschätzung und Tests nichts mit dem Problem zu tun 
hat.
Meiner Meinung nach arbeit die Funktion LEDout() nicht richtig, wenn 
diese nur Nullen an die Schieberegister schicken soll, doch ich kann mir 
überhaupt nicht erklären warum.#

Ich habe zwar noch bis Weihnachten Zeit, aber ich hoffe echt, dass mir 
hier jemand bei meinem Problem helfen kann.

Hier nun der dazugehörige Code:
1
/*
2
 * ChristmasStar.c
3
 *
4
 * Created:        19.07.2012
5
 * Latest Modification:  17.10.2012
6
 *
7
 * Author: Christian Schoknecht
8
 */ 
9
10
#include <avr/io.h>
11
#include <util/delay.h>
12
#include <avr/interrupt.h>
13
14
#define F_CPU    8000000UL
15
#define modicounter  8
16
#define SI      0
17
#define RCK      3
18
#define SCK      4
19
20
volatile char interrupt_counter = 0;
21
volatile char flag        = 0;
22
volatile uint8_t LED0      = 0x00;
23
volatile uint8_t LED1      = 0x00;
24
volatile uint8_t LED2      = 0x00;
25
volatile uint8_t LED3      = 0x00;
26
volatile uint8_t LED4      = 0x00;
27
volatile uint8_t counter    = 9;
28
volatile uint8_t day      = 0;
29
volatile uint8_t cache      = 0x00;
30
volatile uint8_t steps      = 0;
31
volatile uint8_t random_mode  = 0;
32
33
/* function prototypes */
34
void setBit(uint8_t bit);
35
void resetBit(uint8_t bit);
36
void LEDout()__attribute__((optimize(0)));
37
void resetLEDs();
38
void random_();
39
void running();
40
void circle();
41
void circle2();
42
void snail();
43
void rider();
44
void blinking();
45
void filling();
46
47
/* function to set a single bit */
48
void setBit(uint8_t bit)
49
{
50
  PORTB |= (1<<bit);
51
}
52
53
/* function to reset a single bit */
54
void resetBit(uint8_t bit)
55
{
56
  PORTB &= ~(1<<bit);
57
}
58
59
/* function for serial output */
60
/* output of the current content of the LED-Array */
61
void LEDout()
62
{
63
  uint8_t i;    // i for 8 LEDs
64
  uint8_t LEDcache0, LEDcache, LEDcache2, LEDcache3, LEDcache4;
65
  
66
  LEDcache0 = LED0;
67
  LEDcache1 = LED1;
68
  LEDcache2 = LED2;
69
  LEDcache3 = LED3;
70
  LEDcache4 = LED4;
71
  
72
    //loops for the 8 LEDs in each shift register
73
    for (i=0; i<8; i++)
74
    {
75
      // write Date to SI-PIN
76
      if(LEDcache4 >= 0x80)
77
      {
78
        setBit(SI);
79
      }
80
      else
81
      {
82
        resetBit(SI);
83
      }
84
      LEDcache4 = LEDcache4 << 1;
85
      resetBit(SCK);
86
      setBit(SCK);
87
    }  
88
    for (i=0; i<8; i++)
89
    {
90
      // write Date to SI-PIN
91
      if(LEDcache3 >= 0x80)
92
      {
93
        setBit(SI);
94
      }
95
      else
96
      {
97
        resetBit(SI);
98
      }
99
      LEDcache3 = LEDcache3 << 1;
100
      resetBit(SCK);
101
      setBit(SCK);
102
    }  
103
    for (i=0; i<8; i++)
104
    {
105
      // write Date to SI-PIN
106
      if(LEDcache2 >= 0x80)
107
      {
108
        setBit(SI);
109
      }
110
      else
111
      {
112
        resetBit(SI);
113
      }
114
      LEDcache2 = LEDcache2 << 1;
115
      resetBit(SCK);
116
      setBit(SCK);
117
    }
118
    for (i=0; i<8; i++)
119
    {
120
      // write Date to SI-PIN
121
      if(LEDcache1 >= 0x80)
122
      {
123
        setBit(SI);
124
      }
125
      else
126
      {
127
        resetBit(SI);
128
      }
129
      LEDcache1 = LEDcache1 << 1;
130
      resetBit(SCK);
131
      setBit(SCK);
132
    }
133
    for (i=0; i<8; i++)
134
    {
135
      // write Date to SI-PIN
136
      if(LEDcache0 >= 0x80)
137
      {
138
        setBit(SI);
139
      }
140
      else
141
      {
142
        resetBit(SI);
143
      }
144
      LEDcache0 = LEDcache0 << 1;
145
      resetBit(SCK);
146
      setBit(SCK);
147
    }
148
  resetBit(RCK);
149
  setBit(RCK);
150
}
151
152
// switch off all LEDs
153
void resetLEDs()
154
{
155
  LED0 = 0x00;
156
  LED1 = 0x00;
157
  LED2 = 0x00;
158
  LED3 = 0x00;
159
  LED4 = 0x00;
160
  LEDout();
161
}
162
163
// Mode 0
164
void random_()
165
{
166
...
167
}
168
169
// Mode 1
170
void running()
171
{
172
...
173
}
174
175
// Mode 2
176
void circle()
177
{
178
...
179
}
180
181
// Mode 3
182
void rider()
183
{
184
...
185
}
186
187
// Mode 4
188
void snail()
189
{
190
...
191
}
192
193
// Mode 5
194
void blinking()
195
{
196
...
197
}
198
199
// Mode 6
200
void filling()
201
{
202
...
203
}
204
205
// Mode 7
206
void circle2()
207
{  
208
...
209
}
210
211
212
int main()
213
 {
214
   DDRB = 0x19;            // 7 6 5 4   3   2    1    0
215
                    // 0 0 0 1   1   0    0    1
216
                    // - - - SCK RCK ADC1 PCINT1 SI
217
   
218
   PCMSK |= (1<<PCINT1);        // Set Pin 1 (PB1) as the pin for the interrupt
219
   GIMSK |= (1<<PCIE);        // Enable Pin Change Interrupt
220
   
221
   // Timer1 Configuration
222
   TCCR1 |= (1<<CS13) | (1<<CS12) | (1<<CS11) | (1<<CS10);  // prescale of 16384   
223
   TIMSK |= (1<<TOIE1);        // enable overflow interrupt
224
   
225
   // ADC1 Configuration
226
   ADMUX |= (1<<ADLAR);        // adjust result left: value is in ADCH
227
   ADMUX |= (1<<MUX0);        // select ADC1
228
   
229
   ADCSRA |= (1<<ADEN);        // ADC enable
230
   ADCSRA |= (1<<ADIE);        // ADC interrupt enable   
231
   
232
   sei();                // enable global interrupt
233
  
234
  while(1)
235
  {
236
    if (day == 0)
237
    {    
238
      switch(interrupt_counter)
239
      {
240
        case 0:
241
        {
242
          random_();
243
          break;
244
        }
245
        case 1:
246
        {
247
          running();
248
          break;
249
        }
250
        case 2:
251
        {
252
          circle();
253
          break;
254
        }        
255
        case 3:
256
        {
257
          rider();
258
          break;
259
        }
260
        case 4:
261
        {
262
          snail();
263
          break;
264
        }
265
        case 5:
266
        {
267
          blinking();
268
          break;
269
        }
270
        case 6:
271
        {
272
          filling();
273
          break;
274
        }
275
        case 7:
276
        {
277
          circle2();
278
          break;
279
        }
280
      }
281
    }
282
    else
283
    {
284
      resetLEDs();
285
      _delay_ms(250);
286
    }    
287
  }
288
  return 1;
289
}
290
291
292
// Interrupt service routines
293
// Interrupt external switch
294
ISR(PCINT0_vect)
295
{
296
  if (PINB & (1<<PB1))
297
  {
298
    if(interrupt_counter == modicounter-1)
299
    {
300
      interrupt_counter = 0;
301
    }
302
    else
303
    {
304
      interrupt_counter += 1;
305
    }
306
    
307
    switch(interrupt_counter)
308
    {
309
      case 0:    LED0 = 0x01;
310
      break;
311
      case 1:    LED0 = 0x02;
312
      break;
313
      case 2:    LED0 = 0x04;
314
      break;
315
      case 3:    LED0 = 0x08;
316
      break;
317
      case 4:    LED0 = 0x10;
318
      break;
319
      case 5:    LED0 = 0x20;
320
      break;
321
      case 6:    LED0 = 0x40;
322
      break;
323
      case 7:    LED0 = 0x80;
324
      break;
325
    }
326
    LED1 = LED0;
327
    LED2 = LED0;
328
    LED3 = LED0;
329
    LED4 = LED0;
330
    LEDout();
331
    _delay_ms(1000);
332
    resetLEDs();
333
    _delay_ms(500);
334
  }
335
}
336
337
// Timer interrupt
338
ISR(TIM1_OVF_vect)
339
{
340
  ADCSRA |= (1<<ADSC);   // starting ADC conversion
341
}
342
343
// ADC conversion complete interrupt
344
ISR(ADC_vect)
345
{
346
  // switching between day and night with a
347
  if (ADCH <= 0x80)
348
  {
349
    day = 1;
350
  }
351
  if (ADCH >= 0xF0)
352
  {
353
    day = 0;
354
  }
355
  
356
}

von rggre (Gast)


Lesenswert?

1,5s delay in der ISR? Nix gut. Flag setzen und ab in die main damit.

von holger (Gast)


Lesenswert?

Du benutzt

    resetLEDs();

in einer ISR und in der main Loop.
Das macht man nicht!

von bko (Gast)


Lesenswert?

Könnte auch ein Hardware Problem sein?

>Jetzt tritt folgendes Phänomen auf: Immer wenn alle LEDs ausgeschaltet
>werden sollen, dann stürzt der ATTiny45 ab und das Programm startet sich
>von vorne.
>Soweit konnte ich es jedenfalls schon einschränken, denn wenn ich das
>Programm so umschreibe, dass niemals alle LEDs ausgeschaltet sind, dann
>funktioniert es einwandfrei.

Wie sieht der Schaltplan aus, sind genug Blockkondensatoren
an den IC's speziell an den 74HC595 ?

--
[offtopic]
Irgentwie klingt der Titel nach "Ende der Welt" ;-)

von Da D. (dieter)


Lesenswert?

bko schrieb:
> --
> [offtopic]
> Irgentwie klingt der Titel nach "Ende der Welt" ;-)

Ich dachte bei dem Titel eher an eine zu schmale Fensterbank ;-)

von Christian S. (schoker)


Lesenswert?

rggre schrieb:
> 1,5s delay in der ISR? Nix gut. Flag setzen und ab in die main damit.

Das dient derzeit nur zum Testen und direktem erkennen, ob übe

bko schrieb:
> Könnte auch ein Hardware Problem sein?

> Wie sieht der Schaltplan aus, sind genug Blockkondensatoren
> an den IC's speziell an den 74HC595 ?

Das würde ich erstmal bezweifeln, denn wenn ich in der 
ResetLED()-Funktion zwei der LED-Warte auf 0x01 setze, egal welche 
davon, dann stürzt das Programm nicht ab, dann erst wieder wenn in einem 
Leuchtmuster mal alles aus muss.
Mit 16 LEDs und einem ATMega8 sowie einem 89C51 hatte es bereits 
funktioniert und die Steuerung habe ich jetzt halt nur auf 5 
Schieberegister erweitert und dann einen µController gewählt, der 
günstig ist und mir alle Funktionen liefert, die ich brauche (Also auch 
einen ADC).

Schaltplan müsste ich mal zeichnen, aber bei den 16 LEDs hat es wie 
gesagt auch ohne Blockkondensatoren wunderbar funktioniert.

von R. M. (rmax)


Lesenswert?

bko schrieb:
> Wie sieht der Schaltplan aus, sind genug Blockkondensatoren
> an den IC's speziell an den 74HC595 ?

Da würde ich die Probleme aber beim Einschalten aller LEDs erwarten, 
nicht beim Ausschalten.

von bko (Gast)


Lesenswert?

@ Christian Schoknecht
Es werden also 40-LEDs auf einmal abgeschaltet?
Dies könnte auch einen kurzen Überschwinger in der
Versorgung verursachen welche den UC stören...

Ohne Bild von Layout oder Schaltplan ist das Rätselraten...

Aber 100nF an jedem HC-MOS IC sind eigtl. üblich:
Beitrag "74hct595 100nF Kondensator"

Teste dies: Schalte nicht alle auf einmal ab,
            sondern kurz (alle 100 usec oder so)
            nacheinander jeweils z.B. 8 Stück
            und siehe was passiert.

Wenn dies geht, ists doch recht Wahrscheinlich die Versorgung

von Konrad S. (maybee)


Lesenswert?

Da Dieter schrieb:
>> [offtopic]
>> Irgentwie klingt der Titel nach "Ende der Welt" ;-)
>
> Ich dachte bei dem Titel eher an eine zu schmale Fensterbank ;-)

Ein Fall für das Astronomie-Forum von http://www.raumfahrer.net ?  ;-)

von Christian S. (schoker)


Lesenswert?

bko schrieb:
> Es werden also 40-LEDs auf einmal abgeschaltet?
> Dies könnte auch einen kurzen Überschwinger in der
> Versorgung verursachen welche den UC stören...

Nicht in jedem Fall, manchmal werden auch nur zwei oder drei einzeln 
ausgeschaltet, das hängt ja vom Blink-Modus ab. Oder momentan nach 
Verlassen der ISR werden immer 5 abgeschaltet und da hängt es sich auf.

Schaltplan liefere ich aber gleich nach.

von Christian S. (schoker)


Lesenswert?

bko schrieb:
> Teste dies: Schalte nicht alle auf einmal ab,
>             sondern kurz (alle 100 usec oder so)
>             nacheinander jeweils z.B. 8 Stück
>             und siehe was passiert.
>
> Wenn dies geht, ists doch recht Wahrscheinlich die Versorgung

Das bringt zumindest nach dem ersten Test Besserung, muss das Programm 
jetzt nur mal umschreiben, um zu sehen, ob dies überall der Fall ist.

Edit: Jap, damit funktioniert es. Okay, vielen Dank, dann kann komme ich 
jetzt weiter.

von Christian S. (schoker)


Angehängte Dateien:

Lesenswert?

So, hier jetzt der Schaltplan

von holger (Gast)


Lesenswert?

>So, hier jetzt der Schaltplan

Null Abblockkondensatoren an den ICs?
Dann ist es kein Wunder das deine Schaltung spinnt.

von Christian S. (schoker)


Lesenswert?

Okay, dann muss ich jetzt mal auf meiner Platine noch Platz suchen, bei 
den weiteren Exemplaren kann ich das Layout dann ja anpassen, so dass es 
auch gleich funktioniert.

Ich sehe gerade, dass es ja auch Sockel mit Abbklockkondensator gleich 
gibt. Okay, ich werde dann berichten, ob das ganze das Problem löst.

von Route_66 H. (route_66)


Lesenswert?

Hallo!
>  uint8_t LEDcache0, LEDcache, LEDcache2, LEDcache3, LEDcache4;

>  LEDcache0 = LED0;
>  LEDcache1 = LED1;
>  LEDcache2 = LED2;
>  LEDcache3 = LED3;
>  LEDcache4 = LED4;

Wo wird eigentlich "LEDcache1" definiert?

von Christian S. (schoker)


Lesenswert?

Das ist mir beim Übertragen wohl ein Fehler passiert.

Ich hatte nämlich noch paar unnötige Zeilen und Kommentare rausgelöscht 
und dabei alles in eine Zeile geschrieben. LEDcache soll natürlich 
LEDcache1 sein.

So Kondensatoren sind bestellt, in der größe, die noch auf meine Platine 
passt waren die nämlich bei Conrad nicht vorrätig. Ergebnisse folgen.

von Karl H. (kbuchegg)


Lesenswert?

Christian Schoknecht schrieb:
> Das ist mir beim Übertragen wohl ein Fehler passiert.
>
> Ich hatte nämlich noch paar unnötige Zeilen und Kommentare rausgelöscht

Da geht noch einiges mehr. Hast du den Kopf frei für 
Programmvereinfachungen?

von Christian S. (schoker)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Da geht noch einiges mehr. Hast du den Kopf frei für
> Programmvereinfachungen?

Prinzipiell ja, wollte bisher nur erstmal ein Programm erzeugen, dass 
auch funktioniert und das auch noch übersichtlich ist, bevor ich da 
irgendwelche Vereinfachungen reinbaue.

von Karl H. (kbuchegg)


Lesenswert?

Christian Schoknecht schrieb:
> Karl Heinz Buchegger schrieb:
>> Da geht noch einiges mehr. Hast du den Kopf frei für
>> Programmvereinfachungen?
>
> Prinzipiell ja, wollte bisher nur erstmal ein Programm erzeugen, dass
> auch funktioniert und das auch noch übersichtlich ist, bevor ich da
> irgendwelche Vereinfachungen reinbaue.


Genau darum gehts :-)
Die Vereinfachungen sind nicht von der Kategorie 'möglichst viel 
tricksen', sondern von der Kategorie:
Eine 5-zeilige Funktion ist leichter zu überblicken, zu verstehen und im 
Fehlerfall zu analysieren, als derselbe Sachverhalt auf 2 
Bildschirmseiten :-)

von Karl H. (kbuchegg)


Lesenswert?

zb.
1
ISR(PCINT0_vect)
2
{
3
  uint8_t LedCodes[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
4
5
  if (PINB & (1<<PB1))
6
  {
7
    interrupt_counter++;
8
    if(interrupt_counter == modicounter)
9
    {
10
      interrupt_counter = 0;
11
    }
12
13
    LED0 = LedCodes[modicounter];
14
15
    LED1 = LED0;
16
    LED2 = LED0;
17
    LED3 = LED0;
18
    LED4 = LED0;
19
    LEDout();
20
    _delay_ms(1000);
21
    resetLEDs();
22
    _delay_ms(500);
23
  }
24
}

von Karl H. (kbuchegg)


Lesenswert?

zb
1
void SingleLEDout( uint8_t value )
2
{
3
  uint8_t i;
4
5
  for (i=0; i<8; i++)
6
  {
7
    if(value & 0x80)
8
    {
9
      setBit(SI);
10
    }
11
    else
12
    {
13
      resetBit(SI);
14
    }
15
    value = value << 1;
16
    resetBit(SCK);
17
    setBit(SCK);
18
  }  
19
}
20
21
/* function for serial output */
22
/* output of the current content of the LED-Array */
23
void LEDout()
24
{
25
  cli();  // sicher stellen, dass auch wirklich ALLE LEDx Variablen
26
          // in sich konsistent sind!
27
28
  uint8_t LEDcache0 = LED0;
29
  uint8_t LEDcache1 = LED1;
30
  uint8_t LEDcache2 = LED2;
31
  uint8_t LEDcache3 = LED3;
32
  uint8_t LEDcache4 = LED4;
33
34
  sei();  
35
  
36
  SingleLEDout( LEDcache4 );
37
  SingleLEDout( LEDcache3 );
38
  SingleLEDout( LEDcache2 );
39
  SingleLEDout( LEDcache1 );
40
  SingleLEDout( LEDcache0 );
41
42
  resetBit(RCK);
43
  setBit(RCK);
44
}

von Christian S. (schoker)


Lesenswert?

Das ist natürlich bei gleicher Funktion wirklich deutlich kürzer.

von Karl H. (kbuchegg)


Lesenswert?

Mal ein Versuch
1
    if (day == 0)
2
    {    
3
      switch(interrupt_counter)
4
      {
5
        case 0:
6
        {
7
          random_();
8
          break;
9
        }
10
        case 1:
11
        {
12
          running();
13
          break;
14
        }
15
        case 2:
16
        {
17
          circle();
18
          break;
19
        }        
20
        case 3:
21
        {
22
          rider();
23
          break;
24
        }
25
        case 4:
26
        {
27
          snail();
28
          break;
29
        }
30
        case 5:
31
        {
32
          blinking();
33
          break;
34
        }
35
        case 6:
36
        {
37
          filling();
38
          break;
39
        }
40
        case 7:
41
        {
42
          circle2();
43
          break;
44
        }
45
      }


versus
1
    if (day == 0)
2
    {    
3
      if     ( interrupt_counter == 0 )     random_();
4
      else if( interrupt_counter == 1 )     running();
5
      else if( interrupt_counter == 2 )     circle();
6
      else if( interrupt_counter == 3 )     rider();
7
      else if( interrupt_counter == 4 )     snail();
8
      else if( interrupt_counter == 5 )     blinking();
9
      else if( interrupt_counter == 6 )     filling();
10
      else if( interrupt_counter == 7 )     circle2();
11
    }

welche Variante gefällt dir besser?
Was mir an deiner nicht gefällt: das sie extrem in die Länge gezogen 
ist, ohne das es dafür einen guten Grund gibt.

switch-case ist nicht immer in der Lesbarkeit besser.

(Und ja: manchmal darf man sich auch über Formatiervorschriften 
hinwegsetzen, WENN es dafür einen extrem guten Grund gibt. Hier ist es 
mir wichtig, dass die gedankliche Zuordnung von 0, 1, 2, 3, ... zur 
aufgerufenen Funktion schnell, leicht und einfach herzustellen ist. Denn 
im Prinzip hast du es ja hier mit einer 'Tabelle' zu tun. In der einen 
Tabellenspalte steht 'Indexnummer', in der anderen Spalte die 
aufzurufende Funktion. Genau das spiegelt aber auch diese auf den ersten 
Blick seltsame Struktur wieder. WEnn man sich die Formatierung aber mal 
genau ansieht, dann macht sie absolut Sinn.)

von Christian S. (schoker)


Lesenswert?

Wobei das ja auch so funktionieren würde:
1
if (day == 0)
2
    {    
3
      switch(interrupt_counter)
4
      {
5
        case 0: random_;  break;
6
        case 1: running();  break;
7
        case 2: circle();  break;
8
        case 3: rider();  break;
9
        case 4: snail();  break;
10
        case 5:  blinking();  break;
11
        case 6: filling();  break;
12
        case 7: circle2();  break;
13
      }
14
    }

Was allerdings nicht funktioniert hatte: Ich hatte erst LED0-4 als Array 
deklariert und dann eine Schleife in LEDout(), die LED[0] bis LED[4] 
nacheinander abarbeiten soll, und das hat nicht funktioniert, nehme an, 
dass es falsch optimiert worden ist vom Compiler.

von R. M. (rmax)


Lesenswert?

... oder die Funktionen in ein Array stecken und interrupt_counter als 
Index verwenden.

von Karl H. (kbuchegg)


Lesenswert?

Christian Schoknecht schrieb:
> Wobei das ja auch so funktionieren würde:

Ja - Genau.


Nicht falsch verstehen.
Ich bin ein absoluter Verfechter von Formatierungsvorschriften.
Aber - es gibt auch Fälle, in denen man sie 'etwas zurecht biegt' und 
dafür einen Zusatznutzen bekommt.

von Christian S. (schoker)


Lesenswert?

Ich verstehe was du meinst.

Wenn man mit einem großen Monitor arbeitet, dann stört es einen weniger, 
wenn man da ein paar Zeilen scrollen muss bzw. in Editoren die gerade 
nicht benötigten Funktionen einklappt.

Deshalb fällt es ja kaum auf. Hier ist es natürlich klar, wenn das 
jemand liest und das so lang ist, obwohl es kürzer sein könnte, dann ist 
das eventuell ärgerlich.

von Karl H. (kbuchegg)


Lesenswert?

Christian Schoknecht schrieb:
> Ich verstehe was du meinst.
>
> Wenn man mit einem großen Monitor arbeitet, dann stört es einen weniger,
> wenn man da ein paar Zeilen scrollen muss bzw. in Editoren die gerade
> nicht benötigten Funktionen einklappt.
>
> Deshalb fällt es ja kaum auf. Hier ist es natürlich klar, wenn das
> jemand liest und das so lang ist, obwohl es kürzer sein könnte, dann ist
> das eventuell ärgerlich.

Nicht nur. Es wird auch dir irgendwann sauer aufstossen, warum deine 
LEDout Funktion so dermassen in die Länge gezogen ist. Und es wird der 
Tag kommen, an dem du dich fragst, ob eigentlich alle 5 'Ausgabestufen' 
in dieser Funktion tatsächlich Buchstabe für Buchstabe identisch sind 
(abgesehen von dem einen Variablennamen) oder nicht. (Spätestens dann, 
wenn du beobachtest, dass sich SR Nummero 3 aus irgendeinem Grund anders 
verhält)

Dabei hättest du dir mit einer Funktion, die die Ausgabe für 1 SR macht, 
die dann 5 mal aufgerufen wird, von vorne herein gleich mal Zeit 
gespart. Und diese Frage wird auch nie aufgekommen.

Es ist ein Trugschluss zu glauben: "Och, das räum ich später dann schon 
irgendwann mal auf." Denn, Hand aufs Herz, ... es passiert nicht.

von R. M. (rmax)


Lesenswert?

Hier noch der Code zu meinem letzten Vorschlag. Er wirft beim 
Compilieren keine Warnungen, weiter getestet habe ich ihn aber nicht.
1
typedef void (bfunc)(void);
2
bfunc *bfuncs[] = {
3
    resetLEDs, random_, running, circle,
4
    snail, rider, blinking, filling
5
};
6
7
// ...
8
9
    if (day == 0)
10
    {
11
        bfuncs[interrupt_counter]();
12
    }

von Karl H. (kbuchegg)


Lesenswert?

Wobei man ja auch noch die Frage aufwerfen kann:
Müssen das eigentlich eigene Funktionen sein?

Ist es nicht möglich die einzelnen Muster (mit Ausnahme des 
Zufallsmusters in random) nicht in einer gemeinsamen Basis darzustellen?

(Ich will wieder mal auf ein Array raus, in dem die Abfolge der zu 
leuchtenden LEDs festgelegt ist)

Denn: dann wird auch das Hinzufügen eines neuen Musters trivial und 
beschränkt sich einfach nur auf das Hinzufügen von ein paar Einträgen in 
einem Array und dem Anpassen von ein paar #define.



:-)
Und wenn man R. Max-es Vorschlag und meine zusammennimmt, dann könnte 
man daraus auch einen gemeinsamen Nenner ziehen: Es ist oft gar nicht so 
verkehrt, anstelle von immer gleichen Sequenzen Arrays einzusetzen, wenn 
man eine einfache Möglichkeit hat, die Unterschiede einfach nur in den 
Daten festzunageln.

von Christian S. (schoker)


Lesenswert?

Da hast du natürlich recht und deshalb ist es so dann wieder 
übersichtlicher:
1
void BitImpulse(uint8_t bit)
2
{
3
  resetBit(bit);
4
  setBit(bit);
5
}
6
7
void SRout(uint8_t SR)
8
{
9
  uint8_t  LEDcache, i;
10
  LEDcache = LED[SR];
11
  
12
  for (i=0; i<8; i++)
13
  {
14
    // write Date to SI-PIN
15
    if(LEDcache >= 0x80)
16
      setBit(SI);
17
    else
18
      resetBit(SI);
19
    LEDcache = LEDcache << 1;
20
    BitImpulse(SCK);
21
  }
22
}
23
24
/* function for serial output */
25
/* output of the current content of the LED-Array */
26
void LEDout()
27
{
28
  SRout(4); SRout(3); SRout(2); SRout(1); SRout(0);
29
  
30
  BitImpulse(RCK);
31
}

von Karl H. (kbuchegg)


Lesenswert?

Christian Schoknecht schrieb:

> void SRout(uint8_t SR)
> {
>   uint8_t  LEDcache, i;
>   LEDcache = LED[SR];

Ah,
Ich sehe, du bist auf ein Array übergegangen anstelle der LED0, LED1, 
etc.

gute Entscheidung :-)


PS: Gefällt mir, das du da mitmachst.

von Christian S. (schoker)


Lesenswert?

Das hatte ich auch vorher überlegt, weil das mit einem zweidimensionalen 
Array gut machbar wäre, Zeilen für das Muster, Spalte für die einzelnen 
Schieberegister, aber allein der Modus snail() hätte dann schon 5*40 
Einträge. Von der Geschwindigkeit mag das ein enormer Vorteil sein. Der 
ist aber durch die vielen Verzögerungen in diesem Fall nicht so wichtig.

Aber bei Interesse kann ich auch gerne noch mal die Blink-Modi posten. 
Habe diese gerade auch schon der Übersichtlichkeit etwas umformatiert.
Denn mein Code ist inzwischen fast nur noch halb so lang.

von Christian S. (schoker)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ah,
> Ich sehe, du bist auf ein Array übergegangen anstelle der LED0, LED1,
> etc.
>
> gute Entscheidung :-)


Hatte ich vorher auch schon mal. Bei der Fehlersuche, warum die 
verschachtelten Schleifen nicht funktionierten, habe ich dann einzelne 
Werte daraus gemacht. Was keine Besserung brachte und hatte es bisher 
nur noch nicht zurückgeändert.

In dem Fall mit dem Array ist das Programm natürlich leichter zu 
modifzieren, wenn man irgendwann einen sechszackigen oder 4 zackigen 
Stern bauen will.

R. Max schrieb:
> Hier noch der Code zu meinem letzten Vorschlag. Er wirft beim
> Compilieren keine Warnungen, weiter getestet habe ich ihn aber nicht.typedef 
void (bfunc)(void);
> bfunc *bfuncs[] = {
>     resetLEDs, random_, running, circle,
>     snail, rider, blinking, filling
> };
>
> // ...
>
>     if (day == 0)
>     {
>         bfuncs[interrupt_counter]();
>     }

Das ist auch eine nette Idee, aber das wird mir dann gänzlich 
unübersichtlich.

von Karl H. (kbuchegg)


Lesenswert?

Christian Schoknecht schrieb:
> Das hatte ich auch vorher überlegt, weil das mit einem zweidimensionalen
> Array gut machbar wäre, Zeilen für das Muster, Spalte für die einzelnen
> Schieberegister, aber allein der Modus snail() hätte dann schon 5*40
> Einträge.

OK. Ich gesth dir natürlich zu, dass wenn es eine einfache Möglichkeit 
gibt ein Muster algorithmisch zu generieren, dann ist das sicherlich 
einfacher, als so was.

1
#define ARRAY_SIZE(x)  ((sizeof(x)/sizeof(*x))
2
struct Frame
3
{
4
  uint8_t byte[5];
5
};
6
7
struct Muster
8
{
9
  uint8_t       nrFrames;
10
  struct frame* frames;
11
};
12
13
struct Frame snailPattern[] =
14
{
15
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 },
16
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001 },
17
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000011 },
18
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000111 },
19
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00001111 },
20
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00011110 },
21
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00111100 },
22
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b01111000 },
23
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b11110000 },
24
  { 0b00000000, 0b00000000, 0b00000000, 0b00000001, 0b11100000 },
25
  { 0b00000000, 0b00000000, 0b00000000, 0b00000011, 0b11000000 },
26
  { 0b00000000, 0b00000000, 0b00000000, 0b00000111, 0b10000000 },
27
  { 0b00000000, 0b00000000, 0b00000000, 0b00001111, 0b00000000 },
28
  { 0b00000000, 0b00000000, 0b00000000, 0b00011110, 0b00000000 },
29
  { 0b00000000, 0b00000000, 0b00000000, 0b00111100, 0b00000001 },
30
  { 0b00000000, 0b00000000, 0b00000000, 0b01111000, 0b00000010 },
31
  { 0b00000000, 0b00000000, 0b00000000, 0b11110000, 0b00000100 },
32
  { 0b00000000, 0b00000000, 0b00000001, 0b11100000, 0b00001000 },
33
  { 0b00000000, 0b00000000, 0b00000011, 0b11000000, 0b00010000 },
34
  ...
35
};
36
37
struct Muster snail = { ARRAY_SIZE( snailPattern ), snailPattern };
38
39
40
...
41
42
void playIt( struct Muster* muster )
43
{
44
  uint8_t i, j;
45
46
  for( i = 0; i < muster->nrFrames; i++ )
47
  {
48
    for( j = 0; j < 5; j++ )
49
      LED[j] = muster->frames[i][j];
50
  }
51
}

> Von der Geschwindigkeit mag das ein enormer Vorteil sein.

Gar nichtg mal so sehr.
Wenn du dir das Beispiel ansiehst, dann sieht man auf einen Blick sehr 
schnell, wie das Muster eigentlich aussieht, weil man die 1-en in der 
Initialisierung gut verfolgen kann.
D.h. das Musterdesign ist viel einfacher, weil ich einfach in diesen 
(zugegebenen) Datenwust, die 1-en da hin setzte, wo ich sie haben will 
ohne lang rumwurschteln zu müssen. Im Beispiel läuft einer 4er Schlange 
eine einzelne LED nach. Algorithmisch ist das gar nicht mehr so einfach. 
Will ich dann vielleicht von der Gegenseite auch noch eine Schlange 
einlaufen lassen, dann ist das im Array einfach. Ich schreib einfach die 
1-en dort hin, wo ich sie brauche :-)

von R. M. (rmax)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Es ist oft gar nicht so verkehrt, anstelle von immer gleichen
> Sequenzen Arrays einzusetzen, wenn man eine einfache Möglichkeit
> hat, die Unterschiede einfach nur in den Daten festzunageln.

Tja, und der nächste Schritt wäre dann, die Arrays nicht mehr im Code 
unterzubringen, sondern im EEPROM, so daß man sie ändern kann, ohne den 
ganzen Controller neu zu flashen.

von Christian S. (schoker)


Lesenswert?

Karl Heinz Buchegger schrieb:
> OK. Ich gesth dir natürlich zu, dass wenn es eine einfache Möglichkeit
> gibt ein Muster algorithmisch zu generieren, dann ist das sicherlich
> einfacher, als so was.

Für die snail()-Funktion in diesem Fall schon:
1
// Mode 4
2
void snail()
3
{
4
  if(LED[4] == 0xFF)
5
  {
6
    LED[0] = 0x01; LED[1] = 0x00; LED[2] = 0x00; LED[3] = 0x00; LED[4] = 0x00; 
7
  }
8
  if(LED[4] == LED[0])
9
  {
10
    LED[0] = (LED[0]*2)+1;
11
  }  
12
  else
13
  {
14
    LED[4] = LED[3]; LED[3] = LED[2]; LED[2] = LED[1]; LED[1] = LED[0];
15
  }
16
  LEDout();
17
  _delay_ms(100);
18
}

von Karl H. (kbuchegg)


Lesenswert?

> Aber bei Interesse kann ich auch gerne noch mal die Blink-Modi posten.

Ein simples Wechselblinken würde dann so aussehen (neuer Modus)
1
struct Frame blink4Pattern[] =
2
{
3
  { 0b00000011, 0b00000011, 0b00000011, 0b00000011, 0b00000011 },
4
  { 0b00001100, 0b00001100, 0b00001100, 0b00001100, 0b00001100 },
5
  { 0b00110000, 0b00110000, 0b00110000, 0b00110000, 0b00110000 },
6
  { 0b11000000, 0b11000000, 0b11000000, 0b11000000, 0b11000000 },
7
};
8
9
struct Muster blink4 = { ARRAY_SIZE( blink4Pattern), blink4Pattern};

Noch der PlayIt Funktion zu gegebener Zeit einen Pointer auf blink4 
übergeben und fertig ist ein neues Muster.

Und da fängt jetzt die zugegebenermassen etwas kompliziertere 
Infrastruktur für dich zu arbeiten an - neue Muster hinzufügen ist 
plötzlich einfach geworden.

von Karl H. (kbuchegg)


Lesenswert?

Christian Schoknecht schrieb:
> Karl Heinz Buchegger schrieb:
>> OK. Ich gesth dir natürlich zu, dass wenn es eine einfache Möglichkeit
>> gibt ein Muster algorithmisch zu generieren, dann ist das sicherlich
>> einfacher, als so was.
>
> Für die snail()-Funktion in diesem Fall schon:
Ah. du hast das anders aufgebaut.
Pro Aufruf ein Musterschritt - sehr vernünftig.

Dann eben
1
struct Muster
2
{
3
  uint8_t       nrFrames;
4
  struct frame* frames;
5
  uint8_t       actualFrame;
6
};
7
8
9
void playIt( struct Muster* muster )
10
{
11
  uint8_t j;
12
13
  muster->actualFrame++;
14
  if( muster->actualFrame >= muster->nrFrames )
15
    muster->actualFrame = 0;
16
17
  for( j = 0; j < 5; j++ )
18
    LED[j] = muster->frames[muster->actualFrame][j];
19
20
  LEDout();
21
  _delay_ms(100);
22
}

von Christian S. (schoker)


Lesenswert?

Ich frage mich gerade nur, warum du nicht gleich ein zweidimensionales 
Array statt einer Struktur benutzt. Ich sehe darin jetzt keinen Vorteil.
1
Muster[4][5] = {
2
    {0x03,0x03,0x03,0x03,0x03},
3
    {0x0C,0x0C,0x0C,0x0C,0x0C},
4
    {0x30,0x30,0x30,0x30,0x30},
5
    {0xC0,0xC0,0xC0,0xC0,0xC0}};

Beziehungsweise dann auch in der Bitdarstellung.

Denn so kann man nun in Abhängigkeit von static Laufvariablen auch in 
der Funktion durchlaufen lassen.


Karl Heinz Buchegger schrieb:
>> Für die snail()-Funktion in diesem Fall schon:
> Ah. du hast das anders aufgebaut.
> Pro Aufruf ein Musterschritt - sehr vernünftig.

Ja klar, damit nach einem Interrupt sofort in den nächsten Modus 
gewechselt wird, ohne dieses Muster zu Ende laufen lassen zu müssen.

Allerdings funktioniert das mit festen Werten auch. Allerdings braucht 
man dann eventuell noch zwei Laufvariablen.

In dem Fall, dass alle 5 SR gleiche Inhalte bekommen, reicht natürlich 
auch ein 1-Dimensionales Array und die Werte werden nur kopiert.

von Karl H. (kbuchegg)


Lesenswert?

Christian Schoknecht schrieb:
> Ich frage mich gerade nur, warum du nicht gleich ein zweidimensionales
> Array statt einer Struktur benutzt. Ich sehe darin jetzt keinen Vorteil.

Weil ich gerne das Array zusammen mit der Arraygröße in einer STruktur 
haben möchte. Dann hab ich alles beisammen, was für ein Muster relevant 
ist. Da könnte zb noch für jedes Muster eine spezifische 
Verzögerungszeit dazukommen oder wie in der letzten Modifikation die 
Schrittnummer des gerade aktuellen Schrittes.

Meine Sicht der Dinge ist eben die, dass zu der vollständigen 
Beschreibung eines Musters nicht nur die auszugebenden Bytewerte 
gehören, sondern noch mehr.

> Denn so kann man nun in Abhängigkeit von Laufvariablen auch in der
> Funktion durchlaufen lassen.
Überleg dir nochmal, was das bedeutet, wenn du mehr als ein Muster hast. 
Du hast ein

uint8_t Blink1[3][5] = { ... };
uint8_t Clockwise[8][5] = { ... };
uint8_t AntiClockWise[8][5] = { ... };
uint8_t Race[12][5] = { ... };
...

jedes Muster besteht aus einer unterschiedlichen Anzahl von Schritten. 
Diese Information muss irgendwo herkommen, damit die 'Schleifen' richtig 
arbeiten können.
So, mit der Struktur, übergebe ich einen Pointer auf so ein Muster an 
die Funktion, und die Funktion hat alles was sie braucht.


Geht ja dann noch weiter.
Denn ich will ja auch in weiterer Folge, deinen 'interrupt_counter' mit 
einem Muster verheiraten. D.h. da wird es noch ein Array geben, welches 
diesen interrupt_counter in ein Muster übersetzt.

struct Muster* alleMuster[] =
{
  &snail,
  &blink4,
  ...
};

und der Aufruf lautet dann
1
int main()
2
{
3
  ...
4
  sei();                // enable global interrupt
5
  
6
  while(1)
7
  {
8
    if (day == 0)
9
      // spiele einen Schritt vom momentan eingestellten aktiven Muster ab.
10
      playIt( alleMuster[ interrupt_counter ] );
11
12
    else
13
    {
14
      resetLEDs();
15
      _delay_ms(250);
16
    }    
17
  }
18
19
  return 1;
20
}


Wir nähern uns immer mehr einer allgmeinen 'Maschine', deren Fähigkeiten 
einzig und alleine durch ein paar Einträge in Arrays geregelt werden :-)

von R. M. (rmax)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Da könnte zb noch für jedes Muster eine spezifische
> Verzögerungszeit dazukommen

... oder sogar verschiedene Verzögerungszeiten für die einzelnen 
Schritte eines Musters (wie bei animierten GIFs für die einzelnen 
Frames).

> oder wie in der letzten Modifikation die
> Schrittnummer des gerade aktuellen Schrittes.

Das leutchtet mir jetzt nicht ein, denn es ist ja immer nur ein Muster 
aktiv. Warum also den aktuellen Schritt in den Mustern speichern und 
nicht in einer globalen oder statischen Variablen? Nur damit die Muster 
beim Umschalten dort weiter machen, wo man sie zuletzt weggeschaltet 
hat?

von Christian S. (schoker)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Da könnte zb noch für jedes Muster eine spezifische
> Verzögerungszeit dazukommen.

Welche bei mir nicht immer konstant ist. Die ist teilweise abhängig von 
der Anzahl der Funktionsaufrufe.

Aber über deinen Vorschlag müsste ich noch mal etwas genauer nachdenken.
Bei dir wird das Muster (mit Schrittzahl, Blinkmuster und 
Verzögerungszeit) ja quasi zu einem Objekt. Auch wenn man diese in C 
nicht hat, aber du würdest alle Eigenschaften in der Struktur abbilden, 
wenn ich das richtig verstehe.

von Karl H. (kbuchegg)


Lesenswert?

R. Max schrieb:

>> oder wie in der letzten Modifikation die
>> Schrittnummer des gerade aktuellen Schrittes.
>
> Das leutchtet mir jetzt nicht ein, denn es ist ja immer nur ein Muster
> aktiv. Warum also den aktuellen Schritt in den Mustern speichern und
> nicht in einer globalen oder statischen Variablen? Nur damit die Muster
> beim Umschalten dort weiter machen, wo man sie zuletzt weggeschaltet
> hat?

Ist nur ein Vorschlag.
Kann jeder machen wie er will :-)

von Karl H. (kbuchegg)


Lesenswert?

Christian Schoknecht schrieb:

> Aber über deinen Vorschlag müsste ich noch mal etwas genauer nachdenken.
> Bei dir wird das Muster (mit Schrittzahl, Blinkmuster und
> Verzögerungszeit) ja quasi zu einem Objekt.

Bingo!
(Na ja. Fast)

> Auch wenn man diese in C
> nicht hat, aber du würdest alle Eigenschaften in der Struktur abbilden,
> wenn ich das richtig verstehe.

Jep. Das brauch ich, damit ich in der Hauptschleife dann schreiben kann
1
  while(1)
2
  {
3
    if (day == 0)
4
      // spiele einen Schritt vom momentan eingestellten aktiven Muster ab.
5
      playIt( alleMuster[interrupt_counter] );
6
7
    else
8
    {
9
      resetLEDs();
10
      _delay_ms(250);
11
    }    
12
  }

und es passiert immer das richtige. Warum? Weil das Muster-Objekt selbst 
alles mithat, was die Funktion benötigt um es abspielen zu können. Ich 
hab quasi eine 2-teilung. Die Funktion stellt den Mechanismus - das 
Muster Objekt die Details.

von Christian S. (schoker)


Lesenswert?

Und ich hatte mir überlegt, dass die Blinkmuster-Funktion immer den 
nächsten "Blink-Schritt" aufgrund des vorherigen berechnet/zuweist.

Wobei ich deine Überlegungen gut nachvollziehen kann. Ich es aber 
durchaus herausfordernd fand einen Algorithmus für die Leuchtmuster zu 
suchen. Es gibt sicher auch bei mir Fälle, in denen es einfacher ist, 
wenn es fest codiert ist. Nur sehe ich in der algorithmischen Berechnung 
den Vorteil, wenn der Stern auf weitere Register erweitert werden 
sollte. Denn der Speicheraufwand für die festcodierten Werte steigt dann 
ja enorm an.
Allein für meine jetzigen Blinkmuster bräuchte ich schon 5*137 Byte an 
Speicher, was in etwa einem Drittel des jetzigen Programms entspricht.

Bei Erweiterung um weitere SR wächst der Bedarf allein an Speicher für 
jedes SR dann um 137 Byte und das nur bei der jetzigen Anzahl an 
Mustern. Algorithmisch sind das ein paar Speicherplätze in dem 
LED[]-Array und ein paar mehr Codezeilen, weil es ein paar mehr 
Zuweisungen geben muss.

Aber schon einmal vielen Dank für das Interesse an dem Projekt und den 
zahlreichen Hinweisen zur Optimierung.

Sobald die Kondensatoren eingetroffen sind, ich diese angelötet habe und 
der Stern tut, was er soll, werde ich dann mal ein Video erstellen und 
das hierher verlinken. Vielleicht regt das dann ja den einen oder 
anderen zum Nachbau an und dem Ausprobieren deiner Idee mit der 
konsequent umgesetzten Zustandsmaschine. Immerhin ist bald Weihnachten 
und mein Stern soll ein Geschenk werden.

von R. M. (rmax)


Lesenswert?

Christian Schoknecht schrieb:

> Nur sehe ich in der algorithmischen Berechnung den Vorteil,
> wenn der Stern auf weitere Register erweitert werden
> sollte. Denn der Speicheraufwand für die festcodierten
> Werte steigt dann ja enorm an.

Nur dann, wenn jede LED einzeln ansteuerbar sein soll (z.B. bei einem 
umlaufenden Lauflicht). Falls es aber innerhalb des Sterns bei jedem 
Schritt eines Musters periodische Wiederholungen gibt (z.B. alle Zacken 
machen das gleiche), muß nur jeweils eine Periode gespeichert und dann 
entsprechend oft in die Register geschoben werden.

von Christian S. (schoker)


Lesenswert?

Okay, es sind definitiv die Abblockkondensatoren gewesen, denn jetzt 
läuft es wie am Schnürchen. Vielen Dank für den Hinweis.

Deshalb hier mal ein Video, wie das ganze dann im Dunkeln aussieht.

http://www.youtube.com/watch?v=wExSOsv7RWU

von R. M. (rmax)


Lesenswert?

Sieht nett aus! :)

Hier noch ein Mustervorschlag: Alle LEDs sind an und es wird zufällig 
immer mal wieder eine kurz (<1s) ausgeschaltet. Mit dem richtigen Timing 
wirkt das wie ein glitzern oder funkeln.

Am besten sieht es aus, wenn die jeweilige LED nicht schlagartig 
ausgeht, sondern aus- und wieder eingeblendet wird, aber ich weiß nicht, 
ob Deine Schieberegister-Kette schnell genug ist, um auf einzelnen LEDs 
auch noch PWM zu machen.

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.