Forum: Compiler & IDEs 2x 4bit auf einen Port ausgeben


von Marcel S. (blackevil673)


Angehängte Dateien:

Lesenswert?

Hallo,

Ich arbeite im Moment an folgendem Projekt:
Durch 8 Schalter wird eine 8bit Zahl an Port0 eingegeben.
Diese 8bit Zahl wird im nächsten Schritt durch Bitmasken in zwei 4bit 
Zahlen geteilt.
Diese zwei "Tuppels" werden dann entweder (je nach Auswahl durch zweiten 
Schalterblock) addiert, subtrahiert, multipliziert oder dividiert.
Anschließend wird die Zahl dezimal mithilfe der Division durch 10 
(Zehner) und duch Division mit 10 und Rest (Einer) auf zwei 7-Segment 
Anzeigen ausgegeben.
All das funktioniert auch super. Jedoch schaffe ich es nicht, beide 
Stellen, also Zehner (4bit) und Einer (4bit) an einem einzelnen Port 
auszugeben. Ich gebe im Moment die Zehner bei P3 und die Einer bei P1 
raus. P3 benötige ich allerdings für etwas anderes.
Wie bekomme ich beide Zahlen auf einen Port, ohne dass sie sich 
gegenseitig überschreiben?

Danke im voraus.

Edit: Im Bild mit dem Code sind Zehner und Einer vertauscht

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

> Diese 8bit Zahl wird im nächsten Schritt durch Bitmasken
> in zwei 4bit Zahlen geteilt.

Das kann aber noch nicht alles gewesen sein.
Denn danach wirst du ja die oberen 4 Bits durch schieben erst mal 
zurecht geschoben haben, damit du wieder schöne Zahlen hast, mit denen 
du arbeiten kannst.

Und jetzt hast du eben das umgekehrte Problem.
Du hast 2 4-Bit Einheiten, die du zu 8 Bit zusammenführen musst, ehe du 
sie am Port ausgibst.
Also die Zehner um 4 Bit 'nach oben' schieben, noch die Einer rein-Odern 
und du hast 8 Bit, die du am Port ausgeben kannst.

PS: Was sollen diese _PNG Files sein?

: Bearbeitet durch User
von Marcel S. (blackevil673)


Lesenswert?

Das ist das Bild von der Schaltung und dem Code falls notwendig.
Ich verstehe was du meinst. Da werde ich mich gleich einmal dransetzen

Edit: So klappts jetzt:

e = ((P0 & 0b11110000) >> 4) + (P0 & 0b00001111); // Addition der 4bits
P1 = (e % 10) | ((e / 10) << 4); // Dezimale Ausgabe in 8bit :)

: Bearbeitet durch User
von Marcel S. (blackevil673)


Lesenswert?

Eine Frage hätte ich da noch.
Ich möchte, dass wenn jemand eine Zahl über 9 eingibt, also A - F (sind 
ja Hexa Displays), dass dann nicht gerechnet wird. Die Zahl darf auf dem 
Display stehen, jedoch soll nicht gerechnet werden. Dies funktioniert 
auch, jedoch muss ich nach jeder if Abfrage erneut den Wert von Port0 
berechnen, da er ja nicht wartet bis etwas gedrückt wurde, sondern dies 
jederzeit geschehen kann. Ich finde das persönlich sehr unschön und 
Verschwendung von Rechenleistung. Hat hier jemand eine bessere Idee?

von Karl H. (kbuchegg)


Lesenswert?

Marcel Schwegler schrieb:

> jederzeit geschehen kann. Ich finde das persönlich sehr unschön und
> Verschwendung von Rechenleistung.

Kriegst du vom µC-Hersteller Geld zurück?

Wenn etwas jederzeit gerechnet wird, dann muss dieses jederzeit auch die 
notwendigen Prüfungen enthalten.

Ich kann zwar dein Bild nicht sehen (poste doch bitte das nächste mal 
Programmtext auch als Text. Du brauchst einfach nur dein C-File als 
Attachment anhängen), aber wo ist da jetzt das Problem
1
...
2
  while( 1 )
