Forum: Mikrocontroller und Digitale Elektronik union verwenden in c


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von R. F. (rfr)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

bei der Programmierung eines AT tiny 26 tritt eine unerwartete 
Speicherknappheit auf. Zur Behebung sollte ich folgendes Konstrukt in 
eine union ändern:
1
// VHIT, (= vom Hirn ins Terminal, also ohne Test)
2
3
unsigned char ErgebnisH, ErgebnisL;
4
uint result;
5
6
// Ergenbis H + L sind bereits mit Werten gefüllt, 
7
8
result = ErgebnisH << 8 + ErgebnisL;

-----------------------------------------------------
Mit der union würde ich 2 bytes sparen. ich habe aber keine Ahnung, wie 
man damit umgeht.

Kann mir jemand das zeigen?

Gruss + Dank

Robert

[Mod.: [c]-Tags eingefügt.]

: Bearbeitet durch Moderator
von Achim M. (minifloat)


Bewertung
0 lesenswert
nicht lesenswert
so?
1
typedef union
2
{
3
uint8_t bytes[2];
4
uint16_t word;
5
} my_union_t;
6
7
my_union_t foo;
8
9
foo.bytes[0] = 0xAAu;
10
foo.bytes[1] = 0x55u;

mfg mf

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


Bewertung
2 lesenswert
nicht lesenswert
R. F. schrieb:
> Mit der union würde ich 2 bytes sparen.

Nur, wenn du vorher analysiert hast, dass der Compiler das auch 
tatsächlich getrennt ablegt.

Je nachdem, wie du die Variablen deklariert/definiert hast, kann es auch 
gut sein, dass du rein gar nichts sparst, weil der Compiler schon genau 
das tut, was du mit der Union erreichen möchtest.

Als erstes solltest du also anfangen, die Speicherfresser zu ermitteln.

Ansonsten:
1
union // unportable!
2
{
3
  uint16_t word16;
4
  uint8_t byte8[2];
5
}
6
combiner;
7
8
   //
9
   combiner.byte8[0] = lowbyte;
10
   combiner.byte8[1] = highbyte;
11
   use(combiner.word16);

von Frank M. (ukw) (Moderator) Benutzerseite


Bewertung
1 lesenswert
nicht lesenswert
Alternative zu Jörgs Array:
1
#include <stdio.h>
2
3
union
4
{
5
    struct
6
    {
7
        unsigned char l;
8
        unsigned char h;
9
    } ergebnis;
10
    unsigned int result;
11
} Ergebnis;
12
13
int main ()
14
{
15
    Ergebnis.ergebnis.h = 42;
16
    Ergebnis.ergebnis.l = 22;
17
18
    printf ("%d\n", Ergebnis.result);
19
}

Damit wird der Inhalt von Low- und High-Byte besser sichtbar.

Vielleicht postest Du mal den gesamten Code. Da lässtg sich vielleicht 
noch etwas an anderen Stellen optimieren.

: Bearbeitet durch Moderator
von Achim M. (minifloat)


Bewertung
1 lesenswert
nicht lesenswert
Jörg W. schrieb:
> unportable!

Dessen sollte man sich halt bewusst sein. Je nach Endianness kommt bei
printf("%2x", combiner.word16);
verschiedenes raus.

mfg mf

PS @mods. Gibt es da nicht einen Artikel, den man verlinken kann?

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Bewertung
2 lesenswert
nicht lesenswert
Achim M. schrieb:
> Dessen sollte man sich halt bewusst sein. Je nach Endianness kommt bei
> printf("%2x", combiner.word16);
> verschiedenes raus.

Die Endianess hat der TO vorgegeben. Er verwendet einen AVR.

von Achim M. (minifloat)


Bewertung
0 lesenswert
nicht lesenswert
Frank M. schrieb:
> Endianess hat der TO vorgegeben.

Und morgen löst er dasselbe Problem auf einer anderen Maschine und 
wundert sich.

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


Bewertung
1 lesenswert
nicht lesenswert
Achim M. schrieb:
> Und morgen löst er dasselbe Problem auf einer anderen Maschine und
> wundert sich.

