Forum: Mikrocontroller und Digitale Elektronik AT89C51CC03, ein ausgefuelltes Rechteck mit *-Zeichen darstellen


von Francis C. (Firma: Privat) (danger19xx)


Lesenswert?

Hoi Zämen

ich soll eine Funktion schreiben, bei deren Aufruf ein ausgefuelltes 
Rechteck aus dem Charakterzeichen C1 gezeichnet wird. Die obere ecke des 
Rechtecks hat die Koordinaten (X1,Y1) und die untere rechte Ecke 
(X2,Y2).

Ich habe erstmal mit einer Fläsche von 8X8

Bitte um Hilfe, denn was ist erhalten ueber den Bildschirm(PUTTY) sind 
seltsame Zeichnen; ich denke wegen
1
    printf("\x1b\x59%c%c",(32+x1),(32+y1));

Also, mein C-Programm
1
/*** Einbinden von Include-Dateien ***/
2
#include<stdio.h>
3
#include<at89c51cc03.h>
4
5
/**************************************************************************************************************************/
6
/*  Funktion void rect(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2, unsigned char c1)          */
7
/*  positioniert den Terminal Cursor auf die angegebene Position und gibt dort                                            */
8
/*  den wert aus!                                                                                                         */
9
/*  Parameter x1 := Zeile, Parameter y1 := Spalte, --> obere linke Ecke des Rechtecks                                     */
10
/*  Parameter x2 := Zeile, Parameter y2 := Spalte, --> untere rechte Ecke des Rechtecks                                   */
11
/*  Parameter c1 := ASCII-Charakterzeichen                                                                                */
12
/**************************************************************************************************************************/
13
14
void rect(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2, unsigned char c1)
15
  {
16
    printf("\x1b\x59%c%c",(32+x1),(32+y1));
17
    printf("%c",c1);
18
    printf("\x1b\x59%c%c",(32+x2),(32+y2));
19
    printf("%c",c1);
20
  }  
21
22
/********************************************************************************************/
23
/*** Start des Hauptprogramms ***************************************************************/
24
/********************************************************************************************/
25
26
void main (void)     // Start des Hauptprogramms
27
  {
28
    // Definitionen der benötigten Variablen
29
    unsigned char i, j, k, l, c1;          
30
31
    
32
    /****************************************************************************************/
33
    /*  Initialisierung der seriellen Schnittstelle 0 des CC03ers                           */
34
    /*  Schnittstellenparameter: 9600Baud, 8 Datenbit, 1 Stopp-Bit asynchroner Betrieb      */
35
    /****************************************************************************************/
36
37
    SCON=0x52;
38
    TMOD |=0x20;
39
    TH1=0xfd;
40
    TR1=1;
41
    TI=1;
42
43
    c1 = '*';    
44
45
    printf("\x1b\x48\x1b\x4a");         // Bildschirm löschen und Cursor auf Home-Position
46
    printf("\n   Funktionen, -> MIT Uebergabewert, OHNE Rueckgabewert!\n");
47
48
    for(i=2; i<=10; i++) // Zehn Zeilen
49
         for(j=2; j<=10; j++) //Zehn Spalten
50
            rect(i, j, i, j, c1); //Füllen des Rechtecks mit '*'-Zeichen       
51
    
52
53
54
    while (1);                  // Endlosschleife zum ordnungsgemäßen Abschluß des Programms
55
  }                             // Ende des Hauptprogramms, d.h. Ende des gesamten Programms

Danke für die Inputs!

von FPGASchubser (Gast)


Lesenswert?

Putty kann das Verhalten einiger Terminals emulieren.
Jedes dieser Terminals hat besondere Zeichenkominationen zur Steuerung 
des "sichtbaren" Outputs. Du musst dich für eines dieser Termnials 
entscheiden, und dann die Kommandos for goto_xy() implenentieren.

>>> \x1b\x59(posx)(posy)

das hier ist genau der Versuch ein solches Steuerkommando zu senden:

\x1b dürfte eine ESC sein (ESCAPE = 27 dez)
\x59 (dez 89) ist wohl der Kommandocode für bewege Cursor zu posx,posy

