Lauflicht auf Relaiskarte mit Vorgabe des Bitmusters C++

  • #1
H

Hiro

Guest
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.

Was ich noch geschnallt hab ist, dass es was mit _outp(Adresse,Wert) aus conio.h zutun hat.

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

Zwischen den einzelnen Ausgaben ist jeweils eine Sekunde zu warten. Die Relais sind zwischendurch off zu schalten.

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.
 
Thema:

Lauflicht auf Relaiskarte mit Vorgabe des Bitmusters C++

ANGEBOTE & SPONSOREN

Statistik des Forums

Themen
113.840
Beiträge
707.963
Mitglieder
51.494
Neuestes Mitglied
Flensburg45
Oben