Wobei man ehrlich sein muss: eine big-endian-Maschine muss man dieser 
Tage schon mächtig suchen. Klar können die diversen RISCs das oft von 
Haus aus als Option; betrieben werden sie letztlich alle als little 
endian. Ich habe hier noch 'ne UltraSPARC rumstehen, um sowas mal real 
testen zu können, aber die Staubschicht da drauf ist schon ziemlich dick 
:), aktuell ist meiner Erinnerung nach nur noch PowerPC relevant. (Es 
gibt auch noch neuere UltraSPARCs, aber mit der Übernahme von Sun durch 
Oracle ist der Laden ja ziemlich weit weg von Otto Normalnutzer.)

von Frank M. (ukw) (Moderator) Benutzerseite


Bewertung
1 lesenswert
nicht lesenswert
Achim M. schrieb:
> Frank M. schrieb:
>> Endianess hat der TO vorgegeben.
>
> Und morgen löst er dasselbe Problem auf einer anderen Maschine und
> wundert sich.

Du meinst, der TO sucht sich als nächstes den µC mit Big Endianess und 
dem kleinstmöglichen Speicher, damit er dasselbe Problem wieder hat? Ich 
werde nie verstehen, warum man sich immer mit Speicherplatz quälen muss. 
Der nächstgrößere µC hat meist den doppelten Speicher und kostet 
unwesentlich mehr.

Aber der Vollständigkeit halber hier die portable Version:
1
union
2
{
3
    struct
4
    {
5
#if __BYTE_ORDER == __LITTLE_ENDIAN
6
        unsigned char l;
7
        unsigned char h;
8
#else
9
        unsigned char h;
10
        unsigned char l;
11
#endif
12
    } ergebnis;
13
    unsigned int result;
14
} Ergebnis;
15
16
int main ()
17
{
18
    Ergebnis.ergebnis.h = 42;
19
    Ergebnis.ergebnis.l = 22;
20
21
    printf ("%d\n", Ergebnis.result);
22
}

P.S.
Bevor der nächste Bedenkenträger kommt:

Die Version ist nur auf Maschinen, wo "int" = 16 Bit ist, portabel. Man 
sollte daher besser "uint16_t" statt "unsigned int" verwenden. Statt der 
"unsigned char" kann man dann aus Symmetriegründen "uint8_t" nutzen.

: Bearbeitet durch Moderator
von Achim M. (minifloat)


Bewertung
-1 lesenswert
nicht lesenswert
Jörg W. schrieb:
> eine big-endian-Maschine muss man dieser Tage schon mächtig suchen.

Gefunden! Ich halte eine Maschine mit umschaltbarer Endianness gerade in 
der Hand 😉

Jörg W. schrieb:
> aktuell ist meiner Erinnerung nach nur noch PowerPC relevant

Hast du einen fahrbaren Untersatz? Da gibt es reihenweise 
Big-Endian-Maschinchen.

Nicht relevant? Ja, die Automobilindustrie in DE bald nicht mehr...

Frank M. schrieb:
> portable Version:
👍

mfg mf

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
1 lesenswert
nicht lesenswert
Achim M. schrieb:

>> eine big-endian-Maschine muss man dieser Tage schon mächtig suchen.
>
> Gefunden! Ich halte eine Maschine mit umschaltbarer Endianness gerade in
> der Hand 😉

Und, wird sie auch big endian betrieben?

Nur das ist letztlich relevant. "könnte betreiben" zählt nicht viel, wie 
ich schon schrob, das geht bei vielen.

> Jörg W. schrieb:
>> aktuell ist meiner Erinnerung nach nur noch PowerPC relevant
>
> Hast du einen fahrbaren Untersatz?

Ein Fahrrad. :-)

> Da gibt es reihenweise
> Big-Endian-Maschinchen.

Welche eigentlich?