nun musst du nur noch den passeneden Terminal emulator einschalten.
Stichworte: VT100, VT400 usw. da musst Du dann wieder googeln.....

Gruß Thilo

von FPGASchubser (Gast)


Lesenswert?

Ich habe für dich mal den Gockel gefragt.

Dein Code passt zum Terminal-Emulator VT52. Ich glaube aber der sollte 
auch im VT100 mode funktionieren.

Der Code ist eine Escape Sequenz zur Cursor - Kontrolle

ESC Y (Ps) (Ps)

Gruß T.

von Francis C. (Firma: Privat) (danger19xx)


Lesenswert?

Hallo Thilo,

ich gebe dir Recht!

Leider muss ich den PUTTY als Terminal benutzen, den ich Windows 7 
verwende.

Die von dir genannten Terminal Emulator betrifft ehe die Windows XP 
Version, was ich nicht habe. Google führt mich zu keiner passenden 
Antwort...

Da ich auch noch auf die Richtigkeit meines Programms Zweifeln habe, 
kannst du mir sagen, ob es so richtig ausklingt?

Danke und Gruss
Francis

von Easylife (Gast)


Lesenswert?

Mal angenommen, die Espace sequencen stimmen,
deine Funktion "rect()" füllt kein Rechteck, sondern setzt die linke 
obere und rechte untere Ecke.

Es macht wenig Sinn, für jeden (doppelt gezeichneten) Punkt immer den 
Cursor neu zu setzen.
Setze den Cursor besser 1x pro Zeile, und drucke dann soviele Sterne, 
wie das Rechteck breit ist.

von FPGASchubser (Gast)


Lesenswert?

Was Easylife sagt ist richtig.
Zudem kannst Du auch die erste und letzte Linie einfach ohne neue 
Cursor-Positionierung erzeugen, indem du immer '*' ausgibst. Der Cursor 
rückt selbstandig um eine Position weiter.

Deine Funktion 'rect' ergibt Sinn wenn Du sie abwandelst zu einer 
Funktion 'leftandright' wobei Sie den linken '*' ausgibt mit ESC Y 0 
ypos und dann den rechten mit ESC Y xmax ypos, wobei xmax die Position 
des rechten Randes ist.
Dann könnteste du die Schleifen so bauen:

1. Schleife: Gib an ypos = 0 x-mal den '*' aus
2. Schleife: von ypos = 1 bis ypos < ymax rufe 'leftandright' auf
3. Schleife: Gib an ypos = ymax x-mal den '*' aus.

Gruß Thilo

von Francis C. (Firma: Privat) (danger19xx)


Lesenswert?

Hoi Zäme,

Danke fuer die Inputs(!)

bin mehr die Idee von Easylife eingegangen, da rect() mir vorgeschrieben 
ist.

Folgendes habe ich verbessert, aber bin irgendwie nicht so ueberzeugt.
Hab den Cursor mit (x1,y1) gesetzt, aber bin immer noch verwirrt wie ich 
den (x2, y2) reinbringen!

Habt ihr gewusst, dass es einen riesigen Unterschied zwischen
1
    for(i=0; i<=5; i++)rect(5, 5, 9, 9, c1);

und
1
    for(i=0; i<=5; i++)
2
    {
3
        rect(5, 5, 9, 9, c1);
4
    }

gibt?

das Programm
1
/**************************************************************************************************************************/
2
/*  Funktion void rect(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2, unsigned char c1)          */
3
/*  positioniert den Terminal Cursor auf die angegebene Position und gibt dort das Zeichen (*) aus                        */
4
/*  Parameter x1 := Zeile, Parameter y1 := Spalte, -->obere linke Ecke des Rechtecks                                      */
5
/*  Parameter x2 := Zeile, Parameter y2 := Spalte, -->untere rechte Ecke des Rechtecks                                    */
6
/*  Parameter C1 := ASCII-Charakterzeichen, hier ist das Zeichen (*)                                                      */
7
/**************************************************************************************************************************/
8
9
void rect(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2, unsigned char c1)
10
  {
11
    int i;
12
    printf("\x1b\x59%c%c",(32+x1), (32+y1));
13
14
    for(i=y1; i<=y2; i++)
15
    {
16
        printf("%c", c1);
17
    }        
18
    printf("\n");
19
20
    //printf("\x1b\x59%c%c",(32+x2), (32+y2));
21
    //for(i=0; i<=y2; i++) printf("%c", c1);
22
23
    /*printf("\x1b\x59%c",(32+x2));
24
    for(i=x2; i<=y2; i++) printf("%c", c1);*/    
25
  }  
