Lauflicht auf Relaiskarte mit Vorgabe des Bitmusters C++

Dieses Thema Lauflicht auf Relaiskarte mit Vorgabe des Bitmusters C++ im Forum "Webentwicklung, Hosting & Programmierung" wurde erstellt von Hiro, 7. Sep. 2005.

Thema: Lauflicht auf Relaiskarte mit Vorgabe des Bitmusters C++ Hi! Ich habe eine Programmieraufgabe aufbekommen, mit der ich nicht klar komme. Es wäre super wenn mir jemand dabei...

  1. Hi!

    Ich habe eine Programmieraufgabe aufbekommen, mit der ich nicht klar komme.
    Es wäre super wenn mir jemand dabei helfen könnte!
    Über jeden noch so kleinen Tipp freu ich mich schon!


    Also hier ist erstmal die Aufgabe:

    Lauflicht auf Relaiskarte mit Vorgabe des Bitmusters

    Es soll ein Laulicht auf eriner Relaiskarte ereugt werden. Das Bitmuster soll jeweils nach einer Sekunde nach links verschoben werden.
    Das Bitmuster wird als Wert von der Tastertur eingelesen (z.B. 6; 0000 0110 hex). Zwischen den einzelnen Ausgaben ist jeweils eine Sekunde zu warten. Die Relais sind zwischendurch off zu schalten.

    Lösungshinweise:
    Shiften des Bitmusters um eine Stelle: x = x << 1;
    Beachte das die Bits beim Shiften nach links herausgeschoben werden, dies muss erkannt werdeen [ if(bitmuster & 128 = 128) ].
    Wird das Bitmuster herausgeschoben, dann ist es (+1) wieder anzuhängen!



    Also die Zeitverzögerung bekomm ich mit Sleep(); hin. Was ich noch geschnallt hab ist, dass es was mit _outp(Adresse,Wert) aus conio.h zutun hat. Aber da is nu auch schon leider Schluss TT_TT.
     
  2. Handelt es sich um eine Simulation oder hängt wirklich eine Relaiskarte am Rechner? Wenn Relaiskarte: Ist sie an einem Druckerport oder einem seriellen Port angeschlossen? Welcher Compiler, welches Betriebssystem? Windows und Visual C++ scheint ja klar zu sein wegen Sleep() und _outp.

    So klappt es im User-Mode (also außer bei Treibern) nur unter MS-DOS und Windows 95/98/Me.

    Wahrscheinlich stehe ich gerade auf dem Laufli ... ähm, Schlauch: Wann ist zwischendurch? Sicher nicht während der Pause, dann wird's ja dunkel ;)?

    Ok, ein paar Tipps (Windows-Konsolenanwendung; Muster fix 8 Bit, keine Klassendefinitionen etc.).
    ----------------

    Header einbinden:

    Code:
    // Bei Standard-Konsolenprojekt (vorkompilierte Header):
    #include stdafx.h
    
    #define VC_EXTRALEAN // optional
    #include <windows.h>  // für Sleep
    #include <iostream>     // cin, cout etc.
    #include <conio.h>       // für _kbhit() und _outp
    #include <bitset>         // siehe unten
    
    using namespace std;
    ---

    Eingabe mit
    Code:
    cin >> muster;
    muster ist eine Variable, die du vorher deklarieren musst.
    Es sollte nach der Eingabe überprüft werden, ob der Wert zwischen 0 und 255 liegt. Wenn nicht, einfach korrigieren, das dürfte hier reichen.

    ---

    Ausgabe: Ohne weitere Infos kann ich dazu nicht viel sagen. _outp hat zwei Parameter: Port und ein Datenbyte, also in diesem Fall das Muster. Funktioniert so direkt natürlich nur, wenn die Relaiskarte einfach die Muster am Port 1:1 umsetzt. Eine parallele Standard-Schnittstelle hat 3 Ports (Data, Status und Steuerport).
    Um die Funktion zu verwenden, musst du natürlich den Basisport der entspr. parallelen Druckerschnittstelle an diesem Rechner kennen. Der Standard-Druckerport ist 0x378 (hexadezimal).
    Eingegeben werden soll er hier ja nicht, also den *korrekten* Port irgendwo oben im Code als Konstante setzen, z.B.

    Code:
    const unsigned short Druckerport= 0x378;
    Vorsicht: Das ist jetzt etwas Spekulatius!

    Reset:

    Code:
    _outp(Druckerport + 2, 0x8e);
    Die Ausgabe des Musters:

    Code:
    _outp(Druckerport, muster);
    Aber denk' dran: Unter Windows NT/2000/XP/2003 brauchst du erst gar nicht damit rumspielen. Ich weiß nicht, ob das eine normale Exception (wahrscheinlich) oder einen Bluescreen gibt oder einfach ignoriert wird.

    Ausgabe auf dem Bildschirm: Du musst, um dein Programm zu prüfen, die Werte auch ohne Relaiskarte irgendwie sehen können. In C++ gibt es keine Möglichkeit, einen Wert zur Bildschirm-Ausgabe binär zu formatieren. Du kannst es aber (u.a.) so machen:

    Code:
    cout << bitset<8>(pattern) << endl;
    Dafür wird oben bitset eingebunden.

    ---

    Muster berechnen:
    Eine 0 im Binärmuster entspricht einer ausgeschalteten Lampe, eine 1 einer eingeschalteten.
    Mit dem Operator << verschiebst du ein Bitmuster nach links. Dabei fallen aber gesetzte Bits links heraus. Beispiel:
    Anfangswert ist 1, also binär 00000001
    00000001
    00000010
    00000100
    00001000
    00010000
    00100000
    01000000
    10000000
    00000000 ... ups, das gesetzte Bit ist links rausgefallen; das soll nicht sein.

    Anfangswert 178:
    10110010
    01100100 <- schon hier geht es schief
    11001000
    10010000
    00100000
    01000000
    10000000
    00000000  <- und ab hier bleiben alle Lampen aus
    00000000

    So soll es aber bei einem Lauflicht aussehen:

    10110010
    01100101 -> hier setzen wir die gerade links rausgefallene 1 rechts wieder rein
    11001010 -> ok, keine 1 links rausgefallen
    10010101 -> aber hier wieder, also rechts eine setzen
    00101011 -> wieder
    01010110 -> ok, nichts
    10101100 -> ok, nichts
    01011001 -> aber hier müssen wir wieder eingreifen
    -> danach sind wir wieder beim Originalmuster; immer so weiter ...

    Wir müssen also aus dem Verschieben ein Rotieren machen. Wenn *vor* dem Verschieben ganz links eine 1 steht, müssen wir *nach* dem Verschieben das Bit ganz rechts setzen. Erst dann haben wir das korrekte Bitmuster, das sich beliebig oft rotieren lässt.

    Code:
    // Altes Muster merken
    unsigned char alt= muster;
    
    // Muster um 1 Bit nach links verschieben
    muster <<= 1;
    
    // War beim alten Muster das Bit ganz links gesetzt?
      if(alt & 128)
      // Ja: Bit ganz rechts setzen
        muster |= 1;
    muster <<= 1 entspricht vom Resultat her muster= muster << 1 (wie in den Lösungshinweisen aufgeführt); Verschiebung und Zuweisung sind zusammengefasst. Und Vorsicht, das ist muster |= 1;, nicht muster = 1.

    Oder zum Angeben:

    Code:
    muster = (muster << 1) | (muster >> (8 - 1));
    Dann solltest du es aber erklären können ;).

    ---

    Du kannst alles in eine Schleife bringen, die der Benutzer mit einem Tastendruck abbrechen kann. Das klappt bei Visual C++ nicht ohne systemspezifische Funktion. Als einfaches Gerüst:

    Code:
    // Header (siehe oben)
    
    int main() {
    
      cout << Bitte geben Sie den Musterwert ein: ;
    
      // Hier die Eingabe
    
      // Hier die Prüfung der Eingabe
    
      cout << Zum Beenden des Programms bitte eine Taste druecken ... << endl << endl;
      
      while(!_kbhit())
      {
         // Hier dein Code, der das Muster bei jedem Durchlauf
         // um 1 Bit rotieren lässt und die Relaiskarte ansteuert
    
         // Pause:
         Sleep(1000);
       }
    
       return 0;
    }
    ---

    Ich würde Funktionen definieren:

    Code:
    
    // ... siehe oben ...
    
    const unsigned char ALL_OFF = 0;
    
    // Berechnet nächstes Muster
    void calcNextPattern(unsigned char& muster)
    {
       // Hier dein Code
    }
    
    // Gibt Muster als Binärzahl auf Bildschirm aus
    void writeScreen(unsigned char muster)
    {
       cout << bitset<8>(muster) << endl;
    }
    
    void resetRC()
    { 
       // ?
       // Soll nur das Prinzip andeuten; Vorsicht, _outp liefert keine Fehlerwerte zurück ...
        _outp(Druckerport + 2, 0x8e);
    }
    
    void writeRCData(unsigned char pattern)
    {
       // ?
       // Soll nur das Prinzip andeuten; Vorsicht, _outp liefert keine Fehlerwerte zurück ...
        _outp(Druckerport, pattern);
    }
    
    // ... siehe oben ...
    
    // Schnittstelle zurücksetzen
    resetRC();
    
    while(!_kbhit())
    {
      // Bildschirmausgabe des Musters
      writeScreen(muster);
      
      // Berechne nächstes Muster
      calcNextPattern(muster);
    
      // Ausgabe auf Relaiskarte
      writeRCData(muster);
    
      // 1s Pause
      Sleep(1000);
    }
    
    // Alles aus
    writeRCData(ALL_OFF);
    
    // Schnittstelle zurücksetzen
    resetRC();
    
    // ... siehe oben ...
    
    
     
  3. Erstmal Danke für deine Hilfe, du hast mir echt schon geholfen!!! ^^

    So ich hab versucht das ganze mal umzusetzen, doch es ist mir nicht ganz gelungen

    Also ich stell meinen mal Quelltext hier hin:

    Code:
    int main(int argc, char* argv[])
    {
    
    	int muster, alt;
    	int i, taste=0;
    
    	printf(\t\t\t Lauflicht auf Relaiskarte \n\n\n);
    	
    	printf(\t Zum Beenden des Lauflichtes bitte eine Taste drücken! \n\n\n);
    	printf(\t\t Bitte ein Bitmuster von 8 Bit eingeben: \n\n\n);
    	printf(\t\t\t\t); 
    	cin >> muster;
    
    
    
    	do
    	{
    
    		for(i=0; i<8; i++)
    		{
    
    			cout << muster << endl;
    
    			//outp(0x378,muster)
    
    			cout << \n;	
    			
    			alt=muster;
    			
    			muster<<=1;
    			
    				if(alt&128)
    				{
    					muster = muster + 1;
    				}
    
    			Sleep(1000);
    			//outp(0x378,~value)
    		}
    		
    
    		muster = muster - 256;
    
    			
    			if(kbhit())
    			{
    				taste = getch();
    				break;
    			}
    		
    		Sleep(1000);
    
    	}
    	while(taste != 27);
    
    	return 0;
    }
    

    So nur jetzt hab ich das Problem, dass das ganze nur bei 00000001 funktioniert und sonst net.
     
  4. Du meinst, weil z.B. bei einer Eingabe von 200 sowas angezeigt wird:

    200
    401
    803
    1606
    ...

    Wenn ja: Das liegt an

    int muster;

    Die Größe von Variablen des Typs int ist implementierungsabhängig (ebenso, ob char ohne unsigned davor signed oder unsigned ist); bei aktuellen (PC-)Compilern sind es 4 Byte, bei älteren 2 Byte. Du geht aber davon aus, dass muster 1 Byte groß ist.

    Als Beispiel der erste Schritt für 200:

    muster = 11001000 (200)

    muster<<=1;  -> 10010000 (144) (1 ist rausgefallen, da->muster' nur 8 Bit groß ist)

    if(alt&128) // Ja, also:
    muster = muster + 1;  -> 10010001 (145)

    Stimmt.


    'muster' ist aber bei dir mindestens 2 Byte (16 Bit) groß:

    muster = 0000000011001000 (200)

    muster<<=1;  ->  0000000110010000 (400)
    (da ja bei 2 oder mehr Byte bei einem Wert von 200 genug Platz ist und nichts rausfällt)

    if(alt&128) // Ja, also:
    muster = muster + 1;  -> 0000000110010001 (401)

    Der Wert wird immer größer, bevor überhaupt später mal bei der Shift-Operation (<<=1)
    das erste Bit rausfällt. Nach 8 Durchgängen ziehst du dann von der riesigen Zahl
    256 ab (muster = muster - 256), was aber auch nichts mehr hilft.

    Bei 1 funktioniert es deswegen:

    0000000000000001
    0000000000000010
    0000000000000100
    0000000000001000
    0000000000010000
    0000000000100000
    0000000001000000
    0000000010000000
    0000000100000000 -> 256! Dürfte bei nur 8 Bit gar nicht passieren, du bist hier schon im nächsten Byte.
    muster = 256 + 1 = 257

    Dann nach der inneren Schleife:
    muster = 257 - 256 = 1

    Trotz des Fehlers stimmt es dann wieder.

    Bei 2 klappt es nicht mehr, da das gesetzte Bit schon bei der vorletzten Shift-Operation übers Ziel hinausschießt.

    Eigentlich ist int ein Typ mit Vorzeichen, d.h., irgendwann wird sogar eine negative Zahl angezeigt; aber das spielt jetzt keine Rolle.

    ---------------

    Du könntest einen angemessenen Datentypen (mit korrekter Größe und ohne Vorzeichen) benutzen, also unsiged char statt int - und die Eingabe etwas verändern, ich erkläre unten noch, warum - dann kannst du den Rest des Programms so lassen wie er ist.

    Mögliche Lösung (du kannst es natürlich auch anders machen):


    Ersetze

    Code:
    int muster, alt;
    durch

    Code:
    unsigned char muster, alt;
    int musterIn;

    und


    Code:
    cin >> muster;
    durch

    Code:
    cin >> musterIn;
    
    // Zu große Zahl oder andere unsinnige Eingabe?
    if (musterIn > 255) {
      musterIn= 255;
      cout << Verwendeter Wert:  << musterIn << endl;
    }
    
    // Sichere Typumwandlung:  musterIn ist jetzt mindestens 0, maximal 255
    muster= (unsigned char) musterIn;
    
    -

    (Hinweis: Ich weiss nicht, welchen Compiler du verwendest, es könnte evtl. auch anders sein bei unsigned char)
    Sinn: bei cin >> irgendwas wird intern der entsprechende Datentyp angenommen (der Operator >> ist überladen [er hat übrigens nichts mit den Shift-Operatoren zu tun]), und das ist bei->char'- und->unsigned char'-Typen ein Textzeichen (ASCII-Charakter).
    Damit es gleich stimmt, wird ein->int' zum Einlesen genommen, der Wert geprüft und dann der Variable
    muster zugewiesen. Sonst bekommst du z.B. bei einer Eingabe von 1 nicht den Wert 1, sondern den Code für das Textzeichen 1. Man kann hier aber z.B. auch ein->unsigned char' direkt einlesen und dann eine Konstante abziehen, damit der echte Wert in muster steht. Allerdings kann man den Wert dann nicht mehr wirklich prüfen.
    Übrigens muss nur getestet werden, ob musterIn > 255, da wegen des Typs unsigned int erst gar keine negativen Werte vorkommen können, ganz egal, welcher Unsinn als Muster eingetippt wird.

    Da auch der Operator << ein->unsigned char' als Textzeichen interpretiert, musst du den Wert zum
    Anschauen in einen anderen Typ umwandeln, sonst siehst du nur merkwürdige Zeichen.
    Z.B.

    Code:
    cout << (int)muster << endl;
    oder

    Code:
    printf(%d\n, muster);
    Aber ich würde alles auf cout umstellen ... Beispiel:

    Code:
    cout << \t\t\t Lauflicht auf Relaiskarte \n\n\n;
    Die doppelte Pause nach einem kompletten Durchlauf durch das zweite Sleep(1000) ist sicher Absicht?
    Es sollte dann eigentlich soweit funktionieren.

    -------------

    Du meinst, weil z.B. bei einer Eingabe von 200 sowas angezeigt wird:

    200
    401
    803
    1606
    ...

    Wenn ja: Das liegt an

    int muster;

    Die Größe von Variablen des Typs int ist implementierungsabhängig (ebenso, ob char ohne unsigned davor signed oder unsigned ist); bei aktuellen (PC-)Compilern sind es 4 Byte, bei älteren 2 Byte. Du geht aber davon aus, dass muster 1 Byte groß ist.

    Als Beispiel der erste Schritt für 200:

    muster = 11001000 (200)

    muster<<=1;  -> 10010000 (144) (1 ist rausgefallen, da->muster' nur 8 Bit groß ist)

    if(alt&128) // Ja, also:
    muster = muster + 1;  -> 10010001 (145)

    Stimmt.


    'muster' ist aber bei dir mindestens 2 Byte (16 Bit) groß:

    muster = 0000000011001000 (200)

    muster<<=1;  ->  0000000110010000 (400)
    (da ja bei 2 oder mehr Byte bei einem Wert von 200 genug Platz ist und nichts rausfällt)

    if(alt&128) // Ja, also:
    muster = muster + 1;  -> 0000000110010001 (401)

    Der Wert wird immer größer, bevor überhaupt später mal bei der Shift-Operation (<<=1)
    das erste Bit rausfällt. Nach 8 Durchgängen ziehst du dann von der riesigen Zahl
    256 ab (muster = muster - 256), was aber auch nichts mehr hilft.

    Bei 1 funktioniert es deswegen:

    0000000000000001
    0000000000000010
    0000000000000100
    0000000000001000
    0000000000010000
    0000000000100000
    0000000001000000
    0000000010000000
    0000000100000000 -> 256! Dürfte bei nur 8 Bit gar nicht passieren, du bist hier schon im nächsten Byte.
    muster = 256 + 1 = 257

    Dann nach der inneren Schleife:
    muster = 257 - 256 = 1

    Trotz des Fehlers stimmt es dann wieder.

    Bei 2 klappt es nicht mehr, da das gesetzte Bit schon bei der vorletzten Shift-Operation übers Ziel hinausschießt.

    Eigentlich ist int ein Typ mit Vorzeichen, d.h., irgendwann wird sogar eine negative Zahl angezeigt; aber das spielt jetzt keine Rolle.

    ---------------

    Du könntest einen angemessenen Datentypen (mit korrekter Größe und ohne Vorzeichen) benutzen, also unsiged char statt int - und die Eingabe etwas verändern, ich erkläre unten noch, warum - dann kannst du den Rest des Programms so lassen wie er ist.

    Mögliche Lösung (du kannst es natürlich auch anders machen):


    Ersetze

    Code:
    int muster, alt;
    durch

    Code:
    unsigned char muster, alt;
    int musterIn;

    und


    Code:
    cin >> muster;
    durch

    Code:
    cin >> musterIn;
    
    // Zu große Zahl oder andere unsinnige Eingabe?
    if (musterIn > 255) {
      musterIn= 255;
      cout << Verwendeter Wert:  << musterIn << endl;
    }
    
    // Sichere Typumwandlung:  musterIn ist jetzt mindestens 0, maximal 255
    muster= (unsigned char) musterIn;
    
    -

    (Hinweis: Ich weiss nicht, welchen Compiler du verwendest, es könnte evtl. auch anders sein bei unsigned char)
    Sinn: bei cin >> irgendwas wird intern der entsprechende Datentyp angenommen (der Operator >> ist überladen [er hat übrigens nichts mit den Shift-Operatoren zu tun]), und das ist bei->char'- und->unsigned char'-Typen ein Textzeichen (ASCII-Charakter).
    Damit es gleich stimmt, wird ein->int' zum Einlesen genommen, der Wert geprüft und dann der Variable
    muster zugewiesen. Sonst bekommst du z.B. bei einer Eingabe von 1 nicht den Wert 1, sondern den Code für das Textzeichen 1. Man kann hier aber z.B. auch ein->unsigned char' direkt einlesen und dann eine Konstante abziehen, damit der echte Wert in muster steht. Allerdings kann man den Wert dann nicht mehr wirklich prüfen.
    Übrigens muss nur getestet werden, ob musterIn > 255, da wegen des Typs unsigned int erst gar keine negativen Werte vorkommen können, ganz egal, welcher Unsinn als Muster eingetippt wird.

    Da auch der Operator << ein->unsigned char' als Textzeichen interpretiert, musst du den Wert zum
    Anschauen in einen anderen Typ umwandeln, sonst siehst du nur merkwürdige Zeichen.
    Z.B.

    Code:
    cout << (int)muster << endl;
    oder

    Code:
    printf(%d\n, muster);
    Aber ich würde alles auf cout umstellen ... Beispiel:

    Code:
    cout << \t\t\t Lauflicht auf Relaiskarte \n\n\n;
    Die doppelte Pause nach einem kompletten Durchlauf durch das zweite Sleep(1000) ist sicher Absicht?
    Es sollte dann eigentlich soweit funktionieren.

    // Edit

    Noch zur Erlärung: In muster= (unsigned char) musterIn ist hier eigentlich die Angabe der gewünschten Konvertierung als (unsigned char) [-> cast] nicht unbedingt nötig, da der Compiler den Wert auch automatisch umwandelt. Aber es ist erstens übersichtlicher, da du dann dem Code sofort ansiehst, dass eine Typumwandlung stattfindet, zweitens verhindert es Warnungen des Compilers in höheren Warnstufen. Diese Warnung wäre hier unsinnig, da du ja weisst, dass musterIn einen Wert zwischen 0 und 255 hat und deswegen auf jeden Fall ohne Veränderung des Wertes in ein unsigned char, also 1 Byte passt, das ja Werte zwischen 0 und 255 aufnehmen kann.
    Alternativ (in C++ besser und üblich) kannst du hier statt des C-Casts auch muster= static_cast<unsigned char>(musterIn); verwenden.

    Noch ein Vorschlag: Vermeide magische Zahlen in deinen Programmen. Also z.B. oben im Code

    Code:
    const int ASCII_Esc= 27;
    const unsigned short Druckerport= 0x378;
    
    und dann

    Code:
    while (taste != ASCII_Esc);
    Code:
    outp(Druckerport, muster);
    usw.


    Noch eine Kleinigkeit: Deinem Programm ist es egal, welche Taste gedrückt wird, da die Schleife in

    if(kbhit())
    {
      taste = getch();
      break;
    }

    bei einem Tastendruck so oder so verlassen wird und die Bedingung in der do-while-Schleife dann keine Rolle mehr spielt. Du könntest z.B. das->break' entfernen oder diese Abfrage in die innere (die for-)Schleife setzen. Dann kann man das Programm auch schneller abbrechen.