3
  {
4
    Zahl1 = P0 & 0x0F;
5
    Zahl2 = ( P0 >> 4 ) & 0x0F;
6
7
    if( Zahl1 < 10 && Zahl2 < 10 )
8
    {
9
      Ergebnis = Zahl1 + Zahl2;
10
      ....
11
    }
12
    else
13
      Ergebnis = 0;
14
15
    P1 = (( Ergebnis / 10 ) << 4 ) | ( Ergebnis % 10 );
16
  }

diese Bereichsprüfung sieht für mich jetzt nicht wirklich grossartig 
problematisch aus. In Relation dazu sind die nachfolgenden DIvisionen 
das viel größere 'Problem'.

: Bearbeitet durch User
von Marcel S. (blackevil673)


Angehängte Dateien:

Lesenswert?

Im Moment blicke ich nicht ganz durch, was genau du mir vermitteln 
willst.
Ich habe mal die main.c in den Anhang gepackt.

von Karl H. (kbuchegg)


Lesenswert?

Du prüfst ja tatsächlich an allen Ecken und Enden.

Aber mal eine kurze Zwischenfrage
Warum
1
      e = ((P0 & 0b11110000) >> 4) + (P0 & 0b00001111);  // Beide Tupples addieren
verwendest du hier eigentlich P0 und nicht die beiden Tupel-Variablen, 
die du vorhin schon eingelesen und geprüft hast?

Eben.

: Bearbeitet durch User
von Marcel S. (blackevil673)


Lesenswert?

Haha sehr gut. Stimmt, dass mit den Variablen kam erst später dazu, da 
hatte ich noch gar nicht dran gedacht

Edit: Naja, dann klappts nicht mehr xD

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

> Eben
1
...
2
3
  while( 1 )