26
27
/********************************************************************************************/
28
/*** Start des Hauptprogramms ***************************************************************/
29
/********************************************************************************************/
30
31
void main (void)     // Start des Hauptprogramms
32
  {
33
    // Definitionen der benötigten Variablen
34
    unsigned char i, c1;          
35
    
36
    /****************************************************************************************/
37
    /*  Initialisierung der seriellen Schnittstelle 0 des CC03ers                           */
38
    /*  Schnittstellenparameter: 9600Baud, 8 Datenbit, 1 Stopp-Bit asynchroner Betrieb      */
39
    /****************************************************************************************/
40
41
    SCON=0x52;
42
    TMOD |=0x20;
43
    TH1=0xfd;
44
    TR1=1;
45
    TI=1;
46
47
    c1 = '*';
48
  
49
    printf("\x1b\x48\x1b\x4a");         // Bildschirm löschen und Cursor auf Home-Position
50
    printf("\n   Funktionen, -> MIT Uebergabewert, OHNE Rueckgabewert!\n");
51
52
    for(i=0; i<=5; i++)
53
    {
54
        rect(5, 5, 9, 9, c1);
55
    }
56
57
   
58
59
    while (1);                  // Endlosschleife zum ordnungsgemäßen Abschluß des Programms
60
  }                             // Ende des Hauptprogramms, d.h. Ende des gesamten Programms

In den main() hab eine einfache for Schleife(keine verschlachtete mehr) 
benutzt. Desweiteren bin in rect() und hab die Idee von Easylife 
eingesetzt.

Danke fuer die weiteren Inputs

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Francis Chaleu schrieb:
> Habt ihr gewusst, dass es einen riesigen Unterschied zwischen
>     for(i=0; i<=5; i++)rect(5, 5, 9, 9, c1);
> und
>     for(i=0; i<=5; i++)
>     {
>         rect(5, 5, 9, 9, c1);
>     }
> gibt?

Da gibt es exakt keinen.

von Karl H. (kbuchegg)


Lesenswert?

Francis Chaleu schrieb:

> Hab den Cursor mit (x1,y1) gesetzt, aber bin immer noch verwirrt wie ich
> den (x2, y2) reinbringen!

x2 und y2 brauchst du nur um dir auszurechnen, wie breit bzw. wie hoch 
das Rechteck sein muss. Ansonsten taucht es im Code überhaupt nicht auf. 
Wenn du die richtige Anzahl an Zeilen hinmalst, mit der jeweils 
richtigen Anzahl an * in jeder Zeile, dann ergibt sich die rechte untere 
Ecke ganz von alleine.

Mal doch einfach mal mit der Hand ein gewünschtes Rechteck auf 
karriertem Papier! Das wird sowieso unterschätzt, sich erst mal klar zu 
machen, wie man ein Problem selbst mit der Hand lösen würde.

Wie machst du denn das?
Hier sei deine Zeichenfläche
1
    01234567
2
   +--------+
3
 0 |        |
4
 1 |        |
5
 2 |        |
6
 3 |        |
7
 4 |        |
8
 5 |        |
9
 6 |        |
10
 7 |        |
11
   +--------+

und du sollst jetzt ein Rechteck von den Koordinaten  2/3 zu den 
Koordinaten 5/5 malen.

Was machst du? Das erste was du machst ist, du rechnest dir aus, wie 
breit bzw. wie hoch das Rechteck ist. Breit ist es 5-2+1 gleich 4 
Zeichen und hoch ist es 5-3+1 gleich 3 Zeichen.

