Hallo,
ich bin gerade dabei ein Programm zuschreiben, welches Tastereingaben,
welche ich auf dem STK500 tätige in eine Art Wertetabelle schreibt und
anschließend alle Elemente nacheinander auf einem LCD ausgibt. Hier ist
mein vorläufiges Programm:
1
#include<avr/io.h>
2
#include<util/delay.h>
3
#include"lcd-routines.h"
4
#include<stdbool.h>
5
#include<avr/interrupt.h>
6
#include<stdio.h> //nötig zur Benutzung von sprintf-Funktion
7
8
9
10
11
12
charbuffer[20];
13
14
uint8_tlauf;
15
uint8_tarray[2][10]={{0,1,2,3,4,5,6,7,8,9},{}};//array mit zwei Spalten und 10 Zeilen
PORTB=ausgeben;//Eingangsregister zur Kontrolle auf LEDs ausgeben
64
lcd_setcursor(0,1);
65
itoa(ausgeben,buffer,2);//duale Ausgabe des Eingangsregister auf dem LCD
66
lcd_string(buffer);
67
delay_ms(1000);
68
}
69
}
70
71
72
73
intmain(void)
74
{
75
76
lcd_init();
77
DDRB|=0xFF;//alle Pins als Ausgang
78
DDRD=0x00;//Alle Pins als Eingang
79
PORTB|=0xFF;//alle LEDs aus
80
81
82
83
84
85
86
87
while(1)
88
{
89
if(!(PIND&(1<<PD7)))
90
{//Wenn Taster eine Sekunde lang gedrückt
91
delay_ms(1000);
92
befuellen();
93
94
}
95
96
97
}
98
99
100
101
}
Das Programm funktioniert eigentlich so weit. Nur eine Kleinigkeit geht
noch nicht so wie gewünscht. Und zwar wird das Drücken des Tasters an
PD7 nicht richtig auf dem LCD ausgegeben. Auf dem LCD wird immer eine 1,
also nicht gedrückter Taster ausgegeben. Im Array wird das drücken aber
als eine 0, also richtig abgespeichert. Das erkenne ich am richtigen
Blinken der LEDs. Der Fehler liegt also nicht am Array, sondern an der
Ausgabe auf dem LCD.
Wer kann mir sagen warum das so ist?
Gruß
Ich denke, da spielen mehrere Komponenten zusammen.
Einer davon ist
1
itoa(ausgeben,buffer,2);//duale Ausgabe des Eingangsregister auf dem LCD
das "Problem" mit itoa ist, dass es keine führenden 0-en ausgibt. d.h.
unter Umständen springt auf deiner Anzeige die Ausgabe hin und her, je
nachdem welchen Taster du tatsächlich drückst. Und du verwechselst das
mit falschen Werten, weil die nächste Ausgabe die vorhergehende nicht
komplett überschreibt.
Sorg dafür, dass du immer eine Umwandlung des uint8_t auf 8 Bit
bekommst, also inklusive führenden 0-en und teste dann nochmal.
1
voidtoBin(uint8_tzahl,char*Buffer)
2
{
3
uint8_ti;
4
5
for(i=0;i<8;i++){
6
Buffer[7-i]=zahl%2;
7
zahl=zahl/2;
8
}
9
Buffer[8]='\0';
10
}
und diese Funktion benutzt du anstelle von itoa
1
...
2
lcd_setcursor(0,1);
3
toBin(ausgeben,buffer);
4
lcd_string(buffer);
5
...
damit solltest du erst mal alle 8 Bit von ausgeben sehen und das müsste
dann mit deiner LED-Anzeige an PORTB übereinstimmen. Nicht ins Boxhorn
jagen lassen. Je nachdem wie deine LED angeschlossen sind, bedeutet eine
1 am LCD nicht zwangsläufig, dass die LED leuchten muss. Es kann auch
umgekehrt sein. Aber es muss auf jeden Fall eine 1:1 Korrelation
zwischen der Anzeige und dem Leuchten bzw. Nichtleuchten der LED geben.
Karl Heinz Buchegger schrieb:> das "Problem" mit itoa ist, dass es keine führenden 0-en ausgibt
Diese Aussage leuchtet mir ein.
Karl Heinz Buchegger schrieb:> void toBin( uint8_t zahl, char *Buffer )> {> uint8_t i;>> for( i = 0; i < 8; i++ ) {> Buffer[7-i] = zahl % 2;> zahl = zahl / 2;> }> Buffer[8] = '\0';> }
Ich verstehe diesen Programmausschnitt nicht ganz bzw. habe wohl noch
nie so eine Programmierschreibweise gesehen. Man übergibt ja beim
Funktionsaufruf von toBin die Anfangsadresse des ersten Elements von
buffer an den Pointer *Buffer. Soweit steig ich noch mit. Aber in der
Funktion toBin tust du jetzt so, als ob Buffer kein Pointer sondern ein
Array ist. Vielleicht kann mich ja da jemand genauer aufklären...
Aus meiner Sicht müsste die Funktion to Bin so aussehen:
1
voidtoBin(uint8_tzahl,char*Buffer)
2
{
3
uint8_ti;
4
5
for(i=0;i<8;i++){
6
*(Buffer+7-i)=zahl%2;
7
zahl=zahl/2;
8
}
9
*(Buffer+8)='\0';
10
}
Trotz allem habe ich das Programm nun nach deinen Vorschlägen abgeändert
und dabei die Funktion toBin einmal nach deiner und einmal nach meiner
Schreibweise eingefügt. Das Ergebnis ist beides mal das selbe. Und zwar
erscheint auf der LCD-Anzeige die Zeichenkette "|| || || || || || || ||
||". Ich habe jetzt mal nach dem genauen ASCII-Code des Zeichens "||" in
der ASCII-Tabelle gesucht um diesem Problem auf den Grund zu gehen, aber
irgendwie kann ich kein so ein Zeichen finden.
Hier mein vorläufiges Programm:
1
#include<avr/io.h>
2
#include<util/delay.h>
3
#include"lcd-routines.h"
4
#include<stdbool.h>
5
#include<avr/interrupt.h>
6
#include<stdio.h> //nötig zur Benutzung von sprintf-Funktion
7
8
9
10
11
12
charbuffer[20];
13
14
uint8_tlauf;
15
uint8_tarray[2][10]={{0,1,2,3,4,5,6,7,8,9},{}};//array mit zwei Spalten und 10 Zeilen
Andi schrieb:> Ich verstehe diesen Programmausschnitt nicht ganz bzw. habe wohl noch> nie so eine Programmierschreibweise gesehen. Man übergibt ja beim> Funktionsaufruf von toBin die Anfangsadresse des ersten Elements von> buffer an den Pointer *Buffer. Soweit steig ich noch mit. Aber in der> Funktion toBin tust du jetzt so, als ob Buffer kein Pointer sondern ein> Array ist. Vielleicht kann mich ja da jemand genauer aufklären...
C:
Ein Array wird an eine Funktion übergeben, indem die Startadresse des
Arrays übergeben wird. Also ein Pointer.
> Aus meiner Sicht müsste die Funktion to Bin so aussehen:>> void toBin( uint8_t zahl, char *Buffer )> {> uint8_t i;>> for( i = 0; i < 8; i++ ) {> *(Buffer+7-i) = zahl % 2;
Ist völlig identisch zu
Buffer[7-i]
da ich aber weiß, dass mir der Aufrufer ein Array übergeben hat
(übergeben muss) verwende ich hier lieber die Array-Syntax, da ich sie
an dieser Stelle klarer halte.
Eine Funktion kann grundsätzlich nicht unterscheiden, ob der Pointer den
sie bekommt zu einem einzelnen Speicherzelle verweist oder zu einem
Array. Daher muss eine Funktion grundsätzlich immer darauf vertrauen,
dass ihr Aufrufer das schon richtig macht.
Wenn du willst, kannst du den Funktionskopf auch so schreiben
1
voidtoBin(uint8_tzahl,charBuffer[])
2
{
3
...
4
}
Ändert aber nichts daran, dass Buffer innerhalb der Funktion ein Pointer
ist. Ist nur eine andere Schreibweise.
> erscheint auf der LCD-Anzeige die Zeichenkette "|| || || || || || || ||> ||".
Autsch.
Mein Fehler. Da hab ich nicht mitgedacht
Karl Heinz Buchegger schrieb:> Mein Fehler. Da hab ich nicht mitgedacht> void toBin( uint8_t zahl, char *Buffer )> {> uint8_t i;>> for( i = 0; i < 8; i++ ) {> Buffer[7-i] = ( zahl % 2 ) + '0';> zahl = zahl / 2;> }> Buffer[8] = '\0';> }
Respekt. Jetzt funktioniert es. Kannst du mir vielleicht noch kurz
erklären, was deine Änderung mit dem + '0' bewirkt? Kann es sein, dass
dadurch der Inhalt in Buffer nicht als Zahl, sondern als Zeichen
abspeichert?
Gruß
Andi schrieb:> Respekt. Jetzt funktioniert es. Kannst du mir vielleicht noch kurz> erklären, was deine Änderung mit dem + '0' bewirkt? Kann es sein, dass> dadurch der Inhalt in Buffer nicht als Zahl, sondern als Zeichen> abspeichert?
Ein Zeichen ist im Grunde auch nichts anderes als eine Zahl in einer
Variablen. Aber nicht einfach irgendeine Zahl.
Die Funktion lcd_string geht davon aus, dass der 'Text' den man ihr
übergibt so aufgebaut ist, dass sich da drin ASCII Codes befinden. Also
muss man, wenn man den Text zusammenbaut auch ASCII Codes
hineinschreiben.
http://www.torsten-horn.de/techdocs/ascii.htm
Wenn ich in der Funktion toBin aber rumrechne, bekomme ich an dieser
Stelle ja die Zahlen 0 oder 1 raus. Nur kann ich mit denen so erst mal
nichts anfangen. Denn was ich brauche ist ja der ASCII Code von '0' bzw.
'1'
Ich könnte jetzt natürlich schreiben
if( zahl % 2 == 0 )
Buffer[7-i] = '0';
else
Buffer[7-i] = '1';
wäre eine Möglichkeit.
Ich kann aber auch ausnutzen, dass die ASCII Codes der Ziffern alle
aufsteigend sind. Wenn ich also zum ASCII Code von 0 (denn nichts
anderes gebe ich ja mit '0' an) einfach das Rechenergebnis dazuzähle,
dann ändert sich nichts wenn das Rechenergebnis 0 war. Und wenn das
Rechenergebnis 1 war, dann erhalte ich damit automatisch den ASCII Code
von '1'.
Und das ist genau das, was ich ja im String haben will.
Du magst dich vielleicht wundern, wie man mit Zeichen rechnen kann. Die
Lösung besteht eben darin, dass es in Wirklichkeit keine Zeichen in dem
Sinne gibt. Wenn du schreibst
char c = 'A';
dann wird da der ASCII Code von 'A' in c gespeichert. Das ist daher
völlig gleichwertig zu
char c = 65;
(schau in die ASCII Tabelle. Der Code für 'A' ist 65)
oder auch
char c = 0x41;
in allen 3 Fällen enthält die Variable c danach das Bitmuster 01000001.
Und wenn ein Ausgabegerät, welches ASCII versteht (wie zb dein LCD)
dieses Bitmuster zur Ausgabe präsentiert bekommt, dann malt es eben ein
A hin.
Das heißt aber auch: Zeichen und Zahlen sind im Programm eigentlich
dasselbe. Jedes Zeichen wird durch eine bestimmte Zahl repräsentiert.
Erst bei der Ausgabe entscheidet sich, welche Version ich sehen will:
Will ich die Zahl selber sehen oder will ich das Zeichen sehen, für das
diese Zahl steht. Aber innerhalb des µC sind das einfach nur Zahlen in
einem Byte. Und: daher kann man auch mit denen rechnen.
starte ich ja die Funktion befuellen(). Eigentlich habe ich das ja so
gedacht, dass ich die Taste an PD7 eine Sekunde lang drücken muss, also
PD7 eine Sekunde lang auf low ist, damit die Funktion befuellen()
gestartet wird.
Jetzt hab ich aber gerade festgestellt, dass die Funktion befuellen()
auch dann schon aufgerufen wird, wenn PD7 nur ganz kurz (im ms-Bereich)
auf low ist.
Irgendwie verstehe ich dieses Verhalten nicht, denn nachdem PD7 low ist,
wird ja sofort die delay-Funktion aufgerufen, welche nach einer Sekunde
wieder verlassen wird. Aber dann hätte ich gedacht, dass die Funktion
befuellen() nur dann aufgerufen wird, wenn PD7 immer noch low ist.
Gruß
Andi schrieb:> Aber dann hätte ich gedacht, dass die Funktion> befuellen() nur dann aufgerufen wird, wenn PD7 immer noch low ist.
Wenn du das willst musst du es auch schreiben...
>> starte ich ja die Funktion befuellen(). Eigentlich habe ich das ja so> gedacht, dass ich die Taste an PD7 eine Sekunde lang drücken muss, also> PD7 eine Sekunde lang auf low ist, damit die Funktion befuellen()> gestartet wird.
Das ist aber nicht das, was du programmiert hast.
Was du programmiert hast ist:
wenn eine Taste gedrückt ist
warte 1 Sekunde
rufe die Funktion auf
Steht genau so da. Davon, dass die Taste 1 Sekunde lang gedrückt sein
muss, kommt in deinem Code nichts vor.
Wenn ich dir Tür aufmache, dann eine halbe Stunde warte und erst dann
das Licht einschalte, bedeutet das ja nicht, dass die Tür eine halbe
Stunde auf war. Es bedeutet lediglich, dass eine halbe Stunde vor dem
Licht einschalten die Tür aufgemacht wurde. Was in der Zwischenzeit mit
der Tür passierte, ist damit nicht gesagt.
> Irgendwie verstehe ich dieses Verhalten nicht, denn nachdem PD7 low ist,> wird ja sofort die delay-Funktion aufgerufen, welche nach einer Sekunde> wieder verlassen wird. Aber dann hätte ich gedacht, dass die Funktion> befuellen() nur dann aufgerufen wird, wenn PD7 immer noch low ist.
Warum sollte es?
Eine derartige Abfrage hast du nicht programmiert.
Eigentlich ist diese Methode allerdings nicht ganz sauber, denn: Es
reicht wenn der Taster in dem Moment gedrückt ist wo die beiden ifs
durchlaufen, dazwischen (während delay_ms) läuft ist der Zustand egal.
Karl Heinz Buchegger schrieb:> Ich kann aber auch ausnutzen, dass die ASCII Codes der Ziffern alle> aufsteigend sind. Wenn ich also zum ASCII Code von 0 (denn nichts> anderes gebe ich ja mit '0' an) einfach das Rechenergebnis dazuzähle,> dann ändert sich nichts wenn das Rechenergebnis 0 war. Und wenn das> Rechenergebnis 1 war, dann erhalte ich damit automatisch den ASCII Code> von '1'.
Danke für diese tolle Erklärung. Dann wird also zum Beispiel
char variable= 1 + '0'
als variable= 0b00000001 + 0b00110000= 0b00110001
abgespeichert, was wiederum dem ASCII-Wert von 1 entspricht.
Eigentlich nutzt man dabei ja den Umstand aus, dass die ASCII-Zeichen
von 0 und 1 im ASCII-Code auch nur um die Zahl 1 verschieden sind.
Allgemein kann man also sagen, dass von einer Zahl nur dann der
ASCII-Wert in einer Variablen (bzw. in einem Array) abgespeichert
werden, wenn man die Zahl innerhalb der '...' Zeichen schreibt.
Danke, das hab ich jetzt echt gut verstanden.
Gruß
Karl Heinz Buchegger schrieb:> wenn eine Taste gedrückt ist> warte 1 Sekunde> rufe die Funktion auf
Also ist das was man sich bei sowas immer merken muss die Tatsache, dass
das Programm innerhalb eines Blocks streng von oben nach unten arbeitet,
egal ob die vorherige Bedingung für das Springen in den Block (hier
PD7==low) während der Blockabarbeitung immer noch aktiv ist.
Gruß
Andi schrieb:> Also ist das was man sich bei sowas immer merken muss die Tatsache, dass> das Programm innerhalb eines Blocks streng von oben nach unten arbeitet,> egal ob die vorherige Bedingung für das Springen in den Block (hier> PD7==low) während der Blockabarbeitung immer noch aktiv ist.
Ja
if( Bedingung )
{
mach was
mach noch was
}
hat ein anderes Verhalten als
if( Bedingung )
mach was
if( Bedingung )
mach noch was
und das musst du dir auch nicht merken, das ergibt sich ganz automatisch
aus dem Lesefluss von oben nach unten. Der Compiler erfindet nichts
dazu. Wenn du den Block davon abhängig machst, dass er nur dann
ausgeführt wird, wenn eine Bedingung erfüllt ist, dann gilt diese
Bedingungserfüllung auch nur für das Betreten des Blocks. Drum steht sie
ja auch vor dem Block.
So, jetzt hab ich noch ne kleine Frage. Und zwar hab ich das Programm
jetzt noch um eine Ausgabe der Wertetabelle erweitert. Die jeweiligen
Tabellenpaare sollen dabei immer gleichzeitig auf dem LCD ausgegeben
werden.
Dafür hab ich das Programm jetzt so geändert:
1
#include<avr/io.h>
2
#include<util/delay.h>
3
#include"lcd-routines.h"
4
#include<stdbool.h>
5
#include<avr/interrupt.h>
6
#include<stdio.h> //nötig zur Benutzung von sprintf-Funktion
7
8
9
charbuffer[20],buffer1[20];
10
11
uint8_tlauf;
12
uint8_tarray[2][10]={{0,1,2,3,4,5,6,7,8,9},{}};//array mit zwei Spalten und 10 Zeilen
13
14
15
voiddelay_ms(uint16_tms)
16
{
17
while(ms>0)
18
{
19
_delay_ms(1);
20
ms--;
21
}
22
}
23
24
25
voidtoBin(uint8_tzahl,char*ptr)/*Umrechnung des jeweiligen Eingabewertes in Dualzahl, welche als ASCII-Wert abgespeichert wird
26
nötig, da bei itoa() Funktion immer das höchstwertigste Bit eine führende 1 ist*/
27
{
28
uint8_ti;
29
30
for(i=0;i<8;i++)
31
{
32
*(ptr+7-i)=(zahl%2)+'0';//1 bzw. 0 + '0'= 1 bzw. 0 + ASCII-WERT(0)= ASCII-WERT(0) bzw. ASCII-WERT(1)
PORTB=lauf;//Zur Kontrolle den Wert von lauf als Ausgabe auf den LEDs
90
element_spalte1=array[0][lauf];
91
element_spalte2=array[1][lauf];
92
93
lcd_setcursor(0,1);
94
itoa(element_spalte1,buffer1,10);//dezimale Ausgabe der Elemente der ersten Spalte
95
lcd_string(buffer1);
96
97
lcd_setcursor(0,2);
98
toBin(element_spalte2,buffer);//duale Ausgabe der Elemente der zweiten Spalte
99
lcd_string(buffer);
100
delay_ms(1000);
101
}
102
}
103
104
105
106
intmain(void)
107
{
108
109
lcd_init();
110
DDRB|=0xFF;//alle Pins als Ausgang
111
DDRD=0x00;//Alle Pins als Eingang
112
PORTB|=0xFF;//alle LEDs aus
113
114
while(1)
115
{
116
if(!(PIND&(1<<PD7)))
117
{//Nachdem Taster gedrückt ist eine Sekunde lang warten
118
delay_ms(1000);
119
befuellen();
120
121
}
122
}
123
}
Eigentlich sollte diese Erweiterung ja kein Problem darstellen. Jetzt
ist es aber so, dass die neu hinzugefügte for-Schleife in der Funktion
wertetabelle_ausgabe() nur einmal und nicht wie gewünscht achtmal
ausgeführt wird. Über die LEDs kontrolliere ich den Inhalt von lauf.
Dieser ist ständig auf 0.
Wer kann sich dieses Fehlverhalten erklären?
Gruß