4
  {
5
6
     berechne_tupel();
7
8
     if( tup1 < 10 && tup2 < 10 )
9
     {
10
        // die beiden Zahlen sind gültig. Mach mit ihnen was es zu machen
11
        // gibt
12
        if(!P20 && !P21)
13
          e = tup1 + tup2;
14
15
        else if( P20 && !P21 )
16
          e = tup2 - tup1;
17
18
        else if
19
          ....
20
      }
21
      else
22
        e = 0;
23
24
      P1 = ......

du musst anfangen ein bischen zu schauen, wie du Dinge ineinander 
schachteln kannst, welche Dinge du schon geprüft hast, welche WErte du 
bereits wo berechnet hast, etc.

WEnn du nach einem Aufruf von berechne_tupel die Eingangslkeitungen 
bereits in 2 Variablen aufgeteilt hast, dann willst du die auch weiter 
verwenden und nicht erneut auf P0 direkt losgehen.

Genauso mit dem Ergebnis.
Du willst nicht an allen Ecken und Enden verstreut den Code haben, der 
die Zehner und Einer auf den P1 rauspfriemelt. Das braucht es nur an 
einer einzigen Stelle (bzw auch P3). Alles was du tun musst ist, in e 
(deinem Ergebnis) einen entsprechenden Wert zu hinterlassen und diese 
eine Codestelle gibt den dann aus.

von Karl H. (kbuchegg)


Lesenswert?

Marcel Schwegler schrieb:

> Edit: Naja, dann klappts nicht mehr xD

Dann hast du wo einen Fehler gemacht.

von Marcel S. (blackevil673)


Angehängte Dateien:

Lesenswert?

Hier die überarbeitete Version. Kannst du noch etwas beanstanden?

von Karl H. (kbuchegg)


Lesenswert?

>
1
>       if(P3 == 0x0E)P3 = 0x00;          // Error abschalten falls an
2
>

solche Dinge sind zwar immer lieb gemeint, aber im Grunde recht sinnlos. 
Du brauchst hier nicht prüfen, ob der Error eingeschaltet ist. Schalte 
ihn einfach ab.
Wenn er schon abgeschaltet war, dann passiert nichts weiter. Mehr als 
abschalten kannst du ihn nicht
1
      P3 = 0x00;


Fass doch hier
1
      else if(P20 && P21)e = tup1 / tup2;      // Beide Tupples dividieren
2
3
      if(P20 && !P21 && e > 9) P1 = 0x00;      // Falls Subtr. groseser 9 Ergebnis null setzen
4
      else P1 = (e % 10) | ((e / 10) << 4);    // Dezimales Ergebnis an P1 ausgeben

die Dinge zusammen.
Du willst bei einer Subtraktion ein fehlerhaftes Ergebnis abfangen. Gut. 
Aber das gehört zur Subtraktion dazu! Es ist nicht schlau derartige 
Dinge ohne Grund auseinander zu reissen.
1
      if(!P20 && !P21)
2
        e = tup1 + tup2;      // Beide Tupples addieren
3
    
4
      else if(P20 && !P21) {
5
        e = tup1 - tup2;    // Beide Tupples subtrahieren
6
        if( e > 9 )
7
          e = 0; 
8
      } 
9
10
      else if(!P20 && P21)
11
        e = tup1 * tup2;     // Beide Tupples multiplizieren    
12
          
13
      else if(P20 && P21)
14
        e = tup1 / tup2;      // Beide Tupples dividieren
15
16
      P1 = (e % 10) | ((e / 10) << 4);    // Dezimales Ergebnis an P1 ausgeben

Deine Kommentare:
1
       e = tup1 + tup2;      // Beide Tupples addieren
ach echt? Das hätte ich mir nicht gedacht, dass hier die beiden Tupels 
addiert werden, wenn ich mir den Code angesehen habe.

Erzähl mir nichts in einem Kommentar, was ich nicht auch im Code sehen 
kann. Jeder Halbblinde kann anhand von
1
       ... tup1 + tup2;
sehen, dass hier addiert wird. Das brauchst du nicht kommentieren. Denn: 
Im besten Fall erzählt mir der Kommentar nichts neues (weil es eh schon 
im Code steht), im schlimmsten Fall ist aber der Kommentar falsch (weil 
er bei Codeänderungen nicht nachgezogen wurde!).

Als Faustregel: Im Code steht das WIE, im Kommentar steht das WARUM.

: Bearbeitet durch User
von Marcel S. (blackevil673)


Lesenswert?

1
 #include <htc.h>
2
3
unsigned char e, tup1, tup2;              // Variablen
4
5
void main()
6
{
7
  P3 = 0x00;                      // Error duch Voreinst. vermeiden
8
9
  while(1)
10
  {  
11
    tup1 = P0 & 0b00001111;              // 8bit maskieren,
12
    tup2 = (P0 & 0b11110000) >> 4;          // da 2x 4bit benoetigt
13
  
14
    if(tup1 < 10 && tup2 < 10)            // A - F sind keine zulaessigen Werte
15
    {
16
      P3 = 0x00;                  // Error abschalten, da zulaessige Werte
17
      
18
      if(!P20 && !P21)e = tup1 + tup2;      // Addition
19
    
20
      else if(P20 && !P21){e = tup1 - tup2;    // Subtraktion
21
      if(e > 9){e = 0;
22
      P3 = 0x0E;}}                // Error anzeigen, da falsches Ergebnis            
23
24
      else if(!P20 && P21)e = tup1 * tup2;     // Multiplikation  
25
          
26
      else if(P20 && P21)e = tup1 / tup2;      // Divison
27
28
      P1 = (e % 10) | ((e / 10) << 4);      // Dezimales Ergebnis an P1 ausgeben
29
    }
30
    else 
31
    {
32
      P1 = 0x00;                  // Da Error, Ergebnis ausblenden
33
      P3 = 0x0E;                  // Error anzeigen
34
    }
35
  }  
36
}

Ein paar Kommentare hab ich für mich und die Allgemeinheit stehen 
lassen, zur Orientierung. Den Rest hab ich jetzt mal versucht ein wenig 
zu erklären.
Ich möchte auch noch gerne einen Error ausgeben, wenn die Subtraktion 
"falsch" war. Das ist soweit auch im Code ersichtlich denke ich. 
Allerdings flackert dann die Anzeige, da ich ja im gleichen Block auch 
den Error jedesmal zurücksetze. Mir fällt da im Moment wirklich keine 
schicke Lösung ein. Ansonsten ist das Programm erst einmal fertig. Ich 
danke dir für die wirklich gute Hilfe und die Tipps

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Marcel Schwegler schrieb:


> Ich möchte auch noch gerne einen Error ausgeben, wenn die Subtraktion
> "falsch" war. Das ist soweit auch im Code ersichtlich denke ich.
> Allerdings flackert dann die Anzeige, da ich ja im gleichen Block auch
> den Error jedesmal zurücksetze. Mir fällt da im Moment wirklich keine
> schicke Lösung ein.

Dann darf das Abschalten eben nicht mehr generell passieren, sondern 
nur, wenn du sicher bist, dass es keinen Fehler gab/gibt. Da gibt es 
jetzt mehrere Möglichkeiten. Zb das hier
1
        if(!P20 && !P21) {
2
          e = tup1 + tup2;      // Addition
3
          P3 = 0x00;
4
        }
5
6
        else if(P20 && !P21) {
7
          e = tup1 - tup2;    // Subtraktion
8
          if(e > 9) {
9
            e = 0;
10
            P3 = 0x0E;
11
          }
12
          else
13
            P3 = 0x00;
14
        }
15
16
        else if(!P20 && P21) {
17
          e = tup1 * tup2;     // Multiplikation
18
          P3 = 0x00;
19
        }
20
21
        else if(P20 && P21) {
22
          e = tup1 / tup2;      // Divison
23
          P3 = 0x00;
24
        }

ist eine Möglichkeit.
Aber man muss zugeben .. etwas langatmig.
Etwas kürzer wird es mit einer Hilfsvariablen
1
        haveError = 0;
2
3
        if(!P20 && !P21)
4
          e = tup1 + tup2;      // Addition
5
6
        else if(P20 && !P21) {
7
          e = tup1 - tup2;    // Subtraktion
8
          if(e > 9) {
9
            e = 0;
10
            haveError = 1;
11
          }
12
        }
13
14
        else if(!P20 && P21)
15
          e = tup1 * tup2;     // Multiplikation
16
17
18
        else if(P20 && P21)
19
          e = tup1 / tup2;      // Divison
20
21
        if( haveError )
22
          P3 = 0x0E;
23
        else
24
          P3 = 0x00;
25
26
        ...

hat es eigentlich einen speziellen Grund, dass du laufend auf immer 
wieder noch blödere Formatierungen verfällst, wie du die { - } bzw. 
einzelne Statements anordnen kannst?

Gewöhn es dir an
1
        if( Ausdruck )
2
          abhängige Anweisung

das ist nicht einfacher oder änderungsfreundlicher, wenn du die 
abhängige Anweisung in die selbe Zeile wie das if quetscht. Ein Programm 
wartbar zu haben, hat sehr viel damit zu tun es optisch so zu gestalten, 
dass man Dinge, Zusammenhänge auf einen Blick erfassen kann. Dazu gehört 
auch, welche Anweisungen von welchen anderen Anweisungen abhängig sind.
Hier
1
      else if(P20 && !P21){e = tup1 - tup2;    // Subtraktion
2
      if(e > 9){e = 0;
3
      P3 = 0x0E;}}                // Error anzeigen, da falsches Ergebnis
kann man überhaupt nichts auf einen Blick sehen. Das muss man erst mal 
im Geiste umformatieren, um zu sehen, welche Anweisungen aufgrund 
welcher anderen Anweisungen ausgeführt werden
1
      else if(P20 && !P21) {
2
        e = tup1 - tup2;    // Subtraktion
3
        if(e > 9) {
4
          e = 0;
5
          P3 = 0x0E;
6
        }
7
      }                // Error anzeigen, da falsches Ergebnis
kann man das sehen. Jede Abhängigkeit ist eine weitere Einrückstufe. Ob 
man die { jetzt am Zeilenede macht, oder ob man sie in eine eigene Zeile 
macht, so wie hier
1
      else if(P20 && !P21)
2
      {
3
        e = tup1 - tup2;    // Subtraktion
4
        if(e > 9)
5
        {
6
          e = 0;
7
          P3 = 0x0E;
8
        }
9
      }                // Error anzeigen, da falsches Ergebnis
ist Ansichtssache und wird einem in einer Softwarefirma meistens 
vorgeschrieben. Letzteres hat den Vorteil, dass man die jeweils öffnende 
{, die zu einer schliessenden } gehört, optisch leicht finden kann (und 
umgekehrt). Aber auch die erste Variante ist nicht so schlecht, denn 
anhand der Einrückung findet man auch so, wo die öffnende { sein muss 
(sofern man sie nicht vergessen hat) und hat den Vorteil, dass der Code 
nicht so in die Länge gezogen wird.

Aber in einem sind sich alle verbreiteten Stile einig. Das hier
1
      else if(P20 && !P21){e = tup1 - tup2;    // Subtraktion
2
      if(e > 9){e = 0;
3
      P3 = 0x0E;}}                // Error anzeigen, da falsches Ergebnis
geht gar nicht. Denn da muss ich erst mal 30 Sekunden drauf starren, ehe 
mir klar wird, wie da die Verschachtelungen sind. Das ist einfach nur 
ein strukturloser Haufen Buchstaben.

: Bearbeitet durch User
von Marcel S. (blackevil673)


Lesenswert?

> Etwas kürzer wird es mit einer Hilfsvariablen
>
>
1
>         haveError = 0;
2
> 
3
>         if(!P20 && !P21)
4
>           e = tup1 + tup2;      // Addition
5
>         .........
6
>

Aber so flackert die Anzeige doch immernoch, oder hab ich jetzt einen 
Denkfehler? Beim ausprobieren hat es jedenfalls geflackert..
Ich weiß bescheid mit der Formatierung. Mache ich auch normalerweise 
nicht, jedoch wollte ich es unbedingt so schmal wie möglich halten. War 
eine blöde Idee, ich gebs zu ;)

von Karl H. (kbuchegg)


Lesenswert?

Marcel Schwegler schrieb:
>> Etwas kürzer wird es mit einer Hilfsvariablen
>>
>>
1
>>         haveError = 0;
2
>>
3
>>         if(!P20 && !P21)
4
>>           e = tup1 + tup2;      // Addition
5
>>         .........
6
>>
>
> Aber so flackert die Anzeige doch immernoch, oder hab ich jetzt einen
> Denkfehler? Beim ausprobieren hat es jedenfalls geflackert..

Wie soll da was flackern?

Edit:
Oder hast du etwa das Rücksetzen von P3 am Anfang der Schleife drinn 
gelassen? Die muss natürlich raus! P3 wird am Ende der ganzen 
Berechnungen abhängig von haveError entwedder gesetzt oder nicht 
gesetzt. Aber es kann nicht vorkommen, dass es zwischendurch auf 0 
gesetzt wird um gleich darauf wieder auf 0x0E zu gehen.

Ausserdem: das ganze läuft so schnell ab, ich bezweifle ernsthaft, dass 
du da irgendwo ein Flackern sehen kannst. Du bist ja nicht Superman.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Marcel Schwegler schrieb:
> if(!P20 && !P21)e = tup1 + tup2;      // Addition
>
>       else if(P20 && !P21){e = tup1 - tup2;    // Subtraktion
>       if(e > 9){e = 0;
>       P3 = 0x0E;}}                // Error anzeigen, da falsches
> Ergebnis
>
>       else if(!P20 && P21)e = tup1 * tup2;     // Multiplikation
>
>       else if(P20 && P21)e = tup1 / tup2;      // Divison

Das schreit förmlich nach switch/case:
1
enum{ ADD, SUB, MUL, DIV };
2
3
  switch( P2 & 0x03 ){
4
    case ADD: c = a + b; break;
5
    case SUB: c = a - b; break;
6
    case MUL: c = a * b; break;
7
    default:  c = a / b; break;
8
  }

von Marcel S. (blackevil673)


Lesenswert?

1
        haveError = 0;    <------ Wird hier nicht sofort beim naechsten 
2
                                  Durchlauf wieder der Error ausgeschaltet?
3
        if(!P20 && !P21)
4
          e = tup1 + tup2;      // Addition
5
6
        else if(P20 && !P21) {
7
          e = tup1 - tup2;    // Subtraktion
8
          if(e > 9) {
9
            e = 0;
10
            haveError = 1;     <----- Und hier bei jedem Durchlauf
11
          }                           wieder an
12
        }
13
14
        else if(!P20 && P21)
15
          e = tup1 * tup2;     // Multiplikation
16
17
18
        else if(P20 && P21)
19
          e = tup1 / tup2;      // Divison
20
21
        if( haveError )
22
          P3 = 0x0E;
23
        else
24
          P3 = 0x00;
25
26
        ...

von Karl H. (kbuchegg)


Lesenswert?

Marcel Schwegler schrieb:

>
>         haveError = 0;    <------ Wird hier nicht sofort beim naechsten
>                                   Durchlauf wieder der Error
> ausgeschaltet?


Ja, und?
macht ja nix.

Das siehst du ja optisch nicht, dass die Variable einen Wert bekommen 
hat.


>         if( haveError )
>           P3 = 0x0E;
>         else
>           P3 = 0x00;

erst hier siehst du es! Wenn P3 entsprechend gestellt wird.
Und das wird immer gleich gestellt, wenn ein Error vorliegt.
Solange aber die Fehlersituation vorliegt, HAT haveError an dieser 
Stelle immer den Wert 1. Das haveError zwischendurch auch mal 0 war, ist 
uninteressant. Entscheidend ist der Wert an DIESER Stelle! Und der ist 
1, wenn die Subtraktion einen Fehler festgestellt hat.

: Bearbeitet durch User
von Marcel S. (blackevil673)


Lesenswert?

Ahhhhh ich glaub jetzt verstehe ich :D
Werds nochmal testen

Edit: Klappt jetzt!

Noch ein Problem...Mir fällt gerade auf, dass bei Multiplikationen, aber 
nur dann, die Zehnerstelle manchmal um 2 Stellen verschoben ist. 
Beispielsweise ergibt 4*8 = 52 statt 32. 8*8 ergibt aber wieder 64. Hä??

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Marcel Schwegler schrieb:
> Ahhhhh ich glaub jetzt verstehe ich :D
> Werds nochmal testen
>
> Edit: Klappt jetzt!
>
> Noch ein Problem...Mir fällt gerade auf, dass bei Multiplikationen, aber
> nur dann, die Zehnerstelle manchmal um 2 Stellen verschoben ist.
> Beispielsweise ergibt 4*8 = 52 statt 32. 8*8 ergibt aber wieder 64. Hä??

Teste das mal systematisch.
Könnte es sein, dass beim Anschliessen der Dekoder ein Fehler passiert 
ist und da 2 Leitungen (Bit 1 und Bit 2 von der Zehner-Anzeige) 
irrtümlich vertauscht wurden?


> die Zehnerstelle manchmal um 2 Stellen verschoben ist
wie kommst du auf verschoben
1
Binär   5         0101
2
Binär   3         0011
3
                   ^^
4
                   **

eine Vertauschung der beiden Spalten könnte das erklären.

: Bearbeitet durch User
von Marcel S. (blackevil673)


Lesenswert?

Oh mein Gott du bist echt ein Held. Auf soetwas naheliegendes wäre ich 
ewige Zeiten nicht gekommen. Ich habe den Fehler im Code vermutet. Aber 
okay, es klappt jetzt wirklich alles. Für diese tatkräfitge 
Unterstützung muss ich wirklich ein besonders großen Dank aussprechen. 
Einige Tipps und ein paar Erfahrungswerte kann man daraus auf jedenfall 
mitnehmen. Dankeschön!

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.