Also beginnst du in der Zeile 2 (weil x1 gleich 2 ist) in der Spalte 3 
(weil y1 gleich 3 ist) mit der ersten Zeile aus *-nen. Du malst dort 4 
Sterne hin
1
    01234567
2
   +--------+
3
 0 |        |
4
 1 |        |
5
 2 |        |
6
 3 |  ****  |
7
 4 |        |
8
 5 |        |
9
 6 |        |
10
 7 |        |
11
   +--------+

Da dein Rechteck aber 3 Zeilen hoch sein muss, kommt eine weitere Zeile 
darunter. Das ist dann logischerweise die Zeile 3 (weil ja die erste 
Zeile die Zeile mit der Nummer 2, wegen x1, war) und sie beginnt genau 
wie die erste Zeile in Spalte 3 (weil y1 ja 3 war).
Wieder werden 4 *-ne (weil die Breite 4 war hingemalt
1
    01234567
2
   +--------+
3
 0 |        |
4
 1 |        |
5
 2 |        |
6
 3 |  ****  |
7
 4 |  ****  |
8
 5 |        |
9
 6 |        |
10
 7 |        |
11
   +--------+

Da die errechnete Höhe des Rechtecks 3 war, kommt noch eine Zeile dazu. 
Wieder beginnt sie in der Spalte y1, also in der SPalte 3 und ist 4 *-ne 
breit
1
    01234567
2
   +--------+
3
 0 |        |
4
 1 |        |
5
 2 |        |
6
 3 |  ****  |
7
 4 |  ****  |
8
 5 |  ****  |
9
 6 |        |
10
 7 |        |
11
   +--------+

Die zuvor durch die Höhe errechnete Anzahl an Zeilen ist in der 
entsprechenden Breite ausgegeben worden, wobei jede Zeile in der jeweils 
korrekten Spalte beginnt.
Und sieh die mal die rechte untere Ecke an. Sie liegt genau an der 
Position 5/5. Genau so wie es von x2/y2 gefordert wurde.


Wenn ich mich mal zurücklehne und rekapituliere, was ich da eigentlich 
gemacht habe, dann lande ich ganz automatisch bei der Strategie:
1
um ein Rechteck von x1/y1 nach x2/y2 zu malen:
2
3
   breite = x2 - x1 + 1;
4
   hoehe = y2 - y1 + 1;
5
6
   for( i = 0; i < hoehe; i++ ) {
7
     positioniere den Cursor auf x2+i, y1
8
     for( j = 0; j < breite; j++ ) {
9
       male ein '*' hin
10
     }
11
   }


Ja. Manchmal muss man doch tatsächlich in einem Computerprogramm auch 
mal ein bischen rechnen und sich selbst was ausdenken bzw. einfach mal 
sich selbst beobachten, wie man selbst Dinge löst und das dann in Code 
giessen. Das soll vorkommen.

Hinweis: das alles ist noch nicht der Weisheit letzter Schluss, denn was 
passiert, wenn die Funktion den Auftrag kriegt ein Rechteck von den 
Koordinaten 6/7 zu den Koordinaten 3/4 zu malen? D.h die 'linke' Ecke 
ist eigentlich weiter rechts als die 'rechte' Ecke. Sinngemäss auch für 
oben und unten. Die Fälle wollen noch behandelt werden.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Francis Chaleu schrieb:

> In den main() hab eine einfache for Schleife(keine verschlachtete mehr)
> benutzt.

Was Blödsinn ist. Denn um ein Rechteck zu zeichnen, benötigst du 
verschachtelte for-Schleifen.

Deine Funktion rect() macht alles mögliche, nur eben kein Rechteck 
zeichnen. Lass Funktionen das machen, was ihr Name suggeriert. Dann 
machst du dir deinen Job als Programmierer um ein ganzes Stück leichter.

: Bearbeitet durch User
von Francis C. (Firma: Privat) (danger19xx)


Lesenswert?

Vielen vielen Dank lieber Moderator!

Ich hoffe, dass ich irgendwann auf solche logische Schritte kommen 
werde, wie du.

Ich werde auf jeden Fall mein Programm posten.

Viele Grüsse
Francis

von Francis C. (Firma: Privat) (danger19xx)


Lesenswert?

Hoi Zäme,

so klar wie der Moderator erklärt hat, musste ich nur noch Eins zu Eins 
einsetzen(!)

anbei das Programm nach einer Woche Urlaubzeit
1
/*** Einbinden von Include-Dateien ***/
2
#include<stdio.h>
3
#include<at89c51cc03.h>
4
5
/**************************************************************************************************************************/
6
/*  Funktion void rect(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2, unsigned char c1)          */
7
/*  positioniert den Terminal Cursor auf die angegebene Position und gibt dort das Zeichen (*) aus                        */
8
/*  Parameter x1 := Zeile, Parameter y1 := Spalte, -->obere linke Ecke des Rechtecks                                      */
9
/*  Parameter x2 := Zeile, Parameter y2 := Spalte, -->untere rechte Ecke des Rechtecks                                    */
10
/*  Parameter C1 := ASCII-Charakterzeichen, hier ist das Zeichen (*)                                                      */
11
/**************************************************************************************************************************/
12
13
void rect(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2, unsigned char c1)
14
  {
15
    // Definitionen der benötigten Variablen
16
    int i,j;
17
    unsigned char breite, hoehe;
18
19
    breite=x2-x1+1; //Breite des Rechtecks
20
    hoehe=y2-y1+1;  //Höhe des Rechtecks
21
22
    for(i=0; i<hoehe; i++)
23
    {
24
        printf("\x1b\x59%c%c",(32+(x2+i)), (32+y1));
25
        printf("\n");
26
27
        for(j=0; j<=breite; j++)
28
        {
29
            printf("%c", c1);
30
31
        }
32
    }        
33
    
34
  }  
35
36
/********************************************************************************************/
37
/*** Start des Hauptprogramms ***************************************************************/
38
/********************************************************************************************/
39
40
void main (void)     // Start des Hauptprogramms
41
  {
42
    
43
    unsigned char c1;          
44
    
45
    /****************************************************************************************/
46
    /*  Initialisierung der seriellen Schnittstelle 0 des CC03ers                           */
47
    /*  Schnittstellenparameter: 9600Baud, 8 Datenbit, 1 Stopp-Bit asynchroner Betrieb      */
48
    /****************************************************************************************/
49
50
    SCON=0x52;
51
    TMOD |=0x20;
52
    TH1=0xfd;
53
    TR1=1;
54
    TI=1;
55
56
    c1 = '*';
57
  
58
    printf("\x1b\x48\x1b\x4a");         // Bildschirm löschen und Cursor auf Home-Position
59
    printf("\n   Funktion, -> MIT Uebergabewert, OHNE Rueckgabewert!\n");
60
61
62
    rect(2, 3, 10, 5, c1);
63
64
65
    while (1);                  // Endlosschleife zum ordnungsgemäßen Abschluß des Programms
66
  }                             // Ende des Hauptprogramms, d.h. Ende des gesamten Programms

Natuerlich bin ich offen fuer Verbesserung!

Danke nochmal fuer die bedeutenden Hinweise

Viele Gruesse
Francis

von Karl H. (kbuchegg)


Lesenswert?

Francis Chaleu schrieb:

> Natuerlich bin ich offen fuer Verbesserung!

Der nächste wichtige Schritt besteht darin, sich mal zurückzulehnen und 
zu überlegen, ob es im Code grundlegende 'Bausteine' gibt, die für sich 
alleine nützlich wären.

So zum Beispiel das hier
1
....
2
        printf("\x1b\x59%c%c",(32+(x2+i)), (32+y1));
3
....

das positioniert den Cursor, um damit an einer anderen 'Zeichenposition' 
etwas ausgeben zu können.

Das stinkt aber schon geradezu danach, dass es sich hier um eine 
allgemeinere Funktionalität handeln könnte, die für sich alleine 
nützlich sein wird. Willst du einen Kreis ausgeben, dann wird zwar die 
Anfangskoordinate einer Zeile anders berechnet und auch die Nummer der 
auszugebenden Zeile mag eine andere sein, aber die Grundfunktionalität 
'Positioniere den Cursor in Zeile y und Spalte x' wird auch dort 
gebraucht werden. Genauso wie in vielen anderen Aufgaben, die dir in 
diesem Zusammenhang begegnen werden.
Es würde daher Sinn machen, diese Funktionalität in eine eigene Funktion 
zu 'verbannen', so dass man sich an der verwendenden Stelle keine 
Gedanken mehr darum machen muss, wie diese Positionier-Aktion genau 
abläuft.
Ein traditioneller Name für diese Funktion ist zb. gotoXY
1
void gotoXY( unsigned char x, unsigned char y )
2
{
3
  printf("\x1b\x59%c%c",(32+x), (32+y));
4
}
5
6
void rect(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2, unsigned char c1)
7
  {
8
    // Definitionen der benötigten Variablen
9
    int i,j;
10
    unsigned char breite, hoehe;
11
12
    breite=x2-x1+1; //Breite des Rechtecks
13
    hoehe=y2-y1+1;  //Höhe des Rechtecks
14
15
    for(i=0; i<hoehe; i++)
16
    {
17
        gotoXY( x2+i, y1 );
18
        printf("\n");
19
20
        for(j=0; j<=breite; j++)
21
        {
22
            printf("%c", c1);
23
24
        }
25
    }        
26
    
27
  }  
28
}