von foobar (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Pack Zwischenergebnisse nicht in globale Variablen sondern lokal auf den 
Stack.

Btw,
> result = ErgebnisH << 8 + ErgebnisL;
Da fehlen Klammern ...

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


Bewertung
2 lesenswert
nicht lesenswert
foobar schrieb:
> sondern lokal auf den Stack

Streich das "auf den Stack".

Der Compiler wird in aller Regel Register dafür benutzen, und davon hat 
ein ATtiny26 immerhin schon beinahe so viel¹ wie RAM. :-)

¹ OK, „nur“ ein Viertel davon, aber viele Zwischenergebnisse passen da 
rein

von Frank M. (ukw) (Moderator) Benutzerseite


Bewertung
2 lesenswert
nicht lesenswert
foobar schrieb:
> Btw,
>> result = ErgebnisH << 8 + ErgebnisL;
> Da fehlen Klammern ...

Da hast Du allerdings verdammt recht.

von Achim M. (minifloat)


Bewertung
-1 lesenswert
nicht lesenswert
Jörg W. schrieb:
> Ein Fahrrad. :-)
>
>> Da gibt es reihenweise Big-Endian-Maschinchen.
>
> Welche eigentlich?

Auf dem Fahrrad bist du die Maschine, oder? Du liest Zahlen in deiner 
nativen Darreichungsform (Arabische Zahlen) auch so, dass die 
höhersignifikante Stelle zuerst da steht. Ätsch!

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


Bewertung
1 lesenswert
nicht lesenswert
Ich meinte ja, welche real so genutzten big-endian-Systeme es in Kfzs 
(auf die du wohl angespielt hast) gibt.

von A. S. (achs)


Bewertung
2 lesenswert
nicht lesenswert
ich sehe überhaupt kein sinnvolles Codefragment oder eine Operation, die 
irgendwie mit Unions besser werden könnte. Ist das Information aus einem 
anderen Thread?

