Forum: Mikrocontroller und Digitale Elektronik 74HC595 mit drei Pins ansteuern, Funktion


von Karl Käfer (Gast)


Lesenswert?

Hallo Experten,

ich möchte einen 74HC595 zur Ansteuerung von LEDs verwenden. Im Prinzip 
ist mir klar, was der IC macht, aber im Datenblatt bin ich über /G und 
/SCLR gestolpert. Werden diese Pins unbedingt benötigt? Oder kann ich 
die Pins einfach fest auf LOW legen, wenn ich weder das Shift Register 
klären noch die Ausgänge zurücksetzen will?

Darüber hinaus würde ich mich freuen, wenn Ihr mal über die folgende 
Funktion schauen würdet, die die Werte für die LEDs ins Schieberegister 
schieben und zuletzt die Werte in das Speicherregister übernehmen soll. 
Der uC ist ein AVR ATTiny2313 mit 1 MHz-Quarz. Da mir langsam der Flash 
eng wird, würde ich mich insbesondere über Tipps zur Grössenoptimierung 
freuen.
1
void shiftIt8(uint8_t val) {
2
    uint8_t i = 0;
3
    for(i=0; i<8; i++) {
4
        bit_is_set(val, i)    ? \
5
            PORTD |= (1<<PD2) : \
6
            PORTD &= ~(1<<PD2);
7
        PORTD |= (1<<PD3);
8
        asm("nop");
9
        PORTD &= ~(1<<PD3);
10
    }
11
    PORTD |= (1<<PD4);
12
    asm("nop");
13
    PORTD &= ~(1<<PD4);
14
}

Vielen Dank,
Karl

von Karl H. (kbuchegg)


Lesenswert?

beide Pins, G und SCLR sind active Low. D.h. sie machen die ihnen 
zugedachte Operation, wenn der Pin auf Low gezogen ist.

G     ist der Output Enbale
      legst du den fest auf Low, dann ist der Output auf Dauerenable.
      Genau das was du willst

SCLR  ist der Clear Eingang
      legst du den fest auf Low, geht der IC auf Dauerclear.
      NICHT das was du willst. D.h. den Pin legst du auf Vcc


Zu deiner Funktion:

Das hier
        bit_is_set(val, i)    ? \
ist eine Operation, die du nicht machen willst. Du willst auf einem AVR 
keine Bitoperationen machen, bei denen die Bitnummern keine fixen Werte 
sind. Denn dafür (für variable Werte) hat der AVR keine 
Prozessorinstruktion. D.h das ist eine 'teure' Operation.

Was du statt dessen machen willst, ist eine Maske mit einem 1 Bit und 
anstelle, dass du dir jedesmal eine neue Maske mit einer 1 an 
Bitposition i generierst, schiebst du einfach die Maske um 1 Stelle 
weiter

Die asm("nop") kannst du dir auch sparen. Der 595 ist schnell genug, 
dass du vom AVR aus 'volle Kanne' drauf los gehen kannst.