Genauso wie das hier
1
...
2
    printf("\x1b\x48\x1b\x4a");         // Bildschirm löschen und Cursor auf Home-Position
3
...
das Zeug zu einem Grundbaustein hat. Die Funktionalität 'Bildschirm 
löschen und Cursor Home' wirst du noch öfter benötgen. Daher wäre es 
vernünftig, die in eine Funktoin zu verbannen, so dass man nicht 
jedesmal nachsehen muss, wie das genau geht. Ein Aufruf einer Funktion 
Clear() in main() (auch in zukünftigen main()s) muss reichen ohne dass 
man sich ständig an die Details erinnern muss oder will.

Auf die Art entsteht ganz automatisch ein Grundstock an nützlichen 
Funktionen, die von einem Projekt zum nächsten weitergegeben und 
verwendet werden können, ohne dass man in jedem Projekt erneut nach den 
Details suchen muss.
Und irgendwann wirst du dann auch soweit sein, wie man den Cursor auch 
dann positionieren kann, wenn die Spaltenanzahl größer als 9 ist. Eine 
entsprechende Änderung in der Funktion gotoXY kommt dann automatisch 
allen anderen Programmen die gotoXY verwenden zugute.

so etwas
1
...
2
    // Definitionen der benötigten Variablen
3
    int i,j;
4
...
ist bei aller Liebe ein sinnloser Kommentar. Wenn jemand nicht sieht, 
dass es sich bei den nächsten Zeilen um Variablendefinitionen handelt, 
dann soll er zum Optiker gehen oder in die VHS um einen Grundkurs 
'Lesen' zu machen.
Wenn du etwas kommentierst, dann kommentiere das, was man als Leser 
nicht sowieso im Code sieht. Jeder C Programmierer, der mehr als 30 
Minuten C Unterricht hatte, erkennt, dass es sich bei
1
    int i,j;
2
    unsigned char breite, hoehe;
um die Definition von Variablen handelt. Das brauchst du nicht 
dazuschreiben.

Ein Kommentar soll einem Leser des Codes das WARUM erklären und nicht 
das WIE! Das 'WIE' findet sich im Code, aber nur durch Codelesen erfährt 
man nicht warum etwas im Code gemacht wird.

: Bearbeitet durch User
von Francis C. (Firma: Privat) (danger19xx)


Lesenswert?

int i;
for(i=0; i<=1000; i++)
    printf("Vielen Dank(!) Moderator");

Gruss
Francis

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.