von kannAllesBesser! (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Frank M. schrieb:
> union
> {
>     struct
>     {
>         unsigned char l;
>         unsigned char h;
>     } ergebnis;
>     unsigned int result;
> } Ergebnis;

> Ergebnis.ergebnis.h = 42;
> Ergebnis.ergebnis.l = 22;

Das sehen zu müssen,tut wieder richtig weh!
Schon mal was von unnamed struct gehört?

Also "ergebnis" solltet weggelassen werden!

von A. S. (achs)


Bewertung
0 lesenswert
nicht lesenswert
kannAllesBesser! schrieb:
> Schon mal was von unnamed struct gehört?
>
> Also "ergebnis" solltet weggelassen werden!

Die gibt's doch erst seit C11 ;-)

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


Bewertung
1 lesenswert
nicht lesenswert
kannAllesBesser! schrieb:
> Also "ergebnis" solltet weggelassen werden!

Es kann weggelassen werden. Gerade bei einem C-Anfänger ist es 
durchaus sinnvoll, die volle „Tippeltappeltour“ durchzuziehen, sonst 
wird die Verwirrung noch größer.

Davon abgesehen: unnamed members sind kein C99-Feature, sie sind erst 
mit C11 hinzu gekommen. Es soll ja heute sogar noch Compiler geben, die 
noch nicht einmal C99 unterstützen …

: Bearbeitet durch Moderator
von Frank M. (ukw) (Moderator) Benutzerseite


Bewertung
1 lesenswert
nicht lesenswert
kannAllesBesser! schrieb:
> Schon mal was von unnamed struct gehört?

Ja, habe ich.

Ich habe es bewusst defensiv hingeschrieben, damit der TO nicht über die 
nächste Hürde stolpert. Er scheint Anfänger zu sein, da muss man nicht 
direkt die vollen Geschütze auffahren.

Dein "Hinweis" hätte daher ruhig etwas dezenter ausfallen können. Dein 
Ton sowieso.

von B. S. (bestucki)


Bewertung
0 lesenswert
nicht lesenswert
Frank M. schrieb:
> Bevor der nächste Bedenkenträger kommt:
>
> Die Version ist nur auf Maschinen, wo "int" = 16 Bit ist, portabel. Man
> sollte daher besser "uint16_t" statt "unsigned int" verwenden. Statt der
> "unsigned char" kann man dann aus Symmetriegründen "uint8_t" nutzen.

Sowas kann nicht zu 100% portabel sein (muss es auch nicht), denn die 
(u)intXX_t Typen sind optional, weil nicht auf der hinterletzten 
Plattform verfügbar. Aber: Ein solcher Zugriff auf eine union erzeugt 
undefiniertes Verhalten. Bei den allermeisten Compilern funktioniert 
dieses "Feature" einwandfrei, man sollte sich das jedoch im Hinterkopf 
behalten.

Man kann in C legal mit einem unsigned char Zeiger in beliebigen 
Variablen herumwursteln. Schön ist das auch nicht, erzeugt aber kein 
undefiniertes Verhalten.
1
uint16_t Wert;
2
unsigned char * Byte = (unsigned char *)&Wert;
3
*Byte = 0xAB;
4
*(Byte + 1) = 0xCD;

Edit:
Für mich hört sich das nach verfrühter Optimierung durch den TO oder 
nicht eingeschalteter Optimierung des Compilers an. Also:
1. Prüfen was der Compiler draus macht => erzeugter Assemblercode
2. Prüfen, ob eine Einsparung an anderer Stelle einfacher/sinnvoller 
ist. Alle Konstanten im Flash?
3. Prüfen, welche Variablen wirklich gehalten werden müssen (Laufzeit 
vs. Speicher).

: Bearbeitet durch User
von mh (Gast)


Bewertung
0 lesenswert
nicht lesenswert
B. S. schrieb:
> Sowas kann nicht zu 100% portabel sein (muss es auch nicht), denn die
> (u)intXX_t Typen sind optional, weil nicht auf der hinterletzten
> Plattform verfügbar.
Dann informiert der Compiler, das etwas nicht stimmt. Das ist das 
zweitbeste Verhalten.

B. S. schrieb:
> Aber: Ein solcher Zugriff auf eine union erzeugt
> undefiniertes Verhalten.
Nö, nicht mit C. Für C++ würde es stimmen.

von B. S. (bestucki)


Bewertung
0 lesenswert
nicht lesenswert
mh schrieb:
> B. S. schrieb:
>> Aber: Ein solcher Zugriff auf eine union erzeugt
>> undefiniertes Verhalten.
> Nö, nicht mit C. Für C++ würde es stimmen.

Danke für die Korrektur! Ich hatte wohl zu viel C++ im Kopf. In C90 war 
es noch implementation defined, aber der TO wird hoffentlich einen etwas 
neueren Standard verwenden.

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


Bewertung
1 lesenswert
nicht lesenswert
B. S. schrieb:
> In C90 war es noch implementation defined, aber der TO wird hoffentlich
> einen etwas neueren Standard verwenden.

Implementation defined ist es natürlich immer noch. Damit ist es aber 
"defined"
(und nicht "undefined behaviour", was ja alles Mögliche sein kann). Im 
Wesentlichen muss die Implementierung dabei nur die Endianess 
definieren.

von Mark B. (markbrandis)


Bewertung
-1 lesenswert
nicht lesenswert
R. F. schrieb:
> Mit der union würde ich 2 bytes sparen.

Ja, aber für wie lange?

Der von Dir gezeigte Code hat ja einen Scope, und wenn der verlassen 
wird, wird der benötigte Speicher für die zuvor angelegten Variablen 
ErgebnisH, ErgebnisL und result doch ohnehin wieder frei. Es sei denn 
natürlich, das sind alles globale Variablen die dauerhaft Speicher 
belegen. Dann darf man sich freilich nicht wundern, wenn einem der 
Speicherplatz ausgeht ;-)

Also mir kommt es eher nicht so vor, als ob man gerade an der Stelle 
wirklich großartig was an Speicher einsparen könnte. Aber um das 
wirklich beurteilen zu können müsste man die ganze Funktion sehen.

von A. S. (achs)


Bewertung
1 lesenswert
nicht lesenswert
Mark B. schrieb:
> Der von Dir gezeigte Code hat ja einen Scope

Wo sehr ihr alle Code?

Im OP gibt es eine Zuweisung plus die Definition der 3 Variablen.

Das kann kein Konstrukt sinnvoll reduzieren.