Die Line Continuation ( '\' ) brauchst du hier nicht. Die brauchst du 
nur bei Preprozessor Makros. In C darfst du zwischen 2 Worte immer 
beliebig viele Leerzeichen und Zeilenumbrüche machen.
1
void shiftIt8(uint8_t val)
2
{
3
  uint8_t mask = 0x01;
4
5
  while( mask != 0x00 )
6
  {
7
    if( val & mask )
8
      PORTD |= (1<<PD2);
9
    else
10
      PORTD &= ~(1<<PD2);
11
12
    PORTD |= (1<<PD3);
13
    PORTD &= ~(1<<PD3);
14
15
    mask <<= 1;
16
  }
17
18
  PORTD |= (1<<PD4);
19
  PORTD &= ~(1<<PD4);
20
}


> Da mir langsam der Flash
> eng wird, würde ich mich insbesondere über Tipps zur
> Grössenoptimierung freuen.

mit aktiviertem Optimizer wird die Funktion nicht besonders lang sein. 
Generell kann man da ohne konkreten Code meistens wenig sagen. Lass dir 
vom Compiler ein Map-File generieren und sieh nach, welche Funktionen 
den meisten Platz brauchen.

Als Faustregel:
* mach einen großen Bogen um Gleitkommarechnerei.
* keine _delay_ms mit variablen Werten
* Optimizer aktivieren
* immer den kleinsten benötigten Datentyp nehmen
* nicht sinnlos rumcasten

Das sollte eigentlich normalerweise Programme ergeben, die schon sehr 
dicht am Optimum liegen. Ein bischen besser als der Compiler kriegt es 
ein guter Assembler Programmierer schon hin, aber die paar Prozent 
machens normalerweise nicht aus.

von Karl Käfer (Gast)


Lesenswert?

Hallo Karl-Heinz,

vielen Dank für Deine schnelle (und tolle) Antwort.

Karl Heinz Buchegger schrieb:
> Zu deiner Funktion:
>
> Das hier
>         bit_is_set(val, i)    ? \
> ist eine Operation, die du nicht machen willst. Du willst auf einem AVR
> keine Bitoperationen machen, bei denen die Bitnummern keine fixen Werte
> sind. Denn dafür (für variable Werte) hat der AVR keine
> Prozessorinstruktion. D.h das ist eine 'teure' Operation.
>
> Was du statt dessen machen willst, ist eine Maske mit einem 1 Bit und
> anstelle, dass du dir jedesmal eine neue Maske mit einer 1 an
> Bitposition 1 generierst, schiebst du einfach die Maske um 1 Stelle
> weiter

Das ist ein ausgesprochen schicker Trick, vielen Dank!

> Die asm("nop") kannst du dir auch sparen. Der 595 ist schnell genug,
> dass du vom AVR aus 'volle Kanne' drauf los gehen kannst.

Gilt das auch, wenn ich den AVR mal mit 20 MHz takte? Im Datenblatt des 
IC steht was von 59 MHz, die NOPs hatte ich eigentlich nur zur 
Sicherheit eingebaut.

> Die Line Continuation ( '\' ) brauchst du hier nicht. Die brauchst du
> nur bei Preprozessor Makros. In C darfst du zwischen 2 Worte immer
> beliebig viele Leerzeichen und Zeilenumbrüche machen.

Ich finde das so besonders lesbar, (cond) ? (1) : (0) ist ja eh kein so 
gebräuchliches Konstrukt.

>
1
> void shiftIt8(uint8_t val)
2
> {
3
>   uint8_t mask = 0x01;
4
> 
5
>   while( mask != 0x00 )
6
>   {
7
      /*...*/
8
>     mask <<= 1;
9
>   }
10
    /*...*/
11
> }
12
>

Wow. Sehr elegant und trotzdem prima lesbar!

Vielen Dank,
Karl

von Karl H. (kbuchegg)


Lesenswert?

Karl Käfer schrieb:

> Ich finde das so besonders lesbar, (cond) ? (1) : (0) ist ja eh kein so
> gebräuchliches Konstrukt.


Wenn du willst, kannst du natürlich auch den ?: operator benutzen.
1
    ( val & mask ) ? PORTD |= (1<<PD2) : PORTD &= ~(1<<PD2);

oder so formatiert
1
    ( val & mask ) ? PORTD |= (1<<PD2)
2
                   : PORTD &= ~(1<<PD2);

Der springende Punkt ist aber der: Wenn ein Statement über mehrere 
Zeilen geht, dann brich einfach um. Du brauchst dazu keinen Continuation 
Character '\'!

Den brauchst du nur in Makros


#define  TEST     PORTD |=    \
                     ( 1 << PD0 );

um dem Präprozessor anzuzeigen, dass die nächste Zeile auch noch zum 
Makro gehört. Denn für den Präprozessor hört die Zeile (! der arbeitet 
ja textbasiert(*)) eben beim Zeilenvorschub auf.

(*) stimmt nicht ganz. Eigentlich tokenbasiert. Aber er betrachtet immer 
nur 1 Zeile.

von Karl H. (kbuchegg)


Lesenswert?

Karl Käfer schrieb:

>>
1
>> void shiftIt8(uint8_t val)
2
>> {
3
>>   uint8_t mask = 0x01;
4
>>
5
>>   while( mask != 0x00 )
6
>>   {
7
>       /*...*/
8
>>     mask <<= 1;
9
>>   }
10
>     /*...*/
11
>> }
12
>>

Du bist gut, weißt du das?
Du hast problemlos das entscheidende 'wichtige' Pattern rausdestilliert.
Gefällt mir.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl Käfer schrieb:
>> Die asm("nop") kannst du dir auch sparen. Der 595 ist schnell genug,
>> dass du vom AVR aus 'volle Kanne' drauf los gehen kannst.
>
> Gilt das auch, wenn ich den AVR mal mit 20 MHz takte? Im Datenblatt des
> IC steht was von 59 MHz

Da der AVR es nicht schafft, seinen Takt zu verdreifachen, sollte
klar sein, dass es geht: selbst mit nur einem Takt pro IO-Befehl
muss der Impuls bei 20 MHz halt noch 50 ns breit werden.  Mal unter
der Annahme, dass du den 74HC595 nicht nur mit 2 V sondern mit 5 V
betreibst, liegen die 50 ns über allen vom Datenblatt geforderten
Mindestzeiten deutlich drüber.

von Sascha W. (sascha-w)


Lesenswert?

@Karl Käfer

wenn du noch weiter sparen willst, bei gleichzeitiger Erhöhung der 
Geschwindigkeit, dann kannst du auch die SPI oder das USI verwenden - 
wenn nicht schon anderweitig benutzt und die entsprechenden PIN's müssen 
natürlich auch frei sein.

Sascha

von Karl Käfer (Gast)


Lesenswert?

Hallo,

Karl Heinz Buchegger schrieb:
> Du bist gut, weißt du das?

Naja, ich gebe mir Mühe.

> Du hast problemlos das entscheidende 'wichtige' Pattern rausdestilliert.

Schon richtig -- aber mir ging es ja gerade um die effiziente und 
elegante Erzeugung der verAND-baren Bitmaske.

> Gefällt mir.

Och menno! Jetzt hatte ich wirklich gehofft, Fratzbuch wenigstens hier 
mal entkommen zu sein. ;-)

Danke (auch Euch, Jörg und Sascha!),
Karl

von H. B. (Gast)


Lesenswert?

Karl Käfer schrieb:

> Da mir langsam der Flash eng wird

Programm in Assembler schreiben, ist immer noch die platzsparendste 
Lösung.

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.