Hätte er nur "b=a;" gepostet, käme doch auch niemand auf die Idee zu 
sagen, b kann dann entfallen.

von Mark B. (markbrandis)


Bewertung
-1 lesenswert
nicht lesenswert
A. S. schrieb:
> Im OP gibt es eine Zuweisung plus die Definition der 3 Variablen.
>
> Das kann kein Konstrukt sinnvoll reduzieren.

Da bin ich bei Dir.

von R. F. (rfr)


Bewertung
-1 lesenswert
nicht lesenswert
Hallo,

derzeit bin ich damit beschäftigt, das Programm zu planen. Ich habe 
versucht, einige gescheite Datenstrukturen zu bestimmen, aber code 
i.S.e. Programmes gibt es derzeit nicht.

Das Programm soll eine tastatur auslesen, die max. 8*8 Zeilen/Spalten 
haben (eine Erweiterung später ist möglich).

Mit der Definition

volatile * const uchar8_t Zeile = PORTA;
volatile * const uchar8_t Spalte  = PORTB;

sind die Zeilen/Spaltenanschlüsse (im ROM) gespeichert. So kann man 
diese einfach auslesen.

Derzeit ist keine zusätliche Funktion angedacht, denkbar sind her 
Fehlkontakte der Druck mehrerer Tasten gleichzeitig usw. Eine Codierung 
ist nicht vorgesehen, es werden nur die Scancodes übertragen. Vorgesehen 
ist aber eine Implementation von Shift oder CRTL.

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Lass das mal mit den Unions. Das ist so ein typisches Stilmittel, mit 
dem man sich eher ins eigene Knie schießt, als etwas gutes zu erreichen. 
Man hätte sie nie einführen sollen (wie so viele andere Dinge in C/C++ 
auch).

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


Bewertung
1 lesenswert
nicht lesenswert
R. F. schrieb:
> Mit der Definition
> volatile * const uchar8_t Zeile = PORTA;
> volatile * const uchar8_t Spalte  = PORTB;
>
> sind die Zeilen/Spaltenanschlüsse (im ROM) gespeichert.

Nö.

Erstens hast du da auf der linken Seite einen Zeigertypen, auf der 
rechten Seite aber eine 8-Bit-Ganzzahl (uint8_t) als Datentyp. Diese 
Initialisierung hat so keinen Sinn, und sie verursacht vermutlich auch 
eine Warnung.

Zweitens benutzt du in beiden Fällen das Ausgangsregister des Ports. 
Wenn du eine Tastatur einlesen willst, dann solltest du wohl aber auf 
einer von beiden Seiten das Eingangregister der Pins (PINA oder PINB) 
lesen um zu erfahren, welche Tasten da gedrückt sind.

Schließlich und endlich, vergiss nicht, dass mechanische Tasten 
entprellt werden müssen.

Um mit einem ATtiny26 eine 8x8-Tastatur direkt mit den Pins abzufragen, 
musst du alle 16 IO-Pins benutzen – einschließlich des Reset-Pins, 
dessen Reset-Funktion du folglich per Fuse wegdefinieren musst. Danach 
ist der Controller nicht mehr per ISP programmierbar. Zu guter Letzt: 
wohin mit den ausgelesenen Daten? Sind ja schon alle Pins vergeben …

Bist du dir völlig sicher, dass der Controller die adäquate Wahl für 
dein Vorhaben ist? Ein ATmega8 / ATmega<X>8 (X = 4, 8, 16, 32) wäre da 
wohl deutlich praktikabler, und dank des größeren RAMs müsstest du dir 
nicht mit deinen offensichtlich auch nur geringen C-Kenntnissen bereits 
in so einer frühen Phase des Projekts den Kopf über einzelne 
einzusparende Bytes zerbrechen, sondern könntest erst einmal loslegen 
und überhaupt eine Funktion hinzubekommen.

OK, man könnte einen 74HC138 benutzen und die 8 Zeilenleitungen mit 3 
Pins codieren, dann blieben außer Reset noch 4 weitere Pins frei. Aber 
das nimmt sich weder von Platzbedarf noch finanziell dann nennenswert 
was zu den genannten Controller-Alternativen.

von A. S. (achs)


Bewertung
1 lesenswert
nicht lesenswert
R. F. schrieb:
> Hallo,
>
> derzeit bin ich damit beschäftigt, das Programm zu planen. Ich habe
> versucht, einige gescheite Datenstrukturen zu bestimmen, aber code
> i.S.e. Programmes gibt es derzeit nicht.

Darf man dann fragen, wie Dein Background ist? Also hast Du schonmal C 
programmiert oder irgendwas mit µC gemacht?

Wie Du hier siehst, sind abstrakte Fragen oft relativ schwierig, da man 
alle möglichen Fälle berücksichtigen muss. In Deinem UP war noch zu 
vermuten, dass wenigstens Du Dich schon konkret damit beschäftigt hast.

R. F. schrieb:
> eine unerwartete Speicherknappheit

von R. F. (rfr)


Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> R. F. schrieb:
>> Mit der Definition
>> volatile * const uchar8_t Zeile = PORTA;
>> volatile * const uchar8_t Spalte  = PORTB;
>>
>> sind die Zeilen/Spaltenanschlüsse (im ROM) gespeichert.
>
> Nö.
>
> Erstens hast du da auf der linken Seite einen Zeigertypen, auf der
> rechten Seite aber eine 8-Bit-Ganzzahl (uint8_t) als Datentyp. Diese
> Initialisierung hat so keinen Sinn, und sie verursacht vermutlich auch
> eine Warnung.

OK. Das wird überdacht.
>
> Zweitens benutzt du in beiden Fällen das Ausgangsregister des Ports.
> Wenn du eine Tastatur einlesen willst, dann solltest du wohl aber auf
> einer von beiden Seiten das Eingangregister der Pins (PINA oder PINB)
> lesen um zu erfahren, welche Tasten da gedrückt sind.

Gut, das werde ich ändern.
>
> Schließlich und endlich, vergiss nicht, dass mechanische Tasten
> entprellt werden müssen.
>
> Um mit einem ATtiny26 eine 8x8-Tastatur direkt mit den Pins abzufragen,
> musst du alle 16 IO-Pins benutzen – einschließlich des Reset-Pins,
> dessen Reset-Funktion du folglich per Fuse wegdefinieren musst. Danach
> ist der Controller nicht mehr per ISP programmierbar. Zu guter Letzt:
> wohin mit den ausgelesenen Daten? Sind ja schon alle Pins vergeben …

Das ist mir klar. Der tiny26 wird nur mit einer max. 5 * 5 Tastatur 
betrieben. Allerdings gibt es auch AVRs mit mehr Ausgängen.
>

> OK, man könnte einen 74HC138 benutzen und die 8 Zeilenleitungen mit 3
> Pins codieren, dann blieben außer Reset noch 4 weitere Pins frei. Aber
> das nimmt sich weder von Platzbedarf noch finanziell dann nennenswert
> was zu den genannten Controller-Alternativen.

Die Tastenleitungen haben ein oder kein Bit gesetzt, das erleichtert die 
Fehlersuche sehr.

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


Bewertung
1 lesenswert
nicht lesenswert
R. F. schrieb:

> Das ist mir klar. Der tiny26 wird nur mit einer max. 5 * 5 Tastatur
> betrieben. Allerdings gibt es auch AVRs mit mehr Ausgängen.

Dann nimm bitte einen solchen AVR mit mehr Ausgängen, um das alles zu 
testen und in Betrieb zu nehmen. Wenn du einen ATmega328 nimmst, kannst 
du mit einem x-beliebigen Arduino-Clone anfangen und hast eine 
ready-to-run Experimentierplattform mit ausreichend Ressourcen.

Wenn das dann läuft und beim Downsizing auf 5x5 nicht in den 
ATtiny26 passt, dann kannst du dir Gedanken um Mikrooptimierungen 
machen.

(Warum zum Geier™ man heutzutage noch so ein Museumsstück wie ATtiny26 
freiwillig nehmen will, erschließt sich mir allerdings nicht.)

>> OK, man könnte einen 74HC138 benutzen …

> Die Tastenleitungen haben ein oder kein Bit gesetzt, das erleichtert die
> Fehlersuche sehr.

Das kann man aber auch problemlos haben, wenn man die Leitungen direkt 
mit dem Controller steuert. Man schiebt da einfach in jedem Timerschritt 
ein 0-Bit durch:
1
void timertick(void)
2
{
3
   // do other stuff here
4
   static uint8_t outpin = 1;
5
6
   PORTA = ~outpin; // active bit is low
7
   outpin <<= 1;
8
   if (outpin == 0) // all bits shifted, start over
9
      outpin = 1;
10
11
   // ...
12
}

Das implementiert das Äquivalent des 74HC138.

von R. F. (rfr)


Bewertung
0 lesenswert
nicht lesenswert
Jörg W. schrieb:
> (Warum zum Geier™ man heutzutage noch so ein Museumsstück wie ATtiny26
> freiwillig nehmen will, erschließt sich mir allerdings nicht.)

Ich habe vor einiger zeit 40 Stck davon geschenkt bekommen.

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


Bewertung
2 lesenswert
nicht lesenswert
R. F. schrieb:
> Ich habe vor einiger zeit 40 Stck davon geschenkt bekommen.

OK, aber entwickeln würde ich zumindest nicht da drauf. Vermutlich 
würde ich sie aber auch ansonsten eher rumliegen lassen, die sind noch 
ziemlich spartanisch ausgestattet. (Ich lasse ja auch stangenweise 
andere Bauteile rumliegen, die ich mal geschenkt bekommen habe, aber für 
die ich eigentlich auch nur keine Verwendung habe.)

Wofür die ATtiny26 (bzw. deren Nachfolger ATtiny261/461/861) wirklich 
gut sind ist genau das, wofür sie mal designt worden sind: 
PWM-Generierung mit der Highspeed-PLL als Takt.

von Peter D. (peda)


Bewertung
2 lesenswert
nicht lesenswert
R. F. schrieb:
> Mit der union würde ich 2 bytes sparen.

Sowas nennt sich Mikrooptimierung, d.h. wenig Effekt im Vergleich zum 
Aufwand. Die 2 Bytes werden Dich nicht rausreißen.
Nimm den pinkompatiblen ATtiny861, der hat der 4-fachen RAM (512 Byte).

Wenn Du den RAM optimieren willst, mußt Du erstmal feststellen, was der 
Flaschenhals ist, also was wirklich viel RAM belegt.

von Peter D. (peda)


Bewertung
2 lesenswert
nicht lesenswert
R. F. schrieb:
> Mit der Definition
>
> volatile * const uchar8_t Zeile = PORTA;
> volatile * const uchar8_t Spalte  = PORTB;
>
> sind die Zeilen/Spaltenanschlüsse (im ROM) gespeichert. So kann man
> diese einfach auslesen.

Das genaue Gegenteil ist der Fall, es wird nicht einfacher, sondern 
komplizierter. Wenn man mit Pointern gedankenlos um sich schmeißt, 
erhöht das massiv den Codeverbrauch. Statt den Port oder Pins direkt 
einzulesen, muß man erstmal 2 Pointerregister laden. Die mit Pointern 
belegten Register erhöhen wiederum den Stackverbrauch, d.h. belegen auch 
mehr RAM.

Und ein Pointerarray ist dann quasi von hinten durch die Brust ins Auge.
Erstmal muß man einen Pointer mit dem Arraystart laden, den Index 
16bittig addieren und dann damit 2 Register indirekt laden. Diese 2 
Register wieder in ein Pointerpaar moven und schon kann man indirekt 
drauf zugreifen.
Grob geschätzt der 10-fache Code gegenüber einem IN-Befehl. Die 
PUSH/POP-Arie der zusätzlichen Register nicht mit eingerechnet.

Der AVR-GCC ist manchmal auch erstaunlich schlau. Wenn er feststellt, 
daß alle Variablen schon zur Compilezeit bekannt sind, kann er die ganze 
Arie durch ein LDS ersetzen.

: Bearbeitet